diff options
Diffstat (limited to 'src')
165 files changed, 108606 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..00ec3b9 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,126 @@ +# $Id: Makefile 158 2004-10-01 03:45:15Z PoW $
+
+CC = gcc -pipe
+PACKETDEF = -DPACKETVER=5 -DNEW_006b
+#PACKETDEF = -DPACKETVER=4 -DNEW_006b
+#PACKETDEF = -DPACKETVER=3 -DNEW_006b
+#PACKETDEF = -DPACKETVER=2 -DNEW_006b
+#PACKETDEF = -DPACKETVER=1 -DNEW_006b
+
+PLATFORM = $(shell uname)
+
+ifeq ($(findstring FreeBSD,$(PLATFORM)), FreeBSD)
+MAKE = gmake
+else
+MAKE = make
+endif
+
+OPT = -g -O2
+
+ifeq ($(findstring CYGWIN,$(PLATFORM)), CYGWIN)
+OS_TYPE = -DCYGWIN
+CFLAGS = $(OPT) -Wall -DFD_SETSIZE=4096 -I../common $(PACKETDEF) $(OS_TYPE)
+else
+OS_TYPE =
+CFLAGS = $(OPT) -Wall -I../common $(PACKETDEF) $(OS_TYPE)
+endif
+
+MYSQLFLAG_INCLUDE_DEFAULT = /usr/local/include/mysql
+
+ifdef SQLFLAG
+MYSQLFLAG_CONFIG = $(shell which mysql_config)
+ifeq ($(findstring /,$(MYSQLFLAG_CONFIG)), /)
+MYSQLFLAG_VERSION = $(shell $(MYSQLFLAG_CONFIG) --version | sed s:\\..*::)
+endif
+
+ifeq ($(findstring 5,$(MYSQLFLAG_VERSION)), 5)
+MYSQLFLAG_CONFIG_ARGUMENT = --include
+endif
+ifeq ($(findstring 4,$(MYSQLFLAG_VERSION)), 4)
+MYSQLFLAG_CONFIG_ARGUMENT = --include
+endif
+ifndef MYSQLFLAG_CONFIG_ARGUMENT
+MYSQLFLAG_CONFIG_ARGUMENT = --cflags
+endif
+
+ifeq ($(findstring /,$(MYSQLFLAG_CONFIG)), /)
+MYSQLFLAG_INCLUDE = $(shell $(MYSQLFLAG_CONFIG) $(MYSQLFLAG_CONFIG_ARGUMENT))
+else
+MYSQLFLAG_INCLUDE = -I$(MYSQLFLAG_INCLUDE_DEFAULT)
+endif
+
+LIB_S_DEFAULT = -L/usr/local/lib/mysql -lmysqlclient -lz
+MYSQLFLAG_CONFIG = $(shell which mysql_config)
+ifeq ($(findstring /,$(MYSQLFLAG_CONFIG)), /)
+LIB_S = $(shell $(MYSQLFLAG_CONFIG) --libs)
+else
+LIB_S = $(LIB_S_DEFAULT)
+endif
+
+MYLIB = CC="$(CC)" CFLAGS="$(CFLAGS) $(MYSQLFLAG_INCLUDE)" LIB_S="$(LIB_S)"
+
+endif
+
+MKDEF = CC="$(CC)" CFLAGS="$(CFLAGS)"
+
+all: conf txt
+
+conf:
+ cp -r conf-tmpl conf
+ rm -rf conf/.svn conf/*/.svn
+
+txt : src/common/GNUmakefile src/login/GNUmakefile src/char/GNUmakefile src/map/GNUmakefile src/ladmin/GNUmakefile conf
+ cd src ; cd common ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd login ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd char ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd map ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd ladmin ; $(MAKE) $(MKDEF) $@ ; cd ..
+
+
+ifdef SQLFLAG
+sql: src/common/GNUmakefile src/login_sql/GNUmakefile src/char_sql/GNUmakefile src/map/GNUmakefile src/ladmin/GNUmakefile src/txt-converter/login/GNUmakefile src/txt-converter/char/GNUmakefile conf
+ cd src ; cd common ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd login_sql ; $(MAKE) $(MYLIB) $@ ; cd ..
+ cd src ; cd char_sql ; $(MAKE) $(MYLIB) $@ ; cd ..
+ cd src ; cd map ; $(MAKE) $(MYLIB) $@ ; cd ..
+ cd src ; cd ladmin ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd txt-converter ; cd login ; $(MAKE) $(MYLIB) $@ ; cd ..
+ cd src ; cd txt-converter ; cd char ; $(MAKE) $(MYLIB) $@ ; cd ..
+else
+sql:
+ $(MAKE) CC="$(CC)" OPT="$(OPT)" SQLFLAG=1 $@
+endif
+
+clean: src/common/GNUmakefile src/login/GNUmakefile src/char/GNUmakefile src/map/GNUmakefile src/ladmin/GNUmakefile src/txt-converter/login/GNUmakefile src/txt-converter/char/GNUmakefile
+ cd src ; cd common ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd login ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd login_sql ; $(MAKE) $(MKLIB) $@ ; cd ..
+ cd src ; cd char ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd char_sql ; $(MAKE) $(MKLIB) $@ ; cd ..
+ cd src ; cd map ; $(MAKE) $(MKLIB) $@ ; cd ..
+ cd src ; cd ladmin ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd txt-converter ; cd login ; $(MAKE) $(MKLIB) $@ ; cd ..
+ cd src ; cd txt-converter ; cd char ; $(MAKE) $(MKLIB) $@ ; cd ..
+
+tools:
+ cd tool && $(MAKE) $(MKDEF) && cd ..
+ $(CC) -o setupwizard setupwizard.c
+
+src/common/GNUmakefile: src/common/Makefile
+ sed -e 's/$$>/$$^/' src/common/Makefile > src/common/GNUmakefile
+src/login/GNUmakefile: src/login/Makefile
+ sed -e 's/$$>/$$^/' src/login/Makefile > src/login/GNUmakefile
+src/login_sql/GNUmakefile: src/login_sql/Makefile
+ sed -e 's/$$>/$$^/' src/login_sql/Makefile > src/login_sql/GNUmakefile
+src/char/GNUmakefile: src/char/Makefile
+ sed -e 's/$$>/$$^/' src/char/Makefile > src/char/GNUmakefile
+src/char_sql/GNUmakefile: src/char_sql/Makefile
+ sed -e 's/$$>/$$^/' src/char_sql/Makefile > src/char_sql/GNUmakefile
+src/map/GNUmakefile: src/map/Makefile
+ sed -e 's/$$>/$$^/' src/map/Makefile > src/map/GNUmakefile
+src/ladmin/GNUmakefile: src/ladmin/Makefile
+ sed -e 's/$$>/$$^/' src/ladmin/Makefile > src/ladmin/GNUmakefile
+src/txt-converter/login/GNUmakefile: src/txt-converter/login/Makefile
+ sed -e 's/$$>/$$^/' src/txt-converter/login/Makefile > src/txt-converter/login/GNUmakefile
+src/txt-converter/char/GNUmakefile: src/txt-converter/char/Makefile
+ sed -e 's/$$>/$$^/' src/txt-converter/char/Makefile > src/txt-converter/char/GNUmakefile
diff --git a/src/Makefile-optimized b/src/Makefile-optimized new file mode 100644 index 0000000..63dc5ff --- /dev/null +++ b/src/Makefile-optimized @@ -0,0 +1,49 @@ +# $Id: Makefile-optimized,v 1.7 2004/07/29 09:35:21 Yor Exp $
+
+CC = gcc -pipe
+PACKETDEF = -DPACKETVER=5 -DNEW_006b
+#PACKETDEF = -DPACKETVER=4 -DNEW_006b
+#PACKETDEF = -DPACKETVER=3 -DNEW_006b
+#PACKETDEF = -DPACKETVER=2 -DNEW_006b
+#PACKETDEF = -DPACKETVER=1 -DNEW_006b
+
+PLATFORM = $(shell uname)
+
+ifeq ($(findstring FreeBSD,$(PLATFORM)), FreeBSD)
+MAKE = gmake
+else
+MAKE = make
+endif
+
+ifeq ($(findstring CYGWIN,$(PLATFORM)), CYGWIN)
+OS_TYPE = -DCYGWIN
+CFLAGS = -O2 -Wall -DFD_SETSIZE=4096 -I../common $(PACKETDEF) $(OS_TYPE)
+else
+OS_TYPE =
+CFLAGS = -O2 -Wall -I../common $(PACKETDEF) $(OS_TYPE)
+endif
+
+MKDEF = CC="$(CC)" CFLAGS="$(CFLAGS)"
+
+
+all clean: src/common/GNUmakefile src/login/GNUmakefile src/char_unblocked/GNUmakefile src/map/GNUmakefile src/ladmin/GNUmakefile
+ cd src ; cd common ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd login ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd char_unblocked ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd map ; $(MAKE) $(MKDEF) $@ ; cd ..
+ cd src ; cd ladmin ; $(MAKE) $(MKDEF) $@ ; cd ..
+
+tools:
+ cd tool && $(MAKE) $(MKDEF) && cd ..
+ $(CC) -o setupwizard setupwizard.c
+
+src/common/GNUmakefile: src/common/Makefile
+ sed -e 's/$$>/$$^/' src/common/Makefile > src/common/GNUmakefile
+src/login/GNUmakefile: src/login/Makefile
+ sed -e 's/$$>/$$^/' src/login/Makefile > src/login/GNUmakefile
+src/char_unblocked/GNUmakefile: src/char_unblocked/Makefile
+ sed -e 's/$$>/$$^/' src/char_unblocked/Makefile > src/char_unblocked/GNUmakefile
+src/map/GNUmakefile: src/map/Makefile
+ sed -e 's/$$>/$$^/' src/map/Makefile > src/map/GNUmakefile
+src/ladmin/GNUmakefile: src/ladmin/Makefile
+ sed -e 's/$$>/$$^/' src/ladmin/Makefile > src/ladmin/GNUmakefile
diff --git a/src/char/GNUmakefile b/src/char/GNUmakefile new file mode 100644 index 0000000..dd21288 --- /dev/null +++ b/src/char/GNUmakefile @@ -0,0 +1,17 @@ +all: char-server
+txt: char-server
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/lock.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/lock.h ../common/timer.h ../common/malloc.h
+char-server: char.o inter.o int_party.o int_guild.o int_storage.o int_pet.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $^
+
+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_pet.o: int_pet.c int_pet.h inter.h char.h $(COMMON_H)
+
+clean:
+ rm -f *.o ../../char-server
diff --git a/src/char/Makefile b/src/char/Makefile new file mode 100644 index 0000000..6eaae48 --- /dev/null +++ b/src/char/Makefile @@ -0,0 +1,17 @@ +all: char-server
+txt: char-server
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/lock.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/lock.h ../common/timer.h ../common/malloc.h
+char-server: char.o inter.o int_party.o int_guild.o int_storage.o int_pet.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $>
+
+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_pet.o: int_pet.c int_pet.h inter.h char.h $(COMMON_H)
+
+clean:
+ rm -f *.o ../../char-server
diff --git a/src/char/char.c b/src/char/char.c new file mode 100644 index 0000000..c279102 --- /dev/null +++ b/src/char/char.c @@ -0,0 +1,3149 @@ +// $Id: char.c,v 1.3 2004/09/13 16:52:16 Yor Exp $ +// original : char2.c 2003/03/14 11:58:35 Rev.1.5 + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdarg.h> + +#include "core.h" +#include "socket.h" +#include "timer.h" +#include "mmo.h" +#include "version.h" +#include "lock.h" +#include "char.h" + +#include "inter.h" +#include "int_pet.h" +#include "int_guild.h" +#include "int_party.h" +#include "int_storage.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +struct mmo_map_server server[MAX_MAP_SERVERS]; +int server_fd[MAX_MAP_SERVERS]; +int server_freezeflag[MAX_MAP_SERVERS]; // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed +int anti_freeze_enable = 0; +int ANTI_FREEZE_INTERVAL = 6; + +int login_fd, char_fd; +char userid[24]; +char passwd[24]; +char server_name[20]; +char wisp_server_name[24] = "Server"; +char login_ip_str[16]; +int login_ip; +int login_port = 6900; +char char_ip_str[16]; +int char_ip; +int char_port = 6121; +int char_maintenance; +int char_new; +int email_creation = 0; // disabled by default +char char_txt[1024]; +char backup_txt[1024]; //By zanetheinsane +char backup_txt_flag = 0; // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] +char unknown_char_name[1024] = "Unknown"; +char char_log_filename[1024] = "log/char.log"; +//Added for lan support +char lan_map_ip[128]; +int subneti[4]; +int subnetmaski[4]; +int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] +int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor] +char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor] + +struct char_session_data{ + int account_id, login_id1, login_id2, sex; + 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 = 150000; +struct mmo_charstatus *char_dat; +int char_num, char_max; +int max_connect_user = 0; +int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; +int start_zeny = 500; +int start_weapon = 1201; +int start_armor = 1202; + +// Initial position (it's possible to set it in conf file) +struct point start_point = {"new_1-1.gat", 53, 111}; + +struct gm_account *gm_account = NULL; +int GM_num = 0; + +// online players by [Yor] +char online_txt_filename[1024] = "online.txt"; +char online_html_filename[1024] = "online.html"; +int online_sorting_option = 0; // sorting option to display online players in online files +int online_display_option = 1; // display options: to know which columns must be displayed +int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer +int online_gm_display_min_level = 20; // minimum GM level to display 'GM' when we want to display it + +int *online_chars; // same size of char_dat, and id value of current server (or -1) +time_t update_online; // to update online files when we receiving information from a server (not less than 8 seconds) + +//------------------------------ +// Writing function of logs file +//------------------------------ +int char_log(char *fmt, ...) { + FILE *logfp; + va_list ap; + struct timeval tv; + char tmpstr[2048]; + + va_start(ap, fmt); + + logfp = fopen(char_log_filename, "a"); + if (logfp) { + if (fmt[0] == '\0') // jump a line if no message + fprintf(logfp, RETCODE); + else { + gettimeofday(&tv, NULL); + strftime(tmpstr, 24, "%d-%m-%Y %H:%M:%S", localtime(&(tv.tv_sec))); + sprintf(tmpstr + 19, ".%03d: %s", (int)tv.tv_usec / 1000, fmt); + vfprintf(logfp, tmpstr, ap); + } + fclose(logfp); + } + + va_end(ap); + return 0; +} + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars(unsigned char *str) { + int i; + int change = 0; + + for(i = 0; str[i]; i++) { + if (str[i] < 32) { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +int isGM(int account_id) { + int i; + + for(i = 0; i < GM_num; i++) + if (gm_account[i].account_id == account_id) + return gm_account[i].level; + return 0; +} + +//---------------------------------------------- +// Search an character id +// (return character index or -1 (if not found)) +// If exact character name is not found, +// the function checks without case sensitive +// and returns index if only 1 character is found +// and similar to the searched name. +//---------------------------------------------- +int search_character_index(char* character_name) { + int i, quantity, index; + + quantity = 0; + index = -1; + for(i = 0; i < char_num; i++) { + // Without case sensitive check (increase the number of similar character names found) + if (stricmp(char_dat[i].name, character_name) == 0) { + // Strict comparison (if found, we finish the function immediatly with correct value) + if (strcmp(char_dat[i].name, character_name) == 0) + return i; + quantity++; + index = i; + } + } + // Here, the exact character name is not found + // We return the found index of a similar account ONLY if there is 1 similar character + if (quantity == 1) + return index; + + // Exact character name is not found and 0 or more than 1 similar characters have been found ==> we say not found + return -1; +} + +//------------------------------------- +// Return character name with the index +//------------------------------------- +char * search_character_name(int index) { + + if (index >= 0 && index < char_num) + return char_dat[index].name; + + return unknown_char_name; +} + +//------------------------------------------------- +// Function to create the character line (for save) +//------------------------------------------------- +int mmo_char_tostr(char *str, struct mmo_charstatus *p) { + int i; + char *str_p = str; + + // on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart. + if (p->last_point.map[0] == '\0') { + memcpy(p->last_point.map, "prontera.gat", 16); + p->last_point.x = 273; + p->last_point.y = 354; + } + + str_p += sprintf(str_p, "%d\t%d,%d\t%s\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%s,%d,%d\t%s,%d,%d,%d\t", + p->char_id, p->account_id, p->char_num, p->name, // + p->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, + p->partner_id); + for(i = 0; i < 10; i++) + if (p->memo_point[i].map[0]) { + str_p += sprintf(str_p, "%s,%d,%d", p->memo_point[i].map, p->memo_point[i].x, p->memo_point[i].y); + } + *(str_p++) = '\t'; + + for(i = 0; i < MAX_INVENTORY; i++) + if (p->inventory[i].nameid) { + str_p += sprintf(str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->inventory[i].id, p->inventory[i].nameid, p->inventory[i].amount, p->inventory[i].equip, + p->inventory[i].identify, p->inventory[i].refine, p->inventory[i].attribute, + p->inventory[i].card[0], p->inventory[i].card[1], p->inventory[i].card[2], p->inventory[i].card[3], + p->inventory[i].broken); + } + *(str_p++) = '\t'; + + for(i = 0; i < MAX_CART; i++) + if (p->cart[i].nameid) { + str_p += sprintf(str_p, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->cart[i].id, p->cart[i].nameid, p->cart[i].amount, p->cart[i].equip, + p->cart[i].identify, p->cart[i].refine, p->cart[i].attribute, + p->cart[i].card[0], p->cart[i].card[1], p->cart[i].card[2], p->cart[i].card[3], + p->cart[i].broken); + } + *(str_p++) = '\t'; + + for(i = 0; i < MAX_SKILL; i++) + if (p->skill[i].id && 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 < p->global_reg_num; i++) + if (p->global_reg[i].str[0]) + str_p += sprintf(str_p, "%s,%d ", p->global_reg[i].str, p->global_reg[i].value); + *(str_p++) = '\t'; + + *str_p = '\0'; + return 0; +} + +//------------------------------------------------------------------------- +// Function to set the character from the line (at read of characters file) +//------------------------------------------------------------------------- +int mmo_char_fromstr(char *str, struct mmo_charstatus *p) { + int tmp_int[256]; + int set, next, len, i; + + // initilialise character + memset(p, '\0', sizeof(struct mmo_charstatus)); + + // If it's not char structure of version 1008 and after + if ((set = sscanf(str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%[^,],%d,%d\t%[^,],%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_int[6], &tmp_int[7], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], &tmp_int[39], &next)) != 43) { + tmp_int[39] = 0; // partner id + // If not char structure from version 384 to 1007 + if ((set = sscanf(str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%[^,],%d,%d\t%[^,],%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_int[6], &tmp_int[7], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], &next)) != 42) { + // It's char structure of a version before 384 + tmp_int[26] = 0; // pet id + set = sscanf(str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%[^,],%d,%d\t%[^,],%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_int[6], &tmp_int[7], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], // + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], &next); + set += 2; + //printf("char: old char data ver.1\n"); + // Char structure of version 1007 or older + } else { + set++; + //printf("char: old char data ver.2\n"); + } + // Char structure of version 1008+ + } else { + //printf("char: new char data ver.3\n"); + } + if (set != 43) + return 0; + + p->char_id = tmp_int[0]; + p->account_id = tmp_int[1]; + p->char_num = tmp_int[2]; + p->class = tmp_int[3]; + p->base_level = tmp_int[4]; + p->job_level = tmp_int[5]; + p->base_exp = tmp_int[6]; + p->job_exp = tmp_int[7]; + p->zeny = tmp_int[8]; + p->hp = tmp_int[9]; + p->max_hp = tmp_int[10]; + p->sp = tmp_int[11]; + p->max_sp = tmp_int[12]; + p->str = tmp_int[13]; + p->agi = tmp_int[14]; + p->vit = tmp_int[15]; + p->int_ = tmp_int[16]; + p->dex = tmp_int[17]; + p->luk = tmp_int[18]; + p->status_point = tmp_int[19]; + p->skill_point = tmp_int[20]; + p->option = tmp_int[21]; + p->karma = tmp_int[22]; + p->manner = tmp_int[23]; + p->party_id = tmp_int[24]; + p->guild_id = tmp_int[25]; + p->pet_id = tmp_int[26]; + p->hair = tmp_int[27]; + p->hair_color = tmp_int[28]; + p->clothes_color = tmp_int[29]; + p->weapon = tmp_int[30]; + p->shield = tmp_int[31]; + p->head_top = tmp_int[32]; + p->head_mid = tmp_int[33]; + p->head_bottom = tmp_int[34]; + p->last_point.x = tmp_int[35]; + p->last_point.y = tmp_int[36]; + p->save_point.x = tmp_int[37]; + p->save_point.y = tmp_int[38]; + p->partner_id = tmp_int[39]; + + // Some checks + for(i = 0; i < char_num; i++) { + if (char_dat[i].char_id == p->char_id) { + printf("\033[1;31mmmo_auth_init: ******Error: a character has an identical id to another.\n"); + printf(" character id #%d -> new character not readed.\n", p->char_id); + printf(" Character saved in log file.\033[0m\n"); + return -1; + } else if (strcmp(char_dat[i].name, p->name) == 0) { + printf("\033[1;31mmmo_auth_init: ******Error: character name already exists.\n"); + printf(" character name '%s' -> new character not readed.\n", p->name); + printf(" Character saved in log file.\033[0m\n"); + return -2; + } + } + + if (strcmpi(wisp_server_name, p->name) == 0) { + printf("mmo_auth_init: ******WARNING: character name has wisp server name.\n"); + printf(" Character name '%s' = wisp server name '%s'.\n", p->name, wisp_server_name); + printf(" Character readed. Suggestion: change the wisp server name.\n"); + char_log("mmo_auth_init: ******WARNING: character name has wisp server name: Character name '%s' = wisp server name '%s'." RETCODE, + p->name, wisp_server_name); + } + + if (str[next] == '\n' || str[next] == '\r') + return 1; // 新規データ + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if (sscanf(str+next, "%[^,],%d,%d%n", p->memo_point[i].map, &tmp_int[0], &tmp_int[1], &len) != 3) + return -3; + p->memo_point[i].x = tmp_int[0]; + p->memo_point[i].y = tmp_int[1]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { + // do nothing, it's ok + } else if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) { + tmp_int[11] = 0; // broken doesn't exist in this version -> 0 + } else // invalid structure + return -4; + p->inventory[i].id = tmp_int[0]; + p->inventory[i].nameid = tmp_int[1]; + p->inventory[i].amount = tmp_int[2]; + p->inventory[i].equip = tmp_int[3]; + p->inventory[i].identify = tmp_int[4]; + p->inventory[i].refine = tmp_int[5]; + p->inventory[i].attribute = tmp_int[6]; + p->inventory[i].card[0] = tmp_int[7]; + p->inventory[i].card[1] = tmp_int[8]; + p->inventory[i].card[2] = tmp_int[9]; + p->inventory[i].card[3] = tmp_int[10]; + p->inventory[i].broken = tmp_int[11]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { + // do nothing, it's ok + } else if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) { + tmp_int[11] = 0; // broken doesn't exist in this version -> 0 + } else // invalid structure + return -5; + p->cart[i].id = tmp_int[0]; + p->cart[i].nameid = tmp_int[1]; + p->cart[i].amount = tmp_int[2]; + p->cart[i].equip = tmp_int[3]; + p->cart[i].identify = tmp_int[4]; + p->cart[i].refine = tmp_int[5]; + p->cart[i].attribute = tmp_int[6]; + p->cart[i].card[0] = tmp_int[7]; + p->cart[i].card[1] = tmp_int[8]; + p->cart[i].card[2] = tmp_int[9]; + p->cart[i].card[3] = tmp_int[10]; + p->cart[i].broken = tmp_int[11]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if (sscanf(str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len) != 2) + return -6; + p->skill[tmp_int[0]].id = tmp_int[0]; + p->skill[tmp_int[0]].lv = tmp_int[1]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t' && str[next] != '\n' && str[next] != '\r'; i++) { // global_reg実装以前のathena.txt互換のため一応'\n'チェック + if (sscanf(str + next, "%[^,],%d%n", p->global_reg[i].str, &p->global_reg[i].value, &len) != 2) { + // because some scripts are not correct, the str can be "". So, we must check that. + // If it's, we must not refuse the character, but just this REG value. + // Character line will have something like: nov_2nd_cos,9 ,9 nov_1_2_cos_c,1 (here, ,9 is not good) + if (str[next] == ',' && sscanf(str + next, ",%d%n", &p->global_reg[i].value, &len) == 1) + i--; + else + return -7; + } + next += len; + if (str[next] == ' ') + next++; + } + p->global_reg_num = i; + + return 1; +} + +//--------------------------------- +// Function to read characters file +//--------------------------------- +int mmo_char_init(void) { + char line[65536]; + int i; + int ret, line_count; + FILE *fp; + + char_max = 256; + char_dat = calloc(sizeof(struct mmo_charstatus) * 256, 1); + if (!char_dat) { + printf("out of memory: mmo_char_init (calloc of char_dat).\n"); + exit(1); + } + online_chars = calloc(sizeof(int) * 256, 1); + if (!online_chars) { + printf("out of memory: mmo_char_init (calloc of online_chars).\n"); + exit(1); + } + for(i = 0; i < char_max; i++) + online_chars[i] = -1; + + char_num = 0; + + fp = fopen(char_txt, "r"); + if (fp == NULL) { + printf("Characters file not found: %s.\n", char_txt); + char_log("Characters file not found: %s." 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 = realloc(char_dat, sizeof(struct mmo_charstatus) * char_max); + if (!char_dat) { + printf("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); + } + online_chars = realloc(online_chars, sizeof(int) * char_max); + if (!online_chars) { + printf("Out of memory: mmo_char_init (realloc of online_chars).\n"); + char_log("Out of memory: mmo_char_init (realloc of online_chars)." RETCODE); + exit(1); + } + for(i = char_max - 256; i < char_max; i++) + online_chars[i] = -1; + } + + ret = mmo_char_fromstr(line, &char_dat[char_num]); + if (ret > 0) { // negative value or zero for errors + if (char_dat[char_num].char_id >= char_id_count) + char_id_count = char_dat[char_num].char_id + 1; + char_num++; + } else { + printf("mmo_char_init: in characters file, unable to read the line #%d.\n", line_count); + printf(" -> Character saved in log file.\n"); + switch (ret) { + case -1: + char_log("Duplicate character id in the next character line (character not readed):" 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) { + printf("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) { + printf("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 { + printf("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]; + int i, j, k; + int lock; + FILE *fp; + int id[char_num]; + + // Sorting before save (by [Yor]) + for(i = 0; i < char_num; i++) { + id[i] = i; + for(j = 0; j < i; j++) { + if ((char_dat[i].account_id < char_dat[id[j]].account_id) || + // if same account id, we sort by slot. + (char_dat[i].account_id == char_dat[id[j]].account_id && + char_dat[i].char_num < char_dat[id[j]].char_num)) { + for(k = i; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[i] + break; + } + } + } + + // Data save + fp = lock_fopen(char_txt, &lock); + if (fp == NULL) { + printf("WARNING: Server can't not save characters.\n"); + char_log("WARNING: Server can't not save characters." 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]]); // 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) { + printf("WARNING: Server can't not create backup of characters file.\n"); + char_log("WARNING: Server can't not create backup of characters file." RETCODE); + return; + } + for(i = 0; i < char_num; i++) { + // create only once the line, and save it in the 2 files (it's speeder than repeat twice the loop and create twice the line) + mmo_char_tostr(line, &char_dat[id[i]]); // use of sorted index + fprintf(fp, "%s" RETCODE, line); + } + fprintf(fp, "%d\t%%newid%%" RETCODE, char_id_count); + lock_fclose(fp, backup_txt, &lock); + } + + 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) { + mmo_char_sync(); + inter_save(); + return 0; +} + +//----------------------------------- +// Function to create a new character +//----------------------------------- +int make_new_char(int fd, unsigned char *dat) { + int i, j; + struct char_session_data *sd; + + sd = session[fd]->session_data; + + // remove control characters from the name + dat[23] = '\0'; + if (remove_control_chars(dat)) { + char_log("Make new char error (control char received in the name): (connection #%d, account: %d)." RETCODE, + fd, sd->account_id); + return -1; + } + + // check lenght of character name + if (strlen(dat) < 4) { + char_log("Make new char error (character name too small): (connection #%d, account: %d, name: '%s')." 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; dat[i]; i++) + if (strchr(char_name_letters, dat[i]) == NULL) { + char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." RETCODE, + fd, sd->account_id, dat, dat[i]); + return -1; + } + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; dat[i]; i++) + if (strchr(char_name_letters, dat[i]) != NULL) { + char_log("Make new char error (invalid letter in the name): (connection #%d, account: %d), name: %s, invalid letter: %c." 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] >= 20 || // hair style + dat[31] >= 12) { // hair color (dat[31] can not be negativ) + char_log("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" 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; + } + } + + for(i = 0; i < char_num; i++) { + if ((name_ignoring_case != 0 && strcmp(char_dat[i].name, dat) == 0) || + (name_ignoring_case == 0 && strcmpi(char_dat[i].name, dat) == 0)) { + char_log("Make new char error (name already exists): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %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].name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + if (char_dat[i].account_id == sd->account_id && char_dat[i].char_num == dat[30]) { + char_log("Make new char error (slot already used): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %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].name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + } + + if (strcmp(wisp_server_name, dat) == 0) { + char_log("Make new char error (name used is wisp name for server): (connection #%d, account: %d) slot %d, name: %s (actual name of other char: %d), stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d." RETCODE, + fd, sd->account_id, dat[30], dat, char_dat[i].name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); + return -1; + } + + if (char_num >= char_max) { + char_max += 256; + char_dat = realloc(char_dat, sizeof(struct mmo_charstatus) * char_max); + if (!char_dat) { + printf("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); + } + online_chars = realloc(online_chars, sizeof(int) * char_max); + if (!online_chars) { + printf("Out of memory: make_new_char (realloc of online_chars).\n"); + char_log("Out of memory: make_new_char (realloc of online_chars)." RETCODE); + exit(1); + } + for(j = char_max - 256; j < char_max; j++) + online_chars[j] = -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], 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]); + + memset(&char_dat[i], 0, sizeof(struct mmo_charstatus)); + + char_dat[i].char_id = char_id_count++; + char_dat[i].account_id = sd->account_id; + char_dat[i].char_num = dat[30]; + strcpy(char_dat[i].name, dat); + char_dat[i].class = 0; + char_dat[i].base_level = 1; + char_dat[i].job_level = 1; + char_dat[i].base_exp = 0; + char_dat[i].job_exp = 0; + char_dat[i].zeny = start_zeny; + char_dat[i].str = dat[24]; + char_dat[i].agi = dat[25]; + char_dat[i].vit = dat[26]; + char_dat[i].int_ = dat[27]; + char_dat[i].dex = dat[28]; + char_dat[i].luk = dat[29]; + char_dat[i].max_hp = 40 * (100 + char_dat[i].vit) / 100; + char_dat[i].max_sp = 11 * (100 + char_dat[i].int_) / 100; + char_dat[i].hp = char_dat[i].max_hp; + char_dat[i].sp = char_dat[i].max_sp; + char_dat[i].status_point = 0; + char_dat[i].skill_point = 0; + char_dat[i].option = 0; + char_dat[i].karma = 0; + char_dat[i].manner = 0; + char_dat[i].party_id = 0; + char_dat[i].guild_id = 0; + char_dat[i].hair = dat[33]; + char_dat[i].hair_color = dat[31]; + char_dat[i].clothes_color = 0; + char_dat[i].inventory[0].nameid = start_weapon; // Knife + char_dat[i].inventory[0].amount = 1; + char_dat[i].inventory[0].equip = 0x02; + char_dat[i].inventory[0].identify = 1; + char_dat[i].inventory[0].broken = 0; + char_dat[i].inventory[1].nameid = start_armor; // Cotton Shirt + char_dat[i].inventory[1].amount = 1; + char_dat[i].inventory[1].equip = 0x10; + char_dat[i].inventory[1].identify = 1; + char_dat[i].inventory[1].broken = 0; + char_dat[i].weapon = 1; + char_dat[i].shield = 0; + char_dat[i].head_top = 0; + char_dat[i].head_mid = 0; + char_dat[i].head_bottom = 0; + memcpy(&char_dat[i].last_point, &start_point, sizeof(start_point)); + memcpy(&char_dat[i].save_point, &start_point, sizeof(start_point)); + char_num++; + + 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"; +} + +//------------------------------------------------------------- +// Function to create the online files (txt and html). by [Yor] +//------------------------------------------------------------- +void create_online_files(void) { + int i, j, k, l; // for loops + int players; // count the number of players + FILE *fp; // for the txt file + FILE *fp2; // for the html file + char temp[256]; // to prepare what we must display + time_t time_server; // for number of seconds + struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... + int id[char_num]; + + if (online_display_option == 0) // we display nothing, so return + return; + + //char_log("Creation of online players files." RETCODE); + + // Get number of online players, id of each online players + players = 0; + // sort online characters. + for(i = 0; i < char_num; i++) { + if (online_chars[i] != -1) { + id[players] = i; + // use sorting option + switch (online_sorting_option) { + case 1: // by name (without case sensitive) + { + char *p_name = char_dat[i].name; //speed up sorting when there are a lot of players. But very rarely players have same name. + for(j = 0; j < players; j++) + if (stricmp(p_name, char_dat[id[j]].name) < 0 || + // if same name, we sort with case sensitive. + (stricmp(p_name, char_dat[id[j]].name) == 0 && + strcmp(p_name, char_dat[id[j]].name) < 0)) { + for(k = players; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[players] + break; + } + } + break; + case 2: // by zeny + for(j = 0; j < players; j++) + if (char_dat[i].zeny < char_dat[id[j]].zeny || + // if same number of zenys, we sort by name. + (char_dat[i].zeny == char_dat[id[j]].zeny && + stricmp(char_dat[i].name, char_dat[id[j]].name) < 0)) { + for(k = players; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[players] + break; + } + break; + case 3: // by base level + for(j = 0; j < players; j++) + if (char_dat[i].base_level < char_dat[id[j]].base_level || + // if same base level, we sort by base exp. + (char_dat[i].base_level == char_dat[id[j]].base_level && + char_dat[i].base_exp < char_dat[id[j]].base_exp)) { + for(k = players; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[players] + break; + } + break; + case 4: // by job (and job level) + for(j = 0; j < players; j++) + if (char_dat[i].class < char_dat[id[j]].class || + // if same job, we sort by job level. + (char_dat[i].class == char_dat[id[j]].class && + char_dat[i].job_level < char_dat[id[j]].job_level) || + // if same job and job level, we sort by job exp. + (char_dat[i].class == char_dat[id[j]].class && + char_dat[i].job_level == char_dat[id[j]].job_level && + char_dat[i].job_exp < char_dat[id[j]].job_exp)) { + for(k = players; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[players] + break; + } + break; + case 5: // by location map name + { + int cpm_result; // A lot of player maps are identical. So, test if done often twice. + for(j = 0; j < players; j++) + if ((cpm_result = strcmp(char_dat[i].last_point.map, char_dat[id[j]].last_point.map)) < 0 || // no map are identical and with upper cases (not use stricmp) + // if same map name, we sort by name. + (cpm_result == 0 && + stricmp(char_dat[i].name, char_dat[id[j]].name) < 0)) { + for(k = players; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[players] + break; + } + } + break; + default: // 0 or invalid value: no sorting + break; + } + players++; + } + } + + // write files + fp = fopen(online_txt_filename, "w"); + if (fp != NULL) { + fp2 = fopen(online_html_filename, "w"); + if (fp2 != NULL) { + // get time + time(&time_server); // get time in seconds since 1/1/1970 + datetime = localtime(&time_server); // convert seconds in structure + strftime(temp, sizeof(temp), "%d %b %Y %X", datetime); // like sprintf, but only for date/time (05 dec 2003 15:12:52) + // write heading + fprintf(fp2, "<HTML>\n"); + fprintf(fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n", online_refresh_html); // update on client explorer every x seconds + fprintf(fp2, " <HEAD>\n"); + fprintf(fp2, " <TITLE>Online Players on %s</TITLE>\n", server_name); + fprintf(fp2, " </HEAD>\n"); + fprintf(fp2, " <BODY>\n"); + fprintf(fp2, " <H3>Online Players on %s (%s):</H3>\n", server_name, temp); + fprintf(fp, "Online Players on %s (%s):\n", server_name, temp); + fprintf(fp, "\n"); + + // If we display at least 1 player + if (players > 0) { + j = 0; // count the number of characters for the txt version and to set the separate line + fprintf(fp2, " <table border=\"1\" cellspacing=\"1\">\n"); + fprintf(fp2, " <tr>\n"); + if ((online_display_option & 1) || (online_display_option & 64)) { + fprintf(fp2, " <td><b>Name</b></td>\n"); + if (online_display_option & 64) { + fprintf(fp, "Name "); // 30 + j += 30; + } else { + fprintf(fp, "Name "); // 25 + j += 25; + } + } + if ((online_display_option & 6) == 6) { + fprintf(fp2, " <td><b>Job (levels)</b></td>\n"); + fprintf(fp, "Job Levels "); // 27 + j += 27; + } else if (online_display_option & 2) { + fprintf(fp2, " <td><b>Job</b></td>\n"); + fprintf(fp, "Job "); // 19 + j += 19; + } else if (online_display_option & 4) { + fprintf(fp2, " <td><b>Levels</b></td>\n"); + fprintf(fp, " Levels "); // 8 + j += 8; + } + if (online_display_option & 24) { // 8 or 16 + fprintf(fp2, " <td><b>Location</b></td>\n"); + if (online_display_option & 16) { + fprintf(fp, "Location ( x , y ) "); // 23 + j += 23; + } else { + fprintf(fp, "Location "); // 13 + j += 13; + } + } + if (online_display_option & 32) { + fprintf(fp2, " <td ALIGN=CENTER><b>zenys</b></td>\n"); + fprintf(fp, " Zenys "); // 16 + j += 16; + } + fprintf(fp2, " </tr>\n"); + fprintf(fp, "\n"); + for (k = 0; k < j; k++) + fprintf(fp, "-"); + fprintf(fp, "\n"); + + // display each player. + for (i = 0; i < players; i++) { + // get id of the character (more speed) + j = id[i]; + fprintf(fp2, " <tr>\n"); + // displaying the character name + if ((online_display_option & 1) || (online_display_option & 64)) { // without/with 'GM' display + strcpy(temp, char_dat[j].name); + l = isGM(char_dat[j].account_id); + if (online_display_option & 64) { + if (l >= online_gm_display_min_level) + fprintf(fp, "%-24s (GM) ", temp); + else + fprintf(fp, "%-24s ", temp); + } else + fprintf(fp, "%-24s ", temp); + // name of the character in the html (no < >, because that create problem in html code) + fprintf(fp2, " <td>"); + if ((online_display_option & 64) && l >= online_gm_display_min_level) + fprintf(fp2, "<b>"); + for (k = 0; temp[k]; k++) { + switch(temp[k]) { + case '<': // < + fprintf(fp2, "<"); + break; + case '>': // > + fprintf(fp2, ">"); + break; + default: + fprintf(fp2, "%c", temp[k]); + break; + }; + } + if ((online_display_option & 64) && l >= online_gm_display_min_level) + fprintf(fp2, "</b> (GM)"); + fprintf(fp2, "</td>\n"); + } + // displaying of the job + if (online_display_option & 6) { + char * jobname = job_name(char_dat[j].class); + if ((online_display_option & 6) == 6) { + fprintf(fp2, " <td>%s %d/%d</td>\n", jobname, char_dat[j].base_level, char_dat[j].job_level); + fprintf(fp, "%-18s %3d/%3d ", jobname, char_dat[j].base_level, char_dat[j].job_level); + } else if (online_display_option & 2) { + fprintf(fp2, " <td>%s</td>\n", jobname); + fprintf(fp, "%-18s ", jobname); + } else if (online_display_option & 4) { + fprintf(fp2, " <td>%d/%d</td>\n", char_dat[j].base_level, char_dat[j].job_level); + fprintf(fp, "%3d/%3d ", char_dat[j].base_level, char_dat[j].job_level); + } + } + // displaying of the map + if (online_display_option & 24) { // 8 or 16 + // prepare map name + memset(temp, 0, sizeof(temp)); + strncpy(temp, char_dat[j].last_point.map, 16); + if (strchr(temp, '.') != NULL) + temp[strchr(temp, '.') - temp] = '\0'; // suppress the '.gat' + // write map name + if (online_display_option & 16) { // map-name AND coordonates + fprintf(fp2, " <td>%s (%d, %d)</td>\n", temp, char_dat[j].last_point.x, char_dat[j].last_point.y); + fprintf(fp, "%-12s (%3d,%3d) ", temp, char_dat[j].last_point.x, char_dat[j].last_point.y); + } else { + fprintf(fp2, " <td>%s</td>\n", temp); + fprintf(fp, "%-12s ", temp); + } + } + // displaying number of zenys + if (online_display_option & 32) { + // write number of zenys + if (char_dat[j].zeny == 0) { // if no zeny + fprintf(fp2, " <td ALIGN=RIGHT>no zeny</td>\n"); + fprintf(fp, " no zeny "); + } else { + fprintf(fp2, " <td ALIGN=RIGHT>%d z</td>\n", char_dat[j].zeny); + fprintf(fp, "%13d z ", char_dat[j].zeny); + } + } + fprintf(fp, "\n"); + fprintf(fp2, " </tr>\n"); + } + fprintf(fp2, " </table>\n"); + fprintf(fp, "\n"); + } + + // Displaying number of online players + if (players == 0) { + fprintf(fp2, " <p>No user is online.</p>\n"); + fprintf(fp, "No user is online.\n"); + // no display if only 1 player + } else if (players == 1) { + } else { + fprintf(fp2, " <p>%d users are online.</p>\n", players); + fprintf(fp, "%d users are online.\n", players); + } + fprintf(fp2, " </BODY>\n"); + fprintf(fp2, "</HTML>\n"); + fclose(fp2); + } + fclose(fp); + } + + return; +} + +//--------------------------------------------------------------------- +// This function return the number of online players in all map-servers +//--------------------------------------------------------------------- +int count_users(void) { + int i, users; + + users = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + users += server[i].users; + + return users; +} + +//---------------------------------------- +// 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 + + found_num = 0; + for(i = 0; i < char_num; i++) { + if (char_dat[i].account_id == sd->account_id) { + sd->found_char[found_num] = i; + found_num++; + if (found_num == 9) + break; + } + } + for(i = found_num; i < 9; i++) + sd->found_char[i] = -1; + + memset(WFIFOP(fd,0), 0, offset + found_num * 106); + WFIFOW(fd,0) = 0x6b; + WFIFOW(fd,2) = offset + found_num * 106; + + for(i = 0; i < found_num; i++) { + p = &char_dat[sd->found_char[i]]; + j = offset + (i * 106); // increase speed of code + + WFIFOL(fd,j) = p->char_id; + WFIFOL(fd,j+4) = p->base_exp; + WFIFOL(fd,j+8) = p->zeny; + WFIFOL(fd,j+12) = p->job_exp; + WFIFOL(fd,j+16) = 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; + 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, 24); + + WFIFOB(fd,j+98) = (p->str > 255) ? 255 : p->str; + WFIFOB(fd,j+99) = (p->agi > 255) ? 255 : p->agi; + WFIFOB(fd,j+100) = (p->vit > 255) ? 255 : p->vit; + WFIFOB(fd,j+101) = (p->int_ > 255) ? 255 : p->int_; + WFIFOB(fd,j+102) = (p->dex > 255) ? 255 : p->dex; + WFIFOB(fd,j+103) = (p->luk > 255) ? 255 : p->luk; + WFIFOB(fd,j+104) = p->char_num; + } + + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +int set_account_reg2(int acc, int num, struct global_reg *reg) { + int i, c; + + c = 0; + for(i = 0; i < char_num; i++) { + if (char_dat[i].account_id == acc) { + memcpy(char_dat[i].account_reg2, reg, sizeof(char_dat[i].account_reg2)); + char_dat[i].account_reg2_num = num; + c++; + } + } + return c; +} + +// 離婚(char削除時に使用) +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].char_id == cs->partner_id && char_dat[i].partner_id == cs->char_id) { + cs->partner_id = 0; + char_dat[i].partner_id = 0; + for(j = 0; j < MAX_INVENTORY; j++) + if (char_dat[i].inventory[j].nameid == WEDDING_RING_M || char_dat[i].inventory[j].nameid == WEDDING_RING_F) + memset(&char_dat[i].inventory[j], 0, sizeof(char_dat[i].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; +} + +//------------------------------------------------------------ +// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] +//------------------------------------------------------------ +int e_mail_check(unsigned char *email) { + char ch; + unsigned char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +//---------------------------------------------------------------------- +// Force disconnection of an online player (with account value) by [Yor] +//---------------------------------------------------------------------- +int disconnect_player(int accound_id) { + int i; + struct char_session_data *sd; + + // disconnect player if online on char-server + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data)) { + if (sd->account_id == accound_id) { + session[i]->eof = 1; + return 1; + } + } + } + + return 0; +} + +// キャラ削除に伴うデータ削除 +static int char_delete(struct mmo_charstatus *cs) { + int j; + + // ペット削除 + 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(*((long *)(&cs->inventory[j].card[2]))); + for (j = 0; j < MAX_CART; j++) + if (cs->cart[j].card[0] == (short)0xff00) + inter_pet_delete(*((long *)(&cs->cart[j].card[2]))); + // ギルド脱退 + if (cs->guild_id) + inter_guild_leave(cs->guild_id, cs->account_id, cs->char_id); + // パーティー脱退 + if (cs->party_id) + inter_party_leave(cs->party_id, cs->account_id); + // 離婚 + if (cs->partner_id){ + // 離婚情報をmapに通知 + 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; + + // only login-server can have an access to here. + // so, if it isn't the login-server, we disconnect the session (fd != login_fd). + if (fd != login_fd || session[fd]->eof) { + if (fd == login_fd) { + printf("Char-server can't connect to login-server (connection #%d).\n", fd); + login_fd = -1; + } + close(fd); + delete_session(fd); + return 0; + } + + sd = session[fd]->session_data; + + while(RFIFOREST(fd) >= 2) { +// printf("parse_tologin: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch(RFIFOW(fd,0)) { + case 0x2711: + if (RFIFOREST(fd) < 3) + return 0; + if (RFIFOB(fd,2)) { +// printf("connect login server error : %d\n", RFIFOB(fd,2)); + printf("Can not connect to login-server.\n"); + printf("The server communication passwords (default s1/p1) is probably invalid.\n"); + printf("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); + printf("If you changed the communication passwords, change them back at map_athena.conf and char_athena.conf\n"); + exit(1); + } else { + printf("Connected to login-server (connection #%d).\n", fd); + // if no map-server already connected, display a message... + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0 && server[i].map[0][0]) // if map-server online and at least 1 map + break; + if (i == MAX_MAP_SERVERS) + printf("Awaiting maps from map-server.\n"); + } + RFIFOSKIP(fd,3); + break; + + case 0x2713: + if (RFIFOREST(fd) < 51) + return 0; +// printf("parse_tologin 2713 : %d\n", RFIFOB(fd,6)); + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data) && sd->account_id == RFIFOL(fd,2)) { + if (RFIFOB(fd,6) != 0) { + WFIFOW(i,0) = 0x6c; + WFIFOB(i,2) = 0x42; + WFIFOSET(i,3); + } else if (max_connect_user == 0 || count_users() < max_connect_user) { +// if (max_connect_user == 0) +// printf("max_connect_user (unlimited) -> accepted.\n"); +// else +// printf("count_users(): %d < max_connect_user (%d) -> accepted.\n", count_users(), max_connect_user); + memcpy(sd->email, RFIFOP(fd, 7), 40); + if (e_mail_check(sd->email) == 0) + strncpy(sd->email, "a@a.com", 40); // default e-mail + sd->connect_until_time = (time_t)RFIFOL(fd,47); + // send characters to player + mmo_char_send006b(i, sd); + } else { + // refuse connection: too much online players +// printf("count_users(): %d < max_connect_use (%d) -> fail...\n", count_users(), max_connect_user); + WFIFOW(i,0) = 0x6c; + WFIFOW(i,2) = 0; + WFIFOSET(i,3); + } + break; + } + } + RFIFOSKIP(fd,51); + break; + + // Receiving of an e-mail/time limit from the login-server (answer of a request because a player comes back from map-server to char-server) by [Yor] + case 0x2717: + if (RFIFOREST(fd) < 50) + return 0; + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data)) { + if (sd->account_id == RFIFOL(fd,2)) { + memcpy(sd->email, RFIFOP(fd,6), 40); + if (e_mail_check(sd->email) == 0) + strncpy(sd->email, "a@a.com", 40); // default e-mail + sd->connect_until_time = (time_t)RFIFOL(fd,46); + break; + } + } + } + RFIFOSKIP(fd,50); + break; + + case 0x2721: // gm reply + if (RFIFOREST(fd) < 10) + return 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 < char_num; i++) { + if (char_dat[i].account_id == acc) { + int jobclass = char_dat[i].class; + char_dat[i].sex = sex; + auth_fifo[i].sex = sex; + if (jobclass == 19 || jobclass == 20 || + jobclass == 4020 || jobclass == 4021 || + jobclass == 4042 || jobclass == 4043) { + // job modification + if (jobclass == 19 || jobclass == 20) { + char_dat[i].class = (sex) ? 19 : 20; + } else if (jobclass == 4020 || jobclass == 4021) { + char_dat[i].class = (sex) ? 4020 : 4021; + } else if (jobclass == 4042 || jobclass == 4043) { + char_dat[i].class = (sex) ? 4042 : 4043; + } + // remove specifical skills of classes 19, 4020 and 4042 + for(j = 315; j <= 322; j++) { + if (char_dat[i].skill[j].id > 0 && !char_dat[i].skill[j].flag) { + char_dat[i].skill_point += char_dat[i].skill[j].lv; + char_dat[i].skill[j].id = 0; + char_dat[i].skill[j].lv = 0; + } + } + // remove specifical skills of classes 20, 4021 and 4043 + for(j = 323; j <= 330; j++) { + if (char_dat[i].skill[j].id > 0 && !char_dat[i].skill[j].flag) { + char_dat[i].skill_point += char_dat[i].skill[j].lv; + char_dat[i].skill[j].id = 0; + char_dat[i].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].inventory[j].nameid && char_dat[i].inventory[j].equip) + char_dat[i].inventory[j].equip = 0; + } + char_dat[i].weapon = 0; + char_dat[i].shield = 0; + char_dat[i].head_top = 0; + char_dat[i].head_mid = 0; + char_dat[i].head_bottom = 0; + } + } + // disconnect player if online on char-server + disconnect_player(acc); + } + WBUFW(buf,0) = 0x2b0d; + WBUFL(buf,2) = acc; + WBUFB(buf,6) = sex; + mapif_sendall(buf, 7); + } + break; + + case 0x2726: // Request to send a broadcast message (no answer) + if (RFIFOREST(fd) < 8 || RFIFOREST(fd) < (8 + RFIFOL(fd,4))) + return 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 { + char buf[128]; + char message[RFIFOL(fd,4) + 1]; // +1 to add a null terminated if not exist in the packet + int lp; + char *p; + memset(message, '\0', sizeof(message)); + memcpy(message, RFIFOP(fd,8), RFIFOL(fd,4)); + message[sizeof(message)-1] = '\0'; + remove_control_chars(message); + // remove all first spaces + p = message; + while(p[0] == ' ') + p++; + // if message is only composed of spaces + if (p[0] == '\0') + char_log("Receiving a message for broadcast, but message is only a lot of spaces." 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変更通知 + case 0x2729: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + struct global_reg reg[ACCOUNT_REG2_NUM]; + unsigned char buf[4096]; + int j, p, acc; + acc = RFIFOL(fd,4); + for (p = 8, j = 0; p < RFIFOW(fd,2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { + memcpy(reg[j].str, RFIFOP(fd,p), 32); + reg[j].value = RFIFOL(fd,p+32); + } + set_account_reg2(acc, j, reg); + // 同垢ログインを禁止していれば送る必要は無い + memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf,0) = 0x2b11; + mapif_sendall(buf, WBUFW(buf,2)); + RFIFOSKIP(fd,RFIFOW(fd,2)); +// printf("char: save_account_reg_reply\n"); + } + break; + + // 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].account_id == RFIFOL(fd,2)) { + char_delete(&char_dat[i]); + if (i < char_num - 1) { + memcpy(&char_dat[i], &char_dat[char_num-1], sizeof(struct mmo_charstatus)); + // if moved character owns to deleted account, check again it's character + if (char_dat[i].account_id == RFIFOL(fd,2)) { + i--; + // Correct moved character reference in the character's owner by [Yor] + } else { + int j, k; + struct char_session_data *sd2; + for (j = 0; j < fd_max; j++) { + if (session[j] && (sd2 = session[j]->session_data) && + sd2->account_id == char_dat[char_num-1].account_id) { + for (k = 0; k < 9; k++) { + if (sd2->found_char[k] == char_num-1) { + sd2->found_char[k] = i; + break; + } + } + break; + } + } + } + } + char_num--; + } + } + // Deletion of the storage + inter_storage_delete(RFIFOL(fd,2)); + // send to all map-servers to disconnect the player + { + unsigned char buf[6]; + WBUFW(buf,0) = 0x2b13; + WBUFL(buf,2) = RFIFOL(fd,2); + mapif_sendall(buf, 6); + } + // disconnect player if online on char-server + disconnect_player(RFIFOL(fd,2)); + RFIFOSKIP(fd,6); + break; + + // State change of account/ban notification (from login-server) by [Yor] + case 0x2731: + if (RFIFOREST(fd) < 11) + return 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; + { + char buf[32000]; + if (gm_account != NULL) + free(gm_account); + gm_account = calloc(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++; + } + printf("From login-server: receiving of %d GM accounts information.\n", GM_num); + char_log("From login-server: receiving of %d GM accounts information." 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; + + default: + session[fd]->eof = 1; + return 0; + } + } + RFIFOFLUSH(fd); + + return 0; +} + +//-------------------------------- +// Map-server anti-freeze system +//-------------------------------- +int map_anti_freeze_system(int tid, unsigned int tick, int id, int data) { + int i; + + //printf("Entering in map_anti_freeze_system function to check freeze of servers.\n"); + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (server_fd[i] >= 0) {// if map-server is online + //printf("map_anti_freeze_system: server #%d, flag: %d.\n", i, server_freezeflag[i]); + if (server_freezeflag[i]-- < 1) { // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed + printf("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", i); + char_log("Map-server anti-freeze system: char-server #%d is freezed -> disconnection." RETCODE, + i); + session[server_fd[i]]->eof = 1; + } + } + } + + return 0; +} + +int parse_frommap(int fd) { + int i, j; + int id; + + for(id = 0; id < MAX_MAP_SERVERS; id++) + if (server_fd[id] == fd) + break; + if (id == MAX_MAP_SERVERS || session[fd]->eof) { + if (id < MAX_MAP_SERVERS) { + printf("Map-server %d (session #%d) has disconnected.\n", id, fd); + memset(&server[id], 0, sizeof(struct mmo_map_server)); + server_fd[id] = -1; + for(j = 0; j < char_num; j++) + if (online_chars[j] == fd) + online_chars[j] = -1; + create_online_files(); // update online players files (to remove all online players of this server) + } + close(fd); + delete_session(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2) { +// printf("parse_frommap: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch(RFIFOW(fd,0)) { + // request from map-server to reload GM accounts. Transmission to login-server (by Yor) + case 0x2af7: + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd,0) = 0x2709; + WFIFOSET(login_fd, 2); +// printf("char : request from map-server to reload GM accounts -> login-server.\n"); + } + RFIFOSKIP(fd,2); + break; + + // Receiving map names list from the map-server + case 0x2afa: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + memset(server[id].map, 0, sizeof(server[id].map)); + j = 0; + for(i = 4; i < RFIFOW(fd,2); i += 16) { + memcpy(server[id].map[j], RFIFOP(fd,i), 16); +// printf("set map %d.%d : %s\n", id, j, server[id].map[j]); + j++; + } + { + unsigned char *p = (unsigned char *)&server[id].ip; + printf("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", + id, j, p[0], p[1], p[2], p[3], server[id].port); + printf("Map-server %d loading complete.\n", id); + char_log("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d. Map-server %d loading complete." RETCODE, + id, j, p[0], p[1], p[2], p[3], server[id].port, id); + } + WFIFOW(fd,0) = 0x2afb; + WFIFOB(fd,2) = 0; + memcpy(WFIFOP(fd,3), wisp_server_name, 24); // name for wisp to player + WFIFOSET(fd,27); + { + unsigned char buf[16384]; + int x; + if (j == 0) { + printf("WARNING: Map-Server %d have NO map.\n", id); + char_log("WARNING: Map-Server %d have NO map." RETCODE, id); + // Transmitting maps information to the other map-servers + } else { + WBUFW(buf,0) = 0x2b04; + WBUFW(buf,2) = j * 16 + 10; + WBUFL(buf,4) = server[id].ip; + WBUFW(buf,8) = server[id].port; + memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 16); + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + } + // Transmitting the maps of the other map-servers to the new map-server + for(x = 0; x < MAX_MAP_SERVERS; x++) { + if (server_fd[x] >= 0 && x != id) { + WFIFOW(fd,0) = 0x2b04; + WFIFOL(fd,4) = server[x].ip; + WFIFOW(fd,8) = server[x].port; + j = 0; + for(i = 0; i < MAX_MAP_PER_SERVER; i++) + if (server[x].map[i][0]) + memcpy(WFIFOP(fd,10+(j++)*16), server[x].map[i], 16); + if (j > 0) { + WFIFOW(fd,2) = j * 16 + 10; + WFIFOSET(fd,WFIFOW(fd,2)); + } + } + } + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + // 認証要求 + case 0x2afc: + if (RFIFOREST(fd) < 22) + return 0; + //printf("auth_fifo search: account: %d, char: %d, secure: %08x-%08x\n", RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14)); + for(i = 0; i < AUTH_FIFO_SIZE; i++) { + if (auth_fifo[i].account_id == RFIFOL(fd,2) && + auth_fifo[i].char_id == RFIFOL(fd,6) && + auth_fifo[i].login_id1 == RFIFOL(fd,10) && +#if CMP_AUTHFIFO_LOGIN2 != 0 + // here, it's the only area where it's possible that we doesn't know login_id2 (map-server asks just after 0x72 packet, that doesn't given the value) + (auth_fifo[i].login_id2 == RFIFOL(fd,14) || RFIFOL(fd,14) == 0) && // relate to the versions higher than 18 +#endif + (!check_ip_flag || auth_fifo[i].ip == RFIFOL(fd,18)) && + !auth_fifo[i].delflag) { + auth_fifo[i].delflag = 1; + WFIFOW(fd,0) = 0x2afd; + WFIFOW(fd,2) = 16 + sizeof(struct mmo_charstatus); + WFIFOL(fd,4) = RFIFOL(fd,2); + WFIFOL(fd,8) = auth_fifo[i].login_id2; + WFIFOL(fd,12) = (unsigned long)auth_fifo[i].connect_until_time; + char_dat[auth_fifo[i].char_pos].sex = auth_fifo[i].sex; + memcpy(WFIFOP(fd,16), &char_dat[auth_fifo[i].char_pos], sizeof(struct mmo_charstatus)); + WFIFOSET(fd, WFIFOW(fd,2)); + //printf("auth_fifo search success (auth #%d, account %d, character: %d).\n", i, RFIFOL(fd,2), RFIFOL(fd,6)); + break; + } + } + if (i == AUTH_FIFO_SIZE) { + WFIFOW(fd,0) = 0x2afe; + WFIFOL(fd,2) = RFIFOL(fd,2); + WFIFOSET(fd,6); + printf("auth_fifo search error! account %d not authentified.\n", RFIFOL(fd,2)); + } + RFIFOSKIP(fd,22); + break; + + // MAPサーバー上のユーザー数受信 + case 0x2aff: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + server[id].users = RFIFOW(fd,4); + if(anti_freeze_enable) + server_freezeflag[id] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed + // remove all previously online players of the server + for(i = 0; i < char_num; i++) + if (online_chars[i] == id) + online_chars[i] = -1; + // add online players in the list by [Yor] + for(i = 0; i < server[id].users; i++) { + int char_id = RFIFOL(fd,6+i*4); + for(j = 0; j < char_num; j++) + if (char_dat[j].char_id == char_id) { + online_chars[j] = id; + //printf("%d\n", char_id); + break; + } + } + if (update_online < time(NULL)) { // Time is done + update_online = time(NULL) + 8; + create_online_files(); // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection. + // it set to 8 sec because is more than 5 (sec) and if we have more than 1 map-server, informations can be received in shifted. + } + RFIFOSKIP(fd,6+i*4); + break; + + // キャラデータ保存 + case 0x2b01: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + for(i = 0; i < char_num; i++) { + if (char_dat[i].account_id == RFIFOL(fd,4) && + char_dat[i].char_id == RFIFOL(fd,8)) + break; + } + if (i != char_num) + memcpy(&char_dat[i], RFIFOP(fd,12), sizeof(struct mmo_charstatus)); + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + // キャラセレ要求 + case 0x2b02: + if (RFIFOREST(fd) < 18) + return 0; + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); + auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd,2); + auth_fifo[auth_fifo_pos].char_id = 0; + auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd,6); + auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd,10); + auth_fifo[auth_fifo_pos].delflag = 2; + auth_fifo[auth_fifo_pos].char_pos = 0; + auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,14); + auth_fifo_pos++; + WFIFOW(fd,0) = 0x2b03; + WFIFOL(fd,2) = RFIFOL(fd,2); + WFIFOB(fd,6) = 0; + WFIFOSET(fd,7); + RFIFOSKIP(fd,18); + break; + + // マップサーバー間移動要求 + case 0x2b05: + if (RFIFOREST(fd) < 49) + return 0; + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + WFIFOW(fd,0) = 0x2b06; + memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 42); + //printf("auth_fifo set (auth#%d) - account: %d, secure: 0x%08x-0x%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); + auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd,2); + auth_fifo[auth_fifo_pos].char_id = RFIFOL(fd,14); + auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd,6); + auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd,10); + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].sex = RFIFOB(fd,44); + auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) + auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,45); + for(i = 0; i < char_num; i++) + if (char_dat[i].account_id == RFIFOL(fd,2) && + char_dat[i].char_id == RFIFOL(fd,14)) { + auth_fifo[auth_fifo_pos].char_pos = i; + auth_fifo_pos++; + WFIFOL(fd,6) = 0; + break; + } + if (i == char_num) + WFIFOW(fd,6) = 1; + WFIFOSET(fd,44); + RFIFOSKIP(fd,49); + break; + + // キャラ名検索 + case 0x2b08: + if (RFIFOREST(fd) < 6) + return 0; + for(i = 0; i < char_num; i++) { + if (char_dat[i].char_id == RFIFOL(fd,2)) + break; + } + WFIFOW(fd,0) = 0x2b09; + WFIFOL(fd,2) = RFIFOL(fd,2); + if (i != char_num) + memcpy(WFIFOP(fd,6), char_dat[i].name, 24); + else + memcpy(WFIFOP(fd,6), unknown_char_name, 24); + WFIFOSET(fd,30); + RFIFOSKIP(fd,6); + break; + + // it is a request to become GM + case 0x2b0a: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 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[24]; + int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody) + memcpy(character_name, RFIFOP(fd,6), 24); + character_name[sizeof(character_name) -1] = '\0'; + // prepare answer + WFIFOW(fd,0) = 0x2b0f; // answer + WFIFOL(fd,2) = acc; // who want do operation + WFIFOW(fd,30) = RFIFOW(fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex + // search character + i = search_character_index(character_name); + if (i >= 0) { + memcpy(WFIFOP(fd,6), search_character_name(i), 24); // put correct name if found + WFIFOW(fd,32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + switch(RFIFOW(fd, 30)) { + case 1: // block + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = char_dat[i].account_id; // account value + WFIFOL(login_fd,6) = 5; // status of the account + WFIFOSET(login_fd, 10); +// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 5); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 2: // ban + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd, 0) = 0x2725; + WFIFOL(login_fd, 2) = char_dat[i].account_id; // account value + WFIFOW(login_fd, 6) = RFIFOW(fd,32); // year + WFIFOW(login_fd, 8) = RFIFOW(fd,34); // month + WFIFOW(login_fd,10) = RFIFOW(fd,36); // day + WFIFOW(login_fd,12) = RFIFOW(fd,38); // hour + WFIFOW(login_fd,14) = RFIFOW(fd,40); // minute + WFIFOW(login_fd,16) = RFIFOW(fd,42); // second + WFIFOSET(login_fd,18); +// printf("char : status -> login: account %d, ban: %dy %dm %dd %dh %dmn %ds\n", +// char_dat[i].account_id, (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), (short)RFIFOW(fd,38), (short)RFIFOW(fd,40), (short)RFIFOW(fd,42)); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 3: // unblock + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = char_dat[i].account_id; // account value + WFIFOL(login_fd,6) = 0; // status of the account + WFIFOSET(login_fd, 10); +// printf("char : status -> login: account %d, status: %d \n", char_dat[i].account_id, 0); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 4: // unban + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd, 0) = 0x272a; + WFIFOL(login_fd, 2) = char_dat[i].account_id; // account value + WFIFOSET(login_fd, 6); +// printf("char : status -> login: account %d, unban request\n", char_dat[i].account_id); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 5: // changesex + if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd, 0) = 0x2727; + WFIFOL(login_fd, 2) = char_dat[i].account_id; // account value + WFIFOSET(login_fd, 6); +// printf("char : status -> login: account %d, change sex request\n", char_dat[i].account_id); + } else + WFIFOW(fd,32) = 3; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } else + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + } + } else { + // character name not found + memcpy(WFIFOP(fd,6), character_name, 24); + WFIFOW(fd,32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + // send answer if a player ask, not if the server ask + if (acc != -1) { + WFIFOSET(fd, 34); + } + RFIFOSKIP(fd, 44); + break; + } + +// case 0x2b0f: not more used (available for futur usage) + + // account_reg保存要求 + case 0x2b10: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + struct global_reg reg[ACCOUNT_REG2_NUM]; + int p, acc; + acc = RFIFOL(fd,4); + for(p = 8, j = 0; p < RFIFOW(fd,2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { + memcpy(reg[j].str, RFIFOP(fd,p), 32); + reg[j].value = RFIFOL(fd, p+32); + } + set_account_reg2(acc, j, reg); + // loginサーバーへ送る + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd, 0) = 0x2728; + memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WFIFOSET(login_fd, WFIFOW(login_fd,2)); + } + // ワールドへの同垢ログインがなければmapサーバーに送る必要はない + //memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); + //WBUFW(buf,0) = 0x2b11; + //mapif_sendall(buf, WBUFW(buf,2)); + RFIFOSKIP(fd, RFIFOW(fd,2)); +// printf("char: save_account_reg (from map)\n"); + break; + } + + default: + // inter server処理に渡す + { + int r = inter_parse_frommap(fd); + if (r == 1) // 処理できた + break; + if (r == 2) // パケット長が足りない + return 0; + } + // inter server処理でもない場合は切断 + printf("char: unknown packet 0x%04x (%d bytes to read in buffer)! (from map).\n", RFIFOW(fd,0), RFIFOREST(fd)); + session[fd]->eof = 1; + return 0; + } + } + return 0; +} + +int search_mapserver(char *map) { + int i, j; + char temp_map[16]; + int temp_map_len; + +// printf("Searching the map-server for map '%s'... ", map); + strncpy(temp_map, map, sizeof(temp_map)); + temp_map[sizeof(temp_map)-1] = '\0'; + if (strchr(temp_map, '.') != NULL) + temp_map[strchr(temp_map, '.') - temp_map + 1] = '\0'; // suppress the '.gat', but conserve the '.' to be sure of the name of the map + + temp_map_len = strlen(temp_map); + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + for (j = 0; server[i].map[j][0]; j++) + //printf("%s : %s = %d\n", server[i].map[j], map, strncmp(server[i].map[j], temp_map, temp_map_len)); + if (strncmp(server[i].map[j], temp_map, temp_map_len) == 0) { +// printf("found -> server #%d.\n", i); + return i; + } + +// printf("not found.\n"); + return -1; +} + +// char_mapifの初期化処理(現在はinter_mapif初期化のみ) +static int char_mapif_init(int fd) { + return inter_mapif_init(fd); +} + +//----------------------------------------------------- +// Test to know if an IP come from LAN or WAN. by [Yor] +//----------------------------------------------------- +int lan_ip_check(unsigned char *p){ + int i; + int lancheck = 1; + +// printf("lan_ip_check: to compare: %d.%d.%d.%d, network: %d.%d.%d.%d/%d.%d.%d.%d\n", +// p[0], p[1], p[2], p[3], +// subneti[0], subneti[1], subneti[2], subneti[3], +// subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); + for(i = 0; i < 4; i++) { + if ((subneti[i] & subnetmaski[i]) != (p[i] & subnetmaski[i])) { + lancheck = 0; + break; + } + } + printf("LAN test (result): %s source\033[0m.\n", (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); + return lancheck; +} + +int parse_char(int fd) { + int i, ch; + char email[40]; + struct char_session_data *sd; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + + if (login_fd < 0 || session[fd]->eof) { // disconnect any player (already connected to char-server or coming back from map-server) if login-server is diconnected. + if (fd == login_fd) + login_fd = -1; + close(fd); + delete_session(fd); + return 0; + } + + sd = session[fd]->session_data; + + while (RFIFOREST(fd) >= 2) { +// if (RFIFOW(fd,0) < 30000) +// printf("parse_char: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch(RFIFOW(fd,0)) { + case 0x20b: //20040622暗号化ragexe対応 + if (RFIFOREST(fd) < 19) + return 0; + RFIFOSKIP(fd,19); + break; + + case 0x65: // 接続要求 + if (RFIFOREST(fd) < 17) + return 0; + { + int GM_value; + if ((GM_value = isGM(RFIFOL(fd,2)))) + printf("Account Logged On; Account ID: %d (GM level %d).\n", RFIFOL(fd,2), GM_value); + else + printf("Account Logged On; Account ID: %d.\n", RFIFOL(fd,2)); + if (sd == NULL) { + sd = session[fd]->session_data = calloc(sizeof(struct char_session_data), 1); + memset(sd, 0, sizeof(struct char_session_data)); + memcpy(sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail + sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server) + } + sd->account_id = RFIFOL(fd,2); + sd->login_id1 = RFIFOL(fd,6); + sd->login_id2 = RFIFOL(fd,10); + sd->sex = RFIFOB(fd,16); + // send back account_id + WFIFOL(fd,0) = RFIFOL(fd,2); + WFIFOSET(fd,4); + // search authentification + for(i = 0; i < AUTH_FIFO_SIZE; i++) { + if (auth_fifo[i].account_id == sd->account_id && + auth_fifo[i].login_id1 == sd->login_id1 && +#if CMP_AUTHFIFO_LOGIN2 != 0 + auth_fifo[i].login_id2 == sd->login_id2 && // relate to the versions higher than 18 +#endif + (!check_ip_flag || auth_fifo[i].ip == session[fd]->client_addr.sin_addr.s_addr) && + auth_fifo[i].delflag == 2) { + auth_fifo[i].delflag = 1; + if (max_connect_user == 0 || count_users() < max_connect_user) { + if (login_fd > 0) { // don't send request if no login-server + // request to login-server to obtain e-mail/time limit + WFIFOW(login_fd,0) = 0x2716; + WFIFOL(login_fd,2) = sd->account_id; + WFIFOSET(login_fd,6); + } + // send characters to player + mmo_char_send006b(fd, sd); + } else { + // refuse connection (over populated) + WFIFOW(fd,0) = 0x6c; + WFIFOW(fd,2) = 0; + WFIFOSET(fd,3); + } + break; + } + } + // authentification not found + if (i == AUTH_FIFO_SIZE) { + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account + WFIFOL(login_fd,2) = sd->account_id; + WFIFOL(login_fd,6) = sd->login_id1; + WFIFOL(login_fd,10) = sd->login_id2; // relate to the versions higher than 18 + WFIFOB(login_fd,14) = sd->sex; + WFIFOL(login_fd,15) = session[fd]->client_addr.sin_addr.s_addr; + WFIFOSET(login_fd,19); + } else { // if no login-server, we must refuse connection + WFIFOW(fd,0) = 0x6c; + WFIFOW(fd,2) = 0; + WFIFOSET(fd,3); + } + } + } + RFIFOSKIP(fd,17); + break; + + case 0x66: // キャラ選択 + if (RFIFOREST(fd) < 3) + return 0; + + // if we activated email creation and email is default email + if (email_creation != 0 && strcmp(sd->email, "a@a.com") == 0 && login_fd > 0) { // to modify an e-mail, login-server must be online + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd, 3); + + // otherwise, load the character + } else { + for (ch = 0; ch < 9; ch++) + if (sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].char_num == RFIFOB(fd,2)) + break; + if (ch != 9) { + char_log("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s." RETCODE, + sd->account_id, RFIFOB(fd,2), char_dat[sd->found_char[ch]].name); + // searching map server + i = search_mapserver(char_dat[sd->found_char[ch]].last_point.map); + // if map is not found, we check major cities + if (i < 0) { + if ((i = search_mapserver("prontera.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[sd->found_char[ch]].last_point.map, "prontera.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 273; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = 354; + } else if ((i = search_mapserver("geffen.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[sd->found_char[ch]].last_point.map, "geffen.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 120; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = 100; + } else if ((i = search_mapserver("morocc.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[sd->found_char[ch]].last_point.map, "morocc.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 160; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = 94; + } else if ((i = search_mapserver("alberta.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[sd->found_char[ch]].last_point.map, "alberta.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 116; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = 57; + } else if ((i = search_mapserver("payon.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[sd->found_char[ch]].last_point.map, "payon.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 87; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = 117; + } else if ((i = search_mapserver("izlude.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[sd->found_char[ch]].last_point.map, "izlude.gat", 16); + char_dat[sd->found_char[ch]].last_point.x = 94; // savepoint coordonates + char_dat[sd->found_char[ch]].last_point.y = 103; + } else { + int j; + // get first online server (with a map) + i = 0; + for(j = 0; j < MAX_MAP_SERVERS; j++) + if (server_fd[j] >= 0 && server[j].map[0][0]) { // change save point to one of map found on the server (the first) + i = j; + memcpy(char_dat[sd->found_char[ch]].last_point.map, server[j].map[0], 16); + printf("Map-server #%d found with a map: '%s'.\n", j, server[j].map[0]); + // coordonates are unknown + break; + } + // if no map-server is connected, we send: server closed + if (j == MAX_MAP_SERVERS) { + WFIFOW(fd,0) = 0x81; + WFIFOL(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + RFIFOSKIP(fd,3); + break; + } + } + } + WFIFOW(fd,0) = 0x71; + WFIFOL(fd,2) = char_dat[sd->found_char[ch]].char_id; + memcpy(WFIFOP(fd,6), char_dat[sd->found_char[ch]].last_point.map, 16); + printf("Character selection '%s' (account: %d, slot: %d).\n", char_dat[sd->found_char[ch]].name, sd->account_id, ch); + printf("--Send IP of map-server. "); + if (lan_ip_check(p)) + WFIFOL(fd, 22) = inet_addr(lan_map_ip); + else + WFIFOL(fd, 22) = server[i].ip; + WFIFOW(fd,26) = server[i].port; + WFIFOSET(fd,28); + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + //printf("auth_fifo set #%d - account %d, char: %d, secure: %08x-%08x\n", auth_fifo_pos, sd->account_id, char_dat[sd->found_char[ch]].char_id, sd->login_id1, sd->login_id2); + auth_fifo[auth_fifo_pos].account_id = sd->account_id; + auth_fifo[auth_fifo_pos].char_id = char_dat[sd->found_char[ch]].char_id; + auth_fifo[auth_fifo_pos].login_id1 = sd->login_id1; + auth_fifo[auth_fifo_pos].login_id2 = sd->login_id2; + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].char_pos = sd->found_char[ch]; + auth_fifo[auth_fifo_pos].sex = sd->sex; + auth_fifo[auth_fifo_pos].connect_until_time = sd->connect_until_time; + auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; + auth_fifo_pos++; + } + } + RFIFOSKIP(fd,3); + break; + + case 0x67: // 作成 + if (RFIFOREST(fd) < 37) + return 0; + i = make_new_char(fd, RFIFOP(fd,2)); + if (i < 0) { + WFIFOW(fd,0) = 0x6e; + WFIFOB(fd,2) = 0x00; + WFIFOSET(fd,3); + RFIFOSKIP(fd,37); + break; + } + + WFIFOW(fd,0) = 0x6d; + memset(WFIFOP(fd,2), 0, 106); + + WFIFOL(fd,2) = char_dat[i].char_id; + WFIFOL(fd,2+4) = char_dat[i].base_exp; + WFIFOL(fd,2+8) = char_dat[i].zeny; + WFIFOL(fd,2+12) = char_dat[i].job_exp; + WFIFOL(fd,2+16) = char_dat[i].job_level; + + WFIFOL(fd,2+28) = char_dat[i].karma; + WFIFOL(fd,2+32) = char_dat[i].manner; + + WFIFOW(fd,2+40) = 0x30; + WFIFOW(fd,2+42) = (char_dat[i].hp > 0x7fff) ? 0x7fff : char_dat[i].hp; + WFIFOW(fd,2+44) = (char_dat[i].max_hp > 0x7fff) ? 0x7fff : char_dat[i].max_hp; + WFIFOW(fd,2+46) = (char_dat[i].sp > 0x7fff) ? 0x7fff : char_dat[i].sp; + WFIFOW(fd,2+48) = (char_dat[i].max_sp > 0x7fff) ? 0x7fff : char_dat[i].max_sp; + WFIFOW(fd,2+50) = DEFAULT_WALK_SPEED; // char_dat[i].speed; + WFIFOW(fd,2+52) = char_dat[i].class; + WFIFOW(fd,2+54) = char_dat[i].hair; + + WFIFOW(fd,2+58) = char_dat[i].base_level; + WFIFOW(fd,2+60) = char_dat[i].skill_point; + + WFIFOW(fd,2+64) = char_dat[i].shield; + WFIFOW(fd,2+66) = char_dat[i].head_top; + WFIFOW(fd,2+68) = char_dat[i].head_mid; + WFIFOW(fd,2+70) = char_dat[i].hair_color; + + memcpy(WFIFOP(fd,2+74), char_dat[i].name, 24); + + WFIFOB(fd,2+98) = (char_dat[i].str > 255) ? 255 : char_dat[i].str; + WFIFOB(fd,2+99) = (char_dat[i].agi > 255) ? 255 : char_dat[i].agi; + WFIFOB(fd,2+100) = (char_dat[i].vit > 255) ? 255 : char_dat[i].vit; + WFIFOB(fd,2+101) = (char_dat[i].int_ > 255) ? 255 : char_dat[i].int_; + WFIFOB(fd,2+102) = (char_dat[i].dex > 255) ? 255 : char_dat[i].dex; + WFIFOB(fd,2+103) = (char_dat[i].luk > 255) ? 255 : char_dat[i].luk; + WFIFOB(fd,2+104) = char_dat[i].char_num; + + WFIFOSET(fd,108); + RFIFOSKIP(fd,37); + for(ch = 0; ch < 9; ch++) { + if (sd->found_char[ch] == -1) { + sd->found_char[ch] = i; + break; + } + } + + case 0x68: // delete char //Yor's Fix + if (RFIFOREST(fd) < 46) + return 0; + memcpy(email, RFIFOP(fd,6), 40); + if (e_mail_check(email) == 0) + strncpy(email, "a@a.com", 40); // default e-mail + + // if we activated email creation and email is default email + if (email_creation != 0 && strcmp(sd->email, "a@a.com") == 0 && login_fd > 0) { // to modify an e-mail, login-server must be online + // if sended email is incorrect e-mail + if (strcmp(email, "a@a.com") == 0) { + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd, 3); + RFIFOSKIP(fd,46); + // we act like we have selected a character + } else { + // we change the packet to set it like selection. + for (i = 0; i < 9; i++) + if (char_dat[sd->found_char[i]].char_id == RFIFOL(fd,2)) { + // we save new e-mail + memcpy(sd->email, email, 40); + // we send new e-mail to login-server ('online' login-server is checked before) + WFIFOW(login_fd,0) = 0x2715; + WFIFOL(login_fd,2) = sd->account_id; + memcpy(WFIFOP(login_fd, 6), email, 40); + WFIFOSET(login_fd,46); + // skip part of the packet! (46, but leave the size of select packet: 3) + RFIFOSKIP(fd,43); + // change value to put new packet (char selection) + RFIFOW(fd, 0) = 0x66; + RFIFOB(fd, 2) = char_dat[sd->found_char[i]].char_num; + // not send packet, it's modify of actual packet + break; + } + if (i == 9) { + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; // 00 = Incorrect Email address + WFIFOSET(fd, 3); + RFIFOSKIP(fd,46); + } + } + + // otherwise, we delete the character + } else { + if (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]])->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 = session[j]->session_data) && + sd2->account_id == char_dat[char_num-1].account_id) { + for (k = 0; k < 9; k++) { + if (sd2->found_char[k] == char_num-1) { + sd2->found_char[k] = sd->found_char[i]; + break; + } + } + break; + } + } + } + } + + char_num--; + for(ch = i; ch < 9-1; ch++) + sd->found_char[ch] = sd->found_char[ch+1]; + sd->found_char[8] = -1; + WFIFOW(fd,0) = 0x6f; + WFIFOSET(fd,2); + break; + } + } + + if (i == 9) { + WFIFOW(fd,0) = 0x70; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + } + } + RFIFOSKIP(fd,46); + } + break; + + case 0x2af8: // マップサーバーログイン + if (RFIFOREST(fd) < 60) + return 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(RFIFOP(fd,2), userid) || strcmp(RFIFOP(fd,26), passwd)){ + WFIFOB(fd,2) = 3; + WFIFOSET(fd,3); + RFIFOSKIP(fd,60); + } else { + int len; + WFIFOB(fd,2) = 0; + session[fd]->func_parse = parse_frommap; + server_fd[i] = fd; + if(anti_freeze_enable) + server_freezeflag[i] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed + server[i].ip = RFIFOL(fd,54); + server[i].port = RFIFOW(fd,58); + server[i].users = 0; + memset(server[i].map, 0, sizeof(server[i].map)); + WFIFOSET(fd,3); + RFIFOSKIP(fd,60); + realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + char_mapif_init(fd); + // send gm acccounts level to map-servers + len = 4; + WFIFOW(fd,0) = 0x2b15; + for(i = 0; i < GM_num; i++) { + WFIFOL(fd,len) = gm_account[i].account_id; + WFIFOB(fd,len+4) = (unsigned char)gm_account[i].level; + len += 5; + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + return 0; + } + break; + + case 0x187: // Alive信号? + 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: // 接続の切断(defaultと処理は一緒だが明示的にするため) + session[fd]->eof = 1; + return 0; + + default: + session[fd]->eof = 1; + return 0; + } + } + RFIFOFLUSH(fd); + return 0; +} + +// 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) +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) { + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + c++; + } + } + return c; +} + +// 自分以外の全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) +int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { + int i, c; + + c = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + int fd; + if ((fd = server_fd[i]) >= 0 && fd != sfd) { + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd, len); + c++; + } + } + return c; +} +// MAPサーバーにデータ送信(map鯖生存確認有り) +int mapif_send(int fd, unsigned char *buf, unsigned int len) { + int i; + + if (fd >= 0) { + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (fd == server_fd[i]) { + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + return 1; + } + } + } + return 0; +} + +int send_users_tologin(int tid, unsigned int tick, int id, int data) { + int users = count_users(); + char buf[16]; + + if (login_fd > 0 && session[login_fd]) { + // send number of user to login server + WFIFOW(login_fd,0) = 0x2714; + WFIFOL(login_fd,2) = users; + WFIFOSET(login_fd,6); + } + // send number of players to all map-servers + WBUFW(buf,0) = 0x2b00; + WBUFL(buf,2) = users; + mapif_sendall(buf, 6); + + return 0; +} + +int check_connect_login_server(int tid, unsigned int tick, int id, int data) { + if (login_fd <= 0 || session[login_fd] == NULL) { + printf("Attempt to connect to login-server...\n"); + login_fd = make_connection(login_ip, login_port); + session[login_fd]->func_parse = parse_tologin; + realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + WFIFOW(login_fd,0) = 0x2710; + memset(WFIFOP(login_fd,2), 0, 24); + memcpy(WFIFOP(login_fd,2), userid, strlen(userid) < 24 ? strlen(userid) : 24); + memset(WFIFOP(login_fd,26), 0, 24); + memcpy(WFIFOP(login_fd,26), passwd, strlen(passwd) < 24 ? strlen(passwd) : 24); + WFIFOL(login_fd,50) = 0; + WFIFOL(login_fd,54) = char_ip; + WFIFOL(login_fd,58) = char_port; + memset(WFIFOP(login_fd,60), 0, 20); + memcpy(WFIFOP(login_fd,60), server_name, strlen(server_name) < 20 ? strlen(server_name) : 20); + WFIFOW(login_fd,80) = 0; + WFIFOW(login_fd,82) = char_maintenance; + WFIFOW(login_fd,84) = char_new; + WFIFOSET(login_fd,86); + } + return 0; +} + +//---------------------------------------------------------- +// Return numerical value of a switch configuration by [Yor] +// on/off, english, fran軋is, deutsch, espaol +//---------------------------------------------------------- +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) { + printf("LAN support configuration file not found: %s\n", lancfgName); + return 1; + } + + printf ("---start reading of Lan Support configuration...\n"); + + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars(w1); + remove_control_chars(w2); + if (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; + } + printf("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]); + } + printf("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]); + } + printf("Sub-network mask of the map-server: %d.%d.%d.%d.\n", subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); + } + } + fclose(fp); + + // sub-network check of the map-server + { + unsigned int a0, a1, a2, a3; + unsigned char p[4]; + sscanf(lan_map_ip, "%d.%d.%d.%d", &a0, &a1, &a2, &a3); + p[0] = a0; p[1] = a1; p[2] = a2; p[3] = a3; + printf("LAN test of LAN IP of the map-server: "); + if (lan_ip_check(p) == 0) { + printf("\033[1;31m***ERROR: LAN IP of the map-server doesn't belong to the specified Sub-network.\033[0m\n"); + } + } + + printf("---End reading of Lan Support configuration...\n"); + + return 0; +} + +int char_config_read(const char *cfgName) { + struct hostent *h = NULL; + char line[1024], w1[1024], w2[1024]; + FILE *fp = fopen(cfgName, "r"); + + if (fp == NULL) { + printf("Configuration file not found: %s.\n", cfgName); + exit(1); + } + + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars(w1); + remove_control_chars(w2); + if (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'; + printf("%s server has been intialized\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) { + h = gethostbyname(w2); + if (h != NULL) { + printf("Login server IP address : %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + sprintf(login_ip_str, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } else + memcpy(login_ip_str, w2, 16); + } else if (strcmpi(w1, "login_port") == 0) { + login_port = atoi(w2); + } else if (strcmpi(w1, "char_ip") == 0) { + h = gethostbyname(w2); + if (h != NULL) { + printf("Character server IP address : %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + sprintf(char_ip_str, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } else + memcpy(char_ip_str, w2, 16); + } else if (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, "email_creation") == 0) { + email_creation = config_switch(w2); + } else if (strcmpi(w1, "char_txt") == 0) { + strcpy(char_txt, w2); + } else if (strcmpi(w1, "backup_txt") == 0) { //By zanetheinsane + strcpy(backup_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, "check_ip_flag") == 0) { + check_ip_flag = 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, "start_point") == 0) { + char map[32]; + int x, y; + if (sscanf(w2, "%[^,],%d,%d", map, &x, &y) < 3) + continue; + if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name + memcpy(start_point.map, map, 16); + start_point.x = x; + start_point.y = y; + } + } else if (strcmpi(w1, "start_zeny") == 0) { + start_zeny = atoi(w2); + if (start_zeny < 0) + start_zeny = 0; + } else if (strcmpi(w1, "start_weapon") == 0) { + start_zeny = atoi(w2); + if (start_weapon < 0) + start_weapon = 0; + } else if (strcmpi(w1, "start_armor") == 0) { + start_zeny = 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[24] = 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,"anti_freeze_enable")==0){ + anti_freeze_enable = config_switch(w2); + } else if (strcmpi(w1, "anti_freeze_interval") == 0) { + ANTI_FREEZE_INTERVAL = atoi(w2); + if (ANTI_FREEZE_INTERVAL < 5) + ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds + } else if (strcmpi(w1, "import") == 0) { + char_config_read(w2); + } + } + fclose(fp); + + return 0; +} + +void do_final(void) { + int i; + + // write online players files with no player + for(i = 0; i < char_num; i++) + online_chars[i] = -1; + create_online_files(); + free(online_chars); + + mmo_char_sync(); + inter_save(); + + if (gm_account != NULL) + free(gm_account); + + free(char_dat); + delete_session(login_fd); + delete_session(char_fd); + + char_log("----End of char-server (normal end with closing of all files)." RETCODE); +} + +int do_init(int argc, char **argv) { + int i; + + // a newline in the log... + char_log(""); + char_log("The char-server starting..." RETCODE); + + char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); + lan_config_read((argc > 1) ? argv[1] : LOGIN_LAN_CONF_NAME); + + login_ip = inet_addr(login_ip_str); + char_ip = inet_addr(char_ip_str); + + for(i = 0; i < MAX_MAP_SERVERS; i++) { + memset(&server[i], 0, sizeof(struct mmo_map_server)); + server_fd[i] = -1; + } + + mmo_char_init(); + + update_online = time(NULL); + create_online_files(); // update online players files at start of the server + + inter_init((argc > 2) ? argv[2] : inter_cfgName); // inter server 初期化 + + set_termfunc(do_final); + set_defaultparse(parse_char); + + char_fd = make_listen_port(char_port); + + add_timer_func_list(check_connect_login_server, "check_connect_login_server"); + add_timer_func_list(send_users_tologin, "send_users_tologin"); + add_timer_func_list(mmo_char_sync_timer, "mmo_char_sync_timer"); + + i = add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000); + i = add_timer_interval(gettick() + 1000, send_users_tologin, 0, 0, 5 * 1000); + i = add_timer_interval(gettick() + autosave_interval, mmo_char_sync_timer, 0, 0, autosave_interval); + + if(anti_freeze_enable > 0) { + add_timer_func_list(map_anti_freeze_system, "map_anti_freeze_system"); + i = add_timer_interval(gettick() + 1000, map_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); // checks every X seconds user specifies + } + + char_log("The char-server is ready (Server is listening on the port %d)." RETCODE, char_port); + + printf("The char-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", char_port); + + return 0; +} diff --git a/src/char/char.h b/src/char/char.h new file mode 100644 index 0000000..989ca2f --- /dev/null +++ b/src/char/char.h @@ -0,0 +1,31 @@ +// $Id: char.h,v 1.1.1.1 2004/09/10 17:26:50 MagicalTux Exp $ +#ifndef _CHAR_H_ +#define _CHAR_H_ + +#define MAX_MAP_SERVERS 30 + +#define CHAR_CONF_NAME "conf/char_athena.conf" + +#define LOGIN_LAN_CONF_NAME "conf/lan_support.conf" + +#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 + +struct mmo_map_server{ + long ip; + short port; + int users; + char map[MAX_MAP_PER_SERVER][16]; +}; + +int search_character_index(char* character_name); +char * search_character_name(int index); + +int mapif_sendall(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_log(char *fmt, ...); + +extern int autosave_interval; + +#endif diff --git a/src/char/diff.diff b/src/char/diff.diff new file mode 100644 index 0000000..61e91c7 --- /dev/null +++ b/src/char/diff.diff @@ -0,0 +1,4242 @@ +--- char.c 2006-02-17 04:15:48.000000000 +0100 ++++ newchar/char.c 2006-03-15 19:55:35.000000000 +0100 +@@ -1,73 +1,100 @@ +-// $Id: char.c,v 1.3 2004/09/13 16:52:16 Yor Exp $ +-// original : char2.c 2003/03/14 11:58:35 Rev.1.5 ++// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
++// For more information, see LICENCE in the main folder
+ + #include <sys/types.h> +-#include <sys/socket.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 <sys/time.h> +-#include <time.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 <arpa/inet.h> +-#include <netdb.h> + #include <stdarg.h> ++#include <limits.h>
+ +-#include "core.h" +-#include "socket.h" +-#include "timer.h" +-#include "mmo.h" +-#include "version.h" +-#include "lock.h" +-#include "char.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 MEMWATCH +-#include "memwatch.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 server_freezeflag[MAX_MAP_SERVERS]; // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed +-int anti_freeze_enable = 0; +-int ANTI_FREEZE_INTERVAL = 6; + + int login_fd, char_fd; + char userid[24]; + char passwd[24]; + char server_name[20]; +-char wisp_server_name[24] = "Server"; ++char wisp_server_name[NAME_LENGTH] = "Server";
++int login_ip_set_ = 0;
+ char login_ip_str[16]; +-int login_ip; ++in_addr_t login_ip;
+ int login_port = 6900; ++int char_ip_set_ = 0;
+ char char_ip_str[16]; +-int char_ip; ++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]; +-char backup_txt[1024]; //By zanetheinsane ++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"; +-//Added for lan support +-char lan_map_ip[128]; +-int subneti[4]; +-int subnetmaski[4]; ++char db_path[1024]="db";
++
++// Advanced subnet check [LuzZza]
++struct _subnet {
++ long subnet;
++ long mask;
++ long char_ip;
++ long map_ip;
++} subnet[16];
++
++int subnet_count = 0;
++
+ 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]; +@@ -83,18 +110,31 @@ + 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_id_count = 150000; +-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 = 1202; ++int start_armor = 2301;
++
++//Custom limits for the fame lists. [Skotlex]
++int fame_list_size_chemist = MAX_FAME_LIST;
++int fame_list_size_smith = MAX_FAME_LIST;
++int fame_list_size_taekwon = MAX_FAME_LIST;
+ + // Initial position (it's possible to set it in conf file) +-struct point start_point = {"new_1-1.gat", 53, 111}; ++struct point start_point = { 0, 53, 111};
+ + struct gm_account *gm_account = NULL; + int GM_num = 0; +@@ -107,16 +147,31 @@ + int online_refresh_html = 20; // refresh time (in sec) of the html file in the explorer + int online_gm_display_min_level = 20; // minimum GM level to display 'GM' when we want to display it + +-int *online_chars; // same size of char_dat, and id value of current server (or -1) ++//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; +- struct timeval tv; ++ time_t raw_time;
+ char tmpstr[2048]; + + va_start(ap, fmt); +@@ -126,33 +181,16 @@ + if (fmt[0] == '\0') // jump a line if no message + fprintf(logfp, RETCODE); + else { +- gettimeofday(&tv, NULL); +- strftime(tmpstr, 24, "%d-%m-%Y %H:%M:%S", localtime(&(tv.tv_sec))); +- sprintf(tmpstr + 19, ".%03d: %s", (int)tv.tv_usec / 1000, fmt); ++ 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; +-} +- +-//----------------------------------------------------- +-// 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; ++ return 0;
+ } + + //---------------------------------------------------------------------- +@@ -183,9 +221,9 @@ + 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].name, character_name) == 0) { ++ 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].name, character_name) == 0) ++ if (strcmp(char_dat[i].status.name, character_name) == 0)
+ return i; + quantity++; + index = i; +@@ -206,30 +244,140 @@ + char * search_character_name(int index) { + + if (index >= 0 && index < char_num) +- return char_dat[index].name; ++ 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);
++
+ //------------------------------------------------- +-// Function to create the character line (for save) ++// Set Character online/offline [Wizputer]
+ //------------------------------------------------- +-int mmo_char_tostr(char *str, struct mmo_charstatus *p) { ++
++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;
+ ++ /* We shouldn't need this anymore... [Skotlex]
+ // on multi-map server, sometimes it's posssible that last_point become void. (reason???) We check that to not lost character at restart. +- if (p->last_point.map[0] == '\0') { +- memcpy(p->last_point.map, "prontera.gat", 16); ++ 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" ++ */
++ str_p += sprintf(str_p, "%d\t%d,%d\t%s\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d"
+ "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" +- "\t%s,%d,%d\t%s,%d,%d,%d\t", ++ "\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->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, +@@ -238,32 +386,34 @@ + 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, +- p->partner_id); ++ 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[0]) { +- str_p += sprintf(str_p, "%s,%d,%d", p->memo_point[i].map, p->memo_point[i].x, p->memo_point[i].y); ++ 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,%d,%d,%d,%d,%d ", ++ 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, +- p->inventory[i].card[0], p->inventory[i].card[1], p->inventory[i].card[2], p->inventory[i].card[3], +- p->inventory[i].broken); ++ 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,%d,%d,%d,%d,%d ", ++ 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, +- p->cart[i].card[0], p->cart[i].card[1], p->cart[i].card[2], p->cart[i].card[3], +- p->cart[i].broken); ++ 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'; + +@@ -273,9 +423,9 @@ + } + *(str_p++) = '\t'; + +- for(i = 0; i < p->global_reg_num; i++) +- if (p->global_reg[i].str[0]) +- str_p += sprintf(str_p, "%s,%d ", p->global_reg[i].str, p->global_reg[i].value); ++ 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'; +@@ -285,20 +435,62 @@ + //------------------------------------------------------------------------- + // Function to set the character from the line (at read of characters file) + //------------------------------------------------------------------------- +-int mmo_char_fromstr(char *str, struct mmo_charstatus *p) { ++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; ++ unsigned int tmp_uint[2]; //To read exp....
++ int set, next, len, i, j;
+ + // initilialise character + memset(p, '\0', sizeof(struct mmo_charstatus)); + +- // If it's not char structure of version 1008 and after +- if ((set = sscanf(str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" ++ // 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%u,%u,%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_uint[0], &tmp_uint[1], &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%u,%u,%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_uint[0], &tmp_uint[1], &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%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d"
+ "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" +- "\t%[^,],%d,%d\t%[^,],%d,%d,%d%n", +- &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // ++ "\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_uint[0], &tmp_uint[1], &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], +@@ -306,16 +498,17 @@ + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], +- p->last_point.map, &tmp_int[35], &tmp_int[36], // +- p->save_point.map, &tmp_int[37], &tmp_int[38], &tmp_int[39], &next)) != 43) { ++ tmp_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%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" ++ if ((set = sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d"
+ "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" +- "\t%[^,],%d,%d\t%[^,],%d,%d%n", +- &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // ++ "\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_uint[0], &tmp_uint[1], &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], +@@ -323,16 +516,17 @@ + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], +- p->last_point.map, &tmp_int[35], &tmp_int[36], // +- p->save_point.map, &tmp_int[37], &tmp_int[38], &next)) != 42) { ++ 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%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" ++ set = sscanf(str, "%d\t%d,%d\t%127[^\t]\t%d,%d,%d\t%u,%u,%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, // ++ "\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_uint[0], &tmp_uint[1], &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], +@@ -340,8 +534,8 @@ + &tmp_int[24], &tmp_int[25], // + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], +- p->last_point.map, &tmp_int[35], &tmp_int[36], // +- p->save_point.map, &tmp_int[37], &tmp_int[38], &next); ++ 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 +@@ -351,19 +545,30 @@ + } + // Char structure of version 1008+ + } else { ++ set += 3;
+ //printf("char: new char data ver.3\n"); + } +- if (set != 43) ++ // 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->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->base_exp = tmp_uint[0];
++ p->job_exp = tmp_uint[1];
+ p->zeny = tmp_int[8]; + p->hp = tmp_int[9]; + p->max_hp = tmp_int[10]; +@@ -391,31 +596,37 @@ + 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].char_id == p->char_id) { +- printf("\033[1;31mmmo_auth_init: ******Error: a character has an identical id to another.\n"); +- printf(" character id #%d -> new character not readed.\n", p->char_id); +- printf(" Character saved in log file.\033[0m\n"); ++ 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].name, p->name) == 0) { +- printf("\033[1;31mmmo_auth_init: ******Error: character name already exists.\n"); +- printf(" character name '%s' -> new character not readed.\n", p->name); +- printf(" Character saved in log file.\033[0m\n"); ++ } 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) { +- printf("mmo_auth_init: ******WARNING: character name has wisp server name.\n"); +- printf(" Character name '%s' = wisp server name '%s'.\n", p->name, wisp_server_name); +- printf(" Character readed. Suggestion: change the wisp server name.\n"); ++ 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); + } +@@ -426,8 +637,9 @@ + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { +- if (sscanf(str+next, "%[^,],%d,%d%n", p->memo_point[i].map, &tmp_int[0], &tmp_int[1], &len) != 3) ++ 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; +@@ -438,18 +650,10 @@ + 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", ++ 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_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { +- // do nothing, it's ok +- } else if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", +- &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], +- &tmp_int[4], &tmp_int[5], &tmp_int[6], +- &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) { +- tmp_int[11] = 0; // broken doesn't exist in this version -> 0 +- } else // invalid structure +- return -4; ++ &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]; +@@ -457,31 +661,23 @@ + p->inventory[i].identify = tmp_int[4]; + p->inventory[i].refine = tmp_int[5]; + p->inventory[i].attribute = tmp_int[6]; +- p->inventory[i].card[0] = tmp_int[7]; +- p->inventory[i].card[1] = tmp_int[8]; +- p->inventory[i].card[2] = tmp_int[9]; +- p->inventory[i].card[3] = tmp_int[10]; +- p->inventory[i].broken = tmp_int[11]; ++
++ 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,%d,%d,%d,%d,%d%n", +- &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], +- &tmp_int[4], &tmp_int[5], &tmp_int[6], +- &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { +- // do nothing, it's ok +- } else if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", ++ 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_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) { +- tmp_int[11] = 0; // broken doesn't exist in this version -> 0 +- } else // invalid structure +- return -5; ++ &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]; +@@ -489,14 +685,15 @@ + p->cart[i].identify = tmp_int[4]; + p->cart[i].refine = tmp_int[5]; + p->cart[i].attribute = tmp_int[6]; +- p->cart[i].card[0] = tmp_int[7]; +- p->cart[i].card[1] = tmp_int[8]; +- p->cart[i].card[2] = tmp_int[9]; +- p->cart[i].card[3] = tmp_int[10]; +- p->cart[i].broken = tmp_int[11]; ++
++ 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++; +@@ -514,11 +711,11 @@ + next++; + + for(i = 0; str[next] && str[next] != '\t' && str[next] != '\n' && str[next] != '\r'; i++) { // global_reg実装以前のathena.txt互換のため一応'\n'チェック +- if (sscanf(str + next, "%[^,],%d%n", p->global_reg[i].str, &p->global_reg[i].value, &len) != 2) { ++ 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, ",%d%n", &p->global_reg[i].value, &len) == 1) ++ if (str[next] == ',' && sscanf(str + next, ",%[^ ] %n", reg[i].value, &len) == 1)
+ i--; + else + return -7; +@@ -527,39 +724,127 @@ + if (str[next] == ' ') + next++; + } +- p->global_reg_num = i; ++ *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 i; + int ret, line_count; + FILE *fp; + + char_max = 256; +- char_dat = calloc(sizeof(struct mmo_charstatus) * 256, 1); ++ char_dat = (struct character_data*)aCalloc(sizeof(struct character_data) * 256, 1);
+ if (!char_dat) { +- printf("out of memory: mmo_char_init (calloc of char_dat).\n"); +- exit(1); +- } +- online_chars = calloc(sizeof(int) * 256, 1); +- if (!online_chars) { +- printf("out of memory: mmo_char_init (calloc of online_chars).\n"); ++ ShowFatalError("out of memory: mmo_char_init (calloc of char_dat).\n");
+ exit(1); + } +- for(i = 0; i < char_max; i++) +- online_chars[i] = -1; +- + char_num = 0; + + fp = fopen(char_txt, "r"); ++
+ if (fp == NULL) { +- printf("Characters file not found: %s.\n", char_txt); ++ 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; +@@ -583,30 +868,26 @@ + + if (char_num >= char_max) { + char_max += 256; +- char_dat = realloc(char_dat, sizeof(struct mmo_charstatus) * char_max); ++ char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max);
+ if (!char_dat) { +- printf("Out of memory: mmo_char_init (realloc of char_dat).\n"); ++ 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); + } +- online_chars = realloc(online_chars, sizeof(int) * char_max); +- if (!online_chars) { +- printf("Out of memory: mmo_char_init (realloc of online_chars).\n"); +- char_log("Out of memory: mmo_char_init (realloc of online_chars)." RETCODE); +- exit(1); +- } +- for(i = char_max - 256; i < char_max; i++) +- online_chars[i] = -1; + } + +- ret = mmo_char_fromstr(line, &char_dat[char_num]); ++ 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].char_id >= char_id_count) +- char_id_count = char_dat[char_num].char_id + 1; ++ 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 { +- printf("mmo_char_init: in characters file, unable to read the line #%d.\n", line_count); +- printf(" -> Character saved in log file.\n"); ++ 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); +@@ -639,13 +920,13 @@ + fclose(fp); + + if (char_num == 0) { +- printf("mmo_char_init: No character found in %s.\n", char_txt); ++ 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) { +- printf("mmo_char_init: 1 character read in %s.\n", char_txt); ++ 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 { +- printf("mmo_char_init: %d characters read in %s.\n", char_num, char_txt); ++ 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); + } + +@@ -658,20 +939,21 @@ + // Function to save characters in files (speed up by [Yor]) + //--------------------------------------------------------- + void mmo_char_sync(void) { +- char line[65536]; ++ char line[65536],f_line[1024];
+ int i, j, k; + int lock; +- FILE *fp; +- int id[char_num]; ++ 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].account_id < char_dat[id[j]].account_id) || ++ 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].account_id == char_dat[id[j]].account_id && +- char_dat[i].char_num < char_dat[id[j]].char_num)) { ++ (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] +@@ -683,12 +965,12 @@ + // Data save + fp = lock_fopen(char_txt, &lock); + if (fp == NULL) { +- printf("WARNING: Server can't not save characters.\n"); ++ 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]]); // use of sorted index ++ 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); +@@ -699,19 +981,33 @@ + if (backup_txt_flag) { // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor] + fp = lock_fopen(backup_txt, &lock); + if (fp == NULL) { +- printf("WARNING: Server can't not create backup of characters file.\n"); ++ 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]]); // use of sorted index ++ 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; + } + +@@ -719,6 +1015,8 @@ + // 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; +@@ -728,21 +1026,33 @@ + // Function to create a new character + //----------------------------------- + int make_new_char(int fd, unsigned char *dat) { +- int i, j; ++ int i;
+ struct char_session_data *sd; ++ char name[NAME_LENGTH];
+ +- sd = session[fd]->session_data; ++ sd = (struct char_session_data*)session[fd]->session_data;
+ + // remove control characters from the name +- dat[23] = '\0'; +- if (remove_control_chars(dat)) { ++ 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(dat) < 4) { ++ 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; +@@ -750,15 +1060,15 @@ + + // Check Authorised letters/symbols in the name of the character + if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised +- for (i = 0; dat[i]; i++) +- if (strchr(char_name_letters, dat[i]) == NULL) { ++ 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]); ++ 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; dat[i]; i++) +- if (strchr(char_name_letters, dat[i]) != NULL) { ++ 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; +@@ -767,8 +1077,8 @@ + + if (dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29] != 5*6 || // stats + dat[30] >= 9 || // slots (dat[30] can not be negativ) +- dat[33] <= 0 || dat[33] >= 20 || // hair style +- dat[31] >= 12) { // hair color (dat[31] can not be negativ) ++ 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; +@@ -781,98 +1091,96 @@ + 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 && strcmp(char_dat[i].name, dat) == 0) || +- (name_ignoring_case == 0 && strcmpi(char_dat[i].name, dat) == 0)) { ++ 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].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]); ++ 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].account_id == sd->account_id && char_dat[i].char_num == dat[30]) { ++ 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].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]); ++ 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, dat) == 0) { ++ 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], dat, char_dat[i].name, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]); ++ 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 = realloc(char_dat, sizeof(struct mmo_charstatus) * char_max); ++ char_dat = (struct character_data*)aRealloc(char_dat, sizeof(struct character_data) * char_max);
+ if (!char_dat) { +- printf("Out of memory: make_new_char (realloc of char_dat).\n"); ++ 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); + } +- online_chars = realloc(online_chars, sizeof(int) * char_max); +- if (!online_chars) { +- printf("Out of memory: make_new_char (realloc of online_chars).\n"); +- char_log("Out of memory: make_new_char (realloc of online_chars)." RETCODE); +- exit(1); +- } +- for(j = char_max - 256; j < char_max; j++) +- online_chars[j] = -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], 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]); ++ 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 mmo_charstatus)); ++ memset(&char_dat[i], 0, sizeof(struct character_data));
+ +- char_dat[i].char_id = char_id_count++; +- char_dat[i].account_id = sd->account_id; +- char_dat[i].char_num = dat[30]; +- strcpy(char_dat[i].name, dat); +- char_dat[i].class = 0; +- char_dat[i].base_level = 1; +- char_dat[i].job_level = 1; +- char_dat[i].base_exp = 0; +- char_dat[i].job_exp = 0; +- char_dat[i].zeny = start_zeny; +- char_dat[i].str = dat[24]; +- char_dat[i].agi = dat[25]; +- char_dat[i].vit = dat[26]; +- char_dat[i].int_ = dat[27]; +- char_dat[i].dex = dat[28]; +- char_dat[i].luk = dat[29]; +- char_dat[i].max_hp = 40 * (100 + char_dat[i].vit) / 100; +- char_dat[i].max_sp = 11 * (100 + char_dat[i].int_) / 100; +- char_dat[i].hp = char_dat[i].max_hp; +- char_dat[i].sp = char_dat[i].max_sp; +- char_dat[i].status_point = 0; +- char_dat[i].skill_point = 0; +- char_dat[i].option = 0; +- char_dat[i].karma = 0; +- char_dat[i].manner = 0; +- char_dat[i].party_id = 0; +- char_dat[i].guild_id = 0; +- char_dat[i].hair = dat[33]; +- char_dat[i].hair_color = dat[31]; +- char_dat[i].clothes_color = 0; +- char_dat[i].inventory[0].nameid = start_weapon; // Knife +- char_dat[i].inventory[0].amount = 1; +- char_dat[i].inventory[0].equip = 0x02; +- char_dat[i].inventory[0].identify = 1; +- char_dat[i].inventory[0].broken = 0; +- char_dat[i].inventory[1].nameid = start_armor; // Cotton Shirt +- char_dat[i].inventory[1].amount = 1; +- char_dat[i].inventory[1].equip = 0x10; +- char_dat[i].inventory[1].identify = 1; +- char_dat[i].inventory[1].broken = 0; +- char_dat[i].weapon = 1; +- char_dat[i].shield = 0; +- char_dat[i].head_top = 0; +- char_dat[i].head_mid = 0; +- char_dat[i].head_bottom = 0; +- memcpy(&char_dat[i].last_point, &start_point, sizeof(start_point)); +- memcpy(&char_dat[i].save_point, &start_point, sizeof(start_point)); ++ char_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(); +@@ -882,8 +1190,8 @@ + //---------------------------------------------------- + // This function return the name of the job (by [Yor]) + //---------------------------------------------------- +-char * job_name(int class) { +- switch (class) { ++char * job_name(int class_) {
++ switch (class_) {
+ case 0: return "Novice"; + case 1: return "Swordsman"; + case 2: return "Mage"; +@@ -957,108 +1265,138 @@ + return "Unknown Job"; + } + +-//------------------------------------------------------------- +-// Function to create the online files (txt and html). by [Yor] +-//------------------------------------------------------------- +-void create_online_files(void) { +- int i, j, k, l; // for loops +- int players; // count the number of players +- FILE *fp; // for the txt file +- FILE *fp2; // for the html file +- char temp[256]; // to prepare what we must display +- time_t time_server; // for number of seconds +- struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... +- int id[char_num]; +- +- if (online_display_option == 0) // we display nothing, so return +- return; ++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*);
+ +- //char_log("Creation of online players files." RETCODE); ++ // check if map-server is online
++ if (character->server == -1 || character->char_id == -1) { //Character not currently online.
++ return -1;
++ }
+ +- // Get number of online players, id of each online players +- players = 0; +- // sort online characters. +- for(i = 0; i < char_num; i++) { +- if (online_chars[i] != -1) { +- id[players] = i; ++ 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) +- { +- char *p_name = char_dat[i].name; //speed up sorting when there are a lot of players. But very rarely players have same name. +- for(j = 0; j < players; j++) +- if (stricmp(p_name, char_dat[id[j]].name) < 0 || ++ 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(p_name, char_dat[id[j]].name) == 0 && +- strcmp(p_name, char_dat[id[j]].name) < 0)) { +- for(k = players; k > j; k--) +- id[k] = id[k-1]; +- id[j] = i; // id[players] ++ (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(j = 0; j < players; j++) +- if (char_dat[i].zeny < char_dat[id[j]].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[i].zeny == char_dat[id[j]].zeny && +- stricmp(char_dat[i].name, char_dat[id[j]].name) < 0)) { +- for(k = players; k > j; k--) +- id[k] = id[k-1]; +- id[j] = i; // id[players] ++ (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(j = 0; j < players; j++) +- if (char_dat[i].base_level < char_dat[id[j]].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[i].base_level == char_dat[id[j]].base_level && +- char_dat[i].base_exp < char_dat[id[j]].base_exp)) { +- for(k = players; k > j; k--) +- id[k] = id[k-1]; +- id[j] = i; // id[players] ++ (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(j = 0; j < players; j++) +- if (char_dat[i].class < char_dat[id[j]].class || ++ 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[i].class == char_dat[id[j]].class && +- char_dat[i].job_level < char_dat[id[j]].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[i].class == char_dat[id[j]].class && +- char_dat[i].job_level == char_dat[id[j]].job_level && +- char_dat[i].job_exp < char_dat[id[j]].job_exp)) { +- for(k = players; k > j; k--) +- id[k] = id[k-1]; +- id[j] = i; // id[players] ++ (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 + { +- int cpm_result; // A lot of player maps are identical. So, test if done often twice. +- for(j = 0; j < players; j++) +- if ((cpm_result = strcmp(char_dat[i].last_point.map, char_dat[id[j]].last_point.map)) < 0 || // no map are identical and with upper cases (not use stricmp) ++ 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. +- (cpm_result == 0 && +- stricmp(char_dat[i].name, char_dat[id[j]].name) < 0)) { +- for(k = players; k > j; k--) +- id[k] = id[k-1]; +- id[j] = i; // id[players] ++ (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++; ++ (*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"); +@@ -1080,8 +1418,9 @@ + fprintf(fp, "Online Players on %s (%s):\n", server_name, temp); + fprintf(fp, "\n"); + +- // If we display at least 1 player +- if (players > 0) { ++ 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"); +@@ -1128,16 +1467,14 @@ + for (k = 0; k < j; k++) + fprintf(fp, "-"); + fprintf(fp, "\n"); +- +- // display each player. +- for (i = 0; i < players; i++) { ++ }
++ fprintf(fp2, " <tr>\n");
+ // get id of the character (more speed) + j = id[i]; +- fprintf(fp2, " <tr>\n"); + // displaying the character name + if ((online_display_option & 1) || (online_display_option & 64)) { // without/with 'GM' display +- strcpy(temp, char_dat[j].name); +- l = isGM(char_dat[j].account_id); ++ 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); +@@ -1149,7 +1486,7 @@ + fprintf(fp2, " <td>"); + if ((online_display_option & 64) && l >= online_gm_display_min_level) + fprintf(fp2, "<b>"); +- for (k = 0; temp[k]; k++) { ++ for (k = 0; k < strlen(temp); k++) {
+ switch(temp[k]) { + case '<': // < + fprintf(fp2, "<"); +@@ -1168,48 +1505,51 @@ + } + // displaying of the job + if (online_display_option & 6) { +- char * jobname = job_name(char_dat[j].class); ++ 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].base_level, char_dat[j].job_level); +- fprintf(fp, "%-18s %3d/%3d ", jobname, char_dat[j].base_level, char_dat[j].job_level); ++ 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].base_level, char_dat[j].job_level); +- fprintf(fp, "%3d/%3d ", char_dat[j].base_level, char_dat[j].job_level); ++ 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 +- memset(temp, 0, sizeof(temp)); +- strncpy(temp, char_dat[j].last_point.map, 16); +- if (strchr(temp, '.') != NULL) +- temp[strchr(temp, '.') - temp] = '\0'; // suppress the '.gat' ++ 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 coordonates +- fprintf(fp2, " <td>%s (%d, %d)</td>\n", temp, char_dat[j].last_point.x, char_dat[j].last_point.y); +- fprintf(fp, "%-12s (%3d,%3d) ", temp, char_dat[j].last_point.x, char_dat[j].last_point.y); ++ 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 number of zenys ++ // displaying nimber of zenys
+ if (online_display_option & 32) { + // write number of zenys +- if (char_dat[j].zeny == 0) { // if no zeny ++ 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].zeny); +- fprintf(fp, "%13d z ", char_dat[j].zeny); ++ 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"); + } +@@ -1218,8 +1558,9 @@ + if (players == 0) { + fprintf(fp2, " <p>No user is online.</p>\n"); + fprintf(fp, "No user is online.\n"); +- // no display if only 1 player + } else if (players == 1) { ++ 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); +@@ -1254,15 +1595,17 @@ + int mmo_char_send006b(int fd, struct char_session_data *sd) { + int i, j, found_num; + struct mmo_charstatus *p; +-#ifdef NEW_006b ++//#ifdef NEW_006b
+ const int offset = 24; +-#else +- const int offset = 4; +-#endif ++//#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].account_id == sd->account_id) { ++ if (char_dat[i].status.account_id == sd->account_id) {
+ sd->found_char[found_num] = i; + found_num++; + if (found_num == 9) +@@ -1272,18 +1615,19 @@ + 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]]; ++ 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+4) = p->base_exp>LONG_MAX?LONG_MAX:p->base_exp;
+ WFIFOL(fd,j+8) = p->zeny; +- WFIFOL(fd,j+12) = p->job_exp; ++ WFIFOL(fd,j+12) = p->job_exp>LONG_MAX?LONG_MAX:p->job_exp;
+ WFIFOL(fd,j+16) = p->job_level; + + WFIFOL(fd,j+20) = 0; +@@ -1299,9 +1643,16 @@ + 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+52) = p->class_;
+ WFIFOW(fd,j+54) = p->hair; +- WFIFOW(fd,j+56) = p->weapon; ++
++ // 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; +@@ -1311,7 +1662,7 @@ + WFIFOW(fd,j+70) = p->hair_color; + WFIFOW(fd,j+72) = p->clothes_color; + +- memcpy(WFIFOP(fd,j+74), p->name, 24); ++ 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; +@@ -1327,34 +1678,20 @@ + return 0; + } + +-int set_account_reg2(int acc, int num, struct global_reg *reg) { +- int i, c; ++// 離婚(char削除時に使用)
++int char_divorce(struct mmo_charstatus *cs) {
++ if (cs == NULL)
++ return 0;
+ +- c = 0; ++ if (cs->partner_id > 0){
++ int i, j;
+ for(i = 0; i < char_num; i++) { +- if (char_dat[i].account_id == acc) { +- memcpy(char_dat[i].account_reg2, reg, sizeof(char_dat[i].account_reg2)); +- char_dat[i].account_reg2_num = num; +- c++; +- } +- } +- return c; +-} +- +-// 離婚(char削除時に使用) +-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].char_id == cs->partner_id && char_dat[i].partner_id == cs->char_id) { ++ 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].partner_id = 0; ++ char_dat[i].status.partner_id = 0;
+ for(j = 0; j < MAX_INVENTORY; j++) +- if (char_dat[i].inventory[j].nameid == WEDDING_RING_M || char_dat[i].inventory[j].nameid == WEDDING_RING_F) +- memset(&char_dat[i].inventory[j], 0, sizeof(char_dat[i].inventory[0])); ++ 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; +@@ -1364,12 +1701,22 @@ + 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(unsigned char *email) { ++int e_mail_check(char *email) {
+ char ch; +- unsigned char* last_arobas; ++ char* last_arobas;
+ + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) +@@ -1412,7 +1759,7 @@ + + // disconnect player if online on char-server + for(i = 0; i < fd_max; i++) { +- if (session[i] && (sd = session[i]->session_data)) { ++ if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) {
+ if (sd->account_id == accound_id) { + session[i]->eof = 1; + return 1; +@@ -1432,20 +1779,20 @@ + inter_pet_delete(cs->pet_id); + for (j = 0; j < MAX_INVENTORY; j++) + if (cs->inventory[j].card[0] == (short)0xff00) +- inter_pet_delete(*((long *)(&cs->inventory[j].card[2]))); ++ 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(*((long *)(&cs->cart[j].card[2]))); ++ inter_pet_delete( MakeDWord(cs->cart[j].card[1],cs->cart[j].card[2]) );
+ // ギルド脱退 + if (cs->guild_id) + inter_guild_leave(cs->guild_id, cs->account_id, cs->char_id); + // パーティー脱退 + if (cs->party_id) +- inter_party_leave(cs->party_id, cs->account_id); ++ inter_party_leave(cs->party_id, cs->account_id, cs->char_id);
+ // 離婚 + if (cs->partner_id){ + // 離婚情報をmapに通知 +- char buf[10]; ++ unsigned char buf[10];
+ WBUFW(buf,0) = 0x2b12; + WBUFL(buf,2) = cs->char_id; + WBUFL(buf,6) = cs->partner_id; +@@ -1459,22 +1806,24 @@ + 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) { ++ if (fd != login_fd)
++ session[fd]->eof = 1;
++ if(session[fd]->eof) {
+ if (fd == login_fd) { +- printf("Char-server can't connect to login-server (connection #%d).\n", fd); ++ ShowWarning("Connection to login-server lost (connection #%d).\n", fd);
+ login_fd = -1; + } +- close(fd); +- delete_session(fd); ++ do_close(fd);
+ return 0; + } + +- sd = session[fd]->session_data; ++ sd = (struct char_session_data*)session[fd]->session_data;
+ +- while(RFIFOREST(fd) >= 2) { ++ 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)) { +@@ -1483,19 +1832,21 @@ + return 0; + if (RFIFOB(fd,2)) { + // printf("connect login server error : %d\n", RFIFOB(fd,2)); +- printf("Can not connect to login-server.\n"); +- printf("The server communication passwords (default s1/p1) is probably invalid.\n"); +- printf("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); +- printf("If you changed the communication passwords, change them back at map_athena.conf and char_athena.conf\n"); ++ 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 { +- printf("Connected to login-server (connection #%d).\n", fd); ++ 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][0]) // if map-server online and at least 1 map ++ if (server_fd[i] >= 0 && server[i].map[0]) // if map-server online and at least 1 map
+ break; + if (i == MAX_MAP_SERVERS) +- printf("Awaiting maps from map-server.\n"); ++ ShowStatus("Awaiting maps from map-server.\n");
+ } + RFIFOSKIP(fd,3); + break; +@@ -1505,8 +1856,9 @@ + return 0; + // printf("parse_tologin 2713 : %d\n", RFIFOB(fd,6)); + for(i = 0; i < fd_max; i++) { +- if (session[i] && (sd = session[i]->session_data) && sd->account_id == RFIFOL(fd,2)) { ++ 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); +@@ -1521,9 +1873,14 @@ + 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); +@@ -1539,7 +1896,7 @@ + if (RFIFOREST(fd) < 50) + return 0; + for(i = 0; i < fd_max; i++) { +- if (session[i] && (sd = session[i]->session_data)) { ++ 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) +@@ -1552,6 +1909,40 @@ + 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; +@@ -1576,49 +1967,55 @@ + sex = RFIFOB(fd,6); + RFIFOSKIP(fd, 7); + if (acc > 0) { +- for (i = 0; i < char_num; i++) { +- if (char_dat[i].account_id == acc) { +- int jobclass = char_dat[i].class; +- char_dat[i].sex = sex; ++ 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].class = (sex) ? 19 : 20; ++ char_dat[i].status.class_ = (sex) ? 19 : 20;
+ } else if (jobclass == 4020 || jobclass == 4021) { +- char_dat[i].class = (sex) ? 4020 : 4021; ++ char_dat[i].status.class_ = (sex) ? 4020 : 4021;
+ } else if (jobclass == 4042 || jobclass == 4043) { +- char_dat[i].class = (sex) ? 4042 : 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].skill[j].id > 0 && !char_dat[i].skill[j].flag) { +- char_dat[i].skill_point += char_dat[i].skill[j].lv; +- char_dat[i].skill[j].id = 0; +- char_dat[i].skill[j].lv = 0; ++ 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].skill[j].id > 0 && !char_dat[i].skill[j].flag) { +- char_dat[i].skill_point += char_dat[i].skill[j].lv; +- char_dat[i].skill[j].id = 0; +- char_dat[i].skill[j].lv = 0; ++ 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].inventory[j].nameid && char_dat[i].inventory[j].equip) +- char_dat[i].inventory[j].equip = 0; ++ 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].weapon = 0; +- char_dat[i].shield = 0; +- char_dat[i].head_top = 0; +- char_dat[i].head_mid = 0; +- char_dat[i].head_bottom = 0; ++ 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 +@@ -1644,14 +2041,14 @@ + if (i == MAX_MAP_SERVERS) + char_log("'ladmin': Receiving a message for broadcast, but no map-server is online." RETCODE); + else { +- char buf[128]; +- char message[RFIFOL(fd,4) + 1]; // +1 to add a null terminated if not exist in the packet ++ 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(message); ++ remove_control_chars((unsigned char *)message);
+ // remove all first spaces + p = message; + while(p[0] == ' ') +@@ -1702,22 +2099,13 @@ + case 0x2729: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; +- { +- struct global_reg reg[ACCOUNT_REG2_NUM]; +- unsigned char buf[4096]; +- int j, p, acc; +- acc = RFIFOL(fd,4); +- for (p = 8, j = 0; p < RFIFOW(fd,2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { +- memcpy(reg[j].str, RFIFOP(fd,p), 32); +- reg[j].value = RFIFOL(fd,p+32); +- } +- set_account_reg2(acc, j, reg); +- // 同垢ログインを禁止していれば送る必要は無い ++ { //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) = 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)); +-// printf("char: save_account_reg_reply\n"); + } + break; + +@@ -1727,20 +2115,20 @@ + return 0; + // Deletion of all characters of the account + for(i = 0; i < char_num; i++) { +- if (char_dat[i].account_id == RFIFOL(fd,2)) { +- char_delete(&char_dat[i]); ++ if (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 mmo_charstatus)); ++ 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].account_id == RFIFOL(fd,2)) { ++ 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 = session[j]->session_data) && +- sd2->account_id == char_dat[char_num-1].account_id) { ++ 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; +@@ -1792,10 +2180,10 @@ + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { +- char buf[32000]; ++ unsigned char buf[32000];
+ if (gm_account != NULL) +- free(gm_account); +- gm_account = calloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1); ++ 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); +@@ -1803,8 +2191,8 @@ + //printf("GM account: %d -> level %d\n", gm_account[GM_num].account_id, gm_account[GM_num].level); + GM_num++; + } +- printf("From login-server: receiving of %d GM accounts information.\n", GM_num); +- char_log("From login-server: receiving of %d GM accounts information." RETCODE, 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)); +@@ -1814,7 +2202,101 @@ + 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; + } +@@ -1824,57 +2306,130 @@ + return 0; + } + +-//-------------------------------- +-// Map-server anti-freeze system +-//-------------------------------- +-int map_anti_freeze_system(int tid, unsigned int tick, int id, int data) { +- int i; ++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;
++}
+ +- //printf("Entering in map_anti_freeze_system function to check freeze of servers.\n"); +- for(i = 0; i < MAX_MAP_SERVERS; i++) { +- if (server_fd[i] >= 0) {// if map-server is online +- //printf("map_anti_freeze_system: server #%d, flag: %d.\n", i, server_freezeflag[i]); +- if (server_freezeflag[i]-- < 1) { // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed +- printf("Map-server anti-freeze system: char-server #%d is freezed -> disconnection.\n", i); +- char_log("Map-server anti-freeze system: char-server #%d is freezed -> disconnection." RETCODE, +- i); +- session[server_fd[i]]->eof = 1; ++//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) { ++ if(id==MAX_MAP_SERVERS)
++ session[fd]->eof=1;
++ if(session[fd]->eof){
+ if (id < MAX_MAP_SERVERS) { +- printf("Map-server %d (session #%d) has disconnected.\n", id, fd); +- memset(&server[id], 0, sizeof(struct mmo_map_server)); ++ 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; +- for(j = 0; j < char_num; j++) +- if (online_chars[j] == fd) +- online_chars[j] = -1; +- create_online_files(); // update online players files (to remove all online players of this server) ++ online_char_db->foreach(online_char_db,char_db_setoffline,i); //Tag relevant chars as 'in disconnected' server.
+ } +- close(fd); +- delete_session(fd); ++ do_close(fd);
++ create_online_files();
+ return 0; + } + +- while(RFIFOREST(fd) >= 2) { ++ 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"); +@@ -1888,36 +2443,41 @@ + return 0; + memset(server[id].map, 0, sizeof(server[id].map)); + j = 0; +- for(i = 4; i < RFIFOW(fd,2); i += 16) { +- memcpy(server[id].map[j], RFIFOP(fd,i), 16); +-// printf("set map %d.%d : %s\n", id, j, server[id].map[j]); ++ 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; +- printf("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", ++ 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); +- printf("Map-server %d loading complete.\n", id); ++ 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, 24); // name for wisp to player +- WFIFOSET(fd,27); ++ 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) { +- printf("WARNING: Map-Server %d have NO map.\n", id); ++ 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 * 16 + 10; ++ 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 * 16); ++ 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 +@@ -1928,10 +2488,10 @@ + WFIFOW(fd,8) = server[x].port; + j = 0; + for(i = 0; i < MAX_MAP_PER_SERVER; i++) +- if (server[x].map[i][0]) +- memcpy(WFIFOP(fd,10+(j++)*16), server[x].map[i], 16); ++ if (server[x].map[i])
++ WFIFOW(fd,10+(j++)*4) = server[x].map[i];
+ if (j > 0) { +- WFIFOW(fd,2) = j * 16 + 10; ++ WFIFOW(fd,2) = j * 4 + 10;
+ WFIFOSET(fd,WFIFOW(fd,2)); + } + } +@@ -1940,83 +2500,95 @@ + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + +- // 認証要求 ++ //Packet command is now used for sc_data request. [Skotlex]
+ case 0x2afc: +- if (RFIFOREST(fd) < 22) ++ if (RFIFOREST(fd) < 10)
+ return 0; +- //printf("auth_fifo search: account: %d, char: %d, secure: %08x-%08x\n", RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14)); +- for(i = 0; i < AUTH_FIFO_SIZE; i++) { +- if (auth_fifo[i].account_id == RFIFOL(fd,2) && +- auth_fifo[i].char_id == RFIFOL(fd,6) && +- auth_fifo[i].login_id1 == RFIFOL(fd,10) && +-#if CMP_AUTHFIFO_LOGIN2 != 0 +- // here, it's the only area where it's possible that we doesn't know login_id2 (map-server asks just after 0x72 packet, that doesn't given the value) +- (auth_fifo[i].login_id2 == RFIFOL(fd,14) || RFIFOL(fd,14) == 0) && // relate to the versions higher than 18 ++ {
++#ifdef ENABLE_SC_SAVING
++ int aid, cid;
++ struct scdata *data;
++ aid = RFIFOL(fd,2);
++ cid = RFIFOL(fd,6);
+ #endif +- (!check_ip_flag || auth_fifo[i].ip == RFIFOL(fd,18)) && +- !auth_fifo[i].delflag) { +- auth_fifo[i].delflag = 1; +- WFIFOW(fd,0) = 0x2afd; +- WFIFOW(fd,2) = 16 + sizeof(struct mmo_charstatus); +- WFIFOL(fd,4) = RFIFOL(fd,2); +- WFIFOL(fd,8) = auth_fifo[i].login_id2; +- WFIFOL(fd,12) = (unsigned long)auth_fifo[i].connect_until_time; +- char_dat[auth_fifo[i].char_pos].sex = auth_fifo[i].sex; +- memcpy(WFIFOP(fd,16), &char_dat[auth_fifo[i].char_pos], sizeof(struct mmo_charstatus)); ++ 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)); +- //printf("auth_fifo search success (auth #%d, account %d, character: %d).\n", i, RFIFOL(fd,2), RFIFOL(fd,6)); +- break; ++ status_delete_scdata(aid, cid); //Data sent, so it needs be discarded now.
+ } ++#endif
++ break;
+ } +- if (i == AUTH_FIFO_SIZE) { +- WFIFOW(fd,0) = 0x2afe; +- WFIFOL(fd,2) = RFIFOL(fd,2); +- WFIFOSET(fd,6); +- printf("auth_fifo search error! account %d not authentified.\n", RFIFOL(fd,2)); ++
++ //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,22); ++ RFIFOSKIP(fd, 4);
+ break; +- +- // MAPサーバー上のユーザー数受信 ++ //set MAP users
+ case 0x2aff: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + server[id].users = RFIFOW(fd,4); +- if(anti_freeze_enable) +- server_freezeflag[id] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed +- // remove all previously online players of the server +- for(i = 0; i < char_num; i++) +- if (online_chars[i] == id) +- online_chars[i] = -1; +- // add online players in the list by [Yor] ++ // 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 char_id = RFIFOL(fd,6+i*4); +- for(j = 0; j < char_num; j++) +- if (char_dat[j].char_id == char_id) { +- online_chars[j] = id; +- //printf("%d\n", char_id); +- break; ++ 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. + } +- RFIFOSKIP(fd,6+i*4); ++ //If any chars remain in -2, they will be cleaned in the cleanup timer.
++ RFIFOSKIP(fd,6+i*8);
+ break; + + // キャラデータ保存 ++ // 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].account_id == RFIFOL(fd,4) && +- char_dat[i].char_id == RFIFOL(fd,8)) ++ 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], RFIFOP(fd,12), sizeof(struct mmo_charstatus)); ++ 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; + +@@ -2026,7 +2598,6 @@ + return 0; + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; +- //printf("auth_fifo set (auth #%d) - account: %d, secure: %08x-%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); + auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd,2); + auth_fifo[auth_fifo_pos].char_id = 0; + auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd,6); +@@ -2043,35 +2614,58 @@ + RFIFOSKIP(fd,18); + break; + +- // マップサーバー間移動要求 ++ // request "change map server"
+ case 0x2b05: +- if (RFIFOREST(fd) < 49) ++ if (RFIFOREST(fd) < 35)
+ return 0; +- if (auth_fifo_pos >= AUTH_FIFO_SIZE) +- auth_fifo_pos = 0; +- WFIFOW(fd,0) = 0x2b06; +- memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 42); +- //printf("auth_fifo set (auth#%d) - account: %d, secure: 0x%08x-0x%08x\n", auth_fifo_pos, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); +- auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd,2); +- auth_fifo[auth_fifo_pos].char_id = RFIFOL(fd,14); +- auth_fifo[auth_fifo_pos].login_id1 = RFIFOL(fd,6); +- auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd,10); +- auth_fifo[auth_fifo_pos].delflag = 0; +- auth_fifo[auth_fifo_pos].sex = RFIFOB(fd,44); +- auth_fifo[auth_fifo_pos].connect_until_time = 0; // unlimited/unknown time by default (not display in map-server) +- auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,45); +- for(i = 0; i < char_num; i++) +- if (char_dat[i].account_id == RFIFOL(fd,2) && +- char_dat[i].char_id == RFIFOL(fd,14)) { +- auth_fifo[auth_fifo_pos].char_pos = i; +- auth_fifo_pos++; +- WFIFOL(fd,6) = 0; ++ {
++ 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; + } +- if (i == char_num) +- WFIFOW(fd,6) = 1; +- WFIFOSET(fd,44); +- RFIFOSKIP(fd,49); ++ 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; + + // キャラ名検索 +@@ -2079,16 +2673,17 @@ + if (RFIFOREST(fd) < 6) + return 0; + for(i = 0; i < char_num; i++) { +- if (char_dat[i].char_id == RFIFOL(fd,2)) ++ 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].name, 24); ++ memcpy(WFIFOP(fd,6), char_dat[i].status.name, NAME_LENGTH);
+ else +- memcpy(WFIFOP(fd,6), unknown_char_name, 24); +- WFIFOSET(fd,30); ++ memcpy(WFIFOP(fd,6), unknown_char_name, NAME_LENGTH);
++ WFIFOSET(fd,6+NAME_LENGTH);
++ //WFIFOSET(fd,30);
+ RFIFOSKIP(fd,6); + break; + +@@ -2127,10 +2722,10 @@ + if (RFIFOREST(fd) < 44) + return 0; + { +- char character_name[24]; ++ char character_name[NAME_LENGTH];
+ int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody) +- memcpy(character_name, RFIFOP(fd,6), 24); +- character_name[sizeof(character_name) -1] = '\0'; ++ 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 +@@ -2138,14 +2733,15 @@ + // search character + i = search_character_index(character_name); + if (i >= 0) { +- memcpy(WFIFOP(fd,6), search_character_name(i), 24); // put correct name if found +- WFIFOW(fd,32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline ++ 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].account_id)) { ++ 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].account_id; // account value ++ 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); +@@ -2155,10 +2751,10 @@ + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 2: // ban +- if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { ++ if (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].account_id; // account value ++ 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 +@@ -2174,10 +2770,10 @@ + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 3: // unblock +- if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { ++ if (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].account_id; // account value ++ 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); +@@ -2187,10 +2783,10 @@ + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 4: // unban +- if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { ++ if (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].account_id; // account value ++ 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 +@@ -2199,10 +2795,10 @@ + WFIFOW(fd,32) = 2; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + break; + case 5: // changesex +- if (acc == -1 || isGM(acc) >= isGM(char_dat[i].account_id)) { ++ if (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].account_id; // account value ++ 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 +@@ -2213,12 +2809,14 @@ + } + } else { + // character name not found +- memcpy(WFIFOP(fd,6), character_name, 24); +- WFIFOW(fd,32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline ++ 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, 34);
++ WFIFOSET(fd, 10+NAME_LENGTH);
+ } + RFIFOSKIP(fd, 44); + break; +@@ -2226,34 +2824,157 @@ + + // case 0x2b0f: not more used (available for futur usage) + +- // account_reg保存要求 +- case 0x2b10: +- if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) ++ //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; + { +- struct global_reg reg[ACCOUNT_REG2_NUM]; +- int p, acc; +- acc = RFIFOL(fd,4); +- for(p = 8, j = 0; p < RFIFOW(fd,2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { +- memcpy(reg[j].str, RFIFOP(fd,p), 32); +- reg[j].value = RFIFOL(fd, p+32); ++ 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;
+ } +- set_account_reg2(acc, j, reg); +- // loginサーバーへ送る +- if (login_fd > 0) { // don't send request if no login-server +- WFIFOW(login_fd, 0) = 0x2728; +- memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); +- WFIFOSET(login_fd, WFIFOW(login_fd,2)); + } +- // ワールドへの同垢ログインがなければmapサーバーに送る必要はない +- //memcpy(buf, RFIFOP(fd,0), RFIFOW(fd,2)); +- //WBUFW(buf,0) = 0x2b11; +- //mapif_sendall(buf, WBUFW(buf,2)); ++ }
++
++ // starting to send to map
++ WBUFW(buf,0) = 0x2b1b;
++ // add list for blacksmiths
++ for (i = 0, j = 0; i < char_num && j < fame_list_size_smith; 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 < fame_list_size_chemist; 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 < fame_list_size_taekwon; 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)); +-// printf("char: save_account_reg (from map)\n"); + break; + } +- + default: + // inter server処理に渡す + { +@@ -2264,7 +2985,7 @@ + return 0; + } + // inter server処理でもない場合は切断 +- printf("char: unknown packet 0x%04x (%d bytes to read in buffer)! (from map).\n", RFIFOW(fd,0), RFIFOREST(fd)); ++ ShowError("Unknown packet 0x%04x from map server, disconnecting.\n", RFIFOW(fd,0));
+ session[fd]->eof = 1; + return 0; + } +@@ -2272,28 +2993,20 @@ + return 0; + } + +-int search_mapserver(char *map) { ++int search_mapserver(unsigned short map, long ip, short port) {
+ int i, j; +- char temp_map[16]; +- int temp_map_len; +- +-// printf("Searching the map-server for map '%s'... ", map); +- strncpy(temp_map, map, sizeof(temp_map)); +- temp_map[sizeof(temp_map)-1] = '\0'; +- if (strchr(temp_map, '.') != NULL) +- temp_map[strchr(temp_map, '.') - temp_map + 1] = '\0'; // suppress the '.gat', but conserve the '.' to be sure of the name of the map + +- temp_map_len = strlen(temp_map); + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) +- for (j = 0; server[i].map[j][0]; j++) +- //printf("%s : %s = %d\n", server[i].map[j], map, strncmp(server[i].map[j], temp_map, temp_map_len)); +- if (strncmp(server[i].map[j], temp_map, temp_map_len) == 0) { +-// printf("found -> server #%d.\n", i); ++ 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; + } + +-// printf("not found.\n"); + return -1; + } + +@@ -2302,48 +3015,81 @@ + 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){ ++//--------------------------------------------
++// Test to know if an IP come from LAN or WAN.
++// Rewrote: Adnvanced subnet check [LuzZza]
++//--------------------------------------------
++int lan_subnetcheck(long *p) {
++
+ int i; +- int lancheck = 1; ++ unsigned char *sbn, *msk, *src = (unsigned char *)p;
+ +-// 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; ++ for(i=0; i<subnet_count; i++) {
++
++ if((subnet[i].subnet & subnet[i].mask) == (*p & subnet[i].mask)) {
++
++ sbn = (unsigned char *)&subnet[i].subnet;
++ msk = (unsigned char *)&subnet[i].mask;
++
++ ShowInfo("Subnet check [%u.%u.%u.%u]: Matches "CL_CYAN"%u.%u.%u.%u/%u.%u.%u.%u"CL_RESET"\n",
++ src[0], src[1], src[2], src[3], sbn[0], sbn[1], sbn[2], sbn[3], msk[0], msk[1], msk[2], msk[3]);
++
++ return subnet[i].map_ip;
+ } + } +- printf("LAN test (result): %s source\033[0m.\n", (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); +- return lancheck; ++
++ ShowInfo("Subnet check [%u.%u.%u.%u]: "CL_CYAN"WAN"CL_RESET"\n", src[0], src[1], src[2], src[3]);
++ return 0;
+ } + + 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; ++ long subnet_map_ip;
++
++ RFIFOHEAD(fd);
++
++ sd = (struct char_session_data*)session[fd]->session_data;
+ +- if (login_fd < 0 || session[fd]->eof) { // disconnect any player (already connected to char-server or coming back from map-server) if login-server is diconnected. ++ if (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; +- close(fd); +- delete_session(fd); ++ 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; + } + +- sd = session[fd]->session_data; ++ while(RFIFOREST(fd) >= 2 && !session[fd]->eof) {
++ cmd = RFIFOW(fd,0);
++ // crc32のスキップ用
++ if( sd==NULL && // 未ログインor管理パケット
++ RFIFOREST(fd)>=4 && // 最低バイト数制限 & 0x7530,0x7532管理パケ除去
++ RFIFOREST(fd)<=21 && // 最大バイト数制限 & サーバーログイン除去
++ cmd!=0x20b && // md5通知パケット除去
++ (RFIFOREST(fd)<6 || RFIFOW(fd,4)==0x65) ){ // 次に何かパケットが来てるなら、接続でないとだめ
++ RFIFOSKIP(fd,4);
++ cmd = RFIFOW(fd,0);
++ ShowDebug("parse_char : %d crc32 skipped\n",fd);
++ if(RFIFOREST(fd)==0)
++ return 0;
++ }
+ +- while (RFIFOREST(fd) >= 2) { +-// if (RFIFOW(fd,0) < 30000) +-// printf("parse_char: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); ++//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(RFIFOW(fd,0)) { ++ switch(cmd){
+ case 0x20b: //20040622暗号化ragexe対応 + if (RFIFOREST(fd) < 19) + return 0; +@@ -2356,13 +3102,15 @@ + { + int GM_value; + if ((GM_value = isGM(RFIFOL(fd,2)))) +- printf("Account Logged On; Account ID: %d (GM level %d).\n", RFIFOL(fd,2), GM_value); ++ ShowInfo("Account Logged On; Account ID: %d (GM level %d).\n", RFIFOL(fd,2), GM_value);
+ else +- printf("Account Logged On; Account ID: %d.\n", RFIFOL(fd,2)); ++ ShowInfo("Account Logged On; Account ID: %d.\n", RFIFOL(fd,2));
+ if (sd == NULL) { +- sd = session[fd]->session_data = calloc(sizeof(struct char_session_data), 1); +- memset(sd, 0, sizeof(struct char_session_data)); +- memcpy(sd->email, "no mail", 40); // put here a mail without '@' to refuse deletion if we don't receive the e-mail ++ 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); +@@ -2370,6 +3118,7 @@ + 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 +@@ -2382,6 +3131,44 @@ + (!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 +@@ -2421,89 +3208,103 @@ + break; + + case 0x66: // キャラ選択 +- if (RFIFOREST(fd) < 3) +- return 0; ++ 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 +- } else { + for (ch = 0; ch < 9; ch++) +- if (sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].char_num == RFIFOB(fd,2)) ++ if (sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].status.char_num == char_num)
+ break; +- if (ch != 9) { ++ 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, RFIFOB(fd,2), char_dat[sd->found_char[ch]].name); ++ sd->account_id, char_num, cd->name);
++
++ cd->sex = sd->sex;
++
+ // searching map server +- i = search_mapserver(char_dat[sd->found_char[ch]].last_point.map); ++ i = search_mapserver(cd->last_point.map,-1,-1);
+ // if map is not found, we check major cities + if (i < 0) { +- if ((i = search_mapserver("prontera.gat")) >= 0) { // check is done without 'gat'. +- memcpy(char_dat[sd->found_char[ch]].last_point.map, "prontera.gat", 16); +- char_dat[sd->found_char[ch]].last_point.x = 273; // savepoint coordonates +- char_dat[sd->found_char[ch]].last_point.y = 354; +- } else if ((i = search_mapserver("geffen.gat")) >= 0) { // check is done without 'gat'. +- memcpy(char_dat[sd->found_char[ch]].last_point.map, "geffen.gat", 16); +- char_dat[sd->found_char[ch]].last_point.x = 120; // savepoint coordonates +- char_dat[sd->found_char[ch]].last_point.y = 100; +- } else if ((i = search_mapserver("morocc.gat")) >= 0) { // check is done without 'gat'. +- memcpy(char_dat[sd->found_char[ch]].last_point.map, "morocc.gat", 16); +- char_dat[sd->found_char[ch]].last_point.x = 160; // savepoint coordonates +- char_dat[sd->found_char[ch]].last_point.y = 94; +- } else if ((i = search_mapserver("alberta.gat")) >= 0) { // check is done without 'gat'. +- memcpy(char_dat[sd->found_char[ch]].last_point.map, "alberta.gat", 16); +- char_dat[sd->found_char[ch]].last_point.x = 116; // savepoint coordonates +- char_dat[sd->found_char[ch]].last_point.y = 57; +- } else if ((i = search_mapserver("payon.gat")) >= 0) { // check is done without 'gat'. +- memcpy(char_dat[sd->found_char[ch]].last_point.map, "payon.gat", 16); +- char_dat[sd->found_char[ch]].last_point.x = 87; // savepoint coordonates +- char_dat[sd->found_char[ch]].last_point.y = 117; +- } else if ((i = search_mapserver("izlude.gat")) >= 0) { // check is done without 'gat'. +- memcpy(char_dat[sd->found_char[ch]].last_point.map, "izlude.gat", 16); +- char_dat[sd->found_char[ch]].last_point.x = 94; // savepoint coordonates +- char_dat[sd->found_char[ch]].last_point.y = 103; ++ 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 { +- int j; + // get first online server (with a map) + i = 0; + for(j = 0; j < MAX_MAP_SERVERS; j++) +- if (server_fd[j] >= 0 && server[j].map[0][0]) { // change save point to one of map found on the server (the first) ++ if (server_fd[j] >= 0 && server[j].map[0]) { // change save point to one of map found on the server (the first)
+ i = j; +- memcpy(char_dat[sd->found_char[ch]].last_point.map, server[j].map[0], 16); +- printf("Map-server #%d found with a map: '%s'.\n", j, server[j].map[0]); +- // coordonates are unknown ++ 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; +- WFIFOL(fd,2) = 1; // 01 = Server closed ++ WFIFOB(fd,2) = 1; // 01 = Server closed
+ WFIFOSET(fd,3); +- RFIFOSKIP(fd,3); + break; + } + } + } ++ WFIFOHEAD(fd, 28);
+ WFIFOW(fd,0) = 0x71; +- WFIFOL(fd,2) = char_dat[sd->found_char[ch]].char_id; +- memcpy(WFIFOP(fd,6), char_dat[sd->found_char[ch]].last_point.map, 16); +- printf("Character selection '%s' (account: %d, slot: %d).\n", char_dat[sd->found_char[ch]].name, sd->account_id, ch); +- printf("--Send IP of map-server. "); +- if (lan_ip_check(p)) +- WFIFOL(fd, 22) = inet_addr(lan_map_ip); ++ 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);
++
++ // Andvanced subnet check [LuzZza]
++ if((subnet_map_ip = lan_subnetcheck((long *)p)))
++ WFIFOL(fd,22) = subnet_map_ip;
+ else + WFIFOL(fd, 22) = server[i].ip; ++
+ WFIFOW(fd,26) = server[i].port; + WFIFOSET(fd,28); + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; +- //printf("auth_fifo set #%d - account %d, char: %d, secure: %08x-%08x\n", auth_fifo_pos, sd->account_id, char_dat[sd->found_char[ch]].char_id, sd->login_id1, sd->login_id2); + auth_fifo[auth_fifo_pos].account_id = sd->account_id; +- auth_fifo[auth_fifo_pos].char_id = char_dat[sd->found_char[ch]].char_id; ++ auth_fifo[auth_fifo_pos].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; +@@ -2511,62 +3312,109 @@ + auth_fifo[auth_fifo_pos].sex = sd->sex; + auth_fifo[auth_fifo_pos].connect_until_time = sd->connect_until_time; + auth_fifo[auth_fifo_pos].ip = session[fd]->client_addr.sin_addr.s_addr; +- auth_fifo_pos++; ++
++ //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++;
+ } +- RFIFOSKIP(fd,3); + break; + + case 0x67: // 作成 +- if (RFIFOREST(fd) < 37) +- return 0; ++ 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 < 0) { ++
++ 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].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) = char_dat[i].status.char_id;
++ WFIFOL(fd,2+4) = char_dat[i].status.base_exp>LONG_MAX?LONG_MAX: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>LONG_MAX?LONG_MAX:char_dat[i].status.job_exp;
++ WFIFOL(fd,2+16) = char_dat[i].status.job_level;
+ +- WFIFOL(fd,2+28) = char_dat[i].karma; +- WFIFOL(fd,2+32) = char_dat[i].manner; ++ 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].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, 24); +- +- WFIFOB(fd,2+98) = (char_dat[i].str > 255) ? 255 : char_dat[i].str; +- WFIFOB(fd,2+99) = (char_dat[i].agi > 255) ? 255 : char_dat[i].agi; +- WFIFOB(fd,2+100) = (char_dat[i].vit > 255) ? 255 : char_dat[i].vit; +- WFIFOB(fd,2+101) = (char_dat[i].int_ > 255) ? 255 : char_dat[i].int_; +- WFIFOB(fd,2+102) = (char_dat[i].dex > 255) ? 255 : char_dat[i].dex; +- WFIFOB(fd,2+103) = (char_dat[i].luk > 255) ? 255 : char_dat[i].luk; +- WFIFOB(fd,2+104) = char_dat[i].char_num; ++ 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); +@@ -2578,8 +3426,8 @@ + } + + case 0x68: // delete char //Yor's Fix +- if (RFIFOREST(fd) < 46) +- return 0; ++ FIFOSD_CHECK(46);
++
+ memcpy(email, RFIFOP(fd,6), 40); + if (e_mail_check(email) == 0) + strncpy(email, "a@a.com", 40); // default e-mail +@@ -2596,7 +3444,7 @@ + } else { + // we change the packet to set it like selection. + for (i = 0; i < 9; i++) +- if (char_dat[sd->found_char[i]].char_id == RFIFOL(fd,2)) { ++ 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) +@@ -2608,7 +3456,7 @@ + RFIFOSKIP(fd,43); + // change value to put new packet (char selection) + RFIFOW(fd, 0) = 0x66; +- RFIFOB(fd, 2) = char_dat[sd->found_char[i]].char_num; ++ RFIFOB(fd, 2) = char_dat[sd->found_char[i]].status.char_num;
+ // not send packet, it's modify of actual packet + break; + } +@@ -2630,7 +3478,7 @@ + } else { + for (i = 0; i < 9; i++) { + struct mmo_charstatus *cs = NULL; +- if ((cs = &char_dat[sd->found_char[i]])->char_id == RFIFOL(fd,2)) { ++ 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) { +@@ -2640,8 +3488,8 @@ + int j, k; + struct char_session_data *sd2; + for (j = 0; j < fd_max; j++) { +- if (session[j] && (sd2 = session[j]->session_data) && +- sd2->account_id == char_dat[char_num-1].account_id) { ++ 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]; +@@ -2682,7 +3530,7 @@ + if (server_fd[i] < 0) + break; + } +- if (i == MAX_MAP_SERVERS || strcmp(RFIFOP(fd,2), userid) || strcmp(RFIFOP(fd,26), passwd)){ ++ 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); +@@ -2691,8 +3539,6 @@ + WFIFOB(fd,2) = 0; + session[fd]->func_parse = parse_frommap; + server_fd[i] = fd; +- if(anti_freeze_enable) +- server_freezeflag[i] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed + server[i].ip = RFIFOL(fd,54); + server[i].port = RFIFOW(fd,58); + server[i].users = 0; +@@ -2735,9 +3581,6 @@ + return 0; + + case 0x7532: // 接続の切断(defaultと処理は一緒だが明示的にするため) +- session[fd]->eof = 1; +- return 0; +- + default: + session[fd]->eof = 1; + return 0; +@@ -2747,6 +3590,30 @@ + 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;
++}
++
+ // 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) + int mapif_sendall(unsigned char *buf, unsigned int len) { + int i, c; +@@ -2755,6 +3622,16 @@ + 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++; +@@ -2771,6 +3648,9 @@ + 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++; +@@ -2785,6 +3665,9 @@ + 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; +@@ -2796,10 +3679,11 @@ + + int send_users_tologin(int tid, unsigned int tick, int id, int data) { + int users = count_users(); +- char buf[16]; ++ 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); +@@ -2812,17 +3696,52 @@ + 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) { +- printf("Attempt to connect to login-server...\n"); ++ 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); ++ memcpy(WFIFOP(login_fd,2), userid, 24);
++ memcpy(WFIFOP(login_fd,26), passwd, 24);
+ WFIFOL(login_fd,50) = 0; + WFIFOL(login_fd,54) = char_ip; + WFIFOL(login_fd,58) = char_port; +@@ -2830,12 +3749,28 @@ + 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; ++
++ 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軋is, deutsch, espaol +@@ -2849,92 +3784,56 @@ + 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; ++//----------------------------------
++// Reading Lan Support configuration
++// Rewrote: Anvanced subnet check [LuzZza]
++//----------------------------------
++int char_lan_config_read(const char *lancfgName) {
+ +- // 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"); ++ FILE *fp;
++ int line_num = 0;
++ char line[1024], w1[64], w2[64], w3[64], w4[64], w5[64];
+ +- if (fp == NULL) { +- printf("LAN support configuration file not found: %s\n", lancfgName); ++ if((fp = fopen(lancfgName, "r")) == NULL) {
++ ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName);
+ return 1; + } + +- printf ("---start reading of Lan Support configuration...\n"); ++ ShowInfo("Reading the configuration file %s...\n", lancfgName);
+ + while(fgets(line, sizeof(line)-1, fp)) { +- if (line[0] == '/' && line[1] == '/') ++
++ line_num++;
++ if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n')
+ continue; + + line[sizeof(line)-1] = '\0'; +- if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) +- continue; ++ if(sscanf(line,"%[^:]: %[^/]/%[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4, w5) != 5) {
+ +- remove_control_chars(w1); +- remove_control_chars(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; +- } +- printf("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]); +- } +- printf("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]); +- } +- printf("Sub-network mask of the map-server: %d.%d.%d.%d.\n", subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); +- } ++ ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num);
++ continue;
+ } +- fclose(fp); + +- // sub-network check of the map-server +- { +- unsigned int a0, a1, a2, a3; +- unsigned char p[4]; +- sscanf(lan_map_ip, "%d.%d.%d.%d", &a0, &a1, &a2, &a3); +- p[0] = a0; p[1] = a1; p[2] = a2; p[3] = a3; +- printf("LAN test of LAN IP of the map-server: "); +- if (lan_ip_check(p) == 0) { +- printf("\033[1;31m***ERROR: LAN IP of the map-server doesn't belong to the specified Sub-network.\033[0m\n"); +- } ++ remove_control_chars((unsigned char *)w1);
++ remove_control_chars((unsigned char *)w2);
++ remove_control_chars((unsigned char *)w3);
++ remove_control_chars((unsigned char *)w4);
++ remove_control_chars((unsigned char *)w5);
++
++ if(strcmpi(w1, "subnet") == 0) {
++
++ subnet[subnet_count].subnet = inet_addr(w2);
++ subnet[subnet_count].mask = inet_addr(w3);
++ subnet[subnet_count].char_ip = inet_addr(w4);
++ subnet[subnet_count].map_ip = inet_addr(w5);
++
++ subnet_count++;
+ } + +- printf("---End reading of Lan Support configuration...\n"); ++ ShowStatus("Information about %d subnetworks readen.\n", subnet_count);
++ }
+ ++ fclose(fp);
+ return 0; + } + +@@ -2944,10 +3843,11 @@ + FILE *fp = fopen(cfgName, "r"); + + if (fp == NULL) { +- printf("Configuration file not found: %s.\n", cfgName); ++ 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; +@@ -2956,86 +3856,122 @@ + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + +- remove_control_chars(w1); +- remove_control_chars(w2); +- if (strcmpi(w1, "userid") == 0) { +- memcpy(userid, w2, 24); ++ 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) {
++ strncpy(userid, w2, 24);
+ } else if (strcmpi(w1, "passwd") == 0) { +- memcpy(passwd, w2, 24); ++ strncpy(passwd, w2, 24);
+ } else if (strcmpi(w1, "server_name") == 0) { + memcpy(server_name, w2, sizeof(server_name)); + server_name[sizeof(server_name) - 1] = '\0'; +- printf("%s server has been intialized\n", w2); ++ 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) { +- 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]); ++ 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) { +- printf("Character server IP address : %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); ++ 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 +- memcpy(start_point.map, map, 16); ++ 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_zeny = atoi(w2); ++ start_weapon = atoi(w2);
+ if (start_weapon < 0) + start_weapon = 0; + } else if (strcmpi(w1, "start_armor") == 0) { +- start_zeny = atoi(w2); ++ 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[24] = 0; ++ 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) { +@@ -3061,52 +3997,130 @@ + online_refresh_html = atoi(w2); + if (online_refresh_html < 1) + online_refresh_html = 1; +- } else if(strcmpi(w1,"anti_freeze_enable")==0){ +- anti_freeze_enable = config_switch(w2); +- } else if (strcmpi(w1, "anti_freeze_interval") == 0) { +- ANTI_FREEZE_INTERVAL = atoi(w2); +- if (ANTI_FREEZE_INTERVAL < 5) +- ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds ++ } 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, "fame_list_alchemist") == 0) {
++ fame_list_size_chemist = atoi(w2);
++ if (fame_list_size_chemist > MAX_FAME_LIST) {
++ ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST);
++ fame_list_size_chemist = MAX_FAME_LIST;
++ }
++ } else if (strcmpi(w1, "fame_list_blacksmith") == 0) {
++ fame_list_size_smith = atoi(w2);
++ if (fame_list_size_smith > MAX_FAME_LIST) {
++ ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST);
++ fame_list_size_smith = MAX_FAME_LIST;
++ }
++ } else if (strcmpi(w1, "fame_list_taekwon") == 0) {
++ fame_list_size_taekwon = atoi(w2);
++ if (fame_list_size_taekwon > MAX_FAME_LIST) {
++ ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST);
++ fame_list_size_taekwon = MAX_FAME_LIST;
++ }
+ } 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) { +- int i; +- ++ ShowStatus("Terminating server.\n");
+ // write online players files with no player +- for(i = 0; i < char_num; i++) +- online_chars[i] = -1; ++ online_char_db->clear(online_char_db, NULL); //clean the db...
+ create_online_files(); +- free(online_chars); ++ online_char_db->destroy(online_char_db, NULL); //dispose the db...
+ + mmo_char_sync(); + inter_save(); ++ set_all_offline();
+ +- if (gm_account != NULL) +- free(gm_account); ++ if(gm_account) aFree(gm_account);
++ if(char_dat) aFree(char_dat);
+ +- free(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]);
++ char_lan_config_read((argc > 3) ? argv[3] : LOGIN_LAN_CONF_NAME);
++
++ if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) {
++ ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n");
++ ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n");
++ ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n");
++ }
++
+ // 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); + +- char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); +- lan_config_read((argc > 1) ? argv[1] : LOGIN_LAN_CONF_NAME); ++ 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); +@@ -3116,34 +4130,45 @@ + server_fd[i] = -1; + } + +- mmo_char_init(); ++ 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_termfunc(do_final); + set_defaultparse(parse_char); + +- 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(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");
+ +- i = add_timer_interval(gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000); +- i = add_timer_interval(gettick() + 1000, send_users_tologin, 0, 0, 5 * 1000); +- i = add_timer_interval(gettick() + autosave_interval, mmo_char_sync_timer, 0, 0, autosave_interval); +- +- if(anti_freeze_enable > 0) { +- add_timer_func_list(map_anti_freeze_system, "map_anti_freeze_system"); +- i = add_timer_interval(gettick() + 1000, map_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); // checks every X seconds user specifies ++ 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); + +- printf("The char-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", 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/int_guild.c b/src/char/int_guild.c new file mode 100644 index 0000000..665e017 --- /dev/null +++ b/src/char/int_guild.c @@ -0,0 +1,1441 @@ +// $Id: int_guild.c,v 1.2 2004/09/25 19:36:53 Akitasha Exp $ +#include "inter.h" +#include "int_guild.h" +#include "int_storage.h" +#include "mmo.h" +#include "char.h" +#include "socket.h" +#include "db.h" +#include "lock.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +char guild_txt[1024] = "save/guild.txt"; +char castle_txt[1024] = "save/castle.txt"; + +static struct dbt *guild_db; +static struct dbt *castle_db; + +static int guild_newid = 10000; + +static int guild_exp[100]; + +int mapif_parse_GuildLeave(int fd, int guild_id, int account_id, int char_id, int flag, const char *mes); +int mapif_guild_broken(int guild_id, int flag); +int guild_check_empty(struct guild *g); +int guild_calcinfo(struct guild *g); +int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len); +int mapif_guild_info(int fd, struct guild *g); +int guild_break_sub(void *key, void *data, va_list ap); + +// ギルドデータの文字列への変換 +int inter_guild_tostr(char *str, struct guild *g) { + int i, c, len; + + // 基本データ + len = sprintf(str, "%d\t%s\t%s\t%d,%d,%d,%d,%d\t%s#\t%s#\t", + g->guild_id, g->name, g->master, + g->guild_lv, g->max_member, g->exp, g->skill_point, g->castle_id, + g->mes1, g->mes2); + // メンバー + for(i = 0; i < g->max_member; i++) { + struct guild_member *m = &g->member[i]; + len += sprintf(str + len, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\t%s\t", + m->account_id, m->char_id, + m->hair, m->hair_color, m->gender, + m->class, m->lv, m->exp, m->exp_payper, m->position, + ((m->account_id > 0) ? m->name : "-")); + } + // 役職 + for(i = 0; i < MAX_GUILDPOSITION; i++) { + struct guild_position *p = &g->position[i]; + len += sprintf(str + len, "%d,%d\t%s#\t", p->mode, p->exp_mode, p->name); + } + // エンブレム + len += sprintf(str + len, "%d,%d,", g->emblem_len, g->emblem_id); + for(i = 0; i < g->emblem_len; i++) { + len += sprintf(str + len, "%02x", (unsigned char)(g->emblem_data[i])); + } + len += sprintf(str + len, "$\t"); + // 同盟リスト + c = 0; + for(i = 0; i < MAX_GUILDALLIANCE; i++) + if (g->alliance[i].guild_id > 0) + c++; + len += sprintf(str + len, "%d\t", c); + for(i = 0; i < MAX_GUILDALLIANCE; i++) { + struct guild_alliance *a = &g->alliance[i]; + if (a->guild_id > 0) + len += sprintf(str + len, "%d,%d\t%s\t", a->guild_id, a->opposition, a->name); + } + // 追放リスト + c = 0; + for(i = 0; i < MAX_GUILDEXPLUSION; i++) + if (g->explusion[i].account_id > 0) + c++; + len += sprintf(str + len, "%d\t", c); + for(i = 0; i < MAX_GUILDEXPLUSION; i++) { + struct guild_explusion *e = &g->explusion[i]; + if (e->account_id > 0) + len += sprintf(str + len, "%d,%d,%d,%d\t%s\t%s\t%s#\t", + e->account_id, e->rsv1, e->rsv2, e->rsv3, + e->name, e->acc, e->mes ); + } + // ギルドスキル + for(i = 0; i < MAX_GUILDSKILL; i++) { + len += sprintf(str + len, "%d,%d ", g->skill[i].id, g->skill[i].lv); + } + len += sprintf(str + len, "\t"); + + return 0; +} + +// ギルドデータの文字列からの変換 +int inter_guild_fromstr(char *str, struct guild *g) { + int i, j, c; + int tmp_int[16]; + char tmp_str[4][256]; + char tmp_str2[4096]; + char *pstr; + + // 基本データ + memset(g, 0, sizeof(struct guild)); + if (sscanf(str, "%d\t%[^\t]\t%[^\t]\t%d,%d,%d,%d,%d\t%[^\t]\t%[^\t]\t", &tmp_int[0], + tmp_str[0], tmp_str[1], + &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], + tmp_str[2], tmp_str[3]) < 8) + return 1; + + g->guild_id = tmp_int[0]; + g->guild_lv = tmp_int[1]; + g->max_member = tmp_int[2]; + g->exp = tmp_int[3]; + g->skill_point = tmp_int[4]; + g->castle_id = tmp_int[5]; + memcpy(g->name, tmp_str[0], 24); + memcpy(g->master, tmp_str[1], 24); + memcpy(g->mes1, tmp_str[2], 60); + memcpy(g->mes2, tmp_str[3], 120); + g->mes1[strlen(g->mes1)-1] = 0; + g->mes2[strlen(g->mes2)-1] = 0; + + for(j = 0; j < 6 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); +// printf("GuildBaseInfo OK\n"); + + // メンバー + for(i = 0; i < g->max_member; i++) { + struct guild_member *m = &g->member[i]; + if (sscanf(str + 1, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\t%[^\t]\t", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], + &tmp_int[5], &tmp_int[6], &tmp_int[7], &tmp_int[8], &tmp_int[9], + tmp_str[0]) < 11) + return 1; + m->account_id = tmp_int[0]; + m->char_id = 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], 24); + + for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); + } +// printf("GuildMemberInfo OK\n"); + // 役職 + i = 0; + while (sscanf(str+1, "%d,%d%n", &tmp_int[0], &tmp_int[1], &j) == 2 && str[1+j] == '\t') { + struct guild_position *p = &g->position[i]; + if (sscanf(str+1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str[0]) < 3) + return 1; + p->mode = tmp_int[0]; + p->exp_mode = tmp_int[1]; + tmp_str[0][strlen(tmp_str[0])-1] = 0; + memcpy(p->name, tmp_str[0], 24); + + for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr(str+1, '\t'); + i++; + } +// printf("GuildPositionInfo OK\n"); + // エンブレム + tmp_int[1] = 0; + if (sscanf(str + 1, "%d,%d,%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str2)< 3 && + sscanf(str + 1, "%d,%[^\t]\t", &tmp_int[0], tmp_str2) < 2) + return 1; + g->emblem_len = tmp_int[0]; + g->emblem_id = tmp_int[1]; + for(i = 0, pstr = tmp_str2; i < g->emblem_len; i++, pstr += 2) { + int c1 = pstr[0], c2 = pstr[1], x1 = 0, x2 = 0; + if (c1 >= '0' && c1 <= '9') x1 = c1 - '0'; + if (c1 >= 'a' && c1 <= 'f') x1 = c1 - 'a' + 10; + if (c1 >= 'A' && c1 <= 'F') x1 = c1 - 'A' + 10; + if (c2 >= '0' && c2 <= '9') x2 = c2 - '0'; + if (c2 >= 'a' && c2 <= 'f') x2 = c2 - 'a' + 10; + if (c2 >= 'A' && c2 <= 'F') x2 = c2 - 'A' + 10; + g->emblem_data[i] = (x1<<4) | x2; + } +// printf("GuildEmblemInfo OK\n"); + str=strchr(str + 1, '\t'); // 位置スキップ + + // 同盟リスト + if (sscanf(str + 1, "%d\t", &c) < 1) + return 1; + str = strchr(str + 1, '\t'); // 位置スキップ + for(i = 0; i < c; i++) { + struct guild_alliance *a = &g->alliance[i]; + if (sscanf(str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str[0]) < 3) + return 1; + a->guild_id = tmp_int[0]; + a->opposition = tmp_int[1]; + memcpy(a->name, tmp_str[0], 24); + + for(j = 0; j < 2 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); + } +// printf("GuildAllianceInfo OK\n"); + // 追放リスト + if (sscanf(str+1, "%d\t", &c) < 1) + return 1; + str = strchr(str + 1, '\t'); // 位置スキップ + for(i = 0; i < c; i++) { + struct guild_explusion *e = &g->explusion[i]; + if (sscanf(str + 1, "%d,%d,%d,%d\t%[^\t]\t%[^\t]\t%[^\t]\t", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + tmp_str[0], tmp_str[1], tmp_str[2]) < 6) + return 1; + e->account_id = tmp_int[0]; + e->rsv1 = tmp_int[1]; + e->rsv2 = tmp_int[2]; + e->rsv3 = tmp_int[3]; + memcpy(e->name, tmp_str[0], 24); + memcpy(e->acc, tmp_str[1], 24); + tmp_str[2][strlen(tmp_str[2])-1] = 0; + memcpy(e->mes, tmp_str[2], 40); + + for(j = 0; j < 4 && str != NULL; j++) // 位置スキップ + str = strchr(str + 1, '\t'); + } +// printf("GuildExplusionInfo OK\n"); + // ギルドスキル + for(i = 0; i < MAX_GUILDSKILL; i++) { + if (sscanf(str+1,"%d,%d ", &tmp_int[0], &tmp_int[1]) < 2) + break; + g->skill[i].id = tmp_int[0]; + g->skill[i].lv = tmp_int[1]; + str = strchr(str + 1, ' '); + } + str = strchr(str + 1, '\t'); +// printf("GuildSkillInfo OK\n"); + + return 0; +} + +// ギルド城データの文字列への変換 +int inter_guildcastle_tostr(char *str, struct guild_castle *gc) { + int len; + + len = sprintf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", // added Guardian HP [Valaris] + gc->castle_id, gc->guild_id, gc->economy, gc->defense, gc->triggerE, + gc->triggerD, gc->nextTime, gc->payTime, gc->createTime, gc->visibleC, + gc->visibleG0, gc->visibleG1, gc->visibleG2, gc->visibleG3, gc->visibleG4, + gc->visibleG5, gc->visibleG6, gc->visibleG7, gc->Ghp0, gc->Ghp1, gc->Ghp2, + gc->Ghp3, gc->Ghp4, gc->Ghp5, gc->Ghp6, gc->Ghp7); + + return 0; +} + +// ギルド城データの文字列からの変換 +int inter_guildcastle_fromstr(char *str, struct guild_castle *gc) { + int tmp_int[26]; + + memset(gc, 0, sizeof(struct guild_castle)); + // new structure of guild castle + if (sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], + &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], &tmp_int[24], &tmp_int[25]) == 26) { + gc->castle_id = tmp_int[0]; + gc->guild_id = tmp_int[1]; + gc->economy = tmp_int[2]; + gc->defense = tmp_int[3]; + gc->triggerE = tmp_int[4]; + gc->triggerD = tmp_int[5]; + gc->nextTime = tmp_int[6]; + gc->payTime = tmp_int[7]; + gc->createTime = tmp_int[8]; + gc->visibleC = tmp_int[9]; + gc->visibleG0 = tmp_int[10]; + gc->visibleG1 = tmp_int[11]; + gc->visibleG2 = tmp_int[12]; + gc->visibleG3 = tmp_int[13]; + gc->visibleG4 = tmp_int[14]; + gc->visibleG5 = tmp_int[15]; + gc->visibleG6 = tmp_int[16]; + gc->visibleG7 = tmp_int[17]; + gc->Ghp0 = tmp_int[18]; + gc->Ghp1 = tmp_int[19]; + gc->Ghp2 = tmp_int[20]; + gc->Ghp3 = tmp_int[21]; + gc->Ghp4 = tmp_int[22]; + gc->Ghp5 = tmp_int[23]; + gc->Ghp6 = tmp_int[24]; + gc->Ghp7 = tmp_int[25]; // end additions [Valaris] + // old structure of guild castle + } else if (sscanf(str, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], &tmp_int[13], + &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17]) == 18) { + gc->castle_id = tmp_int[0]; + gc->guild_id = tmp_int[1]; + gc->economy = tmp_int[2]; + gc->defense = tmp_int[3]; + gc->triggerE = tmp_int[4]; + gc->triggerD = tmp_int[5]; + gc->nextTime = tmp_int[6]; + gc->payTime = tmp_int[7]; + gc->createTime = tmp_int[8]; + gc->visibleC = tmp_int[9]; + gc->visibleG0 = tmp_int[10]; + gc->visibleG1 = tmp_int[11]; + gc->visibleG2 = tmp_int[12]; + gc->visibleG3 = tmp_int[13]; + gc->visibleG4 = tmp_int[14]; + gc->visibleG5 = tmp_int[15]; + gc->visibleG6 = tmp_int[16]; + gc->visibleG7 = tmp_int[17]; + if (gc->visibleG0 == 1) + gc->Ghp0 = 15670 + 2000 * gc->defense; + else + gc->Ghp0 = 0; + if (gc->visibleG1 == 1) + gc->Ghp1 = 15670 + 2000 * gc->defense; + else + gc->Ghp1 = 0; + if (gc->visibleG2 == 1) + gc->Ghp2 = 15670 + 2000 * gc->defense; + else + gc->Ghp2 = 0; + if (gc->visibleG3 == 1) + gc->Ghp3 = 30214 + 2000 * gc->defense; + else + gc->Ghp3 = 0; + if (gc->visibleG4 == 1) + gc->Ghp4 = 30214 + 2000 * gc->defense; + else + gc->Ghp4 = 0; + if (gc->visibleG5 == 1) + gc->Ghp5 = 28634 + 2000 * gc->defense; + else + gc->Ghp5 = 0; + if (gc->visibleG6 == 1) + gc->Ghp6 = 28634 + 2000 * gc->defense; + else + gc->Ghp6 = 0; + if (gc->visibleG7 == 1) + gc->Ghp7 = 28634 + 2000 * gc->defense; + else + gc->Ghp7 = 0; + } else { + return 1; + } + + return 0; +} + +// ギルド関連データベース読み込み +int inter_guild_readdb() { + int i; + FILE *fp; + char line[1024]; + + fp = fopen("db/exp_guild.txt", "r"); + if (fp == NULL) { + printf("can't read db/exp_guild.txt\n"); + return 1; + } + i = 0; + while(fgets(line, sizeof(line)-1, fp) && i < 100){ + if (line[0] == '/' && line[1] == '/') + continue; + guild_exp[i] = atoi(line); + i++; + } + fclose(fp); + + return 0; +} + +// ギルドデータの読み込み +int inter_guild_init() { + char line[16384]; + struct guild *g; + struct guild_castle *gc; + FILE *fp; + int i, j, c = 0; + + inter_guild_readdb(); + + guild_db = numdb_init(); + castle_db = numdb_init(); + + if ((fp = fopen(guild_txt,"r")) == NULL) + return 1; + while(fgets(line, sizeof(line)-1, fp)) { + j = 0; + if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && guild_newid <= i) { + guild_newid = i; + continue; + } + + g = calloc(sizeof(struct guild), 1); + if(g == NULL){ + printf("int_guild: out of memory!\n"); + exit(0); + } + memset(g, 0, sizeof(struct guild)); + if (inter_guild_fromstr(line, g) == 0 && g->guild_id > 0) { + if (g->guild_id >= guild_newid) + guild_newid = g->guild_id + 1; + numdb_insert(guild_db, g->guild_id, g); + guild_check_empty(g); + guild_calcinfo(g); + } else { + printf("int_guild: broken data [%s] line %d\n", guild_txt, c); + free(g); + } + c++; + } + fclose(fp); +// printf("int_guild: %s read done (%d guilds)\n", guild_txt, c); + + c = 0;//カウンタ初期化 + + if ((fp = fopen(castle_txt, "r")) == NULL) { + return 1; + } + + while(fgets(line, sizeof(line)-1, fp)) { + gc = calloc(sizeof(struct guild_castle), 1); + if(gc == NULL){ + printf("int_guild: out of memory!\n"); + exit(0); + } + memset(gc, 0, sizeof(struct guild_castle)); + if (inter_guildcastle_fromstr(line, gc) == 0) { + numdb_insert(castle_db, gc->castle_id, gc); + } else { + printf("int_guild: broken data [%s] line %d\n", castle_txt, c); + free(gc); + } + c++; + } + + if (!c) { + printf(" %s - making Default Data...\n", castle_txt); + //デフォルトデータを作成 + for(i = 0; i < MAX_GUILDCASTLE; i++) { + gc = calloc(sizeof(struct guild_castle), 1); + if (gc == NULL) { + printf("int_guild: out of memory!\n"); + exit(0); + } + memset(gc, 0, sizeof(struct guild_castle)); + gc->castle_id = i; + gc->guild_id = 0; + gc->economy = 0; + gc->defense = 0; + gc->triggerE = 0; + gc->triggerD = 0; + gc->nextTime = 0; + gc->payTime = 0; + gc->createTime = 0; + gc->visibleC = 0; + gc->visibleG0 = 0; + gc->visibleG1 = 0; + gc->visibleG2 = 0; + gc->visibleG3 = 0; + gc->visibleG4 = 0; + gc->visibleG5 = 0; + gc->visibleG6 = 0; + gc->visibleG7 = 0; + gc->Ghp0 = 0; // guardian HP [Valaris] + gc->Ghp1 = 0; + gc->Ghp2 = 0; + gc->Ghp3 = 0; + gc->Ghp4 = 0; + gc->Ghp5 = 0; + gc->Ghp6 = 0; + gc->Ghp7 = 0; // end additions [Valaris] + numdb_insert(castle_db, gc->castle_id, gc); + } + printf(" %s - making done\n",castle_txt); + return 0; + } + + fclose(fp); + + return 0; +} + +struct guild *inter_guild_search(int guild_id) { + struct guild *g; + + g=numdb_search(guild_db, guild_id); + + return g; +} + +// ギルドデータのセーブ用 +int inter_guild_save_sub(void *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; +} + +// ギルド城データのセーブ用 +int inter_castle_save_sub(void *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; +} + +// ギルドデータのセーブ +int inter_guild_save() { + FILE *fp; + int lock; + + if ((fp = lock_fopen(guild_txt, &lock)) == NULL) { + printf("int_guild: cant write [%s] !!! data is lost !!!\n", guild_txt); + return 1; + } + numdb_foreach(guild_db, inter_guild_save_sub, fp); +// fprintf(fp, "%d\t%%newid%%\n", guild_newid); + lock_fclose(fp, guild_txt, &lock); +// printf("int_guild: %s saved.\n", guild_txt); + + if ((fp = lock_fopen(castle_txt,&lock)) == NULL) { + printf("int_guild: cant write [%s] !!! data is lost !!!\n", castle_txt); + return 1; + } + numdb_foreach(castle_db, inter_castle_save_sub, fp); + lock_fclose(fp, castle_txt, &lock); + + return 0; +} + +// ギルド名検索用 +int search_guildname_sub(void *key, void *data, va_list ap) { + struct guild *g = (struct guild *)data, **dst; + char *str; + + str = va_arg(ap, char *); + dst = va_arg(ap, struct guild **); + if (strcmpi(g->name, str) == 0) + *dst = g; + return 0; +} + +// ギルド名検索 +struct guild* search_guildname(char *str) { + struct guild *g = NULL; + numdb_foreach(guild_db, search_guildname_sub, str, &g); + return g; +} + +// ギルドが空かどうかチェック +int guild_check_empty(struct guild *g) { + int i; + + for(i = 0; i < g->max_member; i++) { + if (g->member[i].account_id > 0) { + return 0; + } + } + // 誰もいないので解散 + numdb_foreach(guild_db, guild_break_sub, g->guild_id); + numdb_erase(guild_db, g->guild_id); + inter_guild_storage_delete(g->guild_id); + mapif_guild_broken(g->guild_id, 0); + free(g); + + return 1; +} + +// キャラの競合がないかチェック用 +int guild_check_conflict_sub(void *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) { + // 別のギルドに偽の所属データがあるので脱退 + printf("int_guild: guild conflict! %d,%d %d!=%d\n", account_id, char_id, guild_id, g->guild_id); + mapif_parse_GuildLeave(-1, g->guild_id, account_id, char_id, 0, "**データ競合**"); + } + } + + return 0; +} +// キャラの競合がないかチェック +int guild_check_conflict(int guild_id, int account_id, int char_id) { + numdb_foreach(guild_db, guild_check_conflict_sub, guild_id, account_id, char_id); + + return 0; +} + +int guild_nextexp(int level) { + if (level < 100) + return guild_exp[level-1]; + + return 0; +} + +// ギルドスキルがあるか確認 +int guild_checkskill(struct guild *g, int id){ + return g->skill[id-10000].lv; +} + +// ギルドの情報の再計算 +int guild_calcinfo(struct guild *g) { + int i, c, nextexp; + struct guild before = *g; + + // スキルIDの設定 + for(i = 0; i < 5; i++) + g->skill[i].id = i + 10000; + + // ギルドレベル + if (g->guild_lv <= 0) + g->guild_lv = 1; + nextexp = guild_nextexp(g->guild_lv); + if (nextexp > 0) { + while(g->exp >= nextexp) { // レベルアップ処理 + g->exp -= nextexp; + g->guild_lv++; + g->skill_point++; + nextexp = guild_nextexp(g->guild_lv); + } + } + + // ギルドの次の経験値 + g->next_exp = guild_nextexp(g->guild_lv); + + // メンバ上限(ギルド拡張適用) + g->max_member = 16 + guild_checkskill(g, 10004) * 2; + + // 平均レベルとオンライン人数 + g->average_lv = 0; + g->connect_member = 0; + c = 0; + for(i = 0; i < g->max_member; i++) { + if (g->member[i].account_id > 0) { + g->average_lv += g->member[i].lv; + c++; + if (g->member[i].online > 0) + g->connect_member++; + } + } + g->average_lv /= c; + + // 全データを送る必要がありそう + if (g->max_member != before.max_member || + g->guild_lv != before.guild_lv || + g->skill_point != before.skill_point) { + mapif_guild_info(-1, g); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// ギルド作成可否 +int mapif_guild_created(int fd, int account_id, struct guild *g) { + WFIFOW(fd,0) = 0x3830; + WFIFOL(fd,2) = account_id; + if (g != NULL) { + WFIFOL(fd,6) = g->guild_id; + printf("int_guild: created! %d %s\n", g->guild_id, g->name); + }else{ + WFIFOL(fd,6) = 0; + } + WFIFOSET(fd,10); + return 0; +} + +// ギルド情報見つからず +int mapif_guild_noinfo(int fd, int guild_id) { + WFIFOW(fd,0) = 0x3831; + WFIFOW(fd,2) = 8; + WFIFOL(fd,4) = guild_id; + WFIFOSET(fd,8); + printf("int_guild: info not found %d\n", guild_id); + + return 0; +} + +// ギルド情報まとめ送り +int mapif_guild_info(int fd, struct guild *g) { + unsigned char buf[4 + sizeof(struct guild)]; + + WBUFW(buf,0) = 0x3831; + memcpy(buf + 4, g, sizeof(struct guild)); + WBUFW(buf,2) = 4 + sizeof(struct guild); +// printf("int_guild: sizeof(guild)=%d\n", sizeof(struct guild)); + if (fd < 0) + mapif_sendall(buf, WBUFW(buf,2)); + else + mapif_send(fd, buf, WBUFW(buf,2)); +// printf("int_guild: info %d %s\n", p->guild_id, p->name); + + return 0; +} + +// メンバ追加可否 +int mapif_guild_memberadded(int fd, int guild_id, int account_id, int char_id, int flag) { + WFIFOW(fd,0) = 0x3832; + WFIFOL(fd,2) = guild_id; + WFIFOL(fd,6) = account_id; + WFIFOL(fd,10) = char_id; + WFIFOB(fd,14) = flag; + WFIFOSET(fd, 15); + + return 0; +} + +// 脱退/追放通知 +int mapif_guild_leaved(int guild_id, int account_id, int char_id, int flag, const char *name, const char *mes) { + unsigned char buf[79]; + + WBUFW(buf, 0) = 0x3834; + WBUFL(buf, 2) = guild_id; + WBUFL(buf, 6) = account_id; + WBUFL(buf,10) = char_id; + WBUFB(buf,14) = flag; + memcpy(WBUFP(buf,15), mes, 40); + memcpy(WBUFP(buf,55), name, 24); + mapif_sendall(buf, 79); + printf("int_guild: guild leaved %d %d %s %s\n", guild_id, account_id, name, mes); + + return 0; +} + +// オンライン状態とLv更新通知 +int mapif_guild_memberinfoshort(struct guild *g, int idx) { + unsigned char buf[19]; + + WBUFW(buf, 0) = 0x3835; + WBUFL(buf, 2) = g->guild_id; + WBUFL(buf, 6) = g->member[idx].account_id; + WBUFL(buf,10) = g->member[idx].char_id; + WBUFB(buf,14) = g->member[idx].online; + WBUFW(buf,15) = g->member[idx].lv; + WBUFW(buf,17) = g->member[idx].class; + mapif_sendall(buf, 19); + return 0; +} + +// 解散通知 +int mapif_guild_broken(int guild_id, int flag) { + unsigned char buf[7]; + + WBUFW(buf,0) = 0x3836; + WBUFL(buf,2) = guild_id; + WBUFB(buf,6) = flag; + mapif_sendall(buf, 7); + printf("int_guild: broken %d\n", guild_id); + + return 0; +} + +// ギルド内発言 +int mapif_guild_message(int guild_id, int account_id, char *mes, int len) { + unsigned char buf[len+12]; + + WBUFW(buf,0) = 0x3837; + WBUFW(buf,2) = len + 12; + WBUFL(buf,4) = guild_id; + WBUFL(buf,8) = account_id; + memcpy(WBUFP(buf,12), mes, len); + mapif_sendall(buf, len + 12); + + return 0; +} + +// ギルド基本情報変更通知 +int mapif_guild_basicinfochanged(int guild_id, int type, const void *data, int len) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x3839; + WBUFW(buf,2) = len+10; + WBUFL(buf,4) = guild_id; + WBUFW(buf,8) = type; + memcpy(WBUFP(buf,10),data,len); + mapif_sendall(buf,len+10); + return 0; +} + +// ギルドメンバ情報変更通知 +int mapif_guild_memberinfochanged(int guild_id, int account_id, int char_id, int type, const void *data, int len) { + unsigned char buf[len + 18]; + + WBUFW(buf, 0) = 0x383a; + WBUFW(buf, 2) = len + 18; + WBUFL(buf, 4) = guild_id; + WBUFL(buf, 8) = account_id; + WBUFL(buf,12) = char_id; + WBUFW(buf,16) = type; + memcpy(WBUFP(buf,18), data, len); + mapif_sendall(buf,len+18); + + return 0; +} + +// ギルドスキルアップ通知 +int mapif_guild_skillupack(int guild_id, int skill_num, int account_id) { + unsigned char buf[14]; + + WBUFW(buf, 0) = 0x383c; + WBUFL(buf, 2) = guild_id; + WBUFL(buf, 6) = skill_num; + WBUFL(buf,10) = account_id; + mapif_sendall(buf, 14); + + return 0; +} + +// ギルド同盟/敵対通知 +int mapif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag, const char *name1, const char *name2) { + unsigned char buf[67]; + + WBUFW(buf, 0) = 0x383d; + WBUFL(buf, 2) = guild_id1; + WBUFL(buf, 6) = guild_id2; + WBUFL(buf,10) = account_id1; + WBUFL(buf,14) = account_id2; + WBUFB(buf,18) = flag; + memcpy(WBUFP(buf,19), name1, 24); + memcpy(WBUFP(buf,43), name2, 24); + mapif_sendall(buf, 67); + + return 0; +} + +// ギルド役職変更通知 +int mapif_guild_position(struct guild *g, int idx) { + unsigned char buf[sizeof(struct guild_position) + 12]; + + WBUFW(buf,0) = 0x383b; + WBUFW(buf,2) = sizeof(struct guild_position) + 12; + WBUFL(buf,4) = g->guild_id; + WBUFL(buf,8) = idx; + memcpy(WBUFP(buf,12), &g->position[idx], sizeof(struct guild_position)); + mapif_sendall(buf, WBUFW(buf,2)); + + return 0; +} + +// ギルド告知変更通知 +int mapif_guild_notice(struct guild *g) { + unsigned char buf[186]; + + WBUFW(buf,0) = 0x383e; + WBUFL(buf,2) = g->guild_id; + memcpy(WBUFP(buf,6), g->mes1, 60); + memcpy(WBUFP(buf,66), g->mes2, 120); + mapif_sendall(buf, 186); + + return 0; +} + +// ギルドエンブレム変更通知 +int mapif_guild_emblem(struct guild *g) { + unsigned char buf[2048]; + + WBUFW(buf,0) = 0x383f; + WBUFW(buf,2) = g->emblem_len + 12; + WBUFL(buf,4) = g->guild_id; + WBUFL(buf,8) = g->emblem_id; + memcpy(WBUFP(buf,12), g->emblem_data, g->emblem_len); + mapif_sendall(buf, WBUFW(buf,2)); + + return 0; +} + +int mapif_guild_castle_dataload(int castle_id, int index, int value) { + unsigned char buf[9]; + + WBUFW(buf,0) = 0x3840; + WBUFW(buf,2) = castle_id; + WBUFB(buf,4) = index; + WBUFL(buf,5) = value; + mapif_sendall(buf,9); + + return 0; +} + +int mapif_guild_castle_datasave(int castle_id, int index, int value) { + unsigned char buf[9]; + + WBUFW(buf,0) = 0x3841; + WBUFW(buf,2) = castle_id; + WBUFB(buf,4) = index; + WBUFL(buf,5) = value; + mapif_sendall(buf,9); + + return 0; +} + +int mapif_guild_castle_alldataload_sub(void *key, void *data, va_list ap) { + int fd = va_arg(ap, int); + int *p = va_arg(ap, int*); + + memcpy(WFIFOP(fd,*p), (struct guild_castle*)data, sizeof(struct guild_castle)); + (*p) += sizeof(struct guild_castle); + + return 0; +} + +int mapif_guild_castle_alldataload(int fd) { + int len = 4; + + WFIFOW(fd,0) = 0x3842; + numdb_foreach(castle_db, mapif_guild_castle_alldataload_sub, fd, &len); + WFIFOW(fd,2) = len; + WFIFOSET(fd, len); + + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + +// ギルド作成要求 +int mapif_parse_CreateGuild(int fd, int account_id, char *name, struct guild_member *master) { + struct guild *g; + int i; + + for(i = 0; i < 24 && name[i]; i++) { + if (!(name[i] & 0xe0) || name[i] == 0x7f) { + printf("int_guild: illeagal guild name [%s]\n", name); + mapif_guild_created(fd, account_id, NULL); + return 0; + } + } + + if ((g = search_guildname(name)) != NULL) { + printf("int_guild: same name guild exists [%s]\n", name); + mapif_guild_created(fd, account_id, NULL); + return 0; + } + g = calloc(sizeof(struct guild), 1); + if (g == NULL) { + printf("int_guild: CreateGuild: out of memory !\n"); + mapif_guild_created(fd, account_id, NULL); + exit(0); + } + memset(g, 0, sizeof(struct guild)); + g->guild_id = guild_newid++; + memcpy(g->name, name, 24); + memcpy(g->master, master->name, 24); + memcpy(&g->member[0], master, sizeof(struct guild_member)); + + g->position[0].mode = 0x11; + strcpy(g->position[ 0].name, "GuildMaster"); + strcpy(g->position[MAX_GUILDPOSITION-1].name, "Newbie"); + for(i = 1; i < MAX_GUILDPOSITION-1; i++) + sprintf(g->position[i].name, "Position %d", i + 1); + + // ここでギルド情報計算が必要と思われる + g->max_member = 16; + g->average_lv = master->lv; + for(i = 0; i < 5; i++) + g->skill[i].id = i + 10000; + + numdb_insert(guild_db, g->guild_id, g); + + mapif_guild_created(fd, account_id, g); + mapif_guild_info(fd, g); + + inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE, + name, g->guild_id, master->name, master->account_id); + + return 0; +} + +// ギルド情報要求 +int mapif_parse_GuildInfo(int fd, int guild_id) { + struct guild *g; + + g = numdb_search(guild_db, guild_id); + if (g != NULL){ + guild_calcinfo(g); + mapif_guild_info(fd, g); + } else + mapif_guild_noinfo(fd, guild_id); + + return 0; +} + +// ギルドメンバ追加要求 +int mapif_parse_GuildAddMember(int fd, int guild_id, struct guild_member *m) { + struct guild *g; + int i; + + g = numdb_search(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; +} + +// ギルド脱退/追放要求 +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 = numdb_search(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) { // 追放の場合追放リストに入れる + for(j = 0; j < MAX_GUILDEXPLUSION; j++) { + if (g->explusion[j].account_id == 0) + break; + } + if (j == MAX_GUILDEXPLUSION) { // 一杯なので古いのを消す + for(j = 0; j < MAX_GUILDEXPLUSION - 1; j++) + g->explusion[j] = g->explusion[j+1]; + j = MAX_GUILDEXPLUSION - 1; + } + g->explusion[j].account_id = account_id; + memcpy(g->explusion[j].acc, "dummy", 24); + memcpy(g->explusion[j].name, g->member[i].name, 24); + memcpy(g->explusion[j].mes, mes, 40); + } + + mapif_guild_leaved(guild_id, account_id, char_id, flag, g->member[i].name, mes); +// printf("%d %d\n", i, (int)(&g->member[i])); +// printf("%d %s\n", i, (&g->member[i])->name); + memset(&g->member[i], 0, sizeof(struct guild_member)); + + if (guild_check_empty(g) == 0) + mapif_guild_info(-1,g);// まだ人がいるのでデータ送信 + + return 0; + } + } + } + return 0; +} + +// オンライン/Lv更新 +int mapif_parse_GuildChangeMemberInfoShort(int fd, int guild_id, int account_id, int char_id, int online, int lv, int class) { + struct guild *g; + int i, alv, c; + + g = numdb_search(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++; + } + // 平均レベル + g->average_lv = alv / c; + + return 0; +} + +// ギルド解散処理用(同盟/敵対を解除) +int guild_break_sub(void *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; +} + +// ギルド解散要求 +int mapif_parse_BreakGuild(int fd, int guild_id) { + struct guild *g; + + g = numdb_search(guild_db, guild_id); + if(g == NULL) + return 0; + + numdb_foreach(guild_db, guild_break_sub, guild_id); + numdb_erase(guild_db, guild_id); + inter_guild_storage_delete(guild_id); + mapif_guild_broken(guild_id, 0); + + inter_log("guild %s (id=%d) broken" RETCODE, g->name, guild_id); + free(g); + + return 0; +} + +// ギルドメッセージ送信 +int mapif_parse_GuildMessage(int fd, int guild_id, int account_id, char *mes, int len) { + return mapif_guild_message(guild_id, account_id, mes, len); +} + +// ギルド基本データ変更要求 +int mapif_parse_GuildBasicInfoChange(int fd, int guild_id, int type, const char *data, int len) { + struct guild *g; + short dw = *((short *)data); + + g = numdb_search(guild_db, guild_id); + if (g == NULL) + return 0; + + switch(type) { + case GBI_GUILDLV: + if (dw > 0 && g->guild_lv + dw <= 50) { + g->guild_lv+=dw; + g->skill_point+=dw; + } else if (dw < 0 && g->guild_lv + dw >= 1) + g->guild_lv += dw; + mapif_guild_info(-1, g); + return 0; + default: + printf("int_guild: GuildBasicInfoChange: Unknown type %d\n", type); + break; + } + mapif_guild_basicinfochanged(guild_id, type, data, len); + + return 0; +} + +// ギルドメンバデータ変更要求 +int mapif_parse_GuildMemberInfoChange(int fd, int guild_id, int account_id, int char_id, int type, const char *data, int len) { + int i; + struct guild *g; + + g = numdb_search(guild_db, guild_id); + if(g == NULL) + return 0; + + for(i = 0; i < g->max_member; i++) + if (g->member[i].account_id == account_id && g->member[i].char_id == char_id) + break; + if (i == g->max_member) { + printf("int_guild: GuildMemberChange: Not found %d,%d in %d[%s]\n", account_id, char_id, guild_id, g->name); + return 0; + } + switch(type) { + case GMI_POSITION: // 役職 + g->member[i].position = *((int *)data); + break; + case GMI_EXP: // EXP + { + int exp, oldexp = g->member[i].exp; + exp = g->member[i].exp = *((unsigned int *)data); + g->exp += (exp - oldexp); + guild_calcinfo(g); // Lvアップ判断 + mapif_guild_basicinfochanged(guild_id, GBI_EXP, &g->exp, 4); + } + break; + default: + printf("int_guild: GuildMemberInfoChange: Unknown type %d\n", type); + break; + } + mapif_guild_memberinfochanged(guild_id, account_id, char_id, type, data, len); + + return 0; +} + +// ギルド役職名変更要求 +int mapif_parse_GuildPosition(int fd, int guild_id, int idx, struct guild_position *p) { + struct guild *g = numdb_search(guild_db, guild_id); + + if (g == NULL || idx < 0 || idx >= MAX_GUILDPOSITION) { + return 0; + } + memcpy(&g->position[idx], p, sizeof(struct guild_position)); + mapif_guild_position(g, idx); + printf("int_guild: position changed %d\n", idx); + + return 0; +} + +// ギルドスキルアップ要求 +int mapif_parse_GuildSkillUp(int fd, int guild_id, int skill_num, int account_id) { + struct guild *g = numdb_search(guild_db, guild_id); + int idx = skill_num - 10000; + + if (g == NULL || skill_num < 10000) + return 0; + + if (g->skill_point > 0 && g->skill[idx].id > 0 && g->skill[idx].lv < 10) { + g->skill[idx].lv++; + g->skill_point--; + if (guild_calcinfo(g) == 0) + mapif_guild_info(-1, g); + mapif_guild_skillupack(guild_id, skill_num, account_id); + printf("int_guild: skill %d up\n", skill_num); + } + + return 0; +} + +// ギルド同盟要求 +int mapif_parse_GuildAlliance(int fd, int guild_id1, int guild_id2, int account_id1, int account_id2, int flag) { + struct guild *g[2]; + int j, i; + + g[0] = numdb_search(guild_db, guild_id1); + g[1] = numdb_search(guild_db, guild_id2); + if (g[0] == NULL || g[1] == NULL) + return 0; + + if (!(flag & 0x8)) { + for(i = 0; i < 2 - (flag & 1); i++) { + for(j = 0; j < MAX_GUILDALLIANCE; j++) + if (g[i]->alliance[j].guild_id == 0) { + g[i]->alliance[j].guild_id = g[1-i]->guild_id; + memcpy(g[i]->alliance[j].name, g[1-i]->name, 24); + g[i]->alliance[j].opposition = flag & 1; + break; + } + } + } else { // 関係解消 + for(i = 0; i < 2 - (flag & 1); i++) { + for(j = 0; j < MAX_GUILDALLIANCE; j++) + if (g[i]->alliance[j].guild_id == g[1-i]->guild_id && g[i]->alliance[j].opposition == (flag & 1)) { + g[i]->alliance[j].guild_id = 0; + break; + } + } + } + mapif_guild_alliance(guild_id1, guild_id2, account_id1, account_id2, flag, g[0]->name, g[1]->name); + + return 0; +} + +// ギルド告知変更要求 +int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char *mes2) { + struct guild *g; + + g = numdb_search(guild_db, guild_id); + if (g == NULL) + return 0; + memcpy(g->mes1, mes1, 60); + memcpy(g->mes2, mes2, 120); + + return mapif_guild_notice(g); +} + +// ギルドエンブレム変更要求 +int mapif_parse_GuildEmblem(int fd, int len, int guild_id, int dummy, const char *data) { + struct guild *g; + + g = numdb_search(guild_db, guild_id); + if (g == NULL) + return 0; + memcpy(g->emblem_data, data, len); + g->emblem_len = len; + g->emblem_id++; + + return mapif_guild_emblem(g); +} + +int mapif_parse_GuildCastleDataLoad(int fd, int castle_id, int index) { + struct guild_castle *gc = numdb_search(castle_db, castle_id); + + if (gc == NULL) { + return mapif_guild_castle_dataload(castle_id, 0, 0); + } + switch(index) { + case 1: return mapif_guild_castle_dataload(gc->castle_id, index, gc->guild_id); + case 2: return mapif_guild_castle_dataload(gc->castle_id, index, gc->economy); + case 3: return mapif_guild_castle_dataload(gc->castle_id, index, gc->defense); + case 4: return mapif_guild_castle_dataload(gc->castle_id, index, gc->triggerE); + case 5: return mapif_guild_castle_dataload(gc->castle_id, index, gc->triggerD); + case 6: return mapif_guild_castle_dataload(gc->castle_id, index, gc->nextTime); + case 7: return mapif_guild_castle_dataload(gc->castle_id, index, gc->payTime); + case 8: return mapif_guild_castle_dataload(gc->castle_id, index, gc->createTime); + case 9: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleC); + case 10: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG0); + case 11: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG1); + case 12: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG2); + case 13: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG3); + case 14: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG4); + case 15: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG5); + case 16: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG6); + case 17: return mapif_guild_castle_dataload(gc->castle_id, index, gc->visibleG7); + case 18: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp0); // guardian HP [Valaris] + case 19: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp1); + case 20: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp2); + case 21: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp3); + case 22: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp4); + case 23: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp5); + case 24: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp6); + case 25: return mapif_guild_castle_dataload(gc->castle_id, index, gc->Ghp7); // end additions [Valaris] + + default: + printf("mapif_parse_GuildCastleDataLoad ERROR!! (Not found index=%d)\n", index); + return 0; + } + + return 0; +} + +int mapif_parse_GuildCastleDataSave(int fd, int castle_id, int index, int value) { + struct guild_castle *gc=numdb_search(castle_db, castle_id); + + if (gc == NULL) { + return mapif_guild_castle_datasave(castle_id, index, value); + } + switch(index) { + case 1: + if (gc->guild_id != value) { + int gid = (value) ? value : gc->guild_id; + struct guild *g = numdb_search(guild_db, gid); + inter_log("guild %s (id=%d) %s castle id=%d" RETCODE, + (g) ? g->name : "??", gid, (value) ? "occupy" : "abandon", index); + } + gc->guild_id = value; + break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: gc->visibleG0 = value; break; + case 11: gc->visibleG1 = value; break; + case 12: gc->visibleG2 = value; break; + case 13: gc->visibleG3 = value; break; + case 14: gc->visibleG4 = value; break; + case 15: gc->visibleG5 = value; break; + case 16: gc->visibleG6 = value; break; + case 17: gc->visibleG7 = value; break; + case 18: gc->Ghp0 = value; break; // guardian HP [Valaris] + case 19: gc->Ghp1 = value; break; + case 20: gc->Ghp2 = value; break; + case 21: gc->Ghp3 = value; break; + case 22: gc->Ghp4 = value; break; + case 23: gc->Ghp5 = value; break; + case 24: gc->Ghp6 = value; break; + case 25: gc->Ghp7 = value; break; // end additions [Valaris] + default: + printf("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index); + return 0; + } + + return mapif_guild_castle_datasave(gc->castle_id, index, value); +} + +// ギルドチェック要求 +int mapif_parse_GuildCheck(int fd, int guild_id, int account_id, int char_id) { + return guild_check_conflict(guild_id, account_id, char_id); +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_guild_parse_frommap(int fd) { + switch(RFIFOW(fd,0)) { + case 0x3030: mapif_parse_CreateGuild(fd, RFIFOL(fd,4), RFIFOP(fd,8), (struct guild_member *)RFIFOP(fd,32)); break; + case 0x3031: mapif_parse_GuildInfo(fd, RFIFOL(fd,2)); break; + case 0x3032: mapif_parse_GuildAddMember(fd, RFIFOL(fd,4), (struct guild_member *)RFIFOP(fd,8)); break; + case 0x3034: mapif_parse_GuildLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), RFIFOP(fd,15)); break; + case 0x3035: mapif_parse_GuildChangeMemberInfoShort(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOB(fd,14), RFIFOW(fd,15), RFIFOW(fd,17)); break; + case 0x3036: mapif_parse_BreakGuild(fd, RFIFOL(fd,2)); break; + case 0x3037: mapif_parse_GuildMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12), RFIFOW(fd,2)-12); break; + case 0x3038: mapif_parse_GuildCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x3039: mapif_parse_GuildBasicInfoChange(fd, RFIFOL(fd,4), RFIFOW(fd,8), RFIFOP(fd,10), RFIFOW(fd,2)-10); break; + case 0x303A: mapif_parse_GuildMemberInfoChange(fd, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOL(fd,12), RFIFOW(fd,16), RFIFOP(fd,18), RFIFOW(fd,2)-18); break; + case 0x303B: mapif_parse_GuildPosition(fd, RFIFOL(fd,4), RFIFOL(fd,8), (struct guild_position *)RFIFOP(fd,12)); break; + case 0x303C: mapif_parse_GuildSkillUp(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; + case 0x303D: mapif_parse_GuildAlliance(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOB(fd,18)); break; + case 0x303E: mapif_parse_GuildNotice(fd, RFIFOL(fd,2), RFIFOP(fd,6), RFIFOP(fd,66)); break; + case 0x303F: mapif_parse_GuildEmblem(fd, RFIFOW(fd,2)-12, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12)); break; + case 0x3040: mapif_parse_GuildCastleDataLoad(fd, RFIFOW(fd,2), RFIFOB(fd,4)); break; + case 0x3041: mapif_parse_GuildCastleDataSave(fd, RFIFOW(fd,2), RFIFOB(fd,4), RFIFOL(fd,5)); break; + + default: + return 0; + } + + return 1; +} + +// マップサーバーの接続時処理 +int inter_guild_mapif_init(int fd) { + return mapif_guild_castle_alldataload(fd); +} + +// サーバーから脱退要求(キャラ削除用) +int inter_guild_leave(int guild_id, int account_id, int char_id) { + return mapif_parse_GuildLeave(-1, guild_id, account_id, char_id, 0, "**サーバー命令**"); +} diff --git a/src/char/int_guild.h b/src/char/int_guild.h new file mode 100644 index 0000000..555f5e1 --- /dev/null +++ b/src/char/int_guild.h @@ -0,0 +1,16 @@ +// $Id: int_guild.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef _INT_GUILD_H_ +#define _INT_GUILD_H_ + +int inter_guild_init(); +int inter_guild_save(); +int inter_guild_parse_frommap(int fd); +struct guild *inter_guild_search(int guild_id); +int inter_guild_mapif_init(int fd); + +int inter_guild_leave(int guild_id,int account_id,int char_id); + +extern char guild_txt[1024]; +extern char castle_txt[1024]; + +#endif diff --git a/src/char/int_party.c b/src/char/int_party.c new file mode 100644 index 0000000..0fd58fa --- /dev/null +++ b/src/char/int_party.c @@ -0,0 +1,595 @@ +// $Id: int_party.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#include "inter.h" +#include "int_party.h" +#include "mmo.h" +#include "char.h" +#include "socket.h" +#include "db.h" +#include "lock.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char party_txt[1024] = "save/party.txt"; + +static struct dbt *party_db; +static int party_newid = 100; + +int mapif_party_broken(int party_id, int flag); +int party_check_empty(struct party *p); +int mapif_parse_PartyLeave(int fd, int party_id, int account_id); + +// パーティデータの文字列への変換 +int inter_party_tostr(char *str, struct party *p) { + int i, len; + + len = sprintf(str, "%d\t%s\t%d,%d\t", p->party_id, p->name, p->exp, p->item); + for(i = 0; i < MAX_PARTY; i++) { + struct party_member *m = &p->member[i]; + len += sprintf(str + len, "%d,%d\t%s\t", m->account_id, m->leader, ((m->account_id > 0) ? m->name : "NoMember")); + } + + return 0; +} + +// パーティデータの文字列からの変換 +int inter_party_fromstr(char *str, struct party *p) { + int i, j; + int tmp_int[16]; + char tmp_str[256]; + + memset(p, 0, sizeof(struct party)); + +// printf("sscanf party main info\n"); + if (sscanf(str, "%d\t%[^\t]\t%d,%d\t", &tmp_int[0], tmp_str, &tmp_int[1], &tmp_int[2]) != 4) + return 1; + + p->party_id = tmp_int[0]; + strcpy(p->name, tmp_str); + p->exp = tmp_int[1]; + p->item = tmp_int[2]; +// printf("%d [%s] %d %d\n", tmp_int[0], tmp_str[0], tmp_int[1], tmp_int[2]); + + for(j = 0; j < 3 && str != NULL; j++) + str = strchr(str + 1, '\t'); + + for(i = 0; i < MAX_PARTY; i++) { + struct party_member *m = &p->member[i]; + if (str == NULL) + return 1; +// printf("sscanf party member info %d\n", i); + + if (sscanf(str + 1, "%d,%d\t%[^\t]\t", &tmp_int[0], &tmp_int[1], tmp_str) != 3) + return 1; + + m->account_id = tmp_int[0]; + m->leader = tmp_int[1]; + strncpy(m->name, tmp_str, sizeof(m->name)); +// printf(" %d %d [%s]\n", tmp_int[0], tmp_int[1], tmp_str); + + for(j = 0; j < 2 && str != NULL; j++) + str = strchr(str + 1, '\t'); + } + + return 0; +} + +// パーティデータのロード +int inter_party_init() { + char line[8192]; + struct party *p; + FILE *fp; + int c = 0; + int i, j; + + party_db = numdb_init(); + + if ((fp = fopen(party_txt, "r")) == NULL) + return 1; + + while(fgets(line, sizeof(line) - 1, fp)) { + j = 0; + if (sscanf(line, "%d\t%%newid%%\n%n", &i, &j) == 1 && j > 0 && party_newid <= i) { + party_newid = i; + continue; + } + + p = calloc(sizeof(struct party), 1); + if (p == NULL){ + printf("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; + numdb_insert(party_db, p->party_id, p); + party_check_empty(p); + } else { + printf("int_party: broken data [%s] line %d\n", party_txt, c + 1); + free(p); + } + c++; + } + fclose(fp); +// printf("int_party: %s read done (%d parties)\n", party_txt, c); + + return 0; +} + +// パーティーデータのセーブ用 +int inter_party_save_sub(void *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; +} + +// パーティーデータのセーブ +int inter_party_save() { + FILE *fp; + int lock; + + if ((fp = lock_fopen(party_txt, &lock)) == NULL) { + printf("int_party: cant write [%s] !!! data is lost !!!\n", party_txt); + return 1; + } + numdb_foreach(party_db, inter_party_save_sub, fp); +// fprintf(fp, "%d\t%%newid%%\n", party_newid); + lock_fclose(fp,party_txt, &lock); +// printf("int_party: %s saved.\n", party_txt); + + return 0; +} + +// パーティ名検索用 +int search_partyname_sub(void *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 (strcmpi(p->name, str) == 0) + *dst = p; + + return 0; +} + +// パーティ名検索 +struct party* search_partyname(char *str) { + struct party *p = NULL; + numdb_foreach(party_db, search_partyname_sub, str, &p); + + return p; +} + +// EXP公平分配できるかチェック +int party_check_exp_share(struct party *p) { + int i; + int maxlv = 0, minlv = 0x7fffffff; + + for(i = 0; i < MAX_PARTY; i++) { + int lv = p->member[i].lv; + if (p->member[i].online) { + if (lv < minlv) + minlv = lv; + if (maxlv < lv) + maxlv = lv; + } + } + + return (maxlv == 0 || maxlv-minlv <= party_share_level); +} + +// パーティが空かどうかチェック +int party_check_empty(struct party *p) { + int i; + +// printf("party check empty %08X\n", (int)p); + for(i = 0; i < MAX_PARTY; i++) { +// printf("%d acc=%d\n", i, p->member[i].account_id); + if (p->member[i].account_id > 0) { + return 0; + } + } + // 誰もいないので解散 + mapif_party_broken(p->party_id, 0); + numdb_erase(party_db, p->party_id); + free(p); + + return 1; +} + +// キャラの競合がないかチェック用 +int party_check_conflict_sub(void *key, void *data, va_list ap) { + struct party *p = (struct party *)data; + int party_id, account_id, i; + char *nick; + + party_id=va_arg(ap, int); + account_id=va_arg(ap, int); + nick=va_arg(ap, char *); + + if (p->party_id == party_id) // 本来の所属なので問題なし + return 0; + + for(i = 0; i < MAX_PARTY; i++) { + if (p->member[i].account_id == account_id && strcmp(p->member[i].name, nick) == 0) { + // 別のパーティに偽の所属データがあるので脱退 + printf("int_party: party conflict! %d %d %d\n", account_id, party_id, p->party_id); + mapif_parse_PartyLeave(-1, p->party_id, account_id); + } + } + + return 0; +} + +// キャラの競合がないかチェック +int party_check_conflict(int party_id, int account_id, char *nick) { + numdb_foreach(party_db, party_check_conflict_sub, party_id, account_id, nick); + + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// パーティ作成可否 +int mapif_party_created(int fd,int account_id, struct party *p) { + WFIFOW(fd,0) = 0x3820; + WFIFOL(fd,2) = account_id; + if (p != NULL) { + WFIFOB(fd,6) = 0; + WFIFOL(fd,7) = p->party_id; + memcpy(WFIFOP(fd,11), p->name, 24); + printf("int_party: created! %d %s\n", p->party_id, p->name); + } else { + WFIFOB(fd,6) = 1; + WFIFOL(fd,7) = 0; + memcpy(WFIFOP(fd,11), "error", 24); + } + WFIFOSET(fd,35); + + return 0; +} + +// パーティ情報見つからず +int mapif_party_noinfo(int fd, int party_id) { + WFIFOW(fd,0) = 0x3821; + WFIFOW(fd,2) = 8; + WFIFOL(fd,4) = party_id; + WFIFOSET(fd,8); + printf("int_party: info not found %d\n", party_id); + + return 0; +} + +// パーティ情報まとめ送り +int mapif_party_info(int fd, struct party *p) { + unsigned char buf[4 + sizeof(struct party)]; + + WBUFW(buf,0) = 0x3821; + memcpy(buf + 4, p, sizeof(struct party)); + WBUFW(buf,2) = 4 + sizeof(struct party); + if (fd < 0) + mapif_sendall(buf, WBUFW(buf,2)); + else + mapif_send(fd, buf, WBUFW(buf,2)); +// printf("int_party: info %d %s\n", p->party_id, p->name); + + return 0; +} + +// パーティメンバ追加可否 +int mapif_party_memberadded(int fd, int party_id, int account_id, int flag) { + WFIFOW(fd,0) = 0x3822; + WFIFOL(fd,2) = party_id; + WFIFOL(fd,6) = account_id; + WFIFOB(fd,10) = flag; + WFIFOSET(fd,11); + + return 0; +} + +// パーティ設定変更通知 +int mapif_party_optionchanged(int fd,struct party *p, int account_id, int flag) { + unsigned char buf[15]; + + WBUFW(buf,0) = 0x3823; + WBUFL(buf,2) = p->party_id; + WBUFL(buf,6) = account_id; + WBUFW(buf,10) = p->exp; + WBUFW(buf,12) = p->item; + WBUFB(buf,14) = flag; + if (flag == 0) + mapif_sendall(buf, 15); + else + mapif_send(fd, buf, 15); + printf("int_party: option changed %d %d %d %d %d\n", p->party_id, account_id, p->exp, p->item, flag); + + return 0; +} + +// パーティ脱退通知 +int mapif_party_leaved(int party_id,int account_id, char *name) { + unsigned char buf[34]; + + WBUFW(buf,0) = 0x3824; + WBUFL(buf,2) = party_id; + WBUFL(buf,6) = account_id; + memcpy(WBUFP(buf,10), name, 24); + mapif_sendall(buf, 34); + printf("int_party: party leaved %d %d %s\n", party_id, account_id, name); + + return 0; +} + +// パーティマップ更新通知 +int mapif_party_membermoved(struct party *p, int idx) { + unsigned char buf[29]; + + WBUFW(buf,0) = 0x3825; + WBUFL(buf,2) = p->party_id; + WBUFL(buf,6) = p->member[idx].account_id; + memcpy(WBUFP(buf,10), p->member[idx].map, 16); + WBUFB(buf,26) = p->member[idx].online; + WBUFW(buf,27) = p->member[idx].lv; + mapif_sendall(buf, 29); + + return 0; +} + +// パーティ解散通知 +int mapif_party_broken(int party_id, int flag) { + unsigned char buf[7]; + WBUFW(buf,0) = 0x3826; + WBUFL(buf,2) = party_id; + WBUFB(buf,6) = flag; + mapif_sendall(buf, 7); + printf("int_party: broken %d\n", party_id); + + return 0; +} + +// パーティ内発言 +int mapif_party_message(int party_id, int account_id, char *mes, int len) { + unsigned char buf[len+12]; + + WBUFW(buf,0) = 0x3827; + WBUFW(buf,2) = len + 12; + WBUFL(buf,4) = party_id; + WBUFL(buf,8) = account_id; + memcpy(WBUFP(buf,12), mes, len); + mapif_sendall(buf,len + 12); + + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + + +// パーティ +int mapif_parse_CreateParty(int fd, int account_id, char *name, char *nick, char *map, int lv) { + struct party *p; + int i; + + for(i = 0; i < 24 && name[i]; i++) { + if (!(name[i] & 0xe0) || name[i] == 0x7f) { + printf("int_party: illegal party name [%s]\n", name); + mapif_party_created(fd, account_id, NULL); + return 0; + } + } + + if ((p = search_partyname(name)) != NULL) { + printf("int_party: same name party exists [%s]\n", name); + mapif_party_created(fd, account_id, NULL); + return 0; + } + p = calloc(sizeof(struct party), 1); + if (p == NULL) { + printf("int_party: out of memory !\n"); + mapif_party_created(fd,account_id,NULL); + return 0; + } + memset(p, 0, sizeof(struct party)); + p->party_id = party_newid++; + memcpy(p->name, name, 24); + p->exp = 0; + p->item = 0; + p->member[0].account_id = account_id; + memcpy(p->member[0].name, nick, 24); + memcpy(p->member[0].map, map, 16); + p->member[0].leader = 1; + p->member[0].online = 1; + p->member[0].lv = lv; + + numdb_insert(party_db, p->party_id, p); + + mapif_party_created(fd, account_id, p); + mapif_party_info(fd, p); + + return 0; +} + +// パーティ情報要求 +int mapif_parse_PartyInfo(int fd, int party_id) { + struct party *p; + + p = numdb_search(party_db, party_id); + if (p != NULL) + mapif_party_info(fd, p); + else + mapif_party_noinfo(fd, party_id); + + return 0; +} + +// パーティ追加要求 +int mapif_parse_PartyAddMember(int fd, int party_id, int account_id, char *nick, char *map, int lv) { + struct party *p; + int i; + + p = numdb_search(party_db, party_id); + if (p == NULL) { + mapif_party_memberadded(fd, party_id, account_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; + memcpy(p->member[i].name, nick, 24); + memcpy(p->member[i].map, map, 16); + p->member[i].leader = 0; + p->member[i].online = 1; + p->member[i].lv = lv; + mapif_party_memberadded(fd, party_id, account_id, 0); + mapif_party_info(-1, p); + + if (p->exp > 0 && !party_check_exp_share(p)) { + p->exp = 0; + flag = 0x01; + } + if (flag) + mapif_party_optionchanged(fd, p, 0, 0); + return 0; + } + } + mapif_party_memberadded(fd, party_id, account_id, 1); + + return 0; +} + +// パーティー設定変更要求 +int mapif_parse_PartyChangeOption(int fd, int party_id, int account_id, int exp, int item) { + struct party *p; + int flag = 0; + + p = numdb_search(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; + } + + p->item = item; + + mapif_party_optionchanged(fd, p, account_id, flag); + return 0; +} + +// パーティ脱退要求 +int mapif_parse_PartyLeave(int fd, int party_id, int account_id) { + struct party *p; + int i; + + p = numdb_search(party_db, party_id); + if (p != NULL) { + for(i = 0; i < MAX_PARTY; i++) { + if (p->member[i].account_id == account_id) { + mapif_party_leaved(party_id, account_id, p->member[i].name); + + memset(&p->member[i], 0, sizeof(struct party_member)); + if (party_check_empty(p) == 0) + mapif_party_info(-1, p);// まだ人がいるのでデータ送信 + return 0; + } + } + } + + return 0; +} + +// パーティマップ更新要求 +int mapif_parse_PartyChangeMap(int fd, int party_id, int account_id, char *map, int online, int lv) { + struct party *p; + int i; + + p = numdb_search(party_db, party_id); + if (p == NULL) + return 0; + + for(i = 0; i < MAX_PARTY; i++) { + if (p->member[i].account_id == account_id) { + int flag = 0; + + memcpy(p->member[i].map, map, 16); + p->member[i].online = online; + p->member[i].lv = lv; + mapif_party_membermoved(p, i); + + if (p->exp > 0 && !party_check_exp_share(p)) { + p->exp = 0; + flag = 1; + } + if (flag) + mapif_party_optionchanged(fd, p, 0, 0); + break; + } + } + + return 0; +} + +// パーティ解散要求 +int mapif_parse_BreakParty(int fd, int party_id) { + struct party *p; + + p = numdb_search(party_db, party_id); + if (p == NULL) + return 0; + + numdb_erase(party_db, party_id); + mapif_party_broken(fd, party_id); + + return 0; +} + +// パーティメッセージ送信 +int mapif_parse_PartyMessage(int fd, int party_id, int account_id, char *mes, int len) { + return mapif_party_message(party_id, account_id, mes, len); +} +// パーティチェック要求 +int mapif_parse_PartyCheck(int fd, int party_id, int account_id, char *nick) { + return party_check_conflict(party_id, account_id, nick); +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_party_parse_frommap(int fd) { + switch(RFIFOW(fd,0)) { + case 0x3020: mapif_parse_CreateParty(fd, RFIFOL(fd,2), RFIFOP(fd,6), RFIFOP(fd,30), RFIFOP(fd,54), RFIFOW(fd,70)); break; + case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break; + case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOP(fd,10), RFIFOP(fd,34), RFIFOW(fd,50)); break; + case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break; + case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6)); break; + case 0x3025: mapif_parse_PartyChangeMap(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOP(fd,10), RFIFOB(fd,26), RFIFOW(fd,27)); break; + case 0x3026: mapif_parse_BreakParty(fd, RFIFOL(fd,2)); break; + case 0x3027: mapif_parse_PartyMessage(fd, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12), RFIFOW(fd,2)-12); break; + case 0x3028: mapif_parse_PartyCheck(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOP(fd,10)); break; + default: + return 0; + } + + return 1; +} + +// サーバーから脱退要求(キャラ削除用) +int inter_party_leave(int party_id, int account_id) { + return mapif_parse_PartyLeave(-1, party_id, account_id); +} + diff --git a/src/char/int_party.h b/src/char/int_party.h new file mode 100644 index 0000000..b265b4c --- /dev/null +++ b/src/char/int_party.h @@ -0,0 +1,14 @@ +// $Id: int_party.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef _INT_PARTY_H_ +#define _INT_PARTY_H_ + +int inter_party_init(); +int inter_party_save(); + +int inter_party_parse_frommap(int fd); + +int inter_party_leave(int party_id,int account_id); + +extern char party_txt[1024]; + +#endif diff --git a/src/char/int_pet.c b/src/char/int_pet.c new file mode 100644 index 0000000..cff1e43 --- /dev/null +++ b/src/char/int_pet.c @@ -0,0 +1,364 @@ +// $Id: int_pet.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#include "inter.h" +#include "int_pet.h" +#include "mmo.h" +#include "char.h" +#include "socket.h" +#include "db.h" +#include "lock.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.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,24); + 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=numdb_init(); + + if( (fp=fopen(pet_txt,"r"))==NULL ) + return 1; + while(fgets(line,sizeof(line),fp)){ + p=calloc(sizeof(struct s_pet), 1); + if(p==NULL){ + printf("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; + numdb_insert(pet_db,p->pet_id,p); + }else{ + printf("int_pet: broken data [%s] line %d\n",pet_txt,c); + free(p); + } + c++; + } + fclose(fp); +// printf("int_pet: %s read done (%d pets)\n",pet_txt,c); + return 0; +} + +int inter_pet_save_sub(void *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 ){ + printf("int_pet: cant write [%s] !!! data is lost !!!\n",pet_txt); + return 1; + } + numdb_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 = numdb_search(pet_db,pet_id); + if( p == NULL) + return 1; + else { + numdb_erase(pet_db,pet_id); + printf("pet_id: %d deleted\n",pet_id); + } + 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; + printf("int_pet: created! %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) +{ + struct s_pet *p; + p=malloc(sizeof(struct s_pet)); + if(p==NULL){ + printf("int_pet: out of memory !\n"); + mapif_pet_created(fd,account_id,NULL); + return 0; + } + memset(p,0,sizeof(struct s_pet)); + p->pet_id = pet_newid++; + memcpy(p->name,pet_name,24); + 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; + + numdb_insert(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=numdb_search(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; +} + +int mapif_save_pet(int fd,int account_id,struct s_pet *data) +{ + struct s_pet *p; + int pet_id; + int len=RFIFOW(fd,2); + if(sizeof(struct s_pet)!=len-8) { + printf("inter pet: data size error %d %d\n",sizeof(struct s_pet),len-8); + } + else{ + pet_id = data->pet_id; + p=numdb_search(pet_db,pet_id); + if(p == NULL) { + p=malloc(sizeof(struct s_pet)); + if(p==NULL){ + printf("int_pet: out of memory !\n"); + mapif_save_pet_ack(fd,account_id,1); + return 0; + } + memset(p,0,sizeof(struct s_pet)); + p->pet_id = data->pet_id; + if(p->pet_id == 0) + data->pet_id = p->pet_id = pet_newid++; + numdb_insert(pet_db,p->pet_id,p); + } + 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) +{ + 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),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; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +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/int_pet.h b/src/char/int_pet.h new file mode 100644 index 0000000..993f913 --- /dev/null +++ b/src/char/int_pet.h @@ -0,0 +1,13 @@ +// $Id: int_pet.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef _INT_PET_H_ +#define _INT_PET_H_ + +int inter_pet_init(); +int inter_pet_save(); +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_storage.c b/src/char/int_storage.c new file mode 100644 index 0000000..8b656fc --- /dev/null +++ b/src/char/int_storage.c @@ -0,0 +1,504 @@ +// $Id: int_storage.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#include "inter.h" +#include "int_storage.h" +#include "int_pet.h" +#include "int_guild.h" +#include "mmo.h" +#include "char.h" +#include "socket.h" +#include "db.h" +#include "lock.h" + +#include <string.h> +#include <stdlib.h> + +// ファイル名のデフォルト +// inter_config_read()で再設定される +char storage_txt[1024]="save/storage.txt"; +char guild_storage_txt[1024]="save/g_storage.txt"; + +static struct dbt *storage_db; +static struct dbt *guild_storage_db; + +// 倉庫データを文字列に変換 +int storage_tostr(char *str,struct storage *p) +{ + int i,f=0; + char *str_p = str; + str_p += sprintf(str_p,"%d,%d\t",p->account_id,p->storage_amount); + + for(i=0;i<MAX_STORAGE;i++) + if( (p->storage[i].nameid) && (p->storage[i].amount) ){ + str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->storage[i].id,p->storage[i].nameid,p->storage[i].amount,p->storage[i].equip, + p->storage[i].identify,p->storage[i].refine,p->storage[i].attribute, + p->storage[i].card[0],p->storage[i].card[1],p->storage[i].card[2],p->storage[i].card[3],p->storage[i].broken); + f++; + } + + *(str_p++)='\t'; + + *str_p='\0'; + if(!f) + str[0]=0; + return 0; +} + +// 文字列を倉庫データに変換 +int storage_fromstr(char *str,struct storage *p) +{ + int tmp_int[256]; + int set,next,len,i; + + set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next); + p->storage_amount=tmp_int[1]; + + if(set!=2) + return 1; + if(str[next]=='\n' || str[next]=='\r') + return 0; + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { + 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]; + p->storage[i].broken = tmp_int[11]; + 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]; + p->storage[i].broken = 0; + next += len; + if (str[next] == ' ') + next++; + } + + else return 1; + } + return 0; +} + +int guild_storage_tostr(char *str,struct guild_storage *p) +{ + int i,f=0; + char *str_p = str; + str_p+=sprintf(str,"%d,%d\t",p->guild_id,p->storage_amount); + + for(i=0;i<MAX_GUILD_STORAGE;i++) + if( (p->storage[i].nameid) && (p->storage[i].amount) ){ + str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ", + p->storage[i].id,p->storage[i].nameid,p->storage[i].amount,p->storage[i].equip, + p->storage[i].identify,p->storage[i].refine,p->storage[i].attribute, + p->storage[i].card[0],p->storage[i].card[1],p->storage[i].card[2],p->storage[i].card[3],p->storage[i].broken); + f++; + } + + *(str_p++)='\t'; + + *str_p='\0'; + if(!f) + str[0]=0; + return 0; +} + +int guild_storage_fromstr(char *str,struct guild_storage *p) +{ + int tmp_int[256]; + int set,next,len,i; + + set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next); + p->storage_amount=tmp_int[1]; + + if(set!=2) + return 1; + if(str[next]=='\n' || str[next]=='\r') + return 0; + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { + 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]; + p->storage[i].broken = tmp_int[11]; + 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]; + p->storage[i].broken = 0; + next += len; + if (str[next] == ' ') + next++; + } + + else return 1; + } + return 0; +} + +// アカウントから倉庫データインデックスを得る(新規倉庫追加可能) +struct storage *account2storage(int account_id) +{ + struct storage *s; + s=numdb_search(storage_db,account_id); + if(s == NULL) { + s = calloc(sizeof(struct storage), 1); + if(s==NULL){ + printf("int_storage: out of memory!\n"); + exit(0); + } + memset(s,0,sizeof(struct storage)); + s->account_id=account_id; + numdb_insert(storage_db,s->account_id,s); + } + return s; +} + +struct guild_storage *guild2storage(int guild_id) +{ + struct guild_storage *gs = NULL; + if(inter_guild_search(guild_id) != NULL) { + gs=numdb_search(guild_storage_db,guild_id); + if(gs == NULL) { + gs = calloc(sizeof(struct guild_storage), 1); + if(gs==NULL){ + printf("int_storage: out of memory!\n"); + exit(0); + } + memset(gs,0,sizeof(struct guild_storage)); + gs->guild_id=guild_id; + numdb_insert(guild_storage_db,gs->guild_id,gs); + } + } + return gs; +} + +//--------------------------------------------------------- +// 倉庫データを読み込む +int inter_storage_init() +{ + char line[65536]; + int c=0,tmp_int; + struct storage *s; + struct guild_storage *gs; + FILE *fp; + + storage_db = numdb_init(); + + fp=fopen(storage_txt,"r"); + if(fp==NULL){ + printf("cant't read : %s\n",storage_txt); + return 1; + } + while(fgets(line,65535,fp)){ + sscanf(line,"%d",&tmp_int); + s=calloc(sizeof(struct storage), 1); + if(s==NULL){ + printf("int_storage: out of memory!\n"); + exit(0); + } + memset(s,0,sizeof(struct storage)); + s->account_id=tmp_int; + if(s->account_id > 0 && storage_fromstr(line,s) == 0) { + numdb_insert(storage_db,s->account_id,s); + } + else{ + printf("int_storage: broken data [%s] line %d\n",storage_txt,c); + free(s); + } + c++; + } + fclose(fp); + + c = 0; + guild_storage_db = numdb_init(); + + fp=fopen(guild_storage_txt,"r"); + if(fp==NULL){ + printf("cant't read : %s\n",guild_storage_txt); + return 1; + } + while(fgets(line,65535,fp)){ + sscanf(line,"%d",&tmp_int); + gs=calloc(sizeof(struct guild_storage), 1); + if(gs==NULL){ + printf("int_storage: out of memory!\n"); + exit(0); + } + memset(gs,0,sizeof(struct guild_storage)); + gs->guild_id=tmp_int; + if(gs->guild_id > 0 && guild_storage_fromstr(line,gs) == 0) { + numdb_insert(guild_storage_db,gs->guild_id,gs); + } + else{ + printf("int_storage: broken data [%s] line %d\n",guild_storage_txt,c); + free(gs); + } + c++; + } + fclose(fp); + + return 0; +} + +int inter_storage_save_sub(void *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; +} +//--------------------------------------------------------- +// 倉庫データを書き込む +int inter_storage_save() +{ + FILE *fp; + int lock; + if( (fp=lock_fopen(storage_txt,&lock))==NULL ){ + printf("int_storage: cant write [%s] !!! data is lost !!!\n",storage_txt); + return 1; + } + numdb_foreach(storage_db,inter_storage_save_sub,fp); + lock_fclose(fp,storage_txt,&lock); +// printf("int_storage: %s saved.\n",storage_txt); + return 0; +} + +int inter_guild_storage_save_sub(void *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; +} +//--------------------------------------------------------- +// 倉庫データを書き込む +int inter_guild_storage_save() +{ + FILE *fp; + int lock; + if( (fp=lock_fopen(guild_storage_txt,&lock))==NULL ){ + printf("int_storage: cant write [%s] !!! data is lost !!!\n",guild_storage_txt); + return 1; + } + numdb_foreach(guild_storage_db,inter_guild_storage_save_sub,fp); + lock_fclose(fp,guild_storage_txt,&lock); +// printf("int_storage: %s saved.\n",guild_storage_txt); + return 0; +} + +// 倉庫データ削除 +int inter_storage_delete(int account_id) +{ + struct storage *s = numdb_search(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(*((long *)(&s->storage[i].card[2]))); + } + numdb_erase(storage_db,account_id); + free(s); + } + return 0; +} + +// ギルド倉庫データ削除 +int inter_guild_storage_delete(int guild_id) +{ + struct guild_storage *gs = numdb_search(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(*((long *)(&gs->storage[i].card[2]))); + } + numdb_erase(guild_storage_db,guild_id); + free(gs); + } + return 0; +} + +//--------------------------------------------------------- +// map serverへの通信 + +// 倉庫データの送信 +int mapif_load_storage(int fd,int account_id) +{ + struct storage *s=account2storage(account_id); + WFIFOW(fd,0)=0x3810; + WFIFOW(fd,2)=sizeof(struct storage)+8; + WFIFOL(fd,4)=account_id; + memcpy(WFIFOP(fd,8),s,sizeof(struct storage)); + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +// 倉庫データ保存完了送信 +int mapif_save_storage_ack(int fd,int account_id) +{ + WFIFOW(fd,0)=0x3811; + WFIFOL(fd,2)=account_id; + WFIFOB(fd,6)=0; + WFIFOSET(fd,7); + return 0; +} + +int mapif_load_guild_storage(int fd,int account_id,int guild_id) +{ + struct guild_storage *gs=guild2storage(guild_id); + WFIFOW(fd,0)=0x3818; + if(gs) { + WFIFOW(fd,2)=sizeof(struct guild_storage)+12; + WFIFOL(fd,4)=account_id; + WFIFOL(fd,8)=guild_id; + memcpy(WFIFOP(fd,12),gs,sizeof(struct guild_storage)); + } + else { + WFIFOW(fd,2)=12; + WFIFOL(fd,4)=account_id; + WFIFOL(fd,8)=0; + } + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} +int mapif_save_guild_storage_ack(int fd,int account_id,int guild_id,int fail) +{ + WFIFOW(fd,0)=0x3819; + WFIFOL(fd,2)=account_id; + WFIFOL(fd,6)=guild_id; + WFIFOB(fd,10)=fail; + WFIFOSET(fd,11); + return 0; +} + +//--------------------------------------------------------- +// map serverからの通信 + +// 倉庫データ要求受信 +int mapif_parse_LoadStorage(int fd) +{ + mapif_load_storage(fd,RFIFOL(fd,2)); + return 0; +} +// 倉庫データ受信&保存 +int mapif_parse_SaveStorage(int fd) +{ + struct storage *s; + int account_id=RFIFOL(fd,4); + int len=RFIFOW(fd,2); + if(sizeof(struct storage)!=len-8){ + printf("inter storage: data size error %d %d\n",sizeof(struct storage),len-8); + } + else { + s=account2storage(account_id); + memcpy(s,RFIFOP(fd,8),sizeof(struct storage)); + mapif_save_storage_ack(fd,account_id); + } + return 0; +} + +int mapif_parse_LoadGuildStorage(int fd) +{ + mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6)); + return 0; +} +int mapif_parse_SaveGuildStorage(int fd) +{ + struct guild_storage *gs; + int guild_id=RFIFOL(fd,8); + int len=RFIFOW(fd,2); + if(sizeof(struct guild_storage)!=len-12){ + printf("inter storage: data size error %d %d\n",sizeof(struct guild_storage),len-12); + } + else { + gs=guild2storage(guild_id); + if(gs) { + memcpy(gs,RFIFOP(fd,12),sizeof(struct guild_storage)); + mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,0); + } + else + mapif_save_guild_storage_ack(fd,RFIFOL(fd,4),guild_id,1); + } + return 0; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_storage_parse_frommap(int fd) +{ + switch(RFIFOW(fd,0)){ + case 0x3010: mapif_parse_LoadStorage(fd); break; + case 0x3011: mapif_parse_SaveStorage(fd); break; + case 0x3018: mapif_parse_LoadGuildStorage(fd); break; + case 0x3019: mapif_parse_SaveGuildStorage(fd); break; + default: + return 0; + } + return 1; +} diff --git a/src/char/int_storage.h b/src/char/int_storage.h new file mode 100644 index 0000000..d918f5f --- /dev/null +++ b/src/char/int_storage.h @@ -0,0 +1,16 @@ +// $Id: int_storage.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef _INT_STORAGE_H_ +#define _INT_STORAGE_H_ + +int inter_storage_init(); +int inter_storage_save(); +int inter_guild_storage_save(); +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 0000000..29ec57b --- /dev/null +++ b/src/char/inter.c @@ -0,0 +1,561 @@ +// $Id: inter.c,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#include "mmo.h" +#include "char.h" +#include "socket.h" +#include "timer.h" +#include "db.h" +#include <string.h> +#include <stdlib.h> + +#include "inter.h" +#include "int_party.h" +#include "int_guild.h" +#include "int_storage.h" +#include "int_pet.h" +#include "lock.h" + +#define WISDATA_TTL (60*1000) // Existence time of Wisp/page data (60 seconds) + // that is the waiting time of answers of all map-servers +#define WISDELLIST_MAX 256 // Number of elements of Wisp/page data deletion list + +char inter_log_filename[1024] = "log/inter.log"; + +char accreg_txt[1024] = "save/accreg.txt"; +static struct dbt *accreg_db = NULL; + +struct accreg { + int account_id, reg_num; + struct global_reg reg[ACCOUNT_REG_NUM]; +}; + +int party_share_level = 10; + + +// 送信パケット長リスト +int inter_send_packet_length[] = { + -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, + 35,-1,11,15, 34,29, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, + 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, + 9, 9,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +// 受信パケット長リスト +int inter_recv_packet_length[] = { + -1,-1, 7,-1, -1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, + 72, 6,52,14, 10,29, 6,-1, 34, 0, 0, 0, 0, 0, 0, 0, + -1, 6,-1, 0, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, + 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +struct WisData { + int id, fd, count, len; + unsigned long tick; + unsigned char src[24], dst[24], msg[1024]; +}; +static struct dbt * wis_db = NULL; +static int wis_dellist[WISDELLIST_MAX], wis_delnum; + + +//-------------------------------------------------------- + +// アカウント変数を文字列へ変換 +int inter_accreg_tostr(char *str, struct accreg *reg) { + int j; + char *p = str; + + p += sprintf(p, "%d\t", reg->account_id); + for(j = 0; j < reg->reg_num; j++) { + p += sprintf(p,"%s,%d ", reg->reg[j].str, reg->reg[j].value); + } + + return 0; +} + +// アカウント変数を文字列から変換 +int inter_accreg_fromstr(const char *str, struct accreg *reg) { + int j, v, n; + char buf[128]; + const char *p = str; + + if (sscanf(p, "%d\t%n", ®->account_id, &n ) != 1 || reg->account_id <= 0) + return 1; + + for(j = 0, p += n; j < ACCOUNT_REG_NUM; j++, p += n) { + if (sscanf(p, "%[^,],%d %n", buf, &v, &n) != 2) + break; + memcpy(reg->reg[j].str, buf, 32); + reg->reg[j].value = v; + } + reg->reg_num = j; + + return 0; +} + +// アカウント変数の読み込み +int inter_accreg_init() { + char line[8192]; + FILE *fp; + int c = 0; + struct accreg *reg; + + accreg_db = numdb_init(); + + if( (fp = fopen(accreg_txt, "r")) == NULL) + return 1; + while(fgets(line, sizeof(line)-1, fp)){ + line[sizeof(line)-1] = '\0'; + + reg = calloc(sizeof(struct accreg), 1); + if (reg == NULL) { + printf("inter: accreg: out of memory!\n"); + exit(0); + } + if (inter_accreg_fromstr(line, reg) == 0 && reg->account_id > 0) { + numdb_insert(accreg_db, reg->account_id, reg); + } else { + printf("inter: accreg: broken data [%s] line %d\n", accreg_txt, c); + free(reg); + } + c++; + } + fclose(fp); +// printf("inter: %s read done (%d)\n", accreg_txt, c); + + return 0; +} + +// アカウント変数のセーブ用 +int inter_accreg_save_sub(void *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; +} + +// アカウント変数のセーブ +int inter_accreg_save() { + FILE *fp; + int lock; + + if ((fp = lock_fopen(accreg_txt,&lock)) == NULL) { + printf("int_accreg: cant write [%s] !!! data is lost !!!\n", accreg_txt); + return 1; + } + numdb_foreach(accreg_db, inter_accreg_save_sub,fp); + lock_fclose(fp, accreg_txt, &lock); +// printf("inter: %s saved.\n", accreg_txt); + + return 0; +} + +//-------------------------------------------------------- + +/*========================================== + * 設定ファイルを読み込む + *------------------------------------------ + */ +int inter_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen(cfgName, "r"); + if (fp == NULL) { + printf("file not found: %s\n", cfgName); + return 1; + } + while(fgets(line, sizeof(line) - 1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof(line)-1] = '\0'; + + if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + if (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, "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, "import") == 0) { + inter_config_read(w2); + } + } + fclose(fp); + + return 0; +} + +// ログ書き出し +int inter_log(char *fmt,...) { + FILE *logfp; + va_list ap; + + va_start(ap,fmt); + logfp = fopen(inter_log_filename, "a"); + if (logfp) { + vfprintf(logfp, fmt, ap); + fclose(logfp); + } + va_end(ap); + + return 0; +} + +// セーブ +int inter_save() { + 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 = numdb_init(); + + inter_party_init(); + inter_guild_init(); + inter_storage_init(); + inter_pet_init(); + inter_accreg_init(); + + return 0; +} + +// マップサーバー接続 +int inter_mapif_init(int fd) { + inter_guild_mapif_init(fd); + + return 0; +} + +//-------------------------------------------------------- +// sended packets to map-server + +// GMメッセージ送信 +int mapif_GMmessage(unsigned char *mes, int len) { + unsigned char buf[len]; + + WBUFW(buf,0) = 0x3800; + WBUFW(buf,2) = len; + memcpy(WBUFP(buf,4), mes, len - 4); + mapif_sendall(buf, len); +// printf("inter server: GM:%d %s\n", len, mes); + + return 0; +} + +// Wisp/page transmission to all map-server +int mapif_wis_message(struct WisData *wd) { + unsigned char buf[56 + wd->len]; + + WBUFW(buf, 0) = 0x3801; + WBUFW(buf, 2) = 56 + wd->len; + WBUFL(buf, 4) = wd->id; + memcpy(WBUFP(buf, 8), wd->src, 24); + memcpy(WBUFP(buf,32), wd->dst, 24); + memcpy(WBUFP(buf,56), wd->msg, wd->len); + wd->count = mapif_sendall(buf, WBUFW(buf,2)); + + return 0; +} + +// Wisp/page transmission result to map-server +int mapif_wis_end(struct WisData *wd, int flag) { + unsigned char buf[27]; + + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), wd->src, 24); + WBUFB(buf,26) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(wd->fd, buf, 27); +// printf("inter server wis_end: flag: %d\n", flag); + + return 0; +} + +// アカウント変数送信 +int mapif_account_reg(int fd, unsigned char *src) { + unsigned char buf[WBUFW(src,2)]; + + memcpy(WBUFP(buf,0),src,WBUFW(src,2)); + WBUFW(buf, 0) = 0x3804; + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + + return 0; +} + +// アカウント変数要求返信 +int mapif_account_reg_reply(int fd,int account_id) { + struct accreg *reg = numdb_search(accreg_db,account_id); + + WFIFOW(fd,0) = 0x3804; + WFIFOL(fd,4) = account_id; + if (reg == NULL) { + WFIFOW(fd,2) = 8; + } else { + int j, p; + for(j = 0, p = 8; j < reg->reg_num; j++, p += 36) { + memcpy(WFIFOP(fd,p), reg->reg[j].str, 32); + WFIFOL(fd,p+32) = reg->reg[j].value; + } + WFIFOW(fd,2) = p; + } + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +//-------------------------------------------------------- + +// Existence check of WISP data +int check_ttl_wisdata_sub(void *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; + numdb_foreach(wis_db, check_ttl_wisdata_sub, tick); + for(i = 0; i < wis_delnum; i++) { + struct WisData *wd = numdb_search(wis_db, wis_dellist[i]); + printf("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst); + // removed. not send information after a timeout. Just no answer for the player + //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + numdb_erase(wis_db, wd->id); + free(wd); + } + } while(wis_delnum >= WISDELLIST_MAX); + + return 0; +} + +//-------------------------------------------------------- +// received packets from map-server + +// GMメッセージ送信 +int mapif_parse_GMmessage(int fd) { + mapif_GMmessage(RFIFOP(fd,4), RFIFOW(fd,2)); + + return 0; +} + +// Wisp/page request to send +int mapif_parse_WisRequest(int fd) { + struct WisData* wd; + static int wisid = 0; + int index; + + if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { + printf("inter: Wis message size too long.\n"); + return 0; + } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows... + printf("inter: Wis message doesn't exist.\n"); + return 0; + } + + // search if character exists before to ask all map-servers + if ((index = search_character_index(RFIFOP(fd,28))) == -1) { + unsigned char buf[27]; + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), 24); + WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(fd, buf, 27); + // Character exists. So, ask all map-servers + } else { + // to be sure of the correct name, rewrite it + memset(RFIFOP(fd,28), 0, 24); + strncpy(RFIFOP(fd,28), search_character_name(index), 24); + // if source is destination, don't ask other servers. + if (strcmp(RFIFOP(fd,4),RFIFOP(fd,28)) == 0) { + unsigned char buf[27]; + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), 24); + WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(fd, buf, 27); + } else { + + wd = (struct WisData *)calloc(sizeof(struct WisData), 1); + if (wd == NULL){ + printf("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), 24); + memcpy(wd->dst, RFIFOP(fd,28), 24); + memcpy(wd->msg, RFIFOP(fd,52), wd->len); + wd->tick = gettick(); + numdb_insert(wis_db, wd->id, wd); + mapif_wis_message(wd); + } + } + + return 0; +} + +// Wisp/page transmission result +int mapif_parse_WisReply(int fd) { + int id = RFIFOL(fd,2), flag = RFIFOB(fd,6); + struct WisData *wd = numdb_search(wis_db, id); + + if (wd == NULL) + return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server + + if ((--wd->count) <= 0 || flag != 1) { + mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + numdb_erase(wis_db, id); + free(wd); + } + + return 0; +} + +// Received wisp message from map-server for ALL gm (just copy the message and resends it to ALL map-servers) +int mapif_parse_WisToGM(int fd) { + unsigned char buf[RFIFOW(fd,2)]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B + + memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf, 0) = 0x3803; + mapif_sendall(buf, RFIFOW(fd,2)); + + return 0; +} + +// アカウント変数保存要求 +int mapif_parse_AccReg(int fd) { + int j, p; + struct accreg *reg = numdb_search(accreg_db, RFIFOL(fd,4)); + + if (reg == NULL) { + if ((reg = calloc(sizeof(struct accreg), 1)) == NULL) { + printf("inter: accreg: out of memory !\n"); + exit(0); + } + reg->account_id = RFIFOL(fd,4); + numdb_insert(accreg_db, RFIFOL(fd,4), reg); + } + + for(j = 0, p = 8; j < ACCOUNT_REG_NUM && p < RFIFOW(fd,2); j++, p += 36) { + memcpy(reg->reg[j].str, RFIFOP(fd,p), 32); + reg->reg[j].value = RFIFOL(fd, p + 32); + } + reg->reg_num = j; + + mapif_account_reg(fd, RFIFOP(fd,0)); // 他のMAPサーバーに送信 + + return 0; +} + +// アカウント変数送信要求 +int mapif_parse_AccRegRequest(int fd) { +// printf("mapif: accreg request\n"); + return mapif_account_reg_reply(fd, RFIFOL(fd,2)); +} + +//-------------------------------------------------------- + +// map server からの通信(1パケットのみ解析すること) +// エラーなら0(false)、処理できたなら1、 +// パケット長が足りなければ2をかえさなければならない +int inter_parse_frommap(int fd) { + int cmd = RFIFOW(fd,0); + int len = 0; + + // inter鯖管轄かを調べる + if (cmd < 0x3000 || cmd >= 0x3000 + (sizeof(inter_recv_packet_length) / sizeof(inter_recv_packet_length[0]))) + return 0; + + // パケット長を調べる + if ((len = inter_check_length(fd, inter_recv_packet_length[cmd - 0x3000])) == 0) + return 2; + + switch(cmd) { + case 0x3000: mapif_parse_GMmessage(fd); break; + case 0x3001: mapif_parse_WisRequest(fd); break; + case 0x3002: mapif_parse_WisReply(fd); break; + case 0x3003: mapif_parse_WisToGM(fd); break; + case 0x3004: mapif_parse_AccReg(fd); break; + case 0x3005: mapif_parse_AccRegRequest(fd); break; + default: + if (inter_party_parse_frommap(fd)) + break; + if (inter_guild_parse_frommap(fd)) + break; + if (inter_storage_parse_frommap(fd)) + break; + if (inter_pet_parse_frommap(fd)) + break; + return 0; + } + RFIFOSKIP(fd, len); + + return 1; +} + +// RFIFOのパケット長確認 +// 必要パケット長があればパケット長、まだ足りなければ0 +int inter_check_length(int fd, int length) { + if (length == -1) { // 可変パケット長 + if (RFIFOREST(fd) < 4) // パケット長が未着 + return 0; + length = RFIFOW(fd,2); + } + + if (RFIFOREST(fd) < length) // パケットが未着 + return 0; + + return length; +} + diff --git a/src/char/inter.h b/src/char/inter.h new file mode 100644 index 0000000..b004c9b --- /dev/null +++ b/src/char/inter.h @@ -0,0 +1,19 @@ +// $Id: inter.h,v 1.1.1.1 2004/09/10 17:26:51 MagicalTux Exp $ +#ifndef _INTER_H_ +#define _INTER_H_ + +int inter_init(const char *file); +int inter_save(); +int inter_parse_frommap(int fd); +int inter_mapif_init(int fd); + +int inter_check_length(int fd,int length); + +int inter_log(char *fmt,...); + +#define inter_cfgName "conf/inter_athena.conf" + +extern int party_share_level; +extern char inter_log_filename[1024]; + +#endif diff --git a/src/char_sql/GNUmakefile b/src/char_sql/GNUmakefile new file mode 100644 index 0000000..9c7b5e5 --- /dev/null +++ b/src/char_sql/GNUmakefile @@ -0,0 +1,20 @@ +all: char-server_sql +sql: char-server_sql + +COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/malloc.o +COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/db.h ../common/malloc.h + +char-server_sql: char.o inter.o int_party.o int_guild.o int_storage.o int_pet.o strlib.o itemdb.o $(COMMON_OBJ) + $(CC) -o ../../$@ $^ $(LIB_S) + +char.o: char.c char.h strlib.h itemdb.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 +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 +int_guild.o: int_guild.c int_guild.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/db.h +int_storage.o: int_storage.c int_storage.h char.h itemdb.h +int_pet.o: int_pet.c int_pet.h inter.h char.h ../common/mmo.h ../common/socket.h ../common/db.h +strlib.o: strlib.c strlib.h +itemdb.o: itemdb.c itemdb.h ../common/db.h ../common/mmo.h + +clean: + rm -f *.o ../../char-server_sql diff --git a/src/char_sql/Makefile b/src/char_sql/Makefile new file mode 100644 index 0000000..cefe36e --- /dev/null +++ b/src/char_sql/Makefile @@ -0,0 +1,20 @@ +all: char-server_sql
+sql: char-server_sql
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/db.h ../common/malloc.h
+
+char-server_sql: char.o inter.o int_party.o int_guild.o int_storage.o int_pet.o strlib.o itemdb.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $^ $(LIB_S)
+
+char.o: char.c char.h strlib.h itemdb.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
+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
+int_guild.o: int_guild.c int_guild.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/db.h
+int_storage.o: int_storage.c int_storage.h char.h itemdb.h
+int_pet.o: int_pet.c int_pet.h inter.h char.h ../common/mmo.h ../common/socket.h ../common/db.h
+strlib.o: strlib.c strlib.h
+itemdb.o: itemdb.c itemdb.h ../common/db.h ../common/mmo.h
+
+clean:
+ rm -f *.o ../../char-server_sql
diff --git a/src/char_sql/char.c b/src/char_sql/char.c new file mode 100644 index 0000000..11b6a49 --- /dev/null +++ b/src/char_sql/char.c @@ -0,0 +1,2917 @@ +// $Id: char.c,v 1.16 2004/09/23 18:31:16 MouseJstr Exp $ +// original : char2.c 2003/03/14 11:58:35 Rev.1.5 +// +// original code from athena +// SQL conversion by Jioh L. Jung +// TXT 1.105 +#include <sys/types.h> + +#ifdef LCCWIN32 +#include <winsock.h> +#pragma 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> +#endif + +#include "utils.h" +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <stdarg.h> + +#include "char.h" +#include "strlib.h" +#include "itemdb.h" +#include "inter.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +char char_db[256] = "char"; +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 login_db[256] = "login"; + +char login_db_account_id[32] = "account_id"; +char login_db_level[32] = "level"; + +int lowest_gm_level = 1; + +unsigned char *SQL_CONF_NAME = "conf/inter_athena.conf"; + +struct mmo_map_server server[MAX_MAP_SERVERS]; +int server_fd[MAX_MAP_SERVERS]; +int server_freezeflag[MAX_MAP_SERVERS]; // Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed + +int anti_freeze_enable = 0; +int ANTI_FREEZE_INTERVAL = 6; + +int login_fd, char_fd; +char userid[24]; +char passwd[24]; +char server_name[20]; +char wisp_server_name[24] = "Server"; +char login_ip_str[128]; +int login_ip; +int login_port = 6900; +char char_ip_str[128]; +int char_ip; +int char_port = 6121; +int char_maintenance; +int char_new; +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] + +char lan_map_ip[128]; // Lan map ip added by kashy +int subnetmaski[4]; // Subnetmask added by kashy +char unknown_char_name[1024] = "Unknown"; + +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 = 150000; +struct mmo_charstatus *char_dat; +int char_num,char_max; +int max_connect_user = 0; +int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; +int start_zeny = 500; +int start_weapon = 1201; +int start_armor = 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 = {"new_1-1.gat", 53, 111}; + +struct gm_account *gm_account = NULL; +int GM_num = 0; + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars(unsigned char *str) { + int i; + int change = 0; + + for(i = 0; i < strlen(str); i++) { + if (str[i] < 32) { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +// 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; +} + +void read_gm_account(void) { + if (gm_account != NULL) + free(gm_account); + GM_num = 0; + + sprintf(tmp_lsql, "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(&lmysql_handle, tmp_lsql)) { + printf("DB server Error (select %s to Memory)- %s\n",login_db,mysql_error(&lmysql_handle)); + } + lsql_res = mysql_store_result(&lmysql_handle); + if (lsql_res) { + gm_account = calloc(sizeof(struct gm_account) * mysql_num_rows(lsql_res), 1); + 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]); + GM_num++; + } + } + + mysql_free_result(lsql_res); +} + +//===================================================================================================== +int mmo_char_tosql(int char_id, struct mmo_charstatus *p){ + int i=0,party_exist,guild_exist; + int eqcount=1; + int noteqcount=1; + char temp_str[32]; + + struct itemtemp mapitem; + if (char_id!=p->char_id) return 0; + + save_flag = p->char_id; + printf("(\033[1;32m%d\033[0m) %s \trequest save char data - ",char_id,char_dat[0].name); + + + +//for(testcount=1;testcount<50;testcount++){//---------------------------test count-------------------- +// printf("test count : %d\n", testcount); +// eqcount=1; +// noteqcount=1; +// dbeqcount=1; +// dbnoteqcount=1; +//----------------------------------------------------------------------------------------------------- + +//=========================================map inventory data > memory =============================== + //map inventory data + for(i=0;i<MAX_INVENTORY;i++){ + if(p->inventory[i].nameid>0){ + if(itemdb_isequip(p->inventory[i].nameid)==1){ + mapitem.equip[eqcount].flag=0; + mapitem.equip[eqcount].id = p->inventory[i].id; + mapitem.equip[eqcount].nameid=p->inventory[i].nameid; + mapitem.equip[eqcount].amount = p->inventory[i].amount; + mapitem.equip[eqcount].equip = p->inventory[i].equip; + mapitem.equip[eqcount].identify = p->inventory[i].identify; + mapitem.equip[eqcount].refine = p->inventory[i].refine; + mapitem.equip[eqcount].attribute = p->inventory[i].attribute; + mapitem.equip[eqcount].card[0] = p->inventory[i].card[0]; + mapitem.equip[eqcount].card[1] = p->inventory[i].card[1]; + mapitem.equip[eqcount].card[2] = p->inventory[i].card[2]; + mapitem.equip[eqcount].card[3] = p->inventory[i].card[3]; + mapitem.equip[eqcount].broken = p->inventory[i].broken; + eqcount++; + } + else if(itemdb_isequip(p->inventory[i].nameid)==0){ + mapitem.notequip[noteqcount].flag=0; + mapitem.notequip[noteqcount].id = p->inventory[i].id; + mapitem.notequip[noteqcount].nameid=p->inventory[i].nameid; + mapitem.notequip[noteqcount].amount = p->inventory[i].amount; + mapitem.notequip[noteqcount].equip = p->inventory[i].equip; + mapitem.notequip[noteqcount].identify = p->inventory[i].identify; + mapitem.notequip[noteqcount].refine = p->inventory[i].refine; + mapitem.notequip[noteqcount].attribute = p->inventory[i].attribute; + mapitem.notequip[noteqcount].card[0] = p->inventory[i].card[0]; + mapitem.notequip[noteqcount].card[1] = p->inventory[i].card[1]; + mapitem.notequip[noteqcount].card[2] = p->inventory[i].card[2]; + mapitem.notequip[noteqcount].card[3] = p->inventory[i].card[3]; + mapitem.notequip[noteqcount].broken = p->inventory[i].broken; + noteqcount++; + } + } + } + //printf("- Save item data to MySQL!\n"); + memitemdata_to_sql(mapitem, eqcount, noteqcount, p->char_id,TABLE_INVENTORY); + +//=========================================map cart data > memory ==================================== + eqcount=1; + noteqcount=1; + + //map cart data + for(i=0;i<MAX_CART;i++){ + if(p->cart[i].nameid>0){ + if(itemdb_isequip(p->cart[i].nameid)==1){ + mapitem.equip[eqcount].flag=0; + mapitem.equip[eqcount].id = p->cart[i].id; + mapitem.equip[eqcount].nameid=p->cart[i].nameid; + mapitem.equip[eqcount].amount = p->cart[i].amount; + mapitem.equip[eqcount].equip = p->cart[i].equip; + mapitem.equip[eqcount].identify = p->cart[i].identify; + mapitem.equip[eqcount].refine = p->cart[i].refine; + mapitem.equip[eqcount].attribute = p->cart[i].attribute; + mapitem.equip[eqcount].card[0] = p->cart[i].card[0]; + mapitem.equip[eqcount].card[1] = p->cart[i].card[1]; + mapitem.equip[eqcount].card[2] = p->cart[i].card[2]; + mapitem.equip[eqcount].card[3] = p->cart[i].card[3]; + mapitem.equip[eqcount].broken = p->cart[i].broken; + eqcount++; + } + else if(itemdb_isequip(p->cart[i].nameid)==0){ + mapitem.notequip[noteqcount].flag=0; + mapitem.notequip[noteqcount].id = p->cart[i].id; + mapitem.notequip[noteqcount].nameid=p->cart[i].nameid; + mapitem.notequip[noteqcount].amount = p->cart[i].amount; + mapitem.notequip[noteqcount].equip = p->cart[i].equip; + mapitem.notequip[noteqcount].identify = p->cart[i].identify; + mapitem.notequip[noteqcount].refine = p->cart[i].refine; + mapitem.notequip[noteqcount].attribute = p->cart[i].attribute; + mapitem.notequip[noteqcount].card[0] = p->cart[i].card[0]; + mapitem.notequip[noteqcount].card[1] = p->cart[i].card[1]; + mapitem.notequip[noteqcount].card[2] = p->cart[i].card[2]; + mapitem.notequip[noteqcount].card[3] = p->cart[i].card[3]; + mapitem.notequip[noteqcount].broken = p->cart[i].broken; + noteqcount++; + } + } + } + + //printf("- Save cart data to MySQL!\n"); + memitemdata_to_sql(mapitem, eqcount, noteqcount, p->char_id,TABLE_CART); + +//===================================================================================================== + +//}//---------------------------test count------------------------------ + //check party_exist + party_exist=0; + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `party_id` = '%d'",party_db, p->party_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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=0; + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id` = '%d'",guild_db, p->guild_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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; + + //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`) + //printf("- Save char data to MySQL!\n"); + 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' 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, + p->last_point.map, p->last_point.x, p->last_point.y, + p->save_point.map, p->save_point.x, p->save_point.y, p->partner_id, p->account_id, p->char_id + ); + + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle)); + } + + //printf("- Save memo data to MySQL!\n"); + //`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)) { + printf("DB server Error (delete `memo`)- %s\n", mysql_error(&mysql_handle)); + } + + //insert here. + for(i=0;i<10;i++){ + if(p->memo_point[i].map[0]){ + sprintf(tmp_sql,"INSERT INTO `%s`(`char_id`,`map`,`x`,`y`) VALUES ('%d', '%s', '%d', '%d')", + memo_db, char_id, p->memo_point[i].map, p->memo_point[i].x, p->memo_point[i].y); + if(mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (insert `memo`)- %s\n", mysql_error(&mysql_handle)); + } + } + + //printf("- Save skill data to MySQL!\n"); + //`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)) { + printf("DB server Error (delete `skill`)- %s\n", mysql_error(&mysql_handle)); + } + //printf("- Insert skill \n"); + //insert here. + for(i=0;i<MAX_SKILL;i++){ + if(p->skill[i].id){ + if (p->skill[i].id && p->skill[i].flag!=1) { + sprintf(tmp_sql,"INSERT delayed INTO `%s`(`char_id`, `id`, `lv`) VALUES ('%d', '%d','%d')", + skill_db, char_id, p->skill[i].id, (p->skill[i].flag==0)?p->skill[i].lv:p->skill[i].flag-2); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (insert `skill`)- %s\n", mysql_error(&mysql_handle)); + } + } + } + } + + + //printf("- Save global_reg_value data to MySQL!\n"); + //`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)) { + printf("DB server Error (delete `global_reg_value`)- %s\n", mysql_error(&mysql_handle)); + } + + //insert here. + for(i=0;i<p->global_reg_num;i++){ + if (p->global_reg[i].str) { + if(p->global_reg[i].value !=0){ + sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`, `str`, `value`) VALUES ('%d', '%s','%d')", + reg_db, char_id, jstrescapecpy(temp_str,(unsigned char*)p->global_reg[i].str), p->global_reg[i].value); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (insert `global_reg_value`)- %s\n", mysql_error(&mysql_handle)); + } + } + } + } + printf("saving char is done.\n"); + save_flag = 0; + + return 0; +} + +int memitemdata_to_sql(struct itemtemp mapitem, int eqcount, int noteqcount, int char_id, int tableswitch){ + //equ + int i, j; + int dbeqcount = 1; + int dbnoteqcount = 1; + struct itemtemp dbitem; + char tablename[16]; + char selectoption[16]; + + switch (tableswitch){ + case TABLE_INVENTORY: + sprintf(tablename,"%s",inventory_db); + sprintf(selectoption,"char_id"); + break; + case TABLE_CART: + sprintf(tablename,"%s",cart_db); + sprintf(selectoption,"char_id"); + break; + case TABLE_STORAGE: + sprintf(tablename,"%s",storage_db); + sprintf(selectoption,"account_id"); + break; + case TABLE_GUILD_STORAGE: + sprintf(tablename,"%s",guild_storage_db); + sprintf(selectoption,"guild_id"); + break; + } + //printf("Working Table : %s \n",tablename); + + //=======================================mysql database data > memory=============================================== + + sprintf(tmp_sql, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3` , `broken` " + "FROM `%s` WHERE `%s`='%d'",tablename ,selectoption ,char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `%s` to Memory)- %s\n",tablename ,mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ + if (itemdb_isequip(atoi(sql_row[1]))==1){ + dbitem.equip[dbeqcount].flag=0; + dbitem.equip[dbeqcount].id = atoi(sql_row[0]); + dbitem.equip[dbeqcount].nameid = atoi(sql_row[1]); + dbitem.equip[dbeqcount].amount = atoi(sql_row[2]); + dbitem.equip[dbeqcount].equip = atoi(sql_row[3]); + dbitem.equip[dbeqcount].identify = atoi(sql_row[4]); + dbitem.equip[dbeqcount].refine = atoi(sql_row[5]); + dbitem.equip[dbeqcount].attribute = atoi(sql_row[6]); + dbitem.equip[dbeqcount].card[0] = atoi(sql_row[7]); + dbitem.equip[dbeqcount].card[1] = atoi(sql_row[8]); + dbitem.equip[dbeqcount].card[2] = atoi(sql_row[9]); + dbitem.equip[dbeqcount].card[3] = atoi(sql_row[10]); + dbitem.equip[dbeqcount].broken = atoi(sql_row[11]); + dbeqcount++; + }else { + dbitem.notequip[dbnoteqcount].flag=0; + dbitem.notequip[dbnoteqcount].id = atoi(sql_row[0]); + dbitem.notequip[dbnoteqcount].nameid = atoi(sql_row[1]); + dbitem.notequip[dbnoteqcount].amount = atoi(sql_row[2]); + dbitem.notequip[dbnoteqcount].equip = atoi(sql_row[3]); + dbitem.notequip[dbnoteqcount].identify = atoi(sql_row[4]); + dbitem.notequip[dbnoteqcount].refine = atoi(sql_row[5]); + dbitem.notequip[dbnoteqcount].attribute = atoi(sql_row[6]); + dbitem.notequip[dbnoteqcount].card[0] = atoi(sql_row[7]); + dbitem.notequip[dbnoteqcount].card[1] = atoi(sql_row[8]); + dbitem.notequip[dbnoteqcount].card[2] = atoi(sql_row[9]); + dbitem.notequip[dbnoteqcount].card[3] = atoi(sql_row[10]); + dbitem.notequip[dbnoteqcount].broken = atoi(sql_row[11]); + dbnoteqcount++; + } + } + mysql_free_result(sql_res); + } + + //==============================================Memory data > SQL =============================== + //======================================Equip ITEM======================================= + if((eqcount==1) && (dbeqcount==1)){//printf("%s Equip Empty\n",tablename); + //item empty + } else { + + for(i=1;i<eqcount;i++){ + for(j=1;j<dbeqcount;j++){ + if(mapitem.equip[i].flag==1) break; + if(!(dbitem.equip[j].flag==1)){ + if(mapitem.equip[i].nameid==dbitem.equip[j].nameid){ + if ((mapitem.equip[i].equip==dbitem.equip[j].equip) && (mapitem.equip[i].identify==dbitem.equip[j].identify) && (mapitem.equip[i].amount==dbitem.equip[j].amount) && + + (mapitem.equip[i].refine==dbitem.equip[j].refine) && (mapitem.equip[i].attribute==dbitem.equip[j].attribute) && (mapitem.equip[i].card[0]==dbitem.equip[j].card[0]) && + (mapitem.equip[i].card[1]==dbitem.equip[j].card[1]) && (mapitem.equip[i].card[2]==dbitem.equip[j].card[2]) && (mapitem.equip[i].card[3]==dbitem.equip[j].card[3]) && + (mapitem.equip[i].broken == dbitem.equip[j].broken)) { + mapitem.equip[i].flag = 1; + dbitem.equip[j].flag = 1; + //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 { + sprintf(tmp_sql,"UPDATE `%s` SET `equip`='%d', `identify`='%d', `refine`='%d'," + "`attribute`='%d', `card0`='%d', `card1`='%d', `card2`='%d', `card3`='%d', `broken`='%d', `amount`='%d' WHERE `id`='%d' LIMIT 1", + tablename, mapitem.equip[i].equip, mapitem.equip[i].identify, mapitem.equip[i].refine,mapitem.equip[i].attribute, mapitem.equip[i].card[0], + mapitem.equip[i].card[1], mapitem.equip[i].card[2], mapitem.equip[i].card[3], mapitem.equip[i].broken, mapitem.equip[i].amount, dbitem.equip[j].id); + //printf("%s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (UPdate `equ %s`)- %s\n", tablename, mysql_error(&mysql_handle)); + mapitem.equip[i].flag=1; + dbitem.equip[j].flag=1; + //printf("not the same item : %d ; i : %d ; flag : %d\n", mapitem.equip[i].nameid, i, mapitem.equip[i].flag); + } + } + } + } + } + + //printf("dbeqcount = %d\n",dbeqcount); + + for(i=1;i<dbeqcount;i++){ + //printf("dbitem.equip[i].flag = %d , dbitem.equip[i].id = %d\n",dbitem.equip[i].flag,dbitem.equip[i].id); + if (!(dbitem.equip[i].flag == 1)) { + sprintf(tmp_sql,"DELETE from `%s` where `id`='%d'",tablename , dbitem.equip[i].id); + //printf("%s", tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (DELETE `equ %s`)- %s\n", tablename ,mysql_error(&mysql_handle)); + } + } + for(i=1;i<eqcount;i++){ + if(!(mapitem.equip[i].flag==1)){ + sprintf(tmp_sql,"INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken`)" + " VALUES ( '%d','%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + tablename, selectoption, char_id, mapitem.equip[i].nameid, mapitem.equip[i].amount, mapitem.equip[i].equip, mapitem.equip[i].identify, mapitem.equip[i].refine, + mapitem.equip[i].attribute, mapitem.equip[i].card[0], mapitem.equip[i].card[1], mapitem.equip[i].card[2], mapitem.equip[i].card[3], mapitem.equip[i].broken); + //printf("%s", tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (INSERT `equ %s`)- %s\n",tablename ,mysql_error(&mysql_handle)); + } + } + + //======================================DEBUG================================================= + +// gettimeofday(&tv,NULL); +// strftime(tmpstr,24,"%Y-%m-%d %H:%M:%S",localtime(&(tv.tv_sec))); +// printf("\n\n"); +// printf("Working Table Name : EQU %s, Count : map %3d | db %3d \n",tablename ,eqcount ,dbeqcount); +// printf("*********************************************************************************\n"); +// printf("======================================MAP===================Char ID %10d===\n",char_id); +// printf("==flag ===name ===equip===ident===amoun===attri===card0===card1===card2===card3==\n"); +// for(j=1;j<eqcount;j++) +// printf("| %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d |\n", mapitem.equip[j].flag,mapitem.equip[j].nameid, mapitem.equip[j].equip, mapitem.equip[j].identify, mapitem.equip[j].refine,mapitem.equip[j].attribute, mapitem.equip[j].card[0], mapitem.equip[j].card[1], mapitem.equip[j].card[2], mapitem.equip[j].card[3]); +// printf("======================================DB=========================================\n"); +// printf("==flag ===name ===equip===ident===refin===attri===card0===card1===card2===card3==\n"); +// for(j=1;j<dbeqcount;j++) +// printf("| %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d |\n", dbitem.equip[j].flag ,dbitem.equip[j].nameid, dbitem.equip[j].equip, dbitem.equip[j].identify, dbitem.equip[j].amount,dbitem.equip[j].attribute, dbitem.equip[j].card[0], dbitem.equip[j].card[1], dbitem.equip[j].card[2], dbitem.equip[j].card[3]); +// printf("=================================================================================\n"); +// printf("=================================================Data Time %s===\n", tmpstr); +// printf("=================================================================================\n"); + + } + + //======================================DEBUG================================================== + + //=============================Not Equip ITEM========================================== + if((noteqcount==1) && (dbnoteqcount==1)){ + //printf("%s Not Equip Empty\n",tablename); + //item empty + } else { + + for(i=1;i<noteqcount;i++){ + for(j=1;j<dbnoteqcount;j++){ + if(mapitem.notequip[i].flag==1) break; + if(!(dbitem.notequip[j].flag==1)){ + if(mapitem.notequip[i].nameid==dbitem.notequip[j].nameid){ + if ((mapitem.notequip[i].amount==dbitem.notequip[j].amount) && (mapitem.notequip[i].equip==dbitem.notequip[j].equip) && (mapitem.notequip[i].identify==dbitem.notequip[j].identify) + && (mapitem.notequip[i].attribute==dbitem.notequip[j].attribute)) + { mapitem.notequip[i].flag=1; + dbitem.notequip[j].flag=1; + //printf("the same item : %d ; i : %d ; flag : %d\n", mapitem.notequip[i].nameid, i, mapitem.notequip[i].flag); //DEBUG-STRING + } + else{ + sprintf(tmp_sql,"UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d'," + "`attribute`='%d' WHERE `%s`='%d' AND `nameid`='%d'", + tablename, mapitem.notequip[i].amount, mapitem.notequip[i].equip, mapitem.notequip[i].identify, mapitem.notequip[i].attribute, + selectoption, char_id, mapitem.notequip[i].nameid); + //printf("%s",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (UPdate `notequ %s`)- %s\n",tablename ,mysql_error(&mysql_handle)); + + mapitem.notequip[i].flag=1; + dbitem.notequip[j].flag=1; + } + } + } + } + } + + //printf("dbnoteqcount = %d\n",dbnoteqcount); + + for(i=1;i<dbnoteqcount;i++){ + //printf("dbitem.notequip[i].flag = %d , dbitem.notequip[i].id = %d\n",dbitem.notequip[i].flag,dbitem.notequip[i].id); + if(!(dbitem.notequip[i].flag==1)){ + sprintf(tmp_sql,"DELETE from `%s` where `id`='%d'", tablename, dbitem.notequip[i].id); + //printf("%s", tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (DELETE `notequ %s`)- %s\n", tablename ,mysql_error(&mysql_handle)); + } + } + for(i=1;i<noteqcount;i++){ + if(!(mapitem.notequip[i].flag==1)){ + sprintf(tmp_sql,"INSERT INTO `%s`( `%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken`)" + " VALUES ('%d','%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + tablename ,selectoption , char_id, mapitem.notequip[i].nameid, mapitem.notequip[i].amount, mapitem.notequip[i].equip, mapitem.notequip[i].identify, mapitem.notequip[i].refine, + mapitem.notequip[i].attribute, mapitem.notequip[i].card[0], mapitem.notequip[i].card[1], mapitem.notequip[i].card[2], mapitem.notequip[i].card[3], mapitem.equip[i].broken); + //printf("%s", tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (INSERT `notequ %s`)- %s\n", tablename, mysql_error(&mysql_handle)); + } + } + + //======================================DEBUG================================================= + +// gettimeofday(&tv,NULL); +// strftime(tmpstr,24,"%Y-%m-%d %H:%M:%S",localtime(&(tv.tv_sec))); +// printf("\n\n"); +// printf("Working Table Name : Not EQU %s, Count : map %3d | db %3d \n",tablename ,noteqcount ,dbnoteqcount); +// printf("*********************************************************************************\n"); +// printf("======================================MAP===================Char ID %10d===\n",char_id); +// printf("==flag ===name ===equip===ident===refin===attri===card0===card1===card2===card3==\n"); +// for(j=1;j<noteqcount;j++) +// printf("| %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d |\n", mapitem.notequip[j].flag,mapitem.notequip[j].nameid, mapitem.notequip[j].equip, mapitem.notequip[j].identify, mapitem.notequip[j].refine,mapitem.notequip[j].attribute, mapitem.notequip[j].card[0], mapitem.notequip[j].card[1], mapitem.notequip[j].card[2], mapitem.notequip[j].card[3]); +// printf("======================================DB=========================================\n"); +// printf("==flag ===name ===equip===ident===refin===attri===card0===card1===card2===card3==\n"); +// for(j=1;j<dbnoteqcount;j++) +// printf("| %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d | %5d |\n", dbitem.notequip[j].flag ,dbitem.notequip[j].nameid, dbitem.notequip[j].equip, dbitem.notequip[j].identify, dbitem.notequip[j].refine,dbitem.notequip[j].attribute, dbitem.notequip[j].card[0], dbitem.notequip[j].card[1], dbitem.notequip[j].card[2], dbitem.notequip[j].card[3]); +// printf("=================================================================================\n"); +// printf("=================================================Data Time %s===\n", tmpstr); +// printf("=================================================================================\n"); +// + } + return 0; +} +//===================================================================================================== +int mmo_char_fromsql(int char_id, struct mmo_charstatus *p, int online){ + int i, n; + + memset(p, 0, sizeof(struct mmo_charstatus)); + + p->char_id = char_id; + printf("Loaded: "); + //`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); + + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `char`)- %s\n", mysql_error(&mysql_handle)); + } + + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + + 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); + } else + printf("char1 - failed\n"); //Error?! ERRRRRR WHAT THAT SAY!? + printf("(\033[1;32m%d\033[0m)\033[1;32m%s\033[0m\t[",p->char_id,p->name); + printf("char1 "); + + 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` FROM `%s` WHERE `char_id` = '%d'",char_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `char2`)- %s\n", mysql_error(&mysql_handle)); + } + + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + + + 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]); + strcpy(p->last_point.map,sql_row[14]); p->last_point.x = atoi(sql_row[15]); p->last_point.y = atoi(sql_row[16]); + strcpy(p->save_point.map,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]); + + //free mysql result. + mysql_free_result(sql_res); + } else + printf("char2 - failed\n"); //Error?! ERRRRRR WHAT THAT SAY!? + + printf("char2 "); + + //read memo data + //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`) + sprintf(tmp_sql, "SELECT `map`,`x`,`y` FROM `%s` WHERE `char_id`='%d'",memo_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `memo`)- %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + for(i=0;(sql_row = mysql_fetch_row(sql_res));i++){ + strcpy (p->memo_point[i].map,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); + } + printf("memo "); + + //read inventory + //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) + sprintf(tmp_sql, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken` " + "FROM `%s` WHERE `char_id`='%d'",inventory_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `inventory`)- %s\n", mysql_error(&mysql_handle)); + } + 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]); + p->inventory[i].card[0] = atoi(sql_row[7]); + p->inventory[i].card[1] = atoi(sql_row[8]); + p->inventory[i].card[2] = atoi(sql_row[9]); + p->inventory[i].card[3] = atoi(sql_row[10]); + p->inventory[i].broken = atoi(sql_row[11]); + } + mysql_free_result(sql_res); + } + printf("inventory "); + + + //read cart. + //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) + sprintf(tmp_sql, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken` " + "FROM `%s` WHERE `char_id`='%d'",cart_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `cart_inventory`)- %s\n", mysql_error(&mysql_handle)); + } + 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]); + p->cart[i].card[0] = atoi(sql_row[7]); + p->cart[i].card[1] = atoi(sql_row[8]); + p->cart[i].card[2] = atoi(sql_row[9]); + p->cart[i].card[3] = atoi(sql_row[10]); + p->cart[i].broken = atoi(sql_row[11]); + } + mysql_free_result(sql_res); + } + printf("cart "); + + //read skill + //`skill` (`char_id`, `id`, `lv`) + sprintf(tmp_sql, "SELECT `id`, `lv` FROM `%s` WHERE `char_id`='%d'",skill_db, char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `skill`)- %s\n", mysql_error(&mysql_handle)); + } + 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); + } + printf("skill "); + + //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); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `global_reg_value`)- %s\n", mysql_error(&mysql_handle)); + } + 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]); + p->global_reg[i].value = atoi (sql_row[1]); + } + mysql_free_result(sql_res); + } + p->global_reg_num=i; + + if (online) { + sprintf(tmp_sql, "UPDATE `%s` SET `online`='%d' WHERE `char_id`='%d'",char_db,online,char_id); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (set char online)- %s\n", mysql_error(&mysql_handle)); + } + } + + printf("global_reg]\n"); //ok. all data load successfuly! + + //printf("char cloade"); + + return 1; +} +//========================================================================================================== +int mmo_char_sql_init(void) { + int i; + + printf("init start.......\n"); + // memory initialize + // no need to set twice size in this routine. but some cause segmentation error. :P + printf("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); +/* Initialized in inter.c already [Wizputer] + // DB connection initialized + // for char-server session only + mysql_init(&mysql_handle); + printf("Connect DB server....(char 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)) { + // SQL connection pointer check + printf("%s\n",mysql_error(&mysql_handle)); + exit(1); + } else { + printf("connect success! (char server)\n"); + } +*/ + sprintf(tmp_sql , "SELECT count(*) FROM `%s`", char_db); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); + printf("total char data -> '%s'.......\n",sql_row[0]); + i = atoi (sql_row[0]); + mysql_free_result(sql_res); + + if (i !=0) { + sprintf(tmp_sql , "SELECT max(`char_id`) FROM `%s`", char_db); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); + char_id_count = atoi (sql_row[0]); + + mysql_free_result(sql_res); + } else + printf("set char_id_count: %d.......\n",char_id_count); + + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `online`='1'", char_db); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (reset_online `%s`)- %s\n", char_db, mysql_error(&mysql_handle)); + } + printf("init end.......\n"); + + return 0; +} + +//========================================================================================================== + +int make_new_char_sql(int fd, unsigned char *dat) { + struct char_session_data *sd; + char t_name[100]; + int i; + //aphostropy error check! - fixed! + jstrescapecpy(t_name, dat); + printf("making new char -"); + + sd = session[fd]->session_data; + + // 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 < strlen(dat); i++) + if (strchr(char_name_letters, dat[i]) == NULL) + return -1; + } else if (char_name_option == 2) { // letters/symbols in char_name_letters are forbidden + for (i = 0; i < strlen(dat); i++) + if (strchr(char_name_letters, dat[i]) != NULL) + return -1; + } // 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]!=5*6 ) || + (dat[30] >= 9) || + (dat[33] <= 0) || (dat[33] >= 20) || + (dat[31] >= 9)) { + + // check individual stat value + for(i = 24; i <= 29; i++) { + if (dat[i] < 1 || dat[i] > 9) { + return -1; + } + } + + // 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], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); + //query + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + printf("make new char error %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]); + return -1; + } + + // 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], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[33], dat[31]); + //query + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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]); + + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `name` = '%s'",char_db, t_name); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + return -1; + } + sql_res = mysql_store_result(&mysql_handle); + sql_row = mysql_fetch_row(sql_res); + printf("\033[1;32m name check result : %s -\033[0m ",sql_row[0]); + if (atoi(sql_row[0]) > 0) { + mysql_free_result(sql_res); + return -1; + } else + mysql_free_result(sql_res); + + // check char slot. + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d'",char_db, sd->account_id, dat[30]); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + sql_row = mysql_fetch_row(sql_res); + + //printf("slot check result : %s\n",sql_row[0]); + if (atoi(sql_row[0]) > 0) { + mysql_free_result(sql_res); + return -1; + } else + mysql_free_result(sql_res); + + char_id_count++; + + // make new char. + 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`)" + " VALUES ('%d', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%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]); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (insert `char`)- %s\n", mysql_error(&mysql_handle)); + } + + //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) + sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", + inventory_db, char_id_count, 1201,1,0x02,1); //add Knife + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (insert `inventory`)- %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')", + inventory_db, char_id_count, 2301,1,0x10,1); //add Cotton Shirt + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (insert `inventory`)- %s\n", mysql_error(&mysql_handle)); + } + // respawn map and start point set + sprintf(tmp_sql,"UPDATE `%s` SET `last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d' WHERE `char_id` = '%d'", + char_db, start_point.map,start_point.x,start_point.y, start_point.map,start_point.x,start_point.y, char_id_count); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle)); + } + printf("making new char success - id:(\033[1;32m%d\033[0m\tname:\033[1;32%s\033[0m\n", char_id_count, t_name); + return char_id_count; +} + +//========================================================================================================== + +void mmo_char_sync(void){ + printf("mmo_char_sync() - nothing to do\n"); +} + +// to do +/////////////////////////// + +int mmo_char_sync_timer(int tid, unsigned int tick, int id, int data) { + printf("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 + + printf("mmo_char_send006b start.. (account:%d)\n",sd->account_id); +// printf("offset -> %d...\n",offset); + + //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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + found_num = mysql_num_rows(sql_res); + printf("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; + + printf("(\033[1;13m%d\033[0m) Request Char Data:\n",sd->account_id); + + for(i = 0; i < found_num; i++) { + mmo_char_fromsql(sd->found_char[i], char_dat, 0); + + 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; + 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, 24); + + WFIFOB(fd,j+98) = (p->str > 255) ? 255 : p->str; + WFIFOB(fd,j+99) = (p->agi > 255) ? 255 : p->agi; + WFIFOB(fd,j+100) = (p->vit > 255) ? 255 : p->vit; + WFIFOB(fd,j+101) = (p->int_ > 255) ? 255 : p->int_; + WFIFOB(fd,j+102) = (p->dex > 255) ? 255 : p->dex; + WFIFOB(fd,j+103) = (p->luk > 255) ? 255 : p->luk; + WFIFOB(fd,j+104) = p->char_num; + } + + WFIFOSET(fd,WFIFOW(fd,2)); +// 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) { + if (fd == login_fd) { + printf("Char-server can't connect to login-server (connection #%d).\n", fd); + login_fd = -1; + } + close(fd); + delete_session(fd); + return 0; + } + + sd = session[fd]->session_data; + + // hehe. no need to set user limite on SQL version. :P + // but char limitation is good way to maintain server. :D + while(RFIFOREST(fd) >= 2) { +// 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)); + printf("Can not connect to login-server.\n"); + printf("The server communication passwords (default s1/p1) is probably invalid.\n"); + printf("Also, please make sure your login db has the username/password present and the sex of the account is S.\n"); + printf("If you changed the communication passwords, change them back at map_athena.conf and char_athena.conf\n"); + return 0; + //exit(1); //fixed for server shutdown. + }else { + printf("Connected to login-server (connection #%d).\n", fd); + // if no map-server already connected, display a message... + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0 && server[i].map[0][0]) // if map-server online and at least 1 map + break; + if (i == MAX_MAP_SERVERS) + printf("Awaiting maps from map-server.\n"); + } + RFIFOSKIP(fd, 3); + break; + + case 0x2713: + if(RFIFOREST(fd)<51) + return 0; + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = 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); + // 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 = 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; + +/* 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` FROM `%s` WHERE `account_id` = '%d'",char_db, acc); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `char`)- %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + int char_id = atoi(sql_row[0]); + int jobclass = atoi(sql_row[1]); + int skill_point = atoi(sql_row[2]); + int 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)) { + printf("DB server Error (select `char`)- %s\n", mysql_error(&mysql_handle)); + } + 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)) { + printf("DB server Error (select `char`)- %s\n", mysql_error(&mysql_handle)); + } + } + // 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)) { + printf("DB server Error (select `char`)- %s\n", mysql_error(&mysql_handle)); + } + 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)) { + printf("DB server Error (select `char`)- %s\n", mysql_error(&mysql_handle)); + } + } + } + // disconnect player if online on char-server + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = 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変更通知 + case 0x2729: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + struct global_reg reg[ACCOUNT_REG2_NUM]; + unsigned char buf[4096]; + int j, p, acc; + acc = RFIFOL(fd,4); + for(p = 8, j = 0; p < RFIFOW(fd,2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { + memcpy(reg[j].str, RFIFOP(fd,p), 32); + reg[j].value = RFIFOL(fd,p+32); + } + // set_account_reg2(acc,j,reg); + // 同垢ログインを禁止していれば送る必要は無い + memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf,0) = 0x2b11; + mapif_sendall(buf, WBUFW(buf,2)); + RFIFOSKIP(fd, RFIFOW(fd,2)); +// printf("char: save_account_reg_reply\n"); + } + break; + + // 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 = session[i]->session_data)) { + if (sd->account_id == RFIFOL(fd,2)) { + session[i]->eof = 1; + break; + } + } + } + RFIFOSKIP(fd,11); + break; + + default: + printf("set eof.\n"); + session[fd]->eof = 1; + return 0; + } + } + + RFIFOFLUSH(fd); + + return 0; +} + +//-------------------------------- +// Map-server anti-freeze system +//-------------------------------- +int map_anti_freeze_system(int tid, unsigned int tick, int id, int data) { + int i; + + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (server_fd[i] >= 0) {// if map-server is online + printf("map_anti_freeze_system: server #%d, flag: %d.\n", i, server_freezeflag[i]); + if (server_freezeflag[i]-- < 1) {// Map-server anti-freeze system. Counter. 5 ok, 4...0 freezed + printf("Map-server anti-freeze system: char-server #%d is frozen -> disconnection.\n", i); + session[server_fd[i]]->eof = 1; + sprintf(tmp_sql, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server_fd[i]); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + } + } + + return 0; +} + +int parse_frommap(int fd) { + int i = 0, j = 0; + int id; + + // Sometimes fd=0, and it will cause server crash. Don't know why. :( + if (fd <= 0) { + printf("parse_frommap error fd=0\n"); + return 0; + } + + for(id = 0; id < MAX_MAP_SERVERS; id++) + if (server_fd[id] == fd) + break; + if(id == MAX_MAP_SERVERS || session[fd]->eof) { + if (id < MAX_MAP_SERVERS) { + memset(&server[id], 0, sizeof(struct mmo_map_server)); + printf("Map-server %d (session #%d) has disconnected.\n", id, fd); + sprintf(tmp_sql, "DELETE FROM `ragsrvinfo` WHERE `index`='%d'", server_fd[id]); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + server_fd[id] = -1; + } + close(fd); + delete_session(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2) { +// printf("parse_frommap : %d %d %x\n", fd, RFIFOREST(fd), RFIFOW(fd,0)); + + switch(RFIFOW(fd, 0)) { + case 0x2af7: + RFIFOSKIP(fd,2); + read_gm_account(); + 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 += 16) { + memcpy(server[id].map[j], RFIFOP(fd,i), 16); +// 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; + printf("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n", + id, j, p[0], p[1], p[2], p[3], server[id].port); + printf("Map-server %d loading complete.\n", id); + } + WFIFOW(fd,0) = 0x2afb; + WFIFOB(fd,2) = 0; + memcpy(WFIFOP(fd,3), wisp_server_name, 24); // name for wisp to player + WFIFOSET(fd,27); + { + unsigned char buf[16384]; + int x; + if (j == 0) { + printf("WARNING: Map-Server %d have NO maps.\n", id); + // Transmitting maps information to the other map-servers + } else { + WBUFW(buf,0) = 0x2b04; + WBUFW(buf,2) = j * 16 + 10; + WBUFL(buf,4) = server[id].ip; + WBUFW(buf,8) = server[id].port; + memcpy(WBUFP(buf,10), RFIFOP(fd,4), j * 16); + mapif_sendallwos(fd, buf, WBUFW(buf,2)); + } + // Transmitting the maps of the other map-servers to the new map-server + for(x = 0; x < MAX_MAP_SERVERS; x++) { + if (server_fd[x] >= 0 && x != id) { + WFIFOW(fd,0) = 0x2b04; + WFIFOL(fd,4) = server[x].ip; + WFIFOW(fd,8) = server[x].port; + j = 0; + for(i = 0; i < MAX_MAP_PER_SERVER; i++) + if (server[x].map[i][0]) + memcpy(WFIFOP(fd,10+(j++)*16), server[x].map[i], 16); + if (j > 0) { + WFIFOW(fd,2) = j * 16 + 10; + WFIFOSET(fd,WFIFOW(fd,2)); + } + } + } + } + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + // auth request + case 0x2afc: + if (RFIFOREST(fd) < 22) + return 0; +// printf("(AUTH request) auth_fifo search %d %d %d\n", RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10)); + for(i = 0; i < AUTH_FIFO_SIZE; i++) { + if (auth_fifo[i].account_id == RFIFOL(fd,2) && + auth_fifo[i].char_id == RFIFOL(fd,6) && + auth_fifo[i].login_id1 == RFIFOL(fd,10) && +#if CMP_AUTHFIFO_LOGIN2 != 0 + // here, it's the only area where it's possible that we doesn't know login_id2 (map-server asks just after 0x72 packet, that doesn't given the value) + (auth_fifo[i].login_id2 == RFIFOL(fd,14) || RFIFOL(fd,14) == 0) && // relate to the versions higher than 18 +#endif + (!check_ip_flag || auth_fifo[i].ip == RFIFOL(fd,18)) && + !auth_fifo[i].delflag) { + auth_fifo[i].delflag = 1; + WFIFOW(fd,0) = 0x2afd; + WFIFOW(fd,2) = 16 + sizeof(struct mmo_charstatus); + WFIFOL(fd,4) = RFIFOL(fd,2); + WFIFOL(fd,8) = auth_fifo[i].login_id2; + WFIFOL(fd,12) = (unsigned long)auth_fifo[i].connect_until_time; + mmo_char_fromsql(auth_fifo[i].char_id, char_dat, 1); + char_dat[0].sex = auth_fifo[i].sex; + memcpy(WFIFOP(fd,16), &char_dat[0], sizeof(struct mmo_charstatus)); + WFIFOSET(fd, WFIFOW(fd,2)); + //printf("auth_fifo search success (auth #%d, account %d, character: %d).\n", i, RFIFOL(fd,2), RFIFOL(fd,6)); + break; + } + } + if (i == AUTH_FIFO_SIZE) { + WFIFOW(fd,0) = 0x2afe; + WFIFOL(fd,2) = RFIFOL(fd,2); + WFIFOSET(fd,6); +// printf("(AUTH request) auth_fifo search error!\n"); + } + RFIFOSKIP(fd,22); + break; + + // set MAP user + case 0x2aff: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + if (RFIFOW(fd,4) != server[id].users) + printf("map user: %d\n", RFIFOW(fd,4)); + server[id].users = RFIFOW(fd,4); + if(anti_freeze_enable) + server_freezeflag[id] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed + RFIFOSKIP(fd,RFIFOW(fd,2)); + break; + + // char saving + case 0x2b01: + i = 0; + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + //check account + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",char_db, RFIFOL(fd,4),RFIFOL(fd,8)); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + if (sql_row) + i = atoi(sql_row[0]); + } + mysql_free_result(sql_res); + + if (i == 1) { + memcpy(&char_dat[0], RFIFOP(fd,12), sizeof(struct mmo_charstatus)); + mmo_char_tosql(RFIFOL(fd,8), char_dat); + //save to DB + } + 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; + +// printf("(charselect) auth_fifo set %d - account_id:%08x login_id1:%08x\n", auth_fifo_pos, RFIFOL(fd, 2), RFIFOL(fd, 6)); + 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) < 49) + return 0; + + if (auth_fifo_pos >= AUTH_FIFO_SIZE) + auth_fifo_pos = 0; + + WFIFOW(fd, 0) = 0x2b06; + memcpy(WFIFOP(fd,2), RFIFOP(fd,2), 42); +// printf("(map change) auth_fifo set %d - account_id:%08x login_id1:%08x\n", auth_fifo_pos, RFIFOL(fd, 2), RFIFOL(fd, 6)); + auth_fifo[auth_fifo_pos].account_id = RFIFOL(fd, 2); + 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].char_id = RFIFOL(fd,14); + auth_fifo[auth_fifo_pos].delflag = 0; + auth_fifo[auth_fifo_pos].sex = RFIFOB(fd,44); + auth_fifo[auth_fifo_pos].ip = RFIFOL(fd,45); + + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", char_db, RFIFOL(fd,2), RFIFOL(fd,14)); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + + if (( sql_row = mysql_fetch_row(sql_res))) { + i = atoi(sql_row[0]); + mysql_free_result(sql_res); + + auth_fifo[auth_fifo_pos].char_pos = auth_fifo[auth_fifo_pos].char_id; + auth_fifo_pos++; + + WFIFOL(fd,6) = 0; + break; + } + if (i == 0) + WFIFOW(fd,6) = 1; + + WFIFOSET(fd,44); + RFIFOSKIP(fd,49); + 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, RFIFOL(fd,2)); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + + sql_row = mysql_fetch_row(sql_res); + + WFIFOW(fd,0) = 0x2b09; + WFIFOL(fd,2) = RFIFOL(fd,2); + + if (sql_row) + memcpy(WFIFOP(fd,6), sql_row[0], 24); + else + memcpy(WFIFOP(fd,6), unknown_char_name, 24); + 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)); + RFIFOSKIP(fd, RFIFOW(fd, 2)); + break; + */ + + // account_reg保存要求 + case 0x2b10: + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + struct global_reg reg[ACCOUNT_REG2_NUM]; + int j,p,acc; + acc=RFIFOL(fd,4); + for(p=8,j=0;p<RFIFOW(fd,2) && j<ACCOUNT_REG2_NUM;p+=36,j++){ + memcpy(reg[j].str,RFIFOP(fd,p),32); + reg[j].value=RFIFOL(fd,p+32); + } + // set_account_reg2(acc,j,reg); + // loginサーバーへ送る + if (login_fd > 0) { // don't send request if no login-server + WFIFOW(login_fd, 0) = 0x2728; + memcpy(WFIFOP(login_fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WFIFOSET(login_fd, WFIFOW(login_fd,2)); + } + // ワールドへの同垢ログインがなければmapサーバーに送る必要はない + //memcpy(buf,RFIFOP(fd,0),RFIFOW(fd,2)); + //WBUFW(buf,0)=0x2b11; + //mapif_sendall(buf,WBUFW(buf,2)); + RFIFOSKIP(fd,RFIFOW(fd,2)); +// printf("char: save_account_reg (from map)\n"); + } + break; + + // Map server 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[24]; + int acc = RFIFOL(fd,2); // account_id of who ask (-1 if nobody) + memcpy(character_name, RFIFOP(fd,6), 24); + character_name[sizeof(character_name) -1] = '\0'; + // prepare answer + WFIFOW(fd,0) = 0x2b0f; // answer + WFIFOL(fd,2) = acc; // who want do operation + WFIFOW(fd,30) = RFIFOW(fd, 30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban + sprintf(tmp_sql, "SELECT `account_id`,`name` FROM `%s` WHERE `name` = '%s'",char_db, character_name); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (select `char`)- %s\n", mysql_error(&mysql_handle)); + } + + 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], 24); // put correct name if found + WFIFOW(fd,32) = 0; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + switch(RFIFOW(fd, 30)) { + case 1: // block + if (acc == -1 || isGM(acc) >= isGM(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, 24); + WFIFOW(fd,32) = 1; // answer: 0-login-server resquest done, 1-player not found, 2-gm level too low, 3-login-server offline + } + // send answer if a player ask, not if the server ask + if (acc != -1) { + WFIFOSET(fd, 34); + } + } + } + RFIFOSKIP(fd, 44); + break; + + // Recieve rates [Wizputer] + case 0x2b16: + if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8)) + return 0; + 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), RFIFOP(fd,10)); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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)); + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `char_id`='%d'", char_db, RFIFOL(fd,2)); + if (mysql_query(&mysql_handle, tmp_sql)) + printf("DB server Error (update online `%s`)- %s\n", char_db, mysql_error(&mysql_handle)); + RFIFOSKIP(fd,6); + 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 + printf("parse_frommap: unknown packet %x! \n", RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + } + return 0; +} + +int search_mapserver(char *map) { + int i, j; + char temp_map[16]; + int temp_map_len; + +// printf("Searching the map-server for map '%s'... ", map); + strncpy(temp_map, map, sizeof(temp_map)); + temp_map[sizeof(temp_map)-1] = '\0'; + if (strchr(temp_map, '.') != NULL) + temp_map[strchr(temp_map, '.') - temp_map + 1] = '\0'; // suppress the '.gat', but conserve the '.' to be sure of the name of the map + + temp_map_len = strlen(temp_map); + for(i = 0; i < MAX_MAP_SERVERS; i++) + if (server_fd[i] >= 0) + for (j = 0; server[i].map[j][0]; j++) + //printf("%s : %s = %d\n", server[i].map[j], map, strncmp(server[i].map[j], temp_map, temp_map_len)); + if (strncmp(server[i].map[j], temp_map, temp_map_len) == 0) { +// printf("found -> server #%d.\n", i); + return i; + } + +// printf("not found.\n"); + return -1; +} + +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; + } + } +// printf("LAN test (result): %s source\033[0m.\n", (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); + return lancheck; +} + +int parse_char(int fd) { + int i, ch = 0; + char email[40]; + struct char_session_data *sd; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + + if(login_fd < 0 || session[fd]->eof) { + if (fd == login_fd) + login_fd = -1; + close(fd); + delete_session(fd); + return 0; + } + + sd = session[fd]->session_data; + + while(RFIFOREST(fd) >= 2) { +// if (RFIFOW(fd, 0) < 30000) +// printf("parse_char : %d %d %x\n", fd, RFIFOREST(fd), RFIFOW(fd, 0)); + + switch(RFIFOW(fd, 0)) { + case 0x20b: //20040622 encryption ragexe correspondence + if (RFIFOREST(fd) < 19) + return 0; + RFIFOSKIP(fd,19); + break; + + case 0x65: // request to connect + printf("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; + { +/*removed from isGM setup + if (isGM(RFIFOL(fd,2))) + printf("Account Logged On; Account ID: %d (GM level %d).\n", RFIFOL(fd,2), isGM(RFIFOL(fd,2))); + else + printf("Account Logged On; Account ID: %d.\n", RFIFOL(fd,2)); +*/ + if (sd == NULL) { + CREATE(session[fd]->session_data, struct char_session_data, 1); + sd = 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 (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; + WFIFOW(fd,2) = 0; + WFIFOSET(fd,3); + } + } + } + RFIFOSKIP(fd, 17); + break; + + case 0x66: // char select +// printf("0x66> request connect - account_id:%d/char_num:%d\n",sd->account_id,RFIFOB(fd, 2)); + if (RFIFOREST(fd) < 3) + return 0; + + sprintf(tmp_sql, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'",char_db, sd->account_id, RFIFOB(fd, 2)); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + + sql_row = mysql_fetch_row(sql_res); + + if (sql_row) + mmo_char_fromsql(atoi(sql_row[0]), char_dat, 0); + else { + mysql_free_result(sql_res); + RFIFOSKIP(fd, 3); + break; + } + + 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), char_dat[0].name); + //query + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + printf("(\033[1;64m%d\033[0m) char selected (\033[1;32m%d\033[0m) \033[1;32m%s\033[0m" RETCODE, sd->account_id, RFIFOB(fd, 2), char_dat[0].name); + + i = search_mapserver(char_dat[0].last_point.map); + + // if map is not found, we check major cities + if (i < 0) { + if ((i = search_mapserver("prontera.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[0].last_point.map, "prontera.gat", 16); + char_dat[0].last_point.x = 273; // savepoint coordonates + char_dat[0].last_point.y = 354; + } else if ((i = search_mapserver("geffen.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[0].last_point.map, "geffen.gat", 16); + char_dat[0].last_point.x = 120; // savepoint coordonates + char_dat[0].last_point.y = 100; + } else if ((i = search_mapserver("morocc.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[0].last_point.map, "morocc.gat", 16); + char_dat[0].last_point.x = 160; // savepoint coordonates + char_dat[0].last_point.y = 94; + } else if ((i = search_mapserver("alberta.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[0].last_point.map, "alberta.gat", 16); + char_dat[0].last_point.x = 116; // savepoint coordonates + char_dat[0].last_point.y = 57; + } else if ((i = search_mapserver("payon.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[0].last_point.map, "payon.gat", 16); + char_dat[0].last_point.x = 87; // savepoint coordonates + char_dat[0].last_point.y = 117; + } else if ((i = search_mapserver("izlude.gat")) >= 0) { // check is done without 'gat'. + memcpy(char_dat[0].last_point.map, "izlude.gat", 16); + char_dat[0].last_point.x = 94; // savepoint coordonates + char_dat[0].last_point.y = 103; + } else { + int j; + // get first online server + i = 0; + for(j = 0; j < MAX_MAP_SERVERS; j++) + if (server_fd[j] >= 0 && server[j].map[0][0]) { + i = j; + printf("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; + WFIFOL(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + RFIFOSKIP(fd,3); + break; + } + } + } + WFIFOW(fd, 0) =0x71; + WFIFOL(fd, 2) =char_dat[0].char_id; + memcpy(WFIFOP(fd, 6), char_dat[0].last_point.map, 16); + //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; + } +// printf("auth_fifo set (auth_fifo_pos:%d) - account_id:%d char_id:%d login_id1:%d\n", auth_fifo_pos, sd->account_id, char_dat[0].char_id, sd->login_id1); + 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 = sd->found_char[ch]; + 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; + auth_fifo_pos++; +// printf("0x66> end\n"); + RFIFOSKIP(fd, 3); + break; + + case 0x67: // make new +// printf("0x67>request make new char\n"); + if (RFIFOREST(fd) < 37) + return 0; + 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; + } + + WFIFOW(fd, 0) = 0x6d; + memset(WFIFOP(fd, 2), 0x00, 106); + + mmo_char_fromsql(i, char_dat, 0); + 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, 24); + + WFIFOB(fd,2+98) = char_dat[i].str; + WFIFOB(fd,2+99) = char_dat[i].agi; + WFIFOB(fd,2+100) = char_dat[i].vit; + WFIFOB(fd,2+101) = char_dat[i].int_; + WFIFOB(fd,2+102) = char_dat[i].dex; + WFIFOB(fd,2+103) = 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; + } + } + + case 0x68: // delete + if (RFIFOREST(fd) < 46) + return 0; + printf("\033[1;31m Request Char Del:\033[0m \033[1;32m%d\033[0m(\033[1;32m%d\033[0m)\n", sd->account_id, RFIFOL(fd, 2)); + memcpy(email, RFIFOP(fd,6), 40); + sprintf(tmp_sql, "SELECT `email` FROM `%s` WHERE `%s`='%d'",login_db, login_db_account_id, sd->account_id); + if (mysql_query(&lmysql_handle, tmp_sql)) { + printf("\033[1;31m DB server Error Delete Char data - %s \033[0m \n", mysql_error(&lmysql_handle)); + } + sql_res = mysql_store_result(&lmysql_handle); + if (sql_res) { + sql_row = mysql_fetch_row(sql_res); + if (strcmp(email,sql_row[0]) == 0) { + mysql_free_result(sql_res); + } else { + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + RFIFOSKIP(fd, 46); + mysql_free_result(sql_res); + break; + } + } else { + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + RFIFOSKIP(fd, 46); + mysql_free_result(sql_res); + break; + } + sprintf(tmp_sql, "SELECT `name`,`partner_id` FROM `%s` WHERE `char_id`='%d'",char_db, RFIFOL(fd,2)); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle); + if(sql_res) + sql_row = mysql_fetch_row(sql_res); + + if (sql_res && sql_row[0]) { + //delete char from SQL + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",pet_db, RFIFOL(fd, 2)); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",inventory_db, RFIFOL(fd, 2)); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",cart_db, RFIFOL(fd, 2)); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",memo_db, RFIFOL(fd, 2)); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",skill_db, RFIFOL(fd, 2)); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",char_db, RFIFOL(fd, 2)); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + //Divorce [Wizputer] + if (sql_row[1] != 0) { + char buf[64]; + sprintf(tmp_sql,"UPDATE `%s` SET `partner_id`='0' WHERE `char_id`='%d'",char_db,atoi(sql_row[1])); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sprintf(tmp_sql,"DELETE FROM `%s` WHERE (`nameid`='%d' OR `nameid`='%d') AND `char_id`='%d'",inventory_db,WEDDING_RING_M,WEDDING_RING_F,atoi(sql_row[1])); + if(mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + WBUFW(buf,0) = 0x2b12; + WBUFL(buf,2) = atoi(sql_row[0]); + WBUFL(buf,6) = atoi(sql_row[1]); + mapif_sendall(buf,10); + } + // Also delete info from guildtables. + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",guild_member_db, RFIFOL(fd,2)); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql, "SELECT `guild_id` FROM `%s` WHERE `master` = '%s'", guild_db, sql_row[0]); + + if (mysql_query(&mysql_handle, tmp_sql) == 0) { + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res != NULL) { + if (mysql_num_rows(sql_res) != 0) { + sql_row = mysql_fetch_row(sql_res); + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_db, atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_member_db, atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_castle_db, atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_storage_db, atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d' OR `alliance_id` = '%d'", guild_alliance_db, atoi(sql_row[0]), atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_position_db, atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_skill_db, atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `guild_id` = '%d'", guild_expulsion_db, atoi(sql_row[0])); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + mysql_free_result(sql_res); + } + } else { + if (mysql_errno(&mysql_handle) != 0) { + printf("Database server error: %s\n", mysql_error(&mysql_handle)); + } + } + } else { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + + for(i = 0; i < 9; i++) { + printf("char comp: %d - %d (%d)\n", sd->found_char[i], RFIFOL(fd, 2), sd->account_id); + if (sd->found_char[i] == RFIFOL(fd, 2)) { + for(ch = i; ch < 9-1; ch++) + sd->found_char[ch] = sd->found_char[ch+1]; + sd->found_char[8] = -1; + break; + } + } + if (i == 9) { // reject + WFIFOW(fd, 0) = 0x70; + WFIFOB(fd, 2) = 0; + WFIFOSET(fd, 3); + } else { // deleted! + 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(RFIFOP(fd,2), userid) || strcmp(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; + if(anti_freeze_enable) + server_freezeflag[i] = 5; // Map anti-freeze system. Counter. 5 ok, 4...0 freezed + server[i].ip = RFIFOL(fd, 54); + server[i].port = RFIFOW(fd, 58); + server[i].users = 0; + memset(server[i].map, 0, sizeof(server[i].map)); + 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; +} + +// 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) { + 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) { + 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]) { + 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(); + 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; +} + +int check_connect_login_server(int tid, unsigned int tick, int id, int data) { + if (login_fd <= 0 || session[login_fd] == NULL) { + printf("Attempt to connect to login-server...\n"); + login_fd = make_connection(login_ip, login_port); + session[login_fd]->func_parse = parse_tologin; + realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + WFIFOW(login_fd,0) = 0x2710; + memset(WFIFOP(login_fd,2), 0, 24); + memcpy(WFIFOP(login_fd,2), userid, strlen(userid) < 24 ? strlen(userid) : 24); + memset(WFIFOP(login_fd,26), 0, 24); + memcpy(WFIFOP(login_fd,26), passwd, strlen(passwd) < 24 ? strlen(passwd) : 24); + WFIFOL(login_fd,50) = 0; + WFIFOL(login_fd,54) = char_ip; + WFIFOL(login_fd,58) = char_port; + memset(WFIFOP(login_fd,60), 0, 20); + memcpy(WFIFOP(login_fd,60), server_name, strlen(server_name) < 20 ? strlen(server_name) : 20); + WFIFOW(login_fd,80) = 0; + WFIFOW(login_fd,82) = char_maintenance; + WFIFOW(login_fd,84) = char_new; + WFIFOSET(login_fd,86); + } + return 0; +} + +//---------------------------------------------------------- +// Return numerical value of a switch configuration by [Yor] +// on/off, english, fran軋is, deutsch, espaol +//---------------------------------------------------------- +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) { + printf("file not found: %s\n", lancfgName); + return 1; + } + + printf("Start reading of Lan Support configuration file\n"); + + 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; + } + printf("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; + printf("set subnetmask : %s\n", w2); + } + } + fclose(fp); + + printf("End reading of Lan Support configuration file\n"); + return 0; +} + +void do_final(void) { + printf("Doing final stage...\n"); + //mmo_char_sync(); + //inter_save(); + do_final_itemdb(); + //check SQL save progress. + //wait until save char complete + printf("waiting until char saving complete...\n"); + do { + sleep (0); + }while (save_flag != 0); + + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `online`='1'", char_db); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (insert `char`)- %s\n", mysql_error(&mysql_handle)); + } + + sprintf(tmp_sql,"DELETE FROM `ragsrvinfo"); + if (mysql_query(&mysql_handle, tmp_sql)) { + printf("DB server Error (insert `char`)- %s\n", mysql_error(&mysql_handle)); + } + + if (gm_account != NULL) + free(gm_account); + + free(char_dat); + free(gm_account); + delete_session(login_fd); + delete_session(char_fd); + + mysql_close(&mysql_handle); + mysql_close(&lmysql_handle); + + printf("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; + + printf("reading configure: %s\n", cfgName); + + if ((fp = fopen(cfgName, "r")) == NULL) { + printf("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, "login_db") == 0) { + strcpy(login_db, w2); + }else if(strcmpi(w1,"char_db")==0){ + strcpy(char_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); + //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] + if (strcmpi(w2, "yes")) { + db_use_sqldbs = 1; + } else if (strcmpi(w2, "no")) { + db_use_sqldbs = 0; + } + printf("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); + printf("set lowest_gm_level : %s\n",w2); + } + } + fclose(fp); + printf("reading configure done.....\n"); +} + +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) { + printf("Configuration file not found: %s.\n", cfgName); + exit(1); + } + + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars(w1); + remove_control_chars(w2); + if (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, 16); + printf("%s server has been intialized\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) { + h = gethostbyname (w2); + if (h != NULL) { + printf("Login server IP address : %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + sprintf(login_ip_str, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } else + memcpy(login_ip_str,w2,16); + } else if (strcmpi(w1, "login_port") == 0) { + login_port=atoi(w2); + } else if (strcmpi(w1, "char_ip") == 0) { + h = gethostbyname (w2); + if(h != NULL) { + printf("Character server IP address : %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + sprintf(char_ip_str, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } else + memcpy(char_ip_str, w2, 16); + } else if (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, "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, "check_ip_flag") == 0) { + check_ip_flag = 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, "start_point") == 0) { + char map[32]; + int x, y; + if (sscanf(w2,"%[^,],%d,%d", map, &x, &y) < 3) + continue; + if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name + memcpy(start_point.map, map, 16); + start_point.x = x; + start_point.y = y; + } + } else if (strcmpi(w1, "start_zeny") == 0) { + start_zeny = atoi(w2); + if (start_zeny < 0) + start_zeny = 0; + } else if (strcmpi(w1, "start_weapon") == 0) { + start_zeny = atoi(w2); + if (start_weapon < 0) + start_weapon = 0; + } else if (strcmpi(w1, "start_armor") == 0) { + start_zeny = 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[24] = 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); + // anti-freeze options [Valaris] + } else if(strcmpi(w1,"anti_freeze_enable")==0){ + anti_freeze_enable = config_switch(w2); + } else if (strcmpi(w1, "anti_freeze_interval") == 0) { + ANTI_FREEZE_INTERVAL = atoi(w2); + if (ANTI_FREEZE_INTERVAL < 5) + ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds + } else if (strcmpi(w1, "import") == 0) { + char_config_read(w2); + } + } + fclose(fp); + +//Read ItemDB + do_init_itemdb(); + + 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; + } + + 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); + + printf("charserver configuration reading done.....\n"); + + inter_init((argc > 2) ? argv[2] : inter_cfgName); // inter server テハア篳ュ + printf("interserver configuration reading done.....\n"); + + printf("start char server initializing.....\n"); + mmo_char_sql_init(); + printf("char server initializing done.....\n"); + + printf("set parser -> parse_char().....\n"); + set_defaultparse(parse_char); + + printf("set terminate function -> do_final().....\n"); + set_termfunc(do_final); + + printf("open port %d.....\n",char_port); + char_fd = make_listen_port(char_port); + + login_ip = inet_addr(login_ip_str); + char_ip = inet_addr(char_ip_str); + + // send ALIVE PING to login server. + printf("add interval tic (check_connect_login_server)....\n"); + i = add_timer_interval(gettick() + 10, check_connect_login_server, 0, 0, 10 * 1000); + + // send USER COUNT PING to login server. + printf("add interval tic (send_users_tologin)....\n"); + i = add_timer_interval(gettick() + 10, send_users_tologin, 0, 0, 5 * 1000); + + //no need to set sync timer on SQL version. + //printf("add interval tic (mmo_char_sync_timer)....\n"); + //i = add_timer_interval(gettick() + 10, mmo_char_sync_timer, 0, 0, autosave_interval); + + if(anti_freeze_enable > 0) { + add_timer_func_list(map_anti_freeze_system, "map_anti_freeze_system"); + i = add_timer_interval(gettick() + 1000, map_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); // checks every X seconds user specifies + } + + read_gm_account(); + + printf("char server init func end (now unlimited loop start!)....\n"); + printf("The char-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", char_port); + return 0; +} + + diff --git a/src/char_sql/char.h b/src/char_sql/char.h new file mode 100644 index 0000000..116a301 --- /dev/null +++ b/src/char_sql/char.h @@ -0,0 +1,82 @@ +#include "../common/core.h" +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/mmo.h" +#include "../common/version.h" +#include "../common/db.h" + +#ifndef _CHAR_H_ +#define _CHAR_H_ + +#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; + char map[MAX_MAP_PER_SERVER][16]; +}; +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]; + short broken; +}; +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 itemtemp mapitem, int eqcount, int noteqcount, 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); + +extern int autosave_interval; +extern char char_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]; + +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; + +#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 0000000..1983896 --- /dev/null +++ b/src/char_sql/int_guild.c @@ -0,0 +1,1604 @@ +// +// original code from athena +// SQL conversion by hack +// + +#include "char.h" +#include "strlib.h" +#include "int_storage.h" +#include "inter.h" +#include "int_guild.h" +#include "int_storage.h" +#include "mmo.h" +#include "socket.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +static struct guild *guild_pt; +static struct guild *guild_pt2; +static struct guild_castle * guildcastle_pt; +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(void *key,void *data,va_list ap); + + +// Save guild into sql +int inter_guild_tosql(struct guild *g,int flag) +{ + // 1 `guild` (`guild_id`, `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`castle_id`,`mes1`,`mes2`,`emblem_len`,`emblem_id`,`emblem_data`) + // 2 `guild_member` (`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name`) + // 4 `guild_position` (`guild_id`,`position`,`name`,`mode`,`exp_mode`) + // 8 `guild_alliance` (`guild_id`,`opposition`,`alliance_id`,`name`) + // 16 `guild_expulsion` (`guild_id`,`name`,`mes`,`acc`,`account_id`,`rsv1`,`rsv2`,`rsv3`) + // 32 `guild_skill` (`guild_id`,`id`,`lv`) + + char t_name[100],t_master[24],t_mes1[60],t_mes2[120],t_member[24],t_position[24],t_alliance[24]; // temporay storage for str convertion; + char t_ename[24],t_emes[40]; + char emblem_data[4096]; + int i=0; + int guild_exist=0,guild_member=0,guild_online_member=0; + + if (g->guild_id<=0) return -1; + + printf("(\033[1;35m%d\033[0m) Request save guild - ",g->guild_id); + + jstrescapecpy(t_name, g->name); + + //printf("- Check if guild %d exists\n",g->guild_id); + sprintf(tmp_sql, "SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_db,g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild`)- %s\n", mysql_error(&mysql_handle) ); + } + 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 + + if (guild_exist >0){ + // Check members in party + sprintf(tmp_sql,"SELECT count(*) FROM `%s` WHERE `guild_id`='%d'",guild_member_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + return -1; + } + 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_member = atoi (sql_row[0]); + // printf("- Check members in guild %d : %d \n",g->guild_id,guild_member); + + } + mysql_free_result(sql_res) ; //resource free + + // Delete old guild from sql + if (flag&1||guild_member==0){ + // printf("- Delete guild %d from guild\n",g->guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild`)- %s\n", mysql_error(&mysql_handle) ); + } + } + if (flag&2||guild_member==0){ + // printf("- Delete guild %d from guild_member\n",g->guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_member_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_member`)- %s\n", mysql_error(&mysql_handle) ); + } + 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) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } + } + if (flag&32||guild_member==0){ + // printf("- Delete guild %d from guild_skill\n",g->guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_skill_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_skill`)- %s\n", mysql_error(&mysql_handle) ); + } + } + if (flag&4||guild_member==0){ + // printf("- Delete guild %d from guild_position\n",g->guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_position_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_position`)- %s\n", mysql_error(&mysql_handle) ); + } + } + if (flag&16||guild_member==0){ + // printf("- Delete guild %d from guild_expulsion\n",g->guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_expulsion_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_expulsion`)- %s\n", mysql_error(&mysql_handle) ); + } + } + if (flag&8||guild_member==0){ + // printf("- Delete guild %d from guild_alliance\n",g->guild_id); + 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) ) { + printf("DB server Error (delete `guild_alliance`)- %s\n", mysql_error(&mysql_handle) ); + } + } + if (flag&2||guild_member==0){ + // printf("- Delete guild %d from char\n",g->guild_id); + 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) ) { + printf("DB server Error (delete `guild_alliance`)- %s\n", mysql_error(&mysql_handle) ); + } + } + if (guild_member==0){ + // printf("- Delete guild %d from guild_castle\n",g->guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_castle_db, g->guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_castle`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + + guild_online_member = 0; + i=0; + while (i<g->max_member) { + if (g->member[i].account_id>0) guild_online_member++; + i++; + } + + // No member in guild , no need to create it in sql + if (guild_member <= 0 && guild_online_member <=0) { + inter_guild_storage_delete(g->guild_id); + printf("No member in guild %d , break it! \n",g->guild_id); + return -2; + } + + // Insert new guild to sqlserver + if (flag&1||guild_member==0){ + int len=0; + //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); + sprintf(tmp_sql,"INSERT INTO `%s` " + "(`guild_id`, `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`castle_id`,`mes1`,`mes2`,`emblem_len`,`emblem_id`,`emblem_data`) " + "VALUES ('%d', '%s', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%d', '%d', '%s')", + 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,g->castle_id, + jstrescapecpy(t_mes1,g->mes1),jstrescapecpy(t_mes2,g->mes2),g->emblem_len,g->emblem_id,emblem_data); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (insert `guild`)- %s\n", mysql_error(&mysql_handle) ); + } + } + + if (flag&2||guild_member==0){ + //printf("- Insert guild %d to guild_member\n",g->guild_id); + for(i=0;i<g->max_member;i++){ + if (g->member[i].account_id>0){ + struct guild_member *m = &g->member[i]; + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `char_id`='%d'",guild_member_db, m->char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_member`)- %s\n", mysql_error(&mysql_handle) ); + } + sprintf(tmp_sql,"INSERT INTO `%s` " + "(`guild_id`,`account_id`,`char_id`,`hair`,`hair_color`,`gender`,`class`,`lv`,`exp`,`exp_payper`,`online`,`position`,`rsv1`,`rsv2`,`name`) " + "VALUES ('%d','%d','%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, + 0,0, + jstrescapecpy(t_member,m->name)); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (insert `guild_member`)- %s\n", mysql_error(&mysql_handle) ); + } + sprintf(tmp_sql, "UPDATE `%s` SET `guild_id`='%d' WHERE `account_id`='%d' AND `char_id`='%d'",char_db, g->guild_id,m->account_id,m->char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + } + + if (flag&4||guild_member==0){ + //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,"INSERT 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) ) { + printf("DB server Error (insert `guild_position`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + + if (flag&8||guild_member==0){ + //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,"INSERT 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) ) { + printf("DB server Error (insert `guild_alliance`)- %s\n", mysql_error(&mysql_handle) ); + } + sprintf(tmp_sql,"INSERT 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) ) { + printf("DB server Error (insert `guild_alliance`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + } + + if (flag&16||guild_member==0){ + //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,"INSERT 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) ) { + printf("DB server Error (insert `guild_expulsion`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + } + + if (flag&32||guild_member==0){ + //printf("- Insert guild %d to guild_skill\n",g->guild_id); + for(i=0;i<MAX_GUILDSKILL;i++){ + if (g->skill[i].id>0){ + sprintf(tmp_sql,"INSERT 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) ) { + printf("DB server Error (insert `guild_skill`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + } + + printf("Save guild done\n"); + return 0; +} + +// Read guild from sql +int inter_guild_fromsql(int guild_id,struct guild *g) +{ + int i; + char emblem_data[4096]; + char *pstr; + + if (g==NULL) return 0; + memset(g,0,sizeof(struct guild)); + if (guild_id==0) return 0; + +// printf("Retrieve guild information from sql ......\n"); +// printf("- Read guild %d from sql \n",guild_id); + + sprintf(tmp_sql,"SELECT `guild_id`, `name`,`master`,`guild_lv`,`connect_member`,`max_member`,`average_lv`,`exp`,`next_exp`,`skill_point`,`castle_id`,`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) ) { + printf("DB server Error (select `guild`)- %s\n", mysql_error(&mysql_handle) ); + 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); + if (sql_row==NULL) { + mysql_free_result(sql_res); + return 0; + } + + g->guild_id=atoi(sql_row[0]); + strncpy(g->name,sql_row[1],24); + strncpy(g->master,sql_row[2],24); + g->guild_lv=atoi(sql_row[3]); + g->connect_member=atoi(sql_row[4]); + g->max_member=atoi(sql_row[5]); + g->average_lv=atoi(sql_row[6]); + g->exp=atoi(sql_row[7]); + g->next_exp=atoi(sql_row[8]); + g->skill_point=atoi(sql_row[9]); + g->castle_id=atoi(sql_row[10]); + strncpy(g->mes1,sql_row[11],60); + strncpy(g->mes2,sql_row[12],120); + g->emblem_len=atoi(sql_row[13]); + g->emblem_id=atoi(sql_row[14]); + strncpy(emblem_data,sql_row[15],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) ) { + printf("DB server Error (select `guild_member`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + 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]); + m->position=atoi(sql_row[11]); + strncpy(m->name,sql_row[14],24); + } + } + 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) ) { + printf("DB server Error (select `guild_position`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + 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],24); + 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) ) { + printf("DB server Error (select `guild_alliance`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + 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],24); + } + } + 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) ) { + printf("DB server Error (select `guild_expulsion`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + 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],24); + strncpy(e->mes,sql_row[2],40); + strncpy(e->acc,sql_row[3],24); + 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) ) { + printf("DB server Error (select `guild_skill`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + 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_GUILDSKILL);i++){ + g->skill[i].id=atoi(sql_row[1]); + g->skill[i].lv=atoi(sql_row[2]); + } + } + mysql_free_result(sql_res); + +// printf("Successfully retrieve guild information from sql!\n"); + + return 0; + +} + +// Save guild_castle to sql +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; + //printf("Save to guild_castle\n"); + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `castle_id`='%d'",guild_castle_db, gc->castle_id); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + return 0; + } + + sprintf(tmp_sql,"INSERT 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->visibleG0, gc->visibleG1, gc->visibleG2, gc->visibleG3, gc->visibleG4, gc->visibleG5, + gc->visibleG6, gc->visibleG7, gc->Ghp0, gc->Ghp1, gc->Ghp2, gc->Ghp3, gc->Ghp4, gc->Ghp5, gc->Ghp6, gc->Ghp7); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + return 0; + } + + sprintf(tmp_sql,"UPDATE `%s` SET `castle_id`='-1' WHERE `castle_id`='%d'",guild_db, gc->castle_id); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + return 0; + } + + sprintf(tmp_sql,"UPDATE `%s` SET `castle_id`='%d' WHERE `guild_id`='%d'",guild_db, gc->castle_id,gc->guild_id); + //printf(" %s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + return 0; + } + + return 0; +} +// Read guild_castle from sql +int inter_guildcastle_fromsql(int castle_id,struct guild_castle *gc) +{ + + if (gc==NULL) return 0; + //printf("Read from guild_castle\n"); + memset(gc,0,sizeof(struct guild_castle)); + gc->castle_id=castle_id; + if (castle_id==-1) return 0; + 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) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + 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); + if (sql_row==NULL){ + mysql_free_result(sql_res); + return 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->visibleG0 = atoi (sql_row[10]); + gc->visibleG1 = atoi (sql_row[11]); + gc->visibleG2 = atoi (sql_row[12]); + gc->visibleG3 = atoi (sql_row[13]); + gc->visibleG4 = atoi (sql_row[14]); + gc->visibleG5 = atoi (sql_row[15]); + gc->visibleG6 = atoi (sql_row[16]); + gc->visibleG7 = atoi (sql_row[17]); + gc->Ghp0 = atoi (sql_row[18]); + gc->Ghp1 = atoi (sql_row[19]); + gc->Ghp2 = atoi (sql_row[20]); + gc->Ghp3 = atoi (sql_row[21]); + gc->Ghp4 = atoi (sql_row[22]); + gc->Ghp5 = atoi (sql_row[23]); + gc->Ghp6 = atoi (sql_row[24]); + gc->Ghp7 = atoi (sql_row[25]); + + //printf("Read Castle %d of guild %d from sql \n",castle_id,gc->guild_id); + + } + mysql_free_result(sql_res) ; //resource free + return 0; +} + +// Read exp_guild.txt +int inter_guild_readdb() +{ + int i; + FILE *fp; + char line[1024]; + for (i=0;i<100;i++) guild_exp[i]=0; + + fp=fopen("db/exp_guild.txt","r"); + if(fp==NULL){ + printf("can't read db/exp_guild.txt\n"); + return 1; + } + i=0; + while(fgets(line,256,fp) && i<100){ + if(line[0]=='/' && line[1]=='/') + continue; + guild_exp[i]=atoi(line); + i++; + } + fclose(fp); + + return 0; +} + + +// Initialize guild sql +int inter_guild_sql_init() +{ + int i; + + printf("interserver guild memory initialize.... (%d byte)\n",sizeof(struct guild)); + guild_pt = calloc(sizeof(struct guild), 1); + guild_pt2= calloc(sizeof(struct guild), 1); + guildcastle_pt=calloc(sizeof(struct guild_castle), 1); + + inter_guild_readdb(); // Read exp + + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0'",guild_member_db); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + exit(0); + } + + sprintf (tmp_sql , "SELECT count(*) FROM `%s`",guild_db); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + exit(0); + } + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); + printf("total guild 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(`guild_id`) FROM `%s`",guild_db); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + exit(0); + } + + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); + guild_newid = atoi(sql_row[0])+1; + mysql_free_result(sql_res); + } + + printf("set guild_newid: %d.......\n",guild_newid); + + return 0; +} + + +// Get guild by its name +struct guild* search_guildname(char *str) +{ + struct guild *g=guild_pt; + char t_name[24]; + int guild_id=0; + printf("search_guildname\n"); + sprintf (tmp_sql , "SELECT `guild_id` FROM `%s` WHERE `name`='%s'",guild_db, jstrescapecpy(t_name,str)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("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) { + sql_row = mysql_fetch_row(sql_res); + guild_id = atoi (sql_row[0]); + } + mysql_free_result(sql_res); + inter_guild_fromsql(guild_id,g); + return g; +} + +// 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; + } + } + + // 誰もいないので解散 + mapif_guild_broken(g->guild_id,0); + inter_guild_storage_delete(g->guild_id); + inter_guild_tosql(g,255); + memset(g,0,sizeof(struct guild)); + return 1; +} + +int guild_nextexp(int level) +{ + if(level < 100 && level >0) // Change by hack + return guild_exp[level-1]; + + return 0; +} + +// ギルドスキルがあるか確認 +int guild_checkskill(struct guild *g,int id){ return g->skill[id-10000].lv; } + +// ギルドの情報の再計算 +int guild_calcinfo(struct guild *g) +{ + int i,c,nextexp; + struct guild before=*g; + + // スキルIDの設定 + for(i=0;i<5;i++) + g->skill[i].id=i+10000; + + // ギルドレベル + if(g->guild_lv<=0) g->guild_lv=1; + nextexp = guild_nextexp(g->guild_lv); + if(nextexp > 0) { + while(g->exp >= nextexp && nextexp>0){ // Change by hack + g->exp-=nextexp; + g->guild_lv++; + g->skill_point++; + nextexp = guild_nextexp(g->guild_lv); + } + } + + // ギルドの次の経験値 + g->next_exp = guild_nextexp(g->guild_lv); + + // メンバ上限(ギルド拡張適用) + g->max_member=16+guild_checkskill(g,10004)*2; // Updated max_members [PoW] + + // 平均レベルとオンライン人数 + 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++; + } + } + g->average_lv/=c; + + // 全データを送る必要がありそう + if( g->max_member!=before.max_member || + g->guild_lv!=before.guild_lv || + g->skill_point!=before.skill_point ){ + mapif_guild_info(-1,g); + return 1; + } + + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// ギルド作成可否 +int mapif_guild_created(int fd,int account_id,struct guild *g) +{ + WFIFOW(fd,0)=0x3830; + WFIFOL(fd,2)=account_id; + if(g!=NULL){ + WFIFOL(fd,6)=g->guild_id; + printf("int_guild: created! %d %s\n",g->guild_id,g->name); + }else{ + WFIFOL(fd,6)=0; + } + WFIFOSET(fd,10); + return 0; +} +// ギルド情報見つからず +int mapif_guild_noinfo(int fd,int guild_id) +{ + WFIFOW(fd,0)=0x3831; + WFIFOW(fd,2)=8; + WFIFOL(fd,4)=guild_id; + WFIFOSET(fd,8); + printf("int_guild: info not found %d\n",guild_id); + return 0; +} +// ギルド情報まとめ送り +int mapif_guild_info(int fd,struct guild *g) +{ + unsigned char buf[16384]; + WBUFW(buf,0)=0x3831; + memcpy(buf+4,g,sizeof(struct guild)); + WBUFW(buf,2)=4+sizeof(struct guild); +// printf("int_guild: sizeof(guild)=%d\n",sizeof(struct guild)); + if(fd<0) + mapif_sendall(buf,WBUFW(buf,2)); + else + mapif_send(fd,buf,WBUFW(buf,2)); +// printf("int_guild: info %d %s\n",p->guild_id,p->name); + return 0; +} + +// メンバ追加可否 +int mapif_guild_memberadded(int fd,int guild_id,int account_id,int char_id,int flag) +{ + WFIFOW(fd,0)=0x3832; + WFIFOL(fd,2)=guild_id; + WFIFOL(fd,6)=account_id; + WFIFOL(fd,10)=char_id; + WFIFOB(fd,14)=flag; + WFIFOSET(fd,15); + return 0; +} +// 脱退/追放通知 +int mapif_guild_leaved(int guild_id,int account_id,int char_id,int flag, + const char *name,const char *mes) +{ + unsigned char buf[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,24); + mapif_sendall(buf,79); + printf("int_guild: guild leaved %d %d %s %s\n",guild_id,account_id,name,mes); + return 0; +} + +// オンライン状態とLv更新通知 +int mapif_guild_memberinfoshort(struct guild *g,int idx) +{ + unsigned char buf[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)=g->member[idx].online; + WBUFW(buf,15)=g->member[idx].lv; + WBUFW(buf,17)=g->member[idx].class; + mapif_sendall(buf,19); + return 0; +} + +// 解散通知 +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); + printf("int_guild: broken %d\n",guild_id); + return 0; +} + +// ギルド内発言 +int mapif_guild_message(int guild_id,int account_id,char *mes,int len) +{ + unsigned char buf[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_sendall(buf,len+12); + return 0; +} + +// ギルド基本情報変更通知 +int mapif_guild_basicinfochanged(int guild_id,int type,const void *data,int len) +{ + unsigned char buf[2048]; + WBUFW(buf, 0)=0x3839; + WBUFW(buf, 2)=len+10; + WBUFL(buf, 4)=guild_id; + WBUFW(buf, 8)=type; + memcpy(WBUFP(buf,10),data,len); + mapif_sendall(buf,len+10); + return 0; +} +// ギルドメンバ情報変更通知 +int mapif_guild_memberinfochanged(int guild_id,int account_id,int char_id, + int type,const void *data,int len) +{ + unsigned char buf[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; +} +// ギルドスキルアップ通知 +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; +} +// ギルド同盟/敵対通知 +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,24); + memcpy(WBUFP(buf,43),name2,24); + mapif_sendall(buf,67); + return 0; +} + +// ギルド役職変更通知 +int mapif_guild_position(struct guild *g,int idx) +{ + unsigned char buf[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; +} + +// ギルド告知変更通知 +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; +} +// ギルドエンブレム変更通知 +int mapif_guild_emblem(struct guild *g) +{ + unsigned char buf[2048]; + WBUFW(buf,0)=0x383f; + WBUFW(buf,2)=g->emblem_len+12; + WBUFL(buf,4)=g->guild_id; + WBUFL(buf,8)=g->emblem_id; + memcpy(WBUFP(buf,12),g->emblem_data,g->emblem_len); + mapif_sendall(buf,WBUFW(buf,2)); + return 0; +} + +int mapif_guild_castle_dataload(int castle_id,int index,int value) // <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 = guildcastle_pt; + 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)) { + printf("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) { + 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->visibleG0 = atoi(sql_row[10]); + gc->visibleG1 = atoi(sql_row[11]); + gc->visibleG2 = atoi(sql_row[12]); + gc->visibleG3 = atoi(sql_row[13]); + gc->visibleG4 = atoi(sql_row[14]); + gc->visibleG5 = atoi(sql_row[15]); + gc->visibleG6 = atoi(sql_row[16]); + gc->visibleG7 = atoi(sql_row[17]); + gc->Ghp0 = atoi(sql_row[18]); + gc->Ghp1 = atoi(sql_row[19]); + gc->Ghp2 = atoi(sql_row[20]); + gc->Ghp3 = atoi(sql_row[21]); + gc->Ghp4 = atoi(sql_row[22]); + gc->Ghp5 = atoi(sql_row[23]); + gc->Ghp6 = atoi(sql_row[24]); + gc->Ghp7 = 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); + + return 0; +} + + +//------------------------------------------------------------------- +// map serverからの通信 + + +// ギルド作成要求 +int mapif_parse_CreateGuild(int fd,int account_id,char *name,struct guild_member *master) +{ + struct guild *g; + int i; + + printf("CreateGuild\n"); + g=search_guildname(name); + if(g!=NULL&&g->guild_id>0){ + printf("int_guild: same name guild exists [%s]\n",name); + mapif_guild_created(fd,account_id,NULL); + return 0; + } + g=guild_pt; + memset(g,0,sizeof(struct guild)); + g->guild_id=guild_newid++; + memcpy(g->name,name,24); + memcpy(g->master,master->name,24); + memcpy(&g->member[0],master,sizeof(struct guild_member)); + + g->position[0].mode=0x11; + strcpy(g->position[ 0].name,"GuildMaster"); + strcpy(g->position[MAX_GUILDPOSITION-1].name,"Newbie"); + for(i=1;i<MAX_GUILDPOSITION-1;i++) + sprintf(g->position[i].name,"Position %d",i+1); + + // Initialize guild property + g->max_member=16; + g->average_lv=master->lv; + g->castle_id=-1; + for(i=0;i<5;i++) + g->skill[i].id=i+10000; + + // Save to sql + printf("Create initialize OK!\n"); + i=inter_guild_tosql(g,255); + + if (i<0) { + mapif_guild_created(fd,account_id,NULL); + return 0; + } + + // Report to client + mapif_guild_created(fd,account_id,g); + mapif_guild_info(fd,g); + + 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; + g=guild_pt; + inter_guild_fromsql(guild_id,g); + if(g!=NULL&&g->guild_id>0){ + guild_calcinfo(g); + mapif_guild_info(fd,g); + //inter_guild_tosql(g,1); // 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=guild_pt; + inter_guild_fromsql(guild_id,g); + + if(g==NULL||g->guild_id<=0){ + 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); + inter_guild_tosql(g,3); // Change guild & guild_member + return 0; + } + } + mapif_guild_memberadded(fd,guild_id,m->account_id,m->char_id,1); + //inter_guild_tosql(g,3); // Change guild & guild_member + 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=NULL; + g=guild_pt; + inter_guild_fromsql(guild_id,g); + + if(g!=NULL&&g->guild_id>0){ + 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){ + printf("%d %d\n",i, (int)(&g->member[i])); + printf("%d %s\n",i, g->member[i].name); + + if(flag){ // 追放の場合追放リストに入れる + int j; + for(j=0;j<MAX_GUILDEXPLUSION;j++){ + if(g->explusion[j].account_id==0) + break; + } + if(j==MAX_GUILDEXPLUSION){ // 一杯なので古いのを消す + for(j=0;j<MAX_GUILDEXPLUSION-1;j++) + g->explusion[j]=g->explusion[j+1]; + j=MAX_GUILDEXPLUSION-1; + } + g->explusion[j].account_id=account_id; + memcpy(g->explusion[j].acc,"dummy",24); + memcpy(g->explusion[j].name,g->member[i].name,24); + memcpy(g->explusion[j].mes,mes,40); + } + + mapif_guild_leaved(guild_id,account_id,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);// まだ人がいるのでデータ送信 + /* + else + inter_guild_save(); // 解散したので一応セーブ + return 0;*/ + } + } + guild_calcinfo(g); + inter_guild_tosql(g,19); // Change guild & guild_member & guild_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) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } + /* 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; + g=guild_pt; + inter_guild_fromsql(guild_id,g); + + if(g==NULL||g->guild_id<=0){ + return 0; + } + + 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++; + } + // 平均レベル + g->average_lv=alv/c; + + inter_guild_tosql(g,3); // Change guild & guild_member + + return 0; +} + +// BreakGuild +int mapif_parse_BreakGuild(int fd,int guild_id) +{ + struct guild *g; + g=guild_pt; + inter_guild_fromsql(guild_id,g); + 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) ) { + printf("DB server Error (delete `guild`)- %s\n", mysql_error(&mysql_handle) ); + } + //printf("- Delete guild %d from guild_member\n",guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_member_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_member`)- %s\n", mysql_error(&mysql_handle) ); + } + //printf("- Delete guild %d from guild_skill\n",guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_skill_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_skill`)- %s\n", mysql_error(&mysql_handle) ); + } + //printf("- Delete guild %d from guild_position\n",guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_position_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_position`)- %s\n", mysql_error(&mysql_handle) ); + } + //printf("- Delete guild %d from guild_expulsion\n",guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_expulsion_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_expulsion`)- %s\n", mysql_error(&mysql_handle) ); + } + //printf("- Delete guild %d from guild_alliance\n",guild_id); + 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) ) { + printf("DB server Error (delete `guild_position`)- %s\n", mysql_error(&mysql_handle) ); + } + + //printf("- Delete guild %d from guild_castle\n",guild_id); + sprintf(tmp_sql, "DELETE FROM `%s` WHERE `guild_id`='%d'",guild_castle_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `guild_position`)- %s\n", mysql_error(&mysql_handle) ); + } + + //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) ) { + printf("DB server Error (delete `guild_position`)- %s\n", mysql_error(&mysql_handle) ); + } + + inter_guild_storage_delete(guild_id); + mapif_guild_broken(guild_id,0); + + inter_log("guild %s (id=%d) broken" RETCODE,g->name,guild_id); + + return 0; +} + +// ギルドメッセージ送信 +int mapif_parse_GuildMessage(int fd,int guild_id,int account_id,char *mes,int len) +{ + return mapif_guild_message(guild_id,account_id,mes,len); +} +// ギルド基本データ変更要求 +int mapif_parse_GuildBasicInfoChange(int fd,int guild_id, + int type,const char *data,int len) +{ + struct guild *g; +// int dd=*((int *)data); + short dw=*((short *)data); + g=guild_pt; + inter_guild_fromsql(guild_id,g); + + if(g==NULL||g->guild_id<=0){ + return 0; + } + switch(type){ + case GBI_GUILDLV: { + printf("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); + inter_guild_tosql(g,1); + } return 0; + default: + printf("int_guild: GuildBasicInfoChange: Unknown type %d\n",type); + break; + } + mapif_guild_basicinfochanged(guild_id,type,data,len); + //inter_guild_tosql(g,1); // Change guild + return 0; +} + +// ギルドメンバデータ変更要求 +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=guild_pt; + inter_guild_fromsql(guild_id,g); + //printf("GuildMemberInfoChange %s \n",(type==GMI_EXP)?"GMI_EXP":"OTHER"); + + 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){ + printf("int_guild: GuildMemberChange: Not found %d,%d in %d[%s]\n", + account_id,char_id,guild_id,g->name); + return 0; + } + switch(type){ + case GMI_POSITION: // 役職 + g->member[i].position=*((int *)data); + break; + case GMI_EXP: { // EXP + int exp,oldexp=g->member[i].exp; + exp=g->member[i].exp=*((unsigned int *)data); + g->exp+=(exp-oldexp); + guild_calcinfo(g); // Lvアップ判断 + mapif_guild_basicinfochanged(guild_id,GBI_EXP,&g->exp,4); + }break; + default: + printf("int_guild: GuildMemberInfoChange: Unknown type %d\n",type); + break; + } + mapif_guild_memberinfochanged(guild_id,account_id,char_id,type,data,len); + inter_guild_tosql(g,3); // Change guild & guild_member + return 0; +} + +// ギルド役職名変更要求 +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=guild_pt; + inter_guild_fromsql(guild_id,g); + + if(g==NULL || idx<0 || idx>=MAX_GUILDPOSITION){ + return 0; + } + memcpy(&g->position[idx],p,sizeof(struct guild_position)); + mapif_guild_position(g,idx); + printf("int_guild: position changed %d\n",idx); + inter_guild_tosql(g,4); // Change guild_position + return 0; +} +// ギルドスキルアップ要求 +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; + g=guild_pt; + inter_guild_fromsql(guild_id,g); + int idx=skill_num-10000; + if(g==NULL || skill_num<10000) + return 0; + //printf("GuildSkillUp\n"); + + if( g->skill_point>0 && g->skill[idx].id>0 && + g->skill[idx].lv<10 ){ + g->skill[idx].lv++; + g->skill_point--; + if(guild_calcinfo(g)==0) + mapif_guild_info(-1,g); + mapif_guild_skillupack(guild_id,skill_num,account_id); + printf("int_guild: skill %d up\n",skill_num); + inter_guild_tosql(g,33); // Change guild & guild_skill + } + + return 0; +} +// ギルド同盟要求 +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]=guild_pt; + g[1]=guild_pt2; + inter_guild_fromsql(guild_id1,g[0]); + inter_guild_fromsql(guild_id2,g[1]); + + if(g[0]==NULL || g[1]==NULL || g[0]->guild_id ==0 || g[1]->guild_id==0) + return 0; + + if(!(flag&0x8)){ + for(i=0;i<2-(flag&1);i++){ + for(j=0;j<MAX_GUILDALLIANCE;j++) + if(g[i]->alliance[j].guild_id==0){ + g[i]->alliance[j].guild_id=g[1-i]->guild_id; + memcpy(g[i]->alliance[j].name,g[1-i]->name,24); + g[i]->alliance[j].opposition=flag&1; + break; + } + } + }else{ // 関係解消 + for(i=0;i<2-(flag&1);i++){ + for(j=0;j<MAX_GUILDALLIANCE;j++) + if( g[i]->alliance[j].guild_id==g[1-i]->guild_id && + g[i]->alliance[j].opposition==(flag&1)){ + g[i]->alliance[j].guild_id=0; + break; + } + } + } + mapif_guild_alliance(guild_id1,guild_id2,account_id1,account_id2,flag, + g[0]->name,g[1]->name); + inter_guild_tosql(g[0],8); // Change guild_alliance + inter_guild_tosql(g[1],8); // Change guild_alliance + return 0; +} +// ギルド告知変更要求 +int mapif_parse_GuildNotice(int fd,int guild_id,const char *mes1,const char *mes2) +{ + struct guild *g; + g=guild_pt; + inter_guild_fromsql(guild_id,g); + + if(g==NULL||g->guild_id<=0) + return 0; + memcpy(g->mes1,mes1,60); + memcpy(g->mes2,mes2,120); + inter_guild_tosql(g,1); // Change mes of guild + return mapif_guild_notice(g); +} +// ギルドエンブレム変更要求 +int mapif_parse_GuildEmblem(int fd,int len,int guild_id,int dummy,const char *data) +{ + struct guild *g; + g=guild_pt; + inter_guild_fromsql(guild_id,g); + + if(g==NULL||g->guild_id<=0) + return 0; + memcpy(g->emblem_data,data,len); + g->emblem_len=len; + g->emblem_id++; + inter_guild_tosql(g,1); // Change guild + return mapif_guild_emblem(g); +} + +int mapif_parse_GuildCastleDataLoad(int fd,int castle_id,int index) // <Agit> +{ + struct guild_castle *gc=guildcastle_pt; + inter_guildcastle_fromsql(castle_id, gc); + if(gc==NULL||gc->castle_id==-1){ + 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: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG0); break; + case 11: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG1); break; + case 12: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG2); break; + case 13: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG3); break; + case 14: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG4); break; + case 15: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG5); break; + case 16: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG6); break; + case 17: return mapif_guild_castle_dataload(gc->castle_id,index,gc->visibleG7); break; + case 18: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp0); break; // guardian HP [Valaris] + case 19: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp1); break; + case 20: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp2); break; + case 21: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp3); break; + case 22: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp4); break; + case 23: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp5); break; + case 24: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp6); break; + case 25: return mapif_guild_castle_dataload(gc->castle_id,index,gc->Ghp7); break; // end additions [Valaris] + default: + printf("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=guildcastle_pt; + inter_guildcastle_fromsql(castle_id, gc); + if(gc==NULL||gc->castle_id==-1){ + 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=guild_pt; + inter_guild_fromsql(gid, g); + inter_log("guild %s (id=%d) %s castle id=%d" RETCODE, + (g)?g->name:"??" ,gid, (value)?"occupy":"abandon", index); + } + gc->guild_id = value; + break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: gc->visibleG0 = value; break; + case 11: gc->visibleG1 = value; break; + case 12: gc->visibleG2 = value; break; + case 13: gc->visibleG3 = value; break; + case 14: gc->visibleG4 = value; break; + case 15: gc->visibleG5 = value; break; + case 16: gc->visibleG6 = value; break; + case 17: gc->visibleG7 = value; break; + case 18: gc->Ghp0 = value; break; // guardian HP [Valaris] + case 19: gc->Ghp1 = value; break; + case 20: gc->Ghp2 = value; break; + case 21: gc->Ghp3 = value; break; + case 22: gc->Ghp4 = value; break; + case 23: gc->Ghp5 = value; break; + case 24: gc->Ghp6 = value; break; + case 25: gc->Ghp7 = value; break; // end additions [Valaris] + default: + printf("mapif_parse_GuildCastleDataSave ERROR!! (Not found index=%d)\n", index); + return 0; + } + inter_guildcastle_tosql(gc); + return mapif_guild_castle_datasave(gc->castle_id,index,value); +} + +// ギルドチェック要求 +int mapif_parse_GuildCheck(int fd,int guild_id,int account_id,int char_id) +{ + // What does this mean? Check if belong to another guild? + return 0; +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_guild_parse_frommap(int fd) +{ + switch(RFIFOW(fd,0)){ + case 0x3030: mapif_parse_CreateGuild(fd,RFIFOL(fd,4),RFIFOP(fd,8),(struct guild_member *)RFIFOP(fd,32)); break; + case 0x3031: mapif_parse_GuildInfo(fd,RFIFOL(fd,2)); break; + case 0x3032: mapif_parse_GuildAddMember(fd,RFIFOL(fd,4),(struct guild_member *)RFIFOP(fd,8)); break; + case 0x3034: mapif_parse_GuildLeave(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOP(fd,15)); break; + case 0x3035: mapif_parse_GuildChangeMemberInfoShort(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17)); break; + case 0x3036: mapif_parse_BreakGuild(fd,RFIFOL(fd,2)); break; + case 0x3037: mapif_parse_GuildMessage(fd,RFIFOL(fd,4),RFIFOL(fd,8),RFIFOP(fd,12),RFIFOW(fd,2)-12); break; + case 0x3038: mapif_parse_GuildCheck(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10)); break; + case 0x3039: mapif_parse_GuildBasicInfoChange(fd,RFIFOL(fd,4),RFIFOW(fd,8),RFIFOP(fd,10),RFIFOW(fd,2)-10); break; + case 0x303A: mapif_parse_GuildMemberInfoChange(fd,RFIFOL(fd,4),RFIFOL(fd,8),RFIFOL(fd,12),RFIFOW(fd,16),RFIFOP(fd,18),RFIFOW(fd,2)-18); break; + case 0x303B: mapif_parse_GuildPosition(fd,RFIFOL(fd,4),RFIFOL(fd,8),(struct guild_position *)RFIFOP(fd,12)); break; + case 0x303C: mapif_parse_GuildSkillUp(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10)); break; + case 0x303D: mapif_parse_GuildAlliance(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOL(fd,14),RFIFOB(fd,18)); break; + case 0x303E: mapif_parse_GuildNotice(fd,RFIFOL(fd,2),RFIFOP(fd,6),RFIFOP(fd,66)); break; + case 0x303F: mapif_parse_GuildEmblem(fd,RFIFOW(fd,2)-12,RFIFOL(fd,4),RFIFOL(fd,8),RFIFOP(fd,12)); break; + case 0x3040: mapif_parse_GuildCastleDataLoad(fd,RFIFOW(fd,2),RFIFOB(fd,4)); break; + case 0x3041: mapif_parse_GuildCastleDataSave(fd,RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5)); break; + + default: + return 0; + } + return 1; +} + +int inter_guild_mapif_init(int fd) +{ + return mapif_guild_castle_alldataload(fd); +} + +// サーバーから脱退要求(キャラ削除用) +int inter_guild_leave(int guild_id,int account_id,int char_id) +{ + return mapif_parse_GuildLeave(-1,guild_id,account_id,char_id,0,"**サーバー命令**"); +} diff --git a/src/char_sql/int_guild.h b/src/char_sql/int_guild.h new file mode 100644 index 0000000..8f4203d --- /dev/null +++ b/src/char_sql/int_guild.h @@ -0,0 +1,10 @@ +#ifndef _INT_GUILD_H_ +#define _INT_GUILD_H_ + +int inter_guild_parse_frommap(int fd); +int inter_guild_sql_init(); +int inter_guild_mapif_init(int fd); + +int inter_guild_leave(int guild_id,int account_id,int char_id); + +#endif diff --git a/src/char_sql/int_party.c b/src/char_sql/int_party.c new file mode 100644 index 0000000..84de078 --- /dev/null +++ b/src/char_sql/int_party.c @@ -0,0 +1,755 @@ +// +// original code from athena +// SQL conversion by hack +// + +#include "char.h" +#include "strlib.h" +#include "socket.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static struct party *party_pt; +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); + +// Save party to mysql +int inter_party_tosql(int party_id,struct party *p) +{ + // 'party' ('party_id','name','exp','item','leader') + + char t_name[100]; + char t_member[24]; + int party_member = 0, party_online_member = 0; + int party_exist = 0; + int leader_id = 0; + int i = 0; + + printf("(\033[1;64m%d\033[0m) Request save party - ",party_id); + + jstrescapecpy(t_name, p->name); + + if (p==NULL || party_id==0 || p->party_id ==0 || party_id!=p->party_id) { + printf("- Party pointer or party_id error \n"); + return 0; + } + + // Check if party exists + sprintf(tmp_sql,"SELECT count(*) FROM `%s` WHERE `party_id`='%d'",party_db, party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + 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]); + //printf("- Check if party %d exists : %s\n",party_id,party_exist==0?"No":"Yes"); + } + mysql_free_result(sql_res) ; //resource free + + if (party_exist >0){ + // Check members in party + sprintf(tmp_sql,"SELECT count(*) FROM `%s` WHERE `party_id`='%d'",char_db, party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + 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_member = atoi (sql_row[0]); + // printf("- Check members in party %d : %d \n",party_id,party_member); + + } + mysql_free_result(sql_res) ; //resource free + + party_online_member = 0; + i=0; + while (i<MAX_PARTY){ + if (p->member[i].account_id>0) party_online_member++; + i++; + } + + //if (party_online_member==0) printf("- No member online \n"); else printf("- Some member %d online \n", party_online_member); + + if (party_member <= 0 && party_online_member == 0) { + + // Delete the party, if has no member. + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `party_id`='%d'",party_db, party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + // printf("No member in party %d, break it \n",party_id); + memset(p, 0, sizeof(struct party)); + return 0; + } else { + // Update party information, if exists + + int i=0; + + for (i=0;i<MAX_PARTY;i++){ + + if (p->member[i].account_id>0){ + sprintf(tmp_sql,"UPDATE `%s` SET `party_id`='%d', `online`='%d' WHERE `account_id`='%d' AND `name`='%s'", + char_db, party_id, p->member[i].online, p->member[i].account_id,jstrescapecpy(t_member,p->member[i].name)); + //printf("%s",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + + + sprintf(tmp_sql,"UPDATE `%s` SET `name`='%s', `exp`='%d', `item`='%d', `leader_id`=`leader_id` WHERE `party_id`='%d'", + party_db, t_name,p->exp,p->item,party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (inset/update `party`)- %s\n", mysql_error(&mysql_handle) ); + } + + + // printf("- Update party %d information \n",party_id); + } + } else { + // Add new party, if not exist + int i = 0; + while (i<MAX_PARTY&&((p->member[i].account_id>0&&p->member[i].leader==0)||(p->member[i].account_id<0))) i++; + if (i<MAX_PARTY) leader_id = p->member[i].account_id; + sprintf(tmp_sql,"INSERT INTO `%s` (`party_id`, `name`, `exp`, `item`, `leader_id`) VALUES ('%d', '%s', '%d', '%d', '%d')", + party_db, party_id, t_name, p->exp, p->item,leader_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (inset/update `party`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + + sprintf(tmp_sql,"UPDATE `%s` SET `party_id`='%d', `online`='1' WHERE `account_id`='%d' AND `name`='%s'", + char_db, party_id,leader_id, jstrescapecpy(t_member,p->member[i].name)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (inset/update `party`)- %s\n", mysql_error(&mysql_handle) ); + } + + //printf("- Insert new party %d \n",party_id); + } + + printf("Party save success\n"); + return 0; + +} + +// Read party from mysql +int inter_party_fromsql(int party_id,struct party *p) +{ + int leader_id=0; + printf("(\033[1;64m%d\033[0m) Request load party - ",party_id); + + memset(p, 0, sizeof(struct party)); + + sprintf(tmp_sql,"SELECT `party_id`, `name`,`exp`,`item`, `leader_id` FROM `%s` WHERE `party_id`='%d'",party_db, party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (select `party`)- %s\n", mysql_error(&mysql_handle) ); + 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); + // printf("- Read party %d from MySQL\n",party_id); + p->party_id = party_id; + strcpy(p->name, sql_row[1]); + p->exp = atoi(sql_row[2]); + p->item = atoi(sql_row[3]); + leader_id = atoi(sql_row[4]); + } else { + mysql_free_result(sql_res); + // printf("- Cannot find party %d \n",party_id); + return 0; + } + + mysql_free_result(sql_res); + + // Load members + sprintf(tmp_sql,"SELECT `account_id`, `name`,`base_level`,`last_map`,`online` FROM `%s` WHERE `party_id`='%d'",char_db, party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (select `party`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + 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]); + if (m->account_id == leader_id) m->leader = 1; else m->leader = 0; + strncpy(m->name,sql_row[1],sizeof(m->name)); + m->lv = atoi(sql_row[2]); + strncpy(m->map,sql_row[3],sizeof(m->map)); + m->online = atoi(sql_row[4]); + } + // printf("- %d members found in party %d \n",i,party_id); + } + mysql_free_result(sql_res); + + + printf("Party load success\n"); + return 0; + +} + +int inter_party_sql_init(){ + int i; + + //memory alloc + printf("interserver party memory initialize.... (%d byte)\n",sizeof(struct party)); + party_pt = calloc(sizeof(struct party), 1); + + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0'", char_db); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } + + sprintf (tmp_sql , "SELECT count(*) FROM `%s`",party_db); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); + printf("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) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + + 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); + } + + printf("set party_newid: %d.......\n",party_newid); + + return 0; +} + + +// Search for the party according to its name +struct party* search_partyname(char *str) +{ + struct party *p=NULL; + int leader_id = 0; + char t_name[24]; + + sprintf(tmp_sql,"SELECT `party_id`, `name`,`exp`,`item`,`leader_id` FROM `%s` WHERE `name`='%s'",party_db, jstrescapecpy(t_name,str)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (select `party`)- %s\n", mysql_error(&mysql_handle) ); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res==NULL || mysql_num_rows(sql_res)<=0) { mysql_free_result(sql_res); return p; } + sql_row = mysql_fetch_row(sql_res); + p = party_pt; + p->party_id = atoi(sql_row[0]); + strcpy(p->name, sql_row[1]); + p->exp = atoi(sql_row[2]); + p->item = atoi(sql_row[3]); + leader_id = atoi(sql_row[4]); + mysql_free_result(sql_res); + + // Load members + sprintf(tmp_sql,"SELECT `account_id`, `name`,`base_level`,`last_map`,`online` FROM `%s` WHERE `party_id`='%d'",char_db, p->party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (select `party`)- %s\n", mysql_error(&mysql_handle) ); + return 0; + } + 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]); + if (m->account_id == leader_id) m->leader = 1; else m->leader = 0; + strncpy(m->name,sql_row[1],sizeof(m->name)); + m->lv = atoi(sql_row[2]); + strncpy(m->map,sql_row[3],sizeof(m->map)); + m->online = atoi(sql_row[4]); + } + printf("- %d members found in party %d \n",i,p->party_id); + } + mysql_free_result(sql_res); + + return p; +} + +// EXP公平分配できるかチェック +int party_check_exp_share(struct party *p) +{ + int i; + int maxlv=0,minlv=0x7fffffff; + for(i=0;i<MAX_PARTY;i++){ + int lv=p->member[i].lv; + if( p->member[i].online ){ + if( lv < minlv ) minlv=lv; + if( maxlv < lv ) maxlv=lv; + } + } + return (maxlv==0 || maxlv-minlv<=party_share_level); +} +// 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; +// printf("party check empty %08X\n",(int)p); + for(i=0;i<MAX_PARTY;i++){ +// printf("%d acc=%d\n",i,p->member[i].account_id); + if(p->member[i].account_id>0){ + return 0; + } + } + // If there is no member, then break the party + mapif_party_broken(p->party_id,0); + inter_party_tosql(p->party_id,p); + return 1; +} + + +// Check if a member is in two party, not necessary :) +int party_check_conflict(int party_id,int account_id,char *nick) +{ + return 0; +} + +//------------------------------------------------------------------- +// map serverへの通信 + +// パーティ作成可否 +int mapif_party_created(int fd,int account_id,struct party *p) +{ + WFIFOW(fd,0)=0x3820; + WFIFOL(fd,2)=account_id; + if(p!=NULL){ + WFIFOB(fd,6)=0; + WFIFOL(fd,7)=p->party_id; + memcpy(WFIFOP(fd,11),p->name,24); + printf("int_party: created! %d %s\n",p->party_id,p->name); + }else{ + WFIFOB(fd,6)=1; + WFIFOL(fd,7)=0; + memcpy(WFIFOP(fd,11),"error",24); + } + WFIFOSET(fd,35); + return 0; +} + +// パーティ情報見つからず +int mapif_party_noinfo(int fd,int party_id) +{ + WFIFOW(fd,0)=0x3821; + WFIFOW(fd,2)=8; + WFIFOL(fd,4)=party_id; + WFIFOSET(fd,8); + printf("int_party: info not found %d\n",party_id); + return 0; +} +// パーティ情報まとめ送り +int mapif_party_info(int fd,struct party *p) +{ + unsigned char buf[1024]; + WBUFW(buf,0)=0x3821; + memcpy(buf+4,p,sizeof(struct party)); + WBUFW(buf,2)=4+sizeof(struct party); + if(fd<0) + mapif_sendall(buf,WBUFW(buf,2)); + else + mapif_send(fd,buf,WBUFW(buf,2)); +// printf("int_party: info %d %s\n",p->party_id,p->name); + return 0; +} +// パーティメンバ追加可否 +int mapif_party_memberadded(int fd,int party_id,int account_id,int flag) +{ + WFIFOW(fd,0)=0x3822; + WFIFOL(fd,2)=party_id; + WFIFOL(fd,6)=account_id; + WFIFOB(fd,10)=flag; + WFIFOSET(fd,11); + return 0; +} +// パーティ設定変更通知 +int mapif_party_optionchanged(int fd,struct party *p,int account_id,int flag) +{ + unsigned char buf[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); + //printf("int_party: option changed %d %d %d %d %d\n",p->party_id,account_id,p->exp,p->item,flag); + return 0; +} +// パーティ脱退通知 +int mapif_party_leaved(int party_id,int account_id,char *name) +{ + unsigned char buf[64]; + WBUFW(buf,0)=0x3824; + WBUFL(buf,2)=party_id; + WBUFL(buf,6)=account_id; + memcpy(WBUFP(buf,10),name,24); + mapif_sendall(buf,34); + //printf("int_party: party leaved %d %d %s\n",party_id,account_id,name); + return 0; +} +// パーティマップ更新通知 +int mapif_party_membermoved(struct party *p,int idx) +{ + unsigned char buf[32]; + WBUFW(buf,0)=0x3825; + WBUFL(buf,2)=p->party_id; + WBUFL(buf,6)=p->member[idx].account_id; + memcpy(WBUFP(buf,10),p->member[idx].map,16); + WBUFB(buf,26)=p->member[idx].online; + WBUFW(buf,27)=p->member[idx].lv; + mapif_sendall(buf,29); + return 0; +} +// パーティ解散通知 +int mapif_party_broken(int party_id,int flag) +{ + unsigned char buf[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; +} +// パーティ内発言 +int mapif_party_message(int party_id,int account_id,char *mes,int len) +{ + 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_sendall(buf,len+12); + return 0; +} + +//------------------------------------------------------------------- +// map serverからの通信 + + +// Create Party +int mapif_parse_CreateParty(int fd,int account_id,char *name,char *nick,char *map,int lv) +{ + struct party *p; + if( (p=search_partyname(name))!=NULL){ +// printf("int_party: same name party exists [%s]\n",name); + mapif_party_created(fd,account_id,NULL); + return 0; + } + p=party_pt; + if(p==NULL){ + printf("int_party: out of memory !\n"); + mapif_party_created(fd,account_id,NULL); + return 0; + } + memset(p,0,sizeof(struct party)); + p->party_id=party_newid++; + memcpy(p->name,name,24); + p->exp=0; + p->item=0; + p->member[0].account_id=account_id; + memcpy(p->member[0].name,nick,24); + memcpy(p->member[0].map,map,16); + p->member[0].leader=1; + p->member[0].online=1; + p->member[0].lv=lv; + + inter_party_tosql(p->party_id,p); + + mapif_party_created(fd,account_id,p); + mapif_party_info(fd,p); + + return 0; +} +// パーティ情報要求 +int mapif_parse_PartyInfo(int fd,int party_id) +{ + struct party *p = party_pt; + if(p==NULL){ + printf("int_party: out of memory !\n"); + return 0; + } + inter_party_fromsql(party_id, p); + + if(p->party_id >= 0) + mapif_party_info(fd,p); + else + mapif_party_noinfo(fd,party_id); + return 0; +} +// パーティ追加要求 +int mapif_parse_PartyAddMember(int fd,int party_id,int account_id,char *nick,char *map,int lv) +{ + struct party *p; + int i; + + p = party_pt; + if(p==NULL){ + printf("int_party: out of memory !\n"); + return 0; + } + inter_party_fromsql(party_id, p); + + if(p->party_id <= 0){ + mapif_party_memberadded(fd,party_id,account_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; + memcpy(p->member[i].name,nick,24); + memcpy(p->member[i].map,map,16); + p->member[i].leader=0; + p->member[i].online=1; + p->member[i].lv=lv; + mapif_party_memberadded(fd,party_id,account_id,0); + mapif_party_info(-1,p); + + if( p->exp>0 && !party_check_exp_share(p) ){ + p->exp=0; + flag=0x01; + } + if(flag) + mapif_party_optionchanged(fd,p,0,0); + + inter_party_tosql(party_id, p); + return 0; + } + } + mapif_party_memberadded(fd,party_id,account_id,1); + //inter_party_tosql(party_id, p); + return 0; +} +// パーティー設定変更要求 +int mapif_parse_PartyChangeOption(int fd,int party_id,int account_id,int exp,int item) +{ + struct party *p; + int flag=0; + + p = party_pt; + if(p==NULL){ + printf("int_party: out of memory !\n"); + return 0; + } + + inter_party_fromsql(party_id, p); + + if(p->party_id <= 0){ + return 0; + } + + p->exp=exp; + if( exp>0 && !party_check_exp_share(p) ){ + flag|=0x01; + p->exp=0; + } + + p->item=item; + + mapif_party_optionchanged(fd,p,account_id,flag); + inter_party_tosql(party_id, p); + return 0; +} +// パーティ脱退要求 +int mapif_parse_PartyLeave(int fd,int party_id,int account_id) +{ + char t_member[24]; + struct party *p = party_pt; + if(p==NULL){ + printf("int_party: out of memory !\n"); + return 0; + } + + inter_party_fromsql(party_id, p); + + if(p->party_id >= 0){ + int i,j; + for(i=0;i<MAX_PARTY;i++){ + + if(p->member[i].account_id==account_id){ + //printf("p->member[i].account_id = %d , account_id = %d \n",p->member[i].account_id,account_id); + mapif_party_leaved(party_id,account_id,p->member[i].name); + + + + // Update char information, does the name need encoding? + sprintf(tmp_sql,"UPDATE `%s` SET `party_id`='0', `online`='1' WHERE `party_id`='%d' AND `name`='%s'", + char_db, party_id, jstrescapecpy(t_member,p->member[i].name)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } +// printf("Delete member %s from MySQL \n", p->member[i].name); + + if (p->member[i].leader==1){ + for(j=0;j<MAX_PARTY;j++) + { + //printf("j = %d , p->member[j].account_id = %d , p->member[j].account_id = %d \n",j,p->member[j].account_id,p->member[j].account_id); + if(p->member[j].account_id>0&&j!=i){ + mapif_party_leaved(party_id,p->member[j].account_id,p->member[j].name); + // Update char information, does the name need encoding? + sprintf(tmp_sql,"UPDATE `%s` SET `party_id`='0', `online`='1' WHERE `party_id`='%d' AND `name`='%s'", + char_db, party_id, jstrescapecpy(t_member,p->member[i].name)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } +// printf("Delete member %s from MySQL \n", p->member[j].name); + } + } + // Delete the party, if has no member. + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `party_id`='%d'",party_db, party_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } +// printf("Leader breaks party %d \n",party_id); + memset(p, 0, sizeof(struct party)); + }else memset(&p->member[i],0,sizeof(struct party_member)); + + break; + + } + } + if( party_check_empty(p)==0 ) + mapif_party_info(-1,p);// まだ人がいるのでデータ送信 + /* + else + inter_party_tosql(party_id,p); // Break the party if no member + */ + }else{ + sprintf(tmp_sql,"UPDATE `%s` SET `party_id`='0' WHERE `party_id`='%d' AND `account_id`='%d' AND `online`='1'", + char_db, party_id, account_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } + } + return 0; +} +// When member goes to other map +int mapif_parse_PartyChangeMap(int fd,int party_id,int account_id,char *map,int online,int lv) +{ + struct party *p; + int i; + + p = party_pt; + if(p==NULL){ + printf("int_party: out of memory !\n"); + return 0; + } + inter_party_fromsql(party_id, p); + + if(p->party_id <= 0){ + return 0; + } + for(i=0;i<MAX_PARTY;i++){ + if(p->member[i].account_id==account_id){ + int flag=0; + + memcpy(p->member[i].map,map,16); + p->member[i].online=online; + p->member[i].lv=lv; + mapif_party_membermoved(p,i); + + if( p->exp>0 && !party_check_exp_share(p) ){ + p->exp=0; + flag=1; + } + if(flag) + mapif_party_optionchanged(fd,p,0,0); + break; + } + } + inter_party_tosql(party_id, p); + return 0; +} +// パーティ解散要求 +int mapif_parse_BreakParty(int fd,int party_id) +{ + struct party *p; + + p = party_pt; + if(p==NULL){ + printf("int_party: out of memory !\n"); + return 0; + } + + inter_party_fromsql(party_id, p); + + if(p->party_id <= 0){ + return 0; + } + inter_party_tosql(party_id,p); + + mapif_party_broken(fd,party_id); + return 0; +} +// パーティメッセージ送信 +int mapif_parse_PartyMessage(int fd,int party_id,int account_id,char *mes,int len) +{ + return mapif_party_message(party_id,account_id,mes,len); +} +// パーティチェック要求 +int mapif_parse_PartyCheck(int fd,int party_id,int account_id,char *nick) +{ + return party_check_conflict(party_id,account_id,nick); +} + +// map server からの通信 +// ・1パケットのみ解析すること +// ・パケット長データはinter.cにセットしておくこと +// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない +// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない +int inter_party_parse_frommap(int fd) +{ + switch(RFIFOW(fd,0)){ + case 0x3020: mapif_parse_CreateParty(fd,RFIFOL(fd,2),RFIFOP(fd,6),RFIFOP(fd,30),RFIFOP(fd,54),RFIFOW(fd,70)); break; + case 0x3021: mapif_parse_PartyInfo(fd,RFIFOL(fd,2)); break; + case 0x3022: mapif_parse_PartyAddMember(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10),RFIFOP(fd,34),RFIFOW(fd,50)); break; + case 0x3023: mapif_parse_PartyChangeOption(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOW(fd,10),RFIFOW(fd,12)); break; + case 0x3024: mapif_parse_PartyLeave(fd,RFIFOL(fd,2),RFIFOL(fd,6)); break; + case 0x3025: mapif_parse_PartyChangeMap(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10),RFIFOB(fd,26),RFIFOW(fd,27)); break; + case 0x3026: mapif_parse_BreakParty(fd,RFIFOL(fd,2)); break; + case 0x3027: mapif_parse_PartyMessage(fd,RFIFOL(fd,4),RFIFOL(fd,8),RFIFOP(fd,12),RFIFOW(fd,2)-12); break; + case 0x3028: mapif_parse_PartyCheck(fd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10)); break; + default: + return 0; + } + return 1; +} + +// サーバーから脱退要求(キャラ削除用) +int inter_party_leave(int party_id,int account_id) +{ + return mapif_parse_PartyLeave(-1,party_id,account_id); +} + + + diff --git a/src/char_sql/int_party.h b/src/char_sql/int_party.h new file mode 100644 index 0000000..04f71c8 --- /dev/null +++ b/src/char_sql/int_party.h @@ -0,0 +1,8 @@ +#ifndef _INT_PARTY_H_ +#define _INT_PARTY_H_ + +int inter_party_parse_frommap(int fd); +int inter_party_sql_init(); +int inter_party_leave(int party_id,int account_id); + +#endif diff --git a/src/char_sql/int_pet.c b/src/char_sql/int_pet.c new file mode 100644 index 0000000..9b5566d --- /dev/null +++ b/src/char_sql/int_pet.c @@ -0,0 +1,324 @@ +// +// original code from athena +// SQL conversion by Jioh L. Jung +// +#include "char.h" +#include "strlib.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct s_pet *pet_pt; +static int pet_newid = 100; + + +//--------------------------------------------------------- +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[100]; + + printf("request save 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) ) { + printf("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) ) { + printf("DB server Error (inset/update `pet`)- %s\n", mysql_error(&mysql_handle) ); + } + + printf("pet save success.......\n"); + return 0; +} + +int inter_pet_fromsql(int pet_id, struct s_pet *p){ + + printf("request load pet: %d.......\n",pet_id); + + 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) ) { + printf("DB server Error (select `pet`)- %s\n", mysql_error(&mysql_handle) ); + 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],24); + 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); + + printf("pet load success.......\n"); + return 0; +} +//---------------------------------------------- + +int inter_pet_sql_init(){ + int i; + + //memory alloc + printf("interserver pet memory initialize.... (%d byte)\n",sizeof(struct s_pet)); + pet_pt = calloc(sizeof(struct s_pet), 1); + + sprintf (tmp_sql , "SELECT count(*) FROM `%s`", pet_db); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + exit(0); + } + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); + printf("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) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + + sql_res = mysql_store_result(&mysql_handle) ; + + sql_row = mysql_fetch_row(sql_res); + pet_newid = atoi (sql_row[0]); + mysql_free_result(sql_res); + } + + printf("set pet_newid: %d.......\n",pet_newid); + + return 0; +} +//---------------------------------- +int inter_pet_delete(int pet_id){ + printf("request delete pet: %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) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + 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; + printf("int_pet: created! %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, 24); + 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) { + printf("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), 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 0000000..b6e3f1b --- /dev/null +++ b/src/char_sql/int_pet.h @@ -0,0 +1,12 @@ +#ifndef _INT_PET_H_ +#define _INT_PET_H_ + +int inter_pet_init(); +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 0000000..fdf85ae --- /dev/null +++ b/src/char_sql/int_storage.c @@ -0,0 +1,366 @@ +// +// original code from athena +// SQL conversion by Jioh L. Jung +// +#include "char.h" +#include "itemdb.h" +#include <string.h> +#include <stdlib.h> + +#define STORAGE_MEMINC 16 + +// reset by inter_config_read() +struct storage *storage_pt=NULL; +struct guild_storage *guild_storage_pt=NULL; + + +// storage data -> DB conversion +int storage_tosql(int account_id,struct storage *p){ + int i; + int eqcount=1; + int noteqcount=1; + struct itemtemp mapitem; + for(i=0;i<MAX_STORAGE;i++){ + if(p->storage[i].nameid>0){ + if(itemdb_isequip(p->storage[i].nameid)==1){ + mapitem.equip[eqcount].flag=0; + mapitem.equip[eqcount].id = p->storage[i].id; + mapitem.equip[eqcount].nameid=p->storage[i].nameid; + mapitem.equip[eqcount].amount = p->storage[i].amount; + mapitem.equip[eqcount].equip = p->storage[i].equip; + mapitem.equip[eqcount].identify = p->storage[i].identify; + mapitem.equip[eqcount].refine = p->storage[i].refine; + mapitem.equip[eqcount].attribute = p->storage[i].attribute; + mapitem.equip[eqcount].card[0] = p->storage[i].card[0]; + mapitem.equip[eqcount].card[1] = p->storage[i].card[1]; + mapitem.equip[eqcount].card[2] = p->storage[i].card[2]; + mapitem.equip[eqcount].card[3] = p->storage[i].card[3]; + mapitem.equip[eqcount].broken = p->storage[i].broken; + eqcount++; + } + else if(itemdb_isequip(p->storage[i].nameid)==0){ + mapitem.notequip[noteqcount].flag=0; + mapitem.notequip[noteqcount].id = p->storage[i].id; + mapitem.notequip[noteqcount].nameid=p->storage[i].nameid; + mapitem.notequip[noteqcount].amount = p->storage[i].amount; + mapitem.notequip[noteqcount].equip = p->storage[i].equip; + mapitem.notequip[noteqcount].identify = p->storage[i].identify; + mapitem.notequip[noteqcount].refine = p->storage[i].refine; + mapitem.notequip[noteqcount].attribute = p->storage[i].attribute; + mapitem.notequip[noteqcount].card[0] = p->storage[i].card[0]; + mapitem.notequip[noteqcount].card[1] = p->storage[i].card[1]; + mapitem.notequip[noteqcount].card[2] = p->storage[i].card[2]; + mapitem.notequip[noteqcount].card[3] = p->storage[i].card[3]; + mapitem.notequip[noteqcount].broken = p->storage[i].broken; + noteqcount++; + } + } + } + + memitemdata_to_sql(mapitem, eqcount, noteqcount, 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; + + 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`} + sprintf(tmp_sql,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`card0`,`card1`,`card2`,`card3`,`broken` FROM `%s` WHERE `account_id`='%d'",storage_db, account_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + 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]); + p->storage[i].card[0]= atoi(sql_row[7]); + p->storage[i].card[1]= atoi(sql_row[8]); + p->storage[i].card[2]= atoi(sql_row[9]); + p->storage[i].card[3]= atoi(sql_row[10]); + p->storage[i].broken = atoi(sql_row[11]); + p->storage_amount = ++i; + } + mysql_free_result(sql_res); + } + + printf ("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; + int eqcount=1; + int noteqcount=1; + struct itemtemp mapitem; + for(i=0;i<MAX_GUILD_STORAGE;i++){ + if(p->storage[i].nameid>0){ + if(itemdb_isequip(p->storage[i].nameid)==1){ + mapitem.equip[eqcount].flag=0; + mapitem.equip[eqcount].id = p->storage[i].id; + mapitem.equip[eqcount].nameid=p->storage[i].nameid; + mapitem.equip[eqcount].amount = p->storage[i].amount; + mapitem.equip[eqcount].equip = p->storage[i].equip; + mapitem.equip[eqcount].identify = p->storage[i].identify; + mapitem.equip[eqcount].refine = p->storage[i].refine; + mapitem.equip[eqcount].attribute = p->storage[i].attribute; + mapitem.equip[eqcount].card[0] = p->storage[i].card[0]; + mapitem.equip[eqcount].card[1] = p->storage[i].card[1]; + mapitem.equip[eqcount].card[2] = p->storage[i].card[2]; + mapitem.equip[eqcount].card[3] = p->storage[i].card[3]; + mapitem.equip[eqcount].broken = p->storage[i].broken; + eqcount++; + } + else if(itemdb_isequip(p->storage[i].nameid)==0){ + mapitem.notequip[noteqcount].flag=0; + mapitem.notequip[noteqcount].id = p->storage[i].id; + mapitem.notequip[noteqcount].nameid=p->storage[i].nameid; + mapitem.notequip[noteqcount].amount = p->storage[i].amount; + mapitem.notequip[noteqcount].equip = p->storage[i].equip; + mapitem.notequip[noteqcount].identify = p->storage[i].identify; + mapitem.notequip[noteqcount].refine = p->storage[i].refine; + mapitem.notequip[noteqcount].attribute = p->storage[i].attribute; + mapitem.notequip[noteqcount].card[0] = p->storage[i].card[0]; + mapitem.notequip[noteqcount].card[1] = p->storage[i].card[1]; + mapitem.notequip[noteqcount].card[2] = p->storage[i].card[2]; + mapitem.notequip[noteqcount].card[3] = p->storage[i].card[3]; + mapitem.notequip[noteqcount].broken = p->storage[i].broken; + noteqcount++; + } + } + } + + memitemdata_to_sql(mapitem, eqcount, noteqcount, guild_id,TABLE_GUILD_STORAGE); + + printf ("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; + struct guild_storage *gs=guild_storage_pt; + 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`} + sprintf(tmp_sql,"SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`card0`,`card1`,`card2`,`card3`,`broken` FROM `%s` WHERE `guild_id`='%d'",guild_storage_db, guild_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + 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]); + p->storage[i].card[0]= atoi(sql_row[7]); + p->storage[i].card[1]= atoi(sql_row[8]); + p->storage[i].card[2]= atoi(sql_row[9]); + p->storage[i].card[3]= atoi(sql_row[10]); + p->storage[i].broken = atoi(sql_row[11]); + p->storage_amount = ++i; + } + mysql_free_result(sql_res); + } + printf ("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 + printf("interserver storage memory initialize....(%d byte)\n",sizeof(struct storage)); + storage_pt=calloc(sizeof(struct storage), 1); + guild_storage_pt=calloc(sizeof(struct guild_storage), 1); + memset(storage_pt,0,sizeof(struct storage)); + memset(guild_storage_pt,0,sizeof(struct guild_storage)); + + return 1; +} +// 倉庫データ削除 +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) ) { + printf("DB server Error (delete `storage`)- %s\n", mysql_error(&mysql_handle) ); + } + 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) ) { + printf("DB server Error (delete `guild_storage`)- %s\n", mysql_error(&mysql_handle) ); + } + 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=0; + WFIFOW(fd,0)=0x3818; + + // 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) ) { + printf("DB server Error (delete `guild`)- %s\n", mysql_error(&mysql_handle) ); + } + 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 + + 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){ + printf("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=0; + int guild_id=RFIFOL(fd,8); + int len=RFIFOW(fd,2); + if(sizeof(struct guild_storage)!=len-12){ + printf("inter storage: data size error %d %d\n",sizeof(struct guild_storage),len-12); + } + else { + // 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) ) { + printf("DB server Error (delete `guild`)- %s\n", mysql_error(&mysql_handle) ); + } + 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 + + 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 0000000..f9f37db --- /dev/null +++ b/src/char_sql/int_storage.h @@ -0,0 +1,13 @@ +#ifndef _INT_STORAGE_H_ +#define _INT_STORAGE_H_ + +int inter_storage_sql_init(); +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 0000000..c8fa9b4 --- /dev/null +++ b/src/char_sql/inter.c @@ -0,0 +1,573 @@ +// +// original code from athena +// SQL conversion by Jioh L. Jung +// + +#include <string.h> +#include <stdlib.h> + +#include "char.h" +#include "strlib.h" +#include "inter.h" +#include "int_party.h" +#include "int_guild.h" +#include "int_storage.h" +#include "int_pet.h" +#include "lock.h" + +#define WISDATA_TTL (60*1000) // Wisデータの生存時間(60秒) +#define WISDELLIST_MAX 256 // Wisデータ削除リストの要素数 + + +struct accreg { + int account_id,reg_num; + struct global_reg reg[ACCOUNT_REG_NUM]; +}; + +static struct accreg *accreg_pt; + + +int party_share_level = 10; +MYSQL mysql_handle; +MYSQL_RES* sql_res ; +MYSQL_ROW sql_row ; +int sql_fields, sql_cnt; +char tmp_sql[65535]; + +MYSQL lmysql_handle; +char tmp_lsql[65535]; +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"; + +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"; + +// sending packet list +int inter_send_packet_length[]={ + -1,-1,27, 0, -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, 0, -1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, + 72, 6,52,14, 10,29, 6,-1, 34, 0, 0, 0, 0, 0, 0, 0, + -1, 6,-1, 0, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1, + 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +struct WisData { + int id,fd,count,len; + unsigned long tick; + unsigned char src[24],dst[24],msg[512]; +}; +static struct dbt * wis_db = NULL; +static int wis_dellist[WISDELLIST_MAX], wis_delnum; + +//-------------------------------------------------------- +// Save account_reg to sql (type=2) +int inter_accreg_tosql(int account_id,struct accreg *reg){ + + int j; + char temp_str[32]; + if (account_id<=0) return 0; + reg->account_id=account_id; + + //`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); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `global_reg_value`)- %s\n", mysql_error(&mysql_handle) ); + } + + 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`, `str`, `value`) VALUES (2,'%d', '%s','%d')", + reg_db, reg->account_id, jstrescapecpy(temp_str,reg->reg[j].str), reg->reg[j].value); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (insert `global_reg_value`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + return 0; +} + +// Load account_reg from sql (type=2) +int inter_accreg_fromsql(int account_id,struct accreg *reg) +{ + int j=0; + if (reg==NULL) return 0; + memset(reg, 0, sizeof(struct accreg)); + reg->account_id=account_id; + + //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`) + sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, reg->account_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (select `global_reg_value`)- %s\n", mysql_error(&mysql_handle) ); + } + sql_res = mysql_store_result(&mysql_handle); + + if (sql_res) { + for(j=0;(sql_row = mysql_fetch_row(sql_res));j++){ + memcpy(reg->reg[j].str, sql_row[0],32); + reg->reg[j].value = atoi(sql_row[1]); + } + mysql_free_result(sql_res); + } + reg->reg_num=j; + return 0; +} + +// Initialize +int inter_accreg_sql_init() +{ + CREATE(accreg_pt, struct accreg, 1); + return 0; + +} + +/*========================================== + * read config file + *------------------------------------------ + */ +int inter_config_read(const char *cfgName) { + printf ("start reading interserver configuration: %s\n",cfgName); + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + if(fp==NULL){ + printf("file not found: %s\n", cfgName); + return 1; + } + while(fgets(line, 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); + printf ("set char_server_ip : %s\n",w2); + } + else if(strcmpi(w1,"char_server_port")==0){ + char_server_port=atoi(w2); + printf ("set char_server_port : %s\n",w2); + } + else if(strcmpi(w1,"char_server_id")==0){ + strcpy(char_server_id, w2); + printf ("set char_server_id : %s\n",w2); + } + else if(strcmpi(w1,"char_server_pw")==0){ + strcpy(char_server_pw, w2); + printf ("set char_server_pw : %s\n",w2); + } + else if(strcmpi(w1,"char_server_db")==0){ + strcpy(char_server_db, w2); + printf ("set char_server_db : %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); + printf ("set login_server_ip : %s\n",w2); + } + else if(strcmpi(w1,"login_server_port")==0){ + login_server_port=atoi(w2); + printf ("set login_server_port : %s\n",w2); + } + else if(strcmpi(w1,"login_server_id")==0){ + strcpy(login_server_id, w2); + printf ("set login_server_id : %s\n",w2); + } + else if(strcmpi(w1,"login_server_pw")==0){ + strcpy(login_server_pw, w2); + printf ("set login_server_pw : %s\n",w2); + } + else if(strcmpi(w1,"login_server_db")==0){ + strcpy(login_server_db, w2); + printf ("set login_server_db : %s\n",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,"import")==0){ + inter_config_read(w2); + } + else if(strcmpi(w1,"login_server_db")==0){ + strcpy(login_server_db, w2); + printf ("set login_server_db : %s\n",w2); + } + } + fclose(fp); + + printf ("success reading interserver configuration\n"); + + return 0; +} + +// Save interlog into sql +int inter_log(char *fmt,...) +{ + char str[255]; + char temp_str[255]; + 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) ) { + printf("DB server Error (insert `interlog`)- %s\n", mysql_error(&mysql_handle) ); + } + + va_end(ap); + return 0; +} + + +// initialize +int inter_init(const char *file) +{ + //int i; + + printf ("interserver initialize...\n"); + inter_config_read(file); + + //DB connection initialized + mysql_init(&mysql_handle); + printf("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 + printf("%s\n",mysql_error(&mysql_handle)); + exit(1); + } + else { + printf ("Connect Success! (Character Server)\n"); + } + + mysql_init(&lmysql_handle); + printf("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 + printf("%s\n",mysql_error(&lmysql_handle)); + exit(1); + }else { + printf ("Connect Success! (Login Server)"); + } + wis_db = numdb_init(); + 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_mapif_init(int fd) { + inter_guild_mapif_init(fd); + + return 0; +} + + +//-------------------------------------------------------- + +// GM message sending +int mapif_GMmessage(unsigned char *mes, int len) { + unsigned char buf[len]; + + WBUFW(buf, 0) = 0x3800; + WBUFW(buf, 2) = len; + memcpy(WBUFP(buf, 4), mes, len-4); + mapif_sendall(buf, len); + printf("\033[1;34m inter server: GM[len:%d] - '%s' \033[0m\n", len, mes); + return 0; +} + +// Wis sending +int mapif_wis_message(struct WisData *wd) { + unsigned char buf[56 + wd->len]; + + WBUFW(buf, 0) = 0x3801; + WBUFW(buf, 2) = 56 +wd->len; + WBUFL(buf, 4) = wd->id; + memcpy(WBUFP(buf, 8), wd->src, 24); + memcpy(WBUFP(buf,32), wd->dst, 24); + memcpy(WBUFP(buf,56), wd->msg, wd->len); + wd->count = mapif_sendall(buf,WBUFW(buf,2)); + + return 0; +} +// 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)]; + memcpy(WBUFP(buf,0),src,WBUFW(src,2)); + WBUFW(buf, 0)=0x3804; + mapif_sendallwos(fd,buf,WBUFW(buf,2)); + return 0; +} + +// Send the requested account_reg +int mapif_account_reg_reply(int fd,int account_id) +{ + struct accreg *reg=accreg_pt; + inter_accreg_fromsql(account_id,reg); + + WFIFOW(fd,0)=0x3804; + WFIFOL(fd,4)=account_id; + if(reg->reg_num==0){ + WFIFOW(fd,2)=8; + }else{ + int j,p; + for(j=0,p=8;j<reg->reg_num;j++,p+=36){ + memcpy(WFIFOP(fd,p),reg->reg[j].str,32); + WFIFOL(fd,p+32)=reg->reg[j].value; + } + WFIFOW(fd,2)=p; + } + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +//-------------------------------------------------------- + +// Existence check of WISP data +int check_ttl_wisdata_sub(void *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; + numdb_foreach(wis_db, check_ttl_wisdata_sub, tick); + for(i = 0; i < wis_delnum; i++) { + struct WisData *wd = numdb_search(wis_db, wis_dellist[i]); + printf("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst); + // removed. not send information after a timeout. Just no answer for the player + //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + numdb_erase(wis_db, wd->id); + free(wd); + } + } while(wis_delnum >= WISDELLIST_MAX); + + return 0; +} + +//-------------------------------------------------------- + +// GM message sending +int mapif_parse_GMmessage(int fd) +{ + mapif_GMmessage(RFIFOP(fd, 4), RFIFOW(fd, 2)); + return 0; +} + + +// Wisp/page request to send +int mapif_parse_WisRequest(int fd) { + struct WisData* wd; + static int wisid = 0; + + if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { + printf("inter: Wis message size too long.\n"); + return 0; + } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows... + printf("inter: Wis message doesn't exist.\n"); + return 0; + } + sprintf (tmp_sql, "SELECT `name` FROM `%s` WHERE `char_id`='%d'",char_db, (int) RFIFOP(fd,28)); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + 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), 24); + WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(fd, buf, 27); + // Character exists. So, ask all map-servers + } else { + // to be sure of the correct name, rewrite it + memset(RFIFOP(fd,28), 0, 24); + strncpy(RFIFOP(fd,28), sql_row[0], 24); + // if source is destination, don't ask other servers. + if (strcmp(RFIFOP(fd,4),RFIFOP(fd,28)) == 0) { + unsigned char buf[27]; + WBUFW(buf, 0) = 0x3802; + memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), 24); + WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + mapif_send(fd, buf, 27); + } else { + + CREATE(wd, struct WisData, 1); + + // Whether the failure of previous wisp/page transmission (timeout) + check_ttl_wisdata(); + + wd->id = ++wisid; + wd->fd = fd; + wd->len= RFIFOW(fd,2)-52; + memcpy(wd->src, RFIFOP(fd, 4), 24); + memcpy(wd->dst, RFIFOP(fd,28), 24); + memcpy(wd->msg, RFIFOP(fd,52), wd->len); + wd->tick = gettick(); + numdb_insert(wis_db, wd->id, wd); + mapif_wis_message(wd); + } + } + + return 0; +} + + +// Wisp/page transmission result +int mapif_parse_WisReply(int fd) { + int id = RFIFOL(fd,2), flag = RFIFOB(fd,6); + struct WisData *wd = numdb_search(wis_db, id); + + if (wd == NULL) + return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server + + if ((--wd->count) <= 0 || flag != 1) { + mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + numdb_erase(wis_db, id); + free(wd); + } + + return 0; +} + + +// Save account_reg into sql (type=2) +int mapif_parse_AccReg(int fd) +{ + int j,p; + struct accreg *reg=accreg_pt; + int account_id = RFIFOL(fd,4); + memset(accreg_pt,0,sizeof(struct accreg)); + + for(j=0,p=8;j<ACCOUNT_REG_NUM && p<RFIFOW(fd,2);j++,p+=36){ + memcpy(reg->reg[j].str,RFIFOP(fd,p),32); + reg->reg[j].value=RFIFOL(fd,p+32); + } + reg->reg_num=j; + + inter_accreg_tosql(account_id,reg); + mapif_account_reg(fd,RFIFOP(fd,0)); // Send confirm message to map + return 0; +} + +// Request the value of account_reg +int mapif_parse_AccRegRequest(int fd) +{ +// printf("mapif: accreg request\n"); + return mapif_account_reg_reply(fd,RFIFOL(fd,2)); +} + + + +//-------------------------------------------------------- +int inter_parse_frommap(int fd) +{ + int cmd=RFIFOW(fd,0); + int len=0; + + // inter鯖管轄かを調べる + if(cmd<0x3000 || cmd>=0x3000+( sizeof(inter_recv_packet_length)/ + sizeof(inter_recv_packet_length[0]) ) ) + return 0; + + // パケット長を調べる + if( (len=inter_check_length(fd,inter_recv_packet_length[cmd-0x3000]))==0 ) + return 2; + + switch(cmd){ + case 0x3000: mapif_parse_GMmessage(fd); break; + case 0x3001: mapif_parse_WisRequest(fd); break; + case 0x3002: mapif_parse_WisReply(fd); break; + case 0x3004: mapif_parse_AccReg(fd); break; + case 0x3005: mapif_parse_AccRegRequest(fd); break; + default: + if( inter_party_parse_frommap(fd) ) + break; + if( inter_guild_parse_frommap(fd) ) + break; + if( inter_storage_parse_frommap(fd) ) + break; + 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(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 0000000..841d534 --- /dev/null +++ b/src/char_sql/inter.h @@ -0,0 +1,44 @@ +#ifndef _INTER_H_ +#define _INTER_H_ + +int inter_init(const char *file); +int inter_parse_frommap(int fd); +int inter_mapif_init(int fd); + + +int inter_check_length(int fd,int length); + +int inter_log(char *fmt,...); + +#define inter_cfgName "conf/inter_athena.conf" + +extern int party_share_level; +extern char inter_log_filename[1024]; + +//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 char tmp_lsql[65535]; +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]; + +#endif diff --git a/src/char_sql/itemdb.c b/src/char_sql/itemdb.c new file mode 100644 index 0000000..0bed07c --- /dev/null +++ b/src/char_sql/itemdb.c @@ -0,0 +1,247 @@ +// $Id: itemdb.c,v 1.1.1.1 2004/09/10 17:44:48 MagicalTux Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "itemdb.h" +#include "db.h" +#include "inter.h" +#include "char.h" +#include "utils.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define MAX_RANDITEM 2000 + +// ** ITEMDB_OVERRIDE_NAME_VERBOSE ** +// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します. +//#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; + +/*========================================== + * DBの検索 + *------------------------------------------ + */ +struct item_data* itemdb_search(int nameid) +{ + struct item_data *id; + + id=numdb_search(item_db,nameid); + if(id) return id; + + CREATE(id, struct item_data, 1); + + numdb_insert(item_db,nameid,id); + + + 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; +} + +/*========================================== + * + *------------------------------------------ + */ +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; +} + + + +/*========================================== + * アイテムデータベースの読み込み + *------------------------------------------ + */ +static int itemdb_readdb(void) +{ + FILE *fp; + char line[1024]; + int ln=0; + int nameid,j; + char *str[32],*p,*np; + struct item_data *id; + + fp=fopen("db/item_db.txt","r"); + if(fp==NULL){ + printf("can't read db/item_db.txt\n"); + 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],24); + memcpy(id->jname,str[2],24); + id->type=atoi(str[3]); + + } + fclose(fp); + printf("read db/item_db.txt done (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)) { + printf("Database server error (executing query for %s): %s\n", item_db_db, mysql_error(&mysql_handle)); + } + + // 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; + } + + // ---------- + + // Insert a new row into the item database +/* + id = calloc(sizeof(struct item_data), 1); + + if (id == NULL) { + printf("out of memory : itemdb_read_sqldb\n"); + exit(1); + } + + memset(id, 0, sizeof(struct item_data)); + numdb_insert(item_db, nameid, id); + + // ---------- +*/ + id=itemdb_search(nameid); + + memcpy(id->name, sql_row[1], 24); + memcpy(id->jname, sql_row[2], 24); + + id->type = atoi(sql_row[3]); + } + + // If the retrieval failed, output an error + if (mysql_errno(&mysql_handle)) { + printf("Database server error (retrieving rows from %s): %s\n", item_db_db, mysql_error(&mysql_handle)); + } + + printf("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 { + printf("MySQL error (storing query result for %s): %s\n", item_db_db, mysql_error(&mysql_handle)); + } + + return 0; +} + +static int itemdb_final(void *key,void *data,va_list ap) +{ + struct item_data *id; + + id=data; + if(id->use_script) + free(id->use_script); + if(id->equip_script) + free(id->equip_script); + free(id); + + return 0; +} + + +/*========================================== + * + *------------------------------------------ + */ +void do_final_itemdb(void) +{ + if(item_db){ + numdb_final(item_db,itemdb_final); + item_db=NULL; + } +} +int do_init_itemdb(void) +{ + item_db = numdb_init(); + + 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 0000000..dea835e --- /dev/null +++ b/src/char_sql/itemdb.h @@ -0,0 +1,34 @@ +#ifndef _ITEMDB_H_ +#define _ITEMDB_H_ + +struct item_data { + int nameid; + char name[24],jname[24]; + 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; // 回復とかも全部この中でやろうかなと + char *equip_script; // 攻撃,防御の属性設定もこの中で可能かな? + 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 0000000..a4ca8b5 --- /dev/null +++ b/src/char_sql/make.sh @@ -0,0 +1,11 @@ +#!/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 strlib.c + 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 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/char_sql/readme.txt b/src/char_sql/readme.txt new file mode 100644 index 0000000..41b1144 --- /dev/null +++ b/src/char_sql/readme.txt @@ -0,0 +1,250 @@ +サソ//Encoded with UTF-8 (UNICODE) +//--------------------------------------------- +// V.018 - Aarlex +1. ADD Makefile & GNUmakefile +2. fix guild_leave. +3. fix char select windows HP & SP value error. + +// V.017 - Aarlex +1. fix guild member view job update.(For mod-0728) + inter.c + int_guild.c + +// V.016 - by Aarlex +1. Add e-mail check when you Delete char data. +2. modify storage save func like 014. +2. remove Lan_support func. + +// v.014 - by Aarlex +I rewrite save function. +besause myfriend find that Mysql will use more than 40% CPU. +And log file is too big (4 days 22G ..= =+) +(maybe he sets autosave_time less then 1 min.) +but. i still rewrite save func. +char server will delete all of user item(inventory & Cart) data then insert them again before. +so i use two struct to save item data from map & database. +then compare two struct to get different . +AND add some debug message.but message maybe too much XD. + + +1. ADDED itemdb.c itemdb.h +2. modify mmo_char_tosql(). +3. ADDED memitemdata_to_sql(). +4. ADDED some debug message in memitemdata_to_sql(). +5. modify make.sh + +// v.013 - by Aarlex +1. some SQL sentance fix in old version Mysql . + +2. in_guild.c mapif_guild_leaved() + unsigned char buf[64] -> unsigned char buf[128] + +3. in_pet.c inter_pet_tosql() + if (sql_res) - > if (mysql_num_rows(sql_res)!=0) + +4. in_char.c mmo_char_send006b() + + WFIFOW(fd, offset+(i*106)+42) = char_dat[0].hp -> WFIFOW(fd,offset+(i*106)+42) = (char_dat[j].hp > 0x7fff)? 0x7fff:char_dat[j].hp; + WFIFOW(fd, offset+(i*106)+44) = char_dat[0].max_hp -> WFIFOW(fd,offset+(i*106)+44) = (char_dat[j].max_hp > 0x7fff)? 0x7fff:char_dat[j].max_hp; + WFIFOW(fd, offset+(i*106)+46) = char_dat[0].sp -> WFIFOW(fd,offset+(i*106)+46) = (char_dat[j].sp > 0x7fff)? 0x7fff:char_dat[j].sp + WFIFOW(fd, offset+(i*106)+48) = char_dat[0].max_sp -> WFIFOW(fd,offset+(i*106)+48) = (char_dat[j].max_sp > 0x7fff)? 0x7fff:char_dat[j].max_sp; + + in_char.c parse_char() + + WFIFOW(fd, offset+(i*106)+42) = char_dat[0].hp -> WFIFOW(fd,offset+(i*106)+42) = (char_dat[j].hp > 0x7fff)? 0x7fff:char_dat[j].hp; + WFIFOW(fd, offset+(i*106)+44) = char_dat[0].max_hp -> WFIFOW(fd,offset+(i*106)+44) = (char_dat[j].max_hp > 0x7fff)? 0x7fff:char_dat[j].max_hp; + WFIFOW(fd, offset+(i*106)+46) = char_dat[0].sp -> WFIFOW(fd,offset+(i*106)+46) = (char_dat[j].sp > 0x7fff)? 0x7fff:char_dat[j].sp + WFIFOW(fd, offset+(i*106)+48) = char_dat[0].max_sp -> WFIFOW(fd,offset+(i*106)+48) = (char_dat[j].max_sp > 0x7fff)? 0x7fff:char_dat[j].max_sp; + +// v.012 - by Jazz +1. 0627 official version縺ォ蜷医o縺帙※菫ョ豁」縺励∪縺励◆. +2. no more binary files are supplied. + +//--------------------------------------------- +// v.011 - by Mark +1. Fixed a couple bugs which would cause segfaults under linux :) + +//--------------------------------------------- +// v.010 - by Jazz +1. added some debug info - for reporting. + +//--------------------------------------------- +// v.009 - by Jazz +1. code added for debug. +2. some SQL sentance fix. + +//--------------------------------------------- +// v.009 - by Jazz +1. fix crash bug. - pet db. + +//--------------------------------------------- +// v.008 - by Jazz +1. DB table fix. - char.fix-from.007.to.008.sql + +譌「蟄倥ョ table 讒矩縺ォ縺ッ遏「縺瑚」逹隗」髯、縺輔l繧句エ蜷医′縺ゅj縺セ縺. + +item.equip縺ッ 'unsigned short' 蠖「蠑上〒縺. +縺薙ョ縺溘a縺ォ SQL table繧剃ソョ豁」縺励↑縺代l縺ー縺ェ繧翫∪縺帙s. + +菫ョ豁」繧ウ繝シ繝峨ッ char.fix-from.007.to.008.sql 縺ァ縺. +MySQL縺ァ荳蠎ヲ陦後▲縺ヲ縺上l繧後ー驕ゥ逕ィ縺輔l縺セ縺. 譌「蟄倥ョ繝繝シ繧ソ縺ッ螳牙ィ縺ァ縺. + +//--------------------------------------------- +// v.007 - by Jazz +1. domain 隗」驥医↓蟇セ縺吶k蝠城。後r菫ョ豁」縺励∪縺励◆. + +//--------------------------------------------- +// v.006 - by Jazz +1. crash bug fix. - when your pet DB is empty + +//--------------------------------------------- +// v.005 - by Jazz +1. 0590 official version縺ォ蜷医o縺帙※菫ョ豁」縺励∪縺励◆. + +//--------------------------------------------- +// v.004 - by Jazz +1. 0586 official version縺ォ蜷医o縺帙※菫ョ豁」縺励∪縺励◆. + +//--------------------------------------------- +// v.003 - by Jazz +1. official縺ョ guild.c 縺ィ party.c 繝輔ぃ繧、繝ォ縺ァ縺セ縺溷堺ソョ豁」縺励∪縺励◆. + +//--------------------------------------------- +// v.002 - by Jazz +1. aphostropy 蝠城。後r隗」豎コ縺励∪縺励◆. 縺薙l縺ッ菫晏ョ峨→騾」髢「縺後≠繧句撫鬘後〒縺. +2. SQL縺ョ讒矩繧剃ソョ豁」縺励∪縺励◆. + +//--------------------------------------------- +// v.001 - by Jazz +1. 荳逡ェ逶ョ螳牙ョ version縺ァ縺. alpha version縺ォ蝠城。檎せ繧貞、壽焚菫ョ豁」縺励∪縺励◆. + +//------------------------------------------------------------------------ +//For JAPANESE USER +Athena Char-Server for MySQL. 005 + +荳蠢 guild縺ィ party縺ッ繧医¥蜻シ縺ウ蜃コ縺励&繧後k驛ィ蛻縺ェ縺ョ縺ァ譌「蟄倥ョ athena char-server縺御スソ縺」縺ヲ縺繧 file 蝓コ逶、縺ョ讒矩繧呈戟縺」縺ヲ陦後″縺セ縺. +縺薙l縺ッ, 荳蠎ヲ縺ォ繝。繝「繝ェ繝シ縺ァ逧隱ュ繧薙〒, 繧ゅ≧荳蠎ヲ縺ォ逧 file縺ァ菴ソ縺譁ケ縺碁Κ荳九′蟆代↑縺上°縺九k縺ィ諤昴>縺セ縺. +縺昴@縺ヲ char 繝繝シ繧ソ縺ォ豈斐∋縺ヲ, lost縺ォ縺ェ縺」縺ヲ繧ょ撫鬘後′繧ゅ▲縺ィ蟆代↑縺縺ィ蛻、譁ュ縺励※縺昴≧縺励∪縺励◆. + +MySQL繝舌シ繧ク繝ァ繝ウ縺ョ compile縺ッ MySQL Clients Library縺悟ソ隕√〒縺. windows(cygwin) 繝舌シ繧ク繝ァ繝ウ縺ォ繧ウ繝ウ繝代う繝ォ縺輔l縺 binary繧呈キサ莉倥@縺セ縺励◆. + +險ュ鄂ョ: +縺セ縺 text->DB縺ョ converter縺ッ縺セ縺ィ繧ゅ↓謾ッ謠エ縺励↑縺縺ァ縺. 蜀驛ィ逧縺ォ縺。繧縺」縺ィ繝舌げ縺檎匱隕九&繧後※繝舌げ繧剃ソョ豁」荳ュ縺ァ縺. +縺ァ縺阪k縺縺第掠縺 upload繧偵@縺セ縺. + +1. char.sql繧 MySQL縺ォ dump縺励∪縺. + +2. inter_athena.conf縺ォ谺。繧定ソス蜉縺励∪縺. 縺昴@縺ヲ閾ェ蛻縺ョ DB繧オ繝シ繝舌シ縺ョ諠蝣ア縺ォ蜷医o縺帙※縺上l縺セ縺. +縺薙%縺ァ windows(cygwin)縺ョ蝣エ蜷医↓縺ッ localhost繧剃スソ縺」縺ヲ縺ッ縺縺代↑縺縺ァ縺. ip縺ァ譖ク縺縺ヲ縺上l縺ェ縺代l縺ー縺ェ繧翫∪縺帙s. +localhost縺ァ菴ソ縺蝣エ蜷 UNIX domain socket縺御ス懷虚縺吶k縺九i騾」邨舌′荳榊庄閭ス縺ォ縺ェ繧翫∪縺. + +//3306 is default +db_server_port: 3306 +//DB ip +db_server_ip: 127.0.0.1 +//DB id +db_server_id: ragnarok +//DB pass +db_server_pw: ragnarok +//DB name +db_server_logindb: ragnarok + +3. MySQL 繝舌シ繧ク繝ァ繝ウ縺ッ 2蛟九ョ MySQL connect session繧呈戟縺。縺セ縺. +荳縺、縺ッ繧ュ繝」繝ゥ繧ッ繧ソ繝シ繝繝シ繧ソ繧定ェュ繧薙〒譖ク縺上ョ縺ォ菴ソ繧上l縺ヲ, 莠檎分逶ョ縺ッ inter server縺ョ縺溘a縺ォ騾」邨舌@縺セ縺. + +髢狗匱縺ィ繝繧ケ繝育腸蠅縺ッ P4 2.4a/1024MB/WinXP pro(MediaCenter Edition 2K4 KR)/Cygwin GCC 縺ァ縺. +譛ャ莠コ縺碁沒蝗ス莠コ縺縺九i髻灘嵜隱樣幕逋コ閠縺ョ騾」邨。繧よュ楢ソ弱@縺セ縺. +髻灘嵜隱槭→譌・譛ャ隱槭ョ荳頑焔縺ェ譁ケ縺ッ騾」邨。繧偵¥縺縺輔>. 譌・譛ャ隱槭ョ菴ソ逕ィ縺ォ髮」縺励&繧呈─縺倥※縺縺セ縺. + +迴セ蝨ィ DarkWeiss縺 login server縺ォ MySQL繧呈髪謠エ縺怜ァ九a縺セ縺励◆. +縺励°縺 athena縺ョ縺昴l縺ッ DarkWeiss 繧医j繧ゅ▲縺ィ繧医¥菴懷虚縺吶k縺ィ諤昴>縺セ縺. + +contact : jazz@creper.com + +//------------------------------------------------------------------------ +//For KOREAN USER +Athena Char-Server for MySQL. 005 + +攵卿 guild凰 party株 梵」シ 从カ罹据株 カカ擽攵 クー。エ攪 athena char-serverー ぎ圸葺ウ 梭株 file クーー們攪 オャ。ー・シ ーァウ ー瀧笈共. +擽 イ捩, 復イ溢乱 ゥ罷ェィヲャ。 ェィ草 攷ウ, 共亨 復イ溢乱 ェィ草 file。 堂株 ェス擽 カ葺ー イ アクヲー共ウ 晝ー鮒笈共. +キクヲャウ char 魂擽┣乱 ケ紛, lostー 据鵠攵巡 ャク懋ー 鵠 共ウ 倹卿紛 キクイ 葺慣笈共. + +MySQLイ攪 compile捩 MySQL Clients Libraryー 符囈鮒笈共. windows(cygwin) イ愍。 サエ血攵頗 binary・シ イィカ葺慣笈共. + +└ケ: +符ァ text->DB攪 converter株 罹劇。 ァ寳葺ァ 賦慣笈共. ざカ愍。 平ー イキクー ー懋イャ据牟 イキク・シ 們菩、卓桿笈共. +据巡。 ケィヲャ upload・シ 葺イ慣笈共. + +1. char.sql揆 MySQL乱 dump鮒笈共. + +2. inter_athena.conf乱 共搆揆 カ緋ー 鮒笈共. キクヲャウ 梵侠攪 DB罹イ攪 簿ウエ乱 ァ樌カ肥牟 、鷺笈共. +流クー乱 windows(cygwin)攪 イス垈乱株 localhost・シ 堂ゥエ 譜姓笈共. ip。 牟」シ牟幣 鮒笈共. +localhost。 ぎ圸葺株 イス垈 UNIX domain socket擽 梠徐 葺クー 阜ャク乱 硫イー擽 カ一ー冠紛 ァ瀧笈共. + +//3306 is default +db_server_port: 3306 +//DB ip +db_server_ip: 127.0.0.1 +//DB id +db_server_id: ragnarok +//DB pass +db_server_pw: ragnarok +//DB name +db_server_logindb: ragnarok + +3. MySQL イ捩 2ー懍攪 MySQL connect session揆 ーァ瀧笈共. +葺x株 コ尖ヲュ┣ 魂擽┣・シ 攷ウ 堂株魂 ぎ圸据ゥー, 草イ溢ァク株 inter server・シ 怱紛 硫イー鮒笈共. + +ー罹ー懋ウシ 護侃敢 劍イス捩 P4 2.4a/1024MB/WinXP pro(MediaCenter Edition 2K4 KR)/Cygwin GCC 桿笈共. +ウク攤擽 復オュ攤擽クー 阜ャク乱 復オュ牟 ー罹ー懍梵攪 硫攷巡 劍鮒笈共. +復オュ牟凰 攵ウク牟ー 冠呰復 カ捩 硫攷揆 」シ┷囈. 攵ウク牟攪 ぎ圸乱 牟、它揆 叶⊂ウ 梭慣笈共. + +椪 DarkWeissー login server乱 MySQL揆 ァ寳葺クー 亨梠毎慣笈共. +葺ァァ athena攪 キクイ捩 DarkWeiss ウエ共 鵠 椈 梠徐復共ウ 晝ー鮒笈共. + +contact : jazz@creper.com + +//------------------------------------------------------------------------ +//For ENGLISH USER +Athena Char-Server for MySQL. alpha 005 + += hehe. My English is poor. and I have no time to write. :) + +anyway this version use guild and party data on text file base system. +It accesses many times, so memory dumping is useful for less cpu consume. + +MySQL version need MySQL Clients Library to compile. This include Win32-binary compiled by cygwin-gcc. + +Install: +not yet text->DB converter. I found some bug on it, so It's under debug progress. + +1. dump char.sql to MySQL. + +2. add below on inter_athena.conf. and set your own information. +do not use 'localhost' as domain on cygwin. you must set as ip. +if you use localhost on cygwin, cygwin tries to connect MySQL as UNIX domain socket. +but, MySQL does not support UNIX domain socket on windows. + +//3306 is default +db_server_port: 3306 +//DB ip +db_server_ip: 127.0.0.1 +//DB id +db_server_id: ragnarok +//DB pass +db_server_pw: ragnarok +//DB name +db_server_logindb: ragnarok + +3. MySQL version has 2 MySQL connect session. +one is for char-server and the other one is for inter-server. + +developement enviroment) + P4 2.4a/1024MB/WinXP pro(MediaCenter Edition 2K4 KR)/Cygwin GCC + +I'm korean, so contect if U're Korean developer. +Please contact me, If U can use Korean & Japanese well. + +DarkWeiss starts to support MySQL version of login server, but Athena's one works better, I thnik. + +contact : jazz@creper.com diff --git a/src/char_sql/strlib.c b/src/char_sql/strlib.c new file mode 100644 index 0000000..b113d96 --- /dev/null +++ b/src/char_sql/strlib.c @@ -0,0 +1,79 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "strlib.h" +#include "utils.h" + +//----------------------------------------------- +// string lib. +unsigned char* jstrescape (unsigned char* pt) { + //copy from here + unsigned char * ptr; + int i =0, j=0; + + //copy string to temporary + CREATE(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; + default: + pt[j++] = ptr[i++]; + } + } + pt[j++] = '\0'; + free (ptr); + return (unsigned char*) &pt[0]; +} + +unsigned char* jstrescapecpy (unsigned char* pt,unsigned char* spt) { + //copy from here + 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; + default: + pt[j++] = spt[i++]; + } + } + pt[j++] = '\0'; + return (unsigned char*) &pt[0]; +} +int jmemescapecpy (unsigned char* pt,unsigned 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; + default: + pt[j++] = spt[i++]; + } + } + // copy size is 0 ~ (j-1) + return j; +} diff --git a/src/char_sql/strlib.h b/src/char_sql/strlib.h new file mode 100644 index 0000000..6b61690 --- /dev/null +++ b/src/char_sql/strlib.h @@ -0,0 +1,10 @@ +#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" +unsigned char* jstrescape (unsigned char* pt); +unsigned char* jstrescapecpy (unsigned char* pt,unsigned char* spt); +int jmemescapecpy (unsigned char* pt,unsigned char* spt, int size); +#endif diff --git a/src/common/GNUmakefile b/src/common/GNUmakefile new file mode 100644 index 0000000..689ac3b --- /dev/null +++ b/src/common/GNUmakefile @@ -0,0 +1,13 @@ +txt sql all: core.o socket.o timer.o grfio.o db.o lock.o nullpo.o malloc.o
+
+core.o: core.c core.h
+socket.o: socket.c socket.h mmo.h
+timer.o: timer.c timer.h
+grfio.o: grfio.c grfio.h
+db.o: db.c db.h
+lock.o: lock.h
+nullpo.o: nullpo.c nullpo.h
+malloc.o: malloc.c malloc.h
+
+clean:
+ rm -f *.o
diff --git a/src/common/Makefile b/src/common/Makefile new file mode 100644 index 0000000..689ac3b --- /dev/null +++ b/src/common/Makefile @@ -0,0 +1,13 @@ +txt sql all: core.o socket.o timer.o grfio.o db.o lock.o nullpo.o malloc.o
+
+core.o: core.c core.h
+socket.o: socket.c socket.h mmo.h
+timer.o: timer.c timer.h
+grfio.o: grfio.c grfio.h
+db.o: db.c db.h
+lock.o: lock.h
+nullpo.o: nullpo.c nullpo.h
+malloc.o: malloc.c malloc.h
+
+clean:
+ rm -f *.o
diff --git a/src/common/core.c b/src/common/core.c new file mode 100644 index 0000000..62af254 --- /dev/null +++ b/src/common/core.c @@ -0,0 +1,152 @@ +// $Id: core.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $ +// original : core.c 2003/02/26 18:03:12 Rev 1.7 + +#include <stdio.h> +#include <stdlib.h> +#ifndef LCCWIN32 +#include <unistd.h> +#endif +#include <signal.h> + +#include "core.h" +#include "socket.h" +#include "timer.h" +#include "version.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +static void (*term_func)(void)=NULL; + +/*====================================== + * CORE : Set function + *-------------------------------------- + */ +void set_termfunc(void (*termfunc)(void)) +{ + term_func = termfunc; +} + +/*====================================== + * CORE : Signal Sub Function + *-------------------------------------- + */ + +static void sig_proc(int sn) +{ + int i; + switch(sn){ + case SIGINT: + case SIGTERM: + if(term_func) + term_func(); + for(i=0;i<fd_max;i++){ + if(!session[i]) + continue; + close(i); + } + exit(0); + break; + } +} + +/*====================================== + * CORE : Display title + *-------------------------------------- + */ + +static void display_title(void) +{ + // 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("\033[2J"); // clear screen and go up/left (0, 0 position in text) + printf("\033[37;44m (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)\033[K\033[0m\n"); // white writing (37) on blue background (44), \033[K clean until end of file + printf("\033[0;44m (\033[1;33m (c)2004 eAthena Development Team presents \033[0;44m)\033[K\033[0m\n"); // yellow writing (33) + printf("\033[0;44m (\033[1m ______ __ __ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m /\\ _ \\/\\ \\__/\\ \\ v%2d.%02d.%02d \033[0;44m)\033[K\033[0m\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m __\\ \\ \\_\\ \\ \\ ,_\\ \\ \\___ __ ___ __ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m /'__`\\ \\ __ \\ \\ \\/\\ \\ _ `\\ /'__`\\/' _ `\\ /'__`\\ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m /\\ __/\\ \\ \\/\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ __//\\ \\/\\ \\/\\ \\_\\.\\_ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m \\ \\____\\\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\____\\ \\_\\ \\_\\ \\__/.\\_\\ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m \\/____/ \\/_/\\/_/\\/__/ \\/_/\\/_/\\/____/\\/_/\\/_/\\/__/\\/_/ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m _ _ _ _ _ _ _ _ _ _ _ _ _ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m ( e | n | g | l | i | s | h ) ( A | t | h | e | n | a ) \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[0;44m (\033[1m \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \033[0;44m)\033[K\033[0m\n"); // 1: bold char, 0: normal char + printf("\033[37;44m (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)\033[K\033[0m\n\n"); // reset color +} + +// 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_. +// +#ifndef 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 : MAINROUTINE + *-------------------------------------- + */ + +int runflag = 1; + +int main(int argc,char **argv) +{ + int next; + + Net_Init(); + do_socket(); + + compat_signal(SIGPIPE,SIG_IGN); + compat_signal(SIGTERM,sig_proc); + compat_signal(SIGINT,sig_proc); + + // Signal to create coredumps by system when necessary (crash) + compat_signal(SIGSEGV, SIG_DFL); +#ifndef LCCWIN32 + compat_signal(SIGBUS, SIG_DFL); + compat_signal(SIGTRAP, SIG_DFL); +#endif + compat_signal(SIGILL, SIG_DFL); + + display_title(); + + do_init(argc,argv); + while(runflag){ + next=do_timer(gettick_nocache()); + do_sendrecv(next); + do_parsepacket(); + } + return 0; +} diff --git a/src/common/core.h b/src/common/core.h new file mode 100644 index 0000000..bc2be02 --- /dev/null +++ b/src/common/core.h @@ -0,0 +1,12 @@ +// original : core.h 2003/03/14 11:55:25 Rev 1.4 + +#ifndef _CORE_H_ +#define _CORE_H_ + +extern int runflag; + +int do_init(int,char**); + +void set_termfunc(void (*termfunc)(void)); + +#endif // _CORE_H_ diff --git a/src/common/db.c b/src/common/db.c new file mode 100644 index 0000000..a2dc695 --- /dev/null +++ b/src/common/db.c @@ -0,0 +1,500 @@ +// $Id: db.c,v 1.2 2004/09/23 14:43:06 MouseJstr Exp $ +// #define MALLOC_DBN +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "db.h" +#include "utils.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define ROOT_SIZE 4096 +#ifdef MALLOC_DBN +static struct dbn *dbn_root[512], *dbn_free; +static int dbn_root_rest=0,dbn_root_num=0; + +static void * malloc_dbn(void) +{ + struct dbn* ret; + + if(dbn_free==NULL){ + if(dbn_root_rest<=0){ + CREATE(dbn_root[dbn_root_num], struct dbn, ROOT_SIZE); + + dbn_root_rest=ROOT_SIZE; + dbn_root_num++; + } + return &(dbn_root[dbn_root_num-1][--dbn_root_rest]); + } + ret=dbn_free; + dbn_free = dbn_free->parent; + return ret; +} + +static void free_dbn(struct dbn* add_dbn) +{ + add_dbn->parent = dbn_free; + dbn_free = add_dbn; +} +#endif + +static int strdb_cmp(struct dbt* table,void* a,void* b) +{ + if(table->maxlen) + return strncmp(a,b,table->maxlen); + return strcmp(a,b); +} + +static unsigned int strdb_hash(struct dbt* table,void* a) +{ + int i; + unsigned int h; + unsigned char *p=a; + + i=table->maxlen; + if(i==0) i=0x7fffffff; + for(h=0;*p && --i>=0;){ + h=(h*33 + *p++) ^ (h>>24); + } + return h; +} + +struct dbt* strdb_init(int maxlen) +{ + int i; + struct dbt* table; + + CREATE(table, struct dbt, 1); + + table->cmp=strdb_cmp; + table->hash=strdb_hash; + table->maxlen=maxlen; + for(i=0;i<HASH_SIZE;i++) + table->ht[i]=NULL; + return table; +} + +static int numdb_cmp(struct dbt* table,void* a,void* b) +{ + int ia,ib; + + ia=(int)a; + ib=(int)b; + + if((ia^ib) & 0x80000000) + return ia<0 ? -1 : 1; + + return ia-ib; +} + +static unsigned int numdb_hash(struct dbt* table,void* a) +{ + return (unsigned int)a; +} + +struct dbt* numdb_init(void) +{ + int i; + struct dbt* table; + + CREATE(table, struct dbt, 1); + + table->cmp=numdb_cmp; + table->hash=numdb_hash; + table->maxlen=sizeof(int); + for(i=0;i<HASH_SIZE;i++) + table->ht[i]=NULL; + return table; +} + +void* db_search(struct dbt *table,void* key) +{ + struct dbn *p; + + for(p=table->ht[table->hash(table,key) % HASH_SIZE];p;){ + int c=table->cmp(table,key,p->key); + if(c==0) + return p->data; + if(c<0) + p=p->left; + else + p=p->right; + } + return NULL; +} + +void * db_search2(struct dbt *table, const char *key) +{ + int i,sp; + struct dbn *p,*pn,*stack[64]; + int slen = strlen(key); + + for(i=0;i<HASH_SIZE;i++){ + if((p=table->ht[i])==NULL) + continue; + sp=0; + while(1){ + if (strncasecmp(key, p->key, slen) == 0) + return p->data; + if((pn=p->left)!=NULL){ + if(p->right){ + stack[sp++]=p->right; + } + p=pn; + } else { + if(p->right){ + p=p->right; + } else { + if(sp==0) + break; + p=stack[--sp]; + } + } + } + } + return 0; +} + +static void db_rotate_left(struct dbn *p,struct dbn **root) +{ + struct dbn * y = p->right; + p->right = y->left; + if (y->left !=0) + y->left->parent = p; + y->parent = p->parent; + + if (p == *root) + *root = y; + else if (p == p->parent->left) + p->parent->left = y; + else + p->parent->right = y; + y->left = p; + p->parent = y; +} + +static void db_rotate_right(struct dbn *p,struct dbn **root) +{ + struct dbn * y = p->left; + p->left = y->right; + if (y->right != 0) + y->right->parent = p; + y->parent = p->parent; + + if (p == *root) + *root = y; + else if (p == p->parent->right) + p->parent->right = y; + else + p->parent->left = y; + y->right = p; + p->parent = y; +} + +static void db_rebalance(struct dbn *p,struct dbn **root) +{ + p->color = RED; + while(p!=*root && p->parent->color==RED){ // rootは必ず黒で親は赤いので親の親は必ず存在する + if (p->parent == p->parent->parent->left) { + struct dbn *y = p->parent->parent->right; + if (y && y->color == RED) { + p->parent->color = BLACK; + y->color = BLACK; + p->parent->parent->color = RED; + p = p->parent->parent; + } else { + if (p == p->parent->right) { + p = p->parent; + db_rotate_left(p, root); + } + p->parent->color = BLACK; + p->parent->parent->color = RED; + db_rotate_right(p->parent->parent, root); + } + } else { + struct dbn* y = p->parent->parent->left; + if (y && y->color == RED) { + p->parent->color = BLACK; + y->color = BLACK; + p->parent->parent->color = RED; + p = p->parent->parent; + } else { + if (p == p->parent->left) { + p = p->parent; + db_rotate_right(p, root); + } + p->parent->color = BLACK; + p->parent->parent->color = RED; + db_rotate_left(p->parent->parent, root); + } + } + } + (*root)->color=BLACK; +} + +static void db_rebalance_erase(struct dbn *z,struct dbn **root) +{ + struct dbn *y = z, *x = NULL, *x_parent = NULL; + + if (y->left == NULL) + x = y->right; + else if (y->right == NULL) + x = y->left; + else { + y = y->right; + while (y->left != NULL) + y = y->left; + x = y->right; + } + if (y != z) { // 左右が両方埋まっていた時 yをzの位置に持ってきてzを浮かせる + z->left->parent = y; + y->left = z->left; + if (y != z->right) { + x_parent = y->parent; + if (x) x->parent = y->parent; + y->parent->left = x; + y->right = z->right; + z->right->parent = y; + } else + x_parent = y; + if (*root == z) + *root = y; + else if (z->parent->left == z) + z->parent->left = y; + else + z->parent->right = y; + y->parent = z->parent; + { int tmp=y->color; y->color=z->color; z->color=tmp; } + y = z; + } else { // どちらか空いていた場合 xをzの位置に持ってきてzを浮かせる + x_parent = y->parent; + if (x) x->parent = y->parent; + if (*root == z) + *root = x; + else if (z->parent->left == z) + z->parent->left = x; + else + z->parent->right = x; + } + // ここまで色の移動の除いて通常の2分木と同じ + if (y->color != RED) { // 赤が消える分には影響無し + while (x != *root && (x == NULL || x->color == BLACK)) + if (x == x_parent->left) { + struct dbn* w = x_parent->right; + if (w->color == RED) { + w->color = BLACK; + x_parent->color = RED; + db_rotate_left(x_parent, root); + w = x_parent->right; + } + if ((w->left == 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 { // same as above, with right <-> left. + struct dbn* w = x_parent->left; + if (w->color == RED) { + w->color = BLACK; + x_parent->color = RED; + db_rotate_right(x_parent, root); + w = x_parent->left; + } + if ((w->right == 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; + } +} + +struct dbn* db_insert(struct dbt *table,void* key,void* data) +{ + struct dbn *p,*priv; + int c,hash; + + hash = table->hash(table,key) % HASH_SIZE; + for(c=0,priv=NULL ,p = table->ht[hash];p;){ + c=table->cmp(table,key,p->key); + if(c==0){ // replace + if (table->release) + table->release(p, 3); + p->data=data; + p->key=key; + return p; + } + priv=p; + if(c<0){ + p=p->left; + } else { + p=p->right; + } + } +#ifdef MALLOC_DBN + p=malloc_dbn(); +#else + CREATE(p, struct dbn, 1); +#endif + if(p==NULL){ + printf("out of memory : db_insert\n"); + return NULL; + } + p->parent= NULL; + p->left = NULL; + p->right = NULL; + p->key = key; + p->data = data; + p->color = RED; + if(c==0){ // hash entry is empty + table->ht[hash] = p; + p->color = BLACK; + } else { + if(c<0){ // left node + priv->left = p; + p->parent=priv; + } else { // right node + priv->right = p; + p->parent=priv; + } + if(priv->color==RED){ // must rebalance + db_rebalance(p,&table->ht[hash]); + } + } + return p; +} + +void* db_erase(struct dbt *table,void* key) +{ + void *data; + struct dbn *p; + int c,hash; + + hash = table->hash(table,key) % HASH_SIZE; + for(c=0,p = table->ht[hash];p;){ + c=table->cmp(table,key,p->key); + if(c==0) + break; + if(c<0) + p=p->left; + else + p=p->right; + } + if(!p) + return NULL; + data=p->data; + db_rebalance_erase(p,&table->ht[hash]); +#ifdef MALLOC_DBN + free_dbn(p); +#else + free(p); +#endif + return data; +} + +void db_foreach(struct dbt *table,int (*func)(void*,void*,va_list),...) +{ + int i,sp; + // red-black treeなので64個stackがあれば2^32個ノードまで大丈夫 + struct dbn *p,*pn,*stack[64]; + va_list ap; + + va_start(ap,func); + for(i=0;i<HASH_SIZE;i++){ + if((p=table->ht[i])==NULL) + continue; + sp=0; + while(1){ + func(p->key,p->data,ap); + if((pn=p->left)!=NULL){ + if(p->right){ + stack[sp++]=p->right; + } + p=pn; + } else { + if(p->right){ + p=p->right; + } else { + if(sp==0) + break; + p=stack[--sp]; + } + } + } + } + va_end(ap); +} + +void db_final(struct dbt *table,int (*func)(void*,void*,va_list),...) +{ + int i,sp; + struct dbn *p,*pn,*stack[64]; + va_list ap; + + va_start(ap,func); + for(i=0;i<HASH_SIZE;i++){ + if((p=table->ht[i])==NULL) + continue; + sp=0; + while(1){ + if(func) + func(p->key,p->data,ap); + if((pn=p->left)!=NULL){ + if(p->right){ + stack[sp++]=p->right; + } + } else { + if(p->right){ + pn=p->right; + } else { + if(sp==0) + break; + pn=stack[--sp]; + } + } +#ifdef MALLOC_DBN + free_dbn(p); +#else + free(p); +#endif + p=pn; + } + } + free(table); + va_end(ap); +} diff --git a/src/common/db.h b/src/common/db.h new file mode 100644 index 0000000..ea9acea --- /dev/null +++ b/src/common/db.h @@ -0,0 +1,47 @@ +#ifndef _DB_H_ +#define _DB_H_ + +#include <stdarg.h> + +#define HASH_SIZE (256+27) + +#define RED 0 +#define BLACK 1 + +struct dbn { + struct dbn *parent,*left,*right; + int color; + void *key; + void *data; +}; + +struct dbt { + int (*cmp)(struct dbt*,void*,void*); + unsigned int (*hash)(struct dbt*,void*); + // which 1 - key, 2 - data, 3 - both + void (*release)(struct dbn*,int which); + int maxlen; + struct dbn *ht[HASH_SIZE]; +}; + +#define strdb_search(t,k) db_search((t),(void*)(k)) +#define strdb_insert(t,k,d) db_insert((t),(void*)(k),(void*)(d)) +#define strdb_erase(t,k) db_erase ((t),(void*)(k)) +#define strdb_foreach db_foreach +#define strdb_final db_final +#define numdb_search(t,k) db_search((t),(void*)(k)) +#define numdb_insert(t,k,d) db_insert((t),(void*)(k),(void*)(d)) +#define numdb_erase(t,k) db_erase ((t),(void*)(k)) +#define numdb_foreach db_foreach +#define numdb_final db_final + +struct dbt* strdb_init(int maxlen); +struct dbt* numdb_init(void); +void* db_search(struct dbt *table,void* key); +void* db_search2(struct dbt *table, const char *key); // [MouseJstr] +struct dbn* db_insert(struct dbt *table,void* key,void* data); +void* db_erase(struct dbt *table,void* key); +void db_foreach(struct dbt*,int(*)(void*,void*,va_list),...); +void db_final(struct dbt*,int(*)(void*,void*,va_list),...); + +#endif diff --git a/src/common/grfio.c b/src/common/grfio.c new file mode 100644 index 0000000..08a8b2a --- /dev/null +++ b/src/common/grfio.c @@ -0,0 +1,948 @@ +/********************************************************************* + * + * 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 <zlib.h> + +#include "utils.h" +#include "grfio.h" +#include "mmo.h" + +#ifdef MEMWATCH +#include "memwatch.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] = ""; // "../"; + +// accessor to data_file,adata_file,sdata_file +char *grfio_setdatafile(const char *str){ strcpy(data_file,str); return data_file; } +char *grfio_setadatafile(const char *str){ strcpy(adata_file,str); return adata_file; } +char *grfio_setsdatafile(const char *str){ strcpy(sdata_file,str); return sdata_file; } + +//---------------------------- +// 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 32768 // temporary maximum, and a theory top maximum are 2G. + +static FILELIST *filelist; +static int filelist_entrys; +static int filelist_maxentry; + +static char **gentry_table; +static int gentry_entrys; +static int gentry_maxentry; + +//---------------------------- +// 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; +} + +/*========================================== + * 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; + 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); +} + +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); +} + +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 + *------------------------------------------ + */ +static int decode_zip(Bytef* dest, uLongf* destLen, const Bytef* source, uLong 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 = 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; +} +/*********************************************************** + *** 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; + + for(hash=filelist_hash[filehash(fname)];hash>=0;hash=filelist[hash].next) { + if(strcasecmp(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) { + printf("filelist limit : filelist_add\n"); + exit(1); + } + + if (filelist_entrys>=filelist_maxentry) { + FILELIST *new_filelist = (FILELIST*)realloc( + (void*)filelist, (filelist_maxentry+FILELIST_ADDS)*sizeof(FILELIST) ); + if (new_filelist != NULL) { + filelist = new_filelist; + memset(filelist + filelist_maxentry, '\0', + FILELIST_ADDS * sizeof(FILELIST)); + filelist_maxentry += FILELIST_ADDS; + } else { + printf("out of memory : filelist_add\n"); + exit(1); + } + } + + memcpy( &filelist[filelist_entrys], entry, sizeof(FILELIST) ); + + hash = filehash(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 *new_filelist = (FILELIST*)realloc( + (void*)filelist,filelist_entrys*sizeof(FILELIST) ); + if (new_filelist != NULL) { + filelist = new_filelist; + filelist_maxentry = filelist_entrys; + } else { + printf("out of memory : filelist\n"); + exit(1); + } + } + } +} + +/*********************************************************** + *** Grfio Sobroutines *** + ***********************************************************/ +/*========================================== + * Grfio : Resnametable replace + *------------------------------------------ + */ +char* grfio_resnametable(char* fname, char *lfname) +{ + FILE *fp; + char *p; + char w1[256],w2[256],restable[256],line[512]; + + sprintf(restable,"%sdata\\resnametable.txt",data_dir); + + for(p=&restable[0];*p!=0;p++) if (*p=='\\') *p = '/'; + + fp = fopen(restable,"rb"); + if(fp==NULL) { + printf("%s not found\n",restable); + exit(1); // 1:not found error + } + + while(fgets(line,508,fp)){ + if((sscanf(line,"%[^#]#%[^#]#",w1,w2)==2) && (sscanf(fname,"%*5s%s",lfname)==1) && (!strcmpi(w1,lfname))){ + sprintf(lfname,"data\\%s",w2); + fclose(fp); + return lfname; + } + } + fclose(fp); + return fname; + +} + +/*========================================== + * 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; + + //printf("%s\t",fname); + sprintf(rname,"%s",grfio_resnametable(fname,lfname)); + //printf("%s\n",rname); + sprintf(lfname,"%s%s",data_dir,rname); + //printf("%s\n",lfname); + + 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) { + printf("%s not found\n", fname); + //exit(1); + return -1; + } + } + return entry->declen; +} + +/*========================================== + * Grfio : Resource file read & size get + *------------------------------------------ + */ +void* grfio_reads(char *fname, int *size) +{ + FILE *in = NULL; + unsigned char *buf=NULL,*buf2=NULL; + char *gfname; + FILELIST *entry; + + entry = filelist_find(fname); + + if (entry==NULL || entry->gentry<=0) { // LocalFileCheck + char lfname[256],rname[256],*p; + FILELIST lentry; + + strncpy(lfname,fname,255); + sprintf(rname,"%s",grfio_resnametable(fname,lfname)); + sprintf(lfname,"%s%s",data_dir,rname); + //printf("%s\n",lfname); + + 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 = calloc(lentry.declen+1024, 1); + if (buf2==NULL) { + printf("file read memory allocate error : declen\n"); + goto errret; + } + fread(buf2,1,lentry.declen,in); + fclose(in); in = NULL; + 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 { + printf("%s not found\n", fname); + //goto errret; + free(buf2); + return NULL; + } + } + } + if (entry!=NULL && entry->gentry>0) { // Archive[GRF] File Read + buf = calloc(entry->srclen_aligned+1024, 1); + if (buf==NULL) { + printf("file read memory allocate error : srclen_aligned\n"); + goto errret; + } + gfname = gentry_table[entry->gentry-1]; + in = fopen(gfname,"rb"); + if(in==NULL) { + printf("%s not found\n",gfname); + //goto errret; + free(buf); + return NULL; + } + fseek(in,entry->srcpos,0); + fread(buf,1,entry->srclen_aligned,in); + fclose(in); + buf2=calloc(entry->declen+1024, 1); + if (buf2==NULL) { + printf("file decode memory allocate error\n"); + goto errret; + } + 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) { + printf("decode_zip size miss match err: %d != %d\n",(int)len,entry->declen); + goto errret; + } + } else { + memcpy(buf2,buf,entry->declen); + } + free(buf); + } + if (size!=NULL && entry!=NULL) + *size = entry->declen; + return buf2; +errret: + if (buf!=NULL) free(buf); + if (buf2!=NULL) free(buf2); + if (in!=NULL) fclose(in); + exit(1); //return NULL; +} + +/*========================================== + * Grfio : Resource file read + *------------------------------------------ + */ +void* grfio_read(char *fname) +{ + return grfio_reads(fname,NULL); +} + +/*========================================== + * Resource filename decode + *------------------------------------------ + */ +static unsigned 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 buf; +} + +/*========================================== + * Grfio : Entry table read + *------------------------------------------ + */ +static int grfio_entryread(char *gfname,int gentry) +{ + FILE *fp; + int grf_size,list_size; + unsigned char grf_header[0x2e]; + int lop,entry,entrys,ofs,grf_version; + unsigned char *fname; + unsigned char *grf_filelist; + + fp = fopen(gfname,"rb"); + if(fp==NULL) { + printf("%s not found\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(grf_header,"Master of Magic") || fseek(fp,getlong(grf_header+0x1e),1)){ // SEEK_CUR + fclose(fp); + printf("%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 = calloc(list_size, 1); + if(grf_filelist==NULL){ + fclose(fp); + printf("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){ + printf("file name too long : %s\n",fname); + free(grf_filelist); + exit(1); + } + srclen=0; + if((period_ptr=rindex(fname,'.'))!=NULL){ + for(lop=0;lop<4;lop++) { + if(strcasecmp(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; + } + free(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 (rSize > grf_size-ftell(fp)) { + fclose(fp); + printf("Illegal data format : grf compress entry size\n"); + return 4; + } + + rBuf = calloc( rSize , 1); // Get a Read Size + if (rBuf==NULL) { + fclose(fp); + printf("out of memory : grf compress entry table buffer\n"); + return 3; + } + grf_filelist = calloc( eSize , 1); // Get a Extend Size + if (grf_filelist==NULL) { + free(rBuf); + fclose(fp); + printf("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; + free(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 = grf_filelist+ofs; + if (strlen(fname)>sizeof(aentry.fn)-1) { + printf("grf : file name too long : %s\n",fname); + free(grf_filelist); + exit(1); + } + ofs2 = ofs+strlen(grf_filelist+ofs)+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; + } + free(grf_filelist); + + } else { //****** Grf Other version ****** + fclose(fp); + printf("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() +{ + int size; + unsigned char *buf,*ptr; + char w1[256],w2[256],src[256],dst[256]; + FILELIST *entry; + + buf=grfio_reads("data\\resnametable.txt",&size); + 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 { + //printf("file not found in data.grf : %s < %s\n",dst,src); + } + } + ptr = strchr(ptr,'\n'); // Next line + if (!ptr) break; + ptr++; + } + free(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) { + printf("gentrys limit : grfio_add\n"); + exit(1); + } + + printf("%s file reading...\n",fname); + + if (gentry_entrys>=gentry_maxentry) { + char **new_gentry = (char**)realloc( + (void*)gentry_table,(gentry_maxentry+GENTRY_ADDS)*sizeof(char*) ); + if (new_gentry!=NULL) { + int lop; + gentry_table = new_gentry; + gentry_maxentry += GENTRY_ADDS; + for(lop=gentry_entrys;lop<gentry_maxentry;lop++) + gentry_table[lop] = NULL; + } else { + printf("out of memory : grfio_add\n"); + exit(1); + } + } + len = strlen( fname ); + buf = calloc(len+1, 1); + if (buf==NULL) { + printf("out of memory : gentry\n"); + exit(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) +{ + int lop; + + if (filelist!=NULL) free(filelist); + filelist = NULL; + filelist_entrys = filelist_maxentry = 0; + + if (gentry_table!=NULL) { + for(lop=0;lop<gentry_entrys;lop++) { + if (gentry_table[lop]!=NULL) { + free(gentry_table[lop]); + } + } + free(gentry_table); + } + gentry_table = NULL; + gentry_entrys = gentry_maxentry = 0; +} + +/*========================================== + * Grfio : Initialize + *------------------------------------------ + */ +void grfio_init(char *fname) +{ + FILE *data_conf; + char line[1024], w1[1024], w2[1024]; + int result = 0, result2 = 0, result3 = 0; + + data_conf = fopen(fname, "r"); + + // It will read, if there is grf-files.txt. + if (data_conf) { + while(fgets(line, 1020, data_conf)) { + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + if(strcmp(w1, "data") == 0) + strcpy(data_file, w2); + else if(strcmp(w1, "sdata") == 0) + strcpy(sdata_file, w2); + else if(strcmp(w1, "adata") == 0) + strcpy(adata_file, w2); + else if(strcmp(w1,"data_dir") == 0) + strcpy(data_dir, w2); + } + } + + fclose(data_conf); + printf("read %s done\n",fname); + } // end of reading grf-files.txt + + hashinit(); // hash table initialization + + filelist = NULL; filelist_entrys = filelist_maxentry = 0; + gentry_table = NULL; gentry_entrys = gentry_maxentry = 0; + atexit(grfio_final); // End processing definition + + // Entry table reading + + if (strcmp(data_file, "") != 0) // If data directive exists in grf-files.txt (i.e. data_file is not equal to "") + result = grfio_add(data_file); // Primary data file + + if (strcmp(sdata_file, "") != 0) // If sdata directive exists in grf-files.txt (i.e. sdata_file is not equal to "") + result2 = grfio_add(sdata_file); // Sakray data file + + if (strcmp(adata_file, "") != 0) // If data directive exists in grf-files.txt (i.e. adata_file is not equal to "") + result3 = grfio_add(adata_file); // Alpha version data file + + if (result != 0 && result2 != 0 && result3 != 0) { + printf("not grf file readed exit!!\n"); + exit(1); // It ends, if a resource cannot read one. + } +} diff --git a/src/common/grfio.h b/src/common/grfio.h new file mode 100644 index 0000000..53b9da8 --- /dev/null +++ b/src/common/grfio.h @@ -0,0 +1,16 @@ +// $Id: grfio.h,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $ +#ifndef _GRFIO_H_ +#define _GRFIO_H_ + +void grfio_init(char*); // GRFIO Initialize +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 + +// Accessor to GRF filenames +char *grfio_setdatafile(const char *str); +char *grfio_setadatafile(const char *str); +char *grfio_setsdatafile(const char *str); + +#endif // _GRFIO_H_ diff --git a/src/common/lock.c b/src/common/lock.c new file mode 100644 index 0000000..9a2205b --- /dev/null +++ b/src/common/lock.c @@ -0,0 +1,37 @@ + +#include <stdio.h> +#include "lock.h" + +// 書き込みファイルの保護処理 +// (書き込みが終わるまで、旧ファイルを保管しておく) + +// 新しいファイルの書き込み開始 +FILE* lock_fopen(const char* filename,int *info) { + char newfile[512]; + FILE *fp; + int no = 0; + + // 安全なファイル名を得る(手抜き) + do { + sprintf(newfile,"%s_%04d.tmp",filename,++no); + } while((fp = fopen(newfile,"r")) && (fclose(fp), no<9999) ); + *info = no; + return fopen(newfile,"w"); +} + +// 旧ファイルを削除&新ファイルをリネーム +int lock_fclose(FILE *fp,const char* filename,int *info) { + int ret = 0; + char newfile[512]; + if(fp != NULL) { + ret = fclose(fp); + sprintf(newfile,"%s_%04d.tmp",filename,*info); + remove(filename); + // このタイミングで落ちると最悪。 + rename(newfile,filename); + return ret; + } else { + return 1; + } +} + diff --git a/src/common/lock.h b/src/common/lock.h new file mode 100644 index 0000000..795bf88 --- /dev/null +++ b/src/common/lock.h @@ -0,0 +1,8 @@ +#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 0000000..eda9bc2 --- /dev/null +++ b/src/common/malloc.c @@ -0,0 +1,44 @@ +#include <stdio.h> +#include <stdlib.h> +#include "malloc.h" + +void* aMalloc_( size_t size, const char *file, int line, const char *func ) +{ + void *ret; + +// printf("%s:%d: in func %s: malloc %d\n",file,line,func,size); + ret=malloc(size); + if(ret==NULL){ + printf("%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 ) +{ + void *ret; + +// printf("%s:%d: in func %s: calloc %d %d\n",file,line,func,num,size); + ret=calloc(num,size); + if(ret==NULL){ + printf("%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 ) +{ + void *ret; + +// printf("%s:%d: in func %s: realloc %p %d\n",file,line,func,p,size); + ret=realloc(p,size); + if(ret==NULL){ + printf("%s:%d: in func %s: realloc error out of memory!\n",file,line,func); + exit(1); + + } + return ret; +} diff --git a/src/common/malloc.h b/src/common/malloc.h new file mode 100644 index 0000000..3733a5e --- /dev/null +++ b/src/common/malloc.h @@ -0,0 +1,25 @@ +#ifndef _MALLOC_H_ +#define _MALLOC_H_ + +#include <stdlib.h> + +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + +#define ALC_MARK __FILE__, __LINE__, __func__ + +void* aMalloc_( size_t size, const char *file, int line, const char *func ); +void* aCalloc_( size_t num, size_t size, const char *file, int line, const char *func ); +void* aRealloc_( void *p, size_t size, const char *file, int line, const char *func ); + +#define aMalloc(n) aMalloc_(n,ALC_MARK) +#define aCalloc(m,n) aCalloc_(m,n,ALC_MARK) +#define aRealloc(p,n) aRealloc_(p,n,ALC_MARK) + + +#endif diff --git a/src/common/mmo.h b/src/common/mmo.h new file mode 100644 index 0000000..4105135 --- /dev/null +++ b/src/common/mmo.h @@ -0,0 +1,304 @@ +// $Id: mmo.h,v 1.3 2004/09/25 20:12:25 PoW Exp $ +// Original : mmo.h 2003/03/14 12:07:02 Rev.1.7 + +#ifndef _MMO_H_ +#define _MMO_H_ + +#include <time.h> +#include "utils.h" // LCCWIN32 + +#ifdef CYGWIN +// txtやlogなどの書き出すファイルの改行コード +#define RETCODE "\r\n" // (CR/LF:Windows系) +#else +#define RETCODE "\n" // (LF:Unix系) +#endif + +#define FIFOSIZE_SERVERLINK 128*1024 + +// set to 0 to not check IP of player between each server. +// set to another value if you want to check (1) +#define CMP_AUTHFIFO_IP 1 + +#define CMP_AUTHFIFO_LOGIN2 1 + +#define MAX_MAP_PER_SERVER 512 +#define MAX_INVENTORY 100 +#define MAX_AMOUNT 30000 +#define MAX_ZENY 1000000000 // 1G zeny +#define MAX_CART 100 +#define MAX_SKILL 450 +#define GLOBAL_REG_NUM 96 +#define ACCOUNT_REG_NUM 16 +#define ACCOUNT_REG2_NUM 16 +#define DEFAULT_WALK_SPEED 150 +#define MIN_WALK_SPEED 0 +#define MAX_WALK_SPEED 1000 +#define MAX_STORAGE 300 +#define MAX_GUILD_STORAGE 1000 +#define MAX_PARTY 12 +#define MAX_GUILD 36 // increased max guild members to accomodate for +2 increase for extension levels [Valaris] (removed) [PoW] +#define MAX_GUILDPOSITION 20 // increased max guild positions to accomodate for all members [Valaris] (removed) [PoW] +#define MAX_GUILDEXPLUSION 32 +#define MAX_GUILDALLIANCE 16 +#define MAX_GUILDSKILL 8 +#define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris] +#define MAX_GUILDLEVEL 50 + +#define MIN_HAIR_STYLE battle_config.min_hair_style +#define MAX_HAIR_STYLE battle_config.max_hair_style +#define MIN_HAIR_COLOR battle_config.min_hair_color +#define MAX_HAIR_COLOR battle_config.max_hair_color +#define MIN_CLOTH_COLOR battle_config.min_cloth_color +#define MAX_CLOTH_COLOR battle_config.max_cloth_color + +// for produce +#define MIN_ATTRIBUTE 0 +#define MAX_ATTRIBUTE 4 +#define ATTRIBUTE_NORMAL 0 +#define MIN_STAR 0 +#define MAX_STAR 3 + +#define MIN_PORTAL_MEMO 0 +#define MAX_PORTAL_MEMO 2 + +#define MAX_STATUS_TYPE 5 + +#define WEDDING_RING_M 2634 +#define WEDDING_RING_F 2635 + +#define CHAR_CONF_NAME "conf/char_athena.conf" + +struct item { + int id; + short nameid; + short amount; + unsigned short equip; + char identify; + char refine; + char attribute; + short card[4]; + short broken; +}; + +struct point{ + char map[24]; + short x,y; +}; + +struct skill { + unsigned short id,lv,flag; +}; + +struct global_reg { + char str[32]; + int value; +}; + +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[24]; + char rename_flag; + char incuvate; +}; + +struct mmo_charstatus { + int char_id; + int account_id; + int partner_id; + + int base_exp,job_exp,zeny; + + short class; + short status_point,skill_point; + int hp,max_hp,sp,max_sp; + short option,karma,manner; + short hair,hair_color,clothes_color; + int party_id,guild_id,pet_id; + + short weapon,shield; + short head_top,head_mid,head_bottom; + + char name[24]; + unsigned char base_level,job_level; + short str,agi,vit,int_,dex,luk; + unsigned char char_num,sex; + + unsigned long mapip; + unsigned int mapport; + + struct point last_point,save_point,memo_point[10]; + struct item inventory[MAX_INVENTORY],cart[MAX_CART]; + struct skill skill[MAX_SKILL]; + int global_reg_num; + struct global_reg global_reg[GLOBAL_REG_NUM]; + int account_reg_num; + struct global_reg account_reg[ACCOUNT_REG_NUM]; + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +}; + +struct storage { + int account_id; + short storage_status; + short storage_amount; + struct item storage[MAX_STORAGE]; +}; + +struct guild_storage { + int guild_id; + short storage_status; + short storage_amount; + struct item storage[MAX_GUILD_STORAGE]; +}; + +struct map_session_data; + +struct gm_account { + int account_id; + int level; +}; + +struct party_member { + int account_id; + char name[24],map[24]; + int leader,online,lv; + struct map_session_data *sd; +}; + +struct party { + int party_id; + char name[24]; + int exp; + int item; + struct party_member member[MAX_PARTY]; +}; + +struct guild_member { + int account_id, char_id; + short hair,hair_color,gender,class,lv; + int exp,exp_payper; + short online,position; + int rsv1,rsv2; + char name[24]; + struct map_session_data *sd; +}; + +struct guild_position { + char name[24]; + int mode; + int exp_mode; +}; + +struct guild_alliance { + int opposition; + int guild_id; + char name[24]; +}; + +struct guild_explusion { + char name[24]; + char mes[40]; + char acc[40]; + int account_id; + int rsv1,rsv2,rsv3; +}; + +struct guild_skill { + int id,lv; +}; + +struct guild { + int guild_id; + short guild_lv, connect_member, max_member, average_lv; + int exp,next_exp,skill_point,castle_id; + char name[24],master[24]; + struct guild_member member[MAX_GUILD]; + struct guild_position position[MAX_GUILDPOSITION]; + char mes1[60],mes2[120]; + int emblem_len,emblem_id; + char emblem_data[2048]; + struct guild_alliance alliance[MAX_GUILDALLIANCE]; + struct guild_explusion explusion[MAX_GUILDEXPLUSION]; + struct guild_skill skill[MAX_GUILDSKILL]; +}; + +struct guild_castle { + int castle_id; + char map_name[24]; + char castle_name[24]; + char castle_event[24]; + int guild_id; + int economy; + int defense; + int triggerE; + int triggerD; + int nextTime; + int payTime; + int createTime; + int visibleC; + int visibleG0; + int visibleG1; + int visibleG2; + int visibleG3; + int visibleG4; + int visibleG5; + int visibleG6; + int visibleG7; + int Ghp0; // added Guardian HP [Valaris] + int Ghp1; + int Ghp2; + int Ghp3; + int Ghp4; + int Ghp5; + int Ghp6; + int Ghp7; + int GID0; + int GID1; + int GID2; + int GID3; + int GID4; + int GID5; + int GID6; + int GID7; // end addition [Valaris] +}; +struct square { + int val1[5]; + int val2[5]; +}; + +enum { + GBI_EXP =1, // ギルドのEXP + GBI_GUILDLV =2, // ギルドのLv + GBI_SKILLPOINT =3, // ギルドのスキルポイント + GBI_SKILLLV =4, // ギルドスキルLv + + GMI_POSITION =0, // メンバーの役職変更 + GMI_EXP =1, // メンバーのEXP + +}; + +#ifndef LCCWIN32 +#ifndef strcmpi +#define strcmpi strcasecmp +#endif +#ifndef stricmp +#define stricmp strcasecmp +#endif +#ifndef strncmpi +#define strncmpi strncasecmp +#endif +#ifndef strnicmp +#define strnicmp strncasecmp +#endif +#endif + +#endif // _MMO_H_ diff --git a/src/common/nullpo.c b/src/common/nullpo.c new file mode 100644 index 0000000..5fbf5fc --- /dev/null +++ b/src/common/nullpo.c @@ -0,0 +1,90 @@ +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "nullpo.h" +// #include "logs.h" // 布石してみる + +static void nullpo_info_core(const char *file, int line, const char *func, + const char *fmt, va_list ap); + +/*====================================== + * Nullチェック 及び 情報出力 + *-------------------------------------- + */ +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情報出力(外部呼出し向けラッパ) + *-------------------------------------- + */ +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情報出力(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; + + printf("--- nullpo info --------------------------------------------\n"); + printf("%s:%d: in func `%s'\n", file, line, func); + if (fmt != NULL) + { + if (fmt[0] != '\0') + { + vprintf(fmt, ap); + + // 最後に改行したか確認 + if (fmt[strlen(fmt)-1] != '\n') + printf("\n"); + } + } + printf("--- end nullpo info ----------------------------------------\n"); + + // ここらでnullpoログをファイルに書き出せたら + // まとめて提出できるなと思っていたり。 +} diff --git a/src/common/nullpo.h b/src/common/nullpo.h new file mode 100644 index 0000000..2d33500 --- /dev/null +++ b/src/common/nullpo.h @@ -0,0 +1,222 @@ +#ifndef _NULLPO_H_ +#define _NULLPO_H_ + + +#define NULLPO_CHECK 1 + // 全体のスイッチを宣言しているヘッダがあれば + // そこに移動していただけると + + +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + +#ifdef LCCWIN32 +#define __attribute__(x) /* nothing */ +#endif + + +#define NLP_MARK __FILE__, __LINE__, __func__ + +/*---------------------------------------------------------------------------- + * Macros + *---------------------------------------------------------------------------- + */ +/*====================================== + * Nullチェック 及び 情報出力後 return + *・展開するとifとかreturn等が出るので + * 一行単体で使ってください。 + *・nullpo_ret(x = func()); + * のような使用法も想定しています。 + *-------------------------------------- + * nullpo_ret(t) + * 戻り値 0固定 + * [引数] + * t チェック対象 + *-------------------------------------- + * nullpo_retv(t) + * 戻り値 なし + * [引数] + * t チェック対象 + *-------------------------------------- + * nullpo_retr(ret, t) + * 戻り値 指定 + * [引数] + * ret return(ret); + * t チェック対象 + *-------------------------------------- + * nullpo_ret_f(t, fmt, ...) + * 詳細情報出力用 + * 戻り値 0 + * [引数] + * t チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + * nullpo_retv_f(t, fmt, ...) + * 詳細情報出力用 + * 戻り値 なし + * [引数] + * t チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + * nullpo_retr_f(ret, t, fmt, ...) + * 詳細情報出力用 + * 戻り値 指定 + * [引数] + * ret return(ret); + * t チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + */ + +#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);} + + +// 可変引数マクロに関する条件コンパイル +#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);} + +#elif __GNUC__ >= 2 +/* GCC用 */ +#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);} + +#else + +/* その他の場合・・・ orz */ + +#endif + +#else /* NULLPO_CHECK */ +/* No Nullpo check */ + +// if((t)){;} +// 良い方法が思いつかなかったので・・・苦肉の策です。 +// 一応ワーニングは出ないはず + +#define nullpo_ret(t) if((t)){;} +#define nullpo_retv(t) if((t)){;} +#define nullpo_retr(ret, t) if((t)){;} + +// 可変引数マクロに関する条件コンパイル +#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)){;} + +#elif __GNUC__ >= 2 +/* GCC用 */ +#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)){;} + +#else +/* その他の場合・・・ orz */ +#endif + +#endif /* NULLPO_CHECK */ + +/*---------------------------------------------------------------------------- + * Functions + *---------------------------------------------------------------------------- + */ +/*====================================== + * nullpo_chk + * Nullチェック 及び 情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + * target チェック対象 + * [返り値] + * 0 OK + * 1 NULL + *-------------------------------------- + */ +int nullpo_chk(const char *file, int line, const char *func, const void *target); + + +/*====================================== + * nullpo_chk_f + * Nullチェック 及び 詳細な情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + * target チェック対象 + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + * [返り値] + * 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情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + *-------------------------------------- + */ +void nullpo_info(const char *file, int line, const char *func); + + +/*====================================== + * nullpo_info_f + * nullpo詳細情報出力 + * [引数] + * file __FILE__ + * line __LINE__ + * func __func__ (関数名) + * これらには NLP_MARK を使うとよい + * fmt ... vprintfに渡される + * 備考や関係変数の書き出しなどに + *-------------------------------------- + */ +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/socket.c b/src/common/socket.c new file mode 100644 index 0000000..1711286 --- /dev/null +++ b/src/common/socket.c @@ -0,0 +1,439 @@ +// $Id: socket.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $ +// original : core.c 2003/02/26 18:03:12 Rev 1.7 + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> + +#ifdef LCCWIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/time.h> +#include <unistd.h> +#endif + +#include <fcntl.h> +#include <string.h> + +#include "mmo.h" // [Valaris] thanks to fov +#include "socket.h" +#include "utils.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +fd_set readfds; +int fd_max; + +int rfifo_size = 65536; +int wfifo_size = 65536; + +struct socket_data *session[FD_SETSIZE]; + +static int null_parse(int fd); +static int (*default_func_parse)(int) = null_parse; + +/*====================================== + * CORE : Set function + *-------------------------------------- + */ +void set_defaultparse(int (*defaultparse)(int)) +{ + default_func_parse = defaultparse; +} + +/*====================================== + * CORE : Socket Sub Function + *-------------------------------------- + */ + +static int recv_to_fifo(int fd) +{ + int len; + + //printf("recv_to_fifo : %d %d\n",fd,session[fd]->eof); + if(session[fd]->eof) + return -1; + + +#ifdef LCCWIN32 + len = recv(fd,session[fd]->rdata+session[fd]->rdata_size, RFIFOSPACE(fd), 0); +#else + len=read(fd,session[fd]->rdata+session[fd]->rdata_size,RFIFOSPACE(fd)); +#endif + +// printf (":::RECEIVE:::\n"); +// dump(session[fd]->rdata, len); printf ("\n"); + + //{ int i; printf("recv %d : ",fd); for(i=0;i<len;i++){ printf("%02x ",RFIFOB(fd,session[fd]->rdata_size+i)); } printf("\n");} + if(len>0){ + session[fd]->rdata_size+=len; + } else if(len<=0){ + // value of connection is not necessary the same +// if (fd == 4) // Removed [Yor] +// printf("Char-Server Has Disconnected.\n"); +// else if (fd == 5) // Removed [Yor] +// printf("Attempt To Log In Successful.\n"); +// else if (fd == 7) // Removed [Yor] +// printf("Char-Server Has Disconnected.\n"); +// else if (fd == 8) // Removed [Valaris] +// printf("%s has logged off your server.\n",RFIFOP(fd,6)); // Removed [Valaris] + +// else if (fd != 8) // [Valaris] + printf("set eof : connection #%d\n", fd); + session[fd]->eof=1; + } + return 0; +} + +static int send_from_fifo(int fd) +{ + int len; + + //printf("send_from_fifo : %d\n",fd); + if(session[fd]->eof) + return -1; + +#ifdef LCCWIN32 + len = send(fd, session[fd]->wdata,session[fd]->wdata_size, 0); +#else + len=write(fd,session[fd]->wdata,session[fd]->wdata_size); +#endif + +// printf (":::SEND:::\n"); +// dump(session[fd]->wdata, len); printf ("\n"); + + //{ int i; printf("send %d : ",fd); for(i=0;i<len;i++){ printf("%02x ",session[fd]->wdata[i]); } printf("\n");} + if(len>0){ + if(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; + } + } else { + printf("set eof :%d\n",fd); + session[fd]->eof=1; + } + return 0; +} + +static int null_parse(int fd) +{ + printf("null_parse : %d\n",fd); + RFIFOSKIP(fd,RFIFOREST(fd)); + return 0; +} + +/*====================================== + * CORE : Socket Function + *-------------------------------------- + */ + +static int connect_client(int listen_fd) +{ + int fd; + struct sockaddr_in client_address; + int len; + int result; + int yes = 1; // reuse fix + + //printf("connect_client : %d\n",listen_fd); + + len=sizeof(client_address); + + fd=accept(listen_fd,(struct sockaddr*)&client_address,&len); + if(fd_max<=fd) fd_max=fd+1; + +// setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,NULL,0); + setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes); // reuse fix +#ifdef SO_REUSEPORT +// setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,NULL,0); + setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes); //reuse fix +#endif +// setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,NULL,0); + setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes); // reuse fix + + if(fd==-1){ + perror("accept"); + } else { + FD_SET(fd,&readfds); + } + +#ifdef LCCWIN32 + { + unsigned long val = 1; + ioctlsocket(fd, FIONBIO, &val); + } +#else + result = fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + + CREATE(session[fd], struct socket_data, 1); + CREATE(session[fd]->rdata, char, rfifo_size); + CREATE(session[fd]->wdata, char, wfifo_size); + + session[fd]->max_rdata = rfifo_size; + session[fd]->max_wdata = wfifo_size; + session[fd]->func_recv = recv_to_fifo; + session[fd]->func_send = send_from_fifo; + session[fd]->func_parse = default_func_parse; + session[fd]->client_addr = client_address; + + //printf("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; + int yes = 1; // reuse fix + + fd = socket( AF_INET, SOCK_STREAM, 0 ); + if(fd_max<=fd) fd_max=fd+1; + +#ifdef LCCWIN32 + { + unsigned long val = 1; + ioctlsocket(fd, FIONBIO, &val); + } +#else + result = fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + +// setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,NULL,0); + setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes); // reuse fix +#ifdef SO_REUSEPORT +// setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,NULL,0); + setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes); //reuse fix +#endif +// setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,NULL,0); + setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes); // reuse fix + + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = htonl( INADDR_ANY ); + server_address.sin_port = htons(port); + + result = bind(fd, (struct sockaddr*)&server_address, sizeof(server_address)); + if( result == -1 ) { + perror("bind"); + exit(1); + } + result = listen( fd, 5 ); + if( result == -1 ) { /* error */ + perror("listen"); + exit(1); + } + + FD_SET(fd, &readfds ); + + CREATE(session[fd], struct socket_data, 1); + + if(session[fd]==NULL){ + printf("out of memory : make_listen_port\n"); + exit(1); + } + memset(session[fd],0,sizeof(*session[fd])); + session[fd]->func_recv = connect_client; + + return fd; +} + +int make_connection(long ip,int port) +{ + struct sockaddr_in server_address; + int fd; + int result; + int yes = 1; // reuse fix + + fd = socket( AF_INET, SOCK_STREAM, 0 ); + if(fd_max<=fd) fd_max=fd+1; +// setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,NULL,0); + setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes); // reuse fix +#ifdef SO_REUSEPORT +// setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,NULL,0); + setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes); //reuse fix +#endif +// setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,NULL,0); + setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes); // reuse fix + + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = ip; + server_address.sin_port = htons(port); + +#ifdef LCCWIN32 + { + unsigned long val = 1; + ioctlsocket(fd, FIONBIO, &val); + } +#else + result = fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + + result = connect(fd, (struct sockaddr *)(&server_address),sizeof(struct sockaddr_in)); + + FD_SET(fd,&readfds); + + CREATE(session[fd], struct socket_data, 1); + CREATE(session[fd]->rdata, char, rfifo_size); + CREATE(session[fd]->wdata, char, wfifo_size); + + session[fd]->max_rdata = rfifo_size; + session[fd]->max_wdata = wfifo_size; + session[fd]->func_recv = recv_to_fifo; + session[fd]->func_send = send_from_fifo; + session[fd]->func_parse = default_func_parse; + + 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) + free(session[fd]->rdata); + if(session[fd]->wdata) + free(session[fd]->wdata); + if(session[fd]->session_data) + free(session[fd]->session_data); + free(session[fd]); + } + session[fd]=NULL; + //printf("delete_session:%d\n",fd); + return 0; +} + +int realloc_fifo(int fd,int rfifo_size,int wfifo_size) +{ + struct socket_data *s=session[fd]; + if( s->max_rdata != rfifo_size && s->rdata_size < rfifo_size){ + RECREATE(s->rdata, char, rfifo_size); + s->max_rdata = rfifo_size; + } + if( s->max_wdata != wfifo_size && s->wdata_size < wfifo_size){ + RECREATE(s->wdata, char, wfifo_size); + s->max_wdata = wfifo_size; + } + return 0; +} + +int WFIFOSET(int fd,int len) +{ + struct socket_data *s=session[fd]; + if( s->wdata_size+len+16384 > s->max_wdata ){ + realloc_fifo(fd,s->max_rdata, s->max_wdata <<1 ); + printf("socket: %d wdata expanded to %d bytes.\n",fd, s->max_wdata); + } + s->wdata_size=(s->wdata_size+(len)+2048 < s->max_wdata) ? + s->wdata_size+len : (printf("socket: %d wdata lost !!\n",fd),s->wdata_size); + return 0; +} + +int do_sendrecv(int next) +{ + fd_set rfd,wfd; + struct timeval timeout; + int ret,i; + + rfd=readfds; + FD_ZERO(&wfd); + for(i=0;i<fd_max;i++){ + if(!session[i] && FD_ISSET(i,&readfds)){ + printf("force clr fds %d\n",i); + FD_CLR(i,&readfds); + continue; + } + if(!session[i]) + continue; + if(session[i]->wdata_size) + FD_SET(i,&wfd); + } + timeout.tv_sec = next/1000; + timeout.tv_usec = next%1000*1000; + ret = select(fd_max,&rfd,&wfd,NULL,&timeout); + if(ret<=0) + return 0; + for(i=0;i<fd_max;i++){ + if(!session[i]) + continue; + if(FD_ISSET(i,&wfd)){ + //printf("write:%d\n",i); + if(session[i]->func_send) + //send_from_fifo(i); + session[i]->func_send(i); + } + if(FD_ISSET(i,&rfd)){ + //printf("read:%d\n",i); + if(session[i]->func_recv) + //recv_to_fifo(i); + session[i]->func_recv(i); + } + } + return 0; +} + +int do_parsepacket(void) +{ + int i; + for(i=0;i<fd_max;i++){ + if(!session[i]) + continue; + if(session[i]->rdata_size==0 && session[i]->eof==0) + continue; + if(session[i]->func_parse){ + session[i]->func_parse(i); + if(!session[i]) + continue; + } + RFIFOFLUSH(i); + } + return 0; +} + +void do_socket(void) +{ + FD_ZERO(&readfds); +} + +int RFIFOSKIP(int fd,int len) +{ + struct socket_data *s=session[fd]; + + if (s->rdata_size-s->rdata_pos-len<0) { + fprintf(stderr,"too many skip\n"); + exit(1); + } + + s->rdata_pos = s->rdata_pos+len; + + return 0; +} + + +int Net_Init(void) +{ + #ifdef LCCWIN32 + /* Start up the windows networking */ + WORD version_wanted = MAKEWORD(1,1); + WSADATA wsaData; + + if ( WSAStartup(version_wanted, &wsaData) != 0 ) { + printf("SYSERR: WinSock not available!\n"); + exit(1); + } + #endif + + return(0); +} + diff --git a/src/common/socket.h b/src/common/socket.h new file mode 100644 index 0000000..fe06e40 --- /dev/null +++ b/src/common/socket.h @@ -0,0 +1,96 @@ +// original : core.h 2003/03/14 11:55:25 Rev 1.4 + +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#include <stdio.h> + +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +// define declaration + +#define RFIFOP(fd,pos) (session[fd]->rdata+session[fd]->rdata_pos+(pos)) +#define RFIFOB(fd,pos) (*(unsigned char*)(session[fd]->rdata+session[fd]->rdata_pos+(pos))) +#define RFIFOW(fd,pos) (*(unsigned short*)(session[fd]->rdata+session[fd]->rdata_pos+(pos))) +#define RFIFOL(fd,pos) (*(unsigned int*)(session[fd]->rdata+session[fd]->rdata_pos+(pos))) +//#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 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 RFIFOSPACE(fd) (session[fd]->max_rdata-session[fd]->rdata_size) +#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 int*)RBUFP((p),(pos))) + +#define WFIFOSPACE(fd) (session[fd]->max_wdata-session[fd]->wdata_size) +#define WFIFOP(fd,pos) (session[fd]->wdata+session[fd]->wdata_size+(pos)) +#define WFIFOB(fd,pos) (*(unsigned char*)(session[fd]->wdata+session[fd]->wdata_size+(pos))) +#define WFIFOW(fd,pos) (*(unsigned short*)(session[fd]->wdata+session[fd]->wdata_size+(pos))) +#define WFIFOL(fd,pos) (*(unsigned int*)(session[fd]->wdata+session[fd]->wdata_size+(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*)WBUFP((p),(pos))) +#define WBUFW(p,pos) (*(unsigned short*)WBUFP((p),(pos))) +#define WBUFL(p,pos) (*(unsigned int*)WBUFP((p),(pos))) + +#ifdef __INTERIX +#define FD_SETSIZE 4096 +#endif // __INTERIX + + +/* Removed Cygwin FD_SETSIZE declarations, now are directly passed on to the compiler through Makefile [Valaris] */ + +// Struct declaration + +struct socket_data{ + int eof; + unsigned char *rdata,*wdata; + int max_rdata,max_wdata; + int rdata_size,wdata_size; + int rdata_pos; + struct sockaddr_in client_addr; + int (*func_recv)(int); + int (*func_send)(int); + int (*func_parse)(int); + void* session_data; +}; + +// Data prototype declaration + +#ifdef LCCWIN32 + + #undef FD_SETSIZE + #define FD_SETSIZE 4096 + +#endif + +extern struct socket_data *session[FD_SETSIZE]; + +extern int rfifo_size,wfifo_size; +extern int fd_max; + +// Function prototype declaration + +int make_listen_port(int); +int make_connection(long,int); +int delete_session(int); +int realloc_fifo(int fd,int rfifo_size,int wfifo_size); +int WFIFOSET(int fd,int len); +int RFIFOSKIP(int fd,int len); + +int do_sendrecv(int next); +int do_parsepacket(void); +void do_socket(void); + +void set_defaultparse(int (*defaultparse)(int)); + +int Net_Init(void); + +#endif // _SOCKET_H_ diff --git a/src/common/timer.c b/src/common/timer.c new file mode 100644 index 0000000..8193ff9 --- /dev/null +++ b/src/common/timer.c @@ -0,0 +1,312 @@ +// $Id: timer.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $ +// original : core.c 2003/02/26 18:03:12 Rev 1.7 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <sys/socket.h> +#include <sys/time.h> +#endif + +#include "timer.h" +#include "utils.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +static struct TimerData* timer_data; +static int timer_data_max,timer_data_num; +static int* free_timer_list; +static int free_timer_list_max, free_timer_list_pos; + +static int timer_heap_max; +static int* timer_heap = NULL; + +// 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; + +#if defined(LCCWIN32) +void gettimeofday(struct timeval *t, struct timezone *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; + + CREATE(tfl, struct timer_func_list, 1); + CREATE(tfl->name, char, 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; + for(tfl = tfl_root;tfl;tfl = tfl->next) { + if (func == tfl->func) + return tfl->name; + } + return "???"; +} + +/*---------------------------- + * 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, h; + + if (timer_heap == NULL || timer_heap[0] + 1 >= timer_heap_max) { + int first = timer_heap == NULL; + + timer_heap_max += 256; + RECREATE(timer_heap, int, timer_heap_max); + memset(timer_heap + (timer_heap_max - 256), 0, sizeof(int) * 256); + if (first) + timer_heap[0] = 0; + } + + timer_heap[0]++; + + for (h = timer_heap[0]-1, i = (h - 1) / 2; + h > 0 && DIFF_TICK(timer_data[index].tick, + timer_data[timer_heap[i + 1]].tick) < 0; + i = (h - 1) / 2) { + timer_heap[h + 1] = timer_heap[i + 1]; + h = i; + } + timer_heap[h + 1] = index; +} + +static int top_timer_heap() +{ + if (timer_heap == NULL || timer_heap[0] <= 0) + return -1; + + return timer_heap[1]; +} + +static int pop_timer_heap() +{ + int i,h,k; + int ret,last; + + if (timer_heap == NULL || timer_heap[0] <= 0) + return -1; + ret = timer_heap[1]; + last = timer_heap[timer_heap[0]]; + timer_heap[0]--; + + for(h = 0,k = 2;k<timer_heap[0];k = k * 2 + 2) { + if (DIFF_TICK(timer_data[timer_heap[k + 1]].tick , timer_data[timer_heap[k]].tick)>0) + k--; + timer_heap[h + 1] = timer_heap[k + 1], h = k; + } + if (k == timer_heap[0]) + timer_heap[h + 1] = timer_heap[k], h = k-1; + + for(i = (h-1)/2; + h>0 && DIFF_TICK(timer_data[timer_heap[i + 1]].tick , timer_data[last].tick)>0; + i = (h-1)/2) { + timer_heap[h + 1] = timer_heap[i + 1],h = i; + } + timer_heap[h + 1] = last; + + return ret; +} + +int add_timer(unsigned int tick,int (*func)(int,unsigned int,int,int),int id,int data) +{ + struct TimerData* td; + 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) { + int j; + if (timer_data_max == 0) { + timer_data_max = 256; + CREATE(timer_data, struct TimerData, timer_data_max); + } else { + timer_data_max += 256; + RECREATE(timer_data, struct TimerData, timer_data_max); + if (timer_data == NULL) { + printf("out of memory : add_timer timer_data\n"); + exit(1); + } + memset(timer_data + (timer_data_max - 256), 0, + sizeof(struct TimerData) * 256); + } + for(j = timer_data_max-256;j<timer_data_max; j++) + timer_data[j].type = 0; + } + td = &timer_data[i]; + td->tick = tick; + td->func = func; + td->id = id; + td->data = data; + td->type = TIMER_ONCE_AUTODEL; + td->interval = 1000; + push_timer_heap(i); + if (i >= timer_data_num) + timer_data_num = i + 1; + return i; +} + +int add_timer_interval(unsigned int tick,int (*func)(int,unsigned int,int,int),int id,int data,int interval) +{ + int tid; + tid = add_timer(tick,func,id,data); + timer_data[tid].type = TIMER_INTERVAL; + timer_data[tid].interval = interval; + return tid; +} + +int delete_timer(int id,int (*func)(int,unsigned int,int,int)) +{ + if (id <= 0 || id >= timer_data_num) { + printf("delete_timer error : no such timer %d\n", id); + return -1; + } + if (timer_data[id].func != func) { + printf("delete_timer error : function dismatch %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; + timer_data[id].tick -= 60 * 60 * 1000; + return 0; +} + +int addtick_timer(int tid,unsigned int tick) +{ + return timer_data[tid].tick += tick; +} +struct TimerData* get_timer(int tid) +{ + return &timer_data[tid]; +} + + +int do_timer(unsigned int tick) +{ + int i,nextmin = 1000; + +#if 0 + static int disp_tick = 0; + if (DIFF_TICK(disp_tick,tick)<-5000 || DIFF_TICK(disp_tick,tick)>5000) { + printf("timer %d(%d + %d)\n",timer_data_num,timer_heap[0],free_timer_list_pos); + disp_tick = tick; + } +#endif + + while((i = top_timer_heap()) >= 0) { + if (DIFF_TICK(timer_data[i].tick , tick)>0) { + nextmin = DIFF_TICK(timer_data[i].tick , tick); + break; + } + pop_timer_heap(); + timer_data[i].type |= TIMER_REMOVE_HEAP; + if (timer_data[i].func) { + if (DIFF_TICK(timer_data[i].tick , tick) < -1000) { + // 1秒以上の大幅な遅延が発生しているので、 + // timer処理タイミングを現在値とする事で + // 呼び出し時タイミング(引数のtick)相対で処理してる + // timer関数の次回処理タイミングを遅らせる + 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; + RECREATE(free_timer_list, int, free_timer_list_max); + memset(free_timer_list + (free_timer_list_max - 256), 0, + 256 * sizeof(free_timer_list[0])); + } + 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<10) + nextmin = 10; + return nextmin; +} + +void timer_final() +{ + free(timer_data); +} diff --git a/src/common/timer.h b/src/common/timer.h new file mode 100644 index 0000000..f6fc5c8 --- /dev/null +++ b/src/common/timer.h @@ -0,0 +1,45 @@ +// original : core.h 2003/03/14 11:55:25 Rev 1.4 + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#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 + +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); +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)); + +extern void timer_final(); + +#endif // _TIMER_H_ diff --git a/src/common/utils.c b/src/common/utils.c new file mode 100644 index 0000000..b0ecd26 --- /dev/null +++ b/src/common/utils.c @@ -0,0 +1,108 @@ +#include <string.h> +#include "utils.h" +#include <stdio.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"); +} + + +#ifdef LCCWIN32 +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) { + printf("SYSERR: str_cmp() passed 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) { + printf("SYSERR: strn_cmp() passed 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 = strlen(name); + while (len--) { + if (*name >= 'a' && *name <= 'z') + *name -= ('a' - 'A'); + name++; + } +} + +void str_lower(char *name) +{ + int len = strlen(name); + + while (len--) { + if (*name >= 'A' && *name <= 'Z') + *name += ('a' - 'A'); + name++; + } +} + +#endif + diff --git a/src/common/utils.h b/src/common/utils.h new file mode 100644 index 0000000..29463cf --- /dev/null +++ b/src/common/utils.h @@ -0,0 +1,33 @@ + +#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 */ + + +#ifdef LCCWIN32 + 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); + + +#define CREATE(result, type, number) do {\ + if ((number) * sizeof(type) <= 0) \ + printf("SYSERR: Zero bytes or less requested at %s:%d.\n", __FILE__, __LINE__); \ + if (!((result) = (type *) calloc ((number), sizeof(type)))) \ + { perror("SYSERR: malloc failure"); abort(); } } while(0) + +#define RECREATE(result,type,number) do {\ + if (!((result) = (type *) realloc ((result), sizeof(type) * (number))))\ + { printf("SYSERR: realloc failure"); abort(); } } while(0) + diff --git a/src/common/version.h b/src/common/version.h new file mode 100644 index 0000000..e33e2b3 --- /dev/null +++ b/src/common/version.h @@ -0,0 +1,27 @@ +// $Id: version.h,v 1.2 2004/09/22 09:49:06 PoW Exp $ +#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_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はパッチ番号です。 +// これは無理に変えなくても気が向いたら変える程度の扱いで。 +// (毎回アップロードの度に変更するのも面倒と思われるし、そもそも +// この項目を参照する人がいるかどうかで疑問だから。) +// その程度の扱いなので、サーバーに問い合わせる側も、あくまで目安程度の扱いで +// あんまり信用しないこと。 +// 鯖snapshotの時や、大きな変更があった場合は設定してほしいです。 +// C言語の仕様上、最初に0を付けると8進数になるので間違えないで下さい。 +#define ATHENA_MOD_VERSION 1052 // mod version (patch No.) + +#endif diff --git a/src/ladmin/GNUmakefile b/src/ladmin/GNUmakefile new file mode 100644 index 0000000..ce19d9d --- /dev/null +++ b/src/ladmin/GNUmakefile @@ -0,0 +1,14 @@ +all: ladmin
+txt: ladmin
+sql: ladmin
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h
+
+ladmin: ladmin.o md5calc.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ ladmin.o md5calc.o $(COMMON_OBJ)
+ladmin.o: ladmin.c ladmin.h md5calc.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h
+
+clean:
+ rm -f *.o ../../ladmin
diff --git a/src/ladmin/Makefile b/src/ladmin/Makefile new file mode 100644 index 0000000..ce19d9d --- /dev/null +++ b/src/ladmin/Makefile @@ -0,0 +1,14 @@ +all: ladmin
+txt: ladmin
+sql: ladmin
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h
+
+ladmin: ladmin.o md5calc.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ ladmin.o md5calc.o $(COMMON_OBJ)
+ladmin.o: ladmin.c ladmin.h md5calc.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h
+
+clean:
+ rm -f *.o ../../ladmin
diff --git a/src/ladmin/ladmin.c b/src/ladmin/ladmin.c new file mode 100644 index 0000000..497f3bd --- /dev/null +++ b/src/ladmin/ladmin.c @@ -0,0 +1,4385 @@ +// $Id: ladmin.c,v 1.1.1.1 2004/09/10 17:26:52 MagicalTux Exp $ +/////////////////////////////////////////////////////////////////////////// +// EAthena login-server remote administration tool +// Ladamin in C by [Yor] +// if you modify this software, modify ladmin in tool too. +/////////////////////////////////////////////////////////////////////////// + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> // gettimeofday +#include <time.h> +#include <sys/ioctl.h> +#include <unistd.h> // close +#include <signal.h> +#include <fcntl.h> +#include <string.h> // str* +#include <arpa/inet.h> // inet_addr +#include <netdb.h> // gethostbyname +#include <stdarg.h> // valist +#include <ctype.h> // tolower + +#include "core.h" +#include "socket.h" +#include "ladmin.h" +#include "version.h" +#include "mmo.h" + +#ifdef PASSWORDENC +#include "md5calc.h" +#endif + +#ifdef MEMWATCH +#include "memwatch.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軋is/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(&(tv.tv_sec))); + sprintf(tmpstr + strlen(tmpstr), ".%03d: %s", (int)tv.tv_usec / 1000, fmt); + vfprintf(logfp, tmpstr, ap); + } + fclose(logfp); + } + + va_end(ap); + return 0; +} + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars(unsigned char *str) { + int i; + int change = 0; + + for(i = 0; str[i]; i++) { + if (str[i] < 32) { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//--------------------------------------------- +// Function to return ordonal text of a number. +//--------------------------------------------- +char* makeordinal(int number) { + if (defaultlanguage == 'F') { + if (number == 0) + return ""; + else if (number == 1) + return "er"; + else + return "鑪e"; + } 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鑽e interdit trouv dans le nom du compte (%d%s caract鑽e).\n", i+1, makeordinal(i+1)); + ladmin_log("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e)." 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鑽es.\n"); + ladmin_log("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es." 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鑽es.\n"); + ladmin_log("Nom du compte trop long. Entrez un nom de compte de 4-23 caract鑽es." 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(unsigned char *email) { + char ch; + unsigned char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +//---------------------------------- +// Sub-function: Input of a password +//---------------------------------- +int typepasswd(char * password) { + char password1[1023], password2[1023]; + int letter; + int i; + + if (defaultlanguage == 'F') { + ladmin_log("Aucun mot de passe n'a 騁 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駻ification du mot de passe: Saisissez le m麥e mot de passe svp.\n"); + ladmin_log("Erreur de v駻ification du mot de passe: Saisissez le m麥e 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鑽e interdit trouv dans le mot de passe (%d%s caract鑽e).\n", i+1, makeordinal(i+1)); + ladmin_log("Caract鑽e interdit trouv dans le nom du compte (%d%s caract鑽e)." 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鑽es.\n"); + ladmin_log("Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es." 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鑽es.\n"); + ladmin_log("Mot de passe trop long. Entrez un mot de passe de 4-23 caract鑽es." 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馥\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馥 un compte avec l'email par d馭aut (a@a.com).\n"); + printf(" Concernant le sexe, seule la premi鑽e lettre compte (F ou M).\n"); + printf(" L'e-mail est a@a.com (e-mail par d馭aut). 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駑ent modifi:\n"); + printf(" a ou y: ann馥\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麥e 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馭aut [hh:mm:ss]: 23:59:59.\n"); + printf("banset <nomcompte> 0\n"); + printf(" D饕anni 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'駲uivalent de state <nom_compte> 5.\n"); + } else if (strcmp(command, "check") == 0) { + printf("check <nomcompte> <motdepasse>\n"); + printf(" V駻ifie 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騁hode que vous poss馘ez pour savoir\n"); + printf(" si un mot de passe est le bon. L'autre m騁hode est\n"); + printf(" d'avoir un acc鑚 ('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鑚 confirmation, le compte est d騁ruit.\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馭aut: 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駭駻al sur tous les serveurs de map (en jaune).\n"); + } else if (strcmp(command, "kamib") == 0) { + printf("kamib <message>\n"); + printf(" Envoi un message g駭駻al 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軋is' 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駱art 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駑o d'un compte.\n"); + printf(" 'memo': Il peut avoir jusqu' 253 caract鑽es (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鑽e.\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麥e que celui du packet 0x006a + 1.\n"); + printf(" les possibilit駸 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駑ent modifi:\n"); + printf(" a ou y: ann馥\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麥e temps.\n"); + printf("NOTE: Vous ne pouvez pas modifier une limite de validit illimit馥. Si vous\n"); + printf(" d駸irez le faire, c'est que vous voulez probablement cr馥r un limite de\n"); + printf(" validit limit馥. 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馭aut [hh:mm:ss]: 23:59:59.\n"); + printf("timeset <nomcompte> 0\n"); + printf(" Donne une limite de validit illimit馥 (0 = illimit馥).\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'駲uivalent 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'駲uivalent 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馥 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駻ifie un mot de passe d'un compte\n"); + printf(" create <nomcompte> <sexe> <email> <motdepasse> -- Cr馥 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駭駻al (en jaune)\n"); + printf(" kamib <message> -- Envoi un message g駭駻al (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馥\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軋is 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鑼res pour cr馥r 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鑼res pour cr馥r 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鑽e interdit (%c) trouv dans le nom du compte (%d%s caract鑽e).\n", name[i], i+1, makeordinal(i+1)); + ladmin_log("Caract鑽e interdit (%c) trouv dans le nom du compte (%d%s caract鑽e)." 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鑽es maximum svp.\n", email); + ladmin_log("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es 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黎e au serveur de logins pour cr馥r 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麥e temps.\n"); + ladmin_log("Nombre incorrect de param鑼res 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馥: %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馥\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麥e 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馥s correct (de -127 127), svp.\n"); + ladmin_log("Ajustement de l'ann馥 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黎e 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黎e 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馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res 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馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res 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馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res 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鑼res 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黎e 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 sr 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馥.\n"); + ladmin_log("Suppression annul馥 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黎e au serveur de logins pour d騁ruire 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鑼res 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鑽es maximum svp.\n", email); + ladmin_log("Email trop longue [%s]. Entrez une e-mail de 39 caract鑽es 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黎e 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() { + if (defaultlanguage == 'F') { + ladmin_log("Envoi d'un requ黎e 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鑼res 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 黎re 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黎e 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黎e au serveur de logins pour connatre 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馮ative a 騁 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黎e 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軋is\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軋is\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軋is.\n"); + ladmin_log("Changement de la langue d'affichage en Fran軋is." 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騁r馥 (langues possibles: 'Fran軋is' ou 'English').\n"); + ladmin_log("Langue non param騁r馥 (Fran軋is ou English n馗essaire)." RETCODE); + } else { + printf("Undefined language (possible languages: Fran軋is or English).\n"); + ladmin_log("Undefined language (must be Fran軋is 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黎e 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駑o svp.\n"); + printf("<exemple> memo nomtest nouveau memo\n"); + ladmin_log("Nombre incorrect de param鑼res pour changer le m駑o 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駑o trop long (%d caract鑽es).\n", strlen(memo)); + printf("Entrez un m駑o de 254 caract鑽es maximum svp.\n"); + ladmin_log("M駑o trop long (%d caract鑽es). Entrez un m駑o de 254 caract鑽es 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黎e au serveur de logins pour changer un m駑o." 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馮atif 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黎e au serveur de logins pour connatre 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鑼res 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黎e 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() { + 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馥." RETCODE); + printf("Demande de recharger le fichier de configuration des GM envoy馥.\n"); + printf("V駻ifiez les comptes GM actuels (apr鑚 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鑼res 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黎e 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鑽es.\n"); + ladmin_log("Message d'erreur trop court. Entrez un message de 1-19 caract鑽es." 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鑽es.\n"); + ladmin_log("Message d'erreur trop long. Entrez un message de 1-19 caract鑽es." 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黎e 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鑼res 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鑼res 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鑼res 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麥e temps.\n"); + ladmin_log("Nombre incorrect de param鑼res 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馥: %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駑ent modifi:\n"); + printf(" a ou y: ann馥\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麥e 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馥s correct (de -127 127), svp.\n"); + ladmin_log("Ajustement de l'ann馥 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黎e 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馭aut [hh:mm:ss]: 23:59:59.\n"); + ladmin_log("Nombre incorrect de param鑼res 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黎e 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 騁 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黎e 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() { + if (defaultlanguage == 'F') + ladmin_log("Envoi d'un requ黎e 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() { + 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馥'.\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鑼re)" RETCODE, command, parameters); + } else { + ladmin_log("Command: '%s' (without parameters)" RETCODE, command, parameters); + } + } else { + if (defaultlanguage == 'F') { + ladmin_log("Commande: '%s', param鑼res: '%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; + + 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 = 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鑪e d'administration non activ, ou\n"); + printf(" - IP non autoris馥.\n"); + ladmin_log("Erreur de login: mot de passe incorrect, syst鑪e d'administration non activ, ou IP non autoris馥." 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 騁ablie.\n"); + ladmin_log("Connexion 騁ablie." 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, RFIFOP(fd,4), RFIFOW(fd,2) - 4); + strcat(md5str, loginserveradminpassword); + } else if (passenc == 2) { + strncpy(md5str, loginserveradminpassword, sizeof(loginserveradminpassword)); + strcat(md5str, RFIFOP(fd,4)); + } + MD5_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馗eption de la clef MD5.\n"); + ladmin_log("R馗eption 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馗eption 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駸.\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馗eption 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 ", RFIFOL(fd,i)); + if (RFIFOB(fd,i+4) == 0) + printf(" "); + else + printf("%2d ", (int)RFIFOB(fd,i+4)); + printf("%-24s", userid); + if (defaultlanguage == 'F') { + if (RFIFOB(fd,i+29) == 0) + printf("%-5s ", "Femme"); + else if (RFIFOB(fd,i+29) == 1) + printf("%-5s ", "Male"); + else + printf("%-5s ", "Servr"); + } else { + if (RFIFOB(fd,i+29) == 0) + printf("%-5s ", "Femal"); + else if (RFIFOB(fd,i+29) == 1) + printf("%-5s ", "Male"); + else + printf("%-5s ", "Servr"); + } + printf("%6d ", RFIFOL(fd,i+30)); + switch(RFIFOL(fd,i+34)) { + case 0: + if (defaultlanguage == 'F') + printf("%-27s\n", "Compte Ok"); + else + printf("%-27s\n", "Account OK"); + break; + case 1: + printf("%-27s\n", "Unregistered ID"); + break; + case 2: + printf("%-27s\n", "Incorrect Password"); + break; + case 3: + printf("%-27s\n", "This ID is expired"); + break; + case 4: + printf("%-27s\n", "Rejected from Server"); + break; + case 5: + printf("%-27s\n", "Blocked by the GM Team"); // You have been blocked by the GM Team + break; + case 6: + printf("%-27s\n", "Your EXE file is too old"); // Your Game's EXE file is not the latest version + break; + case 7: + printf("%-27s\n", "Banishement or"); + printf(" Prohibited to login until...\n"); // You are Prohibited to log in until %s + break; + case 8: + printf("%-27s\n", "Server is over populated"); + break; + case 9: + printf("%-27s\n", "No MSG"); + break; + default: // 100 + printf("%-27s\n", "This ID is totally erased"); // This ID has been totally erased + break; + } + list_count++; + } + } + // asking of the following acounts + if (defaultlanguage == 'F') + ladmin_log("Envoi d'un requ黎e au serveur de logins pour obtenir la liste des comptes de %d %d (compl駑ent)." 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; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec la cr饌tion du compte [%s]. Un compte identique existe d駛.\n", RFIFOP(fd,6)); + ladmin_log("Echec la cr饌tion du compte [%s]. Un compte identique existe d駛." 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鑚 [id: %d].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Compte [%s] cr鳬 avec succ鑚 [id: %d]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s] is successfully created [id: %d].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Account [%s] is successfully created [id: %d]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7933: // Answer of login-server about an account deletion + if (RFIFOREST(fd) < 30) + return 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鑚.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Compte [%s][id: %d] SUPPRIME avec succ鑚." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] is successfully DELETED.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] is successfully DELETED." 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騏ssie.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Modification du mot de passe du compte [%s][id: %d] r騏ssie." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] password successfully changed.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] password successfully changed." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7937: // answer of the change of an account state + if (RFIFOREST(fd) < 34) + return 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鑚 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馗eption 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; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Le compte [%s] n'existe pas ou le mot de passe est incorrect.\n", RFIFOP(fd,6)); + ladmin_log("Le compte [%s] n'existe pas ou le mot de passe est incorrect." 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), RFIFOL(fd,2)); + ladmin_log("Le mot de passe donn correspond bien au compte [%s][id: %d]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("The proposed password is correct for the account [%s][id: %d].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("The proposed password is correct for the account [%s][id: %d]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x793d: // answer of the change of an account sex + if (RFIFOREST(fd) < 30) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la modification du sexe du compte [%s].\n", RFIFOP(fd,6)); + printf("Le compte [%s] n'existe pas ou le sexe est d駛 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駛 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鑚.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Sexe du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] sex successfully changed.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] sex successfully changed." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x793f: // answer of the change of an account GM level + if (RFIFOREST(fd) < 30) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la modification du niveau de GM du compte [%s].\n", RFIFOP(fd,6)); + printf("Le compte [%s] n'existe pas, le niveau de GM est d駛 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駛 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鑚.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Niveau de GM du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] GM level successfully changed.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] GM level successfully changed." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7941: // answer of the change of an account email + if (RFIFOREST(fd) < 30) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec de la modification de l'e-mail du compte [%s].\n", RFIFOP(fd,6)); + printf("Le compte [%s] n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec de la modification de l'e-mail du compte. Le compte [%s] n'existe pas." 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騏ssie.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Modification de l'e-mail du compte [%s][id: %d] r騏ssie." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] e-mail successfully changed.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] e-mail successfully changed." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7943: // answer of the change of an account memo + if (RFIFOREST(fd) < 30) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement du m駑o du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement du m駑o 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駑o du compte [%s][id: %d] chang avec succ鑚.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("M駑o du compte [%s][id: %d] chang avec succ鑚." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Account [%s][id: %d] memo successfully changed.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Account [%s][id: %d] memo successfully changed." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7945: // answer of an account id search + if (RFIFOREST(fd) < 30) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Impossible de trouver l'id du compte [%s]. Le compte n'existe pas." 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), RFIFOL(fd,2)); + ladmin_log("Le compte [%s] a pour id: %d." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("The account [%s] have the id: %d.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("The account [%s] have the id: %d." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7947: // answer of an account name search + if (RFIFOREST(fd) < 30) + return 0; + if (strcmp(RFIFOP(fd,6), "") == 0) { + if (defaultlanguage == 'F') { + printf("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas.\n", RFIFOL(fd,2)); + ladmin_log("Impossible de trouver le nom du compte [%d]. Le compte n'existe pas." RETCODE, RFIFOL(fd,2)); + } else { + printf("Unable to find the account [%d] name. Account doesn't exist.\n", RFIFOL(fd,2)); + ladmin_log("Unable to find the account [%d] name. Account doesn't exist." RETCODE, RFIFOL(fd,2)); + } + } else { + if (defaultlanguage == 'F') { + printf("Le compte [id: %d] a pour nom: %s.\n", RFIFOL(fd,2), RFIFOP(fd,6)); + ladmin_log("Le compte [id: %d] a pour nom: %s." RETCODE, RFIFOL(fd,2), RFIFOP(fd,6)); + } else { + printf("The account [id: %d] have the name: %s.\n", RFIFOL(fd,2), RFIFOP(fd,6)); + ladmin_log("The account [id: %d] have the name: %s." RETCODE, RFIFOL(fd,2), RFIFOP(fd,6)); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,30); + break; + + case 0x7949: // answer of an account validity limit set + if (RFIFOREST(fd) < 34) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la validit du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la validit du compte [%s]. Le compte n'existe pas." 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馥 avec succ鑚 en [illimit饐.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 en [illimit饐." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } else { + printf("Validity Limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Validity Limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x794b: // answer of an account ban set + if (RFIFOREST(fd) < 34) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas." 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馥 avec succ鑚 en [d-bannie].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x794d: // answer of an account ban date/time changing + if (RFIFOREST(fd) < 34) + return 0; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la date finale de banissement du compte [%s]. Le compte n'existe pas." 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馥 avec succ鑚 en [d-bannie].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 en [d-bannie]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n", RFIFOP(fd,6), RFIFOL(fd,2)); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished]." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Date finale de banissement du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } else { + printf("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Final date of banishment of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x794f: // answer of a broadcast + if (RFIFOREST(fd) < 4) + return 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鑚.\n"); + ladmin_log("Message transmis au server de logins avec succ鑚." 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; + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Echec du changement de la validit du compte [%s]. Le compte n'existe pas.\n", RFIFOP(fd,6)); + ladmin_log("Echec du changement de la validit du compte [%s]. Le compte n'existe pas." 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馥.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + printf("Le compte a une validit illimit馥 ou\n"); + printf("la modification est impossible avec les ajustements demand駸.\n"); + ladmin_log("Limite de validit du compte [%s][id: %d] inchang馥. Le compte a une validit illimit馥 ou la modification est impossible avec les ajustements demand駸." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } else { + printf("Validity limit of the account [%s][id: %d] unchanged.\n", RFIFOP(fd,6), RFIFOL(fd,2)); + printf("The account have an unlimited validity limit or\n"); + printf("the changing is impossible with the proposed adjustments.\n"); + ladmin_log("Validity limit of the account [%s][id: %d] unchanged. The account have an unlimited validity limit or the changing is impossible with the proposed adjustments." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2)); + } + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + if (defaultlanguage == 'F') { + printf("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Limite de validit du compte [%s][id: %d] chang馥 avec succ鑚 pour 黎re jusqu'au %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } else { + printf("Validity limit of the account [%s][id: %d] successfully changed to be until %s.\n", RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + ladmin_log("Validity limit of the account [%s][id: %d] successfully changed to be until %s." RETCODE, RFIFOP(fd,6), RFIFOL(fd,2), tmpstr); + } + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,34); + break; + + case 0x7953: // answer of a request about informations of an account (by account name/id) + if (RFIFOREST(fd) < 150 || RFIFOREST(fd) < (150 + RFIFOW(fd,148))) + return 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, RFIFOP(fd,150), RFIFOW(fd,148)); + if (RFIFOL(fd,2) == -1) { + if (defaultlanguage == 'F') { + printf("Impossible de trouver le compte [%s]. Le compte n'existe pas.\n", parameters); + ladmin_log("Impossible de trouver le compte [%s]. Le compte n'existe pas." 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馗eption d'information concernant un compte." RETCODE); + printf("Le compte a les caract駻istiques 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", RFIFOL(fd,2)); + } else { + if (defaultlanguage == 'F') { + printf(" Id: %d (GM niveau %d)\n", RFIFOL(fd,2), (int)RFIFOB(fd,6)); + } else { + printf(" Id: %d (GM level %d)\n", RFIFOL(fd,2), (int)RFIFOB(fd,6)); + } + } + if (defaultlanguage == 'F') { + printf(" Nom: '%s'\n", userid); + if (RFIFOB(fd,31) == 0) + printf(" Sexe: Femme\n"); + else if (RFIFOB(fd,31) == 1) + printf(" Sexe: Male\n"); + else + printf(" Sexe: Serveur\n"); + } else { + printf(" Name: '%s'\n", userid); + if (RFIFOB(fd,31) == 0) + printf(" Sex: Female\n"); + else if (RFIFOB(fd,31) == 1) + printf(" Sex: Male\n"); + else + printf(" Sex: Server\n"); + } + printf(" E-mail: %s\n", email); + switch(RFIFOL(fd,36)) { + case 0: + if (defaultlanguage == 'F') + printf(" Statut: 0 [Compte Ok]\n"); + else + printf(" Statut: 0 [Account OK]\n"); + break; + case 1: + printf(" Statut: 1 [Unregistered ID]\n"); + break; + case 2: + printf(" Statut: 2 [Incorrect Password]\n"); + break; + case 3: + printf(" Statut: 3 [This ID is expired]\n"); + break; + case 4: + printf(" Statut: 4 [Rejected from Server]\n"); + break; + case 5: + printf(" Statut: 5 [You have been blocked by the GM Team]\n"); + break; + case 6: + printf(" Statut: 6 [Your Game's EXE file is not the latest version]\n"); + break; + case 7: + printf(" Statut: 7 [You are Prohibited to log in until %s]\n", error_message); + break; + case 8: + printf(" Statut: 8 [Server is jammed due to over populated]\n"); + break; + case 9: + printf(" Statut: 9 [No MSG]\n"); + break; + default: // 100 + printf(" Statut: %d [This ID is totally erased]\n", RFIFOL(fd,36)); + break; + } + if (defaultlanguage == 'F') { + if (ban_until_time == 0) { + printf(" Banissement: non banni.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&ban_until_time)); + printf(" Banissement: jusqu'au %s.\n", tmpstr); + } + if (RFIFOL(fd,32) > 1) + printf(" Compteur: %d connexions.\n", RFIFOL(fd,32)); + else + printf(" Compteur: %d connexion.\n", RFIFOL(fd,32)); + printf(" Derni鑽e connexion le: %s (ip: %s)\n", lastlogin, last_ip); + if (connect_until_time == 0) { + printf(" Limite de validit: illimit.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&connect_until_time)); + printf(" Limite de validit: jusqu'au %s.\n", tmpstr); + } + } else { + if (ban_until_time == 0) { + printf(" Banishment: not banished.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&ban_until_time)); + printf(" Banishment: until %s.\n", tmpstr); + } + if (RFIFOL(fd,32) > 1) + printf(" Count: %d connections.\n", RFIFOL(fd,32)); + else + printf(" Count: %d connection.\n", RFIFOL(fd,32)); + printf(" Last connection at: %s (ip: %s)\n", lastlogin, last_ip); + if (connect_until_time == 0) { + printf(" Validity limit: unlimited.\n"); + } else { + char tmpstr[128]; + strftime(tmpstr, 24, date_format, localtime(&connect_until_time)); + printf(" Validity limit: until %s.\n", tmpstr); + } + } + printf(" Memo: '%s'\n", memo); + } + } + bytes_to_read = 0; + RFIFOSKIP(fd,150 + RFIFOW(fd,148)); + break; + + default: + printf("Remote administration has been disconnected (unknown packet).\n"); + ladmin_log("'End of connection, unknown packet." 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() { + 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); + +#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軋is, deutsch, espaol +//------------------------------------------------- +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饕ut 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(w1); + remove_control_chars(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馥.\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) { + // 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_termfunc(do_final); + 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黎." RETCODE); + printf("Ladmin est \033[1;32mpr黎\033[0m.\n\n"); + } else { + ladmin_log("Ladmin is ready." RETCODE); + printf("Ladmin is \033[1;32mready\033[0m.\n\n"); + } + + Connect_login_server(); + + atexit(do_final); + + return 0; +} diff --git a/src/ladmin/ladmin.h b/src/ladmin/ladmin.h new file mode 100644 index 0000000..f76bfc2 --- /dev/null +++ b/src/ladmin/ladmin.h @@ -0,0 +1,11 @@ +// $Id: ladmin.h,v 1.1.1.1 2004/09/10 17:26:52 MagicalTux Exp $ +#ifndef _LADMIN_H_ +#define _LADMIN_H_ + +#define LADMIN_CONF_NAME "conf/ladmin_athena.conf" +#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. + // It is 1 at the time of passwordencrypt. + // It is made into 2 at the time of passwordencrypt2. + // When it is made 3, it corresponds to both. + +#endif diff --git a/src/ladmin/md5calc.c b/src/ladmin/md5calc.c new file mode 100644 index 0000000..7b9a9a2 --- /dev/null +++ b/src/ladmin/md5calc.c @@ -0,0 +1,237 @@ +// $Id: md5calc.c,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ +/*********************************************************** + * 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,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 0000000..b4dd614 --- /dev/null +++ b/src/ladmin/md5calc.h @@ -0,0 +1,8 @@ +// $Id: md5calc.h,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ +#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/GNUmakefile b/src/login/GNUmakefile new file mode 100644 index 0000000..df6cb21 --- /dev/null +++ b/src/login/GNUmakefile @@ -0,0 +1,13 @@ +all: login-server
+txt: login-server
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/lock.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/lock.h ../common/malloc.h
+
+login-server: login.o md5calc.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ login.o md5calc.o $(COMMON_OBJ)
+login.o: login.c login.h md5calc.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h
+
+clean:
+ rm -f *.o ../../login-server
diff --git a/src/login/Makefile b/src/login/Makefile new file mode 100644 index 0000000..df6cb21 --- /dev/null +++ b/src/login/Makefile @@ -0,0 +1,13 @@ +all: login-server
+txt: login-server
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/lock.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/lock.h ../common/malloc.h
+
+login-server: login.o md5calc.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ login.o md5calc.o $(COMMON_OBJ)
+login.o: login.c login.h md5calc.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h
+
+clean:
+ rm -f *.o ../../login-server
diff --git a/src/login/login.c b/src/login/login.c new file mode 100644 index 0000000..8d9818d --- /dev/null +++ b/src/login/login.c @@ -0,0 +1,3698 @@ +// $Id: login.c,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ +// new version of the login-server by [Yor] + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> // for stat/lstat/fstat +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "core.h" +#include "socket.h" +#include "timer.h" +#include "login.h" +#include "mmo.h" +#include "version.h" +#include "db.h" +#include "lock.h" + +#ifdef PASSWORDENC +#include "md5calc.h" +#endif + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +int account_id_count = START_ACCOUNT_NUM; +int server_num; +int new_account_flag = 0; +int login_port = 6900; +char lan_char_ip[16]; +int subneti[4]; +int subnetmaski[4]; + +char account_filename[1024] = "save/account.txt"; +char GM_account_filename[1024] = "conf/GM_account.txt"; +char login_log_filename[1024] = "log/login.log"; +char login_log_unknown_packets_filename[1024] = "log/login_unknown_packets.log"; +char date_format[32] = "%Y-%m-%d %H:%M:%S"; +int save_unknown_packets = 0; +long creation_time_GM_account_file; +int gm_account_filename_check_timer = 15; // Timer to check if GM_account file has been changed and reload GM account automaticaly (in seconds; default: 15) + +int display_parse_login = 0; // 0: no, 1: yes +int display_parse_admin = 0; // 0: no, 1: yes +int display_parse_fromchar = 0; // 0: no, 1: yes (without packet 0x2714), 2: all packets + +struct mmo_char_server server[MAX_SERVERS]; +int server_fd[MAX_SERVERS]; +int server_freezeflag[MAX_SERVERS]; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed +int anti_freeze_enable = 0; +int ANTI_FREEZE_INTERVAL = 15; + +int login_fd; + +enum { + ACO_DENY_ALLOW = 0, + ACO_ALLOW_DENY, + ACO_MUTUAL_FAILTURE, + ACO_STRSIZE = 128, +}; + +int access_order = ACO_DENY_ALLOW; +int access_allownum = 0; +int access_denynum = 0; +char *access_allow = NULL; +char *access_deny = NULL; + +int access_ladmin_allownum = 0; +char *access_ladmin_allow = NULL; + +int min_level_to_connect = 0; // minimum level of player/GM (0: player, 1-99: gm) to connect on the server +int add_to_unlimited_account = 0; // Give possibility or not to adjust (ladmin command: timeadd) the time of an unlimited account. +int start_limited_time = -1; // Starting additional sec from now for the limited time at creation of accounts (-1: unlimited time, 0 or more: additional sec from now) +int check_ip_flag = 1; // It's to check IP of a player between login-server and char-server (part of anti-hacking system) + +struct login_session_data { + int md5keylen; + char md5key[20]; +}; + +#define AUTH_FIFO_SIZE 256 +struct { + int account_id, login_id1, login_id2; + int ip, sex, delflag; +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos = 0; + +struct auth_dat { + int account_id, sex; + char userid[24], pass[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 = Your are Prohibited to log in until %s (packet 0x006a) + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + char last_ip[16]; // save of last IP of connection + char memo[255]; // a memo field + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +} *auth_dat; + +int auth_num = 0, auth_max = 0; + +// 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] = ""; +char gm_pass[64] = ""; +int level_new_gm = 60; + +static struct dbt *gm_account_db; + +//------------------------------ +// Writing function of logs file +//------------------------------ +int login_log(char *fmt, ...) { + FILE *logfp; + va_list ap; + struct timeval tv; + char tmpstr[2048]; + + va_start(ap, fmt); + + logfp = fopen(login_log_filename, "a"); + if (logfp) { + if (fmt[0] == '\0') // jump a line if no message + fprintf(logfp, RETCODE); + else { + gettimeofday(&tv, NULL); + strftime(tmpstr, 24, date_format, localtime(&(tv.tv_sec))); + sprintf(tmpstr + strlen(tmpstr), ".%03d: %s", (int)tv.tv_usec / 1000, fmt); + vfprintf(logfp, tmpstr, ap); + } + fclose(logfp); + } + + va_end(ap); + return 0; +} + +//---------------------------------------------------------------------- +// Determine if an account (id) is a GM account +// and returns its level (or 0 if it isn't a GM account or if not found) +//---------------------------------------------------------------------- +int isGM(int account_id) { + struct gm_account *p; + + p = numdb_search(gm_account_db, account_id); + if (p == NULL) + return 0; + return p->level; +} + +//------------------------------------------------------- +// Reading function of GM accounts file (and their level) +//------------------------------------------------------- +int read_gm_account() { + char line[512]; + struct gm_account *p; + FILE *fp; + int c = 0; + int GM_level; + struct stat file_stat; + + free(gm_account_db); + gm_account_db = numdb_init(); + + // get last modify time/date + if (stat(GM_account_filename, &file_stat)) + creation_time_GM_account_file = 0; // error + else + creation_time_GM_account_file = file_stat.st_mtime; + + if ((fp = fopen(GM_account_filename, "r")) == NULL) { + printf("read_gm_account: GM accounts file [%s] not found.\n", GM_account_filename); + printf(" Actually, there is no GM accounts on the server.\n"); + login_log("read_gm_account: GM accounts file [%s] not found." RETCODE, GM_account_filename); + login_log(" Actually, there is no GM accounts on the server." RETCODE); + return 1; + } + // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) + // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) + while(fgets(line, sizeof(line)-1, fp) && c < 4000) { + if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n' || line[0] == '\r') + continue; + p = calloc(sizeof(struct gm_account), 1); + if (p == NULL) { + printf("read_gm_account: memory allocation failure (malloc)!\n"); + exit(0); + } + if (sscanf(line, "%d %d", &p->account_id, &p->level) != 2 && sscanf(line, "%d: %d", &p->account_id, &p->level) != 2) + printf("read_gm_account: file [%s], invalid 'id_acount level' format.\n", GM_account_filename); + else if (p->level <= 0) + printf("read_gm_account: file [%s] %dth account (invalid level [0 or negative]: %d).\n", GM_account_filename, c+1, p->level); + else { + if (p->level > 99) { + printf("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", GM_account_filename, c+1, p->level); + p->level = 99; + } + if ((GM_level = isGM(p->account_id)) > 0) { // if it's not a new account + if (GM_level == p->level) + printf("read_gm_account: GM account %d defined twice (same level: %d).\n", p->account_id, p->level); + else + printf("read_gm_account: GM account %d defined twice (levels: %d and %d).\n", p->account_id, GM_level, p->level); + } + if (GM_level != p->level) { // if new account or new level + numdb_insert(gm_account_db, p->account_id, p); + //printf("GM account:%d, level: %d->%d\n", p->account_id, GM_level, p->level); + if (GM_level == 0) { // if new account + c++; + if (c >= 4000) { + printf("***WARNING: 4000 GM accounts found. Next GM accounts are not readed.\n"); + login_log("***WARNING: 4000 GM accounts found. Next GM accounts are not readed." RETCODE); + } + } + } + } + } + fclose(fp); + + printf("read_gm_account: file '%s' readed (%d GM accounts found).\n", GM_account_filename, c); + login_log("read_gm_account: file '%s' readed (%d GM accounts found)." RETCODE, GM_account_filename, c); + + return 0; +} + +//-------------------------------------------------------------- +// Test of the IP mask +// (ip: IP to be tested, str: mask x.x.x.x/# or x.x.x.x/y.y.y.y) +//-------------------------------------------------------------- +int check_ipmask(unsigned int ip, const unsigned char *str) { + unsigned int mask = 0, i = 0, m, ip2, a0, a1, a2, a3; + unsigned char *p = (unsigned char *)&ip2, *p2 = (unsigned char *)&mask; + + if (sscanf(str, "%d.%d.%d.%d/%n", &a0, &a1, &a2, &a3, &i) != 4 || i == 0) + return 0; + p[0] = a0; p[1] = a1; p[2] = a2; p[3] = a3; + + if (sscanf(str+i, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) == 4) { + p2[0] = a0; p2[1] = a1; p2[2] = a2; p2[3] = a3; + mask = ntohl(mask); + } else if (sscanf(str+i, "%d", &m) == 1 && m >= 0 && m <= 32) { + for(i = 0; i < m && i < 32; i++) + mask = (mask >> 1) | 0x80000000; + } else { + printf("check_ipmask: invalid mask [%s].\n", str); + return 0; + } + +// printf("Tested IP: %08x, network: %08x, network mask: %08x\n", +// (unsigned int)ntohl(ip), (unsigned int)ntohl(ip2), (unsigned int)mask); + return ((ntohl(ip) & mask) == (ntohl(ip2) & mask)); +} + +//--------------------- +// Access control by IP +//--------------------- +int check_ip(unsigned int ip) { + int i; + unsigned char *p = (unsigned char *)&ip; + char buf[32]; + enum { ACF_DEF, ACF_ALLOW, ACF_DENY } flag = ACF_DEF; + + if (access_allownum == 0 && access_denynum == 0) + return 1; // When there is no restriction, all IP are authorised. + +// + 012.345.: front match form, or +// all: all IP are matched, or +// 012.345.678.901/24: network form (mask with # of bits), or +// 012.345.678.901/255.255.255.0: network form (mask with ip mask) +// + Note about the DNS resolution (like www.ne.jp, etc.): +// There is no guarantee to have an answer. +// If we have an answer, there is no guarantee to have a 100% correct value. +// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. +// So, DNS notation isn't authorised for ip checking. + sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); + + for(i = 0; i < access_allownum; i++) { + const char *p = access_allow + i * ACO_STRSIZE; + if (memcmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) { + flag = ACF_ALLOW; + if (access_order == ACO_ALLOW_DENY) + return 1; // With 'allow, deny' (deny if not allow), allow has priority + break; + } + } + + for(i = 0; i < access_denynum; i++) { + const char *p = access_deny + i * ACO_STRSIZE; + if (memcmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) { + flag = ACF_DENY; + return 0; // At this point, if it's 'deny', we refuse connection. + break; + } + } + + return (flag == ACF_ALLOW || access_order == ACO_DENY_ALLOW) ? 1:0; + // With 'mutual-failture', only 'allow' and non 'deny' IP are authorised. + // A non 'allow' (even non 'deny') IP is not authorised. It's like: if allowed and not denied, it's authorised. + // So, it's disapproval if you have no description at the time of 'mutual-failture'. + // With 'deny,allow' (allow if not deny), because here it's not deny, we authorise. +} + +//-------------------------------- +// Access control by IP for ladmin +//-------------------------------- +int check_ladminip(unsigned int ip) { + int i; + unsigned char *p = (unsigned char *)&ip; + char buf[32]; + + if (access_ladmin_allownum == 0) + return 1; // When there is no restriction, all IP are authorised. + +// + 012.345.: front match form, or +// all: all IP are matched, or +// 012.345.678.901/24: network form (mask with # of bits), or +// 012.345.678.901/255.255.255.0: network form (mask with ip mask) +// + Note about the DNS resolution (like www.ne.jp, etc.): +// There is no guarantee to have an answer. +// If we have an answer, there is no guarantee to have a 100% correct value. +// And, the waiting time (to check) can be long (over 1 minute to a timeout). That can block the software. +// So, DNS notation isn't authorised for ip checking. + sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); + + for(i = 0; i < access_ladmin_allownum; i++) { + const char *p = access_ladmin_allow + i * ACO_STRSIZE; + if (memcmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) { + return 1; + } + } + + return 0; +} + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars(unsigned char *str) { + int i; + int change = 0; + + for(i = 0; str[i]; i++) { + if (str[i] < 32) { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +int e_mail_check(unsigned char *email) { + char ch; + unsigned char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +//----------------------------------------------- +// Search an account id +// (return account index or -1 (if not found)) +// If exact account name is not found, +// the function checks without case sensitive +// and returns index if only 1 account is found +// and similar to the searched name. +//----------------------------------------------- +int search_account_index(char* account_name) { + int i, quantity, index; + + quantity = 0; + index = -1; + for(i = 0; i < auth_num; i++) { + // Without case sensitive check (increase the number of similar account names found) + if (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, + p->connect_until_time, p->last_ip, p->memo, p->ban_until_time); + + for(i = 0; i < p->account_reg2_num; i++) + if (p->account_reg2[i].str[0]) + str_p += sprintf(str_p, "%s,%d ", p->account_reg2[i].str, p->account_reg2[i].value); + + return 0; +} + +//--------------------------------- +// Reading of the accounts database +//--------------------------------- +int mmo_auth_init(void) { + FILE *fp; + int account_id, logincount, state, n, i, j, v; + char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048]; + time_t ban_until_time; + time_t connect_until_time; + char str[2048]; + int GM_count = 0; + int server_count = 0; + + auth_dat = calloc(sizeof(struct auth_dat) * 256, 1); + auth_max = 256; + + fp = fopen(account_filename, "r"); + if (fp == NULL) { + // no account file -> no account -> no login, including char-server (ERROR) + printf("\033[1;31mmmo_auth_init: Accounts file [%s] not found.\033[0m\n", account_filename); + return 0; + } + + while(fgets(line, sizeof(line)-1, fp) != NULL) { + if (line[0] == '/' && line[1] == '/') + continue; + line[sizeof(line)-1] = '\0'; + p = line; + + // database version reading (v2) + if (((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, memo, &ban_until_time, &n)) == 13 && line[n] == '\t') || + ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" + "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, + email, error_message, &connect_until_time, last_ip, memo, &n)) == 12 && line[n] == '\t')) { + n = n + 1; + + // Some checks + if (account_id > END_ACCOUNT_NUM) { + printf("\033[1;31mmmo_auth_init: ******Error: an account has an id higher than %d\n", END_ACCOUNT_NUM); + printf(" account id #%d -> account not read (saved in log file).\033[0m\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." 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(userid); + for(j = 0; j < auth_num; j++) { + if (auth_dat[j].account_id == account_id) { + printf("\033[1;31mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + printf(" account id #%d -> new account not read (saved in log file).\033[0m\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an identical id to another." 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) { + printf("\033[1;31mmmo_auth_init: ******Error: account name already exists.\n"); + printf(" account name '%s' -> new account not read.\n", userid); // 2 lines, account name can be long. + printf(" Account saved in log file.\033[0m\n"); + login_log("mmmo_auth_init: ******Error: an account has an identical id to another." 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 = realloc(auth_dat, sizeof(struct auth_dat) * auth_max); + } + + memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy(auth_dat[auth_num].userid, userid, 24); + + pass[23] = '\0'; + remove_control_chars(pass); + strncpy(auth_dat[auth_num].pass, pass, 24); + + lastlogin[23] = '\0'; + remove_control_chars(lastlogin); + strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); + + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + + if (e_mail_check(email) == 0) { + printf("Account %s (%d): invalid e-mail (replaced par a@a.com).\n", auth_dat[auth_num].userid, auth_dat[auth_num].account_id); + strncpy(auth_dat[auth_num].email, "a@a.com", 40); + } else { + remove_control_chars(email); + strncpy(auth_dat[auth_num].email, email, 40); + } + + error_message[19] = '\0'; + remove_control_chars(error_message); + if (error_message[0] == '\0' || state != 7) { // 7, because state is packet 0x006a value + 1 + strncpy(auth_dat[auth_num].error_message, "-", 20); + } else { + strncpy(auth_dat[auth_num].error_message, error_message, 20); + } + + if (i == 13) + auth_dat[auth_num].ban_until_time = ban_until_time; + else + auth_dat[auth_num].ban_until_time = 0; + + auth_dat[auth_num].connect_until_time = connect_until_time; + + last_ip[15] = '\0'; + remove_control_chars(last_ip); + strncpy(auth_dat[auth_num].last_ip, last_ip, 16); + + memo[254] = '\0'; + remove_control_chars(memo); + strncpy(auth_dat[auth_num].memo, memo, 255); + + for(j = 0; j < ACCOUNT_REG2_NUM; j++) { + p += n; + if (sscanf(p, "%[^\t,],%d %n", str, &v, &n) != 2) { + // We must check if a str is void. If it's, we can continue to read other REG2. + // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) + if (p[0] == ',' && sscanf(p, ",%d %n", &v, &n) == 1) { + j--; + continue; + } else + break; + } + str[31] = '\0'; + remove_control_chars(str); + strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); + auth_dat[auth_num].account_reg2[j].value = v; + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM(account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + // Old athena database version reading (v1) + } else if ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t%n", + &account_id, userid, pass, lastlogin, &sex, &logincount, &state, &n)) >= 5) { + if (account_id > END_ACCOUNT_NUM) { + printf("\033[1;31mmmo_auth_init: ******Error: an account has an id higher than %d\n", END_ACCOUNT_NUM); + printf(" account id #%d -> account not read (saved in log file).\033[0m\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an id higher than %d." 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(userid); + for(j = 0; j < auth_num; j++) { + if (auth_dat[j].account_id == account_id) { + printf("\033[1;31mmmo_auth_init: ******Error: an account has an identical id to another.\n"); + printf(" account id #%d -> new account not read (saved in log file).\033[0m\n", account_id); + login_log("mmmo_auth_init: ******Error: an account has an identical id to another." 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) { + printf("\033[1;31mmmo_auth_init: ******Error: account name already exists.\n"); + printf(" account name '%s' -> new account not read.\n", userid); // 2 lines, account name can be long. + printf(" Account saved in log file.\033[0m\n"); + login_log("mmmo_auth_init: ******Error: an account has an identical id to another." 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 = realloc(auth_dat, sizeof(struct auth_dat) * auth_max); + } + + memset(&auth_dat[auth_num], '\0', sizeof(struct auth_dat)); + + auth_dat[auth_num].account_id = account_id; + + strncpy(auth_dat[auth_num].userid, userid, 24); + + pass[23] = '\0'; + remove_control_chars(pass); + strncpy(auth_dat[auth_num].pass, pass, 24); + + lastlogin[23] = '\0'; + remove_control_chars(lastlogin); + strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24); + + auth_dat[auth_num].sex = (sex == 'S' || sex == 's') ? 2 : (sex == 'M' || sex == 'm'); + + if (i >= 6) { + if (logincount >= 0) + auth_dat[auth_num].logincount = logincount; + else + auth_dat[auth_num].logincount = 0; + } else + auth_dat[auth_num].logincount = 0; + + if (i >= 7) { + if (state > 255) + auth_dat[auth_num].state = 100; + else if (state < 0) + auth_dat[auth_num].state = 0; + else + auth_dat[auth_num].state = state; + } else + auth_dat[auth_num].state = 0; + + // Initialization of new data + strncpy(auth_dat[auth_num].email, "a@a.com", 40); + strncpy(auth_dat[auth_num].error_message, "-", 20); + auth_dat[auth_num].ban_until_time = 0; + auth_dat[auth_num].connect_until_time = 0; + strncpy(auth_dat[auth_num].last_ip, "-", 16); + strncpy(auth_dat[auth_num].memo, "-", 255); + + for(j = 0; j < ACCOUNT_REG2_NUM; j++) { + p += n; + if (sscanf(p, "%[^\t,],%d %n", str, &v, &n) != 2) { + // We must check if a str is void. If it's, we can continue to read other REG2. + // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good) + if (p[0] == ',' && sscanf(p, ",%d %n", &v, &n) == 1) { + j--; + continue; + } else + break; + } + str[31] = '\0'; + remove_control_chars(str); + strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32); + auth_dat[auth_num].account_reg2[j].value = v; + } + auth_dat[auth_num].account_reg2_num = j; + + if (isGM(account_id) > 0) + GM_count++; + if (auth_dat[auth_num].sex == 2) + server_count++; + + auth_num++; + if (account_id >= account_id_count) + account_id_count = account_id + 1; + + } else { + i = 0; + if (sscanf(line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 && + i > 0 && account_id > account_id_count) + account_id_count = account_id; + } + } + fclose(fp); + + if (auth_num == 0) { + printf("mmo_auth_init: No account found in %s.\n", account_filename); + sprintf(line, "No account found in %s.", account_filename); + } else { + if (auth_num == 1) { + printf("mmo_auth_init: 1 account read in %s,\n", account_filename); + sprintf(line, "1 account read in %s,", account_filename); + } else { + printf("mmo_auth_init: %d accounts read in %s,\n", auth_num, account_filename); + sprintf(line, "%d accounts read in %s,", auth_num, account_filename); + } + if (GM_count == 0) { + printf(" of which is no GM account, and "); + sprintf(str, "%s of which is no GM account and", line); + } else if (GM_count == 1) { + printf(" of which is 1 GM account, and "); + sprintf(str, "%s of which is 1 GM account and", line); + } else { + printf(" of which is %d GM accounts, and ", GM_count); + sprintf(str, "%s of which is %d GM accounts and", line, GM_count); + } + if (server_count == 0) { + printf("no server account ('S').\n"); + sprintf(line, "%s no server account ('S').", str); + } else if (server_count == 1) { + printf("1 server account ('S').\n"); + sprintf(line, "%s 1 server account ('S').", str); + } else { + printf("%d server accounts ('S').\n", server_count); + sprintf(line, "%s %d server accounts ('S').", str, server_count); + } + } + login_log("%s" RETCODE, line); + + return 0; +} + +//------------------------------------------ +// Writing of the accounts database file +// (accounts are sorted by id before save) +//------------------------------------------ +void mmo_auth_sync(void) { + FILE *fp; + int i, j, k, lock; + int id[auth_num]; + char line[65536]; + + // Sorting before save + for(i = 0; i < auth_num; i++) { + id[i] = i; + for(j = 0; j < i; j++) { + if (auth_dat[i].account_id < auth_dat[id[j]].account_id) { + for(k = i; k > j; k--) + id[k] = id[k-1]; + id[j] = i; // id[i] + break; + } + } + } + + // Data save + fp = lock_fopen(account_filename, &lock); + if (fp == NULL) + return; + fprintf(fp, "// Accounts file: here are saved all information about the accounts.\n"); + fprintf(fp, "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"); + fprintf(fp, "// Some explanations:\n"); + fprintf(fp, "// account name : between 4 to 23 char for a normal account (standard client can't send less than 4 char).\n"); + fprintf(fp, "// account password: between 4 to 23 char\n"); + fprintf(fp, "// sex : M or F for normal accounts, S for server accounts\n"); + fprintf(fp, "// state : 0: account is ok, 1 to 256: error code of packet 0x006a + 1\n"); + fprintf(fp, "// email : between 3 to 39 char (a@a.com is like no email)\n"); + fprintf(fp, "// error message : text for the state 7: 'Your are Prohibited to login until <text>'. Max 19 char\n"); + fprintf(fp, "// valitidy time : 0: unlimited account, <other value>: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); + fprintf(fp, "// memo field : max 254 char\n"); + fprintf(fp, "// ban time : 0: no ban, <other value>: banned until the date: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n"); + for(i = 0; i < auth_num; i++) { + k = id[i]; // use of sorted index + if (auth_dat[k].account_id < 0) + continue; + + mmo_auth_tostr(line, &auth_dat[k]); + fprintf(fp, "%s" 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; + + 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; + + for(i = 0, c = 0; i < MAX_SERVERS; i++) { + int fd; + if ((fd = server_fd[i]) >= 0 && fd != sfd) { + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd, len); + c++; + } + } + return c; +} + +//----------------------------------------------------- +// Send GM accounts to all char-server +//----------------------------------------------------- +void send_GM_accounts() { + int i; + char buf[32000]; + int GM_value; + int len; + + len = 4; + WBUFW(buf,0) = 0x2732; + for(i = 0; i < auth_num; i++) + // send only existing accounts. We can not create a GM account when server is online. + if ((GM_value = isGM(auth_dat[i].account_id)) > 0) { + WBUFL(buf,len) = auth_dat[i].account_id; + WBUFB(buf,len+4) = (unsigned char)GM_value; + len += 5; + } + WBUFW(buf,2) = len; + charif_sendallwos(-1, buf, len); + + return; +} + +//----------------------------------------------------- +// Check if GM file account have been changed +//----------------------------------------------------- +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 = 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 = realloc(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, 24); + auth_dat[i].pass[23] = '\0'; + + memcpy(auth_dat[i].lastlogin, "-", 2); + + auth_dat[i].sex = (sex == 'M'); + + auth_dat[i].logincount = 0; + + auth_dat[i].state = 0; + + if (e_mail_check(email) == 0) + strncpy(auth_dat[i].email, "a@a.com", 40); + else + strncpy(auth_dat[i].email, email, 40); + + strncpy(auth_dat[i].error_message, "-", 20); + + auth_dat[i].ban_until_time = 0; + + if (start_limited_time < 0) + auth_dat[i].connect_until_time = 0; // unlimited + else { // limited time + timestamp = time(NULL) + start_limited_time; + // double conversion to be sure that it is possible + tmtime = localtime(×tamp); + timestamp_temp = mktime(tmtime); + if (timestamp_temp != -1 && (timestamp_temp + 3600) >= timestamp) // check possible value and overflow (and avoid summer/winter hour) + auth_dat[i].connect_until_time = timestamp_temp; + else + auth_dat[i].connect_until_time = 0; // unlimited + } + + strncpy(auth_dat[i].last_ip, "-", 16); + + strncpy(auth_dat[i].memo, "-", 255); + + auth_dat[i].account_reg2_num = 0; + + auth_num++; + + return (account_id_count - 1); +} + +//--------------------------------------- +// Check/authentification of a connection +//--------------------------------------- +int mmo_auth(struct mmo_account* account, int fd) { + int i; + struct timeval tv; + char tmpstr[256]; + int len, newaccount = 0; + 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]); + + len = strlen(account->userid) - 2; + // Account creation with _M/_F + if (account->passwdenc == 0 && account->userid[len] == '_' && + (account->userid[len+1] == 'F' || account->userid[len+1] == 'M') && new_account_flag == 1 && + account_id_count <= END_ACCOUNT_NUM && len >= 4 && strlen(account->passwd) >= 4) { + if (new_account_flag == 1) + newaccount = 1; + account->userid[len] = '\0'; + } + + // Strict account search + for(i = 0; i < auth_num; i++) { + if (strcmp(account->userid, auth_dat[i].userid) == 0) + break; + } + // if there is no creation request and strict account search fails, we do a no sensitive case research for index + if (newaccount == 0 && i == auth_num) { + i = search_account_index(account->userid); + if (i == -1) + i = auth_num; + else + memcpy(account->userid, auth_dat[i].userid, 24); // for the possible tests/checks afterwards (copy correcte sensitive case). + } + + if (i != auth_num) { + int encpasswdok = 0; + struct login_session_data *ld; + if (newaccount) { + login_log("Attempt of creation of an already existant account (account: %s_%c, pass: %s, received pass: %s, ip: %s)" RETCODE, + account->userid, account->userid[len+1], auth_dat[i].pass, account->passwd, ip); + return 9; // 9 = Account already exists + } + ld = session[fd]->session_data; +#ifdef PASSWORDENC + if (account->passwdenc > 0) { + int j = account->passwdenc; + if (!ld) { + login_log("Md5 key not created (account: %s, ip: %s)" RETCODE, account->userid, ip); + return 1; // 1 = Incorrect Password + } + if (j > 2) + j = 1; + do { + if (j == 1) { + strncpy(md5str, ld->md5key, sizeof(ld->md5key)); // 20 + strcat(md5str, auth_dat[i].pass); // 24 + } else if (j == 2) { + strncpy(md5str, auth_dat[i].pass, sizeof(auth_dat[i].pass)); // 24 + strcat(md5str, ld->md5key); // 20 + } else + md5str[0] = '\0'; + md5str[sizeof(md5str)-1] = '\0'; // 64 + MD5_String2binary(md5str, md5bin); + encpasswdok = (memcmp(account->passwd, md5bin, 16) == 0); + } while (j < 2 && !encpasswdok && (j++) != account->passwdenc); +// printf("key[%s] md5 [%s] ", md5key, md5); +// printf("client [%s] accountpass [%s]\n", account->passwd, auth_dat[i].pass); + } +#endif + if ((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; + 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 MSG (actually, all states after 9 except 99 are No MSG, use only this) + case 100: // 99 = This ID has been totally erased + return auth_dat[i].state - 1; + break; + default: + return 99; // 99 = ID has been totally erased + break; + } + } + + if (auth_dat[i].ban_until_time != 0) { // if account is banned + strftime(tmpstr, 20, date_format, 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 == 0) { + 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 + } + } + + gettimeofday(&tv, NULL); + strftime(tmpstr, 24, date_format, localtime(&(tv.tv_sec))); + sprintf(tmpstr + strlen(tmpstr), ".%03d", (int)tv.tv_usec / 1000); + + 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; + 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 +} + +//------------------------------- +// Char-server anti-freeze system +//------------------------------- +int char_anti_freeze_system(int tid, unsigned int tick, int id, int data) { + int i; + + //printf("Entering in char_anti_freeze_system function to check freeze of servers.\n"); + for(i = 0; i < MAX_SERVERS; i++) { + if (server_fd[i] >= 0) {// if char-server is online + //printf("char_anti_freeze_system: server #%d '%s', flag: %d.\n", i, server[i].name, server_freezeflag[i]); + if (server_freezeflag[i]-- < 1) { // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed + printf("Char-server anti-freeze system: char-server #%d '%s' is freezed -> disconnection.\n", i, server[i].name); + login_log("Char-server anti-freeze system: char-server #%d '%s' is freezed -> disconnection." RETCODE, + i, server[i].name); + session[server_fd[i]]->eof = 1; + } + } + } + + return 0; +} + +//-------------------------------- +// Packet parsing for char-servers +//-------------------------------- +int parse_fromchar(int fd) { + int i, j, id; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char ip[16]; + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + for(id = 0; id < MAX_SERVERS; id++) + if (server_fd[id] == fd) + break; + if (id == MAX_SERVERS || session[fd]->eof) { + if (id < MAX_SERVERS) { + printf("Char-server '%s' has disconnected.\n", server[id].name); + login_log("Char-server '%s' has disconnected (ip: %s)." RETCODE, + server[id].name, ip); + server_fd[id] = -1; + memset(&server[id], 0, sizeof(struct mmo_char_server)); + } + close(fd); + delete_session(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) + printf("parse_fromchar: connection #%d, packet: 0x%x (with being read: %d bytes).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch (RFIFOW(fd,0)) { + // request from map-server via char-server to reload GM accounts (by Yor). + case 0x2709: + login_log("Char-server '%s': Request to re-load GM configuration file (ip: %s)." 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) { + int p, k; + 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) { + WFIFOW(fd,0) = 0x2729; // Sending of the account_reg2 + WFIFOL(fd,4) = acc; + for(p = 8, j = 0; j < auth_dat[k].account_reg2_num; p += 36, j++) { + memcpy(WFIFOP(fd,p), auth_dat[k].account_reg2[j].str, 32); + WFIFOL(fd,p+32) = auth_dat[k].account_reg2[j].value; + } + WFIFOW(fd,2) = p; + WFIFOSET(fd,p); +// printf("parse_fromchar: Sending of account_reg2: login->char (auth fifo)\n"); + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = acc; + WFIFOB(fd,6) = 0; + memcpy(WFIFOP(fd, 7), auth_dat[k].email, 40); + WFIFOL(fd,47) = (unsigned long)auth_dat[k].connect_until_time; + WFIFOSET(fd,51); + break; + } + } + break; + } + } + // authentification not found + if (i == AUTH_FIFO_SIZE) { + login_log("Char-server '%s': authentification of the account %d REFUSED (ip: %s)." RETCODE, + server[id].name, acc, ip); + WFIFOW(fd,0) = 0x2713; + WFIFOL(fd,2) = acc; + WFIFOB(fd,6) = 1; + // It is unnecessary to send email + // It is unnecessary to send validity date of the account + WFIFOSET(fd,51); + } + } + RFIFOSKIP(fd,19); + break; + + case 0x2714: + if (RFIFOREST(fd) < 6) + return 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); + if(anti_freeze_enable) + server_freezeflag[id] = 5; // Char anti-freeze system. Counter. 5 ok, 4...0 freezed + RFIFOSKIP(fd,6); + break; + + // we receive a e-mail creation of an account with a default e-mail (no answer) + case 0x2715: { + int acc; + char email[40]; + if (RFIFOREST(fd) < 46) + return 0; + acc = RFIFOL(fd,2); // speed up + memcpy(email, RFIFOP(fd,6), 40); + email[39] = '\0'; + remove_control_chars(email); + //printf("parse_fromchar: an e-mail creation of an account with a default e-mail: server '%s', account: %d, e-mail: '%s'.\n", server[id].name, acc, RFIFOP(fd,6)); + if (e_mail_check(email) == 0) + login_log("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)" 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; + { + int acc; + unsigned char buf[10]; + FILE *fp; + acc = RFIFOL(fd,4); + //printf("parse_fromchar: Request to become a GM acount from %d account.\n", acc); + WBUFW(buf,0) = 0x2721; + WBUFL(buf,2) = acc; + WBUFL(buf,6) = 0; + if (strcmp(RFIFOP(fd,8), gm_pass) == 0) { + // only non-GM can become GM + if (isGM(acc) == 0) { + // if we autorise creation + if (level_new_gm > 0) { + // if we can open the file to add the new GM + if ((fp = fopen(GM_account_filename, "a")) != NULL) { + char tmpstr[24]; + struct timeval tv; + gettimeofday(&tv, NULL); + strftime(tmpstr, 23, date_format, localtime(&(tv.tv_sec))); + 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(); + printf("GM Change of the account %d: level 0 -> %d.\n", acc, level_new_gm); + login_log("Char-server '%s': GM Change of the account %d: level 0 -> %d (ip: %s)." RETCODE, + server[id].name, acc, level_new_gm, ip); + } else { + printf("Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file)\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d, correct password, unable to add a GM account in GM accounts file, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } else { + printf("Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0))\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d, correct password, but GM creation is disable (level_new_gm = 0), ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } else { + printf("Error of GM change (suggested account: %d (already GM), correct password).\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d (already GM), correct password, ip: %s)." RETCODE, + server[id].name, acc, ip); + } + } else { + printf("Error of GM change (suggested account: %d, invalid password).\n", acc); + login_log("Char-server '%s': Error of GM change (suggested account: %d, invalid password, ip: %s)." 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; + { + int acc; + char actual_email[40], new_email[40]; + acc = RFIFOL(fd,2); + memcpy(actual_email, RFIFOP(fd,6), 40); + actual_email[39] = '\0'; + remove_control_chars(actual_email); + memcpy(new_email, RFIFOP(fd,46), 40); + new_email[39] = '\0'; + remove_control_chars(new_email); + if (e_mail_check(actual_email) == 0) + login_log("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)" 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; + { + int acc; + acc = RFIFOL(fd,2); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + time_t timestamp; + struct tm *tmtime; + if (auth_dat[i].ban_until_time == 0 || auth_dat[i].ban_until_time < time(NULL)) + timestamp = time(NULL); + else + timestamp = auth_dat[i].ban_until_time; + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16); + timestamp = 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(×tamp)); + 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) = 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 acc, sex; + acc = RFIFOL(fd,2); + for(i = 0; i < auth_num; i++) { +// printf("%d,", auth_dat[i].account_id); + if (auth_dat[i].account_id == acc) { + if (auth_dat[i].sex == 2) + login_log("Char-server '%s': Error of sex change - Server account (suggested account: %d, actual sex %d (Server), ip: %s)." 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 char-servers. + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + int acc, p; + acc = RFIFOL(fd,4); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + unsigned char buf[RFIFOW(fd,2)+1]; + login_log("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s)." RETCODE, + server[id].name, acc, ip); + for(p = 8, j = 0; p < RFIFOW(fd,2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { + memcpy(auth_dat[i].account_reg2[j].str, RFIFOP(fd,p), 32); + auth_dat[i].account_reg2[j].str[31] = '\0'; + remove_control_chars(auth_dat[i].account_reg2[j].str); + auth_dat[i].account_reg2[j].value = RFIFOL(fd,p+32); + } + auth_dat[i].account_reg2_num = j; + // Sending information towards the other char-servers. + memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WBUFW(buf,0) = 0x2729; + charif_sendallwos(fd, buf, WBUFW(buf,2)); + // Save + mmo_auth_sync(); +// printf("parse_fromchar: receiving (from the char-server) of account_reg2 (account id: %d).\n", acc); + break; + } + } + if (i == auth_num) { +// printf("parse_fromchar: receiving (from the char-server) of account_reg2 (unknwon account id: %d).\n", acc); + login_log("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s)." 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; + { + int acc; + acc = RFIFOL(fd,2); + for(i = 0; i < auth_num; i++) { + if (auth_dat[i].account_id == acc) { + if (auth_dat[i].ban_until_time != 0) { + auth_dat[i].ban_until_time = 0; + login_log("Char-server '%s': UnBan request (account: %d, ip: %s)." 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; + + default: + { + FILE *logfp; + char tmpstr[24]; + struct timeval tv; + logfp = fopen(login_log_unknown_packets_filename, "a"); + if (logfp) { + gettimeofday(&tv, NULL); + strftime(tmpstr, 23, date_format, localtime(&(tv.tv_sec))); + fprintf(logfp, "%s.%03d: receiving of an unknown packet -> disconnection" RETCODE, tmpstr, (int)tv.tv_usec / 1000); + 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); + } + } + printf("parse_fromchar: Unknown packet 0x%x (from a char-server)! -> disconnection.\n", RFIFOW(fd,0)); + session[fd]->eof = 1; + printf("Char-server has been disconnected (unknown packet).\n"); + return 0; + } + } + return 0; +} + +//--------------------------------------- +// Packet parsing for administation login +//--------------------------------------- +int parse_admin(int fd) { + int i, j; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char* account_name; + char ip[16]; + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + if (session[fd]->eof) { + close(fd); + delete_session(fd); + printf("Remote administration has disconnected (session #%d).\n", fd); + return 0; + } + + while(RFIFOREST(fd) >= 2) { + if (display_parse_admin == 1) + printf("parse_admin: connection #%d, packet: 0x%x (with being read: %d).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + + switch(RFIFOW(fd,0)) { + case 0x7530: // Request of the server version + login_log("'ladmin': Sending of the server version (ip: %s)" RETCODE, ip); + WFIFOW(fd,0) = 0x7531; + WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; + WFIFOB(fd,3) = ATHENA_MINOR_VERSION; + WFIFOB(fd,4) = ATHENA_REVISION; + WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; + WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; + WFIFOB(fd,7) = ATHENA_SERVER_LOGIN; + WFIFOW(fd,8) = ATHENA_MOD_VERSION; + WFIFOSET(fd,10); + RFIFOSKIP(fd,2); + break; + + case 0x7532: // Request of end of connection + login_log("'ladmin': End of connection (ip: %s)" 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]; + 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++) { + int k; + id[i] = i; + for(j = 0; j < i; j++) { + if (auth_dat[id[i]].account_id < auth_dat[id[j]].account_id) { + for(k = i; k > j; k--) { + id[k] = id[k-1]; + } + id[j] = i; // id[i] + break; + } + } + } + // Sending accounts information + len = 4; + for(i = 0; i < auth_num && len < 30000; i++) { + int account_id = auth_dat[id[i]].account_id; // use sorted index + if (account_id >= st && account_id <= ed) { + j = id[i]; + WFIFOL(fd,len) = account_id; + WFIFOB(fd,len+4) = (unsigned char)isGM(account_id); + memcpy(WFIFOP(fd,len+5), auth_dat[j].userid, 24); + WFIFOB(fd,len+29) = auth_dat[j].sex; + WFIFOL(fd,len+30) = auth_dat[j].logincount; + if (auth_dat[j].state == 0 && auth_dat[j].ban_until_time != 0) // if no state and banished + WFIFOL(fd,len+34) = 7; // 6 = Your are Prohibited to log in until %s + else + WFIFOL(fd,len+34) = auth_dat[j].state; + len += 38; + } + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + } + break; + + case 0x7930: // Request for an account creation + if (RFIFOREST(fd) < 91) + return 0; + { + struct mmo_account ma; + ma.userid = RFIFOP(fd, 2); + ma.passwd = RFIFOP(fd, 26); + memcpy(ma.lastlogin, "-", 2); + ma.sex = RFIFOB(fd,50); + WFIFOW(fd,0) = 0x7931; + WFIFOL(fd,2) = -1; + memcpy(WFIFOP(fd,6), RFIFOP(fd,2), 24); + if (strlen(ma.userid) > 23 || strlen(ma.passwd) > 23) { + login_log("'ladmin': Attempt to create an invalid account (account or pass is too long, ip: %s)" 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(ma.userid); + remove_control_chars(ma.passwd); + for(i = 0; i < auth_num; i++) { + if (strncmp(auth_dat[i].userid, ma.userid, 24) == 0) { + login_log("'ladmin': Attempt to create an already existing account (account: %s, 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(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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + // Char-server is notified of deletion (for characters deletion). + unsigned char buf[65535]; + WBUFW(buf,0) = 0x2730; + WBUFL(buf,2) = auth_dat[i].account_id; + charif_sendallwos(-1, buf, 6); + // send answer + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + // save deleted account in log file + login_log("'ladmin': Account deletion (account: %s, id: %d, ip: %s) - saved in next line:" RETCODE, + auth_dat[i].userid, auth_dat[i].account_id, ip); + mmo_auth_tostr(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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + memcpy(auth_dat[i].pass, RFIFOP(fd,26), 24); + auth_dat[i].pass[23] = '\0'; + remove_control_chars(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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + statut = RFIFOL(fd,26); + memcpy(error_message, RFIFOP(fd,30), 20); + error_message[19] = '\0'; + remove_control_chars(error_message); + if (statut != 7 || error_message[0] == '\0') { // 7: // 6 = Your are Prohibited to log in until %s + strcpy(error_message, "-"); + } + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + if (auth_dat[i].state == statut && strcmp(auth_dat[i].error_message, error_message) == 0) + login_log("'ladmin': Modification of a state, but the state of the account is already the good state (account: %s, received state: %d, ip: %s)" 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + if (strcmp(auth_dat[i].pass, RFIFOP(fd,26)) == 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 { + char pass[24]; + memcpy(pass, RFIFOP(fd,26), 24); + pass[23] = '\0'; + remove_control_chars(pass); + login_log("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)" 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + memcpy(WFIFOP(fd,6), account_name, 24); + { + char sex; + sex = RFIFOB(fd,26); + if (sex != 'F' && sex != 'M') { + if (sex > 31) + login_log("'ladmin': Attempt to give an invalid sex (account: %s, received sex: %c, ip: %s)" 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + memcpy(WFIFOP(fd,6), account_name, 24); + { + char new_gm_level; + new_gm_level = RFIFOB(fd,26); + if (new_gm_level < 0 || new_gm_level > 99) { + login_log("'ladmin': Attempt to give an invalid GM level (account: %s, received GM level: %d, ip: %s)" 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]; + struct timeval tv; + if ((fp2 = lock_fopen(GM_account_filename, &lock)) != NULL) { + if ((fp = fopen(GM_account_filename, "r")) != NULL) { + gettimeofday(&tv, NULL); + strftime(tmpstr, 23, date_format, localtime(&(tv.tv_sec))); + modify_flag = 0; + // read/write GM file + while(fgets(line, sizeof(line)-1, fp)) { + while(line[0] != '\0' && (line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r')) + line[strlen(line)-1] = '\0'; + if ((line[0] == '/' && line[1] == '/') || line[0] == '\0') + fprintf(fp2, "%s" 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + memcpy(WFIFOP(fd,6), account_name, 24); + { + char email[40]; + memcpy(email, RFIFOP(fd,26), 40); + if (e_mail_check(email) == 0) { + login_log("'ladmin': Attempt to give an invalid e-mail (account: %s, ip: %s)" RETCODE, + account_name, ip); + } else { + remove_control_chars(email); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + memcpy(auth_dat[i].email, email, 40); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Modification of an email (account: %s, new e-mail: %s, ip: %s)" 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 (RFIFOREST(fd) < 28 || RFIFOREST(fd) < (28 + RFIFOW(fd,26))) + return 0; + WFIFOW(fd,0) = 0x7943; + WFIFOL(fd,2) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + int size_of_memo = sizeof(auth_dat[i].memo); + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + memset(auth_dat[i].memo, '\0', size_of_memo); + if (RFIFOW(fd,26) == 0) { + strncpy(auth_dat[i].memo, "-", size_of_memo); + } else if (RFIFOW(fd,26) > size_of_memo - 1) { + memcpy(auth_dat[i].memo, RFIFOP(fd,28), size_of_memo - 1); + } else { + memcpy(auth_dat[i].memo, RFIFOP(fd,28), RFIFOW(fd,26)); + } + auth_dat[i].memo[size_of_memo - 1] = '\0'; + remove_control_chars(auth_dat[i].memo); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Modification of a memo field (account: %s, new memo: %s, ip: %s)" 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Request (by the name) of an account id (account: %s, id: %d, ip: %s)" 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(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(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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + timestamp = (time_t)RFIFOL(fd,26); + strftime(tmpstr, 24, date_format, localtime(×tamp)); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + login_log("'ladmin': Change of a validity limit (account: %s, new validity: %d (%s), ip: %s)" 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) = 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + timestamp = (time_t)RFIFOL(fd,26); + if (timestamp <= time(NULL)) + timestamp = 0; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + i = search_account_index(account_name); + if (i != -1) { + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + WFIFOL(fd,2) = auth_dat[i].account_id; + login_log("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %d (%s), ip: %s)" 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) = 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) = 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + WFIFOL(fd,2) = auth_dat[i].account_id; + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + if (auth_dat[i].ban_until_time == 0 || auth_dat[i].ban_until_time < time(NULL)) + timestamp = time(NULL); + else + timestamp = auth_dat[i].ban_until_time; + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36); + timestamp = mktime(tmtime); + if (timestamp != -1) { + if (timestamp <= time(NULL)) + timestamp = 0; + strftime(tmpstr, 24, date_format, localtime(×tamp)); + login_log("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" 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) = 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) = -1; + 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 { + char buf[32000]; + char message[32000]; + WFIFOW(fd,2) = 0; + memset(message, '\0', sizeof(message)); + memcpy(message, RFIFOP(fd,8), RFIFOL(fd,4)); + message[sizeof(message)-1] = '\0'; + remove_control_chars(message); + if (RFIFOW(fd,2) == 0) + login_log("'ladmin': Receiving a message for broadcast (message (in yellow): %s, ip: %s)" 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + WFIFOL(fd,2) = auth_dat[i].account_id; + memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); + timestamp = auth_dat[i].connect_until_time; + if (add_to_unlimited_account == 0 && timestamp == 0) { + login_log("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)" RETCODE, + auth_dat[i].userid, ip); + WFIFOL(fd,30) = 0; + } else { + if (timestamp == 0 || timestamp < time(NULL)) + timestamp = time(NULL); + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36); + timestamp = mktime(tmtime); + if (timestamp != -1) { + strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].connect_until_time)); + strftime(tmpstr2, 24, date_format, localtime(×tamp)); + login_log("'ladmin': Adjustment of a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" 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) = -1; + account_name = RFIFOP(fd,2); + account_name[23] = '\0'; + remove_control_chars(account_name); + i = search_account_index(account_name); + if (i != -1) { + WFIFOL(fd,2) = auth_dat[i].account_id; + WFIFOB(fd,6) = (unsigned char)isGM(auth_dat[i].account_id); + memcpy(WFIFOP(fd,7), auth_dat[i].userid, 24); + WFIFOB(fd,31) = auth_dat[i].sex; + WFIFOL(fd,32) = auth_dat[i].logincount; + WFIFOL(fd,36) = auth_dat[i].state; + memcpy(WFIFOP(fd,40), auth_dat[i].error_message, 20); + memcpy(WFIFOP(fd,60), auth_dat[i].lastlogin, 24); + memcpy(WFIFOP(fd,84), auth_dat[i].last_ip, 16); + memcpy(WFIFOP(fd,100), auth_dat[i].email, 40); + WFIFOL(fd,140) = (unsigned long)auth_dat[i].connect_until_time; + WFIFOL(fd,144) = (unsigned long)auth_dat[i].ban_until_time; + WFIFOW(fd,148) = strlen(auth_dat[i].memo); + if (auth_dat[i].memo[0]) { + memcpy(WFIFOP(fd,150), auth_dat[i].memo, strlen(auth_dat[i].memo)); + } + login_log("'ladmin': Sending information of an account (request by the name; account: %s, id: %d, ip: %s)" 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(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]; + struct timeval tv; + logfp = fopen(login_log_unknown_packets_filename, "a"); + if (logfp) { + gettimeofday(&tv, NULL); + strftime(tmpstr, 23, date_format, localtime(&(tv.tv_sec))); + fprintf(logfp, "%s.%03d: receiving of an unknown packet -> disconnection" RETCODE, tmpstr, (int)tv.tv_usec / 1000); + 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; + printf("Remote administration has been disconnected (unknown packet).\n"); + return 0; + } + //WFIFOW(fd,0) = 0x791f; + //WFIFOSET(fd,2); + } + 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; + } + } + printf("LAN test (result): %s source\033[0m.\n", (lancheck) ? "\033[1;36mLAN" : "\033[1;32mWAN"); + return lancheck; +} + +//---------------------------------------------------------------------------------------- +// Default packet parsing (normal players or administation/char-server connexion requests) +//---------------------------------------------------------------------------------------- +int parse_login(int fd) { + struct mmo_account account; + int result, i, j; + unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; + char ip[16]; + + sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + if (session[fd]->eof) { + close(fd); + delete_session(fd); + return 0; + } + + while(RFIFOREST(fd) >= 2) { + if (display_parse_login == 1) { + if (RFIFOW(fd,0) == 0x64 || RFIFOW(fd,0) == 0x01dd) { + if (RFIFOREST(fd) >= ((RFIFOW(fd,0) == 0x64) ? 55 : 47)) + printf("parse_login: connection #%d, packet: 0x%x (with being read: %d), account: %s.\n", fd, RFIFOW(fd,0), RFIFOREST(fd), RFIFOP(fd,6)); + } else if (RFIFOW(fd,0) == 0x2710) { + if (RFIFOREST(fd) >= 86) + printf("parse_login: connection #%d, packet: 0x%x (with being read: %d), server: %s.\n", fd, RFIFOW(fd,0), RFIFOREST(fd), RFIFOP(fd,60)); + } else + printf("parse_login: connection #%d, packet: 0x%x (with being read: %d).\n", fd, RFIFOW(fd,0), RFIFOREST(fd)); + } + + switch(RFIFOW(fd,0)) { + case 0x200: // New alive packet: structure: 0x200 <account.userid>.24B. used to verify if client is always alive. + if (RFIFOREST(fd) < 26) + return 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 (RFIFOREST(fd) < ((RFIFOW(fd,0) == 0x64) ? 55 : 47)) + return 0; + + account.userid = RFIFOP(fd,6); + account.userid[23] = '\0'; + remove_control_chars(account.userid); + account.passwd = RFIFOP(fd,30); + if (RFIFOW(fd,0) == 0x64) { + account.passwd[23] = '\0'; + remove_control_chars(account.passwd); + } +#ifdef PASSWORDENC + account.passwdenc = (RFIFOW(fd,0) == 0x64) ? 0 : PASSWORDENC; +#else + account.passwdenc = 0; +#endif + + if (RFIFOW(fd,0) == 0x64) { + login_log("Request for connection (non encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); + } else { + login_log("Request for connection (encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); + } + + if (!check_ip(session[fd]->client_addr.sin_addr.s_addr)) { + login_log("Connection refused: IP isn't authorised (deny/allow, ip: %s)." RETCODE, ip); + WFIFOW(fd,0) = 0x6a; + WFIFOB(fd,2) = 0x03; + WFIFOSET(fd,3); + RFIFOSKIP(fd,(RFIFOW(fd,0) == 0x64) ? 55 : 47); + break; + } + + result = mmo_auth(&account, fd); + if (result == -1) { + int gm_level = isGM(account.account_id); + if (min_level_to_connect > gm_level) { + login_log("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d, ip: %s)." RETCODE, + min_level_to_connect, account.userid, gm_level, ip); + WFIFOW(fd,0) = 0x81; + WFIFOL(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } else { + if (gm_level) + printf("Connection of the GM (level:%d) account '%s' accepted.\n", gm_level, account.userid); + else + printf("Connection of the account '%s' accepted.\n", account.userid); + server_num = 0; + 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; + WFIFOL(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } + } + } else { + memset(WFIFOP(fd,0), '\0', 23); + WFIFOW(fd,0) = 0x6a; + WFIFOB(fd,2) = result; + if (result == 6) { // 6 = Your are Prohibited to log in until %s + i = search_account_index(account.userid); + if (i != -1) { + if (auth_dat[i].ban_until_time != 0) { // if account is banned, we send ban timestamp + char tmpstr[256]; + strftime(tmpstr, 20, date_format, 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) { + printf("login: abnormal request of MD5 key (already opened session).\n"); + session[fd]->eof = 1; + return 0; + } + ld = session[fd]->session_data = calloc(sizeof(*ld), 1); + if (!ld) { + printf("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); + 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; + unsigned char* server_name; + account.userid = RFIFOP(fd,2); + account.userid[23] = '\0'; + remove_control_chars(account.userid); + account.passwd = RFIFOP(fd,26); + account.passwd[23] = '\0'; + remove_control_chars(account.passwd); + account.passwdenc = 0; + server_name = RFIFOP(fd,60); + server_name[19] = '\0'; + remove_control_chars(server_name); + login_log("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (ip: %s)" 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); + printf("Connection of the char-server '%s' accepted.\n", server_name); + memset(&server[account.account_id], 0, sizeof(struct mmo_char_server)); + server[account.account_id].ip = RFIFOL(fd,54); + server[account.account_id].port = RFIFOW(fd,58); + memcpy(server[account.account_id].name, server_name, 20); + server[account.account_id].users = 0; + server[account.account_id].maintenance = RFIFOW(fd,82); + server[account.account_id].new = RFIFOW(fd,84); + server_fd[account.account_id] = fd; + if(anti_freeze_enable) + server_freezeflag[account.account_id] = 5; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed + WFIFOW(fd,0) = 0x2711; + WFIFOB(fd,2) = 0; + WFIFOSET(fd,3); + session[fd]->func_parse = parse_fromchar; + realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + // send GM account to char-server + len = 4; + WFIFOW(fd,0) = 0x2732; + for(i = 0; i < auth_num; i++) + // send only existing accounts. We can not create a GM account when server is online. + if ((GM_value = isGM(auth_dat[i].account_id)) > 0) { + WFIFOL(fd,len) = auth_dat[i].account_id; + WFIFOB(fd,len+4) = (unsigned char)GM_value; + len += 5; + } + WFIFOW(fd,2) = len; + WFIFOSET(fd,len); + } else { + login_log("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)" RETCODE, + server_name, account.userid, account.passwd, ip); + 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); + 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 (RFIFOREST(fd) < 4 || 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 = session[fd]->session_data; + if (RFIFOW(fd,2) == 0) { // non encrypted password + unsigned char* password; + password = RFIFOP(fd,4); + password[23] = '\0'; + remove_control_chars(password); + // If remote administration is enabled and password sent by client matches password read from login server configuration file + if ((admin_state == 1) && (strcmp(password, admin_pass) == 0)) { + login_log("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)" RETCODE, password, ip); + printf("Connection of a remote administration accepted (non encrypted password).\n"); + WFIFOB(fd,2) = 0; + session[fd]->func_parse = parse_admin; + } else if (admin_state != 1) + login_log("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (non encrypted password: %s, ip: %s)" 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) + printf("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n"); + else { + char md5str[64] = "", md5bin[32]; + if (RFIFOW(fd,2) == 1) { + strncpy(md5str, ld->md5key, sizeof(ld->md5key)); // 20 + strcat(md5str, admin_pass); // 24 + } else if (RFIFOW(fd,2) == 2) { + strncpy(md5str, admin_pass, sizeof(admin_pass)); // 24 + strcat(md5str, ld->md5key); // 20 + } + MD5_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); + printf("Connection of a remote administration accepted (encrypted password).\n"); + WFIFOB(fd,2) = 0; + session[fd]->func_parse = parse_admin; + } else if (admin_state != 1) + login_log("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (encrypted password, ip: %s)" 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]; + struct timeval tv; + logfp = fopen(login_log_unknown_packets_filename, "a"); + if (logfp) { + gettimeofday(&tv, NULL); + strftime(tmpstr, 23, date_format, localtime(&(tv.tv_sec))); + fprintf(logfp, "%s.%03d: receiving of an unknown packet -> disconnection" RETCODE, tmpstr, (int)tv.tv_usec / 1000); + 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; + } + } + return 0; +} + + +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, fran軋is, deutsch, espaol +//------------------------------------------------- +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) { + printf("***WARNING: LAN Support configuration file is not found: %s\n", lancfgName); + return 1; + } + + printf("---Start reading Lan Support configuration file\n"); + + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) + continue; + + remove_control_chars(w1); + remove_control_chars(w2); + if (strcmpi(w1, "lan_char_ip") == 0) { // Read Char-Server Lan IP Address + h = gethostbyname(w2); + if (h != NULL) { + sprintf(lan_char_ip, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } else { + strncpy(lan_char_ip, w2, sizeof(lan_char_ip)); + lan_char_ip[sizeof(lan_char_ip)-1] = '\0'; + } + printf("LAN IP of char-server: %s.\n", lan_char_ip); + } else if (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]); + } + printf("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]); + } + printf("Sub-network mask of the char-server: %d.%d.%d.%d.\n", subnetmaski[0], subnetmaski[1], subnetmaski[2], subnetmaski[3]); + } + } + fclose(fp); + + // log the LAN configuration + login_log("The LAN configuration of the server is set:" 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; + printf("LAN test of LAN IP of the char-server: "); + if (lan_ip_check(p) == 0) { + printf("\033[1;31m***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network\033[0m\n"); + login_log("***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network." RETCODE); + } + } + + printf("---End reading of Lan Support configuration file\n"); + + return 0; +} + +//----------------------------------- +// Reading general configuration file +//----------------------------------- +int login_config_read(const char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen(cfgName, "r"); + if (fp == NULL) { + printf("Configuration file (%s) not found.\n", cfgName); + return 1; + } + + printf("---Start reading of Login Server configuration file (%s)\n", cfgName); + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + line[sizeof(line)-1] = '\0'; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + remove_control_chars(w1); + remove_control_chars(w2); + + if (strcmpi(w1, "admin_state") == 0) { + admin_state = config_switch(w2); + } else if (strcmpi(w1, "admin_pass") == 0) { + 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) + free(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) + free(access_ladmin_allow); + // set to all + access_ladmin_allow = calloc(ACO_STRSIZE, 1); + 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 = realloc(access_ladmin_allow, (access_ladmin_allownum+1) * ACO_STRSIZE); + else + access_ladmin_allow = calloc(ACO_STRSIZE, 1); + 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) { + 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, "login_port") == 0) { + login_port = atoi(w2); + } else if (strcmpi(w1, "account_filename") == 0) { + strncpy(account_filename, w2, sizeof(account_filename)); + account_filename[sizeof(account_filename)-1] = '\0'; + } else if (strcmpi(w1, "gm_account_filename") == 0) { + strncpy(GM_account_filename, w2, sizeof(GM_account_filename)); + GM_account_filename[sizeof(GM_account_filename)-1] = '\0'; + } else if (strcmpi(w1, "gm_account_filename_check_timer") == 0) { + gm_account_filename_check_timer = atoi(w2); + } else if (strcmpi(w1, "login_log_filename") == 0) { + strncpy(login_log_filename, w2, sizeof(login_log_filename)); + login_log_filename[sizeof(login_log_filename)-1] = '\0'; + } else if (strcmpi(w1, "login_log_unknown_packets_filename") == 0) { + strncpy(login_log_unknown_packets_filename, w2, sizeof(login_log_unknown_packets_filename)); + login_log_unknown_packets_filename[sizeof(login_log_unknown_packets_filename)-1] = '\0'; + } else if (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! + 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) + free(access_allow); + access_allow = NULL; + access_allownum = 0; + } else { + if (strcmpi(w2, "all") == 0) { + // reset all previous values + if (access_allow) + free(access_allow); + // set to all + access_allow = calloc(ACO_STRSIZE, 1); + 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 = realloc(access_allow, (access_allownum+1) * ACO_STRSIZE); + else + access_allow = calloc(ACO_STRSIZE, 1); + 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) + free(access_deny); + access_deny = NULL; + access_denynum = 0; + } else { + if (strcmpi(w2, "all") == 0) { + // reset all previous values + if (access_deny) + free(access_deny); + // set to all + access_deny = calloc(ACO_STRSIZE, 1); + 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 = realloc(access_deny, (access_denynum+1) * ACO_STRSIZE); + else + access_deny = calloc(ACO_STRSIZE, 1); + strncpy(access_deny + (access_denynum++) * ACO_STRSIZE, w2, ACO_STRSIZE); + access_deny[access_denynum * ACO_STRSIZE - 1] = '\0'; + } + } + } else if(strcmpi(w1,"anti_freeze_enable")==0){ + anti_freeze_enable = config_switch(w2); + } else if (strcmpi(w1, "anti_freeze_interval") == 0) { + ANTI_FREEZE_INTERVAL = atoi(w2); + if (ANTI_FREEZE_INTERVAL < 5) + ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds + } else if (strcmpi(w1, "import") == 0) { + login_config_read(w2); + } + } + } + fclose(fp); + + printf("---End reading of Login Server configuration file.\n"); + + return 0; +} + +//------------------------------------- +// Displaying of configuration warnings +//------------------------------------- +void display_conf_warnings(void) { + if (admin_state != 0 && admin_state != 1) { + printf("***WARNING: Invalid value for admin_state parameter -> set to 0 (no remote admin).\n"); + admin_state = 0; + } + + if (admin_state == 1) { + if (admin_pass[0] == '\0') { + printf("***WARNING: Administrator password is void (admin_pass).\n"); + } else if (strcmp(admin_pass, "admin") == 0) { + printf("***WARNING: You are using the default administrator password (admin_pass).\n"); + printf(" We highly recommend that you change it.\n"); + } + } + + if (gm_pass[0] == '\0') { + printf("***WARNING: 'To GM become' password is void (gm_pass).\n"); + printf(" We highly recommend that you set one password.\n"); + } else if (strcmp(gm_pass, "gm") == 0) { + printf("***WARNING: You are using the default GM password (gm_pass).\n"); + printf(" We highly recommend that you change it.\n"); + } + + if (level_new_gm < 0 || level_new_gm > 99) { + printf("***WARNING: Invalid value for level_new_gm parameter -> set to 60 (default).\n"); + level_new_gm = 60; + } + + if (new_account_flag != 0 && new_account_flag != 1) { + printf("***WARNING: Invalid value for new_account parameter -> set to 0 (no new account).\n"); + new_account_flag = 0; + } + + if (login_port < 1024 || login_port > 65535) { + printf("***WARNING: Invalid value for login_port parameter -> set to 6900 (default).\n"); + login_port = 6900; + } + + if (gm_account_filename_check_timer < 0) { + printf("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"); + printf(" -> set to 15 sec (default).\n"); + gm_account_filename_check_timer = 15; + } else if (gm_account_filename_check_timer == 1) { + printf("***WARNING: Invalid value for gm_account_filename_check_timer parameter.\n"); + printf(" -> set to 2 sec (minimum value).\n"); + gm_account_filename_check_timer = 2; + } + + if (save_unknown_packets != 0 && save_unknown_packets != 1) { + printf("WARNING: Invalid value for save_unknown_packets parameter -> set to 0-no save.\n"); + save_unknown_packets = 0; + } + + if (display_parse_login != 0 && display_parse_login != 1) { // 0: no, 1: yes + printf("***WARNING: Invalid value for display_parse_login parameter\n"); + printf(" -> set to 0 (no display).\n"); + display_parse_login = 0; + } + + if (display_parse_admin != 0 && display_parse_admin != 1) { // 0: no, 1: yes + printf("***WARNING: Invalid value for display_parse_admin parameter\n"); + printf(" -> set to 0 (no display).\n"); + display_parse_admin = 0; + } + + if (display_parse_fromchar < 0 || display_parse_fromchar > 2) { // 0: no, 1: yes (without packet 0x2714), 2: all packets + printf("***WARNING: Invalid value for display_parse_fromchar parameter\n"); + printf(" -> set to 0 (no display).\n"); + display_parse_fromchar = 0; + } + + if (min_level_to_connect < 0) { // 0: all players, 1-99 at least gm level x + printf("***WARNING: Invalid value for min_level_to_connect (%d) parameter\n", min_level_to_connect); + printf(" -> set to 0 (any player).\n"); + min_level_to_connect = 0; + } else if (min_level_to_connect > 99) { // 0: all players, 1-99 at least gm level x + printf("***WARNING: Invalid value for min_level_to_connect (%d) parameter\n", min_level_to_connect); + printf(" -> set to 99 (only GM level 99).\n"); + min_level_to_connect = 99; + } + + if (add_to_unlimited_account != 0 && add_to_unlimited_account != 1) { // 0: no, 1: yes + printf("***WARNING: Invalid value for add_to_unlimited_account parameter\n"); + printf(" -> set to 0 (impossible to add a time to an unlimited account).\n"); + add_to_unlimited_account = 0; + } + + if (start_limited_time < -1) { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time + printf("***WARNING: Invalid value for start_limited_time parameter\n"); + printf(" -> set to -1 (new accounts are created with unlimited time).\n"); + start_limited_time = -1; + } + + if (check_ip_flag != 0 && check_ip_flag != 1) { // 0: no, 1: yes + printf("***WARNING: Invalid value for check_ip_flag parameter\n"); + printf(" -> set to 1 (check players ip between login-server & char-server).\n"); + check_ip_flag = 1; + } + + if (access_order == ACO_DENY_ALLOW) { + if (access_denynum == 1 && access_deny[0] == '\0') { + printf("***WARNING: The IP security order is 'deny,allow' (allow if not deny).\n"); + printf(" And you refuse ALL IP.\n"); + } + } else if (access_order == ACO_ALLOW_DENY) { + if (access_allownum == 0) { + printf("***WARNING: The IP security order is 'allow,deny' (deny if not allow).\n"); + printf(" But, NO IP IS AUTHORISED!\n"); + } + } else { // ACO_MUTUAL_FAILTURE + if (access_allownum == 0) { + printf("***WARNING: The IP security order is 'mutual-failture'\n"); + printf(" (allow if in the allow list and not in the deny list).\n"); + printf(" But, NO IP IS AUTHORISED!\n"); + } else if (access_denynum == 1 && access_deny[0] == '\0') { + printf("***WARNING: The IP security order is mutual-failture\n"); + printf(" (allow if in the allow list and not in the deny list).\n"); + printf(" But, you refuse ALL IP!\n"); + } + } + + return; +} + +//------------------------------- +// Save configuration in log file +//------------------------------- +void save_config_in_log(void) { + int i; + + // a newline in the log... + login_log(""); + login_log("The login-server starting..." 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); + + // 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)); + } + } +} + +//-------------------------------------- +// Function called at exit of the server +//-------------------------------------- +void do_final(void) { + int i, fd; + + mmo_auth_sync(); + + free(auth_dat); + free(gm_account_db); + for (i = 0; i < MAX_SERVERS; i++) { + if ((fd = server_fd[i]) >= 0) + delete_session(fd); + } + delete_session(login_fd); + + login_log("----End of login-server (normal end with closing of all files)." RETCODE); +} + +//------------------------------ +// Main function of login-server +//------------------------------ +int do_init(int argc, char **argv) { + int i, j; + + // read login-server configuration + login_config_read((argc > 1) ? argv[1] : LOGIN_CONF_NAME); + display_conf_warnings(); // not in login_config_read, because we can use 'import' option, and display same message twice or more + save_config_in_log(); // not before, because log file name can be changed + login_lan_config_read((argc > 1) ? argv[1] : LAN_CONF_NAME); + + srand(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 = numdb_init(); + + read_gm_account(); + mmo_auth_init(); + set_termfunc(mmo_auth_sync); + set_defaultparse(parse_login); + login_fd = make_listen_port(login_port); + + add_timer_func_list(check_auth_sync, "check_auth_sync"); + + i = 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) + + if(anti_freeze_enable > 0) { + add_timer_func_list(char_anti_freeze_system, "char_anti_freeze_system"); + i = add_timer_interval(gettick() + 1000, char_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); + } + + // add timer to check GM accounts file modification + j = gm_account_filename_check_timer; + if (j == 0) // if we would not to check, we check every 60 sec, just to have timer (if we change timer, is was not necessary to check if timer already exists) + j = 60; + add_timer_func_list(check_GM_file, "check_GM_file"); + i = add_timer_interval(gettick() + j * 1000, check_GM_file, 0, 0, j * 1000); // every x sec we check if gm file has been changed + + login_log("The login-server is ready (Server is listening on the port %d)." RETCODE, login_port); + + printf("The login-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", login_port); + + atexit(do_final); + return 0; +} + + diff --git a/src/login/login.h b/src/login/login.h new file mode 100644 index 0000000..3a8a6d5 --- /dev/null +++ b/src/login/login.h @@ -0,0 +1,38 @@ +// $Id: login.h,v 1.1.1.1 2004/09/10 17:26:53 MagicalTux Exp $ +#ifndef _LOGIN_H_ +#define _LOGIN_H_ + +#define MAX_SERVERS 30 + +#define LOGIN_CONF_NAME "conf/login_athena.conf" +#define LAN_CONF_NAME "conf/lan_support.conf" +#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. + // It is 1 at the time of passwordencrypt. + // It is made into 2 at the time of passwordencrypt2. + // When it is made 3, it corresponds to both. +#define START_ACCOUNT_NUM 2000000 +#define END_ACCOUNT_NUM 100000000 + +struct mmo_account { + char* userid; + char* passwd; + int passwdenc; + + long account_id; + long login_id1; + long login_id2; + long char_id; + char lastlogin[24]; + int sex; +}; + +struct mmo_char_server { + char name[20]; + long ip; + short port; + int users; + int maintenance; + int new; +}; + +#endif diff --git a/src/login/md5calc.c b/src/login/md5calc.c new file mode 100644 index 0000000..96bfc34 --- /dev/null +++ b/src/login/md5calc.c @@ -0,0 +1,237 @@ +// $Id: md5calc.c,v 1.1.1.1 2004/09/10 17:26:54 MagicalTux Exp $ +/*********************************************************** + * 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,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 0000000..9137b5b --- /dev/null +++ b/src/login/md5calc.h @@ -0,0 +1,8 @@ +// $Id: md5calc.h,v 1.1.1.1 2004/09/10 17:26:54 MagicalTux Exp $ +#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/GNUmakefile b/src/login_sql/GNUmakefile new file mode 100644 index 0000000..c245b79 --- /dev/null +++ b/src/login_sql/GNUmakefile @@ -0,0 +1,17 @@ +all: login-server_sql +sql: login-server_sql + +shared_libs=all +COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/malloc.o +COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h + +login-server_sql: login.o md5calc.o strlib.o $(COMMON_OBJ) + $(CC) -o ../../$@ $^ $(LIB_S) + +login.o: login.c login.h md5calc.h strlib.h $(COMMON_H) +md5calc.o: md5calc.c md5calc.h +strlib.o: strlib.c strlib.h + +clean: + rm -f *.o ../../login-server_sql + diff --git a/src/login_sql/Makefile b/src/login_sql/Makefile new file mode 100644 index 0000000..d1f2734 --- /dev/null +++ b/src/login_sql/Makefile @@ -0,0 +1,17 @@ +all: login-server_sql
+sql: login-server_sql
+
+shared_libs=all
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o ../common/malloc.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h
+
+login-server_sql: login.o md5calc.o strlib.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $^ $(LIB_S)
+
+login.o: login.c login.h md5calc.h strlib.h $(COMMON_H)
+md5calc.o: md5calc.c md5calc.h
+strlib.o: strlib.c strlib.h
+
+clean:
+ rm -f *.o ../../login-server_sql
+
diff --git a/src/login_sql/login.c b/src/login_sql/login.c new file mode 100644 index 0000000..2fab051 --- /dev/null +++ b/src/login_sql/login.c @@ -0,0 +1,1682 @@ +// $Id: login.c,v 1.6 2004/09/19 21:12:07 Valaris Exp $ +// original : login2.c 2003/01/28 02:29:17 Rev.1.1.1.1 +// txt version 1.100 + +#include <sys/types.h> + +#ifdef LCCWIN32 +#include <winsock.h> +#pragma lib <libmysql.lib> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> // for stat/lstat/fstat +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> + +//add include for DBMS(mysql) +#include <mysql.h> + +#include "strlib.h" +#include "timer.h" +/* +#include "timer.h" +#include "core.h" +#include "socket.h" +#include "login.h" +#include "mmo.h" +#include "version.h" +#include "db.h" +*/ + +#include "../common/core.h" +#include "../common/socket.h" +#include "login.h" +#include "../common/mmo.h" +#include "../common/version.h" +#include "../common/db.h" +#include "../common/timer.h" + +#ifdef PASSWORDENC +#include "md5calc.h" +#endif + +#ifdef MEMWATCH +#include "memwatch.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; +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 server_freezeflag[MAX_SERVERS]; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed +int anti_freeze_enable = 0; +int ANTI_FREEZE_INTERVAL = 15; + +int login_fd; + +char date_format[32] = "%Y-%m-%d %H:%M:%S"; +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) + +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"; +int use_md5_passwds = 0; +char login_db[256] = "login"; +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 tmpsql[65535], tmp_sql[65535]; + +//----------------------------------------------------- + +#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; + + +//----------------------------------------------------- + +static char md5key[20], md5keylen = 16; + +//----------------------------------------------------- +// 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)) { + printf("DB server Error (select GM Level to Memory)- %s\n", mysql_error(&mysql_handle)); + } + 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; +} + +//----------------------------------------------------- +// Function to suppress control characters in a string. +//----------------------------------------------------- +int remove_control_chars(unsigned char *str) { + int i; + int change = 0; + + for(i = 0; str[i]; i++) { + if (str[i] < 32) { + str[i] = '_'; + change = 1; + } + } + + return change; +} + +//--------------------------------------------------- +// E-mail check: return 0 (not correct) or 1 (valid). +//--------------------------------------------------- +int e_mail_check(unsigned char *email) { + char ch; + unsigned char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +//----------------------------------------------------- +// Read Account database - mysql db +//----------------------------------------------------- +int mmo_auth_sqldb_init(void) { + + printf("Login server init....\n"); + + // memory initialize + printf("memory initialize....\n"); + + mysql_init(&mysql_handle); + + // DB connection start + printf("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 + printf("%s\n", mysql_error(&mysql_handle)); + exit(1); + } else { + printf("connect success!\n"); + } + + sprintf(tmpsql, "INSERT INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '', 'lserver', '100','login server started')", loginlog_db); + + //query + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + 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) { + + //set log. + sprintf(tmpsql,"INSERT INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '', 'lserver','100', 'login server shutdown')", loginlog_db); + + //query + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + //delete all server status + sprintf(tmpsql,"DELETE FROM `sstatus`"); + //query + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + mysql_close(&mysql_handle); + printf("close DB connect....\n"); + + int i, fd; + + for (i = 0; i < MAX_SERVERS; i++) { + if ((fd = server_fd[i]) >= 0) + delete_session(fd); + } + delete_session(login_fd); +} + +//----------------------------------------------------- +// Make new account +//----------------------------------------------------- +int mmo_auth_sqldb_new(struct mmo_account* account,const char *tmpstr, char sex) { + //no need on DB version + + printf("Request new account.... - not support on this version\n"); + + return 0; +} + +//----------------------------------------------------- +// Make new account +//----------------------------------------------------- +int mmo_auth_new(struct mmo_account* account, const char *tmpstr, char sex) { + + return 0; +} + +#ifdef LCCWIN32 +extern void gettimeofday(struct timeval *t, struct timezone *dummy); +#endif + +//----------------------------------------------------- +// Auth +//----------------------------------------------------- +int mmo_auth( struct mmo_account* account , int fd){ + struct timeval tv; + time_t ban_until_time; + char tmpstr[256]; + char t_uid[256], t_pass[256]; + char user_password[256]; + + 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; + + printf ("auth start...\n"); + + sprintf(ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]); + + // auth start : time seed + gettimeofday(&tv, NULL); + strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&(tv.tv_sec))); + sprintf(tmpstr+19, ".%03d", (int)tv.tv_usec/1000); + + 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'", login_db_account_id, login_db_userid, login_db_user_pass, login_db_level, login_db, 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} + + // query + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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. + printf ("auth failed no account %s %s %s\n", tmpstr, account->userid, account->passwd); + mysql_free_result(sql_res); + return 0; + } + } else { + printf("mmo_auth DB result error ! \n"); + return 0; + } + // 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 + { + 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); + } + printf("account id ok encval:%d\n",account->passwdenc); + int encpasswdok = 0; +#ifdef PASSWORDENC + if (account->passwdenc > 0) { + printf ("start md5calc..\n"); + int j = account->passwdenc; + 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; + printf("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); + printf("client [%s] accountpass [%s]\n", user_password, sql_row[2]); + printf ("end md5calc..\n"); + } +#endif + if ((strcmp(user_password, sql_row[2]) && !encpasswdok)) { + if (account->passwdenc == 0) { + printf ("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); + printf("%s\n", p); +#endif + } + return 1; + } + printf("auth ok %s %s" RETCODE, tmpstr, account->userid); + } + + 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 MSG (actually, all states after 9 except 99 are No MSG, use only this) + case 100: // 99 = This ID has been totally erased + printf("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; + } + } + +/* +// 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 + sprintf(tmpsql, "UPDATE `%s` SET `ban_until`='0' WHERE `%s`='%s'", login_db, login_db_userid, t_uid); + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + } + + if (atol(sql_row[6]) != 0 && atol(sql_row[6]) < time(NULL)) { + return 2; // 2 = This ID is expired + } + + 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'; + + sprintf(tmpsql, "UPDATE `%s` SET `lastlogin` = NOW(), `logincount`=`logincount` +1, `last_ip`='%s' WHERE `%s` = '%s'", + login_db, ip, login_db_userid, sql_row[1]); + mysql_free_result(sql_res) ; //resource free + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + return -1; +} + +// 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) { + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + c++; + } + } + + return c; +} + +//-------------------------------- +// Char-server anti-freeze system +//-------------------------------- +int char_anti_freeze_system(int tid, unsigned int tick, int id, int data) { + int i; + + for(i = 0; i < MAX_SERVERS; i++) { + if (server_fd[i] >= 0) {// if char-server is online +// printf("char_anti_freeze_system: server #%d '%s', flag: %d.\n", i, server[i].name, server_freezeflag[i]); + if (server_freezeflag[i]-- < 1) {// Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed + session[server_fd[i]]->eof = 1; + } + } + } + + 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) { + if (id < MAX_SERVERS) { + printf("Char-server '%s' has disconnected.\n", server[id].name); + server_fd[id] = -1; + memset(&server[id], 0, sizeof(struct mmo_char_server)); + // server delete + sprintf(tmpsql, "DELETE FROM `sstatus` WHERE `index`='%d'", id); + // query + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + close(fd); + delete_session(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 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; + printf("auth -> %d\n", i); + break; + } + } + + if (i != AUTH_FIFO_SIZE) { // send account_reg + int p; + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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); + if (account_id > 0) { + sprintf(tmpsql, "SELECT `str`,`value` FROM `global_reg_value` WHERE `type`='1' AND `account_id`='%d'",account_id); + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sql_res = mysql_store_result(&mysql_handle) ; + if (sql_res) { + WFIFOW(fd,0) = 0x2729; + WFIFOL(fd,4) = account_id; + for(p = 8; (sql_row = mysql_fetch_row(sql_res));p+=36){ + memcpy(WFIFOP(fd,p), sql_row[0], 32); + WFIFOL(fd,p+32) = atoi(sql_row[1]); + } + WFIFOW(fd,2) = p; + WFIFOSET(fd,p); + //printf("account_reg2 send : login->char (auth fifo)\n"); + 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); + } + mysql_free_result(sql_res); + } + } 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)) + printf("set users %s : %d\n", server[id].name, RFIFOL(fd,2)); + server[id].users = RFIFOL(fd,2); + if(anti_freeze_enable) + server_freezeflag[id] = 5; // Char anti-freeze system. Counter. 5 ok, 4...0 freezed + + sprintf(tmpsql,"UPDATE `sstatus` SET `user` = '%d' WHERE `index` = '%d'", server[id].users, id); + // query + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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); + printf("change GM isn't support in this login server version.\n"); + printf("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) + printf("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) + printf("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) + printf("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)) + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + printf("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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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(×tamp); + tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6); + tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8); + tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10); + tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12); + tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14); + tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16); + timestamp = 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) = timestamp; // status or final date of a banishment + charif_sendallwos(-1, buf, 11); + } + printf("Account: %d Banned until: %ld\n", acc, timestamp); + sprintf(tmpsql, "UPDATE `%s` SET `ban_until` = '%ld', `state`='7' WHERE `%s` = '%d'", login_db, timestamp, login_db_account_id, acc); + // query + if (mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + } + RFIFOSKIP(fd,18); + break; + } + return 0; + + case 0x2727: + if (RFIFOREST(fd) < 6) + return 0; + { + int acc,sex; + unsigned char buf[16]; + acc=RFIFOL(fd,4); + sprintf(tmpsql,"SELECT `sex` FROM `%s` WHERE `%s` = '%d'",login_db,login_db_account_id,acc); + + if(mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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_reg + if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) + return 0; + { + int acc,p,j; + char str[32]; + char temp_str[32]; + int value; + acc=RFIFOL(fd,4); + + if (acc>0){ + unsigned char buf[RFIFOW(fd,2)+1]; + for(p=8,j=0;p<RFIFOW(fd,2) && j<ACCOUNT_REG2_NUM;p+=36,j++){ + memcpy(str,RFIFOP(fd,p),32); + value=RFIFOL(fd,p+32); + sprintf(tmpsql,"DELETE FROM `global_reg_value` WHERE `type`='1' AND `account_id`='%d' AND `str`='%s';",acc,jstrescapecpy(temp_str,str)); + if(mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + sprintf(tmpsql,"INSERT INTO `global_reg_value` (`type`, `account_id`, `str`, `value`) VALUES ( 1 , '%d' , '%s' , '%d');", acc, jstrescapecpy(temp_str,str), value); + if(mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + + // Send to char + memcpy(WBUFP(buf,0),RFIFOP(fd,0),RFIFOW(fd,2)); + WBUFW(buf,0)=0x2729; + charif_sendallwos(fd,buf,WBUFW(buf,2)); + } + } + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + break; + } + RFIFOSKIP(fd,6); + } + return 0; + + default: + printf("login: unknown packet %x! (from char).\n", RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + } + + 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; } + + printf("LAN check: %s.\n", (lancheck) ? "\033[1;32mLAN\033[0m" : "\033[1;31mWAN\033[0m"); + 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]); + + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + 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. + printf ("packet from banned ip : %d.%d.%d.%d" RETCODE, p[0], p[1], p[2], p[3]); + sprintf(tmpsql,"INSERT 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + printf ("close session connection...\n"); + + // close connection + session[fd]->eof = 1; + + } else { + printf ("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; + close(fd); + delete_session(fd); + return 0; + } + + while(RFIFOREST(fd)>=2){ + printf("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(RFIFOREST(fd)< ((RFIFOW(fd, 0) ==0x64)?55:47)) + return 0; + + printf("client connection request %s from %d.%d.%d.%d\n", RFIFOP(fd, 6), p[0], p[1], p[2], p[3]); + + account.userid = RFIFOP(fd, 6); + account.passwd = RFIFOP(fd, 30); +#ifdef PASSWORDENC + account.passwdenc= (RFIFOW(fd,0)==0x64)?0:PASSWORDENC; +#else + account.passwdenc=0; +#endif + result=mmo_auth(&account, fd); + + jstrescapecpy(t_uid,RFIFOP(fd, 6)); + if(result==-1){ + int gm_level = isGM(account.account_id); + if (min_level_to_connect > gm_level) { + WFIFOW(fd,0) = 0x81; + WFIFOL(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } else { + if (p[0] != 127) { + sprintf(tmpsql,"INSERT 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + if (gm_level) + printf("Connection of the GM (level:%d) account '%s' accepted.\n", gm_level, account.userid); + else + printf("Connection of the account '%s' accepted.\n", account.userid); + 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; + WFIFOL(fd,2) = 1; // 01 = Server closed + WFIFOSET(fd,3); + } + } + } else { + char tmp_sql[512]; + char error[64]; + sprintf(tmp_sql,"INSERT 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 MSG (actually, all states after 9 except 99 are No MSG, use only this) + sprintf(tmpsql,tmp_sql," "); + sprintf(error," "); + break; + case 100: // 99 = This ID has been totally erased + sprintf(tmpsql,tmp_sql,"Account gone."); + sprintf(error,"Account gone."); + break; + default: + sprintf(tmpsql,tmp_sql,"Uknown Error."); + sprintf(error,"Uknown Error."); + break; + } + //query + if(mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + if ((result == 1) && (dynamic_pass_failure_ban != 0)){ // 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + //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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + } + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + result = -3; + } + + sprintf(tmpsql,"SELECT `ban_until` FROM `%s` WHERE `%s` = '%s'",login_db,login_db_userid, t_uid); + if(mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + 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) { + printf("login: abnormal request of MD5 key (already opened session).\n"); + session[fd]->eof = 1; + return 0; + } + printf("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; + { + sprintf(tmpsql,"INSERT 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + printf("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]); + unsigned char* server_name; + account.userid = RFIFOP(fd, 2); + account.passwd = RFIFOP(fd, 26); + 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){ + printf("Connection of the char-server '%s' accepted.\n", server_name); + memset(&server[account.account_id], 0, sizeof(struct mmo_char_server)); + server[account.account_id].ip=RFIFOL(fd,54); + server[account.account_id].port=RFIFOW(fd,58); + memcpy(server[account.account_id].name,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; + if(anti_freeze_enable) + server_freezeflag[account.account_id] = 5; // Char-server anti-freeze system. Counter. 5 ok, 4...0 freezed + sprintf(tmpsql,"DELETE FROM `sstatus` WHERE `index`='%ld'", account.account_id); + //query + if(mysql_query(&mysql_handle, tmpsql)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + 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)) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + WFIFOW(fd,0)=0x2711; + WFIFOB(fd,2)=0; + WFIFOSET(fd,3); + session[fd]->func_parse=parse_fromchar; + realloc_fifo(fd,FIFOSIZE_SERVERLINK,FIFOSIZE_SERVERLINK); + } 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); + printf ("Athena version check...\n"); + break; + + case 0x7532: + default: + printf ("End of connection (ip: %s)" RETCODE, ip); + session[fd]->eof = 1; + return 0; + } + } + + return 0; +} + +//------------------------------------------------- +// Return numerical value of a switch configuration +// on/off, english, fran軋is, deutsch, espaol +//------------------------------------------------- +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) { + printf("file not found: %s\n", lancfgName); + return 1; + } + printf("Start reading of Lan Support configuration file\n"); + 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); + printf ("set Lan_Char_IP : %s\n",w2); + } + + else if(strcmpi(w1,"subnetmask")==0){ + strcpy(subnetmask, w2); + unsigned int k0, k1, k2, k3; + sscanf(subnetmask, "%d.%d.%d.%d", &k0, &k1, &k2, &k3); + subnetmaski[0] = k0; subnetmaski[1] = k1; subnetmaski[2] = k2; subnetmaski[3] = k3; + printf ("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; + printf("LAN test of LAN IP of the char-server: "); + if (lan_ip_check(p) == 0) { + printf("\033[1;31m***ERROR: LAN IP of the char-server doesn't belong to the specified Sub-network\033[0m\n"); + } + } + + printf("End reading of Lan Support configuration file\n"); + + 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()")) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle)); + } + + return 0; +} + +//----------------------------------------------------- +// reading configuration +//----------------------------------------------------- +int login_config_read(const char *cfgName){ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + + if(fp==NULL){ + printf("Configuration file (%s) not found.\n", cfgName); + return 1; + } + printf ("start reading configuration...\n"); + 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,"login_port")==0){ + login_port=atoi(w2); + printf ("set login_port : %s\n",w2); + } + else if(strcmpi(w1,"ipban")==0){ + ipban=atoi(w2); + printf ("set ipban : %d\n",ipban); + } + //account ban -> ip ban + else if(strcmpi(w1,"dynamic_account_ban")==0){ + dynamic_account_ban=atoi(w2); + printf ("set dynamic_account_ban : %d\n",dynamic_account_ban); + } + else if(strcmpi(w1,"dynamic_account_ban_class")==0){ + dynamic_account_ban_class=atoi(w2); + printf ("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); + printf ("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); + printf ("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); + printf ("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); + printf ("set dynamic_pass_failure_ban_how_long : %d\n",dynamic_pass_failure_ban_how_long); + } + else if(strcmpi(w1,"anti_freeze_enable")==0){ + anti_freeze_enable = config_switch(w2); + } + else if (strcmpi(w1, "anti_freeze_interval") == 0) { + ANTI_FREEZE_INTERVAL = atoi(w2); + if (ANTI_FREEZE_INTERVAL < 5) + ANTI_FREEZE_INTERVAL = 5; // minimum 5 seconds + } + else if (strcmpi(w1, "import") == 0) { + login_config_read(w2); + } + 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; + } + printf ("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); + } + } + fclose(fp); + printf ("End reading configuration...\n"); + return 0; +} + +void sql_config_read(const char *cfgName){ /* Kalaspuff, to get login_db */ + int i; + char line[1024], w1[1024], w2[1024]; + printf("reading configure: %s\n", cfgName); + FILE *fp=fopen(cfgName,"r"); + if(fp==NULL){ + printf("file not found: %s\n",cfgName); + exit(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, "login_db") == 0) { + strcpy(login_db, w2); + } + //add for DB connection + else if(strcmpi(w1,"login_server_ip")==0){ + strcpy(login_server_ip, w2); + printf ("set login_server_ip : %s\n",w2); + } + else if(strcmpi(w1,"login_server_port")==0){ + login_server_port=atoi(w2); + printf ("set login_server_port : %s\n",w2); + } + else if(strcmpi(w1,"login_server_id")==0){ + strcpy(login_server_id, w2); + printf ("set login_server_id : %s\n",w2); + } + else if(strcmpi(w1,"login_server_pw")==0){ + strcpy(login_server_pw, w2); + printf ("set login_server_pw : %s\n",w2); + } + else if(strcmpi(w1,"login_server_db")==0){ + strcpy(login_server_db, w2); + printf ("set login_server_db : %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); + } + //end of custom table config + else if (strcmpi(w1, "loginlog_db") == 0) { + strcpy(loginlog_db, w2); + } + } + fclose(fp); + printf("reading configure done.....\n"); +} + +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. + printf ("memset md5key \n"); + memset(md5key, 0, sizeof(md5key)); + printf ("memset md5key complete\n"); + printf ("memset keyleng\n"); + md5keylen=rand()%4+12; + for(i=0;i<md5keylen;i++) + md5key[i]=rand()%255+1; + printf ("memset keyleng complete\n"); + + printf ("set FIFO Size\n"); + for(i=0;i<AUTH_FIFO_SIZE;i++) + auth_fifo[i].delflag=1; + printf ("set FIFO Size complete\n"); + + printf ("set max servers\n"); + for(i=0;i<MAX_SERVERS;i++) + server_fd[i]=-1; + printf ("set max servers complete\n"); + //server port open & binding + + login_fd=make_listen_port(login_port); + + //Auth start + printf ("Running mmo_auth_sqldb_init()\n"); + mmo_auth_sqldb_init(); + printf ("finished mmo_auth_sqldb_init()\n"); + //sync account when terminating. + //but no need when you using DBMS (mysql) + set_termfunc(mmo_db_close); + + //set default parser as parse_login function + set_defaultparse(parse_login); + + if(anti_freeze_enable > 0) { + add_timer_func_list(char_anti_freeze_system, "char_anti_freeze_system"); + i = add_timer_interval(gettick()+1000, char_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); + } + + // ban deleter timer - 1 minute term + printf("add interval tic (ip_ban_check)....\n"); + i=add_timer_interval(gettick()+10, ip_ban_check,0,0,60*1000); + + printf("The login-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", login_port); + + return 0; +} + + diff --git a/src/login_sql/login.h b/src/login_sql/login.h new file mode 100644 index 0000000..6335168 --- /dev/null +++ b/src/login_sql/login.h @@ -0,0 +1,41 @@ +#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" + +#define PASSWORDENC 3 // A definition is given when making an encryption password correspond. + // It is 1 at the time of passwordencrypt. + // It is made into 2 at the time of passwordencrypt2. + // When it is made 3, it corresponds to both. + +#define START_ACCOUNT_NUM 2000000 +#define END_ACCOUNT_NUM 100000000 + +struct mmo_account { + char* userid; + char* passwd; + int passwdenc; + + long account_id; + long login_id1; + long login_id2; + long char_id; + char lastlogin[24]; + int sex; +}; + +struct mmo_char_server { + char name[20]; + long ip; + short port; + int users; + int maintenance; + int new; +}; + + +#endif diff --git a/src/login_sql/make.sh b/src/login_sql/make.sh new file mode 100644 index 0000000..198d33b --- /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 0000000..58cea12 --- /dev/null +++ b/src/login_sql/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; + +// 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,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 0000000..9bc554f --- /dev/null +++ b/src/login_sql/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/readme.txt b/src/login_sql/readme.txt new file mode 100644 index 0000000..b945af7 --- /dev/null +++ b/src/login_sql/readme.txt @@ -0,0 +1,120 @@ +サソ// Encoded UTF-8 +//--------------------------------------------- +// 007 - by Jazz +1. 0590 official version縺ォ蜷医o縺帙※菫ョ豁」縺励∪縺励◆. + +//--------------------------------------------- +// 006 - by Jazz +1. 繝代せ繝ッ繝シ繝画アコ縺セ縺」縺溷屓謨ー髢馴&縺譎りェ蜍 IP驕ョ譁ュ蜈キ迴セ. +2. login_athena.conf繧剃ソョ豁」 + +//--------------------------------------------- +// 005 - by Jazz +1. 險ア螳ケ縺励↑縺 ID縺梧磁霑代ョ譎りェ蜍輔〒 IP驕ョ譁ュ讖溯ス蜈キ迴セ. +2. 閾ェ蜍輔〒驕ョ譁ュ隗」髯、讖溯ス蜈キ迴セ. + +//--------------------------------------------- +// 004 - by Jazz +1. aphostropy 蝠城。後r隗」豎コ縺励∪縺励◆. 縺薙l縺ッ菫晏ョ峨→騾」髢「縺後≠繧句撫鬘後〒縺. +2. SQL縺ョ讒矩繧剃ソョ豁」縺励∪縺励◆. +3. server 迥カ諷九r SQL縺ァ蜃コ蜉. + +//--------------------------------------------- +// 003 - by Jazz +1. 謗・邯夊ィ倬鹸繧 DB縺ァ蜃コ蜉. +2. IP蝓コ逶、縺ョ謗・邯夐ョ譁ュ蜈キ迴セ. + +//--------------------------------------------- +// 002 - by Jazz +1. 縺縺上▽縺九ョ隧ウ邏ー蝠城。檎せ菫ョ豁」. + +//--------------------------------------------- +// 001 - by Jazz +1. 荳逡ェ逶ョ螳牙ョ version縺ァ縺. + + +// notice some.. +In this program, new parts are under BSD License. +and imported parts are under GPL as athena did. + +this program is not public license. but modification and editing are +unlimit permitted. but auther don't have any responsibility on that. + +this realase does not gurantee working perfectly. and, I would +answer neither that question nor technical one. + +I use... +P4 2.4Gh/512MB/WinXP/cygwin + +athena is under GPL license. +Ragnarok is Gravity's trademark. +login-db for athena is BSD license except code from original athena. +cygwin is Redhat's trademark. + +// About compile. +You need mysqlclient library to compile this. You can get this by +compiling mysql source. + +"-lmysqlclient" option needed when you did compile. + +some patch need on mysql when you did compile under cygwin. + +// ェーァ 簿ウエ乱 劇葺流 + +擽 売。懋キク棹乱 ヨ。 ァ誤豆牟ァ カカ捩 BSD 攵擽┥侃 符椈乱 ーー小 姓笈共. +キクヲャウ import 頗 カカ捩 寳椈 athena攪 GPL 攵擽┥侃 符椈乱 梭慣笈共. + +擽 売。懋キク棹捩 public licenseー 符漁笈共. 葺ァァ ウイス, ー懍。ー, 們菩捩 +ャエ懦復 来圸据ゥー キク乱 劇復 ア桷捩 梵ー ァァ 賦慣笈共. + +椪 擽 ヲエヲャヲ壱株 刋イス梭 徐梠物揆 ウエ棗葺ァ 賦慣笈共. キクヲャウ キク乱 劇復 +メ橋 戦復 壱劇 ー幗ァ 賦慣笈共. + +クー唖攤 カカ攪 メ橋捩 ー幗ァ 賦慣笈共. + +ー罹ー 劍イス +P4 2.4Gh/512MB/WinXP/cygwin + +athena株 GPL 攵擽┥侃・シ 、 鮒笈共. +Ragnarok株 Gravity攪 trademark桿笈共. +login-db for athena株 athena乱 ーク乖 カカ揆 懍匣葺ゥエ BSD 攵擽┥侃・シ 伐ヲ笈共. +cygwin捩 Redhat攪 trademark桿笈共. + +// サエ血攵乱 劇葺流. + +擽 売。懋キク棹揆 サエ血攵葺クー 怱葺流 mysqlclient乱 劇復 libraryー 符囈鮒笈共. +擽株 mysql攪 護侃・シ サエ血攵葺ゥエ 冥揆 梭慣笈共. + +サエ血攵亨乱 -lmysqlclient 亰們擽 符囈鮒笈共. + +cygwin攪 イス垈株 ェーァ 肩ケ俾ー 符囈鮒笈共. + +// 縺縺上▽縺九ョ諠蝣ア縺ォ蟇セ縺励※ + +縺薙ョ繝励Ο繧ー繝ゥ繝縺ァ譁ー縺溘↓菴懊i繧後◆驛ィ蛻縺ッ BSD 繝ゥ繧、繧サ繝ウ繧ケ縺ョ荳九↓驟榊ク縺ォ縺ェ繧翫∪縺. +縺昴@縺ヲ import 縺ォ縺ェ縺」縺滄Κ蛻縺ッ蜈縲 athena縺ョ GPL 繝ゥ繧、繧サ繝ウ繧ケ縺ョ荳九↓縺ゅj縺セ縺. + +縺薙ョ繝励Ο繧ー繝ゥ繝縺ッ public license縺ァ縺ッ縺ェ縺縺ァ縺. 縺励°縺怜、画峩, 謾ケ騾, 菫ョ豁」縺ッ +辟。蛻カ髯占ィア螳ケ縺輔l縺ヲ縺昴l縺ォ蟇セ縺吶k雋ャ莉サ縺ッ闡苓縺瑚イ縺代↑縺縺ァ縺. + +迴セ蝨ィ縺薙ョ繝ェ繝ェ繝シ繧ケ縺ッ螳悟」√↓蜍穂ス懊☆繧九%縺ィ繧剃ソ晞囿縺励↑縺縺ァ縺. 縺昴@縺ヲ蠖シ縺ォ螟ァ髻 +逶ク隲繧らオカ蟇セ蜿励¢縺ェ縺縺ァ縺. + +謚陦鍋噪縺ェ驛ィ蛻縺ョ逶ク隲縺ッ蜿励¢縺ェ縺縺ァ縺. + +髢狗匱迺ー蠅 +P4 2.4Gh/512MB/WinXP/cygwin + +athena縺ッ GPL 繝ゥ繧、繧サ繝ウ繧ケ繧貞ョ医j縺セ縺. +Ragnarok縺ッ Gravity縺ョ trademark縺ァ縺. +login-db for athena縺ッ athena縺九i謖√▲縺ヲ譚・縺滄Κ蛻繧帝勁縺代ー BSD 繝ゥ繧、繧サ繝ウ繧ケ縺ォ莉倥″縺セ縺. +cygwin縺ッ Redhat縺ョ trademark縺ァ縺. + +// 繧ウ繝ウ繝代う繝ォ縺ォ蟇セ縺励※. + +縺薙ョ繝励Ο繧ー繝ゥ繝繧偵さ繝ウ繝代う繝ォ縺吶k縺溘a縺ォ mysqlclient縺ォ蟇セ縺吶k library縺悟ソ隕√〒縺. +縺薙l縺ッ mysql縺ョ繧ス繝シ繧ケ繧偵さ繝ウ繝代う繝ォ縺吶l縺ー蠕励k縺薙→縺後〒縺阪∪縺. + +繧ウ繝ウ繝代う繝ォ縺ョ譎ゅ↓ -lmysqlclient 繧ェ繝励す繝ァ繝ウ縺悟ソ隕√〒縺. + +cygwin縺ョ蝣エ蜷医ッ縺縺上▽縺九ョ繝代ャ繝√′蠢隕√〒縺.
\ No newline at end of file diff --git a/src/login_sql/strlib.c b/src/login_sql/strlib.c new file mode 100644 index 0000000..7f6e197 --- /dev/null +++ b/src/login_sql/strlib.c @@ -0,0 +1,58 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "strlib.h" +#include "utils.h" + +//----------------------------------------------- +// string lib. +unsigned char* jstrescape (unsigned char* pt) { + //copy from here + unsigned char * ptr; + int i =0, j=0; + + //copy string to temporary + CREATE(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; + default: + pt[j++] = ptr[i++]; + } + } + pt[j++] = '\0'; + free (ptr); + return (unsigned char*) &pt[0]; +} + +unsigned char* jstrescapecpy (unsigned char* pt,unsigned char* spt) { + //copy from here + 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; + default: + pt[j++] = spt[i++]; + } + } + pt[j++] = '\0'; + return (unsigned char*) &pt[0]; +} diff --git a/src/login_sql/strlib.h b/src/login_sql/strlib.h new file mode 100644 index 0000000..ddf29ab --- /dev/null +++ b/src/login_sql/strlib.h @@ -0,0 +1,9 @@ +#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" +unsigned char* jstrescape (unsigned char* pt); +unsigned char* jstrescapecpy (unsigned char* pt,unsigned char* spt); +#endif diff --git a/src/login_sql/timer.h b/src/login_sql/timer.h new file mode 100644 index 0000000..be9706a --- /dev/null +++ b/src/login_sql/timer.h @@ -0,0 +1,43 @@ +// original : core.h 2003/03/14 11:55:25 Rev 1.4 + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#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 + +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); +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)); + +#endif // _TIMER_H_ diff --git a/src/map/GNUmakefile b/src/map/GNUmakefile new file mode 100644 index 0000000..852bbb9 --- /dev/null +++ b/src/map/GNUmakefile @@ -0,0 +1,72 @@ +all: txt sql
+
+txt: txtobj map-server
+
+sql: sqlobj map-server_sql
+
+txtobj:
+ mkdir txtobj
+
+sqlobj:
+ mkdir sqlobj
+
+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
+LIBS = -lz -lm
+
+map-server: txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/npc.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/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $^ $(LIBS)
+
+map-server_sql: sqlobj/map.o sqlobj/chrif.o sqlobj/clif.o sqlobj/pc.o sqlobj/npc.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/battle.o sqlobj/intif.o sqlobj/trade.o sqlobj/party.o sqlobj/vending.o sqlobj/guild.o sqlobj/pet.o sqlobj/mail.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $^ $(LIB_S)
+
+txtobj/%.o: %.c
+ $(COMPILE.c) -DTXT_ONLY $(OUTPUT_OPTION) $<
+
+sqlobj/%.o: %.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+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
+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
+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
+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
+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
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h
+txtobj/path.o: path.c map.h battle.h ../common/mmo.h
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h ../common/timer.h ../common/socket.h ../common/mmo.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
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h ../common/timer.h ../common/mmo.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 ../common/socket.h ../common/timer.h ../common/mmo.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
+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
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.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
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.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
+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
+
+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 ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.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
+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
+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 ../common/timer.h ../common/mmo.h ../common/db.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
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h
+sqlobj/path.o: path.c map.h battle.h ../common/mmo.h
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h ../common/timer.h ../common/socket.h ../common/mmo.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 ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h ../common/timer.h ../common/mmo.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 ../common/socket.h ../common/timer.h ../common/mmo.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
+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
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.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
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.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
+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
+sqlobj/mail.o: mail.c mail.h
+
+clean:
+ rm -rf *.o ../../map-server ../../map-server_sql sqlobj txtobj
diff --git a/src/map/Makefile b/src/map/Makefile new file mode 100644 index 0000000..3f5396e --- /dev/null +++ b/src/map/Makefile @@ -0,0 +1,72 @@ +all: txt sql
+
+txt: txtobj map-server
+
+sql: sqlobj map-server_sql
+
+txtobj:
+ mkdir txtobj
+
+sqlobj:
+ mkdir sqlobj
+
+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
+LIBS = -lz -lm
+
+map-server: txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/npc.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/battle.o txtobj/intif.o txtobj/trade.o txtobj/party.o txtobj/vending.o txtobj/guild.o txtobj/pet.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $> $(LIBS)
+
+map-server_sql: sqlobj/map.o sqlobj/chrif.o sqlobj/clif.o sqlobj/pc.o sqlobj/npc.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/battle.o sqlobj/intif.o sqlobj/trade.o sqlobj/party.o sqlobj/vending.o sqlobj/guild.o sqlobj/pet.o sqlobj/mail.o $(COMMON_OBJ)
+ $(CC) -o ../../$@ $> $(LIB_S)
+
+txtobj/%.o: %.c
+ $(COMPILE.c) -DTXT_ONLY $(OUTPUT_OPTION) $<
+
+sqlobj/%.o: %.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+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
+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
+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
+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
+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
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h
+txtobj/path.o: path.c map.h battle.h ../common/mmo.h
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h ../common/timer.h ../common/socket.h ../common/mmo.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
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h ../common/timer.h ../common/mmo.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 ../common/socket.h ../common/timer.h ../common/mmo.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
+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
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.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
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.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
+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
+
+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 ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.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
+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
+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 ../common/timer.h ../common/mmo.h ../common/db.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
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h
+sqlobj/path.o: path.c map.h battle.h ../common/mmo.h
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h ../common/timer.h ../common/socket.h ../common/mmo.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 ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h ../common/timer.h ../common/mmo.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 ../common/socket.h ../common/timer.h ../common/mmo.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
+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
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.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
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.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
+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
+sqlobj/mail.o: mail.c mail.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 0000000..8dc39ba --- /dev/null +++ b/src/map/atcommand.c @@ -0,0 +1,7753 @@ +// $Id: atcommand.c 148 2004-09-30 14:05:37Z MouseJstr $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> + +#include "socket.h" +#include "timer.h" +#include "nullpo.h" + +#include "clif.h" +#include "chrif.h" +#include "intif.h" +#include "itemdb.h" +#include "map.h" +#include "pc.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" +#include "core.h" + +#ifndef TXT_ONLY +#include "mail.h" +#endif + +#define STATE_BLIND 0x10 + +static char command_symbol = '@'; // first char of the commands (by [Yor]) + +static char msg_table[1000][1024]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others) + +#define ATCOMMAND_FUNC(x) int atcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message) +ATCOMMAND_FUNC(broadcast); +ATCOMMAND_FUNC(localbroadcast); +ATCOMMAND_FUNC(rurap); +ATCOMMAND_FUNC(rura); +ATCOMMAND_FUNC(where); +ATCOMMAND_FUNC(jumpto); +ATCOMMAND_FUNC(jump); +ATCOMMAND_FUNC(who); +ATCOMMAND_FUNC(who2); +ATCOMMAND_FUNC(who3); +ATCOMMAND_FUNC(whomap); +ATCOMMAND_FUNC(whomap2); +ATCOMMAND_FUNC(whomap3); +ATCOMMAND_FUNC(whogm); // by Yor +ATCOMMAND_FUNC(save); +ATCOMMAND_FUNC(load); +ATCOMMAND_FUNC(speed); +ATCOMMAND_FUNC(storage); +ATCOMMAND_FUNC(guildstorage); +ATCOMMAND_FUNC(option); +ATCOMMAND_FUNC(hide); +ATCOMMAND_FUNC(jobchange); +ATCOMMAND_FUNC(die); +ATCOMMAND_FUNC(kill); +ATCOMMAND_FUNC(alive); +ATCOMMAND_FUNC(kami); +ATCOMMAND_FUNC(heal); +ATCOMMAND_FUNC(item); +ATCOMMAND_FUNC(item2); +ATCOMMAND_FUNC(itemreset); +ATCOMMAND_FUNC(itemcheck); +ATCOMMAND_FUNC(baselevelup); +ATCOMMAND_FUNC(joblevelup); +ATCOMMAND_FUNC(help); +ATCOMMAND_FUNC(gm); +ATCOMMAND_FUNC(pvpoff); +ATCOMMAND_FUNC(pvpon); +ATCOMMAND_FUNC(gvgoff); +ATCOMMAND_FUNC(gvgon); +ATCOMMAND_FUNC(model); +ATCOMMAND_FUNC(go); +ATCOMMAND_FUNC(monster); +ATCOMMAND_FUNC(spawn); +ATCOMMAND_FUNC(killmonster); +ATCOMMAND_FUNC(killmonster2); +ATCOMMAND_FUNC(refine); +ATCOMMAND_FUNC(produce); +ATCOMMAND_FUNC(memo); +ATCOMMAND_FUNC(gat); +ATCOMMAND_FUNC(packet); +ATCOMMAND_FUNC(statuspoint); +ATCOMMAND_FUNC(skillpoint); +ATCOMMAND_FUNC(zeny); +ATCOMMAND_FUNC(param); +ATCOMMAND_FUNC(guildlevelup); +ATCOMMAND_FUNC(makeegg); +ATCOMMAND_FUNC(hatch); +ATCOMMAND_FUNC(petfriendly); +ATCOMMAND_FUNC(pethungry); +ATCOMMAND_FUNC(petrename); +ATCOMMAND_FUNC(charpetrename); // by Yor +ATCOMMAND_FUNC(recall); +ATCOMMAND_FUNC(recallall); +ATCOMMAND_FUNC(character_job); +ATCOMMAND_FUNC(revive); +ATCOMMAND_FUNC(character_stats); +ATCOMMAND_FUNC(character_stats_all); +ATCOMMAND_FUNC(character_option); +ATCOMMAND_FUNC(character_save); +ATCOMMAND_FUNC(night); +ATCOMMAND_FUNC(day); +ATCOMMAND_FUNC(doom); +ATCOMMAND_FUNC(doommap); +ATCOMMAND_FUNC(raise); +ATCOMMAND_FUNC(raisemap); +ATCOMMAND_FUNC(character_baselevel); +ATCOMMAND_FUNC(character_joblevel); +ATCOMMAND_FUNC(kick); +ATCOMMAND_FUNC(kickall); +ATCOMMAND_FUNC(allskill); +ATCOMMAND_FUNC(questskill); +ATCOMMAND_FUNC(charquestskill); +ATCOMMAND_FUNC(lostskill); +ATCOMMAND_FUNC(charlostskill); +ATCOMMAND_FUNC(spiritball); +ATCOMMAND_FUNC(party); +ATCOMMAND_FUNC(guild); +ATCOMMAND_FUNC(charskreset); +ATCOMMAND_FUNC(charstreset); +ATCOMMAND_FUNC(charreset); +ATCOMMAND_FUNC(charstpoint); +ATCOMMAND_FUNC(charmodel); +ATCOMMAND_FUNC(charskpoint); +ATCOMMAND_FUNC(charzeny); +ATCOMMAND_FUNC(agitstart); +ATCOMMAND_FUNC(agitend); +ATCOMMAND_FUNC(reloaditemdb); +ATCOMMAND_FUNC(reloadmobdb); +ATCOMMAND_FUNC(reloadskilldb); +#ifndef TXT_ONLY +ATCOMMAND_FUNC(rehash);// by Fr3DBr +#else /* TXT_ONLY */ +ATCOMMAND_FUNC(reloadscript); +#endif /* TXT_ONLY */ +ATCOMMAND_FUNC(reloadgmdb); // by Yor +ATCOMMAND_FUNC(mapexit); +ATCOMMAND_FUNC(idsearch); +ATCOMMAND_FUNC(mapinfo); +ATCOMMAND_FUNC(dye); //** by fritz +ATCOMMAND_FUNC(hair_style); //** by fritz +ATCOMMAND_FUNC(hair_color); //** by fritz +ATCOMMAND_FUNC(stat_all); //** by fritz +ATCOMMAND_FUNC(char_change_sex); // by Yor +ATCOMMAND_FUNC(char_block); // by Yor +ATCOMMAND_FUNC(char_ban); // by Yor +ATCOMMAND_FUNC(char_unblock); // by Yor +ATCOMMAND_FUNC(char_unban); // by Yor +ATCOMMAND_FUNC(mount_peco); // by Valaris +ATCOMMAND_FUNC(char_mount_peco); // by Yor +ATCOMMAND_FUNC(guildspy); // [Syrus22] +ATCOMMAND_FUNC(partyspy); // [Syrus22] +ATCOMMAND_FUNC(repairall); // [Valaris] +ATCOMMAND_FUNC(guildrecall); // by Yor +ATCOMMAND_FUNC(partyrecall); // by Yor +//ATCOMMAND_FUNC(nuke); // [Valaris] +ATCOMMAND_FUNC(enablenpc); +ATCOMMAND_FUNC(disablenpc); +ATCOMMAND_FUNC(servertime); // by Yor +ATCOMMAND_FUNC(chardelitem); // by Yor +ATCOMMAND_FUNC(jail); // by Yor +ATCOMMAND_FUNC(unjail); // by Yor +ATCOMMAND_FUNC(disguise); // [Valaris] +ATCOMMAND_FUNC(undisguise); // by Yor +ATCOMMAND_FUNC(ignorelist); // by Yor +ATCOMMAND_FUNC(charignorelist); // by Yor +ATCOMMAND_FUNC(inall); // by Yor +ATCOMMAND_FUNC(exall); // by Yor +ATCOMMAND_FUNC(chardisguise); // Kalaspuff +ATCOMMAND_FUNC(charundisguise); // Kalaspuff +ATCOMMAND_FUNC(email); // by Yor +ATCOMMAND_FUNC(effect);//by Apple +ATCOMMAND_FUNC(character_item_list); // by Yor +ATCOMMAND_FUNC(character_storage_list); // by Yor +ATCOMMAND_FUNC(character_cart_list); // by Yor +ATCOMMAND_FUNC(addwarp); // by MouseJstr +ATCOMMAND_FUNC(follow); // by MouseJstr +ATCOMMAND_FUNC(skillon); // by MouseJstr +ATCOMMAND_FUNC(skilloff); // by MouseJstr +ATCOMMAND_FUNC(killer); // by MouseJstr +ATCOMMAND_FUNC(npcmove); // by MouseJstr +ATCOMMAND_FUNC(killable); // by MouseJstr +ATCOMMAND_FUNC(charkillable); // by MouseJstr +ATCOMMAND_FUNC(chareffect); // by MouseJstr +ATCOMMAND_FUNC(chardye); // by MouseJstr +ATCOMMAND_FUNC(charhairstyle); // by MouseJstr +ATCOMMAND_FUNC(charhaircolor); // by MouseJstr +ATCOMMAND_FUNC(dropall); // by MouseJstr +ATCOMMAND_FUNC(chardropall); // by MouseJstr +ATCOMMAND_FUNC(storeall); // by MouseJstr +ATCOMMAND_FUNC(charstoreall); // by MouseJstr +ATCOMMAND_FUNC(skillid); // by MouseJstr +ATCOMMAND_FUNC(useskill); // by MouseJstr +ATCOMMAND_FUNC(summon); +ATCOMMAND_FUNC(rain); +ATCOMMAND_FUNC(snow); +ATCOMMAND_FUNC(sakura); +ATCOMMAND_FUNC(fog); +ATCOMMAND_FUNC(leaves); +ATCOMMAND_FUNC(adjgmlvl); // by MouseJstr +ATCOMMAND_FUNC(adjcmdlvl); // by MouseJstr +ATCOMMAND_FUNC(trade); // by MouseJstr +ATCOMMAND_FUNC(unmute); // [Valaris] + +#ifndef TXT_ONLY +ATCOMMAND_FUNC(checkmail); // [Valaris] +ATCOMMAND_FUNC(listmail); // [Valaris] +ATCOMMAND_FUNC(listnewmail); // [Valaris] +ATCOMMAND_FUNC(readmail); // [Valaris] +ATCOMMAND_FUNC(sendmail); // [Valaris] +ATCOMMAND_FUNC(sendprioritymail); // [Valaris] +ATCOMMAND_FUNC(deletemail); // [Valaris] +ATCOMMAND_FUNC(sound); // [Valaris] +ATCOMMAND_FUNC(refreshonline); // [Valaris] +#endif /* TXT_ONLY */ + +/*========================================== + *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_RuraP, "@rura+", 60, atcommand_rurap }, + { AtCommand_RuraP, "@charwarp", 60, atcommand_rurap }, + { 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_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_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_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_spawn }, + { AtCommand_Spawn, "@spawn", 50, atcommand_spawn }, + //{ AtCommand_Spawn, "@summon", 50, atcommand_spawn }, + { AtCommand_Monster, "@monster2", 50, atcommand_monster }, + { 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_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_CharPetRename, "@charpetrename", 50, atcommand_charpetrename }, // by Yor + { AtCommand_Recall, "@recall", 60, atcommand_recall }, // + /recall + { AtCommand_CharacterJob, "@charjob", 60, atcommand_character_job }, + { AtCommand_CharacterJob, "@charjobchange", 60, atcommand_character_job }, + { AtCommand_Revive, "@revive", 60, atcommand_revive }, + { AtCommand_CharacterStats, "@charstats", 40, atcommand_character_stats }, + { AtCommand_CharacterStatsAll, "@charstatsall", 40, atcommand_character_stats_all }, + { AtCommand_CharacterOption, "@charoption", 60, atcommand_character_option }, + { AtCommand_CharacterSave, "@charsave", 60, atcommand_character_save }, + { AtCommand_Night, "@night", 80, atcommand_night }, + { AtCommand_Day, "@day", 80, atcommand_day }, + { AtCommand_Doom, "@doom", 80, atcommand_doom }, + { AtCommand_DoomMap, "@doommap", 80, atcommand_doommap }, + { AtCommand_Raise, "@raise", 80, atcommand_raise }, + { AtCommand_RaiseMap, "@raisemap", 80, atcommand_raisemap }, + { AtCommand_CharacterBaseLevel, "@charbaselvl", 60, atcommand_character_baselevel }, + { AtCommand_CharacterJobLevel, "@charjlvl", 60, atcommand_character_joblevel }, + { AtCommand_Kick, "@kick", 20, atcommand_kick }, // + right click menu for GM "(name) force to quit" + { AtCommand_KickAll, "@kickall", 99, atcommand_kickall }, + { AtCommand_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_CharQuestSkill, "@charquestskill", 60, atcommand_charquestskill }, + { AtCommand_LostSkill, "@lostskill", 40, atcommand_lostskill }, + { AtCommand_CharLostSkill, "@charlostskill", 60, atcommand_charlostskill }, + { 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_CharSkReset, "@charskreset", 60, atcommand_charskreset }, + { AtCommand_CharStReset, "@charstreset", 60, atcommand_charstreset }, + { AtCommand_ReloadItemDB, "@reloaditemdb", 99, atcommand_reloaditemdb }, // admin command + { AtCommand_ReloadMobDB, "@reloadmobdb", 99, atcommand_reloadmobdb }, // admin command + { AtCommand_ReloadSkillDB, "@reloadskilldb", 99, atcommand_reloadskilldb }, // admin command +#ifndef TXT_ONLY + { AtCommand_Rehash, "@rehash", 99, atcommand_rehash }, // admin command +#else /* TXT_ONLY */ + { AtCommand_ReloadScript, "@reloadscript", 99, atcommand_reloadscript }, // admin command +#endif /* TXT_ONLY */ + { AtCommand_ReloadGMDB, "@reloadgmdb", 99, atcommand_reloadgmdb }, // admin command + { AtCommand_CharReset, "@charreset", 60, atcommand_charreset }, + { AtCommand_CharModel, "@charmodel", 50, atcommand_charmodel }, + { AtCommand_CharSKPoint, "@charskpoint", 60, atcommand_charskpoint }, + { AtCommand_CharSTPoint, "@charstpoint", 60, atcommand_charstpoint }, + { AtCommand_CharZeny, "@charzeny", 60, atcommand_charzeny }, + { AtCommand_MapInfo, "@mapinfo", 99, atcommand_mapinfo }, + { AtCommand_Dye, "@dye", 40, atcommand_dye }, // by fritz + { AtCommand_Dye, "@ccolor", 40, atcommand_dye }, // by fritz + { AtCommand_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_CharChangeSex, "@charchangesex", 60, atcommand_char_change_sex }, // by Yor + { 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_Enablenpc, "@enablenpc", 80, atcommand_enablenpc }, // [] + { AtCommand_Disablenpc, "@disablenpc", 80, atcommand_disablenpc }, // [] + { AtCommand_ServerTime, "@time", 0, atcommand_servertime }, // by Yor + { AtCommand_ServerTime, "@date", 0, atcommand_servertime }, // by Yor + { AtCommand_ServerTime, "@server_date", 0, atcommand_servertime }, // by Yor + { AtCommand_ServerTime, "@serverdate", 0, atcommand_servertime }, // by Yor + { AtCommand_ServerTime, "@server_time", 0, atcommand_servertime }, // by Yor + { AtCommand_ServerTime, "@servertime", 0, 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_IgnoreList, "@ignorelist", 0, atcommand_ignorelist }, // by Yor + { AtCommand_CharIgnoreList, "@charignorelist", 20, atcommand_charignorelist }, // by Yor + { AtCommand_IgnoreList, "@inall", 20, atcommand_inall }, // by Yor + { AtCommand_ExAll, "@exall", 20, atcommand_exall }, // by Yor + { AtCommand_CharDisguise, "@chardisguise", 60, atcommand_chardisguise }, // Kalaspuff + { AtCommand_CharUnDisguise, "@charundisguise", 60, atcommand_charundisguise }, // Kalaspuff + { AtCommand_EMail, "@email", 0, atcommand_email }, // by Yor + { AtCommand_Effect, "@effect", 40, atcommand_effect }, // by Apple + { AtCommand_Char_Item_List, "@charitemlist", 40, atcommand_character_item_list }, // by Yor + { AtCommand_Char_Storage_List, "@charstoragelist", 40, atcommand_character_storage_list }, // by Yor + { AtCommand_Char_Cart_List, "@charcartlist", 40, atcommand_character_cart_list }, // by Yor + { AtCommand_Follow, "@follow", 10, atcommand_follow }, // by MouseJstr + { AtCommand_AddWarp, "@addwarp", 20, atcommand_addwarp }, // by MouseJstr + { AtCommand_SkillOn, "@skillon", 20, atcommand_skillon }, // by MouseJstr + { AtCommand_SkillOff, "@skilloff", 20, atcommand_skilloff }, // by MouseJstr + { AtCommand_Killer, "@killer", 60, atcommand_killer }, // by MouseJstr + { AtCommand_NpcMove, "@npcmove", 20, atcommand_npcmove }, // by MouseJstr + { AtCommand_Killable, "@killable", 40, atcommand_killable }, // by MouseJstr + { AtCommand_CharKillable, "@charkillable", 40, atcommand_charkillable }, // by MouseJstr + { AtCommand_Chareffect, "@chareffect", 40, atcommand_chareffect }, // MouseJstr + //{ AtCommand_Chardye, "@chardye", 40, atcommand_chardye }, // MouseJstr + //{ AtCommand_Charhairstyle, "@charhairstyle", 40, atcommand_charhairstyle }, // MouseJstr + //{ AtCommand_Charhaircolor, "@charhaircolor", 40, atcommand_charhaircolor }, // MouseJstr + { AtCommand_Dropall, "@dropall", 40, atcommand_dropall }, // MouseJstr + { AtCommand_Chardropall, "@chardropall", 40, atcommand_chardropall }, // MouseJstr + { AtCommand_Storeall, "@storeall", 40, atcommand_storeall }, // MouseJstr + { AtCommand_Charstoreall, "@charstoreall", 40, atcommand_charstoreall }, // MouseJstr + { AtCommand_Skillid, "@skillid", 40, atcommand_skillid }, // MouseJstr + { AtCommand_Useskill, "@useskill", 40, atcommand_useskill }, // MouseJstr + { AtCommand_Rain, "@rain", 99, atcommand_rain }, + { AtCommand_Snow, "@snow", 99, atcommand_snow }, + { AtCommand_Sakura, "@sakura", 99, atcommand_sakura }, + { AtCommand_Fog, "@fog", 99, atcommand_fog }, + { AtCommand_Leaves, "@leaves", 99, atcommand_leaves }, + //{ AtCommand_Shuffle, "@shuffle", 99, atcommand_shuffle }, + //{ AtCommand_Maintenance, "@maintenance", 99, atcommand_maintenance }, + //{ AtCommand_Misceffect, "@misceffect", 60, atcommand_misceffect }, + { AtCommand_Summon, "@summon", 60, atcommand_summon }, + { AtCommand_AdjGmLvl, "@adjgmlvl", 99, atcommand_adjgmlvl }, + { AtCommand_AdjCmdLvl, "@adjcmdlvl", 99, atcommand_adjcmdlvl }, + { AtCommand_Trade, "@trade", 60, atcommand_trade }, + { AtCommand_UnMute, "@unmute", 60, atcommand_unmute }, // [Valaris] + +#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 */ + +// add new commands before this line + { AtCommand_Unknown, NULL, 1, NULL } +}; + +/*==================================================== + * 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"; +} + +//----------------------------------------------------------- +// Return the message string of the specified number by [Yor] +//----------------------------------------------------------- +char * msg_txt(int msg_number) { + if (msg_number >= 0 && msg_number < (int)(sizeof(msg_table) / sizeof(msg_table[0])) && + msg_table[msg_number] != NULL && msg_table[msg_number][0] != '\0') + return msg_table[msg_number]; + + return "??"; +} + +//------------------------------------------------------------ +// E-mail check: return 0 (not correct) or 1 (valid). by [Yor] +//------------------------------------------------------------ +int e_mail_check(unsigned char *email) { + char ch; + unsigned char* last_arobas; + + // athena limits + if (strlen(email) < 3 || strlen(email) > 39) + return 0; + + // part of RFC limits (official reference of e-mail description) + if (strchr(email, '@') == NULL || email[strlen(email)-1] == '@') + return 0; + + if (email[strlen(email)-1] == '.') + return 0; + + last_arobas = strrchr(email, '@'); + + if (strstr(last_arobas, "@.") != NULL || + strstr(last_arobas, "..") != NULL) + return 0; + + for(ch = 1; ch < 32; ch++) { + if (strchr(last_arobas, ch) != NULL) { + return 0; + break; + } + } + + if (strchr(last_arobas, ' ') != NULL || + strchr(last_arobas, ';') != NULL) + return 0; + + // all correct + return 1; +} + +/*========================================== + * get_atcommand_level @コマンドの必要レベルを取得 + *------------------------------------------ + */ +int get_atcommand_level(const AtCommandType type) { + int i; + + for (i = 0; atcommand_info[i].type != AtCommand_None; i++) + if (atcommand_info[i].type == type) + return atcommand_info[i].level; + + return 100; // 100: command can not be used +} + +/*========================================== + *is_atcommand @コマンドに存在するかどうか確認する + *------------------------------------------ + */ +AtCommandType +is_atcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl) { + const char* str = message; + int s_flag = 0; + AtCommandInfo info; + AtCommandType type; + + nullpo_retr(AtCommand_None, sd); + + if (!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(gmlvl > 0 ? gmlvl : pc_isGM(sd), str, &info); + if (type != AtCommand_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 AtCommand_Unknown; + strncpy(command, str, p - str); + while (isspace(*p)) + p++; + + if (type == AtCommand_Unknown || info.proc == NULL) { + sprintf(output, msg_table[153], command); // %s is Unknown Command. + clif_displaymessage(fd, output); + } else { + if (info.proc(fd, sd, command, p) != 0) { + // Command can not be executed + sprintf(output, msg_table[154], command); // %s failed. + clif_displaymessage(fd, output); + } + } + + return info.type; + } + + return AtCommand_None; +} + +/*========================================== + * + *------------------------------------------ + */ +AtCommandType atcommand(const int level, const char* message, struct AtCommandInfo* info) { + char* p = (char *)message; // it's 'char' and not 'const char' to have possibility to modify the first character if necessary + + if (!info) + return AtCommand_None; + if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd) + return AtCommand_None; + if (!p || !*p) { + fprintf(stderr, "at command message is empty\n"); + return AtCommand_None; + } + + if (*p == command_symbol) { // check first char. + char command[101]; + int i = 0; + memset(info, 0, sizeof(AtCommandInfo)); + sscanf(p, "%100s", command); + command[sizeof(command)-1] = '\0'; + + while (atcommand_info[i].type != AtCommand_Unknown) { + if (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; + } + memcpy(info, &atcommand_info[i], sizeof atcommand_info[i]); + } else { + return AtCommand_None; + } + + return info->type; +} + +/*========================================== + * + *------------------------------------------ + */ +static int atkillmonster_sub(struct block_list *bl, va_list ap) { + int flag = va_arg(ap, int); + + nullpo_retr(0, bl); + + if (flag) + mob_damage(NULL, (struct mob_data *)bl, ((struct mob_data *)bl)->hp, 2); + else + mob_delete((struct mob_data *)bl); + + return 0; +} + +#ifndef TXT_ONLY +static int atkillnpc_sub(struct block_list *bl, va_list ap) +{ + int flag = va_arg(ap,int); + + nullpo_retr(0, bl); + + npc_delete((struct npc_data *)bl); + + flag = 0; + + return 0; +} + +void rehash( const int fd, struct map_session_data* sd ) +{ + int map_id = 0; + + int LOADED_MAPS = map_num; + + for (map_id = 0; map_id < LOADED_MAPS;map_id++) { + + if (map_id > LOADED_MAPS) + break; + + map_foreachinarea(atkillmonster_sub, map_id, 0, 0, map[map_id].xs, map[map_id].ys, BL_MOB, 0); + map_foreachinarea(atkillnpc_sub, map_id, 0, 0, map[map_id].xs, map[map_id].ys, BL_NPC, 0); + } +} + +#endif /* not TXT_ONLY */ +/*========================================== + * Read Message Data + *------------------------------------------ + */ +int msg_config_read(const char *cfgName) { + int msg_number; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + if ((fp = fopen(cfgName, "r")) == NULL) { + printf("Messages file not found: %s\n", cfgName); + return 1; + } + + while(fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + if (strcmpi(w1, "import") == 0) { + msg_config_read(w2); + } else { + msg_number = atoi(w1); + if (msg_number >= 0 && msg_number < (int)(sizeof(msg_table) / sizeof(msg_table[0]))) + strcpy(msg_table[msg_number], w2); + // printf("message #%d: '%s'.\n", msg_number, msg_table[msg_number]); + } + } + } + fclose(fp); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static AtCommandInfo* get_atcommandinfo_byname(const char* name) { + int i; + + for (i = 0; atcommand_info[i].type != AtCommand_Unknown; i++) + if (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) { + printf("At commands configuration file not found: %s\n", cfgName); + return 1; + } + + while (fgets(line, sizeof(line)-1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + + if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2) + continue; + p = get_atcommandinfo_byname(w1); + if (p != NULL) { + p->level = atoi(w2); + if (p->level > 100) + p->level = 100; + else if (p->level < 0) + p->level = 0; + } + + if (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 + command_symbol = w2[0]; + } + fclose(fp); + + return 0; +} + +/*========================================== +// @ command processing functions + *------------------------------------------ + */ + +/*========================================== + * @rura+ + *------------------------------------------ + */ +int atcommand_rurap( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char map_name[100]; + char character[100]; + int x = 0, y = 0; + struct map_session_data *pl_sd; + int m; + + memset(map_name, '\0', sizeof(map_name)); + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99s %d %d %99[^\n]", map_name, &x, &y, character) < 4) { + clif_displaymessage(fd, "Usage: @charwarp/@rura+ <mapname> <x> <y> <char name>"); + return -1; + } + + if (x <= 0) + x = rand() % 399 + 1; + if (y <= 0) + y = rand() % 399 + 1; + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can rura+ only lower or same GM level + if (x > 0 && x < 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_name, x, y, 3) == 0) { + clif_displaymessage(pl_sd->fd, msg_table[0]); // Warped. + clif_displaymessage(fd, msg_table[15]); // Player warped (message sends to player too). + } else { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[2]); // Coordinates out of range. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +// @rura +/*========================================== + * + *------------------------------------------ + */ +int atcommand_rura( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char map_name[100]; + int x = 0, y = 0; + int m; + + memset(map_name, '\0', sizeof(map_name)); + + if (!message || !*message || sscanf(message, "%99s %d %d", map_name, &x, &y) < 1) { + clif_displaymessage(fd, "Please, enter a map (usage: @warp/@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) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + + 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 you to this map."); + return -1; + } + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you from your actual map."); + return -1; + } + if (pc_setpos(sd, map_name, x, y, 3) == 0) + clif_displaymessage(fd, msg_table[0]); // Warped. + else { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[2]); // Coordinates out of range. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_where( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (sscanf(message, "%99[^\n]", character) < 1) + strcpy(character, sd->status.name); + + if ((pl_sd = map_nick2sd(character)) != NULL && + !((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pc_isGM(pl_sd) > pc_isGM(sd)))) { // you can look only lower or same level + sprintf(output, "%s: %s (%d,%d)", pl_sd->status.name, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_jumpto( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @jumpto/@warpto/@goto <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you to the map of this player."); + return -1; + } + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you from your actual map."); + return -1; + } + pc_setpos(sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, 3); + sprintf(output, msg_table[4], character); // Jump to %s + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_jump( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + int x = 0, y = 0; + + memset(output, '\0', sizeof(output)); + + sscanf(message, "%d %d", &x, &y); + + if (x <= 0) + x = rand() % 399 + 1; + if (y <= 0) + y = rand() % 399 + 1; + if (x > 0 && x < 400 && y > 0 && y < 400) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you to your actual map."); + return -1; + } + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you from your actual map."); + return -1; + } + pc_setpos(sd, sd->mapname, x, y, 3); + sprintf(output, msg_table[5], x, y); // Jump to %d %d + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[2]); // Coordinates out of range. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_who( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + struct map_session_data *pl_sd; + int i, j, count; + int pl_GM_level, GM_level; + char match_text[100]; + char player_name[24]; + + memset(output, '\0', sizeof(output)); + memset(match_text, '\0', sizeof(match_text)); + memset(player_name, '\0', sizeof(player_name)); + + if (sscanf(message, "%99[^\n]", match_text) < 1) + strcpy(match_text, ""); + for (j = 0; match_text[j]; j++) + match_text[j] = tolower(match_text[j]); + + count = 0; + GM_level = pc_isGM(sd); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_GM_level = pc_isGM(pl_sd); + if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level + memcpy(player_name, pl_sd->status.name, 24); + for (j = 0; player_name[j]; j++) + player_name[j] = tolower(player_name[j]); + if (strstr(player_name, match_text) != NULL) { // search with no case sensitive + if (pl_GM_level > 0) + sprintf(output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + else + sprintf(output, "Name: %s | Location: %s %d %d", pl_sd->status.name, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage(fd, output); + count++; + } + } + } + } + + if (count == 0) + clif_displaymessage(fd, msg_table[28]); // No player found. + else if (count == 1) + clif_displaymessage(fd, msg_table[29]); // 1 player found. + else { + sprintf(output, msg_table[30], count); // %d players found. + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_who2( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + struct map_session_data *pl_sd; + int i, j, count; + int pl_GM_level, GM_level; + char match_text[100]; + char player_name[24]; + + memset(output, '\0', sizeof(output)); + memset(match_text, '\0', sizeof(match_text)); + memset(player_name, '\0', sizeof(player_name)); + + if (sscanf(message, "%99[^\n]", match_text) < 1) + strcpy(match_text, ""); + for (j = 0; match_text[j]; j++) + match_text[j] = tolower(match_text[j]); + + count = 0; + GM_level = pc_isGM(sd); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_GM_level = pc_isGM(pl_sd); + if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level + memcpy(player_name, pl_sd->status.name, 24); + for (j = 0; player_name[j]; j++) + player_name[j] = tolower(player_name[j]); + if (strstr(player_name, match_text) != NULL) { // search with no case sensitive + if (pl_GM_level > 0) + sprintf(output, "Name: %s (GM:%d) | 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(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, output); + count++; + } + } + } + } + + if (count == 0) + clif_displaymessage(fd, msg_table[28]); // No player found. + else if (count == 1) + clif_displaymessage(fd, msg_table[29]); // 1 player found. + else { + sprintf(output, msg_table[30], count); // %d players found. + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_who3( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char temp0[100]; + char temp1[100]; + char output[200]; + struct map_session_data *pl_sd; + int i, j, count; + int pl_GM_level, GM_level; + char match_text[100]; + char player_name[24]; + struct guild *g; + struct party *p; + + memset(temp0, '\0', sizeof(temp0)); + memset(temp1, '\0', sizeof(temp1)); + memset(output, '\0', sizeof(output)); + memset(match_text, '\0', sizeof(match_text)); + memset(player_name, '\0', sizeof(player_name)); + + if (sscanf(message, "%99[^\n]", match_text) < 1) + strcpy(match_text, ""); + for (j = 0; match_text[j]; j++) + match_text[j] = tolower(match_text[j]); + + count = 0; + GM_level = pc_isGM(sd); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_GM_level = pc_isGM(pl_sd); + if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level + memcpy(player_name, pl_sd->status.name, 24); + for (j = 0; player_name[j]; j++) + player_name[j] = tolower(player_name[j]); + if (strstr(player_name, match_text) != NULL) { // search with no case sensitive + g = guild_search(pl_sd->status.guild_id); + if (g == NULL) + sprintf(temp1, "None"); + else + sprintf(temp1, "%s", g->name); + p = party_search(pl_sd->status.party_id); + if (p == NULL) + sprintf(temp0, "None"); + else + sprintf(temp0, "%s", p->name); + if (pl_GM_level > 0) + sprintf(output, "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", pl_sd->status.name, pl_GM_level, temp0, temp1); + else + sprintf(output, "Name: %s | Party: '%s' | Guild: '%s'", pl_sd->status.name, temp0, temp1); + clif_displaymessage(fd, output); + count++; + } + } + } + } + + if (count == 0) + clif_displaymessage(fd, msg_table[28]); // No player found. + else if (count == 1) + clif_displaymessage(fd, msg_table[29]); // 1 player found. + else { + sprintf(output, msg_table[30], count); // %d players found. + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_whomap( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + struct map_session_data *pl_sd; + int i, count; + int pl_GM_level, GM_level; + int map_id; + char map_name[100]; + + memset(output, '\0', sizeof(output)); + memset(map_name, '\0', sizeof(map_name)); + + if (!message || !*message) + map_id = sd->bl.m; + else { + sscanf(message, "%99s", map_name); + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + if ((map_id = map_mapname2mapid(map_name)) < 0) + map_id = sd->bl.m; + } + + count = 0; + GM_level = pc_isGM(sd); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_GM_level = pc_isGM(pl_sd); + if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level + if (pl_sd->bl.m == map_id) { + if (pl_GM_level > 0) + sprintf(output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + else + sprintf(output, "Name: %s | Location: %s %d %d", pl_sd->status.name, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage(fd, output); + count++; + } + } + } + } + + if (count == 0) + sprintf(output, msg_table[54], map[map_id].name); // No player found in map '%s'. + else if (count == 1) + sprintf(output, msg_table[55], map[map_id].name); // 1 player found in map '%s'. + else { + sprintf(output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'. + } + clif_displaymessage(fd, output); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_whomap2( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + struct map_session_data *pl_sd; + int i, count; + int pl_GM_level, GM_level; + int map_id = 0; + char map_name[100]; + + memset(output, '\0', sizeof(output)); + memset(map_name, '\0', sizeof(map_name)); + + if (!message || !*message) + map_id = sd->bl.m; + else { + sscanf(message, "%99s", map_name); + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + if ((map_id = map_mapname2mapid(map_name)) < 0) + map_id = sd->bl.m; + } + + count = 0; + GM_level = pc_isGM(sd); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_GM_level = pc_isGM(pl_sd); + if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level + if (pl_sd->bl.m == map_id) { + if (pl_GM_level > 0) + sprintf(output, "Name: %s (GM:%d) | 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(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, output); + count++; + } + } + } + } + + if (count == 0) + sprintf(output, msg_table[54], map[map_id].name); // No player found in map '%s'. + else if (count == 1) + sprintf(output, msg_table[55], map[map_id].name); // 1 player found in map '%s'. + else { + sprintf(output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'. + } + clif_displaymessage(fd, output); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_whomap3( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char temp0[100]; + char temp1[100]; + char output[200]; + struct map_session_data *pl_sd; + int i, count; + int pl_GM_level, GM_level; + int map_id = 0; + char map_name[100]; + struct guild *g; + struct party *p; + + memset(temp0, '\0', sizeof(temp0)); + memset(temp1, '\0', sizeof(temp1)); + memset(output, '\0', sizeof(output)); + memset(map_name, '\0', sizeof(map_name)); + + if (!message || !*message) + map_id = sd->bl.m; + else { + sscanf(message, "%99s", map_name); + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + if ((map_id = map_mapname2mapid(map_name)) < 0) + map_id = sd->bl.m; + } + + count = 0; + GM_level = pc_isGM(sd); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_GM_level = pc_isGM(pl_sd); + if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level + if (pl_sd->bl.m == map_id) { + g = guild_search(pl_sd->status.guild_id); + if (g == NULL) + sprintf(temp1, "None"); + else + sprintf(temp1, "%s", g->name); + p = party_search(pl_sd->status.party_id); + if (p == NULL) + sprintf(temp0, "None"); + else + sprintf(temp0, "%s", p->name); + if (pl_GM_level > 0) + sprintf(output, "Name: %s (GM:%d) | Party: '%s' | Guild: '%s'", pl_sd->status.name, pl_GM_level, temp0, temp1); + else + sprintf(output, "Name: %s | Party: '%s' | Guild: '%s'", pl_sd->status.name, temp0, temp1); + clif_displaymessage(fd, output); + count++; + } + } + } + } + + if (count == 0) + sprintf(output, msg_table[54], map[map_id].name); // No player found in map '%s'. + else if (count == 1) + sprintf(output, msg_table[55], map[map_id].name); // 1 player found in map '%s'. + else { + sprintf(output, msg_table[56], count, map[map_id].name); // %d players found in map '%s'. + } + clif_displaymessage(fd, output); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_whogm( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char temp0[100]; + char temp1[100]; + char output[200]; + struct map_session_data *pl_sd; + int i, j, count; + int pl_GM_level, GM_level; + char match_text[100]; + char player_name[24]; + struct guild *g; + struct party *p; + + memset(temp0, '\0', sizeof(temp0)); + memset(temp1, '\0', sizeof(temp1)); + memset(output, '\0', sizeof(output)); + memset(match_text, '\0', sizeof(match_text)); + memset(player_name, '\0', sizeof(player_name)); + + if (sscanf(message, "%99[^\n]", match_text) < 1) + strcpy(match_text, ""); + for (j = 0; match_text[j]; j++) + match_text[j] = tolower(match_text[j]); + + count = 0; + GM_level = pc_isGM(sd); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_GM_level = pc_isGM(pl_sd); + if (pl_GM_level > 0) { + if (!((battle_config.hide_GM_session || (pl_sd->status.option & OPTION_HIDE)) && (pl_GM_level > GM_level))) { // you can look only lower or same level + memcpy(player_name, pl_sd->status.name, 24); + for (j = 0; player_name[j]; j++) + player_name[j] = tolower(player_name[j]); + if (strstr(player_name, match_text) != NULL) { // search with no case sensitive + sprintf(output, "Name: %s (GM:%d) | Location: %s %d %d", pl_sd->status.name, pl_GM_level, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage(fd, output); + sprintf(output, " BLvl: %d | Job: %s (Lvl: %d)", pl_sd->status.base_level, job_name(pl_sd->status.class), pl_sd->status.job_level); + clif_displaymessage(fd, output); + g = guild_search(pl_sd->status.guild_id); + if (g == NULL) + sprintf(temp1, "None"); + else + sprintf(temp1, "%s", g->name); + p = party_search(pl_sd->status.party_id); + if (p == NULL) + sprintf(temp0, "None"); + else + sprintf(temp0, "%s", p->name); + sprintf(output, " Party: '%s' | Guild: '%s'", temp0, temp1); + clif_displaymessage(fd, output); + count++; + } + } + } + } + } + + if (count == 0) + clif_displaymessage(fd, msg_table[150]); // No GM found. + else if (count == 1) + clif_displaymessage(fd, msg_table[151]); // 1 GM found. + else { + sprintf(output, msg_table[152], count); // %d GMs found. + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_save( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + pc_setsavepoint(sd, sd->mapname, sd->bl.x, sd->bl.y); + if (sd->status.pet_id > 0 && sd->pd) + intif_save_petdata(sd->status.account_id, &sd->pet); + pc_makesavestatus(sd); + chrif_save(sd); + storage_storage_save(sd); + clif_displaymessage(fd, msg_table[6]); // Character data respawn point saved. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_load( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int m; + + m = map_mapname2mapid(sd->status.save_point.map); + if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you to your save map."); + return -1; + } + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you from your actual map."); + return -1; + } + + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 0); + clif_displaymessage(fd, msg_table[7]); // Warping to respawn point. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_speed( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + int speed; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message) { + sprintf(output, "Please, enter a speed value (usage: @speed <%d-%d>).", MIN_WALK_SPEED, MAX_WALK_SPEED); + clif_displaymessage(fd, output); + return -1; + } + + speed = atoi(message); + if (speed >= MIN_WALK_SPEED && speed <= MAX_WALK_SPEED) { + sd->speed = speed; + //sd->walktimer = x; + //この文を追加 by れ + clif_updatestatus(sd, SP_SPEED); + clif_displaymessage(fd, msg_table[8]); // Speed changed. + } else { + sprintf(output, "Please, enter a valid speed value (usage: @speed <%d-%d>).", MIN_WALK_SPEED, MAX_WALK_SPEED); + clif_displaymessage(fd, output); + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_storage( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + storage_storageopen(sd); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_guildstorage( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (sd->status.guild_id > 0) + storage_guild_storageopen(sd); + + 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; + + if (!message || !*message || sscanf(message, "%d %d %d", ¶m1, ¶m2, ¶m3) < 1 || param1 < 0 || param2 < 0 || param3 < 0) { + clif_displaymessage(fd, "Please, enter at least a option (usage: @option <param1:0+> <param2:0+> <param3:0+>)."); + return -1; + } + + sd->opt1 = param1; + sd->opt2 = param2; + if (!(sd->status.option & CART_MASK) && param3 & CART_MASK) { + clif_cart_itemlist(sd); + clif_cart_equiplist(sd); + clif_updatestatus(sd, SP_CARTINFO); + } + sd->status.option = param3; + // fix pecopeco display + if (sd->status.class == 13 || sd->status.class == 21 || sd->status.class == 4014 || sd->status.class == 4022) { + if (!pc_isriding(sd)) { // sd have the new value... + if (sd->status.class == 13) + sd->status.class = sd->view_class = 7; + else if (sd->status.class == 21) + sd->status.class = sd->view_class = 14; + else if (sd->status.class == 4014) + sd->status.class = sd->view_class = 4008; + else if (sd->status.class == 4022) + sd->status.class = sd->view_class = 4015; + } + } else { + if (pc_isriding(sd)) { // sd have the new value... + if (sd->disguise > 0) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] (code added by [Yor]) + sd->status.option &= ~0x0020; + } else { + if (sd->status.class == 7) + sd->status.class = sd->view_class = 13; + else if (sd->status.class == 14) + sd->status.class = sd->view_class = 21; + else if (sd->status.class == 4008) + sd->status.class = sd->view_class = 4014; + else if (sd->status.class == 4015) + sd->status.class = sd->view_class = 4022; + else + sd->status.option &= ~0x0020; + } + } + } + + clif_changeoption(&sd->bl); + pc_calcstatus(sd, 0); + clif_displaymessage(fd, msg_table[9]); // Options changed. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_hide( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (sd->status.option & OPTION_HIDE) { + sd->status.option &= ~OPTION_HIDE; + clif_displaymessage(fd, msg_table[10]); // Invisible: Off + } else { + sd->status.option |= OPTION_HIDE; + clif_displaymessage(fd, msg_table[11]); // Invisible: On + } + clif_changeoption(&sd->bl); + + return 0; +} + +/*========================================== + * 転職する upperを指定すると転生や養子にもなれる + *------------------------------------------ + */ +int atcommand_jobchange( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int job = 0, upper = -1; + + if (!message || !*message || sscanf(message, "%d %d", &job, &upper) < 1) { + 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)) { + + // fix pecopeco display + if ((job != 13 && job != 21 && job != 4014 && job != 4022)) { + if (pc_isriding(sd)) { + if (sd->status.class == 13) + sd->status.class = sd->view_class = 7; + if (sd->status.class == 21) + sd->status.class = sd->view_class = 14; + if (sd->status.class == 4014) + sd->status.class = sd->view_class = 4008; + if (sd->status.class == 4022) + sd->status.class = sd->view_class = 4015; + sd->status.option &= ~0x0020; + clif_changeoption(&sd->bl); + pc_calcstatus(sd, 0); + } + } else { + if (!pc_isriding(sd)) { + if (job == 13) + job = 7; + if (job == 21) + job = 14; + if (job == 4014) + job = 4008; + if (job == 4022) + job = 4015; + } + } + + 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) +{ + pc_damage(NULL, sd, sd->status.hp + 1); + clif_displaymessage(fd, msg_table[13]); // A pity! You've died. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_kill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @kill <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level + pc_damage(NULL, pl_sd, pl_sd->status.hp + 1); + clif_displaymessage(fd, msg_table[14]); // Character killed. + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_alive( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + sd->status.hp = sd->status.max_hp; + sd->status.sp = sd->status.max_sp; + pc_setstand(sd); + if (battle_config.pc_invincible_time > 0) + pc_setinvincibletimer(sd, battle_config.pc_invincible_time); + clif_updatestatus(sd, SP_HP); + clif_updatestatus(sd, SP_SP); + clif_resurrection(&sd->bl, 1); + clif_displaymessage(fd, msg_table[16]); // You've been revived! It's a miracle! + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_kami( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message) { + clif_displaymessage(fd, "Please, enter a message (usage: @kami <message>)."); + return -1; + } + + sscanf(message, "%199[^\n]", output); + intif_GMmessage(output, strlen(output) + 1, (*(command + 5) == 'b') ? 0x10 : 0); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_heal( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int hp = 0, sp = 0; // [Valaris] thanks to fov + + sscanf(message, "%d %d", &hp, &sp); + + if (hp == 0 && sp == 0) { + hp = sd->status.max_hp - sd->status.hp; + sp = sd->status.max_sp - sd->status.sp; + } else { + if (hp > 0 && (hp > sd->status.max_hp || hp > (sd->status.max_hp - sd->status.hp))) // fix positiv overflow + hp = sd->status.max_hp - sd->status.hp; + else if (hp < 0 && (hp < -sd->status.max_hp || hp < (1 - sd->status.hp))) // fix negativ overflow + hp = 1 - sd->status.hp; + if (sp > 0 && (sp > sd->status.max_sp || sp > (sd->status.max_sp - sd->status.sp))) // fix positiv overflow + sp = sd->status.max_sp - sd->status.sp; + else if (sp < 0 && (sp < -sd->status.max_sp || sp < (1 - sd->status.sp))) // fix negativ overflow + sp = 1 - sd->status.sp; + } + + if (hp > 0) // display like heal + clif_heal(fd, SP_HP, hp); + else if (hp < 0) // display like damage + clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0 , 4, 0); + if (sp > 0) // no display when we lost SP + clif_heal(fd, SP_SP, sp); + + if (hp != 0 || sp != 0) { + pc_heal(sd, hp, sp); + if (hp >= 0 && sp >= 0) + clif_displaymessage(fd, msg_table[17]); // HP, SP recovered. + else + clif_displaymessage(fd, msg_table[156]); // HP or/and SP modified. + } else { + clif_displaymessage(fd, msg_table[157]); // HP and SP are already with the good value. + return -1; + } + + return 0; +} + +/*========================================== + * @item command (usage: @item <name/id_of_item> <quantity>) (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; + + 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, + pet_db[pet_id].class, mob_db[pet_db[pet_id].class].lv, + pet_db[pet_id].EggID, 0, 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); + } + } + 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; + + 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); + } + clif_displaymessage(fd, msg_table[18]); // Item created. + } else { + clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_itemreset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + + for (i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].amount && sd->status.inventory[i].equip == 0) + pc_delitem(sd, i, sd->status.inventory[i].amount, 0); + } + clif_displaymessage(fd, msg_table[20]); // All of your items have been removed. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_itemcheck( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + pc_checkitem(sd); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_baselevelup( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int level, i; + + if (!message || !*message || (level = atoi(message)) == 0) { + clif_displaymessage(fd, "Please, enter a level adjustement (usage: @lvup/@blevel/@baselvlup <number of levels>)."); + return -1; + } + + if (level > 0) { + if (sd->status.base_level == battle_config.maximum_level) { // check for max level by Valaris + clif_displaymessage(fd, msg_table[47]); // Base level can't go any higher. + return -1; + } // End Addition + if (level > battle_config.maximum_level || level > (battle_config.maximum_level - sd->status.base_level)) // fix positiv overflow + level = battle_config.maximum_level - sd->status.base_level; + for (i = 1; i <= level; i++) + sd->status.status_point += (sd->status.base_level + i + 14) / 5; + sd->status.base_level += level; + clif_updatestatus(sd, SP_BASELEVEL); + clif_updatestatus(sd, SP_NEXTBASEEXP); + clif_updatestatus(sd, SP_STATUSPOINT); + pc_calcstatus(sd, 0); + pc_heal(sd, sd->status.max_hp, sd->status.max_sp); + clif_misceffect(&sd->bl, 0); + clif_displaymessage(fd, msg_table[21]); // Base level raised. + } else { + if (sd->status.base_level == 1) { + clif_displaymessage(fd, msg_table[158]); // Base level can't go any lower. + return -1; + } + if (level < -battle_config.maximum_level || level < (1 - sd->status.base_level)) // fix negativ overflow + level = 1 - sd->status.base_level; + if (sd->status.status_point > 0) { + for (i = 0; i > level; i--) + sd->status.status_point -= (sd->status.base_level + i + 14) / 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_calcstatus(sd, 0); + clif_displaymessage(fd, msg_table[22]); // Base level lowered. + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_joblevelup( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int up_level = 50, level; + + if (!message || !*message || (level = atoi(message)) == 0) { + clif_displaymessage(fd, "Please, enter a level adjustement (usage: @joblvup/@jlevel/@joblvlup <number of levels>)."); + return -1; + } + + if (sd->status.class == 0 || sd->status.class == 4001) + up_level -= 40; + else if ((sd->status.class > 4007 && sd->status.class < 4024) || sd->status.class == 23) + up_level += 20; + + if (level > 0) { + if (sd->status.job_level == up_level) { + clif_displaymessage(fd, msg_table[23]); // Job level can't go any higher. + return -1; + } + if (level > up_level || level > (up_level - sd->status.job_level)) // fix positiv overflow + level = up_level - sd->status.job_level; + sd->status.job_level += level; + clif_updatestatus(sd, SP_JOBLEVEL); + clif_updatestatus(sd, SP_NEXTJOBEXP); + sd->status.skill_point += level; + clif_updatestatus(sd, SP_SKILLPOINT); + pc_calcstatus(sd, 0); + clif_misceffect(&sd->bl, 1); + clif_displaymessage(fd, msg_table[24]); // Job level raised. + } else { + if (sd->status.job_level == 1) { + clif_displaymessage(fd, msg_table[159]); // Job level can't go any lower. + return -1; + } + if (level < -up_level || level < (1 - sd->status.job_level)) // fix negativ overflow + level = 1 - sd->status.job_level; + sd->status.job_level += level; + clif_updatestatus(sd, SP_JOBLEVEL); + clif_updatestatus(sd, SP_NEXTJOBEXP); + if (sd->status.skill_point > 0) { + sd->status.skill_point += level; + if (sd->status.skill_point < 0) + sd->status.skill_point = 0; + clif_updatestatus(sd, SP_SKILLPOINT); + } // to add: remove status points from skills + pc_calcstatus(sd, 0); + clif_displaymessage(fd, msg_table[25]); // Job level lowered. + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_help( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char buf[2048], w1[2048], w2[2048]; + int i, gm_level; + FILE* fp; + + memset(buf, '\0', sizeof(buf)); + + if ((fp = fopen(help_txt, "r")) != NULL) { + clif_displaymessage(fd, msg_table[26]); // Help commands: + gm_level = pc_isGM(sd); + while(fgets(buf, sizeof(buf) - 1, fp) != NULL) { + if (buf[0] == '/' && buf[1] == '/') + continue; + for (i = 0; buf[i] != '\0'; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = '\0'; + break; + } + } + if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2) + clif_displaymessage(fd, buf); + else if (gm_level >= atoi(w1)) + clif_displaymessage(fd, w2); + } + fclose(fp); + } else { + clif_displaymessage(fd, msg_table[27]); // File help.txt not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_gm( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char password[100]; + + memset(password, '\0', sizeof(password)); + + if (!message || !*message || sscanf(message, "%99[^\n]", password) < 1) { + clif_displaymessage(fd, "Please, enter a password (usage: @gm <password>)."); + return -1; + } + + if (pc_isGM(sd)) { // a GM can not use this function. only a normal player (become gm is not for gm!) + clif_displaymessage(fd, msg_table[50]); // You already have some GM powers. + return -1; + } else + chrif_changegm(sd->status.account_id, password, strlen(password) + 1); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_pvpoff( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris] + clif_displaymessage(fd, msg_table[52]); // This option cannot be used in PK Mode. + return -1; + } + + if (map[sd->bl.m].flag.pvp) { + map[sd->bl.m].flag.pvp = 0; + clif_send0199(sd->bl.m, 0); + for (i = 0; i < fd_max; i++) { //人数分ループ + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + if (sd->bl.m == pl_sd->bl.m) { + clif_pvpset(pl_sd, 0, 0, 2); + if (pl_sd->pvp_timer != -1) { + delete_timer(pl_sd->pvp_timer, pc_calc_pvprank_timer); + pl_sd->pvp_timer = -1; + } + } + } + } + clif_displaymessage(fd, msg_table[31]); // PvP: Off. + } else { + clif_displaymessage(fd, msg_table[160]); // PvP is already Off. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_pvpon( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + if (battle_config.pk_mode) { //disable command if server is in PK mode [Valaris] + clif_displaymessage(fd, msg_table[52]); // This option cannot be used in PK Mode. + return -1; + } + + if (!map[sd->bl.m].flag.pvp && !map[sd->bl.m].flag.nopvp) { + map[sd->bl.m].flag.pvp = 1; + clif_send0199(sd->bl.m, 1); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + if (sd->bl.m == pl_sd->bl.m && pl_sd->pvp_timer == -1) { + pl_sd->pvp_timer = add_timer(gettick() + 200, + pc_calc_pvprank_timer, pl_sd->bl.id, 0); + pl_sd->pvp_rank = 0; + pl_sd->pvp_lastusers = 0; + pl_sd->pvp_point = 5; + } + } + } + clif_displaymessage(fd, msg_table[32]); // PvP: On. + } else { + clif_displaymessage(fd, msg_table[161]); // PvP is already On. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_gvgoff( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (map[sd->bl.m].flag.gvg) { + map[sd->bl.m].flag.gvg = 0; + clif_send0199(sd->bl.m, 0); + clif_displaymessage(fd, msg_table[33]); // GvG: Off. + } else { + clif_displaymessage(fd, msg_table[162]); // GvG is already Off. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_gvgon( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (!map[sd->bl.m].flag.gvg) { + map[sd->bl.m].flag.gvg = 1; + clif_send0199(sd->bl.m, 3); + clif_displaymessage(fd, msg_table[34]); // GvG: On. + } else { + clif_displaymessage(fd, msg_table[163]); // GvG is already On. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_model( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int hair_style = 0, hair_color = 0, cloth_color = 0; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d %d %d", &hair_style, &hair_color, &cloth_color) < 1) { + sprintf(output, "Please, enter at least a value (usage: @model <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d>).", + MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); + clif_displaymessage(fd, output); + return -1; + } + + if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE && + hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR && + cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) { + //服の色変更 + if (cloth_color != 0 && sd->status.sex == 1 && (sd->status.class == 12 || sd->status.class == 17)) { + //服の色未実装職の判定 + clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class. + return -1; + } else { + pc_changelook(sd, LOOK_HAIR, hair_style); + pc_changelook(sd, LOOK_HAIR_COLOR, hair_color); + pc_changelook(sd, LOOK_CLOTHES_COLOR, cloth_color); + clif_displaymessage(fd, msg_table[36]); // Appearence changed. + } + } else { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + + return 0; +} + +/*========================================== + * @dye && @ccolor + *------------------------------------------ + */ +int atcommand_dye(const int fd, struct map_session_data* sd, const char* command, const char* message) +{ + int cloth_color = 0; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d", &cloth_color) < 1) { + sprintf(output, "Please, enter a clothes color (usage: @dye/@ccolor <clothes color: %d-%d>).", MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); + clif_displaymessage(fd, output); + return -1; + } + + if (cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) { + if (cloth_color != 0 && sd->status.sex == 1 && (sd->status.class == 12 || sd->status.class == 17)) { + clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class. + return -1; + } else { + pc_changelook(sd, LOOK_CLOTHES_COLOR, cloth_color); + clif_displaymessage(fd, msg_table[36]); // Appearence changed. + } + } else { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + + return 0; +} + +/*========================================== + * @chardye by [MouseJstr] + *------------------------------------------ + */ +int +atcommand_chardye(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + return 0; +} + +/*========================================== + * @hairstyle && @hstyle + *------------------------------------------ + */ +int atcommand_hair_style(const int fd, struct map_session_data* sd, const char* command, const char* message) +{ + int hair_style = 0; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d", &hair_style) < 1) { + sprintf(output, "Please, enter a hair style (usage: @hairstyle/@hstyle <hair ID: %d-%d>).", MIN_HAIR_STYLE, MAX_HAIR_STYLE); + clif_displaymessage(fd, output); + return -1; + } + + if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE) { + if (hair_style != 0 && sd->status.sex == 1 && (sd->status.class == 12 || sd->status.class == 17)) { + clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class. + return -1; + } else { + pc_changelook(sd, LOOK_HAIR, hair_style); + clif_displaymessage(fd, msg_table[36]); // Appearence changed. + } + } else { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + + return 0; +} + +/*========================================== + * @charhairstyle by [MouseJstr] + *------------------------------------------ + */ +int +atcommand_charhairstyle(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + return 0; +} + +/*========================================== + * @haircolor && @hcolor + *------------------------------------------ + */ +int atcommand_hair_color(const int fd, struct map_session_data* sd, const char* command, const char* message) +{ + int hair_color = 0; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d", &hair_color) < 1) { + sprintf(output, "Please, enter a hair color (usage: @haircolor/@hcolor <hair color: %d-%d>).", MIN_HAIR_COLOR, MAX_HAIR_COLOR); + clif_displaymessage(fd, output); + return -1; + } + + if (hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR) { + if (hair_color != 0 && sd->status.sex == 1 && (sd->status.class == 12 || sd->status.class == 17)) { + clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class. + return -1; + } else { + pc_changelook(sd, LOOK_HAIR_COLOR, hair_color); + clif_displaymessage(fd, msg_table[36]); // Appearence changed. + } + } else { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + + return 0; +} + +/*========================================== + * @charhaircolor by [MouseJstr] + *------------------------------------------ + */ +int +atcommand_charhaircolor(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + return 0; +} + +/*========================================== + * @go [city_number/city_name]: improved by [yor] to add city names and help + *------------------------------------------ + */ +int atcommand_go( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + int town; + char map_name[100]; + char output[200]; + int m; + + struct { char map[16]; int x, y; } data[] = { + { "prontera.gat", 156, 191 }, // 0=Prontera + { "morocc.gat", 156, 93 }, // 1=Morroc + { "geffen.gat", 119, 59 }, // 2=Geffen + { "payon.gat", 162, 233 }, // 3=Payon + { "alberta.gat", 192, 147 }, // 4=Alberta + { "izlude.gat", 128, 114 }, // 5=Izlude + { "aldebaran.gat",140, 131 }, // 6=Al de Baran + { "xmas.gat", 147, 134 }, // 7=Lutie + { "comodo.gat", 209, 143 }, // 8=Comodo + { "yuno.gat", 157, 51 }, // 9=Yuno + { "amatsu.gat", 198, 84 }, // 10=Amatsu + { "gonryun.gat", 160, 120 }, // 11=Gon Ryun + { "umbala.gat", 89, 157 }, // 12=Umbala + { "niflheim.gat", 21, 153 }, // 13=Niflheim + { "louyang.gat", 217, 40 }, // 14=Lou Yang + { "new_1-1.gat", 53, 111 }, // 15=Start point + { "sec_pri.gat", 23, 61 }, // 16=Prison + }; + + memset(map_name, '\0', sizeof(map_name)); + memset(output, '\0', sizeof(output)); + + // get the number + town = atoi(message); + + // if no value, display all value + if (!message || !*message || sscanf(message, "%99s", map_name) < 1 || town < -3 || town >= (int)(sizeof(data) / sizeof(data[0]))) { + clif_displaymessage(fd, msg_table[38]); // Invalid location number or name. + clif_displaymessage(fd, msg_table[82]); // Please, use one of this number/name: + clif_displaymessage(fd, "-3=(Memo point 2) 4=Alberta 11=Gon Ryun"); + clif_displaymessage(fd, "-2=(Memo point 1) 5=Izlude 12=Umbala"); + clif_displaymessage(fd, "-1=(Memo point 0) 6=Al de Baran 13=Niflheim"); + clif_displaymessage(fd, " 0=Prontera 7=Lutie 14=Lou Yang"); + clif_displaymessage(fd, " 1=Morroc 8=Comodo 15=Start point"); + clif_displaymessage(fd, " 2=Geffen 9=Yuno 16=Prison"); + clif_displaymessage(fd, " 3=Payon 10=Amatsu"); + return -1; + } else { + // get possible name of the city and add .gat if not in the name + map_name[sizeof(map_name)-1] = '\0'; + for (i = 0; map_name[i]; i++) + map_name[i] = tolower(map_name[i]); + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + // try to see if it's a name, and not a number (try a lot of possibilities, write errors and abbreviations too) + if (strncmp(map_name, "prontera.gat", 3) == 0) { // 3 first characters + town = 0; + } else if (strncmp(map_name, "morocc.gat", 3) == 0) { // 3 first characters + town = 1; + } else if (strncmp(map_name, "geffen.gat", 3) == 0) { // 3 first characters + town = 2; + } else if (strncmp(map_name, "payon.gat", 3) == 0 || // 3 first characters + strncmp(map_name, "paion.gat", 3) == 0) { // writing error (3 first characters) + town = 3; + } else if (strncmp(map_name, "alberta.gat", 3) == 0) { // 3 first characters + town = 4; + } else if (strncmp(map_name, "izlude.gat", 3) == 0 || // 3 first characters + strncmp(map_name, "islude.gat", 3) == 0) { // writing error (3 first characters) + town = 5; + } else if (strncmp(map_name, "aldebaran.gat", 3) == 0 || // 3 first characters + strcmp(map_name, "al.gat") == 0) { // al (de baran) + town = 6; + } else if (strncmp(map_name, "lutie.gat", 3) == 0 || // name of the city, not name of the map (3 first characters) + strcmp(map_name, "christmas.gat") == 0 || // name of the symbol + strncmp(map_name, "xmas.gat", 3) == 0 || // 3 first characters + strncmp(map_name, "x-mas.gat", 3) == 0) { // writing error (3 first characters) + town = 7; + } else if (strncmp(map_name, "comodo.gat", 3) == 0) { // 3 first characters + town = 8; + } else if (strncmp(map_name, "yuno.gat", 3) == 0) { // 3 first characters + town = 9; + } else if (strncmp(map_name, "amatsu.gat", 3) == 0 || // 3 first characters + strncmp(map_name, "ammatsu.gat", 3) == 0) { // writing error (3 first characters) + town = 10; + } else if (strncmp(map_name, "gonryun.gat", 3) == 0) { // 3 first characters + town = 11; + } else if (strncmp(map_name, "umbala.gat", 3) == 0) { // 3 first characters + town = 12; + } else if (strncmp(map_name, "niflheim.gat", 3) == 0) { // 3 first characters + town = 13; + } else if (strncmp(map_name, "louyang.gat", 3) == 0) { // 3 first characters + town = 14; + } else if (strncmp(map_name, "new_1-1.gat", 3) == 0 || // 3 first characters (or "newbies") + strncmp(map_name, "startpoint.gat", 3) == 0 || // name of the position (3 first characters) + strncmp(map_name, "begining.gat", 3) == 0) { // name of the position (3 first characters) + town = 15; + } else if (strncmp(map_name, "sec_pri.gat", 3) == 0 || // 3 first characters + strncmp(map_name, "prison.gat", 3) == 0 || // name of the position (3 first characters) + strncmp(map_name, "jails.gat", 3) == 0) { // name of the position + town = 16; + } + + if (town >= -3 && town <= -1) { + if (sd->status.memo_point[-town-1].map[0]) { + m = map_mapname2mapid(sd->status.memo_point[-town-1].map); + if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you to this memo map."); + return -1; + } + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you from your actual map."); + return -1; + } + if (pc_setpos(sd, sd->status.memo_point[-town-1].map, sd->status.memo_point[-town-1].x, sd->status.memo_point[-town-1].y, 3) == 0) { + clif_displaymessage(fd, msg_table[0]); // Warped. + } else { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + } else { + sprintf(output, msg_table[164], -town-1); // Your memo point #%d doesn't exist. + clif_displaymessage(fd, output); + return -1; + } + } else if (town >= 0 && town < (int)(sizeof(data) / sizeof(data[0]))) { + m = map_mapname2mapid(data[town].map); + if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you to this destination map."); + return -1; + } + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp you from your actual map."); + return -1; + } + if (pc_setpos(sd, data[town].map, data[town].x, data[town].y, 3) == 0) { + clif_displaymessage(fd, msg_table[0]); // Warped. + } else { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + } else { // if you arrive here, you have an error in town variable when reading of names + clif_displaymessage(fd, msg_table[38]); // Invalid location number or name. + return -1; + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_monster( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char name[100]; + char monster[100]; + char output[200]; + int mob_id; + int number = 0; + int x = 0, y = 0; + int count; + int i, j, k; + int mx, my, range; + + memset(name, '\0', sizeof(name)); + memset(monster, '\0', sizeof(monster)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || + (sscanf(message, "\"%[^\"]\" %99s %d %d %d", name, monster, &number, &x, &y) < 2 && + sscanf(message, "%99s \"%[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 && + sscanf(message, "%99s %99s %d %d %d", name, monster, &number, &x, &y) < 2)) { + 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 == 1288) { + 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) + printf("%s monster='%s' name='%s' id=%d count=%d (%d,%d)\n", command, monster, name, mob_id, number, x, y); + + count = 0; + range = sqrt(number) / 2; + range = range * 2 + 5; // calculation of an odd number (+ 4 area around) + for (i = 0; i < number; i++) { + j = 0; + k = 0; + while(j++ < 8 && k == 0) { // try 8 times to spawn the monster (needed for close area) + if (x <= 0) + mx = sd->bl.x + (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(output, msg_table[240], count); // %d monster(s) summoned! + clif_displaymessage(fd, output); + } + else { + clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_spawn( + const int fd, struct map_session_data* sd, + const char* command, const char* message) { + char name[100]; + char monster[100]; + char output[200]; + int mob_id; + int number = 0; + int x = 0, y = 0; + int count; + int i, j, k; + int mx, my, range; + + memset(name, '\0', sizeof(name)); + memset(monster, '\0', sizeof(monster)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || + (sscanf(message, "\"%[^\"]\" %99s %d %d %d", name, monster, &number, &x, &y) < 2 && + sscanf(message, "%99s \"%[^\"]\" %d %d %d", monster, name, &number, &x, &y) < 2 && + sscanf(message, "%99s %d %99s %d %d", monster, &number, name, &x, &y) < 1)) { + clif_displaymessage(fd, msg_table[143]); // Give a monster name/id please. + return -1; + } + + // If monster identifier/name argument is a name + if ((mob_id = mobdb_searchname(monster)) == 0) // check name first (to avoid possible name begining by a number) + mob_id = mobdb_checkid(atoi(monster)); + + if (mob_id == 0) { + clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name. + return -1; + } + + if (mob_id == 1288) { + clif_displaymessage(fd, msg_table[83]); // Cannot spawn emperium. + return -1; + } + + if (number <= 0) + number = 1; + + if (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) + printf("%s monster='%s' name='%s' id=%d count=%d (%d,%d)\n", command, monster, name, mob_id, number, x, y); + + count = 0; + range = sqrt(number) / 2; + range = range * 2 + 5; // calculation of an odd number (+ 4 area around) + for (i = 0; i < number; i++) { + j = 0; + k = 0; + while(j++ < 8 && k == 0) { // try 8 times to spawn the monster (needed for close area) + if (x <= 0) + mx = sd->bl.x + (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(output, msg_table[240], count); // %d monster(s) summoned! + clif_displaymessage(fd, output); + } + else { + clif_displaymessage(fd, msg_table[40]); // Invalid monster ID or name. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void atcommand_killmonster_sub( + const int fd, struct map_session_data* sd, const char* message, + const int drop) +{ + int map_id; + char map_name[100]; + + memset(map_name, '\0', sizeof(map_name)); + + if (!message || !*message || sscanf(message, "%99s", map_name) < 1) + map_id = sd->bl.m; + else { + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + if ((map_id = map_mapname2mapid(map_name)) < 0) + map_id = sd->bl.m; + } + + map_foreachinarea(atkillmonster_sub, map_id, 0, 0, map[map_id].xs, map[map_id].ys, BL_MOB, drop); + + clif_displaymessage(fd, msg_table[165]); // All monsters killed! + + return; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_killmonster( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + atcommand_killmonster_sub(fd, sd, message, 1); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_killmonster2( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + atcommand_killmonster_sub(fd, sd, message, 0); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_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; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d %d", &position, &refine) < 2) { + clif_displaymessage(fd, "Please, enter a position and a amount (usage: @refine <equip position> <+/- amount>)."); + return -1; + } + + if (refine < -10) + refine = -10; + else if (refine > 10) + refine = 10; + else if (refine == 0) + refine = 1; + + count = 0; + for (i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].nameid && // 該当個所の装備を精錬する + (sd->status.inventory[i].equip & position || + (sd->status.inventory[i].equip && !position))) { + final_refine = sd->status.inventory[i].refine + refine; + if (final_refine > 10) + final_refine = 10; + else if (final_refine < 0) + final_refine = 0; + if (sd->status.inventory[i].refine != final_refine) { + sd->status.inventory[i].refine = final_refine; + current_position = sd->status.inventory[i].equip; + pc_unequipitem(sd, i, 0); + 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(output, msg_table[168], count); // %d items have been refined! + clif_displaymessage(fd, 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; + char output[200]; + + memset(output, '\0', sizeof(output)); + memset(item_name, '\0', sizeof(item_name)); + + if (!message || !*message || sscanf(message, "%99s %d %d", item_name, &attribute, &star) < 1) { + clif_displaymessage(fd, "Please, enter at least an item name/id (usage: @produce <equip name or equip ID> <element> <# of very's>)."); + return -1; + } + + item_id = 0; + if ((item_data = itemdb_searchname(item_name)) != NULL || + (item_data = itemdb_exists(atoi(item_name))) != NULL) + item_id = item_data->nameid; + + if (itemdb_exists(item_id) && + (item_id <= 500 || item_id > 1099) && + (item_id < 4001 || item_id > 4148) && + (item_id < 7001 || item_id > 10019) && + itemdb_isequip(item_id)) { + if (attribute < MIN_ATTRIBUTE || attribute > MAX_ATTRIBUTE) + attribute = ATTRIBUTE_NORMAL; + if (star < MIN_STAR || star > MAX_STAR) + star = 0; + memset(&tmp_item, 0, sizeof tmp_item); + tmp_item.nameid = item_id; + tmp_item.amount = 1; + tmp_item.identify = 1; + tmp_item.card[0] = 0x00ff; + tmp_item.card[1] = ((star * 5) << 8) + attribute; + *((unsigned long *)(&tmp_item.card[2])) = sd->char_id; + clif_produceeffect(sd, 0, item_id); // 製造エフェクトパケット + clif_misceffect(&sd->bl, 3); // 他人にも成功を通知 + if ((flag = pc_additem(sd, &tmp_item, 1))) + clif_additem(sd, 0, 0, flag); + } else { + if (battle_config.error_log) + printf("@produce NOT WEAPON [%d]\n", item_id); + if (item_id != 0 && itemdb_exists(item_id)) + sprintf(output, msg_table[169], item_id, item_data->name); // This item (%d: '%s') is not an equipment. + else + sprintf(output, msg_table[170]); // This item is not an equipment. + clif_displaymessage(fd, output); + return -1; + } + + return 0; +} + +/*========================================== + * Sub-function to display actual memo points + *------------------------------------------ + */ +void atcommand_memo_sub(struct map_session_data* sd) { + int i; + char output[200]; + + memset(output, '\0', sizeof(output)); + + clif_displaymessage(sd->fd, "Your actual memo positions are (except respawn point):"); + for (i = MIN_PORTAL_MEMO; i <= MAX_PORTAL_MEMO; i++) { + if (sd->status.memo_point[i].map[0]) + sprintf(output, "%d - %s (%d,%d)", i, sd->status.memo_point[i].map, sd->status.memo_point[i].x, sd->status.memo_point[i].y); + else + sprintf(output, msg_table[171], i); // %d - void + clif_displaymessage(sd->fd, output); + } + + return; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_memo( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int position = 0; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d", &position) < 1) + atcommand_memo_sub(sd); + else { + if (position >= MIN_PORTAL_MEMO && position <= MAX_PORTAL_MEMO) { + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to memo this map."); + return -1; + } + if (sd->status.memo_point[position].map[0]) { + sprintf(output, msg_table[172], position, sd->status.memo_point[position].map, sd->status.memo_point[position].x, sd->status.memo_point[position].y); // You replace previous memo position %d - %s (%d,%d). + clif_displaymessage(fd, output); + } + memcpy(sd->status.memo_point[position].map, map[sd->bl.m].name, 24); + sd->status.memo_point[position].x = sd->bl.x; + sd->status.memo_point[position].y = sd->bl.y; + clif_skill_memo(sd, 0); + if (pc_checkskill(sd, AL_WARP) <= (position + 1)) + clif_displaymessage(fd, msg_table[173]); // Note: you don't have the 'Warp' skill level to use it. + atcommand_memo_sub(sd); + } else { + sprintf(output, "Please, enter a valid position (usage: @memo <memo_position:%d-%d>).", MIN_PORTAL_MEMO, MAX_PORTAL_MEMO); + clif_displaymessage(fd, output); + atcommand_memo_sub(sd); + return -1; + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_gat( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + int y; + + memset(output, '\0', sizeof(output)); + + for (y = 2; y >= -2; y--) { + sprintf(output, "%s (x= %d, y= %d) %02X %02X %02X %02X %02X", + map[sd->bl.m].name, sd->bl.x - 2, sd->bl.y + y, + map_getcell(sd->bl.m, sd->bl.x - 2, sd->bl.y + y), + map_getcell(sd->bl.m, sd->bl.x - 1, sd->bl.y + y), + map_getcell(sd->bl.m, sd->bl.x, sd->bl.y + y), + map_getcell(sd->bl.m, sd->bl.x + 1, sd->bl.y + y), + map_getcell(sd->bl.m, sd->bl.x + 2, sd->bl.y + y)); + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_packet( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int x = 0, y = 0; + + if (!message || !*message || sscanf(message, "%d %d", &x, &y) < 2) { + clif_displaymessage(fd, "Please, enter a status type/flag (usage: @packet <status type> <flag>)."); + return -1; + } + + clif_status_change(&sd->bl, x, y); + + return 0; +} + +/*========================================== + * @stpoint (Rewritten by [Yor]) + *------------------------------------------ + */ +int atcommand_statuspoint( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int point, new_status_point; + + if (!message || !*message || (point = atoi(message)) == 0) { + clif_displaymessage(fd, "Please, enter a number (usage: @stpoint <number of points>)."); + return -1; + } + + new_status_point = (int)sd->status.status_point + point; + if (point > 0 && (point > 0x7FFF || new_status_point > 0x7FFF)) // fix positiv overflow + new_status_point = 0x7FFF; + else if (point < 0 && (point < -0x7FFF || new_status_point < 0)) // fix negativ overflow + new_status_point = 0; + + if (new_status_point != (int)sd->status.status_point) { + sd->status.status_point = (short)new_status_point; + clif_updatestatus(sd, SP_STATUSPOINT); + clif_displaymessage(fd, msg_table[174]); // Number of status points changed! + } else { + if (point < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + + return 0; +} + +/*========================================== + * @skpoint (Rewritten by [Yor]) + *------------------------------------------ + */ +int atcommand_skillpoint( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int point, new_skill_point; + + if (!message || !*message || (point = atoi(message)) == 0) { + clif_displaymessage(fd, "Please, enter a number (usage: @skpoint <number of points>)."); + return -1; + } + + new_skill_point = (int)sd->status.skill_point + point; + if (point > 0 && (point > 0x7FFF || new_skill_point > 0x7FFF)) // fix positiv overflow + new_skill_point = 0x7FFF; + else if (point < 0 && (point < -0x7FFF || new_skill_point < 0)) // fix negativ overflow + new_skill_point = 0; + + if (new_skill_point != (int)sd->status.skill_point) { + sd->status.skill_point = (short)new_skill_point; + clif_updatestatus(sd, SP_SKILLPOINT); + clif_displaymessage(fd, msg_table[175]); // Number of skill points changed! + } else { + if (point < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + + return 0; +} + +/*========================================== + * @zeny (Rewritten by [Yor]) + *------------------------------------------ + */ +int atcommand_zeny( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int zeny, new_zeny; + + if (!message || !*message || (zeny = atoi(message)) == 0) { + clif_displaymessage(fd, "Please, enter an amount (usage: @zeny <amount>)."); + return -1; + } + + new_zeny = sd->status.zeny + zeny; + if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow + new_zeny = MAX_ZENY; + else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow + new_zeny = 0; + + if (new_zeny != sd->status.zeny) { + sd->status.zeny = new_zeny; + clif_updatestatus(sd, SP_ZENY); + clif_displaymessage(fd, msg_table[176]); // Number of zenys changed! + } else { + if (zeny < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_param( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i, index, value = 0, new_value; + const char* param[] = { "@str", "@agi", "@vit", "@int", "@dex", "@luk", NULL }; + short* status[] = { + &sd->status.str, &sd->status.agi, &sd->status.vit, + &sd->status.int_, &sd->status.dex, &sd->status.luk + }; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d", &value) < 1 || value == 0) { + sprintf(output, "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>)."); + clif_displaymessage(fd, output); + return -1; + } + + index = -1; + for (i = 0; param[i] != NULL; i++) { + if (strcmpi(command, param[i]) == 0) { + index = i; + break; + } + } + if (index < 0 || index > MAX_STATUS_TYPE) { // normaly impossible... + sprintf(output, "Please, enter a valid value (usage: @str,@agi,@vit,@int,@dex,@luk <+/-adjustement>)."); + clif_displaymessage(fd, output); + return -1; + } + + new_value = (int)*status[index] + value; + if (value > 0 && (value > battle_config.max_parameter || new_value > battle_config.max_parameter)) // fix positiv overflow + new_value = battle_config.max_parameter; + else if (value < 0 && (value < -battle_config.max_parameter || new_value < 1)) // fix negativ overflow + new_value = 1; + + if (new_value != (int)*status[index]) { + *status[index] = new_value; + clif_updatestatus(sd, SP_STR + index); + clif_updatestatus(sd, SP_USTR + index); + pc_calcstatus(sd, 0); + clif_displaymessage(fd, msg_table[42]); // Stat changed. + } else { + if (value < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +//** Stat all by fritz (rewritten by [Yor]) +int atcommand_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 + }; + + if (!message || !*message || sscanf(message, "%d", &value) < 1 || value == 0) + value = battle_config.max_parameter; + + count = 0; + for (index = 0; index < (int)(sizeof(status) / sizeof(status[0])); index++) { + + new_value = (int)*status[index] + value; + if (value > 0 && (value > battle_config.max_parameter || new_value > battle_config.max_parameter)) // fix positiv overflow + new_value = battle_config.max_parameter; + else if (value < 0 && (value < -battle_config.max_parameter || new_value < 1)) // fix negativ overflow + new_value = 1; + + if (new_value != (int)*status[index]) { + *status[index] = new_value; + clif_updatestatus(sd, SP_STR + index); + clif_updatestatus(sd, SP_USTR + index); + pc_calcstatus(sd, 0); + count++; + } + } + + if (count > 0) // if at least 1 stat modified + clif_displaymessage(fd, msg_table[84]); // All stats changed! + else { + if (value < 0) + clif_displaymessage(fd, msg_table[177]); // Impossible to decrease a stat. + else + clif_displaymessage(fd, msg_table[178]); // Impossible to increase a stat. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_guildlevelup( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int level = 0; + short added_level; + struct guild *guild_info; + + if (!message || !*message || sscanf(message, "%d", &level) < 1 || level == 0) { + clif_displaymessage(fd, "Please, enter a valid level (usage: @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; + + 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, + pet_db[pet_id].class, mob_db[pet_db[pet_id].class].lv, + pet_db[pet_id].EggID, 0, 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) +{ + 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; + + 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) + pc_calcstatus(sd, 0); + else + pc_calcstatus(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; + + 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) +{ + 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_charpetrename( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charpetrename <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_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_recall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @recall <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); + return -1; + } + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp this player from its actual map."); + return -1; + } + pc_setpos(pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); + sprintf(output, msg_table[46], character); // %s recalled! + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * 対象キャラクターを転職させる upper指定で転生や養子も可能 + *------------------------------------------ + */ +int atcommand_character_job( + 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: @charjob/@charjobchange <job ID> <char name>)."); + return -1; + } + + if (sscanf(message, "%d %d %99[^\n]", &job, &upper, character) < 3) { //upper指定してある + upper = -1; + if (sscanf(message, "%d %99[^\n]", &job, character) < 2) { //upper指定してない上に何か足りない + clif_displaymessage(fd, "Please, enter a job and a player name (usage: @charjob/@charjobchange <job ID> <char name>)."); + return -1; + } + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + 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)) { + + // fix pecopeco display + if ((job != 13 && job != 21 && job != 4014 && job != 4022)) { + if (pc_isriding(sd)) { + if (pl_sd->status.class == 13) + pl_sd->status.class = pl_sd->view_class = 7; + if (pl_sd->status.class == 21) + pl_sd->status.class = pl_sd->view_class = 14; + if (pl_sd->status.class == 4014) + pl_sd->status.class = pl_sd->view_class = 4008; + if (pl_sd->status.class == 4022) + pl_sd->status.class = pl_sd->view_class = 4015; + pl_sd->status.option &= ~0x0020; + clif_changeoption(&pl_sd->bl); + pc_calcstatus(pl_sd, 0); + } + } else { + if (!pc_isriding(sd)) { + if (job == 13) + job = 7; + if (job == 21) + job = 14; + if (job == 4014) + job = 4008; + if (job == 4022) + job = 4015; + } + } + + 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 atcommand_revive( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @revive <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + pl_sd->status.hp = pl_sd->status.max_hp; + pc_setstand(pl_sd); + if (battle_config.pc_invincible_time > 0) + pc_setinvincibletimer(sd, battle_config.pc_invincible_time); + clif_updatestatus(pl_sd, SP_HP); + clif_updatestatus(pl_sd, SP_SP); + clif_resurrection(&pl_sd->bl, 1); + clif_displaymessage(fd, msg_table[51]); // Character revived. + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_character_stats( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char job_jobname[100]; + char output[200]; + struct map_session_data *pl_sd; + int i; + + memset(character, '\0', sizeof(character)); + memset(job_jobname, '\0', sizeof(job_jobname)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charstats <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + struct { + const char* format; + int value; + } output_table[] = { + { "Base Level - %d", pl_sd->status.base_level }, + { job_jobname, pl_sd->status.job_level }, + { "Hp - %d", pl_sd->status.hp }, + { "MaxHp - %d", pl_sd->status.max_hp }, + { "Sp - %d", pl_sd->status.sp }, + { "MaxSp - %d", pl_sd->status.max_sp }, + { "Str - %3d", pl_sd->status.str }, + { "Agi - %3d", pl_sd->status.agi }, + { "Vit - %3d", pl_sd->status.vit }, + { "Int - %3d", pl_sd->status.int_ }, + { "Dex - %3d", pl_sd->status.dex }, + { "Luk - %3d", pl_sd->status.luk }, + { "Zeny - %d", pl_sd->status.zeny }, + { NULL, 0 } + }; + sprintf(job_jobname, "Job - %s %s", job_name(pl_sd->status.class), "(level %d)"); + sprintf(output, msg_table[53], pl_sd->status.name); // '%s' stats: + clif_displaymessage(fd, output); + for (i = 0; output_table[i].format != NULL; i++) { + sprintf(output, output_table[i].format, output_table[i].value); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +//** Character Stats All by fritz +int atcommand_character_stats_all(const int fd, struct map_session_data* sd, const char* command, const char* message) +{ + char output[1024], gmlevel[1024]; + int i; + int count; + struct map_session_data *pl_sd; + + memset(output, '\0', sizeof(output)); + memset(gmlevel, '\0', sizeof(gmlevel)); + + count = 0; + for(i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + + if (pc_isGM(pl_sd) > 0) + sprintf(gmlevel, "| GM Lvl: %d", pc_isGM(pl_sd)); + else + sprintf(gmlevel, " "); + + sprintf(output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class), pl_sd->status.job_level, pl_sd->status.hp, pl_sd->status.max_hp, pl_sd->status.sp, pl_sd->status.max_sp); + clif_displaymessage(fd, output); + sprintf(output, "STR: %d | AGI: %d | VIT: %d | INT: %d | DEX: %d | LUK: %d | Zeny: %d %s", pl_sd->status.str, pl_sd->status.agi, pl_sd->status.vit, pl_sd->status.int_, pl_sd->status.dex, pl_sd->status.luk, pl_sd->status.zeny, gmlevel); + clif_displaymessage(fd, output); + clif_displaymessage(fd, "--------"); + count++; + } + } + + if (count == 0) + clif_displaymessage(fd, msg_table[28]); // No player found. + else if (count == 1) + clif_displaymessage(fd, msg_table[29]); // 1 player found. + else { + sprintf(output, msg_table[30], count); // %d players found. + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_character_option( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + int opt1 = 0, opt2 = 0, opt3 = 0; + struct map_session_data* pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %d %d %99[^\n]", &opt1, &opt2, &opt3, character) < 4 || opt1 < 0 || opt2 < 0 || opt3 < 0) { + clif_displaymessage(fd, "Please, enter valid options and a player name (usage: @charoption <param1> <param2> <param3> <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change option only to lower or same level + pl_sd->opt1 = opt1; + pl_sd->opt2 = opt2; + pl_sd->status.option = opt3; + // fix pecopeco display + if (pl_sd->status.class == 13 || pl_sd->status.class == 21 || pl_sd->status.class == 4014 || pl_sd->status.class == 4022) { + if (!pc_isriding(pl_sd)) { // pl_sd have the new value... + if (pl_sd->status.class == 13) + pl_sd->status.class = pl_sd->view_class = 7; + else if (pl_sd->status.class == 21) + pl_sd->status.class = pl_sd->view_class = 14; + else if (pl_sd->status.class == 4014) + pl_sd->status.class = pl_sd->view_class = 4008; + else if (pl_sd->status.class == 4022) + pl_sd->status.class = pl_sd->view_class = 4015; + } + } else { + if (pc_isriding(pl_sd)) { // pl_sd have the new value... + if (pl_sd->disguise > 0) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] (code added by [Yor]) + pl_sd->status.option &= ~0x0020; + } else { + if (pl_sd->status.class == 7) + pl_sd->status.class = pl_sd->view_class = 13; + else if (pl_sd->status.class == 14) + pl_sd->status.class = pl_sd->view_class = 21; + else if (pl_sd->status.class == 4008) + pl_sd->status.class = pl_sd->view_class = 4014; + else if (pl_sd->status.class == 4015) + pl_sd->status.class = pl_sd->view_class = 4022; + else + pl_sd->status.option &= ~0x0020; + } + } + } + clif_changeoption(&pl_sd->bl); + pc_calcstatus(pl_sd, 0); + clif_displaymessage(fd, msg_table[58]); // Character's options changed. + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * charchangesex command (usage: charchangesex <player_name>) + *------------------------------------------ + */ +int atcommand_char_change_sex( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charchangesex <name>)."); + return -1; + } + + // check player name + if (strlen(character) < 4) { + clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } else if (strlen(character) > 23) { + clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum. + return -1; + } else { + chrif_char_ask_name(sd->status.account_id, character, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex + clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it. + } + + return 0; +} + +/*========================================== + * charblock command (usage: charblock <player_name>) + * This command do a definitiv ban on a player + *------------------------------------------ + */ +int atcommand_char_block( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charblock/@block <name>)."); + return -1; + } + + // check player name + if (strlen(character) < 4) { + clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } else if (strlen(character) > 23) { + clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum. + return -1; + } else { + chrif_char_ask_name(sd->status.account_id, character, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block + clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it. + } + + return 0; +} + +/*========================================== + * charban command (usage: charban <time> <player_name>) + * This command do a limited ban on a player + * Time is done as follows: + * Adjustment value (-1, 1, +1, etc...) + * Modified element: + * a or y: year + * m: month + * j or d: day + * h: hour + * mn: minute + * s: second + * <example> @ban +1m-2mn1s-6y test_player + * this example adds 1 month and 1 second, and substracts 2 minutes and 6 years at the same time. + *------------------------------------------ + */ +int atcommand_char_ban( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char modif[100], character[100]; + char * modif_p; + int year, month, day, hour, minute, second, value; + + memset(modif, '\0', sizeof(modif)); + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%s %99[^\n]", modif, character) < 2) { + clif_displaymessage(fd, "Please, enter ban time and a player name (usage: @charban/@ban/@banish/@charbanish <time> <name>)."); + return -1; + } + + modif[sizeof(modif)-1] = '\0'; + character[sizeof(character)-1] = '\0'; + + modif_p = modif; + year = month = day = hour = minute = second = 0; + while (modif_p[0] != '\0') { + value = atoi(modif_p); + if (value == 0) + modif_p++; + else { + if (modif_p[0] == '-' || modif_p[0] == '+') + modif_p++; + while (modif_p[0] >= '0' && modif_p[0] <= '9') + modif_p++; + if (modif_p[0] == 's') { + second = value; + modif_p++; + } else if (modif_p[0] == 'm' && modif_p[1] == 'n') { + minute = value; + modif_p = modif_p + 2; + } else if (modif_p[0] == 'h') { + hour = value; + modif_p++; + } else if (modif_p[0] == 'd' || modif_p[0] == 'j') { + day = value; + modif_p++; + } else if (modif_p[0] == 'm') { + month = value; + modif_p++; + } else if (modif_p[0] == 'y' || modif_p[0] == 'a') { + year = value; + modif_p++; + } else if (modif_p[0] != '\0') { + modif_p++; + } + } + } + if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0) { + clif_displaymessage(fd, msg_table[85]); // Invalid time for ban command. + return -1; + } + + // check player name + if (strlen(character) < 4) { + clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } else if (strlen(character) > 23) { + clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum. + return -1; + } else { + chrif_char_ask_name(sd->status.account_id, character, 2, year, month, day, hour, minute, second); // type: 2 - ban + clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it. + } + + return 0; +} + +/*========================================== + * charunblock command (usage: charunblock <player_name>) + *------------------------------------------ + */ +int atcommand_char_unblock( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charunblock <player_name>)."); + return -1; + } + + // check player name + if (strlen(character) < 4) { + clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } else if (strlen(character) > 23) { + clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum. + return -1; + } else { + // send answer to login server via char-server + chrif_char_ask_name(sd->status.account_id, character, 3, 0, 0, 0, 0, 0, 0); // type: 3 - unblock + clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it. + } + + return 0; +} + +/*========================================== + * charunban command (usage: charunban <player_name>) + *------------------------------------------ + */ +int atcommand_char_unban( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charunban <player_name>)."); + return -1; + } + + // check player name + if (strlen(character) < 4) { + clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters. + return -1; + } else if (strlen(character) > 23) { + clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum. + return -1; + } else { + // send answer to login server via char-server + chrif_char_ask_name(sd->status.account_id, character, 4, 0, 0, 0, 0, 0, 0); // type: 4 - unban + clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it. + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_character_save( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char map_name[100]; + char character[100]; + struct map_session_data* pl_sd; + int x = 0, y = 0; + int m; + + memset(map_name, '\0', sizeof(map_name)); + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99s %d %d %99[^\n]", map_name, &x, &y, character) < 4 || x < 0 || y < 0) { + clif_displaymessage(fd, "Please, enter a valid save point and a player name (usage: @charsave <map> <x> <y> <charname>)."); + return -1; + } + + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change save point only to lower or same gm level + m = map_mapname2mapid(map_name); + if (m < 0) { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } else { + if (m >= 0 && map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to set this map as a save map."); + return -1; + } + pc_setsavepoint(pl_sd, map_name, x, y); + clif_displaymessage(fd, msg_table[57]); // Character's respawn point changed. + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_night( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + if (night_flag != 1) { + night_flag = 1; // 0=day, 1=night [Yor] + for(i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_sd->opt2 |= STATE_BLIND; + clif_changeoption(&pl_sd->bl); + clif_displaymessage(pl_sd->fd, msg_table[59]); // Night has fallen. + } + } + } else { + clif_displaymessage(fd, msg_table[89]); // Sorry, it's already the night. Impossible to execute the command. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_day( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + if (night_flag != 0) { + night_flag = 0; // 0=day, 1=night [Yor] + for(i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_sd->opt2 &= ~STATE_BLIND; + clif_changeoption(&pl_sd->bl); + clif_displaymessage(pl_sd->fd, msg_table[60]); // Day has arrived. + } + } + } else { + clif_displaymessage(fd, msg_table[90]); // Sorry, it's already the day. Impossible to execute the command. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_doom( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + for(i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && i != fd && + pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can doom only lower or same gm level + pc_damage(NULL, pl_sd, pl_sd->status.hp + 1); + clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement. + } + } + clif_displaymessage(fd, msg_table[62]); // Judgement was made. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_doommap( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && i != fd && sd->bl.m == pl_sd->bl.m && + pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can doom only lower or same gm level + pc_damage(NULL, pl_sd, pl_sd->status.hp + 1); + clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement. + } + } + clif_displaymessage(fd, msg_table[62]); // Judgement was made. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static void atcommand_raise_sub(struct map_session_data* sd) +{ + if (sd && sd->state.auth && pc_isdead(sd)) { + sd->status.hp = sd->status.max_hp; + sd->status.sp = sd->status.max_sp; + pc_setstand(sd); + clif_updatestatus(sd, SP_HP); + clif_updatestatus(sd, SP_SP); + clif_resurrection(&sd->bl, 1); + clif_displaymessage(sd->fd, msg_table[63]); // Mercy has been shown. + } +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_raise( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + + for (i = 0; i < fd_max; i++) { + if (session[i]) + atcommand_raise_sub(session[i]->session_data); + } + clif_displaymessage(fd, msg_table[64]); // Mercy has been granted. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_raisemap( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && sd->bl.m == pl_sd->bl.m) + atcommand_raise_sub(pl_sd); + } + clif_displaymessage(fd, msg_table[64]); // Mercy has been granted. + + return 0; +} + +/*========================================== + * atcommand_character_baselevel @charbaselvlで対象キャラのレベルを上げる + *------------------------------------------ +*/ +int atcommand_character_baselevel( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[100]; + int level = 0, i; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %99[^\n]", &level, character) < 2 || level == 0) { + clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: @charbaselvl <#> <nickname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change base level only lower or same gm level + + if (level > 0) { + if (pl_sd->status.base_level == battle_config.maximum_level) { // check for max level by Valaris + clif_displaymessage(fd, msg_table[91]); // Character's base level can't go any higher. + return 0; + } // End Addition + if (level > battle_config.maximum_level || level > (battle_config.maximum_level - pl_sd->status.base_level)) // fix positiv overflow + level = battle_config.maximum_level - pl_sd->status.base_level; + for (i = 1; i <= level; i++) + pl_sd->status.status_point += (pl_sd->status.base_level + i + 14) / 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); + pc_calcstatus(pl_sd, 0); + pc_heal(pl_sd, pl_sd->status.max_hp, pl_sd->status.max_sp); + clif_misceffect(&pl_sd->bl, 0); + clif_displaymessage(fd, msg_table[65]); // Character's base level raised. + } else { + if (pl_sd->status.base_level == 1) { + clif_displaymessage(fd, msg_table[193]); // Character's base level can't go any lower. + return -1; + } + if (level < -battle_config.maximum_level || level < (1 - pl_sd->status.base_level)) // fix negativ overflow + level = 1 - pl_sd->status.base_level; + if (pl_sd->status.status_point > 0) { + for (i = 0; i > level; i--) + pl_sd->status.status_point -= (pl_sd->status.base_level + i + 14) / 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); + pc_calcstatus(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; //正常終了 +} + +/*========================================== + * atcommand_character_joblevel @charjoblvlで対象キャラのJobレベルを上げる + *------------------------------------------ + */ +int atcommand_character_joblevel( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[100]; + int max_level = 50, level = 0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job pl_s_class; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %99[^\n]", &level, character) < 2 || level == 0) { + clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: @charjlvl <#> <nickname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + pl_s_class = pc_calc_base_job(pl_sd->status.class); + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job level only lower or same gm level + if (pl_s_class.job == 0) + max_level -= 40; + if ((pl_s_class.job == 23) || (pl_s_class.upper == 1 && pl_s_class.type == 2)) //スパノビと転生職はJobレベルの最高が70 + max_level += 20; + + if (level > 0) { + if (pl_sd->status.job_level == max_level) { + clif_displaymessage(fd, msg_table[67]); // Character's job level can't go any higher. + return -1; + } + if (pl_sd->status.job_level + level > max_level) + level = max_level - pl_sd->status.job_level; + pl_sd->status.job_level += level; + clif_updatestatus(pl_sd, SP_JOBLEVEL); + clif_updatestatus(pl_sd, SP_NEXTJOBEXP); + pl_sd->status.skill_point += level; + clif_updatestatus(pl_sd, SP_SKILLPOINT); + pc_calcstatus(pl_sd, 0); + clif_misceffect(&pl_sd->bl, 1); + clif_displaymessage(fd, msg_table[68]); // character's job level raised. + } else { + if (pl_sd->status.job_level == 1) { + clif_displaymessage(fd, msg_table[194]); // Character's job level can't go any lower. + return -1; + } + if (pl_sd->status.job_level + level < 1) + level = 1 - pl_sd->status.job_level; + pl_sd->status.job_level += level; + clif_updatestatus(pl_sd, SP_JOBLEVEL); + clif_updatestatus(pl_sd, SP_NEXTJOBEXP); + if (pl_sd->status.skill_point > 0) { + pl_sd->status.skill_point += level; + if (pl_sd->status.skill_point < 0) + pl_sd->status.skill_point = 0; + clif_updatestatus(pl_sd, SP_SKILLPOINT); + } // to add: remove status points from skills + pc_calcstatus(pl_sd, 0); + clif_displaymessage(fd, msg_table[69]); // Character's job level lowered. + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_kick( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[100]; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @kick <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) // you can kick only lower or same gm level + clif_GM_kick(sd, pl_sd, 1); + else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_kickall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && + pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kick only lower or same gm level + if (sd->status.account_id != pl_sd->status.account_id) + clif_GM_kick(sd, pl_sd, 0); + } + } + + clif_displaymessage(fd, msg_table[195]); // All players have been kicked! + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_allskill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + pc_allskillup(sd); // all skills + sd->status.skill_point = 0; // 0 skill points + clif_updatestatus(sd, SP_SKILLPOINT); // update + clif_displaymessage(fd, msg_table[76]); // You have received all skills. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_questskill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int skill_id; + + if (!message || !*message || (skill_id = atoi(message)) < 0) { + clif_displaymessage(fd, "Please, enter a quest skill number (usage: @questskill <#:0+>)."); + return -1; + } + + if (skill_id >= 0 && skill_id < MAX_SKILL_DB) { + if (skill_get_inf2(skill_id) & 0x01) { + if (pc_checkskill(sd, skill_id) == 0) { + pc_skill(sd, skill_id, 1, 0); + clif_displaymessage(fd, msg_table[70]); // You have learned the skill. + } else { + clif_displaymessage(fd, msg_table[196]); // You already have this quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_charquestskill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + int skill_id = 0; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %99[^\n]", &skill_id, character) < 2 || skill_id < 0) { + clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charquestskill <#:0+> <char_name>)."); + return -1; + } + + if (skill_id >= 0 && skill_id < MAX_SKILL_DB) { + if (skill_get_inf2(skill_id) & 0x01) { + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_checkskill(pl_sd, skill_id) == 0) { + pc_skill(pl_sd, skill_id, 1, 0); + clif_displaymessage(fd, msg_table[199]); // This player has learned the skill. + } else { + clif_displaymessage(fd, msg_table[200]); // This player already has this quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_lostskill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int skill_id; + + if (!message || !*message || (skill_id = atoi(message)) < 0) { + clif_displaymessage(fd, "Please, enter a quest skill number (usage: @lostskill <#:0+>)."); + return -1; + } + + if (skill_id >= 0 && skill_id < MAX_SKILL) { + if (skill_get_inf2(skill_id) & 0x01) { + if (pc_checkskill(sd, skill_id) > 0) { + sd->status.skill[skill_id].lv = 0; + sd->status.skill[skill_id].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_charlostskill( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + int skill_id = 0; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %99[^\n]", &skill_id, character) < 2 || skill_id < 0) { + clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charlostskill <#:0+> <char_name>)."); + return -1; + } + + if (skill_id >= 0 && skill_id < MAX_SKILL) { + if (skill_get_inf2(skill_id) & 0x01) { + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_checkskill(pl_sd, skill_id) > 0) { + pl_sd->status.skill[skill_id].lv = 0; + pl_sd->status.skill[skill_id].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; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_spiritball( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int number; + + 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[100]; + + memset(party, '\0', sizeof(party)); + + if (!message || !*message || sscanf(message, "%99[^\n]", party) < 1) { + clif_displaymessage(fd, "Please, enter a party name (usage: @party <party_name>)."); + return -1; + } + + party_create(sd, party); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_guild( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char guild[100]; + int prev; + + memset(guild, '\0', sizeof(guild)); + + if (!message || !*message || sscanf(message, "%99[^\n]", guild) < 1) { + clif_displaymessage(fd, "Please, enter a guild name (usage: @guild <guild_name>)."); + return -1; + } + + prev = battle_config.guild_emperium_check; + battle_config.guild_emperium_check = 0; + guild_create(sd, guild); + battle_config.guild_emperium_check = prev; + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_agitstart( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (agit_flag == 1) { + clif_displaymessage(fd, msg_table[73]); // Already it has started siege warfare. + return -1; + } + + agit_flag = 1; + guild_agit_start(); + clif_displaymessage(fd, msg_table[72]); // Guild siege warfare start! + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_agitend( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (agit_flag == 0) { + clif_displaymessage(fd, msg_table[75]); // Siege warfare hasn't started yet. + return -1; + } + + agit_flag = 0; + guild_agit_end(); + clif_displaymessage(fd, msg_table[74]); // Guild siege warfare end! + + return 0; +} + +/*========================================== + * @mapexitでマップサーバーを終了させる + *------------------------------------------ + */ +int atcommand_mapexit( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + if (sd->status.account_id != pl_sd->status.account_id) + clif_GM_kick(sd, pl_sd, 0); + } + } + clif_GM_kick(sd, sd, 0); + + runflag = 0; + + return 0; +} + +/*========================================== + * idsearch <part_of_name>: revrited by [Yor] + *------------------------------------------ + */ +int atcommand_idsearch( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char item_name[100]; + char output[200]; + int i, match; + struct item_data *item; + + memset(item_name, '\0', sizeof(item_name)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99s", item_name) < 0) { + clif_displaymessage(fd, "Please, enter a part of item name (usage: @idsearch <part_of_item_name>)."); + return -1; + } + + sprintf(output, msg_table[77], item_name); // The reference result of '%s' (name: id): + clif_displaymessage(fd, output); + match = 0; + for(i = 0; i < 20000; i++) { + if ((item = itemdb_exists(i)) != NULL && strstr(item->jname, item_name) != NULL) { + match++; + sprintf(output, msg_table[78], item->jname, item->nameid); // %s: %d + clif_displaymessage(fd, output); + } + } + sprintf(output, msg_table[79], match); // It is %d affair above. + clif_displaymessage(fd, output); + + return 0; +} + +/*========================================== + * Character Skill Reset + *------------------------------------------ + */ +int atcommand_charskreset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charskreset <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset skill points only lower or same gm level + pc_resetskill(pl_sd); + sprintf(output, msg_table[206], character); // '%s' skill points reseted! + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Stat Reset + *------------------------------------------ + */ +int atcommand_charstreset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charstreset <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset stats points only lower or same gm level + pc_resetstate(pl_sd); + sprintf(output, msg_table[207], character); // '%s' stats points reseted! + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Reset + *------------------------------------------ + */ +int atcommand_charreset( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charreset <charname>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level + pc_resetstate(pl_sd); + pc_resetskill(pl_sd); + sprintf(output, msg_table[208], character); // '%s' skill and stats points reseted! + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Model by chbrules + *------------------------------------------ + */ +int atcommand_charmodel( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int hair_style = 0, hair_color = 0, cloth_color = 0; + struct map_session_data *pl_sd; + char character[100]; + char output[200]; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%d %d %d %99[^\n]", &hair_style, &hair_color, &cloth_color, character) < 4 || hair_style < 0 || hair_color < 0 || cloth_color < 0) { + sprintf(output, "Please, enter a valid model and a player name (usage: @charmodel <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d> <name>).", + MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); + clif_displaymessage(fd, output); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE && + hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR && + cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) { + + if (cloth_color != 0 && + pl_sd->status.sex == 1 && + (pl_sd->status.class == 12 || pl_sd->status.class == 17)) { + clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class. + return -1; + } else { + pc_changelook(pl_sd, LOOK_HAIR, hair_style); + pc_changelook(pl_sd, LOOK_HAIR_COLOR, hair_color); + pc_changelook(pl_sd, LOOK_CLOTHES_COLOR, cloth_color); + clif_displaymessage(fd, msg_table[36]); // Appearence changed. + } + } else { + clif_displaymessage(fd, msg_table[37]); // An invalid number was specified. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Skill Point (Rewritten by [Yor]) + *------------------------------------------ + */ +int atcommand_charskpoint( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[100]; + int new_skill_point; + int point = 0; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %99[^\n]", &point, character) < 2 || point == 0) { + clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charskpoint <amount> <name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + new_skill_point = (int)pl_sd->status.skill_point + point; + if (point > 0 && (point > 0x7FFF || new_skill_point > 0x7FFF)) // fix positiv overflow + new_skill_point = 0x7FFF; + else if (point < 0 && (point < -0x7FFF || new_skill_point < 0)) // fix negativ overflow + new_skill_point = 0; + if (new_skill_point != (int)pl_sd->status.skill_point) { + pl_sd->status.skill_point = new_skill_point; + clif_updatestatus(pl_sd, SP_SKILLPOINT); + clif_displaymessage(fd, msg_table[209]); // Character's number of skill points changed! + } else { + if (point < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Status Point (rewritten by [Yor]) + *------------------------------------------ + */ +int atcommand_charstpoint( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[100]; + int new_status_point; + int point = 0; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %99[^\n]", &point, character) < 2 || point == 0) { + clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charstpoint <amount> <name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + new_status_point = (int)pl_sd->status.status_point + point; + if (point > 0 && (point > 0x7FFF || new_status_point > 0x7FFF)) // fix positiv overflow + new_status_point = 0x7FFF; + else if (point < 0 && (point < -0x7FFF || new_status_point < 0)) // fix negativ overflow + new_status_point = 0; + if (new_status_point != (int)pl_sd->status.status_point) { + pl_sd->status.status_point = new_status_point; + clif_updatestatus(pl_sd, SP_STATUSPOINT); + clif_displaymessage(fd, msg_table[210]); // Character's number of status points changed! + } else { + if (point < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Character Zeny Point (Rewritten by [Yor]) + *------------------------------------------ + */ +int atcommand_charzeny( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[100]; + int zeny = 0, new_zeny; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%d %99[^\n]", &zeny, character) < 2 || zeny == 0) { + clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charzeny <zeny> <name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + new_zeny = pl_sd->status.zeny + zeny; + if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow + new_zeny = MAX_ZENY; + else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow + new_zeny = 0; + if (new_zeny != pl_sd->status.zeny) { + pl_sd->status.zeny = new_zeny; + clif_updatestatus(pl_sd, SP_ZENY); + clif_displaymessage(fd, msg_table[211]); // Character's number of zenys changed! + } else { + if (zeny < 0) + clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value. + else + clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * Recall All Characters Online To Your Location + *------------------------------------------ + */ +int atcommand_recallall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + int count; + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); + return -1; + } + + count = 0; + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && sd->status.account_id != pl_sd->status.account_id && + pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can recall only lower or same level + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + count++; + else + pc_setpos(pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); + } + } + + clif_displaymessage(fd, msg_table[92]); // All characters recalled! + if (count) { + sprintf(output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count); + clif_displaymessage(fd, output); + } + + return 0; +} + +/*========================================== + * Recall online characters of a guild to your location + *------------------------------------------ + */ +int atcommand_guildrecall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + int i; + char guild_name[100]; + char output[200]; + struct guild *g; + int count; + + memset(guild_name, '\0', sizeof(guild_name)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", guild_name) < 1) { + clif_displaymessage(fd, "Please, enter a guild name/id (usage: @guildrecall <guild_name/id>)."); + return -1; + } + + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); + return -1; + } + + if ((g = guild_searchname(guild_name)) != NULL || // name first to avoid error when name begin with a number + (g = guild_search(atoi(message))) != NULL) { + count = 0; + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && + sd->status.account_id != pl_sd->status.account_id && + pl_sd->status.guild_id == g->guild_id) { + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + count++; + else + pc_setpos(pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); + } + } + sprintf(output, msg_table[93], g->name); // All online characters of the %s guild are near you. + clif_displaymessage(fd, output); + if (count) { + sprintf(output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[94]); // Incorrect name/ID, or no one from the guild is online. + return -1; + } + + return 0; +} + +/*========================================== + * Recall online characters of a party to your location + *------------------------------------------ + */ +int atcommand_partyrecall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + struct map_session_data *pl_sd; + char party_name[100]; + char output[200]; + struct party *p; + int count; + + memset(party_name, '\0', sizeof(party_name)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", party_name) < 1) { + clif_displaymessage(fd, "Please, enter a party name/id (usage: @partyrecall <party_name/id>)."); + return -1; + } + + if (sd->bl.m >= 0 && map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) { + clif_displaymessage(fd, "You are not authorised to warp somenone to your actual map."); + return -1; + } + + if ((p = party_searchname(party_name)) != NULL || // name first to avoid error when name begin with a number + (p = party_search(atoi(message))) != NULL) { + count = 0; + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && + sd->status.account_id != pl_sd->status.account_id && + pl_sd->status.party_id == p->party_id) { + if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) + count++; + else + pc_setpos(pl_sd, sd->mapname, sd->bl.x, sd->bl.y, 2); + } + } + sprintf(output, msg_table[95], p->name); // All online characters of the %s party are near you. + clif_displaymessage(fd, output); + if (count) { + sprintf(output, "Because you are not authorised to warp from some maps, %d player(s) have not been recalled.", count); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[96]); // Incorrect name or ID, or no one from the party is online. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_reloaditemdb( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + itemdb_reload(); + clif_displaymessage(fd, msg_table[97]); // Item database reloaded. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_reloadmobdb( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + mob_reload(); + clif_displaymessage(fd, msg_table[98]); // Monster database reloaded. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_reloadskilldb( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + skill_reload(); + clif_displaymessage(fd, msg_table[99]); // Skill database reloaded. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +#ifndef TXT_ONLY +int atcommand_rehash( +#else /* TXT_ONLY */ +int atcommand_reloadscript( +#endif /* TXT_ONLY */ + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ +#ifndef TXT_ONLY + atcommand_broadcast( fd, sd, "@broadcast", "eAthena SQL Server is Rehashing..." ); + atcommand_broadcast( fd, sd, "@broadcast", "You will feel a bit of lag at this point !" ); + + rehash( fd, sd ); + + atcommand_broadcast( fd, sd, "@broadcast", "Reloading NPCs..." ); +#endif /* not TXT_ONLY */ + do_init_npc(); + do_init_script(); + + npc_event_do_oninit(); + + clif_displaymessage(fd, msg_table[100]); // Scripts reloaded. + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_reloadgmdb( // by [Yor] + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + chrif_reloadGMdb(); + + clif_displaymessage(fd, msg_table[101]); // Login-server asked to reload GM accounts and their level. + + return 0; +} + +/*========================================== + * @mapinfo <map name> [0-3] by MC_Cameri + * => Shows information about the map [map name] + * 0 = no additional information + * 1 = Show users in that map and their location + * 2 = Shows NPCs in that map + * 3 = Shows the shops/chats in that map (not implemented) + *------------------------------------------ + */ +int atcommand_mapinfo( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + struct npc_data *nd = NULL; + struct chat_data *cd = NULL; + char output[200], map_name[100]; + char direction[12]; + int m_id, i, chat_num, list = 0; + + memset(output, '\0', sizeof(output)); + memset(map_name, '\0', sizeof(map_name)); + memset(direction, '\0', sizeof(direction)); + + sscanf(message, "%d %99[^\n]", &list, map_name); + + if (list < 0 || list > 3) { + clif_displaymessage(fd, "Please, enter at least a valid list number (usage: @mapinfo <0-3> [map])."); + return -1; + } + + if (map_name[0] == '\0') + strcpy(map_name, sd->mapname); + if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < 13) // 16 - 4 (.gat) + strcat(map_name, ".gat"); + + if ((m_id = map_mapname2mapid(map_name)) < 0) { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + + clif_displaymessage(fd, "------ Map Info ------"); + sprintf(output, "Map Name: %s", map_name); + clif_displaymessage(fd, output); + sprintf(output, "Players In Map: %d", map[m_id].users); + clif_displaymessage(fd, output); + sprintf(output, "NPCs In Map: %d", map[m_id].npc_num); + clif_displaymessage(fd, output); + chat_num = 0; + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && + (cd = (struct chat_data*)map_id2bl(pl_sd->chatID))) { + chat_num++; + } + } + sprintf(output, "Chats In Map: %d", chat_num); + clif_displaymessage(fd, output); + clif_displaymessage(fd, "------ Map Flags ------"); + sprintf(output, "Player vs Player: %s | No Guild: %s | No Party: %s", + (map[m_id].flag.pvp) ? "True" : "False", + (map[m_id].flag.pvp_noguild) ? "True" : "False", + (map[m_id].flag.pvp_noparty) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "Guild vs Guild: %s | No Party: %s", (map[m_id].flag.gvg) ? "True" : "False", (map[m_id].flag.gvg_noparty) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Dead Branch: %s", (map[m_id].flag.nobranch) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Memo: %s", (map[m_id].flag.nomemo) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Penalty: %s", (map[m_id].flag.nopenalty) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Return: %s", (map[m_id].flag.noreturn) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Save: %s", (map[m_id].flag.nosave) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Teleport: %s", (map[m_id].flag.noteleport) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Monster Teleport: %s", (map[m_id].flag.monster_noteleport) ? "True" : "False"); + clif_displaymessage(fd, output); + sprintf(output, "No Zeny Penalty: %s", (map[m_id].flag.nozenypenalty) ? "True" : "False"); + clif_displaymessage(fd, output); + + switch (list) { + case 0: + // Do nothing. It's list 0, no additional display. + break; + case 1: + clif_displaymessage(fd, "----- Players in Map -----"); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && strcmp(pl_sd->mapname, map_name) == 0) { + sprintf(output, "Player '%s' (session #%d) | Location: %d,%d", + pl_sd->status.name, i, pl_sd->bl.x, pl_sd->bl.y); + clif_displaymessage(fd, output); + } + } + break; + case 2: + clif_displaymessage(fd, "----- NPCs in Map -----"); + for (i = 0; i < map[m_id].npc_num;) { + nd = map[m_id].npc[i]; + switch(nd->dir) { + case 0: strcpy(direction, "North"); break; + case 1: strcpy(direction, "North West"); break; + case 2: strcpy(direction, "West"); break; + case 3: strcpy(direction, "South West"); break; + case 4: strcpy(direction, "South"); break; + case 5: strcpy(direction, "South East"); break; + case 6: strcpy(direction, "East"); break; + case 7: strcpy(direction, "North East"); break; + case 9: strcpy(direction, "North"); break; + default: strcpy(direction, "Unknown"); break; + } + sprintf(output, "NPC %d: %s | Direction: %s | Sprite: %d | Location: %d %d", + ++i, nd->name, direction, nd->class, nd->bl.x, nd->bl.y); + clif_displaymessage(fd, output); + } + break; + case 3: + clif_displaymessage(fd, "----- Chats in Map -----"); + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth && + (cd = (struct chat_data*)map_id2bl(pl_sd->chatID)) && + strcmp(pl_sd->mapname, map_name) == 0 && + cd->usersd[0] == pl_sd) { + sprintf(output, "Chat %d: %s | Player: %s | Location: %d %d", + i, cd->title, pl_sd->status.name, cd->bl.x, cd->bl.y); + clif_displaymessage(fd, output); + sprintf(output, " Users: %d/%d | Password: %s | Public: %s", + cd->users, cd->limit, cd->pass, (cd->pub) ? "Yes" : "No"); + clif_displaymessage(fd, output); + } + } + break; + default: // normally impossible to arrive here + clif_displaymessage(fd, "Please, enter at least a valid list number (usage: @mapinfo <0-3> [map])."); + return -1; + break; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_mount_peco( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (sd->disguise > 0) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage(fd, msg_table[212]); // Cannot mount a Peco while in disguise. + return -1; + } + + if (!pc_isriding(sd)) { // if actually no peco + if (sd->status.class == 7 || sd->status.class == 14 || sd->status.class == 4008 || sd->status.class == 4015) { + if (sd->status.class == 7) + sd->status.class = sd->view_class = 13; + else if (sd->status.class == 14) + sd->status.class = sd->view_class = 21; + else if (sd->status.class == 4008) + sd->status.class = sd->view_class = 4014; + else if (sd->status.class == 4015) + sd->status.class = sd->view_class = 4022; + pc_setoption(sd, sd->status.option | 0x0020); + clif_displaymessage(fd, msg_table[102]); // Mounted Peco. + } else { + clif_displaymessage(fd, msg_table[213]); // You can not mount a peco with your job. + return -1; + } + } else { + if (sd->status.class == 13) + sd->status.class = sd->view_class = 7; + else if (sd->status.class == 21) + sd->status.class = sd->view_class = 14; + else if (sd->status.class == 4014) + sd->status.class = sd->view_class = 4008; + else if (sd->status.class == 4022) + sd->status.class = sd->view_class = 4015; + pc_setoption(sd, sd->status.option & ~0x0020); + clif_displaymessage(fd, msg_table[214]); // Unmounted Peco. + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_char_mount_peco( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charmountpeco <char_name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pl_sd->disguise > 0) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage(fd, msg_table[215]); // This player cannot mount a Peco while in disguise. + return -1; + } + + if (!pc_isriding(pl_sd)) { // if actually no peco + if (pl_sd->status.class == 7 || pl_sd->status.class == 14 || pl_sd->status.class == 4008 || pl_sd->status.class == 4015) { + if (pl_sd->status.class == 7) + pl_sd->status.class = pl_sd->view_class = 13; + else if (pl_sd->status.class == 14) + pl_sd->status.class = pl_sd->view_class = 21; + else if (pl_sd->status.class == 4008) + pl_sd->status.class = pl_sd->view_class = 4014; + else if (pl_sd->status.class == 4015) + pl_sd->status.class = pl_sd->view_class = 4022; + pc_setoption(pl_sd, pl_sd->status.option | 0x0020); + clif_displaymessage(fd, msg_table[216]); // Now, this player mounts a peco. + } else { + clif_displaymessage(fd, msg_table[217]); // This player can not mount a peco with his/her job. + return -1; + } + } else { + if (pl_sd->status.class == 13) + pl_sd->status.class = pl_sd->view_class = 7; + else if (pl_sd->status.class == 21) + pl_sd->status.class = pl_sd->view_class = 14; + else if (pl_sd->status.class == 4014) + pl_sd->status.class = pl_sd->view_class = 4008; + else if (pl_sd->status.class == 4022) + pl_sd->status.class = pl_sd->view_class = 4015; + pc_setoption(pl_sd, pl_sd->status.option & ~0x0020); + clif_displaymessage(fd, msg_table[218]); // Now, this player has not more peco. + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + *Spy Commands by Syrus22 + *------------------------------------------ + */ +int atcommand_guildspy( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char guild_name[100]; + char output[200]; + struct guild *g; + + memset(guild_name, '\0', sizeof(guild_name)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", guild_name) < 1) { + clif_displaymessage(fd, "Please, enter a guild name/id (usage: @guildspy <guild_name/id>)."); + return -1; + } + + if ((g = guild_searchname(guild_name)) != NULL || // name first to avoid error when name begin with a number + (g = guild_search(atoi(message))) != NULL) { + if (sd->guildspy == g->guild_id) { + sd->guildspy = 0; + sprintf(output, msg_table[103], g->name); // No longer spying on the %s guild. + clif_displaymessage(fd, output); + } else { + sd->guildspy = g->guild_id; + sprintf(output, msg_table[104], g->name); // Spying on the %s guild. + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[94]); // Incorrect name/ID, or no one from the guild is online. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_partyspy( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char party_name[100]; + char output[200]; + struct party *p; + + memset(party_name, '\0', sizeof(party_name)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", party_name) < 1) { + clif_displaymessage(fd, "Please, enter a party name/id (usage: @partyspy <party_name/id>)."); + return -1; + } + + if ((p = party_searchname(party_name)) != NULL || // name first to avoid error when name begin with a number + (p = party_search(atoi(message))) != NULL) { + if (sd->partyspy == p->party_id) { + sd->partyspy = 0; + sprintf(output, msg_table[105], p->name); // No longer spying on the %s party. + clif_displaymessage(fd, output); + } else { + sd->partyspy = p->party_id; + sprintf(output, msg_table[106], p->name); // Spying on the %s party. + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[96]); // Incorrect name or ID, or no one from the party is online. + return -1; + } + + return 0; +} + +/*========================================== + * @repairall [Valaris] + *------------------------------------------ + */ +int atcommand_repairall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int count, i; + + count = 0; + for (i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].nameid && sd->status.inventory[i].broken == 1) { + sd->status.inventory[i].broken = 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) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @nuke <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same 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_enablenpc(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char NPCname[100]; + + memset(NPCname, '\0', sizeof(NPCname)); + + if (!message || !*message || sscanf(message, "%99[^\n]", NPCname) < 1) { + clif_displaymessage(fd, "Please, enter a NPC name (usage: @npcon <NPC_name>)."); + return -1; + } + + if (npc_name2id(NPCname) != NULL) { + npc_enable(NPCname, 1); + clif_displaymessage(fd, msg_table[110]); // Npc Enabled. + } else { + clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist. + return -1; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int atcommand_disablenpc(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char NPCname[100]; + + memset(NPCname, '\0', sizeof(NPCname)); + + if (!message || !*message || sscanf(message, "%99[^\n]", NPCname) < 1) { + clif_displaymessage(fd, "Please, enter a NPC name (usage: @npcoff <NPC_name>)."); + return -1; + } + + if (npc_name2id(NPCname) != NULL) { + npc_enable(NPCname, 0); + clif_displaymessage(fd, msg_table[112]); // Npc Disabled. + } else { + clif_displaymessage(fd, msg_table[111]); // This NPC doesn't exist. + return -1; + } + + return 0; +} + +/*========================================== + * time in txt for time command (by [Yor]) + *------------------------------------------ + */ +char * txt_time(unsigned int duration) { + int days, hours, minutes, seconds; + char temp[256]; + static char temp1[256]; + + memset(temp, '\0', sizeof(temp)); + memset(temp1, '\0', sizeof(temp1)); + + if (duration < 0) + duration = 0; + + days = duration / (60 * 60 * 24); + duration = duration - (60 * 60 * 24 * days); + hours = duration / (60 * 60); + duration = duration - (60 * 60 * hours); + minutes = duration / 60; + seconds = duration - (60 * minutes); + + if (days < 2) + sprintf(temp, msg_table[219], days); // %d day + else + sprintf(temp, msg_table[220], days); // %d days + if (hours < 2) + sprintf(temp1, msg_table[221], temp, hours); // %s %d hour + else + sprintf(temp1, msg_table[222], temp, hours); // %s %d hours + if (minutes < 2) + sprintf(temp, msg_table[223], temp1, minutes); // %s %d minute + else + sprintf(temp, msg_table[224], temp1, minutes); // %s %d minutes + if (seconds < 2) + sprintf(temp1, msg_table[225], temp, seconds); // %s and %d second + else + sprintf(temp1, msg_table[226], temp, seconds); // %s and %d seconds + + return temp1; +} + +/*========================================== + * @time/@date/@server_date/@serverdate/@server_time/@servertime: Display the date/time of the server (by [Yor] + * Calculation management of GM modification (@day/@night GM commands) is done + *------------------------------------------ + */ +int atcommand_servertime(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct TimerData * timer_data; + struct TimerData * timer_data2; + time_t time_server; // variable for number of seconds (used with time() function) + struct tm *datetime; // variable for time in structure ->tm_mday, ->tm_sec, ... + char temp[256]; + + memset(temp, '\0', sizeof(temp)); + + time(&time_server); // get time in seconds since 1/1/1970 + datetime = 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((timer_data->tick - gettick()) / 1000)); // Game time: The game is actualy in night for %s. + clif_displaymessage(fd, temp); + clif_displaymessage(fd, msg_table[234]); // Game time: After, the game will be in permanent daylight. + } else + clif_displaymessage(fd, msg_table[231]); // Game time: The game is in permanent daylight. + else if (battle_config.day_duration == 0) + if (night_flag == 0) { // we start with day + timer_data = get_timer(night_timer_tid); + sprintf(temp, msg_table[235], txt_time((timer_data->tick - gettick()) / 1000)); // Game time: The game is actualy in daylight for %s. + clif_displaymessage(fd, temp); + clif_displaymessage(fd, msg_table[236]); // Game time: After, the game will be in permanent night. + } else + clif_displaymessage(fd, msg_table[232]); // Game time: The game is in permanent night. + else { + if (night_flag == 0) { + timer_data = get_timer(night_timer_tid); + timer_data2 = get_timer(day_timer_tid); + sprintf(temp, msg_table[235], txt_time((timer_data->tick - gettick()) / 1000)); // Game time: The game is actualy in daylight for %s. + clif_displaymessage(fd, temp); + if (timer_data->tick > timer_data2->tick) + sprintf(temp, msg_table[237], txt_time((timer_data->interval - abs(timer_data->tick - timer_data2->tick)) / 1000)); // Game time: After, the game will be in night for %s. + else + sprintf(temp, msg_table[237], txt_time(abs(timer_data->tick - timer_data2->tick) / 1000)); // Game time: After, the game will be in night for %s. + clif_displaymessage(fd, temp); + sprintf(temp, msg_table[238], txt_time(timer_data->interval / 1000)); // Game time: A day cycle has a normal duration of %s. + clif_displaymessage(fd, temp); + } else { + timer_data = get_timer(day_timer_tid); + timer_data2 = get_timer(night_timer_tid); + sprintf(temp, msg_table[233], txt_time((timer_data->tick - gettick()) / 1000)); // Game time: The game is actualy in night for %s. + clif_displaymessage(fd, temp); + if (timer_data->tick > timer_data2->tick) + sprintf(temp, msg_table[239], txt_time((timer_data->interval - abs(timer_data->tick - timer_data2->tick)) / 1000)); // Game time: After, the game will be in daylight for %s. + else + sprintf(temp, msg_table[239], txt_time(abs(timer_data->tick - timer_data2->tick) / 1000)); // Game time: After, the game will be in daylight for %s. + clif_displaymessage(fd, temp); + sprintf(temp, msg_table[238], txt_time(timer_data->interval / 1000)); // Game time: A day cycle has a normal duration of %s. + clif_displaymessage(fd, temp); + } + } + + return 0; +} + +/*========================================== + * @chardelitem <item_name_or_ID> <quantity> <player> (by [Yor] + * removes <quantity> item from a character + * item can be equiped or not. + * Inspired from a old command created by RoVeRT + *------------------------------------------ + */ +int atcommand_chardelitem(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + char character[100]; + char item_name[100]; + int i, number = 0, item_id, item_position, count; + char output[200]; + struct item_data *item_data; + + memset(character, '\0', sizeof(character)); + memset(item_name, '\0', sizeof(item_name)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%s %d %99[^\n]", item_name, &number, character) < 3 || number < 1) { + clif_displaymessage(fd, "Please, enter an item name/id, a quantity and a player name (usage: @chardelitem <item_name_or_ID> <quantity> <player>)."); + return -1; + } + + item_id = 0; + if ((item_data = itemdb_searchname(item_name)) != NULL || + (item_data = itemdb_exists(atoi(item_name))) != NULL) + item_id = item_data->nameid; + + if (item_id > 500) { + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level + item_position = pc_search_inventory(pl_sd, item_id); + if (item_position >= 0) { + count = 0; + for(i = 0; i < number && item_position >= 0; i++) { + pc_delitem(pl_sd, item_position, 1, 0); + count++; + item_position = pc_search_inventory(pl_sd, item_id); // for next loop + } + sprintf(output, msg_table[113], count); // %d item(s) removed by a GM. + clif_displaymessage(pl_sd->fd, output); + if (number == count) + sprintf(output, msg_table[114], count); // %d item(s) removed from the player. + else + sprintf(output, msg_table[115], count, count, number); // %d item(s) removed. Player had only %d on %d items. + clif_displaymessage(fd, output); + } else { + clif_displaymessage(fd, msg_table[116]); // Character does not have the item. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name. + return -1; + } + + return 0; +} + +/*========================================== + * @jail <char_name> by [Yor] + * Special warp! No check with nowarp and nowarpto flag + *------------------------------------------ + */ +int atcommand_jail( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + int x, y; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @jail <char_name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can jail only lower or same GM + switch(rand() % 2) { + case 0: + x = 24; + y = 75; + break; + default: + x = 49; + y = 75; + break; + } + if (pc_setpos(pl_sd, "sec_pri.gat", x, y, 3) == 0) { + pc_setsavepoint(pl_sd, "sec_pri.gat", x, y); // Save Char Respawn Point in the jail room [Lupus] + clif_displaymessage(pl_sd->fd, msg_table[117]); // GM has send you in jails. + clif_displaymessage(fd, msg_table[118]); // Player warped in jails. + } else { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @unjail/@discharge <char_name> by [Yor] + * Special warp! No check with nowarp and nowarpto flag + *------------------------------------------ + */ +int atcommand_unjail( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @unjail/@discharge <char_name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can jail only lower or same GM + if (pl_sd->bl.m != map_mapname2mapid("sec_pri.gat")) { + clif_displaymessage(fd, msg_table[119]); // This player is not in jails. + return -1; + } else if (pc_setpos(pl_sd, "prontera.gat", 156, 191, 3) == 0) { + pc_setsavepoint(pl_sd, "prontera.gat", 156, 191); // Save char respawn point in Prontera + clif_displaymessage(pl_sd->fd, msg_table[120]); // GM has discharge you. + clif_displaymessage(fd, msg_table[121]); // Player warped to Prontera. + } else { + clif_displaymessage(fd, msg_table[1]); // Map not found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @disguise <mob_id> by [Valaris] (simplified by [Yor]) + *------------------------------------------ + */ +int atcommand_disguise( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int mob_id; + + if (!message || !*message) { + clif_displaymessage(fd, "Please, enter a Monster/NPC name/id (usage: @disguise <monster_name_or_monster_ID>)."); + return -1; + } + + if ((mob_id = mobdb_searchname(message)) == 0) // check name first (to avoid possible name begining by a number) + mob_id = atoi(message); + + if ((mob_id >= 46 && mob_id <= 125) || (mob_id >= 700 && mob_id <= 718) || // NPC + (mob_id >= 721 && mob_id <= 755) || (mob_id >= 757 && mob_id <= 811) || // NPC + (mob_id >= 813 && mob_id <= 834) || // NPC + (mob_id > 1000 && mob_id < 1521)) { // monsters + if (pc_isriding(sd)) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage(fd, msg_table[227]); // Cannot wear disguise while riding a Peco. + return -1; + } + sd->disguiseflag = 1; // set to override items with disguise script [Valaris] + sd->disguise = mob_id; + pc_setpos(sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + clif_displaymessage(fd, msg_table[122]); // Disguise applied. + } else { + clif_displaymessage(fd, msg_table[123]); // Monster/NPC name/id hasn't been found. + return -1; + } + + return 0; +} + +/*========================================== + * @undisguise by [Yor] + *------------------------------------------ + */ +int atcommand_undisguise( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if (sd->disguise) { + clif_clearchar(&sd->bl, 9); + sd->disguise = 0; + pc_setpos(sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + clif_displaymessage(fd, msg_table[124]); // Undisguise applied. + } else { + clif_displaymessage(fd, msg_table[125]); // You're not disguised. + return -1; + } + + return 0; +} + +/*========================================== + * @broadcast by [Valaris] + *------------------------------------------ + */ +int atcommand_broadcast( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message) { + clif_displaymessage(fd, "Please, enter a message (usage: @broadcast <message>)."); + return -1; + } + + sprintf(output, "%s : %s", sd->status.name, message); + intif_GMmessage(output, strlen(output) + 1, 0); + + return 0; +} + +/*========================================== + * @localbroadcast by [Valaris] + *------------------------------------------ + */ +int atcommand_localbroadcast( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + + memset(output, '\0', sizeof(output)); + + if (!message || !*message) { + clif_displaymessage(fd, "Please, enter a message (usage: @localbroadcast <message>)."); + return -1; + } + + sprintf(output, "%s : %s", sd->status.name, message); + + clif_GMmessage(&sd->bl, output, strlen(output) + 1, 1); // 1: ALL_SAMEMAP + + return 0; +} + +/*========================================== + * @ignorelist by [Yor] + *------------------------------------------ + */ +int atcommand_ignorelist( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char output[200]; + int count; + int i; + + memset(output, '\0', sizeof(output)); + + count = 0; + for(i = 0; i < (int)(sizeof(sd->ignore) / sizeof(sd->ignore[0])); i++) + if (sd->ignore[i].name[0]) + count++; + + if (sd->ignoreAll == 0) + if (count == 0) + clif_displaymessage(fd, msg_table[126]); // You accept any wisp (no wisper is refused). + else { + sprintf(output, msg_table[127], count); // You accept any wisp, except thoses from %d player(s): + clif_displaymessage(fd, output); + } + else + if (count == 0) + clif_displaymessage(fd, msg_table[128]); // You refuse all wisps (no specifical wisper is refused). + else { + sprintf(output, msg_table[129], count); // You refuse all wisps, AND refuse wisps from %d player(s): + clif_displaymessage(fd, output); + } + + if (count > 0) + for(i = 0; i < (int)(sizeof(sd->ignore) / sizeof(sd->ignore[0])); i++) + if (sd->ignore[i].name[0]) + clif_displaymessage(fd, sd->ignore[i].name); + + return 0; +} + +/*========================================== + * @charignorelist <player_name> by [Yor] + *------------------------------------------ + */ +int atcommand_charignorelist( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data *pl_sd; + char output[200]; + int count; + int i; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charignorelist <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + count = 0; + for(i = 0; i < (int)(sizeof(pl_sd->ignore) / sizeof(pl_sd->ignore[0])); i++) + if (pl_sd->ignore[i].name[0]) + count++; + + if (pl_sd->ignoreAll == 0) + if (count == 0) { + sprintf(output, msg_table[130], pl_sd->status.name); // '%s' accept any wisp (no wisper is refused). + clif_displaymessage(fd, output); + } else { + sprintf(output, msg_table[131], pl_sd->status.name, count); // '%s' accept any wisp, except thoses from %d player(s): + clif_displaymessage(fd, output); + } + else + if (count == 0) { + sprintf(output, msg_table[132], pl_sd->status.name); // '%s' refuse all wisps (no specifical wisper is refused). + clif_displaymessage(fd, output); + } else { + sprintf(output, msg_table[133], pl_sd->status.name, count); // '%s' refuse all wisps, AND refuse wisps from %d player(s): + clif_displaymessage(fd, output); + } + + if (count > 0) + for(i = 0; i < (int)(sizeof(pl_sd->ignore) / sizeof(pl_sd->ignore[0])); i++) + if (pl_sd->ignore[i].name[0]) + clif_displaymessage(fd, pl_sd->ignore[i].name); + + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @inall <player_name> by [Yor] + *------------------------------------------ + */ +int atcommand_inall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @inall <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change wisp option only to lower or same level + if (pl_sd->ignoreAll == 0) { + sprintf(output, msg_table[134], pl_sd->status.name); // '%s' already accepts all wispers. + clif_displaymessage(fd, output); + return -1; + } else { + pl_sd->ignoreAll = 0; + sprintf(output, msg_table[135], pl_sd->status.name); // '%s' now accepts all wispers. + clif_displaymessage(fd, output); + // message to player + clif_displaymessage(pl_sd->fd, msg_table[136]); // A GM has authorised all wispers for you. + WFIFOW(pl_sd->fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB(pl_sd->fd,2) = 1; + WFIFOB(pl_sd->fd,3) = 0; // success + WFIFOSET(pl_sd->fd, 4); // packet_len_table[0x0d2] + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @exall <player_name> by [Yor] + *------------------------------------------ + */ +int atcommand_exall( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + char output[200]; + struct map_session_data *pl_sd; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @exall <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change wisp option only to lower or same level + if (pl_sd->ignoreAll == 1) { + sprintf(output, msg_table[137], pl_sd->status.name); // '%s' already blocks all wispers. + clif_displaymessage(fd, output); + return -1; + } else { + pl_sd->ignoreAll = 1; + sprintf(output, msg_table[138], pl_sd->status.name); // '%s' blocks now all wispers. + clif_displaymessage(fd, output); + // message to player + clif_displaymessage(pl_sd->fd, msg_table[139]); // A GM has blocked all wispers for you. + WFIFOW(pl_sd->fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB(pl_sd->fd,2) = 0; + WFIFOB(pl_sd->fd,3) = 0; // success + WFIFOSET(pl_sd->fd,4); // packet_len_table[0x0d2] + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @chardisguise <mob_id> <character> by Kalaspuff (based off Valaris' and Yor's work) + *------------------------------------------ + */ +int atcommand_chardisguise( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int mob_id; + char character[100]; + char mob_name[100]; + struct map_session_data* pl_sd; + + memset(character, '\0', sizeof(character)); + memset(mob_name, '\0', sizeof(mob_name)); + + if (!message || !*message || sscanf(message, "%s %99[^\n]", mob_name, character) < 2) { + clif_displaymessage(fd, "Please, enter a Monster/NPC name/id and a player name (usage: @chardisguise <monster_name_or_monster_ID> <char name>)."); + return -1; + } + + if ((mob_id = mobdb_searchname(mob_name)) == 0) // check name first (to avoid possible name begining by a number) + mob_id = atoi(mob_name); + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can disguise only lower or same level + if ((mob_id >= 46 && mob_id <= 125) || (mob_id >= 700 && mob_id <= 718) || // NPC + (mob_id >= 721 && mob_id <= 755) || (mob_id >= 757 && mob_id <= 811) || // NPC + (mob_id >= 813 && mob_id <= 834) || // NPC + (mob_id > 1000 && mob_id < 1521)) { // monsters + if (pc_isriding(pl_sd)) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage(fd, msg_table[228]); // Character cannot wear disguise while riding a Peco. + return -1; + } + pl_sd->disguiseflag = 1; // set to override items with disguise script [Valaris] + pl_sd->disguise = mob_id; + pc_setpos(pl_sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, 3); + clif_displaymessage(fd, msg_table[140]); // Character's disguise applied. + } else { + clif_displaymessage(fd, msg_table[123]); // Monster/NPC name/id hasn't been found. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @charundisguise <character> by Kalaspuff (based off Yor's work) + *------------------------------------------ + */ +int atcommand_charundisguise( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + struct map_session_data* pl_sd; + + memset(character, '\0', sizeof(character)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charundisguise <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can undisguise only lower or same level + if (pl_sd->disguise) { + clif_clearchar(&pl_sd->bl, 9); + pl_sd->disguise = 0; + pc_setpos(pl_sd, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y, 3); + clif_displaymessage(fd, msg_table[141]); // Character's undisguise applied. + } else { + clif_displaymessage(fd, msg_table[142]); // Character is not disguised. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @email <actual@email> <new@email> by [Yor] + *------------------------------------------ + */ +int atcommand_email( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char actual_email[100]; + char new_email[100]; + + memset(actual_email, '\0', sizeof(actual_email)); + memset(new_email, '\0', sizeof(new_email)); + + if (!message || !*message || sscanf(message, "%99s %99s", actual_email, new_email) < 2) { + clif_displaymessage(fd, "Please enter 2 emails (usage: @email <actual@email> <new@email>)."); + return -1; + } + + if (e_mail_check(actual_email) == 0) { + clif_displaymessage(fd, msg_table[144]); // Invalid actual email. If you have default e-mail, give a@a.com. + return -1; + } else if (e_mail_check(new_email) == 0) { + clif_displaymessage(fd, msg_table[145]); // Invalid new email. Please enter a real e-mail. + return -1; + } else if (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; + int type = 0, flag = 0, i; + + if (!message || !*message || sscanf(message, "%d %d", &type,&flag) < 2) { + clif_displaymessage(fd, "Please, enter at least a option (usage: @effect <type+>)."); + return -1; + } + if(flag <=0){ + clif_specialeffect(&sd->bl, type, flag); + clif_displaymessage(fd, msg_table[229]); // Your effect has changed. + } + else{ + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + clif_specialeffect(&pl_sd->bl, type, flag); + clif_displaymessage(pl_sd->fd, msg_table[229]); // Your effect has changed. + } + } + } + + return 0; +} + +/*========================================== + * @charitemlist <character>: Displays the list of a player's items. + *------------------------------------------ + */ +int +atcommand_character_item_list( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + struct item_data *item_data, *item_temp; + int i, j, equip, count, counter, counter2; + char character[100], output[200], equipstr[100], outputtmp[200]; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + memset(equipstr, '\0', sizeof(equipstr)); + memset(outputtmp, '\0', sizeof(outputtmp)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charitemlist <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level + counter = 0; + count = 0; + for (i = 0; i < MAX_INVENTORY; i++) { + if (pl_sd->status.inventory[i].nameid > 0 && (item_data = itemdb_search(pl_sd->status.inventory[i].nameid)) != NULL) { + counter = counter + pl_sd->status.inventory[i].amount; + count++; + if (count == 1) { + sprintf(output, "------ Items list of '%s' ------", pl_sd->status.name); + clif_displaymessage(fd, output); + } + if ((equip = pl_sd->status.inventory[i].equip)) { + strcpy(equipstr, "| equiped: "); + if (equip & 4) + strcat(equipstr, "robe/gargment, "); + if (equip & 8) + strcat(equipstr, "left accessory, "); + if (equip & 16) + strcat(equipstr, "body/armor, "); + if ((equip & 34) == 2) + strcat(equipstr, "right hand, "); + if ((equip & 34) == 32) + strcat(equipstr, "left hand, "); + if ((equip & 34) == 34) + strcat(equipstr, "both hands, "); + if (equip & 64) + strcat(equipstr, "feet, "); + if (equip & 128) + strcat(equipstr, "right accessory, "); + if ((equip & 769) == 1) + strcat(equipstr, "lower head, "); + if ((equip & 769) == 256) + strcat(equipstr, "top head, "); + if ((equip & 769) == 257) + strcat(equipstr, "lower/top head, "); + if ((equip & 769) == 512) + strcat(equipstr, "mid head, "); + if ((equip & 769) == 512) + strcat(equipstr, "lower/mid head, "); + if ((equip & 769) == 769) + strcat(equipstr, "lower/mid/top head, "); + // remove final ', ' + equipstr[strlen(equipstr) - 2] = '\0'; + } else + memset(equipstr, '\0', sizeof(equipstr)); + if (sd->status.inventory[i].refine) + sprintf(output, "%d %s %+d (%s %+d, id: %d) %s", pl_sd->status.inventory[i].amount, item_data->name, pl_sd->status.inventory[i].refine, item_data->jname, pl_sd->status.inventory[i].refine, pl_sd->status.inventory[i].nameid, equipstr); + else + sprintf(output, "%d %s (%s, id: %d) %s", pl_sd->status.inventory[i].amount, item_data->name, item_data->jname, pl_sd->status.inventory[i].nameid, equipstr); + clif_displaymessage(fd, output); + memset(output, '\0', sizeof(output)); + counter2 = 0; + for (j = 0; j < item_data->slot; j++) { + if (pl_sd->status.inventory[i].card[j]) { + if ((item_temp = itemdb_search(pl_sd->status.inventory[i].card[j])) != NULL) { + if (output[0] == '\0') + sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + else + sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + strcat(output, outputtmp); + } + } + } + if (output[0] != '\0') { + output[strlen(output) - 2] = ')'; + output[strlen(output) - 1] = '\0'; + clif_displaymessage(fd, output); + } + } + } + if (count == 0) + clif_displaymessage(fd, "No item found on this player."); + else { + sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @charstoragelist <character>: Displays the items list of a player's storage. + *------------------------------------------ + */ +int +atcommand_character_storage_list( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct storage *stor; + struct map_session_data *pl_sd; + struct item_data *item_data, *item_temp; + int i, j, count, counter, counter2; + char character[100], output[200], outputtmp[200]; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + memset(outputtmp, '\0', sizeof(outputtmp)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charitemlist <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level + if((stor = account2storage2(pl_sd->status.account_id)) != NULL) { + counter = 0; + count = 0; + for (i = 0; i < MAX_STORAGE; i++) { + if (stor->storage[i].nameid > 0 && (item_data = itemdb_search(stor->storage[i].nameid)) != NULL) { + counter = counter + stor->storage[i].amount; + count++; + if (count == 1) { + sprintf(output, "------ Storage items list of '%s' ------", pl_sd->status.name); + clif_displaymessage(fd, output); + } + if (stor->storage[i].refine) + sprintf(output, "%d %s %+d (%s %+d, id: %d)", stor->storage[i].amount, item_data->name, stor->storage[i].refine, item_data->jname, stor->storage[i].refine, stor->storage[i].nameid); + else + sprintf(output, "%d %s (%s, id: %d)", stor->storage[i].amount, item_data->name, item_data->jname, stor->storage[i].nameid); + clif_displaymessage(fd, output); + memset(output, '\0', sizeof(output)); + counter2 = 0; + for (j = 0; j < item_data->slot; j++) { + if (stor->storage[i].card[j]) { + if ((item_temp = itemdb_search(stor->storage[i].card[j])) != NULL) { + if (output[0] == '\0') + sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + else + sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + strcat(output, outputtmp); + } + } + } + if (output[0] != '\0') { + output[strlen(output) - 2] = ')'; + output[strlen(output) - 1] = '\0'; + clif_displaymessage(fd, output); + } + } + } + if (count == 0) + clif_displaymessage(fd, "No item found in the storage of this player."); + else { + sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, "This player has no storage."); + return -1; + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @charcartlist <character>: Displays the items list of a player's cart. + *------------------------------------------ + */ +int +atcommand_character_cart_list( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd; + struct item_data *item_data, *item_temp; + int i, j, count, counter, counter2; + char character[100], output[200], outputtmp[200]; + + memset(character, '\0', sizeof(character)); + memset(output, '\0', sizeof(output)); + memset(outputtmp, '\0', sizeof(outputtmp)); + + if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) { + clif_displaymessage(fd, "Please, enter a player name (usage: @charitemlist <char name>)."); + return -1; + } + + if ((pl_sd = map_nick2sd(character)) != NULL) { + if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level + counter = 0; + count = 0; + for (i = 0; i < MAX_CART; i++) { + if (pl_sd->status.cart[i].nameid > 0 && (item_data = itemdb_search(pl_sd->status.cart[i].nameid)) != NULL) { + counter = counter + pl_sd->status.cart[i].amount; + count++; + if (count == 1) { + sprintf(output, "------ Cart items list of '%s' ------", pl_sd->status.name); + clif_displaymessage(fd, output); + } + if (pl_sd->status.cart[i].refine) + sprintf(output, "%d %s %+d (%s %+d, id: %d)", pl_sd->status.cart[i].amount, item_data->name, pl_sd->status.cart[i].refine, item_data->jname, pl_sd->status.cart[i].refine, pl_sd->status.cart[i].nameid); + else + sprintf(output, "%d %s (%s, id: %d)", pl_sd->status.cart[i].amount, item_data->name, item_data->jname, pl_sd->status.cart[i].nameid); + clif_displaymessage(fd, output); + memset(output, '\0', sizeof(output)); + counter2 = 0; + for (j = 0; j < item_data->slot; j++) { + if (pl_sd->status.cart[i].card[j]) { + if ((item_temp = itemdb_search(pl_sd->status.cart[i].card[j])) != NULL) { + if (output[0] == '\0') + sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + else + sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname); + strcat(output, outputtmp); + } + } + } + if (output[0] != '\0') { + output[strlen(output) - 2] = ')'; + output[strlen(output) - 1] = '\0'; + clif_displaymessage(fd, output); + } + } + } + if (count == 0) + clif_displaymessage(fd, "No item found in the cart of this player."); + else { + sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count); + clif_displaymessage(fd, output); + } + } else { + clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. + return -1; + } + } else { + clif_displaymessage(fd, msg_table[3]); // Character not found. + return -1; + } + + return 0; +} + +/*========================================== + * @killer by MouseJstr + * enable killing players even when not in pvp + *------------------------------------------ + */ +int +atcommand_killer( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + sd->special_state.killer = !sd->special_state.killer; + + if(sd->special_state.killer) + clif_displaymessage(fd, msg_table[241]); + else + clif_displaymessage(fd, msg_table[242]); + + return 0; +} + +/*========================================== + * @killable by MouseJstr + * enable other people killing you + *------------------------------------------ + */ +int +atcommand_killable( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + sd->special_state.killable = !sd->special_state.killable; + + if(sd->special_state.killable) + clif_displaymessage(fd, msg_table[242]); + else + clif_displaymessage(fd, msg_table[241]); + + return 0; +} + +/*========================================== + * @charkillable by MouseJstr + * enable another player to be killed + *------------------------------------------ + */ +int +atcommand_charkillable( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd = NULL; + + if (!message || !*message) + return -1; + + if((pl_sd=map_nick2sd((char *) message)) == NULL) + return -1; + + pl_sd->special_state.killable = !pl_sd->special_state.killable; + + if(pl_sd->special_state.killable) + clif_displaymessage(fd, "The player is now killable"); + else + clif_displaymessage(fd, "The player is no longer killable"); + + return 0; +} + + +/*========================================== + * @skillon by MouseJstr + * turn skills on for the map + *------------------------------------------ + */ +int +atcommand_skillon( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + map[sd->bl.m].flag.noskill = 0; + clif_displaymessage(fd, msg_table[244]); + return 0; +} + +/*========================================== + * @skilloff by MouseJstr + * Turn skills off on the map + *------------------------------------------ + */ +int +atcommand_skilloff( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + map[sd->bl.m].flag.noskill = 1; + clif_displaymessage(fd, msg_table[243]); + return 0; +} + +/*========================================== + * @npcmove by MouseJstr + * + * move a npc + *------------------------------------------ + */ +int +atcommand_npcmove(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char character[100]; + int x = 0, y = 0; + struct npc_data *nd = 0; + + if( sd == NULL ) + return -1; + + if (!message || !*message) + return -1; + + memset(character, '\0', sizeof character); + + if (sscanf(message, "%d %d %99[^\n]", &x, &y, character) < 4) + return -1; + + nd=npc_name2id(character); + if (nd==NULL) + return -1; + + npc_enable(character, 0); + nd->bl.x = x; + nd->bl.y = y; + npc_enable(character, 1); + + return 0; +} + +/*========================================== + * @addwarp by MouseJstr + * + * Create a new static warp point. + *------------------------------------------ + */ +int +atcommand_addwarp(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char w1[64], w3[64], w4[64]; + char map[30], output[200]; + int x,y,ret; + + if (!message || !*message) + return -1; + + if (sscanf(message, "%99s %d %d[^\n]", map, &x, &y ) < 3) + return -1; + + sprintf(w1,"%s,%d,%d", sd->mapname, sd->bl.x, sd->bl.y); + sprintf(w3,"%s%d%d%d%d", map,sd->bl.x, sd->bl.y, x, y); + sprintf(w4,"1,1,%s.gat,%d,%d", map, x, y); + + ret = npc_parse_warp(w1, "warp", w3, w4); + + sprintf(output, "New warp NPC => %s",w3); + + clif_displaymessage(fd, output); + + return ret; +} + +/*========================================== + * @follow by [MouseJstr] + * + * Follow a player .. staying no more then 5 spaces away + *------------------------------------------ + */ +int +atcommand_follow(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd = NULL; + + if (!message || !*message) + return -1; + if((pl_sd=map_nick2sd((char *) message)) != NULL) + pc_follow(sd, pl_sd->bl.id); + else + return 1; + return 0; +} + + +/*========================================== + * @chareffect by [MouseJstr] + * + * Create a effect localized on another character + *------------------------------------------ + */ +int +atcommand_chareffect(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd = NULL; + char target[255]; + int type = 0; + + if (!message || !*message || sscanf(message, "%d %s", &type, target) != 2) { + clif_displaymessage(fd, "usage: @chareffect <type+> <target>."); + return -1; + } + + if((pl_sd=map_nick2sd((char *) target)) == NULL) + return -1; + + clif_specialeffect(&pl_sd->bl, type, 0); + clif_displaymessage(fd, msg_table[229]); // Your effect has changed. + + return 0; +} +/*========================================== + * @dropall by [MouseJstr] + * + * Drop all your possession on the ground + *------------------------------------------ + */ +int +atcommand_dropall(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + for (i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].amount) { + if(sd->status.inventory[i].equip != 0) + pc_unequipitem(sd, i, 0); + pc_dropitem(sd, i, sd->status.inventory[i].amount); + } + } + return 0; +} +/*========================================== + * @chardropall by [MouseJstr] + * + * Throw all the characters possessions on the ground. Normally + * done in response to them being disrespectful of a GM + *------------------------------------------ + */ +int +atcommand_chardropall(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + struct map_session_data *pl_sd = NULL; + + if (!message || !*message) + return -1; + if((pl_sd=map_nick2sd((char *) message)) == NULL) + return -1; + for (i = 0; i < MAX_INVENTORY; i++) { + if (pl_sd->status.inventory[i].amount) { + if(pl_sd->status.inventory[i].equip != 0) + pc_unequipitem(pl_sd, i, 0); + pc_dropitem(pl_sd, i, pl_sd->status.inventory[i].amount); + } + } + + clif_displaymessage(pl_sd->fd, "Ever play 52 card pickup?"); + clif_displaymessage(fd, "It is done"); + //clif_displaymessage(fd, "It is offical.. your a jerk"); + + return 0; +} +/*========================================== + * @storeall by [MouseJstr] + * + * Put everything into storage to simplify your inventory to make + * debugging easie + *------------------------------------------ + */ +int +atcommand_storeall(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + 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, 0); + storage_storageadd(sd, i, sd->status.inventory[i].amount); + } + } + storage_storageclose(sd); + + clif_displaymessage(fd, "It is done"); + return 0; +} +/*========================================== + * @charstoreall by [MouseJstr] + * + * A way to screw with players who piss you off + *------------------------------------------ + */ +int +atcommand_charstoreall(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i; + struct map_session_data *pl_sd = NULL; + + if (!message || !*message) + return -1; + if((pl_sd=map_nick2sd((char *) message)) == NULL) + return -1; + + if (storage_storageopen(pl_sd) == 1) { + clif_displaymessage(fd, "Had to open the characters storage window..."); + clif_displaymessage(fd, "run this command again.."); + return 0; + } + for (i = 0; i < MAX_INVENTORY; i++) { + if (pl_sd->status.inventory[i].amount) { + if(pl_sd->status.inventory[i].equip != 0) + pc_unequipitem(pl_sd, i, 0); + storage_storageadd(pl_sd, i, sd->status.inventory[i].amount); + } + } + storage_storageclose(pl_sd); + + clif_displaymessage(pl_sd->fd, "Everything you own has been put away for safe keeping."); + clif_displaymessage(pl_sd->fd, "go to the nearest kafka to retrieve it.."); + clif_displaymessage(pl_sd->fd, " -- the management"); + + clif_displaymessage(fd, "It is done"); + + return 0; +} +/*========================================== + * @skillid by [MouseJstr] + * + * lookup a skill by name + *------------------------------------------ + */ +int +atcommand_skillid(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int skillen = 0, idx = 0; + if (!message || !*message) + return -1; + skillen = strlen(message); + while (skill_names[idx].id != 0) { + if ((strnicmp(skill_names[idx].name, message, skillen) == 0) || + (strnicmp(skill_names[idx].desc, message, skillen) == 0)) { + char output[255]; + sprintf(output, "skill %d: %s", skill_names[idx].id, skill_names[idx].desc); + clif_displaymessage(fd, output); + } + idx++; + } + return 0; +} +/*========================================== + * @useskill by [MouseJstr] + * + * A way of using skills without having to find them in the skills menu + *------------------------------------------ + */ +int +atcommand_useskill(const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd = NULL; + int skillnum; + int skilllv; + int inf; + char target[255]; + + if (!message || !*message) + return -1; + if(sscanf(message, "%d %d %s", &skillnum, &skilllv, target) != 3) { + clif_displaymessage(fd, "Usage: @useskill <skillnum> <skillv> <target>"); + return -1; + } + if((pl_sd=map_nick2sd(target)) == NULL) { + return -1; + } + + inf = skill_get_inf(skillnum); + + if ((inf == 2) || (inf == 1)) + skill_use_pos(sd, pl_sd->bl.x, pl_sd->bl.y, skillnum, skilllv); + else + skill_use_id(sd, pl_sd->bl.id, skillnum, skilllv); + + return 0; +} +/*========================================== + * It is made to rain. + *------------------------------------------ + */ +int +atcommand_rain( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int effno = 0; + effno = 161; + nullpo_retr(-1, sd); + if (effno < 0 || map[sd->bl.m].flag.rain) + return -1; + + map[sd->bl.m].flag.rain=1; + clif_specialeffect(&sd->bl,effno,2); + return 0; +} +/*========================================== + * It is made to snow. + *------------------------------------------ + */ +int +atcommand_snow( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int effno = 0; + effno = 162; + nullpo_retr(-1, sd); + if (effno < 0 || map[sd->bl.m].flag.snow) + return -1; + + map[sd->bl.m].flag.snow=1; + clif_specialeffect(&sd->bl,effno,2); + return 0; +} + +/*========================================== + * Cherry tree snowstorm is made to fall. (Sakura) + *------------------------------------------ + */ +int +atcommand_sakura( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int effno = 0; + effno = 163; + nullpo_retr(-1, sd); + if (effno < 0 || map[sd->bl.m].flag.sakura) + return -1; + + map[sd->bl.m].flag.sakura=1; + clif_specialeffect(&sd->bl,effno,2); + return 0; +} + +/*========================================== + * Fog hangs over. + *------------------------------------------ + */ +int +atcommand_fog( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int effno = 0; + effno = 233; + nullpo_retr(-1, sd); + if (effno < 0 || map[sd->bl.m].flag.fog) + return -1; + + map[sd->bl.m].flag.fog=1; + clif_specialeffect(&sd->bl,effno,2); + + return 0; +} + +/*========================================== + * Fallen leaves fall. + *------------------------------------------ + */ +int +atcommand_leaves( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int effno = 0; + effno = 333; + nullpo_retr(-1, sd); + if (effno < 0 || map[sd->bl.m].flag.leaves) + return -1; + + map[sd->bl.m].flag.leaves=1; + clif_specialeffect(&sd->bl,effno,2); + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int +atcommand_summon( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + char name[100]; + int mob_id = 0; + int x = 0; + int y = 0; + int id = 0; + struct mob_data *md; + unsigned int tick=gettick(); + + nullpo_retr(-1, sd); + + if (!message || !*message) + return -1; + if (sscanf(message, "%99s", name) < 1) + return -1; + + if ((mob_id = atoi(name)) == 0) + mob_id = mobdb_searchname(name); + if(mob_id == 0) + return -1; + + x = sd->bl.x + (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->state.special_mob_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,x,y,tick); + + return 0; +} + + +/*========================================== + * @adjcmdlvl by [MouseJstr] + * + * Temp adjust the GM level required to use a GM command + * + * Used during beta testing to allow players to use GM commands + * for short periods of time + *------------------------------------------ + */ +int +atcommand_adjcmdlvl( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + int i, newlev; + char cmd[100]; + + if (!message || !*message || sscanf(message, "%d %s", &newlev, cmd) != 2) { + clif_displaymessage(fd, "usage: @adjcmdlvl <lvl> <command>."); + return -1; + } + + for (i = 0; atcommand_info[i].type != AtCommand_None; i++) + if (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[100]; + struct map_session_data *pl_sd; + + if (!message || !*message || sscanf(message, "%d %s", &newlev, user) != 2) { + clif_displaymessage(fd, "usage: @adjgmlvl <lvl> <user>."); + return -1; + } + + if((pl_sd=map_nick2sd((char *) user)) == NULL) + return -1; + + pc_set_gm_level(pl_sd->status.account_id, newlev); + + return 0; +} + + +/*========================================== + * @trade by [MouseJstr] + * + * Open a trade window with a remote player + * + * If I have to jump to a remote player one more time, I am + * gonna scream! + *------------------------------------------ + */ +int +atcommand_trade( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd = NULL; + + if (!message || !*message) + return -1; + if((pl_sd=map_nick2sd((char *) message)) != NULL) { + trade_traderequest(sd, pl_sd->bl.id); + return 0; + } + return -1; +} + + +/*=========================== + * @unmute [Valaris] + *=========================== +*/ +int atcommand_unmute( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + struct map_session_data *pl_sd = NULL; + if (!message || !*message) + return -1; + + if((pl_sd=map_nick2sd((char *) message)) != NULL) { + if(pl_sd->sc_data[SC_NOCHAT].timer!=-1) { + skill_status_change_end(&pl_sd->bl,SC_NOCHAT,-1); + clif_displaymessage(sd->fd,"Player unmuted"); + } + else + clif_displaymessage(sd->fd,"Player is not muted"); + } + + return 0; +} + +#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(!battle_config.mail_system) + 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) +{ + if(!battle_config.mail_system) + return 0; + + nullpo_retr(-1, sd); + + if (!message || !*message) { + clif_displaymessage(sd->fd,"You must specify a message number."); + return 0; + } + + if(strlen(command)==11) + mail_delete(sd,atoi(message)); + else + mail_read(sd,atoi(message)); + + return 0; +} + +int atcommand_sendmail( + const int fd, struct map_session_data* sd, + const char* command, const char* message) +{ + if(!battle_config.mail_system) + return 0; + + char name[24],text[80]; + + nullpo_retr(-1, sd); + + if (!message || !*message) { + clif_displaymessage(sd->fd,"You must specify a recipient and a message."); + return 0; + } + + if ((sscanf(message, "\"%[^\"]\" %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) +{ + nullpo_retr(-1, sd); + + char_online_check(); + + return 0; +} + +#endif /* end sql only */ diff --git a/src/map/atcommand.h b/src/map/atcommand.h new file mode 100644 index 0000000..06029a5 --- /dev/null +++ b/src/map/atcommand.h @@ -0,0 +1,245 @@ +// $Id: atcommand.h 148 2004-09-30 14:05:37Z MouseJstr $ +#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_Heal, + AtCommand_Item, + AtCommand_Item2, + AtCommand_ItemReset, + AtCommand_ItemCheck, + AtCommand_BaseLevelUp, + AtCommand_JobLevelUp, + AtCommand_H, + AtCommand_Help, + AtCommand_GM, + AtCommand_PvPOff, + AtCommand_PvPOn, + AtCommand_GvGOff, + AtCommand_GvGOn, + AtCommand_Model, + AtCommand_Go, + AtCommand_Spawn, + AtCommand_Monster, + AtCommand_KillMonster, + AtCommand_KillMonster2, + AtCommand_Refine, + AtCommand_Produce, + AtCommand_Memo, + AtCommand_GAT, + AtCommand_Packet, + AtCommand_StatusPoint, + AtCommand_SkillPoint, + AtCommand_Zeny, + AtCommand_Param, + AtCommand_Strength, + AtCommand_Agility, + AtCommand_Vitality, + AtCommand_Intelligence, + AtCommand_Dexterity, + AtCommand_Luck, + AtCommand_GuildLevelUp, + AtCommand_MakeEgg, + AtCommand_PetFriendly, + AtCommand_PetHungry, + AtCommand_PetRename, + AtCommand_CharPetRename, // by Yor + AtCommand_Recall, + AtCommand_CharacterJob, + AtCommand_CharacterJob2, + AtCommand_CharacterJob3, + AtCommand_Revive, + AtCommand_CharacterStats, + AtCommand_CharacterStatsAll, + AtCommand_CharacterOption, + AtCommand_CharacterSave, + AtCommand_CharacterLoad, + AtCommand_Night, + AtCommand_Day, + AtCommand_Doom, + AtCommand_DoomMap, + AtCommand_Raise, + AtCommand_RaiseMap, + AtCommand_CharacterBaseLevel, + AtCommand_CharacterJobLevel, + AtCommand_Kick, + AtCommand_KickAll, + AtCommand_AllSkill, + AtCommand_QuestSkill, + AtCommand_CharQuestSkill, + AtCommand_LostSkill, + AtCommand_CharLostSkill, + AtCommand_SpiritBall, + AtCommand_Party, + AtCommand_Guild, + AtCommand_AgitStart, + AtCommand_AgitEnd, + AtCommand_MapExit, + AtCommand_IDSearch, + AtCommand_CharSkReset, + AtCommand_CharStReset, + AtCommand_CharReset, + //by chbrules + AtCommand_CharModel, + AtCommand_CharSKPoint, + AtCommand_CharSTPoint, + AtCommand_CharZeny, + AtCommand_RecallAll, + AtCommand_ReloadItemDB, + AtCommand_ReloadMobDB, + AtCommand_ReloadSkillDB, +#ifndef TXT_ONLY + AtCommand_Rehash, +#else /* TXT_ONLY */ + AtCommand_ReloadScript, +#endif /* TXT_ONLY */ + AtCommand_ReloadGMDB, + AtCommand_MapInfo, + AtCommand_Dye, + AtCommand_Hstyle, + AtCommand_Hcolor, + AtCommand_StatAll, + AtCommand_CharChangeSex, // by Yor + AtCommand_CharBlock, // by Yor + AtCommand_CharBan, // by Yor + AtCommand_CharUnBlock, // by Yor + AtCommand_CharUnBan, // by Yor + AtCommand_MountPeco, // by Valaris + AtCommand_CharMountPeco, // by Yor + AtCommand_GuildSpy, // [Syrus22] + AtCommand_PartySpy, // [Syrus22] + AtCommand_RepairAll, // [Valaris] + AtCommand_GuildRecall, // by Yor + AtCommand_PartyRecall, // by Yor +// AtCommand_Nuke, // [Valaris] + AtCommand_Enablenpc, + AtCommand_Disablenpc, + AtCommand_ServerTime, // by Yor + AtCommand_CharDelItem, // by Yor + AtCommand_Jail, // by Yor + AtCommand_UnJail, // by Yor + AtCommand_Disguise, // [Valaris] + AtCommand_UnDisguise, // by Yor + AtCommand_IgnoreList, // by Yor + AtCommand_CharIgnoreList, // by Yor + AtCommand_InAll, // by Yor + AtCommand_ExAll, // by Yor + AtCommand_CharDisguise, // Kalaspuff + AtCommand_CharUnDisguise, // Kalaspuff + AtCommand_EMail, // by Yor + AtCommand_Hatch, + AtCommand_Effect, // by Apple + AtCommand_Char_Item_List, // by Yor + AtCommand_Char_Storage_List, // by Yor + AtCommand_Char_Cart_List, // by Yor + AtCommand_AddWarp, // by MouseJstr + AtCommand_Follow, // by MouseJstr + AtCommand_SkillOn, // by MouseJstr + AtCommand_SkillOff, // by MouseJstr + AtCommand_Killer, // by MouseJstr + AtCommand_NpcMove, // by MouseJstr + AtCommand_Killable, // by MouseJstr + AtCommand_CharKillable, // by MouseJstr + AtCommand_Chareffect, // by MouseJstr + AtCommand_Chardye, // by MouseJstr + AtCommand_Charhairstyle, // by MouseJstr + AtCommand_Charhaircolor, // by MouseJstr + AtCommand_Dropall, // by MouseJstr + AtCommand_Chardropall, // by MouseJstr + AtCommand_Storeall, // by MouseJstr + AtCommand_Charstoreall, // by MouseJstr + AtCommand_Skillid, // by MouseJstr + AtCommand_Useskill, // by MouseJstr + AtCommand_Summon, + AtCommand_Rain, + AtCommand_Snow, + AtCommand_Sakura, + AtCommand_Fog, + AtCommand_Leaves, + AtCommand_AdjGmLvl, + AtCommand_AdjCmdLvl, + AtCommand_Trade, + AtCommand_UnMute, + + // 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_Sound, // [Valaris] + AtCommand_RefreshOnline, // [Valaris] + // SQL-only commands end +#endif + + // end + AtCommand_Unknown, + AtCommand_MAX +}; + +typedef enum AtCommandType AtCommandType; + +typedef struct AtCommandInfo { + AtCommandType type; + const char* command; + int level; + int (*proc)(const int, struct map_session_data*, + const char* command, const char* message); +} AtCommandInfo; + +AtCommandType +is_atcommand(const int fd, struct map_session_data* sd, const char* message, int gmlvl); + +AtCommandType atcommand( + const int level, const char* message, AtCommandInfo* info); +int get_atcommand_level(const AtCommandType type); + +char * msg_txt(int msg_number); // [Yor] + +int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Valaris] +int atcommand_rura(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Yor] +int atcommand_spawn(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Valaris] +int atcommand_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_config_read(const char *cfgName); +int msg_config_read(const char *cfgName); + +#endif + diff --git a/src/map/battle.c b/src/map/battle.c new file mode 100644 index 0000000..7a6dd47 --- /dev/null +++ b/src/map/battle.c @@ -0,0 +1,5374 @@ +// $Id: battle.c,v 1.10 2004/09/29 21:08:17 Akitasha Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "battle.h" + +#include "timer.h" +#include "nullpo.h" +#include "malloc.h" + +#include "map.h" +#include "pc.h" +#include "skill.h" +#include "mob.h" +#include "itemdb.h" +#include "clif.h" +#include "pet.h" +#include "guild.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +int attr_fix_table[4][10][10]; + +struct Battle_Config battle_config; + +/*========================================== + * 二点間の距離を返す + * 戻りは整数で0以上 + *------------------------------------------ + */ +static int distance(int x0,int y0,int x1,int y1) +{ + int dx,dy; + + dx=abs(x0-x1); + dy=abs(y0-y1); + return dx>dy ? dx : dy; +} + +/*========================================== + * 自分をロックしている対象の数を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_counttargeted(struct block_list *bl,struct block_list *src,int target_lv) +{ + nullpo_retr(0, bl); + if(bl->type == BL_PC) + return pc_counttargeted((struct map_session_data *)bl,src,target_lv); + else if(bl->type == BL_MOB) + return mob_counttargeted((struct mob_data *)bl,src,target_lv); + return 0; +} +/*========================================== + * 対象のClassを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_class(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return ((struct mob_data *)bl)->class; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->status.class; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return ((struct pet_data *)bl)->class; + else + return 0; +} +/*========================================== + * 対象の方向を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_dir(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return ((struct mob_data *)bl)->dir; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->dir; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return ((struct pet_data *)bl)->dir; + else + return 0; +} +/*========================================== + * 対象のレベルを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_lv(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return mob_db[((struct mob_data *)bl)->class].lv; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->status.base_level; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return ((struct pet_data *)bl)->msd->pet.level; + else + return 0; +} +/*========================================== + * 対象の射程を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_range(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return mob_db[((struct mob_data *)bl)->class].range; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->attackrange; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return mob_db[((struct pet_data *)bl)->class].range; + else + return 0; +} +/*========================================== + * 対象のHPを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_hp(struct block_list *bl) +{ + nullpo_retr(1, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return ((struct mob_data *)bl)->hp; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->status.hp; + else + return 1; +} +/*========================================== + * 対象のMHPを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_max_hp(struct block_list *bl) +{ + nullpo_retr(1, bl); + if(bl->type==BL_PC && ((struct map_session_data *)bl)) + return ((struct map_session_data *)bl)->status.max_hp; + else { + struct status_change *sc_data=battle_get_sc_data(bl); + int max_hp=1; + if(bl->type==BL_MOB && ((struct mob_data*)bl)) { + max_hp = mob_db[((struct mob_data*)bl)->class].max_hp; + if(mob_db[((struct mob_data*)bl)->class].mexp > 0) { + if(battle_config.mvp_hp_rate != 100) + max_hp = (max_hp * battle_config.mvp_hp_rate)/100; + } + else { + if(battle_config.monster_hp_rate != 100) + max_hp = (max_hp * battle_config.monster_hp_rate)/100; + } + } + else if(bl->type==BL_PET && ((struct pet_data*)bl)) { + max_hp = mob_db[((struct pet_data*)bl)->class].max_hp; + if(mob_db[((struct pet_data*)bl)->class].mexp > 0) { + if(battle_config.mvp_hp_rate != 100) + max_hp = (max_hp * battle_config.mvp_hp_rate)/100; + } + else { + if(battle_config.monster_hp_rate != 100) + max_hp = (max_hp * battle_config.monster_hp_rate)/100; + } + } + if(sc_data) { + if(sc_data[SC_APPLEIDUN].timer!=-1) + max_hp += ((5+sc_data[SC_APPLEIDUN].val1*2+((sc_data[SC_APPLEIDUN].val2+1)>>1) + +sc_data[SC_APPLEIDUN].val3/10) * max_hp)/100; + } + if(max_hp < 1) max_hp = 1; + return max_hp; + } + return 1; +} +/*========================================== + * 対象のStrを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_str(struct block_list *bl) +{ + int str=0; + struct status_change *sc_data; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_MOB && ((struct mob_data *)bl)) + str = mob_db[((struct mob_data *)bl)->class].str; + else if(bl->type==BL_PC && ((struct map_session_data *)bl)) + return ((struct map_session_data *)bl)->paramc[0]; + else if(bl->type==BL_PET && ((struct pet_data *)bl)) + str = mob_db[((struct pet_data *)bl)->class].str; + + if(sc_data) { + if(sc_data[SC_LOUD].timer!=-1 && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) + str += 4; + if( sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC){ // ブレッシング + int race=battle_get_race(bl); + if(battle_check_undead(race,battle_get_elem_type(bl)) || race==6 ) str >>= 1; // 悪 魔/不死 + else str += sc_data[SC_BLESSING].val1; // その他 + } + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト + str += 5; + } + if(str < 0) str = 0; + return str; +} +/*========================================== + * 対象のAgiを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ + +int battle_get_agi(struct block_list *bl) +{ + int agi=0; + struct status_change *sc_data; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + agi=mob_db[((struct mob_data *)bl)->class].agi; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + agi=((struct map_session_data *)bl)->paramc[1]; + else if(bl->type==BL_PET && (struct pet_data *)bl) + agi=mob_db[((struct pet_data *)bl)->class].agi; + + if(sc_data) { + if( sc_data[SC_INCREASEAGI].timer!=-1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1 && + bl->type != BL_PC) // 速度増加(PCはpc.cで) + agi += 2+sc_data[SC_INCREASEAGI].val1; + + if(sc_data[SC_CONCENTRATE].timer!=-1 && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) + agi += agi*(2+sc_data[SC_CONCENTRATE].val1)/100; + + if(sc_data[SC_DECREASEAGI].timer!=-1) // 速度減少 + agi -= 2+sc_data[SC_DECREASEAGI].val1; + + if(sc_data[SC_QUAGMIRE].timer!=-1 ) // クァグマイア + agi >>= 1; + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト + agi += 5; + } + if(agi < 0) agi = 0; + return agi; +} +/*========================================== + * 対象のVitを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_vit(struct block_list *bl) +{ + int vit=0; + struct status_change *sc_data; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + vit=mob_db[((struct mob_data *)bl)->class].vit; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + vit=((struct map_session_data *)bl)->paramc[2]; + else if(bl->type==BL_PET && (struct pet_data *)bl) + vit=mob_db[((struct pet_data *)bl)->class].vit; + if(sc_data) { + if(sc_data[SC_STRIPARMOR].timer != -1 && bl->type!=BL_PC) + vit = vit*60/100; + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト + vit += 5; + } + + if(vit < 0) vit = 0; + return vit; +} +/*========================================== + * 対象のIntを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_int(struct block_list *bl) +{ + int int_=0; + struct status_change *sc_data; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + int_=mob_db[((struct mob_data *)bl)->class].int_; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + int_=((struct map_session_data *)bl)->paramc[3]; + else if(bl->type==BL_PET && (struct pet_data *)bl) + int_=mob_db[((struct pet_data *)bl)->class].int_; + + if(sc_data) { + if( sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC){ // ブレッシング + int race=battle_get_race(bl); + if(battle_check_undead(race,battle_get_elem_type(bl)) || race==6 ) int_ >>= 1; // 悪 魔/不死 + else int_ += sc_data[SC_BLESSING].val1; // その他 + } + if( sc_data[SC_STRIPHELM].timer != -1 && bl->type != BL_PC) + int_ = int_*60/100; + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト + int_ += 5; + } + if(int_ < 0) int_ = 0; + return int_; +} +/*========================================== + * 対象のDexを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_dex(struct block_list *bl) +{ + int dex=0; + struct status_change *sc_data; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + dex=mob_db[((struct mob_data *)bl)->class].dex; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + dex=((struct map_session_data *)bl)->paramc[4]; + else if(bl->type==BL_PET && (struct pet_data *)bl) + dex=mob_db[((struct pet_data *)bl)->class].dex; + + if(sc_data) { + if(sc_data[SC_CONCENTRATE].timer!=-1 && sc_data[SC_QUAGMIRE].timer == -1 && bl->type != BL_PC) + dex += dex*(2+sc_data[SC_CONCENTRATE].val1)/100; + + if( sc_data[SC_BLESSING].timer != -1 && bl->type != BL_PC){ // ブレッシング + int race=battle_get_race(bl); + if(battle_check_undead(race,battle_get_elem_type(bl)) || race==6 ) dex >>= 1; // 悪 魔/不死 + else dex += sc_data[SC_BLESSING].val1; // その他 + } + + if(sc_data[SC_QUAGMIRE].timer!=-1 ) // クァグマイア + dex >>= 1; + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト + dex += 5; + } + if(dex < 0) dex = 0; + return dex; +} +/*========================================== + * 対象のLukを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_luk(struct block_list *bl) +{ + int luk=0; + struct status_change *sc_data; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + luk=mob_db[((struct mob_data *)bl)->class].luk; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + luk=((struct map_session_data *)bl)->paramc[5]; + else if(bl->type==BL_PET && (struct pet_data *)bl) + luk=mob_db[((struct pet_data *)bl)->class].luk; + + if(sc_data) { + if(sc_data[SC_GLORIA].timer!=-1 && bl->type != BL_PC) // グロリア(PCはpc.cで) + luk += 30; + if(sc_data[SC_CURSE].timer!=-1 ) // 呪い + luk=0; + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト + luk += 5; + } + if(luk < 0) luk = 0; + return luk; +} + +/*========================================== + * 対象のFleeを返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_flee(struct block_list *bl) +{ + int flee=1; + struct status_change *sc_data; + + nullpo_retr(1, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + flee=((struct map_session_data *)bl)->flee; + else + flee=battle_get_agi(bl) + battle_get_lv(bl); + + if(sc_data) { + if(sc_data[SC_WHISTLE].timer!=-1 && bl->type != BL_PC) + flee += flee*(sc_data[SC_WHISTLE].val1+sc_data[SC_WHISTLE].val2 + +(sc_data[SC_WHISTLE].val3>>16))/100; + if(sc_data[SC_BLIND].timer!=-1 && bl->type != BL_PC) + flee -= flee*25/100; + if(sc_data[SC_WINDWALK].timer!=-1 && bl->type != BL_PC) // ウィンドウォーク + flee += flee*(sc_data[SC_WINDWALK].val2)/100; + if(sc_data[SC_SPIDERWEB].timer!=-1 && bl->type != BL_PC) //スパイダーウェブ + flee -= flee*50/100; + } + if(flee < 1) flee = 1; + return flee; +} +/*========================================== + * 対象のHitを返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_hit(struct block_list *bl) +{ + int hit=1; + struct status_change *sc_data; + + nullpo_retr(1, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + hit=((struct map_session_data *)bl)->hit; + else + hit=battle_get_dex(bl) + battle_get_lv(bl); + + if(sc_data) { + if(sc_data[SC_HUMMING].timer!=-1 && bl->type != BL_PC) // + hit += hit*(sc_data[SC_HUMMING].val1*2+sc_data[SC_HUMMING].val2 + +sc_data[SC_HUMMING].val3)/100; + if(sc_data[SC_BLIND].timer!=-1 && bl->type != BL_PC) // 呪い + hit -= hit*25/100; + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) // トゥルーサイト + hit += 3*(sc_data[SC_TRUESIGHT].val1); + if(sc_data[SC_CONCENTRATION].timer!=-1 && bl->type != BL_PC) //コンセントレーション + hit += (hit*(10*(sc_data[SC_CONCENTRATION].val1)))/100; + } + if(hit < 1) hit = 1; + return hit; +} +/*========================================== + * 対象の完全回避を返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_flee2(struct block_list *bl) +{ + int flee2=1; + struct status_change *sc_data; + + nullpo_retr(1, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl){ + flee2 = battle_get_luk(bl) + 10; + flee2 += ((struct map_session_data *)bl)->flee2 - (((struct map_session_data *)bl)->paramc[5] + 10); + } + else + flee2=battle_get_luk(bl)+1; + + if(sc_data) { + if(sc_data[SC_WHISTLE].timer!=-1 && bl->type != BL_PC) + flee2 += (sc_data[SC_WHISTLE].val1+sc_data[SC_WHISTLE].val2 + +(sc_data[SC_WHISTLE].val3&0xffff))*10; + } + if(flee2 < 1) flee2 = 1; + return flee2; +} +/*========================================== + * 対象のクリティカルを返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_critical(struct block_list *bl) +{ + int critical=1; + struct status_change *sc_data; + + nullpo_retr(1, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl){ + critical = battle_get_luk(bl)*3 + 10; + critical += ((struct map_session_data *)bl)->critical - ((((struct map_session_data *)bl)->paramc[5]*3) + 10); + } + else + critical=battle_get_luk(bl)*3 + 1; + + if(sc_data) { + if(sc_data[SC_FORTUNE].timer!=-1 && bl->type != BL_PC) + critical += (10+sc_data[SC_FORTUNE].val1+sc_data[SC_FORTUNE].val2 + +sc_data[SC_FORTUNE].val3)*10; + if(sc_data[SC_EXPLOSIONSPIRITS].timer!=-1 && bl->type != BL_PC) + critical += sc_data[SC_EXPLOSIONSPIRITS].val2; + if(sc_data[SC_TRUESIGHT].timer!=-1 && bl->type != BL_PC) //トゥルーサイト + critical += critical*sc_data[SC_TRUESIGHT].val1/100; + } + if(critical < 1) critical = 1; + return critical; +} +/*========================================== + * base_atkの取得 + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_baseatk(struct block_list *bl) +{ + struct status_change *sc_data; + int batk=1; + + nullpo_retr(1, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + batk = ((struct map_session_data *)bl)->base_atk; //設定されているbase_atk + else { //それ以外なら + int str,dstr; + str = battle_get_str(bl); //STR + dstr = str/10; + batk = dstr*dstr + str; //base_atkを計算する + } + if(sc_data) { //状態異常あり + if(sc_data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) //PCでプロボック(SM_PROVOKE)状態 + batk = batk*(100+2*sc_data[SC_PROVOKE].val1)/100; //base_atk増加 + if(sc_data[SC_CURSE].timer!=-1 ) //呪われていたら + batk -= batk*25/100; //base_atkが25%減少 + if(sc_data[SC_CONCENTRATION].timer!=-1 && bl->type != BL_PC) //コンセントレーション + batk += batk*(5*sc_data[SC_CONCENTRATION].val1)/100; + } + if(batk < 1) batk = 1; //base_atkは最低でも1 + return batk; +} +/*========================================== + * 対象のAtkを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk(struct block_list *bl) +{ + struct status_change *sc_data; + int atk=0; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + atk = ((struct map_session_data*)bl)->watk; + else if(bl->type==BL_MOB && (struct mob_data *)bl) + atk = mob_db[((struct mob_data*)bl)->class].atk1; + else if(bl->type==BL_PET && (struct pet_data *)bl) + atk = mob_db[((struct pet_data*)bl)->class].atk1; + + if(sc_data) { + if(sc_data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) + atk = atk*(100+2*sc_data[SC_PROVOKE].val1)/100; + if(sc_data[SC_CURSE].timer!=-1 ) + atk -= atk*25/100; + if(sc_data[SC_CONCENTRATION].timer!=-1 && bl->type != BL_PC) //コンセントレーション + atk += atk*(5*sc_data[SC_CONCENTRATION].val1)/100; + } + if(atk < 0) atk = 0; + return atk; +} +/*========================================== + * 対象の左手Atkを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk_(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl){ + int atk=((struct map_session_data*)bl)->watk_; + + if(((struct map_session_data *)bl)->sc_data[SC_CURSE].timer!=-1 ) + atk -= atk*25/100; + return atk; + } + else + return 0; +} +/*========================================== + * 対象のAtk2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk2(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data*)bl)->watk2; + else { + struct status_change *sc_data=battle_get_sc_data(bl); + int atk2=0; + if(bl->type==BL_MOB && (struct mob_data *)bl) + atk2 = mob_db[((struct mob_data*)bl)->class].atk2; + else if(bl->type==BL_PET && (struct pet_data *)bl) + atk2 = mob_db[((struct pet_data*)bl)->class].atk2; + if(sc_data) { + if( sc_data[SC_IMPOSITIO].timer!=-1) + atk2 += sc_data[SC_IMPOSITIO].val1*5; + if( sc_data[SC_PROVOKE].timer!=-1 ) + atk2 = atk2*(100+2*sc_data[SC_PROVOKE].val1)/100; + if( sc_data[SC_CURSE].timer!=-1 ) + atk2 -= atk2*25/100; + if(sc_data[SC_DRUMBATTLE].timer!=-1) + atk2 += sc_data[SC_DRUMBATTLE].val2; + if(sc_data[SC_NIBELUNGEN].timer!=-1 && (battle_get_element(bl)/10) >= 8 ) + atk2 += sc_data[SC_NIBELUNGEN].val2; + if(sc_data[SC_STRIPWEAPON].timer!=-1) + atk2 = atk2*90/100; + if(sc_data[SC_CONCENTRATION].timer!=-1) //コンセントレーション + atk2 += atk2*(5*sc_data[SC_CONCENTRATION].val1)/100; + } + if(atk2 < 0) atk2 = 0; + return atk2; + } + return 0; +} +/*========================================== + * 対象の左手Atk2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_atk_2(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data*)bl)->watk_2; + else + return 0; +} +/*========================================== + * 対象のMAtk1を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_matk1(struct block_list *bl) +{ + struct status_change *sc_data; + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_MOB){ + int matk,int_=battle_get_int(bl); + matk = int_+(int_/5)*(int_/5); + + if(sc_data) + if(sc_data[SC_MINDBREAKER].timer!=-1 && bl->type != BL_PC) + matk = matk*(100+2*sc_data[SC_MINDBREAKER].val1)/100; + return matk; + } + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->matk1; + else if(bl->type==BL_PET){ + int matk,int_=battle_get_int(bl); + matk = int_+(int_/5)*(int_/5); + + if(sc_data) + if(sc_data[SC_MINDBREAKER].timer!=-1 && bl->type != BL_PC) + matk = matk*(100+2*sc_data[SC_MINDBREAKER].val1)/100; + return matk; + } + else + return 0; +} +/*========================================== + * 対象のMAtk2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_matk2(struct block_list *bl) +{ + struct status_change *sc_data=battle_get_sc_data(bl); + nullpo_retr(0, bl); + if(bl->type==BL_MOB){ + int matk,int_=battle_get_int(bl); + matk = int_+(int_/7)*(int_/7); + + if(sc_data) + if(sc_data[SC_MINDBREAKER].timer!=-1 && bl->type != BL_PC) + matk = matk*(100+2*sc_data[SC_MINDBREAKER].val1)/100; + return matk; + } + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->matk2; + else if(bl->type==BL_PET){ + int matk,int_=battle_get_int(bl); + matk = int_+(int_/7)*(int_/7); + if(sc_data) + if(sc_data[SC_MINDBREAKER].timer!=-1 && bl->type != BL_PC) + matk = matk*(100+2*sc_data[SC_MINDBREAKER].val1)/100; + return matk; + } + else + return 0; +} +/*========================================== + * 対象のDefを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_def(struct block_list *bl) +{ + struct status_change *sc_data; + int def=0,skilltimer=-1,skillid=0; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl){ + def = ((struct map_session_data *)bl)->def; + skilltimer = ((struct map_session_data *)bl)->skilltimer; + skillid = ((struct map_session_data *)bl)->skillid; + } + else if(bl->type==BL_MOB && (struct mob_data *)bl) { + def = mob_db[((struct mob_data *)bl)->class].def; + skilltimer = ((struct mob_data *)bl)->skilltimer; + skillid = ((struct mob_data *)bl)->skillid; + } + else if(bl->type==BL_PET && (struct pet_data *)bl) + def = mob_db[((struct pet_data *)bl)->class].def; + + if(def < 1000000) { + if(sc_data) { + //キーピング時はDEF100 + if( sc_data[SC_KEEPING].timer!=-1) + def = 100; + //プロボック時は減算 + if( sc_data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) + def = (def*(100 - 6*sc_data[SC_PROVOKE].val1)+50)/100; + //戦太鼓の響き時は加算 + if( sc_data[SC_DRUMBATTLE].timer!=-1 && bl->type != BL_PC) + def += sc_data[SC_DRUMBATTLE].val3; + //毒にかかっている時は減算 + if(sc_data[SC_POISON].timer!=-1 && bl->type != BL_PC) + def = def*75/100; + //ストリップシールド時は減算 + if(sc_data[SC_STRIPSHIELD].timer!=-1 && bl->type != BL_PC) + def = def*85/100; + //シグナムクルシス時は減算 + if(sc_data[SC_SIGNUMCRUCIS].timer!=-1 && bl->type != BL_PC) + def = def * (100 - sc_data[SC_SIGNUMCRUCIS].val2)/100; + //永遠の混沌時はDEF0になる + if(sc_data[SC_ETERNALCHAOS].timer!=-1 && bl->type != BL_PC) + def = 0; + //凍結、石化時は右シフト + if(sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0)) + def >>= 1; + //コンセントレーション時は減算 + if( sc_data[SC_CONCENTRATION].timer!=-1 && bl->type != BL_PC) + def = (def*(100 - 5*sc_data[SC_CONCENTRATION].val1))/100; + } + //詠唱中は詠唱時減算率に基づいて減算 + if(skilltimer != -1) { + int def_rate = skill_get_castdef(skillid); + if(def_rate != 0) + def = (def * (100 - def_rate))/100; + } + } + if(def < 0) def = 0; + return def; +} +/*========================================== + * 対象のMDefを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_mdef(struct block_list *bl) +{ + struct status_change *sc_data; + int mdef=0; + + nullpo_retr(0, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + mdef = ((struct map_session_data *)bl)->mdef; + else if(bl->type==BL_MOB && (struct mob_data *)bl) + mdef = mob_db[((struct mob_data *)bl)->class].mdef; + else if(bl->type==BL_PET && (struct pet_data *)bl) + mdef = mob_db[((struct pet_data *)bl)->class].mdef; + + if(mdef < 1000000) { + if(sc_data) { + //バリアー状態時はMDEF100 + if(sc_data[SC_BARRIER].timer != -1) + mdef = 100; + //凍結、石化時は1.25倍 + if(sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0)) + mdef = mdef*125/100; + if( sc_data[SC_MINDBREAKER].timer!=-1 && bl->type != BL_PC) + mdef -= (mdef*6*sc_data[SC_MINDBREAKER].val1)/100; + } + } + if(mdef < 0) mdef = 0; + return mdef; +} +/*========================================== + * 対象のDef2を返す(汎用) + * 戻りは整数で1以上 + *------------------------------------------ + */ +int battle_get_def2(struct block_list *bl) +{ + struct status_change *sc_data; + int def2=1; + + nullpo_retr(1, bl); + sc_data=battle_get_sc_data(bl); + if(bl->type==BL_PC) + def2 = ((struct map_session_data *)bl)->def2; + else if(bl->type==BL_MOB) + def2 = mob_db[((struct mob_data *)bl)->class].vit; + else if(bl->type==BL_PET) + def2 = mob_db[((struct pet_data *)bl)->class].vit; + + if(sc_data) { + if( sc_data[SC_ANGELUS].timer!=-1 && bl->type != BL_PC) + def2 = def2*(110+5*sc_data[SC_ANGELUS].val1)/100; + if( sc_data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) + def2 = (def2*(100 - 6*sc_data[SC_PROVOKE].val1)+50)/100; + if(sc_data[SC_POISON].timer!=-1 && bl->type != BL_PC) + def2 = def2*75/100; + //コンセントレーション時は減算 + if( sc_data[SC_CONCENTRATION].timer!=-1 && bl->type != BL_PC) + def2 = def2*(100 - 5*sc_data[SC_CONCENTRATION].val1)/100; + } + if(def2 < 1) def2 = 1; + return def2; +} +/*========================================== + * 対象のMDef2を返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int battle_get_mdef2(struct block_list *bl) +{ + int mdef2=0; + struct status_change *sc_data=battle_get_sc_data(bl); + + nullpo_retr(0, bl); + if(bl->type==BL_MOB) + mdef2 = mob_db[((struct mob_data *)bl)->class].int_ + (mob_db[((struct mob_data *)bl)->class].vit>>1); + else if(bl->type==BL_PC) + mdef2 = ((struct map_session_data *)bl)->mdef2 + (((struct map_session_data *)bl)->paramc[2]>>1); + else if(bl->type==BL_PET) + mdef2 = mob_db[((struct pet_data *)bl)->class].int_ + (mob_db[((struct pet_data *)bl)->class].vit>>1); + if(sc_data) { + if( sc_data[SC_MINDBREAKER].timer!=-1 && bl->type != BL_PC) + mdef2 -= (mdef2*6*sc_data[SC_MINDBREAKER].val1)/100; + } + if(mdef2 < 0) mdef2 = 0; + return mdef2; +} +/*========================================== + * 対象のSpeed(移動速度)を返す(汎用) + * 戻りは整数で1以上 + * Speedは小さいほうが移動速度が速い + *------------------------------------------ + */ +int battle_get_speed(struct block_list *bl) +{ + nullpo_retr(1000, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->speed; + else { + struct status_change *sc_data=battle_get_sc_data(bl); + int speed = 1000; + if(bl->type==BL_MOB && (struct mob_data *)bl) +// speed = mob_db[((struct mob_data *)bl)->class].speed; + speed = ((struct mob_data *)bl)->speed; + else if(bl->type==BL_PET && (struct pet_data *)bl) + speed = ((struct pet_data *)bl)->msd->petDB->speed; + + if(sc_data) { + //速度増加時は25%減算 + if(sc_data[SC_INCREASEAGI].timer!=-1 && sc_data[SC_DONTFORGETME].timer == -1) + speed -= speed*25/100; + //速度減少時は25%加算 + if(sc_data[SC_DECREASEAGI].timer!=-1) + speed = speed*125/100; + //クァグマイア時は50%加算 + if(sc_data[SC_QUAGMIRE].timer!=-1) + speed = speed*3/2; + //私を忘れないで…時は加算 + if(sc_data[SC_DONTFORGETME].timer!=-1) + speed = speed*(100+sc_data[SC_DONTFORGETME].val1*2 + sc_data[SC_DONTFORGETME].val2 + (sc_data[SC_DONTFORGETME].val3&0xffff))/100; + //金剛時は25%加算 + if(sc_data[SC_STEELBODY].timer!=-1) + speed = speed*125/100; + //ディフェンダー時は加算 + if(sc_data[SC_DEFENDER].timer!=-1) + speed = (speed * (155 - sc_data[SC_DEFENDER].val1*5)) / 100; + //踊り状態は4倍遅い + if(sc_data[SC_DANCING].timer!=-1 ) + speed*=4; + //呪い時は450加算 + if(sc_data[SC_CURSE].timer!=-1) + speed = speed + 450; + //ウィンドウォーク時はLv*2%減算 + if(sc_data[SC_WINDWALK].timer!=-1) + speed -= (speed*(sc_data[SC_WINDWALK].val1*2))/100; + } + if(speed < 1) speed = 1; + return speed; + } + + return 1000; +} +/*========================================== + * 対象のaDelay(攻撃時ディレイ)を返す(汎用) + * aDelayは小さいほうが攻撃速度が速い + *------------------------------------------ + */ +int battle_get_adelay(struct block_list *bl) +{ + nullpo_retr(4000, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + return (((struct map_session_data *)bl)->aspd<<1); + else { + struct status_change *sc_data=battle_get_sc_data(bl); + int adelay=4000,aspd_rate = 100,i; + if(bl->type==BL_MOB && (struct mob_data *)bl) + adelay = mob_db[((struct mob_data *)bl)->class].adelay; + else if(bl->type==BL_PET && (struct pet_data *)bl) + adelay = mob_db[((struct pet_data *)bl)->class].adelay; + + if(sc_data) { + //ツーハンドクイッケン使用時でクァグマイアでも私を忘れないで…でもない時は3割減算 + if(sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ + aspd_rate -= 30; + //アドレナリンラッシュ使用時でツーハンドクイッケンでもクァグマイアでも私を忘れないで…でもない時は + if(sc_data[SC_ADRENALINE].timer != -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && + sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) { // アドレナリンラッシュ + //使用者とパーティメンバーで格差が出る設定でなければ3割減算 + if(sc_data[SC_ADRENALINE].val2 || !battle_config.party_skill_penaly) + aspd_rate -= 30; + //そうでなければ2.5割減算 + else + aspd_rate -= 25; + } + //スピアクィッケン時は減算 + if(sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && + sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン + aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; + //夕日のアサシンクロス時は減算 + if(sc_data[SC_ASSNCROS].timer!=-1 && // 夕陽のアサシンクロス + sc_data[SC_TWOHANDQUICKEN].timer==-1 && sc_data[SC_ADRENALINE].timer==-1 && sc_data[SC_SPEARSQUICKEN].timer==-1 && + sc_data[SC_DONTFORGETME].timer == -1) + aspd_rate -= 5+sc_data[SC_ASSNCROS].val1+sc_data[SC_ASSNCROS].val2+sc_data[SC_ASSNCROS].val3; + //私を忘れないで…時は加算 + if(sc_data[SC_DONTFORGETME].timer!=-1) // 私を忘れないで + aspd_rate += sc_data[SC_DONTFORGETME].val1*3 + sc_data[SC_DONTFORGETME].val2 + (sc_data[SC_DONTFORGETME].val3>>16); + //金剛時25%加算 + if(sc_data[SC_STEELBODY].timer!=-1) // 金剛 + aspd_rate += 25; + //増速ポーション使用時は減算 + if( sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1) + aspd_rate -= sc_data[i].val2; + //ディフェンダー時は加算 + if(sc_data[SC_DEFENDER].timer != -1) + adelay += (1100 - sc_data[SC_DEFENDER].val1*100); + } + if(aspd_rate != 100) + adelay = adelay*aspd_rate/100; + if(adelay < battle_config.monster_max_aspd<<1) adelay = battle_config.monster_max_aspd<<1; + return adelay; + } + return 4000; +} +int battle_get_amotion(struct block_list *bl) +{ + nullpo_retr(2000, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->amotion; + else { + struct status_change *sc_data=battle_get_sc_data(bl); + int amotion=2000,aspd_rate = 100,i; + if(bl->type==BL_MOB && (struct mob_data *)bl) + amotion = mob_db[((struct mob_data *)bl)->class].amotion; + else if(bl->type==BL_PET && (struct pet_data *)bl) + amotion = mob_db[((struct pet_data *)bl)->class].amotion; + + if(sc_data) { + if(sc_data[SC_TWOHANDQUICKEN].timer != -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // 2HQ + aspd_rate -= 30; + if(sc_data[SC_ADRENALINE].timer != -1 && sc_data[SC_TWOHANDQUICKEN].timer == -1 && + sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) { // アドレナリンラッシュ + if(sc_data[SC_ADRENALINE].val2 || !battle_config.party_skill_penaly) + aspd_rate -= 30; + else + aspd_rate -= 25; + } + if(sc_data[SC_SPEARSQUICKEN].timer != -1 && sc_data[SC_ADRENALINE].timer == -1 && + sc_data[SC_TWOHANDQUICKEN].timer == -1 && sc_data[SC_QUAGMIRE].timer == -1 && sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン + aspd_rate -= sc_data[SC_SPEARSQUICKEN].val2; + if(sc_data[SC_ASSNCROS].timer!=-1 && // 夕陽のアサシンクロス + sc_data[SC_TWOHANDQUICKEN].timer==-1 && sc_data[SC_ADRENALINE].timer==-1 && sc_data[SC_SPEARSQUICKEN].timer==-1 && + sc_data[SC_DONTFORGETME].timer == -1) + aspd_rate -= 5+sc_data[SC_ASSNCROS].val1+sc_data[SC_ASSNCROS].val2+sc_data[SC_ASSNCROS].val3; + if(sc_data[SC_DONTFORGETME].timer!=-1) // 私を忘れないで + aspd_rate += sc_data[SC_DONTFORGETME].val1*3 + sc_data[SC_DONTFORGETME].val2 + (sc_data[SC_DONTFORGETME].val3>>16); + if(sc_data[SC_STEELBODY].timer!=-1) // 金剛 + aspd_rate += 25; + if( sc_data[i=SC_SPEEDPOTION2].timer!=-1 || sc_data[i=SC_SPEEDPOTION1].timer!=-1 || sc_data[i=SC_SPEEDPOTION0].timer!=-1) + aspd_rate -= sc_data[i].val2; + if(sc_data[SC_DEFENDER].timer != -1) + amotion += (550 - sc_data[SC_DEFENDER].val1*50); + } + if(aspd_rate != 100) + amotion = amotion*aspd_rate/100; + if(amotion < battle_config.monster_max_aspd) amotion = battle_config.monster_max_aspd; + return amotion; + } + return 2000; +} +int battle_get_dmotion(struct block_list *bl) +{ + int ret; + struct status_change *sc_data; + + nullpo_retr(0, bl); + sc_data = battle_get_sc_data(bl); + if(bl->type==BL_MOB && (struct mob_data *)bl){ + ret=mob_db[((struct mob_data *)bl)->class].dmotion; + if(battle_config.monster_damage_delay_rate != 100) + ret = ret*battle_config.monster_damage_delay_rate/400; + } + else if(bl->type==BL_PC && (struct map_session_data *)bl){ + ret=((struct map_session_data *)bl)->dmotion; + if(battle_config.pc_damage_delay_rate != 100) + ret = ret*battle_config.pc_damage_delay_rate/400; + } + else if(bl->type==BL_PET && (struct pet_data *)bl) + ret=mob_db[((struct pet_data *)bl)->class].dmotion; + else + return 2000; + + if((sc_data && sc_data[SC_ENDURE].timer!=-1) || + (bl->type == BL_PC && ((struct map_session_data *)bl)->special_state.infinite_endure)) + ret=0; + + return ret; +} +int battle_get_element(struct block_list *bl) +{ + int ret = 20; + struct status_change *sc_data; + + nullpo_retr(ret, bl); + sc_data = battle_get_sc_data(bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) // 10の位=Lv*2、1の位=属性 + ret=((struct mob_data *)bl)->def_ele; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + ret=20+((struct map_session_data *)bl)->def_ele; // 防御属性Lv1 + else if(bl->type==BL_PET && (struct pet_data *)bl) + ret = mob_db[((struct pet_data *)bl)->class].element; + + if(sc_data) { + if( sc_data[SC_BENEDICTIO].timer!=-1 ) // 聖体降福 + ret=26; + if( sc_data[SC_FREEZE].timer!=-1 ) // 凍結 + ret=21; + if( sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0) + ret=22; + } + + return ret; +} +int battle_get_attack_element(struct block_list *bl) +{ + int ret = 0; + struct status_change *sc_data=battle_get_sc_data(bl); + + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + ret=0; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + ret=((struct map_session_data *)bl)->atk_ele; + else if(bl->type==BL_PET && (struct pet_data *)bl) + ret=0; + + if(sc_data) { + if( sc_data[SC_FROSTWEAPON].timer!=-1) // フロストウェポン + ret=1; + if( sc_data[SC_SEISMICWEAPON].timer!=-1) // サイズミックウェポン + ret=2; + if( sc_data[SC_FLAMELAUNCHER].timer!=-1) // フレームランチャー + ret=3; + if( sc_data[SC_LIGHTNINGLOADER].timer!=-1) // ライトニングローダー + ret=4; + if( sc_data[SC_ENCPOISON].timer!=-1) // エンチャントポイズン + ret=5; + if( sc_data[SC_ASPERSIO].timer!=-1) // アスペルシオ + ret=6; + } + + return ret; +} +int battle_get_attack_element2(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) { + int ret = ((struct map_session_data *)bl)->atk_ele_; + struct status_change *sc_data = ((struct map_session_data *)bl)->sc_data; + + if(sc_data) { + if( sc_data[SC_FROSTWEAPON].timer!=-1) // フロストウェポン + ret=1; + if( sc_data[SC_SEISMICWEAPON].timer!=-1) // サイズミックウェポン + ret=2; + if( sc_data[SC_FLAMELAUNCHER].timer!=-1) // フレームランチャー + ret=3; + if( sc_data[SC_LIGHTNINGLOADER].timer!=-1) // ライトニングローダー + ret=4; + if( sc_data[SC_ENCPOISON].timer!=-1) // エンチャントポイズン + ret=5; + if( sc_data[SC_ASPERSIO].timer!=-1) // アスペルシオ + ret=6; + } + return ret; + } + return 0; +} +int battle_get_party_id(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->status.party_id; + else if(bl->type==BL_MOB && (struct mob_data *)bl){ + struct mob_data *md=(struct mob_data *)bl; + if( md->master_id>0 ) + return -md->master_id; + return -md->bl.id; + } + else if(bl->type==BL_SKILL && (struct skill_unit *)bl) + return ((struct skill_unit *)bl)->group->party_id; + else + return 0; +} +int battle_get_guild_id(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data *)bl)->status.guild_id; + else if(bl->type==BL_MOB && (struct mob_data *)bl) + return ((struct mob_data *)bl)->class; + else if(bl->type==BL_SKILL && (struct skill_unit *)bl) + return ((struct skill_unit *)bl)->group->guild_id; + else + return 0; +} +int battle_get_race(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return mob_db[((struct mob_data *)bl)->class].race; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return 7; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return mob_db[((struct pet_data *)bl)->class].race; + else + return 0; +} +int battle_get_size(struct block_list *bl) +{ + nullpo_retr(1, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return mob_db[((struct mob_data *)bl)->class].size; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return 1; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return mob_db[((struct pet_data *)bl)->class].size; + else + return 1; +} +int battle_get_mode(struct block_list *bl) +{ + nullpo_retr(0x01, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return mob_db[((struct mob_data *)bl)->class].mode; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return mob_db[((struct pet_data *)bl)->class].mode; + else + return 0x01; // とりあえず動くということで1 +} + +int battle_get_mexp(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return mob_db[((struct mob_data *)bl)->class].mexp; + else if(bl->type==BL_PET && (struct pet_data *)bl) + return mob_db[((struct pet_data *)bl)->class].mexp; + else + return 0; +} + +// StatusChange系の所得 +struct status_change *battle_get_sc_data(struct block_list *bl) +{ + nullpo_retr(NULL, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return ((struct mob_data*)bl)->sc_data; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return ((struct map_session_data*)bl)->sc_data; + return NULL; +} +short *battle_get_sc_count(struct block_list *bl) +{ + nullpo_retr(NULL, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return &((struct mob_data*)bl)->sc_count; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return &((struct map_session_data*)bl)->sc_count; + return NULL; +} +short *battle_get_opt1(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return &((struct mob_data*)bl)->opt1; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return &((struct map_session_data*)bl)->opt1; + else if(bl->type==BL_NPC && (struct npc_data *)bl) + return &((struct npc_data*)bl)->opt1; + return 0; +} +short *battle_get_opt2(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return &((struct mob_data*)bl)->opt2; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return &((struct map_session_data*)bl)->opt2; + else if(bl->type==BL_NPC && (struct npc_data *)bl) + return &((struct npc_data*)bl)->opt2; + return 0; +} +short *battle_get_opt3(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return &((struct mob_data*)bl)->opt3; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return &((struct map_session_data*)bl)->opt3; + else if(bl->type==BL_NPC && (struct npc_data *)bl) + return &((struct npc_data*)bl)->opt3; + return 0; +} +short *battle_get_option(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB && (struct mob_data *)bl) + return &((struct mob_data*)bl)->option; + else if(bl->type==BL_PC && (struct map_session_data *)bl) + return &((struct map_session_data*)bl)->status.option; + else if(bl->type==BL_NPC && (struct npc_data *)bl) + return &((struct npc_data*)bl)->option; + return 0; +} + +//------------------------------------------------------------------- + +// ダメージの遅延 +struct battle_delay_damage_ { + struct block_list *src,*target; + int damage; + int flag; +}; +int battle_delay_damage_sub(int tid,unsigned int tick,int id,int data) +{ + struct battle_delay_damage_ *dat=(struct battle_delay_damage_ *)data; + if( dat && map_id2bl(id)==dat->src && dat->target->prev!=NULL) + battle_damage(dat->src,dat->target,dat->damage,dat->flag); + free(dat); + return 0; +} +int battle_delay_damage(unsigned int tick,struct block_list *src,struct block_list *target,int damage,int flag) +{ + struct battle_delay_damage_ *dat = (struct battle_delay_damage_*)aCalloc(1,sizeof(struct battle_delay_damage_)); + + nullpo_retr(0, src); + nullpo_retr(0, target); + + + dat->src=src; + dat->target=target; + dat->damage=damage; + dat->flag=flag; + add_timer(tick,battle_delay_damage_sub,src->id,(int)dat); + return 0; +} + +// 実際にHPを操作 +int battle_damage(struct block_list *bl,struct block_list *target,int damage,int flag) +{ + struct map_session_data *sd=NULL; + struct status_change *sc_data=battle_get_sc_data(target); + short *sc_count; + int i; + + nullpo_retr(0, target); //blはNULLで呼ばれることがあるので他でチェック + + if(damage==0 || target->type == BL_PET) + return 0; + + if(target->prev == NULL) + return 0; + + if(bl) { + if(bl->prev==NULL) + return 0; + + if(bl->type==BL_PC) + sd=(struct map_session_data *)bl; + } + + if(damage<0) + return battle_heal(bl,target,-damage,0,flag); + + if(!flag && (sc_count=battle_get_sc_count(target))!=NULL && *sc_count>0){ + // 凍結、石化、睡眠を消去 + if(sc_data[SC_FREEZE].timer!=-1) + skill_status_change_end(target,SC_FREEZE,-1); + if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0) + skill_status_change_end(target,SC_STONE,-1); + if(sc_data[SC_SLEEP].timer!=-1) + skill_status_change_end(target,SC_SLEEP,-1); + } + + if(target->type==BL_MOB){ // MOB + struct mob_data *md=(struct mob_data *)target; + if(md && md->skilltimer!=-1 && md->state.skillcastcancel) // 詠唱妨害 + skill_castcancel(target,0); + return mob_damage(bl,md,damage,0); + } + else if(target->type==BL_PC){ // PC + + struct map_session_data *tsd=(struct map_session_data *)target; + + if(tsd && tsd->sc_data && tsd->sc_data[SC_DEVOTION].val1){ // ディボーションをかけられている + struct map_session_data *md = map_id2sd(tsd->sc_data[SC_DEVOTION].val1); + if(md && skill_devotion3(&md->bl,target->id)){ + skill_devotion(md,target->id); + } + else if(md && bl) + for(i=0;i<5;i++) + if(md->dev.val1[i] == target->id){ + clif_damage(bl,&md->bl, gettick(), 0, 0, + damage, 0 , 0, 0); + pc_damage(&md->bl,md,damage); + + return 0; + } + } + + if(tsd && tsd->skilltimer!=-1){ // 詠唱妨害 + // フェンカードや妨害されないスキルかの検査 + if( (!tsd->special_state.no_castcancel || map[bl->m].flag.gvg) && tsd->state.skillcastcancel && + !tsd->special_state.no_castcancel2) + skill_castcancel(target,0); + } + + return pc_damage(bl,tsd,damage); + + } + else if(target->type==BL_SKILL) + return skill_unit_ondamaged((struct skill_unit *)target,bl,damage,gettick()); + return 0; +} +int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp,int flag) +{ + nullpo_retr(0, target); //blはNULLで呼ばれることがあるので他でチェック + + if(target->type == BL_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; +} + +// 攻撃停止 +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; +} +// 移動停止 +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; +} + + +/*========================================== + * ダメージの属性修正 + *------------------------------------------ + */ +int battle_attr_fix(int damage,int atk_elem,int def_elem) +{ + int def_type= def_elem%10, def_lv=def_elem/10/2; + + if( atk_elem<0 || atk_elem>9 || def_type<0 || def_type>9 || + def_lv<1 || def_lv>4){ // 属 性値がおかしいのでとりあえずそのまま返す + if(battle_config.error_log) + printf("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n",atk_elem,def_type,def_lv); + return damage; + } + + return damage*attr_fix_table[def_lv-1][atk_elem][def_type]/100; +} + + +/*========================================== + * ダメージ最終計算 + *------------------------------------------ + */ +int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag) +{ + struct map_session_data *sd=NULL; + struct mob_data *md=NULL; + struct status_change *sc_data,*sc; + short *sc_count; + int class; + + nullpo_retr(0, bl); + + class = battle_get_class(bl); + if(bl->type==BL_MOB) md=(struct mob_data *)bl; + else sd=(struct map_session_data *)bl; + + sc_data=battle_get_sc_data(bl); + sc_count=battle_get_sc_count(bl); + + if(sc_count!=NULL && *sc_count>0){ + + if(sc_data[SC_SAFETYWALL].timer!=-1 && damage>0 && flag&BF_WEAPON && flag&BF_SHORT && skill_num != NPC_GUIDEDATTACK){ + // セーフティウォール + struct skill_unit *unit=(struct skill_unit*)sc_data[SC_SAFETYWALL].val2; + if( unit && unit->alive && (--unit->group->val2)<=0 ) + skill_delunit(unit); + skill_unit_move(bl,gettick(),1); // 重ね掛けチェック + damage=0; + } + if(sc_data[SC_PNEUMA].timer!=-1 && damage>0 && flag&BF_WEAPON && flag&BF_LONG && skill_num != NPC_GUIDEDATTACK){ + // ニューマ + damage=0; + } + + if(sc_data[SC_ROKISWEIL].timer!=-1 && damage>0 && + flag&BF_MAGIC ){ + // ニューマ + damage=0; + } + + if(sc_data[SC_AETERNA].timer!=-1 && damage>0){ // レックスエーテルナ + damage<<=1; + skill_status_change_end( bl,SC_AETERNA,-1 ); + } + + //属性場のダメージ増加 + if(sc_data[SC_VOLCANO].timer!=-1){ // ボルケーノ + if(flag&BF_SKILL && skill_get_pl(skill_num)==3) + damage += damage*sc_data[SC_VOLCANO].val4/100; + else if(!flag&BF_SKILL && battle_get_attack_element(bl)==3) + damage += damage*sc_data[SC_VOLCANO].val4/100; + } + + if(sc_data[SC_VIOLENTGALE].timer!=-1){ // バイオレントゲイル + if(flag&BF_SKILL && skill_get_pl(skill_num)==4) + damage += damage*sc_data[SC_VIOLENTGALE].val4/100; + else if(!flag&BF_SKILL && battle_get_attack_element(bl)==4) + damage += damage*sc_data[SC_VIOLENTGALE].val4/100; + } + + if(sc_data[SC_DELUGE].timer!=-1){ // デリュージ + if(flag&BF_SKILL && skill_get_pl(skill_num)==1) + damage += damage*sc_data[SC_DELUGE].val4/100; + else if(!flag&BF_SKILL && battle_get_attack_element(bl)==1) + damage += damage*sc_data[SC_DELUGE].val4/100; + } + + if(sc_data[SC_ENERGYCOAT].timer!=-1 && damage>0 && flag&BF_WEAPON){ // エナジーコート + if(sd){ + if(sd->status.sp>0){ + int per = sd->status.sp * 5 / (sd->status.max_sp + 1); + sd->status.sp -= sd->status.sp * (per * 5 + 10) / 1000; + if( sd->status.sp < 0 ) sd->status.sp = 0; + damage -= damage * ((per+1) * 6) / 100; + clif_updatestatus(sd,SP_SP); + } + if(sd->status.sp<=0) + skill_status_change_end( bl,SC_ENERGYCOAT,-1 ); + } + else + damage -= damage * (sc_data[SC_ENERGYCOAT].val1 * 6) / 100; + } + + if(sc_data[SC_KYRIE].timer!=-1 && damage > 0){ // キリエエレイソン + sc=&sc_data[SC_KYRIE]; + sc->val2-=damage; + if(flag&BF_WEAPON){ + if(sc->val2>=0) damage=0; + else damage=-sc->val2; + } + if((--sc->val3)<=0 || (sc->val2<=0) || skill_num == AL_HOLYLIGHT) + skill_status_change_end(bl, SC_KYRIE, -1); + } + + if(sc_data[SC_BASILICA].timer!=-1 && damage > 0){ + // ニューマ + damage=0; + } + if(sc_data[SC_LANDPROTECTOR].timer!=-1 && damage>0 && flag&BF_MAGIC){ + // ニューマ + damage=0; + } + + if(sc_data[SC_AUTOGUARD].timer != -1 && damage > 0 && flag&BF_WEAPON) { + if(rand()%100 < sc_data[SC_AUTOGUARD].val2) { + damage = 0; + clif_skill_nodamage(bl,bl,CR_AUTOGUARD,sc_data[SC_AUTOGUARD].val1,1); + if(sd) + sd->canmove_tick = gettick() + 300; + else if(md) + md->canmove_tick = gettick() + 300; + } + } +// -- moonsoul (chance to block attacks with new Lord Knight skill parrying) +// + if(sc_data[SC_PARRYING].timer != -1 && damage > 0 && flag&BF_WEAPON) { + if(rand()%100 < sc_data[SC_PARRYING].val2) { + damage = 0; + clif_skill_nodamage(bl,bl,LK_PARRYING,sc_data[SC_PARRYING].val1,1); + } + } + // リジェクトソード + if(sc_data[SC_REJECTSWORD].timer!=-1 && damage > 0 && flag&BF_WEAPON && + ((src->type==BL_PC && ((struct map_session_data *)src)->status.weapon == (1 || 2 || 3)) || src->type==BL_MOB )){ + if(rand()%100 < (10+5*sc_data[SC_REJECTSWORD].val1)){ //反射確率は10+5*Lv + damage = damage*50/100; + battle_damage(bl,src,damage,0); + //ダメージを与えたのは良いんだが、ここからどうして表示するんだかわかんねぇ + //エフェクトもこれでいいのかわかんねぇ + clif_skill_nodamage(bl,bl,ST_REJECTSWORD,sc_data[SC_REJECTSWORD].val1,1); + if((--sc_data[SC_REJECTSWORD].val2)<=0) + skill_status_change_end(bl, SC_REJECTSWORD, -1); + } + } + } + + if(class == 1288 || class == 1287 || class == 1286 || class == 1285) { +// if(class == 1288) { + if(class == 1288 && flag&BF_SKILL) + damage=0; + if(src->type == BL_PC) { + struct guild *g=guild_search(((struct map_session_data *)src)->status.guild_id); + struct guild_castle *gc=guild_mapname2gc(map[bl->m].name); + if(!((struct map_session_data *)src)->status.guild_id) + damage=0; + if(gc && agit_flag==0 && class != 1288) // guardians cannot be damaged during non-woe [Valaris] + damage=0; // end woe check [Valaris] + if(g == NULL) + damage=0;//ギルド未加入ならダメージ無し + else if((gc != NULL) && guild_isallied(g, gc)) + damage=0;//自占領ギルドのエンペならダメージ無し + else if(g && guild_checkskill(g,GD_APPROVAL) <= 0) + damage=0;//正規ギルド承認がないとダメージ無し + else if (battle_config.guild_max_castles != 0 && guild_checkcastles(g)>=battle_config.guild_max_castles) + damage = 0; // [MouseJstr] + } + else damage = 0; + } + + if(map[bl->m].flag.gvg && damage > 0) { //GvG + if(flag&BF_WEAPON) { + if(flag&BF_SHORT) + damage=damage*battle_config.gvg_short_damage_rate/100; + if(flag&BF_LONG) + damage=damage*battle_config.gvg_long_damage_rate/100; + } + if(flag&BF_MAGIC) + damage = damage*battle_config.gvg_magic_damage_rate/100; + if(flag&BF_MISC) + damage=damage*battle_config.gvg_misc_damage_rate/100; + if(damage < 1) damage = 1; + } + + if(battle_config.skill_min_damage || flag&BF_MISC) { + if(div_ < 255) { + if(damage > 0 && damage < div_) + damage = div_; + } + else if(damage > 0 && damage < 3) + damage = 3; + } + + if( md!=NULL && md->hp>0 && damage > 0 ) // 反撃などのMOBスキル判定 + mobskill_event(md,flag); + + return damage; +} + +/*========================================== + * 修練ダメージ + *------------------------------------------ + */ +int battle_addmastery(struct map_session_data *sd,struct block_list *target,int dmg,int type) +{ + int damage,skill; + int race=battle_get_race(target); + int weapon; + damage = 0; + + nullpo_retr(0, sd); + + // デーモンベイン(+3 〜 +30) vs 不死 or 悪魔 (死人は含めない?) + if((skill = pc_checkskill(sd,AL_DEMONBANE)) > 0 && (battle_check_undead(race,battle_get_elem_type(target)) || race==6) ) + damage += (skill * 3); + + // ビーストベイン(+4 〜 +40) vs 動物 or 昆虫 + if((skill = pc_checkskill(sd,HT_BEASTBANE)) > 0 && (race==2 || race==4) ) + damage += (skill * 4); + + if(type == 0) + weapon = sd->weapontype1; + else + weapon = sd->weapontype2; + switch(weapon) + { + case 0x01: // 短剣 (Updated By AppleGirl) + case 0x02: // 1HS + { + // 剣修練(+4 〜 +40) 片手剣 短剣含む + if((skill = pc_checkskill(sd,SM_SWORD)) > 0) { + damage += (skill * 4); + } + break; + } + case 0x03: // 2HS + { + // 両手剣修練(+4 〜 +40) 両手剣 + if((skill = pc_checkskill(sd,SM_TWOHAND)) > 0) { + damage += (skill * 4); + } + break; + } + case 0x04: // 1HL + { + // 槍修練(+4 〜 +40,+5 〜 +50) 槍 + if((skill = pc_checkskill(sd,KN_SPEARMASTERY)) > 0) { + if(!pc_isriding(sd)) + damage += (skill * 4); // ペコに乗ってない + else + damage += (skill * 5); // ペコに乗ってる + } + break; + } + case 0x05: // 2HL + { + // 槍修練(+4 〜 +40,+5 〜 +50) 槍 + if((skill = pc_checkskill(sd,KN_SPEARMASTERY)) > 0) { + if(!pc_isriding(sd)) + damage += (skill * 4); // ペコに乗ってない + else + damage += (skill * 5); // ペコに乗ってる + } + break; + } + case 0x06: // 片手斧 + { + if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) { + damage += (skill * 3); + } + break; + } + case 0x07: // Axe by Tato + { + if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) { + damage += (skill * 3); + } + break; + } + case 0x08: // メイス + { + // メイス修練(+3 〜 +30) メイス + if((skill = pc_checkskill(sd,PR_MACEMASTERY)) > 0) { + damage += (skill * 3); + } + break; + } + case 0x09: // なし? + break; + case 0x0a: // 杖 + break; + case 0x0b: // 弓 + break; + case 0x00: // 素手 + case 0x0c: // Knuckles + { + // 鉄拳(+3 〜 +30) 素手,ナックル + if((skill = pc_checkskill(sd,MO_IRONHAND)) > 0) { + damage += (skill * 3); + } + break; + } + case 0x0d: // Musical Instrument + { + // 楽器の練習(+3 〜 +30) 楽器 + if((skill = pc_checkskill(sd,BA_MUSICALLESSON)) > 0) { + damage += (skill * 3); + } + break; + } + case 0x0e: // Dance Mastery + { + // Dance Lesson Skill Effect(+3 damage for every lvl = +30) 鞭 + if((skill = pc_checkskill(sd,DC_DANCINGLESSON)) > 0) { + damage += (skill * 3); + } + break; + } + case 0x0f: // Book + { + // Advance Book Skill Effect(+3 damage for every lvl = +30) { + if((skill = pc_checkskill(sd,SA_ADVANCEDBOOK)) > 0) { + damage += (skill * 3); + } + break; + } + case 0x10: // Katars + { + // カタール修練(+3 〜 +30) カタール + if((skill = pc_checkskill(sd,AS_KATAR)) > 0) { + //ソニックブロー時は別処理(1撃に付き1/8適応) + damage += (skill * 3); + } + break; + } + } + damage = dmg + damage; + return (damage); +} + +static struct Damage battle_calc_pet_weapon_attack( + struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag) +{ + struct pet_data *pd = (struct pet_data *)src; + struct mob_data *tmd=NULL; + int hitrate,flee,cri = 0,atkmin,atkmax; + int luk,target_count = 1; + int def1 = battle_get_def(target); + int def2 = battle_get_def2(target); + int t_vit = battle_get_vit(target); + struct Damage wd; + int damage,damage2=0,type,div_,blewcount=skill_get_blewcount(skill_num,skill_lv); + int flag,dmg_lv=0; + int t_mode=0,t_race=0,t_size=1,s_race=0,s_ele=0; + struct status_change *t_sc_data; + + //return前の処理があるので情報出力部のみ変更 + if( target == NULL || pd == NULL ){ //srcは内容に直接触れていないのでスルーしてみる + nullpo_info(NLP_MARK); + memset(&wd,0,sizeof(wd)); + return wd; + } + + s_race=battle_get_race(src); + s_ele=battle_get_attack_element(src); + + // ターゲット + if(target->type == BL_MOB) + tmd=(struct mob_data *)target; + else { + memset(&wd,0,sizeof(wd)); + return wd; + } + t_race=battle_get_race( target ); + t_size=battle_get_size( target ); + t_mode=battle_get_mode( target ); + t_sc_data=battle_get_sc_data( target ); + + flag=BF_SHORT|BF_WEAPON|BF_NORMAL; // 攻撃の種類の設定 + + // 回避率計算、回避判定は後で + flee = battle_get_flee(target); + if(battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) + target_count += battle_counttargeted(target,src,battle_config.agi_penaly_count_lv); + if(battle_config.agi_penaly_type > 0) { + if(target_count >= battle_config.agi_penaly_count) { + if(battle_config.agi_penaly_type == 1) + flee = (flee * (100 - (target_count - (battle_config.agi_penaly_count - 1))*battle_config.agi_penaly_num))/100; + else if(battle_config.agi_penaly_type == 2) + flee -= (target_count - (battle_config.agi_penaly_count - 1))*battle_config.agi_penaly_num; + if(flee < 1) flee = 1; + } + } + hitrate=battle_get_hit(src) - flee + 80; + + type=0; // normal + div_ = 1; // single attack + + luk=battle_get_luk(src); + + if(battle_config.pet_str) + damage = battle_get_baseatk(src); + else + damage = 0; + + if(skill_num==HW_MAGICCRASHER){ /* マジッククラッシャーはMATKで殴る */ + atkmin = battle_get_matk1(src); + atkmax = battle_get_matk2(src); + }else{ + atkmin = battle_get_atk(src); + atkmax = battle_get_atk2(src); + } + if(mob_db[pd->class].range>3 ) + flag=(flag&~BF_RANGEMASK)|BF_LONG; + + if(atkmin > atkmax) atkmin = atkmax; + + cri = battle_get_critical(src); + cri -= battle_get_luk(target) * 2; // luk/5*10 => target_luk*2 not target_luk*3 + if(battle_config.enemy_critical_rate != 100) { + cri = cri*battle_config.enemy_critical_rate/100; + if(cri < 1) + cri = 1; + } + if(t_sc_data != NULL && t_sc_data[SC_SLEEP].timer!=-1 ) + cri <<=1; + + if(skill_num == 0 && skill_lv >= 0 && battle_config.enemy_critical && (rand() % 1000) < cri) + { + damage += atkmax; + type = 0x0a; + } + else { + int vitbonusmax; + + if(atkmax > atkmin) + damage += atkmin + rand() % (atkmax-atkmin + 1); + else + damage += atkmin ; + // スキル修正1(攻撃力倍化系) + // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 + // バッシュ,マグナムブレイク, + // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, + // メマーナイト,カートレボリューション + // ダブルストレイフィング,アローシャワー,チャージアロー, + // ソニックブロー + if(skill_num>0){ + int i; + if( (i=skill_get_pl(skill_num))>0 ) + s_ele=i; + + flag=(flag&~BF_SKILLMASK)|BF_SKILL; + switch( skill_num ){ + case SM_BASH: // バッシュ + damage = damage*(100+ 30*skill_lv)/100; + hitrate = (hitrate*(100+5*skill_lv))/100; + break; + case SM_MAGNUM: // マグナムブレイク + damage = damage*(5*skill_lv +(wflag)?65:115 )/100; + break; + case MC_MAMMONITE: // メマーナイト + damage = damage*(100+ 50*skill_lv)/100; + break; + case AC_DOUBLE: // ダブルストレイフィング + damage = damage*(180+ 20*skill_lv)/100; + div_=2; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case AC_SHOWER: // アローシャワー + damage = damage*(75 + 5*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case AC_CHARGEARROW: // チャージアロー + damage = damage*150/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case KN_PIERCE: // ピアース + damage = damage*(100+ 10*skill_lv)/100; + hitrate = hitrate*(100+5*skill_lv)/100; + div_=t_size+1; + damage*=div_; + break; + case KN_SPEARSTAB: // スピアスタブ + damage = damage*(100+ 15*skill_lv)/100; + break; + case KN_SPEARBOOMERANG: // スピアブーメラン + damage = damage*(100+ 50*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case KN_BRANDISHSPEAR: // ブランディッシュスピア + damage = damage*(100+ 20*skill_lv)/100; + if(skill_lv>3 && wflag==1) damage2+=damage/2; + if(skill_lv>6 && wflag==1) damage2+=damage/4; + if(skill_lv>9 && wflag==1) damage2+=damage/8; + if(skill_lv>6 && wflag==2) damage2+=damage/2; + if(skill_lv>9 && wflag==2) damage2+=damage/4; + if(skill_lv>9 && wflag==3) damage2+=damage/2; + damage +=damage2; + blewcount=0; + break; + case KN_BOWLINGBASH: // ボウリングバッシュ + damage = damage*(100+ 50*skill_lv)/100; + blewcount=0; + break; + case AS_SONICBLOW: // ソニックブロウ + damage = damage*(300+ 50*skill_lv)/100; + div_=8; + break; + case TF_SPRINKLESAND: // 砂まき + damage = damage*125/100; + break; + case MC_CARTREVOLUTION: // カートレボリューション + damage = (damage*150)/100; + break; + // 以下MOB + case NPC_COMBOATTACK: // 多段攻撃 + div_=skill_get_num(skill_num,skill_lv); + damage *= div_; + break; + case NPC_RANDOMATTACK: // ランダムATK攻撃 + damage = damage*(50+rand()%150)/100; + break; + // 属性攻撃(適当) + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + div_= pd->skillduration; // [Valaris] + break; + case NPC_GUIDEDATTACK: + hitrate = 1000000; + break; + case NPC_RANGEATTACK: + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case NPC_PIERCINGATT: + flag=(flag&~BF_RANGEMASK)|BF_SHORT; + break; + case RG_BACKSTAP: // バックスタブ + damage = damage*(300+ 40*skill_lv)/100; + hitrate = 1000000; + break; + case RG_RAID: // サプライズアタック + damage = damage*(100+ 40*skill_lv)/100; + break; + case RG_INTIMIDATE: // インティミデイト + damage = damage*(100+ 30*skill_lv)/100; + break; + case CR_SHIELDCHARGE: // シールドチャージ + damage = damage*(100+ 20*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_SHORT; + s_ele = 0; + break; + case CR_SHIELDBOOMERANG: // シールドブーメラン + damage = damage*(100+ 30*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + s_ele = 0; + break; + case CR_HOLYCROSS: // ホーリークロス + damage = damage*(100+ 35*skill_lv)/100; + div_=2; + break; + case CR_GRANDCROSS: + hitrate= 1000000; + break; + case AM_DEMONSTRATION: // デモンストレーション + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + break; + case AM_ACIDTERROR: // アシッドテラー + damage = damage*(100+ 40*skill_lv)/100; + damage2 = damage2*(100+ 40*skill_lv)/100; + break; + case MO_FINGEROFFENSIVE: //指弾 + damage = damage * (100 + 50 * skill_lv) / 100; + div_ = 1; + break; + case MO_INVESTIGATE: // 発 勁 + if(def1 < 1000000) + damage = damage*(100+ 75*skill_lv)/100 * (def1 + def2)/100; + hitrate = 1000000; + s_ele = 0; + break; + case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 + damage = damage * 8 + 250 + (skill_lv * 150); + hitrate = 1000000; + s_ele = 0; + break; + case MO_CHAINCOMBO: // 連打掌 + damage = damage*(150+ 50*skill_lv)/100; + div_=4; + break; + case MO_COMBOFINISH: // 猛龍拳 + damage = damage*(240+ 60*skill_lv)/100; + break; + case DC_THROWARROW: // 矢撃ち + damage = damage*(100+ 50 * skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case BA_MUSICALSTRIKE: // ミュージカルストライク + damage = damage*(100+ 50 * skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case CH_TIGERFIST: // 伏虎拳 + damage = damage*(100+ 20*skill_lv)/100; + break; + case CH_CHAINCRUSH: // 連柱崩撃 + damage = damage*(100+ 20*skill_lv)/100; + div_=skill_get_num(skill_num,skill_lv); + break; + case CH_PALMSTRIKE: // 猛虎硬派山 + damage = damage*(50+ 100*skill_lv)/100; + break; + case LK_SPIRALPIERCE: /* スパイラルピアース */ + damage = damage*(100+ 50*skill_lv)/100; //増加量が分からないので適当に + div_=5; + if(target->type == BL_PC) + ((struct map_session_data *)target)->canmove_tick = gettick() + 1000; + else if(target->type == BL_MOB) + ((struct mob_data *)target)->canmove_tick = gettick() + 1000; + break; + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + damage = damage*(100+ 20*skill_lv)/100; + break; + case LK_JOINTBEAT: /* ジョイントビート */ + damage = damage*(50+ 10*skill_lv)/100; + break; + case ASC_METEORASSAULT: /* メテオアサルト */ + damage = damage*(40+ 40*skill_lv)/100; + break; + case SN_SHARPSHOOTING: /* シャープシューティング */ + damage += damage*(30*skill_lv)/100; + break; + case CG_ARROWVULCAN: /* アローバルカン */ + damage = damage*(160+40*skill_lv)/100; + div_=9; + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + damage = damage*(200+20*skill_lv)/100; + break; + } + } + + if( skill_num!=NPC_CRITICALSLASH ){ + // 対 象の防御力によるダメージの減少 + // ディバインプロテクション(ここでいいのかな?) + if ( skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST && skill_num != KN_AUTOCOUNTER && def1 < 1000000 ) { //DEF, VIT無視 + int t_def; + target_count = 1 + battle_counttargeted(target,src,battle_config.vit_penaly_count_lv); + if(battle_config.vit_penaly_type > 0) { + if(target_count >= battle_config.vit_penaly_count) { + if(battle_config.vit_penaly_type == 1) { + def1 = (def1 * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + def2 = (def2 * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + t_vit = (t_vit * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + } + else if(battle_config.vit_penaly_type == 2) { + def1 -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + def2 -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + t_vit -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + } + if(def1 < 0) def1 = 0; + if(def2 < 1) def2 = 1; + if(t_vit < 1) t_vit = 1; + } + } + t_def = def2*8/10; + vitbonusmax = (t_vit/20)*(t_vit/20)-1; + if(battle_config.pet_defense_type) { + damage = damage - (def1 * battle_config.pet_defense_type) - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + else{ + damage = damage * (100 - def1) /100 - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + } + } + } + + // 0未満だった場合1に補正 + if(damage<1) damage=1; + + // 回避修正 + if(hitrate < 1000000) + hitrate = ( (hitrate>95)?95: ((hitrate<5)?5:hitrate) ); + if( hitrate < 1000000 && // 必中攻撃 + (t_sc_data != NULL && (t_sc_data[SC_SLEEP].timer!=-1 || // 睡眠は必中 + t_sc_data[SC_STAN].timer!=-1 || // スタンは必中 + t_sc_data[SC_FREEZE].timer!=-1 || (t_sc_data[SC_STONE].timer!=-1 && t_sc_data[SC_STONE].val2==0) ) ) ) // 凍結は必中 + hitrate = 1000000; + if(type == 0 && rand()%100 >= hitrate) { + damage = damage2 = 0; + dmg_lv = ATK_FLEE; + } else { + dmg_lv = ATK_DEF; + } + + + if(t_sc_data) { + int cardfix=100; + if(t_sc_data[SC_DEFENDER].timer != -1 && flag&BF_LONG) + cardfix=cardfix*(100-t_sc_data[SC_DEFENDER].val2)/100; + if(cardfix != 100) + damage=damage*cardfix/100; + } + if(damage < 0) damage = 0; + + // 属 性の適用 + if(skill_num != 0 || s_ele != 0 || !battle_config.pet_attack_attr_none) + damage=battle_attr_fix(damage, s_ele, battle_get_element(target) ); + + if(skill_num==PA_PRESSURE) /* プレッシャー 必中? */ + damage = 700+100*skill_lv; + + // インベナム修正 + if(skill_num==TF_POISON){ + damage = battle_attr_fix(damage + 15*skill_lv, s_ele, battle_get_element(target) ); + } + if(skill_num==MC_CARTREVOLUTION){ + damage = battle_attr_fix(damage, 0, battle_get_element(target) ); + } + + // 完全回避の判定 + if(battle_config.enemy_perfect_flee) { + if(skill_num == 0 && skill_lv >= 0 && tmd!=NULL && rand()%1000 < battle_get_flee2(target) ){ + damage=0; + type=0x0b; + dmg_lv = ATK_LUCKY; + } + } + +// if(def1 >= 1000000 && damage > 0) + if(t_mode&0x40 && damage > 0) + damage = 1; + + if(skill_num != CR_GRANDCROSS) + damage=battle_calc_damage(src,target,damage,div_,skill_num,skill_lv,flag); + + wd.damage=damage; + wd.damage2=0; + wd.type=type; + wd.div_=div_; + wd.amotion=battle_get_amotion(src); + if(skill_num == KN_AUTOCOUNTER) + wd.amotion >>= 1; + wd.dmotion=battle_get_dmotion(target); + wd.blewcount=blewcount; + wd.flag=flag; + wd.dmg_lv=dmg_lv; + + return wd; +} + +static struct Damage battle_calc_mob_weapon_attack( + struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag) +{ + struct map_session_data *tsd=NULL; + struct mob_data* md=(struct mob_data *)src,*tmd=NULL; + int hitrate,flee,cri = 0,atkmin,atkmax; + int luk,target_count = 1; + int def1 = battle_get_def(target); + int def2 = battle_get_def2(target); + int t_vit = battle_get_vit(target); + struct Damage wd; + int damage,damage2=0,type,div_,blewcount=skill_get_blewcount(skill_num,skill_lv); + int flag,skill,ac_flag = 0,dmg_lv = 0; + int t_mode=0,t_race=0,t_size=1,s_race=0,s_ele=0; + struct status_change *sc_data,*t_sc_data; + short *sc_count; + short *option, *opt1, *opt2; + + //return前の処理があるので情報出力部のみ変更 + if( src == NULL || target == NULL || md == NULL ){ + nullpo_info(NLP_MARK); + memset(&wd,0,sizeof(wd)); + return wd; + } + + s_race=battle_get_race(src); + s_ele=battle_get_attack_element(src); + sc_data=battle_get_sc_data(src); + sc_count=battle_get_sc_count(src); + option=battle_get_option(src); + opt1=battle_get_opt1(src); + opt2=battle_get_opt2(src); + + // ターゲット + if(target->type==BL_PC) + tsd=(struct map_session_data *)target; + else if(target->type==BL_MOB) + tmd=(struct mob_data *)target; + t_race=battle_get_race( target ); + t_size=battle_get_size( target ); + t_mode=battle_get_mode( target ); + t_sc_data=battle_get_sc_data( target ); + + if((skill_num == 0 || (target->type == BL_PC && battle_config.pc_auto_counter_type&2) || + (target->type == BL_MOB && battle_config.monster_auto_counter_type&2)) && skill_lv >= 0) { + if(skill_num != CR_GRANDCROSS && t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1) { + int dir = map_calc_dir(src,target->x,target->y),t_dir = battle_get_dir(target); + int dist = distance(src->x,src->y,target->x,target->y); + if(dist <= 0 || map_check_dir(dir,t_dir) ) { + memset(&wd,0,sizeof(wd)); + t_sc_data[SC_AUTOCOUNTER].val3 = 0; + t_sc_data[SC_AUTOCOUNTER].val4 = 1; + if(sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) { + int range = battle_get_range(target); + if((target->type == BL_PC && ((struct map_session_data *)target)->status.weapon != 11 && dist <= range+1) || + (target->type == BL_MOB && range <= 3 && dist <= range+1) ) + t_sc_data[SC_AUTOCOUNTER].val3 = src->id; + } + return wd; + } + else ac_flag = 1; + } + } + flag=BF_SHORT|BF_WEAPON|BF_NORMAL; // 攻撃の種類の設定 + + // 回避率計算、回避判定は後で + flee = battle_get_flee(target); + if(battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) + target_count += battle_counttargeted(target,src,battle_config.agi_penaly_count_lv); + if(battle_config.agi_penaly_type > 0) { + if(target_count >= battle_config.agi_penaly_count) { + if(battle_config.agi_penaly_type == 1) + flee = (flee * (100 - (target_count - (battle_config.agi_penaly_count - 1))*battle_config.agi_penaly_num))/100; + else if(battle_config.agi_penaly_type == 2) + flee -= (target_count - (battle_config.agi_penaly_count - 1))*battle_config.agi_penaly_num; + if(flee < 1) flee = 1; + } + } + hitrate=battle_get_hit(src) - flee + 80; + + type=0; // normal + div_ = 1; // single attack + + luk=battle_get_luk(src); + + if(battle_config.enemy_str) + damage = battle_get_baseatk(src); + else + damage = 0; + if(skill_num==HW_MAGICCRASHER){ /* マジッククラッシャーはMATKで殴る */ + atkmin = battle_get_matk1(src); + atkmax = battle_get_matk2(src); + }else{ + atkmin = battle_get_atk(src); + atkmax = battle_get_atk2(src); + } + if(mob_db[md->class].range>3 ) + flag=(flag&~BF_RANGEMASK)|BF_LONG; + + if(atkmin > atkmax) atkmin = atkmax; + + if(sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer!=-1 ){ // マキシマイズパワー + atkmin=atkmax; + } + + cri = battle_get_critical(src); + cri -= battle_get_luk(target) * 3; + if(battle_config.enemy_critical_rate != 100) { + cri = cri*battle_config.enemy_critical_rate/100; + if(cri < 1) + cri = 1; + } + if(t_sc_data != NULL && t_sc_data[SC_SLEEP].timer!=-1 ) // 睡眠中はクリティカルが倍に + cri <<=1; + + if(ac_flag) cri = 1000; + + if(skill_num == KN_AUTOCOUNTER) { + if(!(battle_config.monster_auto_counter_type&1)) + cri = 1000; + else + cri <<= 1; + } + + if(tsd && tsd->critical_def) + cri = cri * (100 - tsd->critical_def) / 100; + + if((skill_num == 0 || skill_num == KN_AUTOCOUNTER) && skill_lv >= 0 && battle_config.enemy_critical && (rand() % 1000) < cri) // 判定(スキルの場合は無視) + // 敵の判定 + { + damage += atkmax; + type = 0x0a; + } + else { + int vitbonusmax; + + if(atkmax > atkmin) + damage += atkmin + rand() % (atkmax-atkmin + 1); + else + damage += atkmin ; + // スキル修正1(攻撃力倍化系) + // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 + // バッシュ,マグナムブレイク, + // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, + // メマーナイト,カートレボリューション + // ダブルストレイフィング,アローシャワー,チャージアロー, + // ソニックブロー + if(sc_data){ //状態異常中のダメージ追加 + if(sc_data[SC_OVERTHRUST].timer!=-1) // オーバートラスト + damage += damage*(5*sc_data[SC_OVERTHRUST].val1)/100; + if(sc_data[SC_TRUESIGHT].timer!=-1) // トゥルーサイト + damage += damage*(2*sc_data[SC_TRUESIGHT].val1)/100; + if(sc_data[SC_BERSERK].timer!=-1) // バーサーク + damage += damage*50/100; + } + + if(skill_num>0){ + int i; + if( (i=skill_get_pl(skill_num))>0 ) + s_ele=i; + + flag=(flag&~BF_SKILLMASK)|BF_SKILL; + switch( skill_num ){ + case SM_BASH: // バッシュ + damage = damage*(100+ 30*skill_lv)/100; + hitrate = (hitrate*(100+5*skill_lv))/100; + break; + case SM_MAGNUM: // マグナムブレイク + damage = damage*(5*skill_lv +(wflag)?65:115 )/100; + break; + case MC_MAMMONITE: // メマーナイト + damage = damage*(100+ 50*skill_lv)/100; + break; + case AC_DOUBLE: // ダブルストレイフィング + damage = damage*(180+ 20*skill_lv)/100; + div_=2; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case AC_SHOWER: // アローシャワー + damage = damage*(75 + 5*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case AC_CHARGEARROW: // チャージアロー + damage = damage*150/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case KN_PIERCE: // ピアース + damage = damage*(100+ 10*skill_lv)/100; + hitrate=hitrate*(100+5*skill_lv)/100; + div_=t_size+1; + damage*=div_; + break; + case KN_SPEARSTAB: // スピアスタブ + damage = damage*(100+ 15*skill_lv)/100; + break; + case KN_SPEARBOOMERANG: // スピアブーメラン + damage = damage*(100+ 50*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case KN_BRANDISHSPEAR: // ブランディッシュスピア + damage = damage*(100+ 20*skill_lv)/100; + if(skill_lv>3 && wflag==1) damage2+=damage/2; + if(skill_lv>6 && wflag==1) damage2+=damage/4; + if(skill_lv>9 && wflag==1) damage2+=damage/8; + if(skill_lv>6 && wflag==2) damage2+=damage/2; + if(skill_lv>9 && wflag==2) damage2+=damage/4; + if(skill_lv>9 && wflag==3) damage2+=damage/2; + damage +=damage2; + blewcount=0; + break; + case KN_BOWLINGBASH: // ボウリングバッシュ + damage = damage*(100+ 50*skill_lv)/100; + blewcount=0; + break; + case KN_AUTOCOUNTER: + if(battle_config.monster_auto_counter_type&1) + hitrate += 20; + else + hitrate = 1000000; + flag=(flag&~BF_SKILLMASK)|BF_NORMAL; + break; + case AS_SONICBLOW: // ソニックブロウ + damage = damage*(300+ 50*skill_lv)/100; + div_=8; + break; + case TF_SPRINKLESAND: // 砂まき + damage = damage*125/100; + break; + case MC_CARTREVOLUTION: // カートレボリューション + damage = (damage*150)/100; + break; + // 以下MOB + case NPC_COMBOATTACK: // 多段攻撃 + div_=skill_get_num(skill_num,skill_lv); + damage *= div_; + break; + case NPC_RANDOMATTACK: // ランダムATK攻撃 + damage = damage*(50+rand()%150)/100; + break; + // 属性攻撃(適当) + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + damage = damage*(100+25*(skill_lv-1))/100; + break; + case NPC_GUIDEDATTACK: + hitrate = 1000000; + break; + case NPC_RANGEATTACK: + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case NPC_PIERCINGATT: + flag=(flag&~BF_RANGEMASK)|BF_SHORT; + break; + case RG_BACKSTAP: // バックスタブ + damage = damage*(300+ 40*skill_lv)/100; + hitrate = 1000000; + break; + case RG_RAID: // サプライズアタック + damage = damage*(100+ 40*skill_lv)/100; + break; + case RG_INTIMIDATE: // インティミデイト + damage = damage*(100+ 30*skill_lv)/100; + break; + case CR_SHIELDCHARGE: // シールドチャージ + damage = damage*(100+ 20*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_SHORT; + s_ele = 0; + break; + case CR_SHIELDBOOMERANG: // シールドブーメラン + damage = damage*(100+ 30*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + s_ele = 0; + break; + case CR_HOLYCROSS: // ホーリークロス + damage = damage*(100+ 35*skill_lv)/100; + div_=2; + break; + case CR_GRANDCROSS: + hitrate= 1000000; + break; + case AM_DEMONSTRATION: // デモンストレーション + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + break; + case AM_ACIDTERROR: // アシッドテラー + damage = damage*(100+ 40*skill_lv)/100; + damage2 = damage2*(100+ 40*skill_lv)/100; + break; + case MO_FINGEROFFENSIVE: //指弾 + damage = damage * (100 + 50 * skill_lv) / 100; + div_ = 1; + break; + case MO_INVESTIGATE: // 発 勁 + if(def1 < 1000000) + damage = damage*(100+ 75*skill_lv)/100 * (def1 + def2)/100; + hitrate = 1000000; + s_ele = 0; + break; + case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 + damage = damage * 8 + 250 + (skill_lv * 150); + hitrate = 1000000; + s_ele = 0; + break; + case MO_CHAINCOMBO: // 連打掌 + damage = damage*(150+ 50*skill_lv)/100; + div_=4; + break; + case BA_MUSICALSTRIKE: // ミュージカルストライク + damage = damage*(100+ 50 * skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case DC_THROWARROW: // 矢撃ち + damage = damage*(100+ 50 * skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case MO_COMBOFINISH: // 猛龍拳 + damage = damage*(240+ 60*skill_lv)/100; + break; + case CH_TIGERFIST: // 伏虎拳 + damage = damage*(100+ 20*skill_lv)/100; + break; + case CH_CHAINCRUSH: // 連柱崩撃 + damage = damage*(100+ 20*skill_lv)/100; + div_=skill_get_num(skill_num,skill_lv); + break; + case CH_PALMSTRIKE: // 猛虎硬派山 + damage = damage*(50+ 100*skill_lv)/100; + break; + case LK_SPIRALPIERCE: /* スパイラルピアース */ + damage = damage*(100+ 50*skill_lv)/100; //増加量が分からないので適当に + div_=5; + if(tsd) + tsd->canmove_tick = gettick() + 1000; + else if(tmd) + tmd->canmove_tick = gettick() + 1000; + break; + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + damage = damage*(100+ 20*skill_lv)/100; + break; + case LK_JOINTBEAT: /* ジョイントビート */ + damage = damage*(50+ 10*skill_lv)/100; + break; + case ASC_METEORASSAULT: /* メテオアサルト */ + damage = damage*(40+ 40*skill_lv)/100; + break; + case SN_SHARPSHOOTING: /* シャープシューティング */ + damage += damage*(30*skill_lv)/100; + break; + case CG_ARROWVULCAN: /* アローバルカン */ + damage = damage*(160+40*skill_lv)/100; + div_=9; + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + damage = damage*(200+20*skill_lv)/100; + break; + } + } + + if( skill_num!=NPC_CRITICALSLASH ){ + // 対 象の防御力によるダメージの減少 + // ディバインプロテクション(ここでいいのかな?) + if ( skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST && skill_num != KN_AUTOCOUNTER && def1 < 1000000) { //DEF, VIT無視 + int t_def; + target_count = 1 + battle_counttargeted(target,src,battle_config.vit_penaly_count_lv); + if(battle_config.vit_penaly_type > 0) { + if(target_count >= battle_config.vit_penaly_count) { + if(battle_config.vit_penaly_type == 1) { + def1 = (def1 * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + def2 = (def2 * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + t_vit = (t_vit * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + } + else if(battle_config.vit_penaly_type == 2) { + def1 -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + def2 -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + t_vit -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + } + if(def1 < 0) def1 = 0; + if(def2 < 1) def2 = 1; + if(t_vit < 1) t_vit = 1; + } + } + t_def = def2*8/10; + if(battle_check_undead(s_race,battle_get_elem_type(src)) || s_race==6) + if(tsd && (skill=pc_checkskill(tsd,AL_DP)) > 0 ) + t_def += skill*3; + + vitbonusmax = (t_vit/20)*(t_vit/20)-1; + if(battle_config.monster_defense_type) { + damage = damage - (def1 * battle_config.monster_defense_type) - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + else{ + damage = damage * (100 - def1) /100 - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + } + } + } + + // 0未満だった場合1に補正 + if(damage<1) damage=1; + + // 回避修正 + if(hitrate < 1000000) + hitrate = ( (hitrate>95)?95: ((hitrate<5)?5:hitrate) ); + if( hitrate < 1000000 && // 必中攻撃 + (t_sc_data != NULL && (t_sc_data[SC_SLEEP].timer!=-1 || // 睡眠は必中 + t_sc_data[SC_STAN].timer!=-1 || // スタンは必中 + t_sc_data[SC_FREEZE].timer!=-1 || (t_sc_data[SC_STONE].timer!=-1 && t_sc_data[SC_STONE].val2==0) ) ) ) // 凍結は必中 + hitrate = 1000000; + if(type == 0 && rand()%100 >= hitrate) { + damage = damage2 = 0; + dmg_lv = ATK_FLEE; + } else { + dmg_lv = ATK_DEF; + } + + if(tsd){ + int cardfix=100,i; + cardfix=cardfix*(100-tsd->subele[s_ele])/100; // 属 性によるダメージ耐性 + cardfix=cardfix*(100-tsd->subrace[s_race])/100; // 種族によるダメージ耐性 + if(mob_db[md->class].mode & 0x20) + cardfix=cardfix*(100-tsd->subrace[10])/100; + else + cardfix=cardfix*(100-tsd->subrace[11])/100; + for(i=0;i<tsd->add_def_class_count;i++) { + if(tsd->add_def_classid[i] == md->class) { + cardfix=cardfix*(100-tsd->add_def_classrate[i])/100; + break; + } + } + if(flag&BF_LONG) + cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; + if(flag&BF_SHORT) + cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; + damage=damage*cardfix/100; + } + if(t_sc_data) { + int cardfix=100; + if(t_sc_data[SC_DEFENDER].timer != -1 && flag&BF_LONG) + cardfix=cardfix*(100-t_sc_data[SC_DEFENDER].val2)/100; + if(cardfix != 100) + damage=damage*cardfix/100; + } + if(t_sc_data && t_sc_data[SC_ASSUMPTIO].timer != -1){ //アシャンプティオ + if(!map[target->m].flag.pvp) + damage=damage/3; + else + damage=damage/2; + } + + if(damage < 0) damage = 0; + + // 属 性の適用 + if (!((battle_config.mob_ghostring_fix == 1) && + (battle_get_element(target) == 8) && + (target->type==BL_PC))) // [MouseJstr] + if(skill_num != 0 || s_ele != 0 || !battle_config.mob_attack_attr_none) + damage=battle_attr_fix(damage, s_ele, battle_get_element(target) ); + + if(sc_data && sc_data[SC_AURABLADE].timer!=-1) /* オーラブレード 必中 */ + damage += sc_data[SC_AURABLADE].val1 * 10; + if(skill_num==PA_PRESSURE) /* プレッシャー 必中? */ + damage = 700+100*skill_lv; + + // インベナム修正 + if(skill_num==TF_POISON){ + damage = battle_attr_fix(damage + 15*skill_lv, s_ele, battle_get_element(target) ); + } + if(skill_num==MC_CARTREVOLUTION){ + damage = battle_attr_fix(damage, 0, battle_get_element(target) ); + } + + // 完全回避の判定 + if(skill_num == 0 && skill_lv >= 0 && tsd!=NULL && rand()%1000 < battle_get_flee2(target) ){ + damage=0; + type=0x0b; + dmg_lv = ATK_LUCKY; + } + + if(battle_config.enemy_perfect_flee) { + if(skill_num == 0 && skill_lv >= 0 && tmd!=NULL && rand()%1000 < battle_get_flee2(target) ){ + damage=0; + type=0x0b; + dmg_lv = ATK_LUCKY; + } + } + +// if(def1 >= 1000000 && damage > 0) + if(t_mode&0x40 && damage > 0) + damage = 1; + + if( tsd && tsd->special_state.no_weapon_damage) + damage = 0; + + if(skill_num != CR_GRANDCROSS) + damage=battle_calc_damage(src,target,damage,div_,skill_num,skill_lv,flag); + + wd.damage=damage; + wd.damage2=0; + wd.type=type; + wd.div_=div_; + wd.amotion=battle_get_amotion(src); + if(skill_num == KN_AUTOCOUNTER) + wd.amotion >>= 1; + wd.dmotion=battle_get_dmotion(target); + wd.blewcount=blewcount; + wd.flag=flag; + wd.dmg_lv=dmg_lv; + return wd; +} +/* + * ========================================================================= + * PCの武器による攻撃 + *------------------------------------------------------------------------- + */ +static struct Damage battle_calc_pc_weapon_attack( + struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag) +{ + struct map_session_data *sd=(struct map_session_data *)src,*tsd=NULL; + struct mob_data *tmd=NULL; + int hitrate,flee,cri = 0,atkmin,atkmax; + int dex,luk,target_count = 1; + int def1 = battle_get_def(target); + int def2 = battle_get_def2(target); + int t_vit = battle_get_vit(target); + struct Damage wd; + int damage,damage2,damage3=0,damage4=0,type,div_,blewcount=skill_get_blewcount(skill_num,skill_lv); + int flag,skill,dmg_lv = 0; + int t_mode=0,t_race=0,t_size=1,s_race=7,s_ele=0; + struct status_change *sc_data,*t_sc_data; + short *sc_count; + short *option, *opt1, *opt2; + int atkmax_=0, atkmin_=0, s_ele_; //二刀流用 + int watk,watk_,cardfix,t_ele; + int da=0,i,t_class,ac_flag = 0; + int idef_flag=0,idef_flag_=0; + + //return前の処理があるので情報出力部のみ変更 + if( src == NULL || target == NULL || sd == NULL ){ + nullpo_info(NLP_MARK); + memset(&wd,0,sizeof(wd)); + return wd; + } + + + // アタッカー + s_race=battle_get_race(src); //種族 + s_ele=battle_get_attack_element(src); //属性 + s_ele_=battle_get_attack_element2(src); //左手属性 + sc_data=battle_get_sc_data(src); //ステータス異常 + sc_count=battle_get_sc_count(src); //ステータス異常の数 + option=battle_get_option(src); //鷹とかペコとかカートとか + opt1=battle_get_opt1(src); //石化、凍結、スタン、睡眠、暗闇 + opt2=battle_get_opt2(src); //毒、呪い、沈黙、暗闇? + + if(skill_num != CR_GRANDCROSS) //グランドクロスでないなら + sd->state.attack_type = BF_WEAPON; //攻撃タイプは武器攻撃 + + // ターゲット + if(target->type==BL_PC) //対象がPCなら + tsd=(struct map_session_data *)target; //tsdに代入(tmdはNULL) + else if(target->type==BL_MOB) //対象がMobなら + tmd=(struct mob_data *)target; //tmdに代入(tsdはNULL) + t_race=battle_get_race( target ); //対象の種族 + t_ele=battle_get_elem_type(target); //対象の属性 + t_size=battle_get_size( target ); //対象のサイズ + t_mode=battle_get_mode( target ); //対象のMode + t_sc_data=battle_get_sc_data( target ); //対象のステータス異常 + +//オートカウンター処理ここから + if((skill_num == 0 || (target->type == BL_PC && battle_config.pc_auto_counter_type&2) || + (target->type == BL_MOB && battle_config.monster_auto_counter_type&2)) && skill_lv >= 0) { + if(skill_num != CR_GRANDCROSS && t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1) { //グランドクロスでなく、対象がオートカウンター状態の場合 + int dir = map_calc_dir(src,target->x,target->y),t_dir = battle_get_dir(target); + int dist = distance(src->x,src->y,target->x,target->y); + if(dist <= 0 || map_check_dir(dir,t_dir) ) { //対象との距離が0以下、または対象の正面? + memset(&wd,0,sizeof(wd)); + t_sc_data[SC_AUTOCOUNTER].val3 = 0; + t_sc_data[SC_AUTOCOUNTER].val4 = 1; + if(sc_data && sc_data[SC_AUTOCOUNTER].timer == -1) { //自分がオートカウンター状態 + int range = battle_get_range(target); + if((target->type == BL_PC && ((struct map_session_data *)target)->status.weapon != 11 && dist <= range+1) || //対象がPCで武器が弓矢でなく射程内 + (target->type == BL_MOB && range <= 3 && dist <= range+1) ) //または対象がMobで射程が3以下で射程内 + t_sc_data[SC_AUTOCOUNTER].val3 = src->id; + } + return wd; //ダメージ構造体を返して終了 + } + else ac_flag = 1; + } + } +//オートカウンター処理ここまで + + flag=BF_SHORT|BF_WEAPON|BF_NORMAL; // 攻撃の種類の設定 + + // 回避率計算、回避判定は後で + flee = battle_get_flee(target); + if(battle_config.agi_penaly_type > 0 || battle_config.vit_penaly_type > 0) //AGI、VITペナルティ設定が有効 + target_count += battle_counttargeted(target,src,battle_config.agi_penaly_count_lv); //対象の数を算出 + if(battle_config.agi_penaly_type > 0) { + if(target_count >= battle_config.agi_penaly_count) { //ペナルティ設定より対象が多い + if(battle_config.agi_penaly_type == 1) //回避率がagi_penaly_num%ずつ減少 + flee = (flee * (100 - (target_count - (battle_config.agi_penaly_count - 1))*battle_config.agi_penaly_num))/100; + else if(battle_config.agi_penaly_type == 2) //回避率がagi_penaly_num分減少 + flee -= (target_count - (battle_config.agi_penaly_count - 1))*battle_config.agi_penaly_num; + if(flee < 1) flee = 1; //回避率は最低でも1 + } + } + hitrate=battle_get_hit(src) - flee + 80; //命中率計算 + + type=0; // normal + div_ = 1; // single attack + + dex=battle_get_dex(src); //DEX + luk=battle_get_luk(src); //LUK + watk = battle_get_atk(src); //ATK + watk_ = battle_get_atk_(src); //ATK左手 + + if(skill_num==HW_MAGICCRASHER){ /* マジッククラッシャーはMATKで殴る */ + damage = damage2 = battle_get_matk1(src); //damega,damega2初登場、base_atkの取得 + }else{ + damage = damage2 = battle_get_baseatk(&sd->bl); //damega,damega2初登場、base_atkの取得 + } + atkmin = atkmin_ = dex; //最低ATKはDEXで初期化? + sd->state.arrow_atk = 0; //arrow_atk初期化 + if(sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]]) + atkmin = atkmin*(80 + sd->inventory_data[sd->equip_index[9]]->wlv*20)/100; + if(sd->equip_index[8] >= 0 && sd->inventory_data[sd->equip_index[8]]) + atkmin_ = atkmin_*(80 + sd->inventory_data[sd->equip_index[8]]->wlv*20)/100; + if(sd->status.weapon == 11) { //武器が弓矢の場合 + atkmin = watk * ((atkmin<watk)? atkmin:watk)/100; //弓用最低ATK計算 + flag=(flag&~BF_RANGEMASK)|BF_LONG; //遠距離攻撃フラグを有効 + if(sd->arrow_ele > 0) //属性矢なら属性を矢の属性に変更 + s_ele = sd->arrow_ele; + sd->state.arrow_atk = 1; //arrow_atk有効化 + } + + // サイズ修正 + // ペコ騎乗していて、槍で攻撃した場合は中型のサイズ修正を100にする + // ウェポンパーフェクション,ドレイクC + if(((sd->special_state.no_sizefix) || (pc_isriding(sd) && (sd->status.weapon==4 || sd->status.weapon==5) && t_size==1) || skill_num == MO_EXTREMITYFIST)){ //ペコ騎乗していて、槍で中型を攻撃 + atkmax = watk; + atkmax_ = watk_; + } else { + atkmax = (watk * sd->atkmods[ t_size ]) / 100; + atkmin = (atkmin * sd->atkmods[ t_size ]) / 100; + atkmax_ = (watk_ * sd->atkmods_[ t_size ]) / 100; + atkmin_ = (atkmin_ * sd->atkmods[ t_size ]) / 100; + } + if( (sc_data != NULL && sc_data[SC_WEAPONPERFECTION].timer!=-1) || (sd->special_state.no_sizefix)) { // ウェポンパーフェクション || ドレイクカード + atkmax = watk; + atkmax_ = watk_; + } + + if(atkmin > atkmax && !(sd->state.arrow_atk)) atkmin = atkmax; //弓は最低が上回る場合あり + if(atkmin_ > atkmax_) atkmin_ = atkmax_; + + if(sc_data != NULL && sc_data[SC_MAXIMIZEPOWER].timer!=-1 ){ // マキシマイズパワー + atkmin=atkmax; + atkmin_=atkmax_; + } + + //ダブルアタック判定 + if(sd->weapontype1 == 0x01) { + if(skill_num == 0 && skill_lv >= 0 && (skill = pc_checkskill(sd,TF_DOUBLE)) > 0) + da = (rand()%100 < (skill*5)) ? 1:0; + } + + //三段掌 + if(skill_num == 0 && skill_lv >= 0 && (skill = pc_checkskill(sd,MO_TRIPLEATTACK)) > 0 && sd->status.weapon <= 16 && !sd->state.arrow_atk) { + da = (rand()%100 < (30 - skill)) ? 2:0; + } + + if(sd->double_rate > 0 && da == 0 && skill_num == 0 && skill_lv >= 0) + da = (rand()%100 < sd->double_rate) ? 1:0; + + // 過剰精錬ボーナス + if(sd->overrefine>0 ) + damage+=(rand()%sd->overrefine)+1; + if(sd->overrefine_>0 ) + damage2+=(rand()%sd->overrefine_)+1; + + if(da == 0){ //ダブルアタックが発動していない + // クリティカル計算 + cri = battle_get_critical(src); + + if(sd->state.arrow_atk) + cri += sd->arrow_cri; + if(sd->status.weapon == 16) + // カタールの場合、クリティカルを倍に + cri <<=1; + cri -= battle_get_luk(target) * 3; + if(t_sc_data != NULL && t_sc_data[SC_SLEEP].timer!=-1 ) // 睡眠中はクリティカルが倍に + cri <<=1; + if(ac_flag) cri = 1000; + + if(skill_num == KN_AUTOCOUNTER) { + if(!(battle_config.pc_auto_counter_type&1)) + cri = 1000; + else + cri <<= 1; + } + + if(skill_num == SN_SHARPSHOOTING && rand()%100 < 50) + cri = 1000; + } + + if(tsd && tsd->critical_def) + cri = cri * (100-tsd->critical_def) / 100; + + if(da == 0 && (skill_num==0 || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING) && skill_lv >= 0 && //ダブルアタックが発動していない + (rand() % 1000) < cri) // 判定(スキルの場合は無視) + { + damage += atkmax; + damage2 += atkmax_; + if(sd->atk_rate != 100) { + damage = (damage * sd->atk_rate)/100; + damage2 = (damage2 * sd->atk_rate)/100; + } + if(sd->state.arrow_atk) + damage += sd->arrow_atk; + type = 0x0a; + +/* if(def1 < 1000000) { + if(sd->def_ratio_atk_ele & (1<<t_ele) || sd->def_ratio_atk_race & (1<<t_race)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(sd->def_ratio_atk_ele_ & (1<<t_ele) || sd->def_ratio_atk_race_ & (1<<t_race)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + if(t_mode & 0x20) { + if(!idef_flag && sd->def_ratio_atk_race & (1<<10)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<10)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + } + else { + if(!idef_flag && sd->def_ratio_atk_race & (1<<11)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<11)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + } + }*/ + } + else { + int vitbonusmax; + + if(atkmax > atkmin) + damage += atkmin + rand() % (atkmax-atkmin + 1); + else + damage += atkmin ; + if(atkmax_ > atkmin_) + damage2 += atkmin_ + rand() % (atkmax_-atkmin_ + 1); + else + damage2 += atkmin_ ; + if(sd->atk_rate != 100) { + damage = (damage * sd->atk_rate)/100; + damage2 = (damage2 * sd->atk_rate)/100; + } + + if(sd->state.arrow_atk) { + if(sd->arrow_atk > 0) + damage += rand()%(sd->arrow_atk+1); + hitrate += sd->arrow_hit; + } + + if(skill_num != MO_INVESTIGATE && def1 < 1000000) { + if(sd->def_ratio_atk_ele & (1<<t_ele) || sd->def_ratio_atk_race & (1<<t_race)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(sd->def_ratio_atk_ele_ & (1<<t_ele) || sd->def_ratio_atk_race_ & (1<<t_race)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + if(t_mode & 0x20) { + if(!idef_flag && sd->def_ratio_atk_race & (1<<10)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<10)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + } + else { + if(!idef_flag && sd->def_ratio_atk_race & (1<<11)) { + damage = (damage * (def1 + def2))/100; + idef_flag = 1; + } + if(!idef_flag_ && sd->def_ratio_atk_race_ & (1<<11)) { + damage2 = (damage2 * (def1 + def2))/100; + idef_flag_ = 1; + } + } + } + + // スキル修正1(攻撃力倍化系) + // オーバートラスト(+5% 〜 +25%),他攻撃系スキルの場合ここで補正 + // バッシュ,マグナムブレイク, + // ボーリングバッシュ,スピアブーメラン,ブランディッシュスピア,スピアスタッブ, + // メマーナイト,カートレボリューション + // ダブルストレイフィング,アローシャワー,チャージアロー, + // ソニックブロー + if(sc_data){ //状態異常中のダメージ追加 + if(sc_data[SC_OVERTHRUST].timer!=-1){ // オーバートラスト + damage += damage*(5*sc_data[SC_OVERTHRUST].val1)/100; + damage2 += damage2*(5*sc_data[SC_OVERTHRUST].val1)/100; + } + if(sc_data[SC_TRUESIGHT].timer!=-1){ // トゥルーサイト + damage += damage*(2*sc_data[SC_TRUESIGHT].val1)/100; + damage2 += damage2*(2*sc_data[SC_TRUESIGHT].val1)/100; + } + if(sc_data[SC_BERSERK].timer!=-1){ // バーサーク + damage += damage*50/100; + damage2 += damage2*50/100; + } + } + + if(skill_num>0){ + int i; + if( (i=skill_get_pl(skill_num))>0 ) + s_ele=s_ele_=i; + + flag=(flag&~BF_SKILLMASK)|BF_SKILL; + switch( skill_num ){ + case SM_BASH: // バッシュ + damage = damage*(100+ 30*skill_lv)/100; + damage2 = damage2*(100+ 30*skill_lv)/100; + hitrate = (hitrate*(100+5*skill_lv))/100; + break; + case SM_MAGNUM: // マグナムブレイク + damage = damage*(5*skill_lv +(wflag)?65:115 )/100; + damage2 = damage2*(5*skill_lv +(wflag)?65:115 )/100; + break; + case MC_MAMMONITE: // メマーナイト + damage = damage*(100+ 50*skill_lv)/100; + damage2 = damage2*(100+ 50*skill_lv)/100; + break; + case AC_DOUBLE: // ダブルストレイフィング + if(!sd->state.arrow_atk && sd->arrow_atk > 0) { + int arr = rand()%(sd->arrow_atk+1); + damage += arr; + damage2 += arr; + } + damage = damage*(180+ 20*skill_lv)/100; + damage2 = damage2*(180+ 20*skill_lv)/100; + div_=2; + if(sd->arrow_ele > 0) { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag=(flag&~BF_RANGEMASK)|BF_LONG; + sd->state.arrow_atk = 1; + break; + case AC_SHOWER: // アローシャワー + if(!sd->state.arrow_atk && sd->arrow_atk > 0) { + int arr = rand()%(sd->arrow_atk+1); + damage += arr; + damage2 += arr; + } + damage = damage*(75 + 5*skill_lv)/100; + damage2 = damage2*(75 + 5*skill_lv)/100; + if(sd->arrow_ele > 0) { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag=(flag&~BF_RANGEMASK)|BF_LONG; + sd->state.arrow_atk = 1; + break; + case AC_CHARGEARROW: // チャージアロー + if(!sd->state.arrow_atk && sd->arrow_atk > 0) { + int arr = rand()%(sd->arrow_atk+1); + damage += arr; + damage2 += arr; + } + damage = damage*150/100; + damage2 = damage2*150/100; + if(sd->arrow_ele > 0) { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag=(flag&~BF_RANGEMASK)|BF_LONG; + sd->state.arrow_atk = 1; + break; + case KN_PIERCE: // ピアース + damage = damage*(100+ 10*skill_lv)/100; + damage2 = damage2*(100+ 10*skill_lv)/100; + hitrate=hitrate*(100+5*skill_lv)/100; + div_=t_size+1; + damage*=div_; + damage2*=div_; + break; + case KN_SPEARSTAB: // スピアスタブ + damage = damage*(100+ 15*skill_lv)/100; + damage2 = damage2*(100+ 15*skill_lv)/100; + break; + case KN_SPEARBOOMERANG: // スピアブーメラン + damage = damage*(100+ 50*skill_lv)/100; + damage2 = damage2*(100+ 50*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case KN_BRANDISHSPEAR: // ブランディッシュスピア + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + if(skill_lv>3 && wflag==1) damage3+=damage/2; + if(skill_lv>6 && wflag==1) damage3+=damage/4; + if(skill_lv>9 && wflag==1) damage3+=damage/8; + if(skill_lv>6 && wflag==2) damage3+=damage/2; + if(skill_lv>9 && wflag==2) damage3+=damage/4; + if(skill_lv>9 && wflag==3) damage3+=damage/2; + damage +=damage3; + if(skill_lv>3 && wflag==1) damage4+=damage2/2; + if(skill_lv>6 && wflag==1) damage4+=damage2/4; + if(skill_lv>9 && wflag==1) damage4+=damage2/8; + if(skill_lv>6 && wflag==2) damage4+=damage2/2; + if(skill_lv>9 && wflag==2) damage4+=damage2/4; + if(skill_lv>9 && wflag==3) damage4+=damage2/2; + damage2 +=damage4; + blewcount=0; + break; + case KN_BOWLINGBASH: // ボウリングバッシュ + damage = damage*(100+ 50*skill_lv)/100; + damage2 = damage2*(100+ 50*skill_lv)/100; + blewcount=0; + break; + case KN_AUTOCOUNTER: + if(battle_config.pc_auto_counter_type&1) + hitrate += 20; + else + hitrate = 1000000; + flag=(flag&~BF_SKILLMASK)|BF_NORMAL; + break; + case AS_SONICBLOW: // ソニックブロウ + hitrate+=30; // hitrate +30, thanks to midas + damage = damage*(300+ 50*skill_lv)/100; + damage2 = damage2*(300+ 50*skill_lv)/100; + div_=8; + break; + case TF_SPRINKLESAND: // 砂まき + damage = damage*125/100; + damage2 = damage2*125/100; + break; + case MC_CARTREVOLUTION: // カートレボリューション + if(sd->cart_max_weight > 0 && sd->cart_weight > 0) { + damage = (damage*(150 + pc_checkskill(sd,BS_WEAPONRESEARCH) + (sd->cart_weight*100/sd->cart_max_weight) ) )/100; + damage2 = (damage2*(150 + pc_checkskill(sd,BS_WEAPONRESEARCH) + (sd->cart_weight*100/sd->cart_max_weight) ) )/100; + } + else { + damage = (damage*150)/100; + damage2 = (damage2*150)/100; + } + break; + // 以下MOB + case NPC_COMBOATTACK: // 多段攻撃 + div_=skill_get_num(skill_num,skill_lv); + damage *= div_; + damage2 *= div_; + break; + case NPC_RANDOMATTACK: // ランダムATK攻撃 + damage = damage*(50+rand()%150)/100; + damage2 = damage2*(50+rand()%150)/100; + break; + // 属性攻撃(適当) + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + damage = damage*(100+25*skill_lv)/100; + damage2 = damage2*(100+25*skill_lv)/100; + break; + case NPC_GUIDEDATTACK: + hitrate = 1000000; + break; + case NPC_RANGEATTACK: + flag=(flag&~BF_RANGEMASK)|BF_LONG; + break; + case NPC_PIERCINGATT: + flag=(flag&~BF_RANGEMASK)|BF_SHORT; + break; + case RG_BACKSTAP: // バックスタブ + if(battle_config.backstab_bow_penalty == 1 && sd->status.weapon == 11){ + damage = (damage*(300+ 40*skill_lv)/100)/2; + damage2 = (damage2*(300+ 40*skill_lv)/100)/2; + }else{ + damage = damage*(300+ 40*skill_lv)/100; + damage2 = damage2*(300+ 40*skill_lv)/100; + } + hitrate = 1000000; + break; + case RG_RAID: // サプライズアタック + damage = damage*(100+ 40*skill_lv)/100; + damage2 = damage2*(100+ 40*skill_lv)/100; + break; + case RG_INTIMIDATE: // インティミデイト + damage = damage*(100+ 30*skill_lv)/100; + damage2 = damage2*(100+ 30*skill_lv)/100; + break; + case CR_SHIELDCHARGE: // シールドチャージ + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_SHORT; + s_ele = 0; + break; + case CR_SHIELDBOOMERANG: // シールドブーメラン + damage = damage*(100+ 30*skill_lv)/100; + damage2 = damage2*(100+ 30*skill_lv)/100; + flag=(flag&~BF_RANGEMASK)|BF_LONG; + s_ele = 0; + break; + case CR_HOLYCROSS: // ホーリークロス + damage = damage*(100+ 35*skill_lv)/100; + damage2 = damage2*(100+ 35*skill_lv)/100; + div_=2; + break; + case CR_GRANDCROSS: + hitrate= 1000000; + break; + case AM_DEMONSTRATION: // デモンストレーション + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + break; + case AM_ACIDTERROR: // アシッドテラー + damage = damage*(100+ 40*skill_lv)/100; + damage2 = damage2*(100+ 40*skill_lv)/100; + break; + case MO_FINGEROFFENSIVE: //指弾 + if(battle_config.finger_offensive_type == 0) { + damage = damage * (100 + 50 * skill_lv) / 100 * sd->spiritball_old; + damage2 = damage2 * (100 + 50 * skill_lv) / 100 * sd->spiritball_old; + div_ = sd->spiritball_old; + } + else { + damage = damage * (100 + 50 * skill_lv) / 100; + damage2 = damage2 * (100 + 50 * skill_lv) / 100; + div_ = 1; + } + break; + case MO_INVESTIGATE: // 発 勁 + if(def1 < 1000000) { + damage = damage*(100+ 75*skill_lv)/100 * (def1 + def2)/100; + damage2 = damage2*(100+ 75*skill_lv)/100 * (def1 + def2)/100; + } + hitrate = 1000000; + s_ele = 0; + s_ele_ = 0; + break; + case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 + damage = damage * (8 + ((sd->status.sp)/10)) + 250 + (skill_lv * 150); + damage2 = damage2 * (8 + ((sd->status.sp)/10)) + 250 + (skill_lv * 150); + sd->status.sp = 0; + clif_updatestatus(sd,SP_SP); + hitrate = 1000000; + s_ele = 0; + s_ele_ = 0; + break; + case MO_CHAINCOMBO: // 連打掌 + damage = damage*(150+ 50*skill_lv)/100; + damage2 = damage2*(150+ 50*skill_lv)/100; + div_=4; + break; + case MO_COMBOFINISH: // 猛龍拳 + damage = damage*(240+ 60*skill_lv)/100; + damage2 = damage2*(240+ 60*skill_lv)/100; + break; + case BA_MUSICALSTRIKE: // ミュージカルストライク + if(!sd->state.arrow_atk && sd->arrow_atk > 0) { + int arr = rand()%(sd->arrow_atk+1); + damage += arr; + damage2 += arr; + } + damage = damage*(100+ 50 * skill_lv)/100; + damage2 = damage2*(100+ 50 * skill_lv)/100; + if(sd->arrow_ele > 0) { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag=(flag&~BF_RANGEMASK)|BF_LONG; + sd->state.arrow_atk = 1; + break; + case DC_THROWARROW: // 矢撃ち + if(!sd->state.arrow_atk && sd->arrow_atk > 0) { + int arr = rand()%(sd->arrow_atk+1); + damage += arr; + damage2 += arr; + } + damage = damage*(100+ 50 * skill_lv)/100; + damage2 = damage2*(100+ 50 * skill_lv)/100; + if(sd->arrow_ele > 0) { + s_ele = sd->arrow_ele; + s_ele_ = sd->arrow_ele; + } + flag=(flag&~BF_RANGEMASK)|BF_LONG; + sd->state.arrow_atk = 1; + break; + case CH_TIGERFIST: // 伏虎拳 + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + break; + case CH_CHAINCRUSH: // 連柱崩撃 + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + div_=skill_get_num(skill_num,skill_lv); + break; + case CH_PALMSTRIKE: // 猛虎硬派山 + damage = damage*(50+ 100*skill_lv)/100; + damage2 = damage2*(50+ 100*skill_lv)/100; + break; + case LK_SPIRALPIERCE: /* スパイラルピアース */ + damage = damage*(100+ 50*skill_lv)/100; //増加量が分からないので適当に + damage2 = damage2*(100+ 50*skill_lv)/100; //増加量が分からないので適当に + div_=5; + if(tsd) + tsd->canmove_tick = gettick() + 1000; + else if(tmd) + tmd->canmove_tick = gettick() + 1000; + break; + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + damage = damage*(100+ 20*skill_lv)/100; + damage2 = damage2*(100+ 20*skill_lv)/100; + break; + case LK_JOINTBEAT: /* ジョイントビート */ + damage = damage*(50+ 10*skill_lv)/100; + damage2 = damage2*(50+ 10*skill_lv)/100; + break; + case ASC_METEORASSAULT: /* メテオアサルト */ + damage = damage*(40+ 40*skill_lv)/100; + damage2 = damage2*(40+ 40*skill_lv)/100; + break; + case SN_SHARPSHOOTING: /* シャープシューティング */ + damage += damage*(30*skill_lv)/100; + damage2 += damage2*(30*skill_lv)/100; + break; + case CG_ARROWVULCAN: /* アローバルカン */ + damage = damage*(160+40*skill_lv)/100; + damage2 = damage2*(160+40*skill_lv)/100; + div_=9; + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + damage = damage*(200+20*skill_lv+20*pc_checkskill(sd,AS_POISONREACT))/100; + damage2 = damage2*(200+20*skill_lv+20*pc_checkskill(sd,AS_POISONREACT))/100; + break; + case PA_SACRIFICE: + if(sd){ + int hp, mhp, damage3; + hp = battle_get_hp(src); + mhp = battle_get_max_hp(src); + damage3 = mhp*((skill_lv/2)+(50/100))/100; + damage = (((skill_lv*15)+90)/100)*damage3/100; + damage2 = (((skill_lv*15)+90)/100)*damage3/100; + } + break; + case ASC_BREAKER: // -- moonsoul (special damage for ASC_BREAKER skill) + if(sd){ + int damage3; + int mdef1=battle_get_mdef(target); + int mdef2=battle_get_mdef2(target); + int imdef_flag=0; + + damage = ((damage * 5) + (skill_lv * battle_get_int(src) * 5) + rand()%500 + 500) /2; + damage2 = ((damage2 * 5) + (skill_lv * battle_get_int(src) * 5) + rand()%500 + 500) /2; + damage3 = damage; + hitrate = 1000000; + + if(sd->ignore_mdef_ele & (1<<t_ele) || sd->ignore_mdef_race & (1<<t_race)) + imdef_flag = 1; + if(t_mode & 0x20) { + if(sd->ignore_mdef_race & (1<<10)) + imdef_flag = 1; + } + else { + if(sd->ignore_mdef_race & (1<<11)) + imdef_flag = 1; + } + if(!imdef_flag){ + if(battle_config.magic_defense_type) { + damage3 = damage3 - (mdef1 * battle_config.magic_defense_type) - mdef2; + } + else{ + damage3 = (damage3*(100-mdef1))/100 - mdef2; + } + } + + if(damage3<1) + damage3=1; + + damage3=battle_attr_fix(damage2,s_ele_, battle_get_element(target) ); + } + break; + } + } + if(da == 2) { //三段掌が発動しているか + type = 0x08; + div_ = 255; //三段掌用に… + damage = damage * (100 + 20 * pc_checkskill(sd, MO_TRIPLEATTACK)) / 100; + } + + if( skill_num!=NPC_CRITICALSLASH ){ + // 対 象の防御力によるダメージの減少 + // ディバインプロテクション(ここでいいのかな?) + if ( skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST && skill_num != KN_AUTOCOUNTER && def1 < 1000000) { //DEF, VIT無視 + int t_def; + target_count = 1 + battle_counttargeted(target,src,battle_config.vit_penaly_count_lv); + if(battle_config.vit_penaly_type > 0) { + if(target_count >= battle_config.vit_penaly_count) { + if(battle_config.vit_penaly_type == 1) { + def1 = (def1 * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + def2 = (def2 * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + t_vit = (t_vit * (100 - (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num))/100; + } + else if(battle_config.vit_penaly_type == 2) { + def1 -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + def2 -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + t_vit -= (target_count - (battle_config.vit_penaly_count - 1))*battle_config.vit_penaly_num; + } + if(def1 < 0) def1 = 0; + if(def2 < 1) def2 = 1; + if(t_vit < 1) t_vit = 1; + } + } + t_def = def2*8/10; + vitbonusmax = (t_vit/20)*(t_vit/20)-1; + if(sd->ignore_def_ele & (1<<t_ele) || sd->ignore_def_race & (1<<t_race)) + idef_flag = 1; + if(sd->ignore_def_ele_ & (1<<t_ele) || sd->ignore_def_race_ & (1<<t_race)) + idef_flag_ = 1; + if(t_mode & 0x20) { + if(sd->ignore_def_race & (1<<10)) + idef_flag = 1; + if(sd->ignore_def_race_ & (1<<10)) + idef_flag_ = 1; + } + else { + if(sd->ignore_def_race & (1<<11)) + idef_flag = 1; + if(sd->ignore_def_race_ & (1<<11)) + idef_flag_ = 1; + } + + if(!idef_flag){ + if(battle_config.player_defense_type) { + damage = damage - (def1 * battle_config.player_defense_type) - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + else{ + damage = damage * (100 - def1) /100 - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + } + if(!idef_flag_){ + if(battle_config.player_defense_type) { + damage2 = damage2 - (def1 * battle_config.player_defense_type) - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + else{ + damage2 = damage2 * (100 - def1) /100 - t_def - ((vitbonusmax < 1)?0: rand()%(vitbonusmax+1) ); + } + } + } + } + } + // 精錬ダメージの追加 + if( skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) { //DEF, VIT無視 + damage += battle_get_atk2(src); + damage2 += battle_get_atk_2(src); + } + if(skill_num == CR_SHIELDBOOMERANG) { + if(sd->equip_index[8] >= 0) { + int index = sd->equip_index[8]; + if(sd->inventory_data[index] && sd->inventory_data[index]->type == 5) { + damage += sd->inventory_data[index]->weight/10; + damage += sd->status.inventory[index].refine * pc_getrefinebonus(0,1); + } + } + } + if(skill_num == LK_SPIRALPIERCE) { /* スパイラルピアース */ + if(sd->equip_index[9] >= 0) { //重量で追加ダメージらしいのでシールドブーメランを参考に追加 + int index = sd->equip_index[9]; + if(sd->inventory_data[index] && sd->inventory_data[index]->type == 4) { + damage += (int)(double)(sd->inventory_data[index]->weight*(0.8*skill_lv*4/10)); + damage += sd->status.inventory[index].refine * pc_getrefinebonus(0,1); + } + } + } + + // 0未満だった場合1に補正 + if(damage<1) damage=1; + if(damage2<1) damage2=1; + + // スキル修正2(修練系) + // 修練ダメージ(右手のみ) ソニックブロー時は別処理(1撃に付き1/8適応) + if( skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST && skill_num != CR_GRANDCROSS) { //修練ダメージ無視 + damage = battle_addmastery(sd,target,damage,0); + damage2 = battle_addmastery(sd,target,damage2,1); + } + + if(sd->perfect_hit > 0) { + if(rand()%100 < sd->perfect_hit) + hitrate = 1000000; + } + + // 回避修正 + hitrate = (hitrate<5)?5:hitrate; + if( hitrate < 1000000 && // 必中攻撃 + (t_sc_data != NULL && (t_sc_data[SC_SLEEP].timer!=-1 || // 睡眠は必中 + t_sc_data[SC_STAN].timer!=-1 || // スタンは必中 + t_sc_data[SC_FREEZE].timer!=-1 || (t_sc_data[SC_STONE].timer!=-1 && t_sc_data[SC_STONE].val2==0) ) ) ) // 凍結は必中 + hitrate = 1000000; + if(type == 0 && rand()%100 >= hitrate) { + damage = damage2 = 0; + dmg_lv = ATK_FLEE; + } else { + dmg_lv = ATK_DEF; + } + // スキル修正3(武器研究) + if( (skill=pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0) { + damage+= skill*2; + damage2+= skill*2; + } + //Advanced Katar Research by zanetheinsane + if(sd->weapontype1 == 0x10 || sd->weapontype2 == 0x10){ + if((skill = pc_checkskill(sd,ASC_KATAR)) > 0) { + damage += (damage*((skill*2)+10)) / 100 ; + } + } + +//スキルによるダメージ補正ここまで + +//カードによるダメージ追加処理ここから + cardfix=100; + if(!sd->state.arrow_atk) { //弓矢以外 + if(!battle_config.left_cardfix_to_right) { //左手カード補正設定無し + cardfix=cardfix*(100+sd->addrace[t_race])/100; // 種族によるダメージ修正 + cardfix=cardfix*(100+sd->addele[t_ele])/100; // 属性によるダメージ修正 + cardfix=cardfix*(100+sd->addsize[t_size])/100; // サイズによるダメージ修正 + } + else { + cardfix=cardfix*(100+sd->addrace[t_race]+sd->addrace_[t_race])/100; // 種族によるダメージ修正(左手による追加あり) + cardfix=cardfix*(100+sd->addele[t_ele]+sd->addele_[t_ele])/100; // 属性によるダメージ修正(左手による追加あり) + cardfix=cardfix*(100+sd->addsize[t_size]+sd->addsize_[t_size])/100; // サイズによるダメージ修正(左手による追加あり) + } + } + else { //弓矢 + cardfix=cardfix*(100+sd->addrace[t_race]+sd->arrow_addrace[t_race])/100; // 種族によるダメージ修正(弓矢による追加あり) + cardfix=cardfix*(100+sd->addele[t_ele]+sd->arrow_addele[t_ele])/100; // 属性によるダメージ修正(弓矢による追加あり) + cardfix=cardfix*(100+sd->addsize[t_size]+sd->arrow_addsize[t_size])/100; // サイズによるダメージ修正(弓矢による追加あり) + } + if(t_mode & 0x20) { //ボス + if(!sd->state.arrow_atk) { //弓矢攻撃以外なら + if(!battle_config.left_cardfix_to_right) //左手カード補正設定無し + cardfix=cardfix*(100+sd->addrace[10])/100; //ボスモンスターに追加ダメージ + else //左手カード補正設定あり + cardfix=cardfix*(100+sd->addrace[10]+sd->addrace_[10])/100; //ボスモンスターに追加ダメージ(左手による追加あり) + } + else //弓矢攻撃 + cardfix=cardfix*(100+sd->addrace[10]+sd->arrow_addrace[10])/100; //ボスモンスターに追加ダメージ(弓矢による追加あり) + } + else { //ボスじゃない + if(!sd->state.arrow_atk) { //弓矢攻撃以外 + if(!battle_config.left_cardfix_to_right) //左手カード補正設定無し + cardfix=cardfix*(100+sd->addrace[11])/100; //ボス以外モンスターに追加ダメージ + else //左手カード補正設定あり + cardfix=cardfix*(100+sd->addrace[11]+sd->addrace_[11])/100; //ボス以外モンスターに追加ダメージ(左手による追加あり) + } + else + cardfix=cardfix*(100+sd->addrace[11]+sd->arrow_addrace[11])/100; //ボス以外モンスターに追加ダメージ(弓矢による追加あり) + } + //特定Class用補正処理(少女の日記→ボンゴン用?) + t_class = battle_get_class(target); + for(i=0;i<sd->add_damage_class_count;i++) { + if(sd->add_damage_classid[i] == t_class) { + cardfix=cardfix*(100+sd->add_damage_classrate[i])/100; + break; + } + } + if(skill_num != CR_GRANDCROSS || !battle_config.gx_cardfix) + damage=damage*cardfix/100; //カード補正によるダメージ増加 +//カードによるダメージ増加処理ここまで + +//カードによるダメージ追加処理(左手)ここから + cardfix=100; + if(!battle_config.left_cardfix_to_right) { //左手カード補正設定無し + cardfix=cardfix*(100+sd->addrace_[t_race])/100; // 種族によるダメージ修正左手 + cardfix=cardfix*(100+sd->addele_[t_ele])/100; // 属 性によるダメージ修正左手 + cardfix=cardfix*(100+sd->addsize_[t_size])/100; // サイズによるダメージ修正左手 + if(t_mode & 0x20) //ボス + cardfix=cardfix*(100+sd->addrace_[10])/100; //ボスモンスターに追加ダメージ左手 + else + cardfix=cardfix*(100+sd->addrace_[11])/100; //ボス以外モンスターに追加ダメージ左手 + } + //特定Class用補正処理左手(少女の日記→ボンゴン用?) + for(i=0;i<sd->add_damage_class_count_;i++) { + if(sd->add_damage_classid_[i] == t_class) { + cardfix=cardfix*(100+sd->add_damage_classrate_[i])/100; + break; + } + } + if(skill_num != CR_GRANDCROSS) damage2=damage2*cardfix/100; //カード補正による左手ダメージ増加 +//カードによるダメージ増加処理(左手)ここまで + +// -- moonsoul (cardfix for magic damage portion of ASC_BREAKER) + if(skill_num == ASC_BREAKER) + damage3 = damage3 * cardfix / 100; + +//カードによるダメージ減衰処理ここから + if(tsd){ //対象がPCの場合 + cardfix=100; + cardfix=cardfix*(100-tsd->subrace[s_race])/100; // 種族によるダメージ耐性 + cardfix=cardfix*(100-tsd->subele[s_ele])/100; // 属性によるダメージ耐性 + if(battle_get_mode(src) & 0x20) + cardfix=cardfix*(100-tsd->subrace[10])/100; //ボスからの攻撃はダメージ減少 + else + cardfix=cardfix*(100-tsd->subrace[11])/100; //ボス以外からの攻撃はダメージ減少 + //特定Class用補正処理左手(少女の日記→ボンゴン用?) + for(i=0;i<tsd->add_def_class_count;i++) { + if(tsd->add_def_classid[i] == sd->status.class) { + cardfix=cardfix*(100-tsd->add_def_classrate[i])/100; + break; + } + } + if(flag&BF_LONG) + cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; //遠距離攻撃はダメージ減少(ホルンCとか) + if(flag&BF_SHORT) + cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; //近距離攻撃はダメージ減少(該当無し?) + damage=damage*cardfix/100; //カード補正によるダメージ減少 + damage2=damage2*cardfix/100; //カード補正による左手ダメージ減少 + } +//カードによるダメージ減衰処理ここまで + +//対象にステータス異常がある場合のダメージ減算処理ここから + if(t_sc_data) { + cardfix=100; + if(t_sc_data[SC_DEFENDER].timer != -1 && flag&BF_LONG) //ディフェンダー状態で遠距離攻撃 + cardfix=cardfix*(100-t_sc_data[SC_DEFENDER].val2)/100; //ディフェンダーによる減衰 + if(cardfix != 100) { + damage=damage*cardfix/100; //ディフェンダー補正によるダメージ減少 + damage2=damage2*cardfix/100; //ディフェンダー補正による左手ダメージ減少 + } + if(t_sc_data[SC_ASSUMPTIO].timer != -1){ //アスムプティオ + if(!map[target->m].flag.pvp){ + damage=damage/3; + damage2=damage2/3; + }else{ + damage=damage/2; + damage2=damage2/2; + } + } + } +//対象にステータス異常がある場合のダメージ減算処理ここまで + + if(damage < 0) damage = 0; + if(damage2 < 0) damage2 = 0; + + // 属 性の適用 + damage=battle_attr_fix(damage,s_ele, battle_get_element(target) ); + damage2=battle_attr_fix(damage2,s_ele_, battle_get_element(target) ); + + // 星のかけら、気球の適用 + damage += sd->star; + damage2 += sd->star_; + damage += sd->spiritball*3; + damage2 += sd->spiritball*3; + + if(sc_data && sc_data[SC_AURABLADE].timer!=-1){ /* オーラブレード 必中 */ + damage += sc_data[SC_AURABLADE].val1 * 10; + damage2 += sc_data[SC_AURABLADE].val1 * 10; + } + if(skill_num==PA_PRESSURE){ /* プレッシャー 必中? */ + damage = 700+100*skill_lv; + damage2 = 700+100*skill_lv; + } + + // >二刀流の左右ダメージ計算誰かやってくれぇぇぇぇえええ! + // >map_session_data に左手ダメージ(atk,atk2)追加して + // >pc_calcstatus()でやるべきかな? + // map_session_data に左手武器(atk,atk2,ele,star,atkmods)追加して + // pc_calcstatus()でデータを入力しています + + //左手のみ武器装備 + if(sd->weapontype1 == 0 && sd->weapontype2 > 0) { + damage = damage2; + damage2 = 0; + } + // 右手、左手修練の適用 + if(sd->status.weapon > 16) {// 二刀流か? + int dmg = damage, dmg2 = damage2; + // 右手修練(60% 〜 100%) 右手全般 + skill = pc_checkskill(sd,AS_RIGHT); + damage = damage * (50 + (skill * 10))/100; + if(dmg > 0 && damage < 1) damage = 1; + // 左手修練(40% 〜 80%) 左手全般 + skill = pc_checkskill(sd,AS_LEFT); + damage2 = damage2 * (30 + (skill * 10))/100; + if(dmg2 > 0 && damage2 < 1) damage2 = 1; + } + else //二刀流でなければ左手ダメージは0 + damage2 = 0; + + // 右手,短剣のみ + if(da == 1) { //ダブルアタックが発動しているか + div_ = 2; + damage += damage; + type = 0x08; + } + + if(sd->status.weapon == 16) { + // カタール追撃ダメージ + skill = pc_checkskill(sd,TF_DOUBLE); + damage2 = damage * (1 + (skill * 2))/100; + if(damage > 0 && damage2 < 1) damage2 = 1; + } + + // インベナム修正 + if(skill_num==TF_POISON){ + damage = battle_attr_fix(damage + 15*skill_lv, s_ele, battle_get_element(target) ); + } + if(skill_num==MC_CARTREVOLUTION){ + damage = battle_attr_fix(damage, 0, battle_get_element(target) ); + } + + // 完全回避の判定 + if(skill_num == 0 && skill_lv >= 0 && tsd!=NULL && div_ < 255 && rand()%1000 < battle_get_flee2(target) ){ + damage=damage2=0; + type=0x0b; + dmg_lv = ATK_LUCKY; + } + + // 対象が完全回避をする設定がONなら + if(battle_config.enemy_perfect_flee) { + if(skill_num == 0 && skill_lv >= 0 && tmd!=NULL && div_ < 255 && rand()%1000 < battle_get_flee2(target) ) { + damage=damage2=0; + type=0x0b; + dmg_lv = ATK_LUCKY; + } + } + + //MobのModeに頑強フラグが立っているときの処理 + if(t_mode&0x40){ + if(damage > 0) + damage = 1; + if(damage2 > 0) + damage2 = 1; + } + + //bNoWeaponDamage(設定アイテム無し?)でグランドクロスじゃない場合はダメージが0 + if( tsd && tsd->special_state.no_weapon_damage && skill_num != CR_GRANDCROSS) + damage = damage2 = 0; + + if(skill_num != CR_GRANDCROSS && (damage > 0 || damage2 > 0) ) { + if(damage2<1) // ダメージ最終修正 + damage=battle_calc_damage(src,target,damage,div_,skill_num,skill_lv,flag); + else if(damage<1) // 右手がミス? + damage2=battle_calc_damage(src,target,damage2,div_,skill_num,skill_lv,flag); + else { // 両 手/カタールの場合はちょっと計算ややこしい + int d1=damage+damage2,d2=damage2; + damage=battle_calc_damage(src,target,damage+damage2,div_,skill_num,skill_lv,flag); + damage2=(d2*100/d1)*damage/100; + if(damage > 1 && damage2 < 1) damage2=1; + damage-=damage2; + } + } + + /* For executioner card [Valaris] */ + if(src->type == BL_PC && sd->random_attack_increase_add > 0 && sd->random_attack_increase_per > 0 && skill_num == 0 ){ + if(rand()%100 < sd->random_attack_increase_per){ + if(damage >0) damage*=sd->random_attack_increase_add/100; + if(damage2 >0) damage2*=sd->random_attack_increase_add/100; + } + } + /* End addition */ + +// -- moonsoul (final combination of phys, mag damage for ASC_BREAKER) + if(skill_num == ASC_BREAKER) { + damage += damage3; + damage2 += damage3; + } + + wd.damage=damage; + wd.damage2=damage2; + wd.type=type; + wd.div_=div_; + wd.amotion=battle_get_amotion(src); + if(skill_num == KN_AUTOCOUNTER) + wd.amotion >>= 1; + wd.dmotion=battle_get_dmotion(target); + wd.blewcount=blewcount; + wd.flag=flag; + wd.dmg_lv=dmg_lv; + + return wd; +} + +/*========================================== + * 武器ダメージ計算 + *------------------------------------------ + */ +struct Damage battle_calc_weapon_attack( + struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag) +{ + struct Damage wd; + + //return前の処理があるので情報出力部のみ変更 + if (src == NULL || target == NULL) { + nullpo_info(NLP_MARK); + memset(&wd,0,sizeof(wd)); + return wd; + } + + if(target->type == BL_PET) + memset(&wd,0,sizeof(wd)); + + else if(src->type == BL_PC) + wd = battle_calc_pc_weapon_attack(src,target,skill_num,skill_lv,wflag); // weapon breaking [Valaris] + else if(src->type == BL_MOB) + wd = battle_calc_mob_weapon_attack(src,target,skill_num,skill_lv,wflag); + else if(src->type == BL_PET) + wd = battle_calc_pet_weapon_attack(src,target,skill_num,skill_lv,wflag); + else + memset(&wd,0,sizeof(wd)); + + if(battle_config.equipment_breaking && src->type==BL_PC && (wd.damage > 0 || wd.damage2 > 0)) { + struct map_session_data *sd=(struct map_session_data *)src; + if(sd->status.weapon && sd->status.weapon!=11) { + int breakrate=1; + if(target->type == BL_PC && sd->sc_data[SC_MELTDOWN].timer!=-1){ + breakrate+=100*sd->sc_data[SC_MELTDOWN].val1; + if(rand()%10000 < breakrate*battle_config.equipment_break_rate/100 || breakrate >= 10000) + pc_breakweapon((struct map_session_data *)target); + } + if(sd->sc_data[SC_OVERTHRUST].timer!=-1) + breakrate+=20*sd->sc_data[SC_OVERTHRUST].val1; + if(wd.type==0x0a) + breakrate*=2; + if(rand()%10000 < breakrate*battle_config.equipment_break_rate/100 || breakrate >= 10000) { + pc_breakweapon(sd); + memset(&wd,0,sizeof(wd)); + } + } + } + + if (battle_config.equipment_breaking && target->type == BL_PC && (wd.damage > 0 || wd.damage2 > 0)) { + int breakrate=1; + if(src->type==BL_PC && ((struct map_session_data *)src)->sc_data[SC_MELTDOWN].timer!=-1) breakrate+=70*((struct map_session_data *)src)->sc_data[SC_MELTDOWN].val1; + if (wd.type==0x0a) + breakrate*=2; + if (rand()%10000 < breakrate*battle_config.equipment_break_rate/100 || breakrate >= 10000) { + pc_breakarmor((struct map_session_data *)target); + } + } + + return wd; +} + +/*========================================== + * 魔法ダメージ計算 + *------------------------------------------ + */ +struct Damage battle_calc_magic_attack( + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag) + { + int mdef1=battle_get_mdef(target); + int mdef2=battle_get_mdef2(target); + int matk1,matk2,damage=0,div_=1,blewcount=skill_get_blewcount(skill_num,skill_lv),rdamage = 0; + struct Damage md; + int aflag; + int normalmagic_flag=1; + int ele=0,race=7,t_ele=0,t_race=7,t_mode = 0,cardfix,t_class,i; + struct map_session_data *sd=NULL,*tsd=NULL; + struct mob_data *tmd = NULL; + + + //return前の処理があるので情報出力部のみ変更 + if( bl == NULL || target == NULL ){ + nullpo_info(NLP_MARK); + memset(&md,0,sizeof(md)); + return md; + } + + if(target->type == BL_PET) { + memset(&md,0,sizeof(md)); + return md; + } + + matk1=battle_get_matk1(bl); + matk2=battle_get_matk2(bl); + ele = skill_get_pl(skill_num); + race = battle_get_race(bl); + t_ele = battle_get_elem_type(target); + t_race = battle_get_race(target); + t_mode = battle_get_mode(target); + +#define MATK_FIX( a,b ) { matk1=matk1*(a)/(b); matk2=matk2*(a)/(b); } + + if( bl->type==BL_PC && (sd=(struct map_session_data *)bl) ){ + sd->state.attack_type = BF_MAGIC; + if(sd->matk_rate != 100) + MATK_FIX(sd->matk_rate,100); + sd->state.arrow_atk = 0; + } + if( target->type==BL_PC ) + tsd=(struct map_session_data *)target; + else if( target->type==BL_MOB ) + tmd=(struct mob_data *)target; + + aflag=BF_MAGIC|BF_LONG|BF_SKILL; + + if(skill_num > 0){ + switch(skill_num){ // 基本ダメージ計算(スキルごとに処理) + // ヒールor聖体 + case AL_HEAL: + case PR_BENEDICTIO: + damage = skill_calc_heal(bl,skill_lv)/2; + normalmagic_flag=0; + break; + case PR_ASPERSIO: /* アスペルシオ */ + damage = 40; //固定ダメージ + normalmagic_flag=0; + break; + case PR_SANCTUARY: // サンクチュアリ + damage = (skill_lv>6)?388:skill_lv*50; + normalmagic_flag=0; + blewcount|=0x10000; + break; + case ALL_RESURRECTION: + case PR_TURNUNDEAD: // 攻撃リザレクションとターンアンデッド + if(target->type != BL_PC && battle_check_undead(t_race,t_ele)){ + int hp, mhp, thres; + hp = battle_get_hp(target); + mhp = battle_get_max_hp(target); + thres = (skill_lv * 20) + battle_get_luk(bl)+ + battle_get_int(bl) + battle_get_lv(bl)+ + ((200 - hp * 200 / mhp)); + if(thres > 700) thres = 700; +// if(battle_config.battle_log) +// printf("ターンアンデッド! 確率%d ‰(千分率)\n", thres); + if(rand()%1000 < thres && !(t_mode&0x20)) // 成功 + damage = hp; + else // 失敗 + damage = battle_get_lv(bl) + battle_get_int(bl) + skill_lv * 10; + } + normalmagic_flag=0; + break; + + case MG_NAPALMBEAT: // ナパームビート(分散計算込み) + MATK_FIX(70+ skill_lv*10,100); + if(flag>0){ + MATK_FIX(1,flag); + }else { + if(battle_config.error_log) + printf("battle_calc_magic_attack(): napam enemy count=0 !\n"); + } + break; + case MG_FIREBALL: // ファイヤーボール + { + const int drate[]={100,90,70}; + if(flag>2) + matk1=matk2=0; + else + MATK_FIX( (95+skill_lv*5)*drate[flag] ,10000 ); + } + break; + case MG_FIREWALL: // ファイヤーウォール +/* + if( (t_ele!=3 && !battle_check_undead(t_race,t_ele)) || target->type==BL_PC ) //PCは火属性でも飛ぶ?そもそもダメージ受ける? + blewcount |= 0x10000; + else + blewcount = 0; +*/ + if((t_ele==3 || battle_check_undead(t_race,t_ele)) && target->type!=BL_PC) + blewcount = 0; + else + blewcount |= 0x10000; + MATK_FIX( 1,2 ); + break; + case MG_THUNDERSTORM: // サンダーストーム + MATK_FIX( 80,100 ); + break; + case MG_FROSTDIVER: // フロストダイバ + MATK_FIX( 100+skill_lv*10, 100); + break; + case WZ_FROSTNOVA: // フロストダイバ + MATK_FIX( ((100+skill_lv*10)*(2/3)), 100); + break; + case WZ_FIREPILLAR: // ファイヤーピラー + if(mdef1 < 1000000) + mdef1=mdef2=0; // MDEF無視 + MATK_FIX( 1,5 ); + matk1+=50; + matk2+=50; + break; + case WZ_SIGHTRASHER: + MATK_FIX( 100+skill_lv*20, 100); + break; + case WZ_METEOR: + case WZ_JUPITEL: // ユピテルサンダー + break; + case WZ_VERMILION: // ロードオブバーミリオン + MATK_FIX( skill_lv*20+80, 100 ); + break; + case WZ_WATERBALL: // ウォーターボール + matk1+= skill_lv*30; + matk2+= skill_lv*30; + break; + case WZ_STORMGUST: // ストームガスト + MATK_FIX( skill_lv*40+100 ,100 ); + blewcount|=0x10000; + break; + case AL_HOLYLIGHT: // ホーリーライト + MATK_FIX( 125,100 ); + break; + case AL_RUWACH: + MATK_FIX( 145,100 ); + break; + case HW_NAPALMVULCAN: // ナパームビート(分散計算込み) + MATK_FIX(70+ skill_lv*10,100); + if(flag>0){ + MATK_FIX(1,flag); + }else { + if(battle_config.error_log) + printf("battle_calc_magic_attack(): napalmvulcan enemy count=0 !\n"); + } + break; + } + } + + if(normalmagic_flag){ // 一般魔法ダメージ計算 + int imdef_flag=0; + if(matk1>matk2) + damage= matk2+rand()%(matk1-matk2+1); + else + damage= matk2; + if(sd) { + if(sd->ignore_mdef_ele & (1<<t_ele) || sd->ignore_mdef_race & (1<<t_race)) + imdef_flag = 1; + if(t_mode & 0x20) { + if(sd->ignore_mdef_race & (1<<10)) + imdef_flag = 1; + } + else { + if(sd->ignore_mdef_race & (1<<11)) + imdef_flag = 1; + } + } + if(!imdef_flag){ + if(battle_config.magic_defense_type) { + damage = damage - (mdef1 * battle_config.magic_defense_type) - mdef2; + } + else{ + damage = (damage*(100-mdef1))/100 - mdef2; + } + } + + if(damage<1) + damage=1; + } + + if(sd) { + cardfix=100; + cardfix=cardfix*(100+sd->magic_addrace[t_race])/100; + cardfix=cardfix*(100+sd->magic_addele[t_ele])/100; + if(t_mode & 0x20) + cardfix=cardfix*(100+sd->magic_addrace[10])/100; + else + cardfix=cardfix*(100+sd->magic_addrace[11])/100; + t_class = battle_get_class(target); + for(i=0;i<sd->add_magic_damage_class_count;i++) { + if(sd->add_magic_damage_classid[i] == t_class) { + cardfix=cardfix*(100+sd->add_magic_damage_classrate[i])/100; + break; + } + } + damage=damage*cardfix/100; + } + + if( tsd ){ + int s_class = battle_get_class(bl); + cardfix=100; + cardfix=cardfix*(100-tsd->subele[ele])/100; // 属 性によるダメージ耐性 + cardfix=cardfix*(100-tsd->subrace[race])/100; // 種族によるダメージ耐性 + cardfix=cardfix*(100-tsd->magic_subrace[race])/100; + if(battle_get_mode(bl) & 0x20) + cardfix=cardfix*(100-tsd->magic_subrace[10])/100; + else + cardfix=cardfix*(100-tsd->magic_subrace[11])/100; + for(i=0;i<tsd->add_mdef_class_count;i++) { + if(tsd->add_mdef_classid[i] == s_class) { + cardfix=cardfix*(100-tsd->add_mdef_classrate[i])/100; + break; + } + } + cardfix=cardfix*(100-tsd->magic_def_rate)/100; + damage=damage*cardfix/100; + } + if(damage < 0) damage = 0; + + damage=battle_attr_fix(damage, ele, battle_get_element(target) ); // 属 性修正 + + if(skill_num == CR_GRANDCROSS) { // グランドクロス + struct Damage wd; + wd=battle_calc_weapon_attack(bl,target,skill_num,skill_lv,flag); + damage = (damage + wd.damage) * (100 + 40*skill_lv)/100; + if(battle_config.gx_dupele) damage=battle_attr_fix(damage, ele, battle_get_element(target) ); //属性2回かかる + if(bl==target) damage=damage/2; //反動は半分 + } + + div_=skill_get_num( skill_num,skill_lv ); + + if(div_>1 && skill_num != WZ_VERMILION) + damage*=div_; + +// if(mdef1 >= 1000000 && damage > 0) + if(t_mode&0x40 && damage > 0) + damage = 1; + + if( tsd && tsd->special_state.no_magic_damage) { + if (battle_config.gtb_pvp_only != 0) { // [MouseJstr] + if ((map[target->m].flag.pvp || map[target->m].flag.gvg) && target->type==BL_PC) + damage = (damage * (100 - battle_config.gtb_pvp_only)) / 100; + } else + damage=0; // 黄 金蟲カード(魔法ダメージ0) + } + + damage=battle_calc_damage(bl,target,damage,div_,skill_num,skill_lv,aflag); // 最終修正 + + /* magic_damage_return by [AppleGirl] and [Valaris] */ + if( target->type==BL_PC && tsd && tsd->magic_damage_return > 0 ){ + rdamage += damage * tsd->magic_damage_return / 100; + if(rdamage < 1) rdamage = 1; + clif_damage(target,bl,gettick(),0,0,rdamage,0,0,0); + battle_damage(target,bl,rdamage,0); + } + /* end magic_damage_return */ + + md.damage=damage; + md.div_=div_; + md.amotion=battle_get_amotion(bl); + md.dmotion=battle_get_dmotion(target); + md.damage2=0; + md.type=0; + md.blewcount=blewcount; + md.flag=aflag; + + return md; +} + +/*========================================== + * その他ダメージ計算 + *------------------------------------------ + */ +struct Damage battle_calc_misc_attack( + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag) +{ + int int_=battle_get_int(bl); +// int luk=battle_get_luk(bl); + int dex=battle_get_dex(bl); + int skill,ele,race,cardfix; + struct map_session_data *sd=NULL,*tsd=NULL; + int damage=0,div_=1,blewcount=skill_get_blewcount(skill_num,skill_lv); + struct Damage md; + int damagefix=1; + + int aflag=BF_MISC|BF_LONG|BF_SKILL; + + //return前の処理があるので情報出力部のみ変更 + if( bl == NULL || target == NULL ){ + nullpo_info(NLP_MARK); + memset(&md,0,sizeof(md)); + return md; + } + + if(target->type == BL_PET) { + memset(&md,0,sizeof(md)); + return md; + } + + if( bl->type == BL_PC && (sd=(struct map_session_data *)bl) ) { + sd->state.attack_type = BF_MISC; + sd->state.arrow_atk = 0; + } + + if( target->type==BL_PC ) + tsd=(struct map_session_data *)target; + + switch(skill_num){ + + case HT_LANDMINE: // ランドマイン + damage=skill_lv*(dex+75)*(100+int_)/100; + break; + + case HT_BLASTMINE: // ブラストマイン + damage=skill_lv*(dex/2+50)*(100+int_)/100; + break; + + case HT_CLAYMORETRAP: // クレイモアートラップ + damage=skill_lv*(dex/2+75)*(100+int_)/100; + break; + + case HT_BLITZBEAT: // ブリッツビート + if( sd==NULL || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0) + skill=0; + damage=(dex/10+int_/2+skill*3+40)*2; + if(flag > 1) + damage /= flag; + break; + + case TF_THROWSTONE: // 石投げ + damage=30; + damagefix=0; + break; + + case BA_DISSONANCE: // 不協和音 + damage=(skill_lv)*20+pc_checkskill(sd,BA_MUSICALLESSON)*3; + break; + + case NPC_SELFDESTRUCTION: // 自爆 + damage=battle_get_hp(bl)-(bl==target?1:0); + damagefix=0; + break; + + case NPC_SMOKING: // タバコを吸う + damage=3; + damagefix=0; + break; + + case NPC_DARKBREATH: + { + struct status_change *sc_data = battle_get_sc_data(target); + int hitrate=battle_get_hit(bl) - battle_get_flee(target) + 80; + hitrate = ( (hitrate>95)?95: ((hitrate<5)?5:hitrate) ); + if(sc_data && (sc_data[SC_SLEEP].timer!=-1 || sc_data[SC_STAN].timer!=-1 || + sc_data[SC_FREEZE].timer!=-1 || (sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0) ) ) + hitrate = 1000000; + if(rand()%100 < hitrate) { + damage = 500 + (skill_lv-1)*1000 + rand()%1000; + if(damage > 9999) damage = 9999; + } + } + break; + case SN_FALCONASSAULT: /* ファルコンアサルト */ + skill = pc_checkskill(sd,HT_BLITZBEAT); + damage=(100+50*skill_lv+(dex/10+int_/2+skill*3+40)*2); + break; + } + + ele = skill_get_pl(skill_num); + race = battle_get_race(bl); + + if(damagefix){ + if(damage<1 && skill_num != NPC_DARKBREATH) + damage=1; + + if( tsd ){ + cardfix=100; + cardfix=cardfix*(100-tsd->subele[ele])/100; // 属性によるダメージ耐性 + cardfix=cardfix*(100-tsd->subrace[race])/100; // 種族によるダメージ耐性 + cardfix=cardfix*(100-tsd->misc_def_rate)/100; + damage=damage*cardfix/100; + } + if(damage < 0) damage = 0; + damage=battle_attr_fix(damage, ele, battle_get_element(target) ); // 属性修正 + } + + div_=skill_get_num( skill_num,skill_lv ); + if(div_>1) + damage*=div_; + + if(damage > 0 && (damage < div_ || (battle_get_def(target) >= 1000000 && battle_get_mdef(target) >= 1000000) ) ) { + damage = div_; + } + + damage=battle_calc_damage(bl,target,damage,div_,skill_num,skill_lv,aflag); // 最終修正 + + md.damage=damage; + md.div_=div_; + md.amotion=battle_get_amotion(bl); + md.dmotion=battle_get_dmotion(target); + md.damage2=0; + md.type=0; + md.blewcount=blewcount; + md.flag=aflag; + return md; + +} +/*========================================== + * ダメージ計算一括処理用 + *------------------------------------------ + */ +struct Damage battle_calc_attack( int attack_type, + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag) +{ + struct Damage d; + switch(attack_type){ + case BF_WEAPON: + return battle_calc_weapon_attack(bl,target,skill_num,skill_lv,flag); + case BF_MAGIC: + return battle_calc_magic_attack(bl,target,skill_num,skill_lv,flag); + case BF_MISC: + return battle_calc_misc_attack(bl,target,skill_num,skill_lv,flag); + default: + if(battle_config.error_log) + printf("battle_calc_attack: unknwon attack type ! %d\n",attack_type); + break; + } + return d; +} +/*========================================== + * 通常攻撃処理まとめ + *------------------------------------------ + */ +int battle_weapon_attack( struct block_list *src,struct block_list *target, + unsigned int tick,int flag) +{ + struct map_session_data *sd=NULL; + struct status_change *sc_data = battle_get_sc_data(src),*t_sc_data=battle_get_sc_data(target); + short *opt1; + int race = 7, ele = 0; + int damage,rdamage = 0; + struct Damage wd; + + nullpo_retr(0, src); + nullpo_retr(0, target); + + if(src->type == BL_PC) + sd = (struct map_session_data *)src; + + if(src->prev == NULL || target->prev == NULL) + return 0; + if(src->type == BL_PC && pc_isdead(sd)) + return 0; + if(target->type == BL_PC && pc_isdead((struct map_session_data *)target)) + return 0; + + opt1=battle_get_opt1(src); + if(opt1 && *opt1 > 0) { + battle_stopattack(src); + return 0; + } + if(sc_data && sc_data[SC_BLADESTOP].timer!=-1){ + battle_stopattack(src); + return 0; + } + + race = battle_get_race(target); + ele = battle_get_elem_type(target); + if(battle_check_target(src,target,BCT_ENEMY) > 0 && + battle_check_range(src,target,0)){ + // 攻撃対象となりうるので攻撃 + if(sd && sd->status.weapon == 11) { + if(sd->equip_index[10] >= 0) { + if(battle_config.arrow_decrement) + pc_delitem(sd,sd->equip_index[10],1,0); + } + else { + clif_arrow_fail(sd,0); + return 0; + } + } + if(flag&0x8000) { + if(sd && battle_config.pc_attack_direction_change) + sd->dir = sd->head_dir = map_calc_dir(src, target->x,target->y ); + else if(src->type == BL_MOB && battle_config.monster_attack_direction_change) + ((struct mob_data *)src)->dir = map_calc_dir(src, target->x,target->y ); + wd=battle_calc_weapon_attack(src,target,KN_AUTOCOUNTER,flag&0xff,0); + } + else + wd=battle_calc_weapon_attack(src,target,0,0,0); + if((damage = wd.damage + wd.damage2) > 0 && src != target) { + if(wd.flag&BF_SHORT) { + if(target->type == BL_PC) { + struct map_session_data *tsd = (struct map_session_data *)target; + if(tsd && tsd->short_weapon_damage_return > 0) { + rdamage += damage * tsd->short_weapon_damage_return / 100; + if(rdamage < 1) rdamage = 1; + } + } + if(t_sc_data && t_sc_data[SC_REFLECTSHIELD].timer != -1) { + rdamage += damage * t_sc_data[SC_REFLECTSHIELD].val2 / 100; + if(rdamage < 1) rdamage = 1; + } + } + else if(wd.flag&BF_LONG) { + if(target->type == BL_PC) { + struct map_session_data *tsd = (struct map_session_data *)target; + if(tsd && tsd->long_weapon_damage_return > 0) { + rdamage += damage * tsd->long_weapon_damage_return / 100; + if(rdamage < 1) rdamage = 1; + } + } + } + if(rdamage > 0) + clif_damage(src,src,tick, wd.amotion,0,rdamage,1,4,0); + } + + if (wd.div_ == 255 && sd) { //三段掌 + int delay = 1000 - 4 * battle_get_agi(src) - 2 * battle_get_dex(src); + int skilllv; + if(wd.damage+wd.damage2 < battle_get_hp(target)) { + if((skilllv = pc_checkskill(sd, MO_CHAINCOMBO)) > 0) + delay += 300 * battle_config.combo_delay_rate /100; //追加ディレイをconfにより調整 + + skill_status_change_start(src,SC_COMBO,MO_TRIPLEATTACK,skilllv,0,0,delay,0); + } + sd->attackabletime = sd->canmove_tick = tick + delay; + clif_combo_delay(src,delay); + clif_skill_damage(src , target , tick , wd.amotion , wd.dmotion , + wd.damage , 3 , MO_TRIPLEATTACK, pc_checkskill(sd,MO_TRIPLEATTACK) , -1 ); + } + else { + clif_damage(src,target,tick, wd.amotion, wd.dmotion, + wd.damage, wd.div_ , wd.type, wd.damage2); + //二刀流左手とカタール追撃のミス表示(無理やり〜) + if(sd && sd->status.weapon >= 16 && wd.damage2 == 0) + clif_damage(src,target,tick+10, wd.amotion, wd.dmotion,0, 1, 0, 0); + } + if(sd && sd->splash_range > 0 && (wd.damage > 0 || wd.damage2 > 0) ) + skill_castend_damage_id(src,target,0,-1,tick,0); + map_freeblock_lock(); + battle_damage(src,target,(wd.damage+wd.damage2),0); + if(target->prev != NULL && + (target->type != BL_PC || (target->type == BL_PC && !pc_isdead((struct map_session_data *)target) ) ) ) { + if(wd.damage > 0 || wd.damage2 > 0) { + skill_additional_effect(src,target,0,0,BF_WEAPON,tick); + if(sd) { + if(sd->weapon_coma_ele[ele] > 0 && rand()%10000 < sd->weapon_coma_ele[ele]) + battle_damage(src,target,battle_get_max_hp(target),1); + if(sd->weapon_coma_race[race] > 0 && rand()%10000 < sd->weapon_coma_race[race]) + battle_damage(src,target,battle_get_max_hp(target),1); + if(battle_get_mode(target) & 0x20) { + if(sd->weapon_coma_race[10] > 0 && rand()%10000 < sd->weapon_coma_race[10]) + battle_damage(src,target,battle_get_max_hp(target),1); + } + else { + if(sd->weapon_coma_race[11] > 0 && rand()%10000 < sd->weapon_coma_race[11]) + battle_damage(src,target,battle_get_max_hp(target),1); + } + } + } + } + if(sc_data && sc_data[SC_AUTOSPELL].timer != -1 && rand()%100 < sc_data[SC_AUTOSPELL].val4) { + int skilllv=sc_data[SC_AUTOSPELL].val3,i,f=0; + i = rand()%100; + if(i >= 50) skilllv -= 2; + else if(i >= 15) skilllv--; + if(skilllv < 1) skilllv = 1; + if(sd) { + int sp = skill_get_sp(sc_data[SC_AUTOSPELL].val2,skilllv)*2/3; + if(sd->status.sp >= sp) { + if((i=skill_get_inf(sc_data[SC_AUTOSPELL].val2) == 2) || i == 32) + f = skill_castend_pos2(src,target->x,target->y,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + else { + switch( skill_get_nk(sc_data[SC_AUTOSPELL].val2) ) { + case 0: case 2: + f = skill_castend_damage_id(src,target,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + break; + case 1:/* 支援系 */ + if((sc_data[SC_AUTOSPELL].val2==AL_HEAL || (sc_data[SC_AUTOSPELL].val2==ALL_RESURRECTION && target->type != BL_PC)) && battle_check_undead(race,ele)) + f = skill_castend_damage_id(src,target,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + else + f = skill_castend_nodamage_id(src,target,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + break; + } + } + if(!f) pc_heal(sd,0,-sp); + } + } + else { + if((i=skill_get_inf(sc_data[SC_AUTOSPELL].val2) == 2) || i == 32) + skill_castend_pos2(src,target->x,target->y,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + else { + switch( skill_get_nk(sc_data[SC_AUTOSPELL].val2) ) { + case 0: case 2: + skill_castend_damage_id(src,target,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + break; + case 1:/* 支援系 */ + if((sc_data[SC_AUTOSPELL].val2==AL_HEAL || (sc_data[SC_AUTOSPELL].val2==ALL_RESURRECTION && target->type != BL_PC)) && battle_check_undead(race,ele)) + skill_castend_damage_id(src,target,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + else + skill_castend_nodamage_id(src,target,sc_data[SC_AUTOSPELL].val2,skilllv,tick,flag); + break; + } + } + } + } + if(sd) { + if(sd->autospell_id > 0 && sd->autospell_lv > 0 && rand()%100 < sd->autospell_rate) { + int skilllv=sd->autospell_lv,i,f=0,sp; + i = rand()%100; + if(i >= 50) skilllv -= 2; + else if(i >= 15) skilllv--; + if(skilllv < 1) skilllv = 1; + sp = skill_get_sp(sd->autospell_id,skilllv)*2/3; + if(sd->status.sp >= sp) { + if((i=skill_get_inf(sd->autospell_id) == 2) || i == 32) + f = skill_castend_pos2(src,target->x,target->y,sd->autospell_id,skilllv,tick,flag); + else { + switch( skill_get_nk(sd->autospell_id) ) { + case 0: case 2: + f = skill_castend_damage_id(src,target,sd->autospell_id,skilllv,tick,flag); + break; + case 1:/* 支援系 */ + if((sd->autospell_id==AL_HEAL || (sd->autospell_id==ALL_RESURRECTION && target->type != BL_PC)) && battle_check_undead(race,ele)) + f = skill_castend_damage_id(src,target,sd->autospell_id,skilllv,tick,flag); + else + f = skill_castend_nodamage_id(src,target,sd->autospell_id,skilllv,tick,flag); + break; + } + } + if(!f) pc_heal(sd,0,-sp); + } + } + if(wd.flag&BF_WEAPON && src != target && (wd.damage > 0 || wd.damage2 > 0)) { + int hp = 0,sp = 0; + if(sd->hp_drain_rate && sd->hp_drain_per > 0 && wd.damage > 0 && rand()%100 < sd->hp_drain_rate) { + hp += (wd.damage * sd->hp_drain_per)/100; + if(sd->hp_drain_rate > 0 && hp < 1) hp = 1; + else if(sd->hp_drain_rate < 0 && hp > -1) hp = -1; + } + if(sd->hp_drain_rate_ && sd->hp_drain_per_ > 0 && wd.damage2 > 0 && rand()%100 < sd->hp_drain_rate_) { + hp += (wd.damage2 * sd->hp_drain_per_)/100; + if(sd->hp_drain_rate_ > 0 && hp < 1) hp = 1; + else if(sd->hp_drain_rate_ < 0 && hp > -1) hp = -1; + } + if(sd->sp_drain_rate && sd->sp_drain_per > 0 && wd.damage > 0 && rand()%100 < sd->sp_drain_rate) { + sp += (wd.damage * sd->sp_drain_per)/100; + if(sd->sp_drain_rate > 0 && sp < 1) sp = 1; + else if(sd->sp_drain_rate < 0 && sp > -1) sp = -1; + } + if(sd->sp_drain_rate_ && sd->sp_drain_per_ > 0 && wd.damage2 > 0 && rand()%100 < sd->sp_drain_rate_) { + sp += (wd.damage2 * sd->sp_drain_per_)/100; + if(sd->sp_drain_rate_ > 0 && sp < 1) sp = 1; + else if(sd->sp_drain_rate_ < 0 && sp > -1) sp = -1; + } + if(hp || sp) pc_heal(sd,hp,sp); + } + } + + if(rdamage > 0) + battle_damage(target,src,rdamage,0); + if(t_sc_data && t_sc_data[SC_AUTOCOUNTER].timer != -1 && t_sc_data[SC_AUTOCOUNTER].val4 > 0) { + if(t_sc_data[SC_AUTOCOUNTER].val3 == src->id) + battle_weapon_attack(target,src,tick,0x8000|t_sc_data[SC_AUTOCOUNTER].val1); + skill_status_change_end(target,SC_AUTOCOUNTER,-1); + } + if(t_sc_data && t_sc_data[SC_BLADESTOP_WAIT].timer != -1){ + int lv = t_sc_data[SC_BLADESTOP_WAIT].val1; + skill_status_change_end(target,SC_BLADESTOP_WAIT,-1); + skill_status_change_start(src,SC_BLADESTOP,lv,1,(int)src,(int)target,skill_get_time2(MO_BLADESTOP,lv),0); + skill_status_change_start(target,SC_BLADESTOP,lv,2,(int)target,(int)src,skill_get_time2(MO_BLADESTOP,lv),0); + } + if(t_sc_data && t_sc_data[SC_SPLASHER].timer!=-1) //殴ったので対象のベナムスプラッシャー状態を解除 + skill_status_change_end(target,SC_SPLASHER,-1); + + map_freeblock_unlock(); + } + return wd.dmg_lv; +} + +int battle_check_undead(int race,int element) +{ + if(battle_config.undead_detect_type == 0) { + if(element == 9) + return 1; + } + else if(battle_config.undead_detect_type == 1) { + if(race == 1) + return 1; + } + else { + if(element == 9 || race == 1) + return 1; + } + return 0; +} + +/*========================================== + * 敵味方判定(1=肯定,0=否定,-1=エラー) + * flag&0xf0000 = 0x00000:敵じゃないか判定(ret:1=敵ではない) + * = 0x10000:パーティー判定(ret:1=パーティーメンバ) + * = 0x20000:全て(ret:1=敵味方両方) + * = 0x40000:敵か判定(ret:1=敵) + * = 0x50000:パーティーじゃないか判定(ret:1=パーティでない) + *------------------------------------------ + */ +int battle_check_target( struct block_list *src, struct block_list *target,int flag) +{ + int s_p,s_g,t_p,t_g; + struct block_list *ss=src; + + nullpo_retr(0, src); + nullpo_retr(0, target); + + if( flag&0x40000 ){ // 反転フラグ + int ret=battle_check_target(src,target,flag&0x30000); + if(ret!=-1) + return !ret; + return -1; + } + + if( flag&0x20000 ){ + if( target->type==BL_MOB || target->type==BL_PC ) + return 1; + else + return -1; + } + + if(src->type == BL_SKILL && target->type == BL_SKILL) // 対象がスキルユニットなら無条件肯定 + return -1; + + if(target->type == BL_PC && ((struct map_session_data *)target)->invincible_timer != -1) + return -1; + + if(target->type == BL_SKILL) { + switch(((struct skill_unit *)target)->group->unit_id){ + case 0x8d: + case 0x8f: + case 0x98: + return 0; + break; + } + } + + if(target->type == BL_PET) + return -1; + + // スキルユニットの場合、親を求める + if( src->type==BL_SKILL) { + int inf2 = skill_get_inf2(((struct skill_unit *)src)->group->skill_id); + if( (ss=map_id2bl( ((struct skill_unit *)src)->group->src_id))==NULL ) + return -1; + if(ss->prev == NULL) + return -1; + if(inf2&0x80 && + (map[src->m].flag.pvp || pc_iskiller((struct map_session_data *)src, (struct map_session_data *)target)) && // [MouseJstr] + !(target->type == BL_PC && pc_isinvisible((struct map_session_data *)target))) + return 0; + if(ss == target) { + if(inf2&0x100) + return 0; + if(inf2&0x200) + return -1; + } + } + // Mobでmaster_idがあってspecial_mob_aiなら、召喚主を求める + if( src->type==BL_MOB ){ + struct mob_data *md=(struct mob_data *)src; + if(md && md->master_id>0){ + if(md->master_id==target->id) // 主なら肯定 + return 1; + if(md->state.special_mob_ai){ + if(target->type==BL_MOB){ //special_mob_aiで対象がMob + struct mob_data *tmd=(struct mob_data *)target; + if(tmd){ + if(tmd->master_id != md->master_id) //召喚主が一緒でなければ否定 + return 0; + else{ //召喚主が一緒なので肯定したいけど自爆は否定 + if(md->state.special_mob_ai>2) + return 0; + else + return 1; + } + } + } + } + if((ss=map_id2bl(md->master_id))==NULL) + return -1; + } + } + + if( src==target || ss==target ) // 同じなら肯定 + return 1; + + if(target->type == BL_PC && pc_isinvisible((struct map_session_data *)target)) + return -1; + + if( src->prev==NULL || // 死んでるならエラー + (src->type==BL_PC && pc_isdead((struct map_session_data *)src) ) ) + return -1; + + if( (ss->type == BL_PC && target->type==BL_MOB) || + (ss->type == BL_MOB && target->type==BL_PC) ) + return 0; // PCvsMOBなら否定 + + if(ss->type == BL_PET && target->type==BL_MOB) + return 0; + + s_p=battle_get_party_id(ss); + s_g=battle_get_guild_id(ss); + + t_p=battle_get_party_id(target); + t_g=battle_get_guild_id(target); + + if(flag&0x10000) { + if(s_p && t_p && s_p == t_p) // 同じパーティなら肯定(味方) + return 1; + else // パーティ検索なら同じパーティじゃない時点で否定 + return 0; + } + + if(ss->type == BL_MOB && s_g > 0 && t_g > 0 && s_g == t_g ) // 同じギルド/mobクラスなら肯定(味方) + return 1; + +//printf("ss:%d src:%d target:%d flag:0x%x %d %d ",ss->id,src->id,target->id,flag,src->type,target->type); +//printf("p:%d %d g:%d %d\n",s_p,t_p,s_g,t_g); + + if( ss->type==BL_PC && target->type==BL_PC) { // 両方PVPモードなら否定(敵) + struct skill_unit *su=NULL; + if(src->type==BL_SKILL) + su=(struct skill_unit *)src; + if(map[ss->m].flag.pvp || pc_iskiller((struct map_session_data *)ss, (struct map_session_data*)target)) { // [MouseJstr] + if(su && su->group->target_flag==BCT_NOENEMY) + return 1; + else if(battle_config.pk_mode && (((struct map_session_data*)ss)->status.class==0 || ((struct map_session_data*)target)->status.class==0)) + return 1; // prevent novice engagement in pk_mode [Valaris] + else if(map[ss->m].flag.pvp_noparty && s_p > 0 && t_p > 0 && s_p == t_p) + return 1; + else if(map[ss->m].flag.pvp_noguild && s_g > 0 && t_g > 0 && s_g == t_g) + return 1; + return 0; + } + if(map[src->m].flag.gvg) { + struct guild *g=NULL; + if(su && su->group->target_flag==BCT_NOENEMY) + return 1; + if( s_g > 0 && s_g == t_g) + return 1; + if(map[src->m].flag.gvg_noparty && s_p > 0 && t_p > 0 && s_p == t_p) + return 1; + if((g = guild_search(s_g))) { + int i; + for(i=0;i<MAX_GUILDALLIANCE;i++){ + if(g->alliance[i].guild_id > 0 && g->alliance[i].guild_id == t_g) { + if(g->alliance[i].opposition) + return 0;//敵対ギルドなら無条件に敵 + else + return 1;//同盟ギルドなら無条件に味方 + } + } + } + return 0; + } + } + + return 1; // 該当しないので無関係人物(まあ敵じゃないので味方) +} +/*========================================== + * 射程判定 + *------------------------------------------ + */ +int battle_check_range(struct block_list *src,struct block_list *bl,int range) +{ + + int dx,dy; + struct walkpath_data wpd; + int arange; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + + dx=abs(bl->x-src->x); + dy=abs(bl->y-src->y); + arange=((dx>dy)?dx:dy); + + if(src->m != bl->m) // 違うマップ + return 0; + + if( range>0 && range < arange ) // 遠すぎる + return 0; + + if( arange<2 ) // 同じマスか隣接 + return 1; + +// if(bl->type == BL_SKILL && ((struct skill_unit *)bl)->group->unit_id == 0x8d) +// return 1; + + // 障害物判定 + wpd.path_len=0; + wpd.path_pos=0; + wpd.path_half=0; + if(path_search(&wpd,src->m,src->x,src->y,bl->x,bl->y,0x10001)!=-1) + return 1; + + dx=(dx>0)?1:((dx<0)?-1:0); + dy=(dy>0)?1:((dy<0)?-1:0); + return (path_search(&wpd,src->m,src->x+dx,src->y+dy, + bl->x-dx,bl->y-dy,0x10001)!=-1)?1:0; +} + +/*========================================== + * Return numerical value of a switch configuration (modified by [Yor]) + * on/off, english, fran軋is, deutsch, espaol + *------------------------------------------ + */ +int battle_config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + return atoi(str); +} +/*========================================== + * 設定ファイルを読み込む + *------------------------------------------ + */ +int battle_config_read(const char *cfgName) +{ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + static int count = 0; + + if ((count++) == 0) { + battle_config.warp_point_debug=0; + battle_config.enemy_critical=0; + battle_config.enemy_critical_rate=100; + battle_config.enemy_str=1; + battle_config.enemy_perfect_flee=0; + battle_config.cast_rate=100; + battle_config.delay_rate=100; + battle_config.delay_dependon_dex=0; + battle_config.sdelay_attack_enable=0; + battle_config.left_cardfix_to_right=0; + battle_config.pc_skill_add_range=0; + battle_config.skill_out_range_consume=1; + battle_config.mob_skill_add_range=0; + battle_config.pc_damage_delay=1; + battle_config.pc_damage_delay_rate=100; + battle_config.defnotenemy=1; + battle_config.random_monster_checklv=1; + battle_config.attr_recover=1; + battle_config.flooritem_lifetime=LIFETIME_FLOORITEM*1000; + battle_config.item_auto_get=0; + battle_config.item_first_get_time=3000; + battle_config.item_second_get_time=1000; + battle_config.item_third_get_time=1000; + battle_config.mvp_item_first_get_time=10000; + battle_config.mvp_item_second_get_time=10000; + battle_config.mvp_item_third_get_time=2000; + + battle_config.drop_rate0item=0; + battle_config.base_exp_rate=100; + battle_config.job_exp_rate=100; + battle_config.pvp_exp=1; + battle_config.gtb_pvp_only=0; + battle_config.death_penalty_type=0; + battle_config.death_penalty_base=0; + battle_config.death_penalty_job=0; + battle_config.zeny_penalty=0; + battle_config.restart_hp_rate=0; + battle_config.restart_sp_rate=0; + battle_config.mvp_item_rate=100; + battle_config.mvp_exp_rate=100; + battle_config.mvp_hp_rate=100; + battle_config.monster_hp_rate=100; + battle_config.monster_max_aspd=199; + battle_config.atc_gmonly=0; + battle_config.gm_allskill=0; + battle_config.gm_allequip=0; + battle_config.gm_skilluncond=0; + battle_config.guild_max_castles=0; + battle_config.skillfree = 0; + battle_config.skillup_limit = 0; + battle_config.wp_rate=100; + battle_config.pp_rate=100; + battle_config.monster_active_enable=1; + battle_config.monster_damage_delay_rate=100; + battle_config.monster_loot_type=0; + battle_config.mob_skill_use=1; + battle_config.mob_count_rate=100; + battle_config.quest_skill_learn=0; + battle_config.quest_skill_reset=1; + battle_config.basic_skill_check=1; + battle_config.guild_emperium_check=1; + battle_config.guild_exp_limit=50; + battle_config.pc_invincible_time = 5000; + battle_config.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_rate=100; + battle_config.pet_attack_exp_to_master=0; + battle_config.pet_attack_exp_rate=100; + battle_config.skill_min_damage=0; + battle_config.finger_offensive_type=0; + battle_config.heal_exp=0; + battle_config.resurrection_exp=0; + battle_config.shop_exp=0; + battle_config.combo_delay_rate=100; + battle_config.item_check=1; + battle_config.wedding_modifydisplay=0; + battle_config.natural_healhp_interval=6000; + battle_config.natural_healsp_interval=8000; + battle_config.natural_heal_skill_interval=10000; + battle_config.natural_heal_weight_rate=50; + battle_config.item_name_override_grffile=1; + battle_config.arrow_decrement=1; + battle_config.max_aspd = 199; + battle_config.max_hp = 32500; + battle_config.max_sp = 32500; + battle_config.max_lv = 99; // [MouseJstr] + battle_config.max_parameter = 99; + battle_config.max_cart_weight = 8000; + battle_config.pc_skill_log = 0; + battle_config.mob_skill_log = 0; + battle_config.battle_log = 0; + battle_config.save_log = 0; + battle_config.error_log = 1; + battle_config.etc_log = 1; + battle_config.save_clothcolor = 0; + battle_config.undead_detect_type = 0; + battle_config.pc_auto_counter_type = 1; + battle_config.monster_auto_counter_type = 1; + battle_config.agi_penaly_type = 0; + battle_config.agi_penaly_count = 3; + battle_config.agi_penaly_num = 0; + battle_config.agi_penaly_count_lv = ATK_FLEE; + battle_config.vit_penaly_type = 0; + battle_config.vit_penaly_count = 3; + battle_config.vit_penaly_num = 0; + battle_config.vit_penaly_count_lv = ATK_DEF; + battle_config.player_defense_type = 0; + battle_config.monster_defense_type = 0; + battle_config.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 = 0; + battle_config.monster_cloak_check_type = 0; + battle_config.gvg_short_damage_rate = 100; + battle_config.gvg_long_damage_rate = 100; + battle_config.gvg_magic_damage_rate = 100; + battle_config.gvg_misc_damage_rate = 100; + battle_config.gvg_eliminate_time = 7000; + battle_config.mob_changetarget_byskill = 0; + battle_config.pc_attack_direction_change = 1; + battle_config.monster_attack_direction_change = 1; + battle_config.pc_undead_nofreeze = 0; + battle_config.pc_land_skill_limit = 1; + battle_config.monster_land_skill_limit = 1; + battle_config.party_skill_penaly = 1; + battle_config.monster_class_change_full_recover = 0; + battle_config.produce_item_name_input = 1; + battle_config.produce_potion_name_input = 1; + battle_config.making_arrow_name_input = 1; + battle_config.holywater_name_input = 1; + battle_config.display_delay_skill_fail = 1; + battle_config.chat_warpportal = 0; + battle_config.mob_warpportal = 0; + battle_config.dead_branch_active = 0; + battle_config.vending_max_value = 10000000; + battle_config.show_steal_in_same_party = 0; + battle_config.enable_upper_class = 0; + battle_config.pet_attack_attr_none = 0; + battle_config.pc_attack_attr_none = 0; + battle_config.mob_attack_attr_none = 1; + battle_config.mob_ghostring_fix = 0; + battle_config.gx_allhit = 0; + battle_config.gx_cardfix = 0; + battle_config.gx_dupele = 1; + battle_config.gx_disptype = 1; + battle_config.player_skill_partner_check = 1; + battle_config.hide_GM_session = 0; + battle_config.unit_movement_type = 0; + battle_config.invite_request_check = 1; + battle_config.skill_removetrap_type = 0; + battle_config.disp_experience = 0; + battle_config.item_rate_common = 100; + battle_config.item_rate_equip = 100; + battle_config.item_rate_card = 100; + battle_config.item_rate_heal = 100; // Added by Valaris + battle_config.item_rate_use = 100; // End + battle_config.item_drop_common_min=1; // Added by TyrNemesis^ + battle_config.item_drop_common_max=10000; + battle_config.item_drop_equip_min=1; + battle_config.item_drop_equip_max=10000; + battle_config.item_drop_card_min=1; + battle_config.item_drop_card_max=10000; + battle_config.item_drop_mvp_min=1; + battle_config.item_drop_mvp_max=10000; // End Addition + battle_config.item_drop_heal_min=1; // Added by Valaris + battle_config.item_drop_heal_max=10000; + battle_config.item_drop_use_min=1; + battle_config.item_drop_use_max=10000; // End + battle_config.prevent_logout = 1; // Added by RoVeRT + battle_config.maximum_level = 255; // Added by Valaris + battle_config.drops_by_luk = 0; // [Valaris] + battle_config.equipment_breaking = 0; // [Valaris] + battle_config.equipment_break_rate = 100; // [Valaris] + battle_config.pk_mode = 0; // [Valaris] + battle_config.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 = 63; // added by [Yor] + battle_config.min_hair_style = 0; + battle_config.max_hair_style = 20; + battle_config.min_hair_color = 0; + battle_config.max_hair_color = 9; + battle_config.min_cloth_color = 0; + battle_config.max_cloth_color = 4; + + battle_config.castrate_dex_scale = 150; + + battle_config.area_size = 14; + +//SQL-only options start +#ifndef TXT_ONLY + battle_config.mail_system = 0; +//SQL-only options end +#endif + +} + + fp = fopen(cfgName,"r"); + if (fp == NULL) { + printf("file not found: %s\n", cfgName); + return 1; + } + while(fgets(line,1020,fp)){ + const struct { + char str[128]; + int *val; + } data[] = { + { "warp_point_debug", &battle_config.warp_point_debug }, + { "enemy_critical", &battle_config.enemy_critical }, + { "enemy_critical_rate", &battle_config.enemy_critical_rate }, + { "enemy_str", &battle_config.enemy_str }, + { "enemy_perfect_flee", &battle_config.enemy_perfect_flee }, + { "casting_rate", &battle_config.cast_rate }, + { "delay_rate", &battle_config.delay_rate }, + { "delay_dependon_dex", &battle_config.delay_dependon_dex }, + { "skill_delay_attack_enable", &battle_config.sdelay_attack_enable }, + { "left_cardfix_to_right", &battle_config.left_cardfix_to_right }, + { "player_skill_add_range", &battle_config.pc_skill_add_range }, + { "skill_out_range_consume", &battle_config.skill_out_range_consume }, + { "monster_skill_add_range", &battle_config.mob_skill_add_range }, + { "player_damage_delay", &battle_config.pc_damage_delay }, + { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate }, + { "defunit_not_enemy", &battle_config.defnotenemy }, + { "random_monster_checklv", &battle_config.random_monster_checklv }, + { "attribute_recover", &battle_config.attr_recover }, + { "flooritem_lifetime", &battle_config.flooritem_lifetime }, + { "item_auto_get", &battle_config.item_auto_get }, + { "item_first_get_time", &battle_config.item_first_get_time }, + { "item_second_get_time", &battle_config.item_second_get_time }, + { "item_third_get_time", &battle_config.item_third_get_time }, + { "mvp_item_first_get_time", &battle_config.mvp_item_first_get_time }, + { "mvp_item_second_get_time", &battle_config.mvp_item_second_get_time }, + { "mvp_item_third_get_time", &battle_config.mvp_item_third_get_time }, + { "item_rate", &battle_config.item_rate }, + { "drop_rate0item", &battle_config.drop_rate0item }, + { "base_exp_rate", &battle_config.base_exp_rate }, + { "job_exp_rate", &battle_config.job_exp_rate }, + { "pvp_exp", &battle_config.pvp_exp }, + { "gtb_pvp_only", &battle_config.gtb_pvp_only }, + { "guild_max_castles", &battle_config.guild_max_castles }, + { "death_penalty_type", &battle_config.death_penalty_type }, + { "death_penalty_base", &battle_config.death_penalty_base }, + { "death_penalty_job", &battle_config.death_penalty_job }, + { "zeny_penalty", &battle_config.zeny_penalty }, + { "restart_hp_rate", &battle_config.restart_hp_rate }, + { "restart_sp_rate", &battle_config.restart_sp_rate }, + { "mvp_hp_rate", &battle_config.mvp_hp_rate }, + { "mvp_item_rate", &battle_config.mvp_item_rate }, + { "mvp_exp_rate", &battle_config.mvp_exp_rate }, + { "monster_hp_rate", &battle_config.monster_hp_rate }, + { "monster_max_aspd", &battle_config.monster_max_aspd }, + { "atcommand_gm_only", &battle_config.atc_gmonly }, + { "atcommand_spawn_quantity_limit", &battle_config.atc_spawn_quantity_limit }, + { "gm_all_skill", &battle_config.gm_allskill }, + { "gm_all_skill_add_abra", &battle_config.gm_allskill_addabra }, + { "gm_all_equipment", &battle_config.gm_allequip }, + { "gm_skill_unconditional", &battle_config.gm_skilluncond }, + { "player_skillfree", &battle_config.skillfree }, + { "player_skillup_limit", &battle_config.skillup_limit }, + { "weapon_produce_rate", &battle_config.wp_rate }, + { "potion_produce_rate", &battle_config.pp_rate }, + { "monster_active_enable", &battle_config.monster_active_enable }, + { "monster_damage_delay_rate", &battle_config.monster_damage_delay_rate}, + { "monster_loot_type", &battle_config.monster_loot_type }, + { "mob_skill_use", &battle_config.mob_skill_use }, + { "mob_count_rate", &battle_config.mob_count_rate }, + { "quest_skill_learn", &battle_config.quest_skill_learn }, + { "quest_skill_reset", &battle_config.quest_skill_reset }, + { "basic_skill_check", &battle_config.basic_skill_check }, + { "guild_emperium_check", &battle_config.guild_emperium_check }, + { "guild_exp_limit", &battle_config.guild_exp_limit }, + { "player_invincible_time", &battle_config.pc_invincible_time }, + { "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_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 }, + { "skill_min_damage", &battle_config.skill_min_damage }, + { "finger_offensive_type", &battle_config.finger_offensive_type }, + { "heal_exp", &battle_config.heal_exp }, + { "resurrection_exp", &battle_config.resurrection_exp }, + { "shop_exp", &battle_config.shop_exp }, + { "combo_delay_rate", &battle_config.combo_delay_rate }, + { "item_check", &battle_config.item_check }, + { "wedding_modifydisplay", &battle_config.wedding_modifydisplay }, + { "natural_healhp_interval", &battle_config.natural_healhp_interval }, + { "natural_healsp_interval", &battle_config.natural_healsp_interval }, + { "natural_heal_skill_interval", &battle_config.natural_heal_skill_interval}, + { "natural_heal_weight_rate", &battle_config.natural_heal_weight_rate }, + { "item_name_override_grffile", &battle_config.item_name_override_grffile}, + { "arrow_decrement", &battle_config.arrow_decrement }, + { "max_aspd", &battle_config.max_aspd }, + { "max_hp", &battle_config.max_hp }, + { "max_sp", &battle_config.max_sp }, + { "max_lv", &battle_config.max_lv }, + { "max_parameter", &battle_config.max_parameter }, + { "max_cart_weight", &battle_config.max_cart_weight }, + { "player_skill_log", &battle_config.pc_skill_log }, + { "monster_skill_log", &battle_config.mob_skill_log }, + { "battle_log", &battle_config.battle_log }, + { "save_log", &battle_config.save_log }, + { "error_log", &battle_config.error_log }, + { "etc_log", &battle_config.etc_log }, + { "save_clothcolor", &battle_config.save_clothcolor }, + { "undead_detect_type", &battle_config.undead_detect_type }, + { "player_auto_counter_type", &battle_config.pc_auto_counter_type }, + { "monster_auto_counter_type", &battle_config.monster_auto_counter_type}, + { "agi_penaly_type", &battle_config.agi_penaly_type }, + { "agi_penaly_count", &battle_config.agi_penaly_count }, + { "agi_penaly_num", &battle_config.agi_penaly_num }, + { "agi_penaly_count_lv", &battle_config.agi_penaly_count_lv }, + { "vit_penaly_type", &battle_config.vit_penaly_type }, + { "vit_penaly_count", &battle_config.vit_penaly_count }, + { "vit_penaly_num", &battle_config.vit_penaly_num }, + { "vit_penaly_count_lv", &battle_config.vit_penaly_count_lv }, + { "player_defense_type", &battle_config.player_defense_type }, + { "monster_defense_type", &battle_config.monster_defense_type }, + { "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 }, + { "gvg_short_attack_damage_rate", &battle_config.gvg_short_damage_rate }, + { "gvg_long_attack_damage_rate", &battle_config.gvg_long_damage_rate }, + { "gvg_magic_attack_damage_rate", &battle_config.gvg_magic_damage_rate }, + { "gvg_misc_attack_damage_rate", &battle_config.gvg_misc_damage_rate }, + { "gvg_eliminate_time", &battle_config.gvg_eliminate_time }, + { "mob_changetarget_byskill", &battle_config.mob_changetarget_byskill}, + { "player_attack_direction_change", &battle_config.pc_attack_direction_change }, + { "monster_attack_direction_change", &battle_config.monster_attack_direction_change }, + { "player_land_skill_limit", &battle_config.pc_land_skill_limit }, + { "monster_land_skill_limit", &battle_config.monster_land_skill_limit}, + { "party_skill_penaly", &battle_config.party_skill_penaly }, + { "monster_class_change_full_recover", &battle_config.monster_class_change_full_recover }, + { "produce_item_name_input", &battle_config.produce_item_name_input }, + { "produce_potion_name_input", &battle_config.produce_potion_name_input}, + { "making_arrow_name_input", &battle_config.making_arrow_name_input }, + { "holywater_name_input", &battle_config.holywater_name_input }, + { "display_delay_skill_fail", &battle_config.display_delay_skill_fail }, + { "chat_warpportal", &battle_config.chat_warpportal }, + { "mob_warpportal", &battle_config.mob_warpportal }, + { "dead_branch_active", &battle_config.dead_branch_active }, + { "vending_max_value", &battle_config.vending_max_value }, + { "show_steal_in_same_party", &battle_config.show_steal_in_same_party }, + { "enable_upper_class", &battle_config.enable_upper_class }, + { "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_cardfix", &battle_config.gx_cardfix }, + { "gx_dupele", &battle_config.gx_dupele }, + { "gx_disptype", &battle_config.gx_disptype }, + { "player_skill_partner_check", &battle_config.player_skill_partner_check}, + { "hide_GM_session", &battle_config.hide_GM_session }, + { "unit_movement_type", &battle_config.unit_movement_type }, + { "invite_request_check", &battle_config.invite_request_check }, + { "skill_removetrap_type", &battle_config.skill_removetrap_type }, + { "disp_experience", &battle_config.disp_experience }, + { "castle_defense_rate", &battle_config.castle_defense_rate }, + { "riding_weight", &battle_config.riding_weight }, + { "item_rate_common", &battle_config.item_rate_common }, // Added by RoVeRT + { "item_rate_equip", &battle_config.item_rate_equip }, + { "item_rate_card", &battle_config.item_rate_card }, // End Addition + { "item_rate_heal", &battle_config.item_rate_heal }, // Added by Valaris + { "item_rate_use", &battle_config.item_rate_use }, // End + { "item_drop_common_min", &battle_config.item_drop_common_min }, // Added by TyrNemesis^ + { "item_drop_common_max", &battle_config.item_drop_common_max }, + { "item_drop_equip_min", &battle_config.item_drop_equip_min }, + { "item_drop_equip_max", &battle_config.item_drop_equip_max }, + { "item_drop_card_min", &battle_config.item_drop_card_min }, + { "item_drop_card_max", &battle_config.item_drop_card_max }, + { "item_drop_mvp_min", &battle_config.item_drop_mvp_min }, + { "item_drop_mvp_max", &battle_config.item_drop_mvp_max }, // End Addition + { "prevent_logout", &battle_config.prevent_logout }, // Added by RoVeRT + { "alchemist_summon_reward", &battle_config.alchemist_summon_reward }, // [Valaris] + { "maximum_level", &battle_config.maximum_level }, // [Valaris] + { "drops_by_luk", &battle_config.drops_by_luk }, // [Valaris] + { "monsters_ignore_gm", &battle_config.monsters_ignore_gm }, // [Valaris] + { "equipment_breaking", &battle_config.equipment_breaking }, // [Valaris] + { "equipment_break_rate", &battle_config.equipment_break_rate }, // [Valaris] + { "pk_mode", &battle_config.pk_mode }, // [Valaris] + { "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] + { "day_duration", &battle_config.day_duration }, // added by [Yor] + { "night_duration", &battle_config.night_duration }, // added by [Yor] + { "show_mob_hp", &battle_config.show_mob_hp }, // [Valaris] + { "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] + { "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] +//SQL-only options start +#ifndef TXT_ONLY + { "mail_system", &battle_config.mail_system }, // added by [Valaris] +//SQL-only options end +#endif + }; + + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%[^:]:%s", w1, w2) != 2) + continue; + for(i = 0; i < sizeof(data) / (sizeof(data[0])); i++) + if (strcmpi(w1, data[i].str) == 0) + *data[i].val = battle_config_switch(w2); + + if (strcmpi(w1, "import") == 0) + battle_config_read(w2); + } + fclose(fp); + + if (--count == 0) { + if(battle_config.flooritem_lifetime < 1000) + battle_config.flooritem_lifetime = LIFETIME_FLOORITEM*1000; + if(battle_config.restart_hp_rate < 0) + battle_config.restart_hp_rate = 0; + else if(battle_config.restart_hp_rate > 100) + battle_config.restart_hp_rate = 100; + if(battle_config.restart_sp_rate < 0) + battle_config.restart_sp_rate = 0; + else if(battle_config.restart_sp_rate > 100) + battle_config.restart_sp_rate = 100; + if(battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL) + battle_config.natural_healhp_interval=NATURAL_HEAL_INTERVAL; + if(battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL) + battle_config.natural_healsp_interval=NATURAL_HEAL_INTERVAL; + if(battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL) + battle_config.natural_heal_skill_interval=NATURAL_HEAL_INTERVAL; + if(battle_config.natural_heal_weight_rate < 50) + battle_config.natural_heal_weight_rate = 50; + if(battle_config.natural_heal_weight_rate > 101) + battle_config.natural_heal_weight_rate = 101; + battle_config.monster_max_aspd = 2000 - battle_config.monster_max_aspd*10; + if(battle_config.monster_max_aspd < 10) + battle_config.monster_max_aspd = 10; + if(battle_config.monster_max_aspd > 1000) + battle_config.monster_max_aspd = 1000; + battle_config.max_aspd = 2000 - battle_config.max_aspd*10; + if(battle_config.max_aspd < 10) + battle_config.max_aspd = 10; + if(battle_config.max_aspd > 1000) + battle_config.max_aspd = 1000; + if(battle_config.max_hp > 1000000) + battle_config.max_hp = 1000000; + if(battle_config.max_hp < 100) + battle_config.max_hp = 100; + if(battle_config.max_sp > 1000000) + battle_config.max_sp = 1000000; + if(battle_config.max_sp < 100) + battle_config.max_sp = 100; + if(battle_config.max_parameter < 10) + battle_config.max_parameter = 10; + if(battle_config.max_parameter > 10000) + battle_config.max_parameter = 10000; + if(battle_config.max_cart_weight > 1000000) + battle_config.max_cart_weight = 1000000; + if(battle_config.max_cart_weight < 100) + battle_config.max_cart_weight = 100; + battle_config.max_cart_weight *= 10; + + if(battle_config.agi_penaly_count < 2) + battle_config.agi_penaly_count = 2; + if(battle_config.vit_penaly_count < 2) + battle_config.vit_penaly_count = 2; + + if(battle_config.guild_exp_limit > 99) + battle_config.guild_exp_limit = 99; + if(battle_config.guild_exp_limit < 0) + battle_config.guild_exp_limit = 0; + + if(battle_config.castle_defense_rate < 0) + battle_config.castle_defense_rate = 0; + if(battle_config.castle_defense_rate > 100) + battle_config.castle_defense_rate = 100; + if(battle_config.item_drop_common_min < 1) // Added by TyrNemesis^ + battle_config.item_drop_common_min = 1; + if(battle_config.item_drop_common_max > 10000) + battle_config.item_drop_common_max = 10000; + if(battle_config.item_drop_equip_min < 1) + battle_config.item_drop_equip_min = 1; + if(battle_config.item_drop_equip_max > 10000) + battle_config.item_drop_equip_max = 10000; + if(battle_config.item_drop_card_min < 1) + battle_config.item_drop_card_min = 1; + if(battle_config.item_drop_card_max > 10000) + battle_config.item_drop_card_max = 10000; + if(battle_config.item_drop_mvp_min < 1) + battle_config.item_drop_mvp_min = 1; + if(battle_config.item_drop_mvp_max > 10000) + battle_config.item_drop_mvp_max = 10000; // End Addition + + if (battle_config.night_at_start < 0) // added by [Yor] + battle_config.night_at_start = 0; + else if (battle_config.night_at_start > 1) // added by [Yor] + battle_config.night_at_start = 1; + if (battle_config.day_duration < 0) // added by [Yor] + battle_config.day_duration = 0; + if (battle_config.night_duration < 0) // added by [Yor] + battle_config.night_duration = 0; + + if (battle_config.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; + + // at least 1 client must be accepted + if ((battle_config.packet_ver_flag & 63) == 0) // added by [Yor] + battle_config.packet_ver_flag = 63; // accept all clients + + add_timer_func_list(battle_delay_damage_sub, "battle_delay_damage_sub"); + } + + return 0; +} diff --git a/src/map/battle.h b/src/map/battle.h new file mode 100644 index 0000000..8f09d22 --- /dev/null +++ b/src/map/battle.h @@ -0,0 +1,342 @@ +// $Id: battle.h,v 1.6 2004/09/29 21:08:17 Akitasha Exp $ +#ifndef _BATTLE_H_ +#define _BATTLE_H_ + +// ダメージ +struct Damage { + int damage,damage2; + int type,div_; + int amotion,dmotion; + int blewcount; + int flag; + int dmg_lv; //囲まれ減算計算用 0:スキル攻撃 ATK_LUCKY,ATK_FLEE,ATK_DEF +}; + +// 属性表(読み込みはpc.c、battle_attr_fixで使用) +extern int attr_fix_table[4][10][10]; + +struct map_session_data; +struct mob_data; +struct block_list; + +// ダメージ計算 + +struct Damage battle_calc_attack( int attack_type, + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag); +struct Damage battle_calc_weapon_attack( + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag); +struct Damage battle_calc_magic_attack( + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag); +struct Damage battle_calc_misc_attack( + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag); + +// 属性修正計算 +int battle_attr_fix(int damage,int atk_elem,int def_elem); + +// ダメージ最終計算 +int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag); +enum { // 最終計算のフラグ + BF_WEAPON = 0x0001, + BF_MAGIC = 0x0002, + BF_MISC = 0x0004, + BF_SHORT = 0x0010, + BF_LONG = 0x0040, + BF_SKILL = 0x0100, + BF_NORMAL = 0x0200, + BF_WEAPONMASK=0x000f, + BF_RANGEMASK= 0x00f0, + BF_SKILLMASK= 0x0f00, +}; + +// 実際にHPを増減 +int battle_delay_damage(unsigned int tick,struct block_list *src,struct block_list *target,int damage,int flag); +int battle_damage(struct block_list *bl,struct block_list *target,int damage,int flag); +int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp,int flag); + +// 攻撃や移動を止める +int battle_stopattack(struct block_list *bl); +int battle_stopwalking(struct block_list *bl,int type); + +// 通常攻撃処理まとめ +int battle_weapon_attack( struct block_list *bl,struct block_list *target, + unsigned int tick,int flag); + +// 各種パラメータを得る +int battle_counttargeted(struct block_list *bl,struct block_list *src,int target_lv); +int battle_get_class(struct block_list *bl); +int battle_get_dir(struct block_list *bl); +int battle_get_lv(struct block_list *bl); +int battle_get_range(struct block_list *bl); +int battle_get_hp(struct block_list *bl); +int battle_get_max_hp(struct block_list *bl); +int battle_get_str(struct block_list *bl); +int battle_get_agi(struct block_list *bl); +int battle_get_vit(struct block_list *bl); +int battle_get_int(struct block_list *bl); +int battle_get_dex(struct block_list *bl); +int battle_get_luk(struct block_list *bl); +int battle_get_hit(struct block_list *bl); +int battle_get_flee(struct block_list *bl); +int battle_get_def(struct block_list *bl); +int battle_get_mdef(struct block_list *bl); +int battle_get_flee2(struct block_list *bl); +int battle_get_def2(struct block_list *bl); +int battle_get_mdef2(struct block_list *bl); +int battle_get_baseatk(struct block_list *bl); +int battle_get_atk(struct block_list *bl); +int battle_get_atk2(struct block_list *bl); +int battle_get_speed(struct block_list *bl); +int battle_get_adelay(struct block_list *bl); +int battle_get_amotion(struct block_list *bl); +int battle_get_dmotion(struct block_list *bl); +int battle_get_element(struct block_list *bl); +int battle_get_attack_element(struct block_list *bl); +int battle_get_attack_element2(struct block_list *bl); //左手武器属性取得 +#define battle_get_elem_type(bl) (battle_get_element(bl)%10) +#define battle_get_elem_level(bl) (battle_get_element(bl)/10/2) +int battle_get_party_id(struct block_list *bl); +int battle_get_guild_id(struct block_list *bl); +int battle_get_race(struct block_list *bl); +int battle_get_size(struct block_list *bl); +int battle_get_mode(struct block_list *bl); +int battle_get_mexp(struct block_list *bl); + +struct status_change *battle_get_sc_data(struct block_list *bl); +short *battle_get_sc_count(struct block_list *bl); +short *battle_get_opt1(struct block_list *bl); +short *battle_get_opt2(struct block_list *bl); +short *battle_get_opt3(struct block_list *bl); +short *battle_get_option(struct block_list *bl); + +enum { + BCT_NOENEMY =0x00000, + BCT_PARTY =0x10000, + BCT_ENEMY =0x40000, + BCT_NOPARTY =0x50000, + BCT_ALL =0x20000, + BCT_NOONE =0x60000, +}; + +int battle_check_undead(int race,int element); +int battle_check_target( struct block_list *src, struct block_list *target,int flag); +int battle_check_range(struct block_list *src,struct block_list *bl,int range); + + +// 設定 + +int battle_config_switch(const char *str); // [Valaris] + +extern struct Battle_Config { + int warp_point_debug; + int enemy_critical; + int enemy_critical_rate; + int enemy_str; + int enemy_perfect_flee; + int cast_rate,delay_rate,delay_dependon_dex; + int sdelay_attack_enable; + int left_cardfix_to_right; + int pc_skill_add_range; + int skill_out_range_consume; + int mob_skill_add_range; + int pc_damage_delay; + int pc_damage_delay_rate; + int defnotenemy; + int random_monster_checklv; + int attr_recover; + int flooritem_lifetime; + int item_auto_get; + int item_first_get_time; + int item_second_get_time; + int item_third_get_time; + int mvp_item_first_get_time; + int mvp_item_second_get_time; + int mvp_item_third_get_time; + int item_rate,base_exp_rate,job_exp_rate; // removed item rate, depreciated + int drop_rate0item; + int death_penalty_type; + int death_penalty_base,death_penalty_job; + int pvp_exp; // [MouseJstr] + int gtb_pvp_only; // [MouseJstr] + int zeny_penalty; + int restart_hp_rate; + int restart_sp_rate; + int mvp_item_rate,mvp_exp_rate; + int mvp_hp_rate; + int monster_hp_rate; + int monster_max_aspd; + int atc_gmonly; + int atc_spawn_quantity_limit; + int gm_allskill; + int gm_allskill_addabra; + int gm_allequip; + int gm_skilluncond; + int skillfree; + int skillup_limit; + int wp_rate; + int pp_rate; + int monster_active_enable; + int monster_damage_delay_rate; + int monster_loot_type; + int mob_skill_use; + int mob_count_rate; + int quest_skill_learn; + int quest_skill_reset; + int basic_skill_check; + int guild_emperium_check; + int guild_exp_limit; + int guild_max_castles; + int pc_invincible_time; + int pet_catch_rate; + int pet_rename; + int pet_friendly_rate; + int pet_hungry_delay_rate; + int pet_hungry_friendly_decrease; + int pet_str; + int pet_status_support; + int pet_attack_support; + int pet_damage_support; + int pet_support_rate; + int pet_attack_exp_to_master; + int pet_attack_exp_rate; + int skill_min_damage; + int finger_offensive_type; + int heal_exp; + int resurrection_exp; + int shop_exp; + int combo_delay_rate; + int item_check; + int wedding_modifydisplay; + int natural_healhp_interval; + int natural_healsp_interval; + int natural_heal_skill_interval; + int natural_heal_weight_rate; + int item_name_override_grffile; + int arrow_decrement; + int max_aspd; + int max_hp; + int max_sp; + int max_lv; + int max_parameter; + int max_cart_weight; + int pc_skill_log; + int mob_skill_log; + int battle_log; + int save_log; + int error_log; + int etc_log; + int save_clothcolor; + int undead_detect_type; + int pc_auto_counter_type; + int monster_auto_counter_type; + int agi_penaly_type; + int agi_penaly_count; + int agi_penaly_num; + int vit_penaly_type; + int vit_penaly_count; + int vit_penaly_num; + int player_defense_type; + int monster_defense_type; + int pet_defense_type; + int magic_defense_type; + int pc_skill_reiteration; + int monster_skill_reiteration; + int pc_skill_nofootset; + int monster_skill_nofootset; + int pc_cloak_check_type; + int monster_cloak_check_type; + int gvg_short_damage_rate; + int gvg_long_damage_rate; + int gvg_magic_damage_rate; + int gvg_misc_damage_rate; + int gvg_eliminate_time; + int mob_changetarget_byskill; + int pc_attack_direction_change; + int monster_attack_direction_change; + int pc_undead_nofreeze; + int pc_land_skill_limit; + int monster_land_skill_limit; + int party_skill_penaly; + int monster_class_change_full_recover; + int produce_item_name_input; + int produce_potion_name_input; + int making_arrow_name_input; + int holywater_name_input; + int display_delay_skill_fail; + int chat_warpportal; + int mob_warpportal; + int dead_branch_active; + int vending_max_value; +// int pet_lootitem; // removed [Valaris] +// int pet_weight; // removed [Valaris] + int show_steal_in_same_party; + int enable_upper_class; + int pet_attack_attr_none; + int mob_attack_attr_none; + int mob_ghostring_fix; + int pc_attack_attr_none; + int item_rate_common,item_rate_card,item_rate_equip,item_rate_heal,item_rate_use; // Added by RoVeRT, Additional Heal and Usable item rate by Val + int item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^ + int item_drop_card_min,item_drop_card_max; + int item_drop_equip_min,item_drop_equip_max; + int item_drop_mvp_min,item_drop_mvp_max; // End Addition + int item_drop_heal_min,item_drop_heal_max; // Added by Valatris + int item_drop_use_min,item_drop_use_max; //End + + int prevent_logout; // Added by RoVeRT + + int alchemist_summon_reward; // [Valaris] + int maximum_level; + int drops_by_luk; + int monsters_ignore_gm; + int equipment_breaking; + int equipment_break_rate; + int pet_equip_required; + int multi_level_up; + int pk_mode; + int show_mob_hp; // end additions [Valaris] + + int agi_penaly_count_lv; + int vit_penaly_count_lv; + + int gx_allhit; + int gx_cardfix; + int gx_dupele; + int gx_disptype; + int player_skill_partner_check; + int hide_GM_session; + int unit_movement_type; + int invite_request_check; + int skill_removetrap_type; + int disp_experience; + int castle_defense_rate; + int riding_weight; + int backstab_bow_penalty; + + int night_at_start; // added by [Yor] + int day_duration; // added by [Yor] + int night_duration; // added by [Yor] + int ban_spoof_namer; // added by [Yor] + int hack_info_GM_level; // added by [Yor] + int any_warp_GM_min_level; // added by [Yor] + int packet_ver_flag; // added by [Yor] + int muting_players; // added by [Apple] + + int min_hair_style; // added by [MouseJstr] + int max_hair_style; // added by [MouseJstr] + int min_hair_color; // added by [MouseJstr] + int max_hair_color; // added by [MouseJstr] + int min_cloth_color; // added by [MouseJstr] + int max_cloth_color; // added by [MouseJstr] + + int castrate_dex_scale; // added by [MouseJstr] + int area_size; // added by [MouseJstr] + +#ifndef TXT_ONLY /* SQL-only options */ + int mail_system; // [Valaris] +#endif + +} battle_config; + +int battle_config_read(const char *cfgName); + +#endif diff --git a/src/map/chat.c b/src/map/chat.c new file mode 100644 index 0000000..ade4dcb --- /dev/null +++ b/src/map/chat.c @@ -0,0 +1,373 @@ +// $Id: chat.c,v 1.2 2004/09/22 02:59:47 Akitasha Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "db.h" +#include "nullpo.h" +#include "malloc.h" +#include "map.h" +#include "clif.h" +#include "pc.h" +#include "chat.h" +#include "npc.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +int chat_triggerevent(struct chat_data *cd); + + +/*========================================== + * チャットルーム作成 + *------------------------------------------ + */ +int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen) +{ + struct chat_data *cd; + + nullpo_retr(0, sd); + + cd = aCalloc(1,sizeof(struct chat_data)); + + cd->limit = limit; + cd->pub = pub; + cd->users = 1; + memcpy(cd->pass,pass,8); + if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; + memcpy(cd->title,title,titlelen); + cd->title[titlelen]=0; + + cd->owner = (struct block_list **)(&cd->usersd[0]); + cd->usersd[0] = sd; + cd->bl.m = sd->bl.m; + cd->bl.x = sd->bl.x; + cd->bl.y = sd->bl.y; + cd->bl.type = BL_CHAT; + + cd->bl.id = map_addobject(&cd->bl); + if(cd->bl.id==0){ + clif_createchat(sd,1); + free(cd); + return 0; + } + pc_setchatid(sd,cd->bl.id); + + clif_createchat(sd,0); + clif_dispchat(cd,0); + + return 0; +} + +/*========================================== + * 既存チャットルームに参加 + *------------------------------------------ + */ +int chat_joinchat(struct map_session_data *sd,int chatid,char* pass) +{ + struct chat_data *cd; + + nullpo_retr(0, sd); + + cd=(struct chat_data*)map_id2bl(chatid); + if(cd==NULL) + return 1; + + if(cd->bl.m != sd->bl.m || cd->limit <= cd->users){ + clif_joinchatfail(sd,0); + return 0; + } + if(cd->pub==0 && strncmp(pass,cd->pass,8)){ + clif_joinchatfail(sd,1); + return 0; + } + + cd->usersd[cd->users] = sd; + cd->users++; + + pc_setchatid(sd,cd->bl.id); + + + clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト + clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告 + clif_dispchat(cd,0); // 周囲の人には人数変化報告 + + chat_triggerevent(cd); // イベント + + return 0; +} + +/*========================================== + * チャットルームから抜ける + *------------------------------------------ + */ +int chat_leavechat(struct map_session_data *sd) +{ + struct chat_data *cd; + int i,leavechar; + + nullpo_retr(1, sd); + + cd=(struct chat_data*)map_id2bl(sd->chatID); + if(cd==NULL) + return 1; + + for(i = 0,leavechar=-1;i < cd->users;i++){ + if(cd->usersd[i] == sd){ + leavechar=i; + break; + } + } + if(leavechar<0) // そのchatに所属していないらしい (バグ時のみ) + return -1; + + if(leavechar==0 && cd->users>1 && (*cd->owner)->type==BL_PC){ + // 所有者だった&他に人が居る&PCのチャット + clif_changechatowner(cd,cd->usersd[1]); + clif_clearchat(cd,0); + } + + // 抜けるPCにも送るのでusersを減らす前に実行 + clif_leavechat(cd,sd); + + cd->users--; + pc_setchatid(sd,0); + + if(cd->users == 0 && (*cd->owner)->type==BL_PC){ + // 全員居なくなった&PCのチャットなので消す + clif_clearchat(cd,0); + map_delobject(cd->bl.id); // freeまでしてくれる + } else { + for(i=leavechar;i < cd->users;i++) + cd->usersd[i] = cd->usersd[i+1]; + if(leavechar==0 && (*cd->owner)->type==BL_PC){ + // PCのチャットなので所有者が抜けたので位置変更 + cd->bl.x=cd->usersd[0]->bl.x; + cd->bl.y=cd->usersd[0]->bl.y; + } + clif_dispchat(cd,0); + } + + return 0; +} + +/*========================================== + * チャットルームの持ち主を譲る + *------------------------------------------ + */ +int chat_changechatowner(struct map_session_data *sd,char *nextownername) +{ + struct chat_data *cd; + struct map_session_data *tmp_sd; + int i,nextowner; + + nullpo_retr(1, sd); + + cd=(struct chat_data*)map_id2bl(sd->chatID); + if(cd==NULL || (struct block_list *)sd!=(*cd->owner)) + return 1; + + for(i = 1,nextowner=-1;i < cd->users;i++){ + if(strcmp(cd->usersd[i]->status.name,nextownername)==0){ + nextowner=i; + break; + } + } + if(nextowner<0) // そんな人は居ない + return -1; + + clif_changechatowner(cd,cd->usersd[nextowner]); + // 一旦消す + clif_clearchat(cd,0); + + // userlistの順番変更 (0が所有者なので) + if( (tmp_sd = cd->usersd[0]) == NULL ) + return 1; //ありえるのかな? + cd->usersd[0] = cd->usersd[nextowner]; + cd->usersd[nextowner] = tmp_sd; + + // 新しい所有者の位置へ変更 + cd->bl.x=cd->usersd[0]->bl.x; + cd->bl.y=cd->usersd[0]->bl.y; + + // 再度表示 + clif_dispchat(cd,0); + + return 0; +} + +/*========================================== + * チャットの状態(タイトル等)を変更 + *------------------------------------------ + */ +int chat_changechatstatus(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen) +{ + struct chat_data *cd; + + nullpo_retr(1, sd); + + cd=(struct chat_data*)map_id2bl(sd->chatID); + if(cd==NULL || (struct block_list *)sd!=(*cd->owner)) + return 1; + + cd->limit = limit; + cd->pub = pub; + memcpy(cd->pass,pass,8); + if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; + memcpy(cd->title,title,titlelen); + cd->title[titlelen]=0; + + clif_changechatstatus(cd); + clif_dispchat(cd,0); + + return 0; +} + +/*========================================== + * チャットルームから蹴り出す + *------------------------------------------ + */ +int chat_kickchat(struct map_session_data *sd,char *kickusername) +{ + struct chat_data *cd; + int i,kickuser; + + nullpo_retr(1, sd); + + cd=(struct chat_data*)map_id2bl(sd->chatID); + if(cd==NULL || (struct block_list *)sd!=(*cd->owner)) + return 1; + + for(i = 0,kickuser=-1;i < cd->users;i++){ + if(strcmp(cd->usersd[i]->status.name,kickusername)==0){ + kickuser=i; + break; + } + } + if(kickuser<0) // そんな人は居ない + return -1; + + chat_leavechat(cd->usersd[kickuser]); + + return 0; +} + +/*========================================== + * npcチャットルーム作成 + *------------------------------------------ + */ +int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev) +{ + struct chat_data *cd; + + nullpo_retr(1, nd); + + cd = 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,"",8); + if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1; + memcpy(cd->title,title,titlelen); + cd->title[titlelen]=0; + + cd->bl.m = nd->bl.m; + cd->bl.x = nd->bl.x; + cd->bl.y = nd->bl.y; + cd->bl.type = BL_CHAT; + cd->owner_ = (struct block_list *)nd; + cd->owner = &cd->owner_; + memcpy(cd->npc_event,ev,sizeof(cd->npc_event)); + + cd->bl.id = map_addobject(&cd->bl); + if(cd->bl.id==0){ + free(cd); + return 0; + } + nd->chat_id=cd->bl.id; + + clif_dispchat(cd,0); + + return 0; +} +/*========================================== + * npcチャットルーム削除 + *------------------------------------------ + */ +int chat_deletenpcchat(struct npc_data *nd) +{ + struct chat_data *cd; + + nullpo_retr(0, nd); + nullpo_retr(0, cd=(struct chat_data*)map_id2bl(nd->chat_id)); + + chat_npckickall(cd); + clif_clearchat(cd,0); + map_delobject(cd->bl.id); // freeまでしてくれる + nd->chat_id=0; + + return 0; +} + +/*========================================== + * 規定人数以上でイベントが定義されてるなら実行 + *------------------------------------------ + */ +int chat_triggerevent(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + if(cd->users>=cd->trigger && cd->npc_event[0]) + npc_event_do(cd->npc_event); + return 0; +} + +/*========================================== + * イベントの有効化 + *------------------------------------------ + */ +int chat_enableevent(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + cd->trigger&=0x7f; + chat_triggerevent(cd); + return 0; +} +/*========================================== + * イベントの無効化 + *------------------------------------------ + */ +int chat_disableevent(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + cd->trigger|=0x80; + return 0; +} +/*========================================== + * チャットルームから全員蹴り出す + *------------------------------------------ + */ +int chat_npckickall(struct chat_data *cd) +{ + nullpo_retr(0, cd); + + while(cd->users>0){ + chat_leavechat(cd->usersd[cd->users-1]); + } + return 0; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +int do_final_chat(void) +{ + return 0; +} diff --git a/src/map/chat.h b/src/map/chat.h new file mode 100644 index 0000000..2761602 --- /dev/null +++ b/src/map/chat.h @@ -0,0 +1,22 @@ +// $Id: chat.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _CHAT_H_ +#define _CHAT_H_ + +#include "map.h" + +int chat_createchat(struct map_session_data *,int,int,char*,char*,int); +int chat_joinchat(struct map_session_data *,int,char*); +int chat_leavechat(struct map_session_data* ); +int chat_changechatowner(struct map_session_data *,char *); +int chat_changechatstatus(struct map_session_data *,int,int,char*,char*,int); +int chat_kickchat(struct map_session_data *,char *); + +int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev); +int chat_deletenpcchat(struct npc_data *nd); +int chat_enableevent(struct chat_data *cd); +int chat_disableevent(struct chat_data *cd); +int chat_npckickall(struct chat_data *cd); + +int do_final_chat(void); + +#endif diff --git a/src/map/chrif.c b/src/map/chrif.c new file mode 100644 index 0000000..dc401b6 --- /dev/null +++ b/src/map/chrif.c @@ -0,0 +1,1016 @@ +// $Id: chrif.c,v 1.6 2004/09/25 11:39:17 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <sys/types.h> +#include <time.h> + +#include "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 "nullpo.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +static const int packet_len_table[0x20] = { + 60, 3,-1,27,22,-1, 6,-1, // 2af8-2aff + 6,-1,18, 7,-1,49,44, 0, // 2b00-2b07 + 6,30,-1,10,86, 7,44,34, // 2b08-2b0f + -1,-1,10, 6,11,-1, 0, 0, // 2b10-2b17 +}; + +int char_fd; +int srvinfo; +static char char_ip_str[16]; +static int char_ip; +static int char_port = 6121; +static char userid[24], passwd[24]; +static int chrif_state; + +// 設定ファイル読み込み関係 +/*========================================== + * + *------------------------------------------ + */ +void chrif_setuserid(char *id) +{ + strncpy(userid, id, 24); +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_setpasswd(char *pwd) +{ + strncpy(passwd, pwd, 24); +} + +/*========================================== + * + *------------------------------------------ + */ +void chrif_setip(char *ip) +{ + strncpy(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 chrif_state == 2; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_save(struct map_session_data *sd) +{ + nullpo_retr(-1, sd); + + if (char_fd < 0) + return -1; + + pc_makesavestatus(sd); + + WFIFOW(char_fd,0) = 0x2b01; + WFIFOW(char_fd,2) = sizeof(sd->status) + 12; + WFIFOL(char_fd,4) = sd->bl.id; + WFIFOL(char_fd,8) = sd->char_id; + memcpy(WFIFOP(char_fd,12), &sd->status, sizeof(sd->status)); + WFIFOSET(char_fd, WFIFOW(char_fd,2)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_connect(int fd) +{ + WFIFOW(fd,0) = 0x2af8; + memcpy(WFIFOP(fd,2), userid, 24); + memcpy(WFIFOP(fd,26), passwd, 24); + WFIFOL(fd,50) = 0; + WFIFOL(fd,54) = clif_getip(); + WFIFOW(fd,58) = clif_getport(); // [Valaris] thanks to fov + WFIFOSET(fd,60); + + return 0; +} + +/*========================================== + * マップ送信 + *------------------------------------------ + */ +int chrif_sendmap(int fd) +{ + int i; + + WFIFOW(fd,0) = 0x2afa; + for(i = 0; i < map_num; i++) + if (map[i].alias[0] != '\0') // [MouseJstr] map aliasing + memcpy(WFIFOP(fd,4+i*16), map[i].alias, 16); + else + memcpy(WFIFOP(fd,4+i*16), map[i].name, 16); + WFIFOW(fd,2) = 4 + i * 16; + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +/*========================================== + * マップ受信 + *------------------------------------------ + */ +int chrif_recvmap(int fd) +{ + int i, j, ip, port; + unsigned char *p = (unsigned char *)&ip; + + if (chrif_state < 2) // まだ準備中 + return -1; + + ip = RFIFOL(fd,4); + port = RFIFOW(fd,8); + for(i = 10, j = 0; i < RFIFOW(fd,2); i += 16, j++) { + map_setipport(RFIFOP(fd,i), ip, port); +// if (battle_config.etc_log) +// printf("recv map %d %s\n", j, RFIFOP(fd,i)); + } + if (battle_config.etc_log) + printf("recv map on %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j); + + return 0; +} + +/*========================================== + * マップ鯖間移動のためのデータ準備要求 + *------------------------------------------ + */ +int chrif_changemapserver(struct map_session_data *sd, char *name, int x, int y, int ip, short port) +{ + int i, s_ip; + + nullpo_retr(-1, sd); + + s_ip = 0; + for(i = 0; i < fd_max; i++) + if (session[i] && session[i]->session_data == sd) { + s_ip = session[i]->client_addr.sin_addr.s_addr; + break; + } + + WFIFOW(char_fd, 0) = 0x2b05; + WFIFOL(char_fd, 2) = sd->bl.id; + WFIFOL(char_fd, 6) = sd->login_id1; + WFIFOL(char_fd,10) = sd->login_id2; + WFIFOL(char_fd,14) = sd->status.char_id; + memcpy(WFIFOP(char_fd,18), name, 16); + WFIFOW(char_fd,34) = x; + WFIFOW(char_fd,36) = y; + WFIFOL(char_fd,38) = ip; + WFIFOL(char_fd,42) = port; + WFIFOB(char_fd,44) = sd->status.sex; + WFIFOL(char_fd,45) = s_ip; + WFIFOSET(char_fd,49); + + return 0; +} + +/*========================================== + * マップ鯖間移動ack + *------------------------------------------ + */ +int chrif_changemapserverack(int fd) +{ + struct map_session_data *sd = map_id2sd(RFIFOL(fd,2)); + + if (sd == NULL || sd->status.char_id != RFIFOL(fd,14)) + return -1; + + if (RFIFOL(fd,6) == 1) { + if (battle_config.error_log) + printf("map server change failed.\n"); + pc_authfail(sd->fd); + return 0; + } + clif_changemapserver(sd, RFIFOP(fd,18), RFIFOW(fd,34), RFIFOW(fd,36), RFIFOL(fd,38), RFIFOW(fd,42)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_connectack(int fd) +{ + if (RFIFOB(fd,2)) { + printf("Connected to char-server failed %d.\n", RFIFOB(fd,2)); + exit(1); + } + printf("Connected to char-server (connection #%d).\n", fd); + chrif_state = 1; + + chrif_sendmap(fd); + + printf("chrif: OnCharIfInit event done. (%d events)\n", npc_event_doall("OnCharIfInit")); + printf("chrif: OnInterIfInit event done. (%d events)\n", npc_event_doall("OnInterIfInit")); + + // <Agit> Run Event [AgitInit] +// printf("NPC_Event:[OnAgitInit] do (%d) events (Agit Initialize).\n", npc_event_doall("OnAgitInit")); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_sendmapack(int fd) +{ + if (RFIFOB(fd,2)) { + printf("chrif : send map list to char server failed %d\n", RFIFOB(fd,2)); + exit(1); + } + + memcpy(wisp_server_name, RFIFOP(fd,3), 24); + + chrif_state = 2; + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_authreq(struct map_session_data *sd) +{ + int i; + + nullpo_retr(-1, sd); + + if (!sd || !char_fd || !sd->bl.id || !sd->login_id1) + return -1; + + for(i = 0; i < fd_max; i++) + if (session[i] && session[i]->session_data == sd) { + WFIFOW(char_fd, 0) = 0x2afc; + WFIFOL(char_fd, 2) = sd->bl.id; + WFIFOL(char_fd, 6) = sd->char_id; + WFIFOL(char_fd,10) = sd->login_id1; + WFIFOL(char_fd,14) = sd->login_id2; + WFIFOL(char_fd,18) = session[i]->client_addr.sin_addr.s_addr; + WFIFOSET(char_fd,22); + break; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int chrif_charselectreq(struct map_session_data *sd) +{ + int i, s_ip; + + nullpo_retr(-1, sd); + + if(!sd || !char_fd || !sd->bl.id || !sd->login_id1) + return -1; + + s_ip = 0; + for(i = 0; i < fd_max; i++) + if (session[i] && session[i]->session_data == sd) { + s_ip = session[i]->client_addr.sin_addr.s_addr; + break; + } + + WFIFOW(char_fd, 0) = 0x2b02; + WFIFOL(char_fd, 2) = sd->bl.id; + WFIFOL(char_fd, 6) = sd->login_id1; + WFIFOL(char_fd,10) = sd->login_id2; + WFIFOL(char_fd,14) = s_ip; + WFIFOSET(char_fd,18); + + return 0; +} + +/*========================================== + * キャラ名問い合わせ + *------------------------------------------ + */ +int chrif_searchcharid(int char_id) +{ + if (!char_id) + return -1; + + WFIFOW(char_fd,0) = 0x2b08; + WFIFOL(char_fd,2) = char_id; + WFIFOSET(char_fd,6); + + return 0; +} + +/*========================================== + * GMに変化要求 + *------------------------------------------ + */ +int chrif_changegm(int id, const char *pass, int len) +{ + if (battle_config.etc_log) + printf("chrif_changegm: account: %d, password: '%s'.\n", id, pass); + + WFIFOW(char_fd,0) = 0x2b0a; + WFIFOW(char_fd,2) = len + 8; + WFIFOL(char_fd,4) = id; + memcpy(WFIFOP(char_fd,8), pass, len); + WFIFOSET(char_fd, len + 8); + + return 0; +} + +/*========================================== + * Change Email + *------------------------------------------ + */ +int chrif_changeemail(int id, const char *actual_email, const char *new_email) +{ + if (battle_config.etc_log) + printf("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email); + + WFIFOW(char_fd,0) = 0x2b0c; + WFIFOL(char_fd,2) = id; + memcpy(WFIFOP(char_fd,6), actual_email, 40); + memcpy(WFIFOP(char_fd,46), new_email, 40); + WFIFOSET(char_fd,86); + + return 0; +} + +/*========================================== + * Send message to char-server with a character name to do some operations (by Yor) + * Used to ask Char-server about a character name to have the account number to modify account file in login-server. + * type of operation: + * 1: block + * 2: ban + * 3: unblock + * 4: unban + * 5: changesex + *------------------------------------------ + */ +int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second) +{ + WFIFOW(char_fd, 0) = 0x2b0e; + WFIFOL(char_fd, 2) = id; // account_id of who ask (for answer) -1 if nobody + memcpy(WFIFOP(char_fd,6), character_name, 24); + WFIFOW(char_fd, 30) = operation_type; // type of operation + if (operation_type == 2) { + WFIFOW(char_fd, 32) = year; + WFIFOW(char_fd, 34) = month; + WFIFOW(char_fd, 36) = day; + WFIFOW(char_fd, 38) = hour; + WFIFOW(char_fd, 40) = minute; + WFIFOW(char_fd, 42) = second; + } + printf("chrif : sended 0x2b0e\n"); + WFIFOSET(char_fd,44); + + return 0; +} + +/*========================================== + * Answer after a request about a character name to do some operations (by Yor) + * Used to answer of chrif_char_ask_name. + * type of operation: + * 1: block + * 2: ban + * 3: unblock + * 4: unban + * 5: changesex + * type of answer: + * 0: login-server resquest done + * 1: player not found + * 2: gm level too low + * 3: login-server offline + *------------------------------------------ + */ +int chrif_char_ask_name_answer(int fd) +{ + int acc; + struct map_session_data *sd; + char output[256]; + char player_name[24]; + + acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody) + memcpy(player_name, RFIFOP(fd,6), sizeof(player_name)); + player_name[sizeof(player_name)-1] = '\0'; + + sd = map_id2sd(acc); + if (acc >= 0 && sd != NULL) { + if (RFIFOW(fd, 32) == 1) // player not found + sprintf(output, "The player '%s' doesn't exist.", player_name); + else { + switch(RFIFOW(fd, 30)) { + case 1: // block + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to block the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to block the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to block the the player '%s'.", player_name); + break; + } + break; + case 2: // ban + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to ban the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to ban the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to ban the the player '%s'.", player_name); + break; + } + break; + case 3: // unblock + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to unblock the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to unblock the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to unblock the the player '%s'.", player_name); + break; + } + break; + case 4: // unban + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to unban the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to unban the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to unban the the player '%s'.", player_name); + break; + } + break; + case 5: // changesex + switch(RFIFOW(fd, 32)) { + case 0: // login-server resquest done + sprintf(output, "Login-server has been asked to change the sex of the player '%s'.", player_name); + break; + //case 1: // player not found + case 2: // gm level too low + sprintf(output, "Your GM level don't authorise you to change the sex of the player '%s'.", player_name); + break; + case 3: // login-server offline + sprintf(output, "Login-server is offline. Impossible to change the sex of the the player '%s'.", player_name); + break; + } + break; + } + } + if (output[0] != '\0') { + output[sizeof(output)-1] = '\0'; + clif_displaymessage(sd->fd, output); + } + } else + printf("chrif_char_ask_name_answer failed - player not online.\n"); + + return 0; +} + +/*========================================== + * End of GM change (@GM) (modified by Yor) + *------------------------------------------ + */ +int chrif_changedgm(int fd) +{ + int acc, level; + struct map_session_data *sd = NULL; + + acc = RFIFOL(fd,2); + level = RFIFOL(fd,6); + + sd = map_id2sd(acc); + + if (battle_config.etc_log) + printf("chrif_changedgm: account: %d, GM level 0 -> %d.\n", acc, level); + if (sd != NULL) { + if (level > 0) + clif_displaymessage(sd->fd, "GM modification success."); + else + clif_displaymessage(sd->fd, "Failure of GM modification."); + } + + return 0; +} + +/*========================================== + * 性別変化終了 (modified by Yor) + *------------------------------------------ + */ +int chrif_changedsex(int fd) +{ + int acc, sex, i; + struct map_session_data *sd; + struct pc_base_job s_class; + + acc = RFIFOL(fd,2); + sex = RFIFOL(fd,6); + if (battle_config.etc_log) + printf("chrif_changedsex %d.\n", acc); + sd = map_id2sd(acc); + if (acc > 0) { + if (sd != NULL && sd->status.sex != sex) { + s_class = pc_calc_base_job(sd->status.class); + if (sd->status.sex == 0) { + sd->status.sex = 1; + sd->sex = 1; + } else if (sd->status.sex == 1) { + sd->status.sex = 0; + sd->sex = 0; + } + // to avoid any problem with equipment and invalid sex, equipment is unequiped. + for (i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].nameid && sd->status.inventory[i].equip) + pc_unequipitem((struct map_session_data*)sd, i, 0); + } + // reset skill of some job + if (s_class.job == 19 || s_class.job == 4020 || s_class.job == 4042 || + s_class.job == 20 || s_class.job == 4021 || s_class.job == 4043) { + // remove specifical skills of classes 19, 4020 and 4042 + 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 classes 20, 4021 and 4043 + 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 (s_class.job == 20 || s_class.job == 4021 || s_class.job == 4043) + sd->status.class -= 1; + else if (s_class.job == 19 || s_class.job == 4020 || s_class.job == 4042) + sd->status.class += 1; + } + // save character + chrif_save(sd); + sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters + // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it) + clif_displaymessage(sd->fd, "Your sex has been changed (need disconexion by the server)..."); + clif_setwaitclose(sd->fd); // forced to disconnect for the change + } + } else { + if (sd != NULL) { + printf("chrif_changedsex failed.\n"); + } + } + + return 0; +} + +/*========================================== + * アカウント変数保存要求 + *------------------------------------------ + */ +int chrif_saveaccountreg2(struct map_session_data *sd) +{ + int p, j; + nullpo_retr(-1, sd); + + p = 8; + for(j = 0; j < sd->status.account_reg2_num; j++) { + struct global_reg *reg = &sd->status.account_reg2[j]; + if (reg->str[0] && reg->value != 0) { + memcpy(WFIFOP(char_fd,p), reg->str, 32); + WFIFOL(char_fd,p+32) = reg->value; + p += 36; + } + } + WFIFOW(char_fd,0) = 0x2b10; + WFIFOW(char_fd,2) = p; + WFIFOL(char_fd,4) = sd->bl.id; + WFIFOSET(char_fd,p); + + return 0; +} + +/*========================================== + * アカウント変数通知 + *------------------------------------------ + */ +int chrif_accountreg2(int fd) +{ + int j, p; + struct map_session_data *sd; + + if ((sd = map_id2sd(RFIFOL(fd,4))) == NULL) + return 1; + + for(p = 8, j = 0; p < RFIFOW(fd,2) && j < ACCOUNT_REG2_NUM; p += 36, j++) { + memcpy(sd->status.account_reg2[j].str, RFIFOP(fd,p), 32); + sd->status.account_reg2[j].value = RFIFOL(fd, p + 32); + } + sd->status.account_reg2_num = j; +// printf("chrif: accountreg2\n"); + + return 0; +} + +/*========================================== + * 離婚情報同期要求 + *------------------------------------------ + */ +int chrif_divorce(int char_id, int partner_id) +{ + struct map_session_data *sd = NULL; + + if (!char_id || !partner_id) + return 0; + + nullpo_retr(0, sd = map_nick2sd(map_charid2nick(partner_id))); + if (sd->status.partner_id == char_id) { + int i; + //離婚(相方は既にキャラが消えている筈なので) + sd->status.partner_id = 0; + + //相方の結婚指輪を剥奪 + for(i = 0; i < MAX_INVENTORY; i++) + if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) + pc_delitem(sd, i, 1, 0); + } + + return 0; +} + +/*========================================== + * Disconnection of a player (account has been deleted in login-server) by [Yor] + *------------------------------------------ + */ +int chrif_accountdeletion(int fd) +{ + int acc; + struct map_session_data *sd; + + acc = RFIFOL(fd,2); + if (battle_config.etc_log) + printf("chrif_accountdeletion %d.\n", acc); + sd = map_id2sd(acc); + if (acc > 0) { + if (sd != NULL) { + sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters + clif_displaymessage(sd->fd, "Your account has been deleted (disconnexion)..."); + clif_setwaitclose(sd->fd); // forced to disconnect for the change + } + } else { + if (sd != NULL) + printf("chrif_accountdeletion failed - player not online.\n"); + } + + return 0; +} + +/*========================================== + * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor] + *------------------------------------------ + */ +int chrif_accountban(int fd) +{ + int acc; + struct map_session_data *sd; + + acc = RFIFOL(fd,2); + if (battle_config.etc_log) + printf("chrif_accountban %d.\n", acc); + sd = map_id2sd(acc); + if (acc > 0) { + if (sd != NULL) { + sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters + if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban + switch (RFIFOL(fd,7)) { // status or final date of a banishment + case 1: // 0 = Unregistered ID + clif_displaymessage(sd->fd, "Your account has 'Unregistered'."); + break; + case 2: // 1 = Incorrect Password + clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'..."); + break; + case 3: // 2 = This ID is expired + clif_displaymessage(sd->fd, "Your account has expired."); + break; + case 4: // 3 = Rejected from Server + clif_displaymessage(sd->fd, "Your account has been rejected from server."); + break; + case 5: // 4 = You have been blocked by the GM Team + clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team."); + break; + case 6: // 5 = Your Game's EXE file is not the latest version + clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version."); + break; + case 7: // 6 = Your are Prohibited to log in until %s + clif_displaymessage(sd->fd, "Your account has been prohibited to log in."); + break; + case 8: // 7 = Server is jammed due to over populated + clif_displaymessage(sd->fd, "Server is jammed due to over populated."); + break; + case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this) + clif_displaymessage(sd->fd, "Your account has not more authorised."); + break; + case 100: // 99 = This ID has been totally erased + clif_displaymessage(sd->fd, "Your account has been totally erased."); + break; + default: + clif_displaymessage(sd->fd, "Your account has not more authorised."); + break; + } + } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban + time_t timestamp; + char tmpstr[2048]; + timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment + strcpy(tmpstr, "Your account has been banished until "); + strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(×tamp)); + clif_displaymessage(sd->fd, tmpstr); + } + clif_setwaitclose(sd->fd); // forced to disconnect for the change + } + } else { + if (sd != NULL) + printf("chrif_accountban failed - player not online.\n"); + } + + return 0; +} + +/*========================================== + * Receiving GM accounts and their levels from char-server by [Yor] + *------------------------------------------ + */ +int chrif_recvgmaccounts(int fd) +{ + printf("From login-server: receiving of %d GM accounts information.\n", pc_read_gm_account(fd)); + + return 0; +} + +/*========================================== + * Request to reload GM accounts and their levels: send to char-server by [Yor] + *------------------------------------------ + */ +int chrif_reloadGMdb(void) +{ + + WFIFOW(char_fd,0) = 0x2af7; + WFIFOSET(char_fd, 2); + + return 0; +} + +/*========================================== + * Send rates and motd to char server [Wizputer] + *------------------------------------------ + */ + int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate) +{ + char buf[256]; + FILE *fp; + int i; + + WFIFOW(char_fd,0) = 0x2b16; + WFIFOW(char_fd,2) = base_rate; + WFIFOW(char_fd,4) = job_rate; + WFIFOW(char_fd,6) = drop_rate; + + if ((fp = fopen(motd_txt, "r")) != NULL) { + if (fgets(buf, 250, fp) != NULL) { + for(i = 0; buf[i]; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = 0; + break; + } + } + WFIFOW(char_fd,8) = sizeof(buf) + 10; + memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); + } + fclose(fp); + } else { + WFIFOW(char_fd,8) = sizeof(buf) + 10; + memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); + } + WFIFOSET(char_fd,WFIFOW(char_fd,8)); + + return 0; +} + +/*========================================= + * Tell char-server charcter disconnected [Wizputer] + *----------------------------------------- + */ + +int chrif_char_offline(struct map_session_data *sd) +{ + if (char_fd < 0) + return -1; + + WFIFOW(char_fd,0) = 0x2b17; + WFIFOL(char_fd,2) = sd->status.char_id; + WFIFOSET(char_fd,6); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +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) { + printf("Map-server can't connect to char-server (connection #%d).\n", fd); + char_fd = -1; + } + close(fd); + delete_session(fd); + return 0; + } + + while (RFIFOREST(fd) >= 2) { + cmd = RFIFOW(fd,0); + if (cmd < 0x2af8 || cmd >= 0x2af8 + (sizeof(packet_len_table) / sizeof(packet_len_table[0])) || + packet_len_table[cmd-0x2af8] == 0) { + + int r = intif_parse(fd); // intifに渡す + + if (r == 1) continue; // intifで処理した + if (r == 2) return 0; // intifで処理したが、データが足りない + + session[fd]->eof = 1; + 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 (RFIFOREST(fd) < packet_len) + return 0; + + switch(cmd) { + case 0x2af9: chrif_connectack(fd); break; + case 0x2afb: chrif_sendmapack(fd); break; + case 0x2afd: pc_authok(RFIFOL(fd,4), RFIFOL(fd,8), (time_t)RFIFOL(fd,12), (struct mmo_charstatus*)RFIFOP(fd,16)); break; + case 0x2afe: pc_authfail(RFIFOL(fd,2)); break; + case 0x2b00: map_setusers(RFIFOL(fd,2)); break; + case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break; + case 0x2b04: chrif_recvmap(fd); break; + case 0x2b06: chrif_changemapserverack(fd); break; + case 0x2b09: map_addchariddb(RFIFOL(fd,2), RFIFOP(fd,6)); break; + case 0x2b0b: chrif_changedgm(fd); break; + case 0x2b0d: chrif_changedsex(fd); break; + case 0x2b0f: chrif_char_ask_name_answer(fd); break; + case 0x2b11: chrif_accountreg2(fd); break; + case 0x2b12: chrif_divorce(RFIFOL(fd,2), RFIFOL(fd,6)); break; + case 0x2b13: chrif_accountdeletion(fd); break; + case 0x2b14: chrif_accountban(fd); break; + case 0x2b15: chrif_recvgmaccounts(fd); break; + + default: + if (battle_config.error_log) + printf("chrif_parse : unknown packet %d %d\n", fd, RFIFOW(fd,0)); + session[fd]->eof = 1; + return 0; + } + RFIFOSKIP(fd, packet_len); + } + + return 0; +} + +/*========================================== + * timer関数 + * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る + *------------------------------------------ + */ +int send_users_tochar(int tid, unsigned int tick, int id, int data) { + int users = 0, i; + struct map_session_data *sd; + + if (char_fd <= 0 || session[char_fd] == NULL) + return 0; + + WFIFOW(char_fd,0) = 0x2aff; + for (i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data) && sd->state.auth && + !((battle_config.hide_GM_session || (sd->status.option & OPTION_HIDE)) && pc_isGM(sd))) { + WFIFOL(char_fd,6+4*users) = sd->status.char_id; + users++; + } + } + WFIFOW(char_fd,2) = 6 + 4 * users; + WFIFOW(char_fd,4) = users; + WFIFOSET(char_fd,6+4*users); + + return 0; +} + +/*========================================== + * timer関数 + * char鯖との接続を確認し、もし切れていたら再度接続する + *------------------------------------------ + */ +int check_connect_char_server(int tid, unsigned int tick, int id, int data) { + if (char_fd <= 0 || session[char_fd] == NULL) { + printf("Attempt to connect to char-server...\n"); + chrif_state = 0; + char_fd = make_connection(char_ip, char_port); + session[char_fd]->func_parse = chrif_parse; + realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); + + chrif_connect(char_fd); +#ifndef TXT_ONLY + srvinfo = 0; + } else { + 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 */ + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int do_init_chrif(void) { + add_timer_func_list(check_connect_char_server, "check_connect_char_server"); + add_timer_func_list(send_users_tochar, "send_users_tochar"); + add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000); + add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, 5 * 1000); + + return 0; +} diff --git a/src/map/chrif.h b/src/map/chrif.h new file mode 100644 index 0000000..19d725d --- /dev/null +++ b/src/map/chrif.h @@ -0,0 +1,29 @@ +// $Id: chrif.h,v 1.3 2004/09/25 11:39:17 MouseJstr Exp $ +#ifndef _CHRIF_H_ +#define _CHRIF_H_ + +void chrif_setuserid(char*); +void chrif_setpasswd(char*); +void chrif_setip(char*); +void chrif_setport(int); + +int chrif_isconnect(void); + +int chrif_authreq(struct map_session_data *); +int chrif_save(struct map_session_data*); +int chrif_charselectreq(struct map_session_data *); + +int chrif_changemapserver(struct map_session_data *sd,char *name,int x,int y,int ip,short port); + +int chrif_searchcharid(int char_id); +int chrif_changegm(int id,const char *pass,int len); +int chrif_changeemail(int id, const char *actual_email, const char *new_email); +int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second); +int chrif_saveaccountreg2(struct map_session_data *sd); +int chrif_reloadGMdb(void); +int chrif_ragsrvinfo(int base_rate,int job_rate, int drop_rate); +int chrif_char_offline(struct map_session_data *sd); + +int do_init_chrif(void); + +#endif diff --git a/src/map/clif.c b/src/map/clif.c new file mode 100644 index 0000000..1f320b2 --- /dev/null +++ b/src/map/clif.c @@ -0,0 +1,9826 @@ +// $Id: clif.c 164 2004-10-01 16:46:58Z $ + +#define DUMP_UNKNOWN_PACKET 1 + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <sys/types.h> +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <time.h> + +#include "socket.h" +#include "timer.h" +#include "malloc.h" +#include "version.h" +#include "nullpo.h" + +#include "map.h" +#include "chrif.h" +#include "clif.h" +#include "pc.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 "intif.h" +#include "battle.h" +#include "mob.h" +#include "party.h" +#include "guild.h" +#include "vending.h" +#include "pet.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define STATE_BLIND 0x10 + +static const int packet_len_table[0x220] = { + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +//#0x0040 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2, +#if PACKETVER < 2 + 3, 28, 19, 11, 3, -1, 9, 5, 52, 51, 56, 58, 41, 2, 6, 6, +#else // 78-7b 亀島以降 lv99エフェクト用 + 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, +#endif +//#0x0080 + 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, // 0x8b unknown... size 2 or 23? + 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6, + 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6, + 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3, +//#0x00C0 + 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27, + 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1, + 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2, + 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10, + +//#0x0100 + 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1, + 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16, + 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1, + 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26, +//#0x0140 + 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6, + 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42, + -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182, + 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1, +//#0x0180 + 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6, +#if PACKETVER < 1 + 90, 86, 24, 6, 30,102, 8, 4, 8, 4, 14, 10, -1, 6, 2, 6, +#else // 196 comodo以降 状態表示アイコン用 + 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6, +#endif + 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, + 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3, +//#0x01C0, Set 0x1d5=-1 + 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 30, 6, 28, + 8, 14, 10, 35, 6, -1, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6, + 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1, + -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10, +//#0x200 + 26, -1, 26, 10, 18, 26, 11, 34, 14, 36, 10, 19, 0, -1, 24, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// size list for each packet version after packet version 4. +static int packet_size_table[6][0x220]; + +// local define +enum { + ALL_CLIENT, + ALL_SAMEMAP, + AREA, + AREA_WOS, + AREA_WOC, + AREA_WOSC, + AREA_CHAT_WOC, + CHAT, + CHAT_WOS, + PARTY, + PARTY_WOS, + PARTY_SAMEMAP, + PARTY_SAMEMAP_WOS, + PARTY_AREA, + PARTY_AREA_WOS, + GUILD, + GUILD_WOS, + GUILD_SAMEMAP, // [Valaris] + GUILD_SAMEMAP_WOS, + GUILD_AREA, + GUILD_AREA_WOS, // end additions [Valaris] + SELF +}; + +#define WBUFPOS(p,pos,x,y) { unsigned char *__p = (p); __p+=(pos); __p[0] = (x)>>2; __p[1] = ((x)<<6) | (((y)>>4)&0x3f); __p[2] = (y)<<4; } +#define WBUFPOS2(p,pos,x0,y0,x1,y1) { unsigned char *__p = (p); __p+=(pos); __p[0] = (x0)>>2; __p[1] = ((x0)<<6) | (((y0)>>4)&0x3f); __p[2] = ((y0)<<4) | (((x1)>>6)&0x0f); __p[3]=((x1)<<2) | (((y1)>>8)&0x03); __p[4]=(y1); } + +#define WFIFOPOS(fd,pos,x,y) { WBUFPOS (WFIFOP(fd,pos),0,x,y); } +#define WFIFOPOS2(fd,pos,x0,y0,x1,y1) { WBUFPOS2(WFIFOP(fd,pos),0,x0,y0,x1,y1); } + +static char map_ip_str[16]; +static in_addr_t map_ip; +static int map_port = 5121; +int map_fd; +char talkie_mes[80]; + +/*========================================== + * map鯖のip設定 + *------------------------------------------ + */ +void clif_setip(char *ip) +{ + memcpy(map_ip_str, ip, 16); + map_ip = inet_addr(map_ip_str); +} + +/*========================================== + * map鯖のport設定 + *------------------------------------------ + */ +void clif_setport(int port) +{ + map_port = port; +} + +/*========================================== + * map鯖のip読み出し + *------------------------------------------ + */ +in_addr_t clif_getip(void) +{ + return map_ip; +} + +/*========================================== + * map鯖のport読み出し + *------------------------------------------ + */ +int clif_getport(void) +{ + return map_port; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_countusers(void) +{ + int users = 0, i; + struct map_session_data *sd; + + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data) && sd && sd->state.auth && + !(battle_config.hide_GM_session && pc_isGM(sd))) + users++; + } + return users; +} + +/*========================================== + * 全てのclientに対してfunc()実行 + *------------------------------------------ + */ +int clif_foreachclient(int (*func)(struct map_session_data*, va_list),...) +{ + int i; + va_list ap; + struct map_session_data *sd; + + va_start(ap,func); + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data) && sd && sd->state.auth) + func(sd, ap); + } + va_end(ap); + return 0; +} + +/*========================================== + * clif_sendでAREA*指定時用 + *------------------------------------------ + */ +int clif_send_sub(struct block_list *bl, va_list ap) +{ + unsigned char *buf; + int len; + struct block_list *src_bl; + int type; + struct map_session_data *sd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, sd = (struct map_session_data *)bl); + + buf = va_arg(ap,unsigned char*); + len = va_arg(ap,int); + nullpo_retr(0, src_bl = va_arg(ap,struct block_list*)); + type = va_arg(ap,int); + + switch(type) { + case AREA_WOS: + if (bl && bl == src_bl) + return 0; + break; + case AREA_WOC: + if ((sd && sd->chatID) || (bl && bl == src_bl)) + return 0; + break; + case AREA_WOSC: + if ((sd) && sd->chatID && sd->chatID == ((struct map_session_data*)src_bl)->chatID) + return 0; + break; + } + + if (sd) { + 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_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(sd->fd,0), buf, len); + WFIFOSET(sd->fd,len); + } + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_send(unsigned char *buf, int len, struct block_list *bl, int type) { + int i; + struct map_session_data *sd; + struct chat_data *cd; + struct party *p = NULL; + struct guild *g = NULL; + int x0 = 0, x1 = 0, y0 = 0, y1 = 0; + + if (type != ALL_CLIENT) { + nullpo_retr(0, bl); + } + + switch(type) { + case ALL_CLIENT: // 全クライアントに送信 + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data) != NULL && sd->state.auth) { + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(i,0), buf, len); + WFIFOSET(i,len); + } + } + } + break; + case ALL_SAMEMAP: // 同じマップの全クライアントに送信 + for(i = 0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data) != NULL && sd->state.auth && sd->bl.m == bl->m) { + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + 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: + cd = (struct chat_data*)bl; + if (bl->type == BL_PC) { + sd = (struct map_session_data*)bl; + cd = (struct chat_data*)map_id2bl(sd->chatID); + } else if (bl->type != BL_CHAT) + break; + if (cd == NULL) + break; + for(i = 0; i < cd->users; i++) { + if (type == CHAT_WOS && cd->usersd[i] == (struct map_session_data*)bl) + continue; + if (packet_size_table[cd->usersd[i]->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(cd->usersd[i]->fd,0), buf, len); + WFIFOSET(cd->usersd[i]->fd,len); + } + } + break; + + case PARTY_AREA: // 同じ画面内の全パーティーメンバに送信 + case PARTY_AREA_WOS: // 自分以外の同じ画面内の全パーティーメンバに送信 + x0 = bl->x - AREA_SIZE; + y0 = bl->y - AREA_SIZE; + x1 = bl->x + AREA_SIZE; + y1 = bl->y + AREA_SIZE; + case PARTY: // 全パーティーメンバに送信 + case PARTY_WOS: // 自分以外の全パーティーメンバに送信 + case PARTY_SAMEMAP: // 同じマップの全パーティーメンバに送信 + case PARTY_SAMEMAP_WOS: // 自分以外の同じマップの全パーティーメンバに送信 + if (bl->type == BL_PC) { + sd = (struct map_session_data *)bl; + if (sd->partyspy > 0) { + p = party_search(sd->partyspy); + } else { + if (sd->status.party_id > 0) + p = party_search(sd->status.party_id); + } + } + if (p) { + for(i=0;i<MAX_PARTY;i++){ + if ((sd = p->member[i].sd) != NULL) { + if (sd->bl.id == bl->id && (type == PARTY_WOS || + type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS)) + continue; + if (type != PARTY && type != PARTY_WOS && bl->m != sd->bl.m) // マップチェック + continue; + if ((type == PARTY_AREA || type == PARTY_AREA_WOS) && + (sd->bl.x < x0 || sd->bl.y < y0 || + sd->bl.x > x1 || sd->bl.y > y1)) + continue; + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(sd->fd,0), buf, len); + WFIFOSET(sd->fd,len); + } +// if(battle_config.etc_log) +// printf("send party %d %d %d\n",p->party_id,i,flag) + + } + } + for (i = 0; i < fd_max; i++){ + if (session[i] && (sd = session[i]->session_data) != NULL && sd->state.auth) { + if (sd->partyspy == p->party_id) { + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(sd->fd,0), buf, len); + WFIFOSET(sd->fd,len); + } + } + } + } + } + break; + case SELF: + sd = (struct map_session_data *)bl; + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(sd->fd,0), buf, len); + WFIFOSET(sd->fd,len); + } + break; + +/* New definitions for guilds [Valaris] */ + + case GUILD_AREA: + case GUILD_AREA_WOS: + x0 = bl->x - AREA_SIZE; + y0 = bl->y - AREA_SIZE; + x1 = bl->x + AREA_SIZE; + y1 = bl->y + AREA_SIZE; + case GUILD: + case GUILD_WOS: + if (bl && bl->type == BL_PC) { // guildspy [Syrus22] + sd = (struct map_session_data *)bl; + if (sd->guildspy > 0) { + g = guild_search(sd->guildspy); + } else { + if (sd->status.guild_id > 0) + g = guild_search(sd->status.guild_id); + } + } + if (g) { + for(i = 0; i < g->max_member; i++) { + if ((sd = g->member[i].sd) != NULL) { + if (type == GUILD_WOS && sd->bl.id == bl->id) + continue; + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(sd->fd,0), buf, len); + WFIFOSET(sd->fd,len); + } + } + } + for (i = 0; i < fd_max; i++){ + if (session[i] && (sd = session[i]->session_data) != NULL && sd->state.auth) { + if (sd->guildspy == g->guild_id) { + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(sd->fd,0), buf, len); + WFIFOSET(sd->fd,len); + } + } + } + } + } + break; + case GUILD_SAMEMAP: + case GUILD_SAMEMAP_WOS: + if (bl->type == BL_PC) { + sd = (struct map_session_data *)bl; + if (sd->status.guild_id > 0) + g = guild_search(sd->status.guild_id); + } + if (g) { + for(i = 0; i < g->max_member; i++) { + if ((sd = g->member[i].sd) != NULL) { + if (sd->bl.id == bl->id && (type == GUILD_WOS || + type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS)) + continue; + if (type != GUILD && type != GUILD_WOS && bl->m != sd->bl.m) // マップチェック + continue; + if ((type == GUILD_AREA || type == GUILD_AREA_WOS) && + (sd->bl.x < x0 || sd->bl.y < y0 || + sd->bl.x > x1 || sd->bl.y > y1)) + continue; + if (packet_size_table[sd->packet_ver-5][RBUFW(buf,0)]) { // packet must exist for the client version + memcpy(WFIFOP(sd->fd,0), buf, len); + WFIFOSET(sd->fd,len); + } + } + } + } + break; +/* End [Valaris] */ + + default: + if (battle_config.error_log) + printf("clif_send まだ作ってないよー\n"); + return -1; + } + + return 0; +} + +// +// パケット作って送信 +// +/*========================================== + * + *------------------------------------------ + */ +int clif_authok(struct map_session_data *sd) { + int fd; + + nullpo_retr(0, sd); + + if (!sd) + return 0; + + if (!sd->fd) + return 0; + + fd = sd->fd; + + WFIFOW(fd, 0) = 0x73; + WFIFOL(fd, 2) = gettick(); + WFIFOPOS(fd, 6, sd->bl.x, sd->bl.y); + WFIFOB(fd, 9) = 5; + WFIFOB(fd,10) = 5; + WFIFOSET(fd,packet_len_table[0x73]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_authfail_fd(int fd, int type) { + if (!fd || !session[fd]) + return 0; + + WFIFOW(fd,0) = 0x81; + WFIFOL(fd,2) = type; + WFIFOSET(fd,packet_len_table[0x81]); + + clif_setwaitclose(fd); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_charselectok(int id) { + struct map_session_data *sd; + int fd; + + if ((sd = map_id2sd(id)) == NULL) + return 1; + + if (!sd->fd) + return 1; + + fd = sd->fd; + WFIFOW(fd,0) = 0xb3; + WFIFOB(fd,2) = 1; + WFIFOSET(fd,packet_len_table[0xb3]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_set009e(struct flooritem_data *fitem,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) { + char buf[64]; + + nullpo_retr(0, fitem); + + if (fitem->item_data.nameid <= 0) + return 0; + clif_set009e(fitem, buf); + clif_send(buf, packet_len_table[0x9e], &fitem->bl, AREA); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_clearflooritem(struct flooritem_data *fitem, int fd) { + unsigned char buf[16]; + + nullpo_retr(0, fitem); + + WBUFW(buf,0) = 0xa1; + WBUFL(buf,2) = fitem->bl.id; + + if (fd == 0) { + clif_send(buf, packet_len_table[0xa1], &fitem->bl, AREA); + } else { + memcpy(WFIFOP(fd,0), buf, 6); + WFIFOSET(fd,packet_len_table[0xa1]); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_clearchar(struct block_list *bl, int type) { + unsigned char buf[16]; + + nullpo_retr(0, bl); + + WBUFW(buf,0) = 0x80; + WBUFL(buf,2) = bl->id; + if (type == 9) { + WBUFB(buf,6) = 0; + clif_send(buf, packet_len_table[0x80], bl, AREA); + } else { + WBUFB(buf,6) = type; + clif_send(buf, packet_len_table[0x80], bl, type == 1 ? AREA : AREA_WOS); + } + + return 0; +} + +static 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); + map_freeblock(bl); + + return 0; +} + +int clif_clearchar_delay(unsigned int tick, struct block_list *bl, int type) { + struct block_list *tmpbl = calloc(sizeof(struct block_list), 1); + if (tmpbl == NULL) { + printf("clif_clearchar_delay: out of memory !\n"); + exit(1); + } + memcpy(tmpbl, bl, sizeof(struct block_list)); + add_timer(tick, clif_clearchar_delay_sub, (int)tmpbl, type); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_clearchar_id(int id, int type, int fd) { + unsigned char buf[16]; + + WBUFW(buf,0) = 0x80; + WBUFL(buf,2) = id; + WBUFB(buf,6) = type; + memcpy(WFIFOP(fd,0), buf, 7); + WFIFOSET(fd, packet_len_table[0x80]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_set0078(struct map_session_data *sd, unsigned char *buf) { + int level=0; + + nullpo_retr(0, sd); + + if (sd->disguise > 23 && sd->disguise < 4001) { // mob disguises [Valaris] + WBUFW(buf,0) = 0x78; + WBUFL(buf,2) = sd->bl.id; + WBUFW(buf,6) = battle_get_speed(&sd->bl); + WBUFW(buf,8) = sd->opt1; + WBUFW(buf,10) = sd->opt2; + WBUFW(buf,12) = sd->status.option; + WBUFW(buf,14) = sd->disguise; + WBUFW(buf,42) = 0; + WBUFB(buf,44) = 0; + WBUFPOS(buf, 46, sd->bl.x, sd->bl.y); + WBUFB(buf,48) |= sd->dir & 0x0f; + WBUFB(buf,49) = 5; + WBUFB(buf,50) = 5; + WBUFB(buf,51) = 0; + WBUFW(buf,52) = ((level = battle_get_lv(&sd->bl)) > battle_config.max_lv) ? battle_config.max_lv : level; + + return packet_len_table[0x78]; + } + +#if PACKETVER < 4 + 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)= sd->status.option; + WBUFW(buf,14)= sd->view_class; + WBUFW(buf,16)= sd->status.hair; + if (sd->view_class != 22) + 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)=(sd->status.base_level>battle_config.max_lv)?battle_config.max_lv:sd->status.base_level; + + return packet_len_table[0x78]; +#else + WBUFW(buf,0) = 0x1d8; + WBUFL(buf,2) = sd->bl.id; + WBUFW(buf,6) = sd->speed; + WBUFW(buf,8) = sd->opt1; + WBUFW(buf,10) = sd->opt2; + WBUFW(buf,12) = sd->status.option; + WBUFW(buf,14) = sd->view_class; + WBUFW(buf,16) = sd->status.hair; + if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != 22) { + if (sd->inventory_data[sd->equip_index[9]]->view_id > 0) + WBUFW(buf,18) = sd->inventory_data[sd->equip_index[9]]->view_id; + else + WBUFW(buf,18) = sd->status.inventory[sd->equip_index[9]].nameid; + } else + WBUFW(buf,18) = 0; + if (sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] && sd->view_class != 22) { + if (sd->inventory_data[sd->equip_index[8]]->view_id > 0) + WBUFW(buf,20) = sd->inventory_data[sd->equip_index[8]]->view_id; + else + WBUFW(buf,20) = sd->status.inventory[sd->equip_index[8]].nameid; + } else + WBUFW(buf,20) = 0; + WBUFW(buf,22)=sd->status.head_bottom; + WBUFW(buf,24)=sd->status.head_top; + WBUFW(buf,26)=sd->status.head_mid; + WBUFW(buf,28)=sd->status.hair_color; + WBUFW(buf,30)=sd->status.clothes_color; + WBUFW(buf,32)=sd->head_dir; + WBUFL(buf,34)=sd->status.guild_id; + WBUFW(buf,38)=sd->guild_emblem_id; + WBUFW(buf,40)=sd->status.manner; + WBUFW(buf,42)=sd->opt3; + WBUFB(buf,44)=sd->status.karma; + WBUFB(buf,45)=sd->sex; + WBUFPOS(buf,46,sd->bl.x,sd->bl.y); + WBUFB(buf,48)|=sd->dir&0x0f; + WBUFB(buf,49)=5; + WBUFB(buf,50)=5; + WBUFB(buf,51)=sd->state.dead_sit; + WBUFW(buf,52)=((level = battle_get_lv(&sd->bl)) > battle_config.max_lv) ? battle_config.max_lv : level; + + return packet_len_table[0x1d8]; +#endif +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_set007b(struct map_session_data *sd,unsigned char *buf) { + int level=0; + + nullpo_retr(0, sd); + + if (sd->disguise > 23 && sd->disguise < 4001) { // mob disguises [Valaris] + WBUFW(buf,0)=0x7b; + WBUFL(buf,2)=sd->bl.id; + WBUFW(buf,6)=battle_get_speed(&sd->bl); + WBUFW(buf,8)=sd->opt1; + WBUFW(buf,10)=sd->opt2; + WBUFW(buf,12)=sd->status.option; + WBUFW(buf,14)=sd->disguise; + WBUFL(buf,22)=gettick(); + WBUFW(buf,46)=0; + WBUFB(buf,48)=0; + WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y); + WBUFB(buf,55)=0; + WBUFB(buf,56)=5; + WBUFB(buf,57)=5; + WBUFW(buf,58)=((level = battle_get_lv(&sd->bl))>battle_config.max_lv)? battle_config.max_lv:level; + + return packet_len_table[0x7b]; + } + +#if PACKETVER < 4 + 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)=sd->status.option; + WBUFW(buf,14)=sd->view_class; + WBUFW(buf,16)=sd->status.hair; + if(sd->view_class != 22) + 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->status.manner; + 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)=0; + WBUFB(buf,56)=5; + WBUFB(buf,57)=5; + WBUFW(buf,58)=(sd->status.base_level>battle_config.max_lv)?battle_config.max_lv:sd->status.base_level; + + return packet_len_table[0x7b]; +#else + WBUFW(buf,0)=0x1da; + WBUFL(buf,2)=sd->bl.id; + WBUFW(buf,6)=sd->speed; + WBUFW(buf,8)=sd->opt1; + WBUFW(buf,10)=sd->opt2; + WBUFW(buf,12)=sd->status.option; + WBUFW(buf,14)=sd->view_class; + WBUFW(buf,16)=sd->status.hair; + if(sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]] && sd->view_class != 22) { + if(sd->inventory_data[sd->equip_index[9]]->view_id > 0) + WBUFW(buf,18)=sd->inventory_data[sd->equip_index[9]]->view_id; + else + WBUFW(buf,18)=sd->status.inventory[sd->equip_index[9]].nameid; + } + else + WBUFW(buf,18)=0; + if(sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] && sd->view_class != 22) { + if(sd->inventory_data[sd->equip_index[8]]->view_id > 0) + WBUFW(buf,20)=sd->inventory_data[sd->equip_index[8]]->view_id; + else + WBUFW(buf,20)=sd->status.inventory[sd->equip_index[8]].nameid; + } + else + WBUFW(buf,20)=0; + WBUFW(buf,22)=sd->status.head_bottom; + WBUFL(buf,24)=gettick(); + WBUFW(buf,28)=sd->status.head_top; + WBUFW(buf,30)=sd->status.head_mid; + WBUFW(buf,32)=sd->status.hair_color; + WBUFW(buf,34)=sd->status.clothes_color; + WBUFW(buf,36)=sd->head_dir; + WBUFL(buf,38)=sd->status.guild_id; + WBUFW(buf,42)=sd->guild_emblem_id; + WBUFW(buf,44)=sd->status.manner; + WBUFW(buf,46)=sd->opt3; + WBUFB(buf,48)=sd->status.karma; + WBUFB(buf,49)=sd->sex; + WBUFPOS2(buf,50,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y); + WBUFB(buf,55)=0; + WBUFB(buf,56)=5; + WBUFB(buf,57)=5; + WBUFW(buf,58)=(sd->status.base_level>battle_config.max_lv)?battle_config.max_lv:sd->status.base_level; + + return packet_len_table[0x1da]; +#endif +} + +/*========================================== + * クラスチェンジ typeはMobの場合は1で他は0? + *------------------------------------------ + */ +int clif_class_change(struct block_list *bl,int class,int type) +{ + 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) { + char buf[16]; + int view = mob_get_viewclass(class); + + nullpo_retr(0, md); + + if(view >= MAX_PC_CLASS) { + WBUFW(buf,0)=0x1b0; + WBUFL(buf,2)=md->bl.id; + WBUFB(buf,6)=1; + WBUFL(buf,7)=view; + + clif_send(buf,packet_len_table[0x1b0],&md->bl,AREA); + } + return 0; +} +// mob equipment [Valaris] + +int clif_mob_equip(struct mob_data *md, int nameid) { + unsigned char buf[16]; + + nullpo_retr(0, md); + + memset(buf,0,packet_len_table[0x1a4]); + + WBUFW(buf,0)=0x1a4; + WBUFB(buf,2)=3; + WBUFL(buf,3)=md->bl.id; + WBUFL(buf,7)=nameid; + + clif_send(buf,packet_len_table[0x1a4],&md->bl,AREA); + + return 0; +} + +/*========================================== + * MOB表示1 + *------------------------------------------ + */ +static int clif_mob0078(struct mob_data *md, unsigned char *buf) +{ + int level; + + memset(buf,0,packet_len_table[0x78]); + + nullpo_retr(0, md); + + WBUFW(buf,0)=0x78; + WBUFL(buf,2)=md->bl.id; + WBUFW(buf,6)=battle_get_speed(&md->bl); + WBUFW(buf,8)=md->opt1; + WBUFW(buf,10)=md->opt2; + WBUFW(buf,12)=md->option; + WBUFW(buf,14)=mob_get_viewclass(md->class); + if((mob_get_viewclass(md->class) <= 23) || (mob_get_viewclass(md->class) == 812) || (mob_get_viewclass(md->class) >= 4001)) { + WBUFW(buf,12)|=mob_db[md->class].option; + 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); //Add for player monster dye - Valaris + WBUFB(buf,45)=mob_get_sex(md->class); + } + + if (md->class >= 1285 && md->class <= 1287) { // Added guardian emblems [Valaris] + struct guild *g; + struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name); + if (gc && gc->guild_id > 0) { + g=guild_search(gc->guild_id); + if (g) { + WBUFL(buf,26)=gc->guild_id; + WBUFL(buf,22)=g->emblem_id; + } + } + } // End addition + + WBUFPOS(buf,46,md->bl.x,md->bl.y); + WBUFB(buf,48)|=md->dir&0x0f; + WBUFB(buf,49)=5; + WBUFB(buf,50)=5; + WBUFW(buf,52)=((level = battle_get_lv(&md->bl))>battle_config.max_lv)? battle_config.max_lv:level; + + return packet_len_table[0x78]; +} + +/*========================================== + * MOB表示2 + *------------------------------------------ + */ +static int clif_mob007b(struct mob_data *md, unsigned char *buf) { + int level; + + memset(buf,0,packet_len_table[0x7b]); + + nullpo_retr(0, md); + + WBUFW(buf,0)=0x7b; + WBUFL(buf,2)=md->bl.id; + WBUFW(buf,6)=battle_get_speed(&md->bl); + WBUFW(buf,8)=md->opt1; + WBUFW(buf,10)=md->opt2; + WBUFW(buf,12)=md->option; + WBUFW(buf,14)=mob_get_viewclass(md->class); + if ((mob_get_viewclass(md->class) < 24) || (mob_get_viewclass(md->class) > 4000)) { + WBUFW(buf,12)|=mob_db[md->class].option; + 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); //Add for player monster dye - Valaris + WBUFB(buf,49)=mob_get_sex(md->class); + } else + WBUFL(buf,22)=gettick(); + + if(md->class >= 1285 && md->class <= 1287) { // Added guardian emblems [Valaris] + struct guild *g; + struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name); + if(gc && gc->guild_id > 0){ + g=guild_search(gc->guild_id); + if(g) { + WBUFL(buf,28)=gc->guild_id; + WBUFL(buf,24)=g->emblem_id; + } + } + } // End addition + + WBUFPOS2(buf,50,md->bl.x,md->bl.y,md->to_x,md->to_y); + WBUFB(buf,56)=5; + WBUFB(buf,57)=5; + WBUFW(buf,58)=((level = battle_get_lv(&md->bl))>battle_config.max_lv)? battle_config.max_lv:level; + + return packet_len_table[0x7b]; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_npc0078(struct npc_data *nd, unsigned char *buf) { + struct guild *g; + + nullpo_retr(0, nd); + + memset(buf,0,packet_len_table[0x78]); + + WBUFW(buf,0)=0x78; + WBUFL(buf,2)=nd->bl.id; + WBUFW(buf,6)=nd->speed; + WBUFW(buf,14)=nd->class; + if ((nd->class == 722) && (nd->u.scr.guild_id > 0) && ((g=guild_search(nd->u.scr.guild_id)) != NULL)) { + WBUFL(buf,22)=g->emblem_id; + WBUFL(buf,26)=g->guild_id; + } + WBUFPOS(buf,46,nd->bl.x,nd->bl.y); + WBUFB(buf,48)|=nd->dir&0x0f; + WBUFB(buf,49)=5; + WBUFB(buf,50)=5; + + return packet_len_table[0x78]; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_pet0078(struct pet_data *pd, unsigned char *buf) { + int view,level; + + nullpo_retr(0, pd); + + 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)=mob_get_viewclass(pd->class); + if((mob_get_viewclass(pd->class) < 24) || (mob_get_viewclass(pd->class) > 4000)) { + WBUFW(buf,12)=mob_db[pd->class].option; + 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); //Add for player pet dye - Valaris + WBUFB(buf,45)=mob_get_sex(pd->class); + } else { + WBUFW(buf,16)=0x14; + if((view = itemdb_viewid(pd->equip)) > 0) + WBUFW(buf,20)=view; + 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)=((level = battle_get_lv(&pd->bl))>battle_config.max_lv)? battle_config.max_lv:level; + + return packet_len_table[0x78]; +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_pet007b(struct pet_data *pd, unsigned char *buf) { + int view,level; + + nullpo_retr(0, pd); + + 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)=mob_get_viewclass(pd->class); + if((mob_get_viewclass(pd->class) < 24) || (mob_get_viewclass(pd->class) > 4000)) { + WBUFW(buf,12)=mob_db[pd->class].option; + 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); //Add for player pet dye - Valaris + WBUFB(buf,49)=mob_get_sex(pd->class); + } else { + WBUFW(buf,16)=0x14; + if ((view = itemdb_viewid(pd->equip)) > 0) + WBUFW(buf,20)=view; + 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,56)=0; + WBUFB(buf,57)=0; + WBUFW(buf,58)=((level = battle_get_lv(&pd->bl))>battle_config.max_lv)? battle_config.max_lv: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) { + WFIFOW(fd,0) = 0x192; + WFIFOW(fd,2) = x; + WFIFOW(fd,4) = y; + WFIFOW(fd,6) = type; + memcpy(WFIFOP(fd,8),map[m].name,16); + WFIFOSET(fd,packet_len_table[0x192]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_spawnpc(struct map_session_data *sd) { + unsigned char buf[128]; + + nullpo_retr(0, sd); + + if (sd->disguise > 23 && sd->disguise < 4001) { // mob disguises [Valaris] + clif_clearchar(&sd->bl, 9); + + memset(buf, 0, packet_len_table[0x119]); + + WBUFW(buf, 0) = 0x119; + WBUFL(buf, 2) = sd->bl.id; + WBUFW(buf, 6) = 0; + WBUFW(buf, 8) = 0; + WBUFW(buf,10) = 0x40; + WBUFB(buf,12) = 0; + + clif_send(buf, packet_len_table[0x119], &sd->bl, SELF); + + memset(buf, 0, packet_len_table[0x7c]); + + WBUFW(buf, 0) = 0x7c; + WBUFL(buf, 2) = sd->bl.id; + WBUFW(buf, 6) = sd->speed; + WBUFW(buf, 8) = sd->opt1; + WBUFW(buf,10) = sd->opt2; + WBUFW(buf,12) = sd->status.option; + WBUFW(buf,20) = sd->disguise; + WBUFPOS(buf, 36, sd->bl.x, sd->bl.y); + clif_send(buf, packet_len_table[0x7c], &sd->bl, AREA); + } + + clif_set0078(sd, buf); + +#if PACKETVER < 4 + WBUFW(buf, 0) = 0x79; + WBUFW(buf,51) = (sd->status.base_level > battle_config.max_lv) ? battle_config.max_lv : sd->status.base_level; + clif_send(buf, packet_len_table[0x79], &sd->bl, AREA_WOS); +#else + WBUFW(buf, 0) = 0x1d9; + WBUFW(buf,51) = (sd->status.base_level > battle_config.max_lv) ? battle_config.max_lv : sd->status.base_level; + clif_send(buf, packet_len_table[0x1d9], &sd->bl, AREA_WOS); +#endif + + + if (sd->spiritball > 0) + clif_spiritball(sd); + + if (sd->status.guild_id > 0) { // force display of guild emblem [Valaris] + struct guild *g = guild_search(sd->status.guild_id); + if (g) + clif_guild_emblem(sd,g); + } // end addition [Valaris] + + if (sd->status.class==13 || sd->status.class==21 || sd->status.class==4014 || sd->status.class==4022) + pc_setoption(sd,sd->status.option|0x0020); // [Valaris] + + if ((pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0) && (sd->status.class==7 || + sd->status.class==14 || sd->status.class==4008 || sd->status.class==4015)) + pc_setriding(sd); // update peco riders for people upgrading athena [Valaris] + + + if (map[sd->bl.m].flag.snow) + clif_specialeffect(&sd->bl, 162, 1); + if (map[sd->bl.m].flag.fog) + clif_specialeffect(&sd->bl, 233, 1); + if (map[sd->bl.m].flag.sakura) + clif_specialeffect(&sd->bl, 163, 1); + if (map[sd->bl.m].flag.leaves) + clif_specialeffect(&sd->bl, 333, 1); + if (map[sd->bl.m].flag.rain) + clif_specialeffect(&sd->bl, 161, 1); + + 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; + + 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; + + nullpo_retr(0, md); + + if (mob_get_viewclass(md->class) > 23 ) { + 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)=mob_get_viewclass(md->class); + WBUFPOS(buf,36,md->bl.x,md->bl.y); + clif_send(buf,packet_len_table[0x7c],&md->bl,AREA); + } + + len = clif_mob0078(md,buf); + clif_send(buf,len,&md->bl,AREA); + + if (mob_get_equip(md->class) > 0) // mob equipment [Valaris] + clif_mob_equip(md,mob_get_equip(md->class)); + + return 0; +} + +// pet + +/*========================================== + * + *------------------------------------------ + */ +int clif_spawnpet(struct pet_data *pd) +{ + unsigned char buf[64]; + int len; + + nullpo_retr(0, pd); + + if (mob_get_viewclass(pd->class) >= MAX_PC_CLASS) { + 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)=mob_get_viewclass(pd->class); + 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; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_servertick(struct map_session_data *sd) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x7f; + WFIFOL(fd,2)=sd->server_tick; + WFIFOSET(fd,packet_len_table[0x7f]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_walkok(struct map_session_data *sd) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x87; + WFIFOL(fd,2)=gettick();; + WFIFOPOS2(fd,6,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y); + WFIFOB(fd,11)=0; + WFIFOSET(fd,packet_len_table[0x87]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_movechar(struct map_session_data *sd) { + int fd; + int len; + unsigned char buf[256]; + + nullpo_retr(0, sd); + + fd = sd->fd; + + len = clif_set007b(sd, buf); + + if (sd->disguise > 23 && sd->disguise < 4001) { + clif_send(buf, len, &sd->bl, AREA); + return 0; + } else + clif_send(buf, len, &sd->bl, AREA_WOS); + + if (battle_config.save_clothcolor == 1 && sd->status.clothes_color > 0) + clif_changelook(&sd->bl, LOOK_CLOTHES_COLOR, sd->status.clothes_color); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_quitsave(int fd,struct map_session_data *sd) +{ + map_quit(sd); +} + +/*========================================== + * + *------------------------------------------ + */ +static int clif_waitclose(int tid, unsigned int tick, int id, int data) { + if (session[id]) + session[id]->eof = 1; + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_setwaitclose(int fd) { + add_timer(gettick() + 5000, clif_waitclose, fd, 0); +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_changemap(struct map_session_data *sd, char *mapname, int x, int y) { + int fd; + + nullpo_retr(0, sd); + + fd = sd->fd; + + WFIFOW(fd,0) = 0x91; + memcpy(WFIFOP(fd,2), mapname, 16); + WFIFOW(fd,18) = x; + WFIFOW(fd,20) = y; + WFIFOSET(fd, packet_len_table[0x91]); + + if (sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] + clif_spawnpc(sd); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_changemapserver(struct map_session_data *sd, char *mapname, int x, int y, int ip, int port) { + int fd; + + nullpo_retr(0, sd); + + fd = sd->fd; + WFIFOW(fd,0) = 0x92; + memcpy(WFIFOP(fd,2), mapname, 16); + WFIFOW(fd,18) = x; + WFIFOW(fd,20) = y; + WFIFOL(fd,22) = ip; + WFIFOW(fd,26) = port; + WFIFOSET(fd, packet_len_table[0x92]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_fixpos(struct block_list *bl) { + char buf[16]; + + nullpo_retr(0, bl); + + WBUFW(buf,0)=0x88; + WBUFL(buf,2)=bl->id; + WBUFW(buf,6)=bl->x; + WBUFW(buf,8)=bl->y; + + clif_send(buf, packet_len_table[0x88], bl, AREA); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_npcbuysell(struct map_session_data* sd, int id) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xc4; + WFIFOL(fd,2)=id; + WFIFOSET(fd,packet_len_table[0xc4]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_buylist(struct map_session_data *sd, struct npc_data *nd) { + struct item_data *id; + int fd,i,val; + + nullpo_retr(0, sd); + nullpo_retr(0, nd); + + fd=sd->fd; + WFIFOW(fd,0)=0xc6; + for(i=0;nd->u.shop_item[i].nameid > 0;i++){ + id = itemdb_search(nd->u.shop_item[i].nameid); + val=nd->u.shop_item[i].value; + WFIFOL(fd,4+i*11)=val; + if (!id->flag.value_notdc) + val=pc_modifybuyvalue(sd,val); + WFIFOL(fd,8+i*11)=val; + WFIFOB(fd,12+i*11)=id->type; + if (id->view_id > 0) + WFIFOW(fd,13+i*11)=id->view_id; + else + WFIFOW(fd,13+i*11)=nd->u.shop_item[i].nameid; + } + WFIFOW(fd,2)=i*11+4; + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_selllist(struct map_session_data *sd) { + int fd,i,c=0,val; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xc7; + for(i=0;i<MAX_INVENTORY;i++) { + if(sd->status.inventory[i].nameid > 0 && sd->inventory_data[i]) { + val=sd->inventory_data[i]->value_sell; + if (val < 0) + continue; + WFIFOW(fd,4+c*10)=i+2; + WFIFOL(fd,6+c*10)=val; + if (!sd->inventory_data[i]->flag.value_notoc) + val=pc_modifysellvalue(sd,val); + WFIFOL(fd,10+c*10)=val; + c++; + } + } + WFIFOW(fd,2)=c*10+4; + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_scriptmes(struct map_session_data *sd, int npcid, char *mes) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xb4; + WFIFOW(fd,2)=strlen(mes)+9; + WFIFOL(fd,4)=npcid; + strcpy(WFIFOP(fd,8),mes); + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_scriptnext(struct map_session_data *sd,int npcid) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xb5; + WFIFOL(fd,2)=npcid; + WFIFOSET(fd,packet_len_table[0xb5]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_scriptclose(struct map_session_data *sd, int npcid) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xb6; + WFIFOL(fd,2)=npcid; + WFIFOSET(fd,packet_len_table[0xb6]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_scriptmenu(struct map_session_data *sd, int npcid, char *mes) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xb7; + WFIFOW(fd,2)=strlen(mes)+8; + WFIFOL(fd,4)=npcid; + strcpy(WFIFOP(fd,8),mes); + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_scriptinput(struct map_session_data *sd, int npcid) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x142; + WFIFOL(fd,2)=npcid; + WFIFOSET(fd,packet_len_table[0x142]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_scriptinputstr(struct map_session_data *sd, int npcid) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x1d4; + WFIFOL(fd,2)=npcid; + WFIFOSET(fd,packet_len_table[0x1d4]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x144; + WFIFOL(fd,2)=npc_id; + WFIFOL(fd,6)=type; + WFIFOL(fd,10)=x; + WFIFOL(fd,14)=y; + WFIFOB(fd,18)=id; + WFIFOL(fd,19)=color; + WFIFOSET(fd,packet_len_table[0x144]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_cutin(struct map_session_data *sd, char *image, int type) { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x1b3; + memcpy(WFIFOP(fd,2),image,64); + WFIFOB(fd,66)=type; + WFIFOSET(fd,packet_len_table[0x1b3]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_additem(struct map_session_data *sd, int n, int amount, int fail) { + int fd,j; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf=WFIFOP(fd,0); + if(fail) { + WBUFW(buf,0)=0xa0; + WBUFW(buf,2)=n+2; + WBUFW(buf,4)=amount; + WBUFW(buf,6)=0; + WBUFB(buf,8)=0; + WBUFB(buf,9)=0; + WBUFB(buf,10)=0; + WBUFW(buf,11)=0; + WBUFW(buf,13)=0; + WBUFW(buf,15)=0; + WBUFW(buf,17)=0; + WBUFW(buf,19)=0; + WBUFB(buf,21)=0; + WBUFB(buf,22)=fail; + } else { + if (n<0 || n>=MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL) + return 1; + + WBUFW(buf,0)=0xa0; + WBUFW(buf,2)=n+2; + WBUFW(buf,4)=amount; + if (sd->inventory_data[n]->view_id > 0) + WBUFW(buf,6)=sd->inventory_data[n]->view_id; + else + WBUFW(buf,6)=sd->status.inventory[n].nameid; + WBUFB(buf,8)=sd->status.inventory[n].identify; + if (sd->status.inventory[n].broken==1) + WBUFB(buf,9)=1; // is weapon broken [Valaris] + else + WBUFB(buf,9)=sd->status.inventory[n].attribute; + WBUFB(buf,10)=sd->status.inventory[n].refine; + if(sd->status.inventory[n].card[0]==0x00ff || sd->status.inventory[n].card[0]==0x00fe || sd->status.inventory[n].card[0]==(short)0xff00) { + WBUFW(buf,11)=sd->status.inventory[n].card[0]; + WBUFW(buf,13)=sd->status.inventory[n].card[1]; + WBUFW(buf,15)=sd->status.inventory[n].card[2]; + WBUFW(buf,17)=sd->status.inventory[n].card[3]; + } else { + if (sd->status.inventory[n].card[0] > 0 && (j=itemdb_viewid(sd->status.inventory[n].card[0])) > 0) + WBUFW(buf,11)=j; + else + WBUFW(buf,11)=sd->status.inventory[n].card[0]; + if (sd->status.inventory[n].card[1] > 0 && (j=itemdb_viewid(sd->status.inventory[n].card[1])) > 0) + WBUFW(buf,13)=j; + else + WBUFW(buf,13)=sd->status.inventory[n].card[1]; + if (sd->status.inventory[n].card[2] > 0 && (j=itemdb_viewid(sd->status.inventory[n].card[2])) > 0) + WBUFW(buf,15)=j; + else + WBUFW(buf,15)=sd->status.inventory[n].card[2]; + if (sd->status.inventory[n].card[3] > 0 && (j=itemdb_viewid(sd->status.inventory[n].card[3])) > 0) + WBUFW(buf,17)=j; + else + WBUFW(buf,17)=sd->status.inventory[n].card[3]; + } + WBUFW(buf,19)=pc_equippoint(sd,n); + WBUFB(buf,21)=(sd->inventory_data[n]->type == 7)? 4:sd->inventory_data[n]->type; + WBUFB(buf,22)=fail; + } + + WFIFOSET(fd,packet_len_table[0xa0]); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_delitem(struct map_session_data *sd,int n,int amount) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xaf; + WFIFOW(fd,2)=n+2; + WFIFOW(fd,4)=amount; + + WFIFOSET(fd,packet_len_table[0xaf]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_itemlist(struct map_session_data *sd) +{ + int i,n,fd,arrow=-1; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf = WFIFOP(fd,0); +#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)=sd->inventory_data[i]->type; + WBUFB(buf,n*10+9)=sd->status.inventory[i].identify; + WBUFW(buf,n*10+10)=sd->status.inventory[i].amount; + if (sd->inventory_data[i]->equip == 0x8000) { + WBUFW(buf,n*10+12)=0x8000; + if (sd->status.inventory[i].equip) + arrow=i; // ついでに矢装備チェック + } else + WBUFW(buf,n*10+12)=0; + n++; + } + if (n) { + WBUFW(buf,2)=4+n*10; + WFIFOSET(fd,WFIFOW(fd,2)); + } +#else + WBUFW(buf,0)=0x1ee; + for(i=0,n=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL || itemdb_isequip2(sd->inventory_data[i])) + continue; + WBUFW(buf,n*18+4)=i+2; + if(sd->inventory_data[i]->view_id > 0) + WBUFW(buf,n*18+6)=sd->inventory_data[i]->view_id; + else + WBUFW(buf,n*18+6)=sd->status.inventory[i].nameid; + WBUFB(buf,n*18+8)=sd->inventory_data[i]->type; + WBUFB(buf,n*18+9)=sd->status.inventory[i].identify; + WBUFW(buf,n*18+10)=sd->status.inventory[i].amount; + if (sd->inventory_data[i]->equip == 0x8000) { + WBUFW(buf,n*18+12)=0x8000; + if(sd->status.inventory[i].equip) + arrow=i; // ついでに矢装備チェック + } else + WBUFW(buf,n*18+12)=0; + WBUFW(buf,n*18+14)=sd->status.inventory[i].card[0]; + WBUFW(buf,n*18+16)=sd->status.inventory[i].card[1]; + WBUFW(buf,n*18+18)=sd->status.inventory[i].card[2]; + WBUFW(buf,n*18+20)=sd->status.inventory[i].card[3]; + n++; + } + if (n) { + WBUFW(buf,2)=4+n*18; + WFIFOSET(fd,WFIFOW(fd,2)); + } +#endif + if(arrow >= 0) + clif_arrowequip(sd,arrow); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_equiplist(struct map_session_data *sd) +{ + int i,j,n,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf = WFIFOP(fd,0); + WBUFW(buf,0)=0xa4; + for(i=0,n=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || !itemdb_isequip2(sd->inventory_data[i])) + continue; + WBUFW(buf,n*20+4)=i+2; + if(sd->inventory_data[i]->view_id > 0) + WBUFW(buf,n*20+6)=sd->inventory_data[i]->view_id; + else + WBUFW(buf,n*20+6)=sd->status.inventory[i].nameid; + WBUFB(buf,n*20+8)=(sd->inventory_data[i]->type == 7)? 4:sd->inventory_data[i]->type; + WBUFB(buf,n*20+9)=sd->status.inventory[i].identify; + WBUFW(buf,n*20+10)=pc_equippoint(sd,i); + WBUFW(buf,n*20+12)=sd->status.inventory[i].equip; + if(sd->status.inventory[i].broken==1) + WBUFB(buf,n*20+14)=1; // is weapon broken [Valaris] + else + WBUFB(buf,n*20+14)=sd->status.inventory[i].attribute; + WBUFB(buf,n*20+15)=sd->status.inventory[i].refine; + if(sd->status.inventory[i].card[0]==0x00ff || sd->status.inventory[i].card[0]==0x00fe || sd->status.inventory[i].card[0]==(short)0xff00) { + WBUFW(buf,n*20+16)=sd->status.inventory[i].card[0]; + WBUFW(buf,n*20+18)=sd->status.inventory[i].card[1]; + WBUFW(buf,n*20+20)=sd->status.inventory[i].card[2]; + WBUFW(buf,n*20+22)=sd->status.inventory[i].card[3]; + } else { + if(sd->status.inventory[i].card[0] > 0 && (j=itemdb_viewid(sd->status.inventory[i].card[0])) > 0) + WBUFW(buf,n*20+16)=j; + else + WBUFW(buf,n*20+16)=sd->status.inventory[i].card[0]; + if(sd->status.inventory[i].card[1] > 0 && (j=itemdb_viewid(sd->status.inventory[i].card[1])) > 0) + WBUFW(buf,n*20+18)=j; + else + WBUFW(buf,n*20+18)=sd->status.inventory[i].card[1]; + if(sd->status.inventory[i].card[2] > 0 && (j=itemdb_viewid(sd->status.inventory[i].card[2])) > 0) + WBUFW(buf,n*20+20)=j; + else + WBUFW(buf,n*20+20)=sd->status.inventory[i].card[2]; + if(sd->status.inventory[i].card[3] > 0 && (j=itemdb_viewid(sd->status.inventory[i].card[3])) > 0) + WBUFW(buf,n*20+22)=j; + else + WBUFW(buf,n*20+22)=sd->status.inventory[i].card[3]; + } + n++; + } + if(n){ + WBUFW(buf,2)=4+n*20; + WFIFOSET(fd,WFIFOW(fd,2)); + } + return 0; +} + +/*========================================== + * カプラさんに預けてある消耗品&収集品リスト + *------------------------------------------ + */ +int clif_storageitemlist(struct map_session_data *sd,struct storage *stor) +{ + struct item_data *id; + int i,n,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + nullpo_retr(0, stor); + + fd=sd->fd; + buf = WFIFOP(fd,0); +#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)=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)=id->type;; + WBUFB(buf,n*18+9)=stor->storage[i].identify; + WBUFW(buf,n*18+10)=stor->storage[i].amount; + WBUFW(buf,n*18+12)=0; + WBUFW(buf,n*18+14)=stor->storage[i].card[0]; + WBUFW(buf,n*18+16)=stor->storage[i].card[1]; + WBUFW(buf,n*18+18)=stor->storage[i].card[2]; + WBUFW(buf,n*18+20)=stor->storage[i].card[3]; + n++; + } + if(n){ + WBUFW(buf,2)=4+n*18; + WFIFOSET(fd,WFIFOW(fd,2)); + } +#endif + return 0; +} + +/*========================================== + * カプラさんに預けてある装備リスト + *------------------------------------------ + */ +int clif_storageequiplist(struct map_session_data *sd,struct storage *stor) +{ + struct item_data *id; + int i,j,n,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + nullpo_retr(0, stor); + + fd=sd->fd; + buf = WFIFOP(fd,0); + WBUFW(buf,0)=0xa6; + for(i=0,n=0;i<MAX_STORAGE;i++){ + if(stor->storage[i].nameid<=0) + continue; + nullpo_retr(0, id = itemdb_search(stor->storage[i].nameid)); + if(!itemdb_isequip2(id)) + continue; + WBUFW(buf,n*20+4)=i+1; + if(id->view_id > 0) + WBUFW(buf,n*20+6)=id->view_id; + else + WBUFW(buf,n*20+6)=stor->storage[i].nameid; + WBUFB(buf,n*20+8)=id->type; + WBUFB(buf,n*20+9)=stor->storage[i].identify; + WBUFW(buf,n*20+10)=id->equip; + WBUFW(buf,n*20+12)=stor->storage[i].equip; + if(stor->storage[i].broken==1) + WBUFB(buf,n*20+14)=1; //is weapon broken [Valaris] + else + WBUFB(buf,n*20+14)=stor->storage[i].attribute; + WBUFB(buf,n*20+15)=stor->storage[i].refine; + if(stor->storage[i].card[0]==0x00ff || stor->storage[i].card[0]==0x00fe || stor->storage[i].card[0]==(short)0xff00) { + WBUFW(buf,n*20+16)=stor->storage[i].card[0]; + WBUFW(buf,n*20+18)=stor->storage[i].card[1]; + WBUFW(buf,n*20+20)=stor->storage[i].card[2]; + WBUFW(buf,n*20+22)=stor->storage[i].card[3]; + } else { + if(stor->storage[i].card[0] > 0 && (j=itemdb_viewid(stor->storage[i].card[0])) > 0) + WBUFW(buf,n*20+16)=j; + else + WBUFW(buf,n*20+16)=stor->storage[i].card[0]; + if(stor->storage[i].card[1] > 0 && (j=itemdb_viewid(stor->storage[i].card[1])) > 0) + WBUFW(buf,n*20+18)=j; + else + WBUFW(buf,n*20+18)=stor->storage[i].card[1]; + if(stor->storage[i].card[2] > 0 && (j=itemdb_viewid(stor->storage[i].card[2])) > 0) + WBUFW(buf,n*20+20)=j; + else + WBUFW(buf,n*20+20)=stor->storage[i].card[2]; + if(stor->storage[i].card[3] > 0 && (j=itemdb_viewid(stor->storage[i].card[3])) > 0) + WBUFW(buf,n*20+22)=j; + else + WBUFW(buf,n*20+22)=stor->storage[i].card[3]; + } + n++; + } + if(n){ + WBUFW(buf,2)=4+n*20; + WFIFOSET(fd,WFIFOW(fd,2)); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_guildstorageitemlist(struct map_session_data *sd,struct guild_storage *stor) +{ + struct item_data *id; + int i,n,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + nullpo_retr(0, stor); + + fd=sd->fd; + buf=WFIFOP(fd,0); + +#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)=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)=id->type;; + WBUFB(buf,n*18+9)=stor->storage[i].identify; + WBUFW(buf,n*18+10)=stor->storage[i].amount; + WBUFW(buf,n*18+12)=0; + WBUFW(buf,n*18+14)=stor->storage[i].card[0]; + WBUFW(buf,n*18+16)=stor->storage[i].card[1]; + WBUFW(buf,n*18+18)=stor->storage[i].card[2]; + WBUFW(buf,n*18+20)=stor->storage[i].card[3]; + n++; + } + if(n){ + WBUFW(buf,2)=4+n*18; + WFIFOSET(fd,WFIFOW(fd,2)); + } +#endif + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_guildstorageequiplist(struct map_session_data *sd,struct guild_storage *stor) +{ + struct item_data *id; + int i,j,n,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf=WFIFOP(fd,0); + + WBUFW(buf,0)=0xa6; + for(i=0,n=0;i<MAX_GUILD_STORAGE;i++){ + if(stor->storage[i].nameid<=0) + continue; + nullpo_retr(0, id = itemdb_search(stor->storage[i].nameid)); + if(!itemdb_isequip2(id)) + continue; + WBUFW(buf,n*20+4)=i+1; + if(id->view_id > 0) + WBUFW(buf,n*20+6)=id->view_id; + else + WBUFW(buf,n*20+6)=stor->storage[i].nameid; + WBUFB(buf,n*20+8)=id->type; + WBUFB(buf,n*20+9)=stor->storage[i].identify; + WBUFW(buf,n*20+10)=id->equip; + WBUFW(buf,n*20+12)=stor->storage[i].equip; + if(stor->storage[i].broken==1) + WBUFB(buf,n*20+14)=1; // is weapon broken [Valaris] + else + WBUFB(buf,n*20+14)=stor->storage[i].attribute; + WBUFB(buf,n*20+15)=stor->storage[i].refine; + if(stor->storage[i].card[0]==0x00ff || stor->storage[i].card[0]==0x00fe || stor->storage[i].card[0]==(short)0xff00) { + WBUFW(buf,n*20+16)=stor->storage[i].card[0]; + WBUFW(buf,n*20+18)=stor->storage[i].card[1]; + WBUFW(buf,n*20+20)=stor->storage[i].card[2]; + WBUFW(buf,n*20+22)=stor->storage[i].card[3]; + } else { + if(stor->storage[i].card[0] > 0 && (j=itemdb_viewid(stor->storage[i].card[0])) > 0) + WBUFW(buf,n*20+16)=j; + else + WBUFW(buf,n*20+16)=stor->storage[i].card[0]; + if(stor->storage[i].card[1] > 0 && (j=itemdb_viewid(stor->storage[i].card[1])) > 0) + WBUFW(buf,n*20+18)=j; + else + WBUFW(buf,n*20+18)=stor->storage[i].card[1]; + if(stor->storage[i].card[2] > 0 && (j=itemdb_viewid(stor->storage[i].card[2])) > 0) + WBUFW(buf,n*20+20)=j; + else + WBUFW(buf,n*20+20)=stor->storage[i].card[2]; + if(stor->storage[i].card[3] > 0 && (j=itemdb_viewid(stor->storage[i].card[3])) > 0) + WBUFW(buf,n*20+22)=j; + else + WBUFW(buf,n*20+22)=stor->storage[i].card[3]; + } + n++; + } + if(n){ + WBUFW(buf,2)=4+n*20; + WFIFOSET(fd,WFIFOW(fd,2)); + } + return 0; +} + +/*========================================== + * ステータスを送りつける + * 表示専用数字はこの中で計算して送る + *------------------------------------------ + */ +int clif_updatestatus(struct map_session_data *sd,int type) +{ + int fd,len=8; + + nullpo_retr(0, sd); + + fd=sd->fd; + + WFIFOW(fd,0)=0xb0; + WFIFOW(fd,2)=type; + switch(type){ + // 00b0 + case SP_WEIGHT: + pc_checkweighticon(sd); + WFIFOW(fd,0)=0xb0; + WFIFOW(fd,2)=type; + WFIFOL(fd,4)=sd->weight; + break; + case SP_MAXWEIGHT: + WFIFOL(fd,4)=sd->max_weight; + break; + case SP_SPEED: + WFIFOL(fd,4)=sd->speed; + break; + case SP_BASELEVEL: + WFIFOL(fd,4)=sd->status.base_level; + break; + case SP_JOBLEVEL: + WFIFOL(fd,4)=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; + break; + case SP_SP: + WFIFOL(fd,4)=sd->status.sp; + break; + case SP_ASPD: + WFIFOL(fd,4)=sd->aspd; + break; + case SP_ATK1: + WFIFOL(fd,4)=sd->base_atk+sd->watk; + break; + case SP_DEF1: + WFIFOL(fd,4)=sd->def; + break; + case SP_MDEF1: + WFIFOL(fd,4)=sd->mdef; + break; + case SP_ATK2: + WFIFOL(fd,4)=sd->watk2; + break; + case SP_DEF2: + WFIFOL(fd,4)=sd->def2; + break; + case SP_MDEF2: + WFIFOL(fd,4)=sd->mdef2; + break; + case SP_CRITICAL: + WFIFOL(fd,4)=sd->critical/10; + break; + case SP_MATK1: + WFIFOL(fd,4)=sd->matk1; + break; + case SP_MATK2: + WFIFOL(fd,4)=sd->matk2; + break; + + + case SP_ZENY: + WFIFOW(fd,0)=0xb1; + if(sd->status.zeny < 0) + sd->status.zeny = 0; + WFIFOL(fd,4)=sd->status.zeny; + break; + case SP_BASEEXP: + WFIFOW(fd,0)=0xb1; + WFIFOL(fd,4)=sd->status.base_exp; + break; + case SP_JOBEXP: + WFIFOW(fd,0)=0xb1; + WFIFOL(fd,4)=sd->status.job_exp; + break; + case SP_NEXTBASEEXP: + WFIFOW(fd,0)=0xb1; + WFIFOL(fd,4)=pc_nextbaseexp(sd); + break; + case SP_NEXTJOBEXP: + WFIFOW(fd,0)=0xb1; + WFIFOL(fd,4)=pc_nextjobexp(sd); + break; + + // 00be 終了 + case SP_USTR: + case SP_UAGI: + case SP_UVIT: + case SP_UINT: + case SP_UDEX: + case SP_ULUK: + WFIFOW(fd,0)=0xbe; + WFIFOB(fd,4)=pc_need_status_point(sd,type-SP_USTR+SP_STR); + len=5; + break; + + // 013a 終了 + case SP_ATTACKRANGE: + WFIFOW(fd,0)=0x13a; + WFIFOW(fd,2)=sd->attackrange; + len=4; + break; + + // 0141 終了 + case SP_STR: + WFIFOW(fd,0)=0x141; + WFIFOL(fd,2)=type; + WFIFOL(fd,6)=sd->status.str; + WFIFOL(fd,10)=sd->paramb[0] + sd->parame[0]; + len=14; + break; + case SP_AGI: + WFIFOW(fd,0)=0x141; + WFIFOL(fd,2)=type; + WFIFOL(fd,6)=sd->status.agi; + WFIFOL(fd,10)=sd->paramb[1] + sd->parame[1]; + len=14; + break; + case SP_VIT: + WFIFOW(fd,0)=0x141; + WFIFOL(fd,2)=type; + WFIFOL(fd,6)=sd->status.vit; + WFIFOL(fd,10)=sd->paramb[2] + sd->parame[2]; + len=14; + break; + case SP_INT: + WFIFOW(fd,0)=0x141; + WFIFOL(fd,2)=type; + WFIFOL(fd,6)=sd->status.int_; + WFIFOL(fd,10)=sd->paramb[3] + sd->parame[3]; + len=14; + break; + case SP_DEX: + WFIFOW(fd,0)=0x141; + WFIFOL(fd,2)=type; + WFIFOL(fd,6)=sd->status.dex; + WFIFOL(fd,10)=sd->paramb[4] + sd->parame[4]; + len=14; + break; + case SP_LUK: + WFIFOW(fd,0)=0x141; + WFIFOL(fd,2)=type; + WFIFOL(fd,6)=sd->status.luk; + WFIFOL(fd,10)=sd->paramb[5] + sd->parame[5]; + len=14; + break; + + case SP_CARTINFO: + WFIFOW(fd,0)=0x121; + WFIFOW(fd,2)=sd->cart_num; + WFIFOW(fd,4)=sd->cart_max_num; + WFIFOL(fd,6)=sd->cart_weight; + WFIFOL(fd,10)=sd->cart_max_weight; + len=14; + break; + + default: + if(battle_config.error_log) + printf("clif_updatestatus : make %d routine\n",type); + return 1; + } + WFIFOSET(fd,len); + + return 0; +} +int clif_changestatus(struct block_list *bl,int type,int val) +{ + unsigned char buf[12]; + struct map_session_data *sd = NULL; + + nullpo_retr(0, bl); + + if(bl->type == BL_PC) + sd = (struct map_session_data *)bl; + +//printf("clif_changestatus id:%d type:%d val:%d\n",bl->id,type,val); + if(sd){ + WBUFW(buf,0)=0x1ab; + WBUFL(buf,2)=bl->id; + WBUFW(buf,6)=type; + switch(type){ + case SP_MANNER: + WBUFL(buf,8)=val; + break; + default: + if(battle_config.error_log) + printf("clif_changestatus : make %d routine\n",type); + return 1; + } + clif_send(buf,packet_len_table[0x1ab],bl,AREA_WOS); + } + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int clif_changelook(struct block_list *bl,int type,int val) +{ + + 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(sd && sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] + return 0; + +#if PACKETVER < 4 + if(sd && (type == LOOK_WEAPON || type == LOOK_SHIELD) && sd->view_class == 22) + 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 != 22) { + if(sd->inventory_data[sd->equip_index[9]]->view_id > 0) + WBUFW(buf,7)=sd->inventory_data[sd->equip_index[9]]->view_id; + else + WBUFW(buf,7)=sd->status.inventory[sd->equip_index[9]].nameid; + } else + WBUFW(buf,7)=0; + if(sd->equip_index[8] >= 0 && sd->equip_index[8] != sd->equip_index[9] && sd->inventory_data[sd->equip_index[8]] && + sd->view_class != 22) { + if(sd->inventory_data[sd->equip_index[8]]->view_id > 0) + WBUFW(buf,9)=sd->inventory_data[sd->equip_index[8]]->view_id; + else + WBUFW(buf,9)=sd->status.inventory[sd->equip_index[8]].nameid; + } else + WBUFW(buf,9)=0; + } + 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; + buf=WFIFOP(fd,0); + + WBUFW(buf,0)=0xbd; + WBUFW(buf,2)=sd->status.status_point; + WBUFB(buf,4)=(sd->status.str > 255)? 255:sd->status.str; + WBUFB(buf,5)=pc_need_status_point(sd,SP_STR); + WBUFB(buf,6)=(sd->status.agi > 255)? 255:sd->status.agi; + WBUFB(buf,7)=pc_need_status_point(sd,SP_AGI); + WBUFB(buf,8)=(sd->status.vit > 255)? 255:sd->status.vit; + WBUFB(buf,9)=pc_need_status_point(sd,SP_VIT); + WBUFB(buf,10)=(sd->status.int_ > 255)? 255:sd->status.int_; + WBUFB(buf,11)=pc_need_status_point(sd,SP_INT); + WBUFB(buf,12)=(sd->status.dex > 255)? 255:sd->status.dex; + WBUFB(buf,13)=pc_need_status_point(sd,SP_DEX); + WBUFB(buf,14)=(sd->status.luk > 255)? 255:sd->status.luk; + WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK); + + WBUFW(buf,16) = sd->base_atk + sd->watk; + WBUFW(buf,18) = sd->watk2; //atk bonus + WBUFW(buf,20) = sd->matk1; + WBUFW(buf,22) = sd->matk2; + WBUFW(buf,24) = sd->def; // def + WBUFW(buf,26) = sd->def2; + WBUFW(buf,28) = sd->mdef; // mdef + WBUFW(buf,30) = sd->mdef2; + WBUFW(buf,32) = sd->hit; + WBUFW(buf,34) = sd->flee; + WBUFW(buf,36) = sd->flee2/10; + WBUFW(buf,38) = sd->critical/10; + WBUFW(buf,40) = sd->status.karma; + WBUFW(buf,42) = sd->status.manner; + + WFIFOSET(fd,packet_len_table[0xbd]); + + clif_updatestatus(sd,SP_STR); + clif_updatestatus(sd,SP_AGI); + clif_updatestatus(sd,SP_VIT); + clif_updatestatus(sd,SP_INT); + clif_updatestatus(sd,SP_DEX); + clif_updatestatus(sd,SP_LUK); + + clif_updatestatus(sd,SP_ATTACKRANGE); + clif_updatestatus(sd,SP_ASPD); + + return 0; +} + +/*========================================== + *矢装備 + *------------------------------------------ + */ +int clif_arrowequip(struct map_session_data *sd,int val) +{ + int fd; + + nullpo_retr(0, sd); + + if(sd->attacktarget && sd->attacktarget > 0) // [Valaris] + sd->attacktarget = 0; + + fd=sd->fd; + WFIFOW(fd,0)=0x013c; + WFIFOW(fd,2)=val+2;//矢のアイテムID + + WFIFOSET(fd,packet_len_table[0x013c]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_arrow_fail(struct map_session_data *sd,int type) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x013b; + WFIFOW(fd,2)=type; + + WFIFOSET(fd,packet_len_table[0x013b]); + + return 0; +} + +/*========================================== + * 作成可能 矢リスト送信 + *------------------------------------------ + */ +int clif_arrow_create_list(struct map_session_data *sd) +{ + int i,c,view; + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x1ad; + + for(i=0,c=0;i<MAX_SKILL_ARROW_DB;i++){ + if(skill_arrow_db[i].nameid > 0 && pc_search_inventory(sd,skill_arrow_db[i].nameid)>=0){ + if((view = itemdb_viewid(skill_arrow_db[i].nameid)) > 0) + WFIFOW(fd,c*2+4) = view; + 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.make_arrow_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; + WFIFOW(fd,0)=0xbc; + WFIFOW(fd,2)=type; + WFIFOB(fd,4)=ok; + WFIFOB(fd,5)=val; + WFIFOSET(fd,packet_len_table[0xbc]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_equipitemack(struct map_session_data *sd,int n,int pos,int ok) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xaa; + WFIFOW(fd,2)=n+2; + WFIFOW(fd,4)=pos; + WFIFOB(fd,6)=ok; + WFIFOSET(fd,packet_len_table[0xaa]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xac; + WFIFOW(fd,2)=n+2; + WFIFOW(fd,4)=pos; + WFIFOB(fd,6)=ok; + WFIFOSET(fd,packet_len_table[0xac]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_misceffect(struct block_list* bl,int type) +{ + char buf[32]; + + nullpo_retr(0, bl); + + WBUFW(buf,0) = 0x19b; + WBUFL(buf,2) = bl->id; + WBUFL(buf,6) = type; + + clif_send(buf,packet_len_table[0x19b],bl,AREA); + + return 0; +} +int clif_misceffect2(struct block_list *bl, int type) { + unsigned char buf[24]; + + nullpo_retr(0, bl); + + memset(buf, 0, packet_len_table[0x1f3]); + + WBUFW(buf,0) = 0x1f3; + WBUFL(buf,2) = bl->id; + WBUFL(buf,6) = type; + + clif_send(buf, packet_len_table[0x1f3], bl, AREA); + + return 0; + +} +/*========================================== + * 表示オプション変更 + *------------------------------------------ + */ +int clif_changeoption(struct block_list* bl) +{ + char buf[32]; + short option; + struct status_change *sc_data; + static const int omask[]={ 0x10,0x20 }; + static const int scnum[]={ SC_FALCON, SC_RIDING }; + int i; + + nullpo_retr(0, bl); + + option = *battle_get_option(bl); + sc_data = battle_get_sc_data(bl); + + WBUFW(buf,0) = 0x119; + WBUFL(buf,2) = bl->id; + WBUFW(buf,6) = *battle_get_opt1(bl); + WBUFW(buf,8) = *battle_get_opt2(bl); + WBUFW(buf,10) = option; + WBUFB(buf,12) = 0; // ?? + + if(bl->type==BL_PC) { // disguises [Valaris] + struct map_session_data *sd=((struct map_session_data *)bl); + if(sd && sd->disguise > 23 && sd->disguise < 4001) { + clif_send(buf,packet_len_table[0x119],bl,AREA_WOS); + clif_spawnpc(sd); + } else + clif_send(buf,packet_len_table[0x119],bl,AREA); + } else + clif_send(buf,packet_len_table[0x119],bl,AREA); + + // アイコンの表示 + for(i=0;i<sizeof(omask)/sizeof(omask[0]);i++){ + if( option&omask[i] ){ + if( sc_data[scnum[i]].timer==-1) + skill_status_change_start(bl,scnum[i],0,0,0,0,0,0); + } else { + skill_status_change_end(bl,scnum[i],-1); + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_useitemack(struct map_session_data *sd,int index,int amount,int ok) +{ + nullpo_retr(0, sd); + + if(!ok) { + int fd=sd->fd; + WFIFOW(fd,0)=0xa8; + WFIFOW(fd,2)=index+2; + WFIFOW(fd,4)=amount; + WFIFOB(fd,6)=ok; + WFIFOSET(fd,packet_len_table[0xa8]); + } + else { +#if PACKETVER < 3 + int fd=sd->fd; + WFIFOW(fd,0)=0xa8; + WFIFOW(fd,2)=index+2; + WFIFOW(fd,4)=amount; + WFIFOB(fd,6)=ok; + WFIFOSET(fd,packet_len_table[0xa8]); +#else + char buf[32]; + + WBUFW(buf,0)=0x1c8; + WBUFW(buf,2)=index+2; + if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0) + WBUFW(buf,4)=sd->inventory_data[index]->view_id; + else + WBUFW(buf,4)=sd->status.inventory[index].nameid; + WBUFL(buf,6)=sd->bl.id; + WBUFW(buf,10)=amount; + WBUFB(buf,12)=ok; + clif_send(buf,packet_len_table[0x1c8],&sd->bl,AREA); +#endif + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_createchat(struct map_session_data *sd,int fail) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xd6; + WFIFOB(fd,2)=fail; + WFIFOSET(fd,packet_len_table[0xd6]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_dispchat(struct chat_data *cd,int fd) +{ + char buf[128]; // 最大title(60バイト)+17 + + if(cd==NULL || *cd->owner==NULL) + return 1; + + WBUFW(buf,0)=0xd7; + WBUFW(buf,2)=strlen(cd->title)+17; + WBUFL(buf,4)=(*cd->owner)->id; + WBUFL(buf,8)=cd->bl.id; + WBUFW(buf,12)=cd->limit; + WBUFW(buf,14)=cd->users; + WBUFB(buf,16)=cd->pub; + strcpy(WBUFP(buf,17),cd->title); + if(fd){ + memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2)); + WFIFOSET(fd,WBUFW(buf,2)); + } else { + clif_send(buf,WBUFW(buf,2),*cd->owner,AREA_WOSC); + } + + return 0; +} + +/*========================================== + * chatの状態変更成功 + * 外部の人用と命令コード(d7->df)が違うだけ + *------------------------------------------ + */ +int clif_changechatstatus(struct chat_data *cd) +{ + char buf[128]; // 最大title(60バイト)+17 + + if(cd==NULL || cd->usersd[0]==NULL) + return 1; + + WBUFW(buf,0)=0xdf; + WBUFW(buf,2)=strlen(cd->title)+17; + WBUFL(buf,4)=cd->usersd[0]->bl.id; + WBUFL(buf,8)=cd->bl.id; + WBUFW(buf,12)=cd->limit; + WBUFW(buf,14)=cd->users; + WBUFB(buf,16)=cd->pub; + strcpy(WBUFP(buf,17),cd->title); + clif_send(buf,WBUFW(buf,2),&cd->usersd[0]->bl,CHAT); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_clearchat(struct chat_data *cd,int fd) +{ + char buf[32]; + + nullpo_retr(0, cd); + + WBUFW(buf,0)=0xd8; + WBUFL(buf,2)=cd->bl.id; + if(fd){ + memcpy(WFIFOP(fd,0),buf,packet_len_table[0xd8]); + WFIFOSET(fd,packet_len_table[0xd8]); + } else { + clif_send(buf,packet_len_table[0xd8],*cd->owner,AREA_WOSC); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_joinchatfail(struct map_session_data *sd,int fail) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + + WFIFOW(fd,0)=0xda; + WFIFOB(fd,2)=fail; + WFIFOSET(fd,packet_len_table[0xda]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_joinchatok(struct map_session_data *sd,struct chat_data* cd) +{ + int fd; + int i; + + nullpo_retr(0, sd); + nullpo_retr(0, cd); + + fd=sd->fd; + WFIFOW(fd,0)=0xdb; + WFIFOW(fd,2)=8+(28*cd->users); + WFIFOL(fd,4)=cd->bl.id; + for(i = 0;i < cd->users;i++){ + WFIFOL(fd,8+i*28) = (i!=0)||((*cd->owner)->type==BL_NPC); + memcpy(WFIFOP(fd,8+i*28+4),cd->usersd[i]->status.name,24); + } + WFIFOSET(fd,WFIFOW(fd,2)); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_addchat(struct chat_data* cd,struct map_session_data *sd) +{ + char buf[32]; + + nullpo_retr(0, sd); + nullpo_retr(0, cd); + + WBUFW(buf, 0) = 0x0dc; + WBUFW(buf, 2) = cd->users; + memcpy(WBUFP(buf, 4),sd->status.name,24); + clif_send(buf,packet_len_table[0xdc],&sd->bl,CHAT_WOS); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_changechatowner(struct chat_data* cd,struct map_session_data *sd) +{ + char buf[64]; + + nullpo_retr(0, sd); + nullpo_retr(0, cd); + + WBUFW(buf, 0) = 0xe1; + WBUFL(buf, 2) = 1; + memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,24); + WBUFW(buf,30) = 0xe1; + WBUFL(buf,32) = 0; + memcpy(WBUFP(buf,36),sd->status.name,24); + + clif_send(buf,packet_len_table[0xe1]*2,&sd->bl,CHAT); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_leavechat(struct chat_data* cd,struct map_session_data *sd) +{ + char buf[32]; + + nullpo_retr(0, sd); + nullpo_retr(0, cd); + + WBUFW(buf, 0) = 0xdd; + WBUFW(buf, 2) = cd->users-1; + memcpy(WBUFP(buf,4),sd->status.name,24); + WBUFB(buf,28) = 0; + + clif_send(buf,packet_len_table[0xdd],&sd->bl,CHAT); + + return 0; +} + +/*========================================== + * 取り引き要請受け + *------------------------------------------ + */ +int clif_traderequest(struct map_session_data *sd,char *name) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xe5; + strcpy(WFIFOP(fd,2),name); + WFIFOSET(fd,packet_len_table[0xe5]); + + return 0; +} + +/*========================================== + * 取り引き要求応答 + *------------------------------------------ + */ +int clif_tradestart(struct map_session_data *sd,int type) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xe7; + WFIFOB(fd,2)=type; + WFIFOSET(fd,packet_len_table[0xe7]); + + return 0; +} + +/*========================================== + * 相手方からのアイテム追加 + *------------------------------------------ + */ +int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount) +{ + int fd,j; + + nullpo_retr(0, sd); + nullpo_retr(0, tsd); + + fd=tsd->fd; + WFIFOW(fd,0)=0xe9; + WFIFOL(fd,2)=amount; + if(index==0){ + WFIFOW(fd,6) = 0; // type id + WFIFOB(fd,8) = 0; //identify flag + WFIFOB(fd,9) = 0; // attribute + WFIFOB(fd,10)= 0; //refine + WFIFOW(fd,11)= 0; //card (4w) + WFIFOW(fd,13)= 0; //card (4w) + WFIFOW(fd,15)= 0; //card (4w) + WFIFOW(fd,17)= 0; //card (4w) + } + else{ + index -= 2; + if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0) + WFIFOW(fd,6) = sd->inventory_data[index]->view_id; + else + WFIFOW(fd,6) = sd->status.inventory[index].nameid; // type id + WFIFOB(fd,8) = sd->status.inventory[index].identify; //identify flag + if(sd->status.inventory[index].broken==1) + WFIFOB(fd,9) = 1; // is broke weapon [Valaris] + else + WFIFOB(fd,9) = sd->status.inventory[index].attribute; // attribute + WFIFOB(fd,10)= sd->status.inventory[index].refine; //refine + if(sd->status.inventory[index].card[0]==0x00ff || sd->status.inventory[index].card[0]==0x00fe || sd->status.inventory[index].card[0]==(short)0xff00) { + WFIFOW(fd,11)= sd->status.inventory[index].card[0]; //card (4w) + WFIFOW(fd,13)= sd->status.inventory[index].card[1]; //card (4w) + WFIFOW(fd,15)= sd->status.inventory[index].card[2]; //card (4w) + WFIFOW(fd,17)= sd->status.inventory[index].card[3]; //card (4w) + } else { + if(sd->status.inventory[index].card[0] > 0 && (j=itemdb_viewid(sd->status.inventory[index].card[0])) > 0) + WFIFOW(fd,11)= j; + else + WFIFOW(fd,11)= sd->status.inventory[index].card[0]; + if(sd->status.inventory[index].card[1] > 0 && (j=itemdb_viewid(sd->status.inventory[index].card[1])) > 0) + WFIFOW(fd,13)= j; + else + WFIFOW(fd,13)= sd->status.inventory[index].card[1]; + if(sd->status.inventory[index].card[2] > 0 && (j=itemdb_viewid(sd->status.inventory[index].card[2])) > 0) + WFIFOW(fd,15)= j; + else + WFIFOW(fd,15)= sd->status.inventory[index].card[2]; + if(sd->status.inventory[index].card[3] > 0 && (j=itemdb_viewid(sd->status.inventory[index].card[3])) > 0) + WFIFOW(fd,17)= j; + else + WFIFOW(fd,17)= sd->status.inventory[index].card[3]; + } + } + WFIFOSET(fd,packet_len_table[0xe9]); + + return 0; +} + +/*========================================== + * アイテム追加成功/失敗 + *------------------------------------------ + */ +int clif_tradeitemok(struct map_session_data *sd,int index,int amount,int fail) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x1b1; + //WFIFOW(fd,0)=0xea; + WFIFOW(fd,2)=index; + WFIFOW(fd,4)=amount; + WFIFOB(fd,6)=fail; + WFIFOSET(fd,packet_len_table[0x1b1]); + + return 0; +} + +/*========================================== + * 取り引きok押し + *------------------------------------------ + */ +int clif_tradedeal_lock(struct map_session_data *sd,int fail) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xec; + WFIFOB(fd,2)=fail; // 0=you 1=the other person + WFIFOSET(fd,packet_len_table[0xec]); + + return 0; +} + +/*========================================== + * 取り引きがキャンセルされました + *------------------------------------------ + */ +int clif_tradecancelled(struct map_session_data *sd) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xee; + WFIFOSET(fd,packet_len_table[0xee]); + + return 0; +} + +/*========================================== + * 取り引き完了 + *------------------------------------------ + */ +int clif_tradecompleted(struct map_session_data *sd,int fail) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xf0; + WFIFOB(fd,2)=fail; + WFIFOSET(fd,packet_len_table[0xf0]); + + return 0; +} + +/*========================================== + * カプラ倉庫のアイテム数を更新 + *------------------------------------------ + */ +int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor) +{ + int fd; + + nullpo_retr(0, sd); + nullpo_retr(0, stor); + + fd=sd->fd; + WFIFOW(fd,0) = 0xf2; // update storage amount + WFIFOW(fd,2) = stor->storage_amount; //items + WFIFOW(fd,4) = MAX_STORAGE; //items max + WFIFOSET(fd,packet_len_table[0xf2]); + + return 0; +} + +/*========================================== + * カプラ倉庫にアイテムを追加する + *------------------------------------------ + */ +int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount) +{ + int view,fd,j; + + nullpo_retr(0, sd); + nullpo_retr(0, stor); + + fd=sd->fd; + WFIFOW(fd,0) =0xf4; // Storage item added + WFIFOW(fd,2) =index+1; // index + WFIFOL(fd,4) =amount; // amount + if((view = itemdb_viewid(stor->storage[index].nameid)) > 0) + WFIFOW(fd,8) =view; + else + WFIFOW(fd,8) =stor->storage[index].nameid; // id + WFIFOB(fd,10)=stor->storage[index].identify; //identify flag + if(stor->storage[index].broken==1) + WFIFOB(fd,11)=1; // is weapon broken [Valaris] + else + WFIFOB(fd,11)=stor->storage[index].attribute; // attribute + WFIFOB(fd,12)=stor->storage[index].refine; //refine + if(stor->storage[index].card[0]==0x00ff || stor->storage[index].card[0]==0x00fe || stor->storage[index].card[0]==(short)0xff00) { + WFIFOW(fd,13)=stor->storage[index].card[0]; //card (4w) + WFIFOW(fd,15)=stor->storage[index].card[1]; //card (4w) + WFIFOW(fd,17)=stor->storage[index].card[2]; //card (4w) + WFIFOW(fd,19)=stor->storage[index].card[3]; //card (4w) + } else { + if(stor->storage[index].card[0] > 0 && (j=itemdb_viewid(stor->storage[index].card[0])) > 0) + WFIFOW(fd,13)= j; + else + WFIFOW(fd,13)= stor->storage[index].card[0]; + if(stor->storage[index].card[1] > 0 && (j=itemdb_viewid(stor->storage[index].card[1])) > 0) + WFIFOW(fd,15)= j; + else + WFIFOW(fd,15)= stor->storage[index].card[1]; + if(stor->storage[index].card[2] > 0 && (j=itemdb_viewid(stor->storage[index].card[2])) > 0) + WFIFOW(fd,17)= j; + else + WFIFOW(fd,17)= stor->storage[index].card[2]; + if(stor->storage[index].card[3] > 0 && (j=itemdb_viewid(stor->storage[index].card[3])) > 0) + WFIFOW(fd,19)= j; + else + WFIFOW(fd,19)= stor->storage[index].card[3]; + } + WFIFOSET(fd,packet_len_table[0xf4]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor) +{ + int fd; + + nullpo_retr(0, sd); + nullpo_retr(0, stor); + + fd=sd->fd; + WFIFOW(fd,0) = 0xf2; // update storage amount + WFIFOW(fd,2) = stor->storage_amount; //items + WFIFOW(fd,4) = MAX_GUILD_STORAGE; //items max + WFIFOSET(fd,packet_len_table[0xf2]); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount) +{ + int view,fd,j; + + nullpo_retr(0, sd); + nullpo_retr(0, stor); + + fd=sd->fd; + WFIFOW(fd,0) =0xf4; // Storage item added + WFIFOW(fd,2) =index+1; // index + WFIFOL(fd,4) =amount; // amount + if((view = itemdb_viewid(stor->storage[index].nameid)) > 0) + WFIFOW(fd,8) =view; + else + WFIFOW(fd,8) =stor->storage[index].nameid; // id + WFIFOB(fd,10)=stor->storage[index].identify; //identify flag + if(stor->storage[index].broken==1) + WFIFOB(fd,11)=1; // is weapon broken [Valaris] + else + WFIFOB(fd,11)=stor->storage[index].attribute; // attribute + WFIFOB(fd,12)=stor->storage[index].refine; //refine + if(stor->storage[index].card[0]==0x00ff || stor->storage[index].card[0]==0x00fe || stor->storage[index].card[0]==(short)0xff00) { + WFIFOW(fd,13)=stor->storage[index].card[0]; //card (4w) + WFIFOW(fd,15)=stor->storage[index].card[1]; //card (4w) + WFIFOW(fd,17)=stor->storage[index].card[2]; //card (4w) + WFIFOW(fd,19)=stor->storage[index].card[3]; //card (4w) + } else { + if(stor->storage[index].card[0] > 0 && (j=itemdb_viewid(stor->storage[index].card[0])) > 0) + WFIFOW(fd,13)= j; + else + WFIFOW(fd,13)= stor->storage[index].card[0]; + if(stor->storage[index].card[1] > 0 && (j=itemdb_viewid(stor->storage[index].card[1])) > 0) + WFIFOW(fd,15)= j; + else + WFIFOW(fd,15)= stor->storage[index].card[1]; + if(stor->storage[index].card[2] > 0 && (j=itemdb_viewid(stor->storage[index].card[2])) > 0) + WFIFOW(fd,17)= j; + else + WFIFOW(fd,17)= stor->storage[index].card[2]; + if(stor->storage[index].card[3] > 0 && (j=itemdb_viewid(stor->storage[index].card[3])) > 0) + WFIFOW(fd,19)= j; + else + WFIFOW(fd,19)= stor->storage[index].card[3]; + } + WFIFOSET(fd,packet_len_table[0xf4]); + + return 0; +} + +/*========================================== + * カプラ倉庫からアイテムを取り去る + *------------------------------------------ + */ +int clif_storageitemremoved(struct map_session_data *sd,int index,int amount) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xf6; // Storage item removed + WFIFOW(fd,2)=index+1; + WFIFOL(fd,4)=amount; + WFIFOSET(fd,packet_len_table[0xf6]); + + return 0; +} + +/*========================================== + * カプラ倉庫を閉じる + *------------------------------------------ + */ +int clif_storageclose(struct map_session_data *sd) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xf8; // Storage Closed + WFIFOSET(fd,packet_len_table[0xf8]); + + return 0; +} + +// +// callback系 ? +// +/*========================================== + * PC表示 + *------------------------------------------ + */ +void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd) +{ + int len; + + nullpo_retv(sd); + nullpo_retv(dstsd); + + if(dstsd->walktimer != -1){ + len = clif_set007b(dstsd,WFIFOP(sd->fd,0)); + WFIFOSET(sd->fd,len); + } else { + len = clif_set0078(dstsd,WFIFOP(sd->fd,0)); + WFIFOSET(sd->fd,len); + } + + if(dstsd->chatID){ + struct chat_data *cd; + cd=(struct chat_data*)map_id2bl(dstsd->chatID); + if(cd->usersd[0]==dstsd) + clif_dispchat(cd,sd->fd); + } + if(dstsd->vender_id){ + clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd); + } + if(dstsd->spiritball > 0) { + clif_set01e1(dstsd,WFIFOP(sd->fd,0)); + WFIFOSET(sd->fd,packet_len_table[0x1e1]); + } + if(battle_config.save_clothcolor==1 && dstsd->status.clothes_color > 0) + clif_changelook(&dstsd->bl,LOOK_CLOTHES_COLOR,dstsd->status.clothes_color); + + if(sd->status.manner < 0) + clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner); + +} + +/*========================================== + * NPC表示 + *------------------------------------------ + */ +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; + + len = clif_npc0078(nd,WFIFOP(sd->fd,0)); + WFIFOSET(sd->fd,len); + + if(nd->chat_id){ + clif_dispchat((struct chat_data*)map_id2bl(nd->chat_id),sd->fd); + } + +} + +/*========================================== + * 移動停止 + *------------------------------------------ + */ +int clif_movemob(struct mob_data *md) +{ + unsigned char buf[256]; + int len; + + nullpo_retr(0, md); + + len = clif_mob007b(md,buf); + clif_send(buf,len,&md->bl,AREA); + + if(mob_get_equip(md->class) > 0) // mob equipment [Valaris] + clif_mob_equip(md,mob_get_equip(md->class)); + + return 0; +} + +/*========================================== + * モンスターの位置修正 + *------------------------------------------ + */ +int clif_fixmobpos(struct mob_data *md) +{ + unsigned char buf[256]; + int len; + + nullpo_retr(0, md); + + if(md->state.state == MS_WALK){ + len = clif_mob007b(md,buf); + clif_send(buf,len,&md->bl,AREA); + } else { + len = clif_mob0078(md,buf); + clif_send(buf,len,&md->bl,AREA); + } + + return 0; +} + +/*========================================== + * PCの位置修正 + *------------------------------------------ + */ +int clif_fixpcpos(struct map_session_data *sd) +{ + unsigned char buf[256]; + int len; + + nullpo_retr(0, sd); + + if(sd->walktimer != -1){ + len = clif_set007b(sd,buf); + clif_send(buf,len,&sd->bl,AREA); + } else { + len = clif_set0078(sd,buf); + clif_send(buf,len,&sd->bl,AREA); + } + + 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; +} + +/*========================================== + * 通常攻撃エフェクト&ダメージ + *------------------------------------------ + */ +int clif_damage(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,int type,int damage2) +{ + unsigned char buf[256]; + struct status_change *sc_data; + + nullpo_retr(0, src); + nullpo_retr(0, dst); + + sc_data = battle_get_sc_data(dst); + + if(type != 4 && dst->type == BL_PC && ((struct map_session_data *)dst)->special_state.infinite_endure) + type = 9; + if(sc_data) { + if(type != 4 && sc_data[SC_ENDURE].timer != -1) + type = 9; + if(sc_data[SC_HALLUCINATION].timer != -1) { + if(damage > 0) + damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100; + if(damage2 > 0) + damage2 = damage2*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100; + } + } + + WBUFW(buf,0)=0x8a; + WBUFL(buf,2)=src->id; + WBUFL(buf,6)=dst->id; + WBUFL(buf,10)=tick; + WBUFL(buf,14)=sdelay; + WBUFL(buf,18)=ddelay; + WBUFW(buf,22)=(damage > 0x7fff)? 0x7fff:damage; + WBUFW(buf,24)=div; + WBUFB(buf,26)=type; + WBUFW(buf,27)=damage2; + clif_send(buf,packet_len_table[0x8a],src,AREA); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_getareachar_mob(struct map_session_data* sd,struct mob_data* md) +{ + int len; + nullpo_retv(sd); + nullpo_retv(md); + + if(md->state.state == MS_WALK){ + len = clif_mob007b(md,WFIFOP(sd->fd,0)); + WFIFOSET(sd->fd,len); + } else { + len = clif_mob0078(md,WFIFOP(sd->fd,0)); + WFIFOSET(sd->fd,len); + } + + if(mob_get_equip(md->class) > 0) // mob equipment [Valaris] + clif_mob_equip(md,mob_get_equip(md->class)); +} + +/*========================================== + * + *------------------------------------------ + */ +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){ + len = clif_pet007b(pd,WFIFOP(sd->fd,0)); + WFIFOSET(sd->fd,len); + } else { + 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 + WFIFOW(fd,0)=0x9d; + WFIFOL(fd,2)=fitem->bl.id; + if((view = itemdb_viewid(fitem->item_data.nameid)) > 0) + WFIFOW(fd,6)=view; + else + WFIFOW(fd,6)=fitem->item_data.nameid; + WFIFOB(fd,8)=fitem->item_data.identify; + WFIFOW(fd,9)=fitem->bl.x; + WFIFOW(fd,11)=fitem->bl.y; + WFIFOW(fd,13)=fitem->item_data.amount; + WFIFOB(fd,15)=fitem->subx; + WFIFOB(fd,16)=fitem->suby; + + WFIFOSET(fd,packet_len_table[0x9d]); +} +/*========================================== + * 場所スキルエフェクトが視界に入る + *------------------------------------------ + */ +int clif_getareachar_skillunit(struct map_session_data *sd,struct skill_unit *unit) +{ + int fd; + struct block_list *bl; + + nullpo_retr(0, unit); + + fd=sd->fd; + bl=map_id2bl(unit->group->src_id); +#if PACKETVER < 3 + 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 + memset(WFIFOP(fd,0),0,packet_len_table[0x1c9]); + WFIFOW(fd, 0)=0x1c9; + WFIFOL(fd, 2)=unit->bl.id; + WFIFOL(fd, 6)=unit->group->src_id; + WFIFOW(fd,10)=unit->bl.x; + WFIFOW(fd,12)=unit->bl.y; + WFIFOB(fd,14)=unit->group->unit_id; + WFIFOB(fd,15)=1; + WFIFOL(fd,15+1)=0; //1-4調べた限り固定 + WFIFOL(fd,15+5)=0; //5-8調べた限り固定 + //9-12マップごとで一定の77-80とはまた違う4バイトのかなり大きな数字 + WFIFOL(fd,15+13)=unit->bl.y - 0x12; //13-16ユニットのY座標-18っぽい(Y:17でFF FF FF FF) + WFIFOL(fd,15+17)=0x004f37dd; //17-20調べた限り固定 + WFIFOL(fd,15+21)=0x0012f674; //21-24調べた限り固定 + WFIFOL(fd,15+25)=0x0012f664; //25-28調べた限り固定 + WFIFOL(fd,15+29)=0x0012f654; //29-32調べた限り固定 + WFIFOL(fd,15+33)=0x77527bbc; //33-36調べた限り固定 + //37-39 + WFIFOB(fd,15+40)=0x2d; //40調べた限り固定 + WFIFOL(fd,15+41)=0; //41-44調べた限り0固定 + WFIFOL(fd,15+45)=0; //45-48調べた限り0固定 + WFIFOL(fd,15+49)=0; //49-52調べた限り0固定 + WFIFOL(fd,15+53)=0x0048d919; //53-56調べた限り固定 + WFIFOL(fd,15+57)=0x0000003e; //57-60調べた限り固定 + WFIFOL(fd,15+61)=0x0012f66c; //61-64調べた限り固定 + //65-68 + //69-72 + if(bl) WFIFOL(fd,15+73)=bl->y; //73-76術者のY座標 + WFIFOL(fd,15+77)=unit->bl.m; //77-80マップIDかなぁ?かなり2バイトで足りそうな数字 + WFIFOB(fd,15+81)=0xaa; //81終端文字0xaa + + /* Graffiti [Valaris] */ + if(unit->group->unit_id==0xb0) { + WFIFOL(fd,15)=1; + WFIFOL(fd,16)=1; + memcpy(WFIFOP(fd,17),unit->group->valstr,80); + } + + WFIFOSET(fd,packet_len_table[0x1c9]); +#endif + if(unit->group->skill_id == WZ_ICEWALL) + clif_set0192(fd,unit->bl.m,unit->bl.x,unit->bl.y,5); + + return 0; +} +/*========================================== + * 場所スキルエフェクトが視界から消える + *------------------------------------------ + */ +int clif_clearchar_skillunit(struct skill_unit *unit,int fd) +{ + nullpo_retr(0, unit); + + WFIFOW(fd, 0)=0x120; + WFIFOL(fd, 2)=unit->bl.id; + WFIFOSET(fd,packet_len_table[0x120]); + if(unit->group->skill_id == WZ_ICEWALL) + clif_set0192(fd,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_01ac(struct block_list *bl) +{ + char buf[32]; + + nullpo_retr(0, bl); + + WBUFW(buf, 0) = 0x1ac; + WBUFL(buf, 2) = bl->id; + + clif_send(buf,packet_len_table[0x1ac],bl,AREA); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ + int clif_getareachar(struct block_list* bl,va_list ap) +{ + struct map_session_data *sd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + sd=va_arg(ap,struct map_session_data*); + + switch(bl->type){ + case BL_PC: + if(sd==(struct map_session_data*)bl) + break; + clif_getareachar_pc(sd,(struct map_session_data*) bl); + break; + case BL_NPC: + clif_getareachar_npc(sd,(struct npc_data*) bl); + break; + case BL_MOB: + clif_getareachar_mob(sd,(struct mob_data*) bl); + break; + case BL_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) + printf("get area char ??? %d\n",bl->type); + break; + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_pcoutsight(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd,*dstsd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, sd=va_arg(ap,struct map_session_data*)); + + switch(bl->type){ + case BL_PC: + dstsd = (struct map_session_data*) bl; + if(sd != dstsd) { + clif_clearchar_id(dstsd->bl.id,0,sd->fd); + clif_clearchar_id(sd->bl.id,0,dstsd->fd); + if(dstsd->chatID){ + struct chat_data *cd; + cd=(struct chat_data*)map_id2bl(dstsd->chatID); + if(cd->usersd[0]==dstsd) + clif_dispchat(cd,sd->fd); + } + 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)){ + clif_clearchar_id(md->bl.id,0,sd->fd); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_mobinsight(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + struct mob_data *md; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + md=va_arg(ap,struct mob_data*); + if(bl->type==BL_PC && (sd = (struct map_session_data *)bl)){ + clif_getareachar_mob(sd,md); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_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)){ + clif_clearchar_id(pd->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)){ + clif_getareachar_pet(sd,pd); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range) +{ + int fd,id; + + nullpo_retr(0, sd); + + fd=sd->fd; + if( (id=sd->status.skill[skillid].id) <= 0 ) + return 0; + WFIFOW(fd,0)=0x147; + WFIFOW(fd,2) = id; + if(type < 0) + WFIFOW(fd,4) = skill_get_inf(id); + else + WFIFOW(fd,4) = type; + WFIFOW(fd,6) = 0; + WFIFOW(fd,8) = sd->status.skill[skillid].lv; + WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[skillid].lv); + if(range < 0) { + range = skill_get_range(id,sd->status.skill[skillid].lv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + WFIFOW(fd,12)= range; + } else + WFIFOW(fd,12)= range; + memset(WFIFOP(fd,14),0,24); + if(!(skill_get_inf2(id)&0x01) || battle_config.quest_skill_learn == 1 || (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; + else + WFIFOB(fd,38) = 0; + WFIFOSET(fd,packet_len_table[0x147]); + + return 0; +} + +/*========================================== + * スキルリストを送信する + *------------------------------------------ + */ +int clif_skillinfoblock(struct map_session_data *sd) +{ + int fd; + int i,c,len=4,id,range; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x10f; + for ( i = c = 0; i < MAX_SKILL; i++){ + if( (id=sd->status.skill[i].id)!=0 ){ + 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); + range = skill_get_range(id,sd->status.skill[i].lv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + WFIFOW(fd,len+10)= range; + memset(WFIFOP(fd,len+12),0,24); + if(!(skill_get_inf2(id)&0x01) || battle_config.quest_skill_learn == 1 || (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; + else + WFIFOB(fd,len+36) = 0; + len+=37; + c++; + } + } + WFIFOW(fd,2)=len; + WFIFOSET(fd,len); + + return 0; +} + +/*========================================== + * スキル割り振り通知 + *------------------------------------------ + */ +int clif_skillup(struct map_session_data *sd,int skill_num) +{ + int range,fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0) = 0x10e; + WFIFOW(fd,2) = skill_num; + WFIFOW(fd,4) = sd->status.skill[skill_num].lv; + WFIFOW(fd,6) = skill_get_sp(skill_num,sd->status.skill[skill_num].lv); + range = skill_get_range(skill_num,sd->status.skill[skill_num].lv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + WFIFOW(fd,8) = range; + WFIFOB(fd,10) = (sd->status.skill[skill_num].lv < skill_get_max(sd->status.skill[skill_num].id)) ? 1 : 0; + WFIFOSET(fd,packet_len_table[0x10e]); + + return 0; +} + +/*========================================== + * スキル詠唱エフェクトを送信する + *------------------------------------------ + */ +int clif_skillcasting(struct block_list* bl, + int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime) +{ + unsigned char buf[32]; + WBUFW(buf,0) = 0x13e; + WBUFL(buf,2) = src_id; + WBUFL(buf,6) = dst_id; + WBUFW(buf,10) = dst_x; + WBUFW(buf,12) = dst_y; + WBUFW(buf,14) = skill_num;//魔法詠唱スキル + WBUFL(buf,16) = skill_get_pl(skill_num);//属性 + WBUFL(buf,20) = casttime;//skill詠唱時間 + clif_send(buf,packet_len_table[0x13e], bl, AREA); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_skillcastcancel(struct block_list* bl) +{ + unsigned char buf[16]; + + nullpo_retr(0, bl); + + WBUFW(buf,0) = 0x1b9; + WBUFL(buf,2) = bl->id; + clif_send(buf,packet_len_table[0x1b9], bl, AREA); + + return 0; +} + +/*========================================== + * スキル詠唱失敗 + *------------------------------------------ + */ +int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + + if(type==0x4 && battle_config.display_delay_skill_fail==0){ + return 0; + } + + WFIFOW(fd,0) = 0x110; + WFIFOW(fd,2) = skill_id; + WFIFOW(fd,4) = btype; + WFIFOW(fd,6) = 0; + WFIFOB(fd,8) = 0; + WFIFOB(fd,9) = type; + WFIFOSET(fd,packet_len_table[0x110]); + + return 0; +} + +/*========================================== + * スキル攻撃エフェクト&ダメージ + *------------------------------------------ + */ +int clif_skill_damage(struct block_list *src,struct block_list *dst, + unsigned int tick,int sdelay,int ddelay,int damage,int div,int skill_id,int skill_lv,int type) +{ + unsigned char buf[64]; + struct status_change *sc_data; + + nullpo_retr(0, src); + nullpo_retr(0, dst); + + sc_data = battle_get_sc_data(dst); + + if(type != 5 && dst->type == BL_PC && ((struct map_session_data *)dst)->special_state.infinite_endure) + type = 9; + if(sc_data) { + if(type != 5 && sc_data[SC_ENDURE].timer != -1) + type = 9; + if(sc_data[SC_HALLUCINATION].timer != -1 && damage > 0) + damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + rand()%100; + } + +#if PACKETVER < 3 + WBUFW(buf,0)=0x114; + 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)=damage; + WBUFW(buf,26)=skill_lv; + WBUFW(buf,28)=div; + WBUFB(buf,30)=(type>0)?type:skill_get_hit(skill_id); + clif_send(buf,packet_len_table[0x114],src,AREA); +#else + WBUFW(buf,0)=0x1de; + WBUFW(buf,2)=skill_id; + WBUFL(buf,4)=src->id; + WBUFL(buf,8)=dst->id; + WBUFL(buf,12)=tick; + WBUFL(buf,16)=sdelay; + WBUFL(buf,20)=ddelay; + WBUFL(buf,24)=damage; + WBUFW(buf,28)=skill_lv; + WBUFW(buf,30)=div; + WBUFB(buf,32)=(type>0)?type:skill_get_hit(skill_id); + clif_send(buf,packet_len_table[0x1de],src,AREA); +#endif + + return 0; +} + +/*========================================== + * 吹き飛ばしスキル攻撃エフェクト&ダメージ + *------------------------------------------ + */ +int clif_skill_damage2(struct block_list *src,struct block_list *dst, + unsigned int tick,int sdelay,int ddelay,int damage,int div,int skill_id,int skill_lv,int type) +{ + unsigned char buf[64]; + struct status_change *sc_data; + + nullpo_retr(0, src); + nullpo_retr(0, dst); + + sc_data = battle_get_sc_data(dst); + + if(type != 5 && dst->type == BL_PC && ((struct map_session_data *)dst)->special_state.infinite_endure) + type = 9; + if(sc_data) { + if(type != 5 && sc_data[SC_ENDURE].timer != -1) + type = 9; + if(sc_data[SC_HALLUCINATION].timer != -1 && damage > 0) + damage = damage*(5+sc_data[SC_HALLUCINATION].val1) + 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>0)?type:skill_get_hit(skill_id); + clif_send(buf,packet_len_table[0x115],src,AREA); + + return 0; +} + +/*========================================== + * 支援/回復スキルエフェクト + *------------------------------------------ + */ +int clif_skill_nodamage(struct block_list *src,struct block_list *dst, + int skill_id,int heal,int fail) +{ + unsigned char buf[32]; + + nullpo_retr(0, src); + nullpo_retr(0, dst); + + WBUFW(buf,0)=0x11a; + WBUFW(buf,2)=skill_id; + WBUFW(buf,4)=(heal > 0x7fff)? 0x7fff:heal; + WBUFL(buf,6)=dst->id; + WBUFL(buf,10)=src->id; + WBUFB(buf,14)=fail; + clif_send(buf,packet_len_table[0x11a],src,AREA); + + return 0; +} + +/*========================================== + * 場所スキルエフェクト + *------------------------------------------ + */ +int clif_skill_poseffect(struct block_list *src,int skill_id,int val,int x,int y,int tick) +{ + unsigned char buf[32]; + + nullpo_retr(0, src); + + WBUFW(buf,0)=0x117; + WBUFW(buf,2)=skill_id; + WBUFL(buf,4)=src->id; + WBUFW(buf,8)=val; + WBUFW(buf,10)=x; + WBUFW(buf,12)=y; + WBUFL(buf,14)=tick; + clif_send(buf,packet_len_table[0x117],src,AREA); + + return 0; +} + +/*========================================== + * 場所スキルエフェクト表示 + *------------------------------------------ + */ +int clif_skill_setunit(struct skill_unit *unit) +{ + unsigned char buf[128]; + struct block_list *bl; + + nullpo_retr(0, unit); + + bl=map_id2bl(unit->group->src_id); + +#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; + WBUFL(buf,15+1)=0; //1-4調べた限り固定 + WBUFL(buf,15+5)=0; //5-8調べた限り固定 + //9-12マップごとで一定の77-80とはまた違う4バイトのかなり大きな数字 + WBUFL(buf,15+13)=unit->bl.y - 0x12; //13-16ユニットのY座標-18っぽい(Y:17でFF FF FF FF) + WBUFL(buf,15+17)=0x004f37dd; //17-20調べた限り固定(0x1b2で0x004fdbddだった) + WBUFL(buf,15+21)=0x0012f674; //21-24調べた限り固定 + WBUFL(buf,15+25)=0x0012f664; //25-28調べた限り固定 + WBUFL(buf,15+29)=0x0012f654; //29-32調べた限り固定 + WBUFL(buf,15+33)=0x77527bbc; //33-36調べた限り固定 + //37-39 + WBUFB(buf,15+40)=0x2d; //40調べた限り固定 + WBUFL(buf,15+41)=0; //41-44調べた限り0固定 + WBUFL(buf,15+45)=0; //45-48調べた限り0固定 + WBUFL(buf,15+49)=0; //49-52調べた限り0固定 + WBUFL(buf,15+53)=0x0048d919; //53-56調べた限り固定(0x01b2で0x00495119だった) + WBUFL(buf,15+57)=0x0000003e; //57-60調べた限り固定 + WBUFL(buf,15+61)=0x0012f66c; //61-64調べた限り固定 + //65-68 + //69-72 + if(bl) WBUFL(buf,15+73)=bl->y; //73-76術者のY座標 + WBUFL(buf,15+77)=unit->bl.m; //77-80マップIDかなぁ?かなり2バイトで足りそうな数字 + WBUFB(buf,15+81)=0xaa; //81終端文字0xaa + + /* Graffiti [Valaris] */ + if(unit->group->unit_id==0xb0) { + WBUFL(buf,15)=1; + WBUFL(buf,16)=1; + memcpy(WBUFP(buf,17),unit->group->valstr,80); + } + + clif_send(buf,packet_len_table[0x1c9],&unit->bl,AREA); +#endif + return 0; +} +/*========================================== + * 場所スキルエフェクト削除 + *------------------------------------------ + */ +int clif_skill_delunit(struct skill_unit *unit) +{ + unsigned char buf[16]; + + nullpo_retr(0, unit); + + WBUFW(buf, 0)=0x120; + WBUFL(buf, 2)=unit->bl.id; + clif_send(buf,packet_len_table[0x120],&unit->bl,AREA); + return 0; +} +/*========================================== + * ワープ場所選択 + *------------------------------------------ + */ +int clif_skill_warppoint(struct map_session_data *sd,int skill_num, + const char *map1,const char *map2,const char *map3,const char *map4) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x11c; + WFIFOW(fd,2)=skill_num; + memcpy(WFIFOP(fd, 4),map1,16); + memcpy(WFIFOP(fd,20),map2,16); + memcpy(WFIFOP(fd,36),map3,16); + memcpy(WFIFOP(fd,52),map4,16); + WFIFOSET(fd,packet_len_table[0x11c]); + return 0; +} +/*========================================== + * メモ応答 + *------------------------------------------ + */ +int clif_skill_memo(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + + WFIFOW(fd,0)=0x11e; + WFIFOB(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0x11e]); + return 0; +} +int clif_skill_teleportmessage(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x189; + WFIFOW(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0x189]); + return 0; +} + +/*========================================== + * モンスター情報 + *------------------------------------------ + */ +int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) +{ + struct mob_data *md; + unsigned char buf[64]; + int i; + + nullpo_retr(0, sd); + nullpo_retr(0, dst); + + if(dst->type!=BL_MOB ) + return 0; + if((md=(struct mob_data *)dst) == NULL) + return 0; + + WBUFW(buf, 0)=0x18c; + WBUFW(buf, 2)=mob_get_viewclass(md->class); + WBUFW(buf, 4)=mob_db[md->class].lv; + WBUFW(buf, 6)=mob_db[md->class].size; + WBUFL(buf, 8)=md->hp; + WBUFW(buf,12)=battle_get_def2(&md->bl); + WBUFW(buf,14)=mob_db[md->class].race; + WBUFW(buf,16)=battle_get_mdef2(&md->bl) - (mob_db[md->class].vit>>1); + WBUFW(buf,18)=battle_get_elem_type(&md->bl); + for(i=0;i<9;i++) + WBUFB(buf,20+i)= battle_attr_fix(100,i+1,md->def_ele); + + if(sd->status.party_id>0) + clif_send(buf,packet_len_table[0x18c],&sd->bl,PARTY_AREA); + else{ + memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x18c]); + WFIFOSET(sd->fd,packet_len_table[0x18c]); + } + return 0; +} +/*========================================== + * アイテム合成可能リスト + *------------------------------------------ + */ +int clif_skill_produce_mix_list(struct map_session_data *sd,int trigger) +{ + int i,c,view,fd; + nullpo_retr(0, sd); + + fd=sd->fd; + 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) ){ + 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; +} + +/*========================================== + * 状態異常アイコン/メッセージ表示 + *------------------------------------------ + */ +int clif_status_change(struct block_list *bl,int type,int flag) +{ + unsigned char buf[16]; + + nullpo_retr(0, bl); + + WBUFW(buf,0)=0x0196; + WBUFW(buf,2)=type; + WBUFL(buf,4)=bl->id; + WBUFB(buf,8)=flag; + clif_send(buf,packet_len_table[0x196],bl,AREA); + return 0; +} + +/*========================================== + * Send message (modified by [Yor]) + *------------------------------------------ + */ +int clif_displaymessage(const int fd, char* mes) +{ + int len_mes = strlen(mes); + + if (len_mes > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line. + WFIFOW(fd,0) = 0x8e; + WFIFOW(fd,2) = 5 + len_mes; // 4 + len + NULL teminate + memcpy(WFIFOP(fd,4), mes, len_mes + 1); + WFIFOSET(fd, 5 + len_mes); + } + + return 0; +} + +/*========================================== + * 天の声を送信する + *------------------------------------------ + */ +int clif_GMmessage(struct block_list *bl, char* mes, int len, int flag) +{ + unsigned char lbuf[255]; + unsigned char *buf = ((len + 16) >= sizeof(lbuf)) ? malloc(len+16) : lbuf; + int lp = (flag&0x10) ? 8 : 4; + + WBUFW(buf,0) = 0x9a; + WBUFW(buf,2) = len + lp; + WBUFL(buf,4) = 0x65756c62; + memcpy(WBUFP(buf,lp), mes, len); + flag &= 0x07; + clif_send(buf, WBUFW(buf,2), bl, + (flag==1) ? ALL_SAMEMAP: + (flag==2) ? AREA: + (flag==3) ? SELF: + ALL_CLIENT); + if (buf != lbuf) + free(buf); + return 0; +} + +/*========================================== + * HPSP回復エフェクトを送信する + *------------------------------------------ + */ +int clif_heal(int fd,int type,int val) +{ + WFIFOW(fd,0)=0x13d; + WFIFOW(fd,2)=type; + WFIFOW(fd,4)=val; + WFIFOSET(fd,packet_len_table[0x13d]); + + return 0; +} + +/*========================================== + * 復活する + *------------------------------------------ + */ +int clif_resurrection(struct block_list *bl,int type) +{ + unsigned char buf[16]; + + nullpo_retr(0, bl); + + if(bl->type==BL_PC) { // disguises [Valaris] + struct map_session_data *sd=((struct map_session_data *)bl); + if(sd && sd->disguise > 23 && sd->disguise < 4001) + clif_spawnpc(sd); + } + + WBUFW(buf,0)=0x148; + WBUFL(buf,2)=bl->id; + WBUFW(buf,6)=type; + + clif_send(buf,packet_len_table[0x148],bl,type==1 ? AREA : AREA_WOS); + + return 0; +} + +/*========================================== + * PVP実装?(仮) + *------------------------------------------ + */ +int clif_set0199(int fd,int type) +{ + WFIFOW(fd,0)=0x199; + WFIFOW(fd,2)=type; + WFIFOSET(fd,packet_len_table[0x199]); + + return 0; +} + +/*========================================== + * PVP実装?(仮) + *------------------------------------------ + */ +int clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type) +{ + nullpo_retr(0, sd); + + if(map[sd->bl.m].flag.nopvp) + return 0; + + if(type == 2) { + WFIFOW(sd->fd,0) = 0x19a; + WFIFOL(sd->fd,2) = sd->bl.id; + if(pvprank<=0) + pc_calc_pvprank(sd); + WFIFOL(sd->fd,6) = pvprank; + WFIFOL(sd->fd,10) = pvpnum; + WFIFOSET(sd->fd,packet_len_table[0x19a]); + } else { + char buf[32]; + + WBUFW(buf,0) = 0x19a; + WBUFL(buf,2) = sd->bl.id; + if(sd->status.option&0x46) + WBUFL(buf,6) = -1; + else + if(pvprank<=0) + pc_calc_pvprank(sd); + WBUFL(buf,6) = pvprank; + WBUFL(buf,10) = pvpnum; + if(!type) + clif_send(buf,packet_len_table[0x19a],&sd->bl,AREA); + else + clif_send(buf,packet_len_table[0x19a],&sd->bl,ALL_SAMEMAP); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_send0199(int map,int type) +{ + struct block_list bl; + char buf[16]; + + bl.m = map; + WBUFW(buf,0)=0x199; + WBUFW(buf,2)=type; + clif_send(buf,packet_len_table[0x199],&bl,ALL_SAMEMAP); + + return 0; +} + +/*========================================== + * 精錬エフェクトを送信する + *------------------------------------------ + */ +int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val) +{ + WFIFOW(fd,0)=0x188; + WFIFOW(fd,2)=fail; + WFIFOW(fd,4)=index+2; + WFIFOW(fd,6)=val; + WFIFOSET(fd,packet_len_table[0x188]); + + return 0; +} + +/*========================================== + * Wisp/page is transmitted to the destination player + *------------------------------------------ + */ +int clif_wis_message(int fd, char *nick, char *mes, int mes_len) // R 0097 <len>.w <nick>.24B <message>.?B +{ + WFIFOW(fd,0) = 0x97; + WFIFOW(fd,2) = mes_len + 24 + 4; + memcpy(WFIFOP(fd,4), nick, 24); + memcpy(WFIFOP(fd,28), mes, mes_len); + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +/*========================================== + * The transmission result of Wisp/page is transmitted to the source player + *------------------------------------------ + */ +int clif_wis_end(int fd, int flag) // R 0098 <type>.B: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target +{ + WFIFOW(fd,0) = 0x98; + WFIFOW(fd,2) = flag; + WFIFOSET(fd,packet_len_table[0x98]); + return 0; +} + +/*========================================== + * キャラID名前引き結果を送信する + *------------------------------------------ + */ +int clif_solved_charname(struct map_session_data *sd,int char_id) +{ + char *p= map_charid2nick(char_id); + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + if(p!=NULL){ + WFIFOW(fd,0)=0x194; + WFIFOL(fd,2)=char_id; + memcpy(WFIFOP(fd,6), p,24 ); + WFIFOSET(fd,packet_len_table[0x194]); + }else{ + map_reqchariddb(sd,char_id); + chrif_searchcharid(char_id); + } + return 0; +} + +/*========================================== + * カードの挿入可能リストを返す + *------------------------------------------ + */ +int clif_use_card(struct map_session_data *sd,int idx) +{ + nullpo_retr(0, sd); + + if(sd->inventory_data[idx]) { + int i,c; + int ep=sd->inventory_data[idx]->equip; + int fd=sd->fd; + WFIFOW(fd,0)=0x017b; + + for(i=c=0;i<MAX_INVENTORY;i++){ + int j; + + if(sd->inventory_data[i] == NULL) + continue; + if(sd->inventory_data[i]->type!=4 && sd->inventory_data[i]->type!=5) // 武器防具じゃない + continue; + if(sd->status.inventory[i].card[0]==0x00ff) // 製造武器 + continue; + if(sd->status.inventory[i].card[0]==(short)0xff00 || sd->status.inventory[i].card[0]==0x00fe) + continue; + if(sd->status.inventory[i].identify==0 ) // 未鑑定 + continue; + + if((sd->inventory_data[i]->equip&ep)==0) // 装備個所が違う + continue; + if(sd->inventory_data[i]->type==4 && ep==32) // 盾カードと両手武器 + continue; + + for(j=0;j<sd->inventory_data[i]->slot;j++){ + if( sd->status.inventory[i].card[j]==0 ) + break; + } + if(j==sd->inventory_data[i]->slot) // すでにカードが一杯 + continue; + + WFIFOW(fd,4+c*2)=i+2; + c++; + } + WFIFOW(fd,2)=4+c*2; + WFIFOSET(fd,WFIFOW(fd,2)); + } + + return 0; +} +/*========================================== + * カードの挿入終了 + *------------------------------------------ + */ +int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x17d; + WFIFOW(fd,2)=idx_equip+2; + WFIFOW(fd,4)=idx_card+2; + WFIFOB(fd,6)=flag; + WFIFOSET(fd,packet_len_table[0x17d]); + return 0; +} + +/*========================================== + * 鑑定可能アイテムリスト送信 + *------------------------------------------ + */ +int clif_item_identify_list(struct map_session_data *sd) +{ + int i,c; + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + + WFIFOW(fd,0)=0x177; + for(i=c=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify!=1){ + WFIFOW(fd,c*2+4)=i+2; + c++; + } + } + if(c > 0) { + WFIFOW(fd,2)=c*2+4; + WFIFOSET(fd,WFIFOW(fd,2)); + } + return 0; +} + +/*========================================== + * 鑑定結果 + *------------------------------------------ + */ +int clif_item_identified(struct map_session_data *sd,int idx,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd, 0)=0x179; + WFIFOW(fd, 2)=idx+2; + WFIFOB(fd, 4)=flag; + WFIFOSET(fd,packet_len_table[0x179]); + return 0; +} + +/*========================================== + * 修理可能アイテムリスト送信 + * ※実際のパケットがわからないので動作しません + *------------------------------------------ + */ +int clif_item_repair_list(struct map_session_data *sd) +{ + int i,c; + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + + WFIFOW(fd,0)=0x0; + for(i=c=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].broken!=0){ + WFIFOW(fd,c*2+4)=i+2; + c++; + } + } + if(c > 0) { + WFIFOW(fd,2)=c*2+4; + WFIFOSET(fd,WFIFOW(fd,2)); + } + return 0; +} + +/*========================================== + * アイテムによる一時的なスキル効果 + *------------------------------------------ + */ +int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name) +{ + int range,fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd, 0)=0x147; + WFIFOW(fd, 2)=skillid; + WFIFOW(fd, 4)=skill_get_inf(skillid); + WFIFOW(fd, 6)=0; + WFIFOW(fd, 8)=skilllv; + WFIFOW(fd,10)=skill_get_sp(skillid,skilllv); + range = skill_get_range(skillid,skilllv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + WFIFOW(fd,12)=range; + memcpy(WFIFOP(fd,14),name,24); + WFIFOB(fd,38)=0; + WFIFOSET(fd,packet_len_table[0x147]); + return 0; +} + +/*========================================== + * カートにアイテム追加 + *------------------------------------------ + */ +int clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail) +{ + int view,j,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf=WFIFOP(fd,0); + if(n<0 || n>=MAX_CART || sd->status.cart[n].nameid<=0) + return 1; + + WBUFW(buf,0)=0x124; + WBUFW(buf,2)=n+2; + WBUFL(buf,4)=amount; + if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0) + WBUFW(buf,8)=view; + else + WBUFW(buf,8)=sd->status.cart[n].nameid; + WBUFB(buf,10)=sd->status.cart[n].identify; + if(sd->status.cart[n].broken==1) //is weapon broken [Valaris] + WBUFB(buf,11)=1; + else + WBUFB(buf,11)=sd->status.cart[n].attribute; + WBUFB(buf,12)=sd->status.cart[n].refine; + if(sd->status.cart[n].card[0]==0x00ff || sd->status.cart[n].card[0]==0x00fe || sd->status.cart[n].card[0]==(short)0xff00) { + WBUFW(buf,13)=sd->status.cart[n].card[0]; + WBUFW(buf,15)=sd->status.cart[n].card[1]; + WBUFW(buf,17)=sd->status.cart[n].card[2]; + WBUFW(buf,19)=sd->status.cart[n].card[3]; + } else { + if(sd->status.cart[n].card[0] > 0 && (j=itemdb_viewid(sd->status.cart[n].card[0])) > 0) + WBUFW(buf,13)= j; + else + WBUFW(buf,13)= sd->status.cart[n].card[0]; + if(sd->status.cart[n].card[1] > 0 && (j=itemdb_viewid(sd->status.cart[n].card[1])) > 0) + WBUFW(buf,15)= j; + else + WBUFW(buf,15)= sd->status.cart[n].card[1]; + if(sd->status.cart[n].card[2] > 0 && (j=itemdb_viewid(sd->status.cart[n].card[2])) > 0) + WBUFW(buf,17)= j; + else + WBUFW(buf,17)= sd->status.cart[n].card[2]; + if(sd->status.cart[n].card[3] > 0 && (j=itemdb_viewid(sd->status.cart[n].card[3])) > 0) + WBUFW(buf,19)= j; + else + WBUFW(buf,19)= sd->status.cart[n].card[3]; + } + WFIFOSET(fd,packet_len_table[0x124]); + return 0; +} + +/*========================================== + * カートからアイテム削除 + *------------------------------------------ + */ +int clif_cart_delitem(struct map_session_data *sd,int n,int amount) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + + WFIFOW(fd,0)=0x125; + WFIFOW(fd,2)=n+2; + WFIFOL(fd,4)=amount; + + WFIFOSET(fd,packet_len_table[0x125]); + + return 0; +} + +/*========================================== + * カートのアイテムリスト + *------------------------------------------ + */ +int clif_cart_itemlist(struct map_session_data *sd) +{ + struct item_data *id; + int i,n,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf = WFIFOP(fd,0); +#if PACKETVER < 5 + WBUFW(buf,0)=0x123; + 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)=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,2)=4+n*10; + WFIFOSET(fd,WFIFOW(fd,2)); + } +#else + WBUFW(buf,0)=0x1ef; + for(i=0,n=0;i<MAX_CART;i++){ + if(sd->status.cart[i].nameid<=0) + continue; + id = itemdb_search(sd->status.cart[i].nameid); + if(itemdb_isequip2(id)) + continue; + WBUFW(buf,n*18+4)=i+2; + if(id->view_id > 0) + WBUFW(buf,n*18+6)=id->view_id; + else + WBUFW(buf,n*18+6)=sd->status.cart[i].nameid; + WBUFB(buf,n*18+8)=id->type; + WBUFB(buf,n*18+9)=sd->status.cart[i].identify; + WBUFW(buf,n*18+10)=sd->status.cart[i].amount; + WBUFW(buf,n*18+12)=0; + WBUFW(buf,n*18+14)=sd->status.cart[i].card[0]; + WBUFW(buf,n*18+16)=sd->status.cart[i].card[1]; + WBUFW(buf,n*18+18)=sd->status.cart[i].card[2]; + WBUFW(buf,n*18+20)=sd->status.cart[i].card[3]; + n++; + } + if(n){ + WBUFW(buf,2)=4+n*18; + WFIFOSET(fd,WFIFOW(fd,2)); + } +#endif + return 0; +} + +/*========================================== + * カートの装備品リスト + *------------------------------------------ + */ +int clif_cart_equiplist(struct map_session_data *sd) +{ + struct item_data *id; + int i,j,n,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf = WFIFOP(fd,0); + + WBUFW(buf,0)=0x122; + for(i=0,n=0;i<MAX_INVENTORY;i++){ + if(sd->status.cart[i].nameid<=0) + continue; + id = itemdb_search(sd->status.cart[i].nameid); + if(!itemdb_isequip2(id)) + continue; + WBUFW(buf,n*20+4)=i+2; + if(id->view_id > 0) + WBUFW(buf,n*20+6)=id->view_id; + else + WBUFW(buf,n*20+6)=sd->status.cart[i].nameid; + WBUFB(buf,n*20+8)=id->type; + WBUFB(buf,n*20+9)=sd->status.cart[i].identify; + WBUFW(buf,n*20+10)=id->equip; + WBUFW(buf,n*20+12)=sd->status.cart[i].equip; + if(sd->status.cart[i].broken==1) + WBUFB(buf,n*20+14)=1; //is weapon broken [Valaris] + else + WBUFB(buf,n*20+14)=sd->status.cart[i].attribute; + WBUFB(buf,n*20+15)=sd->status.cart[i].refine; + if(sd->status.cart[i].card[0]==0x00ff || sd->status.cart[i].card[0]==0x00fe || sd->status.cart[i].card[0]==(short)0xff00) { + WBUFW(buf,n*20+16)=sd->status.cart[i].card[0]; + WBUFW(buf,n*20+18)=sd->status.cart[i].card[1]; + WBUFW(buf,n*20+20)=sd->status.cart[i].card[2]; + WBUFW(buf,n*20+22)=sd->status.cart[i].card[3]; + } else { + if(sd->status.cart[i].card[0] > 0 && (j=itemdb_viewid(sd->status.cart[i].card[0])) > 0) + WBUFW(buf,n*20+16)= j; + else + WBUFW(buf,n*20+16)= sd->status.cart[i].card[0]; + if(sd->status.cart[i].card[1] > 0 && (j=itemdb_viewid(sd->status.cart[i].card[1])) > 0) + WBUFW(buf,n*20+18)= j; + else + WBUFW(buf,n*20+18)= sd->status.cart[i].card[1]; + if(sd->status.cart[i].card[2] > 0 && (j=itemdb_viewid(sd->status.cart[i].card[2])) > 0) + WBUFW(buf,n*20+20)= j; + else + WBUFW(buf,n*20+20)= sd->status.cart[i].card[2]; + if(sd->status.cart[i].card[3] > 0 && (j=itemdb_viewid(sd->status.cart[i].card[3])) > 0) + WBUFW(buf,n*20+22)= j; + else + WBUFW(buf,n*20+22)= sd->status.cart[i].card[3]; + } + n++; + } + if(n){ + WBUFW(buf,2)=4+n*20; + WFIFOSET(fd,WFIFOW(fd,2)); + } + return 0; +} + +/*========================================== + * 露店開設 + *------------------------------------------ + */ +int clif_openvendingreq(struct map_session_data *sd,int num) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x12d; + WFIFOW(fd,2)=num; + WFIFOSET(fd,packet_len_table[0x12d]); + + return 0; +} + +/*========================================== + * 露店看板表示 + *------------------------------------------ + */ +int clif_showvendingboard(struct block_list* bl,char *message,int fd) +{ + unsigned char buf[128]; + + nullpo_retr(0, bl); + + WBUFW(buf,0)=0x131; + WBUFL(buf,2)=bl->id; + strncpy(WBUFP(buf,6),message,80); + if(fd){ + memcpy(WFIFOP(fd,0),buf,packet_len_table[0x131]); + WFIFOSET(fd,packet_len_table[0x131]); + }else{ + clif_send(buf,packet_len_table[0x131],bl,AREA_WOS); + } + return 0; +} + +/*========================================== + * 露店看板消去 + *------------------------------------------ + */ +int clif_closevendingboard(struct block_list* bl,int fd) +{ + unsigned char buf[16]; + + nullpo_retr(0, bl); + + WBUFW(buf,0)=0x132; + WBUFL(buf,2)=bl->id; + if(fd){ + memcpy(WFIFOP(fd,0),buf,packet_len_table[0x132]); + WFIFOSET(fd,packet_len_table[0x132]); + }else{ + clif_send(buf,packet_len_table[0x132],bl,AREA_WOS); + } + + return 0; +} +/*========================================== + * 露店アイテムリスト + *------------------------------------------ + */ +int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending) +{ + struct item_data *data; + int i,j,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; + buf = WFIFOP(fd,0); + WBUFW(buf,0)=0x133; + WBUFL(buf,4)=id; + 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)=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; + if(vsd->status.cart[index].broken==1) + WBUFB(buf,20+n*22)=1; //is weapon broken [Valaris] + else + WBUFB(buf,20+n*22)=vsd->status.cart[index].attribute; + WBUFB(buf,21+n*22)=vsd->status.cart[index].refine; + if(vsd->status.cart[index].card[0]==0x00ff || vsd->status.cart[index].card[0]==0x00fe || vsd->status.cart[index].card[0]==(short)0xff00) { + WBUFW(buf,22+n*22)=vsd->status.cart[index].card[0]; + WBUFW(buf,24+n*22)=vsd->status.cart[index].card[1]; + WBUFW(buf,26+n*22)=vsd->status.cart[index].card[2]; + WBUFW(buf,28+n*22)=vsd->status.cart[index].card[3]; + } else { + if(vsd->status.cart[index].card[0] > 0 && (j=itemdb_viewid(vsd->status.cart[index].card[0])) > 0) + WBUFW(buf,22+n*22)= j; + else + WBUFW(buf,22+n*22)= vsd->status.cart[index].card[0]; + if(vsd->status.cart[index].card[1] > 0 && (j=itemdb_viewid(vsd->status.cart[index].card[1])) > 0) + WBUFW(buf,24+n*22)= j; + else + WBUFW(buf,24+n*22)= vsd->status.cart[index].card[1]; + if(vsd->status.cart[index].card[2] > 0 && (j=itemdb_viewid(vsd->status.cart[index].card[2])) > 0) + WBUFW(buf,26+n*22)= j; + else + WBUFW(buf,26+n*22)= vsd->status.cart[index].card[2]; + if(vsd->status.cart[index].card[3] > 0 && (j=itemdb_viewid(vsd->status.cart[index].card[3])) > 0) + WBUFW(buf,28+n*22)= j; + else + WBUFW(buf,28+n*22)= vsd->status.cart[index].card[3]; + } + n++; + } + if(n > 0){ + WBUFW(buf,2)=8+n*22; + WFIFOSET(fd,WFIFOW(fd,2)); + } + + return 0; +} + +/*========================================== + * 露店アイテム購入失敗 + *------------------------------------------ +*/ +int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x135; + WFIFOW(fd,2)=index+2; + WFIFOW(fd,4)=amount; + WFIFOB(fd,6)=fail; + WFIFOSET(fd,packet_len_table[0x135]); + + return 0; +} + +/*========================================== + * 露店開設成功 + *------------------------------------------ +*/ +int clif_openvending(struct map_session_data *sd,int id,struct vending *vending) +{ + struct item_data *data; + int i,j,n,index,fd; + unsigned char *buf; + + nullpo_retr(0, sd); + + fd=sd->fd; + buf = WFIFOP(fd,0); + + WBUFW(buf,0)=0x136; + WBUFL(buf,4)=id; + 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].broken==1) // Prevent unidentified and broken items from being sold [Valaris] + continue; + data = itemdb_search(sd->status.cart[index].nameid); + WBUFB(buf,16+n*22)=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; + if(sd->status.cart[index].broken==1) + WBUFB(buf,20+n*22)=1; // is weapon broken [Valaris] + else + WBUFB(buf,20+n*22)=sd->status.cart[index].attribute; + WBUFB(buf,21+n*22)=sd->status.cart[index].refine; + if(sd->status.cart[index].card[0]==0x00ff || sd->status.cart[index].card[0]==0x00fe || sd->status.cart[index].card[0]==(short)0xff00) { + WBUFW(buf,22+n*22)=sd->status.cart[index].card[0]; + WBUFW(buf,24+n*22)=sd->status.cart[index].card[1]; + WBUFW(buf,26+n*22)=sd->status.cart[index].card[2]; + WBUFW(buf,28+n*22)=sd->status.cart[index].card[3]; + } else { + if(sd->status.cart[index].card[0] > 0 && (j=itemdb_viewid(sd->status.cart[index].card[0])) > 0) + WBUFW(buf,22+n*22)= j; + else + WBUFW(buf,22+n*22)= sd->status.cart[index].card[0]; + if(sd->status.cart[index].card[1] > 0 && (j=itemdb_viewid(sd->status.cart[index].card[1])) > 0) + WBUFW(buf,24+n*22)= j; + else + WBUFW(buf,24+n*22)= sd->status.cart[index].card[1]; + if(sd->status.cart[index].card[2] > 0 && (j=itemdb_viewid(sd->status.cart[index].card[2])) > 0) + WBUFW(buf,26+n*22)= j; + else + WBUFW(buf,26+n*22)= sd->status.cart[index].card[2]; + if(sd->status.cart[index].card[3] > 0 && (j=itemdb_viewid(sd->status.cart[index].card[3])) > 0) + WBUFW(buf,28+n*22)= j; + else + WBUFW(buf,28+n*22)= sd->status.cart[index].card[3]; + } + n++; + } + if(n > 0){ + WBUFW(buf,2)=8+n*22; + WFIFOSET(fd,WFIFOW(fd,2)); + } + + return n; +} + +/*========================================== + * 露店アイテム販売報告 + *------------------------------------------ +*/ +int clif_vendingreport(struct map_session_data *sd,int index,int amount) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x137; + WFIFOW(fd,2)=index+2; + WFIFOW(fd,4)=amount; + WFIFOSET(fd,packet_len_table[0x137]); + + return 0; +} + +/*========================================== + * パーティ作成完了 + *------------------------------------------ + */ +int clif_party_created(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xfa; + WFIFOB(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0xfa]); + return 0; +} +/*========================================== + * パーティ情報送信 + *------------------------------------------ + */ +int clif_party_info(struct party *p,int fd) +{ + unsigned char buf[1024]; + int i,c; + struct map_session_data *sd=NULL; + + nullpo_retr(0, p); + + WBUFW(buf,0)=0xfb; + memcpy(WBUFP(buf,4),p->name,24); + for(i=c=0;i<MAX_PARTY;i++){ + struct party_member *m=&p->member[i]; + if(m->account_id>0){ + if(sd==NULL) sd=m->sd; + WBUFL(buf,28+c*46)=m->account_id; + memcpy(WBUFP(buf,28+c*46+ 4),m->name,24); + memcpy(WBUFP(buf,28+c*46+28),m->map,16); + WBUFB(buf,28+c*46+44)=(m->leader)?0:1; + WBUFB(buf,28+c*46+45)=(m->online)?0:1; + c++; + } + } + WBUFW(buf,2)=28+c*46; + if(fd>=0){ // fdが設定されてるならそれに送る + memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2)); + WFIFOSET(fd,WFIFOW(fd,2)); + return 9; + } + if(sd!=NULL) + clif_send(buf,WBUFW(buf,2),&sd->bl,PARTY); + return 0; +} +/*========================================== + * パーティ勧誘 + *------------------------------------------ + */ +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; + + WFIFOW(fd,0)=0xfe; + WFIFOL(fd,2)=sd->status.account_id; + memcpy(WFIFOP(fd,6),p->name,24); + WFIFOSET(fd,packet_len_table[0xfe]); + return 0; +} + +/*========================================== + * パーティ勧誘結果 + *------------------------------------------ + */ +int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xfd; + memcpy(WFIFOP(fd,2),nick,24); + WFIFOB(fd,26)=flag; + WFIFOSET(fd,packet_len_table[0xfd]); + return 0; +} + +/*========================================== + * パーティ設定送信 + * flag & 0x001=exp変更ミス + * 0x010=item変更ミス + * 0x100=一人にのみ送信 + *------------------------------------------ + */ +int clif_party_option(struct party *p,struct map_session_data *sd,int flag) +{ + unsigned char buf[16]; + + nullpo_retr(0, p); + +// if(battle_config.etc_log) +// printf("clif_party_option: %d %d %d\n",p->exp,p->item,flag); + if(sd==NULL && flag==0){ + int i; + for(i=0;i<MAX_PARTY;i++) + if((sd=map_id2sd(p->member[i].account_id))!=NULL) + break; + } + if(sd==NULL) + return 0; + WBUFW(buf,0)=0x101; + WBUFW(buf,2)=((flag&0x01)?2:p->exp); + WBUFW(buf,4)=((flag&0x10)?2:p->item); + if(flag==0) + clif_send(buf,packet_len_table[0x101],&sd->bl,PARTY); + else { + memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x101]); + WFIFOSET(sd->fd,packet_len_table[0x101]); + } + return 0; +} +/*========================================== + * パーティ脱退(脱退前に呼ぶこと) + *------------------------------------------ + */ +int clif_party_leaved(struct party *p,struct map_session_data *sd,int account_id,char *name,int flag) +{ + unsigned char buf[64]; + int i; + + nullpo_retr(0, p); + + WBUFW(buf,0)=0x105; + WBUFL(buf,2)=account_id; + memcpy(WBUFP(buf,6),name,24); + WBUFB(buf,30)=flag&0x0f; + + if((flag&0xf0)==0){ + if(sd==NULL) + for(i=0;i<MAX_PARTY;i++) + if((sd=p->member[i].sd)!=NULL) + break; + if (sd!=NULL) + clif_send(buf,packet_len_table[0x105],&sd->bl,PARTY); + } else if (sd!=NULL) { + memcpy(WFIFOP(sd->fd,0),buf,packet_len_table[0x105]); + WFIFOSET(sd->fd,packet_len_table[0x105]); + } + return 0; +} +/*========================================== + * パーティメッセージ送信 + *------------------------------------------ + */ +int clif_party_message(struct party *p,int account_id,char *mes,int len) +{ + struct map_session_data *sd; + int i; + + nullpo_retr(0, p); + + for(i=0;i<MAX_PARTY;i++){ + if((sd=p->member[i].sd)!=NULL) + break; + } + if(sd!=NULL){ + unsigned char buf[1024]; + WBUFW(buf,0)=0x109; + WBUFW(buf,2)=len+8; + WBUFL(buf,4)=account_id; + memcpy(WBUFP(buf,8),mes,len); + clif_send(buf,len+8,&sd->bl,PARTY); + } + return 0; +} +/*========================================== + * パーティ座標通知 + *------------------------------------------ + */ +int clif_party_xy(struct party *p,struct map_session_data *sd) +{ + unsigned char buf[16]; + + nullpo_retr(0, sd); + + WBUFW(buf,0)=0x107; + WBUFL(buf,2)=sd->status.account_id; + WBUFW(buf,6)=sd->bl.x; + WBUFW(buf,8)=sd->bl.y; + clif_send(buf,packet_len_table[0x107],&sd->bl,PARTY_SAMEMAP_WOS); +// if(battle_config.etc_log) +// printf("clif_party_xy %d\n",sd->status.account_id); + return 0; +} +/*========================================== + * パーティHP通知 + *------------------------------------------ + */ +int clif_party_hp(struct party *p,struct map_session_data *sd) +{ + unsigned char buf[16]; + + nullpo_retr(0, sd); + + WBUFW(buf,0)=0x106; + WBUFL(buf,2)=sd->status.account_id; + WBUFW(buf,6)=(sd->status.hp > 0x7fff)? 0x7fff:sd->status.hp; + WBUFW(buf,8)=(sd->status.max_hp > 0x7fff)? 0x7fff:sd->status.max_hp; + clif_send(buf,packet_len_table[0x106],&sd->bl,PARTY_AREA_WOS); +// if(battle_config.etc_log) +// printf("clif_party_hp %d\n",sd->status.account_id); + return 0; +} +/*========================================== + * パーティ場所移動(未使用) + *------------------------------------------ + */ +int clif_party_move(struct party *p,struct map_session_data *sd,int online) +{ + unsigned char buf[128]; + + nullpo_retr(0, sd); + nullpo_retr(0, p); + + WBUFW(buf, 0)=0x104; + WBUFL(buf, 2)=sd->status.account_id; + WBUFL(buf, 6)=0; + WBUFW(buf,10)=sd->bl.x; + WBUFW(buf,12)=sd->bl.y; + WBUFB(buf,14)=!online; + memcpy(WBUFP(buf,15),p->name,24); + memcpy(WBUFP(buf,39),sd->status.name,24); + memcpy(WBUFP(buf,63),map[sd->bl.m].name,16); + clif_send(buf,packet_len_table[0x104],&sd->bl,PARTY); + return 0; +} +/*========================================== + * 攻撃するために移動が必要 + *------------------------------------------ + */ +int clif_movetoattack(struct map_session_data *sd,struct block_list *bl) +{ + int fd; + + nullpo_retr(0, sd); + nullpo_retr(0, bl); + + fd=sd->fd; + WFIFOW(fd, 0)=0x139; + WFIFOL(fd, 2)=bl->id; + WFIFOW(fd, 6)=bl->x; + WFIFOW(fd, 8)=bl->y; + WFIFOW(fd,10)=sd->bl.x; + WFIFOW(fd,12)=sd->bl.y; + WFIFOW(fd,14)=sd->attackrange; + WFIFOSET(fd,packet_len_table[0x139]); + return 0; +} +/*========================================== + * 製造エフェクト + *------------------------------------------ + */ +int clif_produceeffect(struct map_session_data *sd,int flag,int nameid) +{ + int view,fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + // 名前の登録と送信を先にしておく + if( map_charid2nick(sd->status.char_id)==NULL ) + map_addchariddb(sd->status.char_id,sd->status.name); + clif_solved_charname(sd,sd->status.char_id); + + WFIFOW(fd, 0)=0x18f; + WFIFOW(fd, 2)=flag; + if((view = itemdb_viewid(nameid)) > 0) + WFIFOW(fd, 4)=view; + else + WFIFOW(fd, 4)=nameid; + WFIFOSET(fd,packet_len_table[0x18f]); + return 0; +} + +// pet +int clif_catch_process(struct map_session_data *sd) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + 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; + WFIFOW(fd,0)=0x1a0; + WFIFOB(fd,2)=data; + WFIFOSET(fd,packet_len_table[0x1a0]); + + return 0; +} + +/*========================================== + * pet卵リスト作成 + *------------------------------------------ + */ +int clif_sendegg(struct map_session_data *sd) +{ + //R 01a6 <len>.w <index>.w* + int i,n=0,fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + 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); + + fd=sd->fd; + 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; + WFIFOW(fd,0)=0x1a2; + memcpy(WFIFOP(fd,2),sd->pet.name,24); + 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; + WFIFOW(fd,0)=0x1a3; + WFIFOB(fd,2)=fail; + WFIFOW(fd,3)=foodid; + WFIFOSET(fd,packet_len_table[0x1a3]); + + return 0; +} + +/*========================================== + * オートスペル リスト送信 + *------------------------------------------ + */ +int clif_autospell(struct map_session_data *sd,int skilllv) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd, 0)=0x1cd; + + if(skilllv>0 && pc_checkskill(sd,MG_NAPALMBEAT)>0) + WFIFOL(fd,2)= MG_NAPALMBEAT; + else + WFIFOL(fd,2)= 0x00000000; + if(skilllv>1 && pc_checkskill(sd,MG_COLDBOLT)>0) + WFIFOL(fd,6)= MG_COLDBOLT; + else + WFIFOL(fd,6)= 0x00000000; + if(skilllv>1 && pc_checkskill(sd,MG_FIREBOLT)>0) + WFIFOL(fd,10)= MG_FIREBOLT; + else + WFIFOL(fd,10)= 0x00000000; + if(skilllv>1 && pc_checkskill(sd,MG_LIGHTNINGBOLT)>0) + WFIFOL(fd,14)= MG_LIGHTNINGBOLT; + else + WFIFOL(fd,14)= 0x00000000; + if(skilllv>4 && pc_checkskill(sd,MG_SOULSTRIKE)>0) + WFIFOL(fd,18)= MG_SOULSTRIKE; + else + WFIFOL(fd,18)= 0x00000000; + if(skilllv>7 && pc_checkskill(sd,MG_FIREBALL)>0) + WFIFOL(fd,22)= MG_FIREBALL; + else + WFIFOL(fd,22)= 0x00000000; + if(skilllv>9 && pc_checkskill(sd,MG_FROSTDIVER)>0) + WFIFOL(fd,26)= MG_FROSTDIVER; + else + WFIFOL(fd,26)= 0x00000000; + + WFIFOSET(fd,packet_len_table[0x1cd]); + return 0; +} + +/*========================================== + * ディボーションの青い糸 + *------------------------------------------ + */ +int clif_devotion(struct map_session_data *sd,int target) +{ + unsigned char buf[56]; + int n; + + nullpo_retr(0, sd); + + WBUFW(buf,0)=0x1cf; + WBUFL(buf,2)=sd->bl.id; +// WBUFL(buf,6)=target; + for(n=0;n<5;n++) + WBUFL(buf,6+4*n)=sd->dev.val2[n]; +// WBUFL(buf,10+4*n)=0; + WBUFB(buf,26)=8; + WBUFB(buf,27)=0; + + clif_send(buf,packet_len_table[0x1cf],&sd->bl,AREA); + return 0; +} + +/*========================================== + * 氣球 + *------------------------------------------ + */ +int clif_spiritball(struct map_session_data *sd) +{ + unsigned char buf[16]; + + nullpo_retr(0, sd); + + WBUFW(buf,0)=0x1d0; + WBUFL(buf,2)=sd->bl.id; + WBUFW(buf,6)=sd->spiritball; + clif_send(buf,packet_len_table[0x1d0],&sd->bl,AREA); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_combo_delay(struct block_list *bl,int wait) +{ + unsigned char buf[32]; + + nullpo_retr(0, bl); + + WBUFW(buf,0)=0x1d2; + WBUFL(buf,2)=bl->id; + WBUFL(buf,6)=wait; + clif_send(buf,packet_len_table[0x1d2],bl,AREA); + + return 0; +} +/*========================================== + *白刃取り + *------------------------------------------ + */ +int clif_bladestop(struct block_list *src,struct block_list *dst, + int 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; + char buf[32]; + + bl.m = m; + bl.x = x; + bl.y = y; + WBUFW(buf,0) = 0x192; + WBUFW(buf,2) = x; + WBUFW(buf,4) = y; + WBUFW(buf,6) = cell_type; + memcpy(WBUFP(buf,8),map[m].name,16); + if(!type) + clif_send(buf,packet_len_table[0x192],&bl,AREA); + else + clif_send(buf,packet_len_table[0x192],&bl,ALL_SAMEMAP); + + return 0; +} + +/*========================================== + * MVPエフェクト + *------------------------------------------ + */ +int clif_mvp_effect(struct map_session_data *sd) +{ + unsigned char buf[16]; + + nullpo_retr(0, sd); + + WBUFW(buf,0)=0x10c; + WBUFL(buf,2)=sd->bl.id; + clif_send(buf,packet_len_table[0x10c],&sd->bl,AREA); + return 0; +} +/*========================================== + * MVPアイテム所得 + *------------------------------------------ + */ +int clif_mvp_item(struct map_session_data *sd,int nameid) +{ + int view,fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x10a; + if((view = itemdb_viewid(nameid)) > 0) + WFIFOW(fd,2)=view; + else + WFIFOW(fd,2)=nameid; + WFIFOSET(fd,packet_len_table[0x10a]); + return 0; +} +/*========================================== + * MVP経験値所得 + *------------------------------------------ + */ +int clif_mvp_exp(struct map_session_data *sd,int exp) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x10b; + WFIFOL(fd,2)=exp; + WFIFOSET(fd,packet_len_table[0x10b]); + return 0; +} + +/*========================================== + * ギルド作成可否通知 + *------------------------------------------ + */ +int clif_guild_created(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x167; + WFIFOB(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0x167]); + return 0; +} +/*========================================== + * ギルド所属通知 + *------------------------------------------ + */ +int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g) +{ + int ps,fd; + + nullpo_retr(0, sd); + nullpo_retr(0, g); + + fd=sd->fd; + ps=guild_getposition(sd,g); + + memset(WFIFOP(fd,0),0,packet_len_table[0x16c]); + WFIFOW(fd,0)=0x16c; + WFIFOL(fd,2)=g->guild_id; + WFIFOL(fd,6)=g->emblem_id; + WFIFOL(fd,10)=g->position[ps].mode; + memcpy(WFIFOP(fd,19),g->name,24); + WFIFOSET(fd,packet_len_table[0x16c]); + return 0; +} +/*========================================== + * ギルドメンバログイン通知 + *------------------------------------------ + */ +int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag) +{ + unsigned char buf[64]; + + nullpo_retr(0, g); + + WBUFW(buf, 0)=0x16d; + WBUFL(buf, 2)=g->member[idx].account_id; + WBUFL(buf, 6)=g->member[idx].char_id; + WBUFL(buf,10)=flag; + if(g->member[idx].sd==NULL){ + struct map_session_data *sd=guild_getavailablesd(g); + if(sd!=NULL) + clif_send(buf,packet_len_table[0x16d],&sd->bl,GUILD); + }else + clif_send(buf,packet_len_table[0x16d],&g->member[idx].sd->bl,GUILD_WOS); + return 0; +} +/*========================================== + * ギルドマスター通知(14dへの応答) + *------------------------------------------ + */ +int clif_guild_masterormember(struct map_session_data *sd) +{ + int type=0x57,fd; + struct guild *g; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g!=NULL && strcmp(g->master,sd->status.name)==0) + type=0xd7; + WFIFOW(fd,0)=0x14e; + WFIFOL(fd,2)=type; + WFIFOSET(fd,packet_len_table[0x14e]); + return 0; +} +/*========================================== + * Basic Info (Territories [Valaris]) + *------------------------------------------ + */ +int clif_guild_basicinfo(struct map_session_data *sd) +{ + int fd,i,t=0; + struct guild *g; + struct guild_castle *gc=NULL; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + + WFIFOW(fd, 0)=0x1b6;//0x150; + WFIFOL(fd, 2)=g->guild_id; + WFIFOL(fd, 6)=g->guild_lv; + WFIFOL(fd,10)=g->connect_member; + WFIFOL(fd,14)=g->max_member; + WFIFOL(fd,18)=g->average_lv; + WFIFOL(fd,22)=g->exp; + WFIFOL(fd,26)=g->next_exp; + WFIFOL(fd,30)=0; // 上納 + WFIFOL(fd,34)=0; // VW(性格の悪さ?:性向グラフ左右) + WFIFOL(fd,38)=0; // RF(正義の度合い?:性向グラフ上下) + WFIFOL(fd,42)=0; // 人数? + memcpy(WFIFOP(fd,46),g->name,24); + memcpy(WFIFOP(fd,70),g->master,24); + + for(i=0;i<MAX_GUILDCASTLE;i++){ + gc=guild_castle_search(i); + if(!gc) continue; + if(g->guild_id == gc->guild_id) t++; + } + + if (t==1) memcpy(WFIFOP(fd,94),"One Castle",20); + else if (t==2) memcpy(WFIFOP(fd,94),"Two Castles",20); + else if (t==3) memcpy(WFIFOP(fd,94),"Three Castles",20); + else if (t==4) memcpy(WFIFOP(fd,94),"Four Castles",20); + else if (t==5) memcpy(WFIFOP(fd,94),"Five Castles",20); + else if (t==6) memcpy(WFIFOP(fd,94),"Six Castles",20); + else if (t==7) memcpy(WFIFOP(fd,94),"Seven Castles",20); + else if (t==8) memcpy(WFIFOP(fd,94),"Eight Castles",20); + else if (t==9) memcpy(WFIFOP(fd,94),"Nine Castles",20); + else if (t==10) memcpy(WFIFOP(fd,94),"Ten Castles",20); + else if (t==11) memcpy(WFIFOP(fd,94),"Eleven Castles",20); + else if (t==12) memcpy(WFIFOP(fd,94),"Twelve Castles",20); + else if (t==13) memcpy(WFIFOP(fd,94),"Thirteen Castles",20); + else if (t==14) memcpy(WFIFOP(fd,94),"Fourteen Castles",20); + else if (t==15) memcpy(WFIFOP(fd,94),"Fifteen Castles",20); + else if (t==16) memcpy(WFIFOP(fd,94),"Sixteen Castles",20); + else if (t==17) memcpy(WFIFOP(fd,94),"Seventeen Castles",20); + else if (t==18) memcpy(WFIFOP(fd,94),"Eighteen Castles",20); + else if (t==19) memcpy(WFIFOP(fd,94),"Nineteen Castles",20); + else if (t==20) memcpy(WFIFOP(fd,94),"Twenty Castles",20); + else if (t==21) memcpy(WFIFOP(fd,94),"Twenty One Castles",20); + else if (t==22) memcpy(WFIFOP(fd,94),"Twenty Two Castles",20); + else if (t==23) memcpy(WFIFOP(fd,94),"Twenty Three Castles",20); + else if (t==24) memcpy(WFIFOP(fd,94),"Twenty Four Castles",20); + else if (t==MAX_GUILDCASTLE) memcpy(WFIFOP(fd,94),"Total Domination",20); + else memcpy(WFIFOP(fd,94),"None Taken",20); + + WFIFOSET(fd,packet_len_table[WFIFOW(fd,0)]); + clif_guild_emblem(sd,g); // Guild emblem vanish fix [Valaris] + return 0; +} + +/*========================================== + * ギルド同盟/敵対情報 + *------------------------------------------ + */ +int clif_guild_allianceinfo(struct map_session_data *sd) +{ + int fd,i,c; + struct guild *g; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + WFIFOW(fd, 0)=0x14c; + for(i=c=0;i<MAX_GUILDALLIANCE;i++){ + struct guild_alliance *a=&g->alliance[i]; + if(a->guild_id>0){ + WFIFOL(fd,c*32+4)=a->opposition; + WFIFOL(fd,c*32+8)=a->guild_id; + memcpy(WFIFOP(fd,c*32+12),a->name,24); + c++; + } + } + WFIFOW(fd, 2)=c*32+4; + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +/*========================================== + * ギルドメンバーリスト + *------------------------------------------ + */ +int clif_guild_memberlist(struct map_session_data *sd) +{ + int fd; + int i,c; + struct guild *g; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + + WFIFOW(fd, 0)=0x154; + for(i=0,c=0;i<g->max_member;i++){ + struct guild_member *m=&g->member[i]; + if(m->account_id==0) + continue; + WFIFOL(fd,c*104+ 4)=m->account_id; + WFIFOL(fd,c*104+ 8)=m->char_id; + WFIFOW(fd,c*104+12)=m->hair; + WFIFOW(fd,c*104+14)=m->hair_color; + WFIFOW(fd,c*104+16)=m->gender; + WFIFOW(fd,c*104+18)=m->class; + WFIFOW(fd,c*104+20)=m->lv; + WFIFOL(fd,c*104+22)=m->exp; + WFIFOL(fd,c*104+26)=m->online; + WFIFOL(fd,c*104+30)=m->position; + memset(WFIFOP(fd,c*104+34),0,50); // メモ? + memcpy(WFIFOP(fd,c*104+84),m->name,24); + c++; + } + WFIFOW(fd, 2)=c*104+4; + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +/*========================================== + * ギルド役職名リスト + *------------------------------------------ + */ +int clif_guild_positionnamelist(struct map_session_data *sd) +{ + int i,fd; + struct guild *g; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + WFIFOW(fd, 0)=0x166; + for(i=0;i<MAX_GUILDPOSITION;i++){ + WFIFOL(fd,i*28+4)=i; + memcpy(WFIFOP(fd,i*28+8),g->position[i].name,24); + } + WFIFOW(fd,2)=i*28+4; + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +/*========================================== + * ギルド役職情報リスト + *------------------------------------------ + */ +int clif_guild_positioninfolist(struct map_session_data *sd) +{ + int i,fd; + struct guild *g; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + WFIFOW(fd, 0)=0x160; + for(i=0;i<MAX_GUILDPOSITION;i++){ + struct guild_position *p=&g->position[i]; + WFIFOL(fd,i*16+ 4)=i; + WFIFOL(fd,i*16+ 8)=p->mode; + WFIFOL(fd,i*16+12)=i; + WFIFOL(fd,i*16+16)=p->exp_mode; + } + WFIFOW(fd, 2)=i*16+4; + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +/*========================================== + * ギルド役職変更通知 + *------------------------------------------ + */ +int clif_guild_positionchanged(struct guild *g,int idx) +{ + struct map_session_data *sd; + unsigned char buf[128]; + + nullpo_retr(0, g); + + WBUFW(buf, 0)=0x174; + WBUFW(buf, 2)=44; + WBUFL(buf, 4)=idx; + WBUFL(buf, 8)=g->position[idx].mode; + WBUFL(buf,12)=idx; + WBUFL(buf,16)=g->position[idx].exp_mode; + memcpy(WBUFP(buf,20),g->position[idx].name,24); + if( (sd=guild_getavailablesd(g))!=NULL ) + clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD); + return 0; +} +/*========================================== + * ギルドメンバ変更通知 + *------------------------------------------ + */ +int clif_guild_memberpositionchanged(struct guild *g,int idx) +{ + struct map_session_data *sd; + unsigned char buf[64]; + + nullpo_retr(0, g); + + WBUFW(buf, 0)=0x156; + WBUFW(buf, 2)=16; + WBUFL(buf, 4)=g->member[idx].account_id; + WBUFL(buf, 8)=g->member[idx].char_id; + WBUFL(buf,12)=g->member[idx].position; + if( (sd=guild_getavailablesd(g))!=NULL ) + clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD); + return 0; +} +/*========================================== + * ギルドエンブレム送信 + *------------------------------------------ + */ +int clif_guild_emblem(struct map_session_data *sd,struct guild *g) +{ + int fd; + + nullpo_retr(0, sd); + nullpo_retr(0, g); + + fd=sd->fd; + + if(g->emblem_len<=0) + return 0; + WFIFOW(fd,0)=0x152; + WFIFOW(fd,2)=g->emblem_len+12; + WFIFOL(fd,4)=g->guild_id; + WFIFOL(fd,8)=g->emblem_id; + memcpy(WFIFOP(fd,12),g->emblem_data,g->emblem_len); + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +/*========================================== + * ギルドスキル送信 + *------------------------------------------ + */ +int clif_guild_skillinfo(struct map_session_data *sd) +{ + int fd; + int i,id,c; + struct guild *g; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + WFIFOW(fd,0)=0x0162; + WFIFOW(fd,4)=g->skill_point; + for(i=c=0;i<MAX_GUILDSKILL;i++){ + if(g->skill[i].id>0){ + WFIFOW(fd,c*37+ 6) = id = g->skill[i].id; + WFIFOW(fd,c*37+ 8) = guild_skill_get_inf(id); + WFIFOW(fd,c*37+10) = 0; + WFIFOW(fd,c*37+12) = g->skill[i].lv; + WFIFOW(fd,c*37+14) = guild_skill_get_sp(id,g->skill[i].lv); + WFIFOW(fd,c*37+16) = guild_skill_get_range(id); + memset(WFIFOP(fd,c*37+18),0,24); + WFIFOB(fd,c*37+42)= //up; + (g->skill[i].lv < guild_skill_get_max(id))? 1:0; + c++; + } + } + WFIFOW(fd,2)=c*37+6; + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} +/*========================================== + * ギルド告知送信 + *------------------------------------------ + */ +int clif_guild_notice(struct map_session_data *sd,struct guild *g) +{ + int fd; + + nullpo_retr(0, sd); + nullpo_retr(0, g); + + fd=sd->fd; + if(*g->mes1==0 && *g->mes2==0) + return 0; + WFIFOW(fd,0)=0x16f; + memcpy(WFIFOP(fd,2),g->mes1,60); + memcpy(WFIFOP(fd,62),g->mes2,120); + WFIFOSET(fd,packet_len_table[0x16f]); + return 0; +} + +/*========================================== + * ギルドメンバ勧誘 + *------------------------------------------ + */ +int clif_guild_invite(struct map_session_data *sd,struct guild *g) +{ + int fd; + + nullpo_retr(0, sd); + nullpo_retr(0, g); + + fd=sd->fd; + WFIFOW(fd,0)=0x16a; + WFIFOL(fd,2)=g->guild_id; + memcpy(WFIFOP(fd,6),g->name,24); + WFIFOSET(fd,packet_len_table[0x16a]); + return 0; +} +/*========================================== + * ギルドメンバ勧誘結果 + *------------------------------------------ + */ +int clif_guild_inviteack(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x169; + WFIFOB(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0x169]); + return 0; +} +/*========================================== + * ギルドメンバ脱退通知 + *------------------------------------------ + */ +int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes) +{ + unsigned char buf[128]; + + nullpo_retr(0, sd); + + WBUFW(buf, 0)=0x15a; + memcpy(WBUFP(buf, 2),name,24); + memcpy(WBUFP(buf,26),mes,40); + clif_send(buf,packet_len_table[0x15a],&sd->bl,GUILD); + return 0; +} +/*========================================== + * ギルドメンバ追放通知 + *------------------------------------------ + */ +int clif_guild_explusion(struct map_session_data *sd,const char *name,const char *mes, + int account_id) +{ + unsigned char buf[128]; + + nullpo_retr(0, sd); + + WBUFW(buf, 0)=0x15c; + memcpy(WBUFP(buf, 2),name,24); + memcpy(WBUFP(buf,26),mes,40); + memcpy(WBUFP(buf,66),"dummy",24); + clif_send(buf,packet_len_table[0x15c],&sd->bl,GUILD); + return 0; +} +/*========================================== + * ギルド追放メンバリスト + *------------------------------------------ + */ +int clif_guild_explusionlist(struct map_session_data *sd) +{ + int fd; + int i,c; + struct guild *g; + + nullpo_retr(0, sd); + + fd=sd->fd; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + WFIFOW(fd,0)=0x163; + for(i=c=0;i<MAX_GUILDEXPLUSION;i++){ + struct guild_explusion *e=&g->explusion[i]; + if(e->account_id>0){ + memcpy(WFIFOP(fd,c*88+ 4),e->name,24); + memcpy(WFIFOP(fd,c*88+28),e->acc,24); + memcpy(WFIFOP(fd,c*88+52),e->mes,44); + c++; + } + } + WFIFOW(fd,2)=c*88+4; + WFIFOSET(fd,WFIFOW(fd,2)); + return 0; +} + +/*========================================== + * ギルド会話 + *------------------------------------------ + */ +int clif_guild_message(struct guild *g,int account_id,const char *mes,int len) +{ + struct map_session_data *sd; + unsigned char lbuf[255]; + unsigned char *buf = lbuf; + if (len + 32 >= sizeof(lbuf)) + buf = malloc(len + 32); + WBUFW(buf, 0)=0x17f; + WBUFW(buf, 2)=len+4; + memcpy(WBUFP(buf,4),mes,len); + + if( (sd=guild_getavailablesd(g))!=NULL ) + clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD); + if ( buf != lbuf) + free(buf); + return 0; +} +/*========================================== + * ギルドスキル割り振り通知 + *------------------------------------------ + */ +int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0) = 0x10e; + WFIFOW(fd,2) = skill_num; + WFIFOW(fd,4) = lv; + WFIFOW(fd,6) = guild_skill_get_sp(skill_num,lv); + WFIFOW(fd,8) = guild_skill_get_range(skill_num); + WFIFOB(fd,10) = 1; + WFIFOSET(fd,11); + return 0; +} +/*========================================== + * ギルド同盟要請 + *------------------------------------------ + */ +int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x171; + WFIFOL(fd,2)=account_id; + memcpy(WFIFOP(fd,6),name,24); + WFIFOSET(fd,packet_len_table[0x171]); + return 0; +} +/*========================================== + * ギルド同盟結果 + *------------------------------------------ + */ +int clif_guild_allianceack(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x173; + WFIFOL(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0x173]); + return 0; +} +/*========================================== + * ギルド関係解消通知 + *------------------------------------------ + */ +int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x184; + WFIFOL(fd,2)=guild_id; + WFIFOL(fd,6)=flag; + WFIFOSET(fd,packet_len_table[0x184]); + return 0; +} +/*========================================== + * ギルド敵対結果 + *------------------------------------------ + */ +int clif_guild_oppositionack(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x181; + WFIFOB(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0x181]); + return 0; +} +/*========================================== + * ギルド関係追加 + *------------------------------------------ + */ +/*int clif_guild_allianceadded(struct guild *g,int idx) +{ + unsigned char buf[64]; + WBUFW(fd,0)=0x185; + WBUFL(fd,2)=g->alliance[idx].opposition; + WBUFL(fd,6)=g->alliance[idx].guild_id; + memcpy(WBUFP(fd,10),g->alliance[idx].name,24); + clif_send(buf,packet_len_table[0x185],guild_getavailablesd(g),GUILD); + return 0; +}*/ + +/*========================================== + * ギルド解散通知 + *------------------------------------------ + */ +int clif_guild_broken(struct map_session_data *sd,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0x15e; + WFIFOL(fd,2)=flag; + WFIFOSET(fd,packet_len_table[0x15e]); + return 0; +} + +/*========================================== + * エモーション + *------------------------------------------ + */ +void clif_emotion(struct block_list *bl,int type) +{ + unsigned char buf[8]; + + nullpo_retv(bl); + + WBUFW(buf,0)=0xc0; + WBUFL(buf,2)=bl->id; + WBUFB(buf,6)=type; + clif_send(buf,packet_len_table[0xc0],bl,AREA); +} + +/*========================================== + * トーキーボックス + *------------------------------------------ + */ +void clif_talkiebox(struct block_list *bl,char* talkie) +{ + unsigned char buf[86]; + + nullpo_retv(bl); + + WBUFW(buf,0)=0x191; + WBUFL(buf,2)=bl->id; + memcpy(WBUFP(buf,6),talkie,80); + clif_send(buf,packet_len_table[0x191],bl,AREA); +} + +/*========================================== + * 結婚エフェクト + *------------------------------------------ + */ +void clif_wedding_effect(struct block_list *bl) { + unsigned char buf[6]; + + nullpo_retv(bl); + + WBUFW(buf,0) = 0x1ea; + WBUFL(buf,2) = bl->id; + clif_send(buf, packet_len_table[0x1ea], bl, AREA); +} +/*========================================== + * あなたに逢いたい使用時名前叫び + *------------------------------------------ + +void clif_callpartner(struct map_session_data *sd) +{ + unsigned char buf[26]; + char *p; + + nullpo_retv(sd); + + if(sd->status.partner_id){ + WBUFW(buf,0)=0x1e6; + p = map_charid2nick(sd->status.partner_id); + if(p){ + memcpy(WBUFP(buf,2),p,24); + }else{ + map_reqchariddb(sd,sd->status.partner_id); + chrif_searchcharid(sd->status.partner_id); + WBUFB(buf,2) = 0; + } + clif_send(buf,packet_len_table[0x1e6]&sd->bl,AREA); + } + return; +} +*/ +/*========================================== + * 座る + *------------------------------------------ + */ +void clif_sitting(int fd, struct map_session_data *sd) +{ + unsigned char buf[64]; + + nullpo_retv(sd); + + WBUFW(buf, 0) = 0x8a; + WBUFL(buf, 2) = sd->bl.id; + WBUFB(buf,26) = 2; + clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA); +} + +/*========================================== + * + *------------------------------------------ + */ +int clif_disp_onlyself(struct map_session_data *sd, char *mes, int len) +{ + unsigned char lbuf[255]; + unsigned char *buf = (len + 32 >= sizeof(lbuf)) ? malloc(len + 32) : lbuf; + + nullpo_retr(0, sd); + + WBUFW(buf, 0) = 0x17f; + WBUFW(buf, 2) = len + 8; + memcpy(WBUFP(buf,4), mes, len + 4); + + clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); + + if (buf != lbuf) + free(buf); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ + +int clif_GM_kickack(struct map_session_data *sd, int id) +{ + int fd; + + nullpo_retr(0, sd); + + fd = sd->fd; + WFIFOW(fd,0) = 0xcd; + WFIFOL(fd,2) = id; + WFIFOSET(fd, packet_len_table[0xcd]); + return 0; +} + +void clif_parse_QuitGame(int fd,struct map_session_data *sd); + +int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type) +{ + nullpo_retr(0, tsd); + + if(type) + clif_GM_kickack(sd,tsd->status.account_id); + tsd->opt1 = tsd->opt2 = 0; + clif_parse_QuitGame(tsd->fd,tsd); + + return 0; +} +/*========================================== + * Wis拒否許可応答 + *------------------------------------------ + */ +int clif_wisexin(struct map_session_data *sd,int type,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xd1; + WFIFOB(fd,2)=type; + WFIFOB(fd,3)=flag; + WFIFOSET(fd,packet_len_table[0xd1]); + + return 0; +} +/*========================================== + * Wis全拒否許可応答 + *------------------------------------------ + */ +int clif_wisall(struct map_session_data *sd,int type,int flag) +{ + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; + WFIFOW(fd,0)=0xd2; + WFIFOB(fd,2)=type; + WFIFOB(fd,3)=flag; + WFIFOSET(fd,packet_len_table[0xd2]); + + return 0; +} +/*========================================== + * サウンドエフェクト + *------------------------------------------ + */ +void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type) +{ + int fd; + + nullpo_retv(sd); + nullpo_retv(bl); + + fd=sd->fd; + WFIFOW(fd,0)=0x1d3; + memcpy(WFIFOP(fd,2),name,24); + WFIFOB(fd,26)=type; + WFIFOL(fd,27)=0; + WFIFOL(fd,31)=bl->id; + WFIFOSET(fd,packet_len_table[0x1d3]); + + return; +} +// displaying special effects (npcs, weather, etc) [Valaris] +int clif_specialeffect(struct block_list *bl, int type, int flag) { + unsigned char buf[24]; + + nullpo_retr(0, bl); + + memset(buf, 0, packet_len_table[0x1f3]); + + WBUFW(buf,0) = 0x1f3; + WBUFL(buf,2) = bl->id; + WBUFL(buf,6) = type; + + if (flag==2) { + struct map_session_data *sd=NULL; + int i; + for(i=0; i < fd_max; i++) { + if (session[i] && (sd = session[i]->session_data) != NULL && sd->state.auth && sd->bl.m == bl->m) + clif_specialeffect(&sd->bl,type,1); + } + } + + else if (flag==1) + clif_send(buf, packet_len_table[0x1f3], bl, SELF); + else if (!flag) + clif_send(buf, packet_len_table[0x1f3], bl, AREA); + + return 0; + +} +// ------------ +// clif_parse_* +// ------------ +// パケット読み取って色々操作 +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_WantToConnection(int fd, struct map_session_data *sd) +{ + struct map_session_data *old_sd; + int account_id; // account_id in the packet 0x72 or 0x7E + + if (sd) { + if (battle_config.error_log) + printf("clif_parse_WantToConnection : invalid request?\n"); + return; + } + + // 0x72 + if (RFIFOW(fd,0) == 0x72) { + //printf("Received bytes %d with packet 0x72.\n", RFIFOREST(fd)); + if (RFIFOREST(fd) >= 39 && (RFIFOB(fd,38) == 0 || RFIFOB(fd,38) == 1)) // 00 = Female, 01 = Male + account_id = RFIFOL(fd,12); + else if (RFIFOREST(fd) >= 22 && (RFIFOB(fd,21) == 0 || RFIFOB(fd,21) == 1)) // 00 = Female, 01 = Male + account_id = RFIFOL(fd,5); + else // old packet version + account_id = RFIFOL(fd,2); + // 0x7E + } else if (RFIFOW(fd,0) == 0x7E) { + //printf("Received bytes %d with packet 0x7E.\n", RFIFOREST(fd)); + if (RFIFOREST(fd) >= 37 && (RFIFOB(fd,36) == 0 || RFIFOB(fd,36) == 1)) // 00 = Female, 01 = Male + account_id = RFIFOL(fd,9); + else + account_id = RFIFOL(fd,12); + // 0xF5 + } else { + //printf("Received bytes %d with packet 0xF5.\n", RFIFOREST(fd)); + account_id = RFIFOL(fd,7); + } + + // if same account already connected, we disconnect the 2 sessions + if ((old_sd = map_id2sd(account_id)) != NULL) { + clif_authfail_fd(fd, 2); // same id + clif_authfail_fd(old_sd->fd, 2); // same id + printf("clif_parse_WantToConnection: Double connection for account %d (sessions: #%d (new) and #%d (old)).\n", account_id, fd, old_sd->fd); + } else { + sd = session[fd]->session_data = calloc(sizeof(*sd), 1); + if (sd == NULL) { + printf("out of memory : clif_parse_WantToConnection\n"); + exit(1); + } + sd->fd = fd; + + // 0x72 + if (RFIFOW(fd,0) == 0x72) { + if (RFIFOREST(fd) >= 39 && (RFIFOB(fd,38) == 0 || RFIFOB(fd,38) == 1)) { // 00 = Female, 01 = Male + sd->packet_ver = 7; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_setnewpc(sd, account_id, RFIFOL(fd,22), RFIFOL(fd,30), RFIFOL(fd,34), RFIFOB(fd,38), fd); + } else if (RFIFOREST(fd) >= 22 && (RFIFOB(fd,21) == 0 || RFIFOB(fd,21) == 1)) { // 00 = Female, 01 = Male + sd->packet_ver = 6; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_setnewpc(sd, account_id, RFIFOL(fd,9), RFIFOL(fd,13), RFIFOL(fd,17), RFIFOB(fd,21), fd); + } else { // old packet version + sd->packet_ver = 5; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_setnewpc(sd, account_id, RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOB(fd,18), fd); + } + // 0x7E + } else if (RFIFOW(fd,0) == 0x7E) { + if (RFIFOREST(fd) >= 37 && (RFIFOB(fd,36) == 0 || RFIFOB(fd,36) == 1)) { // 00 = Female, 01 = Male + sd->packet_ver = 9; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_setnewpc(sd, account_id, RFIFOL(fd,21), RFIFOL(fd,28), RFIFOL(fd,32), RFIFOB(fd,36), fd); + } else { + sd->packet_ver = 8; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_setnewpc(sd, account_id, RFIFOL(fd,18), RFIFOL(fd,24), RFIFOL(fd,28), RFIFOB(fd,32), fd); + } + // 0xF5 + } else { + sd->packet_ver = 10; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_setnewpc(sd, account_id, RFIFOL(fd,15), RFIFOL(fd,25), RFIFOL(fd,29), RFIFOB(fd,33), fd); + } + + WFIFOL(fd,0) = sd->bl.id; + WFIFOSET(fd,4); + + map_addiddb(&sd->bl); + + chrif_authreq(sd); + } + + return; +} + +/*========================================== + * 007d クライアント側マップ読み込み完了 + * map侵入時に必要なデータを全て送りつける + *------------------------------------------ + */ +void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) +{ +// struct item_data* item; + int i; + nullpo_retv(sd); + + if(sd->bl.prev != NULL) + return; + + // 接続ok時 + //clif_authok(); + if(sd->npc_id) npc_event_dequeue(sd); + clif_skillinfoblock(sd); + pc_checkitem(sd); + //guild_info(); + + // loadendack時 + // next exp + clif_updatestatus(sd,SP_NEXTBASEEXP); + clif_updatestatus(sd,SP_NEXTJOBEXP); + // skill point + clif_updatestatus(sd,SP_SKILLPOINT); + // item + clif_itemlist(sd); + clif_equiplist(sd); + // cart + if(pc_iscarton(sd)){ + clif_cart_itemlist(sd); + clif_cart_equiplist(sd); + clif_updatestatus(sd,SP_CARTINFO); + } + // param all + clif_initialstatus(sd); + // party + party_send_movemap(sd); + // guild + guild_send_memberinfoshort(sd,1); + // 119 + // 78 + + if(battle_config.pc_invincible_time > 0) { + if(map[sd->bl.m].flag.gvg) + pc_setinvincibletimer(sd,battle_config.pc_invincible_time<<1); + else + pc_setinvincibletimer(sd,battle_config.pc_invincible_time); + } + + map_addblock(&sd->bl); // ブロック登録 + clif_spawnpc(sd); // spawn + + // weight max , now + clif_updatestatus(sd,SP_MAXWEIGHT); + clif_updatestatus(sd,SP_WEIGHT); + + // pvp + if(sd->pvp_timer!=-1 && !battle_config.pk_mode) + delete_timer(sd->pvp_timer,pc_calc_pvprank_timer); + if(map[sd->bl.m].flag.pvp){ + if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris] + sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,sd->bl.id,0); + sd->pvp_rank=0; + sd->pvp_lastusers=0; + sd->pvp_point=5; + } + clif_set0199(sd->fd,1); + } else { + sd->pvp_timer=-1; + } + if(map[sd->bl.m].flag.gvg) { + clif_set0199(sd->fd,3); + } + + // 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,0x14); + 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==1 && sd->status.clothes_color > 0) + clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color); + + if(sd->status.hp<sd->status.max_hp>>2 && pc_checkskill(sd,SM_AUTOBERSERK)>0 && + (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0 )) + // オートバーサーク発動 + skill_status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0); + +// if(time(&timer) < ((weddingtime=pc_readglobalreg(sd,"PC_WEDDING_TIME")) + 3600)) +// skill_status_change_start(&sd->bl,SC_WEDDING,0,weddingtime,0,0,36000,0); + + if(battle_config.muting_players && sd->status.manner < 0) + skill_status_change_start(&sd->bl,SC_NOCHAT,0,0,0,0,0,0); + + // option + clif_changeoption(&sd->bl); + if(sd->sc_data[SC_TRICKDEAD].timer != -1) + skill_status_change_end(&sd->bl,SC_TRICKDEAD,-1); + if(sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele)) + skill_status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1); + if(sd->special_state.infinite_endure && sd->sc_data[SC_ENDURE].timer == -1) + skill_status_change_start(&sd->bl,SC_ENDURE,10,1,0,0,0,0); + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].equip && sd->status.inventory[i].equip & 0x0002 && sd->status.inventory[i].broken==1) + skill_status_change_start(&sd->bl,SC_BROKNWEAPON,0,0,0,0,0,0); + if(sd->status.inventory[i].equip && sd->status.inventory[i].equip & 0x0010 && sd->status.inventory[i].broken==1) + skill_status_change_start(&sd->bl,SC_BROKNARMOR,0,0,0,0,0,0); + } + + map_foreachinarea(clif_getareachar,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,0,sd); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_TickSend(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if (sd->packet_ver == 8) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + sd->client_tick = RFIFOL(fd,6); + else if (sd->packet_ver == 9) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + sd->client_tick = RFIFOL(fd,9); + else if (sd->packet_ver == 10) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + sd->client_tick = RFIFOL(fd,7); + else // old version by default (and version 6 + 7) + sd->client_tick = RFIFOL(fd,2); + sd->server_tick = gettick(); + clif_servertick(sd); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_WalkToXY(int fd, struct map_session_data *sd) { + int x, y; + + nullpo_retv(sd); + + if (pc_isdead(sd)) { + clif_clearchar_area(&sd->bl, 1); + return; + } + + if (sd->npc_id != 0 || sd->vender_id != 0) + return; + + if (sd->skilltimer != -1 && pc_checkskill(sd, SA_FREECAST) <= 0) // フリーキャスト + return; + + if (sd->chatID) + return; + + if (sd->canmove_tick > gettick()) + return; + + // ステータス異常やハイディング中(トンネルドライブ無)で動けない + if ((sd->opt1 > 0 && sd->opt1 != 6) || + sd->sc_data[SC_ANKLE].timer !=-1 || //アンクルスネア + sd->sc_data[SC_AUTOCOUNTER].timer !=-1 || //オートカウンター + sd->sc_data[SC_TRICKDEAD].timer !=-1 || //死んだふり + sd->sc_data[SC_BLADESTOP].timer !=-1 || //白刃取り + sd->sc_data[SC_SPIDERWEB].timer !=-1 || //スパイダーウェッブ + (sd->sc_data[SC_DANCING].timer !=-1 && sd->sc_data[SC_DANCING].val4)) //合奏スキル演奏中は動けない + return; + if ((sd->status.option & 2) && pc_checkskill(sd, RG_TUNNELDRIVE) <= 0) + return; + + if (sd->invincible_timer != -1) + pc_delinvincibletimer(sd); + + pc_stopattack(sd); + + if (sd->packet_ver == 6) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + x = RFIFOB(fd,5) * 4 + (RFIFOB(fd,6) >> 6); + y = ((RFIFOB(fd,6) & 0x3f) << 4) + (RFIFOB(fd,7) >> 4); + } else if (sd->packet_ver == 7) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + x = RFIFOB(fd,6) * 4 + (RFIFOB(fd,7) >> 6); + y = ((RFIFOB(fd,7) & 0x3f) << 4) + (RFIFOB(fd,8) >> 4); + } else if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + x = RFIFOB(fd,3) * 4 + (RFIFOB(fd,4) >> 6); + y = ((RFIFOB(fd,4) & 0x3f) << 4) + (RFIFOB(fd,5) >> 4); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + x = RFIFOB(fd,12) * 4 + (RFIFOB(fd,13) >> 6); + y = ((RFIFOB(fd,13) & 0x3f) << 4) + (RFIFOB(fd,14) >> 4); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + x = RFIFOB(fd,6) * 4 + (RFIFOB(fd,7) >> 6); + y = ((RFIFOB(fd,7) & 0x3f) << 4) + (RFIFOB(fd,8) >> 4); + } else { // old version by default + x = RFIFOB(fd,2) * 4 + (RFIFOB(fd,3) >> 6); + y = ((RFIFOB(fd,3) & 0x3f) << 4) + (RFIFOB(fd,4) >> 4); + } + pc_walktoxy(sd, x, y); + +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_QuitGame(int fd, struct map_session_data *sd) { + unsigned int tick=gettick(); + struct skill_unit_group* sg; + + nullpo_retv(sd); + + WFIFOW(fd,0) = 0x18b; + if ((!pc_isdead(sd) && (sd->opt1 || (sd->opt2 && !(night_flag == 1 && sd->opt2 == STATE_BLIND)))) || + sd->skilltimer != -1 || + (DIFF_TICK(tick , sd->canact_tick) < 0) || + (sd->sc_data && sd->sc_data[SC_DANCING].timer!=-1 && sd->sc_data[SC_DANCING].val4 && (sg=(struct skill_unit_group *)sd->sc_data[SC_DANCING].val2) && sg->src_id == sd->bl.id)) { + WFIFOW(fd,2)=1; + WFIFOSET(fd,packet_len_table[0x18b]); + return; + } + + /* Rovert's prevent logout option fixed [Valaris] */ + if ((battle_config.prevent_logout && (gettick() - sd->canlog_tick) >= 10000) || (!battle_config.prevent_logout)) { + clif_setwaitclose(fd); + WFIFOW(fd,2)=0; + } else { + WFIFOW(fd,2)=1; + } + WFIFOSET(fd,packet_len_table[0x18b]); + +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd) { + struct block_list *bl; + int account_id; + + if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + account_id = RFIFOL(fd,11); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + account_id = RFIFOL(fd,8); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + account_id = RFIFOL(fd,10); + } else { // old version by default (+ packet version 6 and 7) + account_id = RFIFOL(fd,2); + } + bl = map_id2bl(account_id); + if (bl == NULL) + return; + + WFIFOW(fd,0) = 0x95; + WFIFOL(fd,2) = account_id; + + switch(bl->type) { + case BL_PC: + { + struct map_session_data *ssd = (struct map_session_data *)bl; + struct party *p = NULL; + struct guild *g = NULL; + + nullpo_retv(ssd); + + memcpy(WFIFOP(fd,6), ssd->status.name, 24); + if (ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL && + (ssd->status.party_id == 0 || (p = party_search(ssd->status.party_id)) != NULL)) { + // ギルド所属ならパケット0195を返す + 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; + } + if (ps >= 0 && ps < MAX_GUILDPOSITION) { + WFIFOW(fd, 0) = 0x195; + if (p) + memcpy(WFIFOP(fd,30), p->name, 24); + else + WFIFOB(fd,30) = 0; + memcpy(WFIFOP(fd,54), g->name,24); + memcpy(WFIFOP(fd,78), g->position[ps].name, 24); + WFIFOSET(fd,packet_len_table[0x195]); + break; + } + } + WFIFOSET(fd,packet_len_table[0x95]); + } + break; + case BL_PET: + memcpy(WFIFOP(fd,6), ((struct pet_data*)bl)->name, 24); + WFIFOSET(fd,packet_len_table[0x95]); + break; + case BL_NPC: + memcpy(WFIFOP(fd,6), ((struct npc_data*)bl)->name, 24); + WFIFOSET(fd,packet_len_table[0x95]); + break; + case BL_MOB: + { + struct mob_data *md = (struct mob_data *)bl; + + nullpo_retv(md); + + memcpy(WFIFOP(fd,6), md->name, 24); + if (md->class >= 1285 && md->class <= 1288) { + struct guild *g; + struct guild_castle *gc = guild_mapname2gc(map[md->bl.m].name); + if (gc && gc->guild_id > 0 && (g = guild_search(gc->guild_id)) != NULL) { + WFIFOW(fd, 0) = 0x195; + WFIFOB(fd,30) = 0; + memcpy(WFIFOP(fd,54), g->name, 24); + memcpy(WFIFOP(fd,78), gc->castle_name, 24); + WFIFOSET(fd,packet_len_table[0x195]); + } else { + WFIFOSET(fd,packet_len_table[0x95]); + } + } else if (battle_config.show_mob_hp == 1) { + char mobhp[50]; + sprintf(mobhp, "hp: %d/%d", md->hp, mob_db[md->class].max_hp); + WFIFOW(fd, 0) = 0x195; + memcpy(WFIFOP(fd,30), mobhp, 24); + WFIFOB(fd,54) = 0; + WFIFOB(fd,78) = 0; + WFIFOSET(fd,packet_len_table[0x195]); + } else { + WFIFOSET(fd,packet_len_table[0x95]); + } + } + break; + default: + if (battle_config.error_log) + printf("clif_parse_GetCharNameRequest : bad type %d(%d)\n", bl->type, account_id); + break; + } +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_GlobalMessage(int fd, struct map_session_data *sd) { // S 008c <len>.w <str>.?B + char *message = (char *) malloc(RFIFOW(fd,2) + 128); + char *buf = (char *) malloc(RFIFOW(fd,2) + 4); + + nullpo_retv(sd); + + memset(message, '\0', RFIFOW(fd,2) + 128); + memset(buf, '\0', RFIFOW(fd,2) + 4); + + if ((is_atcommand(fd, sd, RFIFOP(fd,4), 0) != AtCommand_None) || + ( sd->sc_data && + (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可 + sd->sc_data[SC_NOCHAT].timer!=-1 ) )) //チャット禁止 + { + free(message); + free(buf); + return; + } + + //printf("clif_parse_GlobalMessage: message: '%s'.\n", RFIFOP(fd,4)); + if (strncmp(RFIFOP(fd,4), sd->status.name, strlen(sd->status.name)) != 0) { + printf("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); + + // information is sended to all online GM + sprintf(message, "Hack on global message (normal message): character '%s' (account: %d) uses an other name.", sd->status.name, sd->status.account_id); + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message, strlen(message) + 1); + if (strlen(RFIFOP(fd,4)) == 0) + strcpy(message, " This player sends a void name and a void message."); + else + sprintf(message, " This player sends (name:message): '%s'.", RFIFOP(fd,4)); + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message, strlen(message) + 1); + // 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, strlen(message) + 1); + + // 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 + } + // but for the hacker, we display on his screen (he see/look no difference). + } else { + // 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 + memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WFIFOW(fd,0) = 0x8e; + WFIFOSET(fd, WFIFOW(fd,2)); + + free(message); + free(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); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_MapMove(int fd, struct map_session_data *sd) { +// /m /mapmove (as @rura GM command) + char output[100]; + char map_name[17]; + + nullpo_retv(sd); + + memset(output, '\0', sizeof(output)); + memset(map_name, '\0', sizeof(map_name)); + + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_MapMove))) { + memcpy(map_name, RFIFOP(fd,2), 16); + sprintf(output, "%s %d %d", map_name, RFIFOW(fd,18), RFIFOW(fd,20)); + atcommand_rura(fd, sd, "@rura", output); + } + + return; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChangeDir(int fd, struct map_session_data *sd) { + unsigned char buf[64]; + short headdir, dir; + + nullpo_retv(sd); + + if (sd->packet_ver == 7) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + headdir = RFIFOW(fd,5); + dir = RFIFOB(fd,12); + } else if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + headdir = RFIFOW(fd,5); + dir = RFIFOB(fd,12); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + headdir = RFIFOW(fd,7); + dir = RFIFOB(fd,11); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + headdir = RFIFOW(fd,4); + dir = RFIFOB(fd,9); + } else { // old version by default (and packet version 6) + headdir = RFIFOW(fd,2); + dir = RFIFOB(fd,4); + } + + pc_setdir(sd, dir, headdir); + + WBUFW(buf,0) = 0x9c; + WBUFL(buf,2) = sd->bl.id; + WBUFW(buf,6) = headdir; + WBUFB(buf,8) = dir; + if (sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris] + clif_send(buf, packet_len_table[0x9c], &sd->bl, AREA); + else + clif_send(buf, packet_len_table[0x9c], &sd->bl, AREA_WOS); + +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_Emotion(int fd, struct map_session_data *sd) { + unsigned char buf[64]; + + nullpo_retv(sd); + + if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) { + WBUFW(buf,0) = 0xc0; + WBUFL(buf,2) = sd->bl.id; + WBUFB(buf,6) = RFIFOB(fd,2); + clif_send(buf, packet_len_table[0xc0], &sd->bl, AREA); + } else + clif_skill_fail(sd, 1, 0, 1); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_HowManyConnections(int fd, struct map_session_data *sd) { + WFIFOW(fd,0) = 0xc2; + WFIFOL(fd,2) = map_getusers(); + WFIFOSET(fd,packet_len_table[0xc2]); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ActionRequest(int fd, struct map_session_data *sd) { + unsigned int tick; + unsigned char buf[64]; + int action_type, target_id; + + nullpo_retv(sd); + + if (pc_isdead(sd)) { + clif_clearchar_area(&sd->bl, 1); + return; + } + if (sd->npc_id != 0 || sd->opt1 > 0 || sd->status.option & 2 || + (sd->sc_data && + (sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター + sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り + sd->sc_data[SC_DANCING].timer != -1))) + return; + + tick = gettick(); + + pc_stop_walking(sd, 0); + pc_stopattack(sd); + + if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + target_id = RFIFOL(fd,3); + action_type = RFIFOB(fd,8); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + target_id = RFIFOL(fd,7); + action_type = RFIFOB(fd,17); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + target_id = RFIFOL(fd,9); + action_type = RFIFOB(fd,22); + } else { // old version by default (and packet version 6 and 7) + target_id = RFIFOL(fd,2); + action_type = RFIFOB(fd,6); + } + + switch(action_type) { + case 0x00: // once attack + case 0x07: // continuous attack + if(sd->sc_data[SC_WEDDING].timer != -1 || sd->view_class==22) + 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); + if (sd->attacktarget > 0) // [Valaris] + sd->attacktarget = 0; + 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) { + pc_stop_walking(sd, 1); + skill_gangsterparadise(sd, 1); // ギャングスターパラダイス設定 + pc_setsit(sd); + clif_sitting(fd, sd); + } else + clif_skill_fail(sd, 1, 0, 2); + break; + case 0x03: // standup + skill_gangsterparadise(sd, 0); // ギャングスターパラダイス解除 + pc_setstand(sd); + WBUFW(buf, 0) = 0x8a; + WBUFL(buf, 2) = sd->bl.id; + WBUFB(buf,26) = 3; + clif_send(buf, packet_len_table[0x8a], &sd->bl, AREA); + break; + } +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_Restart(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + switch(RFIFOB(fd,2)) { + case 0x00: + if (pc_isdead(sd)) { + pc_setstand(sd); + pc_setrestartvalue(sd, 3); + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 2); + } + break; + case 0x01: + if(!pc_isdead(sd) && (sd->opt1 || (sd->opt2 && !(night_flag == 1 && sd->opt2 == STATE_BLIND)))) + return; + + /* Rovert's Prevent logout option - Fixed [Valaris] */ + if ((battle_config.prevent_logout && (gettick() - sd->canlog_tick) >= 10000) || (!battle_config.prevent_logout)) { + chrif_charselectreq(sd); + } else { + WFIFOW(fd,0)=0x18b; + WFIFOW(fd,2)=1; + + WFIFOSET(fd,packet_len_table[0x018b]); + } + break; + } +} + +/*========================================== + * 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] + struct map_session_data *dstsd; + int i; + int gmlen = strlen(RFIFOP(fd,28)); + char gmbuf[512]; + char *gm_command = ((gmlen+28) > sizeof(gmbuf)) ? (char *) malloc(gmlen + 28) : gmbuf; + // 24+3+(RFIFOW(fd,2)-28)+1 or 24+3+(strlen(RFIFOP(fd,28))+1 (size can be wrong with hacker) + + //printf("clif_parse_Wis: message: '%s'.\n", RFIFOP(fd,28)); + memset(gm_command, 0, gmlen); + sprintf(gm_command, "%s : %s", sd->status.name, RFIFOP(fd,28)); + if ((is_atcommand(fd, sd, gm_command, 0) != AtCommand_None) || + ( sd && sd->sc_data && + (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可 + sd->sc_data[SC_NOCHAT].timer!=-1 ) )) //チャット禁止 + { + if (gm_command != gmbuf) + free(gm_command); + return; + } + + // searching destination character + dstsd = map_nick2sd(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, RFIFOP(fd,4)) != 0) // not exactly same name + // send message to inter-server + intif_wis_message(sd, RFIFOP(fd,4), 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) + clif_wis_end(fd, 2); // type: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + else { + // if player ignore the source character + for(i = 0; i < (sizeof(dstsd->ignore) / sizeof(dstsd->ignore[0])); i++) + if (strcmp(dstsd->ignore[i].name, sd->status.name) == 0) { + clif_wis_end(fd, 2); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + break; + } + // if source player not found in ignore list + if (i == (sizeof(sd->ignore) / sizeof(sd->ignore[0]))) { + clif_wis_message(dstsd->fd, sd->status.name, RFIFOP(fd,28), RFIFOW(fd,2) - 28); + clif_wis_end(fd, 0); // type: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + } + } + } + } + + if (gm_command != gmbuf) + free(gm_command); + + return; +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_GMmessage(int fd, struct map_session_data *sd) { +// /b + nullpo_retv(sd); + + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_Broadcast))) + intif_GMmessage(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; + + nullpo_retv(sd); + + if (sd->packet_ver == 7) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + map_object_id = RFIFOL(fd,6); + else if (sd->packet_ver == 8) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + map_object_id = RFIFOL(fd,6); + else if (sd->packet_ver == 9) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + map_object_id = RFIFOL(fd,9); + else if (sd->packet_ver == 10) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + map_object_id = RFIFOL(fd,7); + else // old version by default (and parcket version 6) + map_object_id = RFIFOL(fd,2); + fitem = (struct flooritem_data*)map_id2bl(map_object_id); + + if (pc_isdead(sd)) { + clif_clearchar_area(&sd->bl, 1); + return; + } + + if( sd->npc_id!=0 || sd->vender_id != 0 || sd->opt1 > 0 || + (sd->sc_data && (sd->sc_data[SC_TRICKDEAD].timer != -1 || //死んだふり + sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り + sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク + sd->sc_data[SC_NOCHAT].timer!=-1 )) ) //会話禁止 + return; + + if (fitem == NULL || fitem->bl.m != sd->bl.m) + return; + + pc_takeitem(sd, fitem); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_DropItem(int fd, struct map_session_data *sd) { + int item_index, item_amount; + + nullpo_retv(sd); + + if (pc_isdead(sd)) { + clif_clearchar_area(&sd->bl, 1); + return; + } + if (sd->npc_id != 0 || sd->vender_id != 0 || sd->opt1 > 0 || + (sd->sc_data && (sd->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター + sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り + sd->sc_data[SC_BERSERK].timer != -1)) ) //バーサーク + return; + + if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,5) - 2; + item_amount = RFIFOW(fd,12); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,8) - 2; + item_amount = RFIFOW(fd,15); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,6) - 2; + item_amount = RFIFOW(fd,15); + } else { // old version by default (+ packet version 6 and 7) + item_index = RFIFOW(fd,2) - 2; + item_amount = RFIFOW(fd,4); + } + + pc_dropitem(sd, item_index, item_amount); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_UseItem(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if (pc_isdead(sd)) { + clif_clearchar_area(&sd->bl, 1); + return; + } + if (sd->npc_id!=0 || sd->vender_id != 0 || sd->opt1 > 0 || + (sd->sc_data && (sd->sc_data[SC_TRICKDEAD].timer != -1 || //死んだふり + sd->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り + sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク + sd->sc_data[SC_NOCHAT].timer!=-1 )) ) //会話禁止 + return; + + if (sd->invincible_timer != -1) + pc_delinvincibletimer(sd); + + if (sd->packet_ver == 6) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_useitem(sd,RFIFOW(fd,5)-2); + else if (sd->packet_ver == 7) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_useitem(sd,RFIFOW(fd,6)-2); + else if (sd->packet_ver == 8) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_useitem(sd,RFIFOW(fd,6)-2); + else if (sd->packet_ver == 9) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_useitem(sd,RFIFOW(fd,9)-2); + else if (sd->packet_ver == 10) // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + pc_useitem(sd,RFIFOW(fd,7)-2); + else // old version by default + pc_useitem(sd,RFIFOW(fd,2)-2); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_EquipItem(int fd,struct map_session_data *sd) +{ + int index; + + nullpo_retv(sd); + + if(pc_isdead(sd)) { + clif_clearchar_area(&sd->bl,1); + return; + } + index = RFIFOW(fd,2)-2; + if(sd->npc_id!=0 || sd->vender_id != 0) return; + if(sd->sc_data && ( sd->sc_data[SC_BLADESTOP].timer!=-1 || sd->sc_data[SC_BERSERK].timer!=-1 )) return; + + if(sd->status.inventory[index].identify != 1) { // 未鑑定 + // Bjorn: Auto-identify items when equipping them as there + // is no nice way to do this in the client yet. + sd->status.inventory[index].identify = 1; + //clif_equipitemack(sd,index,0,0); // fail + //return; + } + //ペット用装備であるかないか + if(sd->inventory_data[index]) { + if(sd->inventory_data[index]->type != 8){ + if(sd->inventory_data[index]->type == 10) + RFIFOW(fd,4)=0x8000; // 矢を無理やり装備できるように(−−; + pc_equipitem(sd,index,RFIFOW(fd,4)); + } else + pet_equipitem(sd,index); + } +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_UnequipItem(int fd,struct map_session_data *sd) +{ + int index; + + nullpo_retv(sd); + + if(pc_isdead(sd)) { + clif_clearchar_area(&sd->bl,1); + return; + } + index = RFIFOW(fd,2)-2; + if(sd->status.inventory[index].broken == 1 && sd->sc_data && sd->sc_data[SC_BROKNWEAPON].timer!=-1) + skill_status_change_end(&sd->bl,SC_BROKNWEAPON,-1); + if(sd->status.inventory[index].broken == 1 && sd->sc_data && sd->sc_data[SC_BROKNARMOR].timer!=-1) + skill_status_change_end(&sd->bl,SC_BROKNARMOR,-1); + if(sd->sc_data && ( sd->sc_data[SC_BLADESTOP].timer!=-1 || sd->sc_data[SC_BERSERK].timer!=-1 )) + return; + + if(sd->npc_id!=0 || sd->vender_id != 0 || sd->opt1 > 0) + return; + pc_unequipitem(sd,index,0); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcClicked(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + if(pc_isdead(sd)) { + clif_clearchar_area(&sd->bl,1); + return; + } + if(sd->npc_id!=0 || sd->vender_id != 0) + return; + npc_click(sd,RFIFOL(fd,2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd) +{ + npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcBuyListSend(int fd,struct map_session_data *sd) +{ + int fail=0,n; + unsigned short *item_list; + + n = (RFIFOW(fd,2)-4) /4; + item_list = (unsigned short*)RFIFOP(fd,4); + + fail = npc_buylist(sd,n,item_list); + + WFIFOW(fd,0)=0xca; + WFIFOB(fd,2)=fail; + WFIFOSET(fd,packet_len_table[0xca]); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd) +{ + int fail=0,n; + unsigned short *item_list; + + n = (RFIFOW(fd,2)-4) /4; + item_list = (unsigned short*)RFIFOP(fd,4); + + fail = npc_selllist(sd,n,item_list); + + WFIFOW(fd,0)=0xcb; + WFIFOB(fd,2)=fail; + WFIFOSET(fd,packet_len_table[0xcb]); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_CreateChatRoom(int fd,struct map_session_data *sd) +{ + if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 4){ + chat_createchat(sd,RFIFOW(fd,4),RFIFOB(fd,6),RFIFOP(fd,7),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) +{ + chat_joinchat(sd,RFIFOL(fd,2),RFIFOP(fd,6)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChatRoomStatusChange(int fd,struct map_session_data *sd) +{ + chat_changechatstatus(sd,RFIFOW(fd,4),RFIFOB(fd,6),RFIFOP(fd,7),RFIFOP(fd,15),RFIFOW(fd,2)-15); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChangeChatOwner(int fd,struct map_session_data *sd) +{ + chat_changechatowner(sd,RFIFOP(fd,6)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_KickFromChat(int fd,struct map_session_data *sd) +{ + chat_kickchat(sd,RFIFOP(fd,2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_ChatLeave(int fd,struct map_session_data *sd) +{ + chat_leavechat(sd); +} + +/*========================================== + * 取引要請を相手に送る + *------------------------------------------ + */ +void clif_parse_TradeRequest(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 1){ + trade_traderequest(sd,RFIFOL(sd->fd,2)); + } else + clif_skill_fail(sd,1,0,0); +} + +/*========================================== + * 取引要請 + *------------------------------------------ + */ +void clif_parse_TradeAck(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + trade_tradeack(sd,RFIFOB(sd->fd,2)); +} + +/*========================================== + * アイテム追加 + *------------------------------------------ + */ +void clif_parse_TradeAddItem(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + trade_tradeadditem(sd,RFIFOW(sd->fd,2),RFIFOL(sd->fd,4)); +} + +/*========================================== + * アイテム追加完了(ok押し) + *------------------------------------------ + */ +void clif_parse_TradeOk(int fd,struct map_session_data *sd) +{ + trade_tradeok(sd); +} + +/*========================================== + * 取引キャンセル + *------------------------------------------ + */ +void clif_parse_TradeCansel(int fd,struct map_session_data *sd) +{ + trade_tradecancel(sd); +} + +/*========================================== + * 取引許諾(trade押し) + *------------------------------------------ + */ +void clif_parse_TradeCommit(int fd,struct map_session_data *sd) +{ + trade_tradecommit(sd); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_StopAttack(int fd,struct map_session_data *sd) +{ + pc_stopattack(sd); +} + +/*========================================== + * カートへアイテムを移す + *------------------------------------------ + */ +void clif_parse_PutItemToCart(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + if(sd->npc_id!=0 || sd->vender_id != 0) + return; + pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); +} +/*========================================== + * カートからアイテムを出す + *------------------------------------------ + */ +void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + if(sd->npc_id!=0 || sd->vender_id != 0) return; + pc_getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); +} + +/*========================================== + * 付属品(鷹,ペコ,カート)をはずす + *------------------------------------------ + */ +void clif_parse_RemoveOption(int fd,struct map_session_data *sd) +{ + if(pc_isriding(sd)) { // jobchange when removing peco [Valaris] + if(sd->status.class==13) + sd->status.class=sd->view_class=7; + + if(sd->status.class==21) + sd->status.class=sd->view_class=14; + + if(sd->status.class==4014) + sd->status.class=sd->view_class=4008; + + if(sd->status.class==4022) + sd->status.class=sd->view_class=4015; + } + + pc_setoption(sd,0); +} + +/*========================================== + * チェンジカート + *------------------------------------------ + */ +void clif_parse_ChangeCart(int fd,struct map_session_data *sd) +{ + pc_setcart(sd,RFIFOW(fd,2)); +} + +/*========================================== + * ステータスアップ + *------------------------------------------ + */ +void clif_parse_StatusUp(int fd,struct map_session_data *sd) +{ + pc_statusup(sd,RFIFOW(fd,2)); +} + +/*========================================== + * スキルレベルアップ + *------------------------------------------ + */ +void clif_parse_SkillUp(int fd,struct map_session_data *sd) +{ + pc_skillup(sd,RFIFOW(fd,2)); +} + +/*========================================== + * スキル使用(ID指定) + *------------------------------------------ + */ +void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) { + int skillnum, skilllv, lv, target_id; + unsigned int tick = gettick(); + + nullpo_retv(sd); + + if(map[sd->bl.m].flag.noskill) return; + if (sd->chatID || sd->npc_id != 0 || sd->vender_id != 0) + return; + + if (sd->packet_ver == 6) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,4); + skillnum = RFIFOW(fd,9); + target_id = RFIFOL(fd,11); + } else if (sd->packet_ver == 7) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,7); + skillnum = RFIFOW(fd,9); + target_id = RFIFOL(fd,15); + } else if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,7); + skillnum = RFIFOW(fd,12); + target_id = RFIFOL(fd,16); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,11); + skillnum = RFIFOW(fd,18); + target_id = RFIFOL(fd,22); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,9); + skillnum = RFIFOW(fd,15); + target_id = RFIFOL(fd,18); + } else { // old version by default + skilllv = RFIFOW(fd,2); + skillnum = RFIFOW(fd,4); + target_id = RFIFOL(fd,6); + } + + if (sd->skilltimer != -1) { + if (skillnum != SA_CASTCANCEL) + return; + } else if (DIFF_TICK(tick, sd->canact_tick) < 0) { + clif_skill_fail(sd, skillnum, 4, 0); + return; + } + + if ((sd->sc_data[SC_TRICKDEAD].timer != -1 && skillnum != NV_TRICKDEAD) || + sd->sc_data[SC_BERSERK].timer != -1 || sd->sc_data[SC_NOCHAT].timer!=-1 || + sd->sc_data[SC_WEDDING].timer!=-1 || sd->view_class==22) + return; + if (sd->invincible_timer != -1) + pc_delinvincibletimer(sd); + if (sd->skillitem >= 0 && sd->skillitem == skillnum) { + if (skilllv != sd->skillitemlv) + skilllv = sd->skillitemlv; + skill_use_id(sd, target_id, skillnum, skilllv); + } else { + sd->skillitem = sd->skillitemlv = -1; + if (skillnum == MO_EXTREMITYFIST) { + if ((sd->sc_data[SC_COMBO].timer == -1 || (sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH && sd->sc_data[SC_COMBO].val1 != CH_CHAINCRUSH))) { + if (!sd->state.skill_flag ) { + sd->state.skill_flag = 1; + clif_skillinfo(sd, MO_EXTREMITYFIST, 1, -1); + return; + } else if (sd->bl.id == target_id) { + clif_skillinfo(sd, MO_EXTREMITYFIST, 1, -1); + return; + } + } + } + if ((lv = pc_checkskill(sd, skillnum)) > 0) { + if (skilllv > lv) + skilllv = lv; + skill_use_id(sd, target_id, skillnum, skilllv); + if (sd->state.skill_flag) + sd->state.skill_flag = 0; + } + } +} + +/*========================================== + * スキル使用(場所指定) + *------------------------------------------ + */ +void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd) { + int skillnum, skilllv, lv, x, y; + unsigned int tick = gettick(); + int skillmoreinfo; + + nullpo_retv(sd); + + if(map[sd->bl.m].flag.noskill) return; + if (sd->npc_id != 0 || sd->vender_id != 0) return; + if(sd->chatID) return; + + skillmoreinfo = -1; + if (sd->packet_ver == 6) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,4); + skillnum = RFIFOW(fd,9); + x = RFIFOW(fd,11); + y = RFIFOW(fd,13); + if (RFIFOW(fd,0) == 0x190) + skillmoreinfo = 15; + } else if (sd->packet_ver == 7) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,7); + skillnum = RFIFOW(fd,9); + x = RFIFOW(fd,15); + y = RFIFOW(fd,17); + if (RFIFOW(fd,0) == 0x190) + skillmoreinfo = 19; + } else if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,3); + skillnum = RFIFOW(fd,6); + x = RFIFOW(fd,17); + y = RFIFOW(fd,21); + if (RFIFOW(fd,0) == 0x0a2) + skillmoreinfo = 23; + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,5); + skillnum = RFIFOW(fd,15); + x = RFIFOW(fd,29); + y = RFIFOW(fd,38); + if (RFIFOW(fd,0) == 0x0a2) + skillmoreinfo = 40; + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + skilllv = RFIFOW(fd,10); + skillnum = RFIFOW(fd,14); + x = RFIFOW(fd,18); + y = RFIFOW(fd,23); + if (RFIFOW(fd,0) == 0x0a7) + skillmoreinfo = 25; + } else { // old version by default + skilllv = RFIFOW(fd,2); + skillnum = RFIFOW(fd,4); + x = RFIFOW(fd,6); + y = RFIFOW(fd,8); + if (RFIFOW(fd,0) == 0x190) + skillmoreinfo = 10; + } + + if (skillmoreinfo != -1) { + if (pc_issit(sd)) { + clif_skill_fail(sd, skillnum, 0, 0); + return; + } + memcpy(talkie_mes, RFIFOP(fd,skillmoreinfo), 80); + } + + if (sd->skilltimer != -1) + return; + else if (DIFF_TICK(tick, sd->canact_tick) < 0) { + clif_skill_fail(sd, skillnum, 4, 0); + return; + } + + if((sd->sc_data[SC_TRICKDEAD].timer != -1 && skillnum != NV_TRICKDEAD) || + sd->sc_data[SC_BERSERK].timer!=-1 || sd->sc_data[SC_NOCHAT].timer!=-1 || + sd->sc_data[SC_WEDDING].timer!=-1 || sd->view_class==22) + return; + if (sd->invincible_timer != -1) + pc_delinvincibletimer(sd); + if (sd->skillitem >= 0 && sd->skillitem == skillnum) { + if (skilllv != sd->skillitemlv) + skilllv = sd->skillitemlv; + skill_use_pos(sd, x, y, skillnum, skilllv); + } else { + sd->skillitem = sd->skillitemlv = -1; + if ((lv = pc_checkskill(sd, skillnum)) > 0) { + if (skilllv > lv) + skilllv = lv; + skill_use_pos(sd, x, y, skillnum,skilllv); + } + } +} + +/*========================================== + * スキル使用(map指定) + *------------------------------------------ + */ +void clif_parse_UseSkillMap(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + if(map[sd->bl.m].flag.noskill) return; + if(sd->chatID) return; + + if(sd->npc_id!=0 || sd->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==22))) + return; + + if(sd->invincible_timer != -1) + pc_delinvincibletimer(sd); + + skill_castend_map(sd,RFIFOW(fd,2),RFIFOP(fd,4)); +} +/*========================================== + * メモ要求 + *------------------------------------------ + */ +void clif_parse_RequestMemo(int fd,struct map_session_data *sd) +{ + pc_memo(sd,-1); +} +/*========================================== + * アイテム合成 + *------------------------------------------ + */ +void clif_parse_ProduceMix(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + sd->state.produce_flag = 0; + skill_produce_mix(sd,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + sd->npc_menu=RFIFOB(fd,6); + npc_scriptcont(sd,RFIFOL(fd,2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd) +{ + npc_scriptcont(sd,RFIFOL(fd,2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + +#define RFIFOL_(fd,pos) (*(int*)(session[fd]->rdata+session[fd]->rdata_pos+(pos))) + //Input Value overflow Exploit FIX + sd->npc_amount=RFIFOL_(fd,6); //fixed by Lupus. npc_amount is (int) but was RFIFOL changing it to (unsigned int) + +#undef RFIFOL_ + + npc_scriptcont(sd,RFIFOL(fd,2)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcStringInput(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + if(RFIFOW(fd,2)-7 >= sizeof(sd->npc_str)){ + printf("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,RFIFOP(fd,8)); + npc_scriptcont(sd,RFIFOL(fd,4)); +} + +/*========================================== + * + *------------------------------------------ + */ +void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd) +{ + npc_scriptcont(sd,RFIFOL(fd,2)); +} + +/*========================================== + * アイテム鑑定 + *------------------------------------------ + */ +void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) +{ + pc_item_identify(sd,RFIFOW(fd,2)-2); +} +/*========================================== + * 矢作成 + *------------------------------------------ + */ +void clif_parse_SelectArrow(int fd,struct map_session_data *sd) +{ + nullpo_retv(sd); + + sd->state.make_arrow_flag = 0; + skill_arrow_create(sd,RFIFOW(fd,2)); +} +/*========================================== + * オートスペル受信 + *------------------------------------------ + */ +void clif_parse_AutoSpell(int fd,struct map_session_data *sd) +{ + skill_autospell(sd,RFIFOW(fd,2)); +} +/*========================================== + * カード使用 + *------------------------------------------ + */ +void clif_parse_UseCard(int fd,struct map_session_data *sd) +{ + clif_use_card(sd,RFIFOW(fd,2)-2); +} +/*========================================== + * カード挿入装備選択 + *------------------------------------------ + */ +void clif_parse_InsertCard(int fd,struct map_session_data *sd) +{ + pc_insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2); +} + +/*========================================== + * 0193 キャラID名前引き + *------------------------------------------ + */ +void clif_parse_SolveCharName(int fd, struct map_session_data *sd) { + int char_id; + + if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + char_id = RFIFOL(fd,8); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + char_id = RFIFOL(fd,7); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + char_id = RFIFOL(fd,10); + } else { // old version by default (+ packet version 6 and 7) + char_id = RFIFOL(fd,2); + } + clif_solved_charname(sd, char_id); +} + +/*========================================== + * 0197 /resetskill /resetstate + *------------------------------------------ + */ +void clif_parse_ResetChar(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if (battle_config.atc_gmonly == 0 || pc_isGM(sd)) { + switch(RFIFOW(fd,2)){ + case 0: + if (pc_isGM(sd) >= get_atcommand_level(AtCommand_ResetState)) + pc_resetstate(sd); + break; + case 1: + if (pc_isGM(sd) >= get_atcommand_level(AtCommand_ResetState)) + pc_resetskill(sd); + break; + } + } +} + +/*========================================== + * 019c /lb等 + *------------------------------------------ + */ +void clif_parse_LGMmessage(int fd, struct map_session_data *sd) { + unsigned char buf[64]; + + nullpo_retv(sd); + + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_LocalBroadcast))) { + WBUFW(buf,0) = 0x9a; + WBUFW(buf,2) = RFIFOW(fd,2); + memcpy(WBUFP(buf,4), RFIFOP(fd,4), RFIFOW(fd,2) - 4); + clif_send(buf, RFIFOW(fd,2), &sd->bl, ALL_SAMEMAP); + } +} + +/*========================================== + * カプラ倉庫へ入れる + *------------------------------------------ + */ +void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) { + int item_index, item_amount; + + nullpo_retv(sd); + + if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,5) - 2; + item_amount = RFIFOL(fd,12); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,5) - 2; + item_amount = RFIFOL(fd,19); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,3) - 2; + item_amount = RFIFOL(fd,15); + } else { // old version by default (+ packet version 6 and 7) + item_index = RFIFOW(fd,2) - 2; + item_amount = RFIFOL(fd,4); + } + + if (sd->npc_id != 0 || sd->vender_id != 0) + return; + + if (sd->state.storage_flag) + storage_guild_storageadd(sd, item_index, item_amount); + else + storage_storageadd(sd, item_index, item_amount); +} + +/*========================================== + * カプラ倉庫から出す + *------------------------------------------ + */ +void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) { + int item_index, item_amount; + + nullpo_retv(sd); + + if (sd->packet_ver == 8) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,10) - 1; + item_amount = RFIFOL(fd,22); + } else if (sd->packet_ver == 9) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,11) - 1; + item_amount = RFIFOL(fd,22); + } else if (sd->packet_ver == 10) { // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + item_index = RFIFOW(fd,3) - 1; + item_amount = RFIFOL(fd,13); + } else { // old version by default (+ packet version 6 and 7) + item_index = RFIFOW(fd,2) - 1; + item_amount = RFIFOL(fd,4); + } + + if (sd->npc_id != 0 || sd->vender_id != 0) + return; + + if (sd->state.storage_flag) + storage_guild_storageget(sd, item_index, item_amount); + else + storage_storageget(sd, item_index, item_amount); +} + +/*========================================== + * カプラ倉庫へカートから入れる + *------------------------------------------ + */ +void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if (sd->npc_id != 0 || sd->vender_id != 0) + return; + if (sd->state.storage_flag) + storage_guild_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4)); + else + storage_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4)); +} + +/*========================================== + * カプラ倉庫から出す + *------------------------------------------ + */ +void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if (sd->npc_id != 0 || sd->vender_id != 0) + return; + if (sd->state.storage_flag) + storage_guild_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4)); + else + storage_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4)); +} + +/*========================================== + * カプラ倉庫を閉じる + *------------------------------------------ + */ +void clif_parse_CloseKafra(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if (sd->state.storage_flag) + storage_guild_storageclose(sd); + else + storage_storageclose(sd); +} + +/*========================================== + * パーティを作る + *------------------------------------------ + */ +void clif_parse_CreateParty(int fd, struct map_session_data *sd) { + if (battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7) { + party_create(sd,RFIFOP(fd,2)); + } else + clif_skill_fail(sd,1,0,4); +} + +/*========================================== + * パーティを作る + *------------------------------------------ + */ +void clif_parse_CreateParty2(int fd, struct map_session_data *sd) { + if (battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7){ + party_create(sd, RFIFOP(fd,2)); + } else + clif_skill_fail(sd, 1, 0, 4); +} + +/*========================================== + * パーティに勧誘 + *------------------------------------------ + */ +void clif_parse_PartyInvite(int fd, struct map_session_data *sd) { + party_invite(sd, RFIFOL(fd,2)); +} + +/*========================================== + * パーティ勧誘返答 + *------------------------------------------ + */ +void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd) { + if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 5){ + party_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6)); + } else { + party_reply_invite(sd,RFIFOL(fd,2),-1); + clif_skill_fail(sd,1,0,4); + } +} + +/*========================================== + * パーティ脱退要求 + *------------------------------------------ + */ +void clif_parse_LeaveParty(int fd, struct map_session_data *sd) { + party_leave(sd); +} + +/*========================================== + * パーティ除名要求 + *------------------------------------------ + */ +void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) { + party_removemember(sd,RFIFOL(fd,2),RFIFOP(fd,6)); +} + +/*========================================== + * パーティ設定変更要求 + *------------------------------------------ + */ +void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) { + party_changeoption(sd, RFIFOW(fd,2), RFIFOW(fd,4)); +} + +/*========================================== + * パーティメッセージ送信要求 + *------------------------------------------ + */ +void clif_parse_PartyMessage(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if (is_atcommand(fd, sd, RFIFOP(fd,4), 0) != AtCommand_None) + return; + if(sd->sc_data && + (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可 + sd->sc_data[SC_NOCHAT].timer!=-1)) //チャット禁止 + return; + + party_send_message(sd, RFIFOP(fd,4), RFIFOW(fd,2)-4); +} + +/*========================================== + * 露店閉鎖 + *------------------------------------------ + */ +void clif_parse_CloseVending(int fd, struct map_session_data *sd) { + vending_closevending(sd); +} + +/*========================================== + * 露店アイテムリスト要求 + *------------------------------------------ + */ +void clif_parse_VendingListReq(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + vending_vendinglistreq(sd,RFIFOL(fd,2)); + if(sd->npc_id) + npc_event_dequeue(sd); +} + +/*========================================== + * 露店アイテム購入 + *------------------------------------------ + */ +void clif_parse_PurchaseReq(int fd, struct map_session_data *sd) { + vending_purchasereq(sd, RFIFOW(fd,2), RFIFOL(fd,4), RFIFOP(fd,8)); +} + +/*========================================== + * 露店開設 + *------------------------------------------ + */ +void clif_parse_OpenVending(int fd,struct map_session_data *sd) { + vending_openvending(sd, RFIFOW(fd,2), RFIFOP(fd,4), RFIFOB(fd,84), RFIFOP(fd,85)); +} + +/*========================================== + * /monster /item rewriten by [Yor] + *------------------------------------------ + */ +void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) { + char monster_item_name[25]; + + nullpo_retv(sd); + + memset(monster_item_name, '\0', sizeof(monster_item_name)); + + if (battle_config.atc_gmonly == 0 || pc_isGM(sd)) { + memcpy(monster_item_name, RFIFOP(fd,2), 24); + + if (mobdb_searchname(monster_item_name) != 0) { + if (pc_isGM(sd) >= get_atcommand_level(AtCommand_Monster)) + atcommand_spawn(fd, sd, "@spawn", monster_item_name); // as @spawn + } else if (itemdb_searchname(monster_item_name) != NULL) { + if (pc_isGM(sd) >= get_atcommand_level(AtCommand_Item)) + atcommand_item(fd, sd, "@item", monster_item_name); // as @item + } + + } +} + +/*========================================== + * ギルドを作る + *------------------------------------------ + */ +void clif_parse_CreateGuild(int fd,struct map_session_data *sd) { + guild_create(sd, RFIFOP(fd,6)); +} + +/*========================================== + * ギルドマスターかどうか確認 + *------------------------------------------ + */ +void clif_parse_GuildCheckMaster(int fd, struct map_session_data *sd) { + clif_guild_masterormember(sd); +} + +/*========================================== + * ギルド情報要求 + *------------------------------------------ + */ +void clif_parse_GuildReqeustInfo(int fd, struct map_session_data *sd) { + switch(RFIFOL(fd,2)){ + case 0: // ギルド基本情報、同盟敵対情報 + clif_guild_basicinfo(sd); + clif_guild_allianceinfo(sd); + break; + case 1: // メンバーリスト、役職名リスト + clif_guild_positionnamelist(sd); + clif_guild_memberlist(sd); + break; + case 2: // 役職名リスト、役職情報リスト + clif_guild_positionnamelist(sd); + clif_guild_positioninfolist(sd); + break; + case 3: // スキルリスト + clif_guild_skillinfo(sd); + break; + case 4: // 追放リスト + clif_guild_explusionlist(sd); + break; + default: + if (battle_config.error_log) + printf("clif: guild request info: unknown type %d\n", RFIFOL(fd,2)); + break; + } +} + +/*========================================== + * ギルド役職変更 + *------------------------------------------ + */ +void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd) { + int i; + + for(i = 4; i < RFIFOW(fd,2); i += 40 ){ + guild_change_position(sd, RFIFOL(fd,i), RFIFOL(fd,i+4), RFIFOL(fd,i+12), RFIFOP(fd,i+16)); + } +} + +/*========================================== + * ギルドメンバ役職変更 + *------------------------------------------ + */ +void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd) { + int i; + + nullpo_retv(sd); + + for(i=4;i<RFIFOW(fd,2);i+=12){ + guild_change_memberposition(sd->status.guild_id, + RFIFOL(fd,i),RFIFOL(fd,i+4),RFIFOL(fd,i+8)); + } +} + +/*========================================== + * ギルドエンブレム要求 + *------------------------------------------ + */ +void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd) { + struct guild *g=guild_search(RFIFOL(fd,2)); + if(g!=NULL) + clif_guild_emblem(sd,g); +} + +/*========================================== + * ギルドエンブレム変更 + *------------------------------------------ + */ +void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd) { + guild_change_emblem(sd,RFIFOW(fd,2)-4,RFIFOP(fd,4)); +} + +/*========================================== + * ギルド告知変更 + *------------------------------------------ + */ +void clif_parse_GuildChangeNotice(int fd,struct map_session_data *sd) { + guild_change_notice(sd,RFIFOL(fd,2),RFIFOP(fd,6),RFIFOP(fd,66)); +} + +/*========================================== + * ギルド勧誘 + *------------------------------------------ + */ +void clif_parse_GuildInvite(int fd,struct map_session_data *sd) { + guild_invite(sd,RFIFOL(fd,2)); +} + +/*========================================== + * ギルド勧誘返信 + *------------------------------------------ + */ +void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) { + guild_reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6)); +} + +/*========================================== + * ギルド脱退 + *------------------------------------------ + */ +void clif_parse_GuildLeave(int fd,struct map_session_data *sd) { + guild_leave(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOP(fd,14)); +} + +/*========================================== + * ギルド追放 + *------------------------------------------ + */ +void clif_parse_GuildExplusion(int fd,struct map_session_data *sd) { + guild_explusion(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOP(fd,14)); +} + +/*========================================== + * ギルド会話 + *------------------------------------------ + */ +void clif_parse_GuildMessage(int fd,struct map_session_data *sd) { + nullpo_retv(sd); + + if (is_atcommand(fd, sd, RFIFOP(fd, 4), 0) != AtCommand_None) + return; + if(sd->sc_data && + (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可 + sd->sc_data[SC_NOCHAT].timer!=-1)) //チャット禁止 + return; + + guild_send_message(sd, RFIFOP(fd,4), RFIFOW(fd,2)-4); +} + +/*========================================== + * ギルド同盟要求 + *------------------------------------------ + */ +void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) { + guild_reqalliance(sd,RFIFOL(fd,2)); +} + +/*========================================== + * ギルド同盟要求返信 + *------------------------------------------ + */ +void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) { + guild_reply_reqalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6)); +} + +/*========================================== + * ギルド関係解消 + *------------------------------------------ + */ +void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) { + guild_delalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6)); +} + +/*========================================== + * ギルド敵対 + *------------------------------------------ + */ +void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) { + guild_opposition(sd,RFIFOL(fd,2)); +} + +/*========================================== + * ギルド解散 + *------------------------------------------ + */ +void clif_parse_GuildBreak(int fd, struct map_session_data *sd) { + guild_break(sd,RFIFOP(fd,2)); +} + +// pet +void clif_parse_PetMenu(int fd, struct map_session_data *sd) { + pet_menu(sd,RFIFOB(fd,2)); +} + +void clif_parse_CatchPet(int fd, struct map_session_data *sd) { + pet_catch_process2(sd,RFIFOL(fd,2)); +} + +void clif_parse_SelectEgg(int fd, struct map_session_data *sd) { + pet_select_egg(sd,RFIFOW(fd,2)-2); +} + +void clif_parse_SendEmotion(int fd, struct map_session_data *sd) { + nullpo_retv(sd); + + if(sd->pd) + clif_pet_emotion(sd->pd,RFIFOL(fd,2)); +} + +void clif_parse_ChangePetName(int fd, struct map_session_data *sd) { + pet_change_name(sd,RFIFOP(fd,2)); +} + +// Kick (right click menu for GM "(name) force to quit") +void clif_parse_GMKick(int fd, struct map_session_data *sd) { + struct block_list *target; + int tid = RFIFOL(fd,2); + + nullpo_retv(sd); + + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_Kick))) { + target = map_id2bl(tid); + if (target) { + if (target->type == BL_PC) { + struct map_session_data *tsd = (struct map_session_data *)target; + if (pc_isGM(sd) > pc_isGM(tsd)) + clif_GM_kick(sd, tsd, 1); + else + clif_GM_kickack(sd, 0); + } else if (target->type == BL_MOB) { + struct mob_data *md = (struct mob_data *)target; + sd->state.attack_type = 0; + mob_damage(&sd->bl, md, md->hp, 2); + } else + clif_GM_kickack(sd, 0); + } else + clif_GM_kickack(sd, 0); + } +} + +/*========================================== + * /shift + *------------------------------------------ + */ +void clif_parse_Shift(int fd, struct map_session_data *sd) { // Rewriten by [Yor] + char player_name[25]; + + nullpo_retv(sd); + + memset(player_name, '\0', sizeof(player_name)); + + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_JumpTo))) { + memcpy(player_name, RFIFOP(fd,2), 24); + 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]; + + nullpo_retv(sd); + + memset(player_name, '\0', sizeof(player_name)); + + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_Recall))) { + memcpy(player_name, RFIFOP(fd,2), 24); + atcommand_recall(fd, sd, "@recall", player_name); // as @recall + } + + return; +} + +void clif_parse_GMHide(int fd, struct map_session_data *sd) { // Modified by [Yor] + nullpo_retv(sd); + + //printf("%2x %2x %2x\n", RFIFOW(fd,0), RFIFOW(fd,2), RFIFOW(fd,4)); // R 019d <Option_value>.2B <flag>.2B + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide))) { + if (sd->status.option & OPTION_HIDE) { // OPTION_HIDE = 0x40 + sd->status.option &= ~OPTION_HIDE; // OPTION_HIDE = 0x40 + clif_displaymessage(fd, "Invisible: Off."); + } else { + sd->status.option |= OPTION_HIDE; // OPTION_HIDE = 0x40 + clif_displaymessage(fd, "Invisible: On."); + } + clif_changeoption(&sd->bl); + } +} + +/*========================================== + * GMによるチャット禁止時間付与 + *------------------------------------------ + */ +void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd) +{ + int tid = RFIFOL(fd,2); + int type = RFIFOB(fd,6); + int limit = RFIFOW(fd,7); + struct block_list *bl = map_id2bl(tid); + struct map_session_data *dstsd; + int dstfd; + + nullpo_retv(sd); + + if(!battle_config.muting_players) { + clif_displaymessage(fd, "Muting is disabled."); + return; + } + + if(type == 0) + limit = 0 - limit; + if(bl->type == BL_PC && (dstsd =(struct map_session_data *)bl)){ + if((tid == bl->id && type == 2 && !pc_isGM(sd)) || (pc_isGM(sd) > pc_isGM(dstsd)) ){ + dstfd = dstsd->fd; + WFIFOW(dstfd,0)=0x14b; + WFIFOB(dstfd,2)=(type==2)?1:type; + memcpy(WFIFOP(dstfd,3),sd->status.name,24); + WFIFOSET(dstfd,packet_len_table[0x14b]); + dstsd->status.manner -= limit; + if(dstsd->status.manner < 0) + skill_status_change_start(bl,SC_NOCHAT,0,0,0,0,0,0); + else{ + dstsd->status.manner = 0; + skill_status_change_end(bl,SC_NOCHAT,-1); + } + printf("name:%s type:%d limit:%d manner:%d\n",dstsd->status.name,type,limit,dstsd->status.manner); + } + } + + return; +} +/*========================================== + * GMによるチャット禁止時間参照(?) + *------------------------------------------ + */ +void clif_parse_GMReqNoChatCount(int fd,struct map_session_data *sd) +{ + int tid = RFIFOL(fd,2); + + WFIFOW(fd,0)=0x1e0; + WFIFOL(fd,2)=tid; + sprintf(WFIFOP(fd,6),"%d",tid); +// memcpy(WFIFOP(fd,6),"TESTNAME",24); + WFIFOSET(fd,packet_len_table[0x1e0]); + + return; +} + +void clif_parse_PMIgnore(int fd, struct map_session_data *sd) { // Rewritten by [Yor] + char output[1024]; + char *nick; // S 00cf <nick>.24B <type>.B: 00 (/ex nick) deny speech from nick, 01 (/in nick) allow speech from nick + int i; + int pos; + + memset(output, '\0', sizeof(output)); + + nick = RFIFOP(fd,2); // speed up + //printf("Ignore: char '%s' state: %d\n", nick, RFIFOB(fd,26)); + // we ask for deny (we add nick only if it's not already exist + if (RFIFOB(fd,26) == 0) { // type + if (strlen(nick) >= 4 && strlen(nick) < 24) { // do something only if nick can be exist + pos = -1; + for(i = 0; i < (sizeof(sd->ignore) / sizeof(sd->ignore[0])); i++) { + if (strcmp(sd->ignore[i].name, nick) == 0) + break; + else if (pos == -1 && sd->ignore[i].name[0] == '\0') + pos = i; + } + WFIFOW(fd,0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB(fd,2) = 0; + // if a position is found and name not found, we add it in the list + if (pos != -1 && i == (sizeof(sd->ignore) / sizeof(sd->ignore[0]))) { + memcpy(sd->ignore[pos].name, nick, 24); + WFIFOB(fd,3) = 0; // success + WFIFOSET(fd, packet_len_table[0x0d1]); + if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users that automaticaly ignores people. + sprintf(output, "Character '%s' (account: %d) has tried to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name); + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output, strlen(output) + 1); + // send something to be inform and force bot to ignore twice... If GM receiving block + block again, it's a bot :) + clif_wis_message(fd, wisp_server_name, "Add me in your ignore list, doesn't block my wisps.", strlen("Add me in your ignore list, doesn't block my wisps.") + 1); + } + } else { + WFIFOB(fd,3) = 1; // fail + if (i == (sizeof(sd->ignore) / sizeof(sd->ignore[0]))) { + clif_wis_message(fd, wisp_server_name, "You can not block more people.", strlen("You can not block more people.") + 1); + if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users that automaticaly ignores people. + sprintf(output, "Character '%s' (account: %d) has tried to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name); + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output, strlen(output) + 1); + } + } else { + clif_wis_message(fd, wisp_server_name, "This player is already blocked.", strlen("This player is already blocked.") + 1); + if (strcmp(wisp_server_name, nick) == 0) { // to found possible bot users that automaticaly ignores people. + sprintf(output, "Character '%s' (account: %d) has tried AGAIN to block wisps from '%s' (wisp name of the server). Bot user?", sd->status.name, sd->status.account_id, wisp_server_name); + intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, output, strlen(output) + 1); + } + } + } + } else + clif_wis_message(fd, wisp_server_name, "It's impossible to block this player.", strlen("It's impossible to block this player.") + 1); + // we ask for allow (we remove all same nick if exist) + } else { + if (strlen(nick) >= 4 && strlen(nick) < 24) { // do something only if nick can be exist + WFIFOW(fd,0) = 0x0d1; // R 00d1 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB(fd,2) = 1; + for(i = 0; i < (sizeof(sd->ignore) / sizeof(sd->ignore[0])); i++) + if (strcmp(sd->ignore[i].name, nick) == 0) { + memset(sd->ignore[i].name, 0, sizeof(sd->ignore[i].name)); + WFIFOB(fd,3) = 0; // success + WFIFOSET(fd, packet_len_table[0x0d1]); + break; + } + if (i == (sizeof(sd->ignore) / sizeof(sd->ignore[0]))) { + WFIFOB(fd,3) = 1; // fail + WFIFOSET(fd, packet_len_table[0x0d1]); + clif_wis_message(fd, wisp_server_name, "This player is not blocked by you.", strlen("This player is not blocked by you.") + 1); + } + } else + clif_wis_message(fd, wisp_server_name, "It's impossible to unblock this player.", strlen("It's impossible to unblock this player.") + 1); + } + +// for(i = 0; i < (sizeof(sd->ignore) / sizeof(sd->ignore[0])); i++) // for debug only +// if (sd->ignore[i].name[0] != '\0') +// printf("Ignored player: '%s'\n", sd->ignore[i].name); + + return; +} + +void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd) { // Rewritten by [Yor] + //printf("Ignore all: state: %d\n", RFIFOB(fd,2)); + if (RFIFOB(fd,2) == 0) {// S 00d0 <type>len.B: 00 (/exall) deny all speech, 01 (/inall) allow all speech + WFIFOW(fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB(fd,2) = 0; + if (sd->ignoreAll == 0) { + sd->ignoreAll = 1; + WFIFOB(fd,3) = 0; // success + WFIFOSET(fd, packet_len_table[0x0d2]); + } else { + WFIFOB(fd,3) = 1; // fail + WFIFOSET(fd, packet_len_table[0x0d2]); + clif_wis_message(fd, wisp_server_name, "You already block everyone.", strlen("You already block everyone.") + 1); + } + } else { + WFIFOW(fd,0) = 0x0d2; // R 00d2 <type>.B <fail>.B: type: 0: deny, 1: allow, fail: 0: success, 1: fail + WFIFOB(fd,2) = 1; + if (sd->ignoreAll == 1) { + sd->ignoreAll = 0; + WFIFOB(fd,3) = 0; // success + WFIFOSET(fd, packet_len_table[0x0d2]); + } else { + WFIFOB(fd,3) = 1; // fail + WFIFOSET(fd, packet_len_table[0x0d2]); + clif_wis_message(fd, wisp_server_name, "You already allow everyone.", strlen("You already allow everyone.") + 1); + } + } + + return; +} + +void clif_parse_skillMessage(int fd, struct map_session_data *sd) { // Added by RoVeRT + int skillid,skilllv, x, y; + char *mes; + + skilllv = RFIFOW(fd,2); + skillid = RFIFOW(fd,4); + + y = RFIFOB(fd,6); + x = RFIFOB(fd,8); + + mes = RFIFOP(fd,10); + + // skill 220 = graffiti +// printf("skill: %d %d location: %3d %3d message: %s\n", skillid, skilllv, x, y, (char*)mes); +} + +int monk(struct map_session_data *sd, struct block_list *target, int type) { +//R 01d1 <Monk id>L <Target monster id>L <Bool>L + int fd=sd->fd; + WFIFOW(fd,0)=0x1d1; + WFIFOL(fd,2)=sd->bl.id; + WFIFOL(fd,6)=target->id; + WFIFOL(fd,10)=type; + WFIFOSET(fd,packet_len_table[0x1d1]); + + return 0; +} + +/*========================================== + * スパノビの/doridoriによるSPR2倍 + *------------------------------------------ + */ +void clif_parse_sn_doridori(int fd, struct map_session_data *sd) { + if (sd) + sd->doridori_counter = 1; + + return; +} +/*========================================== + * スパノビの爆裂波動 + *------------------------------------------ + */ +void clif_parse_sn_explosionspirits(int fd, struct map_session_data *sd) +{ + if(sd){ + int nextbaseexp=pc_nextbaseexp(sd); + struct pc_base_job s_class = pc_calc_base_job(sd->status.class); + if (battle_config.etc_log){ + if(nextbaseexp != 0) + printf("SuperNovice explosionspirits!! %d %d %d %d\n",sd->bl.id,s_class.job,sd->status.base_exp,(int)((double)1000*sd->status.base_exp/nextbaseexp)); + else + printf("SuperNovice explosionspirits!! %d %d %d 000\n",sd->bl.id,s_class.job,sd->status.base_exp); + } + if(s_class.job == 23 && sd->status.base_exp > 0 && nextbaseexp > 0 && (int)((double)1000*sd->status.base_exp/nextbaseexp)%100==0){ + clif_skill_nodamage(&sd->bl,&sd->bl,MO_EXPLOSIONSPIRITS,5,1); + skill_status_change_start(&sd->bl,SkillStatusChangeTable[MO_EXPLOSIONSPIRITS],5,0,0,0,skill_get_time(MO_EXPLOSIONSPIRITS,5),0 ); + } + } + return; +} + +// functions list +static void (*clif_parse_func_table[4][0x220])() = { + { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + // 40 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + // 70 + NULL, NULL, clif_parse_WantToConnection, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, clif_parse_LoadEndAck, clif_parse_TickSend, NULL, + + // 80 + NULL, NULL, NULL, NULL, NULL, clif_parse_WalkToXY, NULL, NULL, + NULL, clif_parse_ActionRequest, NULL, NULL, clif_parse_GlobalMessage, NULL, NULL, NULL, + // 90 + clif_parse_NpcClicked, NULL, NULL, NULL, clif_parse_GetCharNameRequest, NULL, clif_parse_Wis, NULL, + NULL, clif_parse_GMmessage, NULL, clif_parse_ChangeDir, NULL, NULL, NULL, clif_parse_TakeItem, + // a0 + NULL, NULL, clif_parse_DropItem, NULL, NULL, NULL, NULL, clif_parse_UseItem, + NULL, clif_parse_EquipItem, NULL, clif_parse_UnequipItem, NULL, NULL, NULL, NULL, + // b0 + NULL, NULL, clif_parse_Restart, NULL, NULL, NULL, NULL, NULL, + clif_parse_NpcSelectMenu, clif_parse_NpcNextClicked, NULL, clif_parse_StatusUp, NULL, NULL, NULL, clif_parse_Emotion, + + // c0 + NULL, clif_parse_HowManyConnections, NULL, NULL, NULL, clif_parse_NpcBuySellSelected, NULL, NULL, + clif_parse_NpcBuyListSend, clif_parse_NpcSellListSend, NULL, NULL, clif_parse_GMKick, NULL, NULL, clif_parse_PMIgnore, + // d0 + clif_parse_PMIgnoreAll, NULL, NULL, NULL, NULL, clif_parse_CreateChatRoom, NULL, NULL, + NULL, clif_parse_ChatAddMember, NULL, NULL, NULL, NULL, clif_parse_ChatRoomStatusChange, NULL, + // e0 + clif_parse_ChangeChatOwner, NULL, clif_parse_KickFromChat, clif_parse_ChatLeave, clif_parse_TradeRequest, NULL, clif_parse_TradeAck, NULL, + clif_parse_TradeAddItem, NULL, NULL, clif_parse_TradeOk, NULL, clif_parse_TradeCansel, NULL, clif_parse_TradeCommit, + // f0 + NULL, NULL, NULL, clif_parse_MoveToKafra, NULL, clif_parse_MoveFromKafra, NULL, clif_parse_CloseKafra, + NULL, clif_parse_CreateParty, NULL, NULL, clif_parse_PartyInvite, NULL, NULL, clif_parse_ReplyPartyInvite, + + // 100 + clif_parse_LeaveParty, NULL, clif_parse_PartyChangeOption, clif_parse_RemovePartyMember, NULL, NULL, NULL, NULL, + clif_parse_PartyMessage, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + // 110 + NULL, NULL, clif_parse_SkillUp, clif_parse_UseSkillToId, NULL, NULL, clif_parse_UseSkillToPos, NULL, + clif_parse_StopAttack, NULL, NULL, clif_parse_UseSkillMap, NULL, clif_parse_RequestMemo, NULL, NULL, + // 120 + NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_PutItemToCart, clif_parse_GetItemFromCart, + clif_parse_MoveFromKafraToCart, clif_parse_MoveToKafraFromCart, clif_parse_RemoveOption, NULL, NULL, NULL, clif_parse_CloseVending, NULL, + // 130 + clif_parse_VendingListReq, NULL, NULL, NULL, clif_parse_PurchaseReq, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_GM_Monster_Item, + + // 140 + clif_parse_MapMove, NULL, NULL, clif_parse_NpcAmountInput, NULL, NULL, clif_parse_NpcCloseClicked, NULL, + NULL, clif_parse_GMReqNoChat, NULL, NULL, NULL, clif_parse_GuildCheckMaster, NULL, clif_parse_GuildReqeustInfo, + // 150 + NULL, clif_parse_GuildRequestEmblem, NULL, clif_parse_GuildChangeEmblem, NULL, clif_parse_GuildChangeMemberPosition, NULL, NULL, + NULL, clif_parse_GuildLeave, NULL, clif_parse_GuildExplusion, NULL, clif_parse_GuildBreak, NULL, NULL, + // 160 + NULL, clif_parse_GuildChangePositionInfo, NULL, NULL, NULL, clif_parse_CreateGuild, NULL, NULL, + clif_parse_GuildInvite, NULL, NULL, clif_parse_GuildReplyInvite, NULL, NULL, clif_parse_GuildChangeNotice, NULL, + // 170 + clif_parse_GuildRequestAlliance, NULL, clif_parse_GuildReplyAlliance, NULL, NULL, NULL, NULL, NULL, + clif_parse_ItemIdentify, NULL, clif_parse_UseCard, NULL, clif_parse_InsertCard, NULL, clif_parse_GuildMessage, NULL, + + // 180 + clif_parse_GuildOpposition, NULL, NULL, clif_parse_GuildDelAlliance, NULL, NULL, NULL, NULL, + NULL, NULL, clif_parse_QuitGame, NULL, NULL, NULL, clif_parse_ProduceMix, NULL, + // 190 + clif_parse_UseSkillToPos, NULL, NULL, clif_parse_SolveCharName, NULL, NULL, NULL, clif_parse_ResetChar, + NULL, NULL, NULL, NULL, clif_parse_LGMmessage, clif_parse_GMHide, NULL, clif_parse_CatchPet, + // 1a0 + NULL, clif_parse_PetMenu, NULL, NULL, NULL, clif_parse_ChangePetName, NULL, clif_parse_SelectEgg, + NULL, clif_parse_SendEmotion, NULL, NULL, NULL, NULL, clif_parse_SelectArrow, clif_parse_ChangeCart, + // 1b0 + NULL, NULL, clif_parse_OpenVending, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, clif_parse_Shift, clif_parse_Shift, clif_parse_Recall, clif_parse_Recall, NULL, NULL, + + // 1c0 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_AutoSpell, + NULL, + // 1d0 + NULL, NULL, NULL, NULL, NULL, clif_parse_NpcStringInput, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_GMReqNoChatCount, + // 1e0 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_sn_doridori, + clif_parse_CreateParty2, NULL, NULL, NULL, NULL, clif_parse_sn_explosionspirits, NULL, NULL, + // 1f0 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + // 200 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + // 210 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +#if 0 + case 0xce: clif_parse_GMkillall + case 0xd3: clif_parse_IgnoreList +#endif + }, + {NULL}, + {NULL}, + {NULL} +}; + +/*========================================== + * クライアントからのパケット解析 + * socket.cのdo_parsepacketから呼び出される + *------------------------------------------ + */ +static int clif_parse(int fd) { + int packet_len = 0, cmd=0, packet_ver =0; + struct map_session_data *sd=NULL; + + sd = session[fd]->session_data; + + // 接続が切れてるので後始末 + if (!chrif_isconnect() || session[fd]->eof) { // char鯖に繋がってない間は接続禁止 (!chrif_isconnect()) + if (sd && sd->state.auth) { + clif_quitsave(fd, sd); + if (sd->status.name != NULL) + printf("Player [%s] has logged off your server.\n", sd->status.name); // Player logout display [Valaris] + else + printf("Player with account [%d] has logged off your server.\n", sd->bl.id); // Player logout display [Yor] + } else if (sd) { // not authentified! (refused by char-server or disconnect before to be authentified) + printf("Player with account [%d] has logged off your server (not auth account).\n", sd->bl.id); // Player logout display [Yor] + map_deliddb(&sd->bl); // account_id has been included in the DB before auth answer + } + if(fd) close(fd); + if(fd) delete_session(fd); + return 0; + } + + if (RFIFOREST(fd) < 2) + return 0; + + cmd = RFIFOW(fd,0); + + // 管理用パケット処理 + if (cmd >= 30000) { + switch(cmd) { + case 0x7530: // Athena情報所得 + WFIFOW(fd,0) = 0x7531; + WFIFOB(fd,2) = ATHENA_MAJOR_VERSION; + WFIFOB(fd,3) = ATHENA_MINOR_VERSION; + WFIFOB(fd,4) = ATHENA_REVISION; + WFIFOB(fd,5) = ATHENA_RELEASE_FLAG; + WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG; + WFIFOB(fd,7) = ATHENA_SERVER_MAP; + WFIFOW(fd,8) = ATHENA_MOD_VERSION; + WFIFOSET(fd,10); + RFIFOSKIP(fd,2); + break; + case 0x7532: // 接続の切断 + session[fd]->eof = 1; + break; + } + return 0; + } + + // get packet version before to parse + packet_ver = 0; + if (sd) + packet_ver = sd->packet_ver; + // check authentification packet to know packet version + else { + // 0x72 + if (cmd == 0x72) { + if (RFIFOREST(fd) >= 39 && (RFIFOB(fd,38) == 0 || RFIFOB(fd,38) == 1)) // 00 = Female, 01 = Male + packet_ver = 7; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + else if (RFIFOREST(fd) >= 22 && (RFIFOB(fd,21) == 0 || RFIFOB(fd,21) == 1)) // 00 = Female, 01 = Male + packet_ver = 6; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + else if (RFIFOREST(fd) >= 19 && (RFIFOB(fd,18) == 0 || RFIFOB(fd,18) == 1)) // 00 = Female, 01 = Male + packet_ver = 5; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + // else probably incomplete packet + else if (RFIFOREST(fd) < 19) + return 0; + // 0x7E + } else if (cmd == 0x7E) { + if (RFIFOREST(fd) >= 37 && (RFIFOB(fd,36) == 0 || RFIFOB(fd,36) == 1)) // 00 = Female, 01 = Male + packet_ver = 9; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + else if (RFIFOREST(fd) >= 33 && (RFIFOB(fd,32) == 0 || RFIFOB(fd,32) == 1)) // 00 = Female, 01 = Male + packet_ver = 8; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + // else probably incomplete packet + else if (RFIFOREST(fd) < 33) + return 0; + // 0xF5 + } else { + if (RFIFOREST(fd) >= 34 && (RFIFOB(fd,33) == 0 || RFIFOB(fd,33) == 1)) // 00 = Female, 01 = Male + packet_ver = 10; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + // else probably incomplete packet + else if (RFIFOREST(fd) < 34) + return 0; + } + // check if version is accepted + if ((packet_ver == 5 && (battle_config.packet_ver_flag & 1) == 0) || + (packet_ver == 6 && (battle_config.packet_ver_flag & 2) == 0) || + (packet_ver == 7 && (battle_config.packet_ver_flag & 4) == 0) || + (packet_ver == 8 && (battle_config.packet_ver_flag & 8) == 0) || + (packet_ver == 9 && (battle_config.packet_ver_flag & 16) == 0) || + (packet_ver == 10 && (battle_config.packet_ver_flag & 32) == 0)) { + WFIFOW(fd,0) = 0x6a; + WFIFOB(fd,2) = 5; // 05 = Game's EXE is not the latest version + WFIFOSET(fd,23); + session[fd]->eof = 1; + return 0; + } + } + + // ゲーム用以外パケットか、認証を終える前に0072以外が来たら、切断する + if (packet_ver < 5 || packet_ver > 10 || // if packet is not inside these values: session is incorrect?? or auth packet is unknown + cmd >= 0x220 || packet_size_table[packet_ver-5][cmd] == 0) { + if (!fd) + return 0; + session[fd]->eof = 1; + printf("clif_parse: session #%d, packet 0x%x (%d bytes received) -> disconnected.\n", fd, cmd, RFIFOREST(fd)); + return 0; + } + + // パケット長を計算 + packet_len = packet_size_table[packet_ver-5][cmd]; + if (packet_len == -1) { + if (RFIFOREST(fd) < 4) + return 0; // 可変長パケットで長さの所までデータが来てない + packet_len = RFIFOW(fd,2); + if (packet_len < 4 || packet_len > 32768) { + session[fd]->eof = 1; + return 0; + } + } + if (RFIFOREST(fd) < packet_len) + return 0; // まだ1パケット分データが揃ってない + + if (sd && sd->state.auth == 1 && sd->state.waitingdisconnect == 1) { // 切断待ちの場合パケットを処理しない + + } else if (packet_ver < 8 && clif_parse_func_table[0][cmd]) { // packet version 5-6-7 use same functions, but size are different + // パケット処理 + clif_parse_func_table[0][cmd](fd, sd); + } else if (packet_ver >= 8 && clif_parse_func_table[packet_ver - 7][cmd]) { + // パケット処理 + clif_parse_func_table[packet_ver - 7][cmd](fd, sd); + } else { + // 不明なパケット + if (battle_config.error_log) { + if (fd) + printf("\nclif_parse: session #%d, packet 0x%x, lenght %d\n", fd, cmd, packet_len); +#ifdef DUMP_UNKNOWN_PACKET + { + int i; + FILE *fp; + char packet_txt[256] = "save/packet.txt"; + time_t now; + printf("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F"); + for(i = 0; i < packet_len; i++) { + if ((i & 15) == 0) + printf("\n%04X ",i); + printf("%02X ", RFIFOB(fd,i)); + } + if (sd && sd->state.auth) { + if (sd->status.name != NULL) + printf("\nAccount ID %d, character ID %d, player name %s.\n", + sd->status.account_id, sd->status.char_id, sd->status.name); + else + printf("\nAccount ID %d.\n", sd->bl.id); + } else if (sd) // not authentified! (refused by char-server or disconnect before to be authentified) + printf("\nAccount ID %d.\n", sd->bl.id); + + if ((fp = fopen(packet_txt, "a")) == NULL) { + printf("clif.c: cant write [%s] !!! data is lost !!!\n", packet_txt); + return 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 + } + } + RFIFOSKIP(fd, packet_len); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int do_init_clif(void) { + int i; + + // functions of packet version 5-6-7 are same, but size are different + // init packet function calls for packet ver 8 + memcpy(clif_parse_func_table[1], clif_parse_func_table[0], sizeof(clif_parse_func_table[0])); + clif_parse_func_table[1][0x072] = clif_parse_DropItem; + clif_parse_func_table[1][0x07e] = clif_parse_WantToConnection; + clif_parse_func_table[1][0x085] = clif_parse_UseSkillToId; + clif_parse_func_table[1][0x089] = clif_parse_GetCharNameRequest; + clif_parse_func_table[1][0x08c] = clif_parse_UseSkillToPos; + clif_parse_func_table[1][0x094] = clif_parse_TakeItem; + clif_parse_func_table[1][0x09b] = clif_parse_WalkToXY; + clif_parse_func_table[1][0x09f] = clif_parse_ChangeDir; + clif_parse_func_table[1][0x0a2] = clif_parse_UseSkillToPos; + clif_parse_func_table[1][0x0a7] = clif_parse_SolveCharName; + clif_parse_func_table[1][0x0f3] = clif_parse_GlobalMessage; + clif_parse_func_table[1][0x0f5] = clif_parse_UseItem; + clif_parse_func_table[1][0x0f7] = clif_parse_TickSend; + clif_parse_func_table[1][0x113] = clif_parse_MoveToKafra; + clif_parse_func_table[1][0x116] = clif_parse_CloseKafra; + clif_parse_func_table[1][0x190] = clif_parse_MoveFromKafra; + clif_parse_func_table[1][0x193] = clif_parse_ActionRequest; + // init packet function calls for packet ver 9 (same function of packet version 8, but size are different) + memcpy(clif_parse_func_table[2], clif_parse_func_table[1], sizeof(clif_parse_func_table[0])); + // init packet function calls for packet ver 10 + memcpy(clif_parse_func_table[3], clif_parse_func_table[2], sizeof(clif_parse_func_table[0])); + clif_parse_func_table[3][0x072] = clif_parse_UseItem; + clif_parse_func_table[3][0x07e] = clif_parse_MoveToKafra; + clif_parse_func_table[3][0x085] = clif_parse_ActionRequest; + clif_parse_func_table[3][0x089] = clif_parse_WalkToXY; + clif_parse_func_table[3][0x08c] = clif_parse_UseSkillToPos; + clif_parse_func_table[3][0x094] = clif_parse_DropItem; + clif_parse_func_table[3][0x09b] = clif_parse_GetCharNameRequest; + clif_parse_func_table[3][0x09f] = clif_parse_GlobalMessage; + clif_parse_func_table[3][0x0a2] = clif_parse_SolveCharName; + clif_parse_func_table[3][0x0a7] = clif_parse_UseSkillToPos; + clif_parse_func_table[3][0x0f3] = clif_parse_ChangeDir; + clif_parse_func_table[3][0x0f5] = clif_parse_WantToConnection; + clif_parse_func_table[3][0x0f7] = clif_parse_CloseKafra; + clif_parse_func_table[3][0x113] = clif_parse_TakeItem; + clif_parse_func_table[3][0x116] = clif_parse_TickSend; + clif_parse_func_table[3][0x190] = clif_parse_UseSkillToId; + clif_parse_func_table[3][0x193] = clif_parse_MoveFromKafra; + + // size of packet version 5 + memcpy(&packet_size_table[0], &packet_len_table, sizeof(packet_len_table)); + // size of packet version 6 + memcpy(&packet_size_table[1], &packet_size_table[0], sizeof(packet_len_table)); + packet_size_table[1][0x072] = 22; + packet_size_table[1][0x085] = 8; + packet_size_table[1][0x0a7] = 13; + packet_size_table[1][0x113] = 15; + packet_size_table[1][0x116] = 15; + packet_size_table[1][0x190] = 95; + // size of packet version 7 + memcpy(&packet_size_table[2], &packet_size_table[1], sizeof(packet_len_table)); + packet_size_table[2][0x072] = 39; + packet_size_table[2][0x085] = 9; + packet_size_table[2][0x09b] = 13; + packet_size_table[2][0x09f] = 10; + packet_size_table[2][0x0a7] = 17; + packet_size_table[2][0x113] = 19; + packet_size_table[2][0x116] = 19; + packet_size_table[2][0x190] = 99; + // size of packet version 8 + memcpy(&packet_size_table[3], &packet_size_table[2], sizeof(packet_len_table)); + packet_size_table[3][0x072] = 14; + packet_size_table[3][0x07e] = 33; + packet_size_table[3][0x085] = 20; + packet_size_table[3][0x089] = 15; + packet_size_table[3][0x08c] = 23; + packet_size_table[3][0x094] = 10; + packet_size_table[3][0x09b] = 6; + packet_size_table[3][0x09f] = 13; + packet_size_table[3][0x0a2] = 103; + packet_size_table[3][0x0a7] = 12; + packet_size_table[3][0x0f3] = -1; + packet_size_table[3][0x0f5] = 17; + packet_size_table[3][0x0f7] = 10; + packet_size_table[3][0x113] = 16; + packet_size_table[3][0x116] = 2; + packet_size_table[3][0x190] = 26; + packet_size_table[3][0x193] = 9; + // size of packet version 9 + memcpy(&packet_size_table[4], &packet_size_table[3], sizeof(packet_len_table)); + packet_size_table[4][0x072] = 17; + packet_size_table[4][0x07e] = 37; + packet_size_table[4][0x085] = 26; + packet_size_table[4][0x089] = 12; + packet_size_table[4][0x08c] = 40; + packet_size_table[4][0x094] = 13; + packet_size_table[4][0x09b] = 15; + packet_size_table[4][0x09f] = 12; + packet_size_table[4][0x0a2] = 120; + packet_size_table[4][0x0a7] = 11; +// packet_size_table[4][0x0f3] = -1; + packet_size_table[4][0x0f5] = 24; + packet_size_table[4][0x0f7] = 13; + packet_size_table[4][0x113] = 23; +// packet_size_table[4][0x116] = 2; + packet_size_table[4][0x190] = 26; + packet_size_table[4][0x193] = 18; + // new packet + packet_size_table[4][0x20f] = 10; + packet_size_table[4][0x210] = 22; + packet_size_table[4][0x212] = 26; + packet_size_table[4][0x213] = 26; + packet_size_table[4][0x214] = 42; + // size of packet version 9 + memcpy(&packet_size_table[5], &packet_size_table[4], sizeof(packet_len_table)); + packet_size_table[5][0x072] = 20; + packet_size_table[5][0x07e] = 19; + packet_size_table[5][0x085] = 23; + packet_size_table[5][0x089] = 9; + packet_size_table[5][0x08c] = 105; + packet_size_table[5][0x094] = 17; + packet_size_table[5][0x09b] = 14; + packet_size_table[5][0x09f] = -1; + packet_size_table[5][0x0a2] = 14; + packet_size_table[5][0x0a7] = 25; + packet_size_table[5][0x0f3] = 10; + packet_size_table[5][0x0f5] = 34; + packet_size_table[5][0x0f7] = 2; + packet_size_table[5][0x113] = 11; + packet_size_table[5][0x116] = 11; + packet_size_table[5][0x190] = 22; + packet_size_table[5][0x193] = 17; + + set_defaultparse(clif_parse); + for(i = 0; i < 10; i++) { + if (make_listen_port(map_port)) + break; +#ifdef LCCWIN32 + Sleep(20000); +#else + sleep(20); +#endif + } + if (i == 10) { + printf("cant bind game port\n"); + exit(1); + } + + add_timer_func_list(clif_waitclose, "clif_waitclose"); + add_timer_func_list(clif_clearchar_delay_sub, "clif_clearchar_delay_sub"); + + return 0; +} + diff --git a/src/map/clif.h b/src/map/clif.h new file mode 100644 index 0000000..cdddd8b --- /dev/null +++ b/src/map/clif.h @@ -0,0 +1,280 @@ +// $Id: clif.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _CLIF_H_ +#define _CLIF_H_ + +#include <sys/types.h> + +#ifdef LCCWIN32 +#include <winsock.h> +typedef unsigned int in_addr_t; +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include "map.h" + +void clif_setip(char*); +void clif_setport(int); + +in_addr_t clif_getip(void); +int clif_getport(void); +int clif_countusers(void); +void clif_setwaitclose(int); + +int clif_authok(struct map_session_data *); +int clif_authfail_fd(int,int); +int clif_charselectok(int); +int clif_dropflooritem(struct flooritem_data *); +int clif_clearflooritem(struct flooritem_data *,int); +int clif_clearchar(struct block_list*,int); // area or fd +int clif_clearchar_delay(unsigned int,struct block_list *,int); +#define clif_clearchar_area(bl,type) clif_clearchar(bl,type) +int clif_clearchar_id(int,int,int); +int clif_spawnpc(struct map_session_data*); //area +int clif_spawnnpc(struct npc_data*); // area +int clif_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_changemap(struct map_session_data*,char*,int,int); //self +int clif_changemapserver(struct map_session_data*,char*,int,int,int,int); //self +int clif_fixpos(struct block_list *); // area +int clif_fixmobpos(struct mob_data *md); +int clif_fixpcpos(struct map_session_data *sd); +int clif_fixpetpos(struct pet_data *pd); +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 + +int clif_createchat(struct map_session_data*,int); // self +int clif_dispchat(struct chat_data*,int); // area or fd +int clif_joinchatfail(struct map_session_data*,int); // self +int clif_joinchatok(struct map_session_data*,struct chat_data*); // self +int clif_addchat(struct chat_data*,struct map_session_data*); // chat +int clif_changechatowner(struct chat_data*,struct map_session_data*); // chat +int clif_clearchat(struct chat_data*,int); // area or fd +int clif_leavechat(struct chat_data*,struct map_session_data*); // chat +int clif_changechatstatus(struct chat_data*); // chat + +void clif_emotion(struct block_list *bl,int type); +void clif_talkiebox(struct block_list *bl,char* talkie); +void clif_wedding_effect(struct block_list *bl); +void clif_sitting(int fd, struct map_session_data *sd); +//void clif_callpartner(struct map_session_data *sd); +//void clif_sitting(struct map_session_data *sd); +void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type); + +// trade +int clif_traderequest(struct map_session_data *sd,char *name); +int clif_tradestart(struct map_session_data *sd,int type); +int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount); +int clif_tradeitemok(struct map_session_data *sd,int index,int amount,int fail); +int clif_tradedeal_lock(struct map_session_data *sd,int fail); +int clif_tradecancelled(struct map_session_data *sd); +int clif_tradecompleted(struct map_session_data *sd,int fail); + +// storage +#include "storage.h" +int clif_storageitemlist(struct map_session_data *sd,struct storage *stor); +int clif_storageequiplist(struct map_session_data *sd,struct storage *stor); +int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor); +int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount); +int clif_storageitemremoved(struct map_session_data *sd,int index,int amount); +int clif_storageclose(struct map_session_data *sd); +int clif_guildstorageitemlist(struct map_session_data *sd,struct guild_storage *stor); +int clif_guildstorageequiplist(struct map_session_data *sd,struct guild_storage *stor); +int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor); +int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount); + +int clif_pcinsight(struct block_list *,va_list); // map_forallinmovearea callback +int clif_pcoutsight(struct block_list *,va_list); // map_forallinmovearea callback +int clif_mobinsight(struct block_list *,va_list); // map_forallinmovearea callback +int clif_moboutsight(struct block_list *,va_list); // map_forallinmovearea callback +int clif_petoutsight(struct block_list *bl,va_list ap); +int clif_petinsight(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 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_change(struct block_list *bl,int type,int flag); + +int clif_wis_message(int fd,char *nick,char *mes,int mes_len); +int clif_wis_end(int fd,int flag); + +int clif_solved_charname(struct map_session_data *sd,int char_id); + +int clif_use_card(struct map_session_data *sd,int idx); +int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag); + +int clif_itemlist(struct map_session_data *sd); +int clif_equiplist(struct map_session_data *sd); + +int clif_cart_additem(struct map_session_data*,int,int,int); +int clif_cart_delitem(struct map_session_data*,int,int); +int clif_cart_itemlist(struct map_session_data *sd); +int clif_cart_equiplist(struct map_session_data *sd); + +int clif_item_identify_list(struct map_session_data *sd); +int clif_item_identified(struct map_session_data *sd,int idx,int flag); +int clif_item_repair_list(struct map_session_data *sd); + +int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name); + +int clif_mvp_effect(struct map_session_data *sd); +int clif_mvp_item(struct map_session_data *sd,int nameid); +int clif_mvp_exp(struct map_session_data *sd,int exp); + +// 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_info(struct party *p,int fd); +int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd); +int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag); +int clif_party_option(struct party *p,struct map_session_data *sd,int flag); +int clif_party_leaved(struct party *p,struct map_session_data *sd,int account_id,char *name,int flag); +int clif_party_message(struct party *p,int account_id,char *mes,int len); +int clif_party_move(struct party *p,struct map_session_data *sd,int online); +int clif_party_xy(struct party *p,struct map_session_data *sd); +int clif_party_hp(struct party *p,struct map_session_data *sd); + +// guild +int clif_guild_created(struct map_session_data *sd,int flag); +int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g); +int clif_guild_basicinfo(struct map_session_data *sd); +int clif_guild_allianceinfo(struct map_session_data *sd); +int clif_guild_memberlist(struct map_session_data *sd); +int clif_guild_skillinfo(struct map_session_data *sd); +int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag); +int clif_guild_invite(struct map_session_data *sd,struct guild *g); +int clif_guild_inviteack(struct map_session_data *sd,int flag); +int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes); +int clif_guild_explusion(struct map_session_data *sd,const char *name,const char *mes, + int account_id); +int clif_guild_positionchanged(struct guild *g,int idx); +int clif_guild_memberpositionchanged(struct guild *g,int idx); +int clif_guild_emblem(struct map_session_data *sd,struct guild *g); +int clif_guild_notice(struct map_session_data *sd,struct guild *g); +int clif_guild_message(struct guild *g,int account_id,const char *mes,int len); +int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv); +int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name); +int clif_guild_allianceack(struct map_session_data *sd,int flag); +int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag); +int clif_guild_oppositionack(struct map_session_data *sd,int flag); +int clif_guild_broken(struct map_session_data *sd,int flag); + + +// atcommand +int clif_displaymessage(const int fd,char* mes); +int clif_disp_onlyself(struct map_session_data *sd,char *mes,int len); +int clif_GMmessage(struct block_list *bl,char* mes,int len,int flag); +int clif_heal(int fd,int type,int val); +int clif_resurrection(struct block_list *bl,int type); +int clif_set0199(int fd,int type); +int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type); +int clif_send0199(int map,int type); +int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val); + +//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); + +int clif_specialeffect(struct block_list *bl,int type, int flag); // special effects [Valaris] +int clif_message(struct block_list *bl, char* msg); // messages (from mobs/npcs) [Valaris] + +int clif_GM_kickack(struct map_session_data *sd,int id); +int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type); + +int clif_foreachclient(int (*)(struct map_session_data*,va_list),...); + +int do_final_clif(void); +int do_init_clif(void); + +#endif + + diff --git a/src/map/guild.c b/src/map/guild.c new file mode 100644 index 0000000..c32ebb4 --- /dev/null +++ b/src/map/guild.c @@ -0,0 +1,1543 @@ +// $Id: guild.c,v 1.5 2004/09/25 05:32:18 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "guild.h" +#include "storage.h" +#include "db.h" +#include "timer.h" +#include "socket.h" +#include "nullpo.h" +#include "malloc.h" +#include "battle.h" +#include "npc.h" +#include "pc.h" +#include "map.h" +#include "mob.h" +#include "intif.h" +#include "clif.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +static struct dbt *guild_db; +static struct dbt *castle_db; +static struct dbt *guild_expcache_db; +static struct dbt *guild_infoevent_db; +static struct dbt *guild_castleinfoevent_db; + +struct eventlist { + char name[50]; + struct eventlist *next; +}; + +// ギルドのEXPキャッシュのフラッシュに関連する定数 +#define GUILD_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒) +#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数 + +// ギルドのEXPキャッシュ +struct guild_expcache { + int guild_id, account_id, char_id, exp; +}; + +// ギルドスキルdbのアクセサ(今は直打ちで代用) +int guild_skill_get_inf(int id){ return 0; } +int guild_skill_get_sp(int id,int lv){ return 0; } +int guild_skill_get_range(int id){ return 0; } +int guild_skill_get_max(int id){ return (id==10004)?10:1; } + +// ギルドスキルがあるか確認 +int guild_checkskill(struct guild *g,int id){ return g->skill[id-10000].lv; } + + +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); + + +static int guild_read_castledb(void) +{ + FILE *fp; + char line[1024]; + int j,ln=0; + char *str[32],*p; + struct guild_castle *gc; + + if( (fp=fopen("db/castle_db.txt","r"))==NULL){ + printf("can't read db/castle_db.txt\n"); + return -1; + } + + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + memset(str,0,sizeof(str)); + gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle)); + for(j=0,p=line;j<6 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + + gc->guild_id=0; // <Agit> Clear Data for Initialize + gc->economy=0; gc->defense=0; gc->triggerE=0; gc->triggerD=0; gc->nextTime=0; gc->payTime=0; + gc->createTime=0; gc->visibleC=0; gc->visibleG0=0; gc->visibleG1=0; gc->visibleG2=0; + gc->visibleG3=0; gc->visibleG4=0; gc->visibleG5=0; gc->visibleG6=0; gc->visibleG7=0; + gc->Ghp0=0; gc->Ghp1=0; gc->Ghp2=0; gc->Ghp3=0; gc->Ghp4=0; gc->Ghp5=0; gc->Ghp6=0; gc->Ghp7=0; // guardian HP [Valaris] + + gc->castle_id=atoi(str[0]); + memcpy(gc->map_name,str[1],24); + memcpy(gc->castle_name,str[2],24); + memcpy(gc->castle_event,str[3],24); + + numdb_insert(castle_db,gc->castle_id,gc); + + //intif_guild_castle_info(gc->castle_id); + + ln++; + } + fclose(fp); + printf("read db/castle_db.txt done (count=%d)\n",ln); + return 0; +} + +// 初期化 +void do_init_guild(void) +{ + guild_db=numdb_init(); + castle_db=numdb_init(); + guild_expcache_db=numdb_init(); + guild_infoevent_db=numdb_init(); + guild_castleinfoevent_db=numdb_init(); + + guild_read_castledb(); + + add_timer_func_list(guild_gvg_eliminate_timer,"guild_gvg_eliminate_timer"); + add_timer_func_list(guild_payexp_timer,"guild_payexp_timer"); + add_timer_interval(gettick()+GUILD_PAYEXP_INVERVAL,guild_payexp_timer,0,0,GUILD_PAYEXP_INVERVAL); +} + + +// 検索 +struct guild *guild_search(int guild_id) +{ + return numdb_search(guild_db,guild_id); +} +int guild_searchname_sub(void *key,void *data,va_list ap) +{ + struct guild *g=(struct guild *)data,**dst; + char *str; + str=va_arg(ap,char *); + dst=va_arg(ap,struct guild **); + if(strcmpi(g->name,str)==0) + *dst=g; + return 0; +} +// ギルド名検索 +struct guild* guild_searchname(char *str) +{ + struct guild *g=NULL; + numdb_foreach(guild_db,guild_searchname_sub,str,&g); + return g; +} +struct guild_castle *guild_castle_search(int gcid) +{ + return numdb_search(castle_db,gcid); +} + +// mapnameに対応したアジトのgcを返す +struct guild_castle *guild_mapname2gc(char *mapname) +{ + int i; + struct guild_castle *gc=NULL; + for(i=0;i<MAX_GUILDCASTLE;i++){ + gc=guild_castle_search(i); + if(!gc) continue; + if(strcmp(gc->map_name,mapname)==0) return gc; + } + return NULL; +} + +// ログイン中のギルドメンバーの1人のsdを返す +struct map_session_data *guild_getavailablesd(struct guild *g) +{ + int i; + + nullpo_retr(NULL, g); + + for(i=0;i<g->max_member;i++) + if(g->member[i].sd!=NULL) + return g->member[i].sd; + return NULL; +} + +// ギルドメンバーのインデックスを返す +int guild_getindex(struct guild *g,int account_id,int char_id) +{ + int i; + if(g==NULL) + return -1; + for(i=0;i<g->max_member;i++) + if( g->member[i].account_id==account_id && + g->member[i].char_id==char_id ) + return i; + return -1; +} +// ギルドメンバーの役職を返す +int guild_getposition(struct map_session_data *sd,struct guild *g) +{ + int i; + + nullpo_retr(-1, sd); + + if(g==NULL && (g=guild_search(sd->status.guild_id))==NULL) + return -1; + for(i=0;i<g->max_member;i++) + if( g->member[i].account_id==sd->status.account_id && + g->member[i].char_id==sd->status.char_id ) + return g->member[i].position; + return -1; +} + +// メンバー情報の作成 +void guild_makemember(struct guild_member *m,struct map_session_data *sd) +{ + nullpo_retv(sd); + + memset(m,0,sizeof(struct guild_member)); + m->account_id =sd->status.account_id; + m->char_id =sd->status.char_id; + m->hair =sd->status.hair; + m->hair_color =sd->status.hair_color; + m->gender =sd->sex; + m->class =sd->status.class; + m->lv =sd->status.base_level; + m->exp =0; + m->exp_payper =0; + m->online =1; + m->position =MAX_GUILDPOSITION-1; + memcpy(m->name,sd->status.name,24); + return; +} +// ギルド競合確認 +int guild_check_conflict(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + intif_guild_checkconflict(sd->status.guild_id, + sd->status.account_id,sd->status.char_id); + return 0; +} + +// ギルドのEXPキャッシュをinter鯖にフラッシュする +int guild_payexp_timer_sub(void *key,void *data,va_list ap) +{ + int i, *dellist,*delp, dataid=(int)key; + struct guild_expcache *c; + struct guild *g; + + nullpo_retr(0, ap); + nullpo_retr(0, c=(struct guild_expcache *)data); + nullpo_retr(0, dellist=va_arg(ap,int *)); + nullpo_retr(0, delp=va_arg(ap,int *)); + + if( *delp>=GUILD_PAYEXP_LIST || (g=guild_search(c->guild_id))==NULL ) + return 0; + if( ( i=guild_getindex(g,c->account_id,c->char_id) )<0 ) + return 0; + + g->member[i].exp+=c->exp; + 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; + free(c); + return 0; +} +int guild_payexp_timer(int tid,unsigned int tick,int id,int data) +{ + int dellist[GUILD_PAYEXP_LIST],delp=0,i; + numdb_foreach(guild_expcache_db,guild_payexp_timer_sub, + dellist,&delp); + for(i=0;i<delp;i++) + numdb_erase(guild_expcache_db,dellist[i]); +// if(battle_config.etc_log) +// printf("guild exp %d charactor's exp flushed !\n",delp); + return 0; +} + +//------------------------------------------------------------------------ + +// 作成要求 +int guild_create(struct map_session_data *sd,char *name) +{ + nullpo_retr(0, sd); + + if(sd->status.guild_id==0){ + if(!battle_config.guild_emperium_check || pc_search_inventory(sd,714) >= 0) { + struct guild_member m; + guild_makemember(&m,sd); + m.position=0; + intif_guild_create(name,&m); + } else + clif_guild_created(sd,3); // エンペリウムがいない + }else + clif_guild_created(sd,1); // すでに所属している + + return 0; +} + +// 作成可否 +int guild_created(int account_id,int guild_id) +{ + struct map_session_data *sd=map_id2sd(account_id); + + if(sd==NULL) + return 0; + if(guild_id>0) { + struct guild *g; + sd->status.guild_id=guild_id; + sd->guild_sended=0; + if((g=numdb_search(guild_db,guild_id))!=NULL){ + printf("guild: id already exists!\n"); + exit(1); + } + clif_guild_created(sd,0); + if(battle_config.guild_emperium_check) + pc_delitem(sd,pc_search_inventory(sd,714),1,0); // エンペリウム消耗 + } else { + clif_guild_created(sd,2); // 作成失敗(同名ギルド存在) + } + return 0; +} + +// 情報要求 +int guild_request_info(int guild_id) +{ +// if(battle_config.etc_log) +// printf("guild_request_info\n"); + return intif_guild_request_info(guild_id); +} +// イベント付き情報要求 +int guild_npc_request_info(int guild_id,const char *event) +{ + struct eventlist *ev; + + if( guild_search(guild_id) ){ + if(event && *event) + npc_event_do(event); + return 0; + } + + if(event==NULL || *event==0) + return guild_request_info(guild_id); + + ev=(struct eventlist *)aCalloc(1,sizeof(struct eventlist)); + memcpy(ev->name,event,sizeof(ev->name)); + ev->next=(struct eventlist *)numdb_search(guild_infoevent_db,guild_id); + numdb_insert(guild_infoevent_db,guild_id,ev); + return guild_request_info(guild_id); +} + +// 所属キャラの確認 +int guild_check_member(const struct guild *g) +{ + int i; + struct map_session_data *sd; + + nullpo_retr(0, g); + + for(i=0;i<fd_max;i++){ + if(session[i] && (sd=session[i]->session_data) && sd->state.auth){ + if(sd->status.guild_id==g->guild_id){ + int j,f=1; + for(j=0;j<MAX_GUILD;j++){ // データがあるか + if( g->member[j].account_id==sd->status.account_id && + g->member[j].char_id==sd->status.char_id) + f=0; + } + if(f){ + sd->status.guild_id=0; + sd->guild_sended=0; + sd->guild_emblem_id=0; + if(battle_config.error_log) + printf("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name); + } + } + } + } + return 0; +} +// 情報所得失敗(そのIDのキャラを全部未所属にする) +int guild_recv_noinfo(int guild_id) +{ + int i; + struct map_session_data *sd; + for(i=0;i<fd_max;i++){ + if(session[i] && (sd=session[i]->session_data) && sd->state.auth){ + if(sd->status.guild_id==guild_id) + sd->status.guild_id=0; + } + } + return 0; +} +// 情報所得 +int guild_recv_info(struct guild *sg) +{ + struct guild *g,before; + int i,bm,m; + struct eventlist *ev,*ev2; + + nullpo_retr(0, sg); + + if((g=numdb_search(guild_db,sg->guild_id))==NULL){ + g=(struct guild *)aCalloc(1,sizeof(struct guild)); + numdb_insert(guild_db,sg->guild_id,g); + before=*sg; + + // 最初のロードなのでユーザーのチェックを行う + guild_check_member(sg); + }else + before=*g; + memcpy(g,sg,sizeof(struct guild)); + + for(i=bm=m=0;i<g->max_member;i++){ // sdの設定と人数の確認 + if(g->member[i].account_id>0){ + struct map_session_data *sd = map_id2sd(g->member[i].account_id); + g->member[i].sd=(sd!=NULL && + sd->status.char_id==g->member[i].char_id && + sd->status.guild_id==g->guild_id)? sd:NULL; + m++; + }else + g->member[i].sd=NULL; + if(before.member[i].account_id>0) + bm++; + } + + for(i=0;i<g->max_member;i++){ // 情報の送信 + struct map_session_data *sd = g->member[i].sd; + if( sd==NULL ) + continue; + + if( before.guild_lv!=g->guild_lv || bm!=m || + before.max_member!=g->max_member ){ + clif_guild_basicinfo(sd); // 基本情報送信 + clif_guild_emblem(sd,g); // エンブレム送信 + } + + if(bm!=m){ // メンバー情報送信 + clif_guild_memberlist(g->member[i].sd); + } + + if( before.skill_point!=g->skill_point) + clif_guild_skillinfo(sd); // スキル情報送信 + + if( sd->guild_sended==0){ // 未送信なら所属情報も送る + clif_guild_belonginfo(sd,g); + clif_guild_notice(sd,g); + sd->guild_emblem_id=g->emblem_id; + sd->guild_sended=1; + } + } + + // イベントの発生 + if( (ev=numdb_search(guild_infoevent_db,sg->guild_id))!=NULL ){ + numdb_erase(guild_infoevent_db,sg->guild_id); + for(;ev;ev2=ev->next,free(ev),ev=ev2){ + npc_event_do(ev->name); + } + } + + return 0; +} + + +// ギルドへの勧誘 +int guild_invite(struct map_session_data *sd,int account_id) +{ + struct map_session_data *tsd; + struct guild *g; + int i; + + nullpo_retr(0, sd); + + tsd= map_id2sd(account_id); + g=guild_search(sd->status.guild_id); + + if(tsd==NULL || g==NULL) + return 0; + if(!battle_config.invite_request_check) { + if (tsd->party_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか + clif_guild_inviteack(sd,0); + return 0; + } + } + if( tsd->status.guild_id>0 || tsd->guild_invite>0 ){ // 相手の所属確認 + clif_guild_inviteack(sd,0); + return 0; + } + + // 定員確認 + for(i=0;i<g->max_member;i++) + if(g->member[i].account_id==0) + break; + if(i==g->max_member){ + clif_guild_inviteack(sd,3); + return 0; + } + + tsd->guild_invite=sd->status.guild_id; + tsd->guild_invite_account=sd->status.account_id; + + clif_guild_invite(tsd,g); + return 0; +} +// ギルド勧誘への返答 +int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag) +{ + struct map_session_data *tsd; + + nullpo_retr(0, sd); + nullpo_retr(0, tsd= map_id2sd( sd->guild_invite_account )); + + if(sd->guild_invite!=guild_id) // 勧誘とギルドIDが違う + return 0; + + if(flag==1){ // 承諾 + struct guild_member m; + struct guild *g; + int i; + + // 定員確認 + if( (g=guild_search(tsd->status.guild_id))==NULL ){ + sd->guild_invite=0; + sd->guild_invite_account=0; + return 0; + } + for(i=0;i<g->max_member;i++) + if(g->member[i].account_id==0) + break; + if(i==g->max_member){ + sd->guild_invite=0; + sd->guild_invite_account=0; + clif_guild_inviteack(tsd,3); + return 0; + } + + + //inter鯖へ追加要求 + guild_makemember(&m,sd); + intif_guild_addmember( sd->guild_invite, &m ); + return 0; + }else{ // 拒否 + sd->guild_invite=0; + sd->guild_invite_account=0; + if(tsd==NULL) + return 0; + clif_guild_inviteack(tsd,1); + } + return 0; +} +// ギルドメンバが追加された +int guild_member_added(int guild_id,int account_id,int char_id,int flag) +{ + struct map_session_data *sd= map_id2sd(account_id),*sd2; + struct guild *g; + + if( (g=guild_search(guild_id))==NULL ) + return 0; + + if((sd==NULL || sd->guild_invite==0) && flag==0){ + // キャラ側に登録できなかったため脱退要求を出す + if(battle_config.error_log) + printf("guild: member added error %d is not online\n",account_id); + intif_guild_leave(guild_id,account_id,char_id,0,"**登録失敗**"); + return 0; + } + sd->guild_invite=0; + sd->guild_invite_account=0; + + sd2=map_id2sd(sd->guild_invite_account); + + if(flag==1){ // 失敗 + if( sd2!=NULL ) + clif_guild_inviteack(sd2,3); + return 0; + } + + // 成功 + sd->guild_sended=0; + sd->status.guild_id=guild_id; + + if( sd2!=NULL ) + clif_guild_inviteack(sd2,2); + + // いちおう競合確認 + guild_check_conflict(sd); + + return 0; +} + +// ギルド脱退要求 +int guild_leave(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes) +{ + struct guild *g; + int i; + + nullpo_retr(0, sd); + + g = guild_search(sd->status.guild_id); + + if(g==NULL) + return 0; + + if( sd->status.account_id!=account_id || + sd->status.char_id!=char_id || sd->status.guild_id!=guild_id) + return 0; + + for(i=0;i<g->max_member;i++){ // 所属しているか + if( g->member[i].account_id==sd->status.account_id && + g->member[i].char_id==sd->status.char_id ){ + intif_guild_leave(g->guild_id,sd->status.account_id,sd->status.char_id,0,mes); + return 0; + } + } + return 0; +} +// ギルド追放要求 +int guild_explusion(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes) +{ + struct guild *g; + int i,ps; + + nullpo_retr(0, sd); + + g = guild_search(sd->status.guild_id); + + if(g==NULL) + return 0; + + if( sd->status.guild_id!=guild_id) + return 0; + + if( (ps=guild_getposition(sd,g))<0 || !(g->position[ps].mode&0x0010) ) + return 0; // 処罰権限無し + + for(i=0;i<g->max_member;i++){ // 所属しているか + if( g->member[i].account_id==account_id && + g->member[i].char_id==char_id ){ + intif_guild_leave(g->guild_id,account_id,char_id,1,mes); + return 0; + } + } + return 0; +} +// ギルドメンバが脱退した +int guild_member_leaved(int guild_id,int account_id,int char_id,int flag, + const char *name,const char *mes) +{ + struct map_session_data *sd=map_id2sd(account_id); + struct guild *g=guild_search(guild_id); + int i; + + if(g!=NULL){ + int i; + for(i=0;i<g->max_member;i++) + if( g->member[i].account_id==account_id && + 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); + } + g->member[i].account_id=0; + g->member[i].sd=NULL; + } + } + if(sd!=NULL && sd->status.guild_id==guild_id){ + sd->status.guild_id=0; + sd->guild_emblem_id=0; + sd->guild_sended=0; + } + + // メンバーリストを全員に再通知 + for(i=0;i<g->max_member;i++){ + if( g->member[i].sd!=NULL ) + clif_guild_memberlist(g->member[i].sd); + } + + return 0; +} +// ギルドメンバのオンライン状態/Lv更新送信 +int guild_send_memberinfoshort(struct map_session_data *sd,int online) +{ + struct guild *g; + + nullpo_retr(0, sd); + + if(sd->status.guild_id<=0) + return 0; + g=guild_search(sd->status.guild_id); + if(g==NULL) + return 0; + + intif_guild_memberinfoshort(g->guild_id, + sd->status.account_id,sd->status.char_id,online,sd->status.base_level,sd->status.class); + + if( !online ){ // ログアウトするならsdをクリアして終了 + int i=guild_getindex(g,sd->status.account_id,sd->status.char_id); + if(i>=0) + g->member[i].sd=NULL; + return 0; + } + + if( sd->guild_sended!=0 ) // ギルド初期送信データは送信済み + return 0; + + // 競合確認 + guild_check_conflict(sd); + + // あるならギルド初期送信データ送信 + if( (g=guild_search(sd->status.guild_id))!=NULL ){ + guild_check_member(g); // 所属を確認する + if(sd->status.guild_id==g->guild_id){ + clif_guild_belonginfo(sd,g); + clif_guild_notice(sd,g); + sd->guild_sended=1; + sd->guild_emblem_id=g->emblem_id; + } + } + return 0; +} +// ギルドメンバのオンライン状態/Lv更新通知 +int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class) +{ + int i,alv,c,idx=0,om=0,oldonline=-1; + struct guild *g=guild_search(guild_id); + if(g==NULL) + return 0; + for(i=0,alv=0,c=0,om=0;i<g->max_member;i++){ + struct guild_member *m=&g->member[i]; + if(m->account_id==account_id && 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==g->max_member){ + if(battle_config.error_log) + printf("guild: not found member %d,%d on %d[%s]\n", account_id,char_id,guild_id,g->name); + return 0; + } + g->average_lv=alv/c; + g->connect_member=om; + + if(oldonline!=online) // オンライン状態が変わったので通知 + clif_guild_memberlogin_notice(g,idx,online); + + for(i=0;i<g->max_member;i++){ // sd再設定 + struct map_session_data *sd= map_id2sd(g->member[i].account_id); + g->member[i].sd=(sd!=NULL && + sd->status.char_id==g->member[i].char_id && + sd->status.guild_id==guild_id)?sd:NULL; + } + + // ここにクライアントに送信処理が必要 + + return 0; +} +// ギルド会話送信 +int guild_send_message(struct map_session_data *sd,char *mes,int len) +{ + nullpo_retr(0, sd); + + if(sd->status.guild_id==0) + return 0; + intif_guild_message(sd->status.guild_id,sd->status.account_id,mes,len); + return 0; +} +// ギルド会話受信 +int guild_recv_message(int guild_id,int account_id,char *mes,int len) +{ + struct guild *g; + if( (g=guild_search(guild_id))==NULL) + return 0; + clif_guild_message(g,account_id,mes,len); + return 0; +} +// ギルドメンバの役職変更 +int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx) +{ + return intif_guild_change_memberinfo( + guild_id,account_id,char_id,GMI_POSITION,&idx,sizeof(idx)); +} +// ギルドメンバの役職変更通知 +int guild_memberposition_changed(struct guild *g,int idx,int pos) +{ + nullpo_retr(0, g); + + g->member[idx].position=pos; + clif_guild_memberpositionchanged(g,idx); + return 0; +} +// ギルド役職変更 +int guild_change_position(struct map_session_data *sd,int idx, + int mode,int exp_mode,const char *name) +{ + struct guild_position p; + + nullpo_retr(0, sd); + + if(exp_mode>battle_config.guild_exp_limit) + exp_mode=battle_config.guild_exp_limit; + if(exp_mode<0)exp_mode=0; + p.mode=mode; + p.exp_mode=exp_mode; + memcpy(p.name,name,24); + return intif_guild_position(sd->status.guild_id,idx,&p); +} +// ギルド役職変更通知 +int guild_position_changed(int guild_id,int idx,struct guild_position *p) +{ + struct guild *g=guild_search(guild_id); + if(g==NULL) + return 0; + memcpy(&g->position[idx],p,sizeof(struct guild_position)); + clif_guild_positionchanged(g,idx); + return 0; +} +// ギルド告知変更 +int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2) +{ + nullpo_retr(0, sd); + + if(guild_id!=sd->status.guild_id) + return 0; + return intif_guild_notice(guild_id,mes1,mes2); +} +// ギルド告知変更通知 +int guild_notice_changed(int guild_id,const char *mes1,const char *mes2) +{ + int i; + struct map_session_data *sd; + struct guild *g=guild_search(guild_id); + if(g==NULL) + return 0; + + memcpy(g->mes1,mes1,60); + memcpy(g->mes2,mes2,120); + + for(i=0;i<g->max_member;i++){ + if((sd=g->member[i].sd)!=NULL) + clif_guild_notice(sd,g); + } + return 0; +} +// ギルドエンブレム変更 +int guild_change_emblem(struct map_session_data *sd,int len,const char *data) +{ + nullpo_retr(0, sd); + + return intif_guild_emblem(sd->status.guild_id,len,data); +} +// ギルドエンブレム変更通知 +int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data) +{ + int i; + struct map_session_data *sd; + struct guild *g=guild_search(guild_id); + if(g==NULL) + return 0; + + memcpy(g->emblem_data,data,len); + g->emblem_len=len; + g->emblem_id=emblem_id; + + for(i=0;i<g->max_member;i++){ + if((sd=g->member[i].sd)!=NULL){ + sd->guild_emblem_id=emblem_id; + clif_guild_belonginfo(sd,g); + clif_guild_emblem(sd,g); + } + } + return 0; +} + +// ギルドのEXP上納 +int guild_payexp(struct map_session_data *sd,int exp) +{ + struct guild *g; + struct guild_expcache *c; + int per,exp2; + + nullpo_retr(0, sd); + + if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL ) + return 0; + if( (per=g->position[guild_getposition(sd,g)].exp_mode)<=0 ) + return 0; + if( per>100 )per=100; + + if( (exp2=exp*per/100)<=0 ) + return 0; + + if( (c=numdb_search(guild_expcache_db,sd->status.char_id))==NULL ){ + c=(struct guild_expcache *)aCalloc(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; + c->exp=exp2; + numdb_insert(guild_expcache_db,c->char_id,c); + }else{ + c->exp+=exp2; + } + return exp2; +} + +// スキルポイント割り振り +int guild_skillup(struct map_session_data *sd,int skill_num) +{ + struct guild *g; + int idx; + + nullpo_retr(0, sd); + + if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL) + return 0; + if(strcmp(sd->status.name,g->master)) + return 0; + + if( g->skill_point>0 && + g->skill[(idx=skill_num-10000)].id!=0 && + g->skill[idx].lv < guild_skill_get_max(skill_num) ){ + intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id); + } + return 0; +} +// スキルポイント割り振り通知 +int guild_skillupack(int guild_id,int skill_num,int account_id) +{ + struct map_session_data *sd=map_id2sd(account_id); + struct guild *g=guild_search(guild_id); + int i; + if(g==NULL) + return 0; + if(sd!=NULL) + clif_guild_skillup(sd,skill_num,g->skill[skill_num-10000].lv); + // 全員に通知 + for(i=0;i<g->max_member;i++) + if((sd=g->member[i].sd)!=NULL) + clif_guild_skillinfo(sd); + return 0; +} + +// ギルド同盟数所得 +int guild_get_alliance_count(struct guild *g,int flag) +{ + int i,c; + + nullpo_retr(0, g); + + for(i=c=0;i<MAX_GUILDALLIANCE;i++){ + if( g->alliance[i].guild_id>0 && + g->alliance[i].opposition==flag ) + c++; + } + return c; +} +// ギルド同盟要求 +int guild_reqalliance(struct map_session_data *sd,int account_id) +{ + struct map_session_data *tsd= map_id2sd(account_id); + struct guild *g[2]; + int i; + + if(agit_flag) { // Disable alliance creation during woe [Valaris] + clif_displaymessage(sd->fd,"Alliances cannot be made during Guild Wars!"); + return 0; + } // end addition [Valaris] + + + nullpo_retr(0, sd); + + if(tsd==NULL || tsd->status.guild_id<=0) + return 0; + + g[0]=guild_search(sd->status.guild_id); + g[1]=guild_search(tsd->status.guild_id); + + if(g[0]==NULL || g[1]==NULL) + return 0; + + if( guild_get_alliance_count(g[0],0)>3 ) // 同盟数確認 + clif_guild_allianceack(sd,4); + if( guild_get_alliance_count(g[1],0)>3 ) + clif_guild_allianceack(sd,3); + + if( tsd->guild_alliance>0 ){ // 相手が同盟要請状態かどうか確認 + clif_guild_allianceack(sd,1); + return 0; + } + + for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに同盟状態か確認 + if( g[0]->alliance[i].guild_id==tsd->status.guild_id && + g[0]->alliance[i].opposition==0){ + clif_guild_allianceack(sd,0); + return 0; + } + } + + tsd->guild_alliance=sd->status.guild_id; + tsd->guild_alliance_account=sd->status.account_id; + + clif_guild_reqalliance(tsd,sd->status.account_id,g[0]->name); + return 0; +} +// ギルド勧誘への返答 +int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag) +{ + struct map_session_data *tsd; + + nullpo_retr(0, sd); + nullpo_retr(0, tsd= map_id2sd( account_id )); + + if(sd->guild_alliance!=tsd->status.guild_id) // 勧誘とギルドIDが違う + return 0; + + if(flag==1){ // 承諾 + int i; + + struct guild *g; // 同盟数再確認 + if( (g=guild_search(sd->status.guild_id))==NULL || + guild_get_alliance_count(g,0)>3 ){ + clif_guild_allianceack(sd,4); + clif_guild_allianceack(tsd,3); + return 0; + } + if( (g=guild_search(tsd->status.guild_id))==NULL || + guild_get_alliance_count(g,0)>3 ){ + clif_guild_allianceack(sd,3); + clif_guild_allianceack(tsd,4); + return 0; + } + + // 敵対関係なら敵対を止める + if((g=guild_search(sd->status.guild_id)) == NULL) + return 0; + for(i=0;i<MAX_GUILDALLIANCE;i++){ + if( g->alliance[i].guild_id==tsd->status.guild_id && + g->alliance[i].opposition==1) + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,9 ); + } + if((g=guild_search(tsd->status.guild_id)) == NULL) + return 0; + for(i=0;i<MAX_GUILDALLIANCE;i++){ + if( g->alliance[i].guild_id==sd->status.guild_id && + g->alliance[i].opposition==1) + intif_guild_alliance( tsd->status.guild_id,sd->status.guild_id, + tsd->status.account_id,sd->status.account_id,9 ); + } + + // inter鯖へ同盟要請 + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,0 ); + return 0; + }else{ // 拒否 + sd->guild_alliance=0; + sd->guild_alliance_account=0; + if(tsd!=NULL) + clif_guild_allianceack(tsd,3); + } + return 0; +} +// ギルド関係解消 +int guild_delalliance(struct map_session_data *sd,int guild_id,int flag) +{ + if(agit_flag) { // Disable alliance breaking during woe [Valaris] + clif_displaymessage(sd->fd,"Alliances cannot be broken during Guild Wars!"); + return 0; + } // end addition [Valaris] + + nullpo_retr(0, sd); + + intif_guild_alliance( sd->status.guild_id,guild_id, + sd->status.account_id,0,flag|8 ); + return 0; +} +// ギルド敵対 +int guild_opposition(struct map_session_data *sd,int char_id) +{ + struct map_session_data *tsd=map_id2sd(char_id); + struct guild *g; + int i; + + nullpo_retr(0, sd); + + g=guild_search(sd->status.guild_id); + if(g==NULL || tsd==NULL) + return 0; + + if( guild_get_alliance_count(g,1)>3 ) // 敵対数確認 + clif_guild_oppositionack(sd,1); + + for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに関係を持っているか確認 + if(g->alliance[i].guild_id==tsd->status.guild_id){ + if(g->alliance[i].opposition==1){ // すでに敵対 + clif_guild_oppositionack(sd,2); + return 0; + }else // 同盟破棄 + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,8 ); + } + } + + // inter鯖に敵対要請 + intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id, + sd->status.account_id,tsd->status.account_id,1 ); + return 0; +} +// ギルド同盟/敵対通知 +int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2, + int flag,const char *name1,const char *name2) +{ + struct guild *g[2]; + int guild_id[2]={guild_id1,guild_id2}; + const char *guild_name[2]={name1,name2}; + struct map_session_data *sd[2]={map_id2sd(account_id1),map_id2sd(account_id2)}; + int j,i; + + g[0]=guild_search(guild_id1); + g[1]=guild_search(guild_id2); + + if(sd[0]!=NULL && (flag&0x0f)==0){ + sd[0]->guild_alliance=0; + sd[0]->guild_alliance_account=0; + } + + if(flag&0x70){ // 失敗 + for(i=0;i<2-(flag&1);i++) + if( sd[i]!=NULL ) + clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4); + return 0; + } +// if(battle_config.etc_log) +// printf("guild alliance_ack %d %d %d %d %d %s %s\n",guild_id1,guild_id2,account_id1,account_id2,flag,name1,name2); + + if(!(flag&0x08)){ // 関係追加 + for(i=0;i<2-(flag&1);i++) + if(g[i]!=NULL) + for(j=0;j<MAX_GUILDALLIANCE;j++) + if(g[i]->alliance[j].guild_id==0){ + g[i]->alliance[j].guild_id=guild_id[1-i]; + memcpy(g[i]->alliance[j].name,guild_name[1-i],24); + g[i]->alliance[j].opposition=flag&1; + break; + } + }else{ // 関係解消 + for(i=0;i<2-(flag&1);i++){ + if(g[i]!=NULL) + for(j=0;j<MAX_GUILDALLIANCE;j++) + if( g[i]->alliance[j].guild_id==guild_id[1-i] && + g[i]->alliance[j].opposition==(flag&1)){ + g[i]->alliance[j].guild_id=0; + break; + } + if( sd[i]!=NULL ) // 解消通知 + clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1)); + } + } + + if((flag&0x0f)==0){ // 同盟通知 + if( sd[1]!=NULL ) + clif_guild_allianceack(sd[1],2); + }else if((flag&0x0f)==1){ // 敵対通知 + if( sd[0]!=NULL ) + clif_guild_oppositionack(sd[0],0); + } + + + for(i=0;i<2-(flag&1);i++){ // 同盟/敵対リストの再送信 + struct map_session_data *sd; + if(g[i]!=NULL) + for(j=0;j<g[i]->max_member;j++) + if((sd=g[i]->member[j].sd)!=NULL) + clif_guild_allianceinfo(sd); + } + return 0; +} +// ギルド解散通知用 +int guild_broken_sub(void *key,void *data,va_list ap) +{ + struct guild *g=(struct guild *)data; + int guild_id=va_arg(ap,int); + int i,j; + struct map_session_data *sd=NULL; + + nullpo_retr(0, g); + + for(i=0;i<MAX_GUILDALLIANCE;i++){ // 関係を破棄 + if(g->alliance[i].guild_id==guild_id){ + for(j=0;j<g->max_member;j++) + if( (sd=g->member[j].sd)!=NULL ) + clif_guild_delalliance(sd,guild_id,g->alliance[i].opposition); + g->alliance[i].guild_id=0; + } + } + return 0; +} +// ギルド解散通知 +int guild_broken(int guild_id,int flag) +{ + struct guild *g=guild_search(guild_id); + struct map_session_data *sd; + int i; + if(flag!=0 || g==NULL) + return 0; + + for(i=0;i<g->max_member;i++){ // ギルド解散を通知 + if((sd=g->member[i].sd)!=NULL){ + if(sd->state.storage_flag) + storage_guild_storage_quit(sd,1); + sd->status.guild_id=0; + sd->guild_sended=0; + clif_guild_broken(g->member[i].sd,0); + } + } + + numdb_foreach(guild_db,guild_broken_sub,guild_id); + numdb_erase(guild_db,guild_id); + guild_storage_delete(guild_id); + free(g); + return 0; +} + +// ギルド解散 +int guild_break(struct map_session_data *sd,char *name) +{ + struct guild *g; + int i; + + nullpo_retr(0, sd); + + if( (g=guild_search(sd->status.guild_id))==NULL ) + return 0; + if(strcmp(g->name,name)!=0) + return 0; + if(strcmp(sd->status.name,g->master)!=0) + return 0; + for(i=0;i<g->max_member;i++){ + if( g->member[i].account_id>0 && ( + g->member[i].account_id!=sd->status.account_id || + g->member[i].char_id!=sd->status.char_id )) + break; + } + if(i<g->max_member){ + clif_guild_broken(sd,2); + return 0; + } + + intif_guild_break(g->guild_id); + return 0; +} + +// ギルド城データ要求 +int guild_castledataload(int castle_id,int index) +{ + return intif_guild_castle_dataload(castle_id,index); +} +// ギルド城情報所得時イベント追加 +int guild_addcastleinfoevent(int castle_id,int index,const char *name) +{ + struct eventlist *ev; + int code=castle_id|(index<<16); + + if( name==NULL || *name==0 ) + return 0; + + ev=(struct eventlist *)aCalloc(1,sizeof(struct eventlist)); + memcpy(ev->name,name,sizeof(ev->name)); + ev->next=numdb_search(guild_castleinfoevent_db,code); + numdb_insert(guild_castleinfoevent_db,code,ev); + return 0; +} + +// ギルド城データ要求返信 +int guild_castledataloadack(int castle_id,int index,int value) +{ + struct guild_castle *gc=guild_castle_search(castle_id); + int code=castle_id|(index<<16); + struct eventlist *ev,*ev2; + + if(gc==NULL){ + return 0; + } + switch(index){ + case 1: gc->guild_id = value; break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: gc->visibleG0 = value; break; + case 11: gc->visibleG1 = value; break; + case 12: gc->visibleG2 = value; break; + case 13: gc->visibleG3 = value; break; + case 14: gc->visibleG4 = value; break; + case 15: gc->visibleG5 = value; break; + case 16: gc->visibleG6 = value; break; + case 17: gc->visibleG7 = value; break; + case 18: gc->Ghp0 = value; break; // guardian HP [Valaris] + case 19: gc->Ghp1 = value; break; + case 20: gc->Ghp2 = value; break; + case 21: gc->Ghp3 = value; break; + case 22: gc->Ghp4 = value; break; + case 23: gc->Ghp5 = value; break; + case 24: gc->Ghp6 = value; break; + case 25: gc->Ghp7 = value; break; // end additions [Valaris] + default: + printf("guild_castledataloadack ERROR!! (Not found index=%d)\n", index); + return 0; + } + if( (ev=numdb_search(guild_castleinfoevent_db,code))!=NULL ){ + numdb_erase(guild_castleinfoevent_db,code); + for(;ev;ev2=ev->next,free(ev),ev=ev2){ + npc_event_do(ev->name); + } + } + return 1; +} +// ギルド城データ変更要求 +int guild_castledatasave(int castle_id,int index,int value) +{ + return intif_guild_castle_datasave(castle_id,index,value); +} + +// ギルド城データ変更通知 +int guild_castledatasaveack(int castle_id,int index,int value) +{ + struct guild_castle *gc=guild_castle_search(castle_id); + if(gc==NULL){ + return 0; + } + switch(index){ + case 1: gc->guild_id = value; break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: gc->visibleG0 = value; break; + case 11: gc->visibleG1 = value; break; + case 12: gc->visibleG2 = value; break; + case 13: gc->visibleG3 = value; break; + case 14: gc->visibleG4 = value; break; + case 15: gc->visibleG5 = value; break; + case 16: gc->visibleG6 = value; break; + case 17: gc->visibleG7 = value; break; + case 18: gc->Ghp0 = value; break; // guardian HP [Valaris] + case 19: gc->Ghp1 = value; break; + case 20: gc->Ghp2 = value; break; + case 21: gc->Ghp3 = value; break; + case 22: gc->Ghp4 = value; break; + case 23: gc->Ghp5 = value; break; + case 24: gc->Ghp6 = value; break; + case 25: gc->Ghp7 = value; break; // end additions [Valaris] + default: + printf("guild_castledatasaveack ERROR!! (Not found index=%d)\n", index); + return 0; + } + return 1; +} + +// ギルドデータ一括受信(初期化時) +int guild_castlealldataload(int len,struct guild_castle *gc) +{ + int i; + int n = (len-4) / sizeof(struct guild_castle), ev = -1; + + nullpo_retr(0, gc); + + // イベント付きで要求するデータ位置を探す(最後の占拠データ) + for(i = 0; i < n; i++) { + if ((gc + i)->guild_id) + ev = i; + } + + // 城データ格納とギルド情報要求 + for(i = 0; i < n; i++, gc++) { + struct guild_castle *c = guild_castle_search(gc->castle_id); + if (!c) { + printf("guild_castlealldataload ??\n"); + continue; + } + memcpy(&c->guild_id,&gc->guild_id, + sizeof(struct guild_castle) - ((int)&c->guild_id - (int)c) ); + if( c->guild_id ){ + if(i!=ev) + guild_request_info(c->guild_id); + else + guild_npc_request_info(c->guild_id, "::OnAgitInit"); + } + } + if (ev == -1) + npc_event_doall("OnAgitInit"); + return 0; +} + +int guild_agit_start(void) +{ // Run All NPC_Event[OnAgitStart] + int c = npc_event_doall("OnAgitStart"); + printf("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n",c); + return 0; +} + +int guild_agit_end(void) +{ // Run All NPC_Event[OnAgitEnd] + int c = npc_event_doall("OnAgitEnd"); + printf("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n",c); + return 0; +} + +int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data) +{ // Run One NPC_Event[OnAgitEliminate] + size_t len = strlen((const char*)data); + char *evname=(char*)aCalloc(len + 4,sizeof(char)); + int c=0; + + if(!agit_flag) return 0; // Agit already End + memcpy(evname,(const char *)data,len - 5); + strcpy(evname + len - 5,"Eliminate"); + c = npc_event_do(evname); + printf("NPC_Event:[%s] Run (%d) Events.\n",evname,c); + return 0; +} + +int guild_agit_break(struct mob_data *md) +{ // Run One NPC_Event[OnAgitBreak] + char *evname; + + nullpo_retr(0, md); + + evname=(char *)aCalloc(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; +} + +static int guild_db_final(void *key,void *data,va_list ap) +{ + struct guild *g=data; + + free(g); + + return 0; +} +static int castle_db_final(void *key,void *data,va_list ap) +{ + struct guild_castle *gc=data; + + free(gc); + + return 0; +} +static int guild_expcache_db_final(void *key,void *data,va_list ap) +{ + struct guild_expcache *c=data; + + free(c); + + return 0; +} +static int guild_infoevent_db_final(void *key,void *data,va_list ap) +{ + struct eventlist *ev=data; + + free(ev); + + return 0; +} +void do_final_guild(void) +{ + if(guild_db) + numdb_final(guild_db,guild_db_final); + if(castle_db) + numdb_final(castle_db,castle_db_final); + if(guild_expcache_db) + numdb_final(guild_expcache_db,guild_expcache_db_final); + if(guild_infoevent_db) + numdb_final(guild_infoevent_db,guild_infoevent_db_final); + if(guild_castleinfoevent_db) + numdb_final(guild_castleinfoevent_db,guild_infoevent_db_final); +} diff --git a/src/map/guild.h b/src/map/guild.h new file mode 100644 index 0000000..53e91f0 --- /dev/null +++ b/src/map/guild.h @@ -0,0 +1,87 @@ +// $Id: guild.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _GUILD_H_ +#define _GUILD_H_ + +struct map_session_data; +struct mob_data; +struct guild; +struct guild_member; +struct guild_position; +struct guild_castle; + +int guild_skill_get_inf(int id); +int guild_skill_get_sp(int id,int lv); +int guild_skill_get_range(int id); +int guild_skill_get_max(int id); + +int guild_checkskill(struct guild *g,int id); +int guild_checkcastles(struct guild *g); // [MouseJstr] +int guild_isallied(struct guild *g, struct guild_castle *gc); + +void do_init_guild(void); +struct guild *guild_search(int guild_id); +struct guild *guild_searchname(char *str); +struct guild_castle *guild_castle_search(int gcid); + +struct guild_castle *guild_mapname2gc(char *mapname); + +struct map_session_data *guild_getavailablesd(struct guild *g); +int guild_getindex(struct guild *g,int account_id,int char_id); +int guild_getposition(struct map_session_data *sd,struct guild *g); +int guild_payexp(struct map_session_data *sd,int exp); + +int guild_create(struct map_session_data *sd,char *name); +int guild_created(int account_id,int guild_id); +int guild_request_info(int guild_id); +int guild_recv_noinfo(int guild_id); +int guild_recv_info(struct guild *sg); +int guild_npc_request_info(int guild_id,const char *ev); +int guild_invite(struct map_session_data *sd,int account_id); +int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag); +int guild_member_added(int guild_id,int account_id,int char_id,int flag); +int guild_leave(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes); +int guild_member_leaved(int guild_id,int account_id,int char_id,int flag, + const char *name,const char *mes); +int guild_explusion(struct map_session_data *sd,int guild_id, + int account_id,int char_id,const char *mes); +int guild_skillup(struct map_session_data *sd,int skill_num); +int guild_reqalliance(struct map_session_data *sd,int account_id); +int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag); +int guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2); +int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2, + int flag,const char *name1,const char *name2); +int guild_delalliance(struct map_session_data *sd,int guild_id,int flag); +int guild_opposition(struct map_session_data *sd,int char_id); + +int guild_send_memberinfoshort(struct map_session_data *sd,int online); +int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class); +int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx); +int guild_memberposition_changed(struct guild *g,int idx,int pos); +int guild_change_position(struct map_session_data *sd,int idx, + int mode,int exp_mode,const char *name); +int guild_position_changed(int guild_id,int idx,struct guild_position *p); +int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2); +int guild_notice_changed(int guild_id,const char *mes1,const char *mes2); +int guild_change_emblem(struct map_session_data *sd,int len,const char *data); +int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data); +int guild_send_message(struct map_session_data *sd,char *mes,int len); +int guild_recv_message(int guild_id,int account_id,char *mes,int len); +int guild_skillupack(int guild_id,int skill_num,int account_id); +int guild_break(struct map_session_data *sd,char *name); +int guild_broken(int guild_id,int flag); + +int guild_addcastleinfoevent(int castle_id,int index,const char *name); +int guild_castledataload(int castle_id,int index); +int guild_castledataloadack(int castle_id,int index,int value); +int guild_castledatasave(int castle_id,int index,int value); +int guild_castledatasaveack(int castle_id,int index,int value); +int guild_castlealldataload(int len,struct guild_castle *gc); + +int guild_agit_start(void); +int guild_agit_end(void); +int guild_agit_break(struct mob_data *md); + +void do_final_guild(void); + +#endif diff --git a/src/map/intif.c b/src/map/intif.c new file mode 100644 index 0000000..ace0187 --- /dev/null +++ b/src/map/intif.c @@ -0,0 +1,1042 @@ +// $Id: intif.c,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $ +#include <sys/types.h> +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#ifndef LCCWIN32 +#include <sys/time.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <arpa/inet.h> +#endif +#include <signal.h> +#include <fcntl.h> +#include <string.h> + +#include "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" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +static const int packet_len_table[]={ + -1,-1,27,-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -1, 7, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, + 35,-1,11,15, 34,29, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, + 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, + 9, 9,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +extern int char_fd; // inter serverのfdはchar_fdを使う +#define inter_fd (char_fd) // エイリアス + +//----------------------------------------------------------------- +// inter serverへの送信 + +// 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) +{ + 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,24); + WFIFOSET(inter_fd,48); + + return 0; +} + +int intif_request_petdata(int account_id,int char_id,int pet_id) +{ + 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) +{ + 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) +{ + WFIFOW(inter_fd,0) = 0x3083; + WFIFOL(inter_fd,2) = pet_id; + WFIFOSET(inter_fd,6); + + return 0; +} + +// GMメッセージを送信 +int intif_GMmessage(char* mes,int len,int flag) +{ + int lp = (flag&0x10) ? 8 : 4; + WFIFOW(inter_fd,0) = 0x3000; + WFIFOW(inter_fd,2) = lp + len; + WFIFOL(inter_fd,4) = 0x65756c62; + memcpy(WFIFOP(inter_fd,lp), mes, len); + WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); + + return 0; +} + +// The transmission of Wisp/Page to inter-server (player not found on this server) +int intif_wis_message(struct map_session_data *sd, char *nick, char *mes, int mes_len) { + nullpo_retr(0, sd); + + WFIFOW(inter_fd,0) = 0x3001; + WFIFOW(inter_fd,2) = mes_len + 52; + memcpy(WFIFOP(inter_fd,4), sd->status.name, 24); + memcpy(WFIFOP(inter_fd,28), nick, 24); + memcpy(WFIFOP(inter_fd,52), mes, mes_len); + WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); + + if (battle_config.etc_log) + printf("intif_wis_message from %s to %s (message: '%s')\n", sd->status.name, nick, mes); + + return 0; +} + +// The reply of Wisp/page +int intif_wis_replay(int id, int flag) { + WFIFOW(inter_fd,0) = 0x3002; + WFIFOL(inter_fd,2) = id; + WFIFOB(inter_fd,6) = flag; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + WFIFOSET(inter_fd,7); + + if (battle_config.etc_log) + printf("intif_wis_replay: id: %d, flag:%d\n", id, flag); + + return 0; +} + +// The transmission of GM only Wisp/Page from server to inter-server +int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes, int mes_len) { + WFIFOW(inter_fd,0) = 0x3003; + WFIFOW(inter_fd,2) = mes_len + 30; + memcpy(WFIFOP(inter_fd,4), Wisp_name, 24); + WFIFOW(inter_fd,28) = (short)min_gm_level; + memcpy(WFIFOP(inter_fd,30), mes, mes_len); + WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); + + if (battle_config.etc_log) + printf("intif_wis_message_to_gm: from: '%s', min level: %d, message: '%s'.\n", Wisp_name, min_gm_level, mes); + + return 0; +} + +// アカウント変数送信 +int intif_saveaccountreg(struct map_session_data *sd) { + int j,p; + + nullpo_retr(0, sd); + + WFIFOW(inter_fd,0) = 0x3004; + WFIFOL(inter_fd,4) = sd->bl.id; + for(j=0,p=8;j<sd->status.account_reg_num;j++,p+=36){ + memcpy(WFIFOP(inter_fd,p),sd->status.account_reg[j].str,32); + WFIFOL(inter_fd,p+32)=sd->status.account_reg[j].value; + } + WFIFOW(inter_fd,2)=p; + WFIFOSET(inter_fd,p); + return 0; +} +// アカウント変数要求 +int intif_request_accountreg(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + WFIFOW(inter_fd,0) = 0x3005; + WFIFOL(inter_fd,2) = sd->bl.id; + WFIFOSET(inter_fd,6); + return 0; +} + +// 倉庫データ要求 +int intif_request_storage(int account_id) +{ + WFIFOW(inter_fd,0) = 0x3010; + WFIFOL(inter_fd,2) = account_id; + WFIFOSET(inter_fd,6); + return 0; +} +// 倉庫データ送信 +int intif_send_storage(struct storage *stor) +{ + nullpo_retr(0, stor); + WFIFOW(inter_fd,0) = 0x3011; + WFIFOW(inter_fd,2) = sizeof(struct storage)+8; + WFIFOL(inter_fd,4) = stor->account_id; + memcpy( WFIFOP(inter_fd,8),stor, sizeof(struct storage) ); + WFIFOSET(inter_fd,WFIFOW(inter_fd,2)); + return 0; +} + +int intif_request_guild_storage(int account_id,int guild_id) +{ + WFIFOW(inter_fd,0) = 0x3018; + WFIFOL(inter_fd,2) = account_id; + WFIFOL(inter_fd,6) = guild_id; + WFIFOSET(inter_fd,10); + return 0; +} +int intif_send_guild_storage(int account_id,struct guild_storage *gstor) +{ + WFIFOW(inter_fd,0) = 0x3019; + WFIFOW(inter_fd,2) = sizeof(struct guild_storage)+12; + WFIFOL(inter_fd,4) = account_id; + WFIFOL(inter_fd,8) = gstor->guild_id; + memcpy( WFIFOP(inter_fd,12),gstor, sizeof(struct guild_storage) ); + WFIFOSET(inter_fd,WFIFOW(inter_fd,2)); + return 0; +} + +// パーティ作成要求 +int intif_create_party(struct map_session_data *sd,char *name) +{ + nullpo_retr(0, sd); + + WFIFOW(inter_fd,0) = 0x3020; + WFIFOL(inter_fd,2) = sd->status.account_id; + memcpy(WFIFOP(inter_fd, 6),name,24); + memcpy(WFIFOP(inter_fd,30),sd->status.name,24); + memcpy(WFIFOP(inter_fd,54),map[sd->bl.m].name,16); + WFIFOW(inter_fd,70)= sd->status.base_level; + WFIFOSET(inter_fd,72); +// if(battle_config.etc_log) +// printf("intif: create party\n"); + return 0; +} +// パーティ情報要求 +int intif_request_partyinfo(int party_id) +{ + WFIFOW(inter_fd,0) = 0x3021; + WFIFOL(inter_fd,2) = party_id; + WFIFOSET(inter_fd,6); +// if(battle_config.etc_log) +// printf("intif: request party info\n"); + return 0; +} +// パーティ追加要求 +int intif_party_addmember(int party_id,int account_id) +{ + struct map_session_data *sd; + sd=map_id2sd(account_id); +// if(battle_config.etc_log) +// printf("intif: party add member %d %d\n",party_id,account_id); + if(sd!=NULL){ + WFIFOW(inter_fd,0)=0x3022; + WFIFOL(inter_fd,2)=party_id; + WFIFOL(inter_fd,6)=account_id; + memcpy(WFIFOP(inter_fd,10),sd->status.name,24); + memcpy(WFIFOP(inter_fd,34),map[sd->bl.m].name,16); + WFIFOW(inter_fd,50)=sd->status.base_level; + WFIFOSET(inter_fd,52); + } + return 0; +} +// パーティ設定変更 +int intif_party_changeoption(int party_id,int account_id,int exp,int item) +{ + WFIFOW(inter_fd,0)=0x3023; + WFIFOL(inter_fd,2)=party_id; + WFIFOL(inter_fd,6)=account_id; + WFIFOW(inter_fd,10)=exp; + WFIFOW(inter_fd,12)=item; + WFIFOSET(inter_fd,14); + return 0; +} +// パーティ脱退要求 +int intif_party_leave(int party_id,int account_id) +{ +// if(battle_config.etc_log) +// printf("intif: party leave %d %d\n",party_id,account_id); + WFIFOW(inter_fd,0)=0x3024; + WFIFOL(inter_fd,2)=party_id; + WFIFOL(inter_fd,6)=account_id; + WFIFOSET(inter_fd,10); + return 0; +} +// パーティ移動要求 +int intif_party_changemap(struct map_session_data *sd,int online) +{ + if(sd!=NULL){ + WFIFOW(inter_fd,0)=0x3025; + WFIFOL(inter_fd,2)=sd->status.party_id; + WFIFOL(inter_fd,6)=sd->status.account_id; + memcpy(WFIFOP(inter_fd,10),map[sd->bl.m].name,16); + WFIFOB(inter_fd,26)=online; + WFIFOW(inter_fd,27)=sd->status.base_level; + WFIFOSET(inter_fd,29); + } +// if(battle_config.etc_log) +// printf("party: change map\n"); + return 0; +} +// パーティー解散要求 +int intif_break_party(int party_id) +{ + WFIFOW(inter_fd,0)=0x3026; + WFIFOL(inter_fd,2)=party_id; + WFIFOSET(inter_fd,6); + return 0; +} +// パーティ会話送信 +int intif_party_message(int party_id,int account_id,char *mes,int len) +{ +// if(battle_config.etc_log) +// printf("intif_party_message: %s\n",mes); + WFIFOW(inter_fd,0)=0x3027; + WFIFOW(inter_fd,2)=len+12; + WFIFOL(inter_fd,4)=party_id; + WFIFOL(inter_fd,8)=account_id; + memcpy(WFIFOP(inter_fd,12),mes,len); + WFIFOSET(inter_fd,len+12); + return 0; +} +// パーティ競合チェック要求 +int intif_party_checkconflict(int party_id,int account_id,char *nick) +{ + WFIFOW(inter_fd,0)=0x3028; + WFIFOL(inter_fd,2)=party_id; + WFIFOL(inter_fd,6)=account_id; + memcpy(WFIFOP(inter_fd,10),nick,24); + WFIFOSET(inter_fd,34); + return 0; +} + +// ギルド作成要求 +int intif_guild_create(const char *name,const struct guild_member *master) +{ + nullpo_retr(0, master); + + WFIFOW(inter_fd,0)=0x3030; + WFIFOW(inter_fd,2)=sizeof(struct guild_member)+32; + WFIFOL(inter_fd,4)=master->account_id; + memcpy(WFIFOP(inter_fd,8),name,24); + memcpy(WFIFOP(inter_fd,32),master,sizeof(struct guild_member)); + WFIFOSET(inter_fd,WFIFOW(inter_fd,2)); + return 0; +} +// ギルド情報要求 +int intif_guild_request_info(int guild_id) +{ + WFIFOW(inter_fd,0) = 0x3031; + WFIFOL(inter_fd,2) = guild_id; + WFIFOSET(inter_fd,6); + return 0; +} +// ギルドメンバ追加要求 +int intif_guild_addmember(int guild_id,struct guild_member *m) +{ + WFIFOW(inter_fd,0) = 0x3032; + WFIFOW(inter_fd,2) = sizeof(struct guild_member)+8; + WFIFOL(inter_fd,4) = guild_id; + memcpy(WFIFOP(inter_fd,8),m,sizeof(struct guild_member)); + WFIFOSET(inter_fd,WFIFOW(inter_fd,2)); + return 0; +} +// ギルドメンバ脱退/追放要求 +int intif_guild_leave(int guild_id,int account_id,int char_id,int flag,const char *mes) +{ + WFIFOW(inter_fd, 0) = 0x3034; + WFIFOL(inter_fd, 2) = guild_id; + WFIFOL(inter_fd, 6) = account_id; + WFIFOL(inter_fd,10) = char_id; + WFIFOB(inter_fd,14) = flag; + memcpy(WFIFOP(inter_fd,15),mes,40); + WFIFOSET(inter_fd,55); + return 0; +} +// ギルドメンバのオンライン状況/Lv更新要求 +int intif_guild_memberinfoshort(int guild_id, + int account_id,int char_id,int online,int lv,int class) +{ + WFIFOW(inter_fd, 0) = 0x3035; + WFIFOL(inter_fd, 2) = guild_id; + WFIFOL(inter_fd, 6) = account_id; + WFIFOL(inter_fd,10) = char_id; + WFIFOB(inter_fd,14) = online; + WFIFOW(inter_fd,15) = lv; + WFIFOW(inter_fd,17) = class; + WFIFOSET(inter_fd,19); + return 0; +} +// ギルド解散通知 +int intif_guild_break(int guild_id) +{ + WFIFOW(inter_fd, 0) = 0x3036; + WFIFOL(inter_fd, 2) = guild_id; + WFIFOSET(inter_fd,6); + return 0; +} +// ギルド会話送信 +int intif_guild_message(int guild_id,int account_id,char *mes,int len) +{ + WFIFOW(inter_fd,0)=0x3037; + WFIFOW(inter_fd,2)=len+12; + WFIFOL(inter_fd,4)=guild_id; + WFIFOL(inter_fd,8)=account_id; + memcpy(WFIFOP(inter_fd,12),mes,len); + WFIFOSET(inter_fd,len+12); + return 0; +} +// ギルド競合チェック要求 +int intif_guild_checkconflict(int guild_id,int account_id,int char_id) +{ + WFIFOW(inter_fd, 0)=0x3038; + WFIFOL(inter_fd, 2)=guild_id; + WFIFOL(inter_fd, 6)=account_id; + WFIFOL(inter_fd,10)=char_id; + WFIFOSET(inter_fd,14); + return 0; +} +// ギルド基本情報変更要求 +int intif_guild_change_basicinfo(int guild_id,int type,const void *data,int len) +{ + WFIFOW(inter_fd,0)=0x3039; + WFIFOW(inter_fd,2)=len+10; + WFIFOL(inter_fd,4)=guild_id; + WFIFOW(inter_fd,8)=type; + memcpy(WFIFOP(inter_fd,10),data,len); + WFIFOSET(inter_fd,len+10); + return 0; +} +// ギルドメンバ情報変更要求 +int intif_guild_change_memberinfo(int guild_id,int account_id,int char_id, + int type,const void *data,int len) +{ + WFIFOW(inter_fd, 0)=0x303a; + WFIFOW(inter_fd, 2)=len+18; + WFIFOL(inter_fd, 4)=guild_id; + WFIFOL(inter_fd, 8)=account_id; + WFIFOL(inter_fd,12)=char_id; + WFIFOW(inter_fd,16)=type; + memcpy(WFIFOP(inter_fd,18),data,len); + WFIFOSET(inter_fd,len+18); + return 0; +} +// ギルド役職変更要求 +int intif_guild_position(int guild_id,int idx,struct guild_position *p) +{ + WFIFOW(inter_fd,0)=0x303b; + WFIFOW(inter_fd,2)=sizeof(struct guild_position)+12; + WFIFOL(inter_fd,4)=guild_id; + WFIFOL(inter_fd,8)=idx; + memcpy(WFIFOP(inter_fd,12),p,sizeof(struct guild_position)); + WFIFOSET(inter_fd,WFIFOW(inter_fd,2)); + return 0; +} +// ギルドスキルアップ要求 +int intif_guild_skillup(int guild_id,int skill_num,int account_id) +{ + WFIFOW(inter_fd, 0)=0x303c; + WFIFOL(inter_fd, 2)=guild_id; + WFIFOL(inter_fd, 6)=skill_num; + WFIFOL(inter_fd,10)=account_id; + WFIFOSET(inter_fd,14); + return 0; +} +// ギルド同盟/敵対要求 +int intif_guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2,int flag) +{ + WFIFOW(inter_fd, 0)=0x303d; + WFIFOL(inter_fd, 2)=guild_id1; + WFIFOL(inter_fd, 6)=guild_id2; + WFIFOL(inter_fd,10)=account_id1; + WFIFOL(inter_fd,14)=account_id2; + WFIFOB(inter_fd,18)=flag; + WFIFOSET(inter_fd,19); + return 0; +} +// ギルド告知変更要求 +int intif_guild_notice(int guild_id,const char *mes1,const char *mes2) +{ + WFIFOW(inter_fd,0)=0x303e; + WFIFOL(inter_fd,2)=guild_id; + memcpy(WFIFOP(inter_fd,6),mes1,60); + memcpy(WFIFOP(inter_fd,66),mes2,120); + WFIFOSET(inter_fd,186); + return 0; +} +// ギルドエンブレム変更要求 +int intif_guild_emblem(int guild_id,int len,const char *data) +{ + if(guild_id<=0 || len<0 || len>2000) + return 0; + WFIFOW(inter_fd,0)=0x303f; + WFIFOW(inter_fd,2)=len+12; + WFIFOL(inter_fd,4)=guild_id; + WFIFOL(inter_fd,8)=0; + memcpy(WFIFOP(inter_fd,12),data,len); + WFIFOSET(inter_fd,len+12); + return 0; +} +//現在のギルド城占領ギルドを調べる +int intif_guild_castle_dataload(int castle_id,int index) +{ + WFIFOW(inter_fd,0)=0x3040; + WFIFOW(inter_fd,2)=castle_id; + WFIFOB(inter_fd,4)=index; + WFIFOSET(inter_fd,5); + return 0; +} + +//ギルド城占領ギルド変更要求 +int intif_guild_castle_datasave(int castle_id,int index, int value) +{ + WFIFOW(inter_fd,0)=0x3041; + WFIFOW(inter_fd,2)=castle_id; + WFIFOB(inter_fd,4)=index; + WFIFOL(inter_fd,5)=value; + WFIFOSET(inter_fd,9); + return 0; +} +//----------------------------------------------------------------- +// Packets receive from inter server + +// Wisp/Page reception +int intif_parse_WisMessage(int fd) { // rewritten by [Yor] + struct map_session_data* sd; + int i; + char *wisp_source; + + if (battle_config.etc_log) + printf("intif_parse_wismessage: id: %d, from: %s, to: %s, message: '%s'\n", RFIFOL(fd,4), RFIFOP(fd,8), RFIFOP(fd,32), RFIFOP(fd,56)); + sd = map_nick2sd(RFIFOP(fd,32)); // Searching destination player + if (sd != NULL && strcmp(sd->status.name, RFIFOP(fd,32)) == 0) { // exactly same name (inter-server have checked the name before) + // if player ignore all + if (sd->ignoreAll == 1) + intif_wis_replay(RFIFOL(fd,4), 2); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + else { + wisp_source = RFIFOP(fd,8); // speed up + // if player ignore the source character + for(i = 0; i < (sizeof(sd->ignore) / sizeof(sd->ignore[0])); i++) + if (strcmp(sd->ignore[i].name, wisp_source) == 0) { + intif_wis_replay(RFIFOL(fd,4), 2); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + break; + } + // if source player not found in ignore list + if (i == (sizeof(sd->ignore) / sizeof(sd->ignore[0]))) { + clif_wis_message(sd->fd,RFIFOP(fd,8),RFIFOP(fd,56),RFIFOW(fd,2)-56); + intif_wis_replay(RFIFOL(fd,4), 0); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + } + } + } else + intif_wis_replay(RFIFOL(fd,4), 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + return 0; +} + +// Wisp/page transmission result reception +int intif_parse_WisEnd(int fd) { + struct map_session_data* sd; + + if (battle_config.etc_log) + printf("intif_parse_wisend: player: %s, flag: %d\n", RFIFOP(fd,2), RFIFOB(fd,26)); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target + sd = map_nick2sd(RFIFOP(fd,2)); + if (sd != NULL) + clif_wis_end(sd->fd, RFIFOB(fd,26)); + + return 0; +} + +// Received wisp message from map-server via char-server for ALL gm +int mapif_parse_WisToGM(int fd) { // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B + int i, min_gm_level; + struct map_session_data *pl_sd; + char Wisp_name[24]; + char mbuf[255]; + char *message = ((RFIFOW(fd,2) - 30) >= sizeof(mbuf)) ? (char *) malloc((RFIFOW(fd,2) - 30)) : mbuf; + + min_gm_level = (int)RFIFOW(fd,28); + memcpy(Wisp_name, RFIFOP(fd,4), 24); + Wisp_name[23] = '\0'; + memcpy(message, RFIFOP(fd,30), RFIFOW(fd,2) - 30); + message[sizeof(message) - 1] = '\0'; + // information is sended to all online GM + for (i = 0; i < fd_max; i++) + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) + if (pc_isGM(pl_sd) >= min_gm_level) + clif_wis_message(i, Wisp_name, message, strlen(message) + 1); + + if (message != mbuf) + free(message); + + return 0; +} + +// アカウント変数通知 +int intif_parse_AccountReg(int fd) { + int j,p; + struct map_session_data *sd; + + if( (sd=map_id2sd(RFIFOL(fd,4)))==NULL ) + return 1; + for(p=8,j=0;p<RFIFOW(fd,2) && j<ACCOUNT_REG_NUM;p+=36,j++){ + memcpy(sd->status.account_reg[j].str,RFIFOP(fd,p),32); + sd->status.account_reg[j].value=RFIFOL(fd,p+32); + } + sd->status.account_reg_num = j; +// printf("intif: accountreg\n"); + + return 0; +} + +// 倉庫データ受信 +int intif_parse_LoadStorage(int fd) { + struct storage *stor; + struct map_session_data *sd; + + stor = account2storage( RFIFOL(fd,4)); + if (RFIFOW(fd,2)-8 != sizeof(struct storage)) { + if (battle_config.error_log) + printf("intif_parse_LoadStorage: data size error %d %d\n", RFIFOW(fd,2)-8, sizeof(struct storage)); + return 1; + } + sd=map_id2sd( RFIFOL(fd,4) ); + if(sd==NULL){ + if(battle_config.error_log) + printf("intif_parse_LoadStorage: user not found %d\n",RFIFOL(fd,4)); + return 1; + } + if(battle_config.save_log) + printf("intif_openstorage: %d\n",RFIFOL(fd,4) ); + memcpy(stor,RFIFOP(fd,8),sizeof(struct storage)); + stor->storage_status=1; + sd->state.storage_flag = 0; + clif_storageitemlist(sd,stor); + clif_storageequiplist(sd,stor); + clif_updatestorageamount(sd,stor); + + return 0; +} + +// 倉庫データ送信成功 +int intif_parse_SaveStorage(int fd) +{ + if(battle_config.save_log) + printf("intif_savestorage: done %d %d\n",RFIFOL(fd,2),RFIFOB(fd,6) ); + return 0; +} + +int intif_parse_LoadGuildStorage(int fd) +{ + struct guild_storage *gstor; + struct map_session_data *sd; + int guild_id = RFIFOL(fd,8); + if(guild_id > 0) { + gstor=guild2storage(guild_id); + if(!gstor) { + if(battle_config.error_log) + printf("intif_parse_LoadGuildStorage: error guild_id %d not exist\n",guild_id); + return 1; + } + if( RFIFOW(fd,2)-12 != sizeof(struct guild_storage) ){ + gstor->storage_status = 0; + if(battle_config.error_log) + printf("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-12 , sizeof(struct guild_storage)); + return 1; + } + sd=map_id2sd( RFIFOL(fd,4) ); + if(sd==NULL){ + if(battle_config.error_log) + printf("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); + return 1; + } + if(battle_config.save_log) + printf("intif_open_guild_storage: %d\n",RFIFOL(fd,4) ); + memcpy(gstor,RFIFOP(fd,12),sizeof(struct guild_storage)); + gstor->storage_status = 1; + sd->state.storage_flag = 1; + clif_guildstorageitemlist(sd,gstor); + clif_guildstorageequiplist(sd,gstor); + clif_updateguildstorageamount(sd,gstor); + } + return 0; +} +int intif_parse_SaveGuildStorage(int fd) +{ + if(battle_config.save_log) { + printf("intif_save_guild_storage: done %d %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOB(fd,10) ); + } + return 0; +} + +// パーティ作成可否 +int intif_parse_PartyCreated(int fd) +{ + if(battle_config.etc_log) + printf("intif: party created\n"); + party_created(RFIFOL(fd,2),RFIFOB(fd,6),RFIFOL(fd,7),RFIFOP(fd,11)); + return 0; +} +// パーティ情報 +int intif_parse_PartyInfo(int fd) +{ + if( RFIFOW(fd,2)==8){ + if(battle_config.error_log) + printf("intif: party noinfo %d\n",RFIFOL(fd,4)); + party_recv_noinfo(RFIFOL(fd,4)); + return 0; + } + +// printf("intif: party info %d\n",RFIFOL(fd,4)); + if( RFIFOW(fd,2)!=sizeof(struct party)+4 ){ + if(battle_config.error_log) + printf("intif: party info : data size error %d %d %d\n",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct party)+4); + } + party_recv_info((struct party *)RFIFOP(fd,4)); + return 0; +} +// パーティ追加通知 +int intif_parse_PartyMemberAdded(int fd) +{ + if(battle_config.etc_log) + printf("intif: party member added %d %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOB(fd,10)); + party_member_added(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOB(fd,10)); + return 0; +} +// パーティ設定変更通知 +int intif_parse_PartyOptionChanged(int fd) +{ + party_optionchanged(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOW(fd,10),RFIFOW(fd,12),RFIFOB(fd,14)); + return 0; +} +// パーティ脱退通知 +int intif_parse_PartyMemberLeaved(int fd) +{ + if(battle_config.etc_log) + printf("intif: party member leaved %d %d %s\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10)); + party_member_leaved(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10)); + return 0; +} +// パーティ解散通知 +int intif_parse_PartyBroken(int fd) +{ + party_broken(RFIFOL(fd,2)); + return 0; +} +// パーティ移動通知 +int intif_parse_PartyMove(int fd) +{ +// if(battle_config.etc_log) +// printf("intif: party move %d %d %s %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10),RFIFOB(fd,26),RFIFOW(fd,27)); + party_recv_movemap(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOP(fd,10),RFIFOB(fd,26),RFIFOW(fd,27)); + return 0; +} +// パーティメッセージ +int intif_parse_PartyMessage(int fd) +{ +// if(battle_config.etc_log) +// printf("intif_parse_PartyMessage: %s\n",RFIFOP(fd,12)); + party_recv_message(RFIFOL(fd,4),RFIFOL(fd,8),RFIFOP(fd,12),RFIFOW(fd,2)-12); + return 0; +} + +// ギルド作成可否 +int intif_parse_GuildCreated(int fd) +{ + guild_created(RFIFOL(fd,2),RFIFOL(fd,6)); + return 0; +} +// ギルド情報 +int intif_parse_GuildInfo(int fd) +{ + if( RFIFOW(fd,2)==8){ + if(battle_config.error_log) + printf("intif: guild noinfo %d\n",RFIFOL(fd,4)); + guild_recv_noinfo(RFIFOL(fd,4)); + return 0; + } + +// if(battle_config.etc_log) +// printf("intif: guild info %d\n",RFIFOL(fd,4)); + if( RFIFOW(fd,2)!=sizeof(struct guild)+4 ){ + if(battle_config.error_log) + printf("intif: guild info : data size error\n %d %d %d",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct guild)+4); + } + guild_recv_info((struct guild *)RFIFOP(fd,4)); + return 0; +} +// ギルドメンバ追加通知 +int intif_parse_GuildMemberAdded(int fd) +{ + if(battle_config.etc_log) + printf("intif: guild member added %d %d %d %d\n",RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14)); + guild_member_added(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14)); + return 0; +} +// ギルドメンバ脱退/追放通知 +int intif_parse_GuildMemberLeaved(int fd) +{ + guild_member_leaved(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14), + RFIFOP(fd,55),RFIFOP(fd,15)); + return 0; +} + +// ギルドメンバオンライン状態/Lv変更通知 +int intif_parse_GuildMemberInfoShort(int fd) +{ + guild_recv_memberinfoshort(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17)); + return 0; +} +// ギルド解散通知 +int intif_parse_GuildBroken(int fd) +{ + guild_broken(RFIFOL(fd,2),RFIFOB(fd,6)); + return 0; +} + +// ギルド基本情報変更通知 +int intif_parse_GuildBasicInfoChanged(int fd) +{ + int type=RFIFOW(fd,8),guild_id=RFIFOL(fd,4); + void *data=RFIFOP(fd,10); + struct guild *g=guild_search(guild_id); + short dw=*((short *)data); + int dd=*((int *)data); + if( g==NULL ) + return 0; + switch(type){ + case GBI_EXP: g->exp=dd; break; + case GBI_GUILDLV: g->guild_lv=dw; break; + case GBI_SKILLPOINT: g->skill_point=dd; break; + } + return 0; +} +// ギルドメンバ情報変更通知 +int intif_parse_GuildMemberInfoChanged(int fd) +{ + int type=RFIFOW(fd,16),guild_id=RFIFOL(fd,4); + int account_id=RFIFOL(fd,8),char_id=RFIFOL(fd,12); + void *data=RFIFOP(fd,18); + struct guild *g=guild_search(guild_id); + int idx,dd=*((int *)data); + if( g==NULL ) + return 0; + idx=guild_getindex(g,account_id,char_id); + switch(type){ + case GMI_POSITION: + g->member[idx].position=dd; + guild_memberposition_changed(g,idx,dd); + break; + case GMI_EXP: + g->member[idx].exp=dd; + break; + } + return 0; +} + +// ギルド役職変更通知 +int intif_parse_GuildPosition(int fd) +{ + if( RFIFOW(fd,2)!=sizeof(struct guild_position)+12 ){ + if(battle_config.error_log) + printf("intif: guild info : data size error\n %d %d %d",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct guild_position)+12); + } + guild_position_changed(RFIFOL(fd,4),RFIFOL(fd,8),(struct guild_position *)RFIFOP(fd,12)); + return 0; +} +// ギルドスキル割り振り通知 +int intif_parse_GuildSkillUp(int fd) +{ + guild_skillupack(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10)); + return 0; +} +// ギルド同盟/敵対通知 +int intif_parse_GuildAlliance(int fd) +{ + guild_allianceack(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOL(fd,14), + RFIFOB(fd,18),RFIFOP(fd,19),RFIFOP(fd,43)); + return 0; +} +// ギルド告知変更通知 +int intif_parse_GuildNotice(int fd) +{ + guild_notice_changed(RFIFOL(fd,2),RFIFOP(fd,6),RFIFOP(fd,66)); + return 0; +} +// ギルドエンブレム変更通知 +int intif_parse_GuildEmblem(int fd) +{ + guild_emblem_changed(RFIFOW(fd,2)-12,RFIFOL(fd,4),RFIFOL(fd,8),RFIFOP(fd,12)); + return 0; +} +// ギルド会話受信 +int intif_parse_GuildMessage(int fd) +{ + guild_recv_message(RFIFOL(fd,4),RFIFOL(fd,8),RFIFOP(fd,12),RFIFOW(fd,2)-12); + return 0; +} +// ギルド城データ要求返信 +int intif_parse_GuildCastleDataLoad(int fd) +{ + return guild_castledataloadack(RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5)); +} +// ギルド城データ変更通知 +int intif_parse_GuildCastleDataSave(int fd) +{ + return guild_castledatasaveack(RFIFOW(fd,2),RFIFOB(fd,4),RFIFOL(fd,5)); +} + +// ギルド城データ一括受信(初期化時) +int intif_parse_GuildCastleAllDataLoad(int fd) +{ + return guild_castlealldataload(RFIFOW(fd,2),(struct guild_castle *)RFIFOP(fd,4)); +} + +// pet +int intif_parse_CreatePet(int 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=RFIFOW(fd,2); + if(sizeof(struct s_pet)!=len-9) { + if(battle_config.etc_log) + printf("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) +{ + if(RFIFOB(fd,6) == 1) { + if(battle_config.error_log) + printf("pet data save failure\n"); + } + + return 0; +} + +int intif_parse_DeletePetOk(int fd) +{ + if(RFIFOB(fd,2) == 1) { + if(battle_config.error_log) + printf("pet data delete failure\n"); + } + + return 0; +} +//----------------------------------------------------------------- +// inter serverからの通信 +// エラーがあれば0(false)を返すこと +// パケットが処理できれば1,パケット長が足りなければ2を返すこと +int intif_parse(int fd) +{ + int packet_len; + int cmd = RFIFOW(fd,0); + // パケットのID確認 + if(cmd<0x3800 || cmd>=0x3800+(sizeof(packet_len_table)/sizeof(packet_len_table[0])) || + packet_len_table[cmd-0x3800]==0){ + return 0; + } + // パケットの長さ確認 + packet_len = packet_len_table[cmd-0x3800]; + if(packet_len==-1){ + if(RFIFOREST(fd)<4) + return 2; + packet_len = RFIFOW(fd,2); + } +// if(battle_config.etc_log) +// printf("intif_parse %d %x %d %d\n",fd,cmd,packet_len,RFIFOREST(fd)); + if(RFIFOREST(fd)<packet_len){ + return 2; + } + // 処理分岐 + switch(cmd){ + case 0x3800: clif_GMmessage(NULL,RFIFOP(fd,4),packet_len-4,0); break; + case 0x3801: intif_parse_WisMessage(fd); break; + case 0x3802: intif_parse_WisEnd(fd); break; + case 0x3803: mapif_parse_WisToGM(fd); break; + case 0x3804: intif_parse_AccountReg(fd); break; + case 0x3810: intif_parse_LoadStorage(fd); break; + case 0x3811: intif_parse_SaveStorage(fd); break; + case 0x3818: intif_parse_LoadGuildStorage(fd); break; + case 0x3819: intif_parse_SaveGuildStorage(fd); break; + case 0x3820: intif_parse_PartyCreated(fd); break; + case 0x3821: intif_parse_PartyInfo(fd); break; + case 0x3822: intif_parse_PartyMemberAdded(fd); break; + case 0x3823: intif_parse_PartyOptionChanged(fd); break; + case 0x3824: intif_parse_PartyMemberLeaved(fd); break; + case 0x3825: intif_parse_PartyMove(fd); break; + case 0x3826: intif_parse_PartyBroken(fd); break; + case 0x3827: intif_parse_PartyMessage(fd); break; + case 0x3830: intif_parse_GuildCreated(fd); break; + case 0x3831: intif_parse_GuildInfo(fd); break; + case 0x3832: intif_parse_GuildMemberAdded(fd); break; + case 0x3834: intif_parse_GuildMemberLeaved(fd); break; + case 0x3835: intif_parse_GuildMemberInfoShort(fd); break; + case 0x3836: intif_parse_GuildBroken(fd); break; + case 0x3837: intif_parse_GuildMessage(fd); break; + case 0x3839: intif_parse_GuildBasicInfoChanged(fd); break; + case 0x383a: intif_parse_GuildMemberInfoChanged(fd); break; + case 0x383b: intif_parse_GuildPosition(fd); break; + case 0x383c: intif_parse_GuildSkillUp(fd); break; + case 0x383d: intif_parse_GuildAlliance(fd); break; + case 0x383e: intif_parse_GuildNotice(fd); break; + case 0x383f: intif_parse_GuildEmblem(fd); break; + case 0x3840: intif_parse_GuildCastleDataLoad(fd); break; + case 0x3841: intif_parse_GuildCastleDataSave(fd); break; + case 0x3842: intif_parse_GuildCastleAllDataLoad(fd); break; + case 0x3880: intif_parse_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) + printf("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0)); + return 0; + } + // パケット読み飛ばし + RFIFOSKIP(fd,packet_len); + return 1; +} diff --git a/src/map/intif.h b/src/map/intif.h new file mode 100644 index 0000000..85e1914 --- /dev/null +++ b/src/map/intif.h @@ -0,0 +1,56 @@ +// $Id: intif.h,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _INTIF_H_ +#define _INFIF_H_ + +int intif_parse(int fd); + +int intif_GMmessage(char* mes,int len,int flag); + +int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len); +int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes, int mes_len); + +int intif_saveaccountreg(struct map_session_data *sd); +int intif_request_accountreg(struct map_session_data *sd); + +int intif_request_storage(int account_id); +int intif_send_storage(struct storage *stor); +int intif_request_guild_storage(int account_id, int guild_id); +int intif_send_guild_storage(int account_id, struct guild_storage *gstor); + + +int intif_create_party(struct map_session_data *sd,char *name); +int intif_request_partyinfo(int party_id); +int intif_party_addmember(int party_id, int account_id); +int intif_party_changeoption(int party_id, int account_id, int exp, int item); +int intif_party_leave(int party_id, int accound_id); +int intif_party_changemap(struct map_session_data *sd, int online); +int intif_break_party(int party_id); +int intif_party_message(int party_id, int account_id, char *mes,int len); +int intif_party_checkconflict(int party_id, int account_id, char *nick); + + +int intif_guild_create(const char *name, const struct guild_member *master); +int intif_guild_request_info(int guild_id); +int intif_guild_addmember(int guild_id, struct guild_member *m); +int intif_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes); +int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class); +int intif_guild_break(int guild_id); +int intif_guild_message(int guild_id, int account_id, char *mes, int len); +int intif_guild_checkconflict(int guild_id, int account_id, int char_id); +int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len); +int intif_guild_change_memberinfo(int guild_id, int account_id, int char_id, int type, const void *data, int len); +int intif_guild_position(int guild_id, int idx, struct guild_position *p); +int intif_guild_skillup(int guild_id, int skill_num, int account_id); +int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag); +int intif_guild_notice(int guild_id, const char *mes1, const char *mes2); +int intif_guild_emblem(int guild_id, int len, const char *data); +int intif_guild_castle_dataload(int castle_id, int index); +int intif_guild_castle_datasave(int castle_id, int index, int value); + +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 0000000..a225cff --- /dev/null +++ b/src/map/itemdb.c @@ -0,0 +1,882 @@ +// $Id: itemdb.c,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "db.h" +#include "grfio.h" +#include "nullpo.h" +#include "malloc.h" +#include "map.h" +#include "battle.h" +#include "itemdb.h" +#include "script.h" +#include "pc.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define MAX_RANDITEM 2000 + +// ** ITEMDB_OVERRIDE_NAME_VERBOSE ** +// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します. +//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1 + +static struct dbt* item_db; + +static struct random_item_data blue_box[MAX_RANDITEM],violet_box[MAX_RANDITEM],card_album[MAX_RANDITEM],gift_box[MAX_RANDITEM],scroll[MAX_RANDITEM]; +static int blue_box_count=0,violet_box_count=0,card_album_count=0,gift_box_count=0,scroll_count=0; +static int blue_box_default=0,violet_box_default=0,card_album_default=0,gift_box_default=0,scroll_default=0; + +// Function declarations + +static void itemdb_read(void); +static int itemdb_readdb(void); +#ifndef TXT_ONLY +static int itemdb_read_sqldb(void); +#endif /* not TXT_ONLY */ +static int itemdb_read_randomitem(); +static int itemdb_read_itemavail(void); +static int itemdb_read_itemnametable(void); +static int itemdb_read_noequip(void); +void itemdb_reload(void); + +/*========================================== + * 名前で検索用 + *------------------------------------------ + */ +// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name) +int itemdb_searchname_sub(void *key,void *data,va_list ap) +{ + struct item_data *item=(struct item_data *)data,**dst; + char *str; + str=va_arg(ap,char *); + dst=va_arg(ap,struct item_data **); +// if( strcmpi(item->name,str)==0 || strcmp(item->jname,str)==0 || +// memcmp(item->name,str,24)==0 || memcmp(item->jname,str,24)==0 ) + if( strcmpi(item->name,str)==0 ) //by lupus + *dst=item; + return 0; +} + +/*========================================== + * 名前で検索用 + *------------------------------------------ + */ +int itemdb_searchjname_sub(void *key,void *data,va_list ap) +{ + struct item_data *item=(struct item_data *)data,**dst; + char *str; + str=va_arg(ap,char *); + dst=va_arg(ap,struct item_data **); + if( strcmpi(item->jname,str)==0 ) + *dst=item; + return 0; +} +/*========================================== + * 名前で検索 + *------------------------------------------ + */ +struct item_data* itemdb_searchname(const char *str) +{ + struct item_data *item=NULL; + numdb_foreach(item_db,itemdb_searchname_sub,str,&item); + return item; +} + +/*========================================== + * 箱系アイテム検索 + *------------------------------------------ + */ +int itemdb_searchrandomid(int flags) +{ + int nameid=0,i,index,count; + struct random_item_data *list=NULL; + + struct { + int nameid,count; + struct random_item_data *list; + } data[] ={ + { 0,0,NULL }, + { blue_box_default ,blue_box_count ,blue_box }, + { violet_box_default,violet_box_count ,violet_box }, + { card_album_default,card_album_count ,card_album }, + { gift_box_default ,gift_box_count ,gift_box }, + { scroll_default ,scroll_count ,scroll }, + }; + + if(flags>=1 && flags<=5){ + nameid=data[flags].nameid; + count=data[flags].count; + list=data[flags].list; + + if(count > 0) { + for(i=0;i<1000;i++) { + index = rand()%count; + if( rand()%1000000 < list[index].per) { + nameid = list[index].nameid; + break; + } + } + } + } + return nameid; +} + +/*========================================== + * DBの存在確認 + *------------------------------------------ + */ +struct item_data* itemdb_exists(int nameid) +{ + return numdb_search(item_db,nameid); +} +/*========================================== + * DBの検索 + *------------------------------------------ + */ +struct item_data* itemdb_search(int nameid) +{ + struct item_data *id; + + id=numdb_search(item_db,nameid); + if(id) return id; + + id=(struct item_data *)aCalloc(1,sizeof(struct item_data)); + numdb_insert(item_db,nameid,id); + + id->nameid=nameid; + id->value_buy=10; + id->value_sell=id->value_buy/2; + id->weight=10; + id->sex=2; + id->elv=0; + id->class=0xffffffff; + id->flag.available=0; + id->flag.value_notdc=0; //一応・・・ + id->flag.value_notoc=0; + id->flag.no_equip=0; + id->view_id=0; + + if(nameid>500 && nameid<600) + id->type=0; //heal item + else if(nameid>600 && nameid<700) + id->type=2; //use item + else if((nameid>700 && nameid<1100) || + (nameid>7000 && nameid<8000)) + id->type=3; //correction + else if(nameid>=1750 && nameid<1771) + id->type=10; //arrow + else if(nameid>1100 && nameid<2000) + id->type=4; //weapon + else if((nameid>2100 && nameid<3000) || + (nameid>5000 && nameid<6000)) + id->type=5; //armor + else if(nameid>4000 && nameid<5000) + id->type=6; //card + else if(nameid>9000 && nameid<10000) + id->type=7; //egg + else if(nameid>10000) + id->type=8; //petequip + + return id; +} + +/*========================================== + * + *------------------------------------------ + */ +int itemdb_isequip(int nameid) +{ + int type=itemdb_type(nameid); + if(type==0 || type==2 || type==3 || type==6 || type==10) + return 0; + return 1; +} +/*========================================== + * + *------------------------------------------ + */ +int itemdb_isequip2(struct item_data *data) +{ + if(data) { + int type=data->type; + if(type==0 || type==2 || type==3 || type==6 || type==10) + return 0; + else + return 1; + } + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int itemdb_isequip3(int nameid) +{ + int type=itemdb_type(nameid); + if(type==4 || type==5 || type == 8) + return 1; + return 0; +} + +/*========================================== + * 捨てられるアイテムは1、そうでないアイテムは0 + *------------------------------------------ + */ +int itemdb_isdropable(int nameid) +{ + //結婚指輪は捨てられない + switch(nameid){ + case 2634: //結婚指輪 + case 2635: //結婚指輪 + return 0; + } + + return 1; +} + +// +// 初期化 +// +/*========================================== + * + *------------------------------------------ + */ +static int itemdb_read_itemslottable(void) +{ + char *buf,*p; + int s; + + buf=grfio_read("data\\itemslottable.txt"); + if(buf==NULL) + return -1; + s=grfio_size("data\\itemslottable.txt"); + buf[s]=0; + for(p=buf;p-buf<s;){ + int nameid,equip; + sscanf(p,"%d#%d#",&nameid,&equip); + itemdb_search(nameid)->equip=equip; + p=strchr(p,10); + if(!p) break; + p++; + p=strchr(p,10); + if(!p) break; + p++; + } + free(buf); + + return 0; +} + +#ifndef TXT_ONLY +/*==================================== + * Removed item_value_db, don't re-add + *------------------------------------ + */ +static void itemdb_read(void) +{ + itemdb_read_itemslottable(); + + if (db_use_sqldbs) + { + itemdb_read_sqldb(); + } + else + { + itemdb_readdb(); + } + + itemdb_read_randomitem(); + itemdb_read_itemavail(); + itemdb_read_noequip(); + + if (!battle_config.item_name_override_grffile) + itemdb_read_itemnametable(); +} + +#endif /* not TXT_ONLY */ +/*========================================== + * アイテムデータベースの読み込み + *------------------------------------------ + */ +static int itemdb_readdb(void) +{ + FILE *fp; + char line[1024]; + int ln=0,lines=0; + int nameid,j; + char *str[32],*p,*np; + struct item_data *id; + int i=0; + char *filename[]={ "db/item_db.txt","db/item_db2.txt" }; + + for(i=0;i<2;i++){ + + fp=fopen(filename[i],"r"); + if(fp==NULL){ + if(i>0) + continue; + printf("can't read %s\n",filename[i]); + exit(1); + } + + lines=0; + while(fgets(line,1020,fp)){ + lines++; + if(line[0]=='/' && line[1]=='/') + continue; + memset(str,0,sizeof(str)); + for(j=0,np=p=line;j<17 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p){ *p++=0; np=p; } + } + if(str[0]==NULL) + continue; + + nameid=atoi(str[0]); + if(nameid<=0 || nameid>=20000) + continue; + ln++; + + //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Gender,Loc,wLV,eLV,View + id=itemdb_search(nameid); + memcpy(id->name,str[1],24); + memcpy(id->jname,str[2],24); + id->type=atoi(str[3]); + // buy≠sell*2 は item_value_db.txt で指定してください。 + if (atoi(str[5])) { // sell値を優先とする + id->value_buy=atoi(str[5])*2; + id->value_sell=atoi(str[5]); + } else { + id->value_buy=atoi(str[4]); + id->value_sell=atoi(str[4])/2; + } + 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]); + id->class=atoi(str[11]); + id->sex=atoi(str[12]); + if(id->equip != atoi(str[13])){ + id->equip=atoi(str[13]); + } + id->wlv=atoi(str[14]); + id->elv=atoi(str[15]); + id->look=atoi(str[16]); + id->flag.available=1; + id->flag.value_notdc=0; + id->flag.value_notoc=0; + id->view_id=0; + + id->use_script=NULL; + id->equip_script=NULL; + + if((p=strchr(np,'{'))==NULL) + continue; + id->use_script = parse_script(p,lines); + if((p=strchr(p+1,'{'))==NULL) + continue; + id->equip_script = parse_script(p,lines); + } + fclose(fp); + printf("read %s done (count=%d)\n",filename[i],ln); + } + return 0; +} + +// Removed item_value_db, don't re-add! + +/*========================================== + * ランダムアイテム出現データの読み込み + *------------------------------------------ + */ +static int itemdb_read_randomitem() +{ + FILE *fp; + char line[1024]; + int ln=0; + int nameid,i,j; + char *str[10],*p; + + const struct { + char filename[64]; + struct random_item_data *pdata; + int *pcount,*pdefault; + } data[] = { + {"db/item_bluebox.txt", blue_box, &blue_box_count, &blue_box_default }, + {"db/item_violetbox.txt", violet_box, &violet_box_count, &violet_box_default }, + {"db/item_cardalbum.txt", card_album, &card_album_count, &card_album_default }, + {"db/item_giftbox.txt", gift_box, &gift_box_count, &gift_box_default }, + {"db/item_scroll.txt", scroll, &scroll_count, &scroll_default }, + }; + + for(i=0;i<sizeof(data)/sizeof(data[0]);i++){ + struct random_item_data *pd=data[i].pdata; + int *pc=data[i].pcount; + int *pdefault=data[i].pdefault; + char *fn=data[i].filename; + + *pdefault = 0; + if( (fp=fopen(fn,"r"))==NULL ){ + printf("can't read %s\n",fn); + continue; + } + + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + memset(str,0,sizeof(str)); + for(j=0,p=line;j<3 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + + if(str[0]==NULL) + continue; + + nameid=atoi(str[0]); + if(nameid<0 || nameid>=20000) + continue; + if(nameid == 0) { + if(str[2]) + *pdefault = atoi(str[2]); + continue; + } + + if(str[2]){ + pd[ *pc ].nameid = nameid; + pd[(*pc)++].per = atoi(str[2]); + } + + if(ln >= MAX_RANDITEM) + break; + ln++; + } + fclose(fp); + printf("read %s done (count=%d)\n",fn,*pc); + } + + return 0; +} +/*========================================== + * アイテム使用可能フラグのオーバーライド + *------------------------------------------ + */ +static int itemdb_read_itemavail(void) +{ + FILE *fp; + char line[1024]; + int ln=0; + int nameid,j,k; + char *str[10],*p; + + if( (fp=fopen("db/item_avail.txt","r"))==NULL ){ + printf("can't read db/item_avail.txt\n"); + return -1; + } + + while(fgets(line,1020,fp)){ + struct item_data *id; + if(line[0]=='/' && line[1]=='/') + continue; + memset(str,0,sizeof(str)); + for(j=0,p=line;j<2 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + + if(str[0]==NULL) + continue; + + nameid=atoi(str[0]); + if(nameid<0 || nameid>=20000 || !(id=itemdb_exists(nameid)) ) + continue; + k=atoi(str[1]); + if(k > 0) { + id->flag.available = 1; + id->view_id = k; + } + else + id->flag.available = 0; + ln++; + } + fclose(fp); + printf("read db/item_avail.txt done (count=%d)\n",ln); + return 0; +} + +/*========================================== + * アイテムの名前テーブルを読み込む + *------------------------------------------ + */ +static int itemdb_read_itemnametable(void) +{ + char *buf,*p; + int s; + + buf=grfio_reads("data\\idnum2itemdisplaynametable.txt",&s); + + if(buf==NULL) + return -1; + + buf[s]=0; + for(p=buf;p-buf<s;){ + int nameid; + char buf2[64]; + + if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){ + +#ifdef ITEMDB_OVERRIDE_NAME_VERBOSE + if( itemdb_exists(nameid) && + strncmp(itemdb_search(nameid)->jname,buf2,24)!=0 ){ + printf("[override] %d %s => %s\n",nameid + ,itemdb_search(nameid)->jname,buf2); + } +#endif + + memcpy(itemdb_search(nameid)->jname,buf2,24); + } + + p=strchr(p,10); + if(!p) break; + p++; + } + free(buf); + printf("read data\\idnum2itemdisplaynametable.txt done.\n"); + + return 0; +} +#ifdef TXT_ONLY +/*========================================== + * カードイラストのリソース名前テーブルを読み込む + *------------------------------------------ + */ +static int itemdb_read_cardillustnametable(void) +{ + char *buf,*p; + int s; + + buf=grfio_reads("data\\num2cardillustnametable.txt",&s); + + if(buf==NULL) + return -1; + + buf[s]=0; + for(p=buf;p-buf<s;){ + int nameid; + char buf2[64]; + + if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){ + strcat(buf2,".bmp"); + memcpy(itemdb_search(nameid)->cardillustname,buf2,64); +// printf("%d %s\n",nameid,itemdb_search(nameid)->cardillustname); + } + + p=strchr(p,10); + if(!p) break; + p++; + } + free(buf); + printf("read data\\num2cardillustnametable.txt done.\n"); + + return 0; +} +#endif /* TXT_ONLY */ +/*========================================== + * 装備制限ファイル読み出し + *------------------------------------------ + */ +static int itemdb_read_noequip(void) +{ + FILE *fp; + char line[1024]; + int ln=0; + int nameid,j; + char *str[32],*p; + struct item_data *id; + + if( (fp=fopen("db/item_noequip.txt","r"))==NULL ){ + printf("can't read db/item_noequip.txt\n"); + return -1; + } + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + memset(str,0,sizeof(str)); + for(j=0,p=line;j<2 && p;j++){ + str[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(str[0]==NULL) + continue; + + nameid=atoi(str[0]); + if(nameid<=0 || nameid>=20000 || !(id=itemdb_exists(nameid))) + continue; + + id->flag.no_equip=atoi(str[1]); + + ln++; + + } + fclose(fp); + printf("read db/item_noequip.txt done (count=%d)\n",ln); + return 0; +} +#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 + + // ---------- + + sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_db); + + // 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 | + +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+ + | id | name_english | name_japanese | type | price_buy | price_sell | weight | attack | defence | range | slots | equip_jobs | equip_genders | equip_locations | weapon_level | equip_level | view | script_use | script_equip | + +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+---------------+-----------------+--------------+-------------+------+------------+--------------+ */ + + nameid = atoi(sql_row[0]); + + // If the identifier is not within the valid range, process the next row + if (nameid == 0 || nameid >= 20000) + { + continue; + } + + // Insert a new row into the item database + + /*id = calloc(sizeof(struct item_data), 1); + + if (id == NULL) + { + printf("out of memory : itemdb_read_sqldb\n"); + exit(1); + } + + memset(id, 0, sizeof(struct item_data)); + numdb_insert(item_db, (int) nameid, id);*/ + + // ---------- + id=itemdb_search(nameid); + + memcpy(id->name, sql_row[1], 25); + memcpy(id->jname, sql_row[2], 25); + + id->type = atoi(sql_row[3]); + + // 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; + id->class = (sql_row[11] != NULL) ? atoi(sql_row[11]) : 0; + id->sex = (sql_row[12] != NULL) ? atoi(sql_row[12]) : 0; + id->equip = (sql_row[13] != NULL) ? atoi(sql_row[13]) : 0; + id->wlv = (sql_row[14] != NULL) ? atoi(sql_row[14]) : 0; + id->elv = (sql_row[15] != NULL) ? atoi(sql_row[15]) : 0; + id->look = (sql_row[16] != NULL) ? atoi(sql_row[16]) : 0; + + id->view_id = 0; + + // ---------- + + if (sql_row[17] != NULL) + { + if (sql_row[17][0] == '{') + id->use_script = parse_script(sql_row[17], 0); + else { + sprintf(script, "{%s}", sql_row[17]); + id->use_script = parse_script(script, 0); + } + } + else + { + id->use_script = NULL; + } + + if (sql_row[18] != NULL) + { + if (sql_row[18][0] == '{') + id->equip_script = parse_script(sql_row[18], 0); + else { + sprintf(script, "{%s}", sql_row[18]); + id->equip_script = parse_script(script, 0); + } + } + else + { + id->equip_script = NULL; + } + + // ---------- + + id->flag.available = 1; + id->flag.value_notdc = 0; + id->flag.value_notoc = 0; + } + + // If the retrieval failed, output an error + if (mysql_errno(&mmysql_handle)) + { + printf("Database server error (retrieving rows from %s): %s\n", item_db_db, mysql_error(&mmysql_handle)); + } + + printf("read %s done (count = %lu)\n", item_db_db, (unsigned long) mysql_num_rows(sql_res)); + } + else + { + printf("MySQL error (storing query result for %s): %s\n", item_db_db, mysql_error(&mmysql_handle)); + } + + // Free the query result + mysql_free_result(sql_res); + } + else + { + printf("Database server error (executing query for %s): %s\n", item_db_db, mysql_error(&mmysql_handle)); + } + + return 0; +} + +#endif /* not TXT_ONLY */ +/*========================================== + * + *------------------------------------------ + */ +static int itemdb_final(void *key,void *data,va_list ap) +{ + struct item_data *id; + + nullpo_retr(0, id=data); + + if(id->use_script) + free(id->use_script); + if(id->equip_script) + free(id->equip_script); + free(id); + + return 0; +} + +void itemdb_reload(void) +{ + /* + + <empty item databases> + itemdb_read(); + + */ + + do_init_itemdb(); +} + +/*========================================== + * + *------------------------------------------ + */ +void do_final_itemdb(void) +{ + if(item_db){ + numdb_final(item_db,itemdb_final); + item_db=NULL; + } +} + +/* +static FILE *dfp; +static int itemdebug(void *key,void *data,va_list ap){ +// struct item_data *id=(struct item_data *)data; + fprintf(dfp,"%6d",(int)key); + return 0; +} +void itemdebugtxt() +{ + dfp=fopen("itemdebug.txt","wt"); + numdb_foreach(item_db,itemdebug); + fclose(dfp); +} +*/ +#ifdef TXT_ONLY +/*==================================== + * Removed item_value_db, don't re-add + *------------------------------------ + */ +static void itemdb_read(void) +{ + itemdb_read_itemslottable(); + itemdb_readdb(); + itemdb_read_randomitem(); + itemdb_read_itemavail(); + itemdb_read_noequip(); + itemdb_read_cardillustnametable(); + if (!battle_config.item_name_override_grffile) + itemdb_read_itemnametable(); +} +#endif /* TXT_ONLY */ +/*========================================== + * + *------------------------------------------ + */ +int do_init_itemdb(void) +{ + item_db = numdb_init(); + + itemdb_read(); + + return 0; +} diff --git a/src/map/itemdb.h b/src/map/itemdb.h new file mode 100644 index 0000000..0edfad2 --- /dev/null +++ b/src/map/itemdb.h @@ -0,0 +1,84 @@ +// $Id: itemdb.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _ITEMDB_H_ +#define _ITEMDB_H_ + +#include "map.h" + +struct item_data { + int nameid; + char name[24],jname[24]; + char prefix[24],suffix[24]; + char cardillustname[64]; + int value_buy; + int value_sell; + int type; + int class; + int sex; + int equip; + int weight; + int atk; + int def; + int range; + int slot; + int look; + int elv; + int wlv; + int refine; + char *use_script; // 回復とかも全部この中でやろうかなと + char *equip_script; // 攻撃,防御の属性設定もこの中で可能かな? + struct { + unsigned available : 1; + unsigned value_notdc : 1; + unsigned value_notoc : 1; + unsigned no_equip : 3; + unsigned no_drop : 1; + unsigned no_use : 1; + } flag; + int view_id; +}; + +struct random_item_data { + int nameid; + int per; +}; + +struct item_data* itemdb_searchname(const char *name); +struct item_data* itemdb_search(int nameid); +struct item_data* itemdb_exists(int nameid); +#define itemdb_type(n) itemdb_search(n)->type +#define itemdb_atk(n) itemdb_search(n)->atk +#define itemdb_def(n) itemdb_search(n)->def +#define itemdb_look(n) itemdb_search(n)->look +#define itemdb_weight(n) itemdb_search(n)->weight +#define itemdb_equip(n) itemdb_search(n)->equip +#define itemdb_usescript(n) itemdb_search(n)->use_script +#define itemdb_equipscript(n) itemdb_search(n)->equip_script +#define itemdb_wlv(n) itemdb_search(n)->wlv +#define itemdb_range(n) itemdb_search(n)->range +#define itemdb_slot(n) itemdb_search(n)->slot +#define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available) +#define itemdb_viewid(n) (itemdb_search(n)->view_id) + +int itemdb_searchrandomid(int flags); + +#define itemdb_value_buy(n) itemdb_search(n)->value_buy +#define itemdb_value_sell(n) itemdb_search(n)->value_sell +#define itemdb_value_notdc(n) itemdb_search(n)->flag.value_notdc +#define itemdb_value_notoc(n) itemdb_search(n)->flag.value_notoc + +int itemdb_isequip(int); +int itemdb_isequip2(struct item_data *); +int itemdb_isequip3(int); +int itemdb_isdropable(int nameid); + +// itemdb_equipマクロとitemdb_equippointとの違いは +// 前者が鯖側dbで定義された値そのものを返すのに対し +// 後者はsessiondataを考慮した鞍側での装備可能場所 +// すべての組み合わせを返す + +void itemdb_reload(void); + +void do_final_itemdb(void); +int do_init_itemdb(void); + +#endif diff --git a/src/map/mail.c b/src/map/mail.c new file mode 100644 index 0000000..e50b1ba --- /dev/null +++ b/src/map/mail.c @@ -0,0 +1,324 @@ +// Mail System for eAthena SQL +// Created by Valaris + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "socket.h" +#include "timer.h" +#include "nullpo.h" + +#include "map.h" +#include "clif.h" +#include "chrif.h" +#include "intif.h" +#include "pc.h" +#include "mail.h" + +char mail_db[32] = "mail"; + +int MAIL_CHECK_TIME = 120000; +int mail_timer; + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +int mail_check(struct map_session_data *sd,int type) +{ + int i=0,new=0,priority=0; + char message[50]; + + if(sd==NULL) + return 0; + + sprintf(tmp_msql,"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_msql)) { + printf("Database server error (executing query for %s): %s\n", mail_db, mysql_error(&mail_handle)); + 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."); + mysql_free_result(mail_res); + return 0; + } + + while ((mail_row = mysql_fetch_row(mail_res))) { + i++; + + if(!atoi(mail_row[5])) { + sprintf(tmp_msql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_msql) ) { + printf("DB server Error (update Read `%s`)- %s\n", mail_db, mysql_error(&mail_handle) ); + } + } + + 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]); + clif_displaymessage(sd->fd, message); + } + + else { + sprintf(message, "%d - From : %s (New)", i, mail_row[2]); + clif_displaymessage(sd->fd, message); + } + } + } + + else if(type==2){ + sprintf(message, "%d - From : %s", i, mail_row[2]); + clif_displaymessage(sd->fd, message); + } + + } + + mysql_free_result(mail_res); + + } else { + printf("MySQL error (storing query result for %s): %s\n", mail_db, mysql_error(&mail_handle)); + return 0; + } + + if(i>0 && new>0 && type==1) { + sprintf(message, "You have %d new messages.", new); + clif_displaymessage(sd->fd, message); + } + if(i>0 && new>0 && priority>0 && type==1) { + sprintf(message, "You have %d unread priority messages.", priority); + clif_displaymessage(sd->fd, message); + } + if(!new) { + clif_displaymessage(sd->fd, "You have no new messages."); + } + + return 0; +} + +int mail_read(struct map_session_data *sd, int message_id) +{ + + char message[80]; + + if(sd==NULL) + return 0; + + sprintf(tmp_msql,"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_msql)) { + printf("Database server error (executing query for %s): %s\n", mail_db, mysql_error(&mail_handle)); + 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."); + return 0; + } + + if ((mail_row = mysql_fetch_row(mail_res))) { + + if(!atoi(mail_row[6])) { + sprintf(tmp_msql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_msql) ) { + printf("DB server Error (update Read `%s`)- %s\n", mail_db, mysql_error(&mail_handle) ); + } + } + + sprintf(message, "Reading message from %s", mail_row[2]); + clif_displaymessage(sd->fd, message); + + sprintf(message, "%s", mail_row[3]); + clif_displaymessage(sd->fd, message); + + sprintf(tmp_msql,"UPDATE `%s` SET `read_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_msql) ) { + printf("DB server Error (update Read `%s`)- %s\n", mail_db, mysql_error(&mail_handle) ); + } + } + + mysql_free_result(mail_res); + + } else { + printf("MySQL error (storing query result for %s): %s\n", mail_db, mysql_error(&mail_handle)); + return 0; + } + + return 0; +} + +int mail_delete(struct map_session_data *sd, int message_id) +{ + if(sd==NULL) + return 0; + + sprintf(tmp_msql,"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_msql)) { + printf("Database server error (executing query for %s): %s\n", mail_db, mysql_error(&mail_handle)); + 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."); + 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."); + 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."); + return 0; + } + sprintf(tmp_msql,"DELETE FROM `%s` WHERE `message_id` = \"%d\"", mail_db, atoi(mail_row[0])); + if(mysql_query(&mail_handle, tmp_msql) ) { + mysql_free_result(mail_res); + printf("DB server Error (update Read `%s`)- %s\n", mail_db, mysql_error(&mail_handle) ); + return 0; + } + else clif_displaymessage(sd->fd,"Message deleted."); + } + + mysql_free_result(mail_res); + + } else { + printf("MySQL error (delete query result for %s): %s\n", mail_db, mysql_error(&mail_handle)); + return 0; + } + + return 0; +} + +int mail_send(struct map_session_data *sd, char *name, char *message, int flag) +{ + if(sd==NULL) + return 0; + + if(pc_isGM(sd) < 80 && sd->mail_counter > 0) { + clif_displaymessage(sd->fd,"You must wait 10 minutes before sending another message"); + return 0; + } + + if(strcmp(name,"*")==0) { + if(pc_isGM(sd) < 80) { + clif_displaymessage(sd->fd, "Access Denied."); + return 0; + } + else + sprintf(tmp_msql,"SELECT DISTINCT `account_id` FROM `%s` WHERE `account_id` <> '%d' ORDER BY `account_id`", char_db, sd->status.account_id); + } + else + sprintf(tmp_msql,"SELECT `account_id`,`name` FROM `%s` WHERE `name` = \"%s\"", char_db, name); + + if (mysql_query(&mail_handle, tmp_msql)) { + printf("Database server error (executing query for %s): %s\n", char_db, mysql_error(&mail_handle)); + 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."); + return 0; + } + + while ((mail_row = mysql_fetch_row(mail_res))) { + if(strcmp(name,"*")==0) { + sprintf(tmp_msql, "INSERT 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, message, flag); + } + else { + sprintf(tmp_msql, "INSERT 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, message, flag); + if(pc_isGM(sd) < 80) + sd->mail_counter=5; + } + + if(mysql_query(&mail_handle, tmp_msql) ) { + mysql_free_result(mail_res); + printf("DB server Error (insert `mail_db`)- %s\n", mysql_error(&mail_handle) ); + return 0; + } + + } + } + + clif_displaymessage(sd->fd,"Mail has been sent."); + + return 0; +} + +int mail_check_timer(int tid,unsigned int tick,int id,int data) +{ + if(mail_timer != tid) + return 0; + + sprintf(tmp_msql,"SELECT DISTINCT `to_account_id` FROM `%s` WHERE `read_flag` = '0' AND `check_flag` = '0'", mail_db); + + if (mysql_query(&mail_handle, tmp_msql)) { + printf("Database server error (executing query for %s): %s\n", char_db, mysql_error(&mail_handle)); + mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0); + return 0; + } + + struct map_session_data *sd = NULL; + int i; + + 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 = 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."); + } + } + } + } + + sprintf(tmp_msql,"UPDATE `%s` SET `check_flag`='1' WHERE `check_flag`= '0' ", mail_db); + if(mysql_query(&mail_handle, tmp_msql) ) { + printf("DB server Error (update Read `%s`)- %s\n", mail_db, mysql_error(&mail_handle) ); + } + + 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; +} + diff --git a/src/map/mail.h b/src/map/mail.h new file mode 100644 index 0000000..6bd8e51 --- /dev/null +++ b/src/map/mail.h @@ -0,0 +1,9 @@ +// 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 0000000..040e180 --- /dev/null +++ b/src/map/map.c @@ -0,0 +1,2009 @@ +// $Id: map.c,v 1.6 2004/09/25 17:37:01 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#ifdef LCCWIN32 +#include <winsock.h> +#else +#include <netdb.h> +#endif + +#include "core.h" +#include "timer.h" +#include "db.h" +#include "grfio.h" +#include "malloc.h" + +#include "map.h" +#include "chrif.h" +#include "clif.h" +#include "intif.h" +#include "npc.h" +#include "pc.h" +#include "mob.h" +#include "chat.h" +#include "itemdb.h" +#include "storage.h" +#include "skill.h" +#include "trade.h" +#include "party.h" +#include "battle.h" +#include "script.h" +#include "guild.h" +#include "pet.h" +#include "atcommand.h" +#include "nullpo.h" +#include "socket.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#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 ; +char tmp_lsql[65535]=""; + +MYSQL mail_handle; // mail system [Valaris] +MYSQL_RES* mail_res ; +MYSQL_ROW mail_row ; +char tmp_msql[65535]=""; + +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"; +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 mob_db_db[32] = "mob_db"; +char login_db[32] = "login"; +char login_db_level[32] = "level"; +char login_db_account_id[32] = "account_id"; + +int lowest_gm_level = 1; +int read_gm_interval = 600000; + +char char_db[32] = "char"; + +static int online_timer(int,unsigned int,int,int); + +int CHECK_INTERVAL = 3600000; // [Valaris] +int check_online_timer=0; // [Valaris] + +#endif /* not TXT_ONLY */ +// 極力 staticでローカルに収める +static struct dbt * id_db=NULL; +static struct dbt * map_db=NULL; +static struct dbt * nick_db=NULL; +static struct dbt * charid_db=NULL; + +static int users=0; +static struct block_list *object[MAX_FLOORITEM]; +static int first_free_object_id=0,last_object_id=0; + +#define block_free_max 1048576 +static void *block_free[block_free_max]; +static int block_free_count = 0, block_free_lock = 0; + +#define BL_LIST_MAX 1048576 +static struct block_list *bl_list[BL_LIST_MAX]; +static int bl_list_count = 0; + +struct map_data map[MAX_MAP_PER_SERVER]; +int map_num = 0; + +int map_port=0; + +int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; +int agit_flag = 0; +int night_flag = 0; // 0=day, 1=night [Yor] + +struct charid2nick { + char nick[24]; + int req_id; +}; + +char motd_txt[256] = "conf/motd.txt"; +char help_txt[256] = "conf/help.txt"; + +char wisp_server_name[24] = "Server"; // can be modified in char-server configuration file + + +/*========================================== + * 全map鯖総計での接続数設定 + * (char鯖から送られてくる) + *------------------------------------------ + */ +void map_setusers(int n) { + users = n; +} + +/*========================================== + * 全map鯖総計での接続数取得 (/wへの応答用) + *------------------------------------------ + */ +int map_getusers(void) { + return users; +} + +// +// block削除の安全性確保処理 +// + +/*========================================== + * blockをfreeするときfreeの変わりに呼ぶ + * ロックされているときはバッファにためる + *------------------------------------------ + */ +int map_freeblock( void *bl ) +{ + if(block_free_lock==0){ + free(bl); + bl = NULL; + } + else{ + if( block_free_count>=block_free_max ) { + if(battle_config.error_log) + printf("map_freeblock: *WARNING* too many free block! %d %d\n", + block_free_count,block_free_lock); + } + else + block_free[block_free_count++]=bl; + } + return block_free_lock; +} +/*========================================== + * blockのfreeを一時的に禁止する + *------------------------------------------ + */ +int map_freeblock_lock(void) { + return ++block_free_lock; +} + +/*========================================== + * blockのfreeのロックを解除する + * このとき、ロックが完全になくなると + * バッファにたまっていたblockを全部削除 + *------------------------------------------ + */ +int map_freeblock_unlock(void) { + if ((--block_free_lock) == 0) { + int i; +// if(block_free_count>0) { +// if(battle_config.error_log) +// printf("map_freeblock_unlock: free %d object\n",block_free_count); +// } + for(i=0;i<block_free_count;i++){ + free(block_free[i]); + block_free[i] = NULL; + } + block_free_count=0; + }else if(block_free_lock<0){ + if(battle_config.error_log) + printf("map_freeblock_unlock: lock count < 0 !\n"); + } + return block_free_lock; +} + + +// +// block化処理 +// +/*========================================== + * map[]のblock_listから繋がっている場合に + * bl->prevにbl_headのアドレスを入れておく + *------------------------------------------ + */ +static struct block_list bl_head; + +/*========================================== + * map[]のblock_listに追加 + * mobは数が多いので別リスト + * + * 既にlink済みかの確認が無い。危険かも + *------------------------------------------ + */ +int map_addblock(struct block_list *bl) +{ + int m,x,y; + + nullpo_retr(0, bl); + + if(bl->prev != NULL){ + if(battle_config.error_log) + printf("map_addblock error : bl->prev!=NULL\n"); + return 0; + } + + m=bl->m; + x=bl->x; + y=bl->y; + if(m<0 || m>=map_num || + x<0 || x>=map[m].xs || + y<0 || y>=map[m].ys) + return 1; + + if(bl->type==BL_MOB){ + bl->next = map[m].block_mob[x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs]; + bl->prev = &bl_head; + if(bl->next) bl->next->prev = bl; + map[m].block_mob[x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs] = bl; + map[m].block_mob_count[x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs]++; + } else { + bl->next = map[m].block[x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs]; + bl->prev = &bl_head; + if(bl->next) bl->next->prev = bl; + map[m].block[x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs] = bl; + map[m].block_count[x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs]++; + if(bl->type==BL_PC) + map[m].users++; + } + + return 0; +} + +/*========================================== + * map[]のblock_listから外す + * prevがNULLの場合listに繋がってない + *------------------------------------------ + */ +int map_delblock(struct block_list *bl) +{ + int b; + nullpo_retr(0, bl); + + // 既にblocklistから抜けている + if(bl->prev==NULL){ + if(bl->next!=NULL){ + // prevがNULLでnextがNULLでないのは有ってはならない + if(battle_config.error_log) + printf("map_delblock error : bl->next!=NULL\n"); + } + return 0; + } + + b = bl->x/BLOCK_SIZE+(bl->y/BLOCK_SIZE)*map[bl->m].bxs; + + if(bl->type==BL_PC) + map[bl->m].users--; + if(bl->next) bl->next->prev = bl->prev; + if(bl->prev==&bl_head){ + // リストの頭なので、map[]のblock_listを更新する + if(bl->type==BL_MOB){ + map[bl->m].block_mob[b] = bl->next; + if((map[bl->m].block_mob_count[b]--) < 0) + map[bl->m].block_mob_count[b] = 0; + } else { + map[bl->m].block[b] = bl->next; + if((map[bl->m].block_count[b]--) < 0) + map[bl->m].block_count[b] = 0; + } + } else { + bl->prev->next = bl->next; + } + bl->next = NULL; + bl->prev = NULL; + + return 0; +} + +/*========================================== + * 周囲のPC人数を数える (現在未使用) + *------------------------------------------ + */ +int map_countnearpc(int m, int x, int y) { + int bx,by,c=0; + struct block_list *bl=NULL; + + if(map[m].users==0) + return 0; + for(by=y/BLOCK_SIZE-AREA_SIZE/BLOCK_SIZE-1;by<=y/BLOCK_SIZE+AREA_SIZE/BLOCK_SIZE+1;by++){ + if(by<0 || by>=map[m].bys) + continue; + for(bx=x/BLOCK_SIZE-AREA_SIZE/BLOCK_SIZE-1;bx<=x/BLOCK_SIZE+AREA_SIZE/BLOCK_SIZE+1;bx++){ + if(bx<0 || bx>=map[m].bxs) + continue; + bl = map[m].block[bx+by*map[m].bxs]; + for(;bl;bl=bl->next){ + if(bl->type==BL_PC) + c++; + } + } + } + return c; +} + +/*========================================== + * セル上のPCとMOBの数を数える (グランドクロス用) + *------------------------------------------ + */ +int map_count_oncell(int m, int x, int y) { + int bx,by; + struct block_list *bl=NULL; + int i,c; + int count = 0; + + if (x < 0 || y < 0 || (x >= map[m].xs) || (y >= map[m].ys)) + return 1; + bx = x/BLOCK_SIZE; + by = y/BLOCK_SIZE; + + bl = map[m].block[bx+by*map[m].bxs]; + c = map[m].block_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl->x == x && bl->y == y && bl->type == BL_PC) count++; + } + bl = map[m].block_mob[bx+by*map[m].bxs]; + c = map[m].block_mob_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl->x == x && bl->y == y) count++; + } + if(!count) count = 1; + return count; +} + + +/*========================================== + * map m (x0,y0)-(x1,y1)内の全objに対して + * funcを呼ぶ + * type!=0 ならその種類のみ + *------------------------------------------ + */ +void map_foreachinarea(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int type,...) { + int bx,by; + struct block_list *bl=NULL; + va_list ap=NULL; + int blockcount=bl_list_count,i,c; + + if(m < 0) + return; + va_start(ap,type); + if (x0 < 0) x0 = 0; + if (y0 < 0) y0 = 0; + if (x1 >= map[m].xs) x1 = map[m].xs-1; + if (y1 >= map[m].ys) y1 = map[m].ys-1; + if (type == 0 || type != BL_MOB) + for (by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) { + for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ + bl = map[m].block[bx+by*map[m].bxs]; + c = map[m].block_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl && type && bl->type!=type) + continue; + if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + } + } + if(type==0 || type==BL_MOB) + for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){ + for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ + bl = map[m].block_mob[bx+by*map[m].bxs]; + c = map[m].block_mob_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + } + } + + if(bl_list_count>=BL_LIST_MAX) { + if(battle_config.error_log) + printf("map_foreachinarea: *WARNING* block count too many!\n"); + } + + map_freeblock_lock(); // メモリからの解放を禁止する + + for(i=blockcount;i<bl_list_count;i++) + if(bl_list[i]->prev) // 有効かどうかチェック + func(bl_list[i],ap); + + map_freeblock_unlock(); // 解放を許可する + + va_end(ap); + bl_list_count = blockcount; +} + +/*========================================== + * 矩形(x0,y0)-(x1,y1)が(dx,dy)移動した時の + * 領域外になる領域(矩形かL字形)内のobjに + * 対してfuncを呼ぶ + * + * dx,dyは-1,0,1のみとする(どんな値でもいいっぽい?) + *------------------------------------------ + */ +void map_foreachinmovearea(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int dx,int dy,int type,...) { + int bx,by; + struct block_list *bl=NULL; + va_list ap=NULL; + int blockcount=bl_list_count,i,c; + + va_start(ap,type); + if(dx==0 || dy==0){ + // 矩形領域の場合 + if(dx==0){ + if(dy<0){ + y0=y1+dy+1; + } else { + y1=y0+dy-1; + } + } else if(dy==0){ + if(dx<0){ + x0=x1+dx+1; + } else { + x1=x0+dx-1; + } + } + if(x0<0) x0=0; + if(y0<0) y0=0; + if(x1>=map[m].xs) x1=map[m].xs-1; + if(y1>=map[m].ys) y1=map[m].ys-1; + for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){ + for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ + bl = map[m].block[bx+by*map[m].bxs]; + c = map[m].block_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl && type && bl->type!=type) + continue; + if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + bl = map[m].block_mob[bx+by*map[m].bxs]; + c = map[m].block_mob_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl && type && bl->type!=type) + continue; + if(bl && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + } + } + }else{ + // L字領域の場合 + + if(x0<0) x0=0; + if(y0<0) y0=0; + if(x1>=map[m].xs) x1=map[m].xs-1; + if(y1>=map[m].ys) y1=map[m].ys-1; + for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){ + for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ + bl = map[m].block[bx+by*map[m].bxs]; + c = map[m].block_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl && type && bl->type!=type) + continue; + if((bl) && !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1)) + continue; + if((bl) && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) || + (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) && + bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + bl = map[m].block_mob[bx+by*map[m].bxs]; + c = map[m].block_mob_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next){ + if(bl && type && bl->type!=type) + continue; + if((bl) && !(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1)) + continue; + if((bl) && ((dx>0 && bl->x<x0+dx) || (dx<0 && bl->x>x1+dx) || + (dy>0 && bl->y<y0+dy) || (dy<0 && bl->y>y1+dy)) && + bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + } + } + + } + + if(bl_list_count>=BL_LIST_MAX) { + if(battle_config.error_log) + printf("map_foreachinarea: *WARNING* block count too many!\n"); + } + + map_freeblock_lock(); // メモリからの解放を禁止する + + for(i=blockcount;i<bl_list_count;i++) + if(bl_list[i]->prev) // 有効かどうかチェック + func(bl_list[i],ap); + + map_freeblock_unlock(); // 解放を許可する + + va_end(ap); + bl_list_count = blockcount; +} + +// -- moonsoul (added map_foreachincell which is a rework of map_foreachinarea but +// which only checks the exact single x/y passed to it rather than an +// area radius - may be more useful in some instances) +// +void map_foreachincell(int (*func)(struct block_list*,va_list),int m,int x,int y,int type,...) { + int bx,by; + struct block_list *bl=NULL; + va_list ap=NULL; + int blockcount=bl_list_count,i,c; + + va_start(ap,type); + + by=y/BLOCK_SIZE; + bx=x/BLOCK_SIZE; + + if(type==0 || type!=BL_MOB) + { + bl = map[m].block[bx+by*map[m].bxs]; + c = map[m].block_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next) + { + if(type && bl && bl->type!=type) + continue; + if(bl && bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + } + + if(type==0 || type==BL_MOB) + { + bl = map[m].block_mob[bx+by*map[m].bxs]; + c = map[m].block_mob_count[bx+by*map[m].bxs]; + for(i=0;i<c && bl;i++,bl=bl->next) + { + if(bl && bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX) + bl_list[bl_list_count++]=bl; + } + } + + if(bl_list_count>=BL_LIST_MAX) { + if(battle_config.error_log) + printf("map_foreachincell: *WARNING* block count too many!\n"); + } + + map_freeblock_lock(); // メモリからの解放を禁止する + + for(i=blockcount;i<bl_list_count;i++) + if(bl_list[i]->prev) // 有効かどうかチェック + func(bl_list[i],ap); + + map_freeblock_unlock(); // 解放を許可する + + va_end(ap); + bl_list_count = blockcount; +} + +/*========================================== + * 床アイテムやエフェクト用の一時obj割り当て + * object[]への保存とid_db登録まで + * + * bl->idもこの中で設定して問題無い? + *------------------------------------------ + */ +int map_addobject(struct block_list *bl) { + int i; + if( bl == NULL ){ + printf("map_addobject nullpo?\n"); + return 0; + } + if(first_free_object_id<2 || first_free_object_id>=MAX_FLOORITEM) + first_free_object_id=2; + for(i=first_free_object_id;i<MAX_FLOORITEM;i++) + if(object[i]==NULL) + break; + if(i>=MAX_FLOORITEM){ + if(battle_config.error_log) + printf("no free object id\n"); + return 0; + } + first_free_object_id=i; + if(last_object_id<i) + last_object_id=i; + object[i]=bl; + numdb_insert(id_db,i,bl); + return i; +} + +/*========================================== + * 一時objectの解放 + * map_delobjectのfreeしないバージョン + *------------------------------------------ + */ +int map_delobjectnofree(int id) { + if(object[id]==NULL) + return 0; + + map_delblock(object[id]); + numdb_erase(id_db,id); +// map_freeblock(object[id]); + object[id]=NULL; + + if(first_free_object_id>id) + first_free_object_id=id; + + while(last_object_id>2 && object[last_object_id]==NULL) + last_object_id--; + + return 0; +} + +/*========================================== + * 一時objectの解放 + * block_listからの削除、id_dbからの削除 + * object dataのfree、object[]へのNULL代入 + * + * addとの対称性が無いのが気になる + *------------------------------------------ + */ +int map_delobject(int id) { + struct block_list *obj = object[id]; + + if(obj==NULL) + return 0; + + map_delobjectnofree(id); + map_freeblock(obj); + + return 0; +} + +/*========================================== + * 全一時obj相手にfuncを呼ぶ + * + *------------------------------------------ + */ +void map_foreachobject(int (*func)(struct block_list*,va_list),int type,...) { + int i; + int blockcount=bl_list_count; + va_list ap=NULL; + + va_start(ap,type); + + for(i=2;i<=last_object_id;i++){ + if(object[i]){ + if(type && object[i]->type!=type) + continue; + if(bl_list_count>=BL_LIST_MAX) { + if(battle_config.error_log) + printf("map_foreachobject: too many block !\n"); + } + else + bl_list[bl_list_count++]=object[i]; + } + } + + map_freeblock_lock(); + + for(i=blockcount;i<bl_list_count;i++) + if( bl_list[i]->prev || bl_list[i]->next ) + func(bl_list[i],ap); + + map_freeblock_unlock(); + + va_end(ap); + bl_list_count = blockcount; +} + +/*========================================== + * 床アイテムを消す + * + * data==0の時はtimerで消えた時 + * data!=0の時は拾う等で消えた時として動作 + * + * 後者は、map_clearflooritem(id)へ + * map.h内で#defineしてある + *------------------------------------------ + */ +int map_clearflooritem_timer(int tid,unsigned int tick,int id,int data) { + struct flooritem_data *fitem=NULL; + + fitem = (struct flooritem_data *)object[id]; + if(fitem==NULL || fitem->bl.type!=BL_ITEM || (!data && fitem->cleartimer != tid)){ + if(battle_config.error_log) + printf("map_clearflooritem_timer : error\n"); + return 1; + } + if(data) + delete_timer(fitem->cleartimer,map_clearflooritem_timer); + else if(fitem->item_data.card[0] == (short)0xff00) + intif_delete_petdata(*((long *)(&fitem->item_data.card[1]))); + clif_clearflooritem(fitem,0); + map_delobject(fitem->bl.id); + + return 0; +} + +/*========================================== + * (m,x,y)の周囲rangeマス内の空き(=侵入可能)cellの + * 内から適当なマス目の座標をx+(y<<16)で返す + * + * 現状range=1でアイテムドロップ用途のみ + *------------------------------------------ + */ +int map_searchrandfreecell(int m,int x,int y,int range) { + int free_cell,i,j,c; + + for(free_cell=0,i=-range;i<=range;i++){ + if(i+y<0 || i+y>=map[m].ys) + continue; + for(j=-range;j<=range;j++){ + if(j+x<0 || j+x>=map[m].xs) + continue; + if((c=read_gat(m,j+x,i+y))==1 || c==5) + continue; + free_cell++; + } + } + if(free_cell==0) + return -1; + free_cell=rand()%free_cell; + for(i=-range;i<=range;i++){ + if(i+y<0 || i+y>=map[m].ys) + continue; + for(j=-range;j<=range;j++){ + if(j+x<0 || j+x>=map[m].xs) + continue; + if((c=read_gat(m,j+x,i+y))==1 || c==5) + continue; + if(free_cell==0){ + x+=j; + y+=i; + i=range+1; + break; + } + free_cell--; + } + } + + return x+(y<<16); +} + +/*========================================== + * (m,x,y)を中心に3x3以内に床アイテム設置 + * + * item_dataはamount以外をcopyする + *------------------------------------------ + */ +int map_addflooritem(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->first_get_id = 0; + fitem->first_get_tick = 0; + fitem->second_get_id = 0; + fitem->second_get_tick = 0; + fitem->third_get_id = 0; + fitem->third_get_tick = 0; + + fitem->bl.id = map_addobject(&fitem->bl); + if(fitem->bl.id==0){ + free(fitem); + return 0; + } + + tick = gettick(); + if(first_sd) { + fitem->first_get_id = first_sd->bl.id; + if(type) + fitem->first_get_tick = tick + battle_config.mvp_item_first_get_time; + else + fitem->first_get_tick = tick + battle_config.item_first_get_time; + } + if(second_sd) { + fitem->second_get_id = second_sd->bl.id; + if(type) + fitem->second_get_tick = tick + battle_config.mvp_item_first_get_time + battle_config.mvp_item_second_get_time; + else + fitem->second_get_tick = tick + battle_config.item_first_get_time + battle_config.item_second_get_time; + } + if(third_sd) { + fitem->third_get_id = third_sd->bl.id; + if(type) + fitem->third_get_tick = tick + battle_config.mvp_item_first_get_time + battle_config.mvp_item_second_get_time + battle_config.mvp_item_third_get_time; + else + fitem->third_get_tick = tick + battle_config.item_first_get_time + battle_config.item_second_get_time + battle_config.item_third_get_time; + } + + memcpy(&fitem->item_data,item_data,sizeof(*item_data)); + fitem->item_data.amount=amount; + fitem->subx=(r&3)*3+3; + fitem->suby=((r>>2)&3)*3+3; + fitem->cleartimer=add_timer(gettick()+battle_config.flooritem_lifetime,map_clearflooritem_timer,fitem->bl.id,0); + + map_addblock(&fitem->bl); + clif_dropflooritem(fitem); + + return fitem->bl.id; +} + +/*========================================== + * charid_dbへ追加(返信待ちがあれば返信) + *------------------------------------------ + */ +void map_addchariddb(int charid, char *name) { + struct charid2nick *p=NULL; + int req=0; + + p=numdb_search(charid_db,charid); + if(p==NULL){ // データベースにない + p = (struct charid2nick *)aCalloc(1,sizeof(struct charid2nick)); + p->req_id=0; + }else + numdb_erase(charid_db,charid); + + req=p->req_id; + memcpy(p->nick,name,24); + p->req_id=0; + numdb_insert(charid_db,charid,p); + if(req){ // 返信待ちがあれば返信 + struct map_session_data *sd = map_id2sd(req); + if(sd!=NULL) + clif_solved_charname(sd,charid); + } +} + +/*========================================== + * charid_dbへ追加(返信要求のみ) + *------------------------------------------ + */ +int map_reqchariddb(struct map_session_data * sd,int charid) { + struct charid2nick *p=NULL; + + nullpo_retr(0, sd); + + p=numdb_search(charid_db,charid); + if(p!=NULL) // データベースにすでにある + return 0; + p = (struct charid2nick *)aCalloc(1,sizeof(struct charid2nick)); + p->req_id=sd->bl.id; + numdb_insert(charid_db,charid,p); + return 0; +} + +/*========================================== + * id_dbへblを追加 + *------------------------------------------ + */ +void map_addiddb(struct block_list *bl) { + nullpo_retv(bl); + + numdb_insert(id_db,bl->id,bl); +} + +/*========================================== + * id_dbからblを削除 + *------------------------------------------ + */ +void map_deliddb(struct block_list *bl) { + nullpo_retv(bl); + + numdb_erase(id_db,bl->id); +} + +/*========================================== + * nick_dbへsdを追加 + *------------------------------------------ + */ +void map_addnickdb(struct map_session_data *sd) { + nullpo_retv(sd); + + strdb_insert(nick_db,sd->status.name,sd); +} + +/*========================================== + * PCのquit処理 map.c内分 + * + * quit処理の主体が違うような気もしてきた + *------------------------------------------ + */ +int map_quit(struct map_session_data *sd) { + int i; + + nullpo_retr(0, sd); + + if(sd->chatID) // チャットから出る + chat_leavechat(sd); + + if(sd->trade_partner) // 取引を中断する + trade_tradecancel(sd); + + if(sd->party_invite>0) // パーティ勧誘を拒否する + party_reply_invite(sd,sd->party_invite_account,0); + + if(sd->guild_invite>0) // ギルド勧誘を拒否する + guild_reply_invite(sd,sd->guild_invite,0); + if(sd->guild_alliance>0) // ギルド同盟勧誘を拒否する + guild_reply_reqalliance(sd,sd->guild_alliance_account,0); + + party_send_logout(sd); // パーティのログアウトメッセージ送信 + + guild_send_memberinfoshort(sd,0); // ギルドのログアウトメッセージ送信 + + pc_cleareventtimer(sd); // イベントタイマを破棄する + + if(sd->state.storage_flag) + storage_guild_storage_quit(sd,0); + else + storage_storage_quit(sd); // 倉庫を開いてるなら保存する + + skill_castcancel(&sd->bl,0); // 詠唱を中断する + skill_stop_dancing(&sd->bl,1);// ダンス/演奏中断 + + if(sd->sc_data && sd->sc_data[SC_BERSERK].timer!=-1) //バーサーク中の終了はHPを100に + sd->status.hp = 100; + + skill_status_change_clear(&sd->bl,1); // ステータス異常を解除する + skill_clear_unitgroup(&sd->bl); // スキルユニットグループの削除 + skill_cleartimerskill(&sd->bl); + pc_stop_walking(sd,0); + pc_stopattack(sd); + pc_delinvincibletimer(sd); + pc_delspiritball(sd,sd->spiritball,1); + skill_gangsterparadise(sd,0); + + pc_calcstatus(sd,4); + + clif_clearchar_area(&sd->bl,2); + + if(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_makesavestatus(sd); + //クローンスキルで覚えたスキルは消す + 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; + } + } + chrif_save(sd); + storage_storage_save(sd); + + if( sd->npc_stackbuf && sd->npc_stackbuf != NULL) + free( sd->npc_stackbuf ); + + map_delblock(&sd->bl); + +#ifndef TXT_ONLY + chrif_char_offline(sd); +#endif + + numdb_erase(id_db,sd->bl.id); + strdb_erase(nick_db,sd->status.name); + numdb_erase(charid_db,sd->status.char_id); + + return 0; +} + +/*========================================== + * id番号のPCを探す。居なければNULL + *------------------------------------------ + */ +struct map_session_data * map_id2sd(int id) { +// remove search from db, because: +// 1 - all players, npc, items and mob are in this db (to search, it's not speed, and search in session is more sure) +// 2 - DB seems not always correct. Sometimes, when a player disconnects, its id (account value) is not removed and structure +// point to a memory area that is not more a session_data and value are incorrect (or out of available memory) -> crash +// replaced by searching in all session. +// by searching in session, we are sure that fd, session, and account exist. +/* + struct block_list *bl; + + bl=numdb_search(id_db,id); + if(bl && bl->type==BL_PC) + return (struct map_session_data*)bl; + return NULL; +*/ + int i; + struct map_session_data *sd=NULL; + + for(i = 0; i < fd_max; i++) + if (session[i] && (sd = session[i]->session_data) && sd->bl.id == id) + return sd; + + return NULL; +} + +/*========================================== + * char_id番号の名前を探す + *------------------------------------------ + */ +char * map_charid2nick(int id) { + struct charid2nick *p=numdb_search(charid_db,id); + + if(p==NULL) + return NULL; + if(p->req_id!=0) + return NULL; + return p->nick; +} + + +/*========================================== + * Search session data from a nick name + * (without sensitive case if necessary) + * return map_session_data pointer or NULL + *------------------------------------------ + */ +struct map_session_data * map_nick2sd(char *nick) { + int i, quantity=0, nicklen; + struct map_session_data *sd = NULL; + struct map_session_data *pl_sd = NULL; + + if (nick == NULL) + return NULL; + + nicklen = strlen(nick); + + for (i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) + // 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番号の物を探す + * 一時objectの場合は配列を引くのみ + *------------------------------------------ + */ +struct block_list * map_id2bl(int id) +{ + struct block_list *bl=NULL; + if(id<sizeof(object)/sizeof(object[0])) + bl = object[id]; + else + bl = numdb_search(id_db,id); + + return bl; +} + +/*========================================== + * id_db内の全てにfuncを実行 + *------------------------------------------ + */ +int map_foreachiddb(int (*func)(void*,void*,va_list),...) { + va_list ap=NULL; + + va_start(ap,func); + numdb_foreach(id_db,func,ap); + va_end(ap); + return 0; +} + +/*========================================== + * map.npcへ追加 (warp等の領域持ちのみ) + *------------------------------------------ + */ +int map_addnpc(int m,struct npc_data *nd) { + int i; + if(m<0 || m>=map_num) + return -1; + for(i=0;i<map[m].npc_num && i<MAX_NPC_PER_MAP;i++) + if(map[m].npc[i]==NULL) + break; + if(i==MAX_NPC_PER_MAP){ + if(battle_config.error_log) + printf("too many NPCs in one map %s\n",map[m].name); + return -1; + } + if(i==map[m].npc_num){ + map[m].npc_num++; + } + + nullpo_retr(0, nd); + + map[m].npc[i]=nd; + nd->n = i; + numdb_insert(id_db,nd->bl.id,nd); + + return i; +} + +void map_removenpc(void) { + int i,m,n=0; + + for(m=0;m<map_num;m++) { + for(i=0;i<map[m].npc_num && i<MAX_NPC_PER_MAP;i++) { + if(map[m].npc[i]!=NULL) { + clif_clearchar_area(&map[m].npc[i]->bl,2); + map_delblock(&map[m].npc[i]->bl); + numdb_erase(id_db,map[m].npc[i]->bl.id); + if(map[m].npc[i]->bl.subtype==SCRIPT) { +// free(map[m].npc[i]->u.scr.script); +// free(map[m].npc[i]->u.scr.label_list); + } + free(map[m].npc[i]); + map[m].npc[i] = NULL; + n++; + } + } + } + printf("%d NPCs removed.\n",n); +} + +/*========================================== + * map名からmap番号へ変換 + *------------------------------------------ + */ +int map_mapname2mapid(char *name) { + struct map_data *md=NULL; + + md=strdb_search(map_db,name); + if(md==NULL || md->gat==NULL) + return -1; + return md->m; +} + +/*========================================== + * 他鯖map名からip,port変換 + *------------------------------------------ + */ +int map_mapname2ipport(char *name,int *ip,int *port) { + struct map_data_other_server *mdos=NULL; + + mdos=strdb_search(map_db,name); + if(mdos==NULL || mdos->gat) + return -1; + *ip=mdos->ip; + *port=mdos->port; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int map_check_dir(int s_dir,int t_dir) { + if(s_dir == t_dir) + return 0; + switch(s_dir) { + case 0: + if(t_dir == 7 || t_dir == 1 || t_dir == 0) + return 0; + break; + case 1: + if(t_dir == 0 || t_dir == 2 || t_dir == 1) + return 0; + break; + case 2: + if(t_dir == 1 || t_dir == 3 || t_dir == 2) + return 0; + break; + case 3: + if(t_dir == 2 || t_dir == 4 || t_dir == 3) + return 0; + break; + case 4: + if(t_dir == 3 || t_dir == 5 || t_dir == 4) + return 0; + break; + case 5: + if(t_dir == 4 || t_dir == 6 || t_dir == 5) + return 0; + break; + case 6: + if(t_dir == 5 || t_dir == 7 || t_dir == 6) + return 0; + break; + case 7: + if(t_dir == 6 || t_dir == 0 || t_dir == 7) + return 0; + break; + } + return 1; +} + +/*========================================== + * 彼我の方向を計算 + *------------------------------------------ + */ +int map_calc_dir( struct block_list *src,int x,int y) { + int dir=0; + int dx,dy; + + nullpo_retr(0, src); + + dx=x-src->x; + dy=y-src->y; + if( dx==0 && dy==0 ){ // 彼我の場所一致 + dir=0; // 上 + }else if( dx>=0 && dy>=0 ){ // 方向的に右上 + dir=7; // 右上 + if( dx*3-1<dy ) dir=0; // 上 + if( dx>dy*3 ) dir=6; // 右 + }else if( dx>=0 && dy<=0 ){ // 方向的に右下 + dir=5; // 右下 + if( dx*3-1<-dy ) dir=4; // 下 + if( dx>-dy*3 ) dir=6; // 右 + }else if( dx<=0 && dy<=0 ){ // 方向的に左下 + dir=3; // 左下 + if( dx*3+1>dy ) dir=4; // 下 + if( dx<dy*3 ) dir=2; // 左 + }else{ // 方向的に左上 + dir=1; // 左上 + if( -dx*3-1<dy ) dir=0; // 上 + if( -dx>dy*3 ) dir=2; // 左 + } + return dir; +} + +// gat系 +/*========================================== + * (m,x,y)の状態を調べる + *------------------------------------------ + */ +int map_getcell(int m,int x,int y) { + if(x<0 || x>=map[m].xs-1 || y<0 || y>=map[m].ys-1) + return 1; + return map[m].gat[x+y*map[m].xs]; +} + +/*========================================== + * (m,x,y)の状態をtにする + *------------------------------------------ + */ +int map_setcell(int m,int x,int y,int t) { + if(x<0 || x>=map[m].xs || y<0 || y>=map[m].ys) + return t; + return map[m].gat[x+y*map[m].xs]=t; +} + +/*========================================== + * 他鯖管理のマップをdbに追加 + *------------------------------------------ + */ +int map_setipport(char *name,unsigned long ip,int port) { + struct map_data *md=NULL; + struct map_data_other_server *mdos=NULL; + + md=strdb_search(map_db,name); + if(md==NULL){ // not exist -> add new data + mdos=(struct map_data_other_server *)aCalloc(1,sizeof(struct map_data_other_server)); + memcpy(mdos->name,name,24); + mdos->gat = NULL; + mdos->ip = ip; + mdos->port = port; + strdb_insert(map_db,mdos->name,mdos); + } else { + if(md->gat){ // local -> check data + if(ip!=clif_getip() || port!=clif_getport()){ + printf("from char server : %s -> %08lx:%d\n",name,ip,port); + return 1; + } + } else { // update + mdos=(struct map_data_other_server *)md; + mdos->ip = ip; + mdos->port = port; + } + } + return 0; +} + +// 初期化周り +/*========================================== + * 水場高さ設定 + *------------------------------------------ + */ +static struct { + char mapname[24]; + int waterheight; +} *waterlist=NULL; + +#define NO_WATER 1000000 + +static int map_waterheight(char *mapname) { + if(waterlist){ + int i; + for(i=0;waterlist[i].mapname[0] && i < MAX_MAP_PER_SERVER;i++) + if(strcmp(waterlist[i].mapname,mapname)==0) + return waterlist[i].waterheight; + } + return NO_WATER; +} + +static void map_readwater(char *watertxt) { + char line[1024],w1[1024]; + FILE *fp=NULL; + int n=0; + + fp=fopen(watertxt,"r"); + if(fp==NULL){ + printf("file not found: %s\n",watertxt); + return; + } + if(waterlist==NULL) + waterlist=aCalloc(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; + } + strcpy(waterlist[n].mapname,w1); + if(count >= 2) + waterlist[n].waterheight = wh; + else + waterlist[n].waterheight = 3; + n++; + } + fclose(fp); +} + +/*========================================== + * マップ1枚読み込み + *------------------------------------------ + */ +static int map_readmap(int m,char *fn, char *alias) { + unsigned char *gat = ""; + int s; + int x,y,xs,ys; + struct gat_1cell {char type;} *p; + int wh; + size_t size; + + // read & convert fn + gat=grfio_read(fn); + if(gat==NULL) + return -1; + + printf("\rLoading Maps [%d/%d]: %-50s ",m,map_num,fn); + fflush(stdout); + + map[m].m=m; + xs=map[m].xs=*(short*)(gat); + ys=map[m].ys=*(short*)(gat+2); + printf("\n%i %i\n", xs, ys); + map[m].gat = calloc(s = map[m].xs * map[m].ys, 1); + if(map[m].gat==NULL){ + printf("out of memory : map_readmap gat\n"); + exit(1); + } + + map[m].npc_num=0; + map[m].users=0; + memset(&map[m].flag,0,sizeof(map[m].flag)); + if(battle_config.pk_mode) map[m].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris] + wh=map_waterheight(map[m].name); + for(y=0;y<ys;y++){ + p=(struct gat_1cell*)(gat+y*xs+4); + for(x=0;x<xs;x++){ + /*if(wh!=NO_WATER && p->type==0){ + // 水場判定 + map[m].gat[x+y*xs]=(p->high[0]>wh || p->high[1]>wh || p->high[2]>wh || p->high[3]>wh) ? 3 : 0; + } else {*/ + map[m].gat[x+y*xs]=p->type; + //} + p++; + } + } + free(gat); + + map[m].bxs=(xs+BLOCK_SIZE-1)/BLOCK_SIZE; + map[m].bys=(ys+BLOCK_SIZE-1)/BLOCK_SIZE; + size = map[m].bxs * map[m].bys * sizeof(struct block_list*); + + map[m].block = calloc(size, 1); + if(map[m].block == NULL){ + printf("out of memory : map_readmap block\n"); + exit(1); + } + + map[m].block_mob = calloc(size, 1); + if (map[m].block_mob == NULL) { + printf("out of memory : map_readmap block_mob\n"); + exit(1); + } + + size = map[m].bxs*map[m].bys*sizeof(int); + + map[m].block_count = calloc(size, 1); + if(map[m].block_count==NULL){ + printf("out of memory : map_readmap block\n"); + exit(1); + } + memset(map[m].block_count,0,size); + + map[m].block_mob_count=calloc(size, 1); + if(map[m].block_mob_count==NULL){ + printf("out of memory : map_readmap block_mob\n"); + exit(1); + } + memset(map[m].block_mob_count,0,size); + + strdb_insert(map_db,map[m].name,&map[m]); + +// printf("%s read done\n",fn); + + return 0; +} + +/*========================================== + * 全てのmapデータを読み込む + *------------------------------------------ + */ +int map_readallmap(void) { + int i,maps_removed=0; + char fn[256]=""; + + // 先に全部のャbプの存在を確認 + for(i=0;i<map_num;i++){ + if(strstr(map[i].name,".gat")==NULL) + continue; + sprintf(fn,"data\\%s",map[i].name); + if(grfio_size(fn) == -1) { + map_delmap(map[i].name); + maps_removed++; + } + } + for(i=0;i<map_num;i++){ + if(strstr(map[i].name,".gat")!=NULL) { + char *p = strstr(map[i].name, ">"); // [MouseJstr] + if (p != NULL) { + char alias[64]; + *p = '\0'; + strcpy(alias, map[i].name); + strcpy(map[i].name, p + 1); + sprintf(fn,"data\\%s",map[i].name); + if(map_readmap(i,fn, alias) == -1) { + map_delmap(map[i].name); + maps_removed++; + } + } else { + sprintf(fn,"data\\%s",map[i].name); + if(map_readmap(i,fn, NULL) == -1) { + map_delmap(map[i].name); + maps_removed++; + } + } + } + } + + free(waterlist); + printf("\rMaps Loaded: %d %60s\n",map_num,""); + printf("\rMaps Removed: %d \n",maps_removed); + return 0; +} + +/*========================================== + * 読み込むmapを追加する + *------------------------------------------ + */ +int map_addmap(char *mapname) { + if (strcmpi(mapname,"clear")==0) { + map_num=0; + return 0; + } + + if (map_num >= MAX_MAP_PER_SERVER - 1) { + printf("too many map\n"); + return 1; + } + memcpy(map[map_num].name, mapname, 24); + map_num++; + return 0; +} + +/*========================================== + * 読み込むmapを削除する + *------------------------------------------ + */ +int map_delmap(char *mapname) { + int i; + + if (strcmpi(mapname, "all") == 0) { + map_num = 0; + return 0; + } + + for(i = 0; i < map_num; i++) { + if (strcmp(map[i].name, mapname) == 0) { + printf("Removing map [ %s ] from maplist\n",map[i].name); + memmove(map+i, map+i+1, sizeof(map[0])*(map_num-i-1)); + map_num--; + } + } + return 0; +} + +/*========================================== + * 設定ファイルを読み込む + *------------------------------------------ + */ +int map_config_read(char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + struct hostent *h = NULL; + + fp = fopen(cfgName,"r"); + if (fp == NULL) { + printf("Map configuration file not found at: %s\n", cfgName); + exit(1); + } + while(fgets(line, sizeof(line) -1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { + if (strcmpi(w1, "userid")==0){ + chrif_setuserid(w2); + } else if (strcmpi(w1, "passwd") == 0) { + chrif_setpasswd(w2); + } else if (strcmpi(w1, "char_ip") == 0) { + h = gethostbyname (w2); + if(h != NULL) { + printf("Character server IP address : %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + sprintf(w2,"%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } + chrif_setip(w2); + } else if (strcmpi(w1, "char_port") == 0) { + chrif_setport(atoi(w2)); + } else if (strcmpi(w1, "map_ip") == 0) { + h = gethostbyname (w2); + if (h != NULL) { + printf("Map server IP address : %s -> %d.%d.%d.%d\n", w2, (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + sprintf(w2, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); + } + clif_setip(w2); + } else if (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, "data_grf") == 0) { + grfio_setdatafile(w2); + } else if (strcmpi(w1, "sdata_grf") == 0) { + grfio_setsdatafile(w2); + } else if (strcmpi(w1, "adata_grf") == 0) { + grfio_setadatafile(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, "mapreg_txt") == 0) { + strcpy(mapreg_txt, w2); + } else if (strcmpi(w1, "import") == 0) { + map_config_read(w2); + } + } + } + fclose(fp); + + return 0; +} + +#ifndef TXT_ONLY +/*======================================= + * MySQL Init + *--------------------------------------- + */ + +int map_sql_init(void){ + + mysql_init(&mmysql_handle); + + //DB connection start + printf("Connect 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 + printf("%s\n",mysql_error(&mmysql_handle)); + exit(1); + } + else { + printf ("connect success! (Map Server Connection)\n"); + } + + mysql_init(&lmysql_handle); + + //DB connection start + printf("Connect 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 + printf("%s\n",mysql_error(&lmysql_handle)); + exit(1); + } + else { + printf ("connect success! (Login Server Connection)\n"); + } + + if(battle_config.mail_system) { // mail system [Valaris] + mysql_init(&mail_handle); + if(!mysql_real_connect(&mail_handle, map_server_ip, map_server_id, map_server_pw, + map_server_db ,map_server_port, (char *)NULL, 0)) { + printf("%s\n",mysql_error(&mail_handle)); + exit(1); + } + } + + return 0; +} + +int map_sql_close(void){ + mysql_close(&mmysql_handle); + printf("Close Map DB Connection....\n"); + + mysql_close(&lmysql_handle); + printf("Close Login DB Connection....\n"); + return 0; +} + +int sql_config_read(char *cfgName) +{ + int i; + char line[1024],w1[1024],w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + if(fp==NULL){ + printf("file not found: %s\n",cfgName); + return 1; + } + while(fgets(line,1020,fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); + if(i!=2) + continue; + 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,"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); + //Map Server SQL DB + } else if(strcmpi(w1,"map_server_ip")==0){ + strcpy(map_server_ip, w2); + printf ("set map_server_ip : %s\n",w2); + } else if(strcmpi(w1,"map_server_port")==0){ + map_server_port=atoi(w2); + printf ("set map_server_port : %s\n",w2); + } else if(strcmpi(w1,"map_server_id")==0){ + strcpy(map_server_id, w2); + printf ("set map_server_id : %s\n",w2); + } else if(strcmpi(w1,"map_server_pw")==0){ + strcpy(map_server_pw, w2); + printf ("set map_server_pw : %s\n",w2); + } else if(strcmpi(w1,"map_server_db")==0){ + strcpy(map_server_db, w2); + printf ("set map_server_db : %s\n",w2); + //Map server option to use SQL db or not + } else if(strcmpi(w1,"use_sql_db")==0){ + if (strcmpi(w2,"yes")){db_use_sqldbs=0;} else if (strcmpi(w2,"no")){db_use_sqldbs=1;} + printf ("Using SQL dbs: %s\n",w2); + //Login Server SQL DB + } else if(strcmpi(w1,"login_server_ip")==0){ + strcpy(login_server_ip, w2); + printf ("set login_server_ip : %s\n",w2); + } else if(strcmpi(w1,"login_server_port")==0){ + login_server_port = atoi(w2); + printf ("set login_server_port : %s\n",w2); + } else if(strcmpi(w1,"login_server_id")==0){ + strcpy(login_server_id, w2); + printf ("set login_server_id : %s\n",w2); + } else if(strcmpi(w1,"login_server_pw")==0){ + strcpy(login_server_pw, w2); + printf ("set login_server_pw : %s\n",w2); + } else if(strcmpi(w1,"login_server_db")==0){ + strcpy(login_server_db, w2); + printf ("set login_server_db : %s\n",w2); + } else if(strcmpi(w1,"lowest_gm_level")==0){ + lowest_gm_level = atoi(w2); + printf ("set lowest_gm_level : %s\n",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 + printf ("set read_gm_interval : %s\n",w2); + } + } + fclose(fp); + + return 0; +} + +// sql online status checking [Valaris] +void char_offline(struct map_session_data *sd) +{ + if(sd && sd->status.char_id) { + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `char_id`='%d'", char_db, sd->status.char_id); + if(mysql_query(&mmysql_handle, tmp_sql) ) { + printf("DB server Error (update online `%s`)- %s\n", char_db, mysql_error(&mmysql_handle) ); + } + } +} + +void do_reset_online(void) +{ + sprintf(tmp_sql,"UPDATE `%s` SET `online`='0' WHERE `online`='1'", char_db); + if(mysql_query(&mmysql_handle, tmp_sql) ) { + printf("DB server Error (reset_online `%s`)- %s\n", char_db, mysql_error(&mmysql_handle) ); + } +} + +int online_timer(int tid,unsigned int tick,int id,int data) +{ + if(check_online_timer != tid) + return 0; + + char_online_check(); + + check_online_timer=add_timer(gettick()+CHECK_INTERVAL,online_timer,0,0); + + return 0; +} + +void char_online_check(void) +{ + int i; + struct map_session_data *sd=NULL; + + do_reset_online(); + + for(i=0;i<fd_max;i++){ + if (session[i] && (sd = session[i]->session_data) && sd && sd->state.auth && + !(battle_config.hide_GM_session && pc_isGM(sd))) + if(sd->status.char_id) { + sprintf(tmp_sql,"UPDATE `%s` SET `online`='1' WHERE `char_id`='%d'", char_db, sd->status.char_id); + if(mysql_query(&mmysql_handle, tmp_sql) ) { + printf("DB server Error (update online `%s`)- %s\n", char_db, mysql_error(&mmysql_handle) ); + } + } + } + + + if(check_online_timer && check_online_timer != -1) { + delete_timer(check_online_timer,online_timer); + add_timer(gettick()+CHECK_INTERVAL,online_timer,0,0); + } + +} + +#endif /* not TXT_ONLY */ + +int id_db_final(void *k,void *d,va_list ap){ return 0; } +int map_db_final(void *k,void *d,va_list ap){ return 0; } +int nick_db_final(void *k,void *d,va_list ap){ return 0; } +int charid_db_final(void *k,void *d,va_list ap){ return 0; } + +static int cleanup_sub(struct block_list *bl, va_list ap) { + nullpo_retr(0, bl); + + switch(bl->type) { + case BL_PC: + map_delblock(bl); // There is something better... + break; + case BL_NPC: + npc_delete((struct npc_data *)bl); + break; + case BL_MOB: + mob_delete((struct mob_data *)bl); + break; + case BL_PET: + pet_remove_map((struct map_session_data *)bl); + break; + case BL_ITEM: + map_clearflooritem(bl->id); + break; + case BL_SKILL: + skill_delunit((struct skill_unit *) bl); + break; + } + + return 0; +} + +/*========================================== + * map鯖終了時処理 + *------------------------------------------ + */ +void do_final(void) { + int map_id, i; + + for (map_id = 0; map_id < map_num;map_id++) { + if(map[map_id].m) + map_foreachinarea(cleanup_sub, map_id, 0, 0, map[map_id].xs, map[map_id].ys, 0, 0); + } + + for (i = 0; i < fd_max; i++) + delete_session(i); + + map_removenpc(); + timer_final(); + + numdb_final(id_db, id_db_final); + strdb_final(map_db, map_db_final); + strdb_final(nick_db, nick_db_final); + numdb_final(charid_db, charid_db_final); + + for(i=0;i<=map_num;i++){ + if(map[i].gat) free(map[i].gat); + if(map[i].block) free(map[i].block); + if(map[i].block_mob) free(map[i].block_mob); + if(map[i].block_count) free(map[i].block_count); + if(map[i].block_mob_count) free(map[i].block_mob_count); + } + do_final_script(); + do_final_itemdb(); + do_final_storage(); + do_final_guild(); +#ifndef TXT_ONLY + do_reset_online(); + map_sql_close(); +#endif /* not TXT_ONLY */ +} + +void map_helpscreen() { + exit(1); +} + +/*====================================================== + * Map-Server Init and Command-line Arguments [Valaris] + *------------------------------------------------------ + */ +int do_init(int argc, char *argv[]) { + int i; + +#ifndef TXT_ONLY + unsigned char *SQL_CONF_NAME="conf/inter_athena.conf"; +#endif + unsigned char *MAP_CONF_NAME = "conf/map_athena.conf"; + unsigned char *BATTLE_CONF_FILENAME = "conf/battle_athena.conf"; + unsigned char *ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf"; + unsigned char *SCRIPT_CONF_NAME = "conf/script_athena.conf"; + unsigned char *MSG_CONF_NAME = "conf/msg_athena.conf"; + unsigned char *GRF_PATH_FILENAME = "conf/grf-files.txt"; + + 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(); + else if (strcmp(argv[i], "--map_config") == 0) + MAP_CONF_NAME=argv[i+1]; + else if (strcmp(argv[i],"--battle_config") == 0) + BATTLE_CONF_FILENAME = argv[i+1]; + else if (strcmp(argv[i],"--atcommand_config") == 0) + ATCOMMAND_CONF_FILENAME = argv[i+1]; + else if (strcmp(argv[i],"--script_config") == 0) + SCRIPT_CONF_NAME = argv[i+1]; + else if (strcmp(argv[i],"--msg_config") == 0) + MSG_CONF_NAME = argv[i+1]; + else if (strcmp(argv[i],"--grf_path_file") == 0) + GRF_PATH_FILENAME = argv[i+1]; +#ifndef TXT_ONLY + else if (strcmp(argv[i],"--sql_config") == 0) + SQL_CONF_NAME = argv[i+1]; +#endif /* not TXT_ONLY */ + } + + map_config_read(MAP_CONF_NAME); + battle_config_read(BATTLE_CONF_FILENAME); + atcommand_config_read(ATCOMMAND_CONF_FILENAME); + script_config_read(SCRIPT_CONF_NAME); + msg_config_read(MSG_CONF_NAME); +#ifndef TXT_ONLY + sql_config_read(SQL_CONF_NAME); +#endif /* not TXT_ONLY */ + + atexit(do_final); + + id_db = numdb_init(); + map_db = strdb_init(16); + nick_db = strdb_init(24); + charid_db = numdb_init(); +#ifndef TXT_ONLY + map_sql_init(); +#endif /* not TXT_ONLY */ + + grfio_init(GRF_PATH_FILENAME); + + map_readallmap(); + + add_timer_func_list(map_clearflooritem_timer, "map_clearflooritem_timer"); + +#ifndef TXT_ONLY // online status timer, checks every hour [Valaris] + add_timer_func_list(online_timer, "online_timer"); + check_online_timer=add_timer(gettick()+CHECK_INTERVAL,online_timer,0,0); +#endif /* not TXT_ONLY */ + + do_init_chrif(); + do_init_clif(); + do_init_itemdb(); + do_init_mob(); // npcの初期化時内でmob_spawnして、mob_dbを参照するのでinit_npcより先 + do_init_script(); + do_init_npc(); + do_init_pc(); + do_init_party(); + do_init_guild(); + do_init_storage(); + do_init_skill(); + do_init_pet(); + +#ifndef TXT_ONLY /* mail system [Valaris] */ + if(battle_config.mail_system) + do_init_mail(); +#endif /* not TXT_ONLY */ + + npc_event_do_oninit(); // npcのOnInitイベント実行 + + if (battle_config.pk_mode == 1) + printf("The server is running in \033[1;31mPK Mode\033[0m.\n"); + + printf("The map-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", map_port); + + return 0; +} + diff --git a/src/map/map.h b/src/map/map.h new file mode 100644 index 0000000..703bbb6 --- /dev/null +++ b/src/map/map.h @@ -0,0 +1,705 @@ +// $Id: map.h,v 1.8 2004/09/25 11:39:17 MouseJstr Exp $ +#ifndef _MAP_H_ +#define _MAP_H_ + +#include <stdarg.h> +#include "mmo.h" + +#define MAX_PC_CLASS (1+6+6+1+6+1+1+1+1+4023) +#define PC_CLASS_BASE 0 +#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001) +#define PC_CLASS_BASE3 (PC_CLASS_BASE2 + 22) +#define MAX_NPC_PER_MAP 512 +#define BLOCK_SIZE 8 +#define AREA_SIZE battle_config.area_size +#define LOCAL_REG_NUM 16 +#define LIFETIME_FLOORITEM 60 +#define DAMAGELOG_SIZE 30 +#define LOOTITEM_SIZE 10 +#define MAX_SKILL_LEVEL 100 +#define MAX_STATUSCHANGE 200 +#define MAX_SKILLUNITGROUP 32 +#define MAX_MOBSKILLUNITGROUP 8 +#define MAX_SKILLUNITGROUPTICKSET 128 +#define MAX_SKILLTIMERSKILL 32 +#define MAX_MOBSKILLTIMERSKILL 10 +#define MAX_MOBSKILL 32 +#define MAX_EVENTQUEUE 2 +#define MAX_EVENTTIMER 32 +#define NATURAL_HEAL_INTERVAL 500 +#define MAX_FLOORITEM 500000 +#define MAX_LEVEL 255 +#define MAX_WALKPATH 48 +#define MAX_DROP_PER_MAP 48 + +#define DEFAULT_AUTOSAVE_INTERVAL 60*1000 + +#define OPTION_HIDE 0x40 + +enum { BL_NUL, BL_PC, BL_NPC, BL_MOB, BL_ITEM, BL_CHAT, BL_SKILL , BL_PET }; +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 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; + short amount; + int value; +}; + +struct skill_unit_group; +struct skill_unit { + struct block_list bl; + + struct skill_unit_group *group; + + int limit; + int val1,val2; + short alive,range; +}; +struct skill_unit_group { + int src_id; + int party_id; + int guild_id; + int map,range; + int target_flag; + unsigned int tick; + int limit,interval; + + int skill_id,skill_lv; + int val1,val2; + char *valstr; + int unit_id; + int group_id; + int unit_count,alive_count; + struct skill_unit *unit; +}; +struct skill_unit_group_tickset { + unsigned int tick; + int group_id; +}; +struct skill_timerskill { + int timer; + int src_id; + int target_id; + int map; + short x,y; + short skill_id,skill_lv; + int type; + int flag; +}; + +struct npc_data; +struct pet_db; +struct item_data; +struct square; + +struct map_session_data { + struct block_list bl; + struct { + unsigned auth : 1; + unsigned change_walk_target : 1; + unsigned attack_continue : 1; + unsigned menu_or_input : 1; + unsigned dead_sit : 2; + unsigned skillcastcancel : 1; + unsigned waitingdisconnect : 1; + unsigned lr_flag : 2; + unsigned connect_new : 1; + unsigned arrow_atk : 1; + unsigned attack_type : 3; + unsigned skill_flag : 1; + unsigned gangsterparadise : 1; + unsigned produce_flag : 1; + unsigned make_arrow_flag : 1; + unsigned potionpitcher_flag : 1; + unsigned storage_flag : 1; + } state; + struct { + unsigned killer : 1; + unsigned killable : 1; + unsigned restart_full_recover : 1; + unsigned no_castcancel : 1; + unsigned no_castcancel2 : 1; + unsigned no_sizefix : 1; + unsigned no_magic_damage : 1; + unsigned no_weapon_damage : 1; + unsigned no_gemstone : 1; + unsigned infinite_endure : 1; + unsigned unbreakable_weapon : 1; + unsigned unbreakable_armor : 1; + unsigned infinite_autospell : 1; + } special_state; + int char_id, login_id1, login_id2, sex; + int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04 (by [Yor]) + struct mmo_charstatus status; + struct item_data *inventory_data[MAX_INVENTORY]; + short equip_index[11]; + int weight,max_weight; + int cart_weight,cart_max_weight,cart_num,cart_max_num; + char mapname[24]; + int fd,new_fd; + short to_x,to_y; + short speed,prev_speed; + short opt1,opt2,opt3; + char dir,head_dir; + unsigned int client_tick,server_tick; + struct walkpath_data walkpath; + int walktimer; + int npc_id,areanpc_id,npc_shopid; + int npc_pos; + int npc_menu; + int npc_amount; + int npc_stack,npc_stackmax; + char *npc_script,*npc_scriptroot; + char *npc_stackbuf; + char npc_str[256]; + unsigned int chatID; + + int attacktimer; + int attacktarget; + short attacktarget_lv; + unsigned int attackabletime; + + int followtimer; // [MouseJstr] + int followtarget; + + short attackrange,attackrange_; + int skilltimer; + int skilltarget; + short skillx,skilly; + short skillid,skilllv; + short skillitem,skillitemlv; + short skillid_old,skilllv_old; + short skillid_dance,skilllv_dance; + struct skill_unit_group skillunit[MAX_SKILLUNITGROUP]; + struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET]; + struct skill_timerskill skilltimerskill[MAX_SKILLTIMERSKILL]; + int cloneskill_id,cloneskill_lv; + int potion_hp,potion_sp,potion_per_hp,potion_per_sp; + + int invincible_timer; + unsigned int canact_tick; + unsigned int canmove_tick; + unsigned int canlog_tick; + int hp_sub,sp_sub; + int inchealhptick,inchealsptick,inchealspirithptick,inchealspiritsptick; +// -- moonsoul (new tick for berserk self-damage) + int berserkdamagetick; + int fame; + + short view_class; + short weapontype1,weapontype2; + short disguiseflag,disguise; // [Valaris] + int paramb[6],paramc[6],parame[6],paramcard[6]; + int hit,flee,flee2,aspd,amotion,dmotion; + int watk,watk2,atkmods[3]; + int def,def2,mdef,mdef2,critical,matk1,matk2; + int atk_ele,def_ele,star,overrefine; + int castrate,hprate,sprate,dsprate; + int addele[10],addrace[12],addsize[3],subele[10],subrace[12]; + int addeff[10],addeff2[10],reseff[10]; + int watk_,watk_2,atkmods_[3],addele_[10],addrace_[12],addsize_[3]; //二刀流のために追加 + int atk_ele_,star_,overrefine_; //二刀流のために追加 + int base_atk,atk_rate; + int arrow_atk,arrow_ele,arrow_cri,arrow_hit,arrow_range; + int arrow_addele[10],arrow_addrace[12],arrow_addsize[3],arrow_addeff[10],arrow_addeff2[10]; + int nhealhp,nhealsp,nshealhp,nshealsp,nsshealhp,nsshealsp; + int aspd_rate,speed_rate,hprecov_rate,sprecov_rate,critical_def,double_rate; + int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate; + int matk_rate,ignore_def_ele,ignore_def_race,ignore_def_ele_,ignore_def_race_; + int ignore_mdef_ele,ignore_mdef_race; + int magic_addele[10],magic_addrace[12],magic_subrace[12]; + int perfect_hit,get_zeny_num; + int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate; + int def_ratio_atk_ele,def_ratio_atk_ele_,def_ratio_atk_race,def_ratio_atk_race_; + int add_damage_class_count,add_damage_class_count_,add_magic_damage_class_count; + short add_damage_classid[10],add_damage_classid_[10],add_magic_damage_classid[10]; + int add_damage_classrate[10],add_damage_classrate_[10],add_magic_damage_classrate[10]; + short add_def_class_count,add_mdef_class_count; + short add_def_classid[10],add_mdef_classid[10]; + int add_def_classrate[10],add_mdef_classrate[10]; + short monster_drop_item_count; + short monster_drop_itemid[10]; + int monster_drop_race[10],monster_drop_itemrate[10]; + int double_add_rate,speed_add_rate,aspd_add_rate,perfect_hit_add, get_zeny_add_num; + short splash_range,splash_add_range; + short autospell_id,autospell_lv,autospell_rate; + short hp_drain_rate,hp_drain_per,sp_drain_rate,sp_drain_per; + short hp_drain_rate_,hp_drain_per_,sp_drain_rate_,sp_drain_per_; + int short_weapon_damage_return,long_weapon_damage_return; + int weapon_coma_ele[10],weapon_coma_race[12]; + short break_weapon_rate,break_armor_rate; + short add_steal_rate; + + short spiritball, spiritball_old; + int spirit_timer[MAX_SKILL_LEVEL]; + int magic_damage_return; // AppleGirl Was Here + int random_attack_increase_add,random_attack_increase_per; // [Valaris] + int perfect_hiding; // [Valaris] + int unbreakable; + + int die_counter; + short doridori_counter; + + int reg_num; + struct script_reg *reg; + int regstr_num; + struct script_regstr *regstr; + + struct status_change sc_data[MAX_STATUSCHANGE]; + short sc_count; + struct square dev; + + int trade_partner; + int deal_item_index[10]; + int deal_item_amount[10]; + int deal_zeny; + short deal_locked; + + int party_sended,party_invite,party_invite_account; + int party_hp,party_x,party_y; + + int guild_sended,guild_invite,guild_invite_account; + int guild_emblem_id,guild_alliance,guild_alliance_account; + int guildspy; // [Syrus22] + int partyspy; // [Syrus22] + + int vender_id; + int vend_num; + char message[80]; + struct vending vending[12]; + + int catch_target_class; + struct s_pet pet; + struct pet_db *petDB; + struct pet_data *pd; + int pet_hungry_timer; + + int pvp_point,pvp_rank,pvp_timer,pvp_lastusers; + + char eventqueue[MAX_EVENTQUEUE][50]; + int eventtimer[MAX_EVENTTIMER]; + + int last_skillid,last_skilllv; // Added by RoVeRT + struct{ + char name[24]; + } ignore[80]; + int ignoreAll; + short sg_count; + +#ifndef TXT_ONLY + int mail_counter; // mail counter for mail system [Valaris] +#endif + +}; + +struct npc_timerevent_list { + int timer,pos; +}; +struct npc_label_list { + char name[24]; + int pos; +}; +struct npc_item_list { + int nameid,value; +}; +struct npc_data { + struct block_list bl; + short n; + short class,dir; + short speed; + char name[24]; + char exname[24]; + int chat_id; + short opt1,opt2,opt3,option; + short flag; + union { + struct { + char *script; + short xs,ys; + int guild_id; + int timer,timerid,timeramount,nexttimer; + unsigned int timertick; + struct npc_timerevent_list *timer_event; + int label_list_num; + struct npc_label_list *label_list; + int src_id; + } scr; + struct npc_item_list shop_item[1]; + struct { + short xs,ys; + short x,y; + char name[16]; + } warp; + } u; + // ここにメンバを追加してはならない(shop_itemが可変長の為) + + char eventqueue[MAX_EVENTQUEUE][50]; + int eventtimer[MAX_EVENTTIMER]; + short arenaflag; +}; +struct mob_data { + struct block_list bl; + short n; + short base_class,class,dir,mode; + short m,x0,y0,xs,ys; + char name[24]; + int spawndelay1,spawndelay2; + struct { + unsigned state : 8; + unsigned skillstate : 8; + unsigned targettype : 1; + unsigned steal_flag : 1; + unsigned steal_coin_flag : 1; + unsigned skillcastcancel : 1; + unsigned master_check : 1; + unsigned change_walk_target : 1; + unsigned walk_easy : 1; + unsigned special_mob_ai : 3; + } state; + int timer; + short to_x,to_y; + short speed; + int hp; + int target_id,attacked_id; + short target_lv; + struct walkpath_data walkpath; + unsigned int next_walktime; + unsigned int attackabletime; + unsigned int last_deadtime,last_spawntime,last_thinktime; + unsigned int canmove_tick; + short move_fail_count; + struct { + int id; + int dmg; + } dmglog[DAMAGELOG_SIZE]; + struct item *lootitem; + short lootitem_count; + + struct status_change sc_data[MAX_STATUSCHANGE]; + short sc_count; + short opt1,opt2,opt3,option; + short min_chase; + short sg_count; + int guild_id; + int deletetimer; + + int skilltimer; + int skilltarget; + short skillx,skilly; + short skillid,skilllv,skillidx; + unsigned int skilldelay[MAX_MOBSKILL]; + int def_ele; + int master_id,master_dist; + int exclusion_src,exclusion_party,exclusion_guild; + struct skill_timerskill skilltimerskill[MAX_MOBSKILLTIMERSKILL]; + struct skill_unit_group skillunit[MAX_MOBSKILLUNITGROUP]; + struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET]; + char npc_event[50]; + short size; +}; +struct pet_data { + struct block_list bl; + short n; + short class,dir; + short speed; + char name[24]; + struct { + unsigned state : 8 ; + unsigned skillstate : 8 ; + unsigned change_walk_target : 1 ; + } 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; + int skilltype,skillval,skilltimer,skillduration; // [Valaris] + int skillbonustype,skillbonusval,skillbonustimer,skillbonusduration; // [Valaris] + struct item *lootitem; + short loot; // [Valaris] + short lootmax; // [Valaris] + short lootitem_count; + short lootitem_weight; + int lootitem_timer; + 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}; // 囲まれペナルティ計算用 + +struct map_data { + char name[24]; + char alias[24]; // [MouseJstr] + unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う + struct block_list **block; + struct block_list **block_mob; + int *block_count,*block_mob_count; + int m; + short xs,ys; + short bxs,bys; + int npc_num; + int users; + struct { + unsigned alias : 1; + unsigned nomemo : 1; + unsigned noteleport : 1; + unsigned noreturn : 1; + unsigned monster_noteleport : 1; + unsigned nosave : 1; + unsigned nobranch : 1; + unsigned nopenalty : 1; + unsigned pvp : 1; + unsigned pvp_noparty : 1; + unsigned pvp_noguild : 1; + unsigned pvp_nightmaredrop :1; + unsigned pvp_nocalcrank : 1; + unsigned gvg : 1; + unsigned gvg_noparty : 1; + unsigned nozenypenalty : 1; + unsigned notrade : 1; + unsigned noskill : 1; + unsigned nowarp : 1; + unsigned nowarpto : 1; + unsigned nopvp : 1; // [Valaris] + unsigned noicewall : 1; // [Valaris] + unsigned snow : 1; // [Valaris] + unsigned fog : 1; // [Valaris] + unsigned sakura : 1; // [Valaris] + unsigned leaves : 1; // [Valaris] + unsigned rain : 1; // [Valaris] + } flag; + struct point save; + struct npc_data *npc[MAX_NPC_PER_MAP]; + struct { + int drop_id; + int drop_type; + int drop_per; + } drop_list[MAX_DROP_PER_MAP]; +}; +struct map_data_other_server { + char name[24]; + unsigned char *gat; // NULL固定にして判断 + unsigned long ip; + unsigned int port; +}; +#define read_gat(m,x,y) (map[m].gat[(x)+(y)*map[m].xs]) +#define read_gatp(m,x,y) (m->gat[(x)+(y)*m->xs]) + +struct flooritem_data { + struct block_list bl; + short subx,suby; + int cleartimer; + int first_get_id,second_get_id,third_get_id; + unsigned int first_get_tick,second_get_tick,third_get_tick; + struct item item_data; +}; + +enum { + SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7 + SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15 + SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23 + SP_WEIGHT,SP_MAXWEIGHT,SP_1a,SP_1b,SP_1c,SP_1d,SP_1e,SP_1f, // 24-31 + SP_USTR,SP_UAGI,SP_UVIT,SP_UINT,SP_UDEX,SP_ULUK,SP_26,SP_27, // 32-39 + SP_28,SP_ATK1,SP_ATK2,SP_MATK1,SP_MATK2,SP_DEF1,SP_DEF2,SP_MDEF1, // 40-47 + SP_MDEF2,SP_HIT,SP_FLEE1,SP_FLEE2,SP_CRITICAL,SP_ASPD,SP_36,SP_JOBLEVEL, // 48-55 + SP_UPPER,SP_PARTNER,SP_CART,SP_FAME,SP_UNBREAKABLE, //56-58 + SP_CARTINFO=99, // 99 + + // original 1000- + SP_ATTACKRANGE=1000, SP_ATKELE,SP_DEFELE, // 1000-1002 + SP_CASTRATE, SP_MAXHPRATE, SP_MAXSPRATE, SP_SPRATE, // 1003-1006 + SP_ADDELE, SP_ADDRACE, SP_ADDSIZE, SP_SUBELE, SP_SUBRACE, // 1007-1011 + SP_ADDEFF, SP_RESEFF, // 1012-1013 + SP_BASE_ATK,SP_ASPD_RATE,SP_HP_RECOV_RATE,SP_SP_RECOV_RATE,SP_SPEED_RATE, // 1014-1018 + SP_CRITICAL_DEF,SP_NEAR_ATK_DEF,SP_LONG_ATK_DEF, // 1019-1021 + SP_DOUBLE_RATE, SP_DOUBLE_ADD_RATE, SP_MATK, SP_MATK_RATE, // 1022-1025 + SP_IGNORE_DEF_ELE,SP_IGNORE_DEF_RACE, // 1026-1027 + SP_ATK_RATE,SP_SPEED_ADDRATE,SP_ASPD_ADDRATE, // 1028-1030 + SP_MAGIC_ATK_DEF,SP_MISC_ATK_DEF, // 1031-1032 + SP_IGNORE_MDEF_ELE,SP_IGNORE_MDEF_RACE, // 1033-1034 + SP_MAGIC_ADDELE,SP_MAGIC_ADDRACE,SP_MAGIC_SUBRACE, // 1035-1037 + SP_PERFECT_HIT_RATE,SP_PERFECT_HIT_ADD_RATE,SP_CRITICAL_RATE,SP_GET_ZENY_NUM,SP_ADD_GET_ZENY_NUM, // 1038-1042 + SP_ADD_DAMAGE_CLASS,SP_ADD_MAGIC_DAMAGE_CLASS,SP_ADD_DEF_CLASS,SP_ADD_MDEF_CLASS, // 1043-1046 + SP_ADD_MONSTER_DROP_ITEM,SP_DEF_RATIO_ATK_ELE,SP_DEF_RATIO_ATK_RACE,SP_ADD_SPEED, // 1047-1050 + SP_HIT_RATE,SP_FLEE_RATE,SP_FLEE2_RATE,SP_DEF_RATE,SP_DEF2_RATE,SP_MDEF_RATE,SP_MDEF2_RATE, // 1051-1057 + SP_SPLASH_RANGE,SP_SPLASH_ADD_RANGE,SP_AUTOSPELL,SP_HP_DRAIN_RATE,SP_SP_DRAIN_RATE, // 1058-1062 + SP_SHORT_WEAPON_DAMAGE_RETURN,SP_LONG_WEAPON_DAMAGE_RETURN,SP_WEAPON_COMA_ELE,SP_WEAPON_COMA_RACE, // 1063-1066 + SP_ADDEFF2,SP_BREAK_WEAPON_RATE,SP_BREAK_ARMOR_RATE,SP_ADD_STEAL_RATE, // 1067-1070 + SP_MAGIC_DAMAGE_RETURN,SP_RANDOM_ATTACK_INCREASE,SP_ALL_STATS,SP_AGI_VIT,SP_AGI_DEX_STR,SP_PERFECT_HIDE, // 1071-1077 + SP_DISGUISE, // 1077 + + SP_RESTART_FULL_RECORVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005 + SP_NO_CASTCANCEL2,SP_INFINITE_ENDURE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR // 2006-2009 +}; + +enum { + LOOK_BASE,LOOK_HAIR,LOOK_WEAPON,LOOK_HEAD_BOTTOM,LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HAIR_COLOR,LOOK_CLOTHES_COLOR,LOOK_SHIELD,LOOK_SHOES +}; + +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 char motd_txt[]; +extern char help_txt[]; + +extern char talkie_mes[]; + +extern char wisp_server_name[]; + +// 鯖全体情報 +void map_setusers(int); +int map_getusers(void); +// block削除関連 +int map_freeblock( void *bl ); +int map_freeblock_lock(void); +int map_freeblock_unlock(void); +// block関連 +int map_addblock(struct block_list *); +int map_delblock(struct block_list *); +void map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...); +// -- moonsoul (added map_foreachincell) +void map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...); +void map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...); +int map_countnearpc(int,int,int); +//block関連に追加 +int map_count_oncell(int m,int x,int y); +// 一時的object関連 +int map_addobject(struct block_list *); +int map_delobject(int); +int map_delobjectnofree(int id); +void map_foreachobject(int (*)(struct block_list*,va_list),int,...); +// +int map_quit(struct map_session_data *); +// npc +int map_addnpc(int,struct npc_data *); + +// 床アイテム関連 +int map_clearflooritem_timer(int,unsigned int,int,int); +#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1) +int map_addflooritem(struct item *,int,int,int,int,struct map_session_data *,struct map_session_data *,struct map_session_data *,int); +int map_searchrandfreecell(int,int,int,int); + +// キャラid=>キャラ名 変換関連 +void map_addchariddb(int charid,char *name); +void map_delchariddb(int charid); +int map_reqchariddb(struct map_session_data * sd,int charid); +char * map_charid2nick(int); + +struct map_session_data * map_id2sd(int); +struct block_list * map_id2bl(int); +int map_mapname2mapid(char*); +int map_mapname2ipport(char*,int*,int*); +int map_setipport(char *name,unsigned long ip,int port); +int map_eraseipport(char *name,unsigned long ip,int port); +void map_addiddb(struct block_list *); +void map_deliddb(struct block_list *bl); +int map_foreachiddb(int (*)(void*,void*,va_list),...); +void map_addnickdb(struct map_session_data *); +struct map_session_data * map_nick2sd(char*); + +// gat関連 +int map_getcell(int,int,int); +int map_setcell(int,int,int,int); + +// その他 +int map_check_dir(int s_dir,int t_dir); +int map_calc_dir( struct block_list *src,int x,int y); + +// path.cより +int path_search(struct walkpath_data*,int,int,int,int,int,int); +int path_blownpos(int m,int x0,int y0,int dx,int dy,int count); + +int map_who(int fd); + +void map_helpscreen(); // [Valaris] +int map_delmap(char *mapname); + +#ifndef TXT_ONLY + +// MySQL +#include <mysql.h> + +void char_online_check(void); // [Valaris] +void char_offline(struct map_session_data *sd); + +extern MYSQL mmysql_handle; +extern char tmp_sql[65535]; +extern MYSQL_RES* sql_res ; +extern MYSQL_ROW sql_row ; + +extern MYSQL lmysql_handle; +extern char tmp_lsql[65535]; +extern MYSQL_RES* lsql_res ; +extern MYSQL_ROW lsql_row ; + +extern MYSQL mail_handle; +extern MYSQL_RES* mail_res ; +extern MYSQL_ROW mail_row ; +extern char tmp_msql[65535]; + +extern int db_use_sqldbs; + +extern char item_db_db[32]; +extern char mob_db_db[32]; +extern char login_db[32]; + +extern char login_db_level[32]; +extern char login_db_account_id[32]; + +extern int lowest_gm_level; +extern int read_gm_interval; + +extern char char_db[32]; +#endif /* not TXT_ONLY */ + +#endif diff --git a/src/map/mob.c b/src/map/mob.c new file mode 100644 index 0000000..969eed2 --- /dev/null +++ b/src/map/mob.c @@ -0,0 +1,4216 @@ +// $Id: mob.c,v 1.7 2004/09/25 05:32:18 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.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 "mob.h" +#include "guild.h" +#include "itemdb.h" +#include "skill.h" +#include "battle.h" +#include "party.h" +#include "npc.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define MIN_MOBTHINKTIME 100 + +#define MOB_LAZYMOVEPERC 50 // Move probability in the negligent mode MOB (rate of 1000 minute) +#define MOB_LAZYWARPPERC 20 // Warp probability in the negligent mode MOB (rate of 1000 minute) + +struct mob_db mob_db[2001]; + +/*========================================== + * Local prototype declaration (only required thing) + *------------------------------------------ + */ +static int distance(int,int,int,int); +static int mob_makedummymobdb(int); +static int mob_timer(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); +static int mob_unlocktarget(struct mob_data *md,int tick); + +/*========================================== + * Mob is searched with a name. + *------------------------------------------ + */ +int mobdb_searchname(const char *str) +{ + int i; + + for(i = 0; i < sizeof(mob_db) / sizeof(mob_db[0]); i++) { + if (strcmpi(mob_db[i].name, str) == 0 || strcmp(mob_db[i].jname, str) == 0 || + memcmp(mob_db[i].name, str, 24) == 0 || memcmp(mob_db[i].jname, str, 24) == 0) + return i; + } + + return 0; +} + +/*========================================== + * Id Mob is checked. + *------------------------------------------ + */ +int mobdb_checkid(const int id) +{ + if (id <= 0 || id >= (sizeof(mob_db) / sizeof(mob_db[0])) || mob_db[id].name[0] == '\0') + return 0; + + return id; +} + +/*========================================== + * 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; + if(strcmp(mobname,"--en--")==0) + memcpy(md->name,mob_db[class].name,24); + else if(strcmp(mobname,"--ja--")==0) + memcpy(md->name,mob_db[class].jname,24); + else + memcpy(md->name,mobname,24); + + md->n = 0; + md->base_class = md->class = class; + md->bl.id= npc_get_new_npc_id(); + + memset(&md->state,0,sizeof(md->state)); + md->timer = -1; + md->target_id=0; + md->attacked_id=0; + md->speed=mob_db[class].speed; + + return 0; +} + + +/*========================================== + * 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,r=class; + + if( sd ) + lv=sd->status.base_level; + + if( sd && strcmp(mapname,"this")==0) + m=sd->bl.m; + else + m=map_mapname2mapid(mapname); + + if(m<0 || amount<=0 || (class>=0 && class<=1000) || class>2000) // 値が異常なら召喚を止める + return 0; + + if(class<0){ // ランダムに召喚 + int i=0; + int j=-class-1; + int k; + if(j>=0 && j<MAX_RANDOMMONSTER){ + do{ + class=rand()%1000+1001; + k=rand()%1000000; + }while((mob_db[class].max_hp <= 0 || mob_db[class].summonper[j] <= k || + (lv<mob_db[class].lv && battle_config.random_monster_checklv==1)) && (i++) < 2000); + if(i>=2000){ + class=mob_db[0].summonper[j]; + } + }else{ + return 0; + } +// if(battle_config.etc_log==1) +// printf("mobclass=%d try=%d\n",class,i); + } + if(sd){ + if(x<=0) x=sd->bl.x; + if(y<=0) y=sd->bl.y; + }else if(x<=0 || y<=0){ + printf("mob_once_spawn: ??\n"); + } + + for(count=0;count<amount;count++){ + md=(struct mob_data *)aCalloc(1,sizeof(struct mob_data)); + memset(md, '\0', sizeof *md); + if(mob_db[class].mode&0x02) + md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); + else + md->lootitem=NULL; + + mob_spawn_dataset(md,mobname,class); + md->bl.m=m; + md->bl.x=x; + md->bl.y=y; + if(r<0&&battle_config.dead_branch_active==1) md->mode=0x1+0x4+0x80; //移動してアクティブで反撃する + md->m =m; + md->x0=x; + md->y0=y; + md->xs=0; + md->ys=0; + md->spawndelay1=-1; // Only once is a flag. + md->spawndelay2=-1; // Only once is a flag. + + memcpy(md->npc_event,event,sizeof(md->npc_event)); + + md->bl.type=BL_MOB; + map_addiddb(&md->bl); + mob_spawn(md->bl.id); + + if(class==1288) { // emperium hp based on defense level [Valaris] + struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name); + if(gc) { + mob_db[class].max_hp+=2000*gc->defense; + md->hp=mob_db[class].max_hp; + } + } // end addition [Valaris] + + + } + return (amount>0)?md->bl.id:0; +} +/*========================================== + * The MOB appearance for one time (& area specification for scripts) + *------------------------------------------ + */ +int mob_once_spawn_area(struct map_session_data *sd,char *mapname, + int x0,int y0,int x1,int y1, + const char *mobname,int class,int amount,const char *event) +{ + int x,y,i,c,max,lx=-1,ly=-1,id=0; + int m; + + if(strcmp(mapname,"this")==0) + m=sd->bl.m; + else + m=map_mapname2mapid(mapname); + + max=(y1-y0+1)*(x1-x0+1)*3; + if(max>1000)max=1000; + + if(m<0 || amount<=0 || (class>=0 && class<=1000) || class>2000) // 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( ( (c=map_getcell(m,x,y))==1 || c==5)&& (++j)<max ); + if(j>=max){ + if(lx>=0){ // Since reference went wrong, the place which boiled before is used. + x=lx; + y=ly; + }else + return 0; // Since reference of the place which boils first went wrong, it stops. + } + id=mob_once_spawn(sd,mapname,x,y,mobname,class,1,event); + lx=x; + ly=y; + } + return id; +} + +/*========================================== + * Summoning Guardians [Valaris] + *------------------------------------------ + */ +int mob_spawn_guardian(struct map_session_data *sd,char *mapname, + int x,int y,const char *mobname,int class,int amount,const char *event,int guardian) +{ + struct mob_data *md=NULL; + int m,count=1,lv=255; + + if( sd ) + lv=sd->status.base_level; + + if( sd && strcmp(mapname,"this")==0) + m=sd->bl.m; + else + m=map_mapname2mapid(mapname); + + if(m<0 || amount<=0 || (class>=0 && class<=1000) || class>2000) // 値が異常なら召喚を止める + return 0; + + if(class<0) + return 0; + + if(sd){ + if(x<=0) x=sd->bl.x; + if(y<=0) y=sd->bl.y; + } + + else if(x<=0 || y<=0) + printf("mob_spawn_guardian: ??\n"); + + + for(count=0;count<amount;count++){ + struct guild_castle *gc; + md=calloc(sizeof(struct mob_data), 1); + if(md==NULL){ + printf("mob_spawn_guardian: out of memory !\n"); + exit(1); + } + memset(md, '\0', sizeof *md); + + + + 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. + + memcpy(md->npc_event,event,sizeof(md->npc_event)); + + md->bl.type=BL_MOB; + map_addiddb(&md->bl); + mob_spawn(md->bl.id); + + gc=guild_mapname2gc(map[md->bl.m].name); + if(gc) { + mob_db[class].max_hp+=2000*gc->defense; + if(guardian==0) { md->hp=gc->Ghp0; gc->GID0=md->bl.id; } + if(guardian==1) { md->hp=gc->Ghp1; gc->GID1=md->bl.id; } + if(guardian==2) { md->hp=gc->Ghp2; gc->GID2=md->bl.id; } + if(guardian==3) { md->hp=gc->Ghp3; gc->GID3=md->bl.id; } + if(guardian==4) { md->hp=gc->Ghp4; gc->GID4=md->bl.id; } + if(guardian==5) { md->hp=gc->Ghp5; gc->GID5=md->bl.id; } + if(guardian==6) { md->hp=gc->Ghp6; gc->GID6=md->bl.id; } + if(guardian==7) { md->hp=gc->Ghp7; gc->GID7=md->bl.id; } + + } + } + + return (amount>0)?md->bl.id:0; +} + + +/*========================================== + * Appearance income of mob + *------------------------------------------ + */ +int mob_get_viewclass(int class) +{ + return mob_db[class].view_class; +} +int mob_get_sex(int class) +{ + return mob_db[class].sex; +} +short mob_get_hair(int class) +{ + return mob_db[class].hair; +} +short mob_get_hair_color(int class) +{ + return mob_db[class].hair_color; +} +short mob_get_weapon(int class) +{ + return mob_db[class].weapon; +} +short mob_get_shield(int class) +{ + return mob_db[class].shield; +} +short mob_get_head_top(int class) +{ + return mob_db[class].head_top; +} +short mob_get_head_mid(int class) +{ + return mob_db[class].head_mid; +} +short mob_get_head_buttom(int class) +{ + return mob_db[class].head_buttom; +} +short mob_get_clothes_color(int class) // Add for player monster dye - Valaris +{ + return mob_db[class].clothes_color; // End +} +int mob_get_equip(int class) // mob equip [Valaris] +{ + return mob_db[class].equip; +} +/*========================================== + * Is MOB in the state in which the present movement is possible or not? + *------------------------------------------ + */ +int mob_can_move(struct mob_data *md) +{ + nullpo_retr(0, md); + + if(md->canmove_tick > gettick() || (md->opt1 > 0 && md->opt1 != 6) || md->option&2) + return 0; + // アンクル中で動けないとか + if( md->sc_data[SC_ANKLE].timer != -1 || //アンクルスネア + md->sc_data[SC_AUTOCOUNTER].timer != -1 || //オートカウンター + md->sc_data[SC_BLADESTOP].timer != -1 || //白刃取り + md->sc_data[SC_SPIDERWEB].timer != -1 //スパイダーウェッブ + ) + return 0; + + return 1; +} + +/*========================================== + * Time calculation concerning one step next to mob + *------------------------------------------ + */ +static int calc_next_walk_step(struct mob_data *md) +{ + nullpo_retr(0, md); + + if(md->walkpath.path_pos>=md->walkpath.path_len) + return -1; + if(md->walkpath.path[md->walkpath.path_pos]&1) + return battle_get_speed(&md->bl)*14/10; + return battle_get_speed(&md->bl); +} + +static int mob_walktoxy_sub(struct mob_data *md); + +/*========================================== + * Mob Walk processing + *------------------------------------------ + */ +static int mob_walk(struct mob_data *md,unsigned int tick,int data) +{ + int moveblock; + int i,ctype; + static int dirx[8]={0,-1,-1,-1,0,1,1,1}; + static int diry[8]={1,1,0,-1,-1,-1,0,1}; + int x,y,dx,dy; + + nullpo_retr(0, md); + + md->state.state=MS_IDLE; + if(md->walkpath.path_pos>=md->walkpath.path_len || md->walkpath.path_pos!=data) + return 0; + + md->walkpath.path_half ^= 1; + if(md->walkpath.path_half==0){ + md->walkpath.path_pos++; + if(md->state.change_walk_target){ + mob_walktoxy_sub(md); + return 0; + } + } + else { + if(md->walkpath.path[md->walkpath.path_pos]>=8) + return 1; + + x = md->bl.x; + y = md->bl.y; + ctype = map_getcell(md->bl.m,x,y); + if(ctype == 1 || ctype == 5) { + mob_stop_walking(md,1); + return 0; + } + md->dir=md->walkpath.path[md->walkpath.path_pos]; + dx = dirx[md->dir]; + dy = diry[md->dir]; + + ctype = map_getcell(md->bl.m,x+dx,y+dy); + if(ctype == 1 || ctype == 5) { + mob_walktoxy_sub(md); + return 0; + } + + moveblock = ( x/BLOCK_SIZE != (x+dx)/BLOCK_SIZE || y/BLOCK_SIZE != (y+dy)/BLOCK_SIZE); + + md->state.state=MS_WALK; + map_foreachinmovearea(clif_moboutsight,md->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,md); + + x += dx; + y += dy; + if(md->min_chase>13) + md->min_chase--; + + if(moveblock) map_delblock(&md->bl); + md->bl.x = x; + md->bl.y = y; + if(moveblock) map_addblock(&md->bl); + + map_foreachinmovearea(clif_mobinsight,md->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,md); + md->state.state=MS_IDLE; + + if(md->option&4) + skill_check_cloaking(&md->bl); + + skill_unit_move(&md->bl,tick,1); // Inspection of a skill unit + } + if((i=calc_next_walk_step(md))>0){ + i = i>>1; + if(i < 1 && md->walkpath.path_half == 0) + i = 1; + md->timer=add_timer(tick+i,mob_timer,md->bl.id,md->walkpath.path_pos); + md->state.state=MS_WALK; + + if(md->walkpath.path_pos>=md->walkpath.path_len) + clif_fixmobpos(md); // When mob stops, retransmission current of a position. + } + return 0; +} + +/*========================================== + * Attack processing of mob + *------------------------------------------ + */ +static int mob_attack(struct mob_data *md,unsigned int tick,int data) +{ + struct block_list *tbl=NULL; + struct map_session_data *tsd=NULL; + struct mob_data *tmd=NULL; + + int mode,race,range; + + nullpo_retr(0, md); + + md->min_chase=13; + md->state.state=MS_IDLE; + md->state.skillstate=MSS_IDLE; + + if( md->skilltimer!=-1 ) // スキル使用中 + return 0; + + if(md->opt1>0 || md->option&2) + return 0; + + if(md->sc_data[SC_AUTOCOUNTER].timer != -1) + return 0; + + if(md->sc_data[SC_BLADESTOP].timer != -1) + return 0; + + if((tbl=map_id2bl(md->target_id))==NULL){ + md->target_id=0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + + if(tbl->type==BL_PC) + tsd=(struct map_session_data *)tbl; + else if(tbl->type==BL_MOB) + tmd=(struct mob_data *)tbl; + else + return 0; + + if(tsd){ + if( pc_isdead(tsd) || tsd->invincible_timer != -1 || pc_isinvisible(tsd) || md->bl.m != tbl->m || tbl->prev == NULL || distance(md->bl.x,md->bl.y,tbl->x,tbl->y)>=13 ){ + md->target_id=0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + } + if(tmd){ + if(md->bl.m != tbl->m || tbl->prev == NULL || distance(md->bl.x,md->bl.y,tbl->x,tbl->y)>=13){ + md->target_id=0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + } + + + if(!md->mode) + mode=mob_db[md->class].mode; + else + mode=md->mode; + + race=mob_db[md->class].race; + if(!(mode&0x80)){ + md->target_id=0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + if(tsd && !(mode&0x20) && (tsd->sc_data[SC_TRICKDEAD].timer != -1 || + ((pc_ishiding(tsd) || tsd->state.gangsterparadise) && race!=4 && race!=6) ) ) { + md->target_id=0; + md->state.targettype = NONE_ATTACKABLE; + return 0; + } + + range = mob_db[md->class].range; + if(mode&1) + range++; + if(distance(md->bl.x,md->bl.y,tbl->x,tbl->y) > range) + return 0; + if(battle_config.monster_attack_direction_change) + md->dir=map_calc_dir(&md->bl, tbl->x,tbl->y ); // 向き設定 + + //clif_fixmobpos(md); + + md->state.skillstate=MSS_ATTACK; + if( mobskill_use(md,tick,-2) ) // スキル使用 + return 0; + + md->target_lv = battle_weapon_attack(&md->bl,tbl,tick,0); + + if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1) + skill_status_change_end(&md->bl,SC_CLOAKING,-1); + + md->attackabletime = tick + battle_get_adelay(&md->bl); + + md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0); + md->state.state=MS_ATTACK; + + return 0; +} + + +/*========================================== + * The attack of PC which is attacking id is stopped. + * The callback function of clif_foreachclient + *------------------------------------------ + */ +int mob_stopattacked(struct map_session_data *sd,va_list ap) +{ + int id; + + nullpo_retr(0, sd); + nullpo_retr(0, ap); + + id=va_arg(ap,int); + if(sd->attacktarget==id) + pc_stopattack(sd); + return 0; +} +/*========================================== + * The timer in which the mob's states changes + *------------------------------------------ + */ +int mob_changestate(struct mob_data *md,int state,int type) +{ + unsigned int tick; + int i; + + nullpo_retr(0, md); + + if(md->timer != -1) + delete_timer(md->timer,mob_timer); + md->timer=-1; + md->state.state=state; + + switch(state){ + case MS_WALK: + if((i=calc_next_walk_step(md))>0){ + i = i>>2; + md->timer=add_timer(gettick()+i,mob_timer,md->bl.id,0); + } + else + md->state.state=MS_IDLE; + break; + case MS_ATTACK: + tick = gettick(); + i=DIFF_TICK(md->attackabletime,tick); + if(i>0 && i<2000) + md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0); + else if(type) { + md->attackabletime = tick + battle_get_amotion(&md->bl); + md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0); + } + else { + md->attackabletime = tick + 1; + md->timer=add_timer(md->attackabletime,mob_timer,md->bl.id,0); + } + break; + case MS_DELAY: + md->timer=add_timer(gettick()+type,mob_timer,md->bl.id,0); + break; + case MS_DEAD: + skill_castcancel(&md->bl,0); +// mobskill_deltimer(md); + md->state.skillstate=MSS_DEAD; + md->last_deadtime=gettick(); + // Since it died, all aggressors' attack to this mob is stopped. + clif_foreachclient(mob_stopattacked,md->bl.id); + skill_unit_out_all(&md->bl,gettick(),1); + skill_status_change_clear(&md->bl,2); // The abnormalities in status are canceled. + skill_clear_unitgroup(&md->bl); // All skill unit groups are deleted. + skill_cleartimerskill(&md->bl); + if(md->deletetimer!=-1) + delete_timer(md->deletetimer,mob_timer_delete); + md->deletetimer=-1; + md->hp=md->target_id=md->attacked_id=0; + md->state.targettype = NONE_ATTACKABLE; + break; + } + + return 0; +} + +/*========================================== + * timer processing of mob (timer function) + * It branches to a walk and an attack. + *------------------------------------------ + */ +static int mob_timer(int tid,unsigned int tick,int id,int data) +{ + struct mob_data *md; + struct block_list *bl; + + if( (bl=map_id2bl(id)) == NULL ){ //攻撃してきた敵がもういないのは正常のようだ + return 1; + } + + if(!bl || !bl->type || bl->type!=BL_MOB) + return 1; + + nullpo_retr(1, md=(struct mob_data*)bl); + + if(!md->bl.type || md->bl.type!=BL_MOB) + return 1; + + if(md->timer != tid){ + if(battle_config.error_log==1) + printf("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==1) + printf("mob_timer : %d ?\n",md->state.state); + break; + } + map_freeblock_unlock(); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static int mob_walktoxy_sub(struct mob_data *md) +{ + struct walkpath_data wpd; + + nullpo_retr(0, md); + + if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,md->to_x,md->to_y,md->state.walk_easy)) + return 1; + memcpy(&md->walkpath,&wpd,sizeof(wpd)); + + md->state.change_walk_target=0; + mob_changestate(md,MS_WALK,0); + clif_movemob(md); + + return 0; +} + +/*========================================== + * mob move start + *------------------------------------------ + */ +int mob_walktoxy(struct mob_data *md,int x,int y,int easy) +{ + struct walkpath_data wpd; + + nullpo_retr(0, md); + + if(md->state.state == MS_WALK && path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,x,y,easy) ) + return 1; + + md->state.walk_easy = easy; + md->to_x=x; + md->to_y=y; + if(md->state.state == MS_WALK) { + md->state.change_walk_target=1; + } else { + return mob_walktoxy_sub(md); + } + + return 0; +} + +/*========================================== + * mob spawn with delay (timer function) + *------------------------------------------ + */ +static 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) + return -1; + + if(!bl || !bl->type || bl->type!=BL_MOB) + return -1; + + nullpo_retr(-1, md=(struct mob_data*)bl); + + if(!md || md->bl.type!=BL_MOB) + return -1; + + // Processing of MOB which is not revitalized + if(md->spawndelay1==-1 && md->spawndelay2==-1 && md->n==0){ + map_deliddb(&md->bl); + if(md->lootitem) { + map_freeblock(md->lootitem); + md->lootitem=NULL; + } + map_freeblock(md); // Instead of [ of free ] + return 0; + } + + spawntime1=md->last_spawntime+md->spawndelay1; + spawntime2=md->last_deadtime+md->spawndelay2; + spawntime3=gettick()+5000; + // spawntime = max(spawntime1,spawntime2,spawntime3); + if(DIFF_TICK(spawntime1,spawntime2)>0){ + spawntime=spawntime1; + } else { + spawntime=spawntime2; + } + if(DIFF_TICK(spawntime3,spawntime)>0){ + spawntime=spawntime3; + } + + add_timer(spawntime,mob_delayspawn,id,0); + return 0; +} + +/*========================================== + * Mob spawning. Initialization is also variously here. + *------------------------------------------ + */ +int mob_spawn(int id) +{ + int x=0,y=0,i=0,c; + unsigned int tick = gettick(); + struct mob_data *md; + struct block_list *bl; + + nullpo_retr(-1, bl=map_id2bl(id)); + + if(!bl || !bl->type || bl->type!=BL_MOB) + return -1; + + nullpo_retr(-1, md=(struct mob_data*)bl); + + if(!md || !md->bl.type || md->bl.type!=BL_MOB) + return -1; + + md->last_spawntime=tick; + if( md->bl.prev!=NULL ){ +// clif_clearchar_area(&md->bl,3); + skill_unit_out_all(&md->bl,gettick(),1); + map_delblock(&md->bl); + } + else + md->class = md->base_class; + + 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++; + } while(((c=map_getcell(md->bl.m,x,y))==1 || c==5) && i<50); + + if(i>=50){ +// if(battle_config.error_log==1) +// printf("MOB spawn error %d @ %s\n",id,map[md->bl.m].name); + add_timer(tick+5000,mob_delayspawn,id,0); + return 1; + } + + md->to_x=md->bl.x=x; + md->to_y=md->bl.y=y; + md->dir=0; + + map_addblock(&md->bl); + + memset(&md->state,0,sizeof(md->state)); + md->attacked_id = 0; + md->target_id = 0; + md->move_fail_count = 0; + + if(!md->speed) + md->speed = mob_db[md->class].speed; + md->def_ele = mob_db[md->class].element; + md->master_id=0; + md->master_dist=0; + + md->state.state = MS_IDLE; + md->state.skillstate = MSS_IDLE; + md->timer = -1; + md->last_thinktime = tick; + md->next_walktime = tick+rand()%50+5000; + md->attackabletime = tick; + md->canmove_tick = tick; + + md->sg_count=0; + md->deletetimer=-1; + + md->skilltimer=-1; + for(i=0,c=tick-1000*3600*10;i<MAX_MOBSKILL;i++) + md->skilldelay[i] = c; + md->skillid=0; + md->skilllv=0; + + memset(md->dmglog,0,sizeof(md->dmglog)); + if(md->lootitem) + memset(md->lootitem,0,sizeof(md->lootitem)); + md->lootitem_count = 0; + + for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) + md->skilltimerskill[i].timer = -1; + + for(i=0;i<MAX_STATUSCHANGE;i++) { + md->sc_data[i].timer=-1; + md->sc_data[i].val1 = md->sc_data[i].val2 = md->sc_data[i].val3 = md->sc_data[i].val4 =0; + } + md->sc_count=0; + md->opt1=md->opt2=md->opt3=md->option=0; + + memset(md->skillunit,0,sizeof(md->skillunit)); + memset(md->skillunittick,0,sizeof(md->skillunittick)); + + md->hp = battle_get_max_hp(&md->bl); + if(md->hp<=0){ + mob_makedummymobdb(md->class); + md->hp = battle_get_max_hp(&md->bl); + } + + clif_spawnmob(md); + + return 0; +} + +/*========================================== + * Distance calculation between two points + *------------------------------------------ + */ +static int distance(int x0,int y0,int x1,int y1) +{ + int dx,dy; + + dx=abs(x0-x1); + dy=abs(y0-y1); + return dx>dy ? dx : dy; +} + +/*========================================== + * The stop of MOB's attack + *------------------------------------------ + */ +int mob_stopattack(struct mob_data *md) +{ + md->target_id=0; + md->state.targettype = NONE_ATTACKABLE; + md->attacked_id=0; + return 0; +} +/*========================================== + * The stop of MOB's walking + *------------------------------------------ + */ +int mob_stop_walking(struct mob_data *md,int type) +{ + nullpo_retr(0, md); + + + if(md->state.state == MS_WALK || md->state.state == MS_IDLE) { + int dx=0,dy=0; + + md->walkpath.path_len=0; + if(type&4){ + dx=md->to_x-md->bl.x; + if(dx<0) + dx=-1; + else if(dx>0) + dx=1; + dy=md->to_y-md->bl.y; + if(dy<0) + dy=-1; + else if(dy>0) + dy=1; + } + md->to_x=md->bl.x+dx; + md->to_y=md->bl.y+dy; + if(dx!=0 || dy!=0){ + mob_walktoxy_sub(md); + return 0; + } + mob_changestate(md,MS_IDLE,0); + } + if(type&0x01) + clif_fixmobpos(md); + if(type&0x02) { + int delay=battle_get_dmotion(&md->bl); + unsigned int tick = gettick(); + if(md->canmove_tick < tick) + md->canmove_tick = tick + delay; + } + + return 0; +} + +/*========================================== + * Reachability to a Specification ID existence place + *------------------------------------------ + */ +int mob_can_reach(struct mob_data *md,struct block_list *bl,int range) +{ + int dx,dy; + struct walkpath_data wpd; + int i; + + nullpo_retr(0, md); + nullpo_retr(0, bl); + + dx=abs(bl->x - md->bl.x); + dy=abs(bl->y - md->bl.y); + + //=========== guildcastle guardian no search start=========== + //when players are the guild castle member not attack them ! + if(md->class == 1285 || md->class == 1286 || md->class == 1287){ + struct map_session_data *sd; + struct guild *g=NULL; + struct guild_castle *gc=guild_mapname2gc(map[bl->m].name); + + if(gc && agit_flag==0) // Guardians will not attack during non-woe time [Valaris] + return 0; // end addition [Valaris] + + if(bl && bl->type == BL_PC){ + if((sd=(struct map_session_data *)bl) == NULL){ + printf("mob_can_reach nullpo\n"); + return 0; + } + + if(gc && sd && sd->status.guild_id && sd->status.guild_id>0) { + g=guild_search(sd->status.guild_id); // don't attack guild members [Valaris] + if(g && g->guild_id > 0 && g->guild_id == gc->guild_id) + return 0; + if(g && gc && guild_isallied(g,gc)) + return 0; + + } + } + } + //========== guildcastle guardian no search eof============== + + if(bl && bl->type == BL_PC && battle_config.monsters_ignore_gm==1) { // option to have monsters ignore GMs [Valaris] + struct map_session_data *sd; + if((sd=(struct map_session_data *)bl) != NULL && pc_isGM(sd)) + return 0; + } + + if( md->bl.m != bl->m) // 違うャbプ + return 0; + + if( range>0 && range < ((dx>dy)?dx:dy) ) // 遠すぎる + return 0; + + if( md->bl.x==bl->x && md->bl.y==bl->y ) // 同じャX + return 1; + + // Obstacle judging + wpd.path_len=0; + wpd.path_pos=0; + wpd.path_half=0; + if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x,bl->y,0)!=-1) + return 1; + + if(bl->type!=BL_PC && bl->type!=BL_MOB) + return 0; + + // It judges whether it can adjoin or not. + dx=(dx>0)?1:((dx<0)?-1:0); + dy=(dy>0)?1:((dy<0)?-1:0); + if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-dx,bl->y-dy,0)!=-1) + return 1; + for(i=0;i<9;i++){ + if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-1+i/3,bl->y-1+i%3,0)!=-1) + return 1; + } + return 0; +} + +/*========================================== + * Determination for an attack of a monster + *------------------------------------------ + */ +int mob_target(struct mob_data *md,struct block_list *bl,int dist) +{ + struct map_session_data *sd; + struct status_change *sc_data; + short *option; + int mode,race; + + nullpo_retr(0, md); + nullpo_retr(0, bl); + + sc_data = battle_get_sc_data(bl); + option = battle_get_option(bl); + race=mob_db[md->class].race; + + if(!md->mode){ + mode=mob_db[md->class].mode; + }else{ + mode=md->mode; + } + if(!(mode&0x80)) { + md->target_id = 0; + return 0; + } + // Nothing will be carried out if there is no mind of changing TAGE by TAGE ending. + if( (md->target_id > 0 && md->state.targettype == ATTACKABLE) && ( !(mode&0x04) || rand()%100>25) ) + return 0; + + if(mode&0x20 || // Coercion is exerted if it is MVPMOB. + (sc_data && sc_data[SC_TRICKDEAD].timer == -1 && + ( (option && !(*option&0x06) ) || race==4 || race==6) ) ){ + if(bl->type == BL_PC) { + nullpo_retr(0, sd = (struct map_session_data *)bl); + if(sd->invincible_timer != -1 || pc_isinvisible(sd)) + return 0; + if(!(mode&0x20) && race!=4 && race!=6 && sd->state.gangsterparadise) + return 0; + } + + md->target_id=bl->id; // Since there was no disturbance, it locks on to target. + if(bl->type == BL_PC || bl->type == BL_MOB) + md->state.targettype = ATTACKABLE; + else + md->state.targettype = NONE_ATTACKABLE; + md->min_chase=dist+13; + if(md->min_chase>26) + md->min_chase=26; + } + return 0; +} + +/*========================================== + * The ?? routine of an active monster + *------------------------------------------ + */ +static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap) +{ + struct map_session_data *tsd=NULL; + struct mob_data *smd,*tmd=NULL; + int mode,race,dist,*pcc; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, smd=va_arg(ap,struct mob_data *)); + nullpo_retr(0, pcc=va_arg(ap,int *)); + + if(bl->type==BL_PC) + tsd=(struct map_session_data *)bl; + else if(bl->type==BL_MOB) + tmd=(struct mob_data *)bl; + else + return 0; + + //敵味方判定 + if(battle_check_target(&smd->bl,bl,BCT_ENEMY)==0) + return 0; + + if(!smd->mode) + mode=mob_db[smd->class].mode; + else + mode=smd->mode; + + // アクティブでターゲット射程内にいるなら、ロックする + if( mode&0x04 ){ + race=mob_db[smd->class].race; + //対象がPCの場合 + if(tsd && + !pc_isdead(tsd) && + tsd->bl.m == smd->bl.m && + tsd->invincible_timer == -1 && + !pc_isinvisible(tsd) && + (dist=distance(smd->bl.x,smd->bl.y,tsd->bl.x,tsd->bl.y))<9 + ) + { + if(mode&0x20 || + (tsd->sc_data[SC_TRICKDEAD].timer == -1 && + ((!pc_ishiding(tsd) && !tsd->state.gangsterparadise) || race==4 || race==6))){ // 妨害がないか判定 + if( mob_can_reach(smd,bl,12) && // 到達可能性判定 + rand()%1000<1000/(++(*pcc)) ){ // 範囲内PCで等確率にする + smd->target_id=tsd->bl.id; + smd->state.targettype = ATTACKABLE; + smd->min_chase=13; + } + } + } + //対象がMobの場合 + else if(tmd && + tmd->bl.m == smd->bl.m && + (dist=distance(smd->bl.x,smd->bl.y,tmd->bl.x,tmd->bl.y))<9 + ) + { + if( mob_can_reach(smd,bl,12) && // 到達可能性判定 + rand()%1000<1000/(++(*pcc)) ){ // 範囲内で等確率にする + smd->target_id=bl->id; + smd->state.targettype = ATTACKABLE; + smd->min_chase=13; + } + } + } + return 0; +} + +/*========================================== + * loot monster item search + *------------------------------------------ + */ +static int mob_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) +{ + struct mob_data* md; + int mode,dist,*itc; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md=va_arg(ap,struct mob_data *)); + nullpo_retr(0, itc=va_arg(ap,int *)); + + if(!md->mode){ + mode=mob_db[md->class].mode; + }else{ + mode=md->mode; + } + + if( !md->target_id && mode&0x02){ + if(!md->lootitem || (battle_config.monster_loot_type == 1 && md->lootitem_count >= LOOTITEM_SIZE) ) + return 0; + if(bl->m == md->bl.m && (dist=distance(md->bl.x,md->bl.y,bl->x,bl->y))<9){ + if( mob_can_reach(md,bl,12) && // Reachability judging + 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=13; + } + } + } + return 0; +} + +/*========================================== + * The ?? routine of a link monster + *------------------------------------------ + */ +static int mob_ai_sub_hard_linksearch(struct block_list *bl,va_list ap) +{ + struct mob_data *tmd; + struct mob_data* md; + struct block_list *target; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, tmd=(struct mob_data *)bl); + nullpo_retr(0, md=va_arg(ap,struct mob_data *)); + nullpo_retr(0, target=va_arg(ap,struct block_list *)); + + // same family free in a range at a link monster -- it will be made to lock if MOB is +/* if( (md->target_id > 0 && md->state.targettype == ATTACKABLE) && mob_db[md->class].mode&0x08){ + if( tmd->class==md->class && (!tmd->target_id || md->state.targettype == NONE_ATTACKABLE) && tmd->bl.m == md->bl.m){ + if( mob_can_reach(tmd,target,12) ){ // Reachability judging + tmd->target_id=md->target_id; + tmd->state.targettype = ATTACKABLE; + tmd->min_chase=13; + } + } + }*/ + if( md->attacked_id > 0 && mob_db[md->class].mode&0x08){ + if( tmd->class==md->class && tmd->bl.m == md->bl.m && (!tmd->target_id || md->state.targettype == NONE_ATTACKABLE)){ + if( mob_can_reach(tmd,target,12) ){ // Reachability judging + tmd->target_id=md->attacked_id; + tmd->state.targettype = ATTACKABLE; + tmd->min_chase=13; + } + } + } + + return 0; +} +/*========================================== + * Processing of slave monsters + *------------------------------------------ + */ +static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick) +{ + struct mob_data *mmd=NULL; + struct block_list *bl; + int mode,race,old_dist; + + nullpo_retr(0, md); + + if((bl=map_id2bl(md->master_id)) != NULL ) + mmd=(struct mob_data *)bl; + + mode=mob_db[md->class].mode; + + // It is not main monster/leader. + if(!mmd || mmd->bl.type!=BL_MOB || mmd->bl.id!=md->master_id) + return 0; + + // Since it is in the map on which the master is not, teleport is carried out and it pursues. + if( mmd->bl.m != md->bl.m ){ + mob_warp(md,mmd->bl.m,mmd->bl.x,mmd->bl.y,3); + md->state.master_check = 1; + return 0; + } + + // Distance with between slave and master is measured. + old_dist=md->master_dist; + md->master_dist=distance(md->bl.x,md->bl.y,mmd->bl.x,mmd->bl.y); + + // Since the master was in near immediately before, teleport is carried out and it pursues. + if( old_dist<10 && md->master_dist>18){ + mob_warp(md,-1,mmd->bl.x,mmd->bl.y,3); + md->state.master_check = 1; + return 0; + } + + // Although there is the master, since it is somewhat far, it approaches. + if((!md->target_id || md->state.targettype == NONE_ATTACKABLE) && mob_can_move(md) && + (md->walkpath.path_pos>=md->walkpath.path_len || md->walkpath.path_len==0) && md->master_dist<15){ + int i=0,dx,dy,ret; + if(md->master_dist>4) { + do { + if(i<=5){ + dx=mmd->bl.x - md->bl.x; + dy=mmd->bl.y - md->bl.y; + if(dx<0) dx+=(rand()%( (dx<-3)?3:-dx )+1); + else if(dx>0) dx-=(rand()%( (dx>3)?3:dx )+1); + if(dy<0) dy+=(rand()%( (dy<-3)?3:-dy )+1); + else if(dy>0) dy-=(rand()%( (dy>3)?3:dy )+1); + }else{ + dx=mmd->bl.x - md->bl.x + rand()%7 - 3; + dy=mmd->bl.y - md->bl.y + rand()%7 - 3; + } + + ret=mob_walktoxy(md,md->bl.x+dx,md->bl.y+dy,0); + i++; + } while(ret && i<10); + } + else { + do { + dx = rand()%9 - 5; + dy = rand()%9 - 5; + if( dx == 0 && dy == 0) { + dx = (rand()%1)? 1:-1; + dy = (rand()%1)? 1:-1; + } + dx += mmd->bl.x; + dy += mmd->bl.y; + + ret=mob_walktoxy(md,mmd->bl.x+dx,mmd->bl.y+dy,0); + i++; + } while(ret && i<10); + } + + md->next_walktime=tick+500; + md->state.master_check = 1; + } + + // There is the master, the master locks a target and he does not lock. + if( (mmd->target_id>0 && mmd->state.targettype == ATTACKABLE) && (!md->target_id || md->state.targettype == NONE_ATTACKABLE) ){ + struct map_session_data *sd=map_id2sd(mmd->target_id); + if(sd!=NULL && !pc_isdead(sd) && sd->invincible_timer == -1 && !pc_isinvisible(sd)){ + + race=mob_db[md->class].race; + if(mode&0x20 || + (sd->sc_data[SC_TRICKDEAD].timer == -1 && + ( (!pc_ishiding(sd) && !sd->state.gangsterparadise) || race==4 || race==6) ) ){ // 妨害がないか判定 + + md->target_id=sd->bl.id; + md->state.targettype = ATTACKABLE; + md->min_chase=5+distance(md->bl.x,md->bl.y,sd->bl.x,sd->bl.y); + md->state.master_check = 1; + } + } + } + + // There is the master, the master locks a target and he does not lock. +/* if( (md->target_id>0 && mmd->state.targettype == ATTACKABLE) && (!mmd->target_id || mmd->state.targettype == NONE_ATTACKABLE) ){ + struct map_session_data *sd=map_id2sd(md->target_id); + if(sd!=NULL && !pc_isdead(sd) && sd->invincible_timer == -1 && !pc_isinvisible(sd)){ + + race=mob_db[mmd->class].race; + if(mode&0x20 || + (sd->sc_data[SC_TRICKDEAD].timer == -1 && + (!(sd->status.option&0x06) || race==4 || race==6) + ) ){ // It judges whether there is any disturbance. + + mmd->target_id=sd->bl.id; + mmd->state.targettype = ATTACKABLE; + mmd->min_chase=5+distance(mmd->bl.x,mmd->bl.y,sd->bl.x,sd->bl.y); + } + } + }*/ + + return 0; +} + +/*========================================== + * A lock of target is stopped and mob moves to a standby state. + *------------------------------------------ + */ +static int mob_unlocktarget(struct mob_data *md,int tick) +{ + nullpo_retr(0, md); + + md->target_id=0; + md->state.targettype = NONE_ATTACKABLE; + md->state.skillstate=MSS_IDLE; + md->next_walktime=tick+rand()%3000+3000; + return 0; +} +/*========================================== + * Random walk + *------------------------------------------ + */ +static int mob_randomwalk(struct mob_data *md,int tick) +{ + const int retrycount=20; + int speed; + + nullpo_retr(0, md); + + speed=battle_get_speed(&md->bl); + if(DIFF_TICK(md->next_walktime,tick)<0){ + int i,x,y,c,d=12-md->move_fail_count; + if(d<5) d=5; + for(i=0;i<retrycount;i++){ // Search of a movable place + int r=rand(); + x=md->bl.x+r%(d*2+1)-d; + y=md->bl.y+r/(d*2+1)%(d*2+1)-d; + if((c=map_getcell(md->bl.m,x,y))!=1 && c!=5 && mob_walktoxy(md,x,y,1)==0){ + md->move_fail_count=0; + break; + } + if(i+1>=retrycount){ + md->move_fail_count++; + if(md->move_fail_count>1000){ + if(battle_config.error_log==1) + printf("MOB cant move. random spawn %d, 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,*tmd=NULL; + struct map_session_data *tsd=NULL; + struct block_list *tbl=NULL; + struct flooritem_data *fitem; + unsigned int tick; + int i,dx,dy,ret,dist; + int attack_type=0; + int mode,race; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md=(struct mob_data*)bl); + + tick=va_arg(ap,unsigned int); + + + if(DIFF_TICK(tick,md->last_thinktime)<MIN_MOBTHINKTIME) + return 0; + md->last_thinktime=tick; + + if( md->skilltimer!=-1 || md->bl.prev==NULL ){ // Under a skill aria and death + if(DIFF_TICK(tick,md->next_walktime)>MIN_MOBTHINKTIME) + md->next_walktime=tick; + return 0; + } + + if(!md->mode) + mode=mob_db[md->class].mode; + else + mode=md->mode; + + race=mob_db[md->class].race; + + // Abnormalities + if((md->opt1 > 0 && md->opt1 != 6) || md->state.state==MS_DELAY || md->sc_data[SC_BLADESTOP].timer != -1) + return 0; + + if(!(mode&0x80) && md->target_id > 0) + md->target_id = 0; + + if(md->attacked_id > 0 && mode&0x08){ // Link monster + struct map_session_data *asd=map_id2sd(md->attacked_id); + if(asd){ + if(asd->invincible_timer == -1 && !pc_isinvisible(asd)){ + map_foreachinarea(mob_ai_sub_hard_linksearch,md->bl.m, + md->bl.x-13,md->bl.y-13, + md->bl.x+13,md->bl.y+13, + BL_MOB,md,&asd->bl); + } + } + } + + // It checks to see it was attacked first (if active, it is target change at 25% of probability). + if( mode>0 && md->attacked_id>0 && (!md->target_id || md->state.targettype == NONE_ATTACKABLE + || (mode&0x04 && rand()%100<25 ) ) ){ + struct block_list *abl=map_id2bl(md->attacked_id); + struct map_session_data *asd=NULL; + if(abl){ + if(abl->type==BL_PC) + asd=(struct map_session_data *)abl; + if(asd==NULL || md->bl.m != abl->m || abl->prev == NULL || asd->invincible_timer != -1 || pc_isinvisible(asd) || + (dist=distance(md->bl.x,md->bl.y,abl->x,abl->y))>=32 || battle_check_target(bl,abl,BCT_ENEMY)==0) + md->attacked_id=0; + else { + md->target_id=md->attacked_id; // set target + md->state.targettype = ATTACKABLE; + attack_type = 1; + md->attacked_id=0; + md->min_chase=dist+13; + if(md->min_chase>26) + md->min_chase=26; + } + } + } + + md->state.master_check = 0; + // Processing of slave monster + if( md->master_id > 0 && md->state.special_mob_ai==0) + mob_ai_sub_hard_slavemob(md,tick); + + // アクティヴモンスターの策敵 (?? of a bitter taste TIVU monster) + if( (!md->target_id || md->state.targettype == NONE_ATTACKABLE) && mode&0x04 && !md->state.master_check && + battle_config.monster_active_enable==1){ + i=0; + if(md->state.special_mob_ai){ + map_foreachinarea(mob_ai_sub_hard_activesearch,md->bl.m, + md->bl.x-AREA_SIZE*2,md->bl.y-AREA_SIZE*2, + md->bl.x+AREA_SIZE*2,md->bl.y+AREA_SIZE*2, + 0,md,&i); + }else{ + map_foreachinarea(mob_ai_sub_hard_activesearch,md->bl.m, + md->bl.x-AREA_SIZE*2,md->bl.y-AREA_SIZE*2, + md->bl.x+AREA_SIZE*2,md->bl.y+AREA_SIZE*2, + BL_PC,md,&i); + } + } + + // The item search of a route monster + if( !md->target_id && mode&0x02 && !md->state.master_check){ + i=0; + map_foreachinarea(mob_ai_sub_hard_lootsearch,md->bl.m, + md->bl.x-AREA_SIZE*2,md->bl.y-AREA_SIZE*2, + md->bl.x+AREA_SIZE*2,md->bl.y+AREA_SIZE*2, + BL_ITEM,md,&i); + } + + // It will attack, if the candidate for an attack is. + if(md->target_id > 0){ + if((tbl=map_id2bl(md->target_id))){ + if(tbl->type==BL_PC) + tsd=(struct map_session_data *)tbl; + else if(tbl->type==BL_MOB) + tmd=(struct mob_data *)tbl; + if(tsd || tmd) { + if(tbl->m != md->bl.m || tbl->prev == NULL || (dist=distance(md->bl.x,md->bl.y,tbl->x,tbl->y))>=md->min_chase) + mob_unlocktarget(md,tick); // 別マップか、視界外 + else if( tsd && !(mode&0x20) && (tsd->sc_data[SC_TRICKDEAD].timer != -1 || ((pc_ishiding(tsd) || tsd->state.gangsterparadise) && race!=4 && race!=6)) ) + mob_unlocktarget(md,tick); // スキルなどによる策敵妨害 + else if(!battle_check_range(&md->bl,tbl,mob_db[md->class].range)){ + // 攻撃範囲外なので移動 + if(!(mode&1)){ // 移動しないモード + mob_unlocktarget(md,tick); + return 0; + } + if( !mob_can_move(md) ) // 動けない状態にある + return 0; + md->state.skillstate=MSS_CHASE; // 突撃時スキル + mobskill_use(md,tick,-1); +// if(md->timer != -1 && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tsd->bl.x,tsd->bl.y)<2) ) + if(md->timer != -1 && md->state.state!=MS_ATTACK && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tbl->x,tbl->y)<2) ) + return 0; // 既に移動中 + if( !mob_can_reach(md,tbl,(md->min_chase>13)?md->min_chase:13) ) + mob_unlocktarget(md,tick); // 移動できないのでタゲ解除(IWとか?) + else{ + // 追跡 + md->next_walktime=tick+500; + i=0; + do { + if(i==0){ // 最初はAEGISと同じ方法で検索 + dx=tbl->x - md->bl.x; + dy=tbl->y - md->bl.y; + if(dx<0) dx++; + else if(dx>0) dx--; + if(dy<0) dy++; + else if(dy>0) dy--; + }else{ // だめならAthena式(ランダム) + dx=tbl->x - md->bl.x + rand()%3 - 1; + dy=tbl->y - md->bl.y + rand()%3 - 1; + } + /* if(path_search(&md->walkpath,md->bl.m,md->bl.x,md->bl.y,md->bl.x+dx,md->bl.y+dy,0)){ + dx=tsd->bl.x - md->bl.x; + dy=tsd->bl.y - md->bl.y; + if(dx<0) dx--; + else if(dx>0) dx++; + if(dy<0) dy--; + else if(dy>0) dy++; + }*/ + ret=mob_walktoxy(md,md->bl.x+dx,md->bl.y+dy,0); + i++; + } while(ret && i<5); + + if(ret){ // 移動不可能な所からの攻撃なら2歩下る + if(dx<0) dx=2; + else if(dx>0) dx=-2; + if(dy<0) dy=2; + else if(dy>0) dy=-2; + mob_walktoxy(md,md->bl.x+dx,md->bl.y+dy,0); + } + } + } else { // 攻撃射程範囲内 + md->state.skillstate=MSS_ATTACK; + if(md->state.state==MS_WALK) + mob_stop_walking(md,1); // 歩行中なら停止 + if(md->state.state==MS_ATTACK) + return 0; // 既に攻撃中 + mob_changestate(md,MS_ATTACK,attack_type); + +/* if(mode&0x08){ // リンクモンスター + map_foreachinarea(mob_ai_sub_hard_linksearch,md->bl.m, + md->bl.x-13,md->bl.y-13, + md->bl.x+13,md->bl.y+13, + BL_MOB,md,&tsd->bl); + }*/ + } + return 0; + }else{ // ルートモンスター処理 + if(tbl == NULL || tbl->type != BL_ITEM ||tbl->m != md->bl.m || + (dist=distance(md->bl.x,md->bl.y,tbl->x,tbl->y))>=md->min_chase || !md->lootitem){ + // 遠すぎるかアイテムがなくなった + mob_unlocktarget(md,tick); + if(md->state.state==MS_WALK) + mob_stop_walking(md,1); // 歩行中なら停止 + }else if(dist){ + if(!(mode&1)){ // 移動しないモード + mob_unlocktarget(md,tick); + return 0; + } + if( !mob_can_move(md) ) // 動けない状態にある + return 0; + md->state.skillstate=MSS_LOOT; // ルート時スキル使用 + mobskill_use(md,tick,-1); +// if(md->timer != -1 && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tbl->x,tbl->y)<2) ) + if(md->timer != -1 && md->state.state!=MS_ATTACK && (DIFF_TICK(md->next_walktime,tick)<0 || distance(md->to_x,md->to_y,tbl->x,tbl->y) <= 0)) + return 0; // 既に移動中 + md->next_walktime=tick+500; + dx=tbl->x - md->bl.x; + dy=tbl->y - md->bl.y; +/* if(path_search(&md->walkpath,md->bl.m,md->bl.x,md->bl.y,md->bl.x+dx,md->bl.y+dy,0)){ + dx=tbl->x - md->bl.x; + dy=tbl->y - md->bl.y; + }*/ + ret=mob_walktoxy(md,md->bl.x+dx,md->bl.y+dy,0); + if(ret) + mob_unlocktarget(md,tick);// 移動できないのでタゲ解除(IWとか?) + }else{ // アイテムまでたどり着いた + if(md->state.state==MS_ATTACK) + return 0; // 攻撃中 + if(md->state.state==MS_WALK) + mob_stop_walking(md,1); // 歩行中なら停止 + fitem = (struct flooritem_data *)tbl; + if(md->lootitem_count < LOOTITEM_SIZE) + memcpy(&md->lootitem[md->lootitem_count++],&fitem->item_data,sizeof(md->lootitem[0])); + else if(battle_config.monster_loot_type == 1 && md->lootitem_count >= LOOTITEM_SIZE) { + mob_unlocktarget(md,tick); + return 0; + } + else { + if(md->lootitem[0].card[0] == (short)0xff00) + intif_delete_petdata(*((long *)(&md->lootitem[0].card[1]))); + for(i=0;i<LOOTITEM_SIZE-1;i++) + memcpy(&md->lootitem[i],&md->lootitem[i+1],sizeof(md->lootitem[0])); + memcpy(&md->lootitem[LOOTITEM_SIZE-1],&fitem->item_data,sizeof(md->lootitem[0])); + } + map_clearflooritem(tbl->id); + mob_unlocktarget(md,tick); + } + return 0; + } + }else{ + mob_unlocktarget(md,tick); + if(md->state.state==MS_WALK) + mob_stop_walking(md,4); // 歩行中なら停止 + return 0; + } + } + + // It is skill use at the time of /standby at the time of a walk. + if( mobskill_use(md,tick,-1) ) + return 0; + + // 歩行処理 + if( mode&1 && mob_can_move(md) && // 移動可能MOB&動ける状態にある + (md->master_id==0 || md->state.special_mob_ai || md->master_dist>10) ){ //取り巻きMOBじゃない + + if( DIFF_TICK(md->next_walktime,tick) > + 7000 && + (md->walkpath.path_len==0 || md->walkpath.path_pos>=md->walkpath.path_len) ){ + md->next_walktime = tick + 3000*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(void * key,void * data,va_list app) +{ + struct mob_data *md=data; + unsigned int tick; + va_list ap; + + nullpo_retr(0, md); + nullpo_retr(0, app); + nullpo_retr(0, ap=va_arg(app,va_list)); + + if(md==NULL) + return 0; + + if(!md->bl.type || md->bl.type!=BL_MOB) + return 0; + + 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->skilltimer!=-1){ + if(DIFF_TICK(tick,md->next_walktime)>MIN_MOBTHINKTIME*10) + md->next_walktime=tick; + return 0; + } + + if(DIFF_TICK(md->next_walktime,tick)<0 && + (mob_db[md->class].mode&1) && mob_can_move(md) ){ + + if( map[md->bl.m].users>0 ){ + // Since PC is in the same map, somewhat better negligent processing is carried out. + + // It sometimes moves. + if(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 && + mob_db[md->class].mexp <= 0 && !(mob_db[md->class].mode & 0x20)) + mob_spawn(md->bl.id); + + }else{ + // Since PC is not even in the same map, suitable processing is carried out even if it takes. + + // MOB which is not BOSS which is not Summons MOB, either -- a case -- sometimes -- leaping + if( rand()%1000<MOB_LAZYWARPPERC && md->x0<=0 && md->master_id!=0 && + mob_db[md->class].mexp <= 0 && !(mob_db[md->class].mode & 0x20)) + 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; + int nameid,amount; + struct map_session_data *first_sd,*second_sd,*third_sd; +}; + +struct delay_item_drop2 { + int m,x,y; + struct item item_data; + struct map_session_data *first_sd,*second_sd,*third_sd; +}; + +/*========================================== + * item drop with delay (timer function) + *------------------------------------------ + */ +static int mob_delay_item_drop(int tid,unsigned int tick,int id,int data) +{ + struct delay_item_drop *ditem; + struct item temp_item; + int flag; + + nullpo_retr(0, ditem=(struct delay_item_drop *)id); + + memset(&temp_item,0,sizeof(temp_item)); + temp_item.nameid = ditem->nameid; + temp_item.amount = ditem->amount; + temp_item.identify = !itemdb_isequip3(temp_item.nameid); + + if(battle_config.item_auto_get==1){ + if(ditem->first_sd && (flag = pc_additem(ditem->first_sd,&temp_item,ditem->amount))){ + clif_additem(ditem->first_sd,0,0,flag); + map_addflooritem(&temp_item,1,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0); + } + free(ditem); + return 0; + } + + map_addflooritem(&temp_item,1,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0); + + free(ditem); + return 0; +} + +/*========================================== + * item drop (timer function)-lootitem with delay + *------------------------------------------ + */ +static int mob_delay_item_drop2(int tid,unsigned int tick,int id,int data) +{ + struct delay_item_drop2 *ditem; + int flag; + + nullpo_retr(0, ditem=(struct delay_item_drop2 *)id); + + if(battle_config.item_auto_get==1){ + if(ditem->first_sd && (flag = pc_additem(ditem->first_sd,&ditem->item_data,ditem->item_data.amount))){ + clif_additem(ditem->first_sd,0,0,flag); + map_addflooritem(&ditem->item_data,ditem->item_data.amount,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0); + } + free(ditem); + return 0; + } + + map_addflooritem(&ditem->item_data,ditem->item_data.amount,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0); + + free(ditem); + return 0; +} + +/*========================================== + * mob data is erased. + *------------------------------------------ + */ +int mob_delete(struct mob_data *md) +{ + nullpo_retr(1, md); + + if(md->bl.prev == NULL) + return 1; + mob_changestate(md,MS_DEAD,0); + clif_clearchar_area(&md->bl,1); + map_delblock(&md->bl); + if(mob_get_viewclass(md->class) <= 1000) + clif_clearchar_delay(gettick()+3000,&md->bl,0); + mob_deleteslave(md); + mob_setdelayspawn(md->bl.id); + return 0; +} + +int mob_catch_delete(struct mob_data *md,int type) +{ + nullpo_retr(1, md); + + if(md->bl.prev == NULL) + return 1; + mob_changestate(md,MS_DEAD,0); + clif_clearchar_area(&md->bl,type); + map_delblock(&md->bl); + mob_setdelayspawn(md->bl.id); + return 0; +} + +int mob_timer_delete(int tid, unsigned int tick, int id, int data) +{ + struct block_list *bl=map_id2bl(id); + struct mob_data *md; + + nullpo_retr(0, bl); + + md = (struct mob_data *)bl; + mob_catch_delete(md,3); + 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_foreachinarea(mob_deleteslave_sub, md->bl.m, + 0,0,map[md->bl.m].xs,map[md->bl.m].ys, + BL_MOB,md->bl.id); + return 0; +} + +/*========================================== + * It is the damage of sd to damage to md. + *------------------------------------------ + */ +int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type) +{ + int i,count,minpos,mindmg; + struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE]; + struct { + struct party *p; + int id,base_exp,job_exp; + } pt[DAMAGELOG_SIZE]; + int pnum=0; + int mvp_damage,max_hp; + unsigned int tick = gettick(); + struct map_session_data *mvp_sd = NULL, *second_sd = NULL,*third_sd = NULL; + double dmg_rate,tdmg,temp; + struct item item; + int ret; + int drop_rate; + int skill,sp; + + nullpo_retr(0, md); //srcはNULLで呼ばれる場合もあるので、他でチェック + + max_hp = battle_get_max_hp(&md->bl); + + if(src && src->type == BL_PC) { + sd = (struct map_session_data *)src; + mvp_sd = sd; + } + +// if(battle_config.battle_log) +// printf("mob_damage %d %d %d\n",md->hp,max_hp,damage); + if(md->bl.prev==NULL){ + if(battle_config.error_log==1) + printf("mob_damage : BlockError!!\n"); + return 0; + } + + if(md->state.state==MS_DEAD || md->hp<=0) { + if(md->bl.prev != NULL) { + mob_changestate(md,MS_DEAD,0); + mobskill_use(md,tick,-1); // It is skill at the time of death. + clif_clearchar_area(&md->bl,1); + map_delblock(&md->bl); + mob_setdelayspawn(md->bl.id); + } + return 0; + } + + if(md->sc_data[SC_ENDURE].timer == -1) + mob_stop_walking(md,3); + if(damage > max_hp>>2) + skill_stop_dancing(&md->bl,0); + + if(md->hp > max_hp) + md->hp = max_hp; + + // The amount of overkill rounds to hp. + if(damage>md->hp) + damage=md->hp; + + if(!(type&2)) { + if(sd!=NULL){ + for(i=0,minpos=0,mindmg=0x7fffffff;i<DAMAGELOG_SIZE;i++){ + if(md->dmglog[i].id==sd->bl.id) + break; + if(md->dmglog[i].id==0){ + minpos=i; + mindmg=0; + } + else if(md->dmglog[i].dmg<mindmg){ + minpos=i; + mindmg=md->dmglog[i].dmg; + } + } + if(i<DAMAGELOG_SIZE) + md->dmglog[i].dmg+=damage; + else { + md->dmglog[minpos].id=sd->bl.id; + md->dmglog[minpos].dmg=damage; + } + + if(md->attacked_id <= 0 && md->state.special_mob_ai==0) + md->attacked_id = sd->bl.id; + } + if(src && src->type == BL_PET && battle_config.pet_attack_exp_to_master==1) { + struct pet_data *pd = (struct pet_data *)src; + nullpo_retr(0, pd); + for(i=0,minpos=0,mindmg=0x7fffffff;i<DAMAGELOG_SIZE;i++){ + if(md->dmglog[i].id==pd->msd->bl.id) + break; + if(md->dmglog[i].id==0){ + minpos=i; + mindmg=0; + } + else if(md->dmglog[i].dmg<mindmg){ + minpos=i; + mindmg=md->dmglog[i].dmg; + } + } + if(i<DAMAGELOG_SIZE) + md->dmglog[i].dmg+=(damage*battle_config.pet_attack_exp_rate)/100; + else { + md->dmglog[minpos].id=pd->msd->bl.id; + md->dmglog[minpos].dmg=(damage*battle_config.pet_attack_exp_rate)/100; + } + } + if(src && src->type == BL_MOB && ((struct mob_data*)src)->state.special_mob_ai){ + struct mob_data *md2 = (struct mob_data *)src; + nullpo_retr(0, md2); + for(i=0,minpos=0,mindmg=0x7fffffff;i<DAMAGELOG_SIZE;i++){ + if(md->dmglog[i].id==md2->master_id) + break; + if(md->dmglog[i].id==0){ + minpos=i; + mindmg=0; + } + else if(md->dmglog[i].dmg<mindmg){ + minpos=i; + mindmg=md->dmglog[i].dmg; + } + } + if(i<DAMAGELOG_SIZE) + md->dmglog[i].dmg+=damage; + else { + md->dmglog[minpos].id=md2->master_id; + md->dmglog[minpos].dmg=damage; + + if(md->attacked_id <= 0 && md->state.special_mob_ai==0) + md->attacked_id = md2->master_id; + } + } + + } + + md->hp-=damage; + + if(md->class >= 1285 && md->class <=1287) { // guardian hp update [Valaris] + struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name); + if(gc) { + + if(md->bl.id==gc->GID0) { + gc->Ghp0=md->hp; + if(gc->Ghp0<=0) { + guild_castledatasave(gc->castle_id,10,0); + guild_castledatasave(gc->castle_id,18,0); + } + } + if(md->bl.id==gc->GID1) { + gc->Ghp1=md->hp; + if(gc->Ghp1<=0) { + guild_castledatasave(gc->castle_id,11,0); + guild_castledatasave(gc->castle_id,19,0); + } + } + if(md->bl.id==gc->GID2) { + gc->Ghp2=md->hp; + if(gc->Ghp2<=0) { + guild_castledatasave(gc->castle_id,12,0); + guild_castledatasave(gc->castle_id,20,0); + } + } + if(md->bl.id==gc->GID3) { + gc->Ghp3=md->hp; + if(gc->Ghp3<=0) { + guild_castledatasave(gc->castle_id,13,0); + guild_castledatasave(gc->castle_id,21,0); + } + } + if(md->bl.id==gc->GID4) { + gc->Ghp4=md->hp; + if(gc->Ghp4<=0) { + guild_castledatasave(gc->castle_id,14,0); + guild_castledatasave(gc->castle_id,22,0); + } + } + if(md->bl.id==gc->GID5) { + gc->Ghp5=md->hp; + if(gc->Ghp5<=0) { + guild_castledatasave(gc->castle_id,15,0); + guild_castledatasave(gc->castle_id,23,0); + } + } + if(md->bl.id==gc->GID6) { + gc->Ghp6=md->hp; + if(gc->Ghp6<=0) { + guild_castledatasave(gc->castle_id,16,0); + guild_castledatasave(gc->castle_id,24,0); + } + } + if(md->bl.id==gc->GID7) { + gc->Ghp7=md->hp; + if(gc->Ghp7<=0) { + guild_castledatasave(gc->castle_id,17,0); + guild_castledatasave(gc->castle_id,25,0); + + } + } + } + } // end addition [Valaris] + + if(md->option&2 ) + skill_status_change_end(&md->bl, SC_HIDING, -1); + if(md->option&4 ) + skill_status_change_end(&md->bl, SC_CLOAKING, -1); + + if(md->state.special_mob_ai == 2){//スフィアーマイン + int skillidx=0; + + if((skillidx=mob_skillid2skillidx(md->class,NPC_SELFDESTRUCTION2))>=0){ + md->mode |= 0x1; + md->next_walktime=tick; + mobskill_use_id(md,&md->bl,skillidx);//自爆詠唱開始 + md->state.special_mob_ai++; + } + } + + if(md->hp>0){ + return 0; + } + + // ----- ここから死亡処理 ----- + + map_freeblock_lock(); + mob_changestate(md,MS_DEAD,0); + mobskill_use(md,tick,-1); // 死亡時スキル + + memset(tmpsd,0,sizeof(tmpsd)); + memset(pt,0,sizeof(pt)); + + max_hp = battle_get_max_hp(&md->bl); + + if(src && src->type == BL_MOB) + mob_unlocktarget((struct mob_data *)src,tick); + + /* ソウルドレイン */ + if(sd && (skill=pc_checkskill(sd,HW_SOULDRAIN))>0){ + clif_skill_nodamage(src,&md->bl,HW_SOULDRAIN,skill,1); + sp = (battle_get_lv(&md->bl))*(65+15*skill)/100; + if(sd->status.sp + sp > sd->status.max_sp) + sp = sd->status.max_sp - sd->status.sp; + sd->status.sp += sp; + clif_heal(sd->fd,SP_SP,sp); + } + + // map外に消えた人は計算から除くので + // overkill分は無いけどsumはmax_hpとは違う + + tdmg = 0; + for(i=0,count=0,mvp_damage=0;i<DAMAGELOG_SIZE;i++){ + if(md->dmglog[i].id==0) + continue; + tmpsd[i] = map_id2sd(md->dmglog[i].id); + if(tmpsd[i] == NULL) + continue; + count++; + if(tmpsd[i]->bl.m != md->bl.m || pc_isdead(tmpsd[i])) + continue; + + tdmg += (double)md->dmglog[i].dmg; + if(mvp_damage<md->dmglog[i].dmg){ + third_sd = second_sd; + second_sd = mvp_sd; + mvp_sd=tmpsd[i]; + mvp_damage=md->dmglog[i].dmg; + } + } + + // [MouseJstr] + if((map[md->bl.m].flag.pvp == 0) || (battle_config.pvp_exp == 1)) { + + if((double)max_hp < tdmg) + dmg_rate = ((double)max_hp) / tdmg; + else dmg_rate = 1; + + // 経験値の分配 + for(i=0;i<DAMAGELOG_SIZE;i++){ + int pid,base_exp,job_exp,flag=1; + double per; + struct party *p; + if(tmpsd[i]==NULL || tmpsd[i]->bl.m != md->bl.m) + continue; +/* jAthena's exp formula + per = ((double)md->dmglog[i].dmg)*(9.+(double)((count > 6)? 6:count))/10./((double)max_hp) * dmg_rate; + temp = ((double)mob_db[md->class].base_exp * (double)battle_config.base_exp_rate / 100. * per); + base_exp = (temp > 2147483647.)? 0x7fffffff:(int)temp; + if(mob_db[md->class].base_exp > 0 && base_exp < 1) base_exp = 1; + if(base_exp < 0) base_exp = 0; + temp = ((double)mob_db[md->class].job_exp * (double)battle_config.job_exp_rate / 100. * per); + job_exp = (temp > 2147483647.)? 0x7fffffff:(int)temp; + if(mob_db[md->class].job_exp > 0 && job_exp < 1) job_exp = 1; + if(job_exp < 0) job_exp = 0; +*/ +//eAthena's exp formula rather than jAthena's + per=(double)md->dmglog[i].dmg*256*(9+(double)((count > 6)? 6:count))/10/(double)max_hp; + if(per>512) per=512; + if(per<1) per=1; + base_exp=mob_db[md->class].base_exp*per/256; + if(base_exp < 1) base_exp = 1; + if(sd && md && battle_config.pk_mode==1 && (mob_db[md->class].lv - sd->status.base_level >= 20)) { + base_exp*=1.15; // pk_mode additional exp if monster >20 levels [Valaris] + } + if(md->state.special_mob_ai >= 1 && battle_config.alchemist_summon_reward != 1) base_exp = 0; // Added [Valaris] + job_exp=mob_db[md->class].job_exp*per/256; + if(job_exp < 1) job_exp = 1; + if(sd && md && battle_config.pk_mode==1 && (mob_db[md->class].lv - sd->status.base_level >= 20)) { + job_exp*=1.15; // pk_mode additional exp if monster >20 levels [Valaris] + } + if(md->state.special_mob_ai >= 1 && battle_config.alchemist_summon_reward != 1) job_exp = 0; // Added [Valaris] + + if((pid=tmpsd[i]->status.party_id)>0){ // パーティに入っている + int j=0; + for(j=0;j<pnum;j++) // 公平パーティリストにいるかどうか + if(pt[j].id==pid) + break; + if(j==pnum){ // いないときは公平かどうか確認 + if((p=party_search(pid))!=NULL && p->exp!=0){ + pt[pnum].id=pid; + pt[pnum].p=p; + pt[pnum].base_exp=base_exp; + pt[pnum].job_exp=job_exp; + pnum++; + flag=0; + } + }else{ // いるときは公平 + pt[j].base_exp+=base_exp; + pt[j].job_exp+=job_exp; + flag=0; + } + } + if(flag) // 各自所得 + pc_gainexp(tmpsd[i],base_exp,job_exp); + } + // 公平分配 + for(i=0;i<pnum;i++) + party_exp_share(pt[i].p,md->bl.m,pt[i].base_exp,pt[i].job_exp); + + // item drop + if(!(type&1)) { + for(i=0;i<8;i++){ + struct delay_item_drop *ditem; + int drop_rate; + + if(md->state.special_mob_ai >= 1 && battle_config.alchemist_summon_reward != 1) // Added [Valaris] + break; // End + + if(mob_db[md->class].dropitem[i].nameid <= 0) + continue; + drop_rate = mob_db[md->class].dropitem[i].p; + if(drop_rate <= 0 && battle_config.drop_rate0item==1) + drop_rate = 1; + if(battle_config.drops_by_luk>0 && sd && md) drop_rate+=(sd->status.luk*battle_config.drops_by_luk)/100; // drops affected by luk [Valaris] + if(sd && md && battle_config.pk_mode==1 && (mob_db[md->class].lv - sd->status.base_level >= 20)) drop_rate*=1.25; // pk_mode increase drops if 20 level difference [Valaris] + if(drop_rate <= rand()%10000) + continue; + + ditem=(struct delay_item_drop *)aCalloc(1,sizeof(struct delay_item_drop)); + ditem->nameid = mob_db[md->class].dropitem[i].nameid; + ditem->amount = 1; + ditem->m = md->bl.m; + ditem->x = md->bl.x; + ditem->y = md->bl.y; + ditem->first_sd = mvp_sd; + ditem->second_sd = second_sd; + ditem->third_sd = third_sd; + add_timer(tick+500+i,mob_delay_item_drop,(int)ditem,0); + } + if(sd && sd->state.attack_type == BF_WEAPON) { + for(i=0;i<sd->monster_drop_item_count;i++) { + struct delay_item_drop *ditem; + int race = battle_get_race(&md->bl); + if(sd->monster_drop_itemid[i] <= 0) + continue; + if(sd->monster_drop_race[i] & (1<<race) || + (mob_db[md->class].mode & 0x20 && sd->monster_drop_race[i] & 1<<10) || + (!(mob_db[md->class].mode & 0x20) && sd->monster_drop_race[i] & 1<<11) ) { + if(sd->monster_drop_itemrate[i] <= rand()%10000) + continue; + + ditem=(struct delay_item_drop *)aCalloc(1,sizeof(struct delay_item_drop)); + ditem->nameid = sd->monster_drop_itemid[i]; + ditem->amount = 1; + ditem->m = md->bl.m; + ditem->x = md->bl.x; + ditem->y = md->bl.y; + ditem->first_sd = mvp_sd; + ditem->second_sd = second_sd; + ditem->third_sd = third_sd; + add_timer(tick+520+i,mob_delay_item_drop,(int)ditem,0); + } + } + if(sd->get_zeny_num > 0) + pc_getzeny(sd,mob_db[md->class].lv*10 + rand()%(sd->get_zeny_num+1)); + } + if(md->lootitem) { + for(i=0;i<md->lootitem_count;i++) { + struct delay_item_drop2 *ditem; + + ditem=(struct delay_item_drop2 *)aCalloc(1,sizeof(struct delay_item_drop2)); + memcpy(&ditem->item_data,&md->lootitem[i],sizeof(md->lootitem[0])); + ditem->m = md->bl.m; + ditem->x = md->bl.x; + ditem->y = md->bl.y; + ditem->first_sd = mvp_sd; + ditem->second_sd = second_sd; + ditem->third_sd = third_sd; + add_timer(tick+540+i,mob_delay_item_drop2,(int)ditem,0); + } + } + } + + // mvp処理 + if(mvp_sd && mob_db[md->class].mexp > 0 ){ + int j; + int mexp; + temp = ((double)mob_db[md->class].mexp * (double)battle_config.mvp_exp_rate * (9.+(double)count)/1000.); + mexp = (temp > 2147483647.)? 0x7fffffff:(int)temp; + if(mexp < 1) mexp = 1; + clif_mvp_effect(mvp_sd); // エフェクト + clif_mvp_exp(mvp_sd,mexp); + pc_gainexp(mvp_sd,mexp,0); + for(j=0;j<3;j++){ + i = rand() % 3; + if(mob_db[md->class].mvpitem[i].nameid <= 0) + continue; + drop_rate = mob_db[md->class].mvpitem[i].p; + if(drop_rate <= 0 && battle_config.drop_rate0item==1) + drop_rate = 1; + if(drop_rate < battle_config.item_drop_mvp_min) + drop_rate = battle_config.item_drop_mvp_min; + if(drop_rate > battle_config.item_drop_mvp_max) + drop_rate = battle_config.item_drop_mvp_max; + if(drop_rate <= rand()%10000) + continue; + memset(&item,0,sizeof(item)); + item.nameid=mob_db[md->class].mvpitem[i].nameid; + item.identify=!itemdb_isequip3(item.nameid); + clif_mvp_item(mvp_sd,item.nameid); + if(mvp_sd->weight*2 > mvp_sd->max_weight) + map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1); + else if((ret = pc_additem(mvp_sd,&item,1))) { + clif_additem(sd,0,0,ret); + map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1); + } + break; + } + } + + } // [MouseJstr] + + // <Agit> NPC Event [OnAgitBreak] + if(md->npc_event[0] && strcmp(((md->npc_event)+strlen(md->npc_event)-13),"::OnAgitBreak") == 0) { + printf("MOB.C: Run NPC_Event[OnAgitBreak].\n"); + if (agit_flag == 1) //Call to Run NPC_Event[OnAgitBreak] + guild_agit_break(md); + } + + // SCRIPT実行 + if(md->npc_event[0]){ +// if(battle_config.battle_log==1) +// printf("mob_damage : run event : %s\n",md->npc_event); + if(src && src->type == BL_PET) + sd = ((struct pet_data *)src)->msd; + if(sd == NULL) { + if(mvp_sd != NULL) + sd = mvp_sd; + else { + struct map_session_data *tmpsd; + int i; + for(i=0;i<fd_max;i++){ + if(session[i] && (tmpsd=session[i]->session_data) && tmpsd->state.auth) { + if(md->bl.m == tmpsd->bl.m) { + sd = tmpsd; + break; + } + } + } + } + } + if(sd) + npc_event(sd,md->npc_event,0); + } + + clif_clearchar_area(&md->bl,1); + map_delblock(&md->bl); + if(mob_get_viewclass(md->class) <= 1000) + clif_clearchar_delay(tick+3000,&md->bl,0); + mob_deleteslave(md); + mob_setdelayspawn(md->bl.id); + map_freeblock_unlock(); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int mob_class_change(struct mob_data *md,int *value) +{ + unsigned int tick = gettick(); + int i,c,hp_rate,max_hp,class,count = 0; + + nullpo_retr(0, md); + nullpo_retr(0, value); + + if(value[0]<=1000 || value[0]>2000) + return 0; + if(md->bl.prev == NULL) return 0; + + while(count < 5 && value[count] > 1000 && value[count] <= 2000) count++; + if(count < 1) return 0; + + class = value[rand()%count]; + if(class<=1000 || class>2000) return 0; + + max_hp = battle_get_max_hp(&md->bl); + hp_rate = md->hp*100/max_hp; + clif_mob_class_change(md,class); + md->class = class; + max_hp = battle_get_max_hp(&md->bl); + if(battle_config.monster_class_change_full_recover==1) { + md->hp = max_hp; + memset(md->dmglog,0,sizeof(md->dmglog)); + } + else + md->hp = max_hp*hp_rate/100; + if(md->hp > max_hp) md->hp = max_hp; + else if(md->hp < 1) md->hp = 1; + + memcpy(md->name,mob_db[class].jname,24); + memset(&md->state,0,sizeof(md->state)); + md->attacked_id = 0; + md->target_id = 0; + md->move_fail_count = 0; + + md->speed = mob_db[md->class].speed; + md->def_ele = mob_db[md->class].element; + + mob_changestate(md,MS_IDLE,0); + skill_castcancel(&md->bl,0); + md->state.skillstate = MSS_IDLE; + md->last_thinktime = tick; + md->next_walktime = tick+rand()%50+5000; + md->attackabletime = tick; + md->canmove_tick = tick; + md->sg_count=0; + + for(i=0,c=tick-1000*3600*10;i<MAX_MOBSKILL;i++) + md->skilldelay[i] = c; + md->skillid=0; + md->skilllv=0; + + if(md->lootitem == NULL && mob_db[class].mode&0x02) + 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); + + return 0; +} + +/*========================================== + * mob回復 + *------------------------------------------ + */ +int mob_heal(struct mob_data *md,int heal) +{ + int max_hp = battle_get_max_hp(&md->bl); + + nullpo_retr(0, md); + + md->hp += heal; + if( max_hp < md->hp ) + md->hp = max_hp; + + if(md->class >= 1285 && md->class <=1287) { // guardian hp update [Valaris] + struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name); + if(gc) { + if(md->bl.id==gc->GID0) gc->Ghp0=md->hp; + if(md->bl.id==gc->GID1) gc->Ghp1=md->hp; + if(md->bl.id==gc->GID2) gc->Ghp2=md->hp; + if(md->bl.id==gc->GID3) gc->Ghp3=md->hp; + if(md->bl.id==gc->GID4) gc->Ghp4=md->hp; + if(md->bl.id==gc->GID5) gc->Ghp5=md->hp; + if(md->bl.id==gc->GID6) gc->Ghp6=md->hp; + if(md->bl.id==gc->GID7) gc->Ghp7=md->hp; + } + } // end addition [Valaris] + + return 0; +} + + +/*========================================== + * Added by RoVeRT + *------------------------------------------ + */ +int mob_warpslave_sub(struct block_list *bl,va_list ap) +{ + struct mob_data *md=(struct mob_data *)bl; + int id,x,y; + id=va_arg(ap,int); + x=va_arg(ap,int); + y=va_arg(ap,int); + if( md->master_id==id ) { + mob_warp(md,-1,x,y,2); + } + return 0; +} + +/*========================================== + * Added by RoVeRT + *------------------------------------------ + */ +int mob_warpslave(struct mob_data *md,int x, int y) +{ +//printf("warp slave\n"); + map_foreachinarea(mob_warpslave_sub, md->bl.m, + x-AREA_SIZE,y-AREA_SIZE, + x+AREA_SIZE,y+AREA_SIZE,BL_MOB, + md->bl.id, md->bl.x, md->bl.y ); + return 0; +} + +/*========================================== + * mobワープ + *------------------------------------------ + */ +int mob_warp(struct mob_data *md,int m,int x,int y,int type) +{ + int i=0,c,xs=0,ys=0,bx=x,by=y; + + nullpo_retr(0, md); + + if( md->bl.prev==NULL ) + return 0; + + if( m<0 ) m=md->bl.m; + + if(type >= 0) { + if(map[md->bl.m].flag.monster_noteleport) + return 0; + clif_clearchar_area(&md->bl,type); + } + skill_unit_out_all(&md->bl,gettick(),1); + map_delblock(&md->bl); + + if(bx>0 && by>0){ // 位置指定の場合周囲9セルを探索 + xs=ys=9; + } + + while( ( x<0 || y<0 || ((c=read_gat(m,x,y))==1 || c==5) ) && (i++)<1000 ){ + if( xs>0 && ys>0 && i<250 ){ // 指定位置付近の探索 + x=bx+rand()%xs-xs/2; + y=by+rand()%ys-ys/2; + }else{ // 完全ランダム探索 + x=rand()%(map[m].xs-2)+1; + y=rand()%(map[m].ys-2)+1; + } + } + md->dir=0; + if(i<1000){ + md->bl.x=md->to_x=x; + md->bl.y=md->to_y=y; + md->bl.m=m; + }else { + m=md->bl.m; + if(battle_config.error_log==1) + printf("MOB %d warp failed, class = %d\n",md->bl.id,md->class); + } + + md->target_id=0; // タゲを解除する + md->state.targettype=NONE_ATTACKABLE; + md->attacked_id=0; + md->state.skillstate=MSS_IDLE; + mob_changestate(md,MS_IDLE,0); + + if(type>0 && i==1000) { + if(battle_config.battle_log==1) + printf("MOB %d warp to (%d,%d), class = %d\n",md->bl.id,x,y,md->class); + } + + map_addblock(&md->bl); + if(type>0) { + clif_spawnmob(md); + mob_warpslave(md,md->bl.x,md->bl.y); + } + + return 0; +} + +/*========================================== + * 画面内の取り巻きの数計算用(foreachinarea) + *------------------------------------------ + */ +int mob_countslave_sub(struct block_list *bl,va_list ap) +{ + int id,*c; + struct mob_data *md; + + id=va_arg(ap,int); + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, c=va_arg(ap,int *)); + nullpo_retr(0, md = (struct mob_data *)bl); + + + if( md->master_id==id ) + (*c)++; + return 0; +} +/*========================================== + * 画面内の取り巻きの数計算 + *------------------------------------------ + */ +int mob_countslave(struct mob_data *md) +{ + int c=0; + + nullpo_retr(0, md); + + map_foreachinarea(mob_countslave_sub, md->bl.m, + 0,0,map[md->bl.m].xs-1,map[md->bl.m].ys-1, + BL_MOB,md->bl.id,&c); + return c; +} +/*========================================== + * 手下MOB召喚 + *------------------------------------------ + */ +int mob_summonslave(struct mob_data *md2,int *value,int amount,int flag) +{ + struct mob_data *md; + int bx,by,m,count = 0,class,k,a = amount; + + nullpo_retr(0, md2); + nullpo_retr(0, value); + + bx=md2->bl.x; + by=md2->bl.y; + m=md2->bl.m; + + if(value[0]<=1000 || value[0]>2000) // 値が異常なら召喚を止める + return 0; + while(count < 5 && value[count] > 1000 && value[count] <= 2000) count++; + if(count < 1) return 0; + + for(k=0;k<count;k++) { + amount = a; + class = value[k]; + if(class<=1000 || class>2000) continue; + for(;amount>0;amount--){ + int x=0,y=0,c=0,i=0; + md=(struct mob_data *)aCalloc(1,sizeof(struct mob_data)); + if(mob_db[class].mode&0x02) + md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); + else + md->lootitem=NULL; + + while((x<=0 || y<=0 || (c=map_getcell(m,x,y))==1 || c==5 ) && (i++)<100){ + x=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; + md->speed=md2->speed; + md->spawndelay1=-1; // 一度のみフラグ + md->spawndelay2=-1; // 一度のみフラグ + + memset(md->npc_event,0,sizeof(md->npc_event)); + md->bl.type=BL_MOB; + map_addiddb(&md->bl); + mob_spawn(md->bl.id); + clif_skill_nodamage(&md->bl,&md->bl,(flag)? NPC_SUMMONSLAVE:NPC_SUMMONMONSTER,a,1); + + if(flag) + md->master_id=md2->bl.id; + } + } + return 0; +} + +/*========================================== + * 自分をロックしているPCの数を数える(foreachclient) + *------------------------------------------ + */ +static int mob_counttargeted_sub(struct block_list *bl,va_list ap) +{ + int id,*c,target_lv; + struct block_list *src; + + id=va_arg(ap,int); + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, c=va_arg(ap,int *)); + + src=va_arg(ap,struct block_list *); + target_lv=va_arg(ap,int); + if(id == bl->id || (src && id == src->id)) return 0; + if(bl->type == BL_PC) { + struct map_session_data *sd = (struct map_session_data *)bl; + if(sd && sd->attacktarget == id && sd->attacktimer != -1 && sd->attacktarget_lv >= target_lv) + (*c)++; + } + else if(bl->type == BL_MOB) { + struct mob_data *md = (struct mob_data *)bl; + if(md && md->target_id == id && md->timer != -1 && md->state.state == MS_ATTACK && md->target_lv >= target_lv) + (*c)++; + } + else if(bl->type == BL_PET) { + struct pet_data *pd = (struct pet_data *)bl; + if(pd->target_id == id && pd->timer != -1 && pd->state.state == MS_ATTACK && pd->target_lv >= target_lv) + (*c)++; + } + return 0; +} +/*========================================== + * 自分をロックしているPCの数を数える + *------------------------------------------ + */ +int mob_counttargeted(struct mob_data *md,struct block_list *src,int target_lv) +{ + int c=0; + + nullpo_retr(0, md); + + map_foreachinarea(mob_counttargeted_sub, md->bl.m, + md->bl.x-AREA_SIZE,md->bl.y-AREA_SIZE, + md->bl.x+AREA_SIZE,md->bl.y+AREA_SIZE,0,md->bl.id,&c,src,target_lv); + return c; +} + +/*========================================== + *MOBskillから該当skillidのskillidxを返す + *------------------------------------------ + */ +int mob_skillid2skillidx(int class,int skillid) +{ + int i; + struct mob_skill *ms=mob_db[class].skill; + + if(ms==NULL) + return -1; + + for(i=0;i<mob_db[class].maxskill;i++){ + if(ms[i].skill_id == skillid) + return i; + } + return -1; + +} + +// +// MOBスキル +// + +/*========================================== + * スキル使用(詠唱完了、ID指定) + *------------------------------------------ + */ +int mobskill_castend_id( int tid, unsigned int tick, int id,int data ) +{ + struct mob_data* md=NULL; + struct block_list *bl; + struct block_list *mbl; + int range; + + if((mbl = map_id2bl(id)) == NULL ) //詠唱したMobがもういないというのは良くある正常処理 + return 0; + if((md=(struct mob_data *)mbl) == NULL ){ + printf("mobskill_castend_id nullpo mbl->id:%d\n",mbl->id); + return 0; + } + + if( md->bl.type!=BL_MOB || md->bl.prev==NULL ) + return 0; + + if( md->skilltimer != tid ) // タイマIDの確認 + return 0; + + md->skilltimer=-1; + //沈黙や状態異常など + if(md->sc_data){ + if(md->opt1>0 || md->sc_data[SC_DIVINA].timer != -1 || md->sc_data[SC_ROKISWEIL].timer != -1 || md->sc_data[SC_STEELBODY].timer != -1) + return 0; + if(md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return 0; + if(md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return 0; + if(md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return 0; + } + if(md->skillid != NPC_EMOTION) + md->last_thinktime=tick + battle_get_adelay(&md->bl); + + if((bl = map_id2bl(md->skilltarget)) == NULL || bl->prev==NULL){ //スキルターゲットが存在しない + //printf("mobskill_castend_id nullpo\n");//ターゲットがいないときはnullpoじゃなくて普通に終了 + return 0; + } + if(md->bl.m != bl->m) + return 0; + + if(md->skillid == PR_LEXAETERNA) { + struct status_change *sc_data = battle_get_sc_data(bl); + if(sc_data && (sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0))) + return 0; + } + else if(md->skillid == RG_BACKSTAP) { + int dir = map_calc_dir(&md->bl,bl->x,bl->y),t_dir = battle_get_dir(bl); + int dist = distance(md->bl.x,md->bl.y,bl->x,bl->y); + if(bl->type != BL_SKILL && (dist == 0 || map_check_dir(dir,t_dir))) + return 0; + } + if( ( (skill_get_inf(md->skillid)&1) || (skill_get_inf2(md->skillid)&4) ) && // 彼我敵対関係チェック + battle_check_target(&md->bl,bl, BCT_ENEMY)<=0 ) + return 0; + range = skill_get_range(md->skillid,md->skilllv); + if(range < 0) + range = battle_get_range(&md->bl) - (range + 1); + if(range + battle_config.mob_skill_add_range < distance(md->bl.x,md->bl.y,bl->x,bl->y)) + return 0; + + md->skilldelay[md->skillidx]=tick; + + if(battle_config.mob_skill_log==1) + printf("MOB skill castend skill=%d, class = %d\n",md->skillid,md->class); + mob_stop_walking(md,0); + + switch( skill_get_nk(md->skillid) ) + { + // 攻撃系/吹き飛ばし系 + case 0: case 2: + skill_castend_damage_id(&md->bl,bl,md->skillid,md->skilllv,tick,0); + break; + case 1:// 支援系 + if(!mob_db[md->class].skill[md->skillidx].val[0] && + (md->skillid==AL_HEAL || (md->skillid==ALL_RESURRECTION && bl->type != BL_PC)) && battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl)) ) + skill_castend_damage_id(&md->bl,bl,md->skillid,md->skilllv,tick,0); + else + skill_castend_nodamage_id(&md->bl,bl,md->skillid,md->skilllv,tick,0); + break; + } + + + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、場所指定) + *------------------------------------------ + */ +int mobskill_castend_pos( int tid, unsigned int tick, int id,int data ) +{ + struct mob_data* md=NULL; + struct block_list *bl; + int range,maxcount; + + //mobskill_castend_id同様詠唱したMobが詠唱完了時にもういないというのはありそうなのでnullpoから除外 + if((bl=map_id2bl(id))==NULL) + return 0; + + nullpo_retr(0, md=(struct mob_data *)bl); + + if( md->bl.type!=BL_MOB || md->bl.prev==NULL ) + return 0; + + if( md->skilltimer != tid ) // タイマIDの確認 + return 0; + + md->skilltimer=-1; + if(md->sc_data){ + if(md->opt1>0 || md->sc_data[SC_DIVINA].timer != -1 || md->sc_data[SC_ROKISWEIL].timer != -1 || md->sc_data[SC_STEELBODY].timer != -1) + return 0; + if(md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return 0; + if(md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return 0; + if(md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return 0; + } + + if(battle_config.monster_skill_reiteration == 0) { + range = -1; + switch(md->skillid) { + case MG_SAFETYWALL: + case WZ_FIREPILLAR: + case HT_SKIDTRAP: + case HT_LANDMINE: + case HT_ANKLESNARE: + case HT_SHOCKWAVE: + case HT_SANDMAN: + case HT_FLASHER: + case HT_FREEZINGTRAP: + case HT_BLASTMINE: + case HT_CLAYMORETRAP: + case PF_SPIDERWEB: /* スパイダーウェッブ */ + range = 0; + break; + case AL_PNEUMA: + case AL_WARP: + range = 1; + break; + } + if(range >= 0) { + if(skill_check_unit_range(md->bl.m,md->skillx,md->skilly,range,md->skillid) > 0) + return 0; + } + } + if(battle_config.monster_skill_nofootset==1) { + range = -1; + switch(md->skillid) { + case WZ_FIREPILLAR: + case HT_SKIDTRAP: + case HT_LANDMINE: + case HT_ANKLESNARE: + case HT_SHOCKWAVE: + case HT_SANDMAN: + case HT_FLASHER: + case HT_FREEZINGTRAP: + case HT_BLASTMINE: + case HT_CLAYMORETRAP: + case AM_DEMONSTRATION: + case PF_SPIDERWEB: /* スパイダーウェッブ */ + range = 1; + break; + case AL_WARP: + range = 0; + break; + } + if(range >= 0) { + if(skill_check_unit_range2(md->bl.m,md->skillx,md->skilly,range) > 0) + return 0; + } + } + + if(battle_config.monster_land_skill_limit==1) { + maxcount = skill_get_maxcount(md->skillid); + if(maxcount > 0) { + int i,c; + for(i=c=0;i<MAX_MOBSKILLUNITGROUP;i++) { + if(md->skillunit[i].alive_count > 0 && md->skillunit[i].skill_id == md->skillid) + c++; + } + if(c >= maxcount) + return 0; + } + } + + range = skill_get_range(md->skillid,md->skilllv); + if(range < 0) + range = battle_get_range(&md->bl) - (range + 1); + if(range + battle_config.mob_skill_add_range < distance(md->bl.x,md->bl.y,md->skillx,md->skilly)) + return 0; + md->skilldelay[md->skillidx]=tick; + + if(battle_config.mob_skill_log==1) + printf("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); + + 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,range; + struct mob_skill *ms; + int skill_id, skill_lv, forcecast = 0; + + nullpo_retr(0, md); + nullpo_retr(0, ms=&mob_db[md->class].skill[skill_idx]); + + if( target==NULL && (target=map_id2bl(md->target_id))==NULL ) + return 0; + + if( target->prev==NULL || md->bl.prev==NULL ) + return 0; + + skill_id=ms->skill_id; + skill_lv=ms->skill_lv; + + // 沈黙や異常 + if(md->sc_data){ + if(md->opt1>0 || md->sc_data[SC_DIVINA].timer != -1 || md->sc_data[SC_ROKISWEIL].timer != -1 || md->sc_data[SC_STEELBODY].timer != -1) + return 0; + if(md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return 0; + if(md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return 0; + if(md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return 0; + } + + if(md->option&4 && skill_id==TF_HIDING) + return 0; + if(md->option&2 && skill_id!=TF_HIDING && skill_id!=AS_GRIMTOOTH && skill_id!=RG_BACKSTAP && skill_id!=RG_RAID) + return 0; + + if(map[md->bl.m].flag.gvg && (skill_id == SM_ENDURE || skill_id == AL_TELEPORT || skill_id == AL_WARP || + skill_id == WZ_ICEWALL || skill_id == TF_BACKSLIDING)) + return 0; + + if(skill_get_inf2(skill_id)&0x200 && md->bl.id == target->id) + return 0; + + // 射程と障害物チェック + range = skill_get_range(skill_id,skill_lv); + if(range < 0) + range = battle_get_range(&md->bl) - (range + 1); + if(!battle_check_range(&md->bl,target,range)) + return 0; + +// delay=skill_delayfix(&md->bl, skill_get_delay( skill_id,skill_lv) ); + + casttime=skill_castfix(&md->bl,ms->casttime); + md->state.skillcastcancel=ms->cancel; + md->skilldelay[skill_idx]=gettick(); + + switch(skill_id){ /* 何か特殊な処理が必要 */ + case ALL_RESURRECTION: /* リザレクション */ + if(target->type != BL_PC && battle_check_undead(battle_get_race(target),battle_get_elem_type(target))){ /* 敵がアンデッドなら */ + forcecast=1; /* ターンアンデットと同じ詠唱時間 */ + casttime=skill_castfix(&md->bl, skill_get_cast(PR_TURNUNDEAD,skill_lv) ); + } + break; + case MO_EXTREMITYFIST: /*阿修羅覇鳳拳*/ + case SA_MAGICROD: + case SA_SPELLBREAKER: + forcecast=1; + break; + } + + if(battle_config.mob_skill_log==1) + printf("MOB skill use target_id=%d skill=%d lv=%d cast=%d, class = %d\n",target->id,skill_id,skill_lv,casttime,md->class); + + if(casttime>0 || forcecast){ // 詠唱が必要 +// struct mob_data *md2; + clif_skillcasting( &md->bl, + md->bl.id, target->id, 0,0, skill_id,casttime); + + // 詠唱反応モンスター +/* if( target->type==BL_MOB && mob_db[(md2=(struct mob_data *)target)->class].mode&0x10 && + md2->state.state!=MS_ATTACK){ + md2->target_id=md->bl.id; + md->state.targettype = ATTACKABLE; + md2->min_chase=13; + }*/ + } + + if( casttime<=0 ) // 詠唱の無いものはキャンセルされない + md->state.skillcastcancel=0; + + md->skilltarget = target->id; + md->skillx = 0; + md->skilly = 0; + md->skillid = skill_id; + md->skilllv = skill_lv; + md->skillidx = skill_idx; + + if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1 && md->skillid != AS_CLOAKING) + skill_status_change_end(&md->bl,SC_CLOAKING,-1); + + if( casttime>0 ){ + md->skilltimer = + add_timer( gettick()+casttime, mobskill_castend_id, md->bl.id, 0 ); + }else{ + md->skilltimer = -1; + mobskill_castend_id(md->skilltimer,gettick(),md->bl.id, 0); + } + + return 1; +} +/*========================================== + * スキル使用(場所指定) + *------------------------------------------ + */ +int mobskill_use_pos( struct mob_data *md, + int skill_x, int skill_y, int skill_idx) +{ + int casttime=0,range; + struct mob_skill *ms; + struct block_list bl; + int skill_id, skill_lv; + + nullpo_retr(0, md); + nullpo_retr(0, ms=&mob_db[md->class].skill[skill_idx]); + + if( md->bl.prev==NULL ) + return 0; + + skill_id=ms->skill_id; + skill_lv=ms->skill_lv; + + //沈黙や状態異常など + if(md->sc_data){ + if(md->opt1>0 || md->sc_data[SC_DIVINA].timer != -1 || md->sc_data[SC_ROKISWEIL].timer != -1 || md->sc_data[SC_STEELBODY].timer != -1) + return 0; + if(md->sc_data[SC_AUTOCOUNTER].timer != -1 && md->skillid != KN_AUTOCOUNTER) //オートカウンター + return 0; + if(md->sc_data[SC_BLADESTOP].timer != -1) //白刃取り + return 0; + if(md->sc_data[SC_BERSERK].timer != -1) //バーサーク + return 0; + } + + if(md->option&2) + return 0; + + if(map[md->bl.m].flag.gvg && (skill_id == SM_ENDURE || skill_id == AL_TELEPORT || skill_id == AL_WARP || + skill_id == WZ_ICEWALL || skill_id == TF_BACKSLIDING)) + return 0; + + // 射程と障害物チェック + bl.type = BL_NUL; + bl.m = md->bl.m; + bl.x = skill_x; + bl.y = skill_y; + range = skill_get_range(skill_id,skill_lv); + if(range < 0) + range = battle_get_range(&md->bl) - (range + 1); + if(!battle_check_range(&md->bl,&bl,range)) + return 0; + +// delay=skill_delayfix(&sd->bl, skill_get_delay( skill_id,skill_lv) ); + casttime=skill_castfix(&md->bl,ms->casttime); + md->skilldelay[skill_idx]=gettick(); + md->state.skillcastcancel=ms->cancel; + + if(battle_config.mob_skill_log==1) + printf("MOB skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d, class = %d\n", + skill_x,skill_y,skill_id,skill_lv,casttime,md->class); + + if( casttime>0 ) // A cast time is required. + clif_skillcasting( &md->bl, + md->bl.id, 0, skill_x,skill_y, skill_id,casttime); + + if( casttime<=0 ) // A skill without a cast time wont be cancelled. + md->state.skillcastcancel=0; + + + md->skillx = skill_x; + md->skilly = skill_y; + md->skilltarget = 0; + md->skillid = skill_id; + md->skilllv = skill_lv; + md->skillidx = skill_idx; + if(!(battle_config.monster_cloak_check_type&2) && md->sc_data[SC_CLOAKING].timer != -1) + skill_status_change_end(&md->bl,SC_CLOAKING,-1); + if( casttime>0 ){ + md->skilltimer = + add_timer( gettick()+casttime, mobskill_castend_pos, md->bl.id, 0 ); + }else{ + md->skilltimer = -1; + mobskill_castend_pos(md->skilltimer,gettick(),md->bl.id, 0); + } + + return 1; +} + + +/*========================================== + * Friendly Mob whose HP is decreasing by a nearby MOB is looked for. + *------------------------------------------ + */ +int mob_getfriendhpltmaxrate_sub(struct block_list *bl,va_list ap) +{ + int rate; + struct mob_data **fr, *md, *mmd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, mmd=va_arg(ap,struct mob_data *)); + + md=(struct mob_data *)bl; + + if( mmd->bl.id == bl->id ) + return 0; + rate=va_arg(ap,int); + fr=va_arg(ap,struct mob_data **); + if( md->hp < mob_db[md->class].max_hp*rate/100 ) + (*fr)=md; + return 0; +} +struct mob_data *mob_getfriendhpltmaxrate(struct mob_data *md,int rate) +{ + struct mob_data *fr=NULL; + const int r=8; + + nullpo_retr(NULL, md); + + map_foreachinarea(mob_getfriendhpltmaxrate_sub, md->bl.m, + md->bl.x-r ,md->bl.y-r, md->bl.x+r, md->bl.y+r, + BL_MOB,md,rate,&fr); + return fr; +} +/*========================================== + * What a status state suits by nearby MOB is looked for. + *------------------------------------------ + */ +int mob_getfriendstatus_sub(struct block_list *bl,va_list ap) +{ + int cond1,cond2; + struct mob_data **fr, *md, *mmd; + int flag=0; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md=(struct mob_data *)bl); + nullpo_retr(0, mmd=va_arg(ap,struct mob_data *)); + + if( mmd->bl.id == bl->id ) + return 0; + cond1=va_arg(ap,int); + cond2=va_arg(ap,int); + fr=va_arg(ap,struct mob_data **); + if( cond2==-1 ){ + int j; + for(j=SC_STONE;j<=SC_BLIND && !flag;j++){ + flag=(md->sc_data[j].timer!=-1 ); + } + }else + flag=( md->sc_data[cond2].timer!=-1 ); + if( flag^( cond1==MSC_FRIENDSTATUSOFF ) ) + (*fr)=md; + + return 0; +} +struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2) +{ + struct mob_data *fr=NULL; + const int r=8; + + nullpo_retr(0, md); + + map_foreachinarea(mob_getfriendstatus_sub, md->bl.m, + md->bl.x-r ,md->bl.y-r, md->bl.x+r, md->bl.y+r, + BL_MOB,md,cond1,cond2,&fr); + return fr; +} + +/*========================================== + * Skill use judging + *------------------------------------------ + */ +int mobskill_use(struct mob_data *md,unsigned int tick,int event) +{ + struct mob_skill *ms; +// struct block_list *target=NULL; + int i,max_hp; + + nullpo_retr(0, md); + nullpo_retr(0, ms = mob_db[md->class].skill); + + max_hp = battle_get_max_hp(&md->bl); + + if(battle_config.mob_skill_use == 0 || md->skilltimer != -1) + return 0; + + if(md->state.special_mob_ai) + return 0; + + if(md->sc_data[SC_SELFDESTRUCTION].timer!=-1) //自爆中はスキルを使わない + return 0; + + for(i=0;i<mob_db[md->class].maxskill;i++){ + int c2=ms[i].cond2,flag=0; + struct mob_data *fmd=NULL; + + // ディレイ中 + if( DIFF_TICK(tick,md->skilldelay[i])<ms[i].delay ) + continue; + + // 状態判定 + if( ms[i].state>=0 && ms[i].state!=md->state.skillstate ) + continue; + + // 条件判定 + flag=(event==ms[i].cond1); + if(!flag){ + switch( ms[i].cond1 ){ + case MSC_ALWAYS: + flag=1; break; + case MSC_MYHPLTMAXRATE: // HP< maxhp% + flag=( md->hp < max_hp*c2/100 ); break; + case MSC_MYSTATUSON: // status[num] on + case MSC_MYSTATUSOFF: // status[num] off + if( ms[i].cond2==-1 ){ + int j; + for(j=SC_STONE;j<=SC_BLIND && !flag;j++){ + flag=(md->sc_data[j].timer!=-1 ); + } + }else + flag=( md->sc_data[ms[i].cond2].timer!=-1 ); + flag^=( ms[i].cond1==MSC_MYSTATUSOFF ); break; + case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp% + flag=(( fmd=mob_getfriendhpltmaxrate(md,ms[i].cond2) )!=NULL ); break; + case MSC_FRIENDSTATUSON: // friend status[num] on + case MSC_FRIENDSTATUSOFF: // friend status[num] off + flag=(( fmd=mob_getfriendstatus(md,ms[i].cond1,ms[i].cond2) )!=NULL ); break; + case MSC_SLAVELT: // slave < num + flag=( mob_countslave(md) < c2 ); break; + case MSC_ATTACKPCGT: // attack pc > num + flag=( mob_counttargeted(md,NULL,0) > c2 ); break; + case MSC_SLAVELE: // slave <= num + flag=( mob_countslave(md) <= c2 ); break; + case MSC_ATTACKPCGE: // attack pc >= num + flag=( mob_counttargeted(md,NULL,0) >= c2 ); break; + case MSC_SKILLUSED: // specificated skill used + flag=( (event&0xffff)==MSC_SKILLUSED && ((event>>16)==c2 || c2==0)); break; + } + } + + // 確率判定 + if( flag && rand()%10000 < ms[i].permillage ){ + + if( skill_get_inf(ms[i].skill_id)&2 ){ + // 場所指定 + struct block_list *bl = NULL; + int x=0,y=0; + if( ms[i].target<=MST_AROUND ){ + bl= ((ms[i].target==MST_TARGET || ms[i].target==MST_AROUND5)? map_id2bl(md->target_id): + (ms[i].target==MST_FRIEND)? &fmd->bl : &md->bl); + if(bl!=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, c, m=bl->m, r=ms[i].target-MST_AROUND1; + do{ + bx=x + rand()%(r*2+3) - r; + by=y + rand()%(r*2+3) - r; + }while( ( bx<=0 || by<=0 || bx>=map[m].xs || by>=map[m].ys || + ((c=read_gat(m,bx,by))==1 || c==5) ) && (i++)<1000); + if(i<1000){ + x=bx; y=by; + } + } + // 相手の周囲 + if( ms[i].target>=MST_AROUND5 ){ + int bx=x, by=y, i=0, c, m=bl->m, r=(ms[i].target-MST_AROUND5)+1; + do{ + bx=x + rand()%(r*2+1) - r; + by=y + rand()%(r*2+1) - r; + }while( ( bx<=0 || by<=0 || bx>=map[m].xs || by>=map[m].ys || + ((c=read_gat(m,bx,by))==1 || c==5) ) && (i++)<1000); + if(i<1000){ + x=bx; y=by; + } + } + if(!mobskill_use_pos(md,x,y,i)) + return 0; + + }else{ + // ID指定 + if( ms[i].target<=MST_FRIEND ){ + struct block_list *bl = NULL; + bl= ((ms[i].target==MST_TARGET)? map_id2bl(md->target_id): + (ms[i].target==MST_FRIEND)? &fmd->bl : &md->bl); + if(bl && !mobskill_use_id(md,bl,i)) + return 0; + } + } + if(ms[i].emotion >= 0) + clif_emotion(&md->bl,ms[i].emotion); + return 1; + } + } + + return 0; +} +/*========================================== + * Skill use event processing + *------------------------------------------ + */ +int mobskill_event(struct mob_data *md,int flag) +{ + nullpo_retr(0, md); + + if(flag==-1 && mobskill_use(md,gettick(),MSC_CASTTARGETED)) + return 1; + if( (flag&BF_SHORT) && mobskill_use(md,gettick(),MSC_CLOSEDATTACKED)) + return 1; + if( (flag&BF_LONG) && mobskill_use(md,gettick(),MSC_LONGRANGEATTACKED)) + return 1; + return 0; +} +/*========================================== + * Mobがエンペリウムなどの場合の判定 + *------------------------------------------ + */ +int mob_gvmobcheck(struct map_session_data *sd, struct block_list *bl) +{ + struct mob_data *md=NULL; + + nullpo_retr(0,sd); + nullpo_retr(0,bl); + + if(bl->type==BL_MOB && (md=(struct mob_data *)bl) && + (md->class == 1288 || md->class == 1287 || md->class == 1286 || md->class == 1285)) + { + struct guild_castle *gc=guild_mapname2gc(map[sd->bl.m].name); + struct guild *g=guild_search(sd->status.guild_id); + + if(g == NULL && md->class == 1288) + return 0;//ギルド未加入ならダメージ無し + else if(gc != NULL && !map[sd->bl.m].flag.gvg) + return 0;//砦内でGvじゃないときはダメージなし + else if(g && gc != NULL && g->guild_id == gc->guild_id) + return 0;//自占領ギルドのエンペならダメージ無し + else if(g && guild_checkskill(g,GD_APPROVAL) <= 0 && md->class == 1288) + return 0;//正規ギルド承認がないとダメージ無し + + } + + return 1; +} +/*========================================== + * スキル用タイマー削除 + *------------------------------------------ + */ +int mobskill_deltimer(struct mob_data *md ) +{ + nullpo_retr(0, md); + + if( md->skilltimer!=-1 ){ + if( skill_get_inf( md->skillid )&2 ) + delete_timer( md->skilltimer, mobskill_castend_pos ); + else + delete_timer( md->skilltimer, mobskill_castend_id ); + md->skilltimer=-1; + } + return 0; +} +// +// 初期化 +// +/*========================================== + * Since un-setting [ mob ] up was used, it is an initial provisional value setup. + *------------------------------------------ + */ +static int mob_makedummymobdb(int class) +{ + int i; + + sprintf(mob_db[class].name,"mob%d",class); + sprintf(mob_db[class].jname,"mob%d",class); + mob_db[class].lv=1; + mob_db[class].max_hp=1000; + mob_db[class].max_sp=1; + mob_db[class].base_exp=2; + mob_db[class].job_exp=1; + mob_db[class].range=1; + mob_db[class].atk1=7; + mob_db[class].atk2=10; + mob_db[class].def=0; + mob_db[class].mdef=0; + mob_db[class].str=1; + mob_db[class].agi=1; + mob_db[class].vit=1; + mob_db[class].int_=1; + mob_db[class].dex=6; + mob_db[class].luk=2; + mob_db[class].range2=10; + mob_db[class].range3=10; + mob_db[class].size=0; + mob_db[class].race=0; + mob_db[class].element=0; + mob_db[class].mode=0; + mob_db[class].speed=300; + mob_db[class].adelay=1000; + mob_db[class].amotion=500; + mob_db[class].dmotion=500; + mob_db[class].dropitem[0].nameid=909; // Jellopy + mob_db[class].dropitem[0].p=1000; + for(i=1;i<8;i++){ + mob_db[class].dropitem[i].nameid=0; + mob_db[class].dropitem[i].p=0; + } + // Item1,Item2 + mob_db[class].mexp=0; + mob_db[class].mexpper=0; + for(i=0;i<3;i++){ + mob_db[class].mvpitem[i].nameid=0; + mob_db[class].mvpitem[i].p=0; + } + for(i=0;i<MAX_RANDOMMONSTER;i++) + mob_db[class].summonper[i]=0; + return 0; +} + +/*========================================== + * db/mob_db.txt reading + *------------------------------------------ + */ +static int mob_readdb(void) +{ + FILE *fp; + char line[1024]; + char *filename[]={ "db/mob_db.txt","db/mob_db2.txt" }; + int i; + + memset(mob_db,0,sizeof(mob_db)); + + for(i=0;i<2;i++){ + + fp=fopen(filename[i],"r"); + if(fp==NULL){ + if(i>0) + continue; + return -1; + } + while(fgets(line,1020,fp)){ + int class,i; + char *str[55],*p,*np; + + if(line[0] == '/' && line[1] == '/') + continue; + + for(i=0,p=line;i<55;i++){ + if((np=strchr(p,','))!=NULL){ + str[i]=p; + *np=0; + p=np+1; + } else + str[i]=p; + } + + class=atoi(str[0]); + if(class<=1000 || class>2000) + continue; + + mob_db[class].view_class=class; + memcpy(mob_db[class].name,str[1],24); + memcpy(mob_db[class].jname,str[2],24); + mob_db[class].lv=atoi(str[3]); + mob_db[class].max_hp=atoi(str[4]); + mob_db[class].max_sp=atoi(str[5]); + + mob_db[class].base_exp=atoi(str[6]); + if(mob_db[class].base_exp < 0) + mob_db[class].base_exp = 0; + else if(mob_db[class].base_exp > 0 && (mob_db[class].base_exp*battle_config.base_exp_rate/100 > 1000000000 || + mob_db[class].base_exp*battle_config.base_exp_rate/100 < 0)) + mob_db[class].base_exp=1000000000; + else + mob_db[class].base_exp*= battle_config.base_exp_rate/100; + + mob_db[class].job_exp=atoi(str[7]); + if(mob_db[class].job_exp < 0) + mob_db[class].job_exp = 0; + else if(mob_db[class].job_exp > 0 && (mob_db[class].job_exp*battle_config.job_exp_rate/100 > 1000000000 || + mob_db[class].job_exp*battle_config.job_exp_rate/100 < 0)) + mob_db[class].job_exp=1000000000; + else + mob_db[class].job_exp*=battle_config.job_exp_rate/100; + + mob_db[class].range=atoi(str[8]); + mob_db[class].atk1=atoi(str[9]); + mob_db[class].atk2=atoi(str[10]); + mob_db[class].def=atoi(str[11]); + mob_db[class].mdef=atoi(str[12]); + mob_db[class].str=atoi(str[13]); + mob_db[class].agi=atoi(str[14]); + mob_db[class].vit=atoi(str[15]); + mob_db[class].int_=atoi(str[16]); + mob_db[class].dex=atoi(str[17]); + mob_db[class].luk=atoi(str[18]); + mob_db[class].range2=atoi(str[19]); + mob_db[class].range3=atoi(str[20]); + mob_db[class].size=atoi(str[21]); + mob_db[class].race=atoi(str[22]); + mob_db[class].element=atoi(str[23]); + mob_db[class].mode=atoi(str[24]); + mob_db[class].speed=atoi(str[25]); + mob_db[class].adelay=atoi(str[26]); + mob_db[class].amotion=atoi(str[27]); + mob_db[class].dmotion=atoi(str[28]); + + for(i=0;i<8;i++){ + int rate = 0,type,ratemin,ratemax; + mob_db[class].dropitem[i].nameid=atoi(str[29+i*2]); + type = itemdb_type(mob_db[class].dropitem[i].nameid); + if (type == 0) { // Added [Valaris] + rate = battle_config.item_rate_heal; + ratemin = battle_config.item_drop_heal_min; + ratemax = battle_config.item_drop_heal_max; + } + else if (type == 2) { + rate = battle_config.item_rate_use; + ratemin = battle_config.item_drop_use_min; + ratemax = battle_config.item_drop_use_max; // End + } + else if (type == 4 || type == 5 || type == 8) { // Changed to include Pet Equip + rate = battle_config.item_rate_equip; + ratemin = battle_config.item_drop_equip_min; + ratemax = battle_config.item_drop_equip_max; + } + else if (type == 6) { + rate = battle_config.item_rate_card; + ratemin = battle_config.item_drop_card_min; + ratemax = battle_config.item_drop_card_max; + } + else { + rate = battle_config.item_rate_common; + ratemin = battle_config.item_drop_common_min; + ratemax = battle_config.item_drop_common_max; + } + rate = (rate / 100) * atoi(str[30+i*2]); + rate = (rate < ratemin)? ratemin: (rate > ratemax)? ratemax: rate; + mob_db[class].dropitem[i].p = rate; + } + // Item1,Item2 + mob_db[class].mexp=atoi(str[45])*battle_config.mvp_exp_rate/100; + mob_db[class].mexpper=atoi(str[46]); + for(i=0;i<3;i++){ + mob_db[class].mvpitem[i].nameid=atoi(str[47+i*2]); + mob_db[class].mvpitem[i].p=atoi(str[48+i*2])*battle_config.mvp_item_rate/100; + } + for(i=0;i<MAX_RANDOMMONSTER;i++) + mob_db[class].summonper[i]=0; + mob_db[class].maxskill=0; + + mob_db[class].sex=0; + mob_db[class].hair=0; + mob_db[class].hair_color=0; + mob_db[class].weapon=0; + mob_db[class].shield=0; + mob_db[class].head_top=0; + mob_db[class].head_mid=0; + mob_db[class].head_buttom=0; + mob_db[class].clothes_color=0; //Add for player monster dye - Valaris + } + fclose(fp); + printf("read %s done\n",filename[i]); + } + return 0; +} + +/*========================================== + * MOB display graphic change data reading + *------------------------------------------ + */ +static int mob_readdb_mobavail(void) +{ + FILE *fp; + char line[1024]; + int ln=0; + int class,j,k; + char *str[20],*p,*np; + + if( (fp=fopen("db/mob_avail.txt","r"))==NULL ){ + printf("can't read db/mob_avail.txt\n"); + return -1; + } + + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + memset(str,0,sizeof(str)); + + for(j=0,p=line;j<12;j++){ + if((np=strchr(p,','))!=NULL){ + str[j]=p; + *np=0; + p=np+1; + } else + str[j]=p; + } + + if(str[0]==NULL) + continue; + + class=atoi(str[0]); + + if(class<=1000 || class>2000) // 値が異常なら処理しない。 + continue; + k=atoi(str[1]); + if(k >= 0) + mob_db[class].view_class=k; + + if((mob_db[class].view_class < 24) || (mob_db[class].view_class > 4000)) { + mob_db[class].sex=atoi(str[2]); + mob_db[class].hair=atoi(str[3]); + mob_db[class].hair_color=atoi(str[4]); + mob_db[class].weapon=atoi(str[5]); + mob_db[class].shield=atoi(str[6]); + mob_db[class].head_top=atoi(str[7]); + mob_db[class].head_mid=atoi(str[8]); + mob_db[class].head_buttom=atoi(str[9]); + mob_db[class].option=atoi(str[10])&~0x46; + mob_db[class].clothes_color=atoi(str[11]); // Monster player dye option - Valaris + } + + else if(atoi(str[2]) > 0) mob_db[class].equip=atoi(str[2]); // mob equipment [Valaris] + + ln++; + } + fclose(fp); + printf("read db/mob_avail.txt done (count=%d)\n",ln); + return 0; +} + +/*========================================== + * Reading of random monster data + *------------------------------------------ + */ +static int mob_read_randommonster(void) +{ + FILE *fp; + char line[1024]; + char *str[10],*p; + int i,j; + + const char* mobfile[] = { + "db/mob_branch.txt", + "db/mob_poring.txt", + "db/mob_boss.txt" }; + + for(i=0;i<MAX_RANDOMMONSTER;i++){ + mob_db[0].summonper[i] = 1002; // 設定し忘れた場合はポリンが出るようにしておく + fp=fopen(mobfile[i],"r"); + if(fp==NULL){ + printf("can't read %s\n",mobfile[i]); + return -1; + } + while(fgets(line,1020,fp)){ + int 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((class>1000 && class<=2000) || class==0) + mob_db[class].summonper[i]=per; + } + fclose(fp); + printf("read %s done\n",mobfile[i]); + } + return 0; +} +/*========================================== + * db/mob_skill_db.txt reading + *------------------------------------------ + */ +static int mob_readskilldb(void) +{ + FILE *fp; + char line[1024]; + int i; + + const struct { + char str[32]; + int id; + } cond1[] = { + { "always", MSC_ALWAYS }, + { "myhpltmaxrate", MSC_MYHPLTMAXRATE }, + { "friendhpltmaxrate",MSC_FRIENDHPLTMAXRATE }, + { "mystatuson", MSC_MYSTATUSON }, + { "mystatusoff", MSC_MYSTATUSOFF }, + { "friendstatuson", MSC_FRIENDSTATUSON }, + { "friendstatusoff", MSC_FRIENDSTATUSOFF }, + { "attackpcgt", MSC_ATTACKPCGT }, + { "attackpcge", MSC_ATTACKPCGE }, + { "slavelt", MSC_SLAVELT }, + { "slavele", MSC_SLAVELE }, + { "closedattacked", MSC_CLOSEDATTACKED }, + { "longrangeattacked",MSC_LONGRANGEATTACKED }, + { "skillused", MSC_SKILLUSED }, + { "casttargeted", MSC_CASTTARGETED }, + }, cond2[] ={ + { "anybad", -1 }, + { "stone", SC_STONE }, + { "freeze", SC_FREEZE }, + { "stan", SC_STAN }, + { "sleep", SC_SLEEP }, + { "poison", SC_POISON }, + { "curse", SC_CURSE }, + { "silence", SC_SILENCE }, + { "confusion", SC_CONFUSION }, + { "blind", SC_BLIND }, + { "hiding", SC_HIDING }, + { "sight", SC_SIGHT }, + }, state[] = { + { "any", -1 }, + { "idle", MSS_IDLE }, + { "walk", MSS_WALK }, + { "attack", MSS_ATTACK }, + { "dead", MSS_DEAD }, + { "loot", MSS_LOOT }, + { "chase", MSS_CHASE }, + }, target[] = { + { "target", MST_TARGET }, + { "self", MST_SELF }, + { "friend", MST_FRIEND }, + { "around5", MST_AROUND5 }, + { "around6", MST_AROUND6 }, + { "around7", MST_AROUND7 }, + { "around8", MST_AROUND8 }, + { "around1", MST_AROUND1 }, + { "around2", MST_AROUND2 }, + { "around3", MST_AROUND3 }, + { "around4", MST_AROUND4 }, + { "around", MST_AROUND }, + }; + + int x; + char *filename[]={ "db/mob_skill_db.txt","db/mob_skill_db2.txt" }; + + for(x=0;x<2;x++){ + + fp=fopen(filename[x],"r"); + if(fp==NULL){ + if(x==0) + printf("can't read %s\n",filename[x]); + continue; + } + while(fgets(line,1020,fp)){ + char *sp[20],*p; + int mob_id; + struct mob_skill *ms; + int j=0; + + if(line[0] == '/' && line[1] == '/') + continue; + + memset(sp,0,sizeof(sp)); + for(i=0,p=line;i<18 && p;i++){ + sp[i]=p; + if((p=strchr(p,','))!=NULL) + *p++=0; + } + if( (mob_id=atoi(sp[0]))<=0 ) + continue; + + if( strcmp(sp[1],"clear")==0 ){ + memset(mob_db[mob_id].skill,0,sizeof(mob_db[mob_id].skill)); + mob_db[mob_id].maxskill=0; + continue; + } + + for(i=0;i<MAX_MOBSKILL;i++) + if( (ms=&mob_db[mob_id].skill[i])->skill_id == 0) + break; + if(i==MAX_MOBSKILL){ + printf("mob_skill: readdb: too many skill ! [%s] in %d[%s]\n", + sp[1],mob_id,mob_db[mob_id].jname); + continue; + } + + ms->state=atoi(sp[2]); + for(j=0;j<sizeof(state)/sizeof(state[0]);j++){ + if( strcmp(sp[2],state[j].str)==0) + ms->state=state[j].id; + } + ms->skill_id=atoi(sp[3]); + ms->skill_lv=atoi(sp[4]); + ms->permillage=atoi(sp[5]); + ms->casttime=atoi(sp[6]); + ms->delay=atoi(sp[7]); + ms->cancel=atoi(sp[8]); + if( strcmp(sp[8],"yes")==0 ) ms->cancel=1; + ms->target=atoi(sp[9]); + for(j=0;j<sizeof(target)/sizeof(target[0]);j++){ + if( strcmp(sp[9],target[j].str)==0) + ms->target=target[j].id; + } + ms->cond1=-1; + for(j=0;j<sizeof(cond1)/sizeof(cond1[0]);j++){ + if( strcmp(sp[10],cond1[j].str)==0) + ms->cond1=cond1[j].id; + } + ms->cond2=atoi(sp[11]); + for(j=0;j<sizeof(cond2)/sizeof(cond2[0]);j++){ + if( strcmp(sp[11],cond2[j].str)==0) + ms->cond2=cond2[j].id; + } + ms->val[0]=atoi(sp[12]); + ms->val[1]=atoi(sp[13]); + ms->val[2]=atoi(sp[14]); + ms->val[3]=atoi(sp[15]); + ms->val[4]=atoi(sp[16]); + if(sp[17] != NULL && strlen(sp[17])>2) + ms->emotion=atoi(sp[17]); + else + ms->emotion=-1; + mob_db[mob_id].maxskill=i+1; + } + fclose(fp); + printf("read %s done\n",filename[x]); + } + return 0; +} + +void mob_reload(void) +{ + /* + + <empty monster database> + mob_read(); + + */ + + do_init_mob(); +} + +#ifndef TXT_ONLY +/*========================================== + * SQL reading + *------------------------------------------ + */ +static int mob_read_sqldb(void) +{ + char line[1024]; + int i,class,ln=0; + char *str[55],*p,*np; + + memset(mob_db,0,sizeof(mob_db)); + + sprintf (tmp_sql, "SELECT * FROM `%s`",mob_db_db); + if(mysql_query(&mmysql_handle, tmp_sql) ) { + printf("DB server Error (select %s to Memory)- %s\n",mob_db_db,mysql_error(&mmysql_handle) ); + } + sql_res = mysql_store_result(&mmysql_handle); + if (sql_res) { + while((sql_row = mysql_fetch_row(sql_res))){ + sprintf(line,"%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", + sql_row[0],sql_row[1],sql_row[2],sql_row[3],sql_row[4], + sql_row[5],sql_row[6],sql_row[7],sql_row[8],sql_row[9], + sql_row[10],sql_row[11],sql_row[12],sql_row[13],sql_row[14], + sql_row[15],sql_row[16],sql_row[17],sql_row[18],sql_row[19], + sql_row[20],sql_row[21],sql_row[22],sql_row[23],sql_row[24], + sql_row[25],sql_row[26],sql_row[27],sql_row[28],sql_row[29], + sql_row[30],sql_row[31],sql_row[32],sql_row[33],sql_row[34], + sql_row[35],sql_row[36],sql_row[37],sql_row[38],sql_row[39], + sql_row[40],sql_row[41],sql_row[42],sql_row[43],sql_row[44], + sql_row[45],sql_row[46],sql_row[47],sql_row[48],sql_row[49], + sql_row[50],sql_row[51],sql_row[52]); + + for(i=0,p=line;i<55;i++){ + if((np=strchr(p,','))!=NULL){ + str[i]=p; + *np=0; + p=np+1; + } else + str[i]=p; + } + + class=atoi(str[0]); + if(class<=1000 || class>2000) + continue; + + ln++; + + mob_db[class].view_class=class; + memcpy(mob_db[class].name,str[1],24); + memcpy(mob_db[class].jname,str[2],24); + mob_db[class].lv=atoi(str[3]); + mob_db[class].max_hp=atoi(str[4]); + mob_db[class].max_sp=atoi(str[5]); + mob_db[class].base_exp=atoi(str[6])* + battle_config.base_exp_rate/100; + if(mob_db[class].base_exp <= 0) + mob_db[class].base_exp = 1; + mob_db[class].job_exp=atoi(str[7])* + battle_config.job_exp_rate/100; + if(mob_db[class].job_exp <= 0) + mob_db[class].job_exp = 1; + mob_db[class].range=atoi(str[8]); + mob_db[class].atk1=atoi(str[9]); + mob_db[class].atk2=atoi(str[10]); + mob_db[class].def=atoi(str[11]); + mob_db[class].mdef=atoi(str[12]); + mob_db[class].str=atoi(str[13]); + mob_db[class].agi=atoi(str[14]); + mob_db[class].vit=atoi(str[15]); + mob_db[class].int_=atoi(str[16]); + mob_db[class].dex=atoi(str[17]); + mob_db[class].luk=atoi(str[18]); + mob_db[class].range2=atoi(str[19]); + mob_db[class].range3=atoi(str[20]); + mob_db[class].size=atoi(str[21]); + mob_db[class].race=atoi(str[22]); + mob_db[class].element=atoi(str[23]); + mob_db[class].mode=atoi(str[24]); + mob_db[class].speed=atoi(str[25]); + mob_db[class].adelay=atoi(str[26]); + mob_db[class].amotion=atoi(str[27]); + mob_db[class].dmotion=atoi(str[28]); + + for(i=0;i<8;i++){ + int rate = 0,type,ratemin,ratemax; + mob_db[class].dropitem[i].nameid=atoi(str[29+i*2]); + type = itemdb_type(mob_db[class].dropitem[i].nameid); + if (type == 0) { // Added by Valaris + rate = battle_config.item_rate_heal; + ratemin = battle_config.item_drop_heal_min; + ratemax = battle_config.item_drop_heal_max; + } + else if (type == 2) { + rate = battle_config.item_rate_use; + ratemin = battle_config.item_drop_use_min; + ratemax = battle_config.item_drop_use_max; // End + } + else if (type == 4 || type == 5 || type == 8) { // Changed to include Pet Equip + rate = battle_config.item_rate_equip; + ratemin = battle_config.item_drop_equip_min; + ratemax = battle_config.item_drop_equip_max; + } + else if (type == 6) { + rate = battle_config.item_rate_card; + ratemin = battle_config.item_drop_card_min; + ratemax = battle_config.item_drop_card_max; + } + else { + rate = battle_config.item_rate_common; + ratemin = battle_config.item_drop_common_min; + ratemax = battle_config.item_drop_common_max; + } + rate = (rate / 100) * atoi(str[30+i*2]); + rate = (rate < ratemin)? ratemin: (rate > ratemax)? ratemax: rate; + mob_db[class].dropitem[i].p = rate; + } + + mob_db[class].mexp=atoi(str[45])*battle_config.mvp_exp_rate/100; + mob_db[class].mexpper=atoi(str[46]); + for(i=0;i<3;i++){ + mob_db[class].mvpitem[i].nameid=atoi(str[47+i*2]); + mob_db[class].mvpitem[i].p=atoi(str[48+i*2])*battle_config.mvp_item_rate/100; + } + for(i=0;i<MAX_RANDOMMONSTER;i++) + mob_db[class].summonper[i]=0; + mob_db[class].maxskill=0; + + mob_db[class].sex=0; + mob_db[class].hair=0; + mob_db[class].hair_color=0; + mob_db[class].weapon=0; + mob_db[class].shield=0; + mob_db[class].head_top=0; + mob_db[class].head_mid=0; + mob_db[class].head_buttom=0; + } + mysql_free_result(sql_res); + printf("read %s done (count=%d)\n",mob_db_db,ln); + } + return 0; +} + +#endif /* not TXT_ONLY */ +/*========================================== + * Circumference initialization of mob + *------------------------------------------ + */ +int do_init_mob(void) +{ +#ifndef TXT_ONLY + if(db_use_sqldbs) + mob_read_sqldb(); + else +#endif /* TXT_ONLY */ + mob_readdb(); + + mob_readdb_mobavail(); + mob_read_randommonster(); + mob_readskilldb(); + + 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_delay_item_drop2,"mob_delay_item_drop2"); + 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_interval(gettick()+MIN_MOBTHINKTIME,mob_ai_hard,0,0,MIN_MOBTHINKTIME); + add_timer_interval(gettick()+MIN_MOBTHINKTIME*10,mob_ai_lazy,0,0,MIN_MOBTHINKTIME*10); + + return 0; +} diff --git a/src/map/mob.h b/src/map/mob.h new file mode 100644 index 0000000..7c1467b --- /dev/null +++ b/src/map/mob.h @@ -0,0 +1,137 @@ +// $Id: mob.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _MOB_H_ +#define _MOB_H_ + +#define MAX_RANDOMMONSTER 3 + +struct mob_skill { + short state; + short skill_id,skill_lv; + short permillage; + int casttime,delay; + short cancel; + short cond1,cond2; + short target; + int val[5]; + short emotion; +}; + +struct mob_db { + char name[24],jname[24]; + int lv; + int max_hp,max_sp; + int base_exp,job_exp; + int atk1,atk2; + int def,mdef; + int str,agi,vit,int_,dex,luk; + int range,range2,range3; + int size,race,element,mode; + int speed,adelay,amotion,dmotion; + int mexp,mexpper; + struct { int nameid,p; } dropitem[8]; + struct { int nameid,p; } mvpitem[3]; + int view_class,sex; + short hair,hair_color,weapon,shield,head_top,head_mid,head_buttom,option,clothes_color; // [Valaris] + int equip; // [Valaris] + int summonper[MAX_RANDOMMONSTER]; + int maxskill; + struct mob_skill skill[MAX_MOBSKILL]; +}; +extern struct mob_db mob_db[]; + +enum { + MST_TARGET = 0, + MST_SELF = 1, + MST_FRIEND = 2, + MST_AROUND5 = 3, + MST_AROUND6 = 4, + MST_AROUND7 = 5, + MST_AROUND8 = 6, + MST_AROUND1 = 7, + MST_AROUND2 = 8, + MST_AROUND3 = 9, + MST_AROUND4 = 10, + MST_AROUND = MST_AROUND4, + + MSC_ALWAYS = 0x0000, + MSC_MYHPLTMAXRATE = 0x0001, + MSC_FRIENDHPLTMAXRATE= 0x0010, + MSC_MYSTATUSON = 0x0020, + MSC_MYSTATUSOFF = 0x0021, + MSC_FRIENDSTATUSON = 0x0030, + MSC_FRIENDSTATUSOFF = 0x0031, + + MSC_ATTACKPCGT = 0x0100, + MSC_ATTACKPCGE = 0x0101, + MSC_SLAVELT = 0x0110, + MSC_SLAVELE = 0x0111, + MSC_CLOSEDATTACKED = 0x1000, + MSC_LONGRANGEATTACKED= 0x1001, + MSC_SKILLUSED = 0x1010, + MSC_CASTTARGETED = 0x1011, +}; + +enum { + MSS_IDLE, // 待機 + MSS_WALK, // 移動 + MSS_ATTACK, // 攻撃 + MSS_DEAD, // 死亡 + MSS_LOOT, // ルート + MSS_CHASE, // 突撃 +}; + +int mobdb_searchname(const char *str); +int mobdb_checkid(const int id); +int mob_once_spawn(struct map_session_data *sd,char *mapname, + int x,int y,const char *mobname,int class,int amount,const char *event); +int mob_once_spawn_area(struct map_session_data *sd,char *mapname, + int x0,int y0,int x1,int y1, + const char *mobname,int class,int amount,const char *event); + +int mob_spawn_guardian(struct map_session_data *sd,char *mapname, // Spawning Guardians [Valaris] + int x,int y,const char *mobname,int class,int amount,const char *event,int guardian); // Spawning Guardians [Valaris] + + +int mob_walktoxy(struct mob_data *md,int x,int y,int easy); + +int mob_target(struct mob_data *md,struct block_list *bl,int dist); +int mob_stop_walking(struct mob_data *md,int type); +int mob_stopattack(struct mob_data *); +int mob_spawn(int); +int mob_damage(struct block_list *,struct mob_data*,int,int); +int mob_changestate(struct mob_data *md,int state,int type); +int mob_heal(struct mob_data*,int); +int mob_get_viewclass(int); +int mob_get_sex(int); +short mob_get_hair(int); +short mob_get_hair_color(int); +short mob_get_weapon(int); +short mob_get_shield(int); +short mob_get_head_top(int); +short mob_get_head_mid(int); +short mob_get_head_buttom(int); +short mob_get_clothes_color(int); //player mob dye [Valaris] +int mob_get_equip(int); // mob equip [Valaris] +int do_init_mob(void); + +int mob_delete(struct mob_data *md); +int mob_catch_delete(struct mob_data *md,int type); +int mob_timer_delete(int tid, unsigned int tick, int id, int data); + +int mob_deleteslave(struct mob_data *md); + +int mob_counttargeted(struct mob_data *md,struct block_list *src,int target_lv); + +int mob_class_change(struct mob_data *md,int *value); +int mob_warp(struct mob_data *md,int m,int x,int y,int type); + +int mobskill_use(struct mob_data *md,unsigned int tick,int event); +int mobskill_event(struct mob_data *md,int flag); +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 flag); + +int mob_gvmobcheck(struct map_session_data *sd, struct block_list *bl); +void mob_reload(void); + +#endif diff --git a/src/map/npc.c b/src/map/npc.c new file mode 100644 index 0000000..05a5dc4 --- /dev/null +++ b/src/map/npc.c @@ -0,0 +1,2035 @@ +// $Id: npc.c,v 1.5 2004/09/25 05:32:18 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <math.h> +#include <time.h> + +#include "db.h" +#include "timer.h" +#include "nullpo.h" +#include "malloc.h" +#include "map.h" +#include "npc.h" +#include "clif.h" +#include "intif.h" +#include "pc.h" +#include "itemdb.h" +#include "script.h" +#include "mob.h" +#include "pet.h" +#include "battle.h" +#include "skill.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + + + +struct npc_src_list { + struct npc_src_list * next; + struct npc_src_list * prev; + char name[4]; +} ; + +static struct npc_src_list *npc_src_first,*npc_src_last; +static int npc_id=START_NPC_NUM; +static int npc_warp,npc_shop,npc_script,npc_mob; + +int npc_get_new_npc_id(void){ return npc_id++; } + +static struct dbt *ev_db; +static struct dbt *npcname_db; + +struct event_data { + struct npc_data *nd; + int pos; +}; +static struct tm ev_tm_b; // 時計イベント用 + + +/*========================================== + * NPCの無効化/有効化 + * npc_enable + * npc_enable_sub 有効時にOnTouchイベントを実行 + *------------------------------------------ + */ +int npc_enable_sub( struct block_list *bl, va_list ap ) +{ + struct map_session_data *sd; + struct npc_data *nd; + char *name=(char *)aCalloc(50,sizeof(char)); + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, nd=va_arg(ap,struct npc_data *)); + if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){ + + if (nd->flag&1) // 無効化されている + return 1; + + memcpy(name,nd->name,50); + if(sd->areanpc_id==nd->bl.id) + return 1; + sd->areanpc_id=nd->bl.id; + npc_event(sd,strcat(name,"::OnTouch"),0); + } + free(name); + return 0; +} +int npc_enable(const char *name,int flag) +{ + struct npc_data *nd=strdb_search(npcname_db,name); + if (nd==NULL) + return 0; + + if (flag&1) { // 有効化 + nd->flag&=~1; + clif_spawnnpc(nd); + }else if (flag&2){ + nd->flag&=~1; + nd->option = 0x0000; + clif_changeoption(&nd->bl); + }else if (flag&4){ + nd->flag|=1; + nd->option = 0x0002; + clif_changeoption(&nd->bl); + }else{ // 無効化 + nd->flag|=1; + clif_clearchar(&nd->bl,0); + } + if(flag&3 && (nd->u.scr.xs > 0 || nd->u.scr.ys >0)) + map_foreachinarea( npc_enable_sub,nd->bl.m,nd->bl.x-nd->u.scr.xs,nd->bl.y-nd->u.scr.ys,nd->bl.x+nd->u.scr.xs,nd->bl.y+nd->u.scr.ys,BL_PC,nd); + + return 0; +} + +/*========================================== + * NPCを名前で探す + *------------------------------------------ + */ +struct npc_data* npc_name2id(const char *name) +{ + return strdb_search(npcname_db,name); +} +/*========================================== + * イベントキューのイベント処理 + *------------------------------------------ + */ +int npc_event_dequeue(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + sd->npc_id=0; + if (sd->eventqueue[0][0]) { // キューのイベント処理 + char *name=(char *)aCalloc(50,sizeof(char)); + int i; + + memcpy(name,sd->eventqueue[0],50); + for(i=MAX_EVENTQUEUE-2;i>=0;i--) + memcpy(sd->eventqueue[i],sd->eventqueue[i+1],50); + add_timer(gettick()+100,npc_event_timer,sd->bl.id,(int)name); + } + return 0; +} + +int npc_delete(struct npc_data *nd) +{ + nullpo_retr(1, nd); + + if(nd->bl.prev == NULL) + return 1; + + clif_clearchar_area(&nd->bl,1); + map_delblock(&nd->bl); + return 0; +} + +/*========================================== + * イベントの遅延実行 + *------------------------------------------ + */ +int npc_event_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd=map_id2sd(id); + if (sd==NULL) + return 0; + + npc_event(sd,(const char *)data,0); + free((void*)data); + return 0; +} + +int npc_timer_event(const char *eventname) // Added by RoVeRT +{ + struct event_data *ev=strdb_search(ev_db,eventname); + struct npc_data *nd; +// int xs,ys; + + if((ev==NULL || (nd=ev->nd)==NULL)){ + printf("npc_event: event not found [%s]\n",eventname); + return 0; + } + + run_script(nd->u.scr.script,ev->pos,nd->bl.id,nd->bl.id); + + return 0; +} +/* +int npc_timer_sub_sub(void *key,void *data,va_list ap) // Added by RoVeRT +{ + char *p=(char *)key; + struct event_data *ev=(struct event_data *)data; + int *c=va_arg(ap,int *); + int tick=0,ctick=gettick(); + char temp[10]; + char event[100]; + + if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){ + sscanf(&p[9],"%s",temp); + tick=atoi(temp); + + strcpy( event, ev->nd->name); + strcat( event, p); + + if (ctick >= ev->nd->lastaction && ctick - ev->nd->timer >= tick) { + npc_timer_event(event); + ev->nd->lastaction = ctick; + } + } + return 0; +} + +int npc_timer_sub(void *key,void *data,va_list ap) // Added by RoVeRT +{ + struct npc_data *nd=(struct npc_data*)data; + + if(nd->timer == -1) + return 0; + + strdb_foreach(ev_db,npc_timer_sub_sub,&nd->bl.id); + + return 0; +} + +int npc_timer(int tid,unsigned int tick,int id,int data) // Added by RoVeRT +{ + strdb_foreach(npcname_db,npc_timer_sub); + + free((void*)data); + return 0; +}*/ +/*========================================== + * イベント用ラベルのエクスポート + * npc_parse_script->strdb_foreachから呼ばれる + *------------------------------------------ + */ +int npc_event_export(void *key,void *data,va_list ap) +{ + char *lname=(char *)key; + int pos=(int)data; + struct npc_data *nd=va_arg(ap,struct npc_data *); + + if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) { + struct event_data *ev; + char *buf; + char *p=strchr(lname,':'); + // エクスポートされる + ev=calloc(sizeof(struct event_data), 1); + buf=calloc(50, 1); + if (ev==NULL || buf==NULL) { + printf("npc_event_export: out of memory !\n"); + exit(1); + }else if (p==NULL || (p-lname)>24) { + printf("npc_event_export: label name error !\n"); + exit(1); + }else{ + ev->nd=nd; + ev->pos=pos; + *p='\0'; + sprintf(buf,"%s::%s",nd->exname,lname); + *p=':'; + strdb_insert(ev_db,buf,ev); +// if (battle_config.etc_log) +// printf("npc_event_export: export [%s]\n",buf); + } + } + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/*========================================== + * 全てのNPCのOn*イベント実行 + *------------------------------------------ + */ +int npc_event_doall_sub(void *key,void *data,va_list ap) +{ + char *p=(char *)key; + struct event_data *ev; + int *c; + const 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 char *); + + if( (p=strchr(p,':')) && p && strcasecmp(name,p)==0 ){ + run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id); + (*c)++; + } + + return 0; +} +int npc_event_doall(const char *name) +{ + int c=0; + char buf[64]="::"; + + strncpy(buf+2,name,62); + strdb_foreach(ev_db,npc_event_doall_sub,&c,buf); + return c; +} + +int npc_event_do_sub(void *key,void *data,va_list ap) +{ + char *p=(char *)key; + struct event_data *ev; + int *c; + const 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 char *); + + if (p && strcasecmp(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 char *name) +{ + int c=0; + + if (*name==':' && name[1]==':') { + return npc_event_doall(name+2); + } + + strdb_foreach(ev_db,npc_event_do_sub,&c,name); + return c; +} + +/*========================================== + * 時計イベント実行 + *------------------------------------------ + */ +int npc_event_do_clock(int tid,unsigned int tick,int id,int data) +{ + time_t timer; + struct tm *t; + char buf[64]; + int c=0; + + time(&timer); + t=localtime(&timer); + + if (t->tm_min != ev_tm_b.tm_min ) { + sprintf(buf,"OnMinute%02d",t->tm_min); + c+=npc_event_doall(buf); + sprintf(buf,"OnClock%02d%02d",t->tm_hour,t->tm_min); + c+=npc_event_doall(buf); + } + if (t->tm_hour!= ev_tm_b.tm_hour) { + sprintf(buf,"OnHour%02d",t->tm_hour); + c+=npc_event_doall(buf); + } + if (t->tm_mday!= ev_tm_b.tm_mday) { + sprintf(buf,"OnDay%02d%02d",t->tm_mon+1,t->tm_mday); + c+=npc_event_doall(buf); + } + memcpy(&ev_tm_b,t,sizeof(ev_tm_b)); + return c; +} +/*========================================== + * OnInitイベント実行(&時計イベント開始) + *------------------------------------------ + */ +int npc_event_do_oninit(void) +{ + int c = npc_event_doall("OnInit"); + printf("npc: OnInit Event done. (%d npc)\n",c); + + add_timer_interval(gettick()+100, + npc_event_do_clock,0,0,1000); + + return 0; +} +/*========================================== + * OnTimer NPC event - by RoVeRT + *------------------------------------------ + */ +int npc_addeventtimer(struct npc_data *nd,int tick,const char *name) +{ + int i; + for(i=0;i<MAX_EVENTTIMER;i++) + if( nd->eventtimer[i]==-1 ) + break; + if(i<MAX_EVENTTIMER){ + char *evname=malloc(24); + if(evname==NULL){ + printf("npc_addeventtimer: out of memory !\n");exit(1); + } + memcpy(evname,name,24); + nd->eventtimer[i]=add_timer(gettick()+tick, + npc_event_timer,nd->bl.id,(int)evname); + }else + printf("npc_addtimer: event timer is full !\n"); + + return 0; +} + +int npc_deleventtimer(struct npc_data *nd,const char *name) +{ + int i; + for(i=0;i<MAX_EVENTTIMER;i++) + if( nd->eventtimer[i]!=-1 && strcmp( + (char *)(get_timer(nd->eventtimer[i])->data), name)==0 ){ + delete_timer(nd->eventtimer[i],npc_event_timer); + nd->eventtimer[i]=-1; + break; + } + + return 0; +} + +int npc_cleareventtimer(struct npc_data *nd) +{ + int i; + for(i=0;i<MAX_EVENTTIMER;i++) + if( nd->eventtimer[i]!=-1 ){ + delete_timer(nd->eventtimer[i],npc_event_timer); + nd->eventtimer[i]=-1; + } + + return 0; +} + +int npc_do_ontimer_sub(void *key,void *data,va_list ap) +{ + char *p=(char *)key; + struct event_data *ev=(struct event_data *)data; + int *c=va_arg(ap,int *); +// struct map_session_data *sd=va_arg(ap,struct map_session_data *); + int option=va_arg(ap,int); + int tick=0; + char temp[10]; + char event[50]; + + if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){ + sscanf(&p[9],"%s",temp); + tick=atoi(temp); + + strcpy( event, ev->nd->name); + strcat( event, p); + + if (option!=0) { + npc_addeventtimer(ev->nd,tick,event); + } else { + npc_deleventtimer(ev->nd,event); + } + } + return 0; +} +int npc_do_ontimer(int npc_id, struct map_session_data *sd, int option) +{ + strdb_foreach(ev_db,npc_do_ontimer_sub,&npc_id,sd,option); + return 0; +} +/*========================================== + * タイマーイベント用ラベルの取り込み + * npc_parse_script->strdb_foreachから呼ばれる + *------------------------------------------ + */ +int npc_timerevent_import(void *key,void *data,va_list ap) +{ + char *lname=(char *)key; + int pos=(int)data; + struct npc_data *nd=va_arg(ap,struct npc_data *); + int t=0,i=0; + + if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':') { + // タイマーイベント + struct npc_timerevent_list *te=nd->u.scr.timer_event; + int j,i=nd->u.scr.timeramount; + if(te==NULL) te=malloc(sizeof(struct npc_timerevent_list)); + else te=realloc( te, sizeof(struct npc_timerevent_list) * (i+1) ); + if(te==NULL){ + printf("npc_timerevent_import: out of memory !\n"); + exit(1); + } + for(j=0;j<i;j++){ + if(te[j].timer>t){ + memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j)); + break; + } + } + te[j].timer=t; + te[j].pos=pos; + nd->u.scr.timer_event=te; + nd->u.scr.timeramount=i+1; + } + return 0; +} +/*========================================== + * タイマーイベント実行 + *------------------------------------------ + */ +int npc_timerevent(int tid,unsigned int tick,int id,int data) +{ + int next,t; + struct npc_data* nd=(struct npc_data *)map_id2bl(id); + struct npc_timerevent_list *te; + if( nd==NULL || nd->u.scr.nexttimer<0 ){ + printf("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,0,nd->bl.id); + return 0; +} +/*========================================== + * タイマーイベント開始 + *------------------------------------------ + */ +int npc_timerevent_start(struct npc_data *nd) +{ + int j,n, next; + + nullpo_retr(0, nd); + + n=nd->u.scr.timeramount; + if( nd->u.scr.nexttimer>=0 || n==0 ) + return 0; + + for(j=0;j<n;j++){ + if( nd->u.scr.timer_event[j].timer > nd->u.scr.timer ) + break; + } + nd->u.scr.nexttimer=j; + nd->u.scr.timertick=gettick(); + + if(j>=n) + return 0; + + next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer; + nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,next); + return 0; +} +/*========================================== + * タイマーイベント終了 + *------------------------------------------ + */ +int npc_timerevent_stop(struct npc_data *nd) +{ + nullpo_retr(0, nd); + + if( nd->u.scr.nexttimer>=0 ){ + nd->u.scr.nexttimer = -1; + nd->u.scr.timer += (int)(gettick() - nd->u.scr.timertick); + if(nd->u.scr.timerid!=-1) + delete_timer(nd->u.scr.timerid,npc_timerevent); + nd->u.scr.timerid = -1; + } + return 0; +} +/*========================================== + * タイマー値の所得 + *------------------------------------------ + */ +int npc_gettimerevent_tick(struct npc_data *nd) +{ + int tick; + + nullpo_retr(0, nd); + + tick=nd->u.scr.timer; + + if( nd->u.scr.nexttimer>=0 ) + tick += (int)(gettick() - nd->u.scr.timertick); + return tick; +} +/*========================================== + * タイマー値の設定 + *------------------------------------------ + */ +int npc_settimerevent_tick(struct npc_data *nd,int newtimer) +{ + int flag; + + nullpo_retr(0, nd); + + flag= nd->u.scr.nexttimer; + + npc_timerevent_stop(nd); + nd->u.scr.timer=newtimer; + if(flag>=0) + npc_timerevent_start(nd); + return 0; +} + +/*========================================== + * イベント型のNPC処理 + *------------------------------------------ + */ +int npc_event(struct map_session_data *sd,const char *eventname,int mob_kill) +{ + struct event_data *ev=strdb_search(ev_db,eventname); + struct npc_data *nd; + int xs,ys; + char mobevent[100]; + + if( sd == NULL ){ + printf("npc_event nullpo?\n"); + } + + if(ev==NULL && eventname && strcmp(((eventname)+strlen(eventname)-9),"::OnTouch") == 0) + return 1; + + if(ev==NULL || (nd=ev->nd)==NULL){ + if(mob_kill && (ev==NULL || (nd=ev->nd)==NULL)){ + strcpy( mobevent, eventname); + strcat( mobevent, "::OnMyMobDead"); + ev=strdb_search(ev_db,mobevent); + if (ev==NULL || (nd=ev->nd)==NULL) { + if (strncasecmp(eventname,"GM_MONSTER",10)!=0) + printf("npc_event: event not found [%s]\n",mobevent); + return 0; + } + } + else { + if(battle_config.error_log) + printf("npc_event: event not found [%s]\n",eventname); + return 0; + } + } + + xs=nd->u.scr.xs; + ys=nd->u.scr.ys; + if (xs>=0 && ys>=0 ) { + if (nd->bl.m != sd->bl.m ) + return 1; + if ( xs>0 && (sd->bl.x<nd->bl.x-xs/2 || nd->bl.x+xs/2<sd->bl.x) ) + return 1; + if ( ys>0 && (sd->bl.y<nd->bl.y-ys/2 || nd->bl.y+ys/2<sd->bl.y) ) + return 1; + } + + if ( sd->npc_id!=0) { +// if (battle_config.error_log) +// printf("npc_event: npc_id != 0\n"); + int i; + for(i=0;i<MAX_EVENTQUEUE;i++) + if (!sd->eventqueue[i][0]) + break; + if (i==MAX_EVENTQUEUE) { + if (battle_config.error_log) + printf("npc_event: event queue is full !\n"); + }else{ +// if (battle_config.etc_log) +// printf("npc_event: enqueue\n"); + 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(void *key,void *data,va_list ap) +{ + char *p=(char *)key; + struct event_data *ev=(struct event_data *)data; + char *npcname=va_arg(ap,char *); + char *command=va_arg(ap,char *); + char temp[100]; + + if(strcmp(ev->nd->name,npcname)==0 && (p=strchr(p,':')) && p && strncasecmp("::OnCommand",p,10)==0 ){ + sscanf(&p[11],"%s",temp); + + if (strcmp(command,temp)==0) + run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id); + } + + return 0; +} + +int npc_command(struct map_session_data *sd,char *npcname,char *command) +{ + strdb_foreach(ev_db,npc_command_sub,npcname,command); + + return 0; +} +/*========================================== + * 接触型のNPC処理 + *------------------------------------------ + */ +int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y) +{ + int i,f=1; + int xs,ys; + + nullpo_retr(1, sd); + + if(sd->npc_id) + return 1; + + for(i=0;i<map[m].npc_num;i++) { + if (map[m].npc[i]->flag&1) { // 無効化されている + f=0; + continue; + } + + switch(map[m].npc[i]->bl.subtype) { + case WARP: + xs=map[m].npc[i]->u.warp.xs; + ys=map[m].npc[i]->u.warp.ys; + break; + case SCRIPT: + xs=map[m].npc[i]->u.scr.xs; + ys=map[m].npc[i]->u.scr.ys; + break; + default: + continue; + } + if (x >= map[m].npc[i]->bl.x-xs/2 && x < map[m].npc[i]->bl.x-xs/2+xs && + y >= map[m].npc[i]->bl.y-ys/2 && y < map[m].npc[i]->bl.y-ys/2+ys) + break; + } + if (i==map[m].npc_num) { + if (f) { + if (battle_config.error_log) + printf("npc_touch_areanpc : some bug \n"); + } + return 1; + } + switch(map[m].npc[i]->bl.subtype) { + case WARP: + skill_stop_dancing(&sd->bl,0); + pc_setpos(sd,map[m].npc[i]->u.warp.name,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0); + break; + case SCRIPT: + { + char *name=(char *)aCalloc(50,sizeof(char)); + + memcpy(name,map[m].npc[i]->name,50); + if(sd->areanpc_id==map[m].npc[i]->bl.id) + return 1; + sd->areanpc_id=map[m].npc[i]->bl.id; + if(npc_event(sd,strcat(name,"::OnTouch"),0)>0) + npc_click(sd,map[m].npc[i]->bl.id); + free(name); + break; + } + } + return 0; +} + +/*========================================== + * 近くかどうかの判定 + *------------------------------------------ + */ +int npc_checknear(struct map_session_data *sd,int id) +{ + struct npc_data *nd; + + nullpo_retr(0, sd); + + nd=(struct npc_data *)map_id2bl(id); + if (nd==NULL || nd->bl.type!=BL_NPC) { + if (battle_config.error_log) + printf("no such npc : %d\n",id); + return 1; + } + + if (nd->class<0) // イベント系は常にOK + return 0; + + // エリア判定 + if (nd->bl.m!=sd->bl.m || + nd->bl.x<sd->bl.x-AREA_SIZE-1 || nd->bl.x>sd->bl.x+AREA_SIZE+1 || + nd->bl.y<sd->bl.y-AREA_SIZE-1 || nd->bl.y>sd->bl.y+AREA_SIZE+1) + return 1; + + return 0; +} + +/*========================================== + * クリック時のNPC処理 + *------------------------------------------ + */ +int npc_click(struct map_session_data *sd,int id) +{ + struct npc_data *nd; + + nullpo_retr(1, sd); + + if (sd->npc_id != 0) { + if (battle_config.error_log) + printf("npc_click: npc_id != 0\n"); + return 1; + } + + if (npc_checknear(sd,id)) + 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) + printf("no such shop npc : %d\n",id); + sd->npc_id=0; + return 1; + } + if (nd->flag&1) // 無効化されている + return 1; + + sd->npc_shopid=id; + if (type==0) { + clif_buylist(sd,nd); + } else { + clif_selllist(sd); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int npc_buylist(struct map_session_data *sd,int n,unsigned short *item_list) +{ + struct npc_data *nd; + double z; + int i,j,w,skill,itemamount=0,new=0; + + nullpo_retr(3, sd); + nullpo_retr(3, item_list); + + if (npc_checknear(sd,sd->npc_shopid)) + return 3; + + nd=(struct npc_data*)map_id2bl(sd->npc_shopid); + if (nd->bl.subtype!=SHOP) + return 3; + + for(i=0,w=0,z=0;i<n;i++) { + for(j=0;nd->u.shop_item[j].nameid;j++) { + if (nd->u.shop_item[j].nameid==item_list[i*2+1]) + break; + } + if (nd->u.shop_item[j].nameid==0) + return 3; + + if (itemdb_value_notdc(nd->u.shop_item[j].nameid)) + z+=(double)nd->u.shop_item[j].value * item_list[i*2]; + else + z+=(double)pc_modifybuyvalue(sd,nd->u.shop_item[j].value) * item_list[i*2]; + itemamount+=item_list[i*2]; + + switch(pc_checkadditem(sd,item_list[i*2+1],item_list[i*2])) { + case ADDITEM_EXIST: + break; + case ADDITEM_NEW: + new++; + break; + case ADDITEM_OVERAMOUNT: + return 2; + } + + w+=itemdb_weight(item_list[i*2+1]) * item_list[i*2]; + } + if (z > (double)sd->status.zeny) + return 1; // zeny不足 + if (w+sd->weight > sd->max_weight) + return 2; // 重量超過 + if (pc_inventoryblank(sd)<new) + return 3; // 種類数超過 + + pc_payzeny(sd,(int)z); + for(i=0;i<n;i++) { + struct item item_tmp; + + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = item_list[i*2+1]; + item_tmp.identify = 1; // npc販売アイテムは鑑定済み + + pc_additem(sd,&item_tmp,item_list[i*2]); + } + + //商人経験値 +/* if ((sd->status.class == 5) || (sd->status.class == 10) || (sd->status.class == 18)) { + z = z * pc_checkskill(sd,MC_DISCOUNT) / ((1 + 300 / itemamount) * 4000) * battle_config.shop_exp; + pc_gainexp(sd,0,z); + }*/ + if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) { + if (sd->status.skill[MC_DISCOUNT].flag != 0) + skill = sd->status.skill[MC_DISCOUNT].flag - 2; + if (skill > 0) { + z = (log(z * (double)skill) * (double)battle_config.shop_exp/100.); + if (z < 1) + z = 1; + pc_gainexp(sd,0,(int)z); + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int npc_selllist(struct map_session_data *sd,int n,unsigned short *item_list) +{ + double z; + int i,skill,itemamount=0; + + nullpo_retr(1, sd); + nullpo_retr(1, item_list); + + if (npc_checknear(sd,sd->npc_shopid)) + return 1; + for(i=0,z=0;i<n;i++) { + int nameid; + if (item_list[i*2]-2 <0 || item_list[i*2]-2 >=MAX_INVENTORY) + return 1; + nameid=sd->status.inventory[item_list[i*2]-2].nameid; + if (nameid == 0 || + sd->status.inventory[item_list[i*2]-2].amount < item_list[i*2+1]) + return 1; + if (itemdb_value_notoc(nameid)) + z+=(double)itemdb_value_sell(nameid) * item_list[i*2+1]; + else + z+=(double)pc_modifysellvalue(sd,itemdb_value_sell(nameid)) * item_list[i*2+1]; + itemamount+=item_list[i*2+1]; + } + + if (z > MAX_ZENY) z = MAX_ZENY; + pc_getzeny(sd,(int)z); + for(i=0;i<n;i++) { + int item_id=item_list[i*2]-2; + 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((*(long *)(&sd->status.inventory[item_id].card[1]))); + pc_delitem(sd,item_id,item_list[i*2+1],0); + } + + //商人経験値 +/* if ((sd->status.class == 5) || (sd->status.class == 10) || (sd->status.class == 18)) { + z = z * pc_checkskill(sd,MC_OVERCHARGE) / ((1 + 500 / itemamount) * 4000) * battle_config.shop_exp ; + pc_gainexp(sd,0,z); + }*/ + if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) { + if (sd->status.skill[MC_OVERCHARGE].flag != 0) + skill = sd->status.skill[MC_OVERCHARGE].flag - 2; + if (skill > 0) { + z = (log(z * (double)skill) * (double)battle_config.shop_exp/100.); + if (z < 1) + z = 1; + pc_gainexp(sd,0,(int)z); + } + } + + return 0; + +} + +// +// 初期化関係 +// + +/*========================================== + * 読み込むnpcファイルのクリア + *------------------------------------------ + */ +void npc_clearsrcfile() +{ + struct npc_src_list *p=npc_src_first; + + while( p ) { + struct npc_src_list *p2=p; + p=p->next; + free(p2); + } + npc_src_first=NULL; + npc_src_last=NULL; +} +/*========================================== + * 読み込むnpcファイルの追加 + *------------------------------------------ + */ +void npc_addsrcfile(char *name) +{ + struct npc_src_list *new; + size_t len; + + if ( strcmpi(name,"clear")==0 ) { + npc_clearsrcfile(); + return; + } + + len = sizeof(*new) + strlen(name); + new=(struct npc_src_list *)aCalloc(1,len); + new->next = NULL; + strncpy(new->name,name,strlen(name)+1); + if (npc_src_first==NULL) + npc_src_first = new; + if (npc_src_last) + npc_src_last->next = new; + + npc_src_last=new; +} +/*========================================== + * 読み込むnpcファイルの削除 + *------------------------------------------ + */ +void npc_delsrcfile(char *name) +{ + struct npc_src_list *p=npc_src_first,*pp=NULL,**lp=&npc_src_first; + + if ( strcmpi(name,"all")==0 ) { + npc_clearsrcfile(); + return; + } + + for( ; p; lp=&p->next,pp=p,p=p->next ) { + if ( strcmp(p->name,name)==0 ) { + *lp=p->next; + if ( npc_src_last==p ) + npc_src_last=pp; + free(p); + break; + } + } +} + +/*========================================== + * warp行解析 + *------------------------------------------ + */ +int npc_parse_warp(char *w1,char *w2,char *w3,char *w4) +{ + int x,y,xs,ys,to_x,to_y,m; + int i,j; + char mapname[24],to_mapname[24]; + struct npc_data *nd; + + // 引数の個数チェック + if (sscanf(w1,"%[^,],%d,%d",mapname,&x,&y) != 3 || + sscanf(w4,"%d,%d,%[^,],%d,%d",&xs,&ys,to_mapname,&to_x,&to_y) != 5) { + printf("bad warp line : %s\n",w3); + return 1; + } + + m=map_mapname2mapid(mapname); + + nd=(struct npc_data *)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; + nd->dir=0; + nd->flag=0; + memcpy(nd->name,w3,24); + memcpy(nd->exname,w3,24); + + nd->chat_id=0; + if (!battle_config.warp_point_debug) + nd->class=WARP_CLASS; + else + nd->class=WARP_DEBUG_CLASS; + nd->speed=200; + nd->option = 0; + nd->opt1 = 0; + nd->opt2 = 0; + nd->opt3 = 0; + memcpy(nd->u.warp.name,to_mapname,16); + xs+=2; ys+=2; + nd->u.warp.x=to_x; + nd->u.warp.y=to_y; + nd->u.warp.xs=xs; + nd->u.warp.ys=ys; + + for(i=0;i<ys;i++) { + for(j=0;j<xs;j++) { + int t; + t=map_getcell(m,x-xs/2+j,y-ys/2+i); + if (t==1 || t==5) + continue; + map_setcell(m,x-xs/2+j,y-ys/2+i,t|0x80); + } + } + +// printf("warp npc %s %d read done\n",mapname,nd->bl.id); + npc_warp++; + nd->bl.type=BL_NPC; + nd->bl.subtype=WARP; + map_addblock(&nd->bl); + clif_spawnnpc(nd); + strdb_insert(npcname_db,nd->name,nd); + + return 0; +} + +/*========================================== + * shop行解析 + *------------------------------------------ + */ +static int npc_parse_shop(char *w1,char *w2,char *w3,char *w4) +{ + char *p; + int x, y, dir, m; + int max = 100, pos = 0; + char mapname[24]; + struct npc_data *nd; + + // 引数の個数チェック + if (sscanf(w1, "%[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || + strchr(w4, ',') == NULL) { + printf("bad shop line : %s\n", w3); + return 1; + } + m = map_mapname2mapid(mapname); + + nd = (struct npc_data *)aCalloc(1,sizeof(struct npc_data) + + sizeof(nd->u.shop_item[0]) * (max + 1)); + p = strchr(w4, ','); + + while (p && pos < max) { + int nameid,value; + p++; + if (sscanf(p, "%d:%d", &nameid, &value) != 2) + break; + nd->u.shop_item[pos].nameid = nameid; + if (value < 0) { + struct item_data *id = itemdb_search(nameid); + value = id->value_buy; + } + nd->u.shop_item[pos].value = value; + pos++; + p=strchr(p,','); + } + if (pos == 0) { + free(nd); + return 1; + } + nd->u.shop_item[pos++].nameid = 0; + + nd->bl.prev = nd->bl.next = NULL; + nd->bl.m = m; + nd->bl.x = x; + nd->bl.y = y; + nd->bl.id = npc_get_new_npc_id(); + nd->dir = dir; + nd->flag = 0; + memcpy(nd->name, w3, 24); + nd->class = atoi(w4); + nd->speed = 200; + nd->chat_id = 0; + nd->option = 0; + nd->opt1 = 0; + nd->opt2 = 0; + nd->opt3 = 0; + + nd = (struct npc_data *)aRealloc(nd, + sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos); + + //printf("shop npc %s %d read done\n",mapname,nd->bl.id); + npc_shop++; + nd->bl.type=BL_NPC; + nd->bl.subtype=SHOP; + nd->n=map_addnpc(m,nd); + map_addblock(&nd->bl); + clif_spawnnpc(nd); + strdb_insert(npcname_db,nd->name,nd); + + return 0; +} +/*========================================== + * NPCのラベルデータコンバート + *------------------------------------------ + */ +int npc_convertlabel_db(void *key,void *data,va_list ap) +{ + char *lname=(char *)key; + int pos=(int)data; + struct npc_data *nd; + struct npc_label_list *lst; + int num; + char *p=strchr(lname,':'); + + 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 *)aCalloc(1,sizeof(struct npc_label_list)); + num=0; + }else + lst=(struct npc_label_list *)aRealloc(lst,sizeof(struct npc_label_list)*(num+1)); + + *p='\0'; + strncpy(lst[num].name,lname,24); + *p=':'; + lst[num].pos=pos; + nd->u.scr.label_list=lst; + nd->u.scr.label_list_num=num+1; + return 0; +} +/*========================================== + * script行解析 + *------------------------------------------ + */ +static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines) +{ + int x,y,dir=0,m,xs=0,ys=0,class=0; // [Valaris] thanks to fov + char mapname[24]; + unsigned char *srcbuf=NULL,*script; + int srcsize=65536; + int startline=0; + unsigned char line[1024]; + int i; + struct npc_data *nd; + int evflag=0; + struct dbt *label_db; + char *p; + struct npc_label_list *label_dup=NULL; + int label_dupnum=0; + int src_id=0; + + if(strcmp(w1,"-")==0){ + x=0;y=0;m=-1; + }else{ + // 引数の個数チェック + if (sscanf(w1,"%[^,],%d,%d,%d",mapname,&x,&y,&dir) != 4 || + ( strcmp(w2,"script")==0 && strchr(w4,',')==NULL) ) { + printf("bad script line : %s\n",w3); + return 1; + } + m = map_mapname2mapid(mapname); + } + + if(strcmp(w2,"script")==0){ + // スクリプトの解析 + srcbuf=(char *)aCalloc(srcsize,sizeof(char)); + if (strchr(first_line,'{')) { + strcpy(srcbuf,strchr(first_line,'{')); + startline=*lines; + } else + srcbuf[0]=0; + while(1) { + for(i=strlen(srcbuf)-1;i>=0 && isspace(srcbuf[i]);i--); + if (i>=0 && srcbuf[i]=='}') + break; + fgets(line,1020,fp); + (*lines)++; + if (feof(fp)) + break; + if (strlen(srcbuf)+strlen(line)+1>=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); + } + script=parse_script(srcbuf,startline); + if (script==NULL) { + // script parse error? + free(srcbuf); + return 1; + } + + }else{ + // duplicateする + + char srcname[128]; + struct npc_data *nd2; + if( sscanf(w2,"duplicate(%[^)])",srcname)!=1 ){ + printf("bad duplicate name! : %s",w2); + return 0; + } + if( (nd2=npc_name2id(srcname))==NULL ){ + printf("bad duplicate name! (not exist) : %s\n",srcname); + return 0; + } + script=nd2->u.scr.script; + label_dup=nd2->u.scr.label_list; + label_dupnum=nd2->u.scr.label_list_num; + src_id=nd2->bl.id; + + }// end of スクリプト解析 + + nd=(struct npc_data *)aCalloc(1,sizeof(struct npc_data)); + + if(m==-1){ + // スクリプトコピー用のダミーNPC + + }else if( sscanf(w4,"%d,%d,%d",&class,&xs,&ys)==3) { + // 接触型NPC + int i,j; + + if (xs>=0)xs=xs*2+1; + if (ys>=0)ys=ys*2+1; + + if (class>=0) { + + for(i=0;i<ys;i++) { + for(j=0;j<xs;j++) { + int t; + t=map_getcell(m,x-xs/2+j,y-ys/2+i); + if (t==1 || t==5) + continue; + map_setcell(m,x-xs/2+j,y-ys/2+i,t|0x80); + } + } + } + + nd->u.scr.xs=xs; + nd->u.scr.ys=ys; + } else { // クリック型NPC + class=atoi(w4); + nd->u.scr.xs=0; + nd->u.scr.ys=0; + } + + if (class<0 && m>=0) { // イベント型NPC + evflag=1; + } + + while((p=strchr(w3,':'))) { + if (p[1]==':') break; + } + if (p) { + *p=0; + memcpy(nd->name,w3,24); + memcpy(nd->exname,p+2,24); + }else{ + memcpy(nd->name,w3,24); + memcpy(nd->exname,w3,24); + } + + nd->bl.prev = nd->bl.next = NULL; + nd->bl.m = m; + nd->bl.x = x; + nd->bl.y = y; + nd->bl.id=npc_get_new_npc_id(); + nd->dir = dir; + nd->flag=0; + nd->class=class; + nd->speed=200; + nd->u.scr.script=script; + nd->u.scr.src_id=src_id; + nd->chat_id=0; + nd->option = 0; + nd->opt1 = 0; + nd->opt2 = 0; + nd->opt3 = 0; + + //printf("script npc %s %d %d read done\n",mapname,nd->bl.id,nd->class); + npc_script++; + nd->bl.type=BL_NPC; + nd->bl.subtype=SCRIPT; + if(m>=0){ + nd->n=map_addnpc(m,nd); + map_addblock(&nd->bl); + + if (evflag) { // イベント型 + struct event_data *ev=(struct event_data *)aCalloc(1,sizeof(struct event_data)); + ev->nd=nd; + ev->pos=0; + strdb_insert(ev_db,nd->exname,ev); + }else + clif_spawnnpc(nd); + } + strdb_insert(npcname_db,nd->exname,nd); + + + //----------------------------------------- + // ラベルデータの準備 + if(srcbuf){ + // script本体がある場合の処理 + + // ラベルデータのコンバート + label_db=script_get_label_db(); + strdb_foreach(label_db,npc_convertlabel_db,nd); + + // もう使わないのでバッファ解放 + free(srcbuf); + + }else{ + // duplicate + +// nd->u.scr.label_list=malloc(sizeof(struct npc_label_list)*label_dupnum); +// memcpy(nd->u.scr.label_list,label_dup,sizeof(struct npc_label_list)*label_dupnum); + + nd->u.scr.label_list=label_dup; // ラベルデータ共有 + nd->u.scr.label_list_num=label_dupnum; + } + + //----------------------------------------- + // イベント用ラベルデータのエクスポート + for(i=0;i<nd->u.scr.label_list_num;i++){ + char *lname=nd->u.scr.label_list[i].name; + int pos=nd->u.scr.label_list[i].pos; + + if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) { + struct event_data *ev; + char *buf; + // エクスポートされる + ev=(struct event_data *)aCalloc(1,sizeof(struct event_data)); + buf=(char *)aCalloc(50,sizeof(char)); + if (strlen(lname)>24) { + printf("npc_parse_script: label name error !\n"); + exit(1); + }else{ + ev->nd=nd; + ev->pos=pos; + sprintf(buf,"%s::%s",nd->exname,lname); + strdb_insert(ev_db,buf,ev); + } + } + } + + //----------------------------------------- + // ラベルデータからタイマーイベント取り込み + for(i=0;i<nd->u.scr.label_list_num;i++){ + int t=0,k=0; + char *lname=nd->u.scr.label_list[i].name; + int pos=nd->u.scr.label_list[i].pos; + if(sscanf(lname,"OnTimer%d%n",&t,&k)==1 && lname[k]=='\0') { + // タイマーイベント + struct npc_timerevent_list *te=nd->u.scr.timer_event; + int j,k=nd->u.scr.timeramount; + if(te==NULL) + te=(struct npc_timerevent_list *)aCalloc(1,sizeof(struct npc_timerevent_list)); + else + te=(struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) ); + for(j=0;j<k;j++){ + if(te[j].timer>t){ + memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(k-j)); + break; + } + } + te[j].timer=t; + te[j].pos=pos; + nd->u.scr.timer_event=te; + nd->u.scr.timeramount=k+1; + } + } + nd->u.scr.nexttimer=-1; + nd->u.scr.timerid=-1; + + + return 0; +} + +/*========================================== + * function行解析 + *------------------------------------------ + */ +static int npc_parse_function(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines) +{ + char *srcbuf=NULL,*script; + int srcsize=65536; + int startline=0; + char line[1024]; + int i; +// struct dbt *label_db; + char *p; + + // スクリプトの解析 + srcbuf=(char *)aCalloc(srcsize,sizeof(char)); + if (strchr(first_line,'{')) { + strcpy(srcbuf,strchr(first_line,'{')); + startline=*lines; + } else + srcbuf[0]=0; + while(1) { + for(i=strlen(srcbuf)-1;i>=0 && isspace(srcbuf[i]);i--); + if (i>=0 && srcbuf[i]=='}') + break; + fgets(line,1020,fp); + (*lines)++; + if (feof(fp)) + break; + if (strlen(srcbuf)+strlen(line)+1>=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); + } + script=parse_script(srcbuf,startline); + if (script==NULL) { + // script parse error? + free(srcbuf); + return 1; + } + + p=(char *)aCalloc(50,sizeof(char)); + + strncpy(p,w3,50); + strdb_insert(script_get_userfunc_db(),p,script); + +// label_db=script_get_label_db(); + + // もう使わないのでバッファ解放 + free(srcbuf); + +// printf("function %s => %p\n",p,script); + + return 0; +} + + +/*========================================== + * mob行解析 + *------------------------------------------ + */ +int npc_parse_mob(char *w1,char *w2,char *w3,char *w4) +{ + int m,x,y,xs,ys,class,num,delay1,delay2; + int i; + char mapname[24]; + char eventname[24]=""; + struct mob_data *md; + + xs=ys=0; + delay1=delay2=0; + // 引数の個数チェック + if (sscanf(w1,"%[^,],%d,%d,%d,%d",mapname,&x,&y,&xs,&ys) < 3 || + sscanf(w4,"%d,%d,%d,%d,%s",&class,&num,&delay1,&delay2,eventname) < 2 ) { + printf("bad monster line : %s\n",w3); + return 1; + } + + m=map_mapname2mapid(mapname); + + if ( num>1 && battle_config.mob_count_rate!=100) { + if ( (num=num*battle_config.mob_count_rate/100)<1 ) + num=1; + } + + for(i=0;i<num;i++) { + md=(struct mob_data *)aCalloc(1,sizeof(struct mob_data)); + + md->bl.prev=NULL; + md->bl.next=NULL; + md->bl.m=m; + md->bl.x=x; + md->bl.y=y; + if(strcmp(w3,"--en--")==0) + memcpy(md->name,mob_db[class].name,24); + else if(strcmp(w3,"--ja--")==0) + memcpy(md->name,mob_db[class].jname,24); + else + memcpy(md->name,w3,24); + + md->n = i; + md->base_class = md->class = class; + md->bl.id=npc_get_new_npc_id(); + md->m =m; + md->x0=x; + md->y0=y; + md->xs=xs; + md->ys=ys; + md->spawndelay1=delay1; + md->spawndelay2=delay2; + + memset(&md->state,0,sizeof(md->state)); + md->timer = -1; + md->target_id=0; + md->attacked_id=0; + md->speed=mob_db[class].speed; + + if (mob_db[class].mode&0x02) + md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); + else + md->lootitem=NULL; + + if (strlen(eventname)>=4) { + memcpy(md->npc_event,eventname,24); + }else + memset(md->npc_event,0,24); + + md->bl.type=BL_MOB; + map_addiddb(&md->bl); + mob_spawn(md->bl.id); + + npc_mob++; + } + //printf("warp npc %s %d read done\n",mapname,nd->bl.id); + + return 0; +} + +/*========================================== + * マップフラグ行の解析 + *------------------------------------------ + */ +static int npc_parse_mapflag(char *w1,char *w2,char *w3,char *w4) +{ + int m; + char mapname[24],savemap[16]; + int savex,savey; + char drop_arg1[16],drop_arg2[16]; + int drop_id=0,drop_type=0,drop_per=0; + + // 引数の個数チェック +// if ( sscanf(w1,"%[^,],%d,%d,%d",mapname,&x,&y,&dir) != 4 ) + if ( sscanf(w1,"%[^,]",mapname) != 1 ) + return 1; + + m=map_mapname2mapid(mapname); + if (m<0) + return 1; + +//マップフラグ + if ( strcmpi(w3,"nosave")==0) { + if (strcmp(w4,"SavePoint")==0) { + memcpy(map[m].save.map,"SavePoint",16); + map[m].save.x=-1; + map[m].save.y=-1; + }else if (sscanf(w4,"%[^,],%d,%d",savemap,&savex,&savey)==3) { + memcpy(map[m].save.map,savemap,16); + map[m].save.x=savex; + map[m].save.y=savey; + } + map[m].flag.nosave=1; + } + else if (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) { + 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,"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,"fog")==0) { // fog [Valaris] + map[m].flag.fog=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; + } + + return 0; +} + +static int ev_db_final(void *key,void *data,va_list ap) +{ + free(data); + if(strstr(key,"::")!=NULL) + free(key); + return 0; +} +static int npcname_db_final(void *key,void *data,va_list ap) +{ + return 0; +} +/*========================================== + * 終了 + *------------------------------------------ + */ +int do_final_npc(void) +{ + int i; + struct block_list *bl; + struct npc_data *nd; + struct mob_data *md; + struct chat_data *cd; + struct pet_data *pd; + + if(ev_db) + strdb_final(ev_db,ev_db_final); + if(npcname_db) + strdb_final(npcname_db,npcname_db_final); + + for(i=START_NPC_NUM;i<npc_id;i++){ + if((bl=map_id2bl(i))){ + if(bl->type == BL_NPC && (nd = (struct npc_data *)bl)){ + if(nd->chat_id && (cd=(struct chat_data*)map_id2bl(nd->chat_id))){ + free(cd); + cd = NULL; + } + if(nd->bl.subtype == SCRIPT){ + if(nd->u.scr.timer_event) + free(nd->u.scr.timer_event); + if(nd->u.scr.src_id==0){ + if(nd->u.scr.script){ + free(nd->u.scr.script); + nd->u.scr.script=NULL; + } + if(nd->u.scr.label_list){ + free(nd->u.scr.label_list); + nd->u.scr.label_list = NULL; + } + } + } + free(nd); + nd = NULL; + }else if(bl->type == BL_MOB && (md = (struct mob_data *)bl)){ + if(md->lootitem){ + free(md->lootitem); + md->lootitem = NULL; + } + free(md); + md = NULL; + }else if(bl->type == BL_PET && (pd = (struct pet_data *)bl)){ + free(pd); + pd = NULL; + } + } + } + + return 0; +} + + +void ev_release(struct dbn *db, int which) +{ + if (which & 0x1) + free(db->key); + if (which & 0x2) + free(db->data); +} + +/*========================================== + * npc初期化 + *------------------------------------------ + */ +int do_init_npc(void) +{ + struct npc_src_list *nsl; + FILE *fp; + char line[1024]; + int m,lines; + + ev_db=strdb_init(24); + npcname_db=strdb_init(24); + + ev_db->release = ev_release; + + memset(&ev_tm_b,-1,sizeof(ev_tm_b)); + + for(nsl=npc_src_first;nsl;nsl=nsl->next) { + if(nsl->prev){ + free(nsl->prev); + nsl->prev = NULL; + } + fp=fopen(nsl->name,"r"); + if (fp==NULL) { + printf("file not found : %s\n",nsl->name); + exit(1); + } + lines=0; + while(fgets(line,1020,fp)) { + char w1[1024],w2[1024],w3[1024],w4[1024],mapname[1024]; + int i,j,w4pos,count; + lines++; + + if (line[0] == '/' && line[1] == '/') + continue; + // 不要なスペースやタブの連続は詰める + for(i=j=0;line[i];i++) { + if (line[i]==' ') { + if (!((line[i+1] && (isspace(line[i+1]) || line[i+1]==',')) || + (j && line[j-1]==','))) + line[j++]=' '; + } else if (line[i]=='\t') { + if (!(j && line[j-1]=='\t')) + line[j++]='\t'; + } else + line[j++]=line[i]; + } + // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認 + if ((count=sscanf(line,"%[^\t]\t%[^\t]\t%[^\t\r\n]\t%n%[^\t\r\n]",w1,w2,w3,&w4pos,w4)) < 3 && + (count=sscanf(line,"%s%s%s%n%s",w1,w2,w3,&w4pos,w4)) < 3) { + continue; + } + // マップの存在確認 + if( strcmp(w1,"-")!=0 && strcmpi(w1,"function")!=0 ){ + sscanf(w1,"%[^,]",mapname); + m = map_mapname2mapid(mapname); + if (strlen(mapname)>16 || m<0) { + // "mapname" is not assigned to this server + continue; + } + } + if (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); + } + } + fclose(fp); + printf("\rLoading NPCs [%d]: %-54s",npc_id-START_NPC_NUM,nsl->name); + fflush(stdout); + } + printf("\rNPCs Loaded: %d [Warps:%d Shops:%d Scripts:%d Mobs:%d]\n", + npc_id-START_NPC_NUM,npc_warp,npc_shop,npc_script,npc_mob); + + 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"); + + //exit(1); + + return 0; +} diff --git a/src/map/npc.h b/src/map/npc.h new file mode 100644 index 0000000..63d7765 --- /dev/null +++ b/src/map/npc.h @@ -0,0 +1,48 @@ +// $Id: npc.h,v 1.5 2004/09/25 11:39:17 MouseJstr Exp $ +#ifndef _NPC_H_ +#define _NPC_H_ + +#define START_NPC_NUM 110000000 + +#define WARP_CLASS 45 +#define WARP_DEBUG_CLASS 722 +#define INVISIBLE_CLASS 32767 + +int npc_event_dequeue(struct map_session_data *sd); +int npc_event_timer(int tid,unsigned int tick,int id,int data); +int npc_event(struct map_session_data *sd,const char *npcname,int); +int npc_timer_event(const char *eventname); // Added by RoVeRT +int npc_command(struct map_session_data *sd,char *npcname,char *command); +int npc_touch_areanpc(struct map_session_data *,int,int,int); +int npc_click(struct map_session_data *,int); +int npc_scriptcont(struct map_session_data *,int); +int npc_checknear(struct map_session_data *,int); +int npc_buysellsel(struct map_session_data *,int,int); +int npc_buylist(struct map_session_data *,int,unsigned short *); +int npc_selllist(struct map_session_data *,int,unsigned short *); +int npc_parse_mob(char *w1,char *w2,char *w3,char *w4); +int npc_parse_warp(char *w1,char *w2,char *w3,char *w4); + +int npc_enable(const char *name,int flag); +struct npc_data* npc_name2id(const char *name); + +int npc_get_new_npc_id(void); + +void npc_addsrcfile(char *); +void npc_delsrcfile(char *); +int do_final_npc(void); +int do_init_npc(void); +int npc_event_do_oninit(void); +int npc_do_ontimer(int,struct map_session_data *,int); + +int npc_event_doall(const char *name); +int npc_event_do(const char *name); + +int npc_timerevent_start(struct npc_data *nd); +int npc_timerevent_stop(struct npc_data *nd); +int npc_gettimerevent_tick(struct npc_data *nd); +int npc_settimerevent_tick(struct npc_data *nd,int newtimer); +int npc_delete(struct npc_data *nd); + +#endif + diff --git a/src/map/party.c b/src/map/party.c new file mode 100644 index 0000000..7d8cdaf --- /dev/null +++ b/src/map/party.c @@ -0,0 +1,644 @@ +// $Id: party.c,v 1.2 2004/09/22 02:59:47 Akitasha Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "party.h" +#include "db.h" +#include "timer.h" +#include "socket.h" +#include "nullpo.h" +#include "malloc.h" +#include "pc.h" +#include "map.h" +#include "battle.h" +#include "intif.h" +#include "clif.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define PARTY_SEND_XYHP_INVERVAL 1000 // 座標やHP送信の間隔 + +static struct dbt* party_db; + +int party_send_xyhp_timer(int tid,unsigned int tick,int id,int data); +/*========================================== + * 終了 + *------------------------------------------ + */ +static int party_db_final(void *key,void *data,va_list ap) +{ + free(data); + return 0; +} +void do_final_party(void) +{ + if(party_db) + numdb_final(party_db,party_db_final); +} +// 初期化 +void do_init_party(void) +{ + party_db=numdb_init(); + add_timer_func_list(party_send_xyhp_timer,"party_send_xyhp_timer"); + add_timer_interval(gettick()+PARTY_SEND_XYHP_INVERVAL,party_send_xyhp_timer,0,0,PARTY_SEND_XYHP_INVERVAL); +} + +// 検索 +struct party *party_search(int party_id) +{ + return numdb_search(party_db,party_id); +} +int party_searchname_sub(void *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(strcmpi(p->name,str)==0) + *dst=p; + return 0; +} +// パーティ名検索 +struct party* party_searchname(char *str) +{ + struct party *p=NULL; + numdb_foreach(party_db,party_searchname_sub,str,&p); + return p; +} +// 作成要求 +int party_create(struct map_session_data *sd,char *name) +{ + nullpo_retr(0, sd); + + if(sd->status.party_id==0) + intif_create_party(sd,name); + else + clif_party_created(sd,2); + return 0; +} + +// 作成可否 +int party_created(int account_id,int fail,int party_id,char *name) +{ + struct map_session_data *sd; + sd=map_id2sd(account_id); + + nullpo_retr(0, sd); + + if(fail==0){ + struct party *p; + sd->status.party_id=party_id; + if((p=numdb_search(party_db,party_id))!=NULL){ + printf("party: id already exists!\n"); + exit(1); + } + p=(struct party *)aCalloc(1,sizeof(struct party)); + p->party_id=party_id; + memcpy(p->name,name,24); + numdb_insert(party_db,party_id,p); + clif_party_created(sd,0); + }else{ + clif_party_created(sd,1); + } + return 0; +} + +// 情報要求 +int party_request_info(int party_id) +{ + return intif_request_partyinfo(party_id); +} + +// 所属キャラの確認 +int party_check_member(struct party *p) +{ + int i; + struct map_session_data *sd; + + nullpo_retr(0, p); + + for(i=0;i<fd_max;i++){ + if(session[i] && (sd=session[i]->session_data) && sd->state.auth){ + if(sd->status.party_id==p->party_id){ + int j,f=1; + for(j=0;j<MAX_PARTY;j++){ // パーティにデータがあるか確認 + if( p->member[j].account_id==sd->status.account_id){ + if( strcmp(p->member[j].name,sd->status.name)==0 ) + f=0; // データがある + else + p->member[j].sd=NULL; // 同垢別キャラだった + } + } + if(f){ + sd->status.party_id=0; + if(battle_config.error_log) + printf("party: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name); + } + } + } + } + return 0; +} + +// 情報所得失敗(そのIDのキャラを全部未所属にする) +int party_recv_noinfo(int party_id) +{ + int i; + struct map_session_data *sd; + for(i=0;i<fd_max;i++){ + if(session[i] && (sd=session[i]->session_data) && sd->state.auth){ + if(sd->status.party_id==party_id) + sd->status.party_id=0; + } + } + return 0; +} +// 情報所得 +int party_recv_info(struct party *sp) +{ + struct party *p; + int i; + + nullpo_retr(0, sp); + + if((p=numdb_search(party_db,sp->party_id))==NULL){ + p=(struct party *)aCalloc(1,sizeof(struct party)); + numdb_insert(party_db,sp->party_id,p); + + // 最初のロードなのでユーザーのチェックを行う + party_check_member(sp); + } + memcpy(p,sp,sizeof(struct party)); + + for(i=0;i<MAX_PARTY;i++){ // sdの設定 + struct map_session_data *sd = map_id2sd(p->member[i].account_id); + p->member[i].sd=(sd!=NULL && sd->status.party_id==p->party_id)?sd:NULL; + } + + clif_party_info(p,-1); + + for(i=0;i<MAX_PARTY;i++){ // 設定情報の送信 +// struct map_session_data *sd = map_id2sd(p->member[i].account_id); + struct map_session_data *sd = p->member[i].sd; + if(sd!=NULL && sd->party_sended==0){ + clif_party_option(p,sd,0x100); + sd->party_sended=1; + } + } + + return 0; +} + +// パーティへの勧誘 +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; + + nullpo_retr(0, sd); + + if(tsd==NULL || p==NULL) + return 0; + if(!battle_config.invite_request_check) { + if (tsd->guild_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか + clif_party_inviteack(sd,tsd->status.name,0); + return 0; + } + } + if( tsd->status.party_id>0 || tsd->party_invite>0 ){ // 相手の所属確認 + clif_party_inviteack(sd,tsd->status.name,0); + return 0; + } + for(i=0;i<MAX_PARTY;i++){ // 同アカウント確認 + if(p->member[i].account_id==account_id){ + clif_party_inviteack(sd,tsd->status.name,0); + return 0; + } + } + + tsd->party_invite=sd->status.party_id; + tsd->party_invite_account=sd->status.account_id; + + clif_party_invite(sd,tsd); + return 0; +} +// パーティ勧誘への返答 +int party_reply_invite(struct map_session_data *sd,int account_id,int flag) +{ + struct map_session_data *tsd= map_id2sd(account_id); + + nullpo_retr(0, sd); + + if(flag==1){ // 承諾 + //inter鯖へ追加要求 + intif_party_addmember( sd->party_invite, sd->status.account_id ); + return 0; + } + else { // 拒否 + sd->party_invite=0; + sd->party_invite_account=0; + if(tsd==NULL) + return 0; + clif_party_inviteack(tsd,sd->status.name,1); + } + return 0; +} +// パーティが追加された +int party_member_added(int party_id,int account_id,int flag) +{ + struct map_session_data *sd= map_id2sd(account_id),*sd2; + if(sd==NULL && flag==0){ + if(battle_config.error_log) + printf("party: member added error %d is not online\n",account_id); + intif_party_leave(party_id,account_id); // キャラ側に登録できなかったため脱退要求を出す + return 0; + } + sd2=map_id2sd(sd->party_invite_account); + sd->party_invite=0; + sd->party_invite_account=0; + + if(flag==1){ // 失敗 + if( sd2!=NULL ) + clif_party_inviteack(sd2,sd->status.name,0); + return 0; + } + + // 成功 + sd->party_sended=0; + sd->status.party_id=party_id; + + if( sd2!=NULL) + clif_party_inviteack(sd2,sd->status.name,2); + + // いちおう競合確認 + party_check_conflict(sd); + + return 0; +} +// パーティ除名要求 +int party_removemember(struct map_session_data *sd,int account_id,char *name) +{ + struct party *p; + int i; + + nullpo_retr(0, sd); + + if( (p = party_search(sd->status.party_id)) == NULL ) + return 0; + + for(i=0;i<MAX_PARTY;i++){ // リーダーかどうかチェック + if(p->member[i].account_id==sd->status.account_id) + if(p->member[i].leader==0) + return 0; + } + + for(i=0;i<MAX_PARTY;i++){ // 所属しているか調べる + if(p->member[i].account_id==account_id){ + intif_party_leave(p->party_id,account_id); + return 0; + } + } + return 0; +} + +// パーティ脱退要求 +int party_leave(struct map_session_data *sd) +{ + struct party *p; + int i; + + nullpo_retr(0, sd); + + if( (p = party_search(sd->status.party_id)) == NULL ) + return 0; + + for(i=0;i<MAX_PARTY;i++){ // 所属しているか + if(p->member[i].account_id==sd->status.account_id){ + intif_party_leave(p->party_id,sd->status.account_id); + return 0; + } + } + return 0; +} +// パーティメンバが脱退した +int party_member_leaved(int party_id,int account_id,char *name) +{ + struct map_session_data *sd=map_id2sd(account_id); + struct party *p=party_search(party_id); + if(p!=NULL){ + int i; + for(i=0;i<MAX_PARTY;i++) + if(p->member[i].account_id==account_id){ + clif_party_leaved(p,sd,account_id,name,0x00); + p->member[i].account_id=0; + p->member[i].sd=NULL; + } + } + if(sd!=NULL && sd->status.party_id==party_id){ + sd->status.party_id=0; + sd->party_sended=0; + } + return 0; +} +// パーティ解散通知 +int party_broken(int party_id) +{ + struct party *p; + int i; + if( (p=party_search(party_id))==NULL ) + return 0; + + for(i=0;i<MAX_PARTY;i++){ + if(p->member[i].sd!=NULL){ + clif_party_leaved(p,p->member[i].sd, + p->member[i].account_id,p->member[i].name,0x10); + p->member[i].sd->status.party_id=0; + p->member[i].sd->party_sended=0; + } + } + numdb_erase(party_db,party_id); + return 0; +} +// パーティの設定変更要求 +int party_changeoption(struct map_session_data *sd,int exp,int item) +{ + struct party *p; + + nullpo_retr(0, sd); + + if( sd->status.party_id==0 || (p=party_search(sd->status.party_id))==NULL ) + return 0; + intif_party_changeoption(sd->status.party_id,sd->status.account_id,exp,item); + return 0; +} +// パーティの設定変更通知 +int party_optionchanged(int party_id,int account_id,int exp,int item,int flag) +{ + struct party *p; + struct map_session_data *sd=map_id2sd(account_id); + if( (p=party_search(party_id))==NULL) + return 0; + + if(!(flag&0x01)) p->exp=exp; + if(!(flag&0x10)) p->item=item; + clif_party_option(p,sd,flag); + return 0; +} + +// パーティメンバの移動通知 +int party_recv_movemap(int party_id,int account_id,char *map,int online,int lv) +{ + struct party *p; + int i; + if( (p=party_search(party_id))==NULL) + return 0; + for(i=0;i<MAX_PARTY;i++){ + struct party_member *m=&p->member[i]; + if( m == NULL ){ + printf("party_recv_movemap nullpo?\n"); + return 0; + } + if(m->account_id==account_id){ + memcpy(m->map,map,16); + m->online=online; + m->lv=lv; + break; + } + } + if(i==MAX_PARTY){ + if(battle_config.error_log) + printf("party: not found member %d on %d[%s]",account_id,party_id,p->name); + return 0; + } + + for(i=0;i<MAX_PARTY;i++){ // sd再設定 + struct map_session_data *sd= map_id2sd(p->member[i].account_id); + p->member[i].sd=(sd!=NULL && sd->status.party_id==p->party_id)?sd:NULL; + } + + party_send_xy_clear(p); // 座標再通知要請 + + clif_party_info(p,-1); + return 0; +} + +// パーティメンバの移動 +int party_send_movemap(struct map_session_data *sd) +{ + struct party *p; + + nullpo_retr(0, sd); + + if( sd->status.party_id==0 ) + return 0; + intif_party_changemap(sd,1); + + if( sd->party_sended!=0 ) // もうパーティデータは送信済み + return 0; + + // 競合確認 + party_check_conflict(sd); + + // あるならパーティ情報送信 + if( (p=party_search(sd->status.party_id))!=NULL ){ + party_check_member(p); // 所属を確認する + if(sd->status.party_id==p->party_id){ + clif_party_info(p,sd->fd); + clif_party_option(p,sd,0x100); + sd->party_sended=1; + } + } + + return 0; +} +// パーティメンバのログアウト +int party_send_logout(struct map_session_data *sd) +{ + struct party *p; + + nullpo_retr(0, sd); + + if( sd->status.party_id>0 ) + intif_party_changemap(sd,0); + + // sdが無効になるのでパーティ情報から削除 + if( (p=party_search(sd->status.party_id))!=NULL ){ + int i; + for(i=0;i<MAX_PARTY;i++) + if(p->member[i].sd==sd) + p->member[i].sd=NULL; + } + + return 0; +} +// パーティメッセージ送信 +int party_send_message(struct map_session_data *sd,char *mes,int len) +{ + if(sd->status.party_id==0) + return 0; + intif_party_message(sd->status.party_id,sd->status.account_id,mes,len); + return 0; +} + +// パーティメッセージ受信 +int party_recv_message(int party_id,int account_id,char *mes,int len) +{ + struct party *p; + if( (p=party_search(party_id))==NULL) + return 0; + clif_party_message(p,account_id,mes,len); + return 0; +} +// パーティ競合確認 +int party_check_conflict(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + intif_party_checkconflict(sd->status.party_id,sd->status.account_id,sd->status.name); + return 0; +} + + +// 位置やHP通知用 +int party_send_xyhp_timer_sub(void *key,void *data,va_list ap) +{ + struct party *p=(struct party *)data; + int i; + + nullpo_retr(0, p); + + for(i=0;i<MAX_PARTY;i++){ + struct map_session_data *sd; + if((sd=p->member[i].sd)!=NULL){ + // 座標通知 + if(sd->party_x!=sd->bl.x || sd->party_y!=sd->bl.y){ + clif_party_xy(p,sd); + sd->party_x=sd->bl.x; + sd->party_y=sd->bl.y; + } + // HP通知 + if(sd->party_hp!=sd->status.hp){ + clif_party_hp(p,sd); + sd->party_hp=sd->status.hp; + } + + } + } + return 0; +} +// 位置やHP通知 +int party_send_xyhp_timer(int tid,unsigned int tick,int id,int data) +{ + numdb_foreach(party_db,party_send_xyhp_timer_sub,tick); + return 0; +} + +// 位置通知クリア +int party_send_xy_clear(struct party *p) +{ + int i; + + nullpo_retr(0, p); + + for(i=0;i<MAX_PARTY;i++){ + struct map_session_data *sd; + if((sd=p->member[i].sd)!=NULL){ + sd->party_x=-1; + sd->party_y=-1; + sd->party_hp=-1; + } + } + return 0; +} +// HP通知の必要性検査用(map_foreachinmoveareaから呼ばれる) +int party_send_hp_check(struct block_list *bl,va_list ap) +{ + int party_id; + int *flag; + struct map_session_data *sd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, sd=(struct map_session_data *)bl); + + party_id=va_arg(ap,int); + flag=va_arg(ap,int *); + + if(sd->status.party_id==party_id){ + *flag=1; + sd->party_hp=-1; + } + return 0; +} + +// 経験値公平分配 +int party_exp_share(struct party *p,int map,int base_exp,int job_exp) +{ + struct map_session_data *sd; + int i,c; + + nullpo_retr(0, p); + + for(i=c=0;i<MAX_PARTY;i++) + if((sd=p->member[i].sd)!=NULL && sd->bl.m==map) + c++; + if(c==0) + return 0; + for(i=0;i<MAX_PARTY;i++) + if((sd=p->member[i].sd)!=NULL && sd->bl.m==map) + pc_gainexp(sd,base_exp/c+1,job_exp/c+1); + return 0; +} + +// 同じマップのパーティメンバー全体に処理をかける +// type==0 同じマップ +// !=0 画面内 +void party_foreachsamemap(int (*func)(struct block_list*,va_list), + struct map_session_data *sd,int type,...) +{ + struct party *p; + va_list ap; + int i; + int x0,y0,x1,y1; + struct block_list *list[MAX_PARTY]; + int blockcount=0; + + nullpo_retv(sd); + + if((p=party_search(sd->status.party_id))==NULL) + return; + + x0=sd->bl.x-AREA_SIZE; + y0=sd->bl.y-AREA_SIZE; + x1=sd->bl.x+AREA_SIZE; + y1=sd->bl.y+AREA_SIZE; + + va_start(ap,type); + + for(i=0;i<MAX_PARTY;i++){ + struct party_member *m=&p->member[i]; + if(m->sd!=NULL){ + if(sd->bl.m!=m->sd->bl.m) + continue; + if(type!=0 && + (m->sd->bl.x<x0 || m->sd->bl.y<y0 || + m->sd->bl.x>x1 || m->sd->bl.y>y1 ) ) + continue; + list[blockcount++]=&m->sd->bl; + } + } + + map_freeblock_lock(); // メモリからの解放を禁止する + + for(i=0;i<blockcount;i++) + if(list[i]->prev) // 有効かどうかチェック + func(list[i],ap); + + map_freeblock_unlock(); // 解放を許可する + + va_end(ap); +} diff --git a/src/map/party.h b/src/map/party.h new file mode 100644 index 0000000..28d8096 --- /dev/null +++ b/src/map/party.h @@ -0,0 +1,47 @@ +// $Id: party.h,v 1.3 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _PARTY_H_ +#define _PARTY_H_ + +#include <stdarg.h> + +struct party; +struct map_session_data; +struct block_list; + +void do_init_party(void); +void do_final_party(void); +struct party *party_search(int party_id); +struct party* party_searchname(char *str); + +int party_create(struct map_session_data *sd,char *name); +int party_created(int account_id,int fail,int party_id,char *name); +int party_request_info(int party_id); +int party_invite(struct map_session_data *sd,int account_id); +int party_member_added(int party_id,int account_id,int flag); +int party_leave(struct map_session_data *sd); +int party_removemember(struct map_session_data *sd,int account_id,char *name); +int party_member_leaved(int party_id,int account_id,char *name); +int party_reply_invite(struct map_session_data *sd,int account_id,int flag); +int party_recv_noinfo(int party_id); +int party_recv_info(struct party *sp); +int party_recv_movemap(int party_id,int account_id,char *map,int online,int lv); +int party_broken(int party_id); +int party_optionchanged(int party_id,int account_id,int exp,int item,int flag); +int party_changeoption(struct map_session_data *sd,int exp,int item); + +int party_send_movemap(struct map_session_data *sd); +int party_send_logout(struct map_session_data *sd); + +int party_send_message(struct map_session_data *sd,char *mes,int len); +int party_recv_message(int party_id,int account_id,char *mes,int len); + +int party_check_conflict(struct map_session_data *sd); + +int party_send_xy_clear(struct party *p); +int party_send_hp_check(struct block_list *bl,va_list ap); + +int party_exp_share(struct party *p,int map,int base_exp,int job_exp); + +void party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...); + +#endif diff --git a/src/map/path.c b/src/map/path.c new file mode 100644 index 0000000..b2e0a78 --- /dev/null +++ b/src/map/path.c @@ -0,0 +1,404 @@ +// $Id: path.c,v 1.1.1.1 2004/09/10 17:27:00 MagicalTux Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "map.h" +#include "battle.h" +#include "nullpo.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +//#define PATH_STANDALONETEST + +#define MAX_HEAP 150 +struct tmp_path { short x,y,dist,before,cost; char dir,flag;}; +#define calc_index(x,y) (((x)+(y)*MAX_WALKPATH) & (MAX_WALKPATH*MAX_WALKPATH-1)) + +/*========================================== + * 経路探索補助heap push + *------------------------------------------ + */ +static void push_heap_path(int *heap,struct tmp_path *tp,int index) +{ + int i,h; + + if( heap == NULL || tp == NULL ){ + printf("push_heap_path nullpo\n"); + return; + } + + heap[0]++; + + for(h=heap[0]-1,i=(h-1)/2; + h>0 && tp[index].cost<tp[heap[i+1]].cost; + i=(h-1)/2) + heap[h+1]=heap[i+1],h=i; + heap[h+1]=index; +} + +/*========================================== + * 経路探索補助heap update + * costが減ったので根の方へ移動 + *------------------------------------------ + */ +static void update_heap_path(int *heap,struct tmp_path *tp,int index) +{ + int i,h; + + nullpo_retv(heap); + nullpo_retv(tp); + + for(h=0;h<heap[0];h++) + if(heap[h+1]==index) + break; + if(h==heap[0]){ + fprintf(stderr,"update_heap_path bug\n"); + exit(1); + } + for(i=(h-1)/2; + h>0 && tp[index].cost<tp[heap[i+1]].cost; + i=(h-1)/2) + heap[h+1]=heap[i+1],h=i; + heap[h+1]=index; +} + +/*========================================== + * 経路探索補助heap pop + *------------------------------------------ + */ +static int pop_heap_path(int *heap,struct tmp_path *tp) +{ + int i,h,k; + int ret,last; + + nullpo_retr(-1, heap); + nullpo_retr(-1, tp); + + if(heap[0]<=0) + return -1; + ret=heap[1]; + last=heap[heap[0]]; + heap[0]--; + + for(h=0,k=2;k<heap[0];k=k*2+2){ + if(tp[heap[k+1]].cost>tp[heap[k]].cost) + k--; + heap[h+1]=heap[k+1], h=k; + } + if(k==heap[0]) + heap[h+1]=heap[k], h=k-1; + + for(i=(h-1)/2; + h>0 && tp[heap[i+1]].cost>tp[last].cost; + i=(h-1)/2) + heap[h+1]=heap[i+1],h=i; + heap[h+1]=last; + + return ret; +} + +/*========================================== + * 現在の点のcost計算 + *------------------------------------------ + */ +static int calc_cost(struct tmp_path *p,int x1,int y1) +{ + int xd,yd; + + nullpo_retr(0, p); + + xd=x1-p->x; + if(xd<0) xd=-xd; + yd=y1-p->y; + if(yd<0) yd=-yd; + return (xd+yd)*10+p->dist; +} + +/*========================================== + * 必要ならpathを追加/修正する + *------------------------------------------ + */ +static int add_path(int *heap,struct tmp_path *tp,int x,int y,int dist,int dir,int before,int x1,int y1) +{ + int i; + + nullpo_retr(0, heap); + nullpo_retr(0, tp); + + i=calc_index(x,y); + + if(tp[i].x==x && tp[i].y==y){ + if(tp[i].dist>dist){ + tp[i].dist=dist; + tp[i].dir=dir; + tp[i].before=before; + tp[i].cost=calc_cost(&tp[i],x1,y1); + if(tp[i].flag) + push_heap_path(heap,tp,i); + else + update_heap_path(heap,tp,i); + tp[i].flag=0; + } + return 0; + } + + if(tp[i].x || tp[i].y) + return 1; + + tp[i].x=x; + tp[i].y=y; + tp[i].dist=dist; + tp[i].dir=dir; + tp[i].before=before; + tp[i].cost=calc_cost(&tp[i],x1,y1); + tp[i].flag=0; + push_heap_path(heap,tp,i); + + return 0; +} + + +/*========================================== + * (x,y)が移動不可能地帯かどうか + * flag 0x10000 遠距離攻撃判定 + *------------------------------------------ + */ +static int can_place(struct map_data *m,int x,int y,int flag) +{ + int c; + + nullpo_retr(0, m); + + c=read_gatp(m,x,y); + + if(c==1) + return 0; + if(!(flag&0x10000) && c==5) + return 0; + return 1; +} + +/*========================================== + * (x0,y0)から(x1,y1)へ1歩で移動可能か計算 + *------------------------------------------ + */ +static int can_move(struct map_data *m,int x0,int y0,int x1,int y1,int flag) +{ + nullpo_retr(0, m); + + if(x0-x1<-1 || x0-x1>1 || y0-y1<-1 || y0-y1>1) + return 0; + if(x1<0 || y1<0 || x1>=m->xs || y1>=m->ys) + return 0; + if(!can_place(m,x0,y0,flag)) + return 0; + if(!can_place(m,x1,y1,flag)) + return 0; + if(x0==x1 || y0==y1) + return 1; + if(!can_place(m,x0,y1,flag) || !can_place(m,x1,y0,flag)) + return 0; + return 1; +} +/*========================================== + * (x0,y0)から(dx,dy)方向へcountセル分 + * 吹き飛ばしたあとの座標を所得 + *------------------------------------------ + */ +int path_blownpos(int m,int x0,int y0,int dx,int dy,int count) +{ + struct map_data *md; + + if(!map[m].gat) + return -1; + md=&map[m]; + + if(count>15){ // 最大10マスに制限 + if(battle_config.error_log) + printf("path_blownpos: count too many %d !\n",count); + count=15; + } + if(dx>1 || dx<-1 || dy>1 || dy<-1){ + if(battle_config.error_log) + printf("path_blownpos: illeagal dx=%d or dy=%d !\n",dx,dy); + dx=(dx>=0)?1:((dx<0)?-1:0); + dy=(dy>=0)?1:((dy<0)?-1:0); + } + + while( (count--)>0 && (dx!=0 || dy!=0) ){ + if( !can_move(md,x0,y0,x0+dx,y0+dy,0) ){ + int fx=(dx!=0 && can_move(md,x0,y0,x0+dx,y0,0)); + int fy=(dy!=0 && can_move(md,x0,y0,x0,y0+dy,0)); + if( fx && fy ){ + if(rand()&1) dx=0; + else dy=0; + } + if( !fx ) dx=0; + if( !fy ) dy=0; + } + x0+=dx; + y0+=dy; + } + return (x0<<16)|y0; +} + +/*========================================== + * path探索 (x0,y0)->(x1,y1) + *------------------------------------------ + */ +int path_search(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int flag) +{ + int heap[MAX_HEAP+1]; + struct tmp_path tp[MAX_WALKPATH*MAX_WALKPATH]; + int i,rp,x,y; + struct map_data *md; + int dx,dy; + + nullpo_retr(0, wpd); + + if(!map[m].gat) + return -1; + md=&map[m]; + if(x1<0 || x1>=md->xs || y1<0 || y1>=md->ys || (i=read_gatp(md,x1,y1))==1 || i==5) + return -1; + + // easy + dx = (x1-x0<0) ? -1 : 1; + dy = (y1-y0<0) ? -1 : 1; + for(x=x0,y=y0,i=0;x!=x1 || y!=y1;){ + if(i>=sizeof(wpd->path)) + return -1; + if(x!=x1 && y!=y1){ + if(!can_move(md,x,y,x+dx,y+dy,flag)) + break; + x+=dx; + y+=dy; + wpd->path[i++]=(dx<0) ? ((dy>0)? 1 : 3) : ((dy<0)? 5 : 7); + } else if(x!=x1){ + if(!can_move(md,x,y,x+dx,y ,flag)) + break; + x+=dx; + wpd->path[i++]=(dx<0) ? 2 : 6; + } else { // y!=y1 + if(!can_move(md,x,y,x ,y+dy,flag)) + break; + y+=dy; + wpd->path[i++]=(dy>0) ? 0 : 4; + } + if(x==x1 && y==y1){ + wpd->path_len=i; + wpd->path_pos=0; + wpd->path_half=0; + return 0; + } + } + if(flag&1) + return -1; + + memset(tp,0,sizeof(tp)); + + i=calc_index(x0,y0); + tp[i].x=x0; + tp[i].y=y0; + tp[i].dist=0; + tp[i].dir=0; + tp[i].before=0; + tp[i].cost=calc_cost(&tp[i],x1,y1); + tp[i].flag=0; + heap[0]=0; + push_heap_path(heap,tp,calc_index(x0,y0)); + while(1){ + int e=0,fromdir; + + if(heap[0]==0) + return -1; + rp=pop_heap_path(heap,tp); + x=tp[rp].x; + y=tp[rp].y; + if(x==x1 && y==y1){ + int len,j; + + for(len=0,i=rp;len<100 && i!=calc_index(x0,y0);i=tp[i].before,len++); + if(len==100 || len>=sizeof(wpd->path)) + return -1; + wpd->path_len=len; + wpd->path_pos=0; + wpd->path_half=0; + for(i=rp,j=len-1;j>=0;i=tp[i].before,j--) + wpd->path[j]=tp[i].dir; + + return 0; + } + fromdir=tp[rp].dir; + if(can_move(md,x,y,x+1,y-1,flag)) + e+=add_path(heap,tp,x+1,y-1,tp[rp].dist+14,5,rp,x1,y1); + if(can_move(md,x,y,x+1,y ,flag)) + e+=add_path(heap,tp,x+1,y ,tp[rp].dist+10,6,rp,x1,y1); + if(can_move(md,x,y,x+1,y+1,flag)) + e+=add_path(heap,tp,x+1,y+1,tp[rp].dist+14,7,rp,x1,y1); + if(can_move(md,x,y,x ,y+1,flag)) + e+=add_path(heap,tp,x ,y+1,tp[rp].dist+10,0,rp,x1,y1); + if(can_move(md,x,y,x-1,y+1,flag)) + e+=add_path(heap,tp,x-1,y+1,tp[rp].dist+14,1,rp,x1,y1); + if(can_move(md,x,y,x-1,y ,flag)) + e+=add_path(heap,tp,x-1,y ,tp[rp].dist+10,2,rp,x1,y1); + if(can_move(md,x,y,x-1,y-1,flag)) + e+=add_path(heap,tp,x-1,y-1,tp[rp].dist+14,3,rp,x1,y1); + if(can_move(md,x,y,x ,y-1,flag)) + e+=add_path(heap,tp,x ,y-1,tp[rp].dist+10,4,rp,x1,y1); + tp[rp].flag=1; + if(e || heap[0]>=MAX_HEAP-5) + return -1; + } + return -1; +} + +#ifdef PATH_STANDALONETEST +char gat[64][64]={ + {0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,1,0,0,0,0,0}, +}; +struct map_data map[1]; + +/*========================================== + * 経路探索ルーチン単体テスト用main関数 + *------------------------------------------ + */ +void main(int argc,char *argv[]) +{ + struct walkpath_data wpd; + + map[0].gat=gat; + map[0].xs=64; + map[0].ys=64; + + path_search(&wpd,0,3,4,5,4); + path_search(&wpd,0,5,4,3,4); + path_search(&wpd,0,6,4,3,4); + path_search(&wpd,0,7,4,3,4); + path_search(&wpd,0,4,3,4,5); + path_search(&wpd,0,4,2,4,5); + path_search(&wpd,0,4,1,4,5); + path_search(&wpd,0,4,5,4,3); + path_search(&wpd,0,4,6,4,3); + path_search(&wpd,0,4,7,4,3); + path_search(&wpd,0,7,4,3,4); + path_search(&wpd,0,8,4,3,4); + path_search(&wpd,0,9,4,3,4); + path_search(&wpd,0,10,4,3,4); + path_search(&wpd,0,11,4,3,4); + path_search(&wpd,0,12,4,3,4); + path_search(&wpd,0,13,4,3,4); + path_search(&wpd,0,14,4,3,4); + path_search(&wpd,0,15,4,3,4); + path_search(&wpd,0,16,4,3,4); + path_search(&wpd,0,17,4,3,4); + path_search(&wpd,0,18,4,3,4); +} +#endif diff --git a/src/map/pc.c b/src/map/pc.c new file mode 100644 index 0000000..4e702c0 --- /dev/null +++ b/src/map/pc.c @@ -0,0 +1,7485 @@ +// $Id: pc.c 101 2004-09-25 17:57:22Z Valaris $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include "socket.h" // [Valaris] +#include "timer.h" +#include "db.h" + +#include "malloc.h" +#include "map.h" +#include "chrif.h" +#include "clif.h" +#include "intif.h" +#include "pc.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" + +#ifndef TXT_ONLY // mail system [Valaris] +#include "mail.h" +#endif + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define PVP_CALCRANK_INTERVAL 1000 // PVP順位計算の間隔 + +#define STATE_BLIND 0x10 + +static int max_weight_base[MAX_PC_CLASS]; +static int hp_coefficient[MAX_PC_CLASS]; +static int hp_coefficient2[MAX_PC_CLASS]; +static int hp_sigma_val[MAX_PC_CLASS][MAX_LEVEL]; +static int sp_coefficient[MAX_PC_CLASS]; +static int aspd_base[MAX_PC_CLASS][20]; +static char job_bonus[3][MAX_PC_CLASS][MAX_LEVEL]; +static int exp_table[14][MAX_LEVEL]; +static char statp[255][7]; +static struct { + int id; + int max; + struct { + short id,lv; + } need[6]; +} skill_tree[3][MAX_PC_CLASS][100]; + +static int atkmods[3][20]; // 武器ATKサイズ修正(size_fix.txt) +static int refinebonus[5][3]; // 精錬ボーナステーブル(refine_db.txt) +static int percentrefinery[5][10]; // 精錬成功率(refine_db.txt) + +static int dirx[8]={0,-1,-1,-1,0,1,1,1}; +static int diry[8]={1,1,0,-1,-1,-1,0,1}; + +static unsigned int equip_pos[11]={0x0080,0x0008,0x0040,0x0004,0x0001,0x0200,0x0100,0x0010,0x0020,0x0002,0x8000}; + +//static struct dbt *gm_account_db; +static struct gm_account *gm_account = NULL; +static int GM_num = 0; + +int pc_isGM(struct map_session_data *sd) { +// struct gm_account *p; + int i; + + nullpo_retr(0, sd); + +/* p = numdb_search(gm_account_db, sd->status.account_id); + if (p == NULL) + return 0; + return p->level;*/ + + for(i = 0; i < GM_num; i++) + if (gm_account[i].account_id == sd->status.account_id) + return gm_account[i].level; + return 0; + +} + +int pc_iskiller(struct map_session_data *src, struct map_session_data *target) { + nullpo_retr(0, src); + + if(src->bl.type!=BL_PC ) + return 0; + if (src->special_state.killer) + return 1; + + if(target->bl.type!=BL_PC ) + return 0; + if (target->special_state.killable) + return 1; + + return 0; +} + + +int pc_set_gm_level(int account_id, int level) { + int i; + for (i = 0; i < GM_num; i++) { + if (account_id == gm_account[i].account_id) { + gm_account[i].level = level; + return 0; + } + } + + GM_num++; + gm_account = realloc(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; +} + +int pc_getrefinebonus(int lv, int type) { + if (lv >= 0 && lv < 5 && type >= 0 && type < 3) + return refinebonus[lv][type]; + return 0; +} + +static int distance(int x0, int y0, int x1, int y1) { + int dx, dy; + + dx = abs(x0-x1); + dy = abs(y0-y1); + return dx>dy ? dx : dy; +} + +static 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) + printf("invincible_timer %d != %d\n",sd->invincible_timer,tid); + return 0; + } + sd->invincible_timer=-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; + } + return 0; +} + +static int pc_spiritball_timer(int tid,unsigned int tick,int id,int data) { + struct map_session_data *sd; + int i; + + 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) + printf("spirit_timer %d != %d\n",sd->spirit_timer[0],tid); + return 0; + } + sd->spirit_timer[0]=-1; + for(i=1;i<sd->spiritball;i++) { + sd->spirit_timer[i-1] = sd->spirit_timer[i]; + sd->spirit_timer[i] = -1; + } + sd->spiritball--; + if(sd->spiritball < 0) + sd->spiritball = 0; + clif_spiritball(sd); + + return 0; +} + +int pc_addspiritball(struct map_session_data *sd,int interval,int max) { + int i; + + nullpo_retr(0, sd); + + if(max > MAX_SKILL_LEVEL) + max = MAX_SKILL_LEVEL; + if(sd->spiritball < 0) + sd->spiritball = 0; + + if(sd->spiritball >= max) { + if(sd->spirit_timer[0] != -1) { + delete_timer(sd->spirit_timer[0],pc_spiritball_timer); + sd->spirit_timer[0] = -1; + } + for(i=1;i<max;i++) { + sd->spirit_timer[i-1] = sd->spirit_timer[i]; + sd->spirit_timer[i] = -1; + } + } + else + sd->spiritball++; + + sd->spirit_timer[sd->spiritball-1] = add_timer(gettick()+interval,pc_spiritball_timer,sd->bl.id,0); + clif_spiritball(sd); + + return 0; +} + +int pc_delspiritball(struct map_session_data *sd,int count,int type) { + int i; + + nullpo_retr(0, sd); + + if(sd->spiritball <= 0) { + sd->spiritball = 0; + return 0; + } + + if(count > sd->spiritball) + count = sd->spiritball; + sd->spiritball -= count; + if(count > MAX_SKILL_LEVEL) + count = MAX_SKILL_LEVEL; + + for(i=0;i<count;i++) { + if(sd->spirit_timer[i] != -1) { + delete_timer(sd->spirit_timer[i],pc_spiritball_timer); + sd->spirit_timer[i] = -1; + } + } + for(i=count;i<MAX_SKILL_LEVEL;i++) { + sd->spirit_timer[i-count] = sd->spirit_timer[i]; + sd->spirit_timer[i] = -1; + } + + if(!type) + clif_spiritball(sd); + + return 0; +} + +int pc_setrestartvalue(struct map_session_data *sd,int type) { + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr(0, sd); + + s_class = pc_calc_base_job(sd->status.class); + + //----------------------- + // 死亡した + if(sd->special_state.restart_full_recover) { // オシリスカード + sd->status.hp=sd->status.max_hp; + sd->status.sp=sd->status.max_sp; + } + else { + if(s_class.job == 0 && battle_config.restart_hp_rate < 50) { //ノビは半分回復 + sd->status.hp=(sd->status.max_hp)/2; + } + else { + if(battle_config.restart_hp_rate <= 0) + sd->status.hp = 1; + else { + sd->status.hp = sd->status.max_hp * battle_config.restart_hp_rate /100; + if(sd->status.hp <= 0) + sd->status.hp = 1; + } + } + if(battle_config.restart_sp_rate > 0) { + int sp = sd->status.max_sp * battle_config.restart_sp_rate /100; + if(sd->status.sp < sp) + sd->status.sp = sp; + } + } + if(type&1) + clif_updatestatus(sd,SP_HP); + if(type&1) + clif_updatestatus(sd,SP_SP); + + /* removed exp penalty on spawn [Valaris] */ + + if(type&2 && sd->status.class != 0 && battle_config.zeny_penalty > 0 && !map[sd->bl.m].flag.nozenypenalty) { + int zeny = (int)((double)sd->status.zeny * (double)battle_config.zeny_penalty / 10000.); + if(zeny < 1) zeny = 1; + sd->status.zeny -= zeny; + if(sd->status.zeny < 0) sd->status.zeny = 0; + clif_updatestatus(sd,SP_ZENY); + } + + return 0; +} + +/*========================================== + * 自分をロックしているMOBの数を数える(foreachclient) + *------------------------------------------ + */ +static int pc_counttargeted_sub(struct block_list *bl,va_list ap) +{ + int id,*c,target_lv; + struct block_list *src; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + id=va_arg(ap,int); + + nullpo_retr(0, c=va_arg(ap,int *)); + + src=va_arg(ap,struct block_list *); + target_lv=va_arg(ap,int); + if(id == bl->id || (src && id == src->id)) return 0; + if(bl->type == BL_PC) { + struct map_session_data *sd=(struct map_session_data *)bl; + if( sd && sd->attacktarget == id && sd->attacktimer != -1 && sd->attacktarget_lv >= target_lv) + (*c)++; + } + else if(bl->type == BL_MOB) { + struct mob_data *md = (struct mob_data *)bl; + if(md && md->target_id == id && md->timer != -1 && md->state.state == MS_ATTACK && md->target_lv >= target_lv) + + (*c)++; + //printf("md->target_lv:%d, target_lv:%d\n",((struct mob_data *)bl)->target_lv,target_lv); + } + return 0; +} + +int pc_counttargeted(struct map_session_data *sd,struct block_list *src,int target_lv) +{ + int c=0; + map_foreachinarea(pc_counttargeted_sub, sd->bl.m, + sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE, + sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,0,sd->bl.id,&c,src,target_lv); + return c; +} + +/*========================================== + * ローカルプロトタイプ宣言 (必要な物のみ) + *------------------------------------------ + */ +static int pc_walktoxy_sub(struct map_session_data *); + +/*========================================== + * saveに必要なステータス修正を行なう + *------------------------------------------ + */ +int pc_makesavestatus(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + // 服の色は色々弊害が多いので保存対象にはしない + if(!battle_config.save_clothcolor) + sd->status.clothes_color=0; + + // 死亡状態だったのでhpを1、位置をセーブ場所に変更 + if(pc_isdead(sd)){ + pc_setrestartvalue(sd,0); + memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point)); + } else { + memcpy(sd->status.last_point.map,sd->mapname,24); + sd->status.last_point.x = sd->bl.x; + sd->status.last_point.y = sd->bl.y; + } + + // セーブ禁止マップだったので指定位置に移動 + if(map[sd->bl.m].flag.nosave){ + struct map_data *m=&map[sd->bl.m]; + if(strcmp(m->save.map,"SavePoint")==0) + memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point)); + else + memcpy(&sd->status.last_point,&m->save,sizeof(sd->status.last_point)); + } + + //マナーポイントがプラスだった場合0に + if(battle_config.muting_players && sd->status.manner > 0) + sd->status.manner = 0; + return 0; +} + +/*========================================== + * 接続時の初期化 + *------------------------------------------ + */ +int pc_setnewpc(struct map_session_data *sd, int account_id, int char_id, int login_id1, int client_tick, int sex, int fd) { + nullpo_retr(0, sd); + + sd->bl.id = account_id; + sd->char_id = char_id; + sd->login_id1 = login_id1; + sd->login_id2 = 0; // at this point, we can not know the value :( + sd->client_tick = client_tick; + sd->sex = sex; + sd->state.auth = 0; + sd->bl.type = BL_PC; + sd->canact_tick = sd->canmove_tick = gettick(); + sd->canlog_tick = gettick(); + sd->state.waitingdisconnect = 0; + + return 0; +} + +int pc_equippoint(struct map_session_data *sd,int n) +{ + int ep = 0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr(0, sd); + + s_class = pc_calc_base_job(sd->status.class); + + 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 || s_class.job == 12)) + return 34; + } + } + return ep; +} + +int pc_setinventorydata(struct map_session_data *sd) +{ + int i,id; + + nullpo_retr(0, sd); + + for(i=0;i<MAX_INVENTORY;i++) { + id = sd->status.inventory[i].nameid; + sd->inventory_data[i] = itemdb_search(id); + } + return 0; +} + +int pc_calcweapontype(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if(sd->weapontype1 != 0 && sd->weapontype2 == 0) + sd->status.weapon = sd->weapontype1; + if(sd->weapontype1 == 0 && sd->weapontype2 != 0)// 左手武器 Only + sd->status.weapon = sd->weapontype2; + else if(sd->weapontype1 == 1 && sd->weapontype2 == 1)// 双短剣 + sd->status.weapon = 0x11; + else if(sd->weapontype1 == 2 && sd->weapontype2 == 2)// 双単手剣 + sd->status.weapon = 0x12; + else if(sd->weapontype1 == 6 && sd->weapontype2 == 6)// 双単手斧 + sd->status.weapon = 0x13; + else if( (sd->weapontype1 == 1 && sd->weapontype2 == 2) || + (sd->weapontype1 == 2 && sd->weapontype2 == 1) ) // 短剣 - 単手剣 + sd->status.weapon = 0x14; + else if( (sd->weapontype1 == 1 && sd->weapontype2 == 6) || + (sd->weapontype1 == 6 && sd->weapontype2 == 1) ) // 短剣 - 斧 + sd->status.weapon = 0x15; + else if( (sd->weapontype1 == 2 && sd->weapontype2 == 6) || + (sd->weapontype1 == 6 && sd->weapontype2 == 2) ) // 単手剣 - 斧 + sd->status.weapon = 0x16; + else + sd->status.weapon = sd->weapontype1; + + return 0; +} + +int pc_setequipindex(struct map_session_data *sd) +{ + int i,j; + + nullpo_retr(0, sd); + + for(i=0;i<11;i++) + sd->equip_index[i] = -1; + + for(i=0;i<MAX_INVENTORY;i++) { + if(sd->status.inventory[i].nameid <= 0) + continue; + if(sd->status.inventory[i].equip) { + for(j=0;j<11;j++) + if(sd->status.inventory[i].equip & equip_pos[j]) + sd->equip_index[j] = i; + if(sd->status.inventory[i].equip & 0x0002) { + if(sd->inventory_data[i]) + sd->weapontype1 = sd->inventory_data[i]->look; + else + sd->weapontype1 = 0; + } + if(sd->status.inventory[i].equip & 0x0020) { + if(sd->inventory_data[i]) { + if(sd->inventory_data[i]->type == 4) { + if(sd->status.inventory[i].equip == 0x0020) + sd->weapontype2 = sd->inventory_data[i]->look; + else + sd->weapontype2 = 0; + } + else + sd->weapontype2 = 0; + } + else + sd->weapontype2 = 0; + } + } + } + pc_calcweapontype(sd); + + return 0; +} + +int pc_isequip(struct map_session_data *sd,int n) +{ + struct item_data *item; + struct status_change *sc_data; + //転生や養子の場合の元の職業を算出する + + nullpo_retr(0, sd); + + item = sd->inventory_data[n]; + sc_data = battle_get_sc_data(&sd->bl); + //s_class = pc_calc_base_job(sd->status.class); + + if( battle_config.gm_allequip>0 && pc_isGM(sd)>=battle_config.gm_allequip ) + return 1; + + if(item == NULL) + return 0; + if(item->sex != 2 && sd->status.sex != item->sex) + return 0; + if(item->elv > 0 && sd->status.base_level < item->elv) + return 0; +// -- moonsoul (below statement substituted for commented out version further below +// as it allows all advanced classes to equip items their normal versions +// could equip) +// + if(((sd->status.class==13 || sd->status.class==4014) && ((1<<7)&item->class) == 0) || // have mounted classes use unmounted equipment [Valaris] + ((sd->status.class==21 || sd->status.class==4022) && ((1<<14)&item->class) == 0)) + return 0; + if(sd->status.class!=13 && sd->status.class!=4014 && sd->status.class!=21 && sd->status.class!=4022) + if((sd->status.class<=4000 && ((1<<sd->status.class)&item->class) == 0) || (sd->status.class>4000 && sd->status.class<4023 && ((1<<(sd->status.class-4001))&item->class) == 0) || + (sd->status.class>=4023 && ((1<<(sd->status.class-4023))&item->class) == 0)) + return 0; +// if(((1<<sd->status.class)&item->class) == 0) +// return 0; + if(map[sd->bl.m].flag.pvp && (item->flag.no_equip==1 || item->flag.no_equip==3)) + return 0; + if(map[sd->bl.m].flag.gvg && (item->flag.no_equip==2 || item->flag.no_equip==3)) + return 0; + if(item->equip & 0x0002 && sc_data && sc_data[SC_STRIPWEAPON].timer != -1) + return 0; + if(item->equip & 0x0020 && sc_data && sc_data[SC_STRIPSHIELD].timer != -1) + return 0; + if(item->equip & 0x0010 && sc_data && sc_data[SC_STRIPARMOR].timer != -1) + return 0; + if(item->equip & 0x0100 && sc_data && sc_data[SC_STRIPHELM].timer != -1) + return 0; + return 1; +} + +/*========================================== + * Weapon Breaking [Valaris] + *------------------------------------------ + */ +int pc_breakweapon(struct map_session_data *sd) +{ + struct item_data* item; + char output[255]; + int i; + + if(sd==NULL) + return -1; + if(sd->unbreakable>=rand()%100) + return 0; + if(sd->sc_data && sd->sc_data[SC_CP_WEAPON].timer != -1) + return 0; + + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].equip && sd->status.inventory[i].equip & 0x0002 && !sd->status.inventory[i].broken){ + item=sd->inventory_data[i]; + sd->status.inventory[i].broken=1; + //pc_unequipitem(sd,i,0); + if(sd->status.inventory[i].equip && sd->status.inventory[i].equip & 0x0002 && + sd->status.inventory[i].broken==1){ + sprintf(output, "%s has broken.",item->jname); + clif_emotion(&sd->bl,23); + clif_displaymessage(sd->fd, output); + clif_equiplist(sd); + skill_status_change_start(&sd->bl,SC_BROKNWEAPON,0,0,0,0,0,0); + } + } + if(sd->status.inventory[i].broken==1) + return 0; + } + + return 0; +} +/*========================================== + * Armor Breaking [Valaris] + *------------------------------------------ + */ +int pc_breakarmor(struct map_session_data *sd) +{ + struct item_data* item; + char output[255]; + int i; + + if(sd==NULL) + return -1; + if(sd->unbreakable>=rand()%100) + return 0; + if(sd->sc_data && sd->sc_data[SC_CP_ARMOR].timer != -1) + return 0; + + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].equip && sd->status.inventory[i].equip & 0x0010 && !sd->status.inventory[i].broken){ + item=sd->inventory_data[i]; + sd->status.inventory[i].broken=1; + //pc_unequipitem(sd,i,0); + if(sd->status.inventory[i].equip && sd->status.inventory[i].equip & 0x0010 && + sd->status.inventory[i].broken==1){ + sprintf(output, "%s has broken.",item->jname); + clif_emotion(&sd->bl,23); + clif_displaymessage(sd->fd, output); + clif_equiplist(sd); + skill_status_change_start(&sd->bl,SC_BROKNARMOR,0,0,0,0,0,0); + } + } + if(sd->status.inventory[i].broken==1) + return 0; + } + return 0; +} +/*========================================== + * session idに問題無し + * char鯖から送られてきたステータスを設定 + *------------------------------------------ + */ +int pc_authok(int id, int login_id2, time_t connect_until_time, struct mmo_charstatus *st) +{ + struct map_session_data *sd = NULL; + + struct party *p; + struct guild *g; + int i; + unsigned long tick = gettick(); + + sd = map_id2sd(id); + if(sd==NULL) + 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; + } + + memset(&sd->state, 0, sizeof(sd->state)); + // 基本的な初期化 + sd->state.connect_new = 1; + sd->bl.prev = sd->bl.next = NULL; + + sd->weapontype1 = sd->weapontype2 = 0; + sd->view_class = sd->status.class; + sd->speed = DEFAULT_WALK_SPEED; + sd->state.dead_sit = 0; + sd->dir = 0; + sd->head_dir = 0; + sd->state.auth = 1; + sd->walktimer = -1; + sd->attacktimer = -1; + sd->followtimer = -1; // [MouseJstr] + sd->skilltimer = -1; + sd->skillitem = -1; + sd->skillitemlv = -1; + sd->invincible_timer = -1; + sd->sg_count = 0; + + sd->deal_locked = 0; + sd->trade_partner = 0; + + sd->inchealhptick = 0; + sd->inchealsptick = 0; + sd->hp_sub = 0; + sd->sp_sub = 0; + sd->inchealspirithptick = 0; + sd->inchealspiritsptick = 0; + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->attackabletime = tick; + + sd->doridori_counter = 0; + +#ifndef TXT_ONLY // mail system [Valaris] + if(battle_config.mail_system) + sd->mail_counter = 0; +#endif + sd->spiritball = 0; + for(i = 0; i < MAX_SKILL_LEVEL; i++) + sd->spirit_timer[i] = -1; + for(i = 0; i < MAX_SKILLTIMERSKILL; i++) + sd->skilltimerskill[i].timer = -1; + + memset(&sd->dev,0,sizeof(struct square)); + for(i = 0; i < 5; i++) { + sd->dev.val1[i] = 0; + sd->dev.val2[i] = 0; + } + + // アカウント変数の送信要求 + intif_request_accountreg(sd); + + // アイテムチェック + pc_setinventorydata(sd); + pc_checkitem(sd); + + // pet + sd->petDB = NULL; + sd->pd = NULL; + sd->pet_hungry_timer = -1; + memset(&sd->pet, 0, sizeof(struct s_pet)); + + // ステータス異常の初期化 + for(i = 0; i < MAX_STATUSCHANGE; i++) { + sd->sc_data[i].timer=-1; + sd->sc_data[i].val1 = sd->sc_data[i].val2 = sd->sc_data[i].val3 = sd->sc_data[i].val4 = 0; + } + sd->sc_count=0; + if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && + (pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide))) + sd->status.option &= (OPTION_MASK | OPTION_HIDE); + else + sd->status.option &= OPTION_MASK; + + // スキルユニット関係の初期化 + memset(sd->skillunit, 0, sizeof(sd->skillunit)); + memset(sd->skillunittick, 0, sizeof(sd->skillunittick)); + + // init ignore list + memset(sd->ignore, 0, sizeof(sd->ignore)); + + // パーティー関係の初期化 + sd->party_sended = 0; + sd->party_invite = 0; + sd->party_x = -1; + sd->party_y = -1; + sd->party_hp = -1; + + // ギルド関係の初期化 + sd->guild_sended = 0; + sd->guild_invite = 0; + sd->guild_alliance = 0; + + // イベント関係の初期化 + memset(sd->eventqueue, 0, sizeof(sd->eventqueue)); + for(i = 0; i < MAX_EVENTTIMER; i++) + sd->eventtimer[i] = -1; + + // 位置の設定 + pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, 0); + + // pet + if (sd->status.pet_id > 0) + intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id); + + // パーティ、ギルドデータの要求 + if (sd->status.party_id > 0 && (p = party_search(sd->status.party_id)) == NULL) + party_request_info(sd->status.party_id); + if (sd->status.guild_id > 0 && (g = guild_search(sd->status.guild_id)) == NULL) + guild_request_info(sd->status.guild_id); + + // pvpの設定 + sd->pvp_rank = 0; + sd->pvp_point = 0; + sd->pvp_timer = -1; + + // 通知 + + clif_authok(sd); + map_addnickdb(sd); + if (map_charid2nick(sd->status.char_id) == NULL) + map_addchariddb(sd->status.char_id, sd->status.name); + + //スパノビ用死にカウンターのスクリプト変数からの読み出しとsdへのセット + sd->die_counter = pc_readglobalreg(sd,"PC_DIE_COUNTER"); + + if (night_flag == 1) { + char tmpstr[1024]; + strcpy(tmpstr, msg_txt(500)); // Actually, it's the night... + clif_wis_message(sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1); + sd->opt2 |= STATE_BLIND; + } + + // ステータス初期計算など + pc_calcstatus(sd,1); + + if (pc_isGM(sd)) + printf("Connection accepted: character '%s' (account: %d; GM level %d).\n", sd->status.name, sd->status.account_id, pc_isGM(sd)); + else + printf("Connection accepted: Character '%s' (account: %d).\n", sd->status.name, sd->status.account_id); + + // Message of the Dayの送信 + { + char buf[256]; + FILE *fp; + if ((fp = fopen(motd_txt, "r")) != NULL) { + while (fgets(buf, sizeof(buf)-1, fp) != NULL) { + int i; + for(i=0; buf[i]; i++) { + if (buf[i] == '\r' || buf[i]== '\n') { + buf[i] = 0; + break; + } + } + clif_displaymessage(sd->fd, buf); + } + fclose(fp); + } + } + +#ifndef TXT_ONLY + if(battle_config.mail_system) + 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; +} + +/*========================================== + * session idに問題ありなので後始末 + *------------------------------------------ + */ +int pc_authfail(int id) { + struct map_session_data *sd; + + sd = map_id2sd(id); + if (sd == NULL) + return 1; + + clif_authfail_fd(sd->fd, 0); + + return 0; +} + +static int pc_calc_skillpoint(struct map_session_data* sd) +{ + int i,skill,skill_point=0; + + nullpo_retr(0, sd); + + for(i=1;i<MAX_SKILL;i++){ + if( (skill = pc_checkskill(sd,i)) > 0) { + if(!(skill_get_inf2(i)&0x01) || battle_config.quest_skill_learn) { + if(!sd->status.skill[i].flag) + skill_point += skill; + else if(sd->status.skill[i].flag > 2 && sd->status.skill[i].flag != 13) { + skill_point += (sd->status.skill[i].flag - 2); + } + } + } + } + + return skill_point; +} + +/*========================================== + * 覚えられるスキルの計算 + *------------------------------------------ + */ +int pc_calc_skilltree(struct map_session_data *sd) +{ + int i,id=0,flag; + int c=0, s=0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr(0, sd); + + s_class = pc_calc_base_job(sd->status.class); + c = s_class.job; + s = (s_class.upper==1) ? 1 : 0 ; //転生以外は通常のスキル? + + if((battle_config.skillup_limit) && ((c >= 0 && c < 23) || (c >= 4001 && c < 4023) || (c >= 4023 && c < 4045))) { + int skill_point = pc_calc_skillpoint(sd); + if(skill_point < 9) + c = 0; + else if((sd->status.skill_point >= sd->status.job_level && skill_point < 58) && ((c > 6 && c < 23) || (c > 4007 && c < 4023) || (c > 4029 && c < 4045))) { + switch(c) { + case 7: + case 14: + c = 1; + break; + case 8: + case 15: + c = 4; + break; + case 9: + case 16: + c = 2; + break; + case 10: + case 18: + c = 5; + break; + case 11: + case 19: + case 20: + c = 3; + break; + case 12: + case 17: + c = 6; + break; + case 4008: + case 4015: + c = 4002; + break; + case 4009: + case 4016: + c = 4005; + break; + case 4010: + case 4017: + c = 4003; + break; + case 4011: + case 4019: + c = 4006; + break; + case 4012: + case 4020: + case 4021: + c = 4004; + break; + case 4013: + case 4018: + c = 4007; + break; + case 4030: + case 4037: + c = 4024; + break; + case 4031: + case 4038: + c = 4027; + break; + case 4032: + case 4039: + c = 4025; + break; + case 4033: + case 4040: + c = 4028; + break; + case 4034: + case 4041: + case 4042: + c = 4026; + break; + case 4035: + case 4043: + c = 4029; + break; + + } + } + } + + for(i=0;i<MAX_SKILL;i++){ + if (sd->status.skill[i].flag != 13) sd->status.skill[i].id=0; + if (sd->status.skill[i].flag && sd->status.skill[i].flag != 13){ // cardスキルなら、 + sd->status.skill[i].lv=(sd->status.skill[i].flag==1)?0:sd->status.skill[i].flag-2; // 本当のlvに + sd->status.skill[i].flag=0; // flagは0にしておく + } + } + + if (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill){ + // 全てのスキル + for(i=1;i<158;i++) + sd->status.skill[i].id=i; + for(i=210;i<291;i++) + sd->status.skill[i].id=i; + for(i=304;i<337;i++) + sd->status.skill[i].id=i; + if(battle_config.enable_upper_class){ //confで無効でなければ読み込む + for(i=355;i<MAX_SKILL;i++) + sd->status.skill[i].id=i; + } + }else{ + // 通常の計算 + do{ + flag=0; + for(i=0;(id=skill_tree[s][c][i].id)>0;i++){ + int j,f=1; + if(!battle_config.skillfree) { + for(j=0;j<5;j++) { + if( skill_tree[s][c][i].need[j].id && + pc_checkskill(sd,skill_tree[s][c][i].need[j].id) < skill_tree[s][c][i].need[j].lv) + f=0; + } + } + if(f && sd->status.skill[id].id==0 ){ + sd->status.skill[id].id=id; + flag=1; + } + } + }while(flag); + } +// if(battle_config.etc_log) +// printf("calc skill_tree\n"); + return 0; +} + +/*========================================== + * 重量アイコンの確認 + *------------------------------------------ + */ +int pc_checkweighticon(struct map_session_data *sd) +{ + int flag=0; + + nullpo_retr(0, sd); + + if(sd->weight*2 >= sd->max_weight) + flag=1; + if(sd->weight*10 >= sd->max_weight*9) + flag=2; + + if(flag==1){ + if(sd->sc_data[SC_WEIGHT50].timer==-1) + skill_status_change_start(&sd->bl,SC_WEIGHT50,0,0,0,0,0,0); + }else{ + skill_status_change_end(&sd->bl,SC_WEIGHT50,-1); + } + if(flag==2){ + if(sd->sc_data[SC_WEIGHT90].timer==-1) + skill_status_change_start(&sd->bl,SC_WEIGHT90,0,0,0,0,0,0); + }else{ + skill_status_change_end(&sd->bl,SC_WEIGHT90,-1); + } + return 0; +} + +/*========================================== + * パラメータ計算 + * first==0の時、計算対象のパラメータが呼び出し前から + * 変 化した場合自動でsendするが、 + * 能動的に変化させたパラメータは自前でsendするように + *------------------------------------------ + */ +int pc_calcstatus(struct map_session_data* sd,int first) +{ + int b_speed,b_max_hp,b_max_sp,b_hp,b_sp,b_weight,b_max_weight,b_paramb[6],b_parame[6],b_hit,b_flee; + int b_aspd,b_watk,b_def,b_watk2,b_def2,b_flee2,b_critical,b_attackrange,b_matk1,b_matk2,b_mdef,b_mdef2,b_class; + int b_base_atk; + struct skill b_skill[MAX_SKILL]; + int i,bl,index; + int skill,aspd_rate,wele,wele_,def_ele,refinedef=0; + int pele=0,pdef_ele=0; + int str,dstr,dex; + struct pc_base_job s_class; + + nullpo_retr(0, sd); + + //転生や養子の場合の元の職業を算出する + s_class = pc_calc_base_job(sd->status.class); + + b_speed = sd->speed; + b_max_hp = sd->status.max_hp; + b_max_sp = sd->status.max_sp; + b_hp = sd->status.hp; + b_sp = sd->status.sp; + b_weight = sd->weight; + b_max_weight = sd->max_weight; + memcpy(b_paramb,&sd->paramb,sizeof(b_paramb)); + memcpy(b_parame,&sd->paramc,sizeof(b_parame)); + memcpy(b_skill,&sd->status.skill,sizeof(b_skill)); + b_hit = sd->hit; + b_flee = sd->flee; + b_aspd = sd->aspd; + b_watk = sd->watk; + b_def = sd->def; + b_watk2 = sd->watk2; + b_def2 = sd->def2; + b_flee2 = sd->flee2; + b_critical = sd->critical; + b_attackrange = sd->attackrange; + b_matk1 = sd->matk1; + b_matk2 = sd->matk2; + b_mdef = sd->mdef; + b_mdef2 = sd->mdef2; + b_class = sd->view_class; + sd->view_class = sd->status.class; + b_base_atk = sd->base_atk; + + pc_calc_skilltree(sd); // スキルツリーの計算 + + sd->max_weight = max_weight_base[s_class.job]+sd->status.str*300; + + if(first&1) { + sd->weight=0; + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL) + continue; + sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount; + } + sd->cart_max_weight=battle_config.max_cart_weight; + sd->cart_weight=0; + sd->cart_max_num=MAX_CART; + sd->cart_num=0; + for(i=0;i<MAX_CART;i++){ + if(sd->status.cart[i].nameid==0) + continue; + sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount; + sd->cart_num++; + } + } + + memset(sd->paramb,0,sizeof(sd->paramb)); + memset(sd->parame,0,sizeof(sd->parame)); + sd->hit = 0; + sd->flee = 0; + sd->flee2 = 0; + sd->critical = 0; + sd->aspd = 0; + sd->watk = 0; + sd->def = 0; + sd->mdef = 0; + sd->watk2 = 0; + sd->def2 = 0; + sd->mdef2 = 0; + sd->status.max_hp = 0; + sd->status.max_sp = 0; + sd->attackrange = 0; + sd->attackrange_ = 0; + sd->atk_ele = 0; + sd->def_ele = 0; + sd->star =0; + sd->overrefine =0; + sd->matk1 =0; + sd->matk2 =0; + sd->speed = DEFAULT_WALK_SPEED ; + sd->hprate=100; + sd->sprate=100; + sd->castrate=100; + sd->dsprate=100; + sd->base_atk=0; + sd->arrow_atk=0; + sd->arrow_ele=0; + sd->arrow_hit=0; + sd->arrow_range=0; + sd->nhealhp=sd->nhealsp=sd->nshealhp=sd->nshealsp=sd->nsshealhp=sd->nsshealsp=0; + memset(sd->addele,0,sizeof(sd->addele)); + memset(sd->addrace,0,sizeof(sd->addrace)); + memset(sd->addsize,0,sizeof(sd->addsize)); + memset(sd->addele_,0,sizeof(sd->addele_)); + memset(sd->addrace_,0,sizeof(sd->addrace_)); + memset(sd->addsize_,0,sizeof(sd->addsize_)); + memset(sd->subele,0,sizeof(sd->subele)); + memset(sd->subrace,0,sizeof(sd->subrace)); + memset(sd->addeff,0,sizeof(sd->addeff)); + memset(sd->addeff2,0,sizeof(sd->addeff2)); + memset(sd->reseff,0,sizeof(sd->reseff)); + memset(&sd->special_state,0,sizeof(sd->special_state)); + memset(sd->weapon_coma_ele,0,sizeof(sd->weapon_coma_ele)); + memset(sd->weapon_coma_race,0,sizeof(sd->weapon_coma_race)); + + sd->watk_ = 0; //二刀流用(仮) + sd->watk_2 = 0; + sd->atk_ele_ = 0; + sd->star_ = 0; + sd->overrefine_ = 0; + + sd->aspd_rate = 100; + sd->speed_rate = 100; + sd->hprecov_rate = 100; + sd->sprecov_rate = 100; + sd->critical_def = 0; + sd->double_rate = 0; + sd->near_attack_def_rate = sd->long_attack_def_rate = 0; + sd->atk_rate = sd->matk_rate = 100; + sd->ignore_def_ele = sd->ignore_def_race = 0; + sd->ignore_def_ele_ = sd->ignore_def_race_ = 0; + sd->ignore_mdef_ele = sd->ignore_mdef_race = 0; + sd->arrow_cri = 0; + sd->magic_def_rate = sd->misc_def_rate = 0; + memset(sd->arrow_addele,0,sizeof(sd->arrow_addele)); + memset(sd->arrow_addrace,0,sizeof(sd->arrow_addrace)); + memset(sd->arrow_addsize,0,sizeof(sd->arrow_addsize)); + memset(sd->arrow_addeff,0,sizeof(sd->arrow_addeff)); + memset(sd->arrow_addeff2,0,sizeof(sd->arrow_addeff2)); + memset(sd->magic_addele,0,sizeof(sd->magic_addele)); + memset(sd->magic_addrace,0,sizeof(sd->magic_addrace)); + memset(sd->magic_subrace,0,sizeof(sd->magic_subrace)); + sd->perfect_hit = 0; + sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100; + sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100; + sd->def_ratio_atk_ele = sd->def_ratio_atk_ele_ = 0; + sd->def_ratio_atk_race = sd->def_ratio_atk_race_ = 0; + sd->get_zeny_num = 0; + sd->add_damage_class_count = sd->add_damage_class_count_ = sd->add_magic_damage_class_count = 0; + sd->add_def_class_count = sd->add_mdef_class_count = 0; + sd->monster_drop_item_count = 0; + memset(sd->add_damage_classrate,0,sizeof(sd->add_damage_classrate)); + memset(sd->add_damage_classrate_,0,sizeof(sd->add_damage_classrate_)); + memset(sd->add_magic_damage_classrate,0,sizeof(sd->add_magic_damage_classrate)); + memset(sd->add_def_classrate,0,sizeof(sd->add_def_classrate)); + memset(sd->add_mdef_classrate,0,sizeof(sd->add_mdef_classrate)); + memset(sd->monster_drop_race,0,sizeof(sd->monster_drop_race)); + memset(sd->monster_drop_itemrate,0,sizeof(sd->monster_drop_itemrate)); + sd->speed_add_rate = sd->aspd_add_rate = 100; + sd->double_add_rate = sd->perfect_hit_add = sd->get_zeny_add_num = 0; + sd->splash_range = sd->splash_add_range = 0; + sd->autospell_id = sd->autospell_lv = sd->autospell_rate = 0; + sd->hp_drain_rate = sd->hp_drain_per = sd->sp_drain_rate = sd->sp_drain_per = 0; + sd->hp_drain_rate_ = sd->hp_drain_per_ = sd->sp_drain_rate_ = sd->sp_drain_per_ = 0; + sd->short_weapon_damage_return = sd->long_weapon_damage_return = 0; + sd->magic_damage_return = 0; //AppleGirl Was Here + sd->random_attack_increase_add = sd->random_attack_increase_per = 0; + + if(!sd->disguiseflag && sd->disguise) { + sd->disguise=0; + clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield); + clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); + clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); + clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); + clif_clearchar(&sd->bl, 9); + pc_setpos(sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + } + + for(i=0;i<10;i++) { + index = sd->equip_index[i]; + if(index < 0) + continue; + if(i == 9 && sd->equip_index[8] == index) + continue; + if(i == 5 && sd->equip_index[4] == index) + continue; + if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) + continue; + + if(sd->inventory_data[index]) { + if(sd->inventory_data[index]->type == 4) { + if(sd->status.inventory[index].card[0]!=0x00ff && sd->status.inventory[index].card[0]!=0x00fe && sd->status.inventory[index].card[0]!=(short)0xff00) { + int j; + for(j=0;j<sd->inventory_data[index]->slot;j++){ // カード + int c=sd->status.inventory[index].card[j]; + if(c>0){ + 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; + } + } + } + } + else if(sd->inventory_data[index]->type==5){ // 防具 + if(sd->status.inventory[index].card[0]!=0x00ff && sd->status.inventory[index].card[0]!=0x00fe && sd->status.inventory[index].card[0]!=(short)0xff00) { + int j; + for(j=0;j<sd->inventory_data[index]->slot;j++){ // カード + int c=sd->status.inventory[index].card[j]; + if(c>0) + run_script(itemdb_equipscript(c),0,sd->bl.id,0); + } + } + } + } + } + wele = sd->atk_ele; + wele_ = sd->atk_ele_; + def_ele = sd->def_ele; + if(sd->status.pet_id > 0) { + struct pet_data *pd=sd->pd; + if((pd && battle_config.pet_status_support==1) && (battle_config.pet_equip_required==0 || (battle_config.pet_equip_required && pd->equip > 0))) { + if(sd->status.pet_id > 0 && sd->petDB && sd->pet.intimate > 0) + run_script(sd->petDB->script,0,sd->bl.id,0); + pele = sd->atk_ele; + pdef_ele = sd->def_ele; + sd->atk_ele = sd->def_ele = 0; + } + } + memcpy(sd->paramcard,sd->parame,sizeof(sd->paramcard)); + + // 装備品によるステータス変化はここで実行 + for(i=0;i<10;i++) { + index = sd->equip_index[i]; + if(index < 0) + continue; + if(i == 9 && sd->equip_index[8] == index) + continue; + if(i == 5 && sd->equip_index[4] == index) + continue; + if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) + continue; + if(sd->inventory_data[index]) { + sd->def += sd->inventory_data[index]->def; + if(sd->inventory_data[index]->type == 4) { + int r,wlv = sd->inventory_data[index]->wlv; + if(i == 8 && sd->status.inventory[index].equip == 0x20) { + //二刀流用データ入力 + sd->watk_ += sd->inventory_data[index]->atk; + sd->watk_2 = (r=sd->status.inventory[index].refine)* // 精錬攻撃力 + refinebonus[wlv][0]; + if( (r-=refinebonus[wlv][2])>0 ) // 過剰精錬ボーナス + sd->overrefine_ = r*refinebonus[wlv][1]; + + if(sd->status.inventory[index].card[0]==0x00ff){ // 製造武器 + sd->star_ = (sd->status.inventory[index].card[1]>>8); // 星のかけら + wele_= (sd->status.inventory[index].card[1]&0x0f); // 属 性 + } + sd->attackrange_ += sd->inventory_data[index]->range; + sd->state.lr_flag = 1; + run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0); + sd->state.lr_flag = 0; + } + else { //二刀流武器以外 + sd->watk += sd->inventory_data[index]->atk; + sd->watk2 += (r=sd->status.inventory[index].refine)* // 精錬攻撃力 + refinebonus[wlv][0]; + if( (r-=refinebonus[wlv][2])>0 ) // 過剰精錬ボーナス + sd->overrefine += r*refinebonus[wlv][1]; + + if(sd->status.inventory[index].card[0]==0x00ff){ // 製造武器 + sd->star += (sd->status.inventory[index].card[1]>>8); // 星のかけら + wele = (sd->status.inventory[index].card[1]&0x0f); // 属 性 + } + sd->attackrange += sd->inventory_data[index]->range; + run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0); + } + } + else if(sd->inventory_data[index]->type == 5) { + sd->watk += sd->inventory_data[index]->atk; + refinedef += sd->status.inventory[index].refine*refinebonus[0][0]; + run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0); + } + } + } + + if(sd->equip_index[10] >= 0){ // 矢 + index = sd->equip_index[10]; + if(sd->inventory_data[index]){ //まだ属性が入っていない + sd->state.lr_flag = 2; + run_script(sd->inventory_data[index]->equip_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->atk_ele = wele; + if(wele_ > 0) + sd->atk_ele_ = wele_; + if(def_ele > 0) + sd->def_ele = def_ele; + if(battle_config.pet_status_support) { + if(pele > 0 && !sd->atk_ele) + sd->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->get_zeny_num += sd->get_zeny_add_num; + sd->splash_range += sd->splash_add_range; + if(sd->speed_add_rate != 100) + sd->speed_rate += sd->speed_add_rate - 100; + if(sd->aspd_add_rate != 100) + sd->aspd_rate += sd->aspd_add_rate - 100; + + // 武器ATKサイズ補正 (右手) + sd->atkmods[0] = atkmods[0][sd->weapontype1]; + sd->atkmods[1] = atkmods[1][sd->weapontype1]; + sd->atkmods[2] = atkmods[2][sd->weapontype1]; + //武器ATKサイズ補正 (左手) + sd->atkmods_[0] = atkmods[0][sd->weapontype2]; + sd->atkmods_[1] = atkmods[1][sd->weapontype2]; + sd->atkmods_[2] = atkmods[2][sd->weapontype2]; + + // jobボーナス分 + for(i=0;i<sd->status.job_level && i<MAX_LEVEL;i++){ + if(job_bonus[s_class.upper][s_class.job][i]) + sd->paramb[job_bonus[s_class.upper][s_class.job][i]-1]++; + } + + if( (skill=pc_checkskill(sd,MC_INCCARRY))>0 ) // skill can be used with an item now, thanks to orn [Valaris] + sd->max_weight += skill*1000; + + if( (skill=pc_checkskill(sd,AC_OWL))>0 ) // ふくろうの目 + sd->paramb[4] += skill; + + // ステータス変化による基本パラメータ補正 + if(sd->sc_count){ + if(sd->sc_data[SC_CONCENTRATE].timer!=-1 && sd->sc_data[SC_QUAGMIRE].timer == -1){ // 集中力向上 + sd->paramb[1]+= (sd->status.agi+sd->paramb[1]+sd->parame[1]-sd->paramcard[1])*(2+sd->sc_data[SC_CONCENTRATE].val1)/100; + sd->paramb[4]+= (sd->status.dex+sd->paramb[4]+sd->parame[4]-sd->paramcard[4])*(2+sd->sc_data[SC_CONCENTRATE].val1)/100; + } + if(sd->sc_data[SC_INCREASEAGI].timer!=-1 && sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1){ // 速度増加 + sd->paramb[1]+= 2+sd->sc_data[SC_INCREASEAGI].val1; + sd->speed -= sd->speed *25/100; + } + if(sd->sc_data[SC_DECREASEAGI].timer!=-1) // 速度減少(agiはbattle.cで) + sd->speed = sd->speed *125/100; + if(sd->sc_data[SC_CLOAKING].timer!=-1) + sd->speed = (sd->speed*(76+(sd->sc_data[SC_INCREASEAGI].val1*3)))/100; + if(sd->sc_data[SC_BLESSING].timer!=-1){ // ブレッシング + sd->paramb[0]+= sd->sc_data[SC_BLESSING].val1; + sd->paramb[3]+= sd->sc_data[SC_BLESSING].val1; + sd->paramb[4]+= sd->sc_data[SC_BLESSING].val1; + } + if(sd->sc_data[SC_GLORIA].timer!=-1) // グロリア + sd->paramb[5]+= 30; + if(sd->sc_data[SC_LOUD].timer!=-1 && sd->sc_data[SC_QUAGMIRE].timer == -1) // ラウドボイス + sd->paramb[0]+= 4; + if(sd->sc_data[SC_QUAGMIRE].timer!=-1){ // クァグマイア + sd->speed = sd->speed*3/2; + sd->paramb[1]-=(sd->status.agi+sd->paramb[1]+sd->parame[1])/2; + sd->paramb[4]-=(sd->status.dex+sd->paramb[4]+sd->parame[4])/2; + } + if(sd->sc_data[SC_TRUESIGHT].timer!=-1){ // トゥルーサイト + sd->paramb[0]+= 5; + sd->paramb[1]+= 5; + sd->paramb[2]+= 5; + sd->paramb[3]+= 5; + sd->paramb[4]+= 5; + sd->paramb[5]+= 5; + } + } + + //1度も死んでないJob70スパノビに+10 + if(s_class.job == 23 && sd->die_counter == 0 && sd->status.job_level >= 70){ + sd->paramb[0]+= 15; + sd->paramb[1]+= 15; + sd->paramb[2]+= 15; + sd->paramb[3]+= 15; + sd->paramb[4]+= 15; + sd->paramb[5]+= 15; + } + sd->paramc[0]=sd->status.str+sd->paramb[0]+sd->parame[0]; + sd->paramc[1]=sd->status.agi+sd->paramb[1]+sd->parame[1]; + sd->paramc[2]=sd->status.vit+sd->paramb[2]+sd->parame[2]; + sd->paramc[3]=sd->status.int_+sd->paramb[3]+sd->parame[3]; + sd->paramc[4]=sd->status.dex+sd->paramb[4]+sd->parame[4]; + sd->paramc[5]=sd->status.luk+sd->paramb[5]+sd->parame[5]; + for(i=0;i<6;i++) + if(sd->paramc[i] < 0) sd->paramc[i] = 0; + + if(sd->status.weapon == 11 || sd->status.weapon == 13 || sd->status.weapon == 14) { + str = sd->paramc[4]; + dex = sd->paramc[0]; + } + else { + str = sd->paramc[0]; + dex = sd->paramc[4]; + } + dstr = str/10; + sd->base_atk += str + dstr*dstr + dex/5 + sd->paramc[5]/5; + sd->matk1 += sd->paramc[3]+(sd->paramc[3]/5)*(sd->paramc[3]/5); + sd->matk2 += sd->paramc[3]+(sd->paramc[3]/7)*(sd->paramc[3]/7); + if(sd->matk1 < sd->matk2) { + int temp = sd->matk2; + sd->matk2 = sd->matk1; + sd->matk1 = temp; + } + sd->hit += sd->paramc[4] + sd->status.base_level; + sd->flee += sd->paramc[1] + sd->status.base_level; + sd->def2 += sd->paramc[2]; + sd->mdef2 += sd->paramc[3]; + sd->flee2 += sd->paramc[5]+10; + sd->critical += (sd->paramc[5]*3)+10; + + if(sd->base_atk < 1) + sd->base_atk = 1; + if(sd->critical_rate != 100) + sd->critical = (sd->critical*sd->critical_rate)/100; + if(sd->critical < 10) sd->critical = 10; + if(sd->hit_rate != 100) + sd->hit = (sd->hit*sd->hit_rate)/100; + if(sd->hit < 1) sd->hit = 1; + if(sd->flee_rate != 100) + sd->flee = (sd->flee*sd->flee_rate)/100; + if(sd->flee < 1) sd->flee = 1; + if(sd->flee2_rate != 100) + sd->flee2 = (sd->flee2*sd->flee2_rate)/100; + if(sd->flee2 < 10) sd->flee2 = 10; + if(sd->def_rate != 100) + sd->def = (sd->def*sd->def_rate)/100; + if(sd->def < 0) sd->def = 0; + if(sd->def2_rate != 100) + sd->def2 = (sd->def2*sd->def2_rate)/100; + if(sd->def2 < 1) sd->def2 = 1; + if(sd->mdef_rate != 100) + sd->mdef = (sd->mdef*sd->mdef_rate)/100; + if(sd->mdef < 0) sd->mdef = 0; + if(sd->mdef2_rate != 100) + sd->mdef2 = (sd->mdef2*sd->mdef2_rate)/100; + if(sd->mdef2 < 1) sd->mdef2 = 1; + + // 二刀流 ASPD 修正 + if (sd->status.weapon <= 16) + sd->aspd += aspd_base[s_class.job][sd->status.weapon]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[s_class.job][sd->status.weapon]/1000; + else + sd->aspd += ( + (aspd_base[s_class.job][sd->weapontype1]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[s_class.job][sd->weapontype1]/1000) + + (aspd_base[s_class.job][sd->weapontype2]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[s_class.job][sd->weapontype2]/1000) + ) * 140 / 200; + + aspd_rate = sd->aspd_rate; + + //攻撃速度増加 + + if( (skill=pc_checkskill(sd,AC_VULTURE))>0){ // ワシの目 + sd->hit += skill; + if(sd->status.weapon == 11) + sd->attackrange += skill; + } + + if( (skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0) // 武器研究の命中率増加 + sd->hit += skill*2; + if(sd->status.option&2 && (skill = pc_checkskill(sd,RG_TUNNELDRIVE))>0 ) // トンネルドライブ // トンネルドライブ + sd->speed += (1.2*DEFAULT_WALK_SPEED - skill*9); + if (pc_iscarton(sd) && (skill=pc_checkskill(sd,MC_PUSHCART))>0) // カートによる速度低下 + sd->speed += (10-skill) * (DEFAULT_WALK_SPEED * 0.1); + else if (pc_isriding(sd)) // ペコペコ乗りによる速度増加 + sd->speed -= (0.25 * DEFAULT_WALK_SPEED); + sd->max_weight += 1000; + if(sd->sc_count){ + if(sd->sc_data[SC_WINDWALK].timer!=-1) //ウィンドウォーク時はLv*2%減算 + sd->speed -= sd->speed *(sd->sc_data[SC_WINDWALK].val1*2)/100; + if(sd->sc_data[SC_CARTBOOST].timer!=-1) // カートブースト + sd->speed -= (DEFAULT_WALK_SPEED * 20)/100; + if(sd->sc_data[SC_BERSERK].timer!=-1) //バーサーク中はIAと同じぐらい速い? + sd->speed -= sd->speed *25/100; + if(sd->sc_data[SC_WEDDING].timer!=-1) //結婚中は歩くのが遅い + sd->speed = 2*DEFAULT_WALK_SPEED; + } + + if((skill=pc_checkskill(sd,CR_TRUST))>0) { // フェイス + sd->status.max_hp += skill*200; + sd->subele[6] += skill*5; + } + if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) + sd->subele[3] += skill*4; + + bl=sd->status.base_level; + + sd->status.max_hp += (3500 + bl*hp_coefficient2[s_class.job] + hp_sigma_val[s_class.job][(bl > 0)? bl-1:0])/100 * (100 + sd->paramc[2])/100 + (sd->parame[2] - sd->paramcard[2]); + if (s_class.upper==1) // [MouseJstr] + sd->status.max_hp = sd->status.max_hp * 130/100; + if(sd->hprate!=100) + sd->status.max_hp = sd->status.max_hp*sd->hprate/100; + + if(sd->sc_data && sd->sc_data[SC_BERSERK].timer!=-1){ // バーサーク + sd->status.max_hp = sd->status.max_hp * 3; + sd->status.hp = sd->status.hp * 3; + if(sd->status.max_hp > battle_config.max_hp) // removed negative max hp bug by Valaris + sd->status.max_hp = battle_config.max_hp; + if(sd->status.hp > battle_config.max_hp) // removed negative max hp bug by Valaris + sd->status.hp = battle_config.max_hp; + } + if(s_class.job == 23 && sd->status.base_level >= 99){ + sd->status.max_hp = sd->status.max_hp + 2000; + } + + if(sd->status.max_hp > battle_config.max_hp) // removed negative max hp bug by Valaris + sd->status.max_hp = battle_config.max_hp; + if(sd->status.max_hp <= 0) sd->status.max_hp = 1; // end + + // 最大SP計算 + sd->status.max_sp += ((sp_coefficient[s_class.job] * bl) + 1000)/100 * (100 + sd->paramc[3])/100 + (sd->parame[3] - sd->paramcard[3]); + if (s_class.upper==1) // [MouseJstr] + sd->status.max_sp = sd->status.max_sp * 130/100; + if(sd->sprate!=100) + sd->status.max_sp = sd->status.max_sp*sd->sprate/100; + + if((skill=pc_checkskill(sd,HP_MEDITATIO))>0) // メディテイティオ + sd->status.max_sp += sd->status.max_sp*skill/100; + if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0) /* ソウルドレイン */ + sd->status.max_sp += sd->status.max_sp*2*skill/100; + + if(sd->status.max_sp < 0 || sd->status.max_sp > battle_config.max_sp) + sd->status.max_sp = battle_config.max_sp; + + //自然回復HP + sd->nhealhp = 1 + (sd->paramc[2]/5) + (sd->status.max_hp/200); + if((skill=pc_checkskill(sd,SM_RECOVERY)) > 0) { /* HP回復力向上 */ + sd->nshealhp = skill*5 + (sd->status.max_hp*skill/500); + if(sd->nshealhp > 0x7fff) sd->nshealhp = 0x7fff; + } + //自然回復SP + sd->nhealsp = 1 + (sd->paramc[3]/6) + (sd->status.max_sp/100); + if(sd->paramc[3] >= 120) + sd->nhealsp += ((sd->paramc[3]-120)>>1) + 4; + if((skill=pc_checkskill(sd,MG_SRECOVERY)) > 0) { /* SP回復力向上 */ + sd->nshealsp = skill*3 + (sd->status.max_sp*skill/500); + if(sd->nshealsp > 0x7fff) sd->nshealsp = 0x7fff; + } + + if((skill = pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0) { + sd->nsshealhp = skill*4 + (sd->status.max_hp*skill/500); + sd->nsshealsp = skill*2 + (sd->status.max_sp*skill/500); + if(sd->nsshealhp > 0x7fff) sd->nsshealhp = 0x7fff; + if(sd->nsshealsp > 0x7fff) sd->nsshealsp = 0x7fff; + } + if(sd->hprecov_rate != 100) { + sd->nhealhp = sd->nhealhp*sd->hprecov_rate/100; + if(sd->nhealhp < 1) sd->nhealhp = 1; + } + if(sd->sprecov_rate != 100) { + sd->nhealsp = sd->nhealsp*sd->sprecov_rate/100; + if(sd->nhealsp < 1) sd->nhealsp = 1; + } + if((skill=pc_checkskill(sd,HP_MEDITATIO)) > 0) { // メディテイティオはSPRではなく自然回復にかかる + sd->nhealsp += 3*skill*(sd->status.max_sp)/100; + if(sd->nhealsp > 0x7fff) sd->nhealsp = 0x7fff; + } + + // 種族耐性(これでいいの? ディバインプロテクションと同じ処理がいるかも) + if( (skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){ // ドラゴノロジー + skill = skill*4; + sd->addrace[9]+=skill; + sd->addrace_[9]+=skill; + sd->subrace[9]+=skill; + sd->magic_addrace[9]+=skill; + sd->magic_subrace[9]-=skill; + } + + //Flee上昇 + if( (skill=pc_checkskill(sd,TF_MISS))>0 ){ // 回避率増加 + if(sd->status.class==6||sd->status.class==4007 || sd->status.class==23){ + sd->flee += skill*3; + } + if(sd->status.class==12||sd->status.class==17||sd->status.class==4013||sd->status.class==4018) + sd->flee += skill*4; + if(sd->status.class==12||sd->status.class==4013) + sd->speed -= sd->speed *(skill*.5)/100; + } + if( (skill=pc_checkskill(sd,MO_DODGE))>0 ) // 見切り + sd->flee += (skill*3)>>1; + + // スキルやステータス異常による残りのパラメータ補正 + if(sd->sc_count){ + // ATK/DEF変化形 + if(sd->sc_data[SC_ANGELUS].timer!=-1) // エンジェラス + sd->def2 = sd->def2*(110+5*sd->sc_data[SC_ANGELUS].val1)/100; + if(sd->sc_data[SC_IMPOSITIO].timer!=-1) {// インポシティオマヌス + sd->watk += sd->sc_data[SC_IMPOSITIO].val1*5; + index = sd->equip_index[8]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == 4) + sd->watk_ += sd->sc_data[SC_IMPOSITIO].val1*5; + } + if(sd->sc_data[SC_PROVOKE].timer!=-1){ // プロボック + sd->def2 = sd->def2*(100-6*sd->sc_data[SC_PROVOKE].val1)/100; + sd->base_atk = sd->base_atk*(100+2*sd->sc_data[SC_PROVOKE].val1)/100; + sd->watk = sd->watk*(100+2*sd->sc_data[SC_PROVOKE].val1)/100; + index = sd->equip_index[8]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == 4) + sd->watk_ = sd->watk_*(100+2*sd->sc_data[SC_PROVOKE].val1)/100; + } + if(sd->sc_data[SC_ENDURE].timer!=-1) + sd->mdef2 += sd->sc_data[SC_ENDURE].val1; + if(sd->sc_data[SC_MINDBREAKER].timer!=-1){ // プロボック + sd->mdef2 = sd->mdef2*(100-6*sd->sc_data[SC_MINDBREAKER].val1)/100; + sd->matk1 = sd->matk1*(100+2*sd->sc_data[SC_MINDBREAKER].val1)/100; + sd->matk2 = sd->matk2*(100+2*sd->sc_data[SC_MINDBREAKER].val1)/100; + } + if(sd->sc_data[SC_POISON].timer!=-1) // 毒状態 + sd->def2 = sd->def2*75/100; + if(sd->sc_data[SC_DRUMBATTLE].timer!=-1){ // 戦太鼓の響き + sd->watk += sd->sc_data[SC_DRUMBATTLE].val2; + sd->def += sd->sc_data[SC_DRUMBATTLE].val3; + index = sd->equip_index[8]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == 4) + sd->watk_ += sd->sc_data[SC_DRUMBATTLE].val2; + } + if(sd->sc_data[SC_NIBELUNGEN].timer!=-1) { // ニーベルングの指輪 + index = sd->equip_index[9]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 3) + sd->watk += sd->sc_data[SC_NIBELUNGEN].val3; + index = sd->equip_index[8]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 3) + sd->watk_ += sd->sc_data[SC_NIBELUNGEN].val3; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4) + sd->watk += sd->sc_data[SC_NIBELUNGEN].val2; + index = sd->equip_index[8]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4) + sd->watk_ += sd->sc_data[SC_NIBELUNGEN].val2; + } + + if(sd->sc_data[SC_VOLCANO].timer!=-1 && sd->def_ele==3){ // ボルケーノ + sd->watk += sd->sc_data[SC_VIOLENTGALE].val3; + } + + if(sd->sc_data[SC_SIGNUMCRUCIS].timer!=-1) + sd->def = sd->def * (100 - sd->sc_data[SC_SIGNUMCRUCIS].val2)/100; + if(sd->sc_data[SC_ETERNALCHAOS].timer!=-1) // エターナルカオス + sd->def=0; + + if(sd->sc_data[SC_CONCENTRATION].timer!=-1){ //コンセントレーション + sd->watk = sd->watk * (100 + 5*sd->sc_data[SC_CONCENTRATION].val1)/100; + index = sd->equip_index[8]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == 4) + sd->watk_ = sd->watk * (100 + 5*sd->sc_data[SC_CONCENTRATION].val1)/100; + sd->def = sd->def * (100 - 5*sd->sc_data[SC_CONCENTRATION].val1)/100; + } + + if(sd->sc_data[SC_MAGICPOWER].timer!=-1){ //魔法力増幅 + sd->matk1 = sd->matk1*(100+2*sd->sc_data[SC_MAGICPOWER].val1)/100; + sd->matk2 = sd->matk2*(100+2*sd->sc_data[SC_MAGICPOWER].val1)/100; + } + if(sd->sc_data[SC_ATKPOT].timer!=-1) + sd->watk += sd->sc_data[SC_ATKPOT].val1; + if(sd->sc_data[SC_MATKPOT].timer!=-1){ + sd->matk1 += sd->sc_data[SC_MATKPOT].val1; + sd->matk2 += sd->sc_data[SC_MATKPOT].val1; + } + + // ASPD/移動速度変化系 + if(sd->sc_data[SC_TWOHANDQUICKEN].timer != -1 && sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1) // 2HQ + aspd_rate -= 30; + if(sd->sc_data[SC_ADRENALINE].timer != -1 && sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 && + sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1) { // アドレナリンラッシュ + if(sd->sc_data[SC_ADRENALINE].val2 || !battle_config.party_skill_penaly) + aspd_rate -= 30; + else + aspd_rate -= 25; + } + if(sd->sc_data[SC_SPEARSQUICKEN].timer != -1 && sd->sc_data[SC_ADRENALINE].timer == -1 && + sd->sc_data[SC_TWOHANDQUICKEN].timer == -1 && sd->sc_data[SC_QUAGMIRE].timer == -1 && sd->sc_data[SC_DONTFORGETME].timer == -1) // スピアクィッケン + aspd_rate -= sd->sc_data[SC_SPEARSQUICKEN].val2; + if(sd->sc_data[SC_ASSNCROS].timer!=-1 && // 夕陽のアサシンクロス + sd->sc_data[SC_TWOHANDQUICKEN].timer==-1 && sd->sc_data[SC_ADRENALINE].timer==-1 && sd->sc_data[SC_SPEARSQUICKEN].timer==-1 && + sd->sc_data[SC_DONTFORGETME].timer == -1) + aspd_rate -= 5+sd->sc_data[SC_ASSNCROS].val1+sd->sc_data[SC_ASSNCROS].val2+sd->sc_data[SC_ASSNCROS].val3; + if(sd->sc_data[SC_DONTFORGETME].timer!=-1){ // 私を忘れないで + aspd_rate += sd->sc_data[SC_DONTFORGETME].val1*3 + sd->sc_data[SC_DONTFORGETME].val2 + (sd->sc_data[SC_DONTFORGETME].val3>>16); + sd->speed= sd->speed*(100+sd->sc_data[SC_DONTFORGETME].val1*2 + sd->sc_data[SC_DONTFORGETME].val2 + (sd->sc_data[SC_DONTFORGETME].val3&0xffff))/100; + } + if( sd->sc_data[i=SC_SPEEDPOTION2].timer!=-1 || + sd->sc_data[i=SC_SPEEDPOTION1].timer!=-1 || + sd->sc_data[i=SC_SPEEDPOTION0].timer!=-1) // 増 速ポーション + aspd_rate -= sd->sc_data[i].val2; + + // HIT/FLEE変化系 + if(sd->sc_data[SC_WHISTLE].timer!=-1){ // 口笛 + sd->flee += sd->flee * (sd->sc_data[SC_WHISTLE].val1 + +sd->sc_data[SC_WHISTLE].val2+(sd->sc_data[SC_WHISTLE].val3>>16))/100; + sd->flee2+= (sd->sc_data[SC_WHISTLE].val1+sd->sc_data[SC_WHISTLE].val2+(sd->sc_data[SC_WHISTLE].val3&0xffff)) * 10; + } + if(sd->sc_data[SC_HUMMING].timer!=-1) // ハミング + sd->hit += (sd->sc_data[SC_HUMMING].val1*2+sd->sc_data[SC_HUMMING].val2 + +sd->sc_data[SC_HUMMING].val3) * sd->hit/100; + if(sd->sc_data[SC_VIOLENTGALE].timer!=-1 && sd->def_ele==4){ // バイオレントゲイル + sd->flee += sd->flee*sd->sc_data[SC_VIOLENTGALE].val3/100; + } + if(sd->sc_data[SC_BLIND].timer!=-1){ // 暗黒 + sd->hit -= sd->hit*25/100; + sd->flee -= sd->flee*25/100; + } + if(sd->sc_data[SC_WINDWALK].timer!=-1) // ウィンドウォーク + sd->flee += sd->flee*(sd->sc_data[SC_WINDWALK].val2)/100; + if(sd->sc_data[SC_SPIDERWEB].timer!=-1) //スパイダーウェブ + sd->flee -= sd->flee*50/100; + if(sd->sc_data[SC_TRUESIGHT].timer!=-1) //トゥルーサイト + sd->hit += 3*(sd->sc_data[SC_TRUESIGHT].val1); + if(sd->sc_data[SC_CONCENTRATION].timer!=-1) //コンセントレーション + sd->hit += (10*(sd->sc_data[SC_CONCENTRATION].val1)); + + // 耐性 + if(sd->sc_data[SC_SIEGFRIED].timer!=-1){ // 不死身のジークフリード + sd->subele[1] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + sd->subele[2] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + sd->subele[3] += sd->sc_data[SC_SIEGFRIED].val2; // 火 + sd->subele[4] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + sd->subele[5] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + sd->subele[6] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + sd->subele[7] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + sd->subele[8] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + sd->subele[9] += sd->sc_data[SC_SIEGFRIED].val2; // 水 + } + if(sd->sc_data[SC_PROVIDENCE].timer!=-1){ // プロヴィデンス + sd->subele[6] += sd->sc_data[SC_PROVIDENCE].val2; // 対 聖属性 + sd->subrace[6] += sd->sc_data[SC_PROVIDENCE].val2; // 対 悪魔 + } + + // その他 + if(sd->sc_data[SC_APPLEIDUN].timer!=-1){ // イドゥンの林檎 + sd->status.max_hp += ((5+sd->sc_data[SC_APPLEIDUN].val1*2+((sd->sc_data[SC_APPLEIDUN].val2+1)>>1) + +sd->sc_data[SC_APPLEIDUN].val3/10) * sd->status.max_hp)/100; + if(sd->status.max_hp < 0 || sd->status.max_hp > battle_config.max_hp) + sd->status.max_hp = battle_config.max_hp; + } + if(sd->sc_data[SC_DELUGE].timer!=-1 && sd->def_ele==1){ // デリュージ + sd->status.max_hp += sd->status.max_hp*sd->sc_data[SC_DELUGE].val3/100; + if(sd->status.max_hp < 0 || sd->status.max_hp > battle_config.max_hp) + sd->status.max_hp = battle_config.max_hp; + } + if(sd->sc_data[SC_SERVICE4U].timer!=-1) { // サービスフォーユー + sd->status.max_sp += sd->status.max_sp*(10+sd->sc_data[SC_SERVICE4U].val1+sd->sc_data[SC_SERVICE4U].val2 + +sd->sc_data[SC_SERVICE4U].val3)/100; + if(sd->status.max_sp < 0 || sd->status.max_sp > battle_config.max_sp) + sd->status.max_sp = battle_config.max_sp; + sd->dsprate-=(10+sd->sc_data[SC_SERVICE4U].val1*3+sd->sc_data[SC_SERVICE4U].val2 + +sd->sc_data[SC_SERVICE4U].val3); + if(sd->dsprate<0)sd->dsprate=0; + } + + if(sd->sc_data[SC_FORTUNE].timer!=-1) // 幸運のキス + sd->critical += (10+sd->sc_data[SC_FORTUNE].val1+sd->sc_data[SC_FORTUNE].val2 + +sd->sc_data[SC_FORTUNE].val3)*10; + + if(sd->sc_data[SC_EXPLOSIONSPIRITS].timer!=-1){ // 爆裂波動 + if(s_class.job==23) + sd->critical += sd->sc_data[SC_EXPLOSIONSPIRITS].val1*100; + else + sd->critical += sd->sc_data[SC_EXPLOSIONSPIRITS].val2; + } + + if(sd->sc_data[SC_STEELBODY].timer!=-1){ // 金剛 + sd->def = 90; + sd->mdef = 90; + aspd_rate += 25; + sd->speed = (sd->speed * 125) / 100; + } + if(sd->sc_data[SC_DEFENDER].timer != -1) { + sd->aspd += (550 - sd->sc_data[SC_DEFENDER].val1*50); + sd->speed = (sd->speed * (155 - sd->sc_data[SC_DEFENDER].val1*5)) / 100; + } + if(sd->sc_data[SC_ENCPOISON].timer != -1) + sd->addeff[4] += sd->sc_data[SC_ENCPOISON].val2; + + if( sd->sc_data[SC_DANCING].timer!=-1 ){ // 演奏/ダンス使用中 + sd->speed*=4; + sd->nhealsp = 0; + sd->nshealsp = 0; + sd->nsshealsp = 0; + } + if(sd->sc_data[SC_CURSE].timer!=-1) + sd->speed += 450; + + if(sd->sc_data[SC_TRUESIGHT].timer!=-1) //トゥルーサイト + sd->critical += sd->critical*(sd->sc_data[SC_TRUESIGHT].val1)/100; + +/* if(sd->sc_data[SC_VOLCANO].timer!=-1) // エンチャントポイズン(属性はbattle.cで) + sd->addeff[2]+=sd->sc_data[SC_VOLCANO].val2;//% of granting + if(sd->sc_data[SC_DELUGE].timer!=-1) // エンチャントポイズン(属性はbattle.cで) + sd->addeff[0]+=sd->sc_data[SC_DELUGE].val2;//% of granting + */ + } + + if(sd->speed_rate != 100) + sd->speed = sd->speed*sd->speed_rate/100; + if(sd->speed < 1) sd->speed = 1; + if(aspd_rate != 100) + sd->aspd = sd->aspd*aspd_rate/100; + if(pc_isriding(sd)) // 騎兵修練 + sd->aspd = sd->aspd*(100 + 10*(5 - pc_checkskill(sd,KN_CAVALIERMASTERY)))/ 100; + if(sd->aspd < battle_config.max_aspd) sd->aspd = battle_config.max_aspd; + sd->amotion = sd->aspd; + sd->dmotion = 800-sd->paramc[1]*4; + if(sd->dmotion<400) + sd->dmotion = 400; + if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0) { + sd->prev_speed = sd->speed; + sd->speed = sd->speed*(175 - skill*5)/100; + } + + if(sd->status.hp>sd->status.max_hp) + sd->status.hp=sd->status.max_hp; + if(sd->status.sp>sd->status.max_sp) + sd->status.sp=sd->status.max_sp; + + if(first&4) + return 0; + if(first&3) { + clif_updatestatus(sd,SP_SPEED); + clif_updatestatus(sd,SP_MAXHP); + clif_updatestatus(sd,SP_MAXSP); + if(first&1) { + clif_updatestatus(sd,SP_HP); + clif_updatestatus(sd,SP_SP); + } + return 0; + } + + if(b_class != sd->view_class) { + clif_changelook(&sd->bl,LOOK_BASE,sd->view_class); +#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( memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill)) || b_attackrange != sd->attackrange) + clif_skillinfoblock(sd); // スキル送信 + + if(b_speed != sd->speed) + clif_updatestatus(sd,SP_SPEED); + if(b_weight != sd->weight) + clif_updatestatus(sd,SP_WEIGHT); + if(b_max_weight != sd->max_weight) { + clif_updatestatus(sd,SP_MAXWEIGHT); + pc_checkweighticon(sd); + } + for(i=0;i<6;i++) + if(b_paramb[i] + b_parame[i] != sd->paramb[i] + sd->parame[i]) + clif_updatestatus(sd,SP_STR+i); + if(b_hit != sd->hit) + clif_updatestatus(sd,SP_HIT); + if(b_flee != sd->flee) + clif_updatestatus(sd,SP_FLEE1); + if(b_aspd != sd->aspd) + clif_updatestatus(sd,SP_ASPD); + if(b_watk != sd->watk || b_base_atk != sd->base_atk) + clif_updatestatus(sd,SP_ATK1); + if(b_def != sd->def) + clif_updatestatus(sd,SP_DEF1); + if(b_watk2 != sd->watk2) + clif_updatestatus(sd,SP_ATK2); + if(b_def2 != sd->def2) + clif_updatestatus(sd,SP_DEF2); + if(b_flee2 != sd->flee2) + clif_updatestatus(sd,SP_FLEE2); + if(b_critical != sd->critical) + clif_updatestatus(sd,SP_CRITICAL); + if(b_matk1 != sd->matk1) + clif_updatestatus(sd,SP_MATK1); + if(b_matk2 != sd->matk2) + clif_updatestatus(sd,SP_MATK2); + if(b_mdef != sd->mdef) + clif_updatestatus(sd,SP_MDEF1); + if(b_mdef2 != sd->mdef2) + clif_updatestatus(sd,SP_MDEF2); + if(b_attackrange != sd->attackrange) + clif_updatestatus(sd,SP_ATTACKRANGE); + if(b_max_hp != sd->status.max_hp) + clif_updatestatus(sd,SP_MAXHP); + if(b_max_sp != sd->status.max_sp) + clif_updatestatus(sd,SP_MAXSP); + if(b_hp != sd->status.hp) + clif_updatestatus(sd,SP_HP); + if(b_sp != sd->status.sp) + clif_updatestatus(sd,SP_SP); + +/* if(before.cart_num != before.cart_num || before.cart_max_num != before.cart_max_num || + before.cart_weight != before.cart_weight || before.cart_max_weight != before.cart_max_weight ) + clif_updatestatus(sd,SP_CARTINFO);*/ + + if(sd->status.hp<sd->status.max_hp>>2 && pc_checkskill(sd,SM_AUTOBERSERK)>0 && + (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0 ) && !pc_isdead(sd)) + // オートバーサーク発動 + skill_status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0); + + return 0; +} + +/*========================================== + * 装 備品による能力等のボーナス設定 + *------------------------------------------ + */ +int pc_bonus(struct map_session_data *sd,int type,int val) +{ + nullpo_retr(0, sd); + + switch(type){ + case SP_STR: + case SP_AGI: + case SP_VIT: + case SP_INT: + case SP_DEX: + case SP_LUK: + if(sd->state.lr_flag != 2) + sd->parame[type-SP_STR]+=val; + break; + case SP_ATK1: + if(!sd->state.lr_flag) + sd->watk+=val; + else if(sd->state.lr_flag == 1) + sd->watk_+=val; + break; + case SP_ATK2: + if(!sd->state.lr_flag) + sd->watk2+=val; + else if(sd->state.lr_flag == 1) + sd->watk_2+=val; + break; + case SP_BASE_ATK: + if(sd->state.lr_flag != 2) + sd->base_atk+=val; + break; + case SP_MATK1: + if(sd->state.lr_flag != 2) + sd->matk1 += val; + break; + case SP_MATK2: + if(sd->state.lr_flag != 2) + sd->matk2 += val; + break; + case SP_MATK: + if(sd->state.lr_flag != 2) { + sd->matk1 += val; + sd->matk2 += val; + } + break; + case SP_DEF1: + if(sd->state.lr_flag != 2) + sd->def+=val; + break; + case SP_MDEF1: + if(sd->state.lr_flag != 2) + sd->mdef+=val; + break; + case SP_MDEF2: + if(sd->state.lr_flag != 2) + sd->mdef+=val; + break; + case SP_HIT: + if(sd->state.lr_flag != 2) + sd->hit+=val; + else + sd->arrow_hit+=val; + break; + case SP_FLEE1: + if(sd->state.lr_flag != 2) + sd->flee+=val; + break; + case SP_FLEE2: + if(sd->state.lr_flag != 2) + sd->flee2+=val*10; + break; + case SP_CRITICAL: + if(sd->state.lr_flag != 2) + sd->critical+=val*10; + else + sd->arrow_cri += val*10; + break; + case SP_ATKELE: + if(!sd->state.lr_flag) + sd->atk_ele=val; + else if(sd->state.lr_flag == 1) + sd->atk_ele_=val; + else if(sd->state.lr_flag == 2) + sd->arrow_ele=val; + break; + case SP_DEFELE: + if(sd->state.lr_flag != 2) + sd->def_ele=val; + break; + case SP_MAXHP: + if(sd->state.lr_flag != 2) + sd->status.max_hp+=val; + break; + case SP_MAXSP: + if(sd->state.lr_flag != 2) + sd->status.max_sp+=val; + break; + case SP_CASTRATE: + if(sd->state.lr_flag != 2) + sd->castrate+=val; + break; + case SP_MAXHPRATE: + if(sd->state.lr_flag != 2) + sd->hprate+=val; + break; + case SP_MAXSPRATE: + if(sd->state.lr_flag != 2) + sd->sprate+=val; + break; + case SP_SPRATE: + if(sd->state.lr_flag != 2) + sd->dsprate+=val; + break; + case SP_ATTACKRANGE: + if(!sd->state.lr_flag) + sd->attackrange += val; + else if(sd->state.lr_flag == 1) + sd->attackrange_ += val; + else if(sd->state.lr_flag == 2) + sd->arrow_range += val; + break; + case SP_ADD_SPEED: + if(sd->state.lr_flag != 2) + sd->speed -= val; + break; + case SP_SPEED_RATE: + if(sd->state.lr_flag != 2) { + if(sd->speed_rate > 100-val) + sd->speed_rate = 100-val; + } + break; + case SP_SPEED_ADDRATE: + if(sd->state.lr_flag != 2) + sd->speed_add_rate = sd->speed_add_rate * (100-val)/100; + break; + case SP_ASPD: + if(sd->state.lr_flag != 2) + sd->aspd -= val*10; + break; + case SP_ASPD_RATE: + if(sd->state.lr_flag != 2) { + if(sd->aspd_rate > 100-val) + sd->aspd_rate = 100-val; + } + break; + case SP_ASPD_ADDRATE: + if(sd->state.lr_flag != 2) + sd->aspd_add_rate = sd->aspd_add_rate * (100-val)/100; + break; + case SP_HP_RECOV_RATE: + if(sd->state.lr_flag != 2) + sd->hprecov_rate += val; + break; + case SP_SP_RECOV_RATE: + if(sd->state.lr_flag != 2) + sd->sprecov_rate += val; + break; + case SP_CRITICAL_DEF: + if(sd->state.lr_flag != 2) + sd->critical_def += val; + break; + case SP_NEAR_ATK_DEF: + if(sd->state.lr_flag != 2) + sd->near_attack_def_rate += val; + break; + case SP_LONG_ATK_DEF: + if(sd->state.lr_flag != 2) + sd->long_attack_def_rate += val; + break; + case SP_DOUBLE_RATE: + if(sd->state.lr_flag == 0 && sd->double_rate < val) + sd->double_rate = val; + break; + case SP_DOUBLE_ADD_RATE: + if(sd->state.lr_flag == 0) + sd->double_add_rate += val; + break; + case SP_MATK_RATE: + if(sd->state.lr_flag != 2) + sd->matk_rate += val; + break; + case SP_IGNORE_DEF_ELE: + if(!sd->state.lr_flag) + sd->ignore_def_ele |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->ignore_def_ele_ |= 1<<val; + break; + case SP_IGNORE_DEF_RACE: + if(!sd->state.lr_flag) + sd->ignore_def_race |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->ignore_def_race_ |= 1<<val; + break; + case SP_ATK_RATE: + if(sd->state.lr_flag != 2) + sd->atk_rate += val; + break; + case SP_MAGIC_ATK_DEF: + if(sd->state.lr_flag != 2) + sd->magic_def_rate += val; + break; + case SP_MISC_ATK_DEF: + if(sd->state.lr_flag != 2) + sd->misc_def_rate += val; + break; + case SP_IGNORE_MDEF_ELE: + if(sd->state.lr_flag != 2) + sd->ignore_mdef_ele |= 1<<val; + break; + case SP_IGNORE_MDEF_RACE: + if(sd->state.lr_flag != 2) + sd->ignore_mdef_race |= 1<<val; + break; + case SP_PERFECT_HIT_RATE: + if(sd->state.lr_flag != 2 && sd->perfect_hit < val) + sd->perfect_hit = val; + break; + case SP_PERFECT_HIT_ADD_RATE: + if(sd->state.lr_flag != 2) + sd->perfect_hit_add += val; + break; + case SP_CRITICAL_RATE: + if(sd->state.lr_flag != 2) + sd->critical_rate+=val; + break; + case SP_GET_ZENY_NUM: + if(sd->state.lr_flag != 2 && sd->get_zeny_num < val) + sd->get_zeny_num = val; + break; + case SP_ADD_GET_ZENY_NUM: + if(sd->state.lr_flag != 2) + sd->get_zeny_add_num += val; + break; + case SP_DEF_RATIO_ATK_ELE: + if(!sd->state.lr_flag) + sd->def_ratio_atk_ele |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->def_ratio_atk_ele_ |= 1<<val; + break; + case SP_DEF_RATIO_ATK_RACE: + if(!sd->state.lr_flag) + sd->def_ratio_atk_race |= 1<<val; + else if(sd->state.lr_flag == 1) + sd->def_ratio_atk_race_ |= 1<<val; + break; + case SP_HIT_RATE: + if(sd->state.lr_flag != 2) + sd->hit_rate += val; + break; + case SP_FLEE_RATE: + if(sd->state.lr_flag != 2) + sd->flee_rate += val; + break; + case SP_FLEE2_RATE: + if(sd->state.lr_flag != 2) + sd->flee2_rate += val; + break; + case SP_DEF_RATE: + if(sd->state.lr_flag != 2) + sd->def_rate += val; + break; + case SP_DEF2_RATE: + if(sd->state.lr_flag != 2) + sd->def2_rate += val; + break; + case SP_MDEF_RATE: + if(sd->state.lr_flag != 2) + sd->mdef_rate += val; + break; + case SP_MDEF2_RATE: + if(sd->state.lr_flag != 2) + sd->mdef2_rate += val; + break; + case SP_RESTART_FULL_RECORVER: + if(sd->state.lr_flag != 2) + sd->special_state.restart_full_recover = 1; + break; + case SP_NO_CASTCANCEL: + if(sd->state.lr_flag != 2) + sd->special_state.no_castcancel = 1; + break; + case SP_NO_CASTCANCEL2: + if(sd->state.lr_flag != 2) + sd->special_state.no_castcancel2 = 1; + break; + case SP_NO_SIZEFIX: + if(sd->state.lr_flag != 2) + sd->special_state.no_sizefix = 1; + break; + case SP_NO_MAGIC_DAMAGE: + if(sd->state.lr_flag != 2) + sd->special_state.no_magic_damage = 1; + break; + case SP_NO_WEAPON_DAMAGE: + if(sd->state.lr_flag != 2) + sd->special_state.no_weapon_damage = 1; + break; + case SP_NO_GEMSTONE: + if(sd->state.lr_flag != 2) + sd->special_state.no_gemstone = 1; + break; + case SP_INFINITE_ENDURE: + if(sd->state.lr_flag != 2) + sd->special_state.infinite_endure = 1; + break; + case SP_SPLASH_RANGE: + if(sd->state.lr_flag != 2 && sd->splash_range < val) + sd->splash_range = val; + break; + case SP_SPLASH_ADD_RANGE: + if(sd->state.lr_flag != 2) + sd->splash_add_range += val; + break; + case SP_SHORT_WEAPON_DAMAGE_RETURN: + if(sd->state.lr_flag != 2) + sd->short_weapon_damage_return += val; + break; + case SP_LONG_WEAPON_DAMAGE_RETURN: + if(sd->state.lr_flag != 2) + sd->long_weapon_damage_return += val; + break; + case SP_MAGIC_DAMAGE_RETURN: //AppleGirl Was Here + if(sd->state.lr_flag != 2) + sd->magic_damage_return += val; + break; + case SP_ALL_STATS: // [Valaris] + if(sd->state.lr_flag!=2) { + sd->parame[SP_STR-SP_STR]+=val; + sd->parame[SP_AGI-SP_STR]+=val; + sd->parame[SP_VIT-SP_STR]+=val; + sd->parame[SP_INT-SP_STR]+=val; + sd->parame[SP_DEX-SP_STR]+=val; + sd->parame[SP_LUK-SP_STR]+=val; + clif_updatestatus(sd,13); + clif_updatestatus(sd,14); + clif_updatestatus(sd,15); + clif_updatestatus(sd,16); + clif_updatestatus(sd,17); + clif_updatestatus(sd,18); + } + break; + case SP_AGI_VIT: // [Valaris] + if(sd->state.lr_flag!=2) { + sd->parame[SP_AGI-SP_STR]+=val; + sd->parame[SP_VIT-SP_STR]+=val; + clif_updatestatus(sd,14); + clif_updatestatus(sd,15); + } + break; + case SP_AGI_DEX_STR: // [Valaris] + if(sd->state.lr_flag!=2) { + sd->parame[SP_AGI-SP_STR]+=val; + sd->parame[SP_DEX-SP_STR]+=val; + sd->parame[SP_STR-SP_STR]+=val; + clif_updatestatus(sd,14); + clif_updatestatus(sd,17); + clif_updatestatus(sd,13); + } + break; + case SP_PERFECT_HIDE: // [Valaris] + if(sd->state.lr_flag!=2) { + sd->perfect_hiding=1; + } + break; + case SP_DISGUISE: // Disguise script for items [Valaris] + if(sd->state.lr_flag!=2 && sd->disguiseflag==0) { + if(pc_isriding(sd)) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage(sd->fd, "Cannot wear disguise when riding a Peco."); + break; + } + sd->disguise=val; + clif_clearchar(&sd->bl, 9); + pc_setpos(sd, sd->mapname, sd->bl.x, sd->bl.y, 3); + } + break; + case SP_UNBREAKABLE: + if(sd->state.lr_flag!=2) { + sd->unbreakable += val; + } + break; + default: + if(battle_config.error_log) + printf("pc_bonus: unknown type %d %d !\n",type,val); + break; + } + return 0; +} + +/*========================================== + * 装 備品による能力等のボーナス設定 + *------------------------------------------ + */ +int pc_bonus2(struct map_session_data *sd,int type,int type2,int val) +{ + int i; + + nullpo_retr(0, sd); + + switch(type){ + case SP_ADDELE: + if(!sd->state.lr_flag) + sd->addele[type2]+=val; + else if(sd->state.lr_flag == 1) + sd->addele_[type2]+=val; + else if(sd->state.lr_flag == 2) + sd->arrow_addele[type2]+=val; + break; + case SP_ADDRACE: + if(!sd->state.lr_flag) + sd->addrace[type2]+=val; + else if(sd->state.lr_flag == 1) + sd->addrace_[type2]+=val; + else if(sd->state.lr_flag == 2) + sd->arrow_addrace[type2]+=val; + break; + case SP_ADDSIZE: + if(!sd->state.lr_flag) + sd->addsize[type2]+=val; + else if(sd->state.lr_flag == 1) + sd->addsize_[type2]+=val; + else if(sd->state.lr_flag == 2) + sd->arrow_addsize[type2]+=val; + break; + case SP_SUBELE: + if(sd->state.lr_flag != 2) + sd->subele[type2]+=val; + break; + case SP_SUBRACE: + if(sd->state.lr_flag != 2) + sd->subrace[type2]+=val; + break; + case SP_ADDEFF: + if(sd->state.lr_flag != 2) + sd->addeff[type2]+=val; + else + sd->arrow_addeff[type2]+=val; + break; + case SP_ADDEFF2: + if(sd->state.lr_flag != 2) + sd->addeff2[type2]+=val; + else + sd->arrow_addeff2[type2]+=val; + break; + case SP_RESEFF: + if(sd->state.lr_flag != 2) + sd->reseff[type2]+=val; + break; + case SP_MAGIC_ADDELE: + if(sd->state.lr_flag != 2) + sd->magic_addele[type2]+=val; + break; + case SP_MAGIC_ADDRACE: + if(sd->state.lr_flag != 2) + sd->magic_addrace[type2]+=val; + break; + case SP_MAGIC_SUBRACE: + if(sd->state.lr_flag != 2) + sd->magic_subrace[type2]+=val; + break; + case SP_ADD_DAMAGE_CLASS: + if(!sd->state.lr_flag) { + for(i=0;i<sd->add_damage_class_count;i++) { + if(sd->add_damage_classid[i] == type2) { + sd->add_damage_classrate[i] += val; + break; + } + } + if(i >= sd->add_damage_class_count && sd->add_damage_class_count < 10) { + sd->add_damage_classid[sd->add_damage_class_count] = type2; + sd->add_damage_classrate[sd->add_damage_class_count] += val; + sd->add_damage_class_count++; + } + } + else if(sd->state.lr_flag == 1) { + for(i=0;i<sd->add_damage_class_count_;i++) { + if(sd->add_damage_classid_[i] == type2) { + sd->add_damage_classrate_[i] += val; + break; + } + } + if(i >= sd->add_damage_class_count_ && sd->add_damage_class_count_ < 10) { + sd->add_damage_classid_[sd->add_damage_class_count_] = type2; + sd->add_damage_classrate_[sd->add_damage_class_count_] += val; + sd->add_damage_class_count_++; + } + } + break; + case SP_ADD_MAGIC_DAMAGE_CLASS: + if(sd->state.lr_flag != 2) { + for(i=0;i<sd->add_magic_damage_class_count;i++) { + if(sd->add_magic_damage_classid[i] == type2) { + sd->add_magic_damage_classrate[i] += val; + break; + } + } + if(i >= sd->add_magic_damage_class_count && sd->add_magic_damage_class_count < 10) { + sd->add_magic_damage_classid[sd->add_magic_damage_class_count] = type2; + sd->add_magic_damage_classrate[sd->add_magic_damage_class_count] += val; + sd->add_magic_damage_class_count++; + } + } + break; + case SP_ADD_DEF_CLASS: + if(sd->state.lr_flag != 2) { + for(i=0;i<sd->add_def_class_count;i++) { + if(sd->add_def_classid[i] == type2) { + sd->add_def_classrate[i] += val; + break; + } + } + if(i >= sd->add_def_class_count && sd->add_def_class_count < 10) { + sd->add_def_classid[sd->add_def_class_count] = type2; + sd->add_def_classrate[sd->add_def_class_count] += val; + sd->add_def_class_count++; + } + } + break; + case SP_ADD_MDEF_CLASS: + if(sd->state.lr_flag != 2) { + for(i=0;i<sd->add_mdef_class_count;i++) { + if(sd->add_mdef_classid[i] == type2) { + sd->add_mdef_classrate[i] += val; + break; + } + } + if(i >= sd->add_mdef_class_count && sd->add_mdef_class_count < 10) { + sd->add_mdef_classid[sd->add_mdef_class_count] = type2; + sd->add_mdef_classrate[sd->add_mdef_class_count] += val; + sd->add_mdef_class_count++; + } + } + break; + case SP_HP_DRAIN_RATE: + if(!sd->state.lr_flag) { + sd->hp_drain_rate += type2; + sd->hp_drain_per += val; + } + else if(sd->state.lr_flag == 1) { + sd->hp_drain_rate_ += type2; + sd->hp_drain_per_ += val; + } + break; + case SP_SP_DRAIN_RATE: + if(!sd->state.lr_flag) { + sd->sp_drain_rate += type2; + sd->sp_drain_per += val; + } + else if(sd->state.lr_flag == 1) { + sd->sp_drain_rate_ += type2; + sd->sp_drain_per_ += val; + } + break; + case SP_WEAPON_COMA_ELE: + if(sd->state.lr_flag != 2) + sd->weapon_coma_ele[type2] += val; + break; + case SP_WEAPON_COMA_RACE: + if(sd->state.lr_flag != 2) + sd->weapon_coma_race[type2] += val; + break; + case SP_RANDOM_ATTACK_INCREASE: // [Valaris] + if(sd->state.lr_flag !=2){ + sd->random_attack_increase_add = type2; + sd->random_attack_increase_per += val; + break; + } // end addition + default: + if(battle_config.error_log) + printf("pc_bonus2: unknown type %d %d %d!\n",type,type2,val); + break; + } + return 0; +} + +int pc_bonus3(struct map_session_data *sd,int type,int type2,int type3,int val) +{ + int i; + switch(type){ + case SP_ADD_MONSTER_DROP_ITEM: + if(sd->state.lr_flag != 2) { + for(i=0;i<sd->monster_drop_item_count;i++) { + if(sd->monster_drop_itemid[i] == type2) { + sd->monster_drop_race[i] |= 1<<type3; + if(sd->monster_drop_itemrate[i] < val) + sd->monster_drop_itemrate[i] = val; + break; + } + } + if(i >= sd->monster_drop_item_count && sd->monster_drop_item_count < 10) { + sd->monster_drop_itemid[sd->monster_drop_item_count] = type2; + sd->monster_drop_race[sd->monster_drop_item_count] |= 1<<type3; + sd->monster_drop_itemrate[sd->monster_drop_item_count] = val; + sd->monster_drop_item_count++; + } + } + break; + case SP_AUTOSPELL: + if(sd->state.lr_flag != 2){ + sd->autospell_id = type2; + sd->autospell_lv = type3; + sd->autospell_rate = val; + } + break; + default: + if(battle_config.error_log) + printf("pc_bonus3: unknown type %d %d %d %d!\n",type,type2,type3,val); + break; + } + + return 0; +} + +/*========================================== + * スクリプトによるスキル所得 + *------------------------------------------ + */ +int pc_skill(struct map_session_data *sd,int id,int level,int flag) +{ + nullpo_retr(0, sd); + + if(level>MAX_SKILL_LEVEL){ + if(battle_config.error_log) + printf("support card skill only!\n"); + return 0; + } + if(!flag && (sd->status.skill[id].id == id || level == 0)){ // クエスト所得ならここで条件を確認して送信する + sd->status.skill[id].lv=level; + pc_calcstatus(sd,0); + clif_skillinfoblock(sd); + } + else if(sd->status.skill[id].lv < level){ // 覚えられるがlvが小さいなら + if(sd->status.skill[id].id==id) + sd->status.skill[id].flag=sd->status.skill[id].lv+2; // lvを記憶 + else { + sd->status.skill[id].id=id; + sd->status.skill[id].flag=1; // cardスキルとする + } + sd->status.skill[id].lv=level; + } + + return 0; +} + +/*========================================== + * カード挿入 + *------------------------------------------ + */ +int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip) +{ + nullpo_retr(0, sd); + + if(idx_card >= 0 && idx_card < MAX_INVENTORY && idx_equip >= 0 && idx_equip < MAX_INVENTORY && sd->inventory_data[idx_card]) { + int i; + int nameid=sd->status.inventory[idx_equip].nameid; + int cardid=sd->status.inventory[idx_card].nameid; + int ep=sd->inventory_data[idx_card]->equip; + + if( nameid <= 0 || sd->inventory_data[idx_equip] == NULL || + (sd->inventory_data[idx_equip]->type!=4 && sd->inventory_data[idx_equip]->type!=5)|| // 装 備じゃない + ( sd->status.inventory[idx_equip].identify==0 ) || // 未鑑定 + ( sd->status.inventory[idx_equip].card[0]==0x00ff) || // 製造武器 + ( sd->status.inventory[idx_equip].card[0]==0x00fe) || + ( (sd->inventory_data[idx_equip]->equip&ep)==0 ) || // 装 備個所違い + ( sd->inventory_data[idx_equip]->type==4 && ep==32) || // 両 手武器と盾カード + ( sd->status.inventory[idx_equip].card[0]==(short)0xff00) || sd->status.inventory[idx_equip].equip){ + + clif_insert_card(sd,idx_equip,idx_card,1); + return 0; + } + for(i=0;i<sd->inventory_data[idx_equip]->slot;i++){ + if( sd->status.inventory[idx_equip].card[i] == 0){ + // 空きスロットがあったので差し込む + sd->status.inventory[idx_equip].card[i]=cardid; + + // カードは減らす + clif_insert_card(sd,idx_equip,idx_card,0); + pc_delitem(sd,idx_card,1,1); + return 0; + } + } + } + else + clif_insert_card(sd,idx_equip,idx_card,1); + + return 0; +} + +// +// アイテム物 +// + +/*========================================== + * スキルによる買い値修正 + *------------------------------------------ + */ +int pc_modifybuyvalue(struct map_session_data *sd,int orig_value) +{ + int skill,val = orig_value,rate1 = 0,rate2 = 0; + if((skill=pc_checkskill(sd,MC_DISCOUNT))>0) // ディスカウント + rate1 = 5+skill*2-((skill==10)? 1:0); + if((skill=pc_checkskill(sd,RG_COMPULSION))>0) // コムパルションディスカウント + rate2 = 5+skill*4; + if(rate1 < rate2) rate1 = rate2; + if(rate1) + val = (int)((double)orig_value*(double)(100-rate1)/100.); + if(val < 0) val = 0; + if(orig_value > 0 && val < 1) val = 1; + + return val; +} + +/*========================================== + * スキルによる売り値修正 + *------------------------------------------ + */ +int pc_modifysellvalue(struct map_session_data *sd,int orig_value) +{ + int skill,val = orig_value,rate = 0; + if((skill=pc_checkskill(sd,MC_OVERCHARGE))>0) // オーバーチャージ + rate = 5+skill*2-((skill==10)? 1:0); + if(rate) + val = (int)((double)orig_value*(double)(100+rate)/100.); + if(val < 0) val = 0; + if(orig_value > 0 && val < 1) val = 1; + + return val; +} + +/*========================================== + * アイテムを買った時に、新しいアイテム欄を使うか、 + * 3万個制限にかかるか確認 + *------------------------------------------ + */ +int pc_checkadditem(struct map_session_data *sd,int nameid,int amount) +{ + int i; + + nullpo_retr(0, sd); + + if(itemdb_isequip(nameid)) + return ADDITEM_NEW; + + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid==nameid){ + if(sd->status.inventory[i].amount+amount > MAX_AMOUNT) + return ADDITEM_OVERAMOUNT; + return ADDITEM_EXIST; + } + } + + if(amount > MAX_AMOUNT) + return ADDITEM_OVERAMOUNT; + return ADDITEM_NEW; +} + +/*========================================== + * 空きアイテム欄の個数 + *------------------------------------------ + */ +int pc_inventoryblank(struct map_session_data *sd) +{ + int i,b; + + nullpo_retr(0, sd); + + for(i=0,b=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid==0) + b++; + } + + return b; +} + +/*========================================== + * お金を払う + *------------------------------------------ + */ +int pc_payzeny(struct map_session_data *sd,int zeny) +{ + double z; + + nullpo_retr(0, sd); + + z = (double)sd->status.zeny; + if(sd->status.zeny<zeny || z - (double)zeny > MAX_ZENY) + return 1; + sd->status.zeny-=zeny; + clif_updatestatus(sd,SP_ZENY); + + return 0; +} + +/*========================================== + * お金を得る + *------------------------------------------ + */ +int pc_getzeny(struct map_session_data *sd,int zeny) +{ + double z; + + nullpo_retr(0, sd); + + z = (double)sd->status.zeny; + if(z + (double)zeny > MAX_ZENY) { + zeny = 0; + sd->status.zeny = MAX_ZENY; + } + sd->status.zeny+=zeny; + clif_updatestatus(sd,SP_ZENY); + + return 0; +} + +/*========================================== + * アイテムを探して、インデックスを返す + *------------------------------------------ + */ +int pc_search_inventory(struct map_session_data *sd,int item_id) +{ + int i; + + nullpo_retr(-1, sd); + + for(i=0;i<MAX_INVENTORY;i++) { + if(sd->status.inventory[i].nameid == item_id && + (sd->status.inventory[i].amount > 0 || item_id == 0)) + return i; + } + + return -1; +} + +/*========================================== + * アイテム追加。個数のみitem構造体の数字を無視 + *------------------------------------------ + */ +int pc_additem(struct map_session_data *sd,struct item *item_data,int amount) +{ + struct item_data *data; + int i,w; + + nullpo_retr(1, sd); + nullpo_retr(1, item_data); + + if(item_data->nameid <= 0 || amount <= 0) + return 1; + data = itemdb_search(item_data->nameid); + if((w = data->weight*amount) + sd->weight > sd->max_weight) + return 2; + + i = MAX_INVENTORY; + + if(!itemdb_isequip2(data)){ + // 装 備品ではないので、既所有品なら個数のみ変化させる + for(i=0;i<MAX_INVENTORY;i++) + if(sd->status.inventory[i].nameid == item_data->nameid && + sd->status.inventory[i].card[0] == item_data->card[0] && sd->status.inventory[i].card[1] == item_data->card[1] && + sd->status.inventory[i].card[2] == item_data->card[2] && sd->status.inventory[i].card[3] == item_data->card[3]) { + if(sd->status.inventory[i].amount+amount > MAX_AMOUNT) + return 5; + sd->status.inventory[i].amount+=amount; + clif_additem(sd,i,amount,0); + break; + } + } + if(i >= MAX_INVENTORY){ + // 装 備品か未所有品だったので空き欄へ追加 + i = pc_search_inventory(sd,0); + if(i >= 0) { + memcpy(&sd->status.inventory[i],item_data,sizeof(sd->status.inventory[0])); + sd->status.inventory[i].amount=amount; + sd->inventory_data[i]=data; + clif_additem(sd,i,amount,0); + } + else return 4; + } + sd->weight += w; + clif_updatestatus(sd,SP_WEIGHT); + + return 0; +} + +/*========================================== + * アイテムを減らす + *------------------------------------------ + */ +int pc_delitem(struct map_session_data *sd,int n,int amount,int type) +{ + nullpo_retr(1, sd); + + if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL) + return 1; + + sd->status.inventory[n].amount -= amount; + sd->weight -= sd->inventory_data[n]->weight*amount ; + if(sd->status.inventory[n].amount<=0){ + if(sd->status.inventory[n].equip) + pc_unequipitem(sd,n,0); + memset(&sd->status.inventory[n],0,sizeof(sd->status.inventory[0])); + sd->inventory_data[n] = NULL; + } + if(!(type&1)) + clif_delitem(sd,n,amount); + if(!(type&2)) + clif_updatestatus(sd,SP_WEIGHT); + + return 0; +} + +/*========================================== + * アイテムを落す + *------------------------------------------ + */ +int pc_dropitem(struct map_session_data *sd,int n,int amount) +{ + nullpo_retr(1, sd); + + if (sd->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; + map_addflooritem(&sd->status.inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, NULL, NULL, NULL, 0); + pc_delitem(sd, n, amount, 0); + + return 0; +} + +/*========================================== + * アイテムを拾う + *------------------------------------------ + */ +int pc_takeitem(struct map_session_data *sd,struct flooritem_data *fitem) +{ + int flag; + unsigned int tick = gettick(); + struct map_session_data *first_sd = NULL,*second_sd = NULL,*third_sd = NULL; + + nullpo_retr(0, sd); + nullpo_retr(0, fitem); + + if(fitem->first_get_id > 0) { + first_sd = map_id2sd(fitem->first_get_id); + if(tick < fitem->first_get_tick) { + if(fitem->first_get_id != sd->bl.id && !(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) { + second_sd = map_id2sd(fitem->second_get_id); + if(tick < fitem->second_get_tick) { + if(fitem->first_get_id != sd->bl.id && fitem->second_get_id != sd->bl.id && + !(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) { + third_sd = map_id2sd(fitem->third_get_id); + if(tick < fitem->third_get_tick) { + if(fitem->first_get_id != sd->bl.id && fitem->second_get_id != sd->bl.id && fitem->third_get_id != sd->bl.id && + !(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; + } + } + } + } + } + if((flag = pc_additem(sd,&fitem->item_data,fitem->item_data.amount))) + // 重量overで取得失敗 + clif_additem(sd,0,0,flag); + else { + /* 取得成功 */ + if(sd->attacktimer != -1) + pc_stopattack(sd); + clif_takeitem(&sd->bl,&fitem->bl); + map_clearflooritem(fitem->bl.id); + } + return 0; +} + +int pc_isUseitem(struct map_session_data *sd,int n) +{ + struct item_data *item; + int nameid; + + nullpo_retr(0, sd); + + item = sd->inventory_data[n]; + nameid = sd->status.inventory[n].nameid; + + if(item == NULL) + return 0; + if((nameid == 605) && map[sd->bl.m].flag.gvg) + return 0; + if(nameid == 601 && (map[sd->bl.m].flag.noteleport || map[sd->bl.m].flag.gvg)) { + clif_skill_teleportmessage(sd,0); + return 0; + } + if(nameid == 602 && map[sd->bl.m].flag.noreturn) + return 0; + if(nameid == 604 && (map[sd->bl.m].flag.nobranch || map[sd->bl.m].flag.gvg)) + return 0; + if(item->sex != 2 && sd->status.sex != item->sex) + return 0; + if(item->elv > 0 && sd->status.base_level < item->elv) + return 0; + if(((sd->status.class==13 || sd->status.class==4014) && ((1<<7)&item->class) == 0) || // have mounted classes use unmounted items [Valaris] + ((sd->status.class==21 || sd->status.class==4022) && ((1<<14)&item->class) == 0)) + return 0; + if(sd->status.class!=13 && sd->status.class!=4014 && sd->status.class!=21 && sd->status.class!=4022) + if((sd->status.class<=4000 && ((1<<sd->status.class)&item->class) == 0) || (sd->status.class>4000 && sd->status.class<4023 && ((1<<(sd->status.class-4001))&item->class) == 0) || + (sd->status.class>=4023 && ((1<<(sd->status.class-4023))&item->class) == 0)) + return 0; + return 1; +} + +/*========================================== + * アイテムを使う + *------------------------------------------ + */ +int pc_useitem(struct map_session_data *sd,int n) +{ + int nameid,amount; + + nullpo_retr(1, sd); + + if(n >=0 && n < MAX_INVENTORY) { + nameid = sd->status.inventory[n].nameid; + amount = sd->status.inventory[n].amount; + if(sd->status.inventory[n].nameid <= 0 || + sd->status.inventory[n].amount <= 0 || + sd->sc_data[SC_BERSERK].timer!=-1 || + !pc_isUseitem(sd,n) ) { + clif_useitemack(sd,n,0,0); + return 1; + } + if(sd->inventory_data[n]) + run_script(sd->inventory_data[n]->use_script,0,sd->bl.id,0); + + clif_useitemack(sd,n,amount-1,1); + pc_delitem(sd,n,1,1); + } + + return 0; +} + +/*========================================== + * カートアイテム追加。個数のみitem構造体の数字を無視 + *------------------------------------------ + */ +int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount) +{ + struct item_data *data; + int i,w; + + nullpo_retr(1, sd); + nullpo_retr(1, item_data); + + if(item_data->nameid <= 0 || amount <= 0) + return 1; + data = itemdb_search(item_data->nameid); + + if((w=data->weight*amount) + sd->cart_weight > sd->cart_max_weight) + return 1; + + i=MAX_CART; + if(!itemdb_isequip2(data)){ + // 装 備品ではないので、既所有品なら個数のみ変化させる + for(i=0;i<MAX_CART;i++){ + if(sd->status.cart[i].nameid==item_data->nameid && + sd->status.cart[i].card[0] == item_data->card[0] && sd->status.cart[i].card[1] == item_data->card[1] && + sd->status.cart[i].card[2] == item_data->card[2] && sd->status.cart[i].card[3] == item_data->card[3]){ + if(sd->status.cart[i].amount+amount > MAX_AMOUNT) + return 1; + sd->status.cart[i].amount+=amount; + clif_cart_additem(sd,i,amount,0); + break; + } + } + } + if(i >= MAX_CART){ + // 装 備品か未所有品だったので空き欄へ追加 + for(i=0;i<MAX_CART;i++){ + if(sd->status.cart[i].nameid==0){ + memcpy(&sd->status.cart[i],item_data,sizeof(sd->status.cart[0])); + sd->status.cart[i].amount=amount; + sd->cart_num++; + clif_cart_additem(sd,i,amount,0); + break; + } + } + if(i >= MAX_CART) + return 1; + } + sd->cart_weight += w; + clif_updatestatus(sd,SP_CARTINFO); + + return 0; +} + +/*========================================== + * カートアイテムを減らす + *------------------------------------------ + */ +int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type) +{ + nullpo_retr(1, sd); + + if(sd->status.cart[n].nameid==0 || + sd->status.cart[n].amount<amount) + return 1; + + sd->status.cart[n].amount -= amount; + sd->cart_weight -= itemdb_weight(sd->status.cart[n].nameid)*amount ; + if(sd->status.cart[n].amount <= 0){ + memset(&sd->status.cart[n],0,sizeof(sd->status.cart[0])); + sd->cart_num--; + } + if(!type) { + clif_cart_delitem(sd,n,amount); + clif_updatestatus(sd,SP_CARTINFO); + } + + return 0; +} + +/*========================================== + * カートへアイテム移動 + *------------------------------------------ + */ +int pc_putitemtocart(struct map_session_data *sd,int idx,int amount) { + struct item *item_data; + + nullpo_retr(0, sd); + nullpo_retr(0, item_data = &sd->status.inventory[idx]); + + if (item_data->nameid==0 || item_data->amount<amount || sd->vender_id) + return 1; + if (pc_cart_additem(sd,item_data,amount) == 0) + return pc_delitem(sd,idx,amount,0); + + return 1; +} + +/*========================================== + * カート内のアイテム数確認(個数の差分を返す) + *------------------------------------------ + */ +int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount) +{ + struct item *item_data; + + nullpo_retr(-1, sd); + nullpo_retr(-1, item_data=&sd->status.cart[idx]); + + if( item_data->nameid==0 || !item_data->amount) + return -1; + return item_data->amount-amount; +} +/*========================================== + * カートからアイテム移動 + *------------------------------------------ + */ + +int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount) +{ + struct item *item_data; + int flag; + + nullpo_retr(0, sd); + nullpo_retr(0, item_data=&sd->status.cart[idx]); + + if( item_data->nameid==0 || item_data->amount<amount || sd->vender_id ) + return 1; + if((flag = pc_additem(sd,item_data,amount)) == 0) + return pc_cart_delitem(sd,idx,amount,0); + + clif_additem(sd,0,0,flag); + return 1; +} + +/*========================================== + * アイテム鑑定 + *------------------------------------------ + */ +int pc_item_identify(struct map_session_data *sd,int idx) +{ + int flag=1; + + nullpo_retr(0, sd); + + if(idx >= 0 && idx < MAX_INVENTORY) { + if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){ + flag=0; + sd->status.inventory[idx].identify=1; + } + clif_item_identified(sd,idx,flag); + } + else + clif_item_identified(sd,idx,flag); + + return !flag; +} + +/*========================================== + * スティル品公開 + *------------------------------------------ + */ +int pc_show_steal(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + int itemid; + int type; + + struct item_data *item=NULL; + char output[100]; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, sd=va_arg(ap,struct map_session_data *)); + + itemid=va_arg(ap,int); + type=va_arg(ap,int); + + if(!type){ + if((item=itemdb_exists(itemid))==NULL) + sprintf(output,"%s stole an Unknown_Item.",sd->status.name); + else + sprintf(output,"%s stole %s.",sd->status.name,item->jname); + clif_displaymessage( ((struct map_session_data *)bl)->fd, output); + }else{ + sprintf(output,"%s has not stolen the item because of being overweight.",sd->status.name); + clif_displaymessage( ((struct map_session_data *)bl)->fd, output); + } + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +//** pc.c: Small Steal Item fix by fritz +int pc_steal_item(struct map_session_data *sd,struct block_list *bl) +{ + if(sd != NULL && bl != NULL && bl->type == BL_MOB) { + int i,skill,rate,itemid,flag, count; + struct mob_data *md; + md=(struct mob_data *)bl; + if(!md->state.steal_flag && mob_db[md->class].mexp <= 0 && !(mob_db[md->class].mode&0x20) && md->sc_data[SC_STONE].timer == -1 && md->sc_data[SC_FREEZE].timer == -1 && + (!(md->class>1324 && md->class<1364))) // prevent stealing from treasure boxes [Valaris] + { + skill = sd->paramc[4] - mob_db[md->class].dex + pc_checkskill(sd,TF_STEAL) + 10; + + if(0 < skill) + { + for(count = 8; count <= 8 && count != 0; count--) + { + i = rand()%8; + itemid = mob_db[md->class].dropitem[i].nameid; + + if(itemid > 0 && itemdb_type(itemid) != 6) + { + rate = (mob_db[md->class].dropitem[i].p / battle_config.item_rate_common * 100 * skill)/100; + + if(rand()%10000 < rate) + { + struct item tmp_item; + memset(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid = itemid; + tmp_item.amount = 1; + tmp_item.identify = 1; + flag = pc_additem(sd,&tmp_item,1); + if(battle_config.show_steal_in_same_party) + { + party_foreachsamemap(pc_show_steal,sd,1,sd,tmp_item.nameid,0); + } + + if(flag) + { + if(battle_config.show_steal_in_same_party) + { + party_foreachsamemap(pc_show_steal,sd,1,sd,tmp_item.nameid,1); + } + + clif_additem(sd,0,0,flag); + } + md->state.steal_flag = 1; + return 1; + } + } + } + } + } + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int pc_steal_coin(struct map_session_data *sd,struct block_list *bl) +{ + if(sd != NULL && bl != NULL && bl->type == BL_MOB) { + int rate,skill; + struct mob_data *md=(struct mob_data *)bl; + if(md && !md->state.steal_coin_flag && md->sc_data && md->sc_data[SC_STONE].timer == -1 && md->sc_data[SC_FREEZE].timer == -1) { + skill = pc_checkskill(sd,RG_STEALCOIN)*10; + rate = skill + (sd->status.base_level - mob_db[md->class].lv)*3 + sd->paramc[4]*2 + sd->paramc[5]*2; + if(rand()%1000 < rate) { + pc_getzeny(sd,mob_db[md->class].lv*10 + rand()%100); + md->state.steal_coin_flag = 1; + return 1; + } + } + } + + return 0; +} +// +// +// +/*========================================== + * PCの位置設定 + *------------------------------------------ + */ +int pc_setpos(struct map_session_data *sd,char *mapname_org,int x,int y,int clrtype) +{ + char mapname[24]; + int m=0,c=0,disguise=0; + + nullpo_retr(0, sd); + + if(sd->chatID) // チャットから出る + chat_leavechat(sd); + if(sd->trade_partner) // 取引を中断する + trade_tradecancel(sd); + if(sd->state.storage_flag) + storage_guild_storage_quit(sd,0); + else + storage_storage_quit(sd); // 倉庫を開いてるなら保存する + + if(sd->party_invite>0) // パーティ勧誘を拒否する + party_reply_invite(sd,sd->party_invite_account,0); + if(sd->guild_invite>0) // ギルド勧誘を拒否する + guild_reply_invite(sd,sd->guild_invite,0); + if(sd->guild_alliance>0) // ギルド同盟勧誘を拒否する + guild_reply_reqalliance(sd,sd->guild_alliance_account,0); + + skill_castcancel(&sd->bl,0); // 詠唱中断 + pc_stop_walking(sd,0); // 歩行中断 + pc_stopattack(sd); // 攻撃中断 + + if(pc_issit(sd)) { + pc_setstand(sd); + skill_gangsterparadise(sd,0); + } + + if(sd->sc_data[SC_TRICKDEAD].timer != -1) + skill_status_change_end(&sd->bl, SC_TRICKDEAD, -1); + if(sd->status.option&2) + skill_status_change_end(&sd->bl, SC_HIDING, -1); + if(sd->status.option&4) + skill_status_change_end(&sd->bl, SC_CLOAKING, -1); + if(sd->status.option&16386) + skill_status_change_end(&sd->bl, SC_CHASEWALK, -1); + if(sd->sc_data[SC_BLADESTOP].timer!=-1) + skill_status_change_end(&sd->bl,SC_BLADESTOP,-1); + if(sd->sc_data[SC_DANCING].timer!=-1) // clear dance effect when warping [Valaris] + skill_stop_dancing(&sd->bl,0); + + if(sd->status.pet_id > 0 && sd->pd && sd->pet.intimate > 0) { + pet_stopattack(sd->pd); + pet_changestate(sd->pd,MS_IDLE,0); + } + + if(sd->disguise) { // clear disguises when warping [Valaris] + clif_clearchar(&sd->bl, 9); + disguise=sd->disguise; + sd->disguise=0; + } + + memcpy(mapname,mapname_org,24); + mapname[16]=0; + if(strstr(mapname,".gat")==NULL && strlen(mapname)<16){ + strcat(mapname,".gat"); + } + + m=map_mapname2mapid(mapname); + if(m<0){ + if(sd->mapname[0]){ + int ip,port; + if(map_mapname2ipport(mapname,&ip,&port)==0){ + skill_stop_dancing(&sd->bl,1); + skill_unit_out_all(&sd->bl,gettick(),1); + clif_clearchar_area(&sd->bl,clrtype&0xffff); + skill_gangsterparadise(sd,0); + map_delblock(&sd->bl); + 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) + pc_calcstatus(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); + } + } + memcpy(sd->mapname,mapname,24); + sd->bl.x=x; + sd->bl.y=y; + sd->state.waitingdisconnect=1; + pc_makesavestatus(sd); + if(sd->status.pet_id > 0 && sd->pd) + intif_save_petdata(sd->status.account_id,&sd->pet); + chrif_save(sd); + storage_storage_save(sd); + chrif_changemapserver(sd, mapname, x, y, ip, port); + return 0; + } + } +#if 0 + clif_authfail_fd(sd->fd,0); // cancel + clif_setwaitclose(sd->fd); +#endif + return 1; + } + + if(x <0 || x >= map[m].xs || y <0 || y >= map[m].ys) + x=y=0; + if((x==0 && y==0) || (c=read_gat(m,x,y))==1 || c==5){ + if(x||y) { + if(battle_config.error_log) + printf("stacked (%d,%d)\n",x,y); + } + do { + x=rand()%(map[m].xs-2)+1; + y=rand()%(map[m].ys-2)+1; + } while((c=read_gat(m,x,y))==1 || c==5); + } + + if(sd->mapname[0] && sd->bl.prev != NULL){ + skill_unit_out_all(&sd->bl,gettick(),1); + clif_clearchar_area(&sd->bl,clrtype&0xffff); + skill_gangsterparadise(sd,0); + map_delblock(&sd->bl); + // 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) + pc_calcstatus(sd,2); + pc_makesavestatus(sd); + chrif_save(sd); + storage_storage_save(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].name,x,y); // [MouseJstr] + } + + if(disguise) // disguise teleport fix [Valaris] + sd->disguise=disguise; + + memcpy(sd->mapname,mapname,24); + sd->bl.m = m; + sd->to_x = x; + sd->to_y = y; + + // moved and changed dance effect stopping + + sd->bl.x = x; + sd->bl.y = y; + + 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; + } + +// map_addblock(&sd->bl); /// ブロック登録とspawnは +// clif_spawnpc(sd); + + return 0; +} + +/*========================================== + * PCのランダムワープ + *------------------------------------------ + */ +int pc_randomwarp(struct map_session_data *sd, int type) { + int x,y,c,i=0; + int m; + + nullpo_retr(0, sd); + + m=sd->bl.m; + + if (map[sd->bl.m].flag.noteleport) // テレポート禁止 + return 0; + + do{ + x=rand()%(map[m].xs-2)+1; + y=rand()%(map[m].ys-2)+1; + } while (((c=read_gat(m,x,y)) == 1 || c == 5) && (i++) < 1000); + + if (i < 1000) + pc_setpos(sd,map[m].name,x,y,type); + + return 0; +} + +/*========================================== + * 現在位置のメモ + *------------------------------------------ + */ +int pc_memo(struct map_session_data *sd, int i) { + int skill; + int j; + + nullpo_retr(0, sd); + + skill = pc_checkskill(sd, AL_WARP); + + if (i >= MIN_PORTAL_MEMO) + i -= MIN_PORTAL_MEMO; + else if (map[sd->bl.m].flag.nomemo || (map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd))) { + clif_skill_teleportmessage(sd, 1); + return 0; + } + + if (skill < 1) { + clif_skill_memo(sd,2); + } + + if (skill < 2 || i < -1 || i > 2) { + clif_skill_memo(sd, 1); + return 0; + } + + for(j = 0 ; j < 3; j++) { + if (strcmp(sd->status.memo_point[j].map, map[sd->bl.m].name) == 0) { + i = j; + break; + } + } + + if (i == -1) { + for(i = skill - 3; i >= 0; i--) { + memcpy(&sd->status.memo_point[i+1],&sd->status.memo_point[i], + sizeof(struct point)); + } + i = 0; + } + memcpy(sd->status.memo_point[i].map, map[sd->bl.m].name, 24); + sd->status.memo_point[i].x = sd->bl.x; + sd->status.memo_point[i].y = sd->bl.y; + + clif_skill_memo(sd, 0); + + return 1; +} + +/*========================================== + * + *------------------------------------------ + */ +int pc_can_reach(struct map_session_data *sd,int x,int y) +{ + struct walkpath_data wpd; + + nullpo_retr(0, sd); + + if( sd->bl.x==x && sd->bl.y==y ) // 同じマス + return 1; + + // 障害物判定 + wpd.path_len=0; + wpd.path_pos=0; + wpd.path_half=0; + return (path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,x,y,0)!=-1)?1:0; +} + +// +// 歩 行物 +// +/*========================================== + * 次の1歩にかかる時間を計算 + *------------------------------------------ + */ +static int calc_next_walk_step(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if(sd->walkpath.path_pos>=sd->walkpath.path_len) + return -1; + if(sd->walkpath.path[sd->walkpath.path_pos]&1) + return sd->speed*14/10; + + return sd->speed; +} + +/*========================================== + * 半歩進む(timer関数) + *------------------------------------------ + */ +static int pc_walk(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd; + int i,ctype; + int moveblock; + int x,y,dx,dy; + + sd=map_id2sd(id); + if(sd==NULL) + return 0; + + if(sd->walktimer != tid){ + if(battle_config.error_log) + printf("pc_walk %d != %d\n",sd->walktimer,tid); + return 0; + } + sd->walktimer=-1; + if(sd->walkpath.path_pos>=sd->walkpath.path_len || sd->walkpath.path_pos!=data) + return 0; + + //歩いたので息吹のタイマーを初期化 + sd->inchealspirithptick = 0; + sd->inchealspiritsptick = 0; + + sd->walkpath.path_half ^= 1; + if(sd->walkpath.path_half==0){ // マス目中心へ到着 + sd->walkpath.path_pos++; + if(sd->state.change_walk_target){ + pc_walktoxy_sub(sd); + return 0; + } + } else { // マス目境界へ到着 + if(sd->walkpath.path[sd->walkpath.path_pos]>=8) + return 1; + + x = sd->bl.x; + y = sd->bl.y; + ctype = map_getcell(sd->bl.m,x,y); + if(ctype == 1 || ctype == 5) { + pc_stop_walking(sd,1); + return 0; + } + sd->dir=sd->head_dir=sd->walkpath.path[sd->walkpath.path_pos]; + dx = dirx[(int)sd->dir]; + dy = diry[(int)sd->dir]; + ctype = map_getcell(sd->bl.m,x+dx,y+dy); + if(ctype == 1 || ctype == 5) { + pc_walktoxy_sub(sd); + return 0; + } + + moveblock = ( x/BLOCK_SIZE != (x+dx)/BLOCK_SIZE || y/BLOCK_SIZE != (y+dy)/BLOCK_SIZE); + + sd->walktimer = 1; + map_foreachinmovearea(clif_pcoutsight,sd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,0,sd); + + x += dx; + y += dy; + + if(moveblock) map_delblock(&sd->bl); + sd->bl.x = x; + sd->bl.y = y; + if(moveblock) map_addblock(&sd->bl); + + if(sd->sc_data[SC_DANCING].timer!=-1) + skill_unit_move_unit_group((struct skill_unit_group *)sd->sc_data[SC_DANCING].val2,sd->bl.m,dx,dy); + + map_foreachinmovearea(clif_pcinsight,sd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,0,sd); + sd->walktimer = -1; + + if(sd->status.party_id>0){ // パーティのHP情報通知検査 + struct party *p=party_search(sd->status.party_id); + if(p!=NULL){ + int p_flag=0; + map_foreachinmovearea(party_send_hp_check,sd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,sd->status.party_id,&p_flag); + if(p_flag) + sd->party_hp=-1; + } + } + if(sd->status.option&4) // クローキングの消滅検査 + skill_check_cloaking(&sd->bl); + /* ディボーション検査 */ + for(i=0;i<5;i++) + if(sd->dev.val1[i]){ + skill_devotion3(&sd->bl,sd->dev.val1[i]); + break; + } + /* 被ディボーション検査 */ + if( sd->sc_data && sd->sc_data[SC_DEVOTION].val1){ + skill_devotion2(&sd->bl,sd->sc_data[SC_DEVOTION].val1); + } + + skill_unit_move(&sd->bl,tick,1); // スキルユニットの検査 + + if(map_getcell(sd->bl.m,x,y)&0x80) + npc_touch_areanpc(sd,sd->bl.m,x,y); + else + sd->areanpc_id=0; + } + if((i=calc_next_walk_step(sd))>0) { + i = i>>1; + if(i < 1 && sd->walkpath.path_half == 0) + i = 1; + sd->walktimer=add_timer(tick+i,pc_walk,id,sd->walkpath.path_pos); + } + + return 0; +} + +/*========================================== + * 移動可能か確認して、可能なら歩行開始 + *------------------------------------------ + */ +static int pc_walktoxy_sub(struct map_session_data *sd) +{ + struct walkpath_data wpd; + int i; + + nullpo_retr(1, sd); + + if(path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,sd->to_x,sd->to_y,0)) + return 1; + memcpy(&sd->walkpath,&wpd,sizeof(wpd)); + + clif_walkok(sd); + sd->state.change_walk_target=0; + + if((i=calc_next_walk_step(sd))>0){ + i = i>>2; + sd->walktimer=add_timer(gettick()+i,pc_walk,sd->bl.id,0); + } + clif_movechar(sd); + + return 0; +} + +/*========================================== + * pc歩 行要求 + *------------------------------------------ + */ +int pc_walktoxy(struct map_session_data *sd,int x,int y) +{ + + nullpo_retr(0, sd); + + sd->to_x=x; + sd->to_y=y; + + if(sd->walktimer != -1 && sd->state.change_walk_target==0){ + // 現在歩いている最中の目的地変更なのでマス目の中心に来た時に + // timer関数からpc_walktoxy_subを呼ぶようにする + sd->state.change_walk_target=1; + } else { + pc_walktoxy_sub(sd); + } + + return 0; +} + +/*========================================== + * 歩 行停止 + *------------------------------------------ + */ +int pc_stop_walking(struct map_session_data *sd,int type) +{ + nullpo_retr(0, sd); + + if(sd->walktimer != -1) { + delete_timer(sd->walktimer,pc_walk); + sd->walktimer=-1; + } + sd->walkpath.path_len=0; + sd->to_x = sd->bl.x; + sd->to_y = sd->bl.y; + if(type&0x01) + clif_fixpos(&sd->bl); + if(type&0x02 && battle_config.pc_damage_delay) { + unsigned int tick = gettick(); + int delay = battle_get_dmotion(&sd->bl); + if(sd->canmove_tick < tick) + sd->canmove_tick = tick + delay; + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int pc_movepos(struct map_session_data *sd,int dst_x,int dst_y) +{ + int moveblock; + int dx,dy,dist; + + struct walkpath_data wpd; + + nullpo_retr(0, sd); + + if(path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,dst_x,dst_y,0)) + return 1; + + sd->dir = sd->head_dir = map_calc_dir(&sd->bl, dst_x,dst_y); + + dx = dst_x - sd->bl.x; + dy = dst_y - sd->bl.y; + dist = distance(sd->bl.x,sd->bl.y,dst_x,dst_y); + + moveblock = ( sd->bl.x/BLOCK_SIZE != dst_x/BLOCK_SIZE || sd->bl.y/BLOCK_SIZE != dst_y/BLOCK_SIZE); + + map_foreachinmovearea(clif_pcoutsight,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,dx,dy,0,sd); + + if(moveblock) map_delblock(&sd->bl); + sd->bl.x = dst_x; + sd->bl.y = dst_y; + if(moveblock) map_addblock(&sd->bl); + + map_foreachinmovearea(clif_pcinsight,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,-dx,-dy,0,sd); + + if(sd->status.party_id>0){ // パーティのHP情報通知検査 + struct party *p=party_search(sd->status.party_id); + if(p!=NULL){ + int flag=0; + map_foreachinmovearea(party_send_hp_check,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,-dx,-dy,BL_PC,sd->status.party_id,&flag); + if(flag) + sd->party_hp=-1; + } + } + + if(sd->status.option&4) // クローキングの消滅検査 + skill_check_cloaking(&sd->bl); + + skill_unit_move(&sd->bl,gettick(),dist+7); // スキルユニットの検査 + + if(map_getcell(sd->bl.m,sd->bl.x,sd->bl.y)&0x80) + npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y); + else + sd->areanpc_id=0; + return 0; +} + +// +// 武器戦闘 +// +/*========================================== + * スキルの検索 所有していた場合Lvが返る + *------------------------------------------ + */ +int pc_checkskill(struct map_session_data *sd,int skill_id) +{ + if(sd == NULL) return 0; + if( skill_id>=10000 ){ + struct guild *g; + if( sd->status.guild_id>0 && (g=guild_search(sd->status.guild_id))!=NULL) + return guild_checkskill(g,skill_id); + return 0; + } + + if(sd->status.skill[skill_id].id == skill_id) + return (sd->status.skill[skill_id].lv); + + return 0; +} + +/*========================================== + * 武器変更によるスキルの継続チェック + * 引数: + * struct map_session_data *sd セッションデータ + * int nameid 装備品ID + * 返り値: + * 0 変更なし + * -1 スキルを解除 + *------------------------------------------ + */ +int pc_checkallowskill(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if( sd->sc_data == NULL ) + return 0; + + if(!(skill_get_weapontype(KN_TWOHANDQUICKEN)&(1<<sd->status.weapon)) && sd->sc_data[SC_TWOHANDQUICKEN].timer!=-1) { // 2HQ + skill_status_change_end(&sd->bl,SC_TWOHANDQUICKEN,-1); // 2HQを解除 + return -1; + } + if(!(skill_get_weapontype(LK_AURABLADE)&(1<<sd->status.weapon)) && sd->sc_data[SC_AURABLADE].timer!=-1) { /* オーラブレード */ + skill_status_change_end(&sd->bl,SC_AURABLADE,-1); /* オーラブレードを解除 */ + return -1; + } + if(!(skill_get_weapontype(LK_PARRYING)&(1<<sd->status.weapon)) && sd->sc_data[SC_PARRYING].timer!=-1) { /* パリイング */ + skill_status_change_end(&sd->bl,SC_PARRYING,-1); /* パリイングを解除 */ + return -1; + } + if(!(skill_get_weapontype(LK_CONCENTRATION)&(1<<sd->status.weapon)) && sd->sc_data[SC_CONCENTRATION].timer!=-1) { /* コンセントレーション */ + skill_status_change_end(&sd->bl,SC_CONCENTRATION,-1); /* コンセントレーションを解除 */ + return -1; + } + if(!(skill_get_weapontype(CR_SPEARQUICKEN)&(1<<sd->status.weapon)) && sd->sc_data[SC_SPEARSQUICKEN].timer!=-1){ // スピアクィッケン + skill_status_change_end(&sd->bl,SC_SPEARSQUICKEN,-1); // スピアクイッケンを解除 + return -1; + } + if(!(skill_get_weapontype(BS_ADRENALINE)&(1<<sd->status.weapon)) && sd->sc_data[SC_ADRENALINE].timer!=-1){ // アドレナリンラッシュ + skill_status_change_end(&sd->bl,SC_ADRENALINE,-1); // アドレナリンラッシュを解除 + return -1; + } + + if(sd->status.shield <= 0) { + if(sd->sc_data[SC_AUTOGUARD].timer!=-1){ // オートガード + skill_status_change_end(&sd->bl,SC_AUTOGUARD,-1); + return -1; + } + if(sd->sc_data[SC_DEFENDER].timer!=-1){ // ディフェンダー + skill_status_change_end(&sd->bl,SC_DEFENDER,-1); + return -1; + } + if(sd->sc_data[SC_REFLECTSHIELD].timer!=-1){ //リフレクトシールド + skill_status_change_end(&sd->bl,SC_REFLECTSHIELD,-1); + return -1; + } + } + + return 0; +} + +/*========================================== + * 装 備品のチェック + *------------------------------------------ + */ +int pc_checkequip(struct map_session_data *sd,int pos) +{ + int i; + + nullpo_retr(-1, sd); + + for(i=0;i<11;i++){ + if(pos & equip_pos[i]) + return sd->equip_index[i]; + } + + return -1; +} + +/*========================================== + * 転生職や養子職の元の職業を返す + *------------------------------------------ + */ +struct pc_base_job pc_calc_base_job(int b_class) +{ + struct pc_base_job bj; + //転生や養子の場合の元の職業を算出する + if(b_class < MAX_PC_CLASS){ //通常 + bj.job = b_class; + bj.upper = 0; + }else if(b_class >= 4001 && b_class < 4023){ //転生職 + bj.job = b_class - 4001; + bj.upper = 1; + }else if(b_class == 23 + 4023 -1){ //養子スパノビ + bj.job = b_class - (4023 - 1); + bj.upper = 2; + }else{ //養子スパノビ以外の養子 + bj.job = b_class - 4023; + bj.upper = 2; + } + + if(battle_config.enable_upper_class==0){ //confで無効になっていたらupper=0 + bj.upper = 0; + } + + if(bj.job == 0){ + bj.type = 0; + }else if(bj.job < 7){ + bj.type = 1; + }else{ + bj.type = 2; + } + + return bj; +} + +/*========================================== + * PCの攻撃 (timer関数) + *------------------------------------------ + */ +int pc_attack_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd; + struct block_list *bl; + struct status_change *sc_data; + short *opt; + int dist,skill,range; + + sd=map_id2sd(id); + if(sd == NULL) + return 0; + if(sd->attacktimer != tid){ + if(battle_config.error_log) + printf("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; + + if(bl->type == BL_PC && pc_isdead((struct map_session_data *)bl)) + return 0; + + // 同じmapでないなら攻撃しない + // PCが死んでても攻撃しない + if(sd->bl.m != bl->m || pc_isdead(sd)) + return 0; + + if( sd->opt1>0 || sd->status.option&2 || sd->status.option&16388) // 異常などで攻撃できない + return 0; + + if(sd->sc_data[SC_AUTOCOUNTER].timer != -1) + return 0; + if(sd->sc_data[SC_BLADESTOP].timer != -1) + return 0; + + if((opt = battle_get_option(bl)) != NULL && *opt&0x46) + return 0; + if(((sc_data = battle_get_sc_data(bl)) != NULL && sc_data[SC_TRICKDEAD].timer != -1) || + ((sc_data = battle_get_sc_data(bl)) != NULL && sc_data[SC_BASILICA].timer != -1 )) + return 0; + + if(sd->skilltimer != -1 && pc_checkskill(sd,SA_FREECAST) <= 0) + return 0; + + 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 0; + } + } + + dist = distance(sd->bl.x,sd->bl.y,bl->x,bl->y); + range = sd->attackrange; + if(sd->status.weapon != 11) range++; + if( dist > range ){ // 届 かないので移動 + if(pc_can_reach(sd,bl->x,bl->y)) + clif_movetoattack(sd,bl); + return 0; + } + + if(dist <= range && !battle_check_range(&sd->bl,bl,range) ) { + if(pc_can_reach(sd,bl->x,bl->y) && sd->canmove_tick < tick && (sd->sc_data[SC_ANKLE].timer == -1 || sd->sc_data[SC_SPIDERWEB].timer == -1)) + pc_walktoxy(sd,bl->x,bl->y); + sd->attackabletime = tick + (sd->aspd<<1); + } + else { + if(battle_config.pc_attack_direction_change) + sd->dir=sd->head_dir=map_calc_dir(&sd->bl, bl->x,bl->y ); // 向き設定 + + if(sd->walktimer != -1) + pc_stop_walking(sd,1); + + if(sd->sc_data[SC_COMBO].timer == -1) { + map_freeblock_lock(); + pc_stop_walking(sd,0); + sd->attacktarget_lv = battle_weapon_attack(&sd->bl,bl,tick,0); + if(!(battle_config.pc_cloak_check_type&2) && sd->sc_data[SC_CLOAKING].timer != -1) + skill_status_change_end(&sd->bl,SC_CLOAKING,-1); + if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_attack_support) + pet_target_check(sd,bl,0); + map_freeblock_unlock(); + if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリーキャスト + sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100); + else + sd->attackabletime = tick + (sd->aspd<<1); + } + else if(sd->attackabletime <= tick) { + if(sd->skilltimer != -1 && (skill = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリーキャスト + sd->attackabletime = tick + ((sd->aspd<<1)*(150 - skill*5)/100); + else + sd->attackabletime = tick + (sd->aspd<<1); + } + if(sd->attackabletime <= tick) sd->attackabletime = tick + (battle_config.max_aspd<<1); + } + + if(sd->state.attack_continue) { + sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0); + } + + return 0; +} + +/*========================================== + * 攻撃要求 + * typeが1なら継続攻撃 + *------------------------------------------ + */ +int pc_attack(struct map_session_data *sd,int target_id,int type) +{ + struct block_list *bl; + int d; + + nullpo_retr(0, sd); + + bl=map_id2bl(target_id); + if(bl==NULL) + return 1; + + if(bl->type==BL_NPC) { // monster npcs [Valaris] + npc_click(sd,RFIFOL(sd->fd,2)); + return 0; + } + + if(!battle_check_target(&sd->bl,bl,BCT_ENEMY)) + return 1; + if(sd->attacktimer != -1) + pc_stopattack(sd); + sd->attacktarget=target_id; + sd->state.attack_continue=type; + + d=DIFF_TICK(sd->attackabletime,gettick()); + if(d>0 && d<2000){ // 攻撃delay中 + sd->attacktimer=add_timer(sd->attackabletime,pc_attack_timer,sd->bl.id,0); + } else { + // 本来timer関数なので引数を合わせる + pc_attack_timer(-1,gettick(),sd->bl.id,0); + } + + return 0; +} + +/*========================================== + * 継続攻撃停止 + *------------------------------------------ + */ +int pc_stopattack(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if(sd->attacktimer != -1) { + delete_timer(sd->attacktimer,pc_attack_timer); + sd->attacktimer=-1; + } + sd->attacktarget=0; + sd->state.attack_continue=0; + + return 0; +} + +int pc_follow_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd, *bl; + + sd=map_id2sd(id); + if(sd == NULL || sd->followtimer != tid) + return 0; + + sd->followtimer=-1; + + do { + if(sd->bl.prev == NULL) + break; + + bl=(struct map_session_data *) map_id2bl(sd->followtarget); + + if(bl==NULL) + return 0; + + if(bl->bl.prev == NULL) + break; + + if(bl->bl.type == BL_PC && pc_isdead((struct map_session_data *)bl)) + return 0; + + if (sd->skilltimer == -1 && sd->attacktimer == -1 && sd->walktimer == -1) { + if((sd->bl.m == bl->bl.m) && pc_can_reach(sd,bl->bl.x,bl->bl.y)) { + if (distance(sd->bl.x,sd->bl.y,bl->bl.x,bl->bl.y) > 5) + pc_walktoxy(sd,bl->bl.x,bl->bl.y); + } else + pc_setpos((struct map_session_data*)sd, bl->mapname, bl->bl.x, bl->bl.y, 3); + } + } while (0); + + sd->followtimer=add_timer(tick + sd->aspd,pc_follow_timer,sd->bl.id,0); + + return 0; +} + +int pc_follow(struct map_session_data *sd,int target_id) +{ + struct block_list *bl; + + bl=map_id2bl(target_id); + if(bl==NULL) + return 1; + sd->followtarget=target_id; + if(sd->followtimer != -1) { + delete_timer(sd->followtimer,pc_follow_timer); + sd->followtimer = -1; + } + + pc_follow_timer(-1,gettick(),sd->bl.id,0); + + return 0; +} + +int pc_checkbaselevelup(struct map_session_data *sd) +{ + int next = pc_nextbaseexp(sd); + + nullpo_retr(0, sd); + + if(sd->status.base_exp >= next && next > 0){ + struct pc_base_job s_class = pc_calc_base_job(sd->status.class); + + // base側レベルアップ処理 + sd->status.base_exp -= next; + + sd->status.base_level ++; + 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); + pc_calcstatus(sd,0); + pc_heal(sd,sd->status.max_hp,sd->status.max_sp); + + //スパノビはキリエ、イムポ、マニピ、グロ、サフラLv1がかかる + if(s_class.job == 23){ + skill_status_change_start(&sd->bl,SkillStatusChangeTable[PR_KYRIE],1,0,0,0,skill_get_time(PR_KYRIE,1),0 ); + skill_status_change_start(&sd->bl,SkillStatusChangeTable[PR_IMPOSITIO],1,0,0,0,skill_get_time(PR_IMPOSITIO,1),0 ); + skill_status_change_start(&sd->bl,SkillStatusChangeTable[PR_MAGNIFICAT],1,0,0,0,skill_get_time(PR_MAGNIFICAT,1),0 ); + skill_status_change_start(&sd->bl,SkillStatusChangeTable[PR_GLORIA],1,0,0,0,skill_get_time(PR_GLORIA,1),0 ); + skill_status_change_start(&sd->bl,SkillStatusChangeTable[PR_SUFFRAGIUM],1,0,0,0,skill_get_time(PR_SUFFRAGIUM,1),0 ); + } + + clif_misceffect(&sd->bl,0); + //レベルアップしたのでパーティー情報を更新する + //(公平範囲チェック) + party_send_movemap(sd); + return 1; + } + + return 0; +} + +int pc_checkjoblevelup(struct map_session_data *sd) +{ + int next = pc_nextjobexp(sd); + + nullpo_retr(0, sd); + + if(sd->status.job_exp >= next && next > 0){ + // job側レベルアップ処理 + sd->status.job_exp -= next; + sd->status.job_level ++; + clif_updatestatus(sd,SP_JOBLEVEL); + clif_updatestatus(sd,SP_NEXTJOBEXP); + sd->status.skill_point ++; + clif_updatestatus(sd,SP_SKILLPOINT); + pc_calcstatus(sd,0); + + clif_misceffect(&sd->bl,1); + return 1; + } + + return 0; +} + +/*========================================== + * 経験値取得 + *------------------------------------------ + */ +int pc_gainexp(struct map_session_data *sd,int base_exp,int job_exp) +{ + char output[256]; + nullpo_retr(0, sd); + + if(sd->bl.prev == NULL || pc_isdead(sd)) + return 0; + + if((battle_config.pvp_exp == 0) && map[sd->bl.m].flag.pvp) // [MouseJstr] + return 0; // no exp on pvp maps + + if(sd->sc_data[SC_RICHMANKIM].timer != -1) { // added bounds checking [Vaalris] + base_exp += base_exp*(25 + sd->sc_data[SC_RICHMANKIM].val1*25)/100; + job_exp += job_exp*(25 + sd->sc_data[SC_RICHMANKIM].val1*25)/100; + } + + if(sd->status.guild_id>0){ // ギルドに上納 + base_exp-=guild_payexp(sd,base_exp); + if(base_exp < 0) + base_exp = 0; + } + + if(!battle_config.multi_level_up && pc_nextbaseafter(sd)) { + while(sd->status.base_exp+base_exp >=pc_nextbaseafter(sd) && sd->status.base_exp <= pc_nextbaseexp(sd) && pc_nextbaseafter(sd) > 0) { + base_exp*=.90; + } + } + + sd->status.base_exp += base_exp; + if(sd->status.base_exp < 0) + sd->status.base_exp = 0; + + while(pc_checkbaselevelup(sd)) ; + + clif_updatestatus(sd,SP_BASEEXP); + if(!battle_config.multi_level_up && pc_nextjobafter(sd)) { + while(sd->status.job_exp+job_exp >= pc_nextjobafter(sd) && sd->status.job_exp <= pc_nextjobexp(sd) && pc_nextjobafter(sd) > 0) { + job_exp*=.90; + } + } + + sd->status.job_exp += job_exp; + if(sd->status.job_exp < 0) + sd->status.job_exp = 0; + + while(pc_checkjoblevelup(sd)) ; + + clif_updatestatus(sd,SP_JOBEXP); + + if(battle_config.disp_experience){ + sprintf(output, + "Experienced Gained Base:%d Job:%d",base_exp,job_exp); + clif_disp_onlyself(sd,output,strlen(output)); + } + + return 0; +} + +/*========================================== + * base level側必要経験値計算 + *------------------------------------------ + */ +int pc_nextbaseexp(struct map_session_data *sd) +{ + int i; + + nullpo_retr(0, sd); + + if(sd->status.base_level>=MAX_LEVEL || sd->status.base_level<=0) + return 0; + + if(sd->status.class==0) i=0; + else if(sd->status.class<=6) i=1; + else if(sd->status.class<=22) i=2; + else if(sd->status.class==23) i=3; + else if(sd->status.class==4001) i=4; + else if(sd->status.class<=4007) i=5; + else i=6; + + return exp_table[i][sd->status.base_level-1]; +} + +/*========================================== + * job level側必要経験値計算 + *------------------------------------------ + */ +int pc_nextjobexp(struct map_session_data *sd) +{ + int i; + + nullpo_retr(0, sd); + + if(sd->status.job_level>=MAX_LEVEL || sd->status.job_level<=0) + return 0; + + if(sd->status.class==0) i=7; + else if(sd->status.class<=6) i=8; + else if(sd->status.class<=22) i=9; + else if(sd->status.class==23) i=10; + else if(sd->status.class==4001) i=11; + else if(sd->status.class<=4007) i=12; + else i=13; + + 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; + + if(sd->status.class==0) i=0; + else if(sd->status.class<=6) i=1; + else if(sd->status.class<=22) i=2; + else if(sd->status.class==23) i=3; + else if(sd->status.class==4001) i=4; + else if(sd->status.class<=4007) i=5; + else i=6; + + return exp_table[i][sd->status.base_level]; +} + +/*========================================== + * job level after next [Valaris] + *------------------------------------------ + */ +int pc_nextjobafter(struct map_session_data *sd) +{ + int i; + + nullpo_retr(0, sd); + + if(sd->status.job_level>=MAX_LEVEL || sd->status.job_level<=0) + return 0; + + if(sd->status.class==0) i=7; + else if(sd->status.class<=6) i=8; + else if(sd->status.class<=22) i=9; + else if(sd->status.class==23) i=10; + else if(sd->status.class==4001) i=11; + else if(sd->status.class<=4007) i=12; + else i=13; + + return exp_table[i][sd->status.job_level]; +} +/*========================================== + + * 必要ステータスポイント計算 + *------------------------------------------ + */ +int pc_need_status_point(struct map_session_data *sd,int type) +{ + int val; + + nullpo_retr(-1, sd); + + if(type<SP_STR || type>SP_LUK) + return -1; + val = + type==SP_STR ? sd->status.str : + type==SP_AGI ? sd->status.agi : + type==SP_VIT ? sd->status.vit : + type==SP_INT ? sd->status.int_: + type==SP_DEX ? sd->status.dex : sd->status.luk; + + return (val+9)/10+1; +} + +/*========================================== + * 能力値成長 + *------------------------------------------ + */ +int pc_statusup(struct map_session_data *sd,int type) +{ + int need,val = 0; + + nullpo_retr(0, sd); + + 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 >= battle_config.max_parameter) { + clif_statusupack(sd,type,0,0); + return 1; + } + val= ++sd->status.str; + break; + case SP_AGI: + if(sd->status.agi >= battle_config.max_parameter) { + clif_statusupack(sd,type,0,0); + return 1; + } + val= ++sd->status.agi; + break; + case SP_VIT: + if(sd->status.vit >= battle_config.max_parameter) { + clif_statusupack(sd,type,0,0); + return 1; + } + val= ++sd->status.vit; + break; + case SP_INT: + if(sd->status.int_ >= battle_config.max_parameter) { + clif_statusupack(sd,type,0,0); + return 1; + } + val= ++sd->status.int_; + break; + case SP_DEX: + if(sd->status.dex >= battle_config.max_parameter) { + clif_statusupack(sd,type,0,0); + return 1; + } + val= ++sd->status.dex; + break; + case SP_LUK: + if(sd->status.luk >= battle_config.max_parameter) { + 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); + pc_calcstatus(sd,0); + clif_statusupack(sd,type,1,val); + + return 0; +} + +/*========================================== + * 能力値成長 + *------------------------------------------ + */ +int pc_statusup2(struct map_session_data *sd,int type,int val) +{ + nullpo_retr(0, sd); + + if(type<SP_STR || type>SP_LUK){ + clif_statusupack(sd,type,0,0); + return 1; + } + switch(type){ + case SP_STR: + if(sd->status.str + val >= battle_config.max_parameter) + val = battle_config.max_parameter; + else if(sd->status.str + val < 1) + val = 1; + else + val += sd->status.str; + sd->status.str = val; + break; + case SP_AGI: + if(sd->status.agi + val >= battle_config.max_parameter) + val = battle_config.max_parameter; + else if(sd->status.agi + val < 1) + val = 1; + else + val += sd->status.agi; + sd->status.agi = val; + break; + case SP_VIT: + if(sd->status.vit + val >= battle_config.max_parameter) + val = battle_config.max_parameter; + else if(sd->status.vit + val < 1) + val = 1; + else + val += sd->status.vit; + sd->status.vit = val; + break; + case SP_INT: + if(sd->status.int_ + val >= battle_config.max_parameter) + val = battle_config.max_parameter; + else if(sd->status.int_ + val < 1) + val = 1; + else + val += sd->status.int_; + sd->status.int_ = val; + break; + case SP_DEX: + if(sd->status.dex + val >= battle_config.max_parameter) + val = battle_config.max_parameter; + else if(sd->status.dex + val < 1) + val = 1; + else + val += sd->status.dex; + sd->status.dex = val; + break; + case SP_LUK: + if(sd->status.luk + val >= battle_config.max_parameter) + val = battle_config.max_parameter; + else if(sd->status.luk + val < 1) + val = 1; + else + val = sd->status.luk + val; + sd->status.luk = val; + break; + } + clif_updatestatus(sd,type-SP_STR+SP_USTR); + clif_updatestatus(sd,type); + pc_calcstatus(sd,0); + clif_statusupack(sd,type,1,val); + + return 0; +} + +/*========================================== + * スキルポイント割り振り + *------------------------------------------ + */ +int pc_skillup(struct map_session_data *sd,int skill_num) +{ + nullpo_retr(0, sd); + + if( skill_num>=10000 ){ + guild_skillup(sd,skill_num); + 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) ) + { + sd->status.skill[skill_num].lv++; + sd->status.skill_point--; + pc_calcstatus(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; + int c=0, s=0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class; + + nullpo_retr(0, sd); + + s_class = pc_calc_base_job(sd->status.class); + c = s_class.job; + s = (s_class.upper==1) ? 1 : 0 ; //転生以外は通常のスキル? + + for(i=0;i<MAX_SKILL;i++){ + sd->status.skill[i].id=0; + if (sd->status.skill[i].flag && sd->status.skill[i].flag != 13){ // cardスキルなら、 + sd->status.skill[i].lv=(sd->status.skill[i].flag==1)?0:sd->status.skill[i].flag-2; // 本当のlvに + sd->status.skill[i].flag=0; // flagは0にしておく + } + } + + if (battle_config.gm_allskill > 0 && pc_isGM(sd) >= battle_config.gm_allskill){ + // 全てのスキル + for(i=1;i<158;i++) + sd->status.skill[i].lv=skill_get_max(i); + for(i=210;i<291;i++) + sd->status.skill[i].lv=skill_get_max(i); + for(i=304;i<MAX_SKILL;i++) + sd->status.skill[i].lv=skill_get_max(i); + } + else { + for(i=0;(id=skill_tree[s][c][i].id)>0;i++){ + if(sd->status.skill[id].id==0 && (!(skill_get_inf2(id)&0x01) || battle_config.quest_skill_learn) ) + sd->status.skill[id].lv=skill_get_max(id); + } + } + pc_calcstatus(sd,0); + + return 0; +} + +/*========================================== + * /resetlvl + *------------------------------------------ + */ +int pc_resetlvl(struct map_session_data* sd,int type) +{ + int i; + + nullpo_retr(0, sd); + + for(i=1;i<MAX_SKILL;i++){ + sd->status.skill[i].lv = 0; + } + + if(type == 1){ + sd->status.skill_point=0; + sd->status.base_level=1; + sd->status.job_level=1; + sd->status.base_exp=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 == 4001) + sd->status.status_point=100; + } + + if(type == 2){ + sd->status.skill_point=0; + sd->status.base_level=1; + sd->status.job_level=1; + sd->status.base_exp=0; + sd->status.job_exp=0; + } + if(type == 3){ + sd->status.base_level=1; + sd->status.base_exp=0; + } + if(type == 4){ + sd->status.job_level=1; + sd->status.job_exp=0; + } + + clif_updatestatus(sd,SP_STATUSPOINT); + clif_updatestatus(sd,SP_STR); + clif_updatestatus(sd,SP_AGI); + clif_updatestatus(sd,SP_VIT); + clif_updatestatus(sd,SP_INT); + clif_updatestatus(sd,SP_DEX); + clif_updatestatus(sd,SP_LUK); + clif_updatestatus(sd,SP_BASELEVEL); + clif_updatestatus(sd,SP_JOBLEVEL); + clif_updatestatus(sd,SP_STATUSPOINT); + clif_updatestatus(sd,SP_NEXTBASEEXP); + clif_updatestatus(sd,SP_NEXTJOBEXP); + clif_updatestatus(sd,SP_SKILLPOINT); + + clif_updatestatus(sd,SP_USTR); // Updates needed stat points - Valaris + clif_updatestatus(sd,SP_UAGI); + clif_updatestatus(sd,SP_UVIT); + clif_updatestatus(sd,SP_UINT); + clif_updatestatus(sd,SP_UDEX); + clif_updatestatus(sd,SP_ULUK); // End Addition + + for(i=0;i<11;i++) { // unequip items that can't be equipped by base 1 [Valaris] + if(sd->equip_index[i] >= 0) + if(!pc_isequip(sd,sd->equip_index[i])) + pc_unequipitem(sd,sd->equip_index[i],1); + } + + clif_skillinfoblock(sd); + pc_calcstatus(sd,0); + + return 0; +} +/*========================================== + * /resetstate + *------------------------------------------ + */ +int pc_resetstate(struct map_session_data* sd) +{ + #define sumsp(a) ((a)*((a-2)/10+2) - 5*((a-2)/10)*((a-2)/10) - 6*((a-2)/10) -2) +// int add=0; // Removed by Dexity + + nullpo_retr(0, sd); + +// New statpoint table used here - Dexity + sd->status.status_point = atoi (statp[sd->status.base_level - 1]); +// End addition + +// Removed by Dexity - old count +// add += sumsp(sd->status.str); +// add += sumsp(sd->status.agi); +// add += sumsp(sd->status.vit); +// add += sumsp(sd->status.int_); +// add += sumsp(sd->status.dex); +// add += sumsp(sd->status.luk); +// sd->status.status_point+=add; + + clif_updatestatus(sd,SP_STATUSPOINT); + + sd->status.str=1; + sd->status.agi=1; + sd->status.vit=1; + sd->status.int_=1; + sd->status.dex=1; + sd->status.luk=1; + + clif_updatestatus(sd,SP_STR); + clif_updatestatus(sd,SP_AGI); + clif_updatestatus(sd,SP_VIT); + clif_updatestatus(sd,SP_INT); + clif_updatestatus(sd,SP_DEX); + clif_updatestatus(sd,SP_LUK); + + clif_updatestatus(sd,SP_USTR); // Updates needed stat points - Valaris + clif_updatestatus(sd,SP_UAGI); + clif_updatestatus(sd,SP_UVIT); + clif_updatestatus(sd,SP_UINT); + clif_updatestatus(sd,SP_UDEX); + clif_updatestatus(sd,SP_ULUK); // End Addition + + pc_calcstatus(sd,0); + + return 0; +} + +/*========================================== + * /resetskill + *------------------------------------------ + */ +int pc_resetskill(struct map_session_data* sd) +{ + int i,skill; + + nullpo_retr(0, sd); + + for(i=1;i<MAX_SKILL;i++){ + if( (skill = pc_checkskill(sd,i)) > 0) { + if(!(skill_get_inf2(i)&0x01) || battle_config.quest_skill_learn) { + 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) + 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); + pc_calcstatus(sd,0); + + return 0; +} + +/*========================================== + * pcにダメージを与える + *------------------------------------------ + */ +int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) +{ + int i=0,j=0; + struct pc_base_job s_class; + + nullpo_retr(0, sd); + + //転生や養子の場合の元の職業を算出する + s_class = pc_calc_base_job(sd->status.class); + // 既に死んでいたら無効 + if(pc_isdead(sd)) + return 0; + // 座ってたら立ち上がる + if(pc_issit(sd)) { + pc_setstand(sd); + skill_gangsterparadise(sd,0); + } + + // 歩 いていたら足を止める + if(sd->sc_data[SC_ENDURE].timer == -1 && !sd->special_state.infinite_endure) + pc_stop_walking(sd,3); + // 演奏/ダンスの中断 + if(damage > sd->status.max_hp>>2) + skill_stop_dancing(&sd->bl,0); + + sd->status.hp-=damage; + if(sd->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) + skill_status_change_end(&sd->bl, SC_TRICKDEAD, -1); + if(sd->status.option&2) + skill_status_change_end(&sd->bl, SC_HIDING, -1); + if(sd->status.option&4) + skill_status_change_end(&sd->bl, SC_CLOAKING, -1); + if(sd->status.option&16386) + skill_status_change_end(&sd->bl, SC_CHASEWALK, -1); + + if(sd->status.hp>0){ + // まだ生きているならHP更新 + clif_updatestatus(sd,SP_HP); + + if(sd->status.hp<sd->status.max_hp>>2 && pc_checkskill(sd,SM_AUTOBERSERK)>0 && + (sd->sc_data[SC_PROVOKE].timer==-1 || sd->sc_data[SC_PROVOKE].val2==0 )) + // オートバーサーク発動 + skill_status_change_start(&sd->bl,SC_PROVOKE,10,1,0,0,0,0); + + sd->canlog_tick = gettick(); + + if(sd->status.party_id>0) { // on-the-fly party hp updates [Valaris] + struct party *p=party_search(sd->status.party_id); + if(p!=NULL) clif_party_hp(p,sd); + } // end addition [Valaris] + + return 0; + } + sd->status.hp = 0; + pc_setdead(sd); + if(sd->vender_id) + vending_closevending(sd); + + if(sd->status.pet_id > 0 && sd->pd) { + 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); + } + } + + pc_stop_walking(sd,0); + skill_castcancel(&sd->bl,0); // 詠唱の中止 + clif_clearchar_area(&sd->bl,1); + skill_unit_out_all(&sd->bl,gettick(),1); + if(sd->sc_data[SC_BLADESTOP].timer!=-1)//白刃は事前に解除 + skill_status_change_end(&sd->bl,SC_BLADESTOP,-1); + pc_setglobalreg(sd,"PC_DIE_COUNTER",++sd->die_counter); //死にカウンター書き込み + skill_status_change_clear(&sd->bl,0); // ステータス異常を解除する + clif_updatestatus(sd,SP_HP); + pc_calcstatus(sd,0); + + for(i=0;i<5;i++) + if(sd->dev.val1[i]){ + skill_status_change_end(&map_id2sd(sd->dev.val1[i])->bl,SC_DEVOTION,-1); + sd->dev.val1[i] = sd->dev.val2[i]=0; + } + + if(battle_config.death_penalty_type>0) { // changed penalty options, added death by player if pk_mode [Valaris] + if(sd->status.class != 0 && !map[sd->bl.m].flag.nopenalty && !map[sd->bl.m].flag.gvg){ // only novices will recieve no penalty + if(battle_config.death_penalty_type==1 && battle_config.death_penalty_base > 0) + sd->status.base_exp -= (double)pc_nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000; + if(battle_config.pk_mode && src && src->type==BL_PC) + sd->status.base_exp -= (double)pc_nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000; + else if(battle_config.death_penalty_type==2 && battle_config.death_penalty_base > 0) { + if(pc_nextbaseexp(sd) > 0) + sd->status.base_exp -= (double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000; + if(battle_config.pk_mode && src && src->type==BL_PC) + sd->status.base_exp -= (double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000; + } + if(sd->status.base_exp < 0) + sd->status.base_exp = 0; + clif_updatestatus(sd,SP_BASEEXP); + + if(battle_config.death_penalty_type==1 && battle_config.death_penalty_job > 0) + sd->status.job_exp -= (double)pc_nextjobexp(sd) * (double)battle_config.death_penalty_job/10000; + if(battle_config.pk_mode && src && src->type==BL_PC) + sd->status.job_exp -= (double)pc_nextjobexp(sd) * (double)battle_config.death_penalty_job/10000; + else if(battle_config.death_penalty_type==2 && battle_config.death_penalty_job > 0) { + if(pc_nextjobexp(sd) > 0) + sd->status.job_exp -= (double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000; + if(battle_config.pk_mode && src && src->type==BL_PC) + sd->status.job_exp -= (double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000; + } + if(sd->status.job_exp < 0) + sd->status.job_exp = 0; + clif_updatestatus(sd,SP_JOBEXP); + } + } + //ナイトメアモードアイテムドロップ + if(map[sd->bl.m].flag.pvp_nightmaredrop){ // Moved this outside so it works when PVP isnt enabled and during pk mode [Ancyker] + for(j=0;j<MAX_DROP_PER_MAP;j++){ + int id = map[sd->bl.m].drop_list[j].drop_id; + int type = map[sd->bl.m].drop_list[j].drop_type; + int per = map[sd->bl.m].drop_list[j].drop_per; + if(id == 0) + continue; + if(id == -1){//ランダムドロップ + int eq_num=0,eq_n[MAX_INVENTORY]; + memset(eq_n,0,sizeof(eq_n)); + //先ず装備しているアイテム数をカウント + for(i=0;i<MAX_INVENTORY;i++){ + int k; + if( (type == 1 && !sd->status.inventory[i].equip) + || (type == 2 && sd->status.inventory[i].equip) + || type == 3){ + //InventoryIndexを格納 + for(k=0;k<MAX_INVENTORY;k++){ + if(eq_n[k] <= 0){ + eq_n[k]=i; + break; + } + } + eq_num++; + } + } + if(eq_num > 0){ + int n = eq_n[rand()%eq_num];//該当アイテムの中からランダム + if(rand()%10000 < per){ + if(sd->status.inventory[n].equip) + pc_unequipitem(sd,n,0); + pc_dropitem(sd,n,1); + } + } + } + else if(id > 0){ + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid == id//ItemIDが一致していて + && rand()%10000 < per//ドロップ率判定もOKで + && ((type == 1 && !sd->status.inventory[i].equip)//タイプ判定もOKならドロップ + || (type == 2 && sd->status.inventory[i].equip) + || type == 3) ){ + if(sd->status.inventory[i].equip) + pc_unequipitem(sd,i,0); + pc_dropitem(sd,i,1); + break; + } + } + } + } + } + // pvp + if( map[sd->bl.m].flag.pvp && !battle_config.pk_mode){ // disable certain pvp functions on pk_mode [Valaris] + //ランキング計算 + if(!map[sd->bl.m].flag.pvp_nocalcrank){ + sd->pvp_point-=5; + if(src && src->type==BL_PC ) + ((struct map_session_data *)src)->pvp_point++; + //} //fixed wrong '{' placement by Lupus + pc_setdead(sd); + } + // 強制送還 + if( sd->pvp_point < 0 ){ + sd->pvp_point=0; + pc_setstand(sd); + pc_setrestartvalue(sd,3); + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,0); + } + } + //GvG + if(map[sd->bl.m].flag.gvg){ + pc_setstand(sd); + pc_setrestartvalue(sd,3); + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,0); + } + + return 0; +} + +// +// script関 連 +// +/*========================================== + * script用PCステータス読み出し + *------------------------------------------ + */ +int pc_readparam(struct map_session_data *sd,int type) +{ + int val=0; + struct pc_base_job s_class; + + s_class = pc_calc_base_job(sd->status.class); + + nullpo_retr(0, sd); + + switch(type){ + case SP_SKILLPOINT: + val= sd->status.skill_point; + break; + case SP_STATUSPOINT: + val= sd->status.status_point; + break; + case SP_ZENY: + val= sd->status.zeny; + break; + case SP_BASELEVEL: + val= sd->status.base_level; + break; + case SP_JOBLEVEL: + val= sd->status.job_level; + break; + case SP_CLASS: + if(val>=24 && val < 45) + val+=3978; + else + val= sd->status.class; + break; + case SP_UPPER: + val= s_class.upper; + break; + case SP_SEX: + val= sd->sex; + break; + case SP_WEIGHT: + val= sd->weight; + break; + case SP_MAXWEIGHT: + val= sd->max_weight; + break; + case SP_BASEEXP: + val= sd->status.base_exp; + break; + case SP_JOBEXP: + val= sd->status.job_exp; + break; + case SP_NEXTBASEEXP: + val= pc_nextbaseexp(sd); + break; + case SP_NEXTJOBEXP: + val= pc_nextjobexp(sd); + break; + case SP_HP: + val= sd->status.hp; + break; + case SP_MAXHP: + val= sd->status.max_hp; + break; + case SP_SP: + val= sd->status.sp; + break; + case SP_MAXSP: + val= sd->status.max_sp; + break; + case SP_STR: + val= sd->status.str; + break; + case SP_AGI: + val= sd->status.agi; + break; + case SP_VIT: + val= sd->status.vit; + break; + case SP_INT: + val= sd->status.int_; + break; + case SP_DEX: + val= sd->status.dex; + break; + case SP_LUK: + val= sd->status.luk; + break; + case SP_FAME: + val= sd->fame; + break; + } + + return val; +} + +/*========================================== + * script用PCステータス設定 + *------------------------------------------ + */ +int pc_setparam(struct map_session_data *sd,int type,int val) +{ + int i = 0,up_level = 50; + struct pc_base_job s_class; + + nullpo_retr(0, sd); + + s_class = pc_calc_base_job(sd->status.class); + + switch(type){ + case SP_BASELEVEL: + if (val > sd->status.base_level) { + for (i = 1; i <= (val - sd->status.base_level); i++) + sd->status.status_point += (sd->status.base_level + i + 14) / 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); + pc_calcstatus(sd, 0); + pc_heal(sd, sd->status.max_hp, sd->status.max_sp); + break; + case SP_JOBLEVEL: + if (sd->status.class == 0) + up_level -= 40; + if ((sd->status.class == 23) || (sd->status.class >= 4001 && sd->status.class <= 4022)) + up_level += 20; + if (val >= sd->status.job_level) { + if (val > up_level)val = up_level; + sd->status.skill_point += (val-sd->status.job_level); + sd->status.job_level = val; + sd->status.job_exp = 0; + clif_updatestatus(sd, SP_JOBLEVEL); + clif_updatestatus(sd, SP_NEXTJOBEXP); + clif_updatestatus(sd, SP_JOBEXP); + clif_updatestatus(sd, SP_SKILLPOINT); + pc_calcstatus(sd, 0); + clif_misceffect(&sd->bl, 1); + } else { + sd->status.job_level = val; + sd->status.job_exp = 0; + clif_updatestatus(sd, SP_JOBLEVEL); + clif_updatestatus(sd, SP_NEXTJOBEXP); + clif_updatestatus(sd, SP_JOBEXP); + pc_calcstatus(sd, 0); + } + clif_updatestatus(sd,type); + break; + case SP_SKILLPOINT: + sd->status.skill_point = val; + break; + case SP_STATUSPOINT: + sd->status.status_point = val; + break; + case SP_ZENY: + sd->status.zeny = val; + break; + case SP_BASEEXP: + if(pc_nextbaseexp(sd) > 0) { + sd->status.base_exp = val; + if(sd->status.base_exp < 0) + sd->status.base_exp=0; + pc_checkbaselevelup(sd); + } + break; + case SP_JOBEXP: + if(pc_nextjobexp(sd) > 0) { + sd->status.job_exp = val; + if(sd->status.job_exp < 0) + sd->status.job_exp=0; + pc_checkjoblevelup(sd); + } + break; + case SP_SEX: + sd->sex = val; + break; + case SP_WEIGHT: + sd->weight = val; + break; + case SP_MAXWEIGHT: + sd->max_weight = val; + break; + case SP_HP: + sd->status.hp = val; + break; + case SP_MAXHP: + sd->status.max_hp = val; + break; + case SP_SP: + sd->status.sp = val; + break; + case SP_MAXSP: + sd->status.max_sp = val; + break; + case SP_STR: + sd->status.str = val; + break; + case SP_AGI: + sd->status.agi = val; + break; + case SP_VIT: + sd->status.vit = val; + break; + case SP_INT: + sd->status.int_ = val; + break; + case SP_DEX: + sd->status.dex = val; + break; + case SP_LUK: + sd->status.luk = val; + break; + case SP_FAME: + sd->fame = val; + break; + } + clif_updatestatus(sd,type); + + return 0; +} + +/*========================================== + * HP/SP回復 + *------------------------------------------ + */ +int pc_heal(struct map_session_data *sd,int hp,int sp) +{ +// if(battle_config.battle_log) +// printf("heal %d %d\n",hp,sp); + + nullpo_retr(0, sd); + + if(pc_checkoverhp(sd)) { + if(hp > 0) + hp = 0; + } + if(pc_checkoversp(sd)) { + if(sp > 0) + sp = 0; + } + + if(sd->sc_data && sd->sc_data[SC_BERSERK].timer!=-1) //バーサーク中は回復させないらしい + return 0; + + if(hp+sd->status.hp>sd->status.max_hp) + hp=sd->status.max_hp-sd->status.hp; + if(sp+sd->status.sp>sd->status.max_sp) + sp=sd->status.max_sp-sd->status.sp; + sd->status.hp+=hp; + if(sd->status.hp <= 0) { + sd->status.hp = 0; + pc_damage(NULL,sd,1); + hp = 0; + } + sd->status.sp+=sp; + if(sd->status.sp <= 0) + sd->status.sp = 0; + if(hp) + clif_updatestatus(sd,SP_HP); + if(sp) + clif_updatestatus(sd,SP_SP); + + if(sd->status.party_id>0) { // on-the-fly party hp updates [Valaris] + struct party *p=party_search(sd->status.party_id); + if(p!=NULL) clif_party_hp(p,sd); + } // end addition [Valaris] + + return hp + sp; +} + +/*========================================== + * HP/SP回復 + *------------------------------------------ + */ +int pc_itemheal(struct map_session_data *sd,int hp,int sp) +{ + int bonus; +// if(battle_config.battle_log) +// printf("heal %d %d\n",hp,sp); + + nullpo_retr(0, sd); + + if(sd->sc_data && sd->sc_data[SC_GOSPEL].timer!=-1) //バーサーク中は回復させないらしい + return 0; + + if(sd->state.potionpitcher_flag) { + sd->potion_hp = hp; + sd->potion_sp = sp; + return 0; + } + + if(pc_checkoverhp(sd)) { + if(hp > 0) + hp = 0; + } + if(pc_checkoversp(sd)) { + if(sp > 0) + sp = 0; + } + if(hp > 0) { + bonus = (sd->paramc[2]<<1) + 100 + pc_checkskill(sd,SM_RECOVERY)*10; + if(bonus != 100) + hp = hp * bonus / 100; + bonus = 100 + pc_checkskill(sd,AM_LEARNINGPOTION)*5; + if(bonus != 100) + hp = hp * bonus / 100; + } + if(sp > 0) { + bonus = (sd->paramc[3]<<1) + 100 + pc_checkskill(sd,MG_SRECOVERY)*10; + if(bonus != 100) + sp = sp * bonus / 100; + bonus = 100 + pc_checkskill(sd,AM_LEARNINGPOTION)*5; + if(bonus != 100) + sp = sp * bonus / 100; + } + if(hp+sd->status.hp>sd->status.max_hp) + hp=sd->status.max_hp-sd->status.hp; + if(sp+sd->status.sp>sd->status.max_sp) + sp=sd->status.max_sp-sd->status.sp; + sd->status.hp+=hp; + if(sd->status.hp <= 0) { + sd->status.hp = 0; + pc_damage(NULL,sd,1); + hp = 0; + } + sd->status.sp+=sp; + if(sd->status.sp <= 0) + sd->status.sp = 0; + if(hp) + clif_updatestatus(sd,SP_HP); + if(sp) + clif_updatestatus(sd,SP_SP); + + return 0; +} + +/*========================================== + * HP/SP回復 + *------------------------------------------ + */ +int pc_percentheal(struct map_session_data *sd,int hp,int sp) +{ + nullpo_retr(0, sd); + + if(sd->state.potionpitcher_flag) { + sd->potion_per_hp = hp; + sd->potion_per_sp = sp; + return 0; + } + + if(pc_checkoverhp(sd)) { + if(hp > 0) + hp = 0; + } + if(pc_checkoversp(sd)) { + if(sp > 0) + sp = 0; + } + if(hp) { + if(hp >= 100) { + sd->status.hp = sd->status.max_hp; + } + else if(hp <= -100) { + sd->status.hp = 0; + pc_damage(NULL,sd,1); + } + else { + sd->status.hp += sd->status.max_hp*hp/100; + if(sd->status.hp > sd->status.max_hp) + sd->status.hp = sd->status.max_hp; + if(sd->status.hp <= 0) { + sd->status.hp = 0; + pc_damage(NULL,sd,1); + hp = 0; + } + } + } + if(sp) { + if(sp >= 100) { + sd->status.sp = sd->status.max_sp; + } + else if(sp <= -100) { + sd->status.sp = 0; + } + else { + sd->status.sp += sd->status.max_sp*sp/100; + if(sd->status.sp > sd->status.max_sp) + sd->status.sp = sd->status.max_sp; + if(sd->status.sp < 0) + sd->status.sp = 0; + } + } + if(hp) + clif_updatestatus(sd,SP_HP); + if(sp) + clif_updatestatus(sd,SP_SP); + + return 0; +} + +/*========================================== + * 職変更 + * 引数 job 職業 0〜23 + * upper 通常 0, 転生 1, 養子 2, そのまま -1 + *------------------------------------------ + */ +int pc_jobchange(struct map_session_data *sd,int job, int upper) +{ + int i; + int b_class = 0; + //転生や養子の場合の元の職業を算出する + struct pc_base_job s_class = pc_calc_base_job(sd->status.class); + + nullpo_retr(0, sd); + + if((job > 23) && (job < 68)) + job += 3977; + + if((job > 69) && (job < 4000)) + return 1; + + if(upper < 0) //現在転生かどうかを判断する + upper = s_class.upper; + + if(upper == 0){ //通常職ならjobそのまんま + b_class = job; + }else if(upper == 1){ + if(job == 23){ //転生にスパノビは存在しないのでお断り + return 1; + }else{ + b_class = job + 4001; + } + }else if(upper == 2){ //養子に結婚はないけどどうせ次で蹴られるからいいや + b_class = (job==23)?job + 4022:job + 4023; + }else{ + return 1; + } + + if((sd->status.sex == 0 && job == 19) || (sd->status.sex == 1 && job == 20) || + (sd->status.sex == 0 && job == 4020) || (sd->status.sex == 1 && job == 4021) || + job ==22 || sd->status.class == b_class) //♀はバードになれない、♂はダンサーになれない、結婚衣裳もお断り + return 1; + + sd->status.class = sd->view_class = b_class; + + sd->status.job_level=1; + sd->status.job_exp=0; + clif_updatestatus(sd,SP_JOBLEVEL); + clif_updatestatus(sd,SP_JOBEXP); + clif_updatestatus(sd,SP_NEXTJOBEXP); + + for(i=0;i<11;i++) { + if(sd->equip_index[i] >= 0) + if(!pc_isequip(sd,sd->equip_index[i])) + pc_unequipitem(sd,sd->equip_index[i],1); // 装備外し + } + + clif_changelook(&sd->bl,LOOK_BASE,sd->view_class); // move sprite update to prevent client crashes with incompatible equipment [Valaris] + if(sd->status.clothes_color > 0) + clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->status.clothes_color); + if(battle_config.muting_players && sd->status.manner < 0) + clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner); + + pc_calcstatus(sd,0); + pc_checkallowskill(sd); + pc_equiplookall(sd); + clif_equiplist(sd); + + if(pc_isriding(sd)) { // remove peco status if changing into invalid class [Valaris] + if(!(pc_checkskill(sd,KN_RIDING))) + pc_setoption(sd,sd->status.option|-0x0000); + if(pc_checkskill(sd,KN_RIDING)>0) + pc_setriding(sd); + } + + return 0; +} + +/*========================================== + * 見た目変更 + *------------------------------------------ + */ +int pc_equiplookall(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + +#if PACKETVER < 4 + clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield); +#else + clif_changelook(&sd->bl,LOOK_WEAPON,0); + clif_changelook(&sd->bl,LOOK_SHOES,0); +#endif + clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); + clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); + clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); + + return 0; +} + +/*========================================== + * 見た目変更 + *------------------------------------------ + */ +int pc_changelook(struct map_session_data *sd,int type,int val) +{ + nullpo_retr(0, sd); + + switch(type){ + case LOOK_HAIR: + sd->status.hair=val; + break; + case LOOK_WEAPON: + sd->status.weapon=val; + break; + case LOOK_HEAD_BOTTOM: + sd->status.head_bottom=val; + break; + case LOOK_HEAD_TOP: + sd->status.head_top=val; + break; + case LOOK_HEAD_MID: + sd->status.head_mid=val; + break; + case LOOK_HAIR_COLOR: + sd->status.hair_color=val; + break; + case LOOK_CLOTHES_COLOR: + sd->status.clothes_color=val; + break; + case LOOK_SHIELD: + sd->status.shield=val; + break; + case LOOK_SHOES: + break; + } + clif_changelook(&sd->bl,type,val); + + return 0; +} + +/*========================================== + * 付属品(鷹,ペコ,カート)設定 + *------------------------------------------ + */ +int pc_setoption(struct map_session_data *sd,int type) +{ + nullpo_retr(0, sd); + + sd->status.option=type; + clif_changeoption(&sd->bl); + pc_calcstatus(sd,0); + + return 0; +} + +/*========================================== + * カート設定 + *------------------------------------------ + */ +int pc_setcart(struct map_session_data *sd,int type) +{ + int cart[6]={0x0000,0x0008,0x0080,0x0100,0x0200,0x0400}; + + nullpo_retr(0, sd); + + if(pc_checkskill(sd,MC_PUSHCART)>0){ // プッシュカートスキル所持 + if(!pc_iscarton(sd)){ // カートを付けていない + pc_setoption(sd,cart[type]); + clif_cart_itemlist(sd); + clif_cart_equiplist(sd); + clif_updatestatus(sd,SP_CARTINFO); + clif_status_change(&sd->bl,0x0c,0); + } + else{ + pc_setoption(sd,cart[type]); + } + } + + return 0; +} + +/*========================================== + * 鷹設定 + *------------------------------------------ + */ +int pc_setfalcon(struct map_session_data *sd) +{ + if(pc_checkskill(sd,HT_FALCON)>0){ // ファルコンマスタリースキル所持 + pc_setoption(sd,sd->status.option|0x0010); + } + + return 0; +} + +/*========================================== + * ペコペコ設定 + *------------------------------------------ + */ +int pc_setriding(struct map_session_data *sd) +{ + if(sd->disguise > 0) { // temporary prevention of crash caused by peco + disguise, will look into a better solution [Valaris] + clif_displaymessage(sd->fd, "Cannot mount a Peco while in disguise."); + return 0; + } + + if((pc_checkskill(sd,KN_RIDING)>0)){ // ライディングスキル所持 + pc_setoption(sd,sd->status.option|0x0020); + + if(sd->status.class==7) + sd->status.class=sd->view_class=13; + + if(sd->status.class==14) + sd->status.class=sd->view_class=21; + + if(sd->status.class==4008) + sd->status.class=sd->view_class=4014; + + if(sd->status.class==4015) + sd->status.class=sd->view_class=4022; + } + + return 0; +} + +/*========================================== + * script用変数の値を読む + *------------------------------------------ + */ +int pc_readreg(struct map_session_data *sd,int reg) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<sd->reg_num;i++) + if(sd->reg[i].index==reg) + return sd->reg[i].data; + + return 0; +} +/*========================================== + * script用変数の値を設定 + *------------------------------------------ + */ +int pc_setreg(struct map_session_data *sd,int reg,int val) +{ + int i; + + nullpo_retr(0, sd); + + for (i = 0; i < sd->reg_num; i++) { + if (sd->reg[i].index == reg){ + sd->reg[i].data = val; + return 0; + } + } + sd->reg_num++; + sd->reg = realloc(sd->reg, sizeof(*(sd->reg)) * sd->reg_num); + if (sd->reg == NULL){ + printf("out of memory : pc_setreg\n"); + exit(1); + } +/* memset(sd->reg + (sd->reg_num - 1) * sizeof(*(sd->reg)), 0, + sizeof(*(sd->reg))); +*/ + sd->reg[i].index = reg; + sd->reg[i].data = val; + + return 0; +} + +/*========================================== + * script用文字列変数の値を読む + *------------------------------------------ + */ +char *pc_readregstr(struct map_session_data *sd,int reg) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<sd->regstr_num;i++) + if(sd->regstr[i].index==reg) + return sd->regstr[i].data; + + return NULL; +} +/*========================================== + * script用文字列変数の値を設定 + *------------------------------------------ + */ +int pc_setregstr(struct map_session_data *sd,int reg,char *str) +{ + int i; + + nullpo_retr(0, sd); + + if(strlen(str)+1 >= sizeof(sd->regstr[0].data)){ + printf("pc_setregstr: string too long !\n"); + return 0; + } + + for(i=0;i<sd->regstr_num;i++) + if(sd->regstr[i].index==reg){ + strcpy(sd->regstr[i].data,str); + return 0; + } + sd->regstr_num++; + sd->regstr = realloc(sd->regstr, sizeof(sd->regstr[0]) * sd->regstr_num); + if(sd->regstr==NULL){ + printf("out of memory : pc_setreg\n"); + exit(1); + } +/* memset(sd->reg + (sd->reg_num - 1) * sizeof(*(sd->reg)), 0, + sizeof(*(sd->reg))); +*/ + sd->regstr[i].index=reg; + strcpy(sd->regstr[i].data,str); + + return 0; +} + +/*========================================== + * script用グローバル変数の値を読む + *------------------------------------------ + */ +int pc_readglobalreg(struct map_session_data *sd,char *reg) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<sd->status.global_reg_num;i++){ + if(strcmp(sd->status.global_reg[i].str,reg)==0) + return sd->status.global_reg[i].value; + } + + return 0; +} + +/*========================================== + * script用グローバル変数の値を設定 + *------------------------------------------ + */ +int pc_setglobalreg(struct map_session_data *sd,char *reg,int val) +{ + int i; + + nullpo_retr(0, sd); + + //PC_DIE_COUNTERがスクリプトなどで変更された時の処理 + if(strcmp(reg,"PC_DIE_COUNTER") == 0 && sd->die_counter != val){ + sd->die_counter = val; + pc_calcstatus(sd,0); + } + if(val==0){ + for(i=0;i<sd->status.global_reg_num;i++){ + if(strcmp(sd->status.global_reg[i].str,reg)==0){ + sd->status.global_reg[i]=sd->status.global_reg[sd->status.global_reg_num-1]; + sd->status.global_reg_num--; + break; + } + } + return 0; + } + for(i=0;i<sd->status.global_reg_num;i++){ + if(strcmp(sd->status.global_reg[i].str,reg)==0){ + sd->status.global_reg[i].value=val; + return 0; + } + } + if(sd->status.global_reg_num<GLOBAL_REG_NUM){ + strcpy(sd->status.global_reg[i].str,reg); + sd->status.global_reg[i].value=val; + sd->status.global_reg_num++; + return 0; + } + if(battle_config.error_log) + printf("pc_setglobalreg : couldn't set %s (GLOBAL_REG_NUM = %d)\n", reg, GLOBAL_REG_NUM); + + return 1; +} + +/*========================================== + * script用アカウント変数の値を読む + *------------------------------------------ + */ +int pc_readaccountreg(struct map_session_data *sd,char *reg) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<sd->status.account_reg_num;i++){ + if(strcmp(sd->status.account_reg[i].str,reg)==0) + return sd->status.account_reg[i].value; + } + + return 0; +} +/*========================================== + * script用アカウント変数の値を設定 + *------------------------------------------ + */ +int pc_setaccountreg(struct map_session_data *sd,char *reg,int val) +{ + int i; + + nullpo_retr(0, sd); + + if(val==0){ + for(i=0;i<sd->status.account_reg_num;i++){ + if(strcmp(sd->status.account_reg[i].str,reg)==0){ + sd->status.account_reg[i]=sd->status.account_reg[sd->status.account_reg_num-1]; + sd->status.account_reg_num--; + break; + } + } + intif_saveaccountreg(sd); + return 0; + } + for(i=0;i<sd->status.account_reg_num;i++){ + if(strcmp(sd->status.account_reg[i].str,reg)==0){ + sd->status.account_reg[i].value=val; + intif_saveaccountreg(sd); + return 0; + } + } + if(sd->status.account_reg_num<ACCOUNT_REG_NUM){ + strcpy(sd->status.account_reg[i].str,reg); + sd->status.account_reg[i].value=val; + sd->status.account_reg_num++; + intif_saveaccountreg(sd); + return 0; + } + if(battle_config.error_log) + printf("pc_setaccountreg : couldn't set %s (ACCOUNT_REG_NUM = %d)\n", reg, ACCOUNT_REG_NUM); + + return 1; +} +/*========================================== + * script用アカウント変数2の値を読む + *------------------------------------------ + */ +int pc_readaccountreg2(struct map_session_data *sd,char *reg) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<sd->status.account_reg2_num;i++){ + if(strcmp(sd->status.account_reg2[i].str,reg)==0) + return sd->status.account_reg2[i].value; + } + + return 0; +} +/*========================================== + * script用アカウント変数2の値を設定 + *------------------------------------------ + */ +int pc_setaccountreg2(struct map_session_data *sd,char *reg,int val) +{ + int i; + + nullpo_retr(1, sd); + + if(val==0){ + for(i=0;i<sd->status.account_reg2_num;i++){ + if(strcmp(sd->status.account_reg2[i].str,reg)==0){ + sd->status.account_reg2[i]=sd->status.account_reg2[sd->status.account_reg2_num-1]; + sd->status.account_reg2_num--; + break; + } + } + chrif_saveaccountreg2(sd); + return 0; + } + for(i=0;i<sd->status.account_reg2_num;i++){ + if(strcmp(sd->status.account_reg2[i].str,reg)==0){ + sd->status.account_reg2[i].value=val; + chrif_saveaccountreg2(sd); + return 0; + } + } + if(sd->status.account_reg2_num<ACCOUNT_REG2_NUM){ + strcpy(sd->status.account_reg2[i].str,reg); + sd->status.account_reg2[i].value=val; + sd->status.account_reg2_num++; + chrif_saveaccountreg2(sd); + return 0; + } + if(battle_config.error_log) + printf("pc_setaccountreg2 : couldn't set %s (ACCOUNT_REG2_NUM = %d)\n", reg, ACCOUNT_REG2_NUM); + + return 1; +} +/*========================================== + * 精錬成功率 + *------------------------------------------ + */ +int pc_percentrefinery(struct map_session_data *sd,struct item *item) +{ + int percent; + + nullpo_retr(0, item); + percent=percentrefinery[itemdb_wlv(item->nameid)][(int)item->refine]; + + percent += pc_checkskill(sd,BS_WEAPONRESEARCH); // 武器研究スキル所持 + + // 確率の有効範囲チェック + if( percent > 100 ){ + percent = 100; + } + if( percent < 0 ){ + percent = 0; + } + + return percent; +} + +/*========================================== + * イベントタイマー処理 + *------------------------------------------ + */ +int pc_eventtimer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd=map_id2sd(id); + 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,(const char *)data,0); + break; + } + } + free((void *)data); + if(i==MAX_EVENTTIMER) { + if(battle_config.error_log) + printf("pc_eventtimer: no such event timer\n"); + } + + return 0; +} + +/*========================================== + * イベントタイマー追加 + *------------------------------------------ + */ +int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<MAX_EVENTTIMER;i++) + if( sd->eventtimer[i]==-1 ) + break; + if(i<MAX_EVENTTIMER){ + char *evname=(char *)aCalloc(24,sizeof(char)); + memcpy(evname,name,24); + sd->eventtimer[i]=add_timer(gettick()+tick, + pc_eventtimer,sd->bl.id,(int)evname); + } + + return 0; +} + +/*========================================== + * イベントタイマー削除 + *------------------------------------------ + */ +int pc_deleventtimer(struct map_session_data *sd,const char *name) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<MAX_EVENTTIMER;i++) + if( sd->eventtimer[i]!=-1 && strcmp( + (char *)(get_timer(sd->eventtimer[i])->data), name)==0 ){ + delete_timer(sd->eventtimer[i],pc_eventtimer); + sd->eventtimer[i]=-1; + break; + } + + return 0; +} + +/*========================================== + * イベントタイマーカウント値追加 + *------------------------------------------ + */ +int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<MAX_EVENTTIMER;i++) + if( sd->eventtimer[i]!=-1 && strcmp( + (char *)(get_timer(sd->eventtimer[i])->data), name)==0 ){ + addtick_timer(sd->eventtimer[i],tick); + break; + } + + return 0; +} + +/*========================================== + * イベントタイマー全削除 + *------------------------------------------ + */ +int pc_cleareventtimer(struct map_session_data *sd) +{ + int i; + + nullpo_retr(0, sd); + + for(i=0;i<MAX_EVENTTIMER;i++) + if( sd->eventtimer[i]!=-1 ){ + delete_timer(sd->eventtimer[i],pc_eventtimer); + sd->eventtimer[i]=-1; + } + + return 0; +} + +// +// 装 備物 +// +/*========================================== + * アイテムを装備する + *------------------------------------------ + */ +int pc_equipitem(struct map_session_data *sd,int n,int pos) +{ + int i,nameid, arrow; + struct item_data *id; + //転生や養子の場合の元の職業を算出する + + nullpo_retr(0, sd); + + nameid = sd->status.inventory[n].nameid; + id = sd->inventory_data[n]; + pos = pc_equippoint(sd,n); + + if(battle_config.battle_log) + printf("equip %d(%d) %x:%x\n",nameid,n,id->equip,pos); + if(!pc_isequip(sd,n) || !pos || sd->status.inventory[n].broken==1 ) { // [Valaris] + clif_equipitemack(sd,n,0,0); // fail + return 0; + } + +// -- moonsoul (if player is berserk then cannot equip) +// + if(sd->sc_data[SC_BERSERK].timer!=-1){ + clif_equipitemack(sd,n,0,0); // fail + return 0; + } + + if(pos==0x88){ // アクセサリ用例外処理 + int epor=0; + if(sd->equip_index[0] >= 0) + epor |= sd->status.inventory[sd->equip_index[0]].equip; + if(sd->equip_index[1] >= 0) + epor |= sd->status.inventory[sd->equip_index[1]].equip; + epor &= 0x88; + pos = epor == 0x08 ? 0x80 : 0x08; + } + + // 二刀流処理 + if ((pos==0x22) // 一応、装備要求箇所が二刀流武器かチェックする + && (id->equip==2) // 単 手武器 + && (pc_checkskill(sd, AS_LEFT) > 0 || sd->status.class == 12) ) // 左手修錬有 + { + int tpos=0; + if(sd->equip_index[8] >= 0) + tpos |= sd->status.inventory[sd->equip_index[8]].equip; + if(sd->equip_index[9] >= 0) + tpos |= sd->status.inventory[sd->equip_index[9]].equip; + tpos &= 0x02; + pos = tpos == 0x02 ? 0x20 : 0x02; + } + + arrow=pc_search_inventory(sd,pc_checkequip(sd,9)); // Added by RoVeRT + for(i=0;i<11;i++) { + if(sd->equip_index[i] >= 0 && sd->status.inventory[sd->equip_index[i]].equip&pos) { + pc_unequipitem(sd,sd->equip_index[i],1); + } + } + // 弓矢装備 + if(pos==0x8000){ + clif_arrowequip(sd,n); + clif_arrow_fail(sd,3); // 3=矢が装備できました + } + else + clif_equipitemack(sd,n,pos,1); + + for(i=0;i<11;i++) { + if(pos & equip_pos[i]) + sd->equip_index[i] = n; + } + sd->status.inventory[n].equip=pos; + + if(sd->status.inventory[n].equip & 0x0002) { + if(sd->inventory_data[n]) + sd->weapontype1 = sd->inventory_data[n]->look; + else + sd->weapontype1 = 0; + pc_calcweapontype(sd); + clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + } + if(sd->status.inventory[n].equip & 0x0020) { + if(sd->inventory_data[n]) { + if(sd->inventory_data[n]->type == 4) { + sd->status.shield = 0; + if(sd->status.inventory[n].equip == 0x0020) + sd->weapontype2 = sd->inventory_data[n]->look; + else + sd->weapontype2 = 0; + } + else if(sd->inventory_data[n]->type == 5) { + sd->status.shield = sd->inventory_data[n]->look; + sd->weapontype2 = 0; + } + } + else + sd->status.shield = sd->weapontype2 = 0; + pc_calcweapontype(sd); + clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield); + } + if(sd->status.inventory[n].equip & 0x0001) { + if(sd->inventory_data[n]) + sd->status.head_bottom = sd->inventory_data[n]->look; + else + sd->status.head_bottom = 0; + clif_changelook(&sd->bl,LOOK_HEAD_BOTTOM,sd->status.head_bottom); + } + if(sd->status.inventory[n].equip & 0x0100) { + if(sd->inventory_data[n]) + sd->status.head_top = sd->inventory_data[n]->look; + else + sd->status.head_top = 0; + clif_changelook(&sd->bl,LOOK_HEAD_TOP,sd->status.head_top); + } + if(sd->status.inventory[n].equip & 0x0200) { + if(sd->inventory_data[n]) + sd->status.head_mid = sd->inventory_data[n]->look; + else + sd->status.head_mid = 0; + clif_changelook(&sd->bl,LOOK_HEAD_MID,sd->status.head_mid); + } + if(sd->status.inventory[n].equip & 0x0040) + clif_changelook(&sd->bl,LOOK_SHOES,0); + + pc_checkallowskill(sd); // 装備品でスキルか解除されるかチェック + if (itemdb_look(sd->status.inventory[n].nameid) == 11 && arrow){ // Added by RoVeRT + clif_arrowequip(sd,arrow); + sd->status.inventory[arrow].equip=32768; + } + pc_calcstatus(sd,0); + + if(sd->special_state.infinite_endure) { + if(sd->sc_data[SC_ENDURE].timer == -1) + skill_status_change_start(&sd->bl,SC_ENDURE,10,1,0,0,0,0); + } + else { + if(sd->sc_data[SC_ENDURE].timer != -1 && sd->sc_data[SC_ENDURE].val2) + skill_status_change_end(&sd->bl,SC_ENDURE,-1); + } + + if(sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele)) + skill_status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1); + if(sd->sc_data[SC_DANCING].timer!=-1 && (sd->status.weapon != 13 && sd->status.weapon !=14)) + skill_stop_dancing(&sd->bl,0); + + return 0; +} + +/*========================================== + * 装 備した物を外す + *------------------------------------------ + */ +int pc_unequipitem(struct map_session_data *sd,int n,int type) +{ + nullpo_retr(0, sd); + +// -- moonsoul (if player is berserk then cannot unequip) +// + if(sd->sc_data[SC_BERSERK].timer!=-1){ + clif_unequipitemack(sd,n,0,0); + return 0; + } + + if(battle_config.battle_log) + printf("unequip %d %x:%x\n",n,pc_equippoint(sd,n),sd->status.inventory[n].equip); + if(sd->status.inventory[n].equip){ + int i; + for(i=0;i<11;i++) { + if(sd->status.inventory[n].equip & equip_pos[i]) + sd->equip_index[i] = -1; + } + if(sd->status.inventory[n].equip & 0x0002) { + sd->weapontype1 = 0; + sd->status.weapon = sd->weapontype2; + pc_calcweapontype(sd); + clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + } + 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); + + if(sd->sc_data[SC_BROKNWEAPON].timer != -1 && sd->status.inventory[n].equip & 0x0002 && + sd->status.inventory[i].broken==1) + skill_status_change_end(&sd->bl,SC_BROKNWEAPON,-1); + + clif_unequipitemack(sd,n,sd->status.inventory[n].equip,1); + sd->status.inventory[n].equip=0; + if(!type) + pc_checkallowskill(sd); + if(sd->weapontype1 == 0 && sd->weapontype2 == 0) + skill_encchant_eremental_end(&sd->bl,-1); //武器持ち誓えは無条件で属性付与解除 + } else { + clif_unequipitemack(sd,n,0,0); + } + if(!type) { + pc_calcstatus(sd,0); + if(sd->sc_data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(7,sd->def_ele)) + skill_status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1); + } + + return 0; +} + +/*========================================== + * アイテムのindex番号を詰めたり + * 装 備品の装備可能チェックを行なう + *------------------------------------------ + */ +int pc_checkitem(struct map_session_data *sd) +{ + int i,j,k,id,calc_flag = 0; + struct item_data *it=NULL; + + nullpo_retr(0, sd); + + // 所持品空き詰め + for(i=j=0;i<MAX_INVENTORY;i++){ + if( (id=sd->status.inventory[i].nameid)==0) + continue; + if( battle_config.item_check && !itemdb_available(id) ){ + if(battle_config.error_log) + printf("illeagal item id %d in %d[%s] inventory.\n",id,sd->bl.id,sd->status.name); + pc_delitem(sd,i,sd->status.inventory[i].amount,3); + continue; + } + if(i>j){ + memcpy(&sd->status.inventory[j],&sd->status.inventory[i],sizeof(struct item)); + sd->inventory_data[j] = sd->inventory_data[i]; + } + j++; + } + if(j < MAX_INVENTORY) + memset(&sd->status.inventory[j],0,sizeof(struct item)*(MAX_INVENTORY-j)); + for(k=j;k<MAX_INVENTORY;k++) + sd->inventory_data[k] = NULL; + + // カート内空き詰め + for(i=j=0;i<MAX_CART;i++){ + if( (id=sd->status.cart[i].nameid)==0 ) + continue; + if( battle_config.item_check && !itemdb_available(id) ){ + if(battle_config.error_log) + printf("illeagal item id %d in %d[%s] cart.\n",id,sd->bl.id,sd->status.name); + pc_cart_delitem(sd,i,sd->status.cart[i].amount,1); + continue; + } + if(i>j){ + memcpy(&sd->status.cart[j],&sd->status.cart[i],sizeof(struct item)); + } + j++; + } + if(j < MAX_CART) + memset(&sd->status.cart[j],0,sizeof(struct item)*(MAX_CART-j)); + + // 装 備位置チェック + + for(i=0;i<MAX_INVENTORY;i++){ + + it=sd->inventory_data[i]; + + if(sd->status.inventory[i].nameid==0) + continue; + if(sd->status.inventory[i].equip & ~pc_equippoint(sd,i)) { + sd->status.inventory[i].equip=0; + calc_flag = 1; + } + //装備制限チェック + if(sd->status.inventory[i].equip && map[sd->bl.m].flag.pvp && (it->flag.no_equip==1 || it->flag.no_equip==3)){//PvP制限 + sd->status.inventory[i].equip=0; + calc_flag = 1; + }else if(sd->status.inventory[i].equip && map[sd->bl.m].flag.gvg && (it->flag.no_equip==2 || it->flag.no_equip==3)){//GvG制限 + sd->status.inventory[i].equip=0; + calc_flag = 1; + } + } + + pc_setequipindex(sd); + if(calc_flag) + pc_calcstatus(sd,2); + + return 0; +} + +int pc_checkoverhp(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if(sd->status.hp == sd->status.max_hp) + return 1; + if(sd->status.hp > sd->status.max_hp) { + sd->status.hp = sd->status.max_hp; + clif_updatestatus(sd,SP_HP); + return 2; + } + + return 0; +} + +int pc_checkoversp(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if(sd->status.sp == sd->status.max_sp) + return 1; + if(sd->status.sp > sd->status.max_sp) { + sd->status.sp = sd->status.max_sp; + clif_updatestatus(sd,SP_SP); + return 2; + } + + return 0; +} + +/*========================================== + * PVP順位計算用(foreachinarea) + *------------------------------------------ + */ +int pc_calc_pvprank_sub(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd1,*sd2=NULL; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, sd1=(struct map_session_data *)bl); + nullpo_retr(0, sd2=va_arg(ap,struct map_session_data *)); + + if( sd1->pvp_point > sd2->pvp_point ) + sd2->pvp_rank++; + return 0; +} +/*========================================== + * PVP順位計算 + *------------------------------------------ + */ +int pc_calc_pvprank(struct map_session_data *sd) +{ + int old; + struct map_data *m; + + nullpo_retr(0, sd); + nullpo_retr(0, m=&map[sd->bl.m]); + + old=sd->pvp_rank; + + if( !(m->flag.pvp) ) + return 0; + sd->pvp_rank=1; + map_foreachinarea(pc_calc_pvprank_sub,sd->bl.m,0,0,m->xs,m->ys,BL_PC,sd); + if(old!=sd->pvp_rank || sd->pvp_lastusers!=m->users) + clif_pvpset(sd,sd->pvp_rank,sd->pvp_lastusers=m->users,0); + return sd->pvp_rank; +} +/*========================================== + * PVP順位計算(timer) + *------------------------------------------ + */ +int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd=NULL; + if(battle_config.pk_mode) // disable pvp ranking if pk_mode on [Valaris] + return 0; + + sd=map_id2sd(id); + if(sd==NULL) + return 0; + sd->pvp_timer=-1; + if( pc_calc_pvprank(sd)>0 ) + sd->pvp_timer=add_timer( + gettick()+PVP_CALCRANK_INTERVAL, + pc_calc_pvprank_timer,id,data); + return 0; +} + +/*========================================== + * sdは結婚しているか(既婚の場合は相方のchar_idを返す) + *------------------------------------------ + */ +int pc_ismarried(struct map_session_data *sd) +{ + if(sd == NULL) + return -1; + if(sd->status.partner_id > 0) + return sd->status.partner_id; + else + return 0; +} +/*========================================== + * sdがdstsdと結婚(dstsd→sdの結婚処理も同時に行う) + *------------------------------------------ + */ +int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd) +{ + if(sd == NULL || dstsd == NULL || sd->status.partner_id > 0 || dstsd->status.partner_id > 0) + return -1; + sd->status.partner_id=dstsd->status.char_id; + dstsd->status.partner_id=sd->status.char_id; + return 0; +} + +/*========================================== + * sdが離婚(相手はsd->status.partner_idに依る)(相手も同時に離婚・結婚指輪自動剥奪) + *------------------------------------------ + */ +int pc_divorce(struct map_session_data *sd) +{ + struct map_session_data *p_sd=NULL; + if(sd == NULL || !pc_ismarried(sd)) + return -1; + + if( (p_sd=map_nick2sd(map_charid2nick(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){ + printf("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); + for(i=0;i<MAX_INVENTORY;i++) + 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); + + }else{ + printf("pc_divorce: p_sd nullpo\n"); + 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; + + return p_sd; +} + +// +// 自然回復物 +// +/*========================================== + * SP回復量計算 + *------------------------------------------ + */ +static int natural_heal_tick,natural_heal_prev_tick,natural_heal_diff_tick; +static int pc_spheal(struct map_session_data *sd) +{ + int a; + struct guild_castle *gc = NULL; + + nullpo_retr(0, sd); + + a = natural_heal_diff_tick; + if(pc_issit(sd)) a += a; + if( sd->sc_data[SC_MAGNIFICAT].timer!=-1 ) // マグニフィカート + a += a; + + gc=guild_mapname2gc(sd->mapname); // Increased guild castle regen [Valaris] + if(gc) { + struct guild *g; + g=guild_search(sd->status.guild_id); + if(g && g->guild_id == gc->guild_id) + a += a; + } // end addition [Valaris] + + return a; +} + +/*========================================== + * HP回復量計算 + *------------------------------------------ + */ +static int pc_hpheal(struct map_session_data *sd) +{ + int a; + struct guild_castle *gc; + + nullpo_retr(0, sd); + + a = natural_heal_diff_tick; + if(pc_issit(sd)) a += a; + if( sd->sc_data[SC_MAGNIFICAT].timer!=-1 ) // Modified by RoVeRT + a += a; + + gc=guild_mapname2gc(sd->mapname); // Increased guild castle regen [Valaris] + if(gc) { + struct guild *g; + g=guild_search(sd->status.guild_id); + if(g && g->guild_id == gc->guild_id) + a += a; + } // end addition [Valaris] + + return a; +} + +static int pc_natural_heal_hp(struct map_session_data *sd) +{ + int bhp; + int inc_num,bonus,skill,hp_flag; + + nullpo_retr(0, sd); + + if (sd->sc_data[SC_TRICKDEAD].timer != -1) // Modified by RoVeRT + return 0; + + if(pc_checkoverhp(sd)) { + sd->hp_sub = sd->inchealhptick = 0; + return 0; + } + + bhp=sd->status.hp; + hp_flag = (pc_checkskill(sd,SM_MOVINGRECOVERY) > 0 && sd->walktimer != -1); + + if(sd->walktimer == -1) { + inc_num = pc_hpheal(sd); + if( sd->sc_data[SC_TENSIONRELAX].timer!=-1 ){ // テンションリラックス + sd->hp_sub += 2*inc_num; + sd->inchealhptick += 3*natural_heal_diff_tick; + }else{ + sd->hp_sub += inc_num; + sd->inchealhptick += natural_heal_diff_tick; + } + } + else if(hp_flag) { + inc_num = pc_hpheal(sd); + sd->hp_sub += inc_num; + sd->inchealhptick = 0; + } + else { + sd->hp_sub = sd->inchealhptick = 0; + return 0; + } + + if(sd->hp_sub >= battle_config.natural_healhp_interval) { + bonus = sd->nhealhp; + if(hp_flag) { + bonus >>= 2; + if(bonus <= 0) bonus = 1; + } + while(sd->hp_sub >= battle_config.natural_healhp_interval) { + sd->hp_sub -= battle_config.natural_healhp_interval; + if(sd->status.hp + bonus <= sd->status.max_hp) + sd->status.hp += bonus; + else { + sd->status.hp = sd->status.max_hp; + sd->hp_sub = sd->inchealhptick = 0; + } + } + } + if(bhp!=sd->status.hp) + clif_updatestatus(sd,SP_HP); + + if(sd->nshealhp > 0) { + if(sd->inchealhptick >= battle_config.natural_heal_skill_interval && sd->status.hp < sd->status.max_hp) { + bonus = sd->nshealhp; + while(sd->inchealhptick >= battle_config.natural_heal_skill_interval) { + sd->inchealhptick -= battle_config.natural_heal_skill_interval; + if(sd->status.hp + bonus <= sd->status.max_hp) + sd->status.hp += bonus; + else { + bonus = sd->status.max_hp - sd->status.hp; + sd->status.hp = sd->status.max_hp; + sd->hp_sub = sd->inchealhptick = 0; + } + clif_heal(sd->fd,SP_HP,bonus); + } + } + } + else sd->inchealhptick = 0; + + return 0; + + if(sd->sc_data[SC_APPLEIDUN].timer!=-1) { // Apple of Idun + if(sd->inchealhptick >= 6000 && sd->status.hp < sd->status.max_hp) { + bonus = skill*20; + while(sd->inchealhptick >= 6000) { + sd->inchealhptick -= 6000; + if(sd->status.hp + bonus <= sd->status.max_hp) + sd->status.hp += bonus; + else { + bonus = sd->status.max_hp - sd->status.hp; + sd->status.hp = sd->status.max_hp; + sd->hp_sub = sd->inchealhptick = 0; + } + clif_heal(sd->fd,SP_HP,bonus); + } + } + } + else sd->inchealhptick = 0; + + return 0; +} + +static int pc_natural_heal_sp(struct map_session_data *sd) +{ + int bsp; + int inc_num,bonus; + + nullpo_retr(0, sd); + + if (sd->sc_data[SC_TRICKDEAD].timer != -1) // Modified by RoVeRT + return 0; + + if(pc_checkoversp(sd)) { + sd->sp_sub = sd->inchealsptick = 0; + return 0; + } + + bsp=sd->status.sp; + + inc_num = pc_spheal(sd); + if(sd->sc_data[SC_EXPLOSIONSPIRITS].timer == -1) + sd->sp_sub += inc_num; + if(sd->walktimer == -1) + sd->inchealsptick += natural_heal_diff_tick; + else sd->inchealsptick = 0; + + if(sd->sp_sub >= battle_config.natural_healsp_interval){ + bonus = sd->nhealsp;; + while(sd->sp_sub >= battle_config.natural_healsp_interval){ + sd->sp_sub -= battle_config.natural_healsp_interval; + if(sd->status.sp + bonus <= sd->status.max_sp) + sd->status.sp += bonus; + else { + sd->status.sp = sd->status.max_sp; + sd->sp_sub = sd->inchealsptick = 0; + } + } + } + + if(bsp != sd->status.sp) + clif_updatestatus(sd,SP_SP); + + if(sd->nshealsp > 0) { + if(sd->inchealsptick >= battle_config.natural_heal_skill_interval && sd->status.sp < sd->status.max_sp) { + struct pc_base_job s_class = pc_calc_base_job(sd->status.class); + if(sd->doridori_counter && s_class.job == 23) + bonus = sd->nshealsp*2; + else + bonus = sd->nshealsp; + sd->doridori_counter = 0; + while(sd->inchealsptick >= battle_config.natural_heal_skill_interval) { + sd->inchealsptick -= battle_config.natural_heal_skill_interval; + if(sd->status.sp + bonus <= sd->status.max_sp) + sd->status.sp += bonus; + else { + bonus = sd->status.max_sp - sd->status.sp; + sd->status.sp = sd->status.max_sp; + sd->sp_sub = sd->inchealsptick = 0; + } + clif_heal(sd->fd,SP_SP,bonus); + } + } + } + else sd->inchealsptick = 0; + + return 0; +} + +static int pc_spirit_heal_hp(struct map_session_data *sd,int level) +{ + int bonus_hp,interval = battle_config.natural_heal_skill_interval; + + 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; + while(sd->inchealspirithptick >= interval) { + if(pc_issit(sd)) { + sd->inchealspirithptick -= interval; + if(sd->status.hp < sd->status.max_hp) { + if(sd->status.hp + bonus_hp <= sd->status.max_hp) + sd->status.hp += bonus_hp; + else { + bonus_hp = sd->status.max_hp - sd->status.hp; + sd->status.hp = sd->status.max_hp; + } + clif_heal(sd->fd,SP_HP,bonus_hp); + sd->inchealspirithptick = 0; + } + }else{ + sd->inchealspirithptick -= natural_heal_diff_tick; + break; + } + } + } + + return 0; +} +static int pc_spirit_heal_sp(struct map_session_data *sd,int level) +{ + int bonus_sp,interval = battle_config.natural_heal_skill_interval; + + nullpo_retr(0, sd); + + if(pc_checkoversp(sd)) { + sd->inchealspiritsptick = 0; + return 0; + } + + sd->inchealspiritsptick += natural_heal_diff_tick; + + if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) + interval += interval; + + if(sd->inchealspiritsptick >= interval) { + bonus_sp = sd->nsshealsp; + while(sd->inchealspiritsptick >= interval) { + if(pc_issit(sd)) { + sd->inchealspiritsptick -= interval; + if(sd->status.sp < sd->status.max_sp) { + if(sd->status.sp + bonus_sp <= sd->status.max_sp) + sd->status.sp += bonus_sp; + else { + bonus_sp = sd->status.max_sp - sd->status.sp; + sd->status.sp = sd->status.max_sp; + } + clif_heal(sd->fd,SP_SP,bonus_sp); + sd->inchealspiritsptick = 0; + } + }else{ + sd->inchealspiritsptick -= natural_heal_diff_tick; + break; + } + } + } + + return 0; +} + +/*========================================== + * HP/SP 自然回復 各クライアント + *------------------------------------------ + */ + +static int pc_natural_heal_sub(struct map_session_data *sd,va_list ap) { + int skill; + + nullpo_retr(0, sd); + +// -- moonsoul (if conditions below altered to disallow natural healing if under berserk status) + if ((battle_config.natural_heal_weight_rate > 100 || sd->weight*100/sd->max_weight < battle_config.natural_heal_weight_rate) && + !pc_isdead(sd) && + !pc_ishiding(sd) && + sd->sc_data[SC_POISON].timer == -1 + ) { + pc_natural_heal_hp(sd); + if( sd->sc_data && sd->sc_data[SC_EXTREMITYFIST].timer == -1 && //阿修羅状態ではSPが回復しない + sd->sc_data[SC_DANCING].timer == -1 && //ダンス状態ではSPが回復しない + sd->sc_data[SC_BERSERK].timer == -1 //バーサーク状態ではSPが回復しない + ) + pc_natural_heal_sp(sd); + } else { + sd->hp_sub = sd->inchealhptick = 0; + sd->sp_sub = sd->inchealsptick = 0; + } + if((skill = pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 && !pc_ishiding(sd) && sd->sc_data[SC_POISON].timer == -1 && sd->sc_data[SC_BERSERK].timer == -1){ + pc_spirit_heal_hp(sd,skill); + pc_spirit_heal_sp(sd,skill); + } + else { + sd->inchealspirithptick = 0; + sd->inchealspiritsptick = 0; + } + return 0; +} + +/*========================================== + * HP/SP自然回復 (interval timer関数) + *------------------------------------------ + */ +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); + + natural_heal_prev_tick = tick; + return 0; +} + +/*========================================== + * セーブポイントの保存 + *------------------------------------------ + */ +int pc_setsavepoint(struct map_session_data *sd,char *mapname,int x,int y) +{ + nullpo_retr(0, sd); + + strncpy(sd->status.save_point.map,mapname,24); + sd->status.save_point.x = x; + sd->status.save_point.y = y; + + return 0; +} + +/*========================================== + * 自動セーブ 各クライアント + *------------------------------------------ + */ +static int last_save_fd,save_flag; +static int pc_autosave_sub(struct map_session_data *sd,va_list ap) +{ + nullpo_retr(0, sd); + + if(save_flag==0 && sd->fd>last_save_fd){ + struct guild_castle *gc=NULL; + int i; +// if(battle_config.save_log) +// printf("autosave %d\n",sd->fd); + // pet + if(sd->status.pet_id > 0 && sd->pd) + intif_save_petdata(sd->status.account_id,&sd->pet); + pc_makesavestatus(sd); + chrif_save(sd); + storage_storage_save(sd); + + for(i=0;i<MAX_GUILDCASTLE;i++){ + gc=guild_castle_search(i); + if(!gc) continue; + if(gc->visibleG0==1) guild_castledatasave(gc->castle_id,18,gc->Ghp0); + if(gc->visibleG1==1) guild_castledatasave(gc->castle_id,19,gc->Ghp1); + if(gc->visibleG2==1) guild_castledatasave(gc->castle_id,20,gc->Ghp2); + if(gc->visibleG3==1) guild_castledatasave(gc->castle_id,21,gc->Ghp3); + if(gc->visibleG4==1) guild_castledatasave(gc->castle_id,22,gc->Ghp4); + if(gc->visibleG5==1) guild_castledatasave(gc->castle_id,23,gc->Ghp5); + if(gc->visibleG6==1) guild_castledatasave(gc->castle_id,24,gc->Ghp6); + if(gc->visibleG7==1) guild_castledatasave(gc->castle_id,25,gc->Ghp7); + } + + save_flag=1; + last_save_fd = sd->fd; + } + + return 0; +} + +/*========================================== + * 自動セーブ (timer関数) + *------------------------------------------ + */ +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; +#endif + if (gm_account != NULL) + free(gm_account); + GM_num = 0; +#ifdef TXT_ONLY + gm_account = calloc(sizeof(struct gm_account) * ((RFIFOW(fd,2) - 4) / 5), 1); + 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_lsql, "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(&lmysql_handle, tmp_lsql) ) { + printf("DB server Error (select %s to Memory)- %s\n",login_db,mysql_error(&lmysql_handle) ); + } + lsql_res = mysql_store_result(&lmysql_handle); + if (lsql_res) { + gm_account = calloc(sizeof(struct gm_account) * mysql_num_rows(lsql_res), 1); + 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]); + printf("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 + *------------------------------------------ + */ +int map_day_timer(int tid, unsigned int tick, int id, int data) { // by [yor] + struct map_session_data *pl_sd = NULL; + int i; + char tmpstr[1024]; + + if (battle_config.day_duration > 0) { // if we want a day + if (night_flag != 0) { + strcpy(tmpstr, msg_txt(502)); // The day has arrived! + night_flag = 0; // 0=day, 1=night [Yor] + for(i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_sd->opt2 &= ~STATE_BLIND; + clif_changeoption(&pl_sd->bl); + clif_wis_message(pl_sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1); + } + } + } + } + + return 0; +} + +/*========================================== + * timer to do the night + *------------------------------------------ + */ +int map_night_timer(int tid, unsigned int tick, int id, int data) { // by [yor] + struct map_session_data *pl_sd = NULL; + int i; + char tmpstr[1024]; + + if (battle_config.night_duration > 0) { // if we want a night + if (night_flag == 0) { + strcpy(tmpstr, msg_txt(503)); // The night has fallen... + night_flag = 1; // 0=day, 1=night [Yor] + for(i = 0; i < fd_max; i++) { + if (session[i] && (pl_sd = session[i]->session_data) && pl_sd->state.auth) { + pl_sd->opt2 |= STATE_BLIND; + clif_changeoption(&pl_sd->bl); + clif_wis_message(pl_sd->fd, wisp_server_name, tmpstr, strlen(tmpstr)+1); + } + } + } + } + + return 0; +} + +void pc_setstand(struct map_session_data *sd){ + nullpo_retv(sd); + + if(sd->sc_data && sd->sc_data[SC_TENSIONRELAX].timer!=-1) + skill_status_change_end(&sd->bl,SC_TENSIONRELAX,-1); + + sd->state.dead_sit = 0; +} + +// +// 初期化物 +// +/*========================================== + * 設定ファイル読み込む + * exp.txt 必要経験値 + * job_db1.txt 重量,hp,sp,攻撃速度 + * job_db2.txt job能力値ボーナス + * skill_tree.txt 各職毎のスキルツリー + * attr_fix.txt 属性修正テーブル + * size_fix.txt サイズ補正テーブル + * refine_db.txt 精錬データテーブル + *------------------------------------------ + */ +int pc_readdb(void) +{ + int i,j,k; + FILE *fp; + char line[1024],*p; + + // 必要経験値読み込み + + fp=fopen("db/exp.txt","r"); + if(fp==NULL){ + printf("can't read db/exp.txt\n"); + return 1; + } + i=0; + while(fgets(line, sizeof(line)-1, fp)){ + int bn,b1,b2,b3,b4,b5,b6,jn,j1,j2,j3,j4,j5,j6; + if(line[0]=='/' && line[1]=='/') + continue; + if(sscanf(line,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",&bn,&b1,&b2,&b3,&b4,&b5,&b6,&jn,&j1,&j2,&j3,&j4,&j5,&j6)!=14) + continue; + exp_table[0][i]=bn; + exp_table[1][i]=b1; + exp_table[2][i]=b2; + exp_table[3][i]=b3; + exp_table[4][i]=b4; + exp_table[5][i]=b5; + exp_table[6][i]=b6; + exp_table[7][i]=jn; + exp_table[8][i]=j1; + exp_table[9][i]=j2; + exp_table[10][i]=j3; + exp_table[11][i]=j4; + exp_table[12][i]=j5; + exp_table[13][i]=j6; + i++; + if(i >= battle_config.maximum_level) + break; + } + fclose(fp); + printf("read db/exp.txt done\n"); + + // JOB補正数値1 + fp=fopen("db/job_db1.txt","r"); + if(fp==NULL){ + printf("can't read db/job_db1.txt\n"); + return 1; + } + i=0; + while(fgets(line, sizeof(line)-1, fp)){ + char *split[50]; + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<21 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(j<21) + continue; + max_weight_base[i]=atoi(split[0]); + hp_coefficient[i]=atoi(split[1]); + hp_coefficient2[i]=atoi(split[2]); + sp_coefficient[i]=atoi(split[3]); + for(j=0;j<17;j++) + aspd_base[i][j]=atoi(split[j+4]); + i++; +// -- moonsoul (below two lines added to accommodate high numbered new class ids) + if(i==24) + i=4001; + if(i==MAX_PC_CLASS) + break; + } + fclose(fp); + printf("read db/job_db1.txt done\n"); + + // JOBボーナス + fp=fopen("db/job_db2.txt","r"); + if(fp==NULL){ + printf("can't read db/job_db2.txt\n"); + return 1; + } + i=0; + while(fgets(line, sizeof(line)-1, fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<MAX_LEVEL && p;j++){ + if(sscanf(p,"%d",&k)==0) + break; + job_bonus[0][i][j]=k; + job_bonus[2][i][j]=k; //養子職のボーナスは分からないので仮 + p=strchr(p,','); + if(p) p++; + } + i++; +// -- moonsoul (below two lines added to accommodate high numbered new class ids) + if(i==24) + i=4001; + if(i==MAX_PC_CLASS) + break; + } + fclose(fp); + printf("read db/job_db2.txt done\n"); + + // JOBボーナス2 転生職用 + fp=fopen("db/job_db2-2.txt","r"); + if(fp==NULL){ + printf("can't read db/job_db2-2.txt\n"); + return 1; + } + i=0; + while(fgets(line, sizeof(line)-1, fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<MAX_LEVEL && p;j++){ + if(sscanf(p,"%d",&k)==0) + break; + job_bonus[1][i][j]=k; + p=strchr(p,','); + if(p) p++; + } + i++; + if(i==MAX_PC_CLASS) + break; + } + fclose(fp); + printf("read db/job_db2-2.txt done\n"); + + // スキルツリー + memset(skill_tree,0,sizeof(skill_tree)); + fp=fopen("db/skill_tree.txt","r"); + if(fp==NULL){ + printf("can't read db/skill_tree.txt\n"); + return 1; + } + while(fgets(line, sizeof(line)-1, fp)){ + char *split[50]; + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<13 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(j<13) + continue; + i=atoi(split[0]); + for(j=0;skill_tree[0][i][j].id;j++); + skill_tree[0][i][j].id=atoi(split[1]); + skill_tree[0][i][j].max=atoi(split[2]); + skill_tree[2][i][j].id=atoi(split[1]); //養子職は良く分からないので暫定 + skill_tree[2][i][j].max=atoi(split[2]); //養子職は良く分からないので暫定 + for(k=0;k<5;k++){ + skill_tree[0][i][j].need[k].id=atoi(split[k*2+3]); + skill_tree[0][i][j].need[k].lv=atoi(split[k*2+4]); + skill_tree[2][i][j].need[k].id=atoi(split[k*2+3]); //養子職は良く分からないので暫定 + skill_tree[2][i][j].need[k].lv=atoi(split[k*2+4]); //養子職は良く分からないので暫定 + } + } + fclose(fp); + printf("read db/skill_tree.txt done\n"); + + // 属性修正テーブル + for(i=0;i<4;i++) + for(j=0;j<10;j++) + for(k=0;k<10;k++) + attr_fix_table[i][j][k]=100; + fp=fopen("db/attr_fix.txt","r"); + if(fp==NULL){ + printf("can't read db/attr_fix.txt\n"); + return 1; + } + while(fgets(line, sizeof(line)-1, fp)){ + char *split[10]; + int lv,n; + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<3 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + lv=atoi(split[0]); + n=atoi(split[1]); +// printf("%d %d\n",lv,n); + + for(i=0;i<n;){ + if( !fgets(line, sizeof(line)-1, fp) ) + break; + if(line[0]=='/' && line[1]=='/') + continue; + + for(j=0,p=line;j<n && p;j++){ + while(*p==32 && *p>0) + p++; + attr_fix_table[lv-1][i][j]=atoi(p); + if(battle_config.attr_recover == 0 && attr_fix_table[lv-1][i][j] < 0) + attr_fix_table[lv-1][i][j] = 0; + p=strchr(p,','); + if(p) *p++=0; + } + + i++; + } + } + fclose(fp); + printf("read db/attr_fix.txt done\n"); + + // サイズ補正テーブル + for(i=0;i<3;i++) + for(j=0;j<20;j++) + atkmods[i][j]=100; + fp=fopen("db/size_fix.txt","r"); + if(fp==NULL){ + printf("can't read db/size_fix.txt\n"); + return 1; + } + i=0; + while(fgets(line, sizeof(line)-1, fp)){ + char *split[20]; + if(line[0]=='/' && line[1]=='/') + continue; + if(atoi(line)<=0) + continue; + memset(split,0,sizeof(split)); + for(j=0,p=line;j<20 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + for(j=0;j<20 && split[j];j++) + atkmods[i][j]=atoi(split[j]); + i++; + } + fclose(fp); + printf("read db/size_fix.txt done\n"); + + // 精錬データテーブル + for(i=0;i<5;i++){ + for(j=0;j<10;j++) + percentrefinery[i][j]=100; + refinebonus[i][0]=0; + refinebonus[i][1]=0; + refinebonus[i][2]=10; + } + fp=fopen("db/refine_db.txt","r"); + if(fp==NULL){ + printf("can't read db/refine_db.txt\n"); + return 1; + } + i=0; + while(fgets(line, sizeof(line)-1, fp)){ + char *split[16]; + if(line[0]=='/' && line[1]=='/') + continue; + if(atoi(line)<=0) + continue; + memset(split,0,sizeof(split)); + for(j=0,p=line;j<16 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + refinebonus[i][0]=atoi(split[0]); // 精錬ボーナス + refinebonus[i][1]=atoi(split[1]); // 過剰精錬ボーナス + refinebonus[i][2]=atoi(split[2]); // 安全精錬限界 + for(j=0;j<10 && split[j];j++) + percentrefinery[i][j]=atoi(split[j+3]); + i++; + } + fclose(fp); //Lupus. close this file!!! + printf("read db/refine_db.txt done\n"); + + return 0; +} + +static int pc_calc_sigma(void) +{ + int i,j,k; + + for(i=0;i<MAX_PC_CLASS;i++) { + memset(hp_sigma_val[i],0,sizeof(hp_sigma_val[i])); + for(k=0,j=2;j<=MAX_LEVEL;j++) { + k += hp_coefficient[i]*j + 50; + k -= k%100; + hp_sigma_val[i][j-1] = k; + } + } + return 0; +} + +static void pc_statpointdb(void) +{ + char * buf_stat; + int i=0,j=0,k=0,l=0, end = 0; + + FILE *stp; + + stp=fopen("db/statpoint.txt","r"); + + if(stp==NULL){ + printf("can't read db/statpoint.txt\n"); + return; + } + + fseek(stp, 0, SEEK_END); + end = ftell(stp); + rewind(stp); + + buf_stat = (char *) malloc (end + 1); + l = fread(buf_stat,1,end,stp); + fclose(stp); + printf("read db/statpoint.txt done (size=%d)\n",l); + + for(i=0;i<255;i++) { + j=0; + while (*(buf_stat+k)!='\n') { + statp[i][j]=*(buf_stat+k); + j++;k++; + } + statp[i][j+1]='\0'; + k++; + } + + free(buf_stat); +} + +/*========================================== + * pc関 係初期化 + *------------------------------------------ + */ +int do_init_pc(void) { + pc_readdb(); + pc_statpointdb(); + pc_calc_sigma(); + +// gm_account_db = numdb_init(); + + add_timer_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_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 */ + + // 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] + { + int day_duration = battle_config.day_duration; + int night_duration = battle_config.night_duration; + if (day_duration < 60000) + day_duration = 60000; + if (night_duration < 60000) + night_duration = 60000; + if (battle_config.night_at_start == 0) { + night_flag = 0; // 0=day, 1=night [Yor] + day_timer_tid = add_timer_interval(gettick() + day_duration + night_duration, map_day_timer, 0, 0, day_duration + night_duration); + night_timer_tid = add_timer_interval(gettick() + day_duration, map_night_timer, 0, 0, day_duration + night_duration); + } else { + night_flag = 1; // 0=day, 1=night [Yor] + day_timer_tid = add_timer_interval(gettick() + night_duration, map_day_timer, 0, 0, day_duration + night_duration); + night_timer_tid = add_timer_interval(gettick() + day_duration + night_duration, map_night_timer, 0, 0, day_duration + night_duration); + } + } + + return 0; +} diff --git a/src/map/pc.h b/src/map/pc.h new file mode 100644 index 0000000..1919007 --- /dev/null +++ b/src/map/pc.h @@ -0,0 +1,186 @@ +// $Id: pc.h,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ + +#ifndef _PC_H_ +#define _PC_H_ + +#include "map.h" + +#define OPTION_MASK 0xd7b8 +#define CART_MASK 0x788 + +#define pc_setdead(sd) ((sd)->state.dead_sit = 1) +#define pc_setsit(sd) ((sd)->state.dead_sit = 2) +//#define pc_setstand(sd) ((sd)->state.dead_sit = 0) +#define pc_isdead(sd) ((sd)->state.dead_sit == 1) +#define pc_issit(sd) ((sd)->state.dead_sit == 2) +#define pc_setdir(sd,b,h) ((sd)->dir = (b) ,(sd)->head_dir = (h) ) +#define pc_setchatid(sd,n) ((sd)->chatID = n) +#define pc_ishiding(sd) ((sd)->status.option&0x4006) +#define pc_iscarton(sd) ((sd)->status.option&CART_MASK) +#define pc_isfalcon(sd) ((sd)->status.option&0x0010) +#define pc_isriding(sd) ((sd)->status.option&0x0020) +#define pc_isinvisible(sd) ((sd)->status.option&0x0040) +#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight) +#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9) + +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_counttargeted(struct map_session_data *sd,struct block_list *src,int target_lv); +int pc_setrestartvalue(struct map_session_data *sd,int type); +int pc_makesavestatus(struct map_session_data *); +int pc_setnewpc(struct map_session_data*,int,int,int,int,int,int); +int pc_authok(int, int, time_t, struct mmo_charstatus *); +int pc_authfail(int); + +int pc_isequip(struct map_session_data *sd,int n); +int pc_equippoint(struct map_session_data *sd,int n); + +int pc_breakweapon(struct map_session_data *sd); // weapon breaking [Valaris] +int pc_breakarmor(struct map_session_data *sd); // armor breaking [Valaris] + +int pc_checkskill(struct map_session_data *sd,int skill_id); +int pc_checkallowskill(struct map_session_data *sd); +int pc_checkequip(struct map_session_data *sd,int pos); + +int pc_checkoverhp(struct map_session_data*); +int pc_checkoversp(struct map_session_data*); + +int pc_can_reach(struct map_session_data*,int,int); +int pc_walktoxy(struct map_session_data*,int,int); +int pc_stop_walking(struct map_session_data*,int); +int pc_movepos(struct map_session_data*,int,int); +int pc_setpos(struct map_session_data*,char*,int,int,int); +int pc_setsavepoint(struct map_session_data*,char*,int,int); +int pc_randomwarp(struct map_session_data *sd,int type); +int pc_memo(struct map_session_data *sd,int i); + +int pc_checkadditem(struct map_session_data*,int,int); +int pc_inventoryblank(struct map_session_data*); +int pc_search_inventory(struct map_session_data *sd,int item_id); +int pc_payzeny(struct map_session_data*,int); +int pc_additem(struct map_session_data*,struct item*,int); +int pc_getzeny(struct map_session_data*,int); +int pc_delitem(struct map_session_data*,int,int,int); +int pc_checkitem(struct map_session_data*); + +int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount); +int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type); +int pc_putitemtocart(struct map_session_data *sd,int idx,int amount); +int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount); +int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount); + +int pc_takeitem(struct map_session_data*,struct flooritem_data*); +int pc_dropitem(struct map_session_data*,int,int); + +int pc_checkweighticon(struct map_session_data *sd); + +int pc_calcstatus(struct map_session_data*,int); +int pc_bonus(struct map_session_data*,int,int); +int pc_bonus2(struct map_session_data *sd,int,int,int); +int pc_bonus3(struct map_session_data *sd,int,int,int,int); +int pc_skill(struct map_session_data*,int,int,int); + +int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip); + +int pc_item_identify(struct map_session_data *sd,int idx); +int pc_steal_item(struct map_session_data *sd,struct block_list *bl); +int pc_steal_coin(struct map_session_data *sd,struct block_list *bl); + +int pc_modifybuyvalue(struct map_session_data*,int); +int pc_modifysellvalue(struct map_session_data*,int); + +int pc_attack(struct map_session_data*,int,int); +int pc_stopattack(struct map_session_data*); + +int pc_follow(struct map_session_data*, int); // [MouseJstr] + +int pc_checkbaselevelup(struct map_session_data *sd); +int pc_checkjoblevelup(struct map_session_data *sd); +int pc_gainexp(struct map_session_data*,int,int); +int pc_nextbaseexp(struct map_session_data *); +int pc_nextbaseafter(struct map_session_data *); // [Valaris] +int pc_nextjobexp(struct map_session_data *); +int pc_nextjobafter(struct map_session_data *); // [Valaris] +int pc_need_status_point(struct map_session_data *,int); +int pc_statusup(struct map_session_data*,int); +int pc_statusup2(struct map_session_data*,int,int); +int pc_skillup(struct map_session_data*,int); +int pc_allskillup(struct map_session_data*); +int pc_resetlvl(struct map_session_data*,int type); +int pc_resetstate(struct map_session_data*); +int pc_resetskill(struct map_session_data*); +int pc_equipitem(struct map_session_data*,int,int); +int pc_unequipitem(struct map_session_data*,int,int); +int pc_checkitem(struct map_session_data*); +int pc_useitem(struct map_session_data*,int); + +int pc_damage(struct block_list *,struct map_session_data*,int); +int pc_heal(struct map_session_data *,int,int); +int pc_itemheal(struct map_session_data *sd,int hp,int sp); +int pc_percentheal(struct map_session_data *sd,int,int); +int pc_jobchange(struct map_session_data *,int, int); +int pc_setoption(struct map_session_data *,int); +int pc_setcart(struct map_session_data *sd,int type); +int pc_setfalcon(struct map_session_data *sd); +int pc_setriding(struct map_session_data *sd); +int pc_changelook(struct map_session_data *,int,int); +int pc_equiplookall(struct map_session_data *sd); + +int pc_readparam(struct map_session_data*,int); +int pc_setparam(struct map_session_data*,int,int); +int pc_readreg(struct map_session_data*,int); +int pc_setreg(struct map_session_data*,int,int); +char *pc_readregstr(struct map_session_data *sd,int reg); +int pc_setregstr(struct map_session_data *sd,int reg,char *str); +int pc_readglobalreg(struct map_session_data*,char*); +int pc_setglobalreg(struct map_session_data*,char*,int); +int pc_readaccountreg(struct map_session_data*,char*); +int pc_setaccountreg(struct map_session_data*,char*,int); +int pc_readaccountreg2(struct map_session_data*,char*); +int pc_setaccountreg2(struct map_session_data*,char*,int); +int pc_percentrefinery(struct map_session_data *sd,struct item *item); + +int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name); +int pc_deleventtimer(struct map_session_data *sd,const char *name); +int pc_cleareventtimer(struct map_session_data *sd); +int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick); + +int pc_calc_pvprank(struct map_session_data *sd); +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); +struct map_session_data *pc_get_partner(struct map_session_data *sd); +int pc_set_gm_level(int account_id, int level); +void pc_setstand(struct map_session_data *sd); + + +struct pc_base_job{ + int job; //職業、ただし転生職や養子職の場合は元の職業を返す(廃プリ→プリ) + int type; //ノビ 0, 一次職 1, 二次職 2, スパノビ 3 + int upper; //通常 0, 転生 1, 養子 2 +}; + +struct pc_base_job pc_calc_base_job(int b_class);//転生や養子職の元の職業を返す + +int pc_read_gm_account(int fd); +int pc_setinvincibletimer(struct map_session_data *sd,int); +int pc_delinvincibletimer(struct map_session_data *sd); +int pc_addspiritball(struct map_session_data *sd,int,int); +int pc_delspiritball(struct map_session_data *sd,int,int); + +int do_init_pc(void); + +enum {ADDITEM_EXIST,ADDITEM_NEW,ADDITEM_OVERAMOUNT}; + +// timer for night.day +int day_timer_tid; +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] + +#endif + diff --git a/src/map/pet.c b/src/map/pet.c new file mode 100644 index 0000000..6026b1e --- /dev/null +++ b/src/map/pet.c @@ -0,0 +1,1651 @@ +// $Id: pet.c,v 1.4 2004/09/25 05:32:18 MouseJstr Exp $ +#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 "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" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#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 distance(int x0,int y0,int x1,int y1) +{ + int dx,dy; + + dx=abs(x0-x1); + dy=abs(y0-y1); + return dx>dy ? dx : dy; +} + +static int calc_next_walk_step(struct pet_data *pd) +{ + nullpo_retr(0, 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); + + 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); + + 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); + + if( pd->bl.x==x && pd->bl.y==y ) // 同じマス + return 1; + + // 障害物判定 + wpd.path_len=0; + wpd.path_pos=0; + wpd.path_half=0; + return (path_search(&wpd,pd->bl.m,pd->bl.x,pd->bl.y,x,y,0)!=-1)?1:0; +} + +static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir) +{ + int x,y,dx,dy; + int i,j=0,k; + + nullpo_retr(0, pd); + + 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_attack(struct pet_data *pd,unsigned int tick,int data) +{ + struct mob_data *md; + int mode,race,range; + + nullpo_retr(0, pd); + + pd->state.state=MS_IDLE; + + md=(struct mob_data *)map_id2bl(pd->target_id); + if(md == NULL || md->bl.type != BL_MOB || pd->bl.m != md->bl.m || md->bl.prev == NULL || + distance(pd->bl.x,pd->bl.y,md->bl.x,md->bl.y) > 13) { + pd->target_id=0; + return 0; + } + + mode=mob_db[pd->class].mode; + race=mob_db[pd->class].race; + if(mob_db[pd->class].mexp <= 0 && !(mode&0x20) && (md->option & 0x06 && race != 4 && race != 6) ) { + pd->target_id=0; + return 0; + } + + range = mob_db[pd->class].range + 1; + if(distance(pd->bl.x,pd->bl.y,md->bl.x,md->bl.y) > range) + return 0; + if(battle_config.monster_attack_direction_change) + pd->dir=map_calc_dir(&pd->bl, md->bl.x,md->bl.y ); + + clif_fixpetpos(pd); + + pd->target_lv = battle_weapon_attack(&pd->bl,&md->bl,tick,0); + + pd->attackabletime = tick + battle_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 pet_walk(struct pet_data *pd,unsigned int tick,int data) +{ + int moveblock; + int i,ctype; + 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; +/* ctype = map_getcell(pd->bl.m,x,y); + if(ctype == 1 || ctype == 5) { + pet_stop_walking(pd,1); + return 0; + }*/ + pd->dir=pd->walkpath.path[pd->walkpath.path_pos]; + dx = dirx[pd->dir]; + dy = diry[pd->dir]; + + ctype = map_getcell(pd->bl.m,x+dx,y+dy); + if(ctype == 1 || ctype == 5) { + pet_walktoxy_sub(pd); + return 0; + } + + moveblock = ( x/BLOCK_SIZE != (x+dx)/BLOCK_SIZE || y/BLOCK_SIZE != (y+dy)/BLOCK_SIZE); + + 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; + + if(moveblock) map_delblock(&pd->bl); + pd->bl.x = x; + pd->bl.y = y; + if(moveblock) map_addblock(&pd->bl); + + 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); + + 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; + struct mob_data *md; + int rate,mode,race; + + nullpo_retr(0, sd); + + pd = sd->pd; + + if(bl && pd && bl->type == BL_MOB && sd->pet.intimate > 900 && sd->pet.hungry > 0 && pd->class != battle_get_class(bl) + && pd->state.state != MS_DELAY) { + mode=mob_db[pd->class].mode; + race=mob_db[pd->class].race; + md=(struct mob_data *)bl; + if(md->bl.type != BL_MOB || pd->bl.m != md->bl.m || md->bl.prev == NULL || + distance(pd->bl.x,pd->bl.y,md->bl.x,md->bl.y) > 13) + return 0; + if(mob_db[pd->class].mexp <= 0 && !(mode&0x20) && (md->option & 0x06 && race!=4 && race!=6) ) + return 0; + if(!type) { + rate = sd->petDB->attack_rate; + rate = rate * (150 - (sd->pet.intimate - 1000))/100; + if(battle_config.pet_support_rate != 100) + rate = rate*battle_config.pet_support_rate/100; + if(sd->petDB->attack_rate > 0 && rate <= 0) + rate = 1; + } + else { + rate = sd->petDB->defence_attack_rate; + rate = rate * (150 - (sd->pet.intimate - 1000))/100; + if(battle_config.pet_support_rate != 100) + rate = rate*battle_config.pet_support_rate/100; + 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; +} + +int pet_changestate(struct pet_data *pd,int state,int type) +{ + unsigned int tick; + int i; + + nullpo_retr(0, pd); + + 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; + + if(pd->timer != tid){ + if(battle_config.error_log) + printf("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: + pet_attack(pd,tick,data); + break; + case MS_DELAY: + pet_changestate(pd,MS_IDLE,0); + break; + default: + if(battle_config.error_log) + printf("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); + + 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); + + 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); + + 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; + + if(sd->pet_hungry_timer != tid){ + if(battle_config.error_log) + printf("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) + pc_calcstatus(sd,0); + else + pc_calcstatus(sd,2); + } + } + 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); + + if(sd->status.pet_id && sd->pd) { + + struct pet_data *pd=sd->pd; // [Valaris] + if(pd->skillbonustimer!=-1) pd->skillbonustimer=-1; + if(pd->skillbonusduration!=-1) pd->skillbonusduration=-1; + if(pd->skilltype !=-1) pd->skilltype=-1; + if(pd->skillval !=-1) pd->skillval=-1; + if(pd->skilltimer!=-1) pd->skilltimer=-1; + if(pd->skillduration!=-1) pd->skillduration=-1; + if(pd->skillbonustype!=-1) pd->skillbonustype=-1; + if(pd->skillbonusval!=-1) pd->skillbonusval=-1; + if(sd->perfect_hiding==1) sd->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); + map_freeblock(sd->pd); + } + 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); + + pet_stop_walking(pd,2000<<8); + clif_pet_performance(&pd->bl,rand()%pet_performance_val(sd) + 1); + // ルートしたItemを落とさせる + pet_lootitem_drop(pd,NULL); + + return 0; +} + +int pet_return_egg(struct map_session_data *sd) +{ + struct item tmp_item; + int flag; + + nullpo_retr(0, sd); + + if(sd->status.pet_id && sd->pd) { + struct pet_data *pd=sd->pd; + pet_remove_map(sd); + sd->status.pet_id = 0; + sd->pd = NULL; + + if(sd->petDB == NULL) + return 1; + sd->pet.incuvate = 1; + memset(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid = sd->petDB->EggID; + tmp_item.identify = 1; + tmp_item.card[0] = 0xff00; + *((long *)(&tmp_item.card[1])) = sd->pet.pet_id; + 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); + } + if(battle_config.pet_status_support && sd->pet.intimate > 0) { + if(sd->bl.prev != NULL) + pc_calcstatus(sd,0); + else + pc_calcstatus(sd,2); + } + // ルートしたItemを落とさせる + pet_lootitem_drop(pd,sd); + + intif_save_petdata(sd->status.account_id,&sd->pet); + pc_makesavestatus(sd); + chrif_save(sd); + storage_storage_save(sd); + + 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); + + 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.prev = pd->bl.next = NULL; + 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,24); + pd->class = sd->pet.class; + pd->equip = sd->pet.equip; + pd->dir = sd->dir; + pd->speed = sd->petDB->speed; + pd->bl.subtype = MONS; + pd->bl.type = BL_PET; + memset(&pd->state,0,sizeof(pd->state)); + pd->state.state = MS_IDLE; + pd->state.change_walk_target = 0; + pd->timer = -1; + pd->target_id = 0; + pd->move_fail_count = 0; + pd->next_walktime = pd->attackabletime = pd->last_thinktime = gettick(); + pd->msd = sd; + + map_addiddb(&pd->bl); + + 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); + pd->lootitem=(struct item *)aCalloc(PETLOOT_SIZE,sizeof(struct item)); + pd->lootitem_count = 0; + pd->lootitem_weight = 0; + pd->lootitem_timer = gettick(); + return 0; +} + +int pet_birth_process(struct map_session_data *sd) +{ + nullpo_retr(1, 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); + pc_makesavestatus(sd); + chrif_save(sd); + storage_storage_save(sd); + map_addblock(&sd->pd->bl); + clif_spawnpet(sd->pd); + clif_send_petdata(sd,0,0); + clif_send_petdata(sd,5,0x14); + clif_pet_equip(sd->pd,sd->pet.equip); + clif_send_petstatus(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->bl.prev != NULL) { + map_addblock(&sd->pd->bl); + clif_spawnpet(sd->pd); + clif_send_petdata(sd,0,0); + clif_send_petdata(sd,5,0x14); +// 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) + pc_calcstatus(sd,0); + else + pc_calcstatus(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,*((long *)&sd->status.inventory[egg_index].card[1])); + else { + if(battle_config.error_log) + printf("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); + + md=(struct mob_data*)map_id2bl(target_id); + if(!md){ + clif_pet_rulet(sd,0); + return 1; + } + + i = search_petDB_index(md->class,PET_CLASS); + if(md == NULL || md->bl.type != BL_MOB || md->bl.prev == NULL || i < 0 || sd->catch_target_class != md->class) { + clif_pet_rulet(sd,0); + return 1; + } + + //target_idによる敵→卵判定 +// if(battle_config.etc_log) +// printf("mob_id = %d, mob_class = %d\n",md->bl.id,md->class); + //成功の場合 + pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - mob_db[md->class].lv)*30 + sd->paramc[5]*20)*(200 - md->hp*100/mob_db[md->class].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_catch_delete(md,0); + 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 + clif_pet_rulet(sd,0); + + return 0; +} + +int pet_get_egg(int account_id,int pet_id,int flag) +{ + 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); + 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] = 0xff00; + *((long *)(&tmp_item.card[1])) = pet_id; + tmp_item.card[3] = sd->pet.rename_flag; + 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); + + 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->pet.rename_flag == 1 && battle_config.pet_rename == 0) + return 1; + + for(i=0;i<24 && name[i];i++){ + if( !(name[i]&0xe0) || name[i]==0x7f) + return 1; + } + + pet_stop_walking(sd->pd,1); + memcpy(sd->pet.name,name,24); + memcpy(sd->pd->name,name,24); + clif_clearchar_area(&sd->pd->bl,0); + clif_spawnpet(sd->pd); + clif_send_petdata(sd,0,0); + clif_send_petdata(sd,5,0x14); + 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; + pc_calcstatus(sd,0); + clif_pet_equip(sd->pd,nameid); + } + + 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; + pc_calcstatus(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); + } + + return 0; +} + +int pet_food(struct map_session_data *sd) +{ + int i,k,t; + + nullpo_retr(1, 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) + pc_calcstatus(sd,0); + else + pc_calcstatus(sd,2); + } + } + else if(sd->pet.intimate > 1000) + sd->pet.intimate = 1000; + 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); + + speed = battle_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((c=map_getcell(pd->bl.m,x,y))!=1 && c!=5 && 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) + printf("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_unlocktarget(struct pet_data *pd) +{ + nullpo_retr(0, pd); + + pd->target_id=0; + + return 0; +} + +static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) +{ + struct map_session_data *sd = pd->msd; + struct mob_data *md = NULL; + int dist,i=0,dx,dy,ret; + int mode,race; + + nullpo_retr(0, pd); + + sd = pd->msd; + + if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL) + return 0; + + if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME) + return 0; + pd->last_thinktime=tick; + + if(pd->state.state == MS_DELAY || pd->bl.m != sd->bl.m) + return 0; + // ペットによるルート + if(!pd->target_id && pd->lootitem_count < PETLOOT_SIZE && pd->lootitem_count < pd->lootmax && pd->loot==1 && DIFF_TICK(gettick(),pd->lootitem_timer)>0) + map_foreachinarea(pet_ai_sub_hard_lootsearch,pd->bl.m, + 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(sd->bl.x,sd->bl.y,pd->bl.x,pd->bl.y); + if(dist > 12) { + if(pd->target_id > 0) + pet_unlocktarget(pd); + if(pd->timer != -1 && pd->state.state == MS_WALK && distance(pd->to_x,pd->to_y,sd->bl.x,sd->bl.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) { + mode=mob_db[pd->class].mode; + race=mob_db[pd->class].race; + md=(struct mob_data *)map_id2bl(pd->target_id); + if(md == NULL || md->bl.type != BL_MOB || pd->bl.m != md->bl.m || md->bl.prev == NULL || + distance(pd->bl.x,pd->bl.y,md->bl.x,md->bl.y) > 13) + pet_unlocktarget(pd); + else if(mob_db[pd->class].mexp <= 0 && !(mode&0x20) && (md->option & 0x06 && race!=4 && race!=6) ) + pet_unlocktarget(pd); + else if(!battle_check_range(&pd->bl,&md->bl,mob_db[pd->class].range)){ + if(pd->timer != -1 && pd->state.state == MS_WALK && distance(pd->to_x,pd->to_y,md->bl.x,md->bl.y) < 2) + return 0; + if( !pet_can_reach(pd,md->bl.x,md->bl.y)) + pet_unlocktarget(pd); + else { + i=0; + pd->speed = battle_get_speed(&pd->bl); + do { + if(i==0) { // 最初はAEGISと同じ方法で検索 + dx=md->bl.x - pd->bl.x; + dy=md->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=md->bl.x - pd->bl.x + rand()%3 - 1; + dy=md->bl.y - pd->bl.y + rand()%3 - 1; + } + ret=pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy); + i++; + } while(ret && i<5); + + if(ret) { // 移動不可能な所からの攻撃なら2歩下る + if(dx<0) dx=2; + else if(dx>0) dx=-2; + if(dy<0) dy=2; + else if(dy>0) dy=-2; + pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy); + } + } + } + else { + if(pd->state.state==MS_WALK) + pet_stop_walking(pd,1); + if(pd->state.state==MS_ATTACK) + return 0; + pet_changestate(pd,MS_ATTACK,0); + } + } + else if(pd->target_id > 0){ // ルート処理 + 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(pd->bl.x,pd->bl.y,bl_item->x,bl_item->y))>=5){ + // 遠すぎるかアイテムがなくなった + pet_unlocktarget(pd); + } + else if(dist){ + if(pd->timer != -1 && pd->state.state!=MS_ATTACK && (DIFF_TICK(pd->next_walktime,tick)<0 || distance(pd->to_x,pd->to_y,bl_item->x,bl_item->y) <= 0)) + return 0; // 既に移動中 + + pd->next_walktime=tick+500; + dx=bl_item->x - pd->bl.x; + dy=bl_item->y - pd->bl.y; + + ret=pet_walktoxy(pd,pd->bl.x+dx,pd->bl.y+dy); + } + else{ // アイテムまでたどり着いた + fitem = (struct flooritem_data *)bl_item; + if(pd->state.state==MS_ATTACK) + return 0; // 攻撃中 + if(pd->state.state==MS_WALK){ // 歩行中なら停止 + pet_stop_walking(pd,1); + } + if(pd->lootitem_count < PETLOOT_SIZE && pd->lootitem_count < pd->lootmax){ + memcpy(&pd->lootitem[pd->lootitem_count++],&fitem->item_data,sizeof(pd->lootitem[0])); + pd->lootitem_weight += itemdb_search(fitem->item_data.nameid)->weight*fitem->item_data.amount; + } + else if(pd->lootitem_count >= PETLOOT_SIZE || pd->lootitem_count >=pd->lootmax) { + pet_unlocktarget(pd); + return 0; + } + else { + if(pd->lootitem[0].card[0] == (short)0xff00) + intif_delete_petdata(*((long *)(&pd->lootitem[0].card[1]))); + for(i=0;i<PETLOOT_SIZE-1;i++) + memcpy(&pd->lootitem[i],&pd->lootitem[i+1],sizeof(pd->lootitem[0])); + memcpy(&pd->lootitem[PETLOOT_SIZE-1],&fitem->item_data,sizeof(pd->lootitem[0])); + } + map_clearflooritem(bl_item->id); + pet_unlocktarget(pd); + } + } + else { + if(dist <= 3 || (pd->timer != -1 && pd->state.state == MS_WALK && distance(pd->to_x,pd->to_y,sd->bl.x,sd->bl.y) < 3) ) + return 0; + pd->speed = battle_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 = battle_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 dist,*itc; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, pd=va_arg(ap,struct pet_data *)); + nullpo_retr(0, itc=va_arg(ap,int *)); + + if(!pd->target_id){ + struct flooritem_data *fitem = (struct flooritem_data *)bl; + struct map_session_data *sd = NULL; + // ルート権無し + if(fitem && fitem->first_get_id>0) + sd = map_id2sd(fitem->first_get_id); + // Removed [Valaris] + //if((pd->lootitem_weight + (itemdb_search(fitem->item_data.))->weight * fitem->item_data.amount) > battle_config.pet_weight) + // return 0; + + if(!pd->lootitem || (pd->lootitem_count >= PETLOOT_SIZE) || (pd->lootitem_count >= pd->lootmax) || (sd && sd->pd != pd)) + return 0; + if(bl->m == pd->bl.m && (dist=distance(pd->bl.x,pd->bl.y,bl->x,bl->y))<5){ + if( pet_can_reach(pd,bl->x,bl->y) // 到達可能性判定 + && rand()%1000<1000/(++(*itc)) ){ // 範囲内PCで等確率にする + pd->target_id=bl->id; + } + } + } + return 0; +} +int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd) +{ + int i,flag=0; + + if(pd){ + if(pd->lootitem) { + for(i=0;i<pd->lootitem_count;i++) { + struct delay_item_drop2 *ditem; + + ditem=(struct delay_item_drop2 *)aCalloc(1,sizeof(struct delay_item_drop2)); + memcpy(&ditem->item_data,&pd->lootitem[i],sizeof(pd->lootitem[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); + } + free(ditem); + } + else + add_timer(gettick()+540+i,pet_delay_item_drop2,(int)ditem,0); + } + pd->lootitem=NULL; + pd->lootitem=(struct item *)aCalloc(PETLOOT_SIZE,sizeof(struct item)); + pd->lootitem_count = 0; + pd->lootitem_weight = 0; + pd->lootitem_timer = gettick()+10000; // 10*1000msの間拾わない + } + } + return 1; +} + +int pet_delay_item_drop2(int tid,unsigned int tick,int id,int data) +{ + struct delay_item_drop2 *ditem; + + ditem=(struct delay_item_drop2 *)id; + + map_addflooritem(&ditem->item_data,ditem->item_data.amount,ditem->m,ditem->x,ditem->y,ditem->first_sd,ditem->second_sd,ditem->third_sd,0); + + free(ditem); + return 0; +} + +/*========================================== + * pet bonus giving skills [Valaris] + *------------------------------------------ + */ + +int pet_skill_bonus(struct map_session_data *sd,struct pet_data *pd,int type,int val,int duration,int timer,int data) +{ + if(pd==NULL || sd==NULL) + return 1; + + pd->skillbonustype=type; + pd->skillbonusval=val; + pd->skillduration=duration; + pd->skilltimer=timer; + + pd->skillbonustimer=add_timer(gettick()+pd->skilltimer*1000,pet_skill_bonus_timer,sd->bl.id,0); + + return 0; + +} + +int pet_skill_bonus_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->bl.type!=BL_PC) + return 1; + + pd=sd->pd; + + if(pd==NULL || pd->bl.type!=BL_PET) + return 1; + + if(pd->skillbonustimer != tid) + return 0; + + pd->skillbonustimer=-1; + + pc_bonus(sd,pd->skillbonustype,pd->skillbonusval); + if(pd->skillbonustype < 56) clif_updatestatus(sd,pd->skillbonustype); + pd->skillbonusduration=add_timer(gettick()+pd->skillduration*1000,pet_skill_bonus_duration,sd->bl.id,0); + + return 0; +} + +int pet_skill_bonus_duration(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->bl.type!=BL_PC) + return 1; + + pd=sd->pd; + + if(pd==NULL || pd->bl.type!=BL_PET) + return 1; + + if(pd->skillbonusduration != tid) + return 0; + + pd->skillbonusduration=-1; + + pc_bonus(sd,pd->skillbonustype,-pd->skillbonusval); + if(pd->skillbonustype < 56) clif_updatestatus(sd,pd->skillbonustype); + + pet_skill_bonus(sd,pd,pd->skillbonustype,pd->skillbonusval,pd->skillduration,pd->skilltimer,0); + + return 0; +} + +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->bl.type!=BL_PC) + return 1; + + pd=sd->pd; + + if(pd==NULL || pd->bl.type!=BL_PET) + return 1; + + if(pd->skillbonustimer != tid) + return 0; + + if(sd->sc_data[pd->skilltype].timer != -1) + skill_status_change_end(&sd->bl,pd->skilltype,-1); + + pd->skillbonustimer=add_timer(gettick()+pd->skilltimer*1000,pet_recovery_timer,sd->bl.id,0); + + 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; + + if(sd==NULL || sd->bl.type!=BL_PC) + return 1; + + pd=sd->pd; + + if(pd==NULL || pd->bl.type!=BL_PET) + return 1; + + if(pd->skillbonustimer != tid) + return 0; + + if(sd->status.hp < sd->status.max_hp * pd->skilltype/100) { + clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->skillval,1); + pc_heal(sd,pd->skillval,0); + } + + pd->skillbonustimer=add_timer(gettick()+pd->skilltimer*1000,pet_heal_timer,sd->bl.id,0); + + return 0; +} + +int pet_mag_timer(int tid,unsigned int tick,int id,int data) +{ + struct pet_data *pd; + struct map_session_data *sd=(struct map_session_data*)map_id2bl(id); + + if(sd==NULL || sd->bl.type!=BL_PC) + return 1; + + pd=sd->pd; + + if(pd==NULL || pd->bl.type!=BL_PET) + return 1; + + if(pd->skillbonustimer != tid) + return 0; + + if(sd->status.hp < sd->status.max_hp * pd->skilltype/100 && sd->status.sp < sd->status.max_sp * pd->skillduration/100) { + clif_skill_nodamage(&pd->bl,&sd->bl,PR_MAGNIFICAT,pd->skillval,1); + skill_status_change_start(&sd->bl,SkillStatusChangeTable[PR_MAGNIFICAT],pd->skillval,0,0,0,skill_get_time(PR_MAGNIFICAT,pd->skillval),0 ); + } + + pd->skillbonustimer=add_timer(gettick()+pd->skilltimer*1000,pet_mag_timer,sd->bl.id,0); + + return 0; +} + +int pet_skillattack_timer(int tid,unsigned int tick,int id,int data) +{ + struct mob_data *md; + struct map_session_data *sd=(struct map_session_data*)map_id2bl(id); + struct pet_data *pd; + + if(sd==NULL || sd->bl.type!=BL_PC) + return 1; + + pd=sd->pd; + + if(pd==NULL || pd->bl.type!=BL_PET) + return 1; + + if(pd->skillbonustimer != tid) + return 0; + + md=(struct mob_data *)map_id2bl(sd->attacktarget); + if(md == NULL || md->bl.type != BL_MOB || pd->bl.m != md->bl.m || md->bl.prev == NULL || + distance(pd->bl.x,pd->bl.y,md->bl.x,md->bl.y) > 6) { + pd->target_id=0; + pd->skillbonustimer=add_timer(gettick()+100,pet_skillattack_timer,sd->bl.id,pd->skillduration); + return 0; + } + + if(md && rand()%100 < sd->pet.intimate*pd->skilltimer/100 ) { + if(pd->skilltype==6 || pd->skilltype==176) { + skill_castend_nodamage_id(&pd->bl,&md->bl,pd->skilltype,pd->skillval,tick,0); + } + + else if(pd->skilltype==110){ + skill_castend_pos2(&pd->bl,md->bl.x,md->bl.y,pd->skilltype,pd->skillval,tick,0); + } + + else if(pd->skilltype==91) { + skill_castend_pos2(&pd->bl,md->bl.x,md->bl.y,pd->skilltype,pd->skillval+rand()%100,tick,0); + } + else + skill_castend_damage_id(&pd->bl,&md->bl,pd->skilltype,pd->skillval,tick,0); + pd->skillbonustimer=add_timer(gettick()+1000,pet_skillattack_timer,sd->bl.id,0); + return 0; + } + + pd->skillbonustimer=add_timer(gettick()+100,pet_skillattack_timer,sd->bl.id,0); + + return 0; +} + +/*========================================== + *ペットデータ読み込み + *------------------------------------------ + */ +int read_petdb() +{ + FILE *fp; + char line[1024]; + int i; + int j=0; + char *filename[]={"db/pet_db.txt","db/pet_db2.txt"}; + + memset(pet_db,0,sizeof(pet_db)); + for(i=0;i<2;i++){ + fp=fopen(filename[i],"r"); + if(fp==NULL){ + if(i>0) + continue; + printf("can't read %s\n",filename[i]); + return -1; + } + while(fgets(line,1020,fp)){ + int nameid,i; + char *str[32],*p,*np; + + if(line[0] == '/' && line[1] == '/') + continue; + + for(i=0,p=line;i<20;i++){ + if((np=strchr(p,','))!=NULL){ + str[i]=p; + *np=0; + p=np+1; + } else { + str[i]=p; + p+=strlen(p); + } + } + + nameid=atoi(str[0]); + if(nameid<=0 || nameid>2000) + continue; + + //MobID,Name,JName,ItemID,EggID,AcceID,FoodID,"Fullness (1回の餌での満腹度増加率%)","HungryDeray (/min)","R_Hungry (空腹時餌やり親密度増加率%)","R_Full (とても満腹時餌やり親密度減少率%)","Intimate (捕獲時親密度%)","Die (死亡時親密度減少率%)","Capture (捕獲率%)",(Name) + pet_db[j].class = nameid; + memcpy(pet_db[j].name,str[1],24); + memcpy(pet_db[j].jname,str[2],24); + 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(np,0); + j++; + } + fclose(fp); + printf("read %s done (count=%d)\n",filename[i],j); + } + return 0; +} + +/*========================================== + * スキル関係初期化処理 + *------------------------------------------ + */ +int do_init_pet(void) +{ + 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_skill_bonus_duration,"pet_skill_bonus_duration"); // [Valaris] + add_timer_func_list(pet_recovery_timer,"pet_recovery_timer"); // [Valaris] + add_timer_func_list(pet_mag_timer,"pet_mag_timer"); // [Valaris] + add_timer_func_list(pet_heal_timer,"pet_heal_timer"); // [Valaris] + add_timer_func_list(pet_skillattack_timer,"pet_skillattack_timer"); // [Valaris] + add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME); + + return 0; +} + diff --git a/src/map/pet.h b/src/map/pet.h new file mode 100644 index 0000000..365a449 --- /dev/null +++ b/src/map/pet.h @@ -0,0 +1,69 @@ +// $Id: pet.h,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $ +#ifndef _PET_H_ +#define _PET_H_ + +#define MAX_PET_DB 100 +#define PETLOOT_SIZE 20 // [Valaris] + +struct pet_db { + int class; + char name[24],jname[24]; + int itemID; + int EggID; + int AcceID; + int 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; + 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_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 pet_skill_bonus(struct map_session_data *sd,struct pet_data *pd,int type,int val,int duration,int timer,int data); +int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data); // [Valaris] +int pet_skill_bonus_duration(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_mag_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_skillattack_timer(int tid,unsigned int tick,int id,int data); // [Valaris] + +int do_init_pet(void); + +#endif + diff --git a/src/map/script.c b/src/map/script.c new file mode 100644 index 0000000..a9a171b --- /dev/null +++ b/src/map/script.c @@ -0,0 +1,6700 @@ +// $Id: script.c 148 2004-09-30 14:05:37Z MouseJstr $ +//#define DEBUG_FUNCIN +//#define DEBUG_DISP +//#define DEBUG_RUN + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#ifndef LCCWIN32 +#include <sys/time.h> +#endif + +#include <time.h> + +#include "socket.h" +#include "timer.h" +#include "malloc.h" +#include "lock.h" + +#include "map.h" +#include "clif.h" +#include "chrif.h" +#include "itemdb.h" +#include "pc.h" +#include "script.h" +#include "storage.h" +#include "mob.h" +#include "npc.h" +#include "pet.h" +#include "intif.h" +#include "db.h" +#include "skill.h" +#include "chat.h" +#include "battle.h" +#include "party.h" +#include "guild.h" +#include "lock.h" +#include "atcommand.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define SCRIPT_BLOCK_SIZE 256 +enum { LABEL_NEXTLINE=1,LABEL_START }; +static unsigned char * script_buf; +static int script_pos,script_size; + +char *str_buf; +int str_pos,str_size; +static struct { + int type; + int str; + int backpatch; + int label; + int (*func)(); + int val; + int next; +} *str_data; +int str_num=LABEL_START,str_data_size; +int str_hash[16]; + +static struct dbt *mapreg_db=NULL; +static struct dbt *mapregstr_db=NULL; +static int mapreg_dirty=-1; +char mapreg_txt[256]="save/mapreg.txt"; +#define MAPREG_AUTOSAVE_INTERVAL (10*1000) + +static struct dbt *scriptlabel_db=NULL; +static struct dbt *userfunc_db=NULL; + +struct dbt* script_get_label_db(){ return scriptlabel_db; } +struct dbt* script_get_userfunc_db(){ if(!userfunc_db) userfunc_db=strdb_init(50); return userfunc_db; } + +int scriptlabel_final(void *k,void *d,va_list ap){ return 0; } +static char pos[11][100] = {"頭","体","左手","右手","ローブ","靴","アクセサリー1","アクセサリー2","頭2","頭3","装着していない"}; + +static struct Script_Config { + int warn_func_no_comma; + int warn_cmd_no_comma; + int warn_func_mismatch_paramnum; + int warn_cmd_mismatch_paramnum; + int check_cmdcount; + int check_gotocount; +} script_config; +static int parse_cmd_if=0; +static int parse_cmd; + +/*========================================== + * ローカルプロトタイプ宣言 (必要な物のみ) + *------------------------------------------ + */ +unsigned char* parse_subexpr(unsigned char *,int); +int buildin_mes(struct script_state *st); +int buildin_goto(struct script_state *st); +int buildin_callsub(struct script_state *st); +int buildin_callfunc(struct script_state *st); +int buildin_return(struct script_state *st); +int buildin_getarg(struct script_state *st); +int buildin_next(struct script_state *st); +int buildin_close(struct script_state *st); +int buildin_close2(struct script_state *st); +int buildin_menu(struct script_state *st); +int buildin_rand(struct script_state *st); +int buildin_warp(struct script_state *st); +int buildin_areawarp(struct script_state *st); +int buildin_heal(struct script_state *st); +int buildin_itemheal(struct script_state *st); +int buildin_percentheal(struct script_state *st); +int buildin_jobchange(struct script_state *st); +int buildin_input(struct script_state *st); +int buildin_setlook(struct script_state *st); +int buildin_set(struct script_state *st); +int buildin_setarray(struct script_state *st); +int buildin_cleararray(struct script_state *st); +int buildin_copyarray(struct script_state *st); +int buildin_getarraysize(struct script_state *st); +int buildin_deletearray(struct script_state *st); +int buildin_getelementofarray(struct script_state *st); +int buildin_if(struct script_state *st); +int buildin_getitem(struct script_state *st); +int buildin_getitem2(struct script_state *st); +int buildin_makeitem(struct script_state *st); +int buildin_delitem(struct script_state *st); +int buildin_viewpoint(struct script_state *st); +int buildin_countitem(struct script_state *st); +int buildin_checkweight(struct script_state *st); +int buildin_readparam(struct script_state *st); +int buildin_getcharid(struct script_state *st); +int buildin_getpartyname(struct script_state *st); +int buildin_getpartymember(struct script_state *st); +int buildin_getguildname(struct script_state *st); +int buildin_getguildmaster(struct script_state *st); +int buildin_getguildmasterid(struct script_state *st); +int buildin_strcharinfo(struct script_state *st); +int buildin_getequipid(struct script_state *st); +int buildin_getequipname(struct script_state *st); +int buildin_getbrokenid(struct script_state *st); // [Valaris] +int buildin_repair(struct script_state *st); // [Valaris] +int buildin_getequipisequiped(struct script_state *st); +int buildin_getequipisenableref(struct script_state *st); +int buildin_getequipisidentify(struct script_state *st); +int buildin_getequiprefinerycnt(struct script_state *st); +int buildin_getequipweaponlv(struct script_state *st); +int buildin_getequippercentrefinery(struct script_state *st); +int buildin_successrefitem(struct script_state *st); +int buildin_failedrefitem(struct script_state *st); +int buildin_cutin(struct script_state *st); +int buildin_cutincard(struct script_state *st); +int buildin_statusup(struct script_state *st); +int buildin_statusup2(struct script_state *st); +int buildin_bonus(struct script_state *st); +int buildin_bonus2(struct script_state *st); +int buildin_bonus3(struct script_state *st); +int buildin_skill(struct script_state *st); +int buildin_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_doevent(struct script_state *st); +int buildin_donpcevent(struct script_state *st); +int buildin_addtimer(struct script_state *st); +int buildin_deltimer(struct script_state *st); +int buildin_addtimercount(struct script_state *st); +int buildin_initnpctimer(struct script_state *st); +int buildin_stopnpctimer(struct script_state *st); +int buildin_startnpctimer(struct script_state *st); +int buildin_setnpctimer(struct script_state *st); +int buildin_getnpctimer(struct script_state *st); +int buildin_announce(struct script_state *st); +int buildin_mapannounce(struct script_state *st); +int buildin_areaannounce(struct script_state *st); +int buildin_getusers(struct script_state *st); +int buildin_getmapusers(struct script_state *st); +int buildin_getareausers(struct script_state *st); +int buildin_getareadropitem(struct script_state *st); +int buildin_enablenpc(struct script_state *st); +int buildin_disablenpc(struct script_state *st); +int buildin_enablearena(struct script_state *st); // Added by RoVeRT +int buildin_disablearena(struct script_state *st); // Added by RoVeRT +int buildin_hideoffnpc(struct script_state *st); +int buildin_hideonnpc(struct script_state *st); +int buildin_sc_start(struct script_state *st); +int buildin_sc_start2(struct script_state *st); +int buildin_sc_end(struct script_state *st); +int buildin_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_getitemname(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_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 [Valaris] +int buildin_npcskilleffect(struct script_state *st); // skill effects for npcs [Valaris] +int buildin_specialeffect(struct script_state *st); // special effect script [Valaris] +int buildin_specialeffect2(struct script_state *st); // special effect script [Valaris] +int buildin_nude(struct script_state *st); // nude [Valaris] +int buildin_gmcommand(struct script_state *st); // [MouseJstr] +int buildin_movenpc(struct script_state *st); // [MouseJstr] +int buildin_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] + + +void push_val(struct script_stack *stack,int type,int val); +int run_func(struct script_state *st); + +int mapreg_setreg(int num,int val); +int mapreg_setregstr(int num,const char *str); + +struct { + int (*func)(); + char *name; + char *arg; +} buildin_func[]={ + {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_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_if,"if","i*"}, + {buildin_getitem,"getitem","ii**"}, + {buildin_getitem2,"getitem2","iiiiiiiii*"}, + {buildin_makeitem,"makeitem","iisii"}, + {buildin_delitem,"delitem","ii"}, + {buildin_cutin,"cutin","si"}, + {buildin_cutincard,"cutincard","i"}, + {buildin_viewpoint,"viewpoint","iiiii"}, + {buildin_heal,"heal","ii"}, + {buildin_itemheal,"itemheal","ii"}, + {buildin_percentheal,"percentheal","ii"}, + {buildin_rand,"rand","i*"}, + {buildin_countitem,"countitem","i"}, + {buildin_checkweight,"checkweight","ii"}, + {buildin_readparam,"readparam","i*"}, + {buildin_getcharid,"getcharid","i*"}, + {buildin_getpartyname,"getpartyname","i"}, + {buildin_getpartymember,"getpartymember","i"}, + {buildin_getguildname,"getguildname","i"}, + {buildin_getguildmaster,"getguildmaster","i"}, + {buildin_getguildmasterid,"getguildmasterid","i"}, + {buildin_strcharinfo,"strcharinfo","i"}, + {buildin_getequipid,"getequipid","i"}, + {buildin_getequipname,"getequipname","i"}, + {buildin_getbrokenid,"getbrokenid","i"}, // [Valaris] + {buildin_repair,"repair","i"}, // [Valaris] + {buildin_getequipisequiped,"getequipisequiped","i"}, + {buildin_getequipisenableref,"getequipisenableref","i"}, + {buildin_getequipisidentify,"getequipisidentify","i"}, + {buildin_getequiprefinerycnt,"getequiprefinerycnt","i"}, + {buildin_getequipweaponlv,"getequipweaponlv","i"}, + {buildin_getequippercentrefinery,"getequippercentrefinery","i"}, + {buildin_successrefitem,"successrefitem","i"}, + {buildin_failedrefitem,"failedrefitem","i"}, + {buildin_statusup,"statusup","i"}, + {buildin_statusup2,"statusup2","ii"}, + {buildin_bonus,"bonus","ii"}, + {buildin_bonus2,"bonus2","iii"}, + {buildin_bonus3,"bonus3","iiii"}, + {buildin_skill,"skill","ii*"}, + {buildin_guildskill,"guildskill","ii"}, + {buildin_getskilllv,"getskilllv","i"}, + {buildin_getgdskilllv,"getgdskilllv","ii"}, + {buildin_basicskillcheck,"basicskillcheck","*"}, + {buildin_getgmlevel,"getgmlevel","*"}, + {buildin_end,"end",""}, + {buildin_end,"break",""}, + {buildin_checkoption,"checkoption","i"}, + {buildin_setoption,"setoption","i"}, + {buildin_setcart,"setcart",""}, + {buildin_checkcart,"checkcart","*"}, //fixed by Lupus (added '*') + {buildin_setfalcon,"setfalcon",""}, + {buildin_checkfalcon,"checkfalcon","*"}, //fixed by Lupus (fixed wrong pointer, added '*') + {buildin_setriding,"setriding",""}, + {buildin_checkriding,"checkriding","*"}, //fixed by Lupus (fixed wrong pointer, added '*') + {buildin_savepoint,"save","sii"}, + {buildin_savepoint,"savepoint","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_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_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_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_getitemname,"getitemname","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_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","iii"}, // [Valaris] + {buildin_petmag,"petmag","iiii"}, // [Valaris] + {buildin_petskillattack,"petskillattack","iiii"}, // [Valaris] + {buildin_npcskilleffect,"npcskilleffect","iiii"}, // npc skill effect [Valaris] + {buildin_specialeffect,"specialeffect","i"}, // npc skill effect [Valaris] + {buildin_specialeffect2,"specialeffect2","i"}, // skill effect on players[Valaris] + {buildin_nude,"nude",""}, // nude command [Valaris] + {buildin_mapwarp,"mapwarp","ssii"}, // Added by RoVeRT + {buildin_inittimer,"inittimer",""}, + {buildin_stoptimer,"stoptimer",""}, + {buildin_cmdothernpc,"cmdothernpc","ss"}, + {buildin_gmcommand,"gmcommand","*"}, // [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"}, // End Additions + {NULL,NULL,NULL}, +}; +int buildin_message(struct script_state *st); // [MouseJstr] + + +enum { + C_NOP,C_POS,C_INT,C_PARAM,C_FUNC,C_STR,C_CONSTSTR,C_ARG, + C_NAME,C_EOL, C_RETINFO, + + C_LOR,C_LAND,C_LE,C_LT,C_GE,C_GT,C_EQ,C_NE, //operator + C_XOR,C_OR,C_AND,C_ADD,C_SUB,C_MUL,C_DIV,C_MOD,C_NEG,C_LNOT,C_NOT,C_R_SHIFT,C_L_SHIFT +}; + +/*========================================== + * 文字列のハッシュを計算 + *------------------------------------------ + */ +static int calc_hash(const unsigned char *p) +{ + int h=0; + while(*p){ + h=(h<<1)+(h>>3)+(h>>5)+(h>>8); + h+=*p++; + } + return h&15; +} + +/*========================================== + * str_dataの中に名前があるか検索する + *------------------------------------------ + */ +// 既存のであれば番号、無ければ-1 +static int search_str(const unsigned char *p) +{ + int i; + i=str_hash[calc_hash(p)]; + while(i){ + if(strcmp(str_buf+str_data[i].str,p)==0){ + return i; + } + i=str_data[i].next; + } + return -1; +} + +/*========================================== + * str_dataに名前を登録 + *------------------------------------------ + */ +// 既存のであれば番号、無ければ登録して新規番号 +static int add_str(const unsigned char *p) +{ + int i; + char *lowcase; + + lowcase=strdup(p); + for(i=0;lowcase[i];i++) + lowcase[i]=tolower(lowcase[i]); + if((i=search_str(lowcase))>=0){ + free(lowcase); + return i; + } + free(lowcase); + + i=calc_hash(p); + if(str_hash[i]==0){ + str_hash[i]=str_num; + } else { + i=str_hash[i]; + for(;;){ + if(strcmp(str_buf+str_data[i].str,p)==0){ + return i; + } + if(str_data[i].next==0) + break; + i=str_data[i].next; + } + str_data[i].next=str_num; + } + if(str_num>=str_data_size){ + str_data_size+=128; + str_data=aRealloc(str_data,sizeof(str_data[0])*str_data_size); + memset(str_data + (str_data_size - 128), '\0', 128); + } + while(str_pos+strlen(p)+1>=str_size){ + str_size+=256; + str_buf=(char *)aRealloc(str_buf,str_size); + memset(str_buf + (str_size - 256), '\0', 256); + } + strcpy(str_buf+str_pos,p); + str_data[str_num].type=C_NOP; + str_data[str_num].str=str_pos; + str_data[str_num].next=0; + str_data[str_num].func=NULL; + str_data[str_num].backpatch=-1; + str_data[str_num].label=-1; + str_pos+=strlen(p)+1; + return str_num++; +} + + +/*========================================== + * スクリプトバッファサイズの確認と拡張 + *------------------------------------------ + */ +static void check_script_buf(int size) +{ + if(script_pos+size>=script_size){ + script_size+=SCRIPT_BLOCK_SIZE; + script_buf=(char *)aRealloc(script_buf,script_size); + memset(script_buf + script_size - SCRIPT_BLOCK_SIZE, '\0', + SCRIPT_BLOCK_SIZE); + } +} + +/*========================================== + * スクリプトバッファに1バイト書き込む + *------------------------------------------ + */ +static void add_scriptb(int a) +{ + check_script_buf(1); + script_buf[script_pos++]=a; +} + +/*========================================== + * スクリプトバッファにデータタイプを書き込む + *------------------------------------------ + */ +static void add_scriptc(int a) +{ + while(a>=0x40){ + add_scriptb((a&0x3f)|0x40); + a=(a-0x40)>>6; + } + add_scriptb(a&0x3f); +} + +/*========================================== + * スクリプトバッファに整数を書き込む + *------------------------------------------ + */ +static void add_scripti(int a) +{ + while(a>=0x40){ + add_scriptb(a|0xc0); + a=(a-0x40)>>6; + } + add_scriptb(a|0x80); +} + +/*========================================== + * スクリプトバッファにラベル/変数/関数を書き込む + *------------------------------------------ + */ +// 最大16Mまで +static void add_scriptl(int l) +{ + int backpatch = str_data[l].backpatch; + + switch(str_data[l].type){ + case C_POS: + add_scriptc(C_POS); + add_scriptb(str_data[l].label); + add_scriptb(str_data[l].label>>8); + add_scriptb(str_data[l].label>>16); + break; + case C_NOP: + // ラベルの可能性があるのでbackpatch用データ埋め込み + add_scriptc(C_NAME); + str_data[l].backpatch=script_pos; + add_scriptb(backpatch); + add_scriptb(backpatch>>8); + add_scriptb(backpatch>>16); + break; + case C_INT: + add_scripti(str_data[l].val); + break; + default: + // もう他の用途と確定してるので数字をそのまま + add_scriptc(C_NAME); + add_scriptb(l); + add_scriptb(l>>8); + add_scriptb(l>>16); + break; + } +} + +/*========================================== + * ラベルを解決する + *------------------------------------------ + */ +void set_label(int l,int pos) +{ + int i,next; + + str_data[l].type=C_POS; + str_data[l].label=pos; + for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){ + next=(*(int*)(script_buf+i)) & 0x00ffffff; + script_buf[i-1]=C_POS; + script_buf[i]=pos; + script_buf[i+1]=pos>>8; + script_buf[i+2]=pos>>16; + i=next; + } +} + +/*========================================== + * スペース/コメント読み飛ばし + *------------------------------------------ + */ +static unsigned char *skip_space(unsigned char *p) +{ + while(1){ + while(isspace(*p)) + p++; + if(p[0]=='/' && p[1]=='/'){ + while(*p && *p!='\n') + p++; + } else if(p[0]=='/' && p[1]=='*'){ + p++; + while(*p && (p[-1]!='*' || p[0]!='/')) + p++; + if(*p) p++; + } else + break; + } + return p; +} + +/*========================================== + * 1単語スキップ + *------------------------------------------ + */ +static unsigned char *skip_word(unsigned char *p) +{ + // prefix + if(*p=='$') p++; // MAP鯖内共有変数用 + if(*p=='@') p++; // 一時的変数用(like weiss) + if(*p=='#') p++; // account変数用 + if(*p=='#') p++; // ワールドaccount変数用 + if(*p=='l') p++; // 一時的変数用(like weiss) + + while(isalnum(*p)||*p=='_'|| *p>=0x81) + if(*p>=0x81 && p[1]){ + p+=2; + } else + p++; + + // postfix + if(*p=='$') p++; // 文字列変数 + + return p; +} + +static unsigned char *startptr; +static int startline; + +/*========================================== + * エラーメッセージ出力 + *------------------------------------------ + */ +static void disp_error_message(const char *mes,const unsigned char *pos) +{ + int line,c=0,i; + unsigned char *p,*linestart,*lineend; + + for(line=startline,p=startptr;p && *p;line++){ + linestart=p; + lineend=strchr(p,'\n'); + if(lineend){ + c=*lineend; + *lineend=0; + } + if(lineend==NULL || pos<lineend){ + printf("%s line %d : ",mes,line); + for(i=0;(linestart[i]!='\r') && (linestart[i]!='\n') && linestart[i];i++){ + if(linestart+i!=pos) + printf("%c",linestart[i]); + else + printf("\'%c\'",linestart[i]); + } + printf("\a\n"); + if(lineend) + *lineend=c; + return; + } + *lineend=c; + p=lineend+1; + } +} + +/*========================================== + * 項の解析 + *------------------------------------------ + */ +unsigned char* parse_simpleexpr(unsigned char *p) +{ + int i; + p=skip_space(p); + +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + printf("parse_simpleexpr %s\n",p); +#endif + if(*p==';' || *p==','){ + disp_error_message("unexpected expr end",p); + exit(1); + } + if(*p=='('){ + + p=parse_subexpr(p+1,-1); + p=skip_space(p); + if((*p++)!=')'){ + disp_error_message("unmatch ')'",p); + exit(1); + } + } else if(isdigit(*p) || ((*p=='-' || *p=='+') && isdigit(p[1]))){ + char *np; + i=strtoul(p,&np,0); + add_scripti(i); + p=np; + } else if(*p=='"'){ + add_scriptc(C_STR); + p++; + while(*p && *p!='"'){ + if(p[-1]<=0x7e && *p=='\\') + p++; + else if(*p=='\n'){ + disp_error_message("unexpected newline @ string",p); + exit(1); + } + add_scriptb(*p++); + } + if(!*p){ + disp_error_message("unexpected eof @ string",p); + exit(1); + } + add_scriptb(0); + p++; //'"' + } else { + int c,l; + char *p2; + // label , register , function etc + if(skip_word(p)==p){ + disp_error_message("unexpected character",p); + exit(1); + } + p2=skip_word(p); + c=*p2; *p2=0; // 名前をadd_strする + l=add_str(p); + + parse_cmd=l; // warn_*_mismatch_paramnumのために必要 + if(l==search_str("if")) // warn_cmd_no_commaのために必要 + parse_cmd_if++; +/* + // 廃止予定のl14/l15,およびプレフィックスlの警告 + if( strcmp(str_buf+str_data[l].str,"l14")==0 || + strcmp(str_buf+str_data[l].str,"l15")==0 ){ + disp_error_message("l14 and l15 is DEPRECATED. use @menu instead of l15.",p); + }else if(str_buf[str_data[l].str]=='l'){ + disp_error_message("prefix 'l' is DEPRECATED. use prefix '@' instead.",p2); + } +*/ + *p2=c; p=p2; + + if(str_data[l].type!=C_FUNC && c=='['){ + // array(name[i] => getelementofarray(name,i) ) + add_scriptl(search_str("getelementofarray")); + add_scriptc(C_ARG); + add_scriptl(l); + p=parse_subexpr(p+1,-1); + p=skip_space(p); + if((*p++)!=']'){ + disp_error_message("unmatch ']'",p); + exit(1); + } + add_scriptc(C_FUNC); + }else + add_scriptl(l); + + } + +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + printf("parse_simpleexpr end %s\n",p); +#endif + return p; +} + +/*========================================== + * 式の解析 + *------------------------------------------ + */ +unsigned char* parse_subexpr(unsigned char *p,int limit) +{ + int op,opl,len; + char *tmpp; + +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + printf("parse_subexpr %s\n",p); +#endif + p=skip_space(p); + + if(*p=='-'){ + tmpp=skip_space(p+1); + if(*tmpp==';' || *tmpp==','){ + add_scriptl(LABEL_NEXTLINE); + p++; + return p; + } + } + tmpp=p; + if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){ + p=parse_subexpr(p+1,100); + add_scriptc(op); + } else + p=parse_simpleexpr(p); + p=skip_space(p); + while(((op=C_ADD,opl=6,len=1,*p=='+') || + (op=C_SUB,opl=6,len=1,*p=='-') || + (op=C_MUL,opl=7,len=1,*p=='*') || + (op=C_DIV,opl=7,len=1,*p=='/') || + (op=C_MOD,opl=7,len=1,*p=='%') || + (op=C_FUNC,opl=8,len=1,*p=='(') || + (op=C_LAND,opl=1,len=2,*p=='&' && p[1]=='&') || + (op=C_AND,opl=5,len=1,*p=='&') || + (op=C_LOR,opl=0,len=2,*p=='|' && p[1]=='|') || + (op=C_OR,opl=4,len=1,*p=='|') || + (op=C_XOR,opl=3,len=1,*p=='^') || + (op=C_EQ,opl=2,len=2,*p=='=' && p[1]=='=') || + (op=C_NE,opl=2,len=2,*p=='!' && p[1]=='=') || + (op=C_R_SHIFT,opl=5,len=2,*p=='>' && p[1]=='>') || + (op=C_GE,opl=2,len=2,*p=='>' && p[1]=='=') || + (op=C_GT,opl=2,len=1,*p=='>') || + (op=C_L_SHIFT,opl=5,len=2,*p=='<' && p[1]=='<') || + (op=C_LE,opl=2,len=2,*p=='<' && p[1]=='=') || + (op=C_LT,opl=2,len=1,*p=='<')) && opl>limit){ + p+=len; + if(op==C_FUNC){ + int i=0,func=parse_cmd; + const char *plist[128]; + + if( str_data[func].type!=C_FUNC ){ + disp_error_message("expect function",tmpp); + exit(0); + } + + add_scriptc(C_ARG); + do { + plist[i]=p; + p=parse_subexpr(p,-1); + p=skip_space(p); + if(*p==',') p++; + else if(*p!=')' && script_config.warn_func_no_comma){ + disp_error_message("expect ',' or ')' at func params",p); + } + p=skip_space(p); + i++; + } while(*p && *p!=')' && i<128); + plist[i]=p; + if(*(p++)!=')'){ + disp_error_message("func request '(' ')'",p); + exit(1); + } + + if( str_data[func].type==C_FUNC && script_config.warn_func_mismatch_paramnum){ + const char *arg=buildin_func[str_data[func].val].arg; + int j=0; + for(j=0;arg[j];j++) if(arg[j]=='*')break; + if( (arg[j]==0 && i!=j) || (arg[j]=='*' && i<j) ){ + disp_error_message("illegal number of parameters",plist[(i<j)?i:j]); + } + } + } else { + p=parse_subexpr(p,opl); + } + add_scriptc(op); + p=skip_space(p); + } +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + printf("parse_subexpr end %s\n",p); +#endif + return p; /* return first untreated operator */ +} + +/*========================================== + * 式の評価 + *------------------------------------------ + */ +unsigned char* parse_expr(unsigned char *p) +{ +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + printf("parse_expr %s\n",p); +#endif + switch(*p){ + case ')': case ';': case ':': case '[': case ']': + case '}': + disp_error_message("unexpected char",p); + exit(1); + } + p=parse_subexpr(p,-1); +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + printf("parse_expr end %s\n",p); +#endif + return p; +} + +/*========================================== + * 行の解析 + *------------------------------------------ + */ +unsigned char* parse_line(unsigned char *p) +{ + int i=0,cmd; + const char *plist[128]; + char *p2; + + p=skip_space(p); + if(*p==';') + return p; + + parse_cmd_if=0; // warn_cmd_no_commaのために必要 + + // 最初は関数名 + p2=p; + p=parse_simpleexpr(p); + p=skip_space(p); + + cmd=parse_cmd; + if( str_data[cmd].type!=C_FUNC ){ + disp_error_message("expect command",p2); +// exit(0); + } + + add_scriptc(C_ARG); + while(p && *p && *p!=';' && i<128){ + plist[i]=p; + + p=parse_expr(p); + p=skip_space(p); + // 引数区切りの,処理 + if(*p==',') p++; + else if(*p!=';' && script_config.warn_cmd_no_comma && parse_cmd_if*2<=i ){ + disp_error_message("expect ',' or ';' at cmd params",p); + } + p=skip_space(p); + i++; + } + plist[i]=p; + if(!p || *(p++)!=';'){ + disp_error_message("need ';'",p); + exit(1); + } + add_scriptc(C_FUNC); + + if( str_data[cmd].type==C_FUNC && script_config.warn_cmd_mismatch_paramnum){ + const char *arg=buildin_func[str_data[cmd].val].arg; + int j=0; + for(j=0;arg[j];j++) if(arg[j]=='*')break; + if( (arg[j]==0 && i!=j) || (arg[j]=='*' && i<j) ){ + disp_error_message("illegal number of parameters",plist[(i<j)?i:j]); + } + } + + + return p; +} + +/*========================================== + * 組み込み関数の追加 + *------------------------------------------ + */ +static void add_buildin_func(void) +{ + int i,n; + for(i=0;buildin_func[i].func;i++){ + n=add_str(buildin_func[i].name); + str_data[n].type=C_FUNC; + str_data[n].val=i; + str_data[n].func=buildin_func[i].func; + } +} + +/*========================================== + * 定数データベースの読み込み + *------------------------------------------ + */ +static void read_constdb(void) +{ + FILE *fp; + char line[1024],name[1024]; + int val,n,i,type; + + fp=fopen("db/const.txt","r"); + if(fp==NULL){ + printf("can't read db/const.txt\n"); + return ; + } + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + type=0; + if(sscanf(line,"%[A-Za-z0-9_],%d,%d",name,&val,&type)>=2 || + sscanf(line,"%[A-Za-z0-9_] %d %d",name,&val,&type)>=2){ + for(i=0;name[i];i++) + name[i]=tolower(name[i]); + n=add_str(name); + if(type==0) + str_data[n].type=C_INT; + else + str_data[n].type=C_PARAM; + str_data[n].val=val; + } + } + fclose(fp); +} + +/*========================================== + * スクリプトの解析 + *------------------------------------------ + */ +unsigned char* parse_script(unsigned char *src,int line) +{ + unsigned char *p,*tmpp; + int i; + static int first=1; + + if(first){ + add_buildin_func(); + read_constdb(); + } + first=0; + script_buf=(unsigned char *)aCalloc(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_NOP; + str_data[i].backpatch=-1; + str_data[i].label=-1; + } + } + + // 外部用label dbの初期化 + if(scriptlabel_db!=NULL) + strdb_final(scriptlabel_db,scriptlabel_final); + scriptlabel_db=strdb_init(50); + + // for error message + startptr = src; + startline = line; + + p=src; + p=skip_space(p); + if(*p!='{'){ + disp_error_message("not found '{'",p); + return NULL; + } + for(p++;p && *p && *p!='}';){ + p=skip_space(p); + // labelだけ特殊処理 + tmpp=skip_space(skip_word(p)); + if(*tmpp==':'){ + int l,c; + + c=*skip_word(p); + *skip_word(p)=0; + l=add_str(p); + if(str_data[l].label!=-1){ + *skip_word(p)=c; + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + strdb_insert(scriptlabel_db,p,script_pos); // 外部用label db登録 + *skip_word(p)=c; + p=tmpp+1; + continue; + } + + // 他は全部一緒くた + p=parse_line(p); + p=skip_space(p); + add_scriptc(C_EOL); + + set_label(LABEL_NEXTLINE,script_pos); + str_data[LABEL_NEXTLINE].type=C_NOP; + str_data[LABEL_NEXTLINE].backpatch=-1; + str_data[LABEL_NEXTLINE].label=-1; + } + + add_scriptc(C_NOP); + + script_size = script_pos; + script_buf=(char *)aRealloc(script_buf,script_pos + 1); + + // 未解決のラベルを解決 + for(i=LABEL_START;i<str_num;i++){ + if(str_data[i].type==C_NOP){ + int j,next; + str_data[i].type=C_NAME; + str_data[i].label=i; + for(j=str_data[i].backpatch;j>=0 && j!=0x00ffffff;){ + next=(*(int*)(script_buf+j)) & 0x00ffffff; + script_buf[j]=i; + script_buf[j+1]=i>>8; + script_buf[j+2]=i>>16; + j=next; + } + } + } + +#ifdef DEBUG_DISP + for(i=0;i<script_pos;i++){ + if((i&15)==0) printf("%04x : ",i); + printf("%02x ",script_buf[i]); + if((i&15)==15) printf("\n"); + } + printf("\n"); +#endif + + return script_buf; +} + +// +// 実行系 +// +enum {STOP=1,END,RERUNLINE,GOTO,RETFUNC}; + +/*========================================== + * ridからsdへの解決 + *------------------------------------------ + */ +struct map_session_data *script_rid2sd(struct script_state *st) +{ + struct map_session_data *sd=map_id2sd(st->rid); + if(!sd){ + printf("script_rid2sd: fatal error ! player not attached!\n"); + } + return sd; +} + + +/*========================================== + * 変数の読み取り + *------------------------------------------ + */ +int get_val(struct script_state*st,struct script_data* data) +{ + struct map_session_data *sd=NULL; + if(data->type==C_NAME){ + char *name=str_buf+str_data[data->u.num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if(prefix!='$'){ + if((sd=script_rid2sd(st))==NULL) + printf("get_val error name?:%s\n",name); + } + if(postfix=='$'){ + + data->type=C_CONSTSTR; + if( prefix=='@' || prefix=='l' ){ + if(sd) + data->u.str = pc_readregstr(sd,data->u.num); + }else if(prefix=='$'){ + data->u.str = (char *)numdb_search(mapregstr_db,data->u.num); + }else{ + printf("script: get_val: illegal scope string variable.\n"); + data->u.str = "!!ERROR!!"; + } + if( data->u.str == NULL ) + data->u.str =""; + + }else{ + + data->type=C_INT; + if(str_data[data->u.num&0x00ffffff].type==C_INT){ + data->u.num = str_data[data->u.num&0x00ffffff].val; + }else if(str_data[data->u.num&0x00ffffff].type==C_PARAM){ + if(sd) + data->u.num = pc_readparam(sd,str_data[data->u.num&0x00ffffff].val); + }else if(prefix=='@' || prefix=='l'){ + if(sd) + data->u.num = pc_readreg(sd,data->u.num); + }else if(prefix=='$'){ + data->u.num = (int)numdb_search(mapreg_db,data->u.num); + }else if(prefix=='#'){ + if( name[1]=='#'){ + if(sd) + data->u.num = pc_readaccountreg2(sd,name); + }else{ + if(sd) + data->u.num = pc_readaccountreg(sd,name); + } + }else{ + if(sd) + data->u.num = pc_readglobalreg(sd,name); + } + } + } + return 0; +} +/*========================================== + * 変数の読み取り2 + *------------------------------------------ + */ +void* get_val2(struct script_state*st,int num) +{ + struct script_data dat; + dat.type=C_NAME; + dat.u.num=num; + get_val(st,&dat); + if( dat.type==C_INT ) return (void*)dat.u.num; + else return (void*)dat.u.str; +} + +/*========================================== + * 変数設定用 + *------------------------------------------ + */ +static int set_reg(struct map_session_data *sd,int num,char *name,void *v) +{ + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if( postfix=='$' ){ + char *str=(char*)v; + if( prefix=='@' || prefix=='l'){ + pc_setregstr(sd,num,str); + }else if(prefix=='$') { + mapreg_setregstr(num,str); + }else{ + printf("script: set_reg: illegal scope string variable !"); + } + }else{ + // 数値 + int val = (int)v; + if(str_data[num&0x00ffffff].type==C_PARAM){ + pc_setparam(sd,str_data[num&0x00ffffff].val,val); + }else if(prefix=='@' || prefix=='l') { + pc_setreg(sd,num,val); + }else if(prefix=='$') { + mapreg_setreg(num,val); + }else if(prefix=='#') { + if( name[1]=='#' ) + pc_setaccountreg2(sd,name,val); + else + pc_setaccountreg(sd,name,val); + }else{ + pc_setglobalreg(sd,name,val); + } + } + return 0; +} + +/*========================================== + * 文字列への変換 + *------------------------------------------ + */ +char* conv_str(struct script_state *st,struct script_data *data) +{ + get_val(st,data); + if(data->type==C_INT){ + char *buf; + buf=(char *)aCalloc(16,sizeof(char)); + sprintf(buf,"%d",data->u.num); + data->type=C_STR; + data->u.str=buf; +#if 1 + } else if(data->type==C_NAME){ + // テンポラリ。本来無いはず + data->type=C_CONSTSTR; + data->u.str=str_buf+str_data[data->u.num].str; +#endif + } + return data->u.str; +} + +/*========================================== + * 数値へ変換 + *------------------------------------------ + */ +int conv_num(struct script_state *st,struct script_data *data) +{ + char *p; + get_val(st,data); + if(data->type==C_STR || data->type==C_CONSTSTR){ + p=data->u.str; + data->u.num = atoi(p); + if(data->type==C_STR) + free(p); + data->type=C_INT; + } + return data->u.num; +} + +/*========================================== + * スタックへ数値をプッシュ + *------------------------------------------ + */ +void push_val(struct script_stack *stack,int type,int val) +{ + if(stack->sp >= stack->sp_max){ + stack->sp_max += 64; + stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, + sizeof(stack->stack_data[0]) * stack->sp_max); + memset(stack->stack_data + (stack->sp_max - 64), 0, + 64 * sizeof(*(stack->stack_data))); + } +// if(battle_config.etc_log) +// printf("push (%d,%d)-> %d\n",type,val,stack->sp); + stack->stack_data[stack->sp].type=type; + stack->stack_data[stack->sp].u.num=val; + stack->sp++; +} + +/*========================================== + * スタックへ文字列をプッシュ + *------------------------------------------ + */ +void push_str(struct script_stack *stack,int type,unsigned char *str) +{ + if(stack->sp>=stack->sp_max){ + stack->sp_max += 64; + stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, + sizeof(stack->stack_data[0]) * stack->sp_max); + memset(stack->stack_data + (stack->sp_max - 64), '\0', + 64 * sizeof(*(stack->stack_data))); + } +// if(battle_config.etc_log) +// printf("push (%d,%x)-> %d\n",type,str,stack->sp); + stack->stack_data[stack->sp].type=type; + stack->stack_data[stack->sp].u.str=str; + stack->sp++; +} + +/*========================================== + * スタックへ複製をプッシュ + *------------------------------------------ + */ +void push_copy(struct script_stack *stack,int pos) +{ + switch(stack->stack_data[pos].type){ + case C_CONSTSTR: + push_str(stack,C_CONSTSTR,stack->stack_data[pos].u.str); + break; + case C_STR: + push_str(stack,C_STR,strdup(stack->stack_data[pos].u.str)); + break; + default: + push_val(stack,stack->stack_data[pos].type,stack->stack_data[pos].u.num); + break; + } +} + +/*========================================== + * スタックからポップ + *------------------------------------------ + */ +void pop_stack(struct script_stack* stack,int start,int end) +{ + int i; + for(i=start;i<end;i++){ + if(stack->stack_data[i].type==C_STR){ + free(stack->stack_data[i].u.str); + } + } + if(stack->sp>end){ + memmove(&stack->stack_data[start],&stack->stack_data[end],sizeof(stack->stack_data[0])*(stack->sp-end)); + } + stack->sp-=end-start; +} + +// +// 埋め込み関数 +// +/*========================================== + * + *------------------------------------------ + */ +int buildin_mes(struct script_state *st) +{ + conv_str(st,& (st->stack->stack_data[st->start+2])); + clif_scriptmes(script_rid2sd(st),st->oid,st->stack->stack_data[st->start+2].u.str); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_goto(struct script_state *st) +{ + int pos; + + if( st->stack->stack_data[st->start+2].type!=C_POS ){ + printf("script: goto: not label !\n"); + st->state=END; + return 0; + } + + pos=conv_num(st,& (st->stack->stack_data[st->start+2])); + st->pos=pos; + st->state=GOTO; + return 0; +} + +/*========================================== + * ユーザー定義関数の呼び出し + *------------------------------------------ + */ +int buildin_callfunc(struct script_state *st) +{ + char *scr; + char *str=conv_str(st,& (st->stack->stack_data[st->start+2])); + + if( (scr=strdb_search(script_get_userfunc_db(),str)) ){ + int i,j; + for(i=st->start+3,j=0;i<st->end;i++,j++) + push_copy(st->stack,i); + + push_val(st->stack,C_INT,j); // 引数の数をプッシュ + push_val(st->stack,C_INT,st->defsp); // 現在の基準スタックポインタをプッシュ + push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ + push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ + + st->pos=0; + st->script=scr; + st->defsp=st->start+4+j; + st->state=GOTO; + }else{ + printf("script:callfunc: function not found! [%s]\n",str); + st->state=END; + } + return 0; +} +/*========================================== + * サブルーティンの呼び出し + *------------------------------------------ + */ +int buildin_callsub(struct script_state *st) +{ + int pos=conv_num(st,& (st->stack->stack_data[st->start+2])); + int i,j; + for(i=st->start+3,j=0;i<st->end;i++,j++) + push_copy(st->stack,i); + + push_val(st->stack,C_INT,j); // 引数の数をプッシュ + push_val(st->stack,C_INT,st->defsp); // 現在の基準スタックポインタをプッシュ + push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ + push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ + + st->pos=pos; + st->defsp=st->start+4+j; + st->state=GOTO; + return 0; +} + +/*========================================== + * 引数の所得 + *------------------------------------------ + */ +int buildin_getarg(struct script_state *st) +{ + int num=conv_num(st,& (st->stack->stack_data[st->start+2])); + int max,stsp; + if( st->defsp<4 || st->stack->stack_data[st->defsp-1].type!=C_RETINFO ){ + printf("script:getarg without callfunc or callsub!\n"); + st->state=END; + return 0; + } + max=conv_num(st,& (st->stack->stack_data[st->defsp-4])); + stsp=st->defsp - max -4; + if( num >= max ){ + printf("script:getarg arg1(%d) out of range(%d) !\n",num,max); + st->state=END; + return 0; + } + push_copy(st->stack,stsp+num); + return 0; +} + +/*========================================== + * サブルーチン/ユーザー定義関数の終了 + *------------------------------------------ + */ +int buildin_return(struct script_state *st) +{ + if(st->end>st->start+2){ // 戻り値有り + push_copy(st->stack,st->start+2); + } + st->state=RETFUNC; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_next(struct script_state *st) +{ + st->state=STOP; + clif_scriptnext(script_rid2sd(st),st->oid); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_close(struct script_state *st) +{ + st->state=END; + clif_scriptclose(script_rid2sd(st),st->oid); + return 0; +} +int buildin_close2(struct script_state *st) +{ + st->state=STOP; + clif_scriptclose(script_rid2sd(st),st->oid); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_menu(struct script_state *st) +{ + char *buf; + int 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+=strlen(st->stack->stack_data[i].u.str)+1; + } + buf=(char *)aCalloc(len,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); + free(buf); + } else if(sd->npc_menu==0xff){ // cansel + sd->state.menu_or_input=0; + st->state=END; + } else { // goto動作 + // ragemu互換のため + pc_setreg(sd,add_str("l15"),sd->npc_menu); + pc_setreg(sd,add_str("@menu"),sd->npc_menu); + sd->state.menu_or_input=0; + if(sd->npc_menu>0 && sd->npc_menu<(st->end-st->start)/2){ + int pos; + if( st->stack->stack_data[st->start+sd->npc_menu*2+1].type!=C_POS ){ + printf("script: menu: not label !\n"); + st->state=END; + return 0; + } + pos=conv_num(st,& (st->stack->stack_data[st->start+sd->npc_menu*2+1])); + st->pos=pos; + st->state=GOTO; + } + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_rand(struct script_state *st) +{ + int range,min,max; + + if(st->end>st->start+3){ + min=conv_num(st,& (st->stack->stack_data[st->start+2])); + max=conv_num(st,& (st->stack->stack_data[st->start+3])); + if(max<min){ + int tmp; + tmp=min; + min=max; + max=tmp; + } + range=max-min+1; + push_val(st->stack,C_INT,rand()%range+min); + } else { + range=conv_num(st,& (st->stack->stack_data[st->start+2])); + 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,str,x,y,0); + return 0; +} +/*========================================== + * エリア指定ワープ + *------------------------------------------ + */ +int buildin_areawarp_sub(struct block_list *bl,va_list ap) +{ + int x,y; + char *map; + map=va_arg(ap, char *); + x=va_arg(ap,int); + y=va_arg(ap,int); + if(strcmp(map,"Random")==0) + pc_randomwarp((struct map_session_data *)bl,3); + else + pc_setpos((struct map_session_data *)bl,map,x,y,0); + return 0; +} +int buildin_areawarp(struct script_state *st) +{ + int x,y,m; + char *str; + char *mapname; + int x0,y0,x1,y1; + + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + str=conv_str(st,& (st->stack->stack_data[st->start+7])); + x=conv_num(st,& (st->stack->stack_data[st->start+8])); + y=conv_num(st,& (st->stack->stack_data[st->start+9])); + + if( (m=map_mapname2mapid(mapname))< 0) + return 0; + + map_foreachinarea(buildin_areawarp_sub, + m,x0,y0,x1,y1,BL_PC, str,x,y ); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_heal(struct script_state *st) +{ + int hp,sp; + + hp=conv_num(st,& (st->stack->stack_data[st->start+2])); + sp=conv_num(st,& (st->stack->stack_data[st->start+3])); + pc_heal(script_rid2sd(st),hp,sp); + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_itemheal(struct script_state *st) +{ + int hp,sp; + + hp=conv_num(st,& (st->stack->stack_data[st->start+2])); + sp=conv_num(st,& (st->stack->stack_data[st->start+3])); + pc_itemheal(script_rid2sd(st),hp,sp); + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_percentheal(struct script_state *st) +{ + int hp,sp; + + hp=conv_num(st,& (st->stack->stack_data[st->start+2])); + sp=conv_num(st,& (st->stack->stack_data[st->start+3])); + pc_percentheal(script_rid2sd(st),hp,sp); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_jobchange(struct script_state *st) +{ + int job, upper=-1; + + job=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end>st->start+3 ) + upper=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if ((job >= 0 && job < MAX_PC_CLASS)) + pc_jobchange(script_rid2sd(st),job, upper); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_input(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=(st->end>st->start+2)?st->stack->stack_data[st->start+2].u.num:0; + char *name=(st->end>st->start+2)?str_buf+str_data[num&0x00ffffff].str:""; +// char prefix=*name; + char postfix=name[strlen(name)-1]; + + sd=script_rid2sd(st); + if(sd->state.menu_or_input){ + sd->state.menu_or_input=0; + if( postfix=='$' ){ + // 文字列 + if(st->end>st->start+2){ // 引数1個 + set_reg(sd,num,name,(void*)sd->npc_str); + }else{ + printf("buildin_input: string discarded !!\n"); + } + }else{ + + //commented by Lupus (check Value Number Input fix in clif.c) + //** Fix by fritz :X keeps people from abusing old input bugs + if(sd->npc_amount < 0) //** If input amount is less then 0 + { + clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris + buildin_close(st); //** close + } + + // 数値 + if(st->end>st->start+2){ // 引数1個 + set_reg(sd,num,name,(void*)sd->npc_amount); + } else { + // ragemu互換のため + pc_setreg(sd,add_str("l14"),sd->npc_amount); + } + } + } else { + st->state=RERUNLINE; + if(postfix=='$')clif_scriptinputstr(sd,st->oid); + else clif_scriptinput(sd,st->oid); + sd->state.menu_or_input=1; + } + return 0; +} + + +/*========================================== + * + *------------------------------------------ + */ +int buildin_if(struct script_state *st) +{ + int sel,i; + + sel=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(!sel) + return 0; + + // 関数名をコピー + push_copy(st->stack,st->start+3); + // 間に引数マーカを入れて + push_val(st->stack,C_ARG,0); + // 残りの引数をコピー + for(i=st->start+4;i<st->end;i++){ + push_copy(st->stack,i); + } + run_func(st); + + return 0; +} + + +/*========================================== + * 変数設定 + *------------------------------------------ + */ +int buildin_set(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if( st->stack->stack_data[st->start+2].type!=C_NAME ){ + printf("script: buildin_set: not name\n"); + return 0; + } + + if( prefix!='$' ) + sd=script_rid2sd(st); + + + if( postfix=='$' ){ + // 文字列 + char *str = conv_str(st,& (st->stack->stack_data[st->start+3])); + set_reg(sd,num,name,(void*)str); + }else{ + // 数値 + int val = conv_num(st,& (st->stack->stack_data[st->start+3])); + set_reg(sd,num,name,(void*)val); + } + + return 0; +} +/*========================================== + * 配列変数設定 + *------------------------------------------ + */ +int buildin_setarray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int i,j; + + if( prefix!='$' && prefix!='@' ){ + printf("buildin_setarray: illegal scope !\n"); + return 0; + } + if( prefix!='$' ) + sd=script_rid2sd(st); + + for(j=0,i=st->start+3; i<st->end && j<128;i++,j++){ + void *v; + if( postfix=='$' ) + v=(void*)conv_str(st,& (st->stack->stack_data[i])); + else + v=(void*)conv_num(st,& (st->stack->stack_data[i])); + set_reg( sd, num+(j<<24), name, v); + } + return 0; +} +/*========================================== + * 配列変数クリア + *------------------------------------------ + */ +int buildin_cleararray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int sz=conv_num(st,& (st->stack->stack_data[st->start+4])); + int i; + void *v; + + if( prefix!='$' && prefix!='@' ){ + printf("buildin_cleararray: illegal scope !\n"); + return 0; + } + if( prefix!='$' ) + sd=script_rid2sd(st); + + if( postfix=='$' ) + v=(void*)conv_str(st,& (st->stack->stack_data[st->start+3])); + else + v=(void*)conv_num(st,& (st->stack->stack_data[st->start+3])); + + for(i=0;i<sz;i++) + set_reg(sd,num+(i<<24),name,v); + return 0; +} +/*========================================== + * 配列変数コピー + *------------------------------------------ + */ +int buildin_copyarray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int num2=st->stack->stack_data[st->start+3].u.num; + char *name2=str_buf+str_data[num2&0x00ffffff].str; + char prefix2=*name2; + char postfix2=name2[strlen(name2)-1]; + int sz=conv_num(st,& (st->stack->stack_data[st->start+4])); + int i; + + if( prefix!='$' && prefix!='@' && prefix2!='$' && prefix2!='@' ){ + printf("buildin_copyarray: illegal scope !\n"); + return 0; + } + if( (postfix=='$' || postfix2=='$') && postfix!=postfix2 ){ + printf("buildin_copyarray: type mismatch !\n"); + return 0; + } + if( prefix!='$' || prefix2!='$' ) + sd=script_rid2sd(st); + + + for(i=0;i<sz;i++) + set_reg(sd,num+(i<<24),name, get_val2(st,num2+(i<<24)) ); + return 0; +} +/*========================================== + * 配列変数のサイズ所得 + *------------------------------------------ + */ +static int getarraysize(struct script_state *st,int num,int postfix) +{ + int i=(num>>24),c=i; + for(;i<128;i++){ + void *v=get_val2(st,num+(i<<24)); + if(postfix=='$' && *((char*)v) ) c=i; + if(postfix!='$' && (int)v )c=i; + } + return c+1; +} +int buildin_getarraysize(struct script_state *st) +{ + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if( prefix!='$' && prefix!='@' ){ + printf("buildin_copyarray: illegal scope !\n"); + return 0; + } + + push_val(st->stack,C_INT,getarraysize(st,num,postfix) ); + return 0; +} +/*========================================== + * 配列変数から要素削除 + *------------------------------------------ + */ +int buildin_deletearray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int count=1; + int i,sz=getarraysize(st,num,postfix)-(num>>24)-count+1; + + + if( (st->end > st->start+3) ) + count=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if( prefix!='$' && prefix!='@' ){ + printf("buildin_deletearray: illegal scope !\n"); + return 0; + } + if( prefix!='$' ) + sd=script_rid2sd(st); + + for(i=0;i<sz;i++){ + set_reg(sd,num+(i<<24),name, get_val2(st,num+((i+count)<<24) ) ); + } + for(;i<(128-(num>>24));i++){ + if( postfix!='$' ) set_reg(sd,num+(i<<24),name, 0); + if( postfix=='$' ) set_reg(sd,num+(i<<24),name, ""); + } + return 0; +} + +/*========================================== + * 指定要素を表す値(キー)を所得する + *------------------------------------------ + */ +int buildin_getelementofarray(struct script_state *st) +{ + if( st->stack->stack_data[st->start+2].type==C_NAME ){ + int i=conv_num(st,& (st->stack->stack_data[st->start+3])); + if(i>127 || i<0){ + printf("script: getelementofarray (operator[]): param2 illegal number %d\n",i); + push_val(st->stack,C_INT,0); + }else{ + push_val(st->stack,C_NAME, + (i<<24) | st->stack->stack_data[st->start+2].u.num ); + } + }else{ + printf("script: getelementofarray (operator[]): param1 not name !\n"); + push_val(st->stack,C_INT,0); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_setlook(struct script_state *st) +{ + int type,val; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=conv_num(st,& (st->stack->stack_data[st->start+3])); + + pc_changelook(script_rid2sd(st),type,val); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_cutin(struct script_state *st) +{ + int type; + + conv_str(st,& (st->stack->stack_data[st->start+2])); + type=conv_num(st,& (st->stack->stack_data[st->start+3])); + + clif_cutin(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str,type); + + return 0; +} +/*========================================== + * カードのイラストを表示する + *------------------------------------------ + */ +int buildin_cutincard(struct script_state *st) +{ + int itemid; + + itemid=conv_num(st,& (st->stack->stack_data[st->start+2])); + + clif_cutin(script_rid2sd(st),itemdb_search(itemid)->cardillustname,4); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_viewpoint(struct script_state *st) +{ + int type,x,y,id,color; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + id=conv_num(st,& (st->stack->stack_data[st->start+5])); + color=conv_num(st,& (st->stack->stack_data[st->start+6])); + + clif_viewpoint(script_rid2sd(st),st->oid,type,x,y,id,color); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_countitem(struct script_state *st) +{ + int nameid=0,count=0,i; + struct map_session_data *sd; + + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data; + if( (item_data = itemdb_searchname(name)) != NULL) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + if (nameid>=500) //if no such ID then skip this iteration + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid==nameid) + count+=sd->status.inventory[i].amount; + } + else{ + if(battle_config.error_log) + printf("wrong item ID : countitem(%i)\n",nameid); + } + push_val(st->stack,C_INT,count); + + return 0; +} + +/*========================================== + * 重量チェック + *------------------------------------------ + */ +int buildin_checkweight(struct script_state *st) +{ + int nameid=0,amount; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + if ( amount<=0 || nameid<500 ) { //if get wrong item ID or amount<=0, don't count weight of non existing items + push_val(st->stack,C_INT,0); + } + + sd=script_rid2sd(st); + if(itemdb_weight(nameid)*amount + sd->weight > sd->max_weight){ + push_val(st->stack,C_INT,0); + } else { + push_val(st->stack,C_INT,1); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_getitem(struct script_state *st) +{ + int nameid,amount,flag = 0; + struct item item_tmp; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + nameid=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(nameid<0) { // ランダム + nameid=itemdb_searchrandomid(-nameid); + flag = 1; + } + + if(nameid > 0) { + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid=nameid; + if(!flag) + item_tmp.identify=1; + else + item_tmp.identify=!itemdb_isequip3(nameid); + if( st->end>st->start+5 ) //アイテムを指定したIDに渡す + sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+5]))); + if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り + return 0; + if((flag = pc_additem(sd,&item_tmp,amount))) { + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_getitem2(struct script_state *st) +{ + int nameid,amount,flag = 0; + int iden,ref,attr,c1,c2,c3,c4; + struct item_data *item_data; + struct item item_tmp; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + nameid=512; //Apple item ID + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + iden=conv_num(st,& (st->stack->stack_data[st->start+4])); + ref=conv_num(st,& (st->stack->stack_data[st->start+5])); + attr=conv_num(st,& (st->stack->stack_data[st->start+6])); + c1=conv_num(st,& (st->stack->stack_data[st->start+7])); + c2=conv_num(st,& (st->stack->stack_data[st->start+8])); + c3=conv_num(st,& (st->stack->stack_data[st->start+9])); + c4=conv_num(st,& (st->stack->stack_data[st->start+10])); + if( st->end>st->start+11 ) //アイテムを指定したIDに渡す + sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+11]))); + if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り + return 0; + + if(nameid<0) { // ランダム + nameid=itemdb_searchrandomid(-nameid); + flag = 1; + } + + if(nameid > 0) { + memset(&item_tmp,0,sizeof(item_tmp)); + item_data=itemdb_search(nameid); + if(item_data->type==4 || item_data->type==5){ + if(ref > 10) ref = 10; + } + else if(item_data->type==7) { + iden = 1; + ref = 0; + } + else { + iden = 1; + ref = attr = 0; + } + + item_tmp.nameid=nameid; + if(!flag) + item_tmp.identify=iden; + else if(item_data->type==4 || item_data->type==5) + item_tmp.identify=0; + item_tmp.refine=ref; + item_tmp.attribute=attr; + item_tmp.card[0]=c1; + item_tmp.card[1]=c2; + item_tmp.card[2]=c3; + item_tmp.card[3]=c4; + if((flag = pc_additem(sd,&item_tmp,amount))) { + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_makeitem(struct script_state *st) +{ + int nameid,amount,flag = 0; + int x,y,m; + char *mapname; + struct item item_tmp; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + nameid=512; //Apple Item ID + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + mapname =conv_str(st,& (st->stack->stack_data[st->start+4])); + x =conv_num(st,& (st->stack->stack_data[st->start+5])); + y =conv_num(st,& (st->stack->stack_data[st->start+6])); + + if( sd && strcmp(mapname,"this")==0) + m=sd->bl.m; + else + m=map_mapname2mapid(mapname); + + if(nameid<0) { // ランダム + nameid=itemdb_searchrandomid(-nameid); + flag = 1; + } + + if(nameid > 0) { + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid=nameid; + if(!flag) + item_tmp.identify=1; + else + item_tmp.identify=!itemdb_isequip3(nameid); + +// clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,amount,m,x,y,NULL,NULL,NULL,0); + } + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_delitem(struct script_state *st) +{ + int nameid=0,amount,i; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + //nameid=512; + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0 + //printf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); + return 0; + } + sd=script_rid2sd(st); + + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || + sd->inventory_data[i]->type!=7 || + sd->status.inventory[i].amount<=0) + continue; + if(sd->status.inventory[i].nameid == nameid){ + if(sd->status.inventory[i].card[0] == (short)0xff00){ + if(search_petDB_index(nameid, PET_EGG) >= 0){ + intif_delete_petdata(*((long *)(&sd->status.inventory[i].card[1]))); + break; + } + } + } + } + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid==nameid){ + if(sd->status.inventory[i].amount>=amount){ + pc_delitem(sd,i,amount,0); + break; + } else { + amount-=sd->status.inventory[i].amount; + if(amount==0) + amount=sd->status.inventory[i].amount; + pc_delitem(sd,i,amount,0); + break; + } + } + } + + return 0; +} + +/*========================================== + *キャラ関係のパラメータ取得 + *------------------------------------------ + */ +int buildin_readparam(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end>st->start+3 ) + sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + sd=script_rid2sd(st); + + if(sd==NULL){ + push_val(st->stack,C_INT,-1); + return 0; + } + + push_val(st->stack,C_INT,pc_readparam(sd,type)); + + return 0; +} +/*========================================== + *キャラ関係のID取得 + *------------------------------------------ + */ +int buildin_getcharid(struct script_state *st) +{ + int num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end>st->start+3 ) + sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + sd=script_rid2sd(st); + if(sd==NULL){ + push_val(st->stack,C_INT,-1); + return 0; + } + if(num==0) + push_val(st->stack,C_INT,sd->status.char_id); + if(num==1) + push_val(st->stack,C_INT,sd->status.party_id); + if(num==2) + push_val(st->stack,C_INT,sd->status.guild_id); + if(num==3) + push_val(st->stack,C_INT,sd->status.account_id); + return 0; +} +/*========================================== + *指定IDのPT名取得 + *------------------------------------------ + */ +char *buildin_getpartyname_sub(int party_id) +{ + struct party *p; + + p=NULL; + p=party_search(party_id); + + if(p!=NULL){ + char *buf; + buf=(char *)aCalloc(24,sizeof(char)); + strcpy(buf,p->name); + return buf; + } + + return 0; +} +int buildin_getpartyname(struct script_state *st) +{ + char *name; + int party_id; + + party_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + name=buildin_getpartyname_sub(party_id); + if(name!=0) + push_str(st->stack,C_STR,name); + else + push_str(st->stack,C_CONSTSTR,"null"); + + return 0; +} +/*========================================== + *指定IDのPT人数とメンバーID取得 + *------------------------------------------ + */ +int buildin_getpartymember(struct script_state *st) +{ + struct party *p; + int i,j=0; + + p=NULL; + p=party_search(conv_num(st,& (st->stack->stack_data[st->start+2]))); + + if(p!=NULL){ + for(i=0;i<MAX_PARTY;i++){ + if(p->member[i].account_id){ +// printf("name:%s %d\n",p->member[i].name,i); + mapreg_setregstr(add_str("$@partymembername$")+(i<<24),p->member[i].name); + j++; + } + } + } + mapreg_setreg(add_str("$@partymembercount"),j); + + return 0; +} +/*========================================== + *指定IDのギルド名取得 + *------------------------------------------ + */ +char *buildin_getguildname_sub(int guild_id) +{ + struct guild *g=NULL; + g=guild_search(guild_id); + + if(g!=NULL){ + char *buf; + buf=(char *)aCalloc(24,sizeof(char)); + strcpy(buf,g->name); + return buf; + } + return 0; +} +int buildin_getguildname(struct script_state *st) +{ + char *name; + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + name=buildin_getguildname_sub(guild_id); + if(name!=0) + push_str(st->stack,C_STR,name); + else + push_str(st->stack,C_CONSTSTR,"null"); + return 0; +} + +/*========================================== + *指定IDのGuildMaster名取得 + *------------------------------------------ + */ +char *buildin_getguildmaster_sub(int guild_id) +{ + struct guild *g=NULL; + g=guild_search(guild_id); + + if(g!=NULL){ + char *buf; + buf=(char *)aCalloc(24,sizeof(char)); + strncpy(buf,g->master, 23); + return buf; + } + + return 0; +} +int buildin_getguildmaster(struct script_state *st) +{ + char *master; + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + master=buildin_getguildmaster_sub(guild_id); + if(master!=0) + push_str(st->stack,C_STR,master); + else + push_str(st->stack,C_CONSTSTR,"null"); + return 0; +} + +int buildin_getguildmasterid(struct script_state *st) +{ + char *master; + struct map_session_data *sd=NULL; + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + master=buildin_getguildmaster_sub(guild_id); + if(master!=0){ + if((sd=map_nick2sd(master)) == NULL){ + push_val(st->stack,C_INT,0); + return 0; + } + push_val(st->stack,C_INT,sd->status.char_id); + }else{ + push_val(st->stack,C_INT,0); + } + return 0; +} + +/*========================================== + * キャラクタの名前 + *------------------------------------------ + */ +int buildin_strcharinfo(struct script_state *st) +{ + struct map_session_data *sd; + int num; + + sd=script_rid2sd(st); + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(num==0){ + char *buf; + buf=(char *)aCalloc(24,sizeof(char)); + strncpy(buf,sd->status.name, 23); + push_str(st->stack,C_STR,buf); + } + if(num==1){ + char *buf; + buf=buildin_getpartyname_sub(sd->status.party_id); + if(buf!=0) + push_str(st->stack,C_STR,buf); + else + push_str(st->stack,C_CONSTSTR,""); + } + if(num==2){ + char *buf; + buf=buildin_getguildname_sub(sd->status.guild_id); + if(buf!=0) + push_str(st->stack,C_STR,buf); + else + push_str(st->stack,C_CONSTSTR,""); + } + + return 0; +} + +unsigned int equip[10]={0x0100,0x0010,0x0020,0x0002,0x0004,0x0040,0x0008,0x0080,0x0200,0x0001}; + +/*========================================== + * GetEquipID(Pos); Pos: 1-10 + *------------------------------------------ + */ +int buildin_getequipid(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + struct item_data* item; + + sd=script_rid2sd(st); + if(sd == NULL) + { + printf("getequipid: sd == NULL\n"); + return 0; + } + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0){ + item=sd->inventory_data[i]; + if(item) + push_val(st->stack,C_INT,item->nameid); + else + push_val(st->stack,C_INT,0); + }else{ + push_val(st->stack,C_INT,-1); + } + return 0; +} + +/*========================================== + * 装備名文字列(精錬メニュー用) + *------------------------------------------ + */ +int buildin_getequipname(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + struct item_data* item; + char *buf; + + buf=(char *)aCalloc(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,buf); + + return 0; +} + +/*========================================== + * getbrokenid [Valaris] + *------------------------------------------ + */ +int buildin_getbrokenid(struct script_state *st) +{ + int i,num,id=0,brokencounter=0; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + for(i=0; i<MAX_INVENTORY; i++) { + if(sd->status.inventory[i].broken==1){ + brokencounter++; + if(num==brokencounter){ + id=sd->status.inventory[i].nameid; + break; + } + } + } + + push_val(st->stack,C_INT,id); + + return 0; +} + +/*========================================== + * repair [Valaris] + *------------------------------------------ + */ +int buildin_repair(struct script_state *st) +{ + int i,num; + int repaircounter=0; + struct map_session_data *sd; + + + sd=script_rid2sd(st); + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + for(i=0; i<MAX_INVENTORY; i++) { + if(sd->status.inventory[i].broken==1){ + repaircounter++; + if(num==repaircounter){ + sd->status.inventory[i].broken=0; + clif_equiplist(sd); + clif_produceeffect(sd, 0, sd->status.inventory[i].nameid); + clif_misceffect(&sd->bl, 3); + clif_displaymessage(sd->fd,"Item has been repaired."); + break; + } + } + } + + return 0; +} + +/*========================================== + * 装備チェック + *------------------------------------------ + */ +int buildin_getequipisequiped(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0){ + push_val(st->stack,C_INT,1); + }else{ + push_val(st->stack,C_INT,0); + } + + return 0; +} + +/*========================================== + * 装備品精錬可能チェック + *------------------------------------------ + */ +int buildin_getequipisenableref(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0 && num<7 && sd->inventory_data[i] && (num!=1 || sd->inventory_data[i]->def > 1 + || (sd->inventory_data[i]->def==1 && sd->inventory_data[i]->equip_script==NULL) + || (sd->inventory_data[i]->def<=0 && sd->inventory_data[i]->equip_script!=NULL)) + ){ + push_val(st->stack,C_INT,1); + }else{ + push_val(st->stack,C_INT,0); + } + + return 0; +} + +/*========================================== + * 装備品鑑定チェック + *------------------------------------------ + */ +int buildin_getequipisidentify(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) + push_val(st->stack,C_INT,sd->status.inventory[i].identify); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 装備品精錬度 + *------------------------------------------ + */ +int buildin_getequiprefinerycnt(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) + push_val(st->stack,C_INT,sd->status.inventory[i].refine); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 装備品武器LV + *------------------------------------------ + */ +int buildin_getequipweaponlv(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0 && sd->inventory_data[i]) + push_val(st->stack,C_INT,sd->inventory_data[i]->wlv); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 装備品精錬成功率 + *------------------------------------------ + */ +int buildin_getequippercentrefinery(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) + push_val(st->stack,C_INT,pc_percentrefinery(sd,&sd->status.inventory[i])); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 精錬成功 + *------------------------------------------ + */ +int buildin_successrefitem(struct script_state *st) +{ + int i,num,ep; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) { + ep=sd->status.inventory[i].equip; + + sd->status.inventory[i].refine++; + pc_unequipitem(sd,i,0); + clif_refine(sd->fd,sd,0,i,sd->status.inventory[i].refine); + clif_delitem(sd,i,1); + clif_additem(sd,i,1,0); + pc_equipitem(sd,i,ep); + clif_misceffect(&sd->bl,3); + } + + return 0; +} + +/*========================================== + * 精錬失敗 + *------------------------------------------ + */ +int buildin_failedrefitem(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) { + sd->status.inventory[i].refine = 0; + pc_unequipitem(sd,i,0); + // 精錬失敗エフェクトのパケット + clif_refine(sd->fd,sd,1,i,sd->status.inventory[i].refine); + pc_delitem(sd,i,1,0); + // 他の人にも失敗を通知 + clif_misceffect(&sd->bl,2); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_statusup(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + pc_statusup(sd,type); + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_statusup2(struct script_state *st) +{ + int type,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + pc_statusup2(sd,type,val); + + return 0; +} +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +int buildin_bonus(struct script_state *st) +{ + int type,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + pc_bonus(sd,type,val); + + return 0; +} +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +int buildin_bonus2(struct script_state *st) +{ + int type,type2,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + type2=conv_num(st,& (st->stack->stack_data[st->start+3])); + val=conv_num(st,& (st->stack->stack_data[st->start+4])); + sd=script_rid2sd(st); + pc_bonus2(sd,type,type2,val); + + return 0; +} +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +int buildin_bonus3(struct script_state *st) +{ + int type,type2,type3,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + type2=conv_num(st,& (st->stack->stack_data[st->start+3])); + type3=conv_num(st,& (st->stack->stack_data[st->start+4])); + val=conv_num(st,& (st->stack->stack_data[st->start+5])); + sd=script_rid2sd(st); + pc_bonus3(sd,type,type2,type3,val); + + return 0; +} +/*========================================== + * スキル所得 + *------------------------------------------ + */ +int buildin_skill(struct script_state *st) +{ + int id,level,flag=1; + struct map_session_data *sd; + + id=conv_num(st,& (st->stack->stack_data[st->start+2])); + level=conv_num(st,& (st->stack->stack_data[st->start+3])); + if( st->end>st->start+4 ) + flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); + sd=script_rid2sd(st); + pc_skill(sd,id,level,flag); + + return 0; +} +/*========================================== + * ギルドスキル取得 + *------------------------------------------ + */ +int buildin_guildskill(struct script_state *st) +{ + int id,level; + struct map_session_data *sd; + int i=0; + + id=conv_num(st,& (st->stack->stack_data[st->start+2])); + level=conv_num(st,& (st->stack->stack_data[st->start+3])); +// if( st->end>st->start+4 ) +// flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); + sd=script_rid2sd(st); + for(i=0;i<level;i++) + guild_skillup(sd,id); + + return 0; +} +/*========================================== + * スキルレベル所得 + *------------------------------------------ + */ +int buildin_getskilllv(struct script_state *st) +{ + int id=conv_num(st,& (st->stack->stack_data[st->start+2])); + push_val(st->stack,C_INT, pc_checkskill( script_rid2sd(st) ,id) ); + return 0; +} +/*========================================== + * getgdskilllv(Guild_ID, Skill_ID); + * skill_id = 10000 : GD_APPROVAL + * 10001 : GD_KAFRACONTACT + * 10002 : GD_GUARDIANRESEARCH + * 10003 : GD_CHARISMA + * 10004 : GD_EXTENSION + *------------------------------------------ + */ +int buildin_getgdskilllv(struct script_state *st) +{ + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + int skill_id=conv_num(st,& (st->stack->stack_data[st->start+3])); + struct guild *g=guild_search(guild_id); + push_val(st->stack,C_INT, (g==NULL)?-1:guild_checkskill(g,skill_id) ); + return 0; +/* + struct map_session_data *sd=NULL; + struct guild *g=NULL; + int skill_id; + + skill_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + if(sd && sd->status.guild_id > 0) g=guild_search(sd->status.guild_id); + if(sd && g) { + push_val(st->stack,C_INT, guild_checkskill(g,skill_id+9999) ); + } else { + push_val(st->stack,C_INT,-1); + } + return 0; +*/ +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_basicskillcheck(struct script_state *st) +{ + push_val(st->stack,C_INT, battle_config.basic_skill_check); + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_getgmlevel(struct script_state *st) +{ + push_val(st->stack,C_INT, pc_isGM(script_rid2sd(st))); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_end(struct script_state *st) +{ + st->state = END; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_checkoption(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + + if(sd->status.option & type){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_setoption(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + pc_setoption(sd,type); + + return 0; +} + +/*========================================== + * Checkcart [Valaris] + *------------------------------------------ + */ + +int buildin_checkcart(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(pc_iscarton(sd)){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + return 0; +} + +/*========================================== + * カートを付ける + *------------------------------------------ + */ +int buildin_setcart(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + pc_setcart(sd,1); + + return 0; +} + +/*========================================== + * checkfalcon [Valaris] + *------------------------------------------ + */ + +int buildin_checkfalcon(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(pc_isfalcon(sd)){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + + +/*========================================== + * 鷹を付ける + *------------------------------------------ + */ +int buildin_setfalcon(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + pc_setfalcon(sd); + + return 0; +} + +/*========================================== + * Checkcart [Valaris] + *------------------------------------------ + */ + +int buildin_checkriding(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(pc_isriding(sd)){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + + +/*========================================== + * ペコペコ乗り + *------------------------------------------ + */ +int buildin_setriding(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + pc_setriding(sd); + + return 0; +} + +/*========================================== + * セーブポイントの保存 + *------------------------------------------ + */ +int buildin_savepoint(struct script_state *st) +{ + int x,y; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + pc_setsavepoint(script_rid2sd(st),str,x,y); + return 0; +} + +/*========================================== + * GetTimeTick(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 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 *)aCalloc(maxlen+1,sizeof(char)); + strftime(tmpstr,maxlen,fmtstr,localtime(&now)); + tmpstr[maxlen]='\0'; + + push_str(st->stack,C_STR,tmpstr); + return 0; +} + +/*========================================== + * カプラ倉庫を開く + *------------------------------------------ + */ +int buildin_openstorage(struct script_state *st) +{ + storage_storageopen(script_rid2sd(st)); + return 0; +} + +int buildin_guildopenstorage(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int ret; + ret = storage_guild_storageopen(sd); + push_val(st->stack,C_INT,ret); + return 0; +} + +/*========================================== + * アイテムによるスキル発動 + *------------------------------------------ + */ +int buildin_itemskill(struct script_state *st) +{ + int id,lv; + char *str; + struct map_session_data *sd=script_rid2sd(st); + + id=conv_num(st,& (st->stack->stack_data[st->start+2])); + lv=conv_num(st,& (st->stack->stack_data[st->start+3])); + str=conv_str(st,& (st->stack->stack_data[st->start+4])); + + // 詠唱中にスキルアイテムは使用できない + if(sd->skilltimer != -1) + return 0; + + sd->skillitem=id; + sd->skillitemlv=lv; + clif_item_skill(sd,id,lv,str); + return 0; +} +/*========================================== + * アイテム作成 + *------------------------------------------ + */ +int buildin_produce(struct script_state *st) +{ + int trigger; + struct map_session_data *sd=script_rid2sd(st); + + if( sd->state.produce_flag == 1) return 0; + trigger=conv_num(st,& (st->stack->stack_data[st->start+2])); + clif_skill_produce_mix_list(sd,trigger); + return 0; +} +/*========================================== + * NPCでペット作る + *------------------------------------------ + */ +int buildin_makepet(struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd(st); + struct script_data *data; + int id,pet_id; + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + + id=conv_num(st,data); + + pet_id = search_petDB_index(id, PET_CLASS); + + if (pet_id < 0) + pet_id = search_petDB_index(id, PET_EGG); + if (pet_id >= 0 && sd) { + sd->catch_target_class = pet_db[pet_id].class; + intif_create_pet( + sd->status.account_id, sd->status.char_id, + pet_db[pet_id].class, mob_db[pet_db[pet_id].class].lv, + pet_db[pet_id].EggID, 0, pet_db[pet_id].intimate, + 100, 0, 1, pet_db[pet_id].jname); + } + + return 0; +} +/*========================================== + * NPCで経験値上げる + *------------------------------------------ + */ +int buildin_getexp(struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd(st); + int base=0,job=0; + + base=conv_num(st,& (st->stack->stack_data[st->start+2])); + job =conv_num(st,& (st->stack->stack_data[st->start+3])); + if(base<0 || job<0) + return 0; + if(sd) + pc_gainexp(sd,base,job); + + return 0; +} + +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +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])); + + mob_once_spawn(map_id2sd(st->rid),map,x,y,str,class,amount,event); + return 0; +} +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +int buildin_areamonster(struct script_state *st) +{ + int class,amount,x0,y0,x1,y1; + char *str,*map,*event=""; + + map =conv_str(st,& (st->stack->stack_data[st->start+2])); + x0 =conv_num(st,& (st->stack->stack_data[st->start+3])); + y0 =conv_num(st,& (st->stack->stack_data[st->start+4])); + x1 =conv_num(st,& (st->stack->stack_data[st->start+5])); + y1 =conv_num(st,& (st->stack->stack_data[st->start+6])); + str =conv_str(st,& (st->stack->stack_data[st->start+7])); + class=conv_num(st,& (st->stack->stack_data[st->start+8])); + amount=conv_num(st,& (st->stack->stack_data[st->start+9])); + if( st->end>st->start+10 ) + event=conv_str(st,& (st->stack->stack_data[st->start+10])); + + mob_once_spawn_area(map_id2sd(st->rid),map,x0,y0,x1,y1,str,class,amount,event); + return 0; +} +/*========================================== + * モンスター削除 + *------------------------------------------ + */ +int buildin_killmonster_sub(struct block_list *bl,va_list ap) +{ + char *event=va_arg(ap,char *); + int allflag=va_arg(ap,int); + + if(!allflag){ + if(strcmp(event,((struct mob_data *)bl)->npc_event)==0) + mob_delete((struct mob_data *)bl); + return 0; + }else if(allflag){ + if(((struct mob_data *)bl)->spawndelay1==-1 && ((struct mob_data *)bl)->spawndelay2==-1) + mob_delete((struct mob_data *)bl); + return 0; + } + return 0; +} +int buildin_killmonster(struct script_state *st) +{ + char *mapname,*event; + int m,allflag=0; + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + if(strcmp(event,"All")==0) + allflag = 1; + + if( (m=map_mapname2mapid(mapname))<0 ) + return 0; + map_foreachinarea(buildin_killmonster_sub, + m,0,0,map[m].xs,map[m].ys,BL_MOB, event ,allflag); + return 0; +} + +int buildin_killmonsterall_sub(struct block_list *bl,va_list ap) +{ + mob_delete((struct mob_data *)bl); + return 0; +} +int buildin_killmonsterall(struct script_state *st) +{ + char *mapname; + int m; + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + + if( (m=map_mapname2mapid(mapname))<0 ) + return 0; + map_foreachinarea(buildin_killmonsterall_sub, + m,0,0,map[m].xs,map[m].ys,BL_MOB); + return 0; +} + +/*========================================== + * イベント実行 + *------------------------------------------ + */ +int buildin_doevent(struct script_state *st) +{ + char *event; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_event(map_id2sd(st->rid),event,0); + return 0; +} +/*========================================== + * NPC主体イベント実行 + *------------------------------------------ + */ +int buildin_donpcevent(struct script_state *st) +{ + char *event; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_event_do(event); + return 0; +} +/*========================================== + * イベントタイマー追加 + *------------------------------------------ + */ +int buildin_addtimer(struct script_state *st) +{ + char *event; + int tick; + tick=conv_num(st,& (st->stack->stack_data[st->start+2])); + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + pc_addeventtimer(script_rid2sd(st),tick,event); + return 0; +} +/*========================================== + * イベントタイマー削除 + *------------------------------------------ + */ +int buildin_deltimer(struct script_state *st) +{ + char *event; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + pc_deleventtimer(script_rid2sd(st),event); + return 0; +} +/*========================================== + * イベントタイマーのカウント値追加 + *------------------------------------------ + */ +int buildin_addtimercount(struct script_state *st) +{ + char *event; + int tick; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + tick=conv_num(st,& (st->stack->stack_data[st->start+3])); + pc_addeventtimercount(script_rid2sd(st),event,tick); + return 0; +} + +/*========================================== + * NPCタイマー初期化 + *------------------------------------------ + */ +int buildin_initnpctimer(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + npc_settimerevent_tick(nd,0); + npc_timerevent_start(nd); + return 0; +} +/*========================================== + * NPCタイマー開始 + *------------------------------------------ + */ +int buildin_startnpctimer(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + npc_timerevent_start(nd); + return 0; +} +/*========================================== + * NPCタイマー停止 + *------------------------------------------ + */ +int buildin_stopnpctimer(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + npc_timerevent_stop(nd); + return 0; +} +/*========================================== + * NPCタイマー情報所得 + *------------------------------------------ + */ +int buildin_getnpctimer(struct script_state *st) +{ + struct npc_data *nd; + int type=conv_num(st,& (st->stack->stack_data[st->start+2])); + int val=0; + if( st->end > st->start+3 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + switch(type){ + case 0: val=npc_gettimerevent_tick(nd); break; + case 1: val= (nd->u.scr.nexttimer>=0); break; + case 2: val= nd->u.scr.timeramount; break; + } + push_val(st->stack,C_INT,val); + return 0; +} +/*========================================== + * NPCタイマー値設定 + *------------------------------------------ + */ +int buildin_setnpctimer(struct script_state *st) +{ + int tick; + struct npc_data *nd; + tick=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end > st->start+3 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + npc_settimerevent_tick(nd,tick); + return 0; +} + +/*========================================== + * 天の声アナウンス + *------------------------------------------ + */ +int buildin_announce(struct script_state *st) +{ + char *str; + int flag; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + flag=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if(flag&0x0f){ + struct block_list *bl=(flag&0x08)? map_id2bl(st->oid) : + (struct block_list *)script_rid2sd(st); + clif_GMmessage(bl,str,strlen(str)+1,flag); + }else + intif_GMmessage(str,strlen(str)+1,flag); + return 0; +} +/*========================================== + * 天の声アナウンス(特定マップ) + *------------------------------------------ + */ +int buildin_mapannounce_sub(struct block_list *bl,va_list ap) +{ + char *str; + int len,flag; + str=va_arg(ap,char *); + len=va_arg(ap,int); + flag=va_arg(ap,int); + clif_GMmessage(bl,str,len,flag|3); + return 0; +} +int buildin_mapannounce(struct script_state *st) +{ + char *mapname,*str; + int flag,m; + + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + str=conv_str(st,& (st->stack->stack_data[st->start+3])); + flag=conv_num(st,& (st->stack->stack_data[st->start+4])); + + if( (m=map_mapname2mapid(mapname))<0 ) + return 0; + map_foreachinarea(buildin_mapannounce_sub, + m,0,0,map[m].xs,map[m].ys,BL_PC, str,strlen(str)+1,flag&0x10); + return 0; +} +/*========================================== + * 天の声アナウンス(特定エリア) + *------------------------------------------ + */ +int buildin_areaannounce(struct script_state *st) +{ + char *map,*str; + int flag,m; + int x0,y0,x1,y1; + + map=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + str=conv_str(st,& (st->stack->stack_data[st->start+7])); + flag=conv_num(st,& (st->stack->stack_data[st->start+8])); + + if( (m=map_mapname2mapid(map))<0 ) + return 0; + + map_foreachinarea(buildin_mapannounce_sub, + m,x0,y0,x1,y1,BL_PC, str,strlen(str)+1,flag&0x10 ); + return 0; +} +/*========================================== + * ユーザー数所得 + *------------------------------------------ + */ +int buildin_getusers(struct script_state *st) +{ + int flag=conv_num(st,& (st->stack->stack_data[st->start+2])); + struct block_list *bl=map_id2bl((flag&0x08)?st->oid:st->rid); + int val=0; + switch(flag&0x07){ + case 0: val=map[bl->m].users; break; + case 1: val=map_getusers(); break; + } + push_val(st->stack,C_INT,val); + return 0; +} +/*========================================== + * マップ指定ユーザー数所得 + *------------------------------------------ + */ +int buildin_getmapusers(struct script_state *st) +{ + char *str; + int m; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + if( (m=map_mapname2mapid(str))< 0){ + push_val(st->stack,C_INT,-1); + return 0; + } + push_val(st->stack,C_INT,map[m].users); + return 0; +} +/*========================================== + * エリア指定ユーザー数所得 + *------------------------------------------ + */ +int buildin_getareausers_sub(struct block_list *bl,va_list ap) +{ + int *users=va_arg(ap,int *); + (*users)++; + return 0; +} +int buildin_getareausers(struct script_state *st) +{ + char *str; + int m,x0,y0,x1,y1,users=0; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + if( (m=map_mapname2mapid(str))< 0){ + push_val(st->stack,C_INT,-1); + return 0; + } + map_foreachinarea(buildin_getareausers_sub, + m,x0,y0,x1,y1,BL_PC,&users); + push_val(st->stack,C_INT,users); + return 0; +} + +/*========================================== + * エリア指定ドロップアイテム数所得 + *------------------------------------------ + */ +int buildin_getareadropitem_sub(struct block_list *bl,va_list ap) +{ + int item=va_arg(ap,int); + int *amount=va_arg(ap,int *); + struct flooritem_data *drop=(struct flooritem_data *)bl; + + if(drop->item_data.nameid==item) + (*amount)+=drop->item_data.amount; + + return 0; +} +int buildin_getareadropitem(struct script_state *st) +{ + char *str; + int m,x0,y0,x1,y1,item,amount=0; + struct script_data *data; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + + data=&(st->stack->stack_data[st->start+7]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + item=512; + if( item_data ) + item=item_data->nameid; + }else + item=conv_num(st,data); + + if( (m=map_mapname2mapid(str))< 0){ + push_val(st->stack,C_INT,-1); + return 0; + } + map_foreachinarea(buildin_getareadropitem_sub, + m,x0,y0,x1,y1,BL_ITEM,item,&amount); + push_val(st->stack,C_INT,amount); + return 0; +} +/*========================================== + * NPCの有効化 + *------------------------------------------ + */ +int buildin_enablenpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,1); + return 0; +} +/*========================================== + * NPCの無効化 + *------------------------------------------ + */ +int buildin_disablenpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,0); + return 0; +} + +int buildin_enablearena(struct script_state *st) // Added by RoVeRT +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + struct chat_data *cd; + + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL) + return 0; + + npc_enable(nd->name,1); + nd->arenaflag=1; + + if(cd->users>=cd->trigger && cd->npc_event[0]) + npc_timer_event(cd->npc_event); + + return 0; +} +int buildin_disablearena(struct script_state *st) // Added by RoVeRT +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + nd->arenaflag=0; + + return 0; +} +/*========================================== + * 隠れているNPCの表示 + *------------------------------------------ + */ +int buildin_hideoffnpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,2); + return 0; +} +/*========================================== + * NPCをハイディング + *------------------------------------------ + */ +int buildin_hideonnpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,4); + return 0; +} +/*========================================== + * 状態異常にかかる + *------------------------------------------ + */ +int buildin_sc_start(struct script_state *st) +{ + struct block_list *bl; + int type,tick,val1; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + tick=conv_num(st,& (st->stack->stack_data[st->start+3])); + val1=conv_num(st,& (st->stack->stack_data[st->start+4])); + if( st->end>st->start+5 ) //指定したキャラを状態異常にする + bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+5]))); + else + bl = map_id2bl(st->rid); + if(bl->type == BL_PC && ((struct map_session_data *)bl)->state.potionpitcher_flag) + bl = map_id2bl(((struct map_session_data *)bl)->skilltarget); + skill_status_change_start(bl,type,val1,0,0,0,tick,0); + return 0; +} + +/*========================================== + * 状態異常にかかる(確率指定) + *------------------------------------------ + */ +int buildin_sc_start2(struct script_state *st) +{ + struct block_list *bl; + int type,tick,val1,per; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + tick=conv_num(st,& (st->stack->stack_data[st->start+3])); + val1=conv_num(st,& (st->stack->stack_data[st->start+4])); + per=conv_num(st,& (st->stack->stack_data[st->start+5])); + if( st->end>st->start+6 ) //指定したキャラを状態異常にする + bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6]))); + else + bl = map_id2bl(st->rid); + if(bl->type == BL_PC && ((struct map_session_data *)bl)->state.potionpitcher_flag) + bl = map_id2bl(((struct map_session_data *)bl)->skilltarget); + if(rand()%10000 < per) + skill_status_change_start(bl,type,val1,0,0,0,tick,0); + return 0; +} + +/*========================================== + * 状態異常が直る + *------------------------------------------ + */ +int buildin_sc_end(struct script_state *st) +{ + struct block_list *bl; + int type; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + bl = map_id2bl(st->rid); + if(bl->type == BL_PC && ((struct map_session_data *)bl)->state.potionpitcher_flag) + bl = map_id2bl(((struct map_session_data *)bl)->skilltarget); + skill_status_change_end(bl,type,-1); +// if(battle_config.etc_log) +// printf("sc_end : %d %d\n",st->rid,type); + return 0; +} +/*========================================== + * 状態異常耐性を計算した確率を返す + *------------------------------------------ + */ +int buildin_getscrate(struct script_state *st) +{ + struct block_list *bl; + int sc_def=100,sc_def_mdef2,sc_def_vit2,sc_def_int2,sc_def_luk2; + int type,rate,luk; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + rate=conv_num(st,& (st->stack->stack_data[st->start+3])); + if( st->end>st->start+4 ) //指定したキャラの耐性を計算する + bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6]))); + else + bl = map_id2bl(st->rid); + + luk = battle_get_luk(bl); + sc_def_mdef2=100 - (3 + battle_get_mdef(bl) + luk/3); + sc_def_vit2=100 - (3 + battle_get_vit(bl) + luk/3); + sc_def_int2=100 - (3 + battle_get_int(bl) + luk/3); + sc_def_luk2=100 - (3 + luk); + + if(type==SC_STONE || type==SC_FREEZE) + sc_def=sc_def_mdef2; + else if(type==SC_STAN || type==SC_POISON || type==SC_SILENCE) + sc_def=sc_def_vit2; + else if(type==SC_SLEEP || type==SC_CONFUSION || type==SC_BLIND) + sc_def=sc_def_int2; + else if(type==SC_CURSE) + sc_def=sc_def_luk2; + + rate=rate*sc_def/100; + push_val(st->stack,C_INT,rate); + + return 0; + +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_debugmes(struct script_state *st) +{ + conv_str(st,& (st->stack->stack_data[st->start+2])); + printf("script debug : %d %d : %s\n",st->rid,st->oid,st->stack->stack_data[st->start+2].u.str); + return 0; +} + +/*========================================== + *捕獲アイテム使用 + *------------------------------------------ + */ +int buildin_catchpet(struct script_state *st) +{ + int pet_id; + struct map_session_data *sd; + pet_id= conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + pet_catch_process1(sd,pet_id); + return 0; +} + +/*========================================== + *携帯卵孵化機使用 + *------------------------------------------ + */ +int buildin_birthpet(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + clif_sendegg(sd); + return 0; +} + +/*========================================== + * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes) + *------------------------------------------ + */ +int buildin_resetlvl(struct script_state *st) +{ + struct map_session_data *sd; + + int type=conv_num(st,& (st->stack->stack_data[st->start+2])); + + sd=script_rid2sd(st); + pc_resetlvl(sd,type); + return 0; +} +/*========================================== + * ステータスリセット + *------------------------------------------ + */ +int buildin_resetstatus(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + pc_resetstate(sd); + return 0; +} + +/*========================================== + * スキルリセット + *------------------------------------------ + */ +int buildin_resetskill(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + pc_resetskill(sd); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_changebase(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int vclass; + + if( st->end>st->start+3 ) + sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+3]))); + else + sd=script_rid2sd(st); + + if(sd == NULL) + return 0; + + vclass = conv_num(st,& (st->stack->stack_data[st->start+2])); + if(vclass == 22 && !battle_config.wedding_modifydisplay) + return 0; + +// if(vclass==22) { +// pc_unequipitem(sd,sd->equip_index[9],0); // 装備外 +// } + + sd->view_class = vclass; + + return 0; +} + +/*========================================== + * 性別変換 + *------------------------------------------ + */ +int buildin_changesex(struct script_state *st) { + struct map_session_data *sd = NULL; + sd = script_rid2sd(st); + + if (sd->status.sex == 0) { + sd->status.sex = 1; + sd->sex = 1; + if (sd->status.class == 20 || sd->status.class == 4021) + sd->status.class -= 1; + } else if (sd->status.sex == 1) { + sd->status.sex = 0; + sd->sex = 0; + if(sd->status.class == 19 || sd->status.class == 4020) + 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); + return 0; +} + +/*========================================== + * npcチャット作成 + *------------------------------------------ + */ +int buildin_waitingroom(struct script_state *st) +{ + char *name,*ev=""; + int limit, trigger = 0,pub=1; + name=conv_str(st,& (st->stack->stack_data[st->start+2])); + limit= conv_num(st,& (st->stack->stack_data[st->start+3])); + if(limit==0) + pub=3; + + if( (st->end > st->start+5) ){ + struct script_data* data=&(st->stack->stack_data[st->start+5]); + get_val(st,data); + if(data->type==C_INT){ + // 新Athena仕様(旧Athena仕様と互換性あり) + ev=conv_str(st,& (st->stack->stack_data[st->start+4])); + trigger=conv_num(st,& (st->stack->stack_data[st->start+5])); + }else{ + // eathena仕様 + trigger=conv_num(st,& (st->stack->stack_data[st->start+4])); + ev=conv_str(st,& (st->stack->stack_data[st->start+5])); + } + }else{ + // 旧Athena仕様 + if( st->end > st->start+4 ) + ev=conv_str(st,& (st->stack->stack_data[st->start+4])); + } + chat_createnpcchat( (struct npc_data *)map_id2bl(st->oid), + limit,pub,trigger,name,strlen(name)+1,ev); + return 0; +} +/*========================================== + * npcチャット削除 + *------------------------------------------ + */ +int buildin_delwaitingroom(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + chat_deletenpcchat(nd); + return 0; +} +/*========================================== + * npcチャット全員蹴り出す + *------------------------------------------ + */ +int buildin_waitingroomkickall(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + chat_npckickall(cd); + return 0; +} + +/*========================================== + * npcチャットイベント有効化 + *------------------------------------------ + */ +int buildin_enablewaitingroomevent(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + chat_enableevent(cd); + return 0; +} + +/*========================================== + * npcチャットイベント無効化 + *------------------------------------------ + */ +int buildin_disablewaitingroomevent(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + chat_disableevent(cd); + return 0; +} +/*========================================== + * npcチャット状態所得 + *------------------------------------------ + */ +int buildin_getwaitingroomstate(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + int val=0,type; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end > st->start+3 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ){ + push_val(st->stack,C_INT,-1); + return 0; + } + + switch(type){ + case 0: val=cd->users; break; + case 1: val=cd->limit; break; + case 2: val=cd->trigger&0x7f; break; + case 3: val=((cd->trigger&0x80)>0); break; + case 32: val=(cd->users >= cd->limit); break; + case 33: val=(cd->users >= cd->trigger); break; + + case 4: + push_str(st->stack,C_CONSTSTR,cd->title); + return 0; + case 5: + push_str(st->stack,C_CONSTSTR,cd->pass); + return 0; + case 16: + push_str(st->stack,C_CONSTSTR,cd->npc_event); + return 0; + } + push_val(st->stack,C_INT,val); + return 0; +} + +/*========================================== + * チャットメンバー(規定人数)ワープ + *------------------------------------------ + */ +int buildin_warpwaitingpc(struct script_state *st) +{ + int x,y,i,n; + char *str; + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + struct chat_data *cd; + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + + n=cd->trigger&0x7f; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + + if( st->end > st->start+5 ) + n=conv_num(st,& (st->stack->stack_data[st->start+5])); + + for(i=0;i<n;i++){ + struct map_session_data *sd=cd->usersd[0]; // リスト先頭のPCを次々に。 + + mapreg_setreg(add_str("$@warpwaitingpc")+(i<<24),sd->bl.id); + + if(strcmp(str,"Random")==0) + pc_randomwarp(sd,3); + else if(strcmp(str,"SavePoint")==0){ + if(map[sd->bl.m].flag.noteleport) // テレポ禁止 + return 0; + + pc_setpos(sd,sd->status.save_point.map, + sd->status.save_point.x,sd->status.save_point.y,3); + }else + pc_setpos(sd,str,x,y,0); + } + mapreg_setreg(add_str("$@warpwaitingpcnum"),n); + return 0; +} +/*========================================== + * RIDのアタッチ + *------------------------------------------ + */ +int buildin_attachrid(struct script_state *st) +{ + st->rid=conv_num(st,& (st->stack->stack_data[st->start+2])); + push_val(st->stack,C_INT, (map_id2sd(st->rid)!=NULL)); + return 0; +} +/*========================================== + * RIDのデタッチ + *------------------------------------------ + */ +int buildin_detachrid(struct script_state *st) +{ + st->rid=0; + return 0; +} +/*========================================== + * 存在チェック + *------------------------------------------ + */ +int buildin_isloggedin(struct script_state *st) +{ + push_val(st->stack,C_INT, map_id2sd( + conv_num(st,& (st->stack->stack_data[st->start+2])) )!=NULL ); + return 0; +} + + +/*========================================== + * + *------------------------------------------ + */ +enum { MF_NOMEMO,MF_NOTELEPORT,MF_NOSAVE,MF_NOBRANCH,MF_NOPENALTY,MF_NOZENYPENALTY,MF_PVP,MF_PVP_NOPARTY,MF_PVP_NOGUILD,MF_GVG,MF_GVG_NOPARTY,MF_NOTRADE,MF_NOSKILL, MF_NOWARP,MF_NOPVP,MF_NOICEWALL, + MF_SNOW, MF_FOG, MF_SAKURA, MF_LEAVES, MF_RAIN }; + +int buildin_setmapflagnosave(struct script_state *st) +{ + int m,x,y; + char *str,*str2; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + str2=conv_str(st,& (st->stack->stack_data[st->start+3])); + x=conv_num(st,& (st->stack->stack_data[st->start+4])); + y=conv_num(st,& (st->stack->stack_data[st->start+5])); + m = map_mapname2mapid(str); + if(m >= 0) { + map[m].flag.nosave=1; + memcpy(map[m].save.map,str2,16); + map[m].save.x=x; + map[m].save.y=y; + } + + return 0; +} + +int buildin_setmapflag(struct script_state *st) +{ + int m,i; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + i=conv_num(st,& (st->stack->stack_data[st->start+3])); + m = map_mapname2mapid(str); + if(m >= 0) { + switch(i) { + case MF_NOMEMO: + map[m].flag.nomemo=1; + break; + case MF_NOTELEPORT: + map[m].flag.noteleport=1; + break; + case MF_NOBRANCH: + map[m].flag.nobranch=1; + break; + case MF_NOPENALTY: + map[m].flag.nopenalty=1; + break; + case MF_PVP_NOPARTY: + map[m].flag.pvp_noparty=1; + break; + case MF_PVP_NOGUILD: + map[m].flag.pvp_noguild=1; + break; + case MF_GVG_NOPARTY: + map[m].flag.gvg_noparty=1; + break; + case MF_NOZENYPENALTY: + map[m].flag.nozenypenalty=1; + break; + case MF_NOTRADE: + map[m].flag.notrade=1; + break; + case MF_NOSKILL: + map[m].flag.noskill=1; + break; + case MF_NOWARP: + map[m].flag.nowarp=1; + break; + case MF_NOPVP: + map[m].flag.nopvp=1; + break; + case MF_NOICEWALL: // [Valaris] + map[m].flag.noicewall=1; + break; + case MF_SNOW: // [Valaris] + map[m].flag.snow=1; + break; + case MF_FOG: // [Valaris] + map[m].flag.fog=1; + break; + case MF_SAKURA: // [Valaris] + map[m].flag.sakura=1; + break; + case MF_LEAVES: // [Valaris] + map[m].flag.leaves=1; + break; + case MF_RAIN: // [Valaris] + map[m].flag.rain=1; + break; + } + } + + return 0; +} + +int buildin_removemapflag(struct script_state *st) +{ + int m,i; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + i=conv_num(st,& (st->stack->stack_data[st->start+3])); + m = map_mapname2mapid(str); + if(m >= 0) { + switch(i) { + case MF_NOMEMO: + map[m].flag.nomemo=0; + break; + case MF_NOTELEPORT: + map[m].flag.noteleport=0; + break; + case MF_NOSAVE: + map[m].flag.nosave=0; + break; + case MF_NOBRANCH: + map[m].flag.nobranch=0; + break; + case MF_NOPENALTY: + map[m].flag.nopenalty=0; + break; + case MF_PVP_NOPARTY: + map[m].flag.pvp_noparty=0; + break; + case MF_PVP_NOGUILD: + map[m].flag.pvp_noguild=0; + break; + case MF_GVG_NOPARTY: + map[m].flag.gvg_noparty=0; + break; + case MF_NOZENYPENALTY: + map[m].flag.nozenypenalty=0; + break; + case MF_NOSKILL: + map[m].flag.noskill=0; + break; + case MF_NOWARP: + map[m].flag.nowarp=0; + break; + case MF_NOPVP: + map[m].flag.nopvp=0; + break; + case MF_NOICEWALL: // [Valaris] + map[m].flag.noicewall=0; + break; + case MF_SNOW: // [Valaris] + map[m].flag.snow=0; + break; + case MF_FOG: // [Valaris] + map[m].flag.fog=0; + break; + case MF_SAKURA: // [Valaris] + map[m].flag.sakura=0; + break; + case MF_LEAVES: // [Valaris] + map[m].flag.leaves=0; + break; + case MF_RAIN: // [Valaris] + map[m].flag.rain=0; + break; + + } + } + + return 0; +} + +int buildin_pvpon(struct script_state *st) +{ + int m,i; + char *str; + struct map_session_data *pl_sd=NULL; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && !map[m].flag.pvp && !map[m].flag.nopvp) { + map[m].flag.pvp = 1; + clif_send0199(m,1); + + if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris] + return 0; + + for(i=0;i<fd_max;i++){ //人数分ループ + if(session[i] && (pl_sd=session[i]->session_data) && pl_sd->state.auth){ + if(m == pl_sd->bl.m && pl_sd->pvp_timer == -1) { + pl_sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,pl_sd->bl.id,0); + pl_sd->pvp_rank=0; + pl_sd->pvp_lastusers=0; + pl_sd->pvp_point=5; + } + } + } + } + + return 0; +} + +int buildin_pvpoff(struct script_state *st) +{ + int m,i; + char *str; + struct map_session_data *pl_sd=NULL; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && map[m].flag.pvp && map[m].flag.nopvp) { + map[m].flag.pvp = 0; + clif_send0199(m,0); + + if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] + return 0; + + for(i=0;i<fd_max;i++){ //人数分ループ + if(session[i] && (pl_sd=session[i]->session_data) && pl_sd->state.auth){ + if(m == pl_sd->bl.m) { + clif_pvpset(pl_sd,0,0,2); + if(pl_sd->pvp_timer != -1) { + delete_timer(pl_sd->pvp_timer,pc_calc_pvprank_timer); + pl_sd->pvp_timer = -1; + } + } + } + } + } + + return 0; +} + +int buildin_gvgon(struct script_state *st) +{ + int m; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && !map[m].flag.gvg) { + map[m].flag.gvg = 1; + clif_send0199(m,3); + } + + return 0; +} +int buildin_gvgoff(struct script_state *st) +{ + int m; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && map[m].flag.gvg) { + map[m].flag.gvg = 0; + clif_send0199(m,0); + } + + return 0; +} +/*========================================== + * NPCエモーション + *------------------------------------------ + */ + +int buildin_emotion(struct script_state *st) +{ + int type; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(type < 0 || type > 100) + return 0; + clif_emotion(map_id2bl(st->oid),type); + return 0; +} + +int buildin_maprespawnguildid_sub(struct block_list *bl,va_list ap) +{ + int g_id=va_arg(ap,int); + int flag=va_arg(ap,int); + struct map_session_data *sd=NULL; + struct mob_data *md=NULL; + + if(bl->type == BL_PC) + sd=(struct map_session_data*)bl; + if(bl->type == BL_MOB) + md=(struct mob_data *)bl; + + if(sd){ + if((sd->status.guild_id == g_id) && (flag&1)) + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + else if((sd->status.guild_id != g_id) && (flag&2)) + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + else if (sd->status.guild_id == 0) // Warp out players not in guild [Valaris] + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); // end addition [Valaris] + } + if(md && flag&4){ + if(md->class < 1285 || md->class > 1288) + mob_delete(md); + } + return 0; +} +int buildin_maprespawnguildid(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + int g_id=conv_num(st,& (st->stack->stack_data[st->start+3])); + int flag=conv_num(st,& (st->stack->stack_data[st->start+4])); + + int m=map_mapname2mapid(mapname); + + if(m) map_foreachinarea(buildin_maprespawnguildid_sub,m,0,0,map[m].xs-1,map[m].ys-1,BL_NUL,g_id,flag); + return 0; +} + +int buildin_agitstart(struct script_state *st) +{ + if(agit_flag==1) return 1; // Agit already Start. + agit_flag=1; + guild_agit_start(); + return 0; +} + +int buildin_agitend(struct script_state *st) +{ + if(agit_flag==0) return 1; // Agit already End. + agit_flag=0; + guild_agit_end(); + return 0; +} +/*========================================== + * agitcheck 1; // choice script + * if(@agit_flag == 1) goto agit; + * if(agitcheck(0) == 1) goto agit; + *------------------------------------------ + */ +int buildin_agitcheck(struct script_state *st) +{ + struct map_session_data *sd; + int cond; + + sd=script_rid2sd(st); + cond=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(cond == 0) { + if (agit_flag==1) push_val(st->stack,C_INT,1); + if (agit_flag==0) push_val(st->stack,C_INT,0); + } else { + if (agit_flag==1) pc_setreg(sd,add_str("@agit_flag"),1); + if (agit_flag==0) pc_setreg(sd,add_str("@agit_flag"),0); + } + return 0; +} +int buildin_flagemblem(struct script_state *st) +{ + int g_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(g_id < 0) return 0; + +// printf("Script.c: [FlagEmblem] GuildID=%d, Emblem=%d.\n", g->guild_id, g->emblem_id); + ((struct npc_data *)map_id2bl(st->oid))->u.scr.guild_id = g_id; + return 1; +} + +int buildin_getcastlename(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + struct guild_castle *gc; + int i; + char *buf=NULL; + for(i=0;i<MAX_GUILDCASTLE;i++){ + if( (gc=guild_castle_search(i)) != NULL ){ + if(strcmp(mapname,gc->map_name)==0){ + buf=(char *)aCalloc(24,sizeof(char)); + strncpy(buf,gc->castle_name,24); + break; + } + } + } + if(buf) + push_str(st->stack,C_STR,buf); + else + push_str(st->stack,C_CONSTSTR,""); + return 0; +} + +int buildin_getcastledata(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + int index=conv_num(st,& (st->stack->stack_data[st->start+3])); + char *event=NULL; + struct guild_castle *gc; + int i,j; + + if( st->end>st->start+4 && index==0){ + for(i=0,j=-1;i<MAX_GUILDCASTLE;i++) + if( (gc=guild_castle_search(i)) != NULL && + strcmp(mapname,gc->map_name)==0 ) + j=i; + if(j>=0){ + event=conv_str(st,& (st->stack->stack_data[st->start+4])); + guild_addcastleinfoevent(j,17,event); + } + } + + for(i=0;i<MAX_GUILDCASTLE;i++){ + if( (gc=guild_castle_search(i)) != NULL ){ + if(strcmp(mapname,gc->map_name)==0){ + switch(index){ + case 0: for(j=1;j<26;j++) guild_castledataload(gc->castle_id,j); break; // Initialize[AgitInit] + case 1: push_val(st->stack,C_INT,gc->guild_id); break; + case 2: push_val(st->stack,C_INT,gc->economy); break; + case 3: push_val(st->stack,C_INT,gc->defense); break; + case 4: push_val(st->stack,C_INT,gc->triggerE); break; + case 5: push_val(st->stack,C_INT,gc->triggerD); break; + case 6: push_val(st->stack,C_INT,gc->nextTime); break; + case 7: push_val(st->stack,C_INT,gc->payTime); break; + case 8: push_val(st->stack,C_INT,gc->createTime); break; + case 9: push_val(st->stack,C_INT,gc->visibleC); break; + case 10: push_val(st->stack,C_INT,gc->visibleG0); break; + case 11: push_val(st->stack,C_INT,gc->visibleG1); break; + case 12: push_val(st->stack,C_INT,gc->visibleG2); break; + case 13: push_val(st->stack,C_INT,gc->visibleG3); break; + case 14: push_val(st->stack,C_INT,gc->visibleG4); break; + case 15: push_val(st->stack,C_INT,gc->visibleG5); break; + case 16: push_val(st->stack,C_INT,gc->visibleG6); break; + case 17: push_val(st->stack,C_INT,gc->visibleG7); break; + case 18: push_val(st->stack,C_INT,gc->Ghp0); break; + case 19: push_val(st->stack,C_INT,gc->Ghp1); break; + case 20: push_val(st->stack,C_INT,gc->Ghp2); break; + case 21: push_val(st->stack,C_INT,gc->Ghp3); break; + case 22: push_val(st->stack,C_INT,gc->Ghp4); break; + case 23: push_val(st->stack,C_INT,gc->Ghp5); break; + case 24: push_val(st->stack,C_INT,gc->Ghp6); break; + case 25: push_val(st->stack,C_INT,gc->Ghp7); break; + default: + push_val(st->stack,C_INT,0); break; + } + return 0; + } + } + } + push_val(st->stack,C_INT,0); + return 0; +} + +int buildin_setcastledata(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + int index=conv_num(st,& (st->stack->stack_data[st->start+3])); + int value=conv_num(st,& (st->stack->stack_data[st->start+4])); + struct guild_castle *gc; + int i; + + for(i=0;i<MAX_GUILDCASTLE;i++){ + if( (gc=guild_castle_search(i)) != NULL ){ + if(strcmp(mapname,gc->map_name)==0){ + // Save Data byself First + switch(index){ + case 1: gc->guild_id = value; break; + case 2: gc->economy = value; break; + case 3: gc->defense = value; break; + case 4: gc->triggerE = value; break; + case 5: gc->triggerD = value; break; + case 6: gc->nextTime = value; break; + case 7: gc->payTime = value; break; + case 8: gc->createTime = value; break; + case 9: gc->visibleC = value; break; + case 10: gc->visibleG0 = value; break; + case 11: gc->visibleG1 = value; break; + case 12: gc->visibleG2 = value; break; + case 13: gc->visibleG3 = value; break; + case 14: gc->visibleG4 = value; break; + case 15: gc->visibleG5 = value; break; + case 16: gc->visibleG6 = value; break; + case 17: gc->visibleG7 = value; break; + case 18: gc->Ghp0 = value; break; + case 19: gc->Ghp1 = value; break; + case 20: gc->Ghp2 = value; break; + case 21: gc->Ghp3 = value; break; + case 22: gc->Ghp4 = value; break; + case 23: gc->Ghp5 = value; break; + case 24: gc->Ghp6 = value; break; + case 25: gc->Ghp7 = value; break; + default: return 0; + } + guild_castledatasave(gc->castle_id,index,value); + return 0; + } + } + } + return 0; +} + +/* ===================================================================== + * ギルド情報を要求する + * --------------------------------------------------------------------- + */ +int buildin_requestguildinfo(struct script_state *st) +{ + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + char *event=NULL; + + if( st->end>st->start+3 ) + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + + if(guild_id>0) + guild_npc_request_info(guild_id,event); + return 0; +} + +/* ===================================================================== + * カードの数を得る + * --------------------------------------------------------------------- + */ +int buildin_getequipcardcnt(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + int c=4; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(sd->status.inventory[i].card[0] == 0x00ff){ // 製造武器はカードなし + push_val(st->stack,C_INT,0); + return 0; + } + do{ + if( (sd->status.inventory[i].card[c-1] > 4000) && + (sd->status.inventory[i].card[c-1] < 5000)){ + + push_val(st->stack,C_INT,(c)); + return 0; + } + }while(c--); + push_val(st->stack,C_INT,0); + return 0; +} + +/* ================================================================ + * カード取り外し成功 + * ---------------------------------------------------------------- + */ +int buildin_successremovecards(struct script_state *st) +{ + int i,num,cardflag=0,flag; + struct map_session_data *sd; + struct item item_tmp; + int c=4; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない + return 0; + } + do{ + if( (sd->status.inventory[i].card[c-1] > 4000) && + (sd->status.inventory[i].card[c-1] < 5000)){ + + cardflag = 1; + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1]; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0; + item_tmp.attribute=0; + item_tmp.card[0]=0,item_tmp.card[1]=0,item_tmp.card[2]=0,item_tmp.card[3]=0; + + if((flag=pc_additem(sd,&item_tmp,1))){ // 持てないならドロップ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + }while(c--); + + if(cardflag == 1){ // カードを取り除いたアイテム所得 + flag=0; + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine; + item_tmp.attribute=sd->status.inventory[i].attribute; + item_tmp.card[0]=0,item_tmp.card[1]=0,item_tmp.card[2]=0,item_tmp.card[3]=0; + pc_delitem(sd,i,1,0); + if((flag=pc_additem(sd,&item_tmp,1))){ // もてないならドロップ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + clif_misceffect(&sd->bl,3); + return 0; + } + return 0; +} + +/* ================================================================ + * カード取り外し失敗 slot,type + * type=0: 両方損失、1:カード損失、2:武具損失、3:損失無し + * ---------------------------------------------------------------- + */ +int buildin_failedremovecards(struct script_state *st) +{ + int i,num,cardflag=0,flag,typefail; + struct map_session_data *sd; + struct item item_tmp; + int c=4; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + typefail=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない + return 0; + } + do{ + if(( sd->status.inventory[i].card[c-1] > 4000) && + (sd->status.inventory[i].card[c-1] < 5000)){ + + cardflag = 1; + + if(typefail == 2){ // 武具のみ損失なら、カードは受け取らせる + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1]; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0; + item_tmp.attribute=0; + item_tmp.card[0]=0,item_tmp.card[1]=0,item_tmp.card[2]=0,item_tmp.card[3]=0; + if((flag=pc_additem(sd,&item_tmp,1))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + } + }while(c--); + + if(cardflag == 1){ + + if(typefail == 0 || typefail == 2){ // 武具損失 + pc_delitem(sd,i,1,0); + clif_misceffect(&sd->bl,2); + return 0; + } + if(typefail == 1){ // カードのみ損失(武具を返す) + flag=0; + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine; + item_tmp.attribute=sd->status.inventory[i].attribute; + item_tmp.card[0]=0,item_tmp.card[1]=0,item_tmp.card[2]=0,item_tmp.card[3]=0; + pc_delitem(sd,i,1,0); + if((flag=pc_additem(sd,&item_tmp,1))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + clif_misceffect(&sd->bl,2); + return 0; + } + return 0; +} + +int buildin_mapwarp(struct script_state *st) // Added by RoVeRT +{ + int x,y,m; + char *str; + char *mapname; + int x0,y0,x1,y1; + + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=0; + y0=0; + x1=map[map_mapname2mapid(mapname)].xs; + y1=map[map_mapname2mapid(mapname)].ys; + str=conv_str(st,& (st->stack->stack_data[st->start+3])); + x=conv_num(st,& (st->stack->stack_data[st->start+4])); + y=conv_num(st,& (st->stack->stack_data[st->start+5])); + + if( (m=map_mapname2mapid(mapname))< 0) + return 0; + + map_foreachinarea(buildin_areawarp_sub, + m,x0,y0,x1,y1,BL_PC, str,x,y ); + return 0; +} + +int buildin_cmdothernpc(struct script_state *st) // Added by RoVeRT +{ + char *npc,*command; + + npc=conv_str(st,& (st->stack->stack_data[st->start+2])); + command=conv_str(st,& (st->stack->stack_data[st->start+3])); + + npc_command(map_id2sd(st->rid),npc,command); + return 0; +} + +int buildin_inittimer(struct script_state *st) // Added by RoVeRT +{ +// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid); + +// nd->lastaction=nd->timer=gettick(); + npc_do_ontimer(st->oid, map_id2sd(st->rid), 1); + + return 0; +} + +int buildin_stoptimer(struct script_state *st) // Added by RoVeRT +{ +// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid); + +// nd->lastaction=nd->timer=-1; + npc_do_ontimer(st->oid, map_id2sd(st->rid), 0); + + return 0; +} + +int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT +{ + char *event=va_arg(ap,char *); + int *c=va_arg(ap,int *); + + if(strcmp(event,((struct mob_data *)bl)->npc_event)==0) + (*c)++; + return 0; +} + +int buildin_mobcount(struct script_state *st) // Added by RoVeRT +{ + char *mapname,*event; + int m,c=0; + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + + if( (m=map_mapname2mapid(mapname))<0 ) { + push_val(st->stack,C_INT,-1); + return 0; + } + map_foreachinarea(buildin_mobcount_sub, + m,0,0,map[m].xs,map[m].ys,BL_MOB, event,&c ); + + push_val(st->stack,C_INT, (c - 1)); + + return 0; +} +int buildin_marriage(struct script_state *st) +{ + char *partner=conv_str(st,& (st->stack->stack_data[st->start+2])); + struct map_session_data *sd=script_rid2sd(st); + struct map_session_data *p_sd=map_nick2sd(partner); + + if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){ + push_val(st->stack,C_INT,0); + return 0; + } + push_val(st->stack,C_INT,1); + return 0; +} +int buildin_wedding_effect(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL) + return 0; + clif_wedding_effect(&sd->bl); + return 0; +} +int buildin_divorce(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + if(sd==NULL || pc_divorce(sd) < 0){ + push_val(st->stack,C_INT,0); + return 0; + } + push_val(st->stack,C_INT,1); + return 0; +} + +/*================================================ + * Script for Displaying MOB Information [Valaris] + *------------------------------------------------ + */ +int buildin_strmobinfo(struct script_state *st) +{ + + int num=conv_num(st,& (st->stack->stack_data[st->start+2])); + int class=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if(num<=0 || num>=8 || (class>=0 && class<=1000) || class >2000) + return 0; + + if(num==1) { + char *buf; + buf=calloc(24, 1); + buf=mob_db[class].name; + push_str(st->stack,C_STR,buf); + return 0; + } + else if(num==2) { + char *buf; + buf=calloc(24, 1); + buf=mob_db[class].jname; + push_str(st->stack,C_STR,buf); + return 0; + } + else if(num==3) + push_val(st->stack,C_INT,mob_db[class].lv); + else if(num==4) + push_val(st->stack,C_INT,mob_db[class].max_hp); + else if(num==5) + push_val(st->stack,C_INT,mob_db[class].max_sp); + else if(num==6) + push_val(st->stack,C_INT,mob_db[class].base_exp); + else if(num==7) + push_val(st->stack,C_INT,mob_db[class].job_exp); + 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 && gc->visibleG0 == 1) push_val(st->stack,C_INT,gc->Ghp0); + if(guardian==1 && gc->visibleG1 == 1) push_val(st->stack,C_INT,gc->Ghp1); + if(guardian==2 && gc->visibleG2 == 1) push_val(st->stack,C_INT,gc->Ghp2); + if(guardian==3 && gc->visibleG3 == 1) push_val(st->stack,C_INT,gc->Ghp3); + if(guardian==4 && gc->visibleG4 == 1) push_val(st->stack,C_INT,gc->Ghp4); + if(guardian==5 && gc->visibleG5 == 1) push_val(st->stack,C_INT,gc->Ghp5); + if(guardian==6 && gc->visibleG6 == 1) push_val(st->stack,C_INT,gc->Ghp6); + if(guardian==7 && gc->visibleG7 == 1) push_val(st->stack,C_INT,gc->Ghp7); + else push_val(st->stack,C_INT,-1); + + return 0; +} +/*========================================== + * IDからItem名 + *------------------------------------------ + */ +int buildin_getitemname(struct script_state *st) +{ + int item_id; + struct item_data *i_data; + char *item_name; + + item_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + + i_data = NULL; + i_data = itemdb_search(item_id); + item_name=(char *)aCalloc(24,sizeof(char)); + + strncpy(item_name,i_data->jname,23); + push_str(st->stack,C_STR,item_name); + return 0; +} + +/*========================================== + * petskillbonus [Valaris] + *------------------------------------------ + */ + +int buildin_petskillbonus(struct script_state *st) +{ + int type,val,duration,timer; + 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==NULL) + return 0; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=conv_num(st,& (st->stack->stack_data[st->start+3])); + duration=conv_num(st,& (st->stack->stack_data[st->start+4])); + timer=conv_num(st,& (st->stack->stack_data[st->start+5])); + + pd->skillbonusduration=-1; + pd->skillbonustimer=-1; + + pet_skill_bonus(sd,pd,type,val,duration,timer,0); + + return 0; +} + +/*========================================== + * pet looting [Valaris] + *------------------------------------------ + */ +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; + + pd=sd->pd; + + if(pd==NULL) + return 0; + + max=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(!max) + return 0; + + pd->loot=1; + pd->lootmax=max; + + return 0; +} +/*========================================== + * PCの所持品情報読み取り + *------------------------------------------ + */ +int buildin_getinventorylist(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int i,j=0; + if(!sd) return 0; + for(i=0;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){ + pc_setreg(sd,add_str("@inventorylist_id")+(j<<24),sd->status.inventory[i].nameid); + pc_setreg(sd,add_str("@inventorylist_amount")+(j<<24),sd->status.inventory[i].amount); + pc_setreg(sd,add_str("@inventorylist_equip")+(j<<24),sd->status.inventory[i].equip); + pc_setreg(sd,add_str("@inventorylist_refine")+(j<<24),sd->status.inventory[i].refine); + pc_setreg(sd,add_str("@inventorylist_identify")+(j<<24),sd->status.inventory[i].identify); + pc_setreg(sd,add_str("@inventorylist_attribute")+(j<<24),sd->status.inventory[i].attribute); + pc_setreg(sd,add_str("@inventorylist_card1")+(j<<24),sd->status.inventory[i].card[0]); + pc_setreg(sd,add_str("@inventorylist_card2")+(j<<24),sd->status.inventory[i].card[1]); + pc_setreg(sd,add_str("@inventorylist_card3")+(j<<24),sd->status.inventory[i].card[2]); + pc_setreg(sd,add_str("@inventorylist_card4")+(j<<24),sd->status.inventory[i].card[3]); + j++; + } + } + pc_setreg(sd,add_str("@inventorylist_count"),j); + return 0; +} + +int buildin_getskilllist(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int i,j=0; + if(!sd) return 0; + for(i=0;i<MAX_SKILL;i++){ + if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0){ + pc_setreg(sd,add_str("@skilllist_id")+(j<<24),sd->status.skill[i].id); + pc_setreg(sd,add_str("@skilllist_lv")+(j<<24),sd->status.skill[i].lv); + pc_setreg(sd,add_str("@skilllist_flag")+(j<<24),sd->status.skill[i].flag); + j++; + } + } + pc_setreg(sd,add_str("@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) + pc_delitem(sd, i, sd->status.inventory[i].amount, 0); + } + return 0; +} + +/*========================================== + * NPCクラスチェンジ + * classは変わりたいclass + * typeは通常0なのかな? + *------------------------------------------ + */ +int buildin_classchange(struct script_state *st) +{ + int class,type; + struct block_list *bl=map_id2bl(st->oid); + + if(bl==NULL) return 0; + + class=conv_num(st,& (st->stack->stack_data[st->start+2])); + type=conv_num(st,& (st->stack->stack_data[st->start+3])); + clif_class_change(bl,class,type); + return 0; +} + +/*========================================== + * NPCから発生するエフェクト + *------------------------------------------ + */ +int buildin_misceffect(struct script_state *st) +{ + int type; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(st->oid) + clif_misceffect2(map_id2bl(st->oid),type); + else{ + struct map_session_data *sd=script_rid2sd(st); + if(sd) + clif_misceffect2(&sd->bl,type); + } + return 0; +} +/*========================================== + * サウンドエフェクト + *------------------------------------------ + */ +int buildin_soundeffect(struct script_state *st) +{ + 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; +} +/*========================================== + * pet status recovery [Valaris] + *------------------------------------------ + */ +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==NULL) + return 0; + + pd->skilltype=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->skilltimer=conv_num(st,& (st->stack->stack_data[st->start+3])); + + pd->skillbonustimer=add_timer(gettick()+pd->skilltimer*1000,pet_recovery_timer,sd->bl.id,0); + + return 0; +} + +/*========================================== + * pet healing [Valaris] + *------------------------------------------ + */ +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==NULL) + return 0; + + pd->skilltype=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->skillval=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->skilltimer=conv_num(st,& (st->stack->stack_data[st->start+4])); + + pd->skillbonustimer=add_timer(gettick()+pd->skilltimer*1000,pet_heal_timer,sd->bl.id,0); + + return 0; +} + +/*========================================== + * pet magnificat [Valaris] + *------------------------------------------ + */ +int buildin_petmag(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==NULL) + return 0; + + pd->skilltype=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->skillduration=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->skillval=conv_num(st,& (st->stack->stack_data[st->start+4])); + pd->skilltimer=conv_num(st,& (st->stack->stack_data[st->start+5])); + + pd->skillbonustimer=add_timer(gettick()+pd->skilltimer*1000,pet_mag_timer,sd->bl.id,0); + + return 0; +} + +/*========================================== + * pet attack skills [Valaris] + *------------------------------------------ + */ +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==NULL) + return 0; + + pd->skilltype=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->skillval=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->skillduration=conv_num(st,& (st->stack->stack_data[st->start+4])); + pd->skilltimer=conv_num(st,& (st->stack->stack_data[st->start+5])); + + pd->skillbonustimer=add_timer(gettick()+100,pet_skillattack_timer,sd->bl.id,0); + + return 0; +} +/*========================================== + * NPC skill effects [Valaris] + *------------------------------------------ + */ +int buildin_npcskilleffect(struct script_state *st) +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + + int skillid=conv_num(st,& (st->stack->stack_data[st->start+2])); + int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3])); + int x=conv_num(st,& (st->stack->stack_data[st->start+4])); + int y=conv_num(st,& (st->stack->stack_data[st->start+5])); + + clif_skill_poseffect(&nd->bl,skillid,skilllv,x,y,gettick()); + + return 0; +} + +/*========================================== + * Special effects [Valaris] + *------------------------------------------ + */ +int buildin_specialeffect(struct script_state *st) +{ + struct block_list *bl=map_id2bl(st->oid); + + if(bl==NULL) + return 0; + + clif_specialeffect(bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0); + + return 0; +} + +int buildin_specialeffect2(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL) + return 0; + + clif_specialeffect(&sd->bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0); + + return 0; +} + +/*========================================== + * Nude [Valaris] + *------------------------------------------ + */ + +int buildin_nude(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int i; + + if(sd==NULL) + return 0; + + for(i=0;i<11;i++) + if(sd->equip_index[i] >= 0) + pc_unequipitem(sd,sd->equip_index[i],1); + + return 0; +} + +/*========================================== + * gmcommand [MouseJstr] + * + * suggested on the forums... + *------------------------------------------ + */ + +int buildin_gmcommand(struct script_state *st) +{ + struct map_session_data *sd; + char *cmd; + + sd = script_rid2sd(st); + cmd = conv_str(st,& (st->stack->stack_data[st->start+2])); + + is_atcommand(sd->fd, sd, cmd, 99); + + return 0; +} + +/*========================================== + * movenpc [MouseJstr] + *------------------------------------------ + */ + +int buildin_movenpc(struct script_state *st) +{ + struct map_session_data *sd; + char *map,*npc; + int x,y; + + sd = script_rid2sd(st); + + map = conv_str(st,& (st->stack->stack_data[st->start+2])); + x = conv_num(st,& (st->stack->stack_data[st->start+3])); + y = conv_num(st,& (st->stack->stack_data[st->start+4])); + npc = conv_str(st,& (st->stack->stack_data[st->start+5])); + + return 0; +} + +/*========================================== + * 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 1; + clif_displaymessage(pl_sd->fd, msg); + + return 0; +} + +/*========================================== + * npctalk (sends message to surrounding + * area) [Valaris] + *------------------------------------------ + */ + +int buildin_npctalk(struct script_state *st) +{ + char *str; + char message[255]; + + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + + if(nd) { + memcpy(message,nd->name,24); + strcat(message," : "); + strcat(message,str); + clif_message(&(nd->bl), message); + } + + return 0; +} + +/*========================================== + * hasitems (checks to see if player has any + * items on them, if so will return a 1) + * [Valaris] + *------------------------------------------ + */ + +int buildin_hasitems(struct script_state *st) +{ + int i; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + for(i=0; i<MAX_INVENTORY; i++) { + if(sd->status.inventory[i].amount) { + push_val(st->stack,C_INT,1); + return 0; + } + } + + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * getlook char info. getlook(arg) + *------------------------------------------ + */ +int buildin_getlook(struct script_state *st){ + int type,val; + struct map_session_data *sd; + sd=script_rid2sd(st); + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=-1; + switch(type){ + case LOOK_HAIR: //1 + val=sd->status.hair; + break; + case LOOK_WEAPON: //2 + val=sd->status.weapon; + break; + case LOOK_HEAD_BOTTOM: //3 + val=sd->status.head_bottom; + break; + case LOOK_HEAD_TOP: //4 + val=sd->status.head_top; + break; + case LOOK_HEAD_MID: //5 + val=sd->status.head_mid; + break; + case LOOK_HAIR_COLOR: //6 + val=sd->status.hair_color; + break; + case LOOK_CLOTHES_COLOR: //7 + val=sd->status.clothes_color; + break; + case LOOK_SHIELD: //8 + val=sd->status.shield; + break; + case LOOK_SHOES: //9 + break; + } + + push_val(st->stack,C_INT,val); + return 0; +} + +/*========================================== + * get char save point. argument: 0- map name, 1- x, 2- y + *------------------------------------------ +*/ +int buildin_getsavepoint(struct script_state *st) +{ + int x,y,type; + char *mapname; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + mapname=calloc(24, 1); + + x=sd->status.save_point.x; + y=sd->status.save_point.y; + strncpy(mapname,sd->status.save_point.map,24); + switch(type){ + case 0: + push_str(st->stack,C_STR,mapname); + break; + case 1: + push_val(st->stack,C_INT,x); + break; + case 2: + push_val(st->stack,C_INT,y); + break; + } + return 0; +} + + +// +// 実行部main +// +/*========================================== + * コマンドの読み取り + *------------------------------------------ + */ +static int unget_com_data=-1; +int get_com(unsigned char *script,int *pos) +{ + int i,j; + if(unget_com_data>=0){ + i=unget_com_data; + unget_com_data=-1; + return i; + } + if(script[*pos]>=0x80){ + return C_INT; + } + i=0; j=0; + while(script[*pos]>=0x40){ + i=script[(*pos)++]<<j; + j+=6; + } + return i+(script[(*pos)++]<<j); +} + +/*========================================== + * コマンドのプッシュバック + *------------------------------------------ + */ +void unget_com(int c) +{ + if(unget_com_data!=-1){ + if(battle_config.error_log) + printf("unget_com can back only 1 data\n"); + } + unget_com_data=c; +} + +/*========================================== + * 数値の所得 + *------------------------------------------ + */ +int get_num(unsigned char *script,int *pos) +{ + int i,j; + i=0; j=0; + while(script[*pos]>=0xc0){ + i+=(script[(*pos)++]&0x7f)<<j; + j+=6; + } + return i+((script[(*pos)++]&0x7f)<<j); +} + +/*========================================== + * スタックから値を取り出す + *------------------------------------------ + */ +int pop_val(struct script_state* st) +{ + if(st->stack->sp<=0) + return 0; + st->stack->sp--; + get_val(st,&(st->stack->stack_data[st->stack->sp])); + if(st->stack->stack_data[st->stack->sp].type==C_INT) + return st->stack->stack_data[st->stack->sp].u.num; + return 0; +} + +#define isstr(c) ((c).type==C_STR || (c).type==C_CONSTSTR) + +/*========================================== + * 加算演算子 + *------------------------------------------ + */ +void op_add(struct script_state* st) +{ + st->stack->sp--; + get_val(st,&(st->stack->stack_data[st->stack->sp])); + get_val(st,&(st->stack->stack_data[st->stack->sp-1])); + + if(isstr(st->stack->stack_data[st->stack->sp]) || isstr(st->stack->stack_data[st->stack->sp-1])){ + conv_str(st,&(st->stack->stack_data[st->stack->sp])); + conv_str(st,&(st->stack->stack_data[st->stack->sp-1])); + } + if(st->stack->stack_data[st->stack->sp].type==C_INT){ // ii + st->stack->stack_data[st->stack->sp-1].u.num += st->stack->stack_data[st->stack->sp].u.num; + } else { // ssの予定 + char *buf; + buf=(char *)aCalloc(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) + free(st->stack->stack_data[st->stack->sp-1].u.str); + if(st->stack->stack_data[st->stack->sp].type==C_STR) + free(st->stack->stack_data[st->stack->sp].u.str); + st->stack->stack_data[st->stack->sp-1].type=C_STR; + st->stack->stack_data[st->stack->sp-1].u.str=buf; + } +} + +/*========================================== + * 二項演算子(文字列) + *------------------------------------------ + */ +void op_2str(struct script_state *st,int op,int sp1,int sp2) +{ + char *s1=st->stack->stack_data[sp1].u.str, + *s2=st->stack->stack_data[sp2].u.str; + int a=0; + + switch(op){ + case C_EQ: + a= (strcmp(s1,s2)==0); + break; + case C_NE: + a= (strcmp(s1,s2)!=0); + break; + case C_GT: + a= (strcmp(s1,s2)> 0); + break; + case C_GE: + a= (strcmp(s1,s2)>=0); + break; + case C_LT: + a= (strcmp(s1,s2)< 0); + break; + case C_LE: + a= (strcmp(s1,s2)<=0); + break; + default: + printf("illegal string operater\n"); + break; + } + + push_val(st->stack,C_INT,a); + + if(st->stack->stack_data[sp1].type==C_STR) free(s1); + if(st->stack->stack_data[sp2].type==C_STR) free(s2); +} +/*========================================== + * 二項演算子(数値) + *------------------------------------------ + */ +void op_2num(struct script_state *st,int op,int i1,int i2) +{ + switch(op){ + case C_SUB: + i1-=i2; + break; + case C_MUL: + i1*=i2; + break; + case C_DIV: + i1/=i2; + break; + case C_MOD: + i1%=i2; + break; + case C_AND: + i1&=i2; + break; + case C_OR: + i1|=i2; + break; + case C_XOR: + i1^=i2; + break; + case C_LAND: + i1=i1&&i2; + break; + case C_LOR: + i1=i1||i2; + break; + case C_EQ: + i1=i1==i2; + break; + case C_NE: + i1=i1!=i2; + break; + case C_GT: + i1=i1>i2; + break; + case C_GE: + i1=i1>=i2; + break; + case C_LT: + i1=i1<i2; + break; + case C_LE: + i1=i1<=i2; + break; + case C_R_SHIFT: + i1=i1>>i2; + break; + case C_L_SHIFT: + i1=i1<<i2; + break; + } + push_val(st->stack,C_INT,i1); +} +/*========================================== + * 二項演算子 + *------------------------------------------ + */ +void op_2(struct script_state *st,int op) +{ + int i1,i2; + char *s1=NULL,*s2=NULL; + + i2=pop_val(st); + if( isstr(st->stack->stack_data[st->stack->sp]) ) + s2=st->stack->stack_data[st->stack->sp].u.str; + + i1=pop_val(st); + if( isstr(st->stack->stack_data[st->stack->sp]) ) + s1=st->stack->stack_data[st->stack->sp].u.str; + + if( s1!=NULL && s2!=NULL ){ + // ss => op_2str + op_2str(st,op,st->stack->sp,st->stack->sp+1); + }else if( s1==NULL && s2==NULL ){ + // ii => op_2num + op_2num(st,op,i1,i2); + }else{ + // si,is => error + printf("script: op_2: int&str, str&int not allow."); + push_val(st->stack,C_INT,0); + } +} + +/*========================================== + * 単項演算子 + *------------------------------------------ + */ +void op_1num(struct script_state *st,int op) +{ + int i1; + i1=pop_val(st); + switch(op){ + case C_NEG: + i1=-i1; + break; + case C_NOT: + i1=~i1; + break; + case C_LNOT: + i1=!i1; + break; + } + push_val(st->stack,C_INT,i1); +} + + +/*========================================== + * 関数の実行 + *------------------------------------------ + */ +int run_func(struct script_state *st) +{ + int i,start_sp,end_sp,func; + + end_sp=st->stack->sp; + for(i=end_sp-1;i>=0 && st->stack->stack_data[i].type!=C_ARG;i--); + if(i==0){ + if(battle_config.error_log) + printf("function not found\n"); +// st->stack->sp=0; + st->state=END; + return 0; + } + start_sp=i-1; + st->start=i-1; + st->end=end_sp; + + func=st->stack->stack_data[st->start].u.num; + if( st->stack->stack_data[st->start].type!=C_NAME || str_data[func].type!=C_FUNC ){ + printf("run_func: not function and command! \n"); +// st->stack->sp=0; + st->state=END; + return 0; + } +#ifdef DEBUG_RUN + if(battle_config.etc_log) { + printf("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type); + printf("stack dump :"); + for(i=0;i<end_sp;i++){ + switch(st->stack->stack_data[i].type){ + case C_INT: + printf(" int(%d)",st->stack->stack_data[i].u.num); + break; + case C_NAME: + printf(" name(%s)",str_buf+str_data[st->stack->stack_data[i].u.num].str); + break; + case C_ARG: + printf(" arg"); + break; + case C_POS: + printf(" pos(%d)",st->stack->stack_data[i].u.num); + break; + default: + printf(" %d,%d",st->stack->stack_data[i].type,st->stack->stack_data[i].u.num); + } + } + printf("\n"); + } +#endif + if(str_data[func].func){ + str_data[func].func(st); + } else { + if(battle_config.error_log) + printf("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type); + push_val(st->stack,C_INT,0); + } + + pop_stack(st->stack,start_sp,end_sp); + + if(st->state==RETFUNC){ + // ユーザー定義関数からの復帰 + int olddefsp=st->defsp; + int i; + + pop_stack(st->stack,st->defsp,start_sp); // 復帰に邪魔なスタック削除 + if(st->defsp<4 || st->stack->stack_data[st->defsp-1].type!=C_RETINFO){ + printf("script:run_func(return) return without callfunc or callsub!\n"); + st->state=END; + return 0; + } + i = conv_num(st,& (st->stack->stack_data[st->defsp-4])); // 引数の数所得 + st->pos=conv_num(st,& (st->stack->stack_data[st->defsp-1])); // スクリプト位置の復元 + st->script=(char*)conv_num(st,& (st->stack->stack_data[st->defsp-2])); // スクリプトを復元 + st->defsp=conv_num(st,& (st->stack->stack_data[st->defsp-3])); // 基準スタックポインタを復元 + + pop_stack(st->stack,olddefsp-4-i,olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 + + st->state=GOTO; + } + + return 0; +} + +/*========================================== + * スクリプトの実行メイン部分 + *------------------------------------------ + */ +int run_script_main(unsigned char *script,int pos,int rid,int oid,struct script_state *st,unsigned char *rootscript) +{ + int c,rerun_pos; + int cmdcount=script_config.check_cmdcount; + int gotocount=script_config.check_gotocount; + struct script_stack *stack=st->stack; + + st->defsp=stack->sp; + st->script=script; + + rerun_pos=st->pos; + for(st->state=0;st->state==0;){ + switch(c=get_com(script,&st->pos)){ + case C_EOL: + if(stack->sp!=st->defsp){ + if(battle_config.error_log) + printf("stack.sp(%d) != default(%d)\n",stack->sp,st->defsp); + stack->sp=st->defsp; + } + rerun_pos=st->pos; + break; + case C_INT: + push_val(stack,C_INT,get_num(script,&st->pos)); + break; + case C_POS: + case C_NAME: + push_val(stack,c,(*(int*)(script+st->pos))&0xffffff); + st->pos+=3; + break; + case C_ARG: + push_val(stack,c,0); + break; + case C_STR: + push_str(stack,C_CONSTSTR,script+st->pos); + while(script[st->pos++]); + break; + case C_FUNC: + run_func(st); + if(st->state==GOTO){ + rerun_pos=st->pos; + script=st->script; + st->state=0; + if( gotocount>0 && (--gotocount)<=0 ){ + printf("run_script: infinity loop !\n"); + st->state=END; + } + } + break; + + case C_ADD: + op_add(st); + break; + + case C_SUB: + case C_MUL: + case C_DIV: + case C_MOD: + case C_EQ: + case C_NE: + case C_GT: + case C_GE: + case C_LT: + case C_LE: + case C_AND: + case C_OR: + case C_XOR: + case C_LAND: + case C_LOR: + case C_R_SHIFT: + case C_L_SHIFT: + op_2(st,c); + break; + + case C_NEG: + case C_NOT: + case C_LNOT: + op_1num(st,c); + break; + + case C_NOP: + st->state=END; + break; + + default: + if(battle_config.error_log) + printf("unknown command : %d @ %d\n",c,pos); + st->state=END; + break; + } + if( cmdcount>0 && (--cmdcount)<=0 ){ + printf("run_script: infinity loop !\n"); + st->state=END; + } + } + switch(st->state){ + case STOP: + break; + case END: + { + struct map_session_data *sd=map_id2sd(st->rid); + st->pos=-1; + if(sd && sd->npc_id==st->oid) + npc_event_dequeue(sd); + } + break; + case RERUNLINE: + { + st->pos=rerun_pos; + } + break; + } + + if( st->state!=END){ + // 再開するためにスタック情報を保存 + struct map_session_data *sd=map_id2sd(st->rid); + if(sd/* && sd->npc_stackbuf==NULL*/){ + if( sd->npc_stackbuf ) + free( sd->npc_stackbuf ); + sd->npc_stackbuf = (char *)aCalloc(sizeof(stack->stack_data[0])*stack->sp_max,sizeof(char)); + memcpy(sd->npc_stackbuf, stack->stack_data, sizeof(stack->stack_data[0]) * stack->sp_max); + sd->npc_stack = stack->sp; + sd->npc_stackmax = stack->sp_max; + sd->npc_script=script; + sd->npc_scriptroot=rootscript; + } + } + + return 0; +} + +/*========================================== + * スクリプトの実行 + *------------------------------------------ + */ +int run_script(unsigned char *script,int pos,int rid,int oid) +{ + struct script_stack stack; + struct script_state st; + struct map_session_data *sd=map_id2sd(rid); + unsigned char *rootscript=script; + + if(script==NULL || pos<0) + return -1; + + if(sd && sd->npc_stackbuf && sd->npc_scriptroot==(char*)rootscript){ + // 前回のスタックを復帰 + script=sd->npc_script; + stack.sp=sd->npc_stack; + stack.sp_max=sd->npc_stackmax; + stack.stack_data=(struct script_data *)aCalloc(stack.sp_max,sizeof(stack.stack_data[0])); + memcpy(stack.stack_data,sd->npc_stackbuf,sizeof(stack.stack_data[0])*stack.sp_max); + free(sd->npc_stackbuf); + sd->npc_stackbuf=NULL; + }else{ + // スタック初期化 + stack.sp=0; + stack.sp_max=64; + stack.stack_data=(struct script_data *)aCalloc(stack.sp_max,sizeof(stack.stack_data[0])); + } + st.stack=&stack; + st.pos=pos; + st.rid=rid; + st.oid=oid; + run_script_main(script,pos,rid,oid,&st,rootscript); + + free(stack.stack_data); + stack.stack_data=NULL; + return st.pos; +} + + +/*========================================== + * マップ変数の変更 + *------------------------------------------ + */ +int mapreg_setreg(int num,int val) +{ + if(val!=0) + numdb_insert(mapreg_db,num,val); + else + numdb_erase(mapreg_db,num); + + mapreg_dirty=1; + return 0; +} +/*========================================== + * 文字列型マップ変数の変更 + *------------------------------------------ + */ +int mapreg_setregstr(int num,const char *str) +{ + char *p; + + if( (p=numdb_search(mapregstr_db,num))!=NULL ) + free(p); + + if( str==NULL || *str==0 ){ + numdb_erase(mapregstr_db,num); + mapreg_dirty=1; + return 0; + } + p=(char *)aCalloc(strlen(str)+1, sizeof(char)); + strcpy(p,str); + numdb_insert(mapregstr_db,num,p); + mapreg_dirty=1; + return 0; +} + +/*========================================== + * 永続的マップ変数の読み込み + *------------------------------------------ + */ +static int script_load_mapreg() +{ + FILE *fp; + char line[1024]; + + if( (fp=fopen(mapreg_txt,"rt"))==NULL ) + return -1; + + while(fgets(line,sizeof(line),fp)){ + char buf1[256],buf2[1024],*p; + int n,v,s,i; + if( sscanf(line,"%255[^,],%d\t%n",buf1,&i,&n)!=2 && + (i=0,sscanf(line,"%[^\t]\t%n",buf1,&n)!=1) ) + continue; + if( buf1[strlen(buf1)-1]=='$' ){ + if( sscanf(line+n,"%[^\n\r]",buf2)!=1 ){ + printf("%s: %s broken data !\n",mapreg_txt,buf1); + continue; + } + p=(char *)aCalloc(strlen(buf2) + 1,sizeof(char)); + strcpy(p,buf2); + s=add_str(buf1); + numdb_insert(mapregstr_db,(i<<24)|s,p); + }else{ + if( sscanf(line+n,"%d",&v)!=1 ){ + printf("%s: %s broken data !\n",mapreg_txt,buf1); + continue; + } + s=add_str(buf1); + numdb_insert(mapreg_db,(i<<24)|s,v); + } + } + fclose(fp); + mapreg_dirty=0; + return 0; +} +/*========================================== + * 永続的マップ変数の書き込み + *------------------------------------------ + */ +static int script_save_mapreg_intsub(void *key,void *data,va_list ap) +{ + FILE *fp=va_arg(ap,FILE*); + int num=((int)key)&0x00ffffff, i=((int)key)>>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; +} +static int script_save_mapreg_strsub(void *key,void *data,va_list ap) +{ + FILE *fp=va_arg(ap,FILE*); + int num=((int)key)&0x00ffffff, i=((int)key)>>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; +} +static int script_save_mapreg() +{ + FILE *fp; + int lock; + + if( (fp=lock_fopen(mapreg_txt,&lock))==NULL ) + return -1; + numdb_foreach(mapreg_db,script_save_mapreg_intsub,fp); + numdb_foreach(mapregstr_db,script_save_mapreg_strsub,fp); + lock_fclose(fp,mapreg_txt,&lock); + mapreg_dirty=0; + return 0; +} +static int script_autosave_mapreg(int tid,unsigned int tick,int id,int data) +{ + if(mapreg_dirty) + script_save_mapreg(); + 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(char *cfgName) +{ + int i; + char line[1024],w1[1024],w2[1024]; + FILE *fp; + + script_config.warn_func_no_comma=1; + script_config.warn_cmd_no_comma=1; + script_config.warn_func_mismatch_paramnum=1; + script_config.warn_cmd_mismatch_paramnum=1; + script_config.check_cmdcount=8192; + script_config.check_gotocount=512; + + fp=fopen(cfgName,"r"); + if(fp==NULL){ + printf("file not found: %s\n",cfgName); + return 1; + } + while(fgets(line,1020,fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); + if(i!=2) + continue; + if(strcmpi(w1,"refine_posword")==0) { + set_posword(w2); + } + if(strcmpi(w1,"import")==0){ + script_config_read(w2); + } + } + fclose(fp); + + return 0; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +static int mapreg_db_final(void *key,void *data,va_list ap) +{ + return 0; +} +static int mapregstr_db_final(void *key,void *data,va_list ap) +{ + free(data); + return 0; +} +static int scriptlabel_db_final(void *key,void *data,va_list ap) +{ + return 0; +} +static int userfunc_db_final(void *key,void *data,va_list ap) +{ + free(key); + free(data); + return 0; +} +int do_final_script() +{ + if(mapreg_dirty>=0) + script_save_mapreg(); + if(script_buf) + free(script_buf); + + if(mapreg_db) + numdb_final(mapreg_db,mapreg_db_final); + if(mapregstr_db) + strdb_final(mapregstr_db,mapregstr_db_final); + if(scriptlabel_db) + strdb_final(scriptlabel_db,scriptlabel_db_final); + if(userfunc_db) + strdb_final(userfunc_db,userfunc_db_final); + + if (str_data) + free(str_data); + if (str_buf) + free(str_buf); + + return 0; +} +/*========================================== + * 初期化 + *------------------------------------------ + */ +int do_init_script() +{ + mapreg_db=numdb_init(); + mapregstr_db=numdb_init(); + 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); + + scriptlabel_db=strdb_init(50); + return 0; +} diff --git a/src/map/script.h b/src/map/script.h new file mode 100644 index 0000000..b50c466 --- /dev/null +++ b/src/map/script.h @@ -0,0 +1,39 @@ +// $Id: script.h,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ +#ifndef _SCRIPT_H_ +#define _SCRIPT_H_ + +struct script_data { + int type; + union { + int num; + char *str; + } u; +}; + +struct script_stack { + int sp,sp_max; + struct script_data *stack_data; +}; +struct script_state { + struct script_stack *stack; + int start,end; + int pos,state; + int rid,oid; + char *script,*new_script; + int defsp,new_pos,new_defsp; +}; + +unsigned char * parse_script(unsigned char *,int); +int run_script(unsigned char *,int,int,int); + +struct dbt* script_get_label_db(); +struct dbt* script_get_userfunc_db(); + +int script_config_read(char *cfgName); +int do_init_script(); +int do_final_script(); + +extern char mapreg_txt[]; + +#endif + diff --git a/src/map/skill.c b/src/map/skill.c new file mode 100644 index 0000000..1e92b3f --- /dev/null +++ b/src/map/skill.c @@ -0,0 +1,10637 @@ +// $Id: skill.c,v 1.8 2004/09/25 05:32:19 MouseJstr Exp $ +/* スキル関係 */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "timer.h" +#include "nullpo.h" +#include "malloc.h" + +#include "skill.h" +#include "map.h" +#include "clif.h" +#include "pc.h" +#include "pet.h" +#include "mob.h" +#include "battle.h" +#include "party.h" +#include "itemdb.h" +#include "script.h" +#include "intif.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define SKILLUNITTIMER_INVERVAL 100 + +#define STATE_BLIND 0x10 + +/* スキル番号=>ステータス異常番号変換テーブル */ +int SkillStatusChangeTable[]={ /* skill.hのenumのSC_***とあわせること */ +/* 0- */ + -1,-1,-1,-1,-1,-1, + SC_PROVOKE, /* プロボック */ + -1, 1,-1, +/* 10- */ + SC_SIGHT, /* サイト */ + -1,-1,-1,-1, + SC_FREEZE, /* フロストダイバー */ + SC_STONE, /* ストーンカース */ + -1,-1,-1, +/* 20- */ + -1,-1,-1,-1, + SC_RUWACH, /* ルアフ */ + -1,-1,-1,-1, + SC_INCREASEAGI, /* 速度増加 */ +/* 30- */ + SC_DECREASEAGI, /* 速度減少 */ + -1, + SC_SIGNUMCRUCIS, /* シグナムクルシス */ + SC_ANGELUS, /* エンジェラス */ + SC_BLESSING, /* ブレッシング */ + -1,-1,-1,-1,-1, +/* 40- */ + -1,-1,-1,-1,-1, + SC_CONCENTRATE, /* 集中力向上 */ + -1,-1,-1,-1, +/* 50- */ + -1, + SC_HIDING, /* ハイディング */ + -1,-1,-1,-1,-1,-1,-1,-1, +/* 60- */ + SC_TWOHANDQUICKEN, /* 2HQ */ + SC_AUTOCOUNTER, + -1,-1,-1,-1, + SC_IMPOSITIO, /* インポシティオマヌス */ + SC_SUFFRAGIUM, /* サフラギウム */ + SC_ASPERSIO, /* アスペルシオ */ + SC_BENEDICTIO, /* 聖体降福 */ +/* 70- */ + -1, + SC_SLOWPOISON, + -1, + SC_KYRIE, /* キリエエレイソン */ + SC_MAGNIFICAT, /* マグニフィカート */ + SC_GLORIA, /* グロリア */ + SC_DIVINA, /* レックスディビーナ */ + -1, + SC_AETERNA, /* レックスエーテルナ */ + -1, +/* 80- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +/* 90- */ + -1,-1, + SC_QUAGMIRE, /* クァグマイア */ + -1,-1,-1,-1,-1,-1,-1, +/* 100- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +/* 110- */ + -1, + SC_ADRENALINE, /* アドレナリンラッシュ */ + SC_WEAPONPERFECTION,/* ウェポンパーフェクション */ + SC_OVERTHRUST, /* オーバートラスト */ + SC_MAXIMIZEPOWER, /* マキシマイズパワー */ + -1,-1,-1,-1,-1, +/* 120- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +/* 130- */ + -1,-1,-1,-1,-1, + SC_CLOAKING, /* クローキング */ + SC_STAN, /* ソニックブロー */ + -1, + SC_ENCPOISON, /* エンチャントポイズン */ + SC_POISONREACT, /* ポイズンリアクト */ +/* 140- */ + SC_POISON, /* ベノムダスト */ + SC_SPLASHER, /* ベナムスプラッシャー */ + -1, + SC_TRICKDEAD, /* 死んだふり */ + -1,-1,-1,-1,-1,-1, +/* 150- */ + -1,-1,-1,-1,-1, + SC_LOUD, /* ラウドボイス */ + -1, + SC_ENERGYCOAT, /* エナジーコート */ + -1,-1, +/* 160- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1, + SC_SELFDESTRUCTION, + -1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1, + SC_KEEPING, + -1,-1, + SC_BARRIER, + -1,-1, + SC_HALLUCINATION, + -1,-1, +/* 210- */ + -1,-1,-1,-1,-1, + SC_STRIPWEAPON, + SC_STRIPSHIELD, + SC_STRIPARMOR, + SC_STRIPHELM, + -1, +/* 220- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +/* 230- */ + -1,-1,-1,-1, + SC_CP_WEAPON, + SC_CP_SHIELD, + SC_CP_ARMOR, + SC_CP_HELM, + -1,-1, +/* 240- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1, + SC_AUTOGUARD, +/* 250- */ + -1,-1, + SC_REFLECTSHIELD, + -1,-1, + SC_DEVOTION, + SC_PROVIDENCE, + SC_DEFENDER, + SC_SPEARSQUICKEN, + -1, +/* 260- */ + -1,-1,-1,-1,-1,-1,-1,-1, + SC_STEELBODY, + SC_BLADESTOP_WAIT, +/* 270- */ + SC_EXPLOSIONSPIRITS, + SC_EXTREMITYFIST, + -1,-1,-1,-1, + SC_MAGICROD, + -1,-1,-1, +/* 280- */ + SC_FLAMELAUNCHER, + SC_FROSTWEAPON, + SC_LIGHTNINGLOADER, + SC_SEISMICWEAPON, + -1, + SC_VOLCANO, + SC_DELUGE, + SC_VIOLENTGALE, + SC_LANDPROTECTOR, + -1, +/* 290- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +/* 300- */ + -1,-1,-1,-1,-1,-1, + SC_LULLABY, + SC_RICHMANKIM, + SC_ETERNALCHAOS, + SC_DRUMBATTLE, +/* 310- */ + SC_NIBELUNGEN, + SC_ROKISWEIL, + SC_INTOABYSS, + SC_SIEGFRIED, + -1,-1,-1, + SC_DISSONANCE, + -1, + SC_WHISTLE, +/* 320- */ + SC_ASSNCROS, + SC_POEMBRAGI, + SC_APPLEIDUN, + -1,-1, + SC_UGLYDANCE, + -1, + SC_HUMMING, + SC_DONTFORGETME, + SC_FORTUNE, +/* 330- */ + SC_SERVICE4U, + SC_SELFDESTRUCTION, + -1,-1,-1,-1,-1,-1,-1,-1, +/* 340- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +/* 350- */ + -1,-1,-1,-1,-1, + SC_AURABLADE, + SC_PARRYING, + SC_CONCENTRATION, + SC_TENSIONRELAX, + SC_BERSERK, +/* 360- */ + SC_BERSERK, + SC_ASSUMPTIO, + SC_BASILICA, + -1,-1,-1, + SC_MAGICPOWER, + -1,-1, + SC_GOSPEL, +/* 370- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +/* 380- */ + SC_TRUESIGHT, + -1,-1, + SC_WINDWALK, + SC_MELTDOWN, + -1,-1, + SC_CARTBOOST, + -1, + SC_CHASEWALK, +/* 390- */ + SC_REJECTSWORD, + -1,-1,-1,-1,-1, + SC_MARIONETTE, + -1, + SC_HEADCRUSH, + SC_JOINTBEAT, +/* 400 */ + -1,-1, + SC_MINDBREAKER, + SC_MEMORIZE, + SC_FOGWALL, + SC_SPIDERWEB, + -1,-1,-1,-1, +/* 410- */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +}; + +struct skill_name_db skill_names[] = { + { AC_CHARGEARROW, "CHARGEARROW", "Charge_Arrow" } , + { AC_CONCENTRATION, "CONCENTRATION", "Improve_Concentration" } , + { AC_DOUBLE, "DOUBLE", "Double_Strafe" } , + { AC_MAKINGARROW, "MAKINGARROW", "Arrow_Creation" } , + { AC_OWL, "OWL", "Owl's_Eye" } , + { AC_SHOWER, "SHOWER", "Arrow_Shower" } , + { AC_VULTURE, "VULTURE", "Vulture's_Eye" } , + { ALL_RESURRECTION, "RESURRECTION", "Resurrection" } , + { AL_ANGELUS, "ANGELUS", "Angelus" } , + { AL_BLESSING, "BLESSING", "Blessing" } , + { AL_CRUCIS, "CRUCIS", "Signum_Crusis" } , + { AL_CURE, "CURE", "Cure" } , + { AL_DECAGI, "DECAGI", "Decrease_AGI" } , + { AL_DEMONBANE, "DEMONBANE", "Demon_Bane" } , + { AL_DP, "DP", "Divine_Protection" } , + { AL_HEAL, "HEAL", "Heal" } , + { AL_HOLYLIGHT, "HOLYLIGHT", "Holy_Light" } , + { AL_HOLYWATER, "HOLYWATER", "Aqua_Benedicta" } , + { AL_INCAGI, "INCAGI", "Increase_AGI" } , + { AL_PNEUMA, "PNEUMA", "Pneuma" } , + { AL_RUWACH, "RUWACH", "Ruwach" } , + { AL_TELEPORT, "TELEPORT", "Teleport" } , + { AL_WARP, "WARP", "Warp_Portal" } , + { AM_ACIDTERROR, "ACIDTERROR", "Acid_Terror" } , + { AM_AXEMASTERY, "AXEMASTERY", "Axe_Mastery" } , + { AM_BERSERKPITCHER, "BERSERKPITCHER", "Berserk Pitcher" } , + { AM_BIOETHICS, "BIOETHICS", "Bioethics" } , + { AM_BIOTECHNOLOGY, "BIOTECHNOLOGY", "Biotechnology" } , + { AM_CALLHOMUN, "CALLHOMUN", "Call_Homunculus" } , + { AM_CANNIBALIZE, "CANNIBALIZE", "Bio_Cannibalize" } , + { AM_CP_ARMOR, "ARMOR", "Chemical_Protection_Armor" } , + { AM_CP_HELM, "HELM", "Chemical_Protection_Helm" } , + { AM_CP_SHIELD, "SHIELD", "Chemical_Protection_Shield" } , + { AM_CP_WEAPON, "WEAPON", "Chemical_Protection_Weapon" } , + { AM_CREATECREATURE, "CREATECREATURE", "Life_Creation" } , + { AM_CULTIVATION, "CULTIVATION", "Cultivation" } , + { AM_DEMONSTRATION, "DEMONSTRATION", "Demonstration" } , + { AM_DRILLMASTER, "DRILLMASTER", "Drillmaster" } , + { AM_FLAMECONTROL, "FLAMECONTROL", "Flame_Control" } , + { AM_HEALHOMUN, "HEALHOMUN", "Heal_Homunculus" } , + { AM_LEARNINGPOTION, "LEARNINGPOTION", "AM_LEARNINGPOTION" } , + { AM_PHARMACY, "PHARMACY", "Pharmacy" } , + { AM_POTIONPITCHER, "POTIONPITCHER", "Potion_Pitcher" } , + { AM_REST, "REST", "Sabbath" } , + { AM_RESURRECTHOMUN, "RESURRECTHOMUN", "Ressurect_Homunculus" } , + { AM_SPHEREMINE, "SPHEREMINE", "Sphere_Mine" } , + { ASC_BREAKER, "BREAKER", "Breaker" } , + { ASC_CDP, "CDP", "Create_Deadly_Poison" } , + { ASC_EDP, "EDP", "Deadly_Poison_Enchantment" } , + { ASC_HALLUCINATION, "HALLUCINATION", "Hallucination_Walk" } , + { ASC_KATAR, "KATAR", "Advanced_Katar_Mastery" } , + { ASC_METEORASSAULT, "METEORASSAULT", "Meteor_Assault" } , + { AS_CLOAKING, "CLOAKING", "Cloaking" } , + { AS_ENCHANTPOISON, "ENCHANTPOISON", "Enchant_Poison" } , + { AS_GRIMTOOTH, "GRIMTOOTH", "Grimtooth" } , + { AS_KATAR, "KATAR", "Katar_Mastery" } , + { AS_LEFT, "LEFT", "Lefthand_Mastery" } , + { AS_POISONREACT, "POISONREACT", "Poison_React" } , + { AS_RIGHT, "RIGHT", "Righthand_Mastery" } , + { AS_SONICBLOW, "SONICBLOW", "Sonic_Blow" } , + { AS_SPLASHER, "SPLASHER", "Venom_Splasher" } , + { AS_VENOMDUST, "VENOMDUST", "Venom_Dust" } , + { BA_APPLEIDUN, "APPLEIDUN", "Apple_of_Idun" } , + { BA_ASSASSINCROSS, "ASSASSINCROSS", "Assassin_Cross" } , + { BA_DISSONANCE, "DISSONANCE", "Dissonance" } , + { BA_FROSTJOKE, "FROSTJOKE", "Dumb_Joke" } , + { BA_MUSICALLESSON, "MUSICALLESSON", "Musical_Lesson" } , + { BA_MUSICALSTRIKE, "MUSICALSTRIKE", "Musical_Strike" } , + { BA_POEMBRAGI, "POEMBRAGI", "Poem_of_Bragi" } , + { BA_WHISTLE, "WHISTLE", "Whistle" } , + { BD_ADAPTATION, "ADAPTATION", "Adaption" } , + { BD_DRUMBATTLEFIELD, "DRUMBATTLEFIELD", "Drumb_BattleField" } , + { BD_ENCORE, "ENCORE", "Encore" } , + { BD_ETERNALCHAOS, "ETERNALCHAOS", "Eternal_Chaos" } , + { BD_INTOABYSS, "INTOABYSS", "Into_the_Abyss" } , + { BD_LULLABY, "LULLABY", "Lullaby" } , + { BD_RAGNAROK, "RAGNAROK", "Ragnarok" } , + { BD_RICHMANKIM, "RICHMANKIM", "Rich_Mankim" } , + { BD_RINGNIBELUNGEN, "RINGNIBELUNGEN", "Ring_of_Nibelugen" } , + { BD_ROKISWEIL, "ROKISWEIL", "Loki's_Wail" } , + { BD_SIEGFRIED, "SIEGFRIED", "Invulnerable_Siegfried" } , + { BS_ADRENALINE, "ADRENALINE", "Adrenaline_Rush" } , + { BS_ADRENALINE2, "ADRENALINE2", "Adrenaline Rush 2" } , + { BS_AXE, "AXE", "Smith_Axe" } , + { BS_DAGGER, "DAGGER", "Smith_Dagger" } , + { BS_ENCHANTEDSTONE, "ENCHANTEDSTONE", "Enchantedstone_Craft" } , + { BS_FINDINGORE, "FINDINGORE", "Ore_Discovery" } , + { BS_HAMMERFALL, "HAMMERFALL", "Hammer_Fall" } , + { BS_HILTBINDING, "HILTBINDING", "Hilt_Binding" } , + { BS_IRON, "IRON", "Iron_Tempering" } , + { BS_KNUCKLE, "KNUCKLE", "Smith_Knucklebrace" } , + { BS_MACE, "MACE", "Smith_Mace" } , + { BS_MAXIMIZE, "MAXIMIZE", "Power_Maximize" } , + { BS_ORIDEOCON, "ORIDEOCON", "Orideocon_Research" } , + { BS_OVERTHRUST, "OVERTHRUST", "Power-Thrust" } , + { BS_REPAIRWEAPON, "REPAIRWEAPON", "Weapon_Repair" } , + { BS_SKINTEMPER, "SKINTEMPER", "Skin_Tempering" } , + { BS_SPEAR, "SPEAR", "Smith_Spear" } , + { BS_STEEL, "STEEL", "Steel_Tempering" } , + { BS_SWORD, "SWORD", "Smith_Sword" } , + { BS_TWOHANDSWORD, "TWOHANDSWORD", "Smith_Two-handed_Sword" } , + { BS_WEAPONPERFECT, "WEAPONPERFECT", "Weapon_Perfection" } , + { BS_WEAPONRESEARCH, "WEAPONRESEARCH", "Weaponry_Research" } , + { CG_ARROWVULCAN, "ARROWVULCAN", "Vulcan_Arrow" } , + { CG_MARIONETTE, "MARIONETTE", "Marionette_Control" } , + { CG_MOONLIT, "MOONLIT", "Moonlight_Petals" } , + { CH_CHAINCRUSH, "CHAINCRUSH", "Chain_Crush_Combo" } , + { CH_PALMSTRIKE, "PALMSTRIKE", "Palm_Push_Strike" } , + { CH_SOULCOLLECT, "SOULCOLLECT", "Collect_Soul" } , + { CH_TIGERFIST, "TIGERFIST", "Tiger_Knuckle_Fist" } , + { CR_ALCHEMY, "ALCHEMY", "Alchemy" } , + { CR_AUTOGUARD, "AUTOGUARD", "Guard" } , + { CR_DEFENDER, "DEFENDER", "Defender" } , + { CR_DEVOTION, "DEVOTION", "Sacrifice" } , + { CR_GRANDCROSS, "GRANDCROSS", "Grand_Cross" } , + { CR_HOLYCROSS, "HOLYCROSS", "Holy_Cross" } , + { CR_PROVIDENCE, "PROVIDENCE", "Providence" } , + { CR_REFLECTSHIELD, "REFLECTSHIELD", "Shield_Reflect" } , + { CR_SHIELDBOOMERANG, "SHIELDBOOMERANG", "Shield_Boomerang" } , + { CR_SHIELDCHARGE, "SHIELDCHARGE", "Shield_Charge" } , + { CR_SPEARQUICKEN, "SPEARQUICKEN", "Spear_Quicken" } , + { CR_SYNTHESISPOTION, "SYNTHESISPOTION", "Potion_Synthesis" } , + { CR_TRUST, "TRUST", "Faith" } , + { DC_DANCINGLESSON, "DANCINGLESSON", "Dancing_Lesson" } , + { DC_DONTFORGETME, "DONTFORGETME", "Don't_Forget_Me" } , + { DC_FORTUNEKISS, "FORTUNEKISS", "Fortune_Kiss" } , + { DC_HUMMING, "HUMMING", "Humming" } , + { DC_SCREAM, "SCREAM", "Scream" } , + { DC_SERVICEFORYOU, "SERVICEFORYOU", "Prostitute" } , + { DC_THROWARROW, "THROWARROW", "Throw_Arrow" } , + { DC_UGLYDANCE, "UGLYDANCE", "Ugly_Dance" } , + { HP_ASSUMPTIO, "ASSUMPTIO", "Assumptio" } , + { HP_BASILICA, "BASILICA", "Basilica" } , + { HP_MEDITATIO, "MEDITATIO", "Meditation" } , + { HT_ANKLESNARE, "ANKLESNARE", "Ankle_Snare" } , + { HT_BEASTBANE, "BEASTBANE", "Beast_Bane" } , + { HT_BLASTMINE, "BLASTMINE", "Blast_Mine" } , + { HT_BLITZBEAT, "BLITZBEAT", "Blitz_Beat" } , + { HT_CLAYMORETRAP, "CLAYMORETRAP", "Claymore_Trap" } , + { HT_DETECTING, "DETECTING", "Detect" } , + { HT_FALCON, "FALCON", "Falconry_Mastery" } , + { HT_FLASHER, "FLASHER", "Flasher" } , + { HT_FREEZINGTRAP, "FREEZINGTRAP", "Freezing_Trap" } , + { HT_LANDMINE, "LANDMINE", "Land_Mine" } , + { HT_REMOVETRAP, "REMOVETRAP", "Remove_Trap" } , + { HT_SANDMAN, "SANDMAN", "Sandman" } , + { HT_SHOCKWAVE, "SHOCKWAVE", "Shockwave_Trap" } , + { HT_SKIDTRAP, "SKIDTRAP", "Skid_Trap" } , + { HT_SPRINGTRAP, "SPRINGTRAP", "Spring_Trap" } , + { HT_STEELCROW, "STEELCROW", "Steel_Crow" } , + { HT_TALKIEBOX, "TALKIEBOX", "Talkie_Box" } , + { HW_MAGICCRASHER, "MAGICCRASHER", "Magic_Crasher" } , + { HW_MAGICPOWER, "MAGICPOWER", "Magic_Power" } , + { HW_NAPALMVULCAN, "NAPALMVULCAN", "Napalm_Vulcan" } , + { HW_SOULDRAIN, "SOULDRAIN", "Soul_Drain" } , + { KN_AUTOCOUNTER, "AUTOCOUNTER", "Counter_Attack" } , + { KN_BOWLINGBASH, "BOWLINGBASH", "Bowling_Bash" } , + { KN_BRANDISHSPEAR, "BRANDISHSPEAR", "Brandish_Spear" } , + { KN_CAVALIERMASTERY, "CAVALIERMASTERY", "Cavalier_Mastery" } , + { KN_PIERCE, "PIERCE", "Pierce" } , + { KN_RIDING, "RIDING", "Peco_Peco_Ride" } , + { KN_SPEARBOOMERANG, "SPEARBOOMERANG", "Spear_Boomerang" } , + { KN_SPEARMASTERY, "SPEARMASTERY", "Spear_Mastery" } , + { KN_SPEARSTAB, "SPEARSTAB", "Spear_Stab" } , + { KN_TWOHANDQUICKEN, "TWOHANDQUICKEN", "Twohand_Quicken" } , + { LK_AURABLADE, "AURABLADE", "Aura_Blade" } , + { LK_BERSERK, "BERSERK", "Berserk" } , + { LK_CONCENTRATION, "CONCENTRATION", "Concentration" } , + { LK_FURY, "FURY", "LK_FURY" } , + { LK_HEADCRUSH, "HEADCRUSH", "Head_Crusher" } , + { LK_JOINTBEAT, "JOINTBEAT", "Joint_Beat" } , + { LK_PARRYING, "PARRYING", "Parrying" } , + { LK_SPIRALPIERCE, "SPIRALPIERCE", "Spiral_Pierce" } , + { LK_TENSIONRELAX, "TENSIONRELAX", "Tension_Relax" } , + { MC_CARTREVOLUTION, "CARTREVOLUTION", "Cart_Revolution" } , + { MC_CHANGECART, "CHANGECART", "Change_Cart" } , + { MC_DISCOUNT, "DISCOUNT", "Discount" } , + { MC_IDENTIFY, "IDENTIFY", "Item_Appraisal" } , + { MC_INCCARRY, "INCCARRY", "Enlarge_Weight_Limit" } , + { MC_LOUD, "LOUD", "Lord_Exclamation" } , + { MC_MAMMONITE, "MAMMONITE", "Mammonite" } , + { MC_OVERCHARGE, "OVERCHARGE", "Overcharge" } , + { MC_PUSHCART, "PUSHCART", "Pushcart" } , + { MC_VENDING, "VENDING", "Vending" } , + { MG_COLDBOLT, "COLDBOLT", "Cold_Bolt" } , + { MG_ENERGYCOAT, "ENERGYCOAT", "Energy_Coat" } , + { MG_FIREBALL, "FIREBALL", "Fire_Ball" } , + { MG_FIREBOLT, "FIREBOLT", "Fire_Bolt" } , + { MG_FIREWALL, "FIREWALL", "Fire_Wall" } , + { MG_FROSTDIVER, "FROSTDIVER", "Frost_Diver" } , + { MG_LIGHTNINGBOLT, "LIGHTNINGBOLT", "Lightening_Bolt" } , + { MG_NAPALMBEAT, "NAPALMBEAT", "Napalm_Beat" } , + { MG_SAFETYWALL, "SAFETYWALL", "Safety_Wall" } , + { MG_SIGHT, "SIGHT", "Sight" } , + { MG_SOULSTRIKE, "SOULSTRIKE", "Soul_Strike" } , + { MG_SRECOVERY, "SRECOVERY", "Increase_SP_Recovery" } , + { MG_STONECURSE, "STONECURSE", "Stone_Curse" } , + { MG_THUNDERSTORM, "THUNDERSTORM", "Thunderstorm" } , + { MO_ABSORBSPIRITS, "ABSORBSPIRITS", "Absorb_Spirits" } , + { MO_BLADESTOP, "BLADESTOP", "Blade_Stop" } , + { MO_BODYRELOCATION, "BODYRELOCATION", "Body_Relocation" } , + { MO_CALLSPIRITS, "CALLSPIRITS", "Call_Spirits" } , + { MO_CHAINCOMBO, "CHAINCOMBO", "Chain_Combo" } , + { MO_COMBOFINISH, "COMBOFINISH", "Combo_Finish" } , + { MO_DODGE, "DODGE", "Dodge" } , + { MO_EXPLOSIONSPIRITS, "EXPLOSIONSPIRITS", "Explosion_Spirits" } , + { MO_EXTREMITYFIST, "EXTREMITYFIST", "Extremity_Fist" } , + { MO_FINGEROFFENSIVE, "FINGEROFFENSIVE", "Finger_Offensive" } , + { MO_INVESTIGATE, "INVESTIGATE", "Investigate" } , + { MO_IRONHAND, "IRONHAND", "Iron_Hand" } , + { MO_SPIRITSRECOVERY, "SPIRITSRECOVERY", "Spirit_Recovery" } , + { MO_STEELBODY, "STEELBODY", "Steel_Body" } , + { MO_TRIPLEATTACK, "TRIPLEATTACK", "Triple_Blows" } , + { NPC_ATTRICHANGE, "ATTRICHANGE", "NPC_ATTRICHANGE" } , + { NPC_BARRIER, "BARRIER", "NPC_BARRIER" } , + { NPC_BLINDATTACK, "BLINDATTACK", "NPC_BLINDATTACK" } , + { NPC_BLOODDRAIN, "BLOODDRAIN", "NPC_BLOODDRAIN" } , + { NPC_CHANGEDARKNESS, "CHANGEDARKNESS", "NPC_CHANGEDARKNESS" } , + { NPC_CHANGEFIRE, "CHANGEFIRE", "NPC_CHANGEFIRE" } , + { NPC_CHANGEGROUND, "CHANGEGROUND", "NPC_CHANGEGROUND" } , + { NPC_CHANGEHOLY, "CHANGEHOLY", "NPC_CHANGEHOLY" } , + { NPC_CHANGEPOISON, "CHANGEPOISON", "NPC_CHANGEPOISON" } , + { NPC_CHANGETELEKINESIS, "CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS" } , + { NPC_CHANGEWATER, "CHANGEWATER", "NPC_CHANGEWATER" } , + { NPC_CHANGEWIND, "CHANGEWIND", "NPC_CHANGEWIND" } , + { NPC_COMBOATTACK, "COMBOATTACK", "NPC_COMBOATTACK" } , + { NPC_CRITICALSLASH, "CRITICALSLASH", "NPC_CRITICALSLASH" } , + { NPC_CURSEATTACK, "CURSEATTACK", "NPC_CURSEATTACK" } , + { NPC_DARKBLESSING, "DARKBLESSING", "NPC_DARKBLESSING" } , + { NPC_DARKBREATH, "DARKBREATH", "NPC_DARKBREATH" } , + { NPC_DARKCROSS, "DARKCROSS", "NPC_DARKCROSS" } , + { NPC_DARKNESSATTACK, "DARKNESSATTACK", "NPC_DARKNESSATTACK" } , + { NPC_DEFENDER, "DEFENDER", "NPC_DEFENDER" } , + { NPC_EMOTION, "EMOTION", "NPC_EMOTION" } , + { NPC_ENERGYDRAIN, "ENERGYDRAIN", "NPC_ENERGYDRAIN" } , + { NPC_FIREATTACK, "FIREATTACK", "NPC_FIREATTACK" } , + { NPC_GROUNDATTACK, "GROUNDATTACK", "NPC_GROUNDATTACK" } , + { NPC_GUIDEDATTACK, "GUIDEDATTACK", "NPC_GUIDEDATTACK" } , + { NPC_HALLUCINATION, "HALLUCINATION", "NPC_HALLUCINATION" } , + { NPC_HOLYATTACK, "HOLYATTACK", "NPC_HOLYATTACK" } , + { NPC_KEEPING, "KEEPING", "NPC_KEEPING" } , + { NPC_LICK, "LICK", "NPC_LICK" } , + { NPC_MAGICALATTACK, "MAGICALATTACK", "NPC_MAGICALATTACK" } , + { NPC_MENTALBREAKER, "MENTALBREAKER", "NPC_MENTALBREAKER" } , + { NPC_METAMORPHOSIS, "METAMORPHOSIS", "NPC_METAMORPHOSIS" } , + { NPC_PETRIFYATTACK, "PETRIFYATTACK", "NPC_PETRIFYATTACK" } , + { NPC_PIERCINGATT, "PIERCINGATT", "NPC_PIERCINGATT" } , + { NPC_POISON, "POISON", "NPC_POISON" } , + { NPC_POISONATTACK, "POISONATTACK", "NPC_POISONATTACK" } , + { NPC_PROVOCATION, "PROVOCATION", "NPC_PROVOCATION" } , + { NPC_RANDOMATTACK, "RANDOMATTACK", "NPC_RANDOMATTACK" } , + { NPC_RANGEATTACK, "RANGEATTACK", "NPC_RANGEATTACK" } , + { NPC_REBIRTH, "REBIRTH", "NPC_REBIRTH" } , + { NPC_SELFDESTRUCTION, "SELFDESTRUCTION", "Kabooooom!" } , + { NPC_SELFDESTRUCTION2, "SELFDESTRUCTION2", "NPC_SELFDESTRUCTION2" } , + { NPC_SILENCEATTACK, "SILENCEATTACK", "NPC_SILENCEATTACK" } , + { NPC_SLEEPATTACK, "SLEEPATTACK", "NPC_SLEEPATTACK" } , + { NPC_SMOKING, "SMOKING", "NPC_SMOKING" } , + { NPC_SPLASHATTACK, "SPLASHATTACK", "NPC_SPLASHATTACK" } , + { NPC_STUNATTACK, "STUNATTACK", "NPC_STUNATTACK" } , + { NPC_SUICIDE, "SUICIDE", "NPC_SUICIDE" } , + { NPC_SUMMONMONSTER, "SUMMONMONSTER", "NPC_SUMMONMONSTER" } , + { NPC_SUMMONSLAVE, "SUMMONSLAVE", "NPC_SUMMONSLAVE" } , + { NPC_TELEKINESISATTACK, "TELEKINESISATTACK", "NPC_TELEKINESISATTACK" } , + { NPC_TRANSFORMATION, "TRANSFORMATION", "NPC_TRANSFORMATION" } , + { NPC_WATERATTACK, "WATERATTACK", "NPC_WATERATTACK" } , + { NPC_WINDATTACK, "WINDATTACK", "NPC_WINDATTACK" } , + { NV_BASIC, "BASIC", "Basic_Skill" } , + { NV_FIRSTAID, "FIRSTAID", "First Aid" } , + { NV_TRICKDEAD, "TRICKDEAD", "Play_Dead" } , + { PA_GOSPEL, "GOSPEL", "Gospel" } , + { PA_PRESSURE, "PRESSURE", "Pressure" } , + { PA_SACRIFICE, "SACRIFICE", "Sacrificial_Ritual" } , + { PF_FOGWALL, "FOGWALL", "Wall_of_Fog" } , + { PF_HPCONVERSION, "HPCONVERSION", "Health_Conversion" } , + { PF_MEMORIZE, "MEMORIZE", "Memorize" } , + { PF_MINDBREAKER, "MINDBREAKER", "Mind_Breaker" } , + { PF_SOULBURN, "SOULBURN", "Soul_Burn" } , + { PF_SOULCHANGE, "SOULCHANGE", "Soul_Change" } , + { PF_SPIDERWEB, "SPIDERWEB", "Spider_Web" } , + { PR_ASPERSIO, "ASPERSIO", "Aspersio" } , + { PR_BENEDICTIO, "BENEDICTIO", "B.S_Sacramenti" } , + { PR_GLORIA, "GLORIA", "Gloria" } , + { PR_IMPOSITIO, "IMPOSITIO", "Impositio_Manus" } , + { PR_KYRIE, "KYRIE", "Kyrie_Eleison" } , + { PR_LEXAETERNA, "LEXAETERNA", "Lex_Aeterna" } , + { PR_LEXDIVINA, "LEXDIVINA", "Lex_Divina" } , + { PR_MACEMASTERY, "MACEMASTERY", "Mace_Mastery" } , + { PR_MAGNIFICAT, "MAGNIFICAT", "Magnificat" } , + { PR_MAGNUS, "MAGNUS", "Magnus_Exorcismus" } , + { PR_SANCTUARY, "SANCTUARY", "Santuary" } , + { PR_SLOWPOISON, "SLOWPOISON", "Slow_Poison" } , + { PR_STRECOVERY, "STRECOVERY", "Status_Recovery" } , + { PR_SUFFRAGIUM, "SUFFRAGIUM", "Suffragium" } , + { PR_TURNUNDEAD, "TURNUNDEAD", "Turn_Undead" } , + { RG_BACKSTAP, "BACKSTAP", "Back_Stab" } , + { RG_CLEANER, "CLEANER", "Remover" } , + { RG_COMPULSION, "COMPULSION", "Compulsion_Discount" } , + { RG_FLAGGRAFFITI, "FLAGGRAFFITI", "Flag_Graffity" } , + { RG_GANGSTER, "GANGSTER", "Gangster's_Paradise" } , + { RG_GRAFFITI, "GRAFFITI", "Graffiti" } , + { RG_INTIMIDATE, "INTIMIDATE", "Intimidate" } , + { RG_PLAGIARISM, "PLAGIARISM", "Plagiarism" } , + { RG_RAID, "RAID", "Raid" } , + { RG_SNATCHER, "SNATCHER", "Snatcher" } , + { RG_STEALCOIN, "STEALCOIN", "Steal_Coin" } , + { RG_STRIPARMOR, "STRIPARMOR", "Strip_Armor" } , + { RG_STRIPHELM, "STRIPHELM", "Strip_Helm" } , + { RG_STRIPSHIELD, "STRIPSHIELD", "Strip_Shield" } , + { RG_STRIPWEAPON, "STRIPWEAPON", "Strip_Weapon" } , + { RG_TUNNELDRIVE, "TUNNELDRIVE", "Tunnel_Drive" } , + { SA_ABRACADABRA, "ABRACADABRA", "Hocus-pocus" } , + { SA_ADVANCEDBOOK, "ADVANCEDBOOK", "Advanced_Book" } , + { SA_AUTOSPELL, "AUTOSPELL", "Auto_Cast" } , + { SA_CASTCANCEL, "CASTCANCEL", "Cast_Cancel" } , + { SA_CLASSCHANGE, "CLASSCHANGE", "Class_Change" } , + { SA_COMA, "COMA", "Coma" } , + { SA_DEATH, "DEATH", "Death" } , + { SA_DELUGE, "DELUGE", "Deluge" } , + { SA_DISPELL, "DISPELL", "Dispel" } , + { SA_DRAGONOLOGY, "DRAGONOLOGY", "Dragonology" } , + { SA_FLAMELAUNCHER, "FLAMELAUNCHER", "Flame_Launcher" } , + { SA_FORTUNE, "FORTUNE", "Fortune" } , + { SA_FREECAST, "FREECAST", "Cast_Freedom" } , + { SA_FROSTWEAPON, "FROSTWEAPON", "Frost_Weapon" } , + { SA_FULLRECOVERY, "FULLRECOVERY", "Full_Recovery" } , + { SA_GRAVITY, "GRAVITY", "Gravity" } , + { SA_INSTANTDEATH, "INSTANTDEATH", "Instant_Death" } , + { SA_LANDPROTECTOR, "LANDPROTECTOR", "Land_Protector" } , + { SA_LEVELUP, "LEVELUP", "Level_Up" } , + { SA_LIGHTNINGLOADER, "LIGHTNINGLOADER", "Lightning_Loader" } , + { SA_MAGICROD, "MAGICROD", "Magic_Rod" } , + { SA_MONOCELL, "MONOCELL", "Monocell" } , + { SA_QUESTION, "QUESTION", "Question?" } , + { SA_REVERSEORCISH, "REVERSEORCISH", "Reverse_Orcish" } , + { SA_SEISMICWEAPON, "SEISMICWEAPON", "Seismic_Weapon" } , + { SA_SPELLBREAKER, "SPELLBREAKER", "Break_Spell" } , + { SA_SUMMONMONSTER, "SUMMONMONSTER", "Summon_Monster" } , + { SA_TAMINGMONSTER, "TAMINGMONSTER", "Taming_Monster" } , + { SA_VIOLENTGALE, "VIOLENTGALE", "Violent_Gale" } , + { SA_VOLCANO, "VOLCANO", "Volcano" } , + { SG_DEVIL, "DEVIL", "Devil" } , + { SG_FEEL, "FEEL", "Feel" } , + { SG_FRIEND, "FRIEND", "Friend" } , + { SG_FUSION, "FUSION", "Fusion" } , + { SG_HATE, "HATE", "Hate" } , + { SG_KNOWLEDGE, "KNOWLEDGE", "Knowledge" } , + { SG_MOON_ANGER, "ANGER", "Moon Anger" } , + { SG_MOON_BLESS, "BLESS", "Moon Bless" } , + { SG_MOON_COMFORT, "COMFORT", "Moon Comfort" } , + { SG_MOON_WARM, "WARM", "Moon Warm" } , + { SG_STAR_ANGER, "ANGER", "Star Anger" } , + { SG_STAR_BLESS, "BLESS", "Star Bless" } , + { SG_STAR_COMFORT, "COMFORT", "Star Comfort" } , + { SG_STAR_WARM, "WARM", "Star Warm" } , + { SG_SUN_ANGER, "ANGER", "Sun Anger" } , + { SG_SUN_BLESS, "BLESS", "Sun Bless" } , + { SG_SUN_COMFORT, "COMFORT", "Sun Comfort" } , + { SG_SUN_WARM, "WARM", "Sun Warm" } , + { SL_ALCHEMIST, "ALCHEMIST", "Alchemist" } , + { SL_ASSASIN, "ASSASIN", "Assasin" } , + { SL_BARDDANCER, "BARDDANCER", "Bard Dancer" } , + { SL_BLACKSMITH, "BLACKSMITH", "Black Smith" } , + { SL_CRUSADER, "CRUSADER", "Crusader" } , + { SL_HUNTER, "HUNTER", "Hunter" } , + { SL_KAAHI, "KAAHI", "Kaahi" } , + { SL_KAINA, "KAINA", "Kaina" } , + { SL_KAITE, "KAITE", "Kaite" } , + { SL_KAIZEL, "KAIZEL", "Kaizel" } , + { SL_KAUPE, "KAUPE", "Kaupe" } , + { SL_KNIGHT, "KNIGHT", "Knight" } , + { SL_MONK, "MONK", "Monk" } , + { SL_PRIEST, "PRIEST", "Priest" } , + { SL_ROGUE, "ROGUE", "Rogue" } , + { SL_SAGE, "SAGE", "Sage" } , + { SL_SKA, "SKA", "SKA" } , + { SL_SKE, "SKE", "SKE" } , + { SL_SMA, "SMA", "SMA" } , + { SL_SOULLINKER, "SOULLINKER", "Soul Linker" } , + { SL_STAR, "STAR", "Star" } , + { SL_STIN, "STIN", "Stin" } , + { SL_STUN, "STUN", "Stun" } , + { SL_SUPERNOVICE, "SUPERNOVICE", "Super Novice" } , + { SL_SWOO, "SWOO", "Swoo" } , + { SL_WIZARD, "WIZARD", "Wizard" } , + { SM_AUTOBERSERK, "AUTOBERSERK", "Auto_Berserk" } , + { SM_BASH, "BASH", "Bash" } , + { SM_ENDURE, "ENDURE", "Endure" } , + { SM_FATALBLOW, "FATALBLOW", "Attack_Weak_Point" } , + { SM_MAGNUM, "MAGNUM", "Magnum_Break" } , + { SM_MOVINGRECOVERY, "MOVINGRECOVERY", "Moving_HP_Recovery" } , + { SM_PROVOKE, "PROVOKE", "Provoke" } , + { SM_RECOVERY, "RECOVERY", "Increase_HP_Recovery" } , + { SM_SWORD, "SWORD", "Sword_Mastery" } , + { SM_TWOHAND, "TWOHAND", "Two-Handed_Sword_Mastery" } , + { SN_FALCONASSAULT, "FALCONASSAULT", "Falcon_Assault" } , + { SN_SHARPSHOOTING, "SHARPSHOOTING", "Sharpshooting" } , + { SN_SIGHT, "SIGHT", "True_Sight" } , + { SN_WINDWALK, "WINDWALK", "Wind_Walk" } , + { ST_CHASEWALK, "CHASEWALK", "Chase_Walk" } , + { ST_REJECTSWORD, "REJECTSWORD", "Reject_Sword" } , + { ST_STEALBACKPACK, "STEALBACKPACK", "Steal_Backpack" } , + { TF_BACKSLIDING, "BACKSLIDING", "Back_Sliding" } , + { TF_DETOXIFY, "DETOXIFY", "Detoxify" } , + { TF_DOUBLE, "DOUBLE", "Double_Attack" } , + { TF_HIDING, "HIDING", "Hiding" } , + { TF_MISS, "MISS", "Improve_Dodge" } , + { TF_PICKSTONE, "PICKSTONE", "Take_Stone" } , + { TF_POISON, "POISON", "Envenom" } , + { TF_SPRINKLESAND, "SPRINKLESAND", "Throw_Sand" } , + { TF_STEAL, "STEAL", "Steal" } , + { TF_THROWSTONE, "THROWSTONE", "Throw_Stone" } , + { TK_COUNTER, "COUNTER", "Counter" } , + { TK_DODGE, "DODGE", "Dodge" } , + { TK_DOWNKICK, "DOWNKICK", "Down Kick" } , + { TK_HIGHJUMP, "HIGHJUMP", "High Jump" } , + { TK_HPTIME, "HPTIME", "HP Time" } , + { TK_JUMPKICK, "JUMPKICK", "Jump Kick" } , + { TK_POWER, "POWER", "Power" } , + { TK_READYCOUNTER, "READYCOUNTER", "Ready Counter" } , + { TK_READYDOWN, "READYDOWN", "Ready Down" } , + { TK_READYSTORM, "READYSTORM", "Ready Storm" } , + { TK_READYTURN, "READYTURN", "Ready Turn" } , + { TK_RUN, "RUN", "TK_RUN" } , + { TK_SEVENWIND, "SEVENWIND", "Seven Wind" } , + { TK_SPTIME, "SPTIME", "SP Time" } , + { TK_STORMKICK, "STORMKICK", "Storm Kick" } , + { TK_TURNKICK, "TURNKICK", "Turn Kick" } , + { WE_BABY, "BABY", "Adopt_Baby" } , + { WE_CALLBABY, "CALLBABY", "Call_Baby" } , + { WE_CALLPARENT, "CALLPARENT", "Call_Parent" } , + { WE_CALLPARTNER, "CALLPARTNER", "I Want to See You" } , + { WE_FEMALE, "FEMALE", "I Only Look Up to You" } , + { WE_MALE, "MALE", "I Will Protect You" } , + { WS_CARTBOOST, "CARTBOOST", "Cart_Boost" } , + { WS_CREATECOIN, "CREATECOIN", "Create_Coins" } , + { WS_CREATENUGGET, "CREATENUGGET", "Create_Nuggets" } , + { WS_MELTDOWN, "MELTDOWN", "Meltdown" } , + { WS_SYSTEMCREATE, "SYSTEMCREATE", "Create_System_tower" } , + { WZ_EARTHSPIKE, "EARTHSPIKE", "Earth_Spike" } , + { WZ_ESTIMATION, "ESTIMATION", "Sense" } , + { WZ_FIREIVY, "FIREIVY", "Fire_Ivy" } , + { WZ_FIREPILLAR, "FIREPILLAR", "Fire_Pillar" } , + { WZ_FROSTNOVA, "FROSTNOVA", "Frost_Nova" } , + { WZ_HEAVENDRIVE, "HEAVENDRIVE", "Heaven's_Drive" } , + { WZ_ICEWALL, "ICEWALL", "Ice_Wall" } , + { WZ_JUPITEL, "JUPITEL", "Jupitel_Thunder" } , + { WZ_METEOR, "METEOR", "Meteor_Storm" } , + { WZ_QUAGMIRE, "QUAGMIRE", "Quagmire" } , + { WZ_SIGHTRASHER, "SIGHTRASHER", "Sightrasher" } , + { WZ_STORMGUST, "STORMGUST", "Storm_Gust" } , + { WZ_VERMILION, "VERMILION", "Lord_of_Vermilion" } , + { WZ_WATERBALL, "WATERBALL", "Water_Ball" } , + { 0, 0, 0 } +}; + +static const int dirx[8]={0,-1,-1,-1,0,1,1,1}; +static const int diry[8]={1,1,0,-1,-1,-1,0,1}; + +static int rdamage; + +/* スキルデータベース */ +struct skill_db skill_db[MAX_SKILL_DB]; + +/* アイテム作成データベース */ +struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; + +/* 矢作成スキルデータベース */ +struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; + +/* アブラカダブラ発動スキルデータベース */ +struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; + +int skill_get_hit( int id ){ return skill_db[id].hit; } +int skill_get_inf( int id ){ return skill_db[id].inf; } +int skill_get_pl( int id ){ return skill_db[id].pl; } +int skill_get_nk( int id ){ return skill_db[id].nk; } +int skill_get_max( int id ){ return skill_db[id].max; } +int skill_get_range( int id , int lv ){ return (lv <= 0) ? 0:skill_db[id].range[lv-1]; } +int skill_get_hp( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].hp[lv-1]; } +int skill_get_sp( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].sp[lv-1]; } +int skill_get_zeny( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].zeny[lv-1]; } +int skill_get_num( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].num[lv-1]; } +int skill_get_cast( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].cast[lv-1]; } +int skill_get_delay( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].delay[lv-1]; } +int skill_get_time( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].upkeep_time[lv-1]; } +int skill_get_time2( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].upkeep_time2[lv-1]; } +int skill_get_castdef( int id ){ return skill_db[id].cast_def_rate; } +int skill_get_weapontype( int id ){ return skill_db[id].weapon; } +int skill_get_inf2( int id ){ return skill_db[id].inf2; } +int skill_get_maxcount( int id ){ return skill_db[id].maxcount; } +int skill_get_blewcount( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].blewcount[lv-1]; } +int skill_get_mhp( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].mhp[lv-1]; } +int skill_get_castnodex( int id ,int lv ){ return (lv <= 0) ? 0:skill_db[id].castnodex[lv-1]; } + +/* プロトタイプ */ +struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag); +int skill_check_condition( struct map_session_data *sd,int type); +int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); +int skill_frostjoke_scream(struct block_list *bl,va_list ap); +int skill_status_change_timer_sub(struct block_list *bl, va_list ap ); +int skill_attack_area(struct block_list *bl,va_list ap); +int skill_abra_dataset(int skilllv); +int skill_clear_element_field(struct block_list *bl); +int skill_landprotector(struct block_list *bl, va_list ap ); +int skill_trap_splash(struct block_list *bl, va_list ap ); +int skill_count_target(struct block_list *bl, va_list ap ); + +// [MouseJstr] - skill ok to cast? and when? +static int skillnotok(int skillid, struct map_session_data *sd) { + if (sd == 0) + return 0; + if (pc_isGM(sd) >= 20) + return 0; // gm's can do anything damn thing they want + switch (skillid) { + case AL_WARP: + case AL_TELEPORT: + case MC_VENDING: + case MC_IDENTIFY: + return 0; // always allowed + default: + return(map[sd->bl.m].flag.noskill); + } +} + + +static int distance(int x0,int y0,int x1,int y1) +{ + int dx,dy; + + dx=abs(x0-x1); + dy=abs(y0-y1); + return dx>dy ? dx : dy; +} + +/* スキルユニットIDを返す(これもデータベースに入れたいな) */ +int skill_get_unit_id(int id,int flag) +{ + + switch(id){ + case MG_SAFETYWALL: return 0x7e; /* セイフティウォール */ + case MG_FIREWALL: return 0x7f; /* ファイアーウォール */ + case AL_WARP: return (flag==0)?0x81:0x80; /* ワープポータル */ + case PR_BENEDICTIO: return 0x82; /* 聖体降福 */ + case PR_SANCTUARY: return 0x83; /* サンクチュアリ */ + case PR_MAGNUS: return 0x84; /* マグヌスエクソシズム */ + case AL_PNEUMA: return 0x85; /* ニューマ */ + case MG_THUNDERSTORM: return 0x86; /* サンダーストーム */ + case WZ_HEAVENDRIVE: return 0x86; /* ヘヴンズドライブ */ + case WZ_SIGHTRASHER: return 0x86; /* サイトラッシャー */ + case WZ_METEOR: return 0x86; /* メテオストーム */ + case WZ_VERMILION: return 0x86; /* ロードオブヴァーミリオン */ + case WZ_FROSTNOVA: return 0x86; /* フロストノヴァ */ + case WZ_STORMGUST: return 0x86; /* ストームガスト(とりあえずLoVと同じで処理) */ + case CR_GRANDCROSS: return 0x86; /* グランドクロス */ + case WZ_FIREPILLAR: return (flag==0)?0x87:0x88; /* ファイアーピラー */ + case HT_TALKIEBOX: return 0x99; /* トーキーボックス */ + case WZ_ICEWALL: return 0x8d; /* アイスウォール */ + case WZ_QUAGMIRE: return 0x8e; /* クァグマイア */ + case HT_BLASTMINE: return 0x8f; /* ブラストマイン */ + case HT_SKIDTRAP: return 0x90; /* スキッドトラップ */ + case HT_ANKLESNARE: return 0x91; /* アンクルスネア */ + case AS_VENOMDUST: return 0x92; /* ベノムダスト */ + case HT_LANDMINE: return 0x93; /* ランドマイン */ + case HT_SHOCKWAVE: return 0x94; /* ショックウェーブトラップ */ + case HT_SANDMAN: return 0x95; /* サンドマン */ + case HT_FLASHER: return 0x96; /* フラッシャー */ + case HT_FREEZINGTRAP: return 0x97; /* フリージングトラップ */ + case HT_CLAYMORETRAP: return 0x98; /* クレイモアートラップ */ + case SA_VOLCANO: return 0x9a; /* ボルケーノ */ + case SA_DELUGE: return 0x9b; /* デリュージ */ + case SA_VIOLENTGALE: return 0x9c; /* バイオレントゲイル */ + case SA_LANDPROTECTOR: return 0x9d; /* ランドプロテクター */ + case BD_LULLABY: return 0x9e; /* 子守歌 */ + case BD_RICHMANKIM: return 0x9f; /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: return 0xa0; /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD:return 0xa1; /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: return 0xa2; /* ニーベルングの指輪 */ + case BD_ROKISWEIL: return 0xa3; /* ロキの叫び */ + case BD_INTOABYSS: return 0xa4; /* 深淵の中に */ + case BD_SIEGFRIED: return 0xa5; /* 不死身のジークフリード */ + case BA_DISSONANCE: return 0xa6; /* 不協和音 */ + case BA_WHISTLE: return 0xa7; /* 口笛 */ + case BA_ASSASSINCROSS: return 0xa8; /* 夕陽のアサシンクロス */ + case BA_POEMBRAGI: return 0xa9; /* ブラギの詩 */ + case BA_APPLEIDUN: return 0xaa; /* イドゥンの林檎 */ + case DC_UGLYDANCE: return 0xab; /* 自分勝手なダンス */ + case DC_HUMMING: return 0xac; /* ハミング */ + case DC_DONTFORGETME: return 0xad; /* 私を忘れないで… */ + case DC_FORTUNEKISS: return 0xae; /* 幸運のキス */ + case DC_SERVICEFORYOU: return 0xaf; /* サービスフォーユー */ + case RG_GRAFFITI: return 0xb0; /* グラフィティ */ + case AM_DEMONSTRATION: return 0xb1; /* デモンストレーション */ + case WE_CALLPARTNER: return 0xb2; /* あなたに逢いたい */ + case PA_GOSPEL: return 0xb3; /* ゴスペル */ + case HP_BASILICA: return 0xb4; /* バジリカ */ + case PF_FOGWALL: return 0xb6; /* フォグウォール */ + case PF_SPIDERWEB: return 0xb7; /* スパイダーウェッブ */ + } + return 0; + /* + 0x89,0x8a,0x8b 表示無し + 0x9a 炎属性の詠唱みたいなエフェクト + 0x9b 水属性の詠唱みたいなエフェクト + 0x9c 風属性の詠唱みたいなエフェクト + 0x9d 白い小さなエフェクト + 0xb1 Alchemist Demonstration + 0xb2 = Pink Warp Portal + 0xb3 = Gospel For Paladin + 0xb4 = Basilica + 0xb5 = Empty + 0xb6 = Fog Wall for Professor + 0xb7 = Spider Web for Professor + 0xb8 = Empty + 0xb9 = + */ +} + +/*========================================== + * スキル追加効果 + *------------------------------------------ + */ +int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick) +{ + /* MOB追加効果スキル用 */ + const int sc[]={ + SC_POISON, SC_BLIND, SC_SILENCE, SC_STAN, + SC_STONE, SC_CURSE, SC_SLEEP + }; + const int sc2[]={ + MG_STONECURSE,MG_FROSTDIVER,NPC_STUNATTACK, + NPC_SLEEPATTACK,TF_POISON,NPC_CURSEATTACK, + NPC_SILENCEATTACK,0,NPC_BLINDATTACK + }; + + struct map_session_data *sd=NULL; + struct map_session_data *dstsd=NULL; + struct mob_data *md=NULL; + struct mob_data *dstmd=NULL; + struct pet_data *pd=NULL; + + int skill,skill2; + int rate,luk; + + int sc_def_mdef,sc_def_vit,sc_def_int,sc_def_luk; + int sc_def_mdef2,sc_def_vit2,sc_def_int2,sc_def_luk2; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + + if(skilllv < 0) return 0; + + if(src->type==BL_PC){ + nullpo_retr(0, sd=(struct map_session_data *)src); + }else if(src->type==BL_MOB){ + nullpo_retr(0, md=(struct mob_data *)src); //未使用? + }else if(src->type==BL_PET){ + nullpo_retr(0, pd=(struct pet_data *)src); // [Valaris] + } + + //対象の耐性 + luk = battle_get_luk(bl); + sc_def_mdef=100 - (3 + battle_get_mdef(bl) + luk/3); + sc_def_vit=100 - (3 + battle_get_vit(bl) + luk/3); + sc_def_int=100 - (3 + battle_get_int(bl) + luk/3); + sc_def_luk=100 - (3 + luk); + //自分の耐性 + luk = battle_get_luk(src); + sc_def_mdef2=100 - (3 + battle_get_mdef(src) + luk/3); + sc_def_vit2=100 - (3 + battle_get_vit(src) + luk/3); + sc_def_int2=100 - (3 + battle_get_int(src) + luk/3); + sc_def_luk2=100 - (3 + luk); + if(bl->type==BL_PC) + dstsd=(struct map_session_data *)bl; + else if(bl->type==BL_MOB){ + dstmd=(struct mob_data *)bl; //未使用? + if(sc_def_mdef>50) + sc_def_mdef=50; + if(sc_def_vit>50) + sc_def_vit=50; + if(sc_def_int>50) + sc_def_int=50; + if(sc_def_luk>50) + sc_def_luk=50; + } + if(sc_def_mdef<0) + sc_def_mdef=0; + if(sc_def_vit<0) + sc_def_vit=0; + if(sc_def_int<0) + sc_def_int=0; + + switch(skillid){ + case 0: /* 通常攻撃 */ + /* 自動鷹 */ + if( sd && pc_isfalcon(sd) && sd->status.weapon == 11 && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 && + 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); + } + // スナッチャー + if(sd && sd->status.weapon != 11 && (skill=pc_checkskill(sd,RG_SNATCHER)) > 0) + if((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 + clif_skill_fail(sd,skillid,0,0); + } + break; + + case SM_BASH: /* バッシュ(急所攻撃) */ + if( sd && (skill=pc_checkskill(sd,SM_FATALBLOW))>0 ){ + if( rand()%100 < 6*(skilllv-5)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(SM_FATALBLOW,skilllv),0); + } + break; + + case TF_POISON: /* インベナム */ + case AS_SPLASHER: /* ベナムスプラッシャー */ + if(rand()%100< (2*skilllv+10)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_POISON,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + else{ + if(sd && skillid==TF_POISON) + clif_skill_fail(sd,skillid,0,0); + } + break; + + case AS_SONICBLOW: /* ソニックブロー */ + if( rand()%100 < (2*skilllv+10)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + + case HT_FREEZINGTRAP: /* フリージングトラップ */ + rate=skilllv*3+35; + if(rand()%100 < rate*sc_def_mdef/100) + skill_status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case MG_FROSTDIVER: /* フロストダイバー */ + case WZ_FROSTNOVA: /* フロストノヴァ */ + rate=(skilllv*3+35)*sc_def_mdef/100-(battle_get_int(bl)+battle_get_luk(bl))/15; + rate=rate<=5?5:rate; + if(rand()%100 < rate) + skill_status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + else if(sd) + clif_skill_fail(sd,skillid,0,0); + break; + + case WZ_STORMGUST: /* ストームガスト */ + { + struct status_change *sc_data = battle_get_sc_data(bl); + if(sc_data) { + sc_data[SC_FREEZE].val3++; + if(sc_data[SC_FREEZE].val3 >= 3) + skill_status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + } + } + break; + + case HT_LANDMINE: /* ランドマイン */ + if( rand()%100 < (5*skilllv+30)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case HT_SHOCKWAVE: /* ショックウェーブトラップ */ + if(map[bl->m].flag.pvp && dstsd){ + dstsd->status.sp -= dstsd->status.sp*(5+15*skilllv)/100; + pc_calcstatus(dstsd,0); + } + break; + case HT_SANDMAN: /* サンドマン */ + if( rand()%100 < (5*skilllv+30)*sc_def_int/100 ) + skill_status_change_start(bl,SC_SLEEP,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case TF_SPRINKLESAND: /* 砂まき */ + if( rand()%100 < 15*sc_def_int/100 ) + skill_status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case TF_THROWSTONE: /* 石投げ */ + if( rand()%100 < 5*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case CR_HOLYCROSS: /* ホーリークロス */ + if( rand()%100 < 3*skilllv*sc_def_int/100 ) + skill_status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case CR_GRANDCROSS: /* グランドクロス */ + { + int race = battle_get_race(bl); + if( (battle_check_undead(race,battle_get_elem_type(bl)) || race == 6) && rand()%100 < 100000*sc_def_int/100) //強制付与だが完全耐性には無効 + skill_status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + } + break; + + case CR_SHIELDCHARGE: /* シールドチャージ */ + if( rand()%100 < (15 + skilllv*5)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case RG_RAID: /* サプライズアタック */ + if( rand()%100 < (10+3*skilllv)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + if( rand()%100 < (10+3*skilllv)*sc_def_int/100 ) + skill_status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case BA_FROSTJOKE: + if(rand()%100 < (15+5*skilllv)*sc_def_mdef/100) + skill_status_change_start(bl,SC_FREEZE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case DC_SCREAM: + if( rand()%100 < (25+5*skilllv)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case BD_LULLABY: /* 子守唄 */ + if( rand()%100 < 15*sc_def_int/100 ) + skill_status_change_start(bl,SC_SLEEP,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + /* MOBの追加効果付きスキル */ + + case NPC_PETRIFYATTACK: + if(rand()%100 < sc_def_mdef) + skill_status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case NPC_POISON: + case NPC_SILENCEATTACK: + case NPC_STUNATTACK: + if(rand()%100 < sc_def_vit && src->type!=BL_PET) + skill_status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + if(src->type==BL_PET) + skill_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) + skill_status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case NPC_SLEEPATTACK: + case NPC_BLINDATTACK: + if(rand()%100 < sc_def_int) + skill_status_change_start(bl,sc[skillid-NPC_POISON],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case NPC_MENTALBREAKER: + if(dstsd) { + int sp = dstsd->status.max_sp*(10+skilllv)/100; + if(sp < 1) sp = 1; + pc_heal(dstsd,0,-sp); + } + break; + +// -- moonsoul (adding status effect chance given to wizard aoe skills meteor and vermillion) +// + case WZ_METEOR: + if(rand()%100 < sc_def_vit) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case WZ_VERMILION: + if(rand()%100 < sc_def_int) + skill_status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + +// -- moonsoul (stun ability of new champion skill tigerfist) +// + case CH_TIGERFIST: + if( rand()%100 < (5 + skilllv*5)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case LK_SPIRALPIERCE: + if( rand()%100 < (15 + skilllv*5)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case ST_REJECTSWORD: /* フリージングトラップ */ + if( rand()%100 < (10 + skilllv*5) ) + skill_status_change_start(bl,SC_AUTOCOUNTER,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case PF_FOGWALL: /* ホーリークロス */ + if( rand()%100 < 3*skilllv*sc_def_int/100 ) + skill_status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + {//条件が良く分からないので適当に + int race=battle_get_race(bl); + if( !(battle_check_undead(race,battle_get_elem_type(bl)) || race == 6) && rand()%100 < (2*skilllv+10)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_HEADCRUSH,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + } + break; + case LK_JOINTBEAT: /* ジョイントビート */ + //条件が良く分からないので適当に + if( rand()%100 < (2*skilllv+10)*sc_def_vit/100 ) + skill_status_change_start(bl,SC_JOINTBEAT,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case PF_SPIDERWEB: /* スパイダーウェッブ */ + { + int sec=skill_get_time2(skillid,skilllv); + if(map[src->m].flag.pvp) //PvPでは拘束時間半減? + sec = sec/2; + battle_stopwalking(bl,1); + skill_status_change_start(bl,SC_SPIDERWEB,skilllv,0,0,0,sec,0); + } + break; + case ASC_METEORASSAULT: /* メテオアサルト */ + if( rand()%100 < (15 + skilllv*5)*sc_def_vit/100 ) //状態異常は詳細が分からないので適当に + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + if( rand()%100 < (10+3*skilllv)*sc_def_int/100 ) + skill_status_change_start(bl,SC_BLIND,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + case MO_EXTREMITYFIST: /* 阿修羅覇凰拳 */ + //阿修羅を使うと5分間自然回復しないようになる + skill_status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time2(skillid,skilllv),0 ); + break; + } + + if(sd && skillid != MC_CARTREVOLUTION && attack_type&BF_WEAPON){ /* カードによる追加効果 */ + int i; + int sc_def_card=100; + + for(i=SC_STONE;i<=SC_BLIND;i++){ + //対象に状態異常 + if(i==SC_STONE || i==SC_FREEZE) + sc_def_card=sc_def_mdef; + else if(i==SC_STAN || i==SC_POISON || i==SC_SILENCE) + sc_def_card=sc_def_vit; + else if(i==SC_SLEEP || i==SC_CONFUSION || i==SC_BLIND) + sc_def_card=sc_def_int; + else if(i==SC_CURSE) + sc_def_card=sc_def_luk; + + if(!sd->state.arrow_atk) { + if(rand()%10000 < (sd->addeff[i-SC_STONE])*sc_def_card/100 ){ + if(battle_config.battle_log) + printf("PC %d skill_addeff: cardによる異常発動 %d %d\n",sd->bl.id,i,sd->addeff[i-SC_STONE]); + skill_status_change_start(bl,i,7,0,0,0,(i==SC_CONFUSION)? 10000+7000:skill_get_time2(sc2[i-SC_STONE],7),0); + } + } + else { + if(rand()%10000 < (sd->addeff[i-SC_STONE]+sd->arrow_addeff[i-SC_STONE])*sc_def_card/100 ){ + if(battle_config.battle_log) + printf("PC %d skill_addeff: cardによる異常発動 %d %d\n",sd->bl.id,i,sd->addeff[i-SC_STONE]); + skill_status_change_start(bl,i,7,0,0,0,(i==SC_CONFUSION)? 10000+7000:skill_get_time2(sc2[i-SC_STONE],7),0); + } + } + //自分に状態異常 + if(i==SC_STONE || i==SC_FREEZE) + sc_def_card=sc_def_mdef2; + else if(i==SC_STAN || i==SC_POISON || i==SC_SILENCE) + sc_def_card=sc_def_vit2; + else if(i==SC_SLEEP || i==SC_CONFUSION || i==SC_BLIND) + sc_def_card=sc_def_int2; + else if(i==SC_CURSE) + sc_def_card=sc_def_luk2; + + if(!sd->state.arrow_atk) { + if(rand()%10000 < (sd->addeff2[i-SC_STONE])*sc_def_card/100 ){ + if(battle_config.battle_log) + printf("PC %d skill_addeff: cardによる異常発動 %d %d\n",src->id,i,sd->addeff2[i-SC_STONE]); + skill_status_change_start(src,i,7,0,0,0,(i==SC_CONFUSION)? 10000+7000:skill_get_time2(sc2[i-SC_STONE],7),0); + } + } + else { + if(rand()%10000 < (sd->addeff2[i-SC_STONE]+sd->arrow_addeff2[i-SC_STONE])*sc_def_card/100 ){ + if(battle_config.battle_log) + printf("PC %d skill_addeff: cardによる異常発動 %d %d\n",src->id,i,sd->addeff2[i-SC_STONE]); + skill_status_change_start(src,i,7,0,0,0,(i==SC_CONFUSION)? 10000+7000:skill_get_time2(sc2[i-SC_STONE],7),0); + } + } + } + } + return 0; +} + +/*========================================================================= + スキル攻撃吹き飛ばし処理 +-------------------------------------------------------------------------*/ +int skill_blown( struct block_list *src, struct block_list *target,int count) +{ + int dx=0,dy=0,nx,ny; + int x=target->x,y=target->y; + int ret,prev_state=MS_IDLE; + int moveblock; + struct map_session_data *sd=NULL; + struct mob_data *md=NULL; + struct pet_data *pd=NULL; + struct skill_unit *su=NULL; + + nullpo_retr(0, src); + nullpo_retr(0, target); + + if(target->type==BL_PC){ + nullpo_retr(0, sd=(struct map_session_data *)target); + }else if(target->type==BL_MOB){ + nullpo_retr(0, md=(struct mob_data *)target); + }else if(target->type==BL_PET){ + nullpo_retr(0, pd=(struct pet_data *)target); + }else if(target->type==BL_SKILL){ + nullpo_retr(0, su=(struct skill_unit *)target); + }else return 0; + + if(!(count&0x10000 && (sd||md||pd||su))){ /* 指定なしなら位置関係から方向を求める */ + dx=target->x-src->x; dx=(dx>0)?1:((dx<0)?-1: 0); + dy=target->y-src->y; dy=(dy>0)?1:((dy<0)?-1: 0); + } + if(dx==0 && dy==0){ + int dir=battle_get_dir(target); + if(dir>=0 && dir<8){ + dx=-dirx[dir]; + dy=-diry[dir]; + } + } + + ret=path_blownpos(target->m,x,y,dx,dy,count&0xffff); + nx=ret>>16; + ny=ret&0xffff; + moveblock=( x/BLOCK_SIZE != nx/BLOCK_SIZE || y/BLOCK_SIZE != ny/BLOCK_SIZE); + + if(count&0x20000) { + battle_stopwalking(target,1); + if(sd){ + sd->to_x=nx; + sd->to_y=ny; + sd->walktimer = 1; + clif_walkok(sd); + clif_movechar(sd); + } + else if(md) { + md->to_x=nx; + md->to_y=ny; + prev_state = md->state.state; + md->state.state = MS_WALK; + clif_fixmobpos(md); + } + else if(pd) { + pd->to_x=nx; + pd->to_y=ny; + prev_state = pd->state.state; + pd->state.state = MS_WALK; + clif_fixpetpos(pd); + } + } + else + battle_stopwalking(target,2); + + dx = nx - x; + dy = ny - y; + + if(sd) /* 画面外に出たので消去 */ + map_foreachinmovearea(clif_pcoutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,0,sd); + else if(md) + map_foreachinmovearea(clif_moboutsight,target->m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,md); + 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(su){ + skill_unit_move_unit_group(su->group,target->m,dx,dy); + }else{ +// struct status_change *sc_data=battle_get_sc_data(target); + if(moveblock) map_delblock(target); + target->x=nx; + target->y=ny; + if(moveblock) map_addblock(target); +/*ダンス中にエフェクトは移動しないらしい + if(sc_data && sc_data[SC_DANCING].timer!=-1){ //対象がダンス中なのでエフェクトも移動 + struct skill_unit_group *sg=(struct skill_unit_group *)sc_data[SC_DANCING].val2; + if(sg) + skill_unit_move_unit_group(sg,target->m,dx,dy); + } +*/ + } + + if(sd) { /* 画面内に入ってきたので表示 */ + map_foreachinmovearea(clif_pcinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,0,sd); + if(count&0x20000) + sd->walktimer = -1; + } + else if(md) { + map_foreachinmovearea(clif_mobinsight,target->m,nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE,-dx,-dy,BL_PC,md); + if(count&0x20000) + md->state.state = prev_state; + } + 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) + pd->state.state = prev_state; + } + + skill_unit_move(target,gettick(),(count&0xffff)+7); /* スキルユニットの判定 */ + + return 0; +} + + +/* + * ========================================================================= + * スキル攻撃効果処理まとめ + * flagの説明。16進図 + * 00XRTTff + * ff = magicで計算に渡される) + * TT = パケットのtype部分(0でデフォルト) + * X = パケットのスキルLv + * R = 予約(skill_area_subで使用する) + *------------------------------------------------------------------------- + */ + +int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc, + struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ) +{ + struct Damage dmg; + struct status_change *sc_data; + int type,lv,damage; + + rdamage = 0; + nullpo_retr(0, src); + nullpo_retr(0, dsrc); + nullpo_retr(0, bl); + + sc_data = battle_get_sc_data(bl); + +//何もしない判定ここから + if(dsrc->m != bl->m) //対象が同じマップにいなければ何もしない + return 0; + if(src->prev == NULL || dsrc->prev == NULL || bl->prev == NULL) //prevよくわからない※ + return 0; + if(src->type == BL_PC && pc_isdead((struct map_session_data *)src)) //術者?がPCですでに死んでいたら何もしない + return 0; + if(dsrc->type == BL_PC && pc_isdead((struct map_session_data *)dsrc)) //術者?がPCですでに死んでいたら何もしない + return 0; + if(bl->type == BL_PC && pc_isdead((struct map_session_data *)bl)) //対象がPCですでに死んでいたら何もしない + return 0; + if(skillnotok(skillid, (struct map_session_data *) bl)) + return 0; // [MouseJstr] + if(sc_data && sc_data[SC_HIDING].timer != -1) { //ハイディング状態で + if(skill_get_pl(skillid) != 2) //スキルの属性が地属性でなければ何もしない + return 0; + } + if(sc_data && sc_data[SC_TRICKDEAD].timer != -1) //死んだふり中は何もしない + return 0; + if(skillid == WZ_STORMGUST) { //使用スキルがストームガストで + if(sc_data && sc_data[SC_FREEZE].timer != -1) //凍結状態なら何もしない + return 0; + } + if(skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //使用スキルがフロストノヴァで、dsrcとblが同じ場所なら何もしない + return 0; + if(src->type == BL_PC && ((struct map_session_data *)src)->chatID) //術者がPCでチャット中なら何もしない + return 0; + if(dsrc->type == BL_PC && ((struct map_session_data *)dsrc)->chatID) //術者がPCでチャット中なら何もしない + return 0; + if(src->type == BL_PC && bl && mob_gvmobcheck(((struct map_session_data *)src),bl)==0) + return 0; + +//何もしない判定ここまで + + type=-1; + lv=(flag>>20)&0xf; + dmg=battle_calc_attack(attack_type,src,bl,skillid,skilllv,flag&0xff ); //ダメージ計算 + +//マジックロッド処理ここから + if(attack_type&BF_MAGIC && sc_data && sc_data[SC_MAGICROD].timer != -1 && src == dsrc) { //魔法攻撃でマジックロッド状態でsrc=dsrcなら + dmg.damage = dmg.damage2 = 0; //ダメージ0 + if(bl->type == BL_PC) { //対象がPCの場合 + int sp = skill_get_sp(skillid,skilllv); //使用されたスキルのSPを吸収 + sp = sp * sc_data[SC_MAGICROD].val2 / 100; //吸収率計算 + if(skillid == WZ_WATERBALL && skilllv > 1) //ウォーターボールLv1以上 + sp = sp/((skilllv|1)*(skilllv|1)); //さらに計算? + if(sp > 0x7fff) sp = 0x7fff; //SP多すぎの場合は理論最大値 + else if(sp < 1) sp = 1; //1以下の場合は1 + if(((struct map_session_data *)bl)->status.sp + sp > ((struct map_session_data *)bl)->status.max_sp) { //回復SP+現在のSPがMSPより大きい場合 + sp = ((struct map_session_data *)bl)->status.max_sp - ((struct map_session_data *)bl)->status.sp; //SPをMSP-現在SPにする + ((struct map_session_data *)bl)->status.sp = ((struct map_session_data *)bl)->status.max_sp; //現在のSPにMSPを代入 + } + else //回復SP+現在のSPがMSPより小さい場合は回復SPを加算 + ((struct map_session_data *)bl)->status.sp += sp; + clif_heal(((struct map_session_data *)bl)->fd,SP_SP,sp); //SP回復エフェクトの表示 + ((struct map_session_data *)bl)->canact_tick = tick + skill_delayfix(bl, skill_get_delay(SA_MAGICROD,sc_data[SC_MAGICROD].val1)); // + } + clif_skill_nodamage(bl,bl,SA_MAGICROD,sc_data[SC_MAGICROD].val1,1); //マジックロッドエフェクトを表示 + } +//マジックロッド処理ここまで + + if(src->type==BL_PET) { // [Valaris] + dmg.damage=battle_attr_fix(skilllv, skill_get_pl(skillid), battle_get_element(bl) ); + dmg.damage2=0; + } + + damage = dmg.damage + dmg.damage2; + + if(lv==15) + lv=-1; + + if( flag&0xff00 ) + type=(flag&0xff00)>>8; + + if(damage <= 0 || damage < dmg.div_) //吹き飛ばし判定?※ + dmg.blewcount = 0; + + if(skillid == CR_GRANDCROSS) {//グランドクロス + if(battle_config.gx_disptype) dsrc = src; // 敵ダメージ白文字表示 + if( src == bl) type = 4; // 反動はダメージモーションなし + } + +//使用者がPCの場合の処理ここから + if(src->type == BL_PC) { + struct map_session_data *sd = (struct map_session_data *)src; + nullpo_retr(0, sd); +//連打掌(MO_CHAINCOMBO)ここから + if(skillid == MO_CHAINCOMBO) { + int delay = 1000 - 4 * battle_get_agi(src) - 2 * battle_get_dex(src); //基本ディレイの計算 + if(damage < battle_get_hp(bl)) { //ダメージが対象のHPより小さい場合 + if(pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0) //猛龍拳(MO_COMBOFINISH)取得&気球保持時は+300ms + delay += 300 * battle_config.combo_delay_rate /100; //追加ディレイをconfにより調整 + + skill_status_change_start(src,SC_COMBO,MO_CHAINCOMBO,skilllv,0,0,delay,0); //コンボ状態に + } + sd->attackabletime = sd->canmove_tick = tick + delay; + clif_combo_delay(src,delay); //コンボディレイパケットの送信 + } +//連打掌(MO_CHAINCOMBO)ここまで +//猛龍拳(MO_COMBOFINISH)ここから + else if(skillid == MO_COMBOFINISH) { + int delay = 700 - 4 * battle_get_agi(src) - 2 * battle_get_dex(src); + if(damage < battle_get_hp(bl)) { + //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms + //伏虎拳(CH_TIGERFIST)取得時も+300ms + if((pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) || + (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0) || + (pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)) + delay += 300 * battle_config.combo_delay_rate /100; //追加ディレイをconfにより調整 + + skill_status_change_start(src,SC_COMBO,MO_COMBOFINISH,skilllv,0,0,delay,0); //コンボ状態に + } + sd->attackabletime = sd->canmove_tick = tick + delay; + clif_combo_delay(src,delay); //コンボディレイパケットの送信 + } +//猛龍拳(MO_COMBOFINISH)ここまで +//伏虎拳(CH_TIGERFIST)ここから + else if(skillid == CH_TIGERFIST) { + int delay = 1000 - 4 * battle_get_agi(src) - 2 * battle_get_dex(src); + if(damage < battle_get_hp(bl)) { + if(pc_checkskill(sd, CH_CHAINCRUSH) > 0) //連柱崩撃(CH_CHAINCRUSH)取得時は+300ms + delay += 300 * battle_config.combo_delay_rate /100; //追加ディレイをconfにより調整 + + skill_status_change_start(src,SC_COMBO,CH_TIGERFIST,skilllv,0,0,delay,0); //コンボ状態に + } + sd->attackabletime = sd->canmove_tick = tick + delay; + clif_combo_delay(src,delay); //コンボディレイパケットの送信 + } +//伏虎拳(CH_TIGERFIST)ここまで +//連柱崩撃(CH_CHAINCRUSH)ここから + else if(skillid == CH_CHAINCRUSH) { + int delay = 1000 - 4 * battle_get_agi(src) - 2 * battle_get_dex(src); + if(damage < battle_get_hp(bl)) { + //阿修羅覇凰拳(MO_EXTREMITYFIST)取得&気球4個保持&爆裂波動(MO_EXPLOSIONSPIRITS)状態時は+300ms + if(pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc_data[SC_EXPLOSIONSPIRITS].timer != -1) + delay += 300 * battle_config.combo_delay_rate /100; //追加ディレイをconfにより調整 + + skill_status_change_start(src,SC_COMBO,CH_CHAINCRUSH,skilllv,0,0,delay,0); //コンボ状態に + } + sd->attackabletime = sd->canmove_tick = tick + delay; + clif_combo_delay(src,delay); //コンボディレイパケットの送信 + } +//連柱崩撃(CH_CHAINCRUSH)ここまで + } +//使用者がPCの場合の処理ここまで +//武器スキル?ここから + //AppleGirl Was Here + if(attack_type&BF_MAGIC && damage > 0 && src != bl && src == dsrc) { //Blah Blah + if(bl->type == BL_PC) { //Blah Blah + struct map_session_data *tsd = (struct map_session_data *)bl; + if(tsd->magic_damage_return > 0) { //More Blah + rdamage += damage * tsd->magic_damage_return / 100; + if(rdamage < 1) rdamage = 1; + } + } + } + //Stop Here + if(attack_type&BF_WEAPON && damage > 0 && src != bl && src == dsrc) { //武器スキル&ダメージあり&使用者と対象者が違う&src=dsrc + if(dmg.flag&BF_SHORT) { //近距離攻撃時?※ + if(bl->type == BL_PC) { //対象がPCの時 + struct map_session_data *tsd = (struct map_session_data *)bl; + nullpo_retr(0, tsd); + if(tsd->short_weapon_damage_return > 0) { //近距離攻撃跳ね返し?※ + rdamage += damage * tsd->short_weapon_damage_return / 100; + if(rdamage < 1) rdamage = 1; + } + } + if(sc_data && sc_data[SC_REFLECTSHIELD].timer != -1) { //リフレクトシールド時 + rdamage += damage * sc_data[SC_REFLECTSHIELD].val2 / 100; //跳ね返し計算 + if(rdamage < 1) rdamage = 1; + } + } + else if(dmg.flag&BF_LONG) { //遠距離攻撃時?※ + if(bl->type == BL_PC) { //対象がPCの時 + struct map_session_data *tsd = (struct map_session_data *)bl; + nullpo_retr(0, tsd); + if(tsd->long_weapon_damage_return > 0) { //遠距離攻撃跳ね返し?※ + rdamage += damage * tsd->long_weapon_damage_return / 100; + if(rdamage < 1) rdamage = 1; + } + } + } + if(rdamage > 0) + clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0); + } +//武器スキル?ここまで + + switch(skillid){ + case WZ_SIGHTRASHER: + clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, 5); + break; + case AS_SPLASHER: + clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); + break; + case NPC_SELFDESTRUCTION: + case NPC_SELFDESTRUCTION2: + break; + default: + clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, (skillid==0)? 5:type ); + } + if(dmg.blewcount > 0 && !map[src->m].flag.gvg) { /* 吹き飛ばし処理とそのパケット */ + if(skillid == WZ_SIGHTRASHER) + skill_blown(src,bl,dmg.blewcount); + else + skill_blown(dsrc,bl,dmg.blewcount); + if(bl->type == BL_MOB) + clif_fixmobpos((struct mob_data *)bl); + else if(bl->type == BL_PET) + clif_fixpetpos((struct pet_data *)bl); + else + clif_fixpos(bl); + } + + map_freeblock_lock(); + /* 実際にダメージ処理を行う */ + if(skillid != KN_BOWLINGBASH || flag) + battle_damage(src,bl,damage,0); + if(skillid == RG_INTIMIDATE && damage > 0 && !(battle_get_mode(bl)&0x20) && !map[src->m].flag.gvg ) { + int s_lv = battle_get_lv(src),t_lv = battle_get_lv(bl); + int rate = 50 + skilllv * 5; + rate = rate + (s_lv - t_lv); + if(rand()%100 < rate) + skill_addtimerskill(src,tick + 800,bl->id,0,0,skillid,skilllv,0,flag); + } + if(damage > 0 && dmg.flag&BF_SKILL && bl->type==BL_PC && pc_checkskill((struct map_session_data *)bl,RG_PLAGIARISM)){ + struct map_session_data *tsd = (struct map_session_data *)bl; + nullpo_retr(0, tsd); + if(!tsd->status.skill[skillid].id && !tsd->status.skill[skillid].id + && !(skillid > NPC_PIERCINGATT && skillid < NPC_SUMMONMONSTER) ){ + //既に盗んでいるスキルがあれば該当スキルを消す + if (tsd->cloneskill_id && tsd->cloneskill_lv && tsd->status.skill[tsd->cloneskill_id].flag==13){ + tsd->status.skill[tsd->cloneskill_id].id=0; + tsd->status.skill[tsd->cloneskill_id].lv=0; + tsd->status.skill[tsd->cloneskill_id].flag=0; + } + tsd->cloneskill_id=skillid; + tsd->cloneskill_lv=skilllv; + tsd->status.skill[skillid].id=skillid; + tsd->status.skill[skillid].lv=(pc_checkskill(tsd,RG_PLAGIARISM) > skill_get_max(skillid))? + skill_get_max(skillid):pc_checkskill(tsd,RG_PLAGIARISM); + tsd->status.skill[skillid].flag=13;//cloneskill flag + clif_skillinfoblock(tsd); + } + } + /* ダメージがあるなら追加効果判定 */ + if(bl->prev != NULL){ + struct map_session_data *sd = (struct map_session_data *)bl; + nullpo_retr(0, sd); + if( bl->type != BL_PC || (sd && !pc_isdead(sd)) ) { + if(damage > 0) + skill_additional_effect(src,bl,skillid,skilllv,attack_type,tick); + if(bl->type==BL_MOB && src!=bl) /* スキル使用条件のMOBスキル */ + { + struct mob_data *md=(struct mob_data *)bl; + nullpo_retr(0, md); + if(battle_config.mob_changetarget_byskill == 1) + { + int target; + target=md->target_id; + if(src->type == BL_PC) + md->target_id=src->id; + mobskill_use(md,tick,MSC_SKILLUSED|(skillid<<16)); + md->target_id=target; + } + else + mobskill_use(md,tick,MSC_SKILLUSED|(skillid<<16)); + } + } + } + + if(src->type == BL_PC && dmg.flag&BF_WEAPON && src != bl && src == dsrc && damage > 0) { + struct map_session_data *sd = (struct map_session_data *)src; + int hp = 0,sp = 0; + nullpo_retr(0, sd); + if(sd->hp_drain_rate && sd->hp_drain_per > 0 && dmg.damage > 0 && rand()%100 < sd->hp_drain_rate) { + hp += (dmg.damage * sd->hp_drain_per)/100; + if(sd->hp_drain_rate > 0 && hp < 1) hp = 1; + else if(sd->hp_drain_rate < 0 && hp > -1) hp = -1; + } + if(sd->hp_drain_rate_ && sd->hp_drain_per_ > 0 && dmg.damage2 > 0 && rand()%100 < sd->hp_drain_rate_) { + hp += (dmg.damage2 * sd->hp_drain_per_)/100; + if(sd->hp_drain_rate_ > 0 && hp < 1) hp = 1; + else if(sd->hp_drain_rate_ < 0 && hp > -1) hp = -1; + } + if(sd->sp_drain_rate > 0 && sd->sp_drain_per > 0 && dmg.damage > 0 && rand()%100 < sd->sp_drain_rate) { + sp += (dmg.damage * sd->sp_drain_per)/100; + if(sd->sp_drain_rate > 0 && sp < 1) sp = 1; + else if(sd->sp_drain_rate < 0 && sp > -1) sp = -1; + } + if(sd->sp_drain_rate_ > 0 && sd->sp_drain_per_ > 0 && dmg.damage2 > 0 && rand()%100 < sd->sp_drain_rate_) { + sp += (dmg.damage2 * sd->sp_drain_per_)/100; + if(sd->sp_drain_rate_ > 0 && sp < 1) sp = 1; + else if(sd->sp_drain_rate_ < 0 && sp > -1) sp = -1; + } + if(hp || sp) pc_heal(sd,hp,sp); + } + + if((skillid != KN_BOWLINGBASH || flag) && rdamage > 0) + battle_damage(bl,src,rdamage,0); + + if(attack_type&BF_WEAPON && sc_data && sc_data[SC_AUTOCOUNTER].timer != -1 && sc_data[SC_AUTOCOUNTER].val4 > 0) { + if(sc_data[SC_AUTOCOUNTER].val3 == dsrc->id) + battle_weapon_attack(bl,dsrc,tick,0x8000|sc_data[SC_AUTOCOUNTER].val1); + skill_status_change_end(bl,SC_AUTOCOUNTER,-1); + } + + map_freeblock_unlock(); + + return (dmg.damage+dmg.damage2); /* 与ダメを返す */ +} + +/*========================================== + * スキル範囲攻撃用(map_foreachinareaから呼ばれる) + * flagについて:16進図を確認 + * MSB <- 00fTffff ->LSB + * T =ターゲット選択用(BCT_*) + * ffff=自由に使用可能 + * 0 =予約。0に固定 + *------------------------------------------ + */ +static int skill_area_temp[8]; /* 一時変数。必要なら使う。 */ +typedef int (*SkillFunc)(struct block_list *,struct block_list *,int,int,unsigned int,int); +int skill_area_sub( struct block_list *bl,va_list ap ) +{ + struct block_list *src; + int skill_id,skill_lv,flag; + unsigned int tick; + SkillFunc func; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + if(bl->type!=BL_PC && bl->type!=BL_MOB && bl->type!=BL_SKILL) + return 0; + + src=va_arg(ap,struct block_list *); //ここではsrcの値を参照していないのでNULLチェックはしない + skill_id=va_arg(ap,int); + skill_lv=va_arg(ap,int); + tick=va_arg(ap,unsigned int); + flag=va_arg(ap,int); + func=va_arg(ap,SkillFunc); + + if(battle_check_target(src,bl,flag) > 0) + func(src,bl,skill_id,skill_lv,tick,flag); + return 0; +} + +static int skill_check_unit_range_sub( struct block_list *bl,va_list ap ) +{ + struct skill_unit *unit; + int *c,x,y,range,sx[4],sy[4]; + int t_range,tx[4],ty[4]; + int i,r_flag,skillid; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, unit = (struct skill_unit *)bl); + nullpo_retr(0, c = va_arg(ap,int *)); + + if(bl->prev == NULL || bl->type != BL_SKILL) + return 0; + + if(!unit->alive) + return 0; + + x = va_arg(ap,int); + y = va_arg(ap,int); + range = va_arg(ap,int); + skillid = va_arg(ap,int); + + if(skillid == MG_SAFETYWALL || skillid == AL_PNEUMA) { + if(unit->group->unit_id != 0x7e && unit->group->unit_id != 0x85) + return 0; + } + else if(skillid == AL_WARP) { + if((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) && unit->group->unit_id != 0x92) + return 0; + } + else if((skillid >= HT_SKIDTRAP && skillid <= HT_CLAYMORETRAP) || skillid == HT_TALKIEBOX) { + if((unit->group->unit_id < 0x8f || unit->group->unit_id > 0x99) && unit->group->unit_id != 0x92) + return 0; + } + else if(skillid == WZ_FIREPILLAR) { + if(unit->group->unit_id != 0x87) + return 0; + } + else return 0; + t_range=(unit->range!=0)? unit->range:unit->group->range; + tx[0] = tx[3] = unit->bl.x - t_range; + tx[1] = tx[2] = unit->bl.x + t_range; + ty[0] = ty[1] = unit->bl.y - t_range; + ty[2] = ty[3] = unit->bl.y + t_range; + sx[0] = sx[3] = x - range; + sx[1] = sx[2] = x + range; + sy[0] = sy[1] = y - range; + sy[2] = sy[3] = y + range; + for(i=r_flag=0;i<4;i++) { + if(sx[i] >= tx[0] && sx[i] <= tx[1] && sy[i] >= ty[0] && sy[i] <= ty[2]) { + r_flag = 1; + break; + } + if(tx[i] >= sx[0] && tx[i] <= sx[1] && ty[i] >= sy[0] && ty[i] <= sy[2]) { + r_flag = 1; + break; + } + } + if(r_flag) (*c)++; + + return 0; +} + +int skill_check_unit_range(int m,int x,int y,int range,int skillid) +{ + int c = 0; + + map_foreachinarea(skill_check_unit_range_sub,m,x-10,y-10,x+10,y+10,BL_SKILL,&c,x,y,range,skillid); + + return c; +} + +static int skill_check_unit_range2_sub( struct block_list *bl,va_list ap ) +{ + int *c; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, c = va_arg(ap,int *)); + + if(bl->prev == NULL || (bl->type != BL_PC && bl->type != BL_MOB)) + return 0; + + if(bl->type == BL_PC && pc_isdead((struct map_session_data *)bl)) + return 0; + + (*c)++; + + return 0; +} + +int skill_check_unit_range2(int m,int x,int y,int range) +{ + int c = 0; + + map_foreachinarea(skill_check_unit_range2_sub,m,x-range,y-range,x+range,y+range,0,&c); + + return c; +} + +/*========================================================================= + * 範囲スキル使用処理小分けここから + */ +/* 対象の数をカウントする。(skill_area_temp[0]を初期化しておくこと) */ +int skill_area_sub_count(struct block_list *src,struct block_list *target,int skillid,int skilllv,unsigned int tick,int flag) +{ + if(skill_area_temp[0] < 0xffff) + skill_area_temp[0]++; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static 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->prev == NULL) + return 0; + + 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(skl->target_id) { + struct block_list tbl; + target = map_id2bl(skl->target_id); + if(skl->skill_id == RG_INTIMIDATE) { + if(target == NULL) { + target = &tbl; //初期化してないのにアドレス突っ込んでいいのかな? + target->type = BL_NUL; + target->m = src->m; + target->prev = target->next = NULL; + } + } + if(target == NULL) + return 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 TF_BACKSLIDING: + clif_skill_nodamage(src,src,skl->skill_id,skl->skill_lv,1); + break; + case RG_INTIMIDATE: + if(sd && !map[src->m].flag.noteleport) { + int x,y,i,j,c; + pc_randomwarp(sd,3); + for(i=0;i<16;i++) { + j = rand()%8; + x = sd->bl.x + dirx[j]; + y = sd->bl.y + diry[j]; + if((c=map_getcell(sd->bl.m,x,y)) != 1 && c != 5) + break; + } + if(i >= 16) { + x = sd->bl.x; + y = sd->bl.y; + } + if(target->prev != NULL) { + if(target->type == BL_PC && !pc_isdead((struct map_session_data *)target)) + pc_setpos((struct map_session_data *)target,map[sd->bl.m].name,x,y,3); + else if(target->type == BL_MOB) + mob_warp((struct mob_data *)target,-1,x,y,3); + } + } + else if(md && !map[src->m].flag.monster_noteleport) { + int x,y,i,j,c; + mob_warp(md,-1,-1,-1,3); + for(i=0;i<16;i++) { + j = rand()%8; + x = md->bl.x + dirx[j]; + y = md->bl.y + diry[j]; + if((c=map_getcell(md->bl.m,x,y)) != 1 && c != 5) + break; + } + if(i >= 16) { + x = md->bl.x; + y = md->bl.y; + } + if(target->prev != NULL) { + if(target->type == BL_PC && !pc_isdead((struct map_session_data *)target)) + pc_setpos((struct map_session_data *)target,map[md->bl.m].name,x,y,3); + else if(target->type == BL_MOB) + mob_warp((struct mob_data *)target,-1,x,y,3); + } + } + break; + + case BA_FROSTJOKE: /* 寒いジョーク */ + case DC_SCREAM: /* スクリーム */ + range=15; //視界全体 + map_foreachinarea(skill_frostjoke_scream,src->m,src->x-range,src->y-range, + src->x+range,src->y+range,0,src,skl->skill_id,skl->skill_lv,tick); + break; + + default: + skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); + break; + } + } + else { + if(src->m != skl->map) + return 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,0); + clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick); + } + else + skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,0); + break; + } + } + + 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; + + 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); + for(i=0;i<MAX_SKILLTIMERSKILL;i++) { + if(sd->skilltimerskill[i].timer != -1) { + delete_timer(sd->skilltimerskill[i].timer, skill_timerskill); + sd->skilltimerskill[i].timer = -1; + } + } + } + else if(src->type == BL_MOB) { + struct mob_data *md = (struct mob_data *)src; + nullpo_retr(0, md); + for(i=0;i<MAX_MOBSKILLTIMERSKILL;i++) { + if(md->skilltimerskill[i].timer != -1) { + delete_timer(md->skilltimerskill[i].timer, skill_timerskill); + md->skilltimerskill[i].timer = -1; + } + } + } + + return 0; +} + +/* 範囲スキル使用処理小分けここまで + * ------------------------------------------------------------------------- + */ + +/*========================================== + * スキル使用(詠唱完了、ID指定攻撃系) + * (スパゲッティに向けて1歩前進!(ダメポ)) + *------------------------------------------ + */ +int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ) +{ + struct map_session_data *sd=NULL; + int i; + + nullpo_retr(1, src); + nullpo_retr(1, bl); + + if(src->type==BL_PC) + sd=(struct map_session_data *)src; + if(sd && pc_isdead(sd)) + return 1; + + if((skillid == WZ_SIGHTRASHER || skillid == CR_GRANDCROSS) && src != bl) + bl = src; + if(bl->prev == NULL) + return 1; + if(bl->type == BL_PC && pc_isdead((struct map_session_data *)bl)) + return 1; + map_freeblock_lock(); + switch(skillid) + { + /* 武器攻撃系スキル */ + case SM_BASH: /* バッシュ */ + case MC_MAMMONITE: /* メマーナイト */ + case AC_DOUBLE: /* ダブルストレイフィング */ + case AS_SONICBLOW: /* ソニックブロー */ + case KN_PIERCE: /* ピアース */ + case KN_SPEARBOOMERANG: /* スピアブーメラン */ + case TF_POISON: /* インベナム */ + case TF_SPRINKLESAND: /* 砂まき */ + case AC_CHARGEARROW: /* チャージアロー */ + case KN_SPEARSTAB: /* スピアスタブ */ + case RG_RAID: /* サプライズアタック */ + case RG_INTIMIDATE: /* インティミデイト */ + case BA_MUSICALSTRIKE: /* ミュージカルストライク */ + case DC_THROWARROW: /* 矢撃ち */ + case BA_DISSONANCE: /* 不協和音 */ + case CR_HOLYCROSS: /* ホーリークロス */ + case CR_SHIELDCHARGE: + case CR_SHIELDBOOMERANG: + + /* 以下MOB専用 */ + /* 単体攻撃、SP減少攻撃、遠距離攻撃、防御無視攻撃、多段攻撃 */ + case NPC_PIERCINGATT: + case NPC_MENTALBREAKER: + case NPC_RANGEATTACK: + case NPC_CRITICALSLASH: + case NPC_COMBOATTACK: + /* 必中攻撃、毒攻撃、暗黒攻撃、沈黙攻撃、スタン攻撃 */ + case NPC_GUIDEDATTACK: + case NPC_POISON: + case NPC_BLINDATTACK: + case NPC_SILENCEATTACK: + case NPC_STUNATTACK: + /* 石化攻撃、呪い攻撃、睡眠攻撃、ランダムATK攻撃 */ + case NPC_PETRIFYATTACK: + case NPC_CURSEATTACK: + case NPC_SLEEPATTACK: + case NPC_RANDOMATTACK: + /* 水属性攻撃、地属性攻撃、火属性攻撃、風属性攻撃 */ + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + /* 毒属性攻撃、聖属性攻撃、闇属性攻撃、念属性攻撃、SP減少攻撃 */ + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + case LK_AURABLADE: /* オーラブレード */ + case LK_SPIRALPIERCE: /* スパイラルピアース */ + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + case LK_JOINTBEAT: /* ジョイントビート */ + case PA_PRESSURE: /* プレッシャー */ + case PA_SACRIFICE: /* サクリファイス */ + case SN_SHARPSHOOTING: /* シャープシューティング */ + case CG_ARROWVULCAN: /* アローバルカン */ + case ASC_BREAKER: /* ソウルブレーカー */ + case HW_MAGICCRASHER: /* マジッククラッシャー */ + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + case NPC_DARKBREATH: + clif_emotion(src,7); + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); + break; + case MO_INVESTIGATE: /* 発勁 */ + { + struct status_change *sc_data = battle_get_sc_data(src); + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if(sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end(src,SC_BLADESTOP,-1); + } + break; + case SN_FALCONASSAULT: /* ファルコンアサルト */ + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); + break; + case KN_BRANDISHSPEAR: /* ブランディッシュスピア */ + { + struct mob_data *md = (struct mob_data *)bl; + nullpo_retr(1, md); + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if(md->hp > 0){ + skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)); + if(bl->type == BL_MOB) + clif_fixmobpos((struct mob_data *)bl); + else if(bl->type == BL_PET) + clif_fixpetpos((struct pet_data *)bl); + else + clif_fixpos(bl); + } + } + break; + case RG_BACKSTAP: /* バックスタブ */ + { + int dir = map_calc_dir(src,bl->x,bl->y),t_dir = battle_get_dir(bl); + int dist = distance(src->x,src->y,bl->x,bl->y); + if((dist > 0 && !map_check_dir(dir,t_dir)) || bl->type == BL_SKILL) { + struct status_change *sc_data = battle_get_sc_data(src); + if(sc_data && sc_data[SC_HIDING].timer != -1) + skill_status_change_end(src, SC_HIDING, -1); // ハイディング解除 + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)); + } + else if(src->type == BL_PC) + clif_skill_fail(sd,sd->skillid,0,0); + } + break; + + case AM_ACIDTERROR: /* アシッドテラー */ + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if(bl->type == BL_PC && rand()%100 < skill_get_time(skillid,skilllv) && battle_config.equipment_breaking) + pc_breakarmor((struct map_session_data *)bl); + break; + case MO_FINGEROFFENSIVE: /* 指弾 */ + { + struct status_change *sc_data = battle_get_sc_data(src); + + if(!battle_config.finger_offensive_type) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else { + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if(sd) { + for(i=1;i<sd->spiritball_old;i++) + skill_addtimerskill(src,tick+i*200,bl->id,0,0,skillid,skilllv,BF_WEAPON,flag); + sd->canmove_tick = tick + (sd->spiritball_old-1)*200; + } + } + if(sc_data && sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end(src,SC_BLADESTOP,-1); + } + break; + case MO_CHAINCOMBO: /* 連打掌 */ + { + struct status_change *sc_data = battle_get_sc_data(src); + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if(sc_data && sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end(src,SC_BLADESTOP,-1); + } + break; + case MO_COMBOFINISH: /* 猛龍拳 */ + case CH_TIGERFIST: /* 伏虎拳 */ + case CH_CHAINCRUSH: /* 連柱崩撃 */ + case CH_PALMSTRIKE: /* 猛虎硬派山 */ + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + case MO_EXTREMITYFIST: /* 阿修羅覇鳳拳 */ + { + struct status_change *sc_data = battle_get_sc_data(src); + + if(sd) { + struct walkpath_data wpd; + int dx,dy; + + dx = bl->x - sd->bl.x; + dy = bl->y - sd->bl.y; + if(dx > 0) dx++; + else if(dx < 0) dx--; + if(dy > 0) dy++; + else if(dy < 0) dy--; + if(dx == 0 && dy == 0) dx++; + if(path_search(&wpd,src->m,sd->bl.x,sd->bl.y,sd->bl.x+dx,sd->bl.y+dy,1) == -1) { + dx = bl->x - sd->bl.x; + dy = bl->y - sd->bl.y; + if(path_search(&wpd,src->m,sd->bl.x,sd->bl.y,sd->bl.x+dx,sd->bl.y+dy,1) == -1) { + clif_skill_fail(sd,sd->skillid,0,0); + break; + } + } + sd->to_x = sd->bl.x + dx; + sd->to_y = sd->bl.y + dy; + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + clif_walkok(sd); + clif_movechar(sd); + if(dx < 0) dx = -dx; + if(dy < 0) dy = -dy; + sd->attackabletime = sd->canmove_tick = tick + 100 + sd->speed * ((dx > dy)? dx:dy); + if(sd->canact_tick < sd->canmove_tick) + sd->canact_tick = sd->canmove_tick; + pc_movepos(sd,sd->to_x,sd->to_y); + skill_status_change_end(&sd->bl,SC_COMBO,-1); + } + else + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + skill_status_change_end(src, SC_EXPLOSIONSPIRITS, -1); + if(sc_data && sc_data[SC_BLADESTOP].timer != -1) + skill_status_change_end(src,SC_BLADESTOP,-1); + } + break; + /* 武器系範囲攻撃スキル */ + case AC_SHOWER: /* アローシャワー */ + case SM_MAGNUM: /* マグナムブレイク */ + case AS_GRIMTOOTH: /* グリムトゥース */ + case MC_CARTREVOLUTION: /* カートレヴォリューション */ + case NPC_SPLASHATTACK: /* スプラッシュアタック */ + case ASC_METEORASSAULT: /* メテオアサルト */ + case AS_SPLASHER: /* [Valaris] */ + if(flag&1){ + /* 個別にダメージを与える */ + if(bl->id!=skill_area_temp[1]){ + int dist=0; + if(skillid==SM_MAGNUM){ /* マグナムブレイクなら中心からの距離を計算 */ + int dx=abs( bl->x - skill_area_temp[2] ); + int dy=abs( bl->y - skill_area_temp[3] ); + dist=((dx>dy)?dx:dy); + } + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick, + 0x0500|dist ); + } + }else{ + int ar=1; + int x=bl->x,y=bl->y; + if( skillid==SM_MAGNUM){ + x=src->x; + y=src->y; + }else if(skillid==AC_SHOWER || skillid==ASC_METEORASSAULT) /* アローシャワー、メテオアサルト範囲5*5 */ + ar=2; + else if(skillid==AS_SPLASHER) /* ベナムスプラッシャー範囲3*3 */ + ar=1; + else if(skillid==NPC_SPLASHATTACK) /* スプラッシュアタックは範囲7*7 */ + ar=3; + skill_area_temp[1]=bl->id; + skill_area_temp[2]=x; + skill_area_temp[3]=y; + /* まずターゲットに攻撃を加える */ + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0); + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea(skill_area_sub, + bl->m,x-ar,y-ar,x+ar,y+ar,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + case KN_BOWLINGBASH: /* ボウリングバッシュ */ + if(flag&1){ + /* 個別にダメージを与える */ + if(bl->id!=skill_area_temp[1]) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500); + } + else { + int damage; + map_freeblock_lock(); + damage = skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0); + if(damage > 0) { + int i,c; /* 他人から聞いた動きなので間違ってる可能性大&効率が悪いっす>< */ + c = skill_get_blewcount(skillid,skilllv); + if(map[bl->m].flag.gvg) c = 0; + for(i=0;i<c;i++){ + skill_blown(src,bl,1); + if(bl->type == BL_MOB) + clif_fixmobpos((struct mob_data *)bl); + else if(bl->type == BL_PET) + clif_fixpetpos((struct pet_data *)bl); + else + clif_fixpos(bl); + skill_area_temp[0]=0; + map_foreachinarea(skill_area_sub, + bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY , + skill_area_sub_count); + if(skill_area_temp[0]>1) break; + } + skill_area_temp[1]=bl->id; + skill_area_temp[2]=bl->x; + skill_area_temp[3]=bl->y; + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea(skill_area_sub, + bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + battle_damage(src,bl,damage,1); + if(rdamage > 0) + battle_damage(bl,src,rdamage,0); + } + map_freeblock_unlock(); + } + break; + + case ALL_RESURRECTION: /* リザレクション */ + case PR_TURNUNDEAD: /* ターンアンデッド */ + if(bl->type != BL_PC && battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + else { + map_freeblock_unlock(); + return 1; + } + break; + + /* 魔法系スキル */ + case MG_SOULSTRIKE: /* ソウルストライク */ + case MG_COLDBOLT: /* コールドボルト */ + case MG_FIREBOLT: /* ファイアーボルト */ + case MG_LIGHTNINGBOLT: /* ライトニングボルト */ + case WZ_EARTHSPIKE: /* アーススパイク */ + case AL_HEAL: /* ヒール */ + case AL_HOLYLIGHT: /* ホーリーライト */ + case MG_FROSTDIVER: /* フロストダイバー */ + case WZ_JUPITEL: /* ユピテルサンダー */ + case NPC_MAGICALATTACK: /* MOB:魔法打撃攻撃 */ + case PR_ASPERSIO: /* アスペルシオ */ + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + + case WZ_WATERBALL: /* ウォーターボール */ + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + if(skilllv>1) + skill_status_change_start(src,SC_WATERBALL,skilllv,bl->id,0,0,0,0); + break; + + case PR_BENEDICTIO: /* 聖体降福 */ + if(battle_get_race(bl)==1 || battle_get_race(bl)==6) + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + + /* 魔法系範囲攻撃スキル */ + case MG_NAPALMBEAT: /* ナパームビート */ + case MG_FIREBALL: /* ファイヤーボール */ + if(flag&1){ + /* 個別にダメージを与える */ + if(bl->id!=skill_area_temp[1]){ + if(skillid==MG_FIREBALL){ /* ファイヤーボールなら中心からの距離を計算 */ + int dx=abs( bl->x - skill_area_temp[2] ); + int dy=abs( bl->y - skill_area_temp[3] ); + skill_area_temp[0]=((dx>dy)?dx:dy); + } + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, + skill_area_temp[0]| 0x0500); + } + }else{ + int ar=(skillid==MG_NAPALMBEAT)?1:2; + skill_area_temp[1]=bl->id; + if(skillid==MG_NAPALMBEAT){ /* ナパームでは先に数える */ + skill_area_temp[0]=0; + map_foreachinarea(skill_area_sub, + bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY , + skill_area_sub_count); + }else{ + skill_area_temp[0]=0; + skill_area_temp[2]=bl->x; + skill_area_temp[3]=bl->y; + } + /* まずターゲットに攻撃を加える */ + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, + skill_area_temp[0] ); + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea(skill_area_sub, + bl->m,bl->x-ar,bl->y-ar,bl->x+ar,bl->y+ar,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + case HW_NAPALMVULCAN: // Fixed By SteelViruZ + if(flag&1){ + if(bl->id!=skill_area_temp[1]){ + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, + skill_area_temp[0]); + } + }else{ + int ar=(skillid==HW_NAPALMVULCAN)?1:2; + skill_area_temp[1]=bl->id; + if(skillid==HW_NAPALMVULCAN){ + skill_area_temp[0]=0; + map_foreachinarea(skill_area_sub, + bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY , + skill_area_sub_count); + }else{ + skill_area_temp[0]=0; + skill_area_temp[2]=bl->x; + skill_area_temp[3]=bl->y; + } + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, + skill_area_temp[0] ); + map_foreachinarea(skill_area_sub, + bl->m,bl->x-ar,bl->y-ar,bl->x+ar,bl->y+ar,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + case WZ_FROSTNOVA: /* フロストノヴァ */ + skill_castend_pos2(src,bl->x,bl->y,skillid,skilllv,tick,0); + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + + case WZ_SIGHTRASHER: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_castend_pos2(src,bl->x,bl->y,skillid,skilllv,tick,0); + skill_status_change_end(src,SC_SIGHT,-1); + break; + + /* その他 */ + case HT_BLITZBEAT: /* ブリッツビート */ + if(flag&1){ + /* 個別にダメージを与える */ + if(bl->id!=skill_area_temp[1]) + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,skill_area_temp[0]|(flag&0xf00000)); + }else{ + skill_area_temp[0]=0; + skill_area_temp[1]=bl->id; + if(flag&0xf00000) + map_foreachinarea(skill_area_sub,bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY ,skill_area_sub_count); + /* まずターゲットに攻撃を加える */ + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,skill_area_temp[0]|(flag&0xf00000)); + /* その後ターゲット以外の範囲内の敵全体に処理を行う */ + map_foreachinarea(skill_area_sub, + bl->m,bl->x-1,bl->y-1,bl->x+1,bl->y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + case CR_GRANDCROSS: /* グランドクロス */ + /* スキルユニット配置 */ + skill_castend_pos2(src,bl->x,bl->y,skillid,skilllv,tick,0); + if(sd) + sd->canmove_tick = tick + 1000; + else if(src->type == BL_MOB) + mob_changestate((struct mob_data *)src,MS_DELAY,1000); + break; + + case TF_THROWSTONE: /* 石投げ */ + case NPC_SMOKING: /* スモーキング */ + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,0 ); + break; + + case NPC_SELFDESTRUCTION: /* 自爆 */ + case NPC_SELFDESTRUCTION2: /* 自爆2 */ + if(flag&1){ + /* 個別にダメージを与える */ + if(src->type==BL_MOB){ + struct mob_data* mb = (struct mob_data*)src; + nullpo_retr(1, mb); + mb->hp=skill_area_temp[2]; + if(bl->id!=skill_area_temp[1]) + skill_attack(BF_MISC,src,src,bl,NPC_SELFDESTRUCTION,skilllv,tick,flag ); + mb->hp=1; + } + }else{ + struct mob_data *md; + if((md=(struct mob_data *)src)){ + skill_area_temp[1]=bl->id; + skill_area_temp[2]=battle_get_hp(src); + clif_skill_nodamage(src,src,NPC_SELFDESTRUCTION,-1,1); + map_foreachinarea(skill_area_sub, + bl->m,bl->x-5,bl->y-5,bl->x+5,bl->y+5,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + battle_damage(src,src,md->hp,0); + } + } + break; + + /* HP吸収/HP吸収魔法 */ + case NPC_BLOODDRAIN: + case NPC_ENERGYDRAIN: + { + int heal; + heal = skill_attack((skillid==NPC_BLOODDRAIN)?BF_WEAPON:BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + if( heal > 0 ){ + struct block_list tbl; + tbl.id = 0; + tbl.m = src->m; + tbl.x = src->x; + tbl.y = src->y; + clif_skill_nodamage(&tbl,src,AL_HEAL,heal,1); + battle_heal(NULL,src,heal,0,0); + } + } + break; + case 0: + if(sd) { + if(flag&3){ + if(bl->id!=skill_area_temp[1]) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500); + } + else{ + int ar=sd->splash_range; + skill_area_temp[1]=bl->id; + map_foreachinarea(skill_area_sub, + bl->m, bl->x - ar, bl->y - ar, bl->x + ar, bl->y + ar, 0, + src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, + skill_castend_damage_id); + } + } + break; + + default: + map_freeblock_unlock(); + return 1; + } + map_freeblock_unlock(); + + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、ID指定支援系) + *------------------------------------------ + */ +int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ) +{ + struct map_session_data *sd=NULL; + struct map_session_data *dstsd=NULL; + struct mob_data *md=NULL; + struct mob_data *dstmd=NULL; + int i,abra_skillid=0,abra_skilllv; + int sc_def_vit,sc_def_mdef,strip_fix,strip_time,strip_per; + int sc_dex,sc_luk; + //クラスチェンジ用ボスモンスターID + int changeclass[]={1038,1039,1046,1059,1086,1087,1112,1115 + ,1157,1159,1190,1272,1312,1373,1492}; + int poringclass[]={1002}; + + nullpo_retr(1, src); + nullpo_retr(1, bl); + + if(src->type==BL_PC) + sd=(struct map_session_data *)src; + else if(src->type==BL_MOB) + md=(struct mob_data *)src; + + sc_dex=battle_get_mdef(bl); + sc_luk=battle_get_luk(bl); + sc_def_vit = 100 - (3 + battle_get_vit(bl) + battle_get_luk(bl)/3); + sc_def_vit = 100 - (3 + battle_get_vit(bl) + battle_get_luk(bl)/3); + sc_def_mdef = 100 - (3 + battle_get_mdef(bl) + battle_get_luk(bl)/3); + strip_fix = battle_get_dex(src) - battle_get_dex(bl); + + if(bl->type==BL_PC){ + nullpo_retr(1, dstsd=(struct map_session_data *)bl); + }else if(bl->type==BL_MOB){ + nullpo_retr(1, dstmd=(struct mob_data *)bl); + if(sc_def_vit>50) + sc_def_vit=50; + if(sc_def_mdef>50) + sc_def_mdef=50; + } + if(sc_def_vit < 0) + sc_def_vit=0; + if(sc_def_mdef < 0) + sc_def_mdef=0; + if(strip_fix < 0) + strip_fix=0; + + if(bl == NULL || bl->prev == NULL) + return 1; + if(sd && pc_isdead(sd)) + return 1; + if(dstsd && pc_isdead(dstsd) && skillid != ALL_RESURRECTION) + return 1; + if(battle_get_class(bl) == 1288) + return 1; + if (skillnotok(skillid, (struct map_session_data *)bl)) // [MouseJstr] + return 0; + + map_freeblock_lock(); + switch(skillid) + { + case AL_HEAL: /* ヒール */ + { + int heal=skill_calc_heal( src, skilllv ); + int heal_get_jobexp; + int skill; + struct pc_base_job s_class; + + if( dstsd && dstsd->special_state.no_magic_damage ) + heal=0; /* 黄金蟲カード(ヒール量0) */ + if (sd){ + s_class = pc_calc_base_job(sd->status.class); + if((skill=pc_checkskill(sd,HP_MEDITATIO))>0) // メディテイティオ + heal += heal*(skill*2/100); + if(sd && dstsd && sd->status.partner_id == dstsd->status.char_id && s_class.job == 23 && sd->status.sex == 0) //自分も対象もPC、対象が自分のパートナー、自分がスパノビ、自分が♀なら + heal = heal*2; //スパノビの嫁が旦那にヒールすると2倍になる + } + + + clif_skill_nodamage(src,bl,skillid,heal,1); + heal_get_jobexp = battle_heal(NULL,bl,heal,0,0); + + // JOB経験値獲得 + if(src->type == BL_PC && bl->type==BL_PC && heal > 0 && src != bl && battle_config.heal_exp > 0){ + heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100; + if(heal_get_jobexp <= 0) + heal_get_jobexp = 1; + pc_gainexp((struct map_session_data *)src,0,heal_get_jobexp); + } + } + break; + + case ALL_RESURRECTION: /* リザレクション */ + if(bl->type==BL_PC){ + int per=0; + struct map_session_data *tsd = (struct map_session_data*)bl; + nullpo_retr(1, tsd); + if( (map[bl->m].flag.pvp) && tsd->pvp_point<0 ) + break; /* PVPで復活不可能状態 */ + + if(pc_isdead(tsd)){ /* 死亡判定 */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + switch(skilllv){ + case 1: per=10; break; + case 2: per=30; break; + case 3: per=50; break; + case 4: per=80; break; + } + tsd->status.hp=tsd->status.max_hp*per/100; + if(tsd->status.hp<=0) tsd->status.hp=1; + if(tsd->special_state.restart_full_recover ){ /* オシリスカード */ + tsd->status.hp=tsd->status.max_hp; + tsd->status.sp=tsd->status.max_sp; + } + pc_setstand(tsd); + if(battle_config.pc_invincible_time > 0) + pc_setinvincibletimer(tsd,battle_config.pc_invincible_time); + clif_updatestatus(tsd,SP_HP); + clif_resurrection(&tsd->bl,1); + if(src != bl && sd && battle_config.resurrection_exp > 0) { + int exp = 0,jexp = 0; + int lv = tsd->status.base_level - sd->status.base_level, jlv = tsd->status.job_level - sd->status.job_level; + if(lv > 0) { + exp = (int)((double)tsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); + if(exp < 1) exp = 1; + } + if(jlv > 0) { + jexp = (int)((double)tsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); + if(jexp < 1) jexp = 1; + } + if(exp > 0 || jexp > 0) + pc_gainexp(sd,exp,jexp); + } + } + } + break; + + case AL_DECAGI: /* 速度減少 */ + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if( rand()%100 < (50+skilllv*3+(battle_get_lv(src)+battle_get_int(src)/5)-sc_def_mdef) ) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0); + } + break; + + case AL_CRUCIS: + if(flag&1) { + int race = battle_get_race(bl),ele = battle_get_elem_type(bl); + if(battle_check_target(src,bl,BCT_ENEMY) && (race == 6 || battle_check_undead(race,ele))) { + int slv=battle_get_lv(src),tlv=battle_get_lv(bl),rate; + rate = 25 + skilllv*2 + slv - tlv; + if(rand()%100 < rate) + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,0,0); + } + } + else { + int range = 15; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinarea(skill_area_sub, + src->m,src->x-range,src->y-range,src->x+range,src->y+range,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_nodamage_id); + } + break; + + case PR_LEXDIVINA: /* レックスディビーナ */ + { + struct status_change *sc_data = battle_get_sc_data(bl); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if(sc_data && sc_data[SC_DIVINA].timer != -1) + skill_status_change_end(bl,SC_DIVINA,-1); + else if( rand()%100 < sc_def_vit ) { + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0); + } + } + break; + case SA_ABRACADABRA: + //require 1 yellow gemstone even with mistress card or Into the Abyss + if (pc_search_inventory(sd, 715) <= 0 ) { + clif_skill_fail(sd,sd->skillid,0,0); + break; + } + pc_delitem(sd, pc_search_inventory(sd, 715), 1, 0); + // + do{ + abra_skillid=skill_abra_dataset(skilllv); + }while(abra_skillid == 0); + abra_skilllv=skill_get_max(abra_skillid)>pc_checkskill(sd,SA_ABRACADABRA)?pc_checkskill(sd,SA_ABRACADABRA):skill_get_max(abra_skillid); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + sd->skillitem=abra_skillid; + sd->skillitemlv=abra_skilllv; + clif_item_skill(sd,abra_skillid,abra_skilllv,"アブラカダブラ"); + break; + case SA_COMA: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if(dstsd){ + dstsd->status.hp=1; + dstsd->status.sp=1; + clif_updatestatus(dstsd,SP_HP); + clif_updatestatus(dstsd,SP_SP); + } + if(dstmd) dstmd->hp=1; + break; + case SA_FULLRECOVERY: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if(dstsd) pc_heal(dstsd,dstsd->status.max_hp,dstsd->status.max_sp); + if(dstmd) dstmd->hp=battle_get_max_hp(&dstmd->bl); + break; + case SA_SUMMONMONSTER: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (sd) mob_once_spawn(sd,map[sd->bl.m].name,sd->bl.x,sd->bl.y,"--ja--",-1,1,""); + break; + case SA_LEVELUP: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd,pc_nextbaseexp(sd)*10/100,0); + break; + + case SA_INSTANTDEATH: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (sd) pc_damage(NULL,sd,sd->status.max_hp); + break; + + case SA_QUESTION: + case SA_GRAVITY: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + case SA_CLASSCHANGE: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(dstmd) mob_class_change(dstmd,changeclass); + break; + case SA_MONOCELL: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(dstmd) mob_class_change(dstmd,poringclass); + break; + case SA_DEATH: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (dstsd) pc_damage(NULL,dstsd,dstsd->status.max_hp); + if (dstmd) mob_damage(NULL,dstmd,dstmd->hp,1); + break; + case SA_REVERSEORCISH: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (dstsd) pc_setoption(dstsd,dstsd->status.option|0x0800); + break; + case SA_FORTUNE: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(sd) pc_getzeny(sd,battle_get_lv(bl)*100); + break; + case SA_TAMINGMONSTER: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (dstmd){ + for(i=0;i<MAX_PET_DB;i++){ + if(dstmd->class == pet_db[i].class){ + pet_catch_process1(sd,dstmd->class); + break; + } + } + } + break; + case AL_INCAGI: /* 速度増加 */ + case AL_BLESSING: /* ブレッシング */ + case PR_SLOWPOISON: + case PR_IMPOSITIO: /* イムポシティオマヌス */ + case PR_LEXAETERNA: /* レックスエーテルナ */ + case PR_SUFFRAGIUM: /* サフラギウム */ + case PR_BENEDICTIO: /* 聖体降福 */ + case CR_PROVIDENCE: /* プロヴィデンス */ + case CG_MARIONETTE: /* マリオネットコントロール */ + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + }else{ + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris] + case SA_FROSTWEAPON: + case SA_LIGHTNINGLOADER: + case SA_SEISMICWEAPON: + if(bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ){ + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + if(bl->type==BL_PC) { + struct map_session_data *sd2=(struct map_session_data *)bl; + if(sd2->status.weapon==0 || sd2->sc_data[SC_FLAMELAUNCHER].timer!=-1 || sd2->sc_data[SC_FROSTWEAPON].timer!=-1 || + sd2->sc_data[SC_LIGHTNINGLOADER].timer!=-1 || sd2->sc_data[SC_SEISMICWEAPON].timer!=-1 || + sd2->sc_data[SC_ENCPOISON].timer!=-1) { + clif_skill_fail(sd,skillid,0,0); + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + } + if(rand()%100 > (75+skilllv*1) && (skilllv != 5)) { + clif_skill_fail(sd,skillid,0,0); + clif_skill_nodamage(src,bl,skillid,skilllv,0); + if(bl->type==BL_PC && battle_config.equipment_breaking) { + struct map_session_data *sd2=(struct map_session_data *)bl; + if(sd!=sd2) clif_displaymessage(sd->fd,"You broke target's weapon"); + pc_breakweapon(sd2); + } + break; + } + else { + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case PR_ASPERSIO: /* アスペルシオ */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if(bl->type==BL_MOB) + break; + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + case PR_KYRIE: /* キリエエレイソン */ + clif_skill_nodamage(bl,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + case KN_AUTOCOUNTER: /* オートカウンター */ + case KN_TWOHANDQUICKEN: /* ツーハンドクイッケン */ + case CR_SPEARQUICKEN: /* スピアクイッケン */ + case CR_REFLECTSHIELD: + case AS_POISONREACT: /* ポイズンリアクト */ + case MC_LOUD: /* ラウドボイス */ + case MG_ENERGYCOAT: /* エナジーコート */ + case SM_ENDURE: /* インデュア */ + case MG_SIGHT: /* サイト */ + case AL_RUWACH: /* ルアフ */ + case MO_EXPLOSIONSPIRITS: // 爆裂波動 + case MO_STEELBODY: // 金剛 + case LK_AURABLADE: /* オーラブレード */ + case LK_PARRYING: /* パリイング */ + case LK_CONCENTRATION: /* コンセントレーション */ + case LK_BERSERK: /* バーサーク */ + case HP_ASSUMPTIO: /* */ + case WS_CARTBOOST: /* カートブースト */ + case SN_SIGHT: /* トゥルーサイト */ + case WS_MELTDOWN: /* メルトダウン */ + case ST_REJECTSWORD: /* リジェクトソード */ + case HW_MAGICPOWER: /* 魔法力増幅 */ + case PF_MEMORIZE: /* メモライズ */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + case AS_ENCHANTPOISON: // Prevent spamming [Valaris] + if(bl->type==BL_PC) { + struct map_session_data *sd2=(struct map_session_data *)bl; + if(sd2->sc_data[SC_FLAMELAUNCHER].timer!=-1 || sd2->sc_data[SC_FROSTWEAPON].timer!=-1 || + sd2->sc_data[SC_LIGHTNINGLOADER].timer!=-1 || sd2->sc_data[SC_SEISMICWEAPON].timer!=-1 || + sd2->sc_data[SC_ENCPOISON].timer!=-1) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + clif_skill_fail(sd,skillid,0,0); + break; + } + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + case LK_TENSIONRELAX: /* テンションリラックス */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + pc_setsit(sd); + clif_sitting(sd->fd,sd); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + case MC_CHANGECART: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + case AC_CONCENTRATION: /* 集中力向上 */ + { + int range = 1; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + map_foreachinarea( skill_status_change_timer_sub, + src->m, src->x-range, src->y-range, src->x+range,src->y+range,0, + src,SkillStatusChangeTable[skillid],tick); + } + break; + case SM_PROVOKE: /* プロボック */ + { + struct status_change *sc_data = battle_get_sc_data(bl); + + /* MVPmobと不死には効かない */ + if((bl->type==BL_MOB && battle_get_mode(bl)&0x20) || battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) //不死には効かない + { + map_freeblock_unlock(); + return 1; + } + + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + + if(dstmd && dstmd->skilltimer!=-1 && dstmd->state.skillcastcancel) // 詠唱妨害 + skill_castcancel(bl,0); + if(dstsd && dstsd->skilltimer!=-1 && (!dstsd->special_state.no_castcancel || map[bl->m].flag.gvg) + && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2) + skill_castcancel(bl,0); + + if(sc_data){ + if(sc_data[SC_FREEZE].timer!=-1) + skill_status_change_end(bl,SC_FREEZE,-1); + if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0) + skill_status_change_end(bl,SC_STONE,-1); + if(sc_data[SC_SLEEP].timer!=-1) + skill_status_change_end(bl,SC_SLEEP,-1); + } + + if(bl->type==BL_MOB) { + int range = skill_get_range(skillid,skilllv); + if(range < 0) + range = battle_get_range(src) - (range + 1); + mob_target((struct mob_data *)bl,src,range); + } + } + break; + + case CR_DEVOTION: /* ディボーション */ + if(sd && dstsd){ + //転生や養子の場合の元の職業を算出する + + int lv = sd->status.base_level-dstsd->status.base_level; + lv = (lv<0)?-lv:lv; + if((dstsd->bl.type!=BL_PC) // 相手はPCじゃないとだめ + ||(sd->bl.id == dstsd->bl.id) // 相手が自分はだめ + ||(lv > 10) // レベル差±10まで + ||(!sd->status.party_id && !sd->status.guild_id) // PTにもギルドにも所属無しはだめ + ||((sd->status.party_id != dstsd->status.party_id) // 同じパーティーか、 + ||(sd->status.guild_id != dstsd->status.guild_id)) // 同じギルドじゃないとだめ + ||(dstsd->status.class==14||dstsd->status.class==21 + ||dstsd->status.class==4015||dstsd->status.class==4022)){ // クルセだめ + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + for(i=0;i<skilllv;i++){ + if(!sd->dev.val1[i]){ // 空きがあったら入れる + sd->dev.val1[i] = bl->id; + sd->dev.val2[i] = bl->id; + break; + }else if(i==skilllv-1){ // 空きがなかった + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + clif_devotion(sd,bl->id); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],src->id,1,0,0,1000*(15+15*skilllv),0 ); + } + else clif_skill_fail(sd,skillid,0,0); + break; + case MO_CALLSPIRITS: // 気功 + if(sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + pc_addspiritball(sd,skill_get_time(skillid,skilllv),skilllv); + } + break; + case CH_SOULCOLLECT: // 狂気功 + if(sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + for(i=0;i<5;i++) + pc_addspiritball(sd,skill_get_time(skillid,skilllv),5); + } + break; + case MO_BLADESTOP: // 白刃取り + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(src,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + case MO_ABSORBSPIRITS: // 気奪 + i=0; + if(sd && dstsd) { + if(sd == dstsd || map[sd->bl.m].flag.pvp || map[sd->bl.m].flag.gvg) { + if(dstsd->spiritball > 0) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + i = dstsd->spiritball * 7; + pc_delspiritball(dstsd,dstsd->spiritball,0); + if(i > 0x7FFF) + i = 0x7FFF; + if(sd->status.sp + i > sd->status.max_sp) + i = sd->status.max_sp - sd->status.sp; + } + } + }else if(sd && dstmd){ //対象がモンスターの場合 + //20%の確率で対象のLv*2のSPを回復する。成功したときはターゲット(σ゚Д゚)σゲッツ!! + if(rand()%100<20){ + i=2*mob_db[dstmd->class].lv; + mob_target(dstmd,src,0); + } + } + if(i){ + sd->status.sp += i; + clif_heal(sd->fd,SP_SP,i); + } + else + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + + case AC_MAKINGARROW: /* 矢作成 */ + if(sd) { + clif_arrow_create_list(sd); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case AM_PHARMACY: /* ポーション作成 */ + if(sd) { + clif_skill_produce_mix_list(sd,32); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + case WS_CREATECOIN: /* クリエイトコイン */ + if(sd) { + clif_skill_produce_mix_list(sd,64); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + case WS_CREATENUGGET: /* 塊製造 */ + if(sd) { + clif_skill_produce_mix_list(sd,128); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + case BS_HAMMERFALL: /* ハンマーフォール */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_weapon_damage ) + break; + if( rand()%100 < (20+ 10*skilllv)*sc_def_vit/100 ) { + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + } + break; + + case RG_RAID: /* サプライズアタック */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + { + int x=bl->x,y=bl->y; + skill_area_temp[1]=bl->id; + skill_area_temp[2]=x; + skill_area_temp[3]=y; + map_foreachinarea(skill_area_sub, + bl->m,x-1,y-1,x+1,y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + skill_status_change_end(src, SC_HIDING, -1); // ハイディング解除 + break; + + case KN_BRANDISHSPEAR: /*ブランディッシュスピア*/ + { + int c,n=4,ar; + int dir = map_calc_dir(src,bl->x,bl->y); + struct square tc; + int x=bl->x,y=bl->y; + ar=skilllv/3; + skill_brandishspear_first(&tc,dir,x,y); + skill_brandishspear_dir(&tc,dir,4); + /* 範囲C */ + if(skilllv == 10){ + for(c=1;c<4;c++){ + map_foreachinarea(skill_area_sub, + bl->m,tc.val1[c],tc.val2[c],tc.val1[c],tc.val2[c],0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|n, + skill_castend_damage_id); + } + } + /* 範囲BA */ + if(skilllv > 6){ + skill_brandishspear_dir(&tc,dir,-1); + n--; + }else{ + skill_brandishspear_dir(&tc,dir,-2); + n-=2; + } + + if(skilllv > 3){ + for(c=0;c<5;c++){ + map_foreachinarea(skill_area_sub, + bl->m,tc.val1[c],tc.val2[c],tc.val1[c],tc.val2[c],0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|n, + skill_castend_damage_id); + if(skilllv > 6 && n==3 && c==4){ + skill_brandishspear_dir(&tc,dir,-1); + n--;c=-1; + } + } + } + /* 範囲@ */ + for(c=0;c<10;c++){ + if(c==0||c==5) skill_brandishspear_dir(&tc,dir,-1); + map_foreachinarea(skill_area_sub, + bl->m,tc.val1[c%5],tc.val2[c%5],tc.val1[c%5],tc.val2[c%5],0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + } + break; + + /* パーティスキル */ + case AL_ANGELUS: /* エンジェラス */ + case PR_MAGNIFICAT: /* マグニフィカート */ + case PR_GLORIA: /* グロリア */ + case SN_WINDWALK: /* ウインドウォーク */ + if(sd == NULL || sd->status.party_id==0 || (flag&1) ){ + /* 個別の処理 */ + clif_skill_nodamage(bl,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0); + } + else{ + /* パーティ全体への処理 */ + party_foreachsamemap(skill_area_sub, + sd,1, + src,skillid,skilllv,tick, flag|BCT_PARTY|1, + skill_castend_nodamage_id); + } + break; + case BS_ADRENALINE: /* アドレナリンラッシュ */ + case BS_WEAPONPERFECT: /* ウェポンパーフェクション */ + case BS_OVERTHRUST: /* オーバートラスト */ + if(sd == NULL || sd->status.party_id==0 || (flag&1) ){ + /* 個別の処理 */ + clif_skill_nodamage(bl,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,(src == bl)? 1:0,0,0,skill_get_time(skillid,skilllv),0); + } + else{ + /* パーティ全体への処理 */ + party_foreachsamemap(skill_area_sub, + sd,1, + src,skillid,skilllv,tick, flag|BCT_PARTY|1, + skill_castend_nodamage_id); + } + break; + + /*(付加と解除が必要) */ + case BS_MAXIMIZE: /* マキシマイズパワー */ + case NV_TRICKDEAD: /* 死んだふり */ + case CR_DEFENDER: /* ディフェンダー */ + case CR_AUTOGUARD: /* オートガード */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + int sc=SkillStatusChangeTable[skillid]; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( tsc_data ){ + if( tsc_data[sc].timer==-1 ) + /* 付加する */ + skill_status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0); + else + /* 解除する */ + skill_status_change_end(bl, sc, -1); + } + } + break; + + case TF_HIDING: /* ハイディング */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + int sc=SkillStatusChangeTable[skillid]; + clif_skill_nodamage(src,bl,skillid,-1,1); + if( tsc_data ){ + if( tsc_data[sc].timer==-1 ) + /* 付加する */ + skill_status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0); + else + /* 解除する */ + skill_status_change_end(bl, sc, -1); + } + } + break; + + case AS_CLOAKING: /* クローキング */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + int sc=SkillStatusChangeTable[skillid]; + clif_skill_nodamage(src,bl,skillid,-1,1); + if( tsc_data ){ + if( tsc_data[sc].timer==-1 ) + /* 付加する */ + skill_status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0); + else + /* 解除する */ + skill_status_change_end(bl, sc, -1); + } + + skill_check_cloaking(bl); + } + break; + + case ST_CHASEWALK: /* ハイディング */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + int sc=SkillStatusChangeTable[skillid]; + clif_skill_nodamage(src,bl,skillid,-1,1); + if( tsc_data ){ + if( tsc_data[sc].timer==-1 ) + /* 付加する */ + skill_status_change_start(bl,sc,skilllv,0,0,0,skill_get_time(skillid,skilllv),0); + else + /* 解除する */ + skill_status_change_end(bl, sc, -1); + } + } + break; + + /* 対地スキル */ + case BD_LULLABY: /* 子守唄 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BA_DISSONANCE: /* 不協和音 */ + case BA_POEMBRAGI: /* ブラギの詩 */ + case BA_WHISTLE: /* 口笛 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + case BA_APPLEIDUN: /* イドゥンの林檎 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + case DC_HUMMING: /* ハミング */ + case DC_DONTFORGETME: /* 私を忘れないで… */ + case DC_FORTUNEKISS: /* 幸運のキス */ + case DC_SERVICEFORYOU: /* サービスフォーユー */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); + break; + + case HP_BASILICA: /* バジリカ */ + case PA_GOSPEL: /* ゴスペル */ + skill_clear_unitgroup(src); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); + break; + + case BD_ADAPTATION: /* アドリブ */ + { + struct status_change *sc_data = battle_get_sc_data(src); + if(sc_data && sc_data[SC_DANCING].timer!=-1){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_stop_dancing(src,0); + } + } + break; + + case BA_FROSTJOKE: /* 寒いジョーク */ + case DC_SCREAM: /* スクリーム */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_addtimerskill(src,tick+3000,bl->id,0,0,skillid,skilllv,0,flag); + break; + + case TF_STEAL: // スティール + if(sd) { + if(pc_steal_item(sd,bl)) + clif_skill_nodamage(src,bl,skillid,skilllv,1); + else + clif_skill_nodamage(src,bl,skillid,skilllv,0); + } + break; + + case RG_STEALCOIN: // スティールコイン + if(sd) { + if(pc_steal_coin(sd,bl)) { + int range = skill_get_range(skillid,skilllv); + if(range < 0) + range = battle_get_range(src) - (range + 1); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + mob_target((struct mob_data *)bl,src,range); + } + else + clif_skill_nodamage(src,bl,skillid,skilllv,0); + } + break; + + case MG_STONECURSE: /* ストーンカース */ + if (bl->type==BL_MOB && battle_get_mode(bl)&0x20) { + clif_skill_fail(sd,sd->skillid,0,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if( rand()%100 < skilllv*4+20 && !battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) + skill_status_change_start(bl,SC_STONE,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + else if(sd) + clif_skill_fail(sd,skillid,0,0); + break; + + case NV_FIRSTAID: /* 応急手当 */ + clif_skill_nodamage(src,bl,skillid,5,1); + battle_heal(NULL,bl,5,0,0); + break; + + case AL_CURE: /* キュアー */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + skill_status_change_end(bl, SC_SILENCE , -1 ); + skill_status_change_end(bl, SC_BLIND , -1 ); + skill_status_change_end(bl, SC_CONFUSION, -1 ); + if( battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl)) ){//アンデッドなら暗闇効果 + skill_status_change_start(bl, SC_CONFUSION,1,0,0,0,6000,0); + } + break; + + case TF_DETOXIFY: /* 解毒 */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_end(bl, SC_POISON , -1 ); + break; + + case PR_STRECOVERY: /* リカバリー */ + { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + skill_status_change_end(bl, SC_FREEZE , -1 ); + skill_status_change_end(bl, SC_STONE , -1 ); + skill_status_change_end(bl, SC_SLEEP , -1 ); + skill_status_change_end(bl, SC_STAN , -1 ); + if( battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl)) ){//アンデッドなら暗闇効果 + int blind_time; + //blind_time=30-battle_get_vit(bl)/10-battle_get_int/15; + blind_time=30*(100-(battle_get_int(bl)+battle_get_vit(bl))/2)/100; + if(rand()%100 < (100-(battle_get_int(bl)/2+battle_get_vit(bl)/3+battle_get_luk(bl)/10))) + skill_status_change_start(bl, SC_BLIND,1,0,0,0,blind_time,0); + } + if(dstmd){ + dstmd->attacked_id=0; + dstmd->target_id=0; + dstmd->state.targettype = NONE_ATTACKABLE; + dstmd->state.skillstate=MSS_IDLE; + dstmd->next_walktime=tick+rand()%3000+3000; + } + } + break; + + case WZ_ESTIMATION: /* モンスター情報 */ + if(src->type==BL_PC){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + clif_skill_estimation((struct map_session_data *)src,bl); + } + break; + + case MC_IDENTIFY: /* アイテム鑑定 */ + if(sd) + clif_item_identify_list(sd); + break; + + case BS_REPAIRWEAPON: /* 武器修理 */ + if(sd) +//動作しないのでとりあえずコメントアウト +// clif_item_repair_list(sd); + break; + + case MC_VENDING: /* 露店開設 */ + if(sd) + clif_openvendingreq(sd,2+sd->skilllv); + break; + + case AL_TELEPORT: /* テレポート */ + if( sd ){ + if(map[sd->bl.m].flag.noteleport){ /* テレポ禁止 */ + clif_skill_teleportmessage(sd,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( sd->skilllv==1 ) + clif_skill_warppoint(sd,sd->skillid,"Random","","",""); + else{ + clif_skill_warppoint(sd,sd->skillid,"Random", + sd->status.save_point.map,"",""); + } + }else if( bl->type==BL_MOB ) + mob_warp((struct mob_data *)bl,-1,-1,-1,3); + break; + + case AL_HOLYWATER: /* アクアベネディクタ */ + if(sd) { + int eflag; + struct item item_tmp; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = 523; + item_tmp.identify = 1; + if(battle_config.holywater_name_input) { + item_tmp.card[0] = 0xfe; + item_tmp.card[1] = 0; + *((unsigned long *)(&item_tmp.card[2]))=sd->char_id; /* キャラID */ + } + eflag = pc_additem(sd,&item_tmp,1); + if(eflag) { + clif_additem(sd,0,0,eflag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + break; + case TF_PICKSTONE: + if(sd) { + int eflag; + struct item item_tmp; + struct block_list tbl; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + memset(&item_tmp,0,sizeof(item_tmp)); + memset(&tbl,0,sizeof(tbl)); // [MouseJstr] + item_tmp.nameid = 7049; + item_tmp.identify = 1; + tbl.id = 0; + clif_takeitem(&sd->bl,&tbl); + eflag = pc_additem(sd,&item_tmp,1); + if(eflag) { + clif_additem(sd,0,0,eflag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + break; + + case RG_STRIPWEAPON: /* ストリップウェポン */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + + if(tsc_data && tsc_data[SC_CP_WEAPON].timer != -1 ) + break; + strip_per = 5+2*skilllv+strip_fix/5; + strip_time = skill_get_time(skillid,skilllv)+strip_fix/2; + if(rand()%100 < strip_per){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,strip_time,0 ); + if(dstsd){ + for(i=0;i<MAX_INVENTORY;i++){ + if(dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0002){ + pc_unequipitem(dstsd,i,0); + break; + } + } + } + } + } + break; + + case RG_STRIPSHIELD: /* ストリップシールド */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + + if(tsc_data && tsc_data[SC_CP_SHIELD].timer != -1 ) + break; + strip_per = 5+2*skilllv+strip_fix/5; + strip_time = skill_get_time(skillid,skilllv)+strip_fix/2; + if(rand()%100 < strip_per){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,strip_time,0 ); + if(dstsd){ + for(i=0;i<MAX_INVENTORY;i++){ + if(dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0020){ + pc_unequipitem(dstsd,i,0); + break; + } + } + } + } + } + break; + + case RG_STRIPARMOR: /* ストリップアーマー */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + + if(tsc_data && tsc_data[SC_CP_ARMOR].timer != -1 ) + break; + strip_per = 5+2*skilllv+strip_fix/5; + strip_time = skill_get_time(skillid,skilllv)+strip_fix/2; + if(rand()%100 < strip_per){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,strip_time,0 ); + if(dstsd){ + for(i=0;i<MAX_INVENTORY;i++){ + if(dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0010){ + pc_unequipitem(dstsd,i,0); + break; + } + } + } + } + } + break; + case RG_STRIPHELM: /* ストリップヘルム */ + { + struct status_change *tsc_data = battle_get_sc_data(bl); + + if(tsc_data && tsc_data[SC_CP_HELM].timer != -1 ) + break; + strip_per = 5+2*skilllv+strip_fix/5; + strip_time = skill_get_time(skillid,skilllv)+strip_fix/2; + if(rand()%100 < strip_per){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,strip_time,0 ); + if(dstsd){ + for(i=0;i<MAX_INVENTORY;i++){ + if(dstsd->status.inventory[i].equip && dstsd->status.inventory[i].equip & 0x0100){ + pc_unequipitem(dstsd,i,0); + break; + } + } + } + } + } + break; + /* PotionPitcher */ + case AM_POTIONPITCHER: /* ポーションピッチャー */ + { + struct block_list tbl; + int i,x,hp = 0,sp = 0; + if(sd) { + if(sd==dstsd) { // cancel use on oneself + map_freeblock_unlock(); + return 1; + } + x = skilllv%11 - 1; + i = pc_search_inventory(sd,skill_db[skillid].itemid[x]); + if(i < 0 || skill_db[skillid].itemid[x] <= 0) { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skillid].amount[x]) { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + sd->state.potionpitcher_flag = 1; + sd->potion_hp = sd->potion_sp = sd->potion_per_hp = sd->potion_per_sp = 0; + sd->skilltarget = bl->id; + run_script(sd->inventory_data[i]->use_script,0,sd->bl.id,0); + pc_delitem(sd,i,skill_db[skillid].amount[x],0); + sd->state.potionpitcher_flag = 0; + if(sd->potion_per_hp > 0 || sd->potion_per_sp > 0) { + hp = battle_get_max_hp(bl) * sd->potion_per_hp / 100; + hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)/100; + if(dstsd) { + sp = dstsd->status.max_sp * sd->potion_per_sp / 100; + sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER) + pc_checkskill(sd,AM_LEARNINGPOTION)*5)/100; + } + } + else { + if(sd->potion_hp > 0) { + hp = sd->potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)/100; + hp = hp * (100 + (battle_get_vit(bl)<<1)) / 100; + if(dstsd) + hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; + } + if(sd->potion_sp > 0) { + sp = sd->potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER) + pc_checkskill(sd,AM_LEARNINGPOTION)*5)/100; + sp = sp * (100 + (battle_get_int(bl)<<1)) / 100; + if(dstsd) + sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100; + } + } + } + else { + hp = (1 + rand()%400) * (100 + skilllv*10) / 100; + hp = hp * (100 + (battle_get_vit(bl)<<1)) / 100; + if(dstsd) + hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; + } + tbl.id = 0; + tbl.m = src->m; + tbl.x = src->x; + tbl.y = src->y; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(hp > 0 || (hp <= 0 && sp <= 0)) + clif_skill_nodamage(&tbl,bl,AL_HEAL,hp,1); + if(sp > 0) + clif_skill_nodamage(&tbl,bl,MG_SRECOVERY,sp,1); + battle_heal(src,bl,hp,sp,0); + } + break; + case AM_CP_WEAPON: + { + struct status_change *tsc_data = battle_get_sc_data(bl); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(tsc_data && tsc_data[SC_STRIPWEAPON].timer != -1) + skill_status_change_end(bl, SC_STRIPWEAPON, -1 ); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + } + break; + case AM_CP_SHIELD: + { + struct status_change *tsc_data = battle_get_sc_data(bl); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(tsc_data && tsc_data[SC_STRIPSHIELD].timer != -1) + skill_status_change_end(bl, SC_STRIPSHIELD, -1 ); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + } + break; + case AM_CP_ARMOR: + { + struct status_change *tsc_data = battle_get_sc_data(bl); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(tsc_data && tsc_data[SC_STRIPARMOR].timer != -1) + skill_status_change_end(bl, SC_STRIPARMOR, -1 ); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + } + break; + case AM_CP_HELM: + { + struct status_change *tsc_data = battle_get_sc_data(bl); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(tsc_data && tsc_data[SC_STRIPHELM].timer != -1) + skill_status_change_end(bl, SC_STRIPHELM, -1 ); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + } + break; + case SA_DISPELL: /* ディスペル */ + { + int i; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + for(i=0;i<136;i++){ + if(i==SC_RIDING || i== SC_FALCON || i==SC_HALLUCINATION || i==SC_WEIGHT50 + || i==SC_WEIGHT90 || i==SC_STRIPWEAPON || i==SC_STRIPSHIELD || i==SC_STRIPARMOR + || i==SC_STRIPHELM || i==SC_CP_WEAPON || i==SC_CP_SHIELD || i==SC_CP_ARMOR + || i==SC_CP_HELM || i==SC_COMBO) + continue; + skill_status_change_end(bl,i,-1); + } + } + break; + + case TF_BACKSLIDING: /* バックステップ */ + battle_stopwalking(src,1); + skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)|0x10000); + if(src->type == BL_MOB) + clif_fixmobpos((struct mob_data *)src); + else if(src->type == BL_PET) + clif_fixpetpos((struct pet_data *)src); + else if(src->type == BL_PC) + clif_fixpos(src); + skill_addtimerskill(src,tick + 200,src->id,0,0,skillid,skilllv,0,flag); + break; + + case SA_CASTCANCEL: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_castcancel(src,1); + if(sd) { + int sp = skill_get_sp(sd->skillid_old,sd->skilllv_old); + sp = sp * (90 - (skilllv-1)*20) / 100; + if(sp < 0) sp = 0; + pc_heal(sd,0,-sp); + } + break; + case SA_SPELLBREAKER: // スペルブレイカー + { + struct status_change *sc_data = battle_get_sc_data(bl); + int sp; + if(sc_data && sc_data[SC_MAGICROD].timer != -1) { + if(dstsd) { + sp = skill_get_sp(skillid,skilllv); + sp = sp * sc_data[SC_MAGICROD].val2 / 100; + if(sp > 0x7fff) sp = 0x7fff; + else if(sp < 1) sp = 1; + if(dstsd->status.sp + sp > dstsd->status.max_sp) { + sp = dstsd->status.max_sp - dstsd->status.sp; + dstsd->status.sp = dstsd->status.max_sp; + } + else + dstsd->status.sp += sp; + clif_heal(dstsd->fd,SP_SP,sp); + } + clif_skill_nodamage(bl,bl,SA_MAGICROD,sc_data[SC_MAGICROD].val1,1); + if(sd) { + sp = sd->status.max_sp/5; + if(sp < 1) sp = 1; + pc_heal(sd,0,-sp); + } + } + else { + int bl_skillid=0,bl_skilllv=0; + if(bl->type == BL_PC) { + if(dstsd && dstsd->skilltimer != -1) { + bl_skillid = dstsd->skillid; + bl_skilllv = dstsd->skilllv; + } + } + else if(bl->type == BL_MOB) { + if(dstmd && dstmd->skilltimer != -1) { + bl_skillid = dstmd->skillid; + bl_skilllv = dstmd->skilllv; + } + } + if(bl_skillid > 0 && skill_db[bl_skillid].skill_type == BF_MAGIC) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_castcancel(bl,0); + sp = skill_get_sp(bl_skillid,bl_skilllv); + if(dstsd) + pc_heal(dstsd,0,-sp); + if(sd) { + sp = sp*(25*(skilllv-1))/100; + if(skilllv > 1 && sp < 1) sp = 1; + if(sp > 0x7fff) sp = 0x7fff; + else if(sp < 1) sp = 1; + if(sd->status.sp + sp > sd->status.max_sp) { + sp = sd->status.max_sp - sd->status.sp; + sd->status.sp = sd->status.max_sp; + } + else + sd->status.sp += sp; + clif_heal(sd->fd,SP_SP,sp); + } + } + else if(sd) + clif_skill_fail(sd,skillid,0,0); + } + } + break; + case SA_MAGICROD: + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + case SA_AUTOSPELL: /* オートスペル */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(sd) + clif_autospell(sd,skilllv); + else { + int maxlv=1,spellid=0; + static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT }; + if(skilllv >= 10) { + spellid = MG_FROSTDIVER; + maxlv = skilllv - 9; + } + else if(skilllv >=8) { + spellid = MG_FIREBALL; + maxlv = skilllv - 7; + } + else if(skilllv >=5) { + spellid = MG_SOULSTRIKE; + maxlv = skilllv - 4; + } + else if(skilllv >=2) { + int i = rand()%3; + spellid = spellarray[i]; + maxlv = skilllv - 1; + } + else if(skilllv > 0) { + spellid = MG_NAPALMBEAT; + maxlv = 3; + } + if(spellid > 0) + skill_status_change_start(src,SC_AUTOSPELL,skilllv,spellid,maxlv,0, + skill_get_time(SA_AUTOSPELL,skilllv),0); + } + break; + + /* ランダム属性変化、水属性変化、地、火、風 */ + case NPC_ATTRICHANGE: + case NPC_CHANGEWATER: + case NPC_CHANGEGROUND: + case NPC_CHANGEFIRE: + case NPC_CHANGEWIND: + /* 毒、聖、念、闇 */ + case NPC_CHANGEPOISON: + case NPC_CHANGEHOLY: + case NPC_CHANGEDARKNESS: + case NPC_CHANGETELEKINESIS: + if(md){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + md->def_ele=skill_get_pl(skillid); + if(md->def_ele==0) /* ランダム変化、ただし、*/ + md->def_ele=rand()%10; /* 不死属性は除く */ + md->def_ele+=(1+rand()%4)*20; /* 属性レベルはランダム */ + } + break; + + case NPC_PROVOCATION: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(md) + clif_pet_performance(src,mob_db[md->class].skill[md->skillidx].val[0]); + break; + + case NPC_HALLUCINATION: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + break; + + case NPC_KEEPING: + case NPC_BARRIER: + { + int skill_time = skill_get_time(skillid,skilllv); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_time,0 ); + mob_changestate((struct mob_data *)src,MS_DELAY,skill_time); + } + break; + + case NPC_DARKBLESSING: + { + int sc_def = 100 - battle_get_mdef(bl); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if(battle_get_elem_type(bl) == 7 || battle_get_race(bl) == 6) + break; + if(rand()%100 < sc_def*(50+skilllv*5)/100) { + if(dstsd) { + int hp = battle_get_hp(bl)-1; + pc_heal(dstsd,-hp,0); + } + else if(dstmd) + dstmd->hp = 1; + } + } + break; + + case NPC_SELFDESTRUCTION: /* 自爆 */ + case NPC_SELFDESTRUCTION2: /* 自爆2 */ + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,0,0,skill_get_time(skillid,skilllv),0); + break; + case NPC_LICK: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_weapon_damage ) + break; + if(dstsd) + pc_heal(dstsd,0,-100); + if(rand()%100 < (skilllv*5)*sc_def_vit/100) + skill_status_change_start(bl,SC_STAN,skilllv,0,0,0,skill_get_time2(skillid,skilllv),0); + break; + + case NPC_SUICIDE: /* 自決 */ + if(src && bl && md){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + mob_damage(NULL,md,md->hp,0); + } + break; + + case NPC_SUMMONSLAVE: /* 手下召喚 */ + case NPC_SUMMONMONSTER: /* MOB召喚 */ + if(md && !md->master_id){ + mob_summonslave(md,mob_db[md->class].skill[md->skillidx].val,skilllv,(skillid==NPC_SUMMONSLAVE)?1:0); + } + break; + + case NPC_TRANSFORMATION: + case NPC_METAMORPHOSIS: + if(md) + mob_class_change(md,mob_db[md->class].skill[md->skillidx].val); + break; + + case NPC_EMOTION: /* エモーション */ + if(md) + clif_emotion(&md->bl,mob_db[md->class].skill[md->skillidx].val[0]); + break; + + case NPC_DEFENDER: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + case WE_MALE: /* 君だけは護るよ */ + if(sd && dstsd){ + int hp_rate=(skilllv <= 0)? 0:skill_db[skillid].hp_rate[skilllv-1]; + int gain_hp=sd->status.max_hp*abs(hp_rate)/100;// 15% + clif_skill_nodamage(src,bl,skillid,gain_hp,1); + battle_heal(NULL,bl,gain_hp,0,0); + } + break; + case WE_FEMALE: /* あなたの為に犠牲になります */ + if(sd && dstsd){ + int sp_rate=(skilllv <= 0)? 0:skill_db[skillid].sp_rate[skilllv-1]; + int gain_sp=sd->status.max_sp*abs(sp_rate)/100;// 15% + clif_skill_nodamage(src,bl,skillid,gain_sp,1); + battle_heal(NULL,bl,0,gain_sp,0); + } + break; + + case WE_CALLPARTNER: /* あなたに会いたい */ + if(sd && dstsd){ + if(map[sd->bl.m].flag.nomemo){ + clif_skill_teleportmessage(sd,1); + return 0; + } + if((dstsd = pc_get_partner(sd)) == NULL){ + clif_skill_fail(sd,skillid,0,0); + return 0; + } + skill_unitsetting(src,skillid,skilllv,sd->bl.x,sd->bl.y,0); + } + break; + + case PF_HPCONVERSION: /* ライフ置き換え */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(sd){ + int conv_hp=0,conv_sp=0; + conv_hp=sd->status.hp/10; //基本はHPの10% + sd->status.hp -= conv_hp; //HPを減らす + conv_sp=conv_hp*20*skilllv/100; + conv_sp=(sd->status.sp+conv_sp>sd->status.max_sp)?sd->status.max_sp-sd->status.sp:conv_sp; + sd->status.sp += conv_sp; //SPを増やす + pc_heal(sd,-conv_hp,conv_sp); + clif_heal(sd->fd,SP_SP,conv_sp); + } + break; + case HT_REMOVETRAP: /* リムーブトラップ */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + { + struct skill_unit *su=NULL; + struct item item_tmp; + int flag; + if((bl->type==BL_SKILL) && + (su=(struct skill_unit *)bl) && + (su->group->src_id == src->id || map[bl->m].flag.pvp || map[bl->m].flag.gvg) && + (su->group->unit_id >= 0x8f && su->group->unit_id <= 0x99) && + (su->group->unit_id != 0x92)){ //罠を取り返す + if(sd){ + if(battle_config.skill_removetrap_type == 1){ + for(i=0;i<10;i++) { + if(skill_db[su->group->skill_id].itemid[i] > 0){ + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = skill_db[su->group->skill_id].itemid[i]; + item_tmp.identify = 1; + if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i]))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + } + }else{ + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = 1065; + item_tmp.identify = 1; + if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + + } + if(su->group->unit_id == 0x91 && su->group->val2){ + struct block_list *target=map_id2bl(su->group->val2); + if(target && (target->type == BL_PC || target->type == BL_MOB)) + skill_status_change_end(target,SC_ANKLE,-1); + } + skill_delunit(su); + } + } + break; + case HT_SPRINGTRAP: /* スプリングトラップ */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + { + struct skill_unit *su=NULL; + if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){ + switch(su->group->unit_id){ + case 0x8f: /* ブラストマイン */ + case 0x90: /* スキッドトラップ */ + case 0x93: /* ランドマイン */ + case 0x94: /* ショックウェーブトラップ */ + case 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x97: /* フリージングトラップ */ + case 0x98: /* クレイモアートラップ */ + case 0x99: /* トーキーボックス */ + su->group->unit_id = 0x8c; + clif_changelook(bl,LOOK_BASE,su->group->unit_id); + su->group->limit=DIFF_TICK(tick+1500,su->group->tick); + su->limit=DIFF_TICK(tick+1500,su->group->tick); + } + } + } + break; + case BD_ENCORE: /* アンコール */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(sd) + skill_use_id(sd,src->id,sd->skillid_dance,sd->skilllv_dance); + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + if((double)battle_get_max_hp(bl)*2/3 < battle_get_hp(bl)) //HPが2/3以上残っていたら失敗 + return 1; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,skillid,src->id,0,skill_get_time(skillid,skilllv),0 ); + break; + case PF_MINDBREAKER: /* プロボック */ + { + struct status_change *sc_data = battle_get_sc_data(bl); + + /* MVPmobと不死には効かない */ + if((bl->type==BL_MOB && battle_get_mode(bl)&0x20) || battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) //不死には効かない + { + map_freeblock_unlock(); + return 1; + } + + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_status_change_start(bl,SkillStatusChangeTable[skillid],skilllv,0,0,0,skill_get_time(skillid,skilllv),0 ); + + if(dstmd && dstmd->skilltimer!=-1 && dstmd->state.skillcastcancel) // 詠唱妨害 + skill_castcancel(bl,0); + if(dstsd && dstsd->skilltimer!=-1 && (!dstsd->special_state.no_castcancel || map[bl->m].flag.gvg) + && dstsd->state.skillcastcancel && !dstsd->special_state.no_castcancel2) + skill_castcancel(bl,0); + + if(sc_data){ + if(sc_data[SC_FREEZE].timer!=-1) + skill_status_change_end(bl,SC_FREEZE,-1); + if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2==0) + skill_status_change_end(bl,SC_STONE,-1); + if(sc_data[SC_SLEEP].timer!=-1) + skill_status_change_end(bl,SC_SLEEP,-1); + } + + if(bl->type==BL_MOB) { + int range = skill_get_range(skillid,skilllv); + if(range < 0) + range = battle_get_range(src) - (range + 1); + mob_target((struct mob_data *)bl,src,range); + } + } + break; + + + + + + + case RG_CLEANER: //AppleGirl + clif_skill_nodamage(src,bl,skillid,skilllv,1); + { + struct skill_unit *su=NULL; + if((bl->type==BL_SKILL) && + (su=(struct skill_unit *)bl) && + (su->group->src_id == src->id || map[bl->m].flag.pvp || map[bl->m].flag.gvg) && + (su->group->unit_id == 0xb0)){ //罠を取り返す + if(sd) + skill_delunit(su); + } + } + break; + default: + printf("Unknown skill used:%d\n",skillid); + map_freeblock_unlock(); + return 1; + } + + map_freeblock_unlock(); + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、ID指定) + *------------------------------------------ + */ +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 range,inf2; + + nullpo_retr(0, sd); + + if( sd->bl.prev == NULL ) //prevが無いのはありなの? + return 0; + + if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != tid ) /* タイマIDの確認 */ + return 0; + if(sd->skillid != SA_CASTCANCEL && sd->skilltimer != -1 && pc_checkskill(sd,SA_FREECAST) > 0) { + sd->speed = sd->prev_speed; + clif_updatestatus(sd,SP_SPEED); + } + if(sd->skillid != SA_CASTCANCEL) + sd->skilltimer=-1; + + if((bl=map_id2bl(sd->skilltarget))==NULL || bl->prev==NULL) { + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + if(sd->bl.m != bl->m || pc_isdead(sd)) { //マップが違うか自分が死んでいる + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + + if(sd->skillid == PR_LEXAETERNA) { + struct status_change *sc_data = battle_get_sc_data(bl); + if(sc_data && (sc_data[SC_FREEZE].timer != -1 || (sc_data[SC_STONE].timer != -1 && sc_data[SC_STONE].val2 == 0))) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + else if(sd->skillid == RG_BACKSTAP) { + int dir = map_calc_dir(&sd->bl,bl->x,bl->y),t_dir = battle_get_dir(bl); + int dist = distance(sd->bl.x,sd->bl.y,bl->x,bl->y); + if(bl->type != BL_SKILL && (dist == 0 || map_check_dir(dir,t_dir))) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + + inf2 = skill_get_inf2(sd->skillid); + if( ( (skill_get_inf(sd->skillid)&1) || inf2&4 ) && // 彼我敵対関係チェック + battle_check_target(&sd->bl,bl, BCT_ENEMY)<=0 ) { + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + if(inf2 & 0xC00 && sd->bl.id != bl->id) { + int fail_flag = 1; + if(inf2 & 0x400 && battle_check_target(&sd->bl,bl, BCT_PARTY) > 0) + fail_flag = 0; + if(inf2 & 0x800 && sd->status.guild_id > 0 && sd->status.guild_id == battle_get_guild_id(bl)) + fail_flag = 0; + if(fail_flag) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + + range = skill_get_range(sd->skillid,sd->skilllv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + range += battle_config.pc_skill_add_range; + if((sd->skillid == MO_EXTREMITYFIST && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) || + (sd->skillid == CH_TIGERFIST && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) || + (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH) || + (sd->skillid == CH_CHAINCRUSH && sd->sc_data[SC_COMBO].timer != -1 && sd->sc_data[SC_COMBO].val1 == CH_TIGERFIST)) + range += skill_get_blewcount(MO_COMBOFINISH,sd->sc_data[SC_COMBO].val2); + if(battle_config.skill_out_range_consume) { // changed to allow casting when target walks out of range [Valaris] + if(range < distance(sd->bl.x,sd->bl.y,bl->x,bl->y)) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + if(!skill_check_condition(sd,1)) { /* 使用条件チェック */ + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + sd->skillitem = sd->skillitemlv = -1; + if(battle_config.skill_out_range_consume) { + if(range < distance(sd->bl.x,sd->bl.y,bl->x,bl->y)) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + return 0; + } + } + + if(battle_config.pc_skill_log) + printf("PC %d skill castend skill=%d\n",sd->bl.id,sd->skillid); + pc_stop_walking(sd,0); + + switch( skill_get_nk(sd->skillid) ) + { + /* 攻撃系/吹き飛ばし系 */ + case 0: case 2: + skill_castend_damage_id(&sd->bl,bl,sd->skillid,sd->skilllv,tick,0); + break; + case 1:/* 支援系 */ + if( (sd->skillid==AL_HEAL || (sd->skillid==ALL_RESURRECTION && bl->type != BL_PC) || sd->skillid==PR_ASPERSIO) && battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) + skill_castend_damage_id(&sd->bl,bl,sd->skillid,sd->skilllv,tick,0); + else + skill_castend_nodamage_id(&sd->bl,bl,sd->skillid,sd->skilllv,tick,0); + break; + } + + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、場所指定の実際の処理) + *------------------------------------------ + */ +int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag) +{ + struct map_session_data *sd=NULL; + int i,tmpx = 0,tmpy = 0, x1 = 0, y1 = 0; + + nullpo_retr(0, src); + + if(src->type==BL_PC){ + nullpo_retr(0, sd=(struct map_session_data *)src); + } + if( skillid != WZ_METEOR && + skillid != WZ_SIGHTRASHER && + skillid != AM_CANNIBALIZE && + skillid != AM_SPHEREMINE) + clif_skill_poseffect(src,skillid,skilllv,x,y,tick); + + if (skillnotok(skillid, sd)) // [MouseJstr] + return 0; + + switch(skillid) + { + case PR_BENEDICTIO: /* 聖体降福 */ + skill_area_temp[1]=src->id; + map_foreachinarea(skill_area_sub, + src->m,x-1,y-1,x+1,y+1,0, + src,skillid,skilllv,tick, flag|BCT_NOENEMY|1, + skill_castend_nodamage_id); + map_foreachinarea(skill_area_sub, + src->m,x-1,y-1,x+1,y+1,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + break; + + case BS_HAMMERFALL: /* ハンマーフォール */ + skill_area_temp[1]=src->id; + skill_area_temp[2]=x; + skill_area_temp[3]=y; + map_foreachinarea(skill_area_sub, + src->m,x-2,y-2,x+2,y+2,0, + src,skillid,skilllv,tick, flag|BCT_ENEMY|2, + skill_castend_nodamage_id); + break; + + case HT_DETECTING: /* ディテクティング */ + { + const int range=7; + map_foreachinarea( skill_status_change_timer_sub, + src->m, src->x-range, src->y-range, src->x+range,src->y+range,0, + src,SC_SIGHT,tick); + } + break; + + case MG_SAFETYWALL: /* セイフティウォール */ + case MG_FIREWALL: /* ファイヤーウォール */ + case MG_THUNDERSTORM: /* サンダーストーム */ + case AL_PNEUMA: /* ニューマ */ + case WZ_ICEWALL: /* アイスウォール */ + case WZ_FIREPILLAR: /* ファイアピラー */ + case WZ_SIGHTRASHER: + case WZ_QUAGMIRE: /* クァグマイア */ + case WZ_VERMILION: /* ロードオブヴァーミリオン */ + case WZ_FROSTNOVA: /* フロストノヴァ */ + case WZ_STORMGUST: /* ストームガスト */ + case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ + case PR_SANCTUARY: /* サンクチュアリ */ + case PR_MAGNUS: /* マグヌスエクソシズム */ + case CR_GRANDCROSS: /* グランドクロス */ + case HT_SKIDTRAP: /* スキッドトラップ */ + case HT_LANDMINE: /* ランドマイン */ + case HT_ANKLESNARE: /* アンクルスネア */ + case HT_SHOCKWAVE: /* ショックウェーブトラップ */ + case HT_SANDMAN: /* サンドマン */ + case HT_FLASHER: /* フラッシャー */ + case HT_FREEZINGTRAP: /* フリージングトラップ */ + case HT_BLASTMINE: /* ブラストマイン */ + case HT_CLAYMORETRAP: /* クレイモアートラップ */ + case AS_VENOMDUST: /* ベノムダスト */ + case AM_DEMONSTRATION: /* デモンストレーション */ + case PF_SPIDERWEB: /* スパイダーウェッブ */ + case PF_FOGWALL: /* フォグウォール */ + case HT_TALKIEBOX: /* トーキーボックス */ + skill_unitsetting(src,skillid,skilllv,x,y,0); + break; + + case RG_GRAFFITI: /* Graffiti [Valaris] */ + skill_clear_unitgroup(src); + skill_unitsetting(src,skillid,skilllv,x,y,0); + break; + + case SA_VOLCANO: /* ボルケーノ */ + case SA_DELUGE: /* デリュージ */ + case SA_VIOLENTGALE: /* バイオレントゲイル */ + case SA_LANDPROTECTOR: /* ランドプロテクター */ + skill_clear_element_field(src);//既に自分が発動している属性場をクリア + skill_unitsetting(src,skillid,skilllv,x,y,0); + break; + + case WZ_METEOR: //メテオストーム + { + int flag=0; + for(i=0;i<2+(skilllv>>1);i++) { + int j=0, c; + do { + tmpx = x + (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(((c=map_getcell(src->m,tmpx,tmpy))==1 || c==5) && j<100); + if(j >= 100) + continue; + if(flag==0){ + clif_skill_poseffect(src,skillid,skilllv,tmpx,tmpy,tick); + flag=1; + } + if(i > 0) + skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,(x1<<16)|y1,flag); + x1 = tmpx; + y1 = tmpy; + } + skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,-1,flag); + } + break; + + case AL_WARP: /* ワープポータル */ + if(sd) { + if(map[sd->bl.m].flag.noteleport) /* テレポ禁止 */ + break; + clif_skill_warppoint(sd,sd->skillid,sd->status.save_point.map, + (sd->skilllv>1)?sd->status.memo_point[0].map:"", + (sd->skilllv>2)?sd->status.memo_point[1].map:"", + (sd->skilllv>3)?sd->status.memo_point[2].map:""); + } + break; + case MO_BODYRELOCATION: + if(sd){ + pc_movepos(sd,x,y); + }else if( src->type==BL_MOB ) + mob_warp((struct mob_data *)src,-1,x,y,0); + break; + case AM_CANNIBALIZE: // バイオプラント + if(sd){ + int mx,my,id=0; + struct mob_data *md; + + mx = x;// + (rand()%10 - 5); + my = y;// + (rand()%10 - 5); + id=mob_once_spawn(sd,"this",mx,my,"--ja--",1118,1,""); + if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){ + md->master_id=sd->bl.id; + md->hp=2210+skilllv*200; + md->state.special_mob_ai=1; + md->deletetimer=add_timer(gettick()+skill_get_time(skillid,skilllv),mob_timer_delete,id,0); + } + clif_skill_poseffect(src,skillid,skilllv,x,y,tick); + } + break; + case AM_SPHEREMINE: // スフィアーマイン + if(sd){ + int mx,my,id=0; + struct mob_data *md; + + mx = x;// + (rand()%10 - 5); + my = y;// + (rand()%10 - 5); + id=mob_once_spawn(sd,"this",mx,my,"--ja--",1142,1,""); + if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){ + md->master_id=sd->bl.id; + md->hp=1000+skilllv*200; + md->state.special_mob_ai=2; + md->deletetimer=add_timer(gettick()+skill_get_time(skillid,skilllv),mob_timer_delete,id,0); + } + clif_skill_poseffect(src,skillid,skilllv,x,y,tick); + } + break; + } + + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、map指定) + *------------------------------------------ + */ +int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map) +{ + int x=0,y=0; + + nullpo_retr(0, sd); + if( sd->bl.prev == NULL || pc_isdead(sd) ) + return 0; + + if( sd->opt1>0 || sd->status.option&2 ) + return 0; + //スキルが使えない状態異常中 + if(sd->sc_data){ + if( sd->sc_data[SC_DIVINA].timer!=-1 || + sd->sc_data[SC_ROKISWEIL].timer!=-1 || + sd->sc_data[SC_AUTOCOUNTER].timer != -1 || + sd->sc_data[SC_STEELBODY].timer != -1 || + sd->sc_data[SC_DANCING].timer!=-1 || + sd->sc_data[SC_BERSERK].timer != -1 ) + return 0; + } + + if( skill_num != sd->skillid) /* 不正パケットらしい */ + return 0; + + pc_stopattack(sd); + + if(battle_config.pc_skill_log) + printf("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_num,map); + pc_stop_walking(sd,0); + + if(strcmp(map,"cancel")==0) + return 0; + + switch(skill_num){ + case AL_TELEPORT: /* テレポート */ + if(strcmp(map,"Random")==0) + pc_randomwarp(sd,3); + else + pc_setpos(sd,sd->status.save_point.map, + sd->status.save_point.x,sd->status.save_point.y,3); + break; + + case AL_WARP: /* ワープポータル */ + { + const struct point *p[]={ + &sd->status.save_point,&sd->status.memo_point[0], + &sd->status.memo_point[1],&sd->status.memo_point[2], + }; + struct skill_unit_group *group; + int i; + int maxcount=0; + + if((maxcount = skill_get_maxcount(sd->skillid)) > 0) { + int c; + for(i=c=0;i<MAX_SKILLUNITGROUP;i++) { + if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == sd->skillid) + c++; + } + if(c >= maxcount) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = gettick(); + sd->canmove_tick = gettick(); + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + + for(i=0;i<sd->skilllv;i++){ + if(strcmp(map,p[i]->map)==0){ + x=p[i]->x; + y=p[i]->y; + break; + } + } + if(x==0 || y==0) /* 不正パケット? */ + return 0; + + if(!skill_check_condition(sd,3)) + return 0; + if((group=skill_unitsetting(&sd->bl,sd->skillid,sd->skilllv,sd->skillx,sd->skilly,0))==NULL) + return 0; + group->valstr=(char *)aCalloc(24,sizeof(char)); + memcpy(group->valstr,map,24); + group->val2=(x<<16)|y; + } + break; + } + + return 0; +} + +/*========================================== + * スキルユニット設定処理 + *------------------------------------------ + */ +struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag) +{ + struct skill_unit_group *group; + int i,count=1,limit=10000,val1=0,val2=0; + int target=BCT_ENEMY,interval=1000,range=0; + int dir=0,aoe_diameter=0; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills + + nullpo_retr(0, src); + + switch(skillid){ /* 設定 */ + + case MG_SAFETYWALL: /* セイフティウォール */ + limit=skill_get_time(skillid,skilllv); + val2=skilllv+1; + interval = -1; + target=(battle_config.defnotenemy)?BCT_NOENEMY:BCT_ALL; + break; + + case MG_FIREWALL: /* ファイヤーウォール */ + if(src->x == x && src->y == y) + dir = 2; + else + dir=map_calc_dir(src,x,y); + if(dir&1) count=5; + else count=3; + limit=skill_get_time(skillid,skilllv); + val2=4+skilllv; + interval=1; + break; + + case AL_PNEUMA: /* ニューマ */ + limit=skill_get_time(skillid,skilllv); + interval = -1; + target=(battle_config.defnotenemy)?BCT_NOENEMY:BCT_ALL; + count = 9; + break; + + case AL_WARP: /* ワープポータル */ + target=BCT_ALL; + val1=skilllv+6; + if(flag==0) + limit=2000; + else + limit=skill_get_time(skillid,skilllv); + break; + + case PR_SANCTUARY: /* サンクチュアリ */ + count=21; + limit=skill_get_time(skillid,skilllv); + val1=skilllv+3; + val2=(skilllv>6)?777:skilllv*100; + target=BCT_ALL; + range=1; + break; + + case PR_MAGNUS: /* マグヌスエクソシズム */ + count=33; + limit=skill_get_time(skillid,skilllv); + interval=3000; + break; + + case WZ_FIREPILLAR: /* ファイアーピラー */ + if(flag==0) + limit=skill_get_time(skillid,skilllv); + else + limit=1000; + interval=2000; + val1=skilllv+2; + range=1; + break; + + case MG_THUNDERSTORM: /* サンダーストーム */ + limit=500; + range=1; + break; + + case WZ_FROSTNOVA: /* フロストノヴァ */ + limit=500; + range=5; + break; + case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ + limit=500; + range=2; + break; + + case WZ_METEOR: /* メテオストーム */ + limit=500; + range=3; + break; + + case WZ_SIGHTRASHER: + limit=500; + count=41; + break; + + case WZ_VERMILION: /* ロードオブヴァーミリオン */ + limit=4100; + interval=1000; + range=6; + break; + + case WZ_ICEWALL: /* アイスウォール */ + limit=skill_get_time(skillid,skilllv); + count=5; + break; + + case WZ_STORMGUST: /* ストームガスト */ + limit=4600; + interval=450; + range=5; + break; + + case WZ_QUAGMIRE: /* クァグマイア */ + limit=skill_get_time(skillid,skilllv); + interval=200; + count=25; + break; + + case HT_SKIDTRAP: /* スキッドトラップ */ + case HT_LANDMINE: /* ランドマイン */ + case HT_ANKLESNARE: /* アンクルスネア */ + case HT_SANDMAN: /* サンドマン */ + case PF_SPIDERWEB: /* スパイダーウェッブ */ + case HT_FLASHER: /* フラッシャー */ + case HT_FREEZINGTRAP: /* フリージングトラップ */ + case HT_BLASTMINE: /* ブラストマイン */ + case HT_CLAYMORETRAP: /* クレイモアートラップ */ + limit=skill_get_time(skillid,skilllv); + range=1; + break; + + case HT_TALKIEBOX: /* トーキーボックス */ + limit=skill_get_time(skillid,skilllv); + range=1; + target=BCT_ALL; + break; + + case HT_SHOCKWAVE: /* ショックウェーブトラップ */ + limit=skill_get_time(skillid,skilllv); + range=1; + val1=skilllv*15+10; + break; + + case AS_VENOMDUST: /* ベノムダスト */ + limit=skill_get_time(skillid,skilllv); + interval=1000; + count=5; + break; + + case CR_GRANDCROSS: /* グランドクロス */ + count=29; + limit=1000; + interval=300; + break; + + case SA_VOLCANO: /* ボルケーノ */ + case SA_DELUGE: /* デリュージ */ + case SA_VIOLENTGALE: /* バイオレントゲイル */ + limit=skill_get_time(skillid,skilllv); + count=skilllv<=2?25:(skilllv<=4?49:81); + target=BCT_ALL; + break; + + case SA_LANDPROTECTOR: /* グランドクロス */ + limit=skill_get_time(skillid,skilllv); // changed to get duration from cast_db (moonsoul) + val1=skilllv*15+10; + aoe_diameter=skilllv+skilllv%2+5; + target=BCT_ALL; + count=aoe_diameter*aoe_diameter; // -- this will not function if changed to ^2 (moonsoul) + break; + + case BD_LULLABY: /* 子守唄 */ + case BD_ETERNALCHAOS: /* エターナルカオス */ + case BD_ROKISWEIL: /* ロキの叫び */ + count=81; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_ALL; + break; + case BD_RICHMANKIM: + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + count=81; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_PARTY; + break; + + case BA_WHISTLE: /* 口笛 */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_NOENEMY; + if(src->type == BL_PC) + val1 = (pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON)+1)>>1; + val2 = ((battle_get_agi(src)/10)&0xffff)<<16; + val2 |= (battle_get_luk(src)/10)&0xffff; + break; + case DC_HUMMING: /* ハミング */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_NOENEMY; + if(src->type == BL_PC) + val1 = (pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON)+1)>>1; + val2 = battle_get_dex(src)/10; + break; + + case BA_DISSONANCE: /* 不協和音 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_ENEMY; + break; + + case DC_DONTFORGETME: /* 私を忘れないで… */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_ENEMY; + if(src->type == BL_PC) + val1 = (pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON)+1)>>1; + val2 = ((battle_get_str(src)/20)&0xffff)<<16; + val2 |= (battle_get_agi(src)/10)&0xffff; + break; + case BA_POEMBRAGI: /* ブラギの詩 */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_NOENEMY; + if(src->type == BL_PC) + val1 = pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); + val2 = ((battle_get_dex(src)/10)&0xffff)<<16; + val2 |= (battle_get_int(src)/5)&0xffff; + break; + case BA_APPLEIDUN: /* イドゥンの林檎 */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_NOENEMY; + if(src->type == BL_PC) + val1 = ((pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON))&0xffff)<<16; + else + val1 = 0; + val1 |= (battle_get_vit(src))&0xffff; + val2 = 0;//回復用タイムカウンタ(6秒毎に1増加) + break; + case DC_SERVICEFORYOU: /* サービスフォーユー */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_PARTY; + if(src->type == BL_PC) + val1 = (pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON)+1)>>1; + val2 = battle_get_int(src)/10; + break; + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_NOENEMY; + if(src->type == BL_PC) + val1 = (pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON)+1)>>1; + val2 = battle_get_agi(src)/20; + break; + case DC_FORTUNEKISS: /* 幸運のキス */ + count=49; + limit=skill_get_time(skillid,skilllv); + range=5; + target=BCT_NOENEMY; + if(src->type == BL_PC) + val1 = (pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON)+1)>>1; + val2 = battle_get_luk(src)/10; + break; + case AM_DEMONSTRATION: /* デモンストレーション */ + limit=skill_get_time(skillid,skilllv); + interval=1000; + range=1; + target=BCT_ENEMY; + break; + case WE_CALLPARTNER: /* あなたに逢いたい */ + limit=skill_get_time(skillid,skilllv); + range=-1; + break; + + case HP_BASILICA: /* バジリカ */ + limit=skill_get_time(skillid,skilllv); + target=BCT_ALL; + range=3; + //Fix to prevent the priest from walking while Basilica is up. + battle_stopwalking(src,1); + skill_status_change_start(src,SC_ANKLE,skilllv,0,0,0,limit,0); + break; + case PA_GOSPEL: /* ゴスペル */ + count=49; + target=BCT_PARTY; + limit=skill_get_time(skillid,skilllv); + break; + case PF_FOGWALL: /* フォグウォール */ + count=15; + limit=skill_get_time(skillid,skilllv); + break; + case RG_GRAFFITI: /* Graffiti */ + count=1; // Leave this at 1 [Valaris] + limit=600000; // Time length [Valaris] + break; + }; + + nullpo_retr(NULL, group=skill_initunitgroup(src,count,skillid,skilllv,skill_get_unit_id(skillid,flag&1))); + group->limit=limit; + group->val1=val1; + group->val2=val2; + group->target_flag=target; + group->interval=interval; + group->range=range; + if(skillid==HT_TALKIEBOX || + skillid==RG_GRAFFITI){ + group->valstr=calloc(80, 1); + if(group->valstr==NULL){ + printf("skill_castend_map: out of memory !\n"); + exit(1); + } + memcpy(group->valstr,talkie_mes,80); + } + for(i=0;i<count;i++){ + struct skill_unit *unit; + int ux=x,uy=y,val1=skilllv,val2=0,limit=group->limit,alive=1; + int range=group->range; + switch(skillid){ /* 設定 */ + case AL_PNEUMA: /* ニューマ */ + { + static const int dx[9]={-1, 0, 1,-1, 0, 1,-1, 0, 1}; + static const int dy[9]={-1,-1,-1, 0, 0, 0, 1, 1, 1}; + ux+=dx[i]; + uy+=dy[i]; + } + break; + case MG_FIREWALL: /* ファイヤーウォール */ + { + if(dir&1){ /* 斜め配置 */ + static const int dx[][5]={ + { 1,1,0,0,-1 }, { -1,-1,0,0,1 }, + },dy[][5]={ + { 1,0,0,-1,-1 }, { 1,0,0,-1,-1 }, + }; + ux+=dx[(dir>>1)&1][i]; + uy+=dy[(dir>>1)&1][i]; + }else{ /* 上下配置 */ + if(dir%4==0) /* 上下 */ + ux+=i-1; + else /* 左右 */ + uy+=i-1; + } + val2=group->val2; + } + break; + + case PR_SANCTUARY: /* サンクチュアリ */ + { + static const int dx[]={ + -1,0,1, -2,-1,0,1,2, -2,-1,0,1,2, -2,-1,0,1,2, -1,0,1 }; + static const int dy[]={ + -2,-2,-2, -1,-1,-1,-1,-1, 0,0,0,0,0, 1,1,1,1,1, 2,2,2, }; + ux+=dx[i]; + uy+=dy[i]; + } + break; + + case PR_MAGNUS: /* マグヌスエクソシズム */ + { + static const int dx[]={ -1,0,1, -1,0,1, -3,-2,-1,0,1,2,3, + -3,-2,-1,0,1,2,3, -3,-2,-1,0,1,2,3, -1,0,1, -1,0,1, }; + static const int dy[]={ + -3,-3,-3, -2,-2,-2, -1,-1,-1,-1,-1,-1,-1, + 0,0,0,0,0,0,0, 1,1,1,1,1,1,1, 2,2,2, 3,3,3 }; + ux+=dx[i]; + uy+=dy[i]; + } + break; + + case WZ_SIGHTRASHER: + { + static const int dx[]={ + -5, 0, 5, -4, 0, 4, -3, 0, 3, -2, 0, 2, -1, 0, 1, -5,-4,-3,-2,-1, 0, 1, 2, 3, 4, 5, -1, 0, 1, -2, 0, 2, -3, 0, 3, -4, 0, 4, -5, 0, 5 }; + static const int dy[]={ + -5,-5,-5, -4,-4,-4, -3,-3,-3, -2,-2,-2, -1,-1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 }; + ux+=dx[i]; + uy+=dy[i]; + } + break; + + case WZ_ICEWALL: /* アイスウォール */ + { + static const int dirx[8]={0,-1,-1,-1,0,1,1,1}; + static const int diry[8]={1,1,0,-1,-1,-1,0,1}; + if(skilllv <= 1) + val1 = 500; + else + val1 = 200 + 200*skilllv; + if(src->x == x && src->y == y) + dir = 2; + else + dir=map_calc_dir(src,x,y); + ux+=(2-i)*diry[dir]; + uy+=(i-2)*dirx[dir]; + } + break; + + case WZ_QUAGMIRE: /* クァグマイア */ + ux+=(i%5-2); + uy+=(i/5-2); + if(i==12) + range=2; + else + range=-1; + + break; + + case AS_VENOMDUST: /* ベノムダスト */ + { + static const int dx[]={-1,0,0,0,1}; + static const int dy[]={0,-1,0,1,0}; + ux+=dx[i]; + uy+=dy[i]; + } + break; + + case CR_GRANDCROSS: /* グランドクロス */ + { + static const int dx[]={ + 0, 0, -1,0,1, -2,-1,0,1,2, -4,-3,-2,-1,0,1,2,3,4, -2,-1,0,1,2, -1,0,1, 0, 0, }; + static const int dy[]={ + -4, -3, -2,-2,-2, -1,-1,-1,-1,-1, 0,0,0,0,0,0,0,0,0, 1,1,1,1,1, 2,2,2, 3, 4, }; + ux+=dx[i]; + uy+=dy[i]; + } + break; + case SA_VOLCANO: /* ボルケーノ */ + case SA_DELUGE: /* デリュージ */ + case SA_VIOLENTGALE: /* バイオレントゲイル */ + { + int u_range=0,central=0; + if(skilllv<=2){ + u_range=2; + central=12; + }else if(skilllv<=4){ + u_range=3; + central=24; + }else if(skilllv>=5){ + u_range=4; + central=40; + } + ux+=(i%(u_range*2+1)-u_range); + uy+=(i/(u_range*2+1)-u_range); + + if(i==central) + range=u_range;//中央のユニットの効果範囲は全範囲 + else + range=-1;//中央以外のユニットは飾り + } + break; + case SA_LANDPROTECTOR: /* ランドプロテクター */ + { + int u_range=0; + + if(skilllv<=2) u_range=3; + else if(skilllv<=4) u_range=4; + else if(skilllv>=5) u_range=5; + + ux+=(i%(u_range*2+1)-u_range); + uy+=(i/(u_range*2+1)-u_range); + + range=0; + } + break; + + /* ダンスなど */ + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD:/* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + ux+=(i%9-4); + uy+=(i/9-4); + if(i==40) + range=4; /* 中心の場合は範囲を4にオーバーライド */ + else + range=-1; /* 中心じゃない場合は範囲を-1にオーバーライド */ + break; + case BA_DISSONANCE: /* 不協和音 */ + case BA_WHISTLE: /* 口笛 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + case BA_POEMBRAGI: /* ブラギの詩 */ + case BA_APPLEIDUN: /* イドゥンの林檎 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + case DC_HUMMING: /* ハミング */ + case DC_DONTFORGETME: /* 私を忘れないで… */ + case DC_FORTUNEKISS: /* 幸運のキス */ + case DC_SERVICEFORYOU: /* サービスフォーユー */ + ux+=(i%7-3); + uy+=(i/7-3); + if(i==40) + range=4; /* 中心の場合は範囲を4にオーバーライド */ + else + range=-1; /* 中心じゃない場合は範囲を-1にオーバーライド */ + break; + case PA_GOSPEL: /* ゴスペル */ + ux+=(i%7-3); + uy+=(i/7-3); + break; + case PF_FOGWALL: /* フォグウォール */ + ux+=(i%5-2); + uy+=(i/5-1); + break; + case RG_GRAFFITI: /* Graffiti [Valaris] */ + ux+=(i%5-2); + uy+=(i/5-2); + break; + } + //直上スキルの場合設置座標上にランドプロテクターがないかチェック + if(range<=0) + map_foreachinarea(skill_landprotector,src->m,ux,uy,ux,uy,BL_SKILL,skillid,&alive); + + if(skillid==WZ_ICEWALL && alive){ + val2=map_getcell(src->m,ux,uy); + if(val2==5 || val2==1) + alive=0; + else { + map_setcell(src->m,ux,uy,5); + clif_changemapcell(src->m,ux,uy,5,0); + } + } + + if(alive){ + nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy)); + unit->val1=val1; + unit->val2=val2; + unit->limit=limit; + unit->range=range; + } + } + return group; +} + +/*========================================== + * スキルユニットの発動イベント + *------------------------------------------ + */ +int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick) +{ + struct skill_unit_group *sg; + struct block_list *ss; + struct skill_unit_group_tickset *ts; + struct map_session_data *srcsd=NULL; + int diff,goflag,splash_count=0; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + + if( bl->prev==NULL || !src->alive || (bl->type == BL_PC && pc_isdead((struct map_session_data *)bl) ) ) + return 0; + + nullpo_retr(0, sg=src->group); + nullpo_retr(0, ss=map_id2bl(sg->src_id)); + + if(ss->type == BL_PC) + nullpo_retr(0, srcsd=(struct map_session_data *)ss); + if(srcsd && srcsd->chatID) + return 0; + + if( bl->type!=BL_PC && bl->type!=BL_MOB ) + return 0; + nullpo_retr(0, ts=skill_unitgrouptickset_search( bl, sg->group_id)); + diff=DIFF_TICK(tick,ts->tick); + goflag=(diff>sg->interval || diff<0); + if (sg->skill_id == CR_GRANDCROSS && !battle_config.gx_allhit) // 重なっていたら3HITしない + goflag = (diff>sg->interval*map_count_oncell(bl->m,bl->x,bl->y) || diff<0); + + //対象がLP上に居る場合は無効 + map_foreachinarea(skill_landprotector,bl->m,bl->x,bl->y,bl->x,bl->y,BL_SKILL,0,&goflag); + + if(!goflag) + return 0; + ts->tick=tick; + ts->group_id=sg->group_id; + + switch(sg->unit_id){ + case 0x83: /* サンクチュアリ */ + { + int race=battle_get_race(bl); + int damage_flag = (battle_check_undead(race,battle_get_elem_type(bl)) || race == 6)? 1:0; + + if( battle_get_hp(bl)>=battle_get_max_hp(bl) && !damage_flag) + break; + + if((sg->val1--)<=0){ + skill_delunitgroup(sg); + return 0; + } + if(!damage_flag) { + int heal=sg->val2; + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage) + heal=0; /* 黄金蟲カード(ヒール量0) */ + clif_skill_nodamage(&src->bl,bl,AL_HEAL,heal,1); + battle_heal(NULL,bl,heal,0,0); + } + else + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + } + break; + + case 0x84: /* マグヌスエクソシズム */ + { + int race=battle_get_race(bl); + int damage_flag = (battle_check_undead(race,battle_get_elem_type(bl)) || race == 6)? 1:0; + + if(!damage_flag) + return 0; + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + } + break; + + case 0x85: /* ニューマ */ + { + struct skill_unit *unit2; + struct status_change *sc_data=battle_get_sc_data(bl); + int type=SC_PNEUMA; + if(sc_data && sc_data[type].timer==-1) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,0,0); + else if((unit2=(struct skill_unit *)sc_data[type].val2) && unit2 != src ){ + if(DIFF_TICK(sg->tick,unit2->group->tick)>0 ) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,0,0); + ts->tick-=sg->interval; + } + } + break; + case 0x7e: /* セイフティウォール */ + { + struct skill_unit *unit2; + struct status_change *sc_data=battle_get_sc_data(bl); + int type=SC_SAFETYWALL; + if(sc_data && sc_data[type].timer==-1) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,0,0); + else if((unit2=(struct skill_unit *)sc_data[type].val2) && unit2 != src ){ + if(sg->val1 < unit2->group->val1 ) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,0,0); + ts->tick-=sg->interval; + } + } + break; + + case 0x86: /* ロードオブヴァーミリオン(&ストームガスト &グランドクロス) */ + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + + case 0x7f: /* ファイヤーウォール */ + if( (src->val2--)>0) + skill_attack(BF_MAGIC,ss,&src->bl,bl, + sg->skill_id,sg->skill_lv,tick,0); + if( src->val2<=0 ) + skill_delunit(src); + break; + + case 0x87: /* ファイアーピラー(発動前) */ + skill_delunit(src); + skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1); + break; + + case 0x88: /* ファイアーピラー(発動後) */ + if(DIFF_TICK(tick,sg->tick) < 150) + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + + case 0x90: /* スキッドトラップ */ + { + int i,c = skill_get_blewcount(sg->skill_id,sg->skill_lv); + if(map[bl->m].flag.gvg) c = 0; + for(i=0;i<c;i++) + skill_blown(&src->bl,bl,1|0x30000); + sg->unit_id = 0x8c; + clif_changelook(&src->bl,LOOK_BASE,sg->unit_id); + sg->limit=DIFF_TICK(tick,sg->tick)+1500; + } + break; + + case 0x93: /* ランドマイン */ + skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + sg->unit_id = 0x8c; + clif_changelook(&src->bl,LOOK_BASE,0x88); + sg->limit=DIFF_TICK(tick,sg->tick)+1500; + break; + + case 0x8f: /* ブラストマイン */ + case 0x94: /* ショックウェーブトラップ */ + case 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x97: /* フリージングトラップ */ + case 0x98: /* クレイモアートラップ */ + map_foreachinarea(skill_count_target,src->bl.m + ,src->bl.x-src->range,src->bl.y-src->range + ,src->bl.x+src->range,src->bl.y+src->range + ,0,&src->bl,&splash_count); + map_foreachinarea(skill_trap_splash,src->bl.m + ,src->bl.x-src->range,src->bl.y-src->range + ,src->bl.x+src->range,src->bl.y+src->range + ,0,&src->bl,tick,splash_count); + sg->unit_id = 0x8c; + clif_changelook(&src->bl,LOOK_BASE,sg->unit_id); + sg->limit=DIFF_TICK(tick,sg->tick)+1500; + break; + + case 0x91: /* アンクルスネア */ + { + struct status_change *sc_data=battle_get_sc_data(bl); + if(sg->val2==0 && sc_data && sc_data[SC_ANKLE].timer==-1){ + int moveblock = ( bl->x/BLOCK_SIZE != src->bl.x/BLOCK_SIZE || bl->y/BLOCK_SIZE != src->bl.y/BLOCK_SIZE); + int sec=skill_get_time2(sg->skill_id,sg->skill_lv) - (double)battle_get_agi(bl)*0.1; + if(battle_get_mode(bl)&0x20) + sec = sec/5; + battle_stopwalking(bl,1); + skill_status_change_start(bl,SC_ANKLE,sg->skill_lv,0,0,0,sec,0); + + if(moveblock) map_delblock(bl); + bl->x = src->bl.x; + bl->y = src->bl.y; + if(moveblock) map_addblock(bl); + if(bl->type == BL_MOB) + clif_fixmobpos((struct mob_data *)bl); + else if(bl->type == BL_PET) + clif_fixpetpos((struct pet_data *)bl); + else + clif_fixpos(bl); + clif_01ac(&src->bl); + sg->limit=DIFF_TICK(tick,sg->tick) + sec; + sg->val2=bl->id; + } + } + break; + + case 0x80: /* ワープポータル(発動後) */ + if(bl->type==BL_PC){ + struct map_session_data *sd = (struct map_session_data *)bl; + if(sd && src->bl.m == bl->m && src->bl.x == bl->x && src->bl.y == bl->y && src->bl.x == sd->to_x && src->bl.y == sd->to_y) { + if( battle_config.chat_warpportal || !sd->chatID ){ + if((sg->val1--)>0){ + pc_setpos(sd,sg->valstr,sg->val2>>16,sg->val2&0xffff,3); + if(sg->src_id == bl->id ||( strcmp(map[src->bl.m].name,sg->valstr) == 0 && src->bl.x == (sg->val2>>16) && src->bl.y == (sg->val2&0xffff) )) + skill_delunitgroup(sg); + }else + skill_delunitgroup(sg); + } + } + }else if(bl->type==BL_MOB && battle_config.mob_warpportal){ + int m=map_mapname2mapid(sg->valstr); + struct mob_data *md; + md=(struct mob_data *)bl; + mob_warp((struct mob_data *)bl,m,sg->val2>>16,sg->val2&0xffff,3); + } + break; + + case 0x8e: /* クァグマイア */ + { + int type=SkillStatusChangeTable[sg->skill_id]; + if( bl->type==BL_PC && ((struct map_session_data *)bl)->special_state.no_magic_damage ) + break; + if( battle_get_sc_data(bl)[type].timer==-1 ) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0); + } + break; + case 0x92: /* ベノムダスト */ + { + struct status_change *sc_data=battle_get_sc_data(bl); + int type=SkillStatusChangeTable[sg->skill_id]; + if( sc_data && sc_data[type].timer==-1 ) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0); + } + break; + case 0x9a: /* ボルケーノ */ + case 0x9b: /* デリュージ */ + case 0x9c: /* バイオレントゲイル */ + { + struct skill_unit *unit2; + struct status_change *sc_data=battle_get_sc_data(bl); + int type=SkillStatusChangeTable[sg->skill_id]; + if(sc_data && sc_data[type].timer==-1) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0); + else if((unit2=(struct skill_unit *)sc_data[type].val2) && unit2 != src ){ + if( DIFF_TICK(sg->tick,unit2->group->tick)>0 ) + skill_status_change_start(bl,type,sg->skill_lv,(int)src,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0); + ts->tick-=sg->interval; + } + } break; + + case 0x9e: /* 子守唄 */ + case 0x9f: /* ニヨルドの宴 */ + case 0xa0: /* 永遠の混沌 */ + case 0xa1: /* 戦太鼓の響き */ + case 0xa2: /* ニーベルングの指輪 */ + case 0xa3: /* ロキの叫び */ + case 0xa4: /* 深淵の中に */ + case 0xa5: /* 不死身のジークフリード */ + case 0xa6: /* 不協和音 */ + case 0xa7: /* 口笛 */ + case 0xa8: /* 夕陽のアサシンクロス */ + case 0xa9: /* ブラギの詩 */ + case 0xab: /* 自分勝手なダンス */ + case 0xac: /* ハミング */ + case 0xad: /* 私を忘れないで… */ + case 0xae: /* 幸運のキス */ + case 0xaf: /* サービスフォーユー */ + case 0xb4: + { + struct skill_unit *unit2; + struct status_change *sc_data=battle_get_sc_data(bl); + int type=SkillStatusChangeTable[sg->skill_id]; + if(sg->src_id == bl->id) + break; + if(sc_data && sc_data[type].timer==-1) + skill_status_change_start(bl,type,sg->skill_lv,sg->val1,sg->val2, + (int)src,skill_get_time2(sg->skill_id,sg->skill_lv),0); + else if( (unit2=(struct skill_unit *)sc_data[type].val4) && unit2 != src ){ + if( unit2->group && DIFF_TICK(sg->tick,unit2->group->tick)>0 ) + skill_status_change_start(bl,type,sg->skill_lv,sg->val1,sg->val2, + (int)src,skill_get_time2(sg->skill_id,sg->skill_lv),0); + ts->tick-=sg->interval; + } + } break; + + case 0xaa: /* イドゥンの林檎 */ + { + struct skill_unit *unit2; + struct status_change *sc_data=battle_get_sc_data(bl); + int type=SkillStatusChangeTable[sg->skill_id]; + if(sg->src_id == bl->id) + break; + if( sc_data && sc_data[type].timer==-1) + skill_status_change_start(bl,type,sg->skill_lv,(sg->val1)>>16,(sg->val1)&0xffff, + (int)src,skill_get_time2(sg->skill_id,sg->skill_lv),0); + else if((unit2=(struct skill_unit *)sc_data[type].val4) && unit2 != src ){ + if( DIFF_TICK(sg->tick,unit2->group->tick)>0 ) + skill_status_change_start(bl,type,sg->skill_lv,(sg->val1)>>16,(sg->val1)&0xffff, + (int)src,skill_get_time2(sg->skill_id,sg->skill_lv),0); + ts->tick-=sg->interval; + } + } break; + + case 0xb1: /* デモンストレーション */ + skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + if(bl->type == BL_PC && rand()%100 < sg->skill_lv && battle_config.equipment_breaking) + pc_breakweapon((struct map_session_data *)bl); + break; + case 0x99: /* トーキーボックス */ + if(sg->src_id == bl->id) //自分が踏んでも発動しない + break; + if(sg->val2==0){ + clif_talkiebox(&src->bl,sg->valstr); + sg->unit_id = 0x8c; + clif_changelook(&src->bl,LOOK_BASE,sg->unit_id); + sg->limit=DIFF_TICK(tick,sg->tick)+5000; + sg->val2=-1; //踏んだ + } + break; + case 0xb2: /* あなたを_会いたいです */ + case 0xb3: /* ゴスペル */ + case 0xb6: /* フォグウォール */ + //とりあえず何もしない + break; + + + + + + + case 0xb7: /* スパイダーウェッブ */ + if(sg->val2==0){ + int moveblock = ( bl->x/BLOCK_SIZE != src->bl.x/BLOCK_SIZE || bl->y/BLOCK_SIZE != src->bl.y/BLOCK_SIZE); + skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick); + if(moveblock) map_delblock(bl); + bl->x = (&src->bl)->x; + bl->y = (&src->bl)->y; + if(moveblock) map_addblock(bl); + if(bl->type == BL_MOB) + clif_fixmobpos((struct mob_data *)bl); + else if(bl->type == BL_PET) + clif_fixpetpos((struct pet_data *)bl); + else + clif_fixpos(bl); + clif_01ac(&src->bl); + sg->limit=DIFF_TICK(tick,sg->tick) + skill_get_time2(sg->skill_id,sg->skill_lv); + sg->val2=bl->id; + } + break; + +/* default: + if(battle_config.error_log) + printf("skill_unit_onplace: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); + break;*/ + } + if(bl->type==BL_MOB && ss!=bl) /* スキル使用条件のMOBスキル */ + { + if(battle_config.mob_changetarget_byskill == 1) + { + int target=((struct mob_data *)bl)->target_id; + if(ss->type == BL_PC) + ((struct mob_data *)bl)->target_id=ss->id; + mobskill_use((struct mob_data *)bl,tick,MSC_SKILLUSED|(sg->skill_id<<16)); + ((struct mob_data *)bl)->target_id=target; + } + else + mobskill_use((struct mob_data *)bl,tick,MSC_SKILLUSED|(sg->skill_id<<16)); + } + + return 0; +} +/*========================================== + * スキルユニットから離脱する(もしくはしている)場合 + *------------------------------------------ + */ +int skill_unit_onout(struct skill_unit *src,struct block_list *bl,unsigned int tick) +{ + struct skill_unit_group *sg; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + nullpo_retr(0, sg=src->group); + + if( bl->prev==NULL || !src->alive ) + return 0; + + if( bl->type!=BL_PC && bl->type!=BL_MOB ) + return 0; + + switch(sg->unit_id){ + case 0x7e: /* セイフティウォール */ + case 0x85: /* ニューマ */ + case 0x8e: /* クァグマイア */ + { + struct status_change *sc_data=battle_get_sc_data(bl); + int type= + (sg->unit_id==0x85)?SC_PNEUMA: + ((sg->unit_id==0x7e)?SC_SAFETYWALL: + SC_QUAGMIRE); + if((type != SC_QUAGMIRE || bl->type != BL_MOB) && + sc_data && sc_data[type].timer!=-1 && ((struct skill_unit *)sc_data[type].val2)==src){ + skill_status_change_end(bl,type,-1); + } + } break; + + case 0x91: /* アンクルスネア */ + { + struct block_list *target=map_id2bl(sg->val2); + if( target && target==bl ){ + skill_status_change_end(bl,SC_ANKLE,-1); + sg->limit=DIFF_TICK(tick,sg->tick)+1000; + } + } + break; + case 0xb5: + case 0xb8: + { + struct block_list *target=map_id2bl(sg->val2); + if( target==bl ) + skill_status_change_end(bl,SC_SPIDERWEB,-1); + sg->limit=DIFF_TICK(tick,sg->tick)+1000; + } + break; + case 0xb6: + { + struct block_list *target=map_id2bl(sg->val2); + if( target==bl ) + skill_status_change_end(bl,SC_FOGWALL,-1); + sg->limit=DIFF_TICK(tick,sg->tick)+1000; + } + break; + case 0x9a: /* ボルケーノ */ + case 0x9b: /* デリュージ */ + case 0x9c: /* バイオレントゲイル */ + { + struct status_change *sc_data=battle_get_sc_data(bl); + struct skill_unit *su; + int type=SkillStatusChangeTable[sg->skill_id]; + if( sc_data && sc_data[type].timer!=-1 && (su=((struct skill_unit *)sc_data[type].val2)) && su == src ){ + skill_status_change_end(bl,type,-1); + } + } + break; + + case 0x9e: /* 子守唄 */ + case 0x9f: /* ニヨルドの宴 */ + case 0xa0: /* 永遠の混沌 */ + case 0xa1: /* 戦太鼓の響き */ + case 0xa2: /* ニーベルングの指輪 */ + case 0xa3: /* ロキの叫び */ + case 0xa4: /* 深淵の中に */ + case 0xa5: /* 不死身のジークフリード */ + case 0xa6: /* 不協和音 */ + case 0xa7: /* 口笛 */ + case 0xa8: /* 夕陽のアサシンクロス */ + case 0xa9: /* ブラギの詩 */ + case 0xaa: /* イドゥンの林檎 */ + case 0xab: /* 自分勝手なダンス */ + case 0xac: /* ハミング */ + case 0xad: /* 私を忘れないで… */ + case 0xae: /* 幸運のキス */ + case 0xaf: /* サービスフォーユー */ + case 0xb4: + { + struct status_change *sc_data=battle_get_sc_data(bl); + struct skill_unit *su; + int type=SkillStatusChangeTable[sg->skill_id]; + if( sc_data && sc_data[type].timer!=-1 && (su=((struct skill_unit *)sc_data[type].val4)) && su == src ){ + skill_status_change_end(bl,type,-1); + } + } + break; + case 0xb7: /* スパイダーウェッブ */ + { + struct block_list *target=map_id2bl(sg->val2); + if( target && target==bl ) + skill_status_change_end(bl,SC_SPIDERWEB,-1); + sg->limit=DIFF_TICK(tick,sg->tick)+1000; + } + break; + +/* default: + if(battle_config.error_log) + printf("skill_unit_onout: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); + break;*/ + } + skill_unitgrouptickset_delete(bl,sg->group_id); + return 0; +} +/*========================================== + * スキルユニットの削除イベント + *------------------------------------------ + */ +int skill_unit_ondelete(struct skill_unit *src,struct block_list *bl,unsigned int tick) +{ + struct skill_unit_group *sg; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + nullpo_retr(0, sg = src->group); + + if( bl->prev==NULL || !src->alive ) + return 0; + + if( bl->type!=BL_PC && bl->type!=BL_MOB ) + return 0; + + switch(sg->unit_id){ + case 0x85: /* ニューマ */ + case 0x7e: /* セイフティウォール */ + case 0x8e: /* クァグマイヤ */ + case 0x9a: /* ボルケーノ */ + case 0x9b: /* デリュージ */ + case 0x9c: /* バイオレントゲイル */ + case 0x9e: /* 子守唄 */ + case 0x9f: /* ニヨルドの宴 */ + case 0xa0: /* 永遠の混沌 */ + case 0xa1: /* 戦太鼓の響き */ + case 0xa2: /* ニーベルングの指輪 */ + case 0xa3: /* ロキの叫び */ + case 0xa4: /* 深淵の中に */ + case 0xa5: /* 不死身のジークフリード */ + case 0xa6: /* 不協和音 */ + case 0xa7: /* 口笛 */ + case 0xa8: /* 夕陽のアサシンクロス */ + case 0xa9: /* ブラギの詩 */ + case 0xaa: /* イドゥンの林檎 */ + case 0xab: /* 自分勝手なダンス */ + case 0xac: /* ハミング */ + case 0xad: /* 私を忘れないで… */ + case 0xae: /* 幸運のキス */ + case 0xaf: /* サービスフォーユー */ + case 0xb4: + return skill_unit_onout(src,bl,tick); + +/* default: + if(battle_config.error_log) + printf("skill_unit_ondelete: Unknown skill unit id=%d block=%d\n",sg->unit_id,bl->id); + break;*/ + } + skill_unitgrouptickset_delete(bl,sg->group_id); + return 0; +} +/*========================================== + * スキルユニットの限界イベント + *------------------------------------------ + */ +int skill_unit_onlimit(struct skill_unit *src,unsigned int tick) +{ + struct skill_unit_group *sg; + + nullpo_retr(0, src); + nullpo_retr(0, sg=src->group); + + switch(sg->unit_id){ + case 0x81: /* ワープポータル(発動前) */ + { + struct skill_unit_group *group= + skill_unitsetting(map_id2bl(sg->src_id),sg->skill_id,sg->skill_lv, + src->bl.x,src->bl.y,1); + if(group == NULL) + return 0; + group->valstr=calloc(24, 1); + if(group->valstr==NULL){ + printf("skill_unit_onlimit: out of memory !\n"); + exit(1); + } + memcpy(group->valstr,sg->valstr,24); + group->val2=sg->val2; + } + break; + + case 0x8d: /* アイスウォール */ + map_setcell(src->bl.m,src->bl.x,src->bl.y,src->val2); + clif_changemapcell(src->bl.m,src->bl.x,src->bl.y,src->val2,1); + break; + case 0xb2: /* あなたに会いたい */ + { + struct map_session_data *sd = NULL; + struct map_session_data *p_sd = NULL; + if((sd = (struct map_session_data *)(map_id2bl(sg->src_id))) == NULL) + return 0; + if((p_sd = pc_get_partner(sd)) == NULL) + return 0; + + pc_setpos(p_sd,map[src->bl.m].name,src->bl.x,src->bl.y,3); + } + break; + } + return 0; +} +/*========================================== + * スキルユニットのダメージイベント + *------------------------------------------ + */ +int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl, + int damage,unsigned int tick) +{ + struct skill_unit_group *sg; + + nullpo_retr(0, src); + nullpo_retr(0, sg=src->group); + + switch(sg->unit_id){ + case 0x8d: /* アイスウォール */ + src->val1-=damage; + break; + case 0x8f: /* ブラストマイン */ + case 0x98: /* クレイモアートラップ */ + skill_blown(bl,&src->bl,2); //吹き飛ばしてみる + break; + default: + damage = 0; + break; + } + return damage; +} + + +/*---------------------------------------------------------------------------- */ + +/*========================================== + * スキル使用(詠唱完了、場所指定) + *------------------------------------------ + */ +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 range,maxcount; + + nullpo_retr(0, sd); + + if( sd->bl.prev == NULL ) + return 0; + if( sd->skilltimer != tid ) /* タイマIDの確認 */ + return 0; + if(sd->skilltimer != -1 && pc_checkskill(sd,SA_FREECAST) > 0) { + sd->speed = sd->prev_speed; + clif_updatestatus(sd,SP_SPEED); + } + sd->skilltimer=-1; + if(pc_isdead(sd)) { + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + + if(battle_config.pc_skill_reiteration == 0) { + range = -1; + switch(sd->skillid) { + case MG_SAFETYWALL: + case WZ_FIREPILLAR: + case HT_SKIDTRAP: + case HT_LANDMINE: + case HT_ANKLESNARE: + case HT_SHOCKWAVE: + case HT_SANDMAN: + case HT_FLASHER: + case HT_FREEZINGTRAP: + case HT_BLASTMINE: + case HT_CLAYMORETRAP: + case HT_TALKIEBOX: + case AL_WARP: + case PF_SPIDERWEB: /* スパイダーウェッブ */ + case RG_GRAFFITI: /* グラフィティ */ + range = 0; + break; + case AL_PNEUMA: + range = 1; + break; + } + if(range >= 0) { + if(skill_check_unit_range(sd->bl.m,sd->skillx,sd->skilly,range,sd->skillid) > 0) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + } + if(battle_config.pc_skill_nofootset) { + range = -1; + switch(sd->skillid) { + case WZ_FIREPILLAR: + case HT_SKIDTRAP: + case HT_LANDMINE: + case HT_ANKLESNARE: + case HT_SHOCKWAVE: + case HT_SANDMAN: + case HT_FLASHER: + case HT_FREEZINGTRAP: + case HT_BLASTMINE: + case HT_CLAYMORETRAP: + case HT_TALKIEBOX: + case PF_SPIDERWEB: /* スパイダーウェッブ */ + case WZ_ICEWALL: + range = 1; + break; + case AL_WARP: + range = 0; + break; + } + if(range >= 0) { + if(skill_check_unit_range2(sd->bl.m,sd->skillx,sd->skilly,range) > 0) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 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); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + } + + range = skill_get_range(sd->skillid,sd->skilllv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + range += battle_config.pc_skill_add_range; + if(battle_config.skill_out_range_consume) { // changed to allow casting when target walks out of range [Valaris] + if(range < distance(sd->bl.x,sd->bl.y,sd->skillx,sd->skilly)) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + } + if(!skill_check_condition(sd,1)) { /* 使用条件チェック */ + sd->canact_tick = tick; + sd->canmove_tick = tick; + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + sd->skillitem = sd->skillitemlv = -1; + if(battle_config.skill_out_range_consume) { + if(range < distance(sd->bl.x,sd->bl.y,sd->skillx,sd->skilly)) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->canact_tick = tick; + sd->canmove_tick = tick; + return 0; + } + } + + if(battle_config.pc_skill_log) + printf("PC %d skill castend skill=%d\n",sd->bl.id,sd->skillid); + pc_stop_walking(sd,0); + + skill_castend_pos2(&sd->bl,sd->skillx,sd->skilly,sd->skillid,sd->skilllv,tick,0); + + return 0; +} + +/*========================================== + * 範囲内キャラ存在確認判定処理(foreachinarea) + *------------------------------------------ + */ + +static int skill_check_condition_char_sub(struct block_list *bl,va_list ap) +{ + int *c; + struct block_list *src; + struct map_session_data *sd; + struct map_session_data *ssd; + struct pc_base_job s_class; + struct pc_base_job ss_class; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, sd=(struct map_session_data*)bl); + nullpo_retr(0, src=va_arg(ap,struct block_list *)); + nullpo_retr(0, c=va_arg(ap,int *)); + nullpo_retr(0, ssd=(struct map_session_data*)src); + + s_class = pc_calc_base_job(sd->status.class); + //チェックしない設定ならcにありえない大きな数字を返して終了 + if(!battle_config.player_skill_partner_check){ //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ + (*c)=99; + return 0; + } + + ; + ss_class = pc_calc_base_job(ssd->status.class); + + switch(ssd->skillid){ + case PR_BENEDICTIO: /* 聖体降福 */ + if(sd != ssd && (sd->status.class == 4 || sd->status.class == 8 || sd->status.class == 15 || + sd->status.class == 4005 || sd->status.class == 4009 || sd->status.class == 4016) && + (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) && sd->status.sp >= 10) + (*c)++; + break; + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + if(sd != ssd && + ((ssd->status.class==19 && sd->status.class==20) || + (ssd->status.class==20 && sd->status.class==19) || + (ssd->status.class==4020 && sd->status.class==4021) || + (ssd->status.class==4021 && sd->status.class==4020) || + (ssd->status.class==20 && sd->status.class==4020) || + (ssd->status.class==19 && sd->status.class==4021)) && + pc_checkskill(sd,ssd->skillid) > 0 && + (*c)==0 && + sd->status.party_id == ssd->status.party_id && + !pc_issit(sd) && + sd->sc_data[SC_DANCING].timer==-1 + ) + (*c)=pc_checkskill(sd,ssd->skillid); + break; + } + return 0; +} +/*========================================== + * 範囲内キャラ存在確認判定後スキル使用処理(foreachinarea) + *------------------------------------------ + */ + +static int skill_check_condition_use_sub(struct block_list *bl,va_list ap) +{ + int *c; + struct block_list *src; + struct map_session_data *sd; + struct map_session_data *ssd; + struct pc_base_job s_class; + struct pc_base_job ss_class; + int skillid,skilllv; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, sd=(struct map_session_data*)bl); + nullpo_retr(0, src=va_arg(ap,struct block_list *)); + nullpo_retr(0, c=va_arg(ap,int *)); + nullpo_retr(0, ssd=(struct map_session_data*)src); + + s_class = pc_calc_base_job(sd->status.class); + + //チェックしない設定ならcにありえない大きな数字を返して終了 + if(!battle_config.player_skill_partner_check){ //本当はforeachの前にやりたいけど設定適用箇所をまとめるためにここへ + (*c)=99; + return 0; + } + + ss_class = pc_calc_base_job(ssd->status.class); + skillid=ssd->skillid; + skilllv=ssd->skilllv; + switch(skillid){ + case PR_BENEDICTIO: /* 聖体降福 */ + if(sd != ssd && (sd->status.class == 4 || sd->status.class == 8 || sd->status.class == 15 || + sd->status.class == 4005 || sd->status.class == 4009 || sd->status.class == 4016) && + (sd->bl.x == ssd->bl.x - 1 || sd->bl.x == ssd->bl.x + 1) && sd->status.sp >= 10){ + sd->status.sp -= 10; + pc_calcstatus(sd,0); + (*c)++; + } + break; + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + if(sd != ssd && //本人以外で + ((ssd->status.class==19 && sd->status.class==20) || + (ssd->status.class==20 && sd->status.class==19) || + (ssd->status.class==4020 && sd->status.class==4021) || + (ssd->status.class==4021 && sd->status.class==4020) || + (ssd->status.class==20 && sd->status.class==4020) || + (ssd->status.class==19 && sd->status.class==4021)) && //自分がダンサーならバードで + pc_checkskill(sd,skillid) > 0 && //スキルを持っていて + (*c)==0 && //最初の一人で + sd->status.party_id == ssd->status.party_id && //パーティーが同じで + !pc_issit(sd) && //座ってない + sd->sc_data[SC_DANCING].timer==-1 //ダンス中じゃない + ){ + ssd->sc_data[SC_DANCING].val4=bl->id; + clif_skill_nodamage(bl,src,skillid,skilllv,1); + skill_status_change_start(bl,SC_DANCING,skillid,ssd->sc_data[SC_DANCING].val2,0,src->id,skill_get_time(skillid,skilllv)+1000,0); + sd->skillid_dance=sd->skillid=skillid; + sd->skilllv_dance=sd->skilllv=skilllv; + (*c)++; + } + break; + } + return 0; +} +/*========================================== + * 範囲内バイオプラント、スフィアマイン用Mob存在確認判定処理(foreachinarea) + *------------------------------------------ + */ + +static int skill_check_condition_mob_master_sub(struct block_list *bl,va_list ap) +{ + int *c,src_id=0,mob_class=0; + struct mob_data *md; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md=(struct mob_data*)bl); + 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; +} + +/*========================================== + * スキル使用条件(偽で使用失敗) + *------------------------------------------ + */ +int skill_check_condition(struct map_session_data *sd,int type) +{ + int i,hp,sp,hp_rate,sp_rate,zeny,weapon,state,spiritball,skill,lv,mhp; + int index[10],itemid[10],amount[10]; + + nullpo_retr(0, sd); + + if( battle_config.gm_skilluncond>0 && pc_isGM(sd)>= battle_config.gm_skilluncond ) { + sd->skillitem = sd->skillitemlv = -1; + return 1; + } + + if( sd->opt1>0) { + clif_skill_fail(sd,sd->skillid,0,0); + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + if(pc_is90overweight(sd)) { + clif_skill_fail(sd,sd->skillid,9,0); + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + + if(sd->skillid == AC_MAKINGARROW && sd->state.make_arrow_flag == 1) { + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + if(sd->skillid == AM_PHARMACY && sd->state.produce_flag == 1) { + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + + if(sd->skillitem == sd->skillid) { /* アイテムの場合無条件成功 */ + if(type&1) + sd->skillitem = sd->skillitemlv = -1; + return 1; + } + if( sd->opt1>0 ){ + clif_skill_fail(sd,sd->skillid,0,0); + return 0; + } + if(sd->sc_data){ + if( sd->sc_data[SC_DIVINA].timer!=-1 || + sd->sc_data[SC_ROKISWEIL].timer!=-1 || + (sd->sc_data[SC_AUTOCOUNTER].timer != -1 && sd->skillid != KN_AUTOCOUNTER) || + sd->sc_data[SC_STEELBODY].timer != -1 || + sd->sc_data[SC_BERSERK].timer != -1 + ){ + clif_skill_fail(sd,sd->skillid,0,0); + return 0; /* 状態異常や沈黙など */ + } + } + skill = sd->skillid; + lv = sd->skilllv; + hp=skill_get_hp(skill, lv); /* 消費HP */ + sp=skill_get_sp(skill, lv); /* 消費SP */ + if((sd->skillid_old == BD_ENCORE) && skill==sd->skillid_dance) + sp=sp/2; //アンコール時はSP消費が半分 + hp_rate = (lv <= 0)? 0:skill_db[skill].hp_rate[lv-1]; + sp_rate = (lv <= 0)? 0:skill_db[skill].sp_rate[lv-1]; + zeny = skill_get_zeny(skill,lv); + weapon = skill_db[skill].weapon; + state = skill_db[skill].state; + spiritball = (lv <= 0)? 0:skill_db[skill].spiritball[lv-1]; + mhp=skill_get_mhp(skill, lv); /* 消費HP */ + for(i=0;i<10;i++) { + itemid[i] = skill_db[skill].itemid[i]; + amount[i] = skill_db[skill].amount[i]; + } + if(mhp > 0) + hp += (sd->status.max_hp * mhp)/100; + if(hp_rate > 0) + hp += (sd->status.hp * hp_rate)/100; + else + hp += (sd->status.max_hp * abs(hp_rate))/100; + if(sp_rate > 0) + sp += (sd->status.sp * sp_rate)/100; + else + sp += (sd->status.max_sp * abs(sp_rate))/100; + if(sd->dsprate!=100) + sp=sp*sd->dsprate/100; /* 消費SP修正 */ + + switch(skill) { + case SA_CASTCANCEL: + if(sd->skilltimer == -1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case BS_MAXIMIZE: /* マキシマイズパワー */ + case NV_TRICKDEAD: /* 死んだふり */ + case TF_HIDING: /* ハイディング */ + case AS_CLOAKING: /* クローキング */ + case CR_AUTOGUARD: /* オートガード */ + case CR_DEFENDER: /* ディフェンダー */ + case ST_CHASEWALK: + if(sd->sc_data[SkillStatusChangeTable[skill]].timer!=-1) + return 1; /* 解除する場合はSP消費しない */ + break; + case AL_TELEPORT: + case AL_WARP: + if(map[sd->bl.m].flag.noteleport) { + clif_skill_teleportmessage(sd,0); + return 0; + } + break; + case MO_CALLSPIRITS: /* 気功 */ + if(sd->spiritball >= lv) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case CH_SOULCOLLECT: /* 狂気功 */ + if(sd->spiritball >= 5) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case MO_FINGEROFFENSIVE: //指弾 + if (sd->spiritball > 0 && sd->spiritball < spiritball) { + spiritball = sd->spiritball; + sd->spiritball_old = sd->spiritball; + } + else sd->spiritball_old = lv; + break; + case MO_CHAINCOMBO: //連打掌 + if(sd->sc_data[SC_BLADESTOP].timer==-1){ + if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_TRIPLEATTACK) + return 0; + } + break; + case MO_COMBOFINISH: //猛龍拳 + if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_CHAINCOMBO) + return 0; + break; + case CH_TIGERFIST: //伏虎拳 + if(sd->sc_data[SC_COMBO].timer == -1 || sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH) + return 0; + break; + case CH_CHAINCRUSH: //連柱崩撃 + if(sd->sc_data[SC_COMBO].timer == -1) + return 0; + if(sd->sc_data[SC_COMBO].val1 != MO_COMBOFINISH && sd->sc_data[SC_COMBO].val1 != CH_TIGERFIST) + return 0; + break; + case MO_EXTREMITYFIST: // 阿修羅覇鳳拳 + if((sd->sc_data[SC_COMBO].timer != -1 && (sd->sc_data[SC_COMBO].val1 == MO_COMBOFINISH || sd->sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) || sd->sc_data[SC_BLADESTOP].timer!=-1) + spiritball--; + break; + case BD_ADAPTATION: /* アドリブ */ + { + struct skill_unit_group *group=NULL; + if(sd->sc_data[SC_DANCING].timer==-1 || ((group=(struct skill_unit_group*)sd->sc_data[SC_DANCING].val2) && (skill_get_time(sd->sc_data[SC_DANCING].val1,group->skill_lv) - sd->sc_data[SC_DANCING].val3*1000) <= skill_get_time2(skill,lv))){ //ダンス中で使用後5秒以上のみ? + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + break; + case PR_BENEDICTIO: /* 聖体降福 */ + { + int range=1; + int c=0; + if(!(type&1)){ + map_foreachinarea(skill_check_condition_char_sub,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC,&sd->bl,&c); + if(c<2){ + clif_skill_fail(sd,skill,0,0); + return 0; + } + }else{ + map_foreachinarea(skill_check_condition_use_sub,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC,&sd->bl,&c); + } + } + break; + case WE_CALLPARTNER: /* あなたに逢いたい */ + if(!sd->status.partner_id){ + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case AM_CANNIBALIZE: /* バイオプラント */ + case AM_SPHEREMINE: /* スフィアーマイン */ + if(type&1){ + int c=0; + int maxcount=skill_get_maxcount(skill); + int mob_class=(skill==AM_CANNIBALIZE)?1118:1142; + if(battle_config.pc_land_skill_limit && maxcount>0) { + map_foreachinarea(skill_check_condition_mob_master_sub ,sd->bl.m, 0, 0, map[sd->bl.m].xs, map[sd->bl.m].ys, BL_MOB, sd->bl.id, mob_class,&c ); + if(c >= maxcount){ + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + } + break; + case MG_FIREWALL: /* ファイアーウォール */ + /* 数制限 */ + if(battle_config.pc_land_skill_limit) { + int maxcount = skill_get_maxcount(skill); + if(maxcount > 0) { + int i,c; + for(i=c=0;i<MAX_SKILLUNITGROUP;i++) { + if(sd->skillunit[i].alive_count > 0 && sd->skillunit[i].skill_id == skill) + c++; + } + if(c >= maxcount) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + } + break; + } + + if(!(type&2)){ + if( hp>0 && sd->status.hp < hp) { /* HPチェック */ + clif_skill_fail(sd,skill,2,0); /* HP不足:失敗通知 */ + return 0; + } + if( sp>0 && sd->status.sp < sp) { /* SPチェック */ + clif_skill_fail(sd,skill,1,0); /* SP不足:失敗通知 */ + return 0; + } + if( zeny>0 && sd->status.zeny < zeny) { + clif_skill_fail(sd,skill,5,0); + return 0; + } + if(!(weapon & (1<<sd->status.weapon) ) ) { + clif_skill_fail(sd,skill,6,0); + return 0; + } + if( spiritball > 0 && sd->spiritball < spiritball) { + clif_skill_fail(sd,skill,0,0); // 氣球不足 + return 0; + } + } + + switch(state) { + case ST_HIDING: + if(!(sd->status.option&2)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_CLOAKING: + if(!(sd->status.option&4)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_HIDDEN: + if(!pc_ishiding(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_RIDING: + if(!pc_isriding(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_FALCON: + if(!pc_isfalcon(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_CART: + if(!pc_iscarton(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_SHIELD: + if(sd->status.shield <= 0) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_SIGHT: + if(sd->sc_data[SC_SIGHT].timer == -1 && type&1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_EXPLOSIONSPIRITS: + if(sd->sc_data[SC_EXPLOSIONSPIRITS].timer == -1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_RECOV_WEIGHT_RATE: + if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_MOVE_ENABLE: + { + struct walkpath_data wpd; + if(path_search(&wpd,sd->bl.m,sd->bl.x,sd->bl.y,sd->skillx,sd->skilly,1)==-1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + break; + case ST_WATER: + if(map_getcell(sd->bl.m,sd->bl.x,sd->bl.y) != 3 && (sd->sc_data[SC_DELUGE].timer==-1)){ //水場判定 + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + } + + for(i=0;i<10;i++) { + int x = lv%11 - 1; + index[i] = -1; + if(itemid[i] <= 0) + continue; + if(itemid[i] >= 715 && itemid[i] <= 717 && sd->special_state.no_gemstone) + continue; + if(((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065) && sd->sc_data[SC_INTOABYSS].timer != -1) + continue; + if(skill == AM_POTIONPITCHER && i != x) + continue; + + index[i] = pc_search_inventory(sd,itemid[i]); + if(index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i]) { + if(itemid[i] == 716 || itemid[i] == 717) + clif_skill_fail(sd,skill,(7+(itemid[i]-716)),0); + else + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + + if(!(type&1)) + return 1; + + if(skill != AM_POTIONPITCHER) { + if(skill == AL_WARP && !(type&2)) + return 1; + for(i=0;i<10;i++) { + if(index[i] >= 0) + pc_delitem(sd,index[i],amount[i],0); // アイテム消費 + } + } + + if(type&2) + return 1; + + if(sp > 0) { // SP消費 + sd->status.sp-=sp; + clif_updatestatus(sd,SP_SP); + } + if(hp > 0) { // HP消費 + sd->status.hp-=hp; + clif_updatestatus(sd,SP_HP); + } + if(zeny > 0) // Zeny消費 + pc_payzeny(sd,zeny); + if(spiritball > 0) // 氣球消費 + pc_delspiritball(sd,spiritball,0); + + + return 1; +} + +/*========================================== + * 詠唱時間計算 + *------------------------------------------ + */ +int skill_castfix( struct block_list *bl, int time ) +{ + struct map_session_data *sd; + struct mob_data *md; // [Valaris] + struct status_change *sc_data; + int dex; + int castrate=100; + int skill,lv,castnodex; + + nullpo_retr(0, bl); + + if(bl->type==BL_MOB){ // Crash fix [Valaris] + md=(struct mob_data*)bl; + skill = md->skillid; + lv = md->skilllv; + } + + else { + sd=(struct map_session_data*)bl; + skill = sd->skillid; + lv = sd->skilllv; + } + + sc_data = battle_get_sc_data(bl); + dex=battle_get_dex(bl); + + if (skill > MAX_SKILL_DB || skill < 0) + return 0; + + castnodex=skill_get_castnodex(skill, lv); + + if(time==0) + return 0; + if(castnodex > 0 && bl->type==BL_PC) + castrate=((struct map_session_data *)bl)->castrate; + else if (castnodex <= 0 && bl->type==BL_PC) { + castrate=((struct map_session_data *)bl)->castrate; + time=time*castrate*(battle_config.castrate_dex_scale - dex)/(battle_config.castrate_dex_scale * 100); + time=time*battle_config.cast_rate/100; + } + + /* サフラギウム */ + if(sc_data && sc_data[SC_SUFFRAGIUM].timer!=-1 ){ + time=time*(100-sc_data[SC_SUFFRAGIUM].val1*15)/100; + skill_status_change_end( bl, SC_SUFFRAGIUM, -1); + } + /* ブラギの詩 */ + if(sc_data && sc_data[SC_POEMBRAGI].timer!=-1 ) + time=time*(100-(sc_data[SC_POEMBRAGI].val1*3+sc_data[SC_POEMBRAGI].val2 + +(sc_data[SC_POEMBRAGI].val3>>16)))/100; + + return (time>0)?time:0; +} +/*========================================== + * ディレイ計算 + *------------------------------------------ + */ +int skill_delayfix( struct block_list *bl, int time ) +{ + struct status_change *sc_data; + + nullpo_retr(0, bl); + + sc_data = battle_get_sc_data(bl); + if(time<=0) + return 0; + + if(bl->type == BL_PC) { + if( battle_config.delay_dependon_dex ) /* dexの影響を計算する */ + time=time*(battle_config.castrate_dex_scale - battle_get_dex(bl))/battle_config.castrate_dex_scale; + time=time*battle_config.delay_rate/100; + } + + /* ブラギの詩 */ + if(sc_data && sc_data[SC_POEMBRAGI].timer!=-1 ) + time=time*(100-(sc_data[SC_POEMBRAGI].val1*3+sc_data[SC_POEMBRAGI].val2 + +(sc_data[SC_POEMBRAGI].val3&0xffff)))/100; + + return (time>0)?time:0; +} + +/*========================================== + * スキル使用(ID指定) + *------------------------------------------ + */ +int skill_use_id( struct map_session_data *sd, int target_id, + int skill_num, int skill_lv) +{ + unsigned int tick; + int casttime=0,delay=0,skill,range; + struct map_session_data* target_sd=NULL; + int forcecast=0; + struct block_list *bl; + struct status_change *sc_data; + tick=gettick(); + + nullpo_retr(0, sd); + + if( (bl=map_id2bl(target_id)) == NULL ){ +/* if(battle_config.error_log) + printf("skill target not found %d\n",target_id); */ + return 0; + } + if(sd->bl.m != bl->m || pc_isdead(sd)) + return 0; + + if(skillnotok(skill_num, sd)) // [MouseJstr] + return 0; + + if(sd->skillid==WZ_ICEWALL && map[sd->bl.m].flag.noicewall && !map[sd->bl.m].flag.pvp) { // noicewall flag [Valaris] + clif_skill_fail(sd,sd->skillid,0,0); + return 0; + } + sc_data=sd->sc_data; + + /* 沈黙や異常(ただし、グリムなどの判定をする) */ + if( sd->opt1>0 ) + return 0; + if(sd->sc_data){ + if(sc_data[SC_CHASEWALK].timer != -1) return 0; + if(sc_data[SC_VOLCANO].timer != -1){ + if(skill_num==WZ_ICEWALL) return 0; + } + if(sc_data[SC_ROKISWEIL].timer!=-1){ + if(skill_num==BD_ADAPTATION) return 0; + } + if( sd->sc_data[SC_DIVINA].timer!=-1 || + sd->sc_data[SC_ROKISWEIL].timer!=-1 || + (sd->sc_data[SC_AUTOCOUNTER].timer != -1 && sd->skillid != KN_AUTOCOUNTER) || + sd->sc_data[SC_STEELBODY].timer != -1 || + sd->sc_data[SC_BERSERK].timer != -1 ){ + return 0; /* 状態異常や沈黙など */ + } + + if(sc_data[SC_BLADESTOP].timer != -1){ + int lv = sc_data[SC_BLADESTOP].val1; + if(sc_data[SC_BLADESTOP].val2==1) return 0;//白羽された側なのでダメ + if(lv==1) return 0; + if(lv==2 && skill_num!=MO_FINGEROFFENSIVE) return 0; + if(lv==3 && skill_num!=MO_FINGEROFFENSIVE && skill_num!=MO_INVESTIGATE) return 0; + if(lv==4 && skill_num!=MO_FINGEROFFENSIVE && skill_num!=MO_INVESTIGATE && skill_num!=MO_CHAINCOMBO) return 0; + if(lv==5 && skill_num!=MO_FINGEROFFENSIVE && skill_num!=MO_INVESTIGATE && skill_num!=MO_CHAINCOMBO && skill_num!=MO_EXTREMITYFIST) return 0; + } + } + + if(sd->status.option&4 && skill_num==TF_HIDING) + return 0; + if(sd->status.option&2 && skill_num!=TF_HIDING && skill_num!=AS_GRIMTOOTH && skill_num!=RG_BACKSTAP && skill_num!=RG_RAID ) + return 0; + + if(map[sd->bl.m].flag.gvg){ //GvGで使用できないスキル + switch(skill_num){ + case SM_ENDURE: + case AL_TELEPORT: + case AL_WARP: + case WZ_ICEWALL: + case TF_BACKSLIDING: + case LK_BERSERK: + case HP_BASILICA: + case ST_CHASEWALK: + return 0; + } + } + + /* 演奏/ダンス中 */ + if( sc_data && sc_data[SC_DANCING].timer!=-1 ){ +// if(battle_config.pc_skill_log) +// printf("dancing! %d\n",skill_num); + if( sc_data[SC_DANCING].val4 && skill_num!=BD_ADAPTATION ) //合奏中はアドリブ以外不可 + return 0; + if(skill_num!=BD_ADAPTATION && skill_num!=BA_MUSICALSTRIKE && skill_num!=DC_THROWARROW){ + return 0; + } + } + + if(skill_get_inf2(skill_num)&0x200 && sd->bl.id == target_id) + return 0; + //直前のスキルが何か覚える必要のあるスキル + switch(skill_num){ + case SA_CASTCANCEL: + if(sd->skillid != skill_num){ //キャストキャンセル自体は覚えない + sd->skillid_old = sd->skillid; + sd->skilllv_old = sd->skilllv; + break; + } + case BD_ENCORE: /* アンコール */ + if(!sd->skillid_dance){ //前回使用した踊りがないとだめ + clif_skill_fail(sd,skill_num,0,0); + return 0; + }else{ + sd->skillid_old = skill_num; + } + break; + } + + sd->skillid = skill_num; + sd->skilllv = skill_lv; + + switch(skill_num){ //事前にレベルが変わったりするスキル + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + { + int range=1; + int c=0; + map_foreachinarea(skill_check_condition_char_sub,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC,&sd->bl,&c); + if(c<1){ + clif_skill_fail(sd,skill_num,0,0); + return 0; + }else if(c==99){ //相方不要設定だった + ; + }else{ + sd->skilllv=(c + skill_lv)/2; + } + } + break; + } + + if(!skill_check_condition(sd,0)) return 0; + + /* 射程と障害物チェック */ + range = skill_get_range(skill_num,skill_lv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + if(!battle_check_range(&sd->bl,bl,range) ) + return 0; + + if(bl->type==BL_PC) { + target_sd=(struct map_session_data*)bl; + if(target_sd && skill_num == ALL_RESURRECTION && !pc_isdead(target_sd)) + return 0; + } + if((skill_num != MO_CHAINCOMBO && + skill_num != MO_COMBOFINISH && + skill_num != MO_EXTREMITYFIST && + skill_num != CH_TIGERFIST && + skill_num != CH_CHAINCRUSH) || + (skill_num == MO_EXTREMITYFIST && sd->state.skill_flag) ) + pc_stopattack(sd); + + casttime=skill_castfix(&sd->bl, skill_get_cast( skill_num,skill_lv) ); + if(skill_num != SA_MAGICROD) + delay=skill_delayfix(&sd->bl, skill_get_delay( skill_num,skill_lv) ); + sd->state.skillcastcancel = skill_db[skill_num].castcancel; + + switch(skill_num){ /* 何か特殊な処理が必要 */ +// case AL_HEAL: /* ヒール */ +// if(battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))) +// forcecast=1; /* ヒールアタックなら詠唱エフェクト有り */ +// break; + case ALL_RESURRECTION: /* リザレクション */ + if(bl->type != BL_PC && battle_check_undead(battle_get_race(bl),battle_get_elem_type(bl))){ /* 敵がアンデッドなら */ + forcecast=1; /* ターンアンデットと同じ詠唱時間 */ + casttime=skill_castfix(&sd->bl, skill_get_cast(PR_TURNUNDEAD,skill_lv) ); + } + break; + case MO_FINGEROFFENSIVE: /* 指弾 */ + casttime += casttime * ((skill_lv > sd->spiritball)? sd->spiritball:skill_lv); + break; + case MO_CHAINCOMBO: /*連打掌*/ + target_id = sd->attacktarget; + if( sc_data && sc_data[SC_BLADESTOP].timer!=-1 ){ + struct block_list *tbl; + if((tbl=(struct block_list *)sc_data[SC_BLADESTOP].val4) == NULL) //ターゲットがいない? + return 0; + target_id = tbl->id; + } + break; + case MO_COMBOFINISH: /*猛龍拳*/ + case CH_TIGERFIST: /* 伏虎拳 */ + case CH_CHAINCRUSH: /* 連柱崩撃 */ + target_id = sd->attacktarget; + break; + +// -- moonsoul (altered to allow proper usage of extremity from new champion combos) +// + case MO_EXTREMITYFIST: /*阿修羅覇鳳拳*/ + if(sc_data && sc_data[SC_COMBO].timer != -1 && (sc_data[SC_COMBO].val1 == MO_COMBOFINISH || sc_data[SC_COMBO].val1 == CH_CHAINCRUSH)) { + casttime = 0; + target_id = sd->attacktarget; + } + forcecast=1; + break; + case SA_MAGICROD: + case SA_SPELLBREAKER: + forcecast=1; + break; + case WE_MALE: + case WE_FEMALE: + { + struct map_session_data *p_sd = NULL; + if((p_sd = pc_get_partner(sd)) == NULL) + return 0; + target_id = p_sd->bl.id; + //rangeをもう1回検査 + range = skill_get_range(skill_num,skill_lv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + if(!battle_check_range(&sd->bl,&p_sd->bl,range) ){ + return 0; + } + } + break; + case AS_SPLASHER: /* ベナムスプラッシャー */ + { + struct status_change *t_sc_data = battle_get_sc_data(bl); + if(t_sc_data && t_sc_data[SC_POISON].timer==-1){ + clif_skill_fail(sd,skill_num,0,10); + return 0; + } + } + break; + case PF_MEMORIZE: /* メモライズ */ + casttime = 12000; + break; + + } + + //メモライズ状態ならキャストタイムが1/3 + if(sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0){ + casttime = casttime/3; + if((--sc_data[SC_MEMORIZE].val2)<=0) + skill_status_change_end(&sd->bl, SC_MEMORIZE, -1); + } + + if(battle_config.pc_skill_log) + printf("PC %d skill use target_id=%d skill=%d lv=%d cast=%d\n",sd->bl.id,target_id,skill_num,skill_lv,casttime); + +// if(sd->skillitem == skill_num) +// casttime = delay = 0; + + if( casttime>0 || forcecast ){ /* 詠唱が必要 */ + struct mob_data *md; + clif_skillcasting( &sd->bl, + sd->bl.id, target_id, 0,0, skill_num,casttime); + + /* 詠唱反応モンスター */ + if( bl->type==BL_MOB && (md=(struct mob_data *)bl) && mob_db[md->class].mode&0x10 && + md->state.state!=MS_ATTACK && sd->invincible_timer == -1){ + md->target_id=sd->bl.id; + md->state.targettype = ATTACKABLE; + md->min_chase=13; + } + } + + if( casttime<=0 ) /* 詠唱の無いものはキャンセルされない */ + sd->state.skillcastcancel=0; + + sd->skilltarget = target_id; +/* sd->cast_target_bl = bl; */ + sd->skillx = 0; + sd->skilly = 0; + sd->canact_tick = tick + casttime + delay; + sd->canmove_tick = tick; + if(!(battle_config.pc_cloak_check_type&2) && sc_data && sc_data[SC_CLOAKING].timer != -1 && sd->skillid != AS_CLOAKING) + skill_status_change_end(&sd->bl,SC_CLOAKING,-1); + if(casttime > 0) { + sd->skilltimer = add_timer( tick+casttime, skill_castend_id, sd->bl.id, 0 ); + if((skill = pc_checkskill(sd,SA_FREECAST)) > 0) { + sd->prev_speed = sd->speed; + sd->speed = sd->speed*(175 - skill*5)/100; + clif_updatestatus(sd,SP_SPEED); + } + else + pc_stop_walking(sd,0); + } + else { + if(skill_num != SA_CASTCANCEL) + sd->skilltimer = -1; + skill_castend_id(sd->skilltimer,tick,sd->bl.id,0); + } + + //マジックパワーの効果終了 + if(sc_data && sc_data[SC_MAGICPOWER].timer != -1 && skill_num != HW_MAGICPOWER) + skill_status_change_end(&sd->bl,SC_MAGICPOWER,-1); + + return 0; +} + +/*========================================== + * スキル使用(場所指定) + *------------------------------------------ + */ +int skill_use_pos( struct map_session_data *sd, + int skill_x, int skill_y, int skill_num, int skill_lv) +{ + struct block_list bl; + struct status_change *sc_data; + unsigned int tick; + int casttime=0,delay=0,skill,range; + + nullpo_retr(0, sd); + + if(pc_isdead(sd)) + return 0; + + if (skillnotok(skill_num, sd)) // [MoueJstr] + return 0; + + sc_data=sd->sc_data; + + if( sd->opt1>0 ) + return 0; + if(sc_data){ + if( sc_data[SC_DIVINA].timer!=-1 || + sc_data[SC_ROKISWEIL].timer!=-1 || + sc_data[SC_AUTOCOUNTER].timer != -1 || + sc_data[SC_STEELBODY].timer != -1 || + sc_data[SC_DANCING].timer!=-1 || + sc_data[SC_BERSERK].timer != -1 ) + return 0; /* 状態異常や沈黙など */ + } + + if(sd->status.option&2) + return 0; + + if(map[sd->bl.m].flag.gvg && (skill_num == SM_ENDURE || skill_num == AL_TELEPORT || skill_num == AL_WARP || + skill_num == WZ_ICEWALL || skill_num == TF_BACKSLIDING)) + return 0; + + sd->skillid = skill_num; + sd->skilllv = skill_lv; + sd->skillx = skill_x; + sd->skilly = skill_y; + if(!skill_check_condition(sd,0)) return 0; + + /* 射程と障害物チェック */ + bl.type = BL_NUL; + bl.m = sd->bl.m; + bl.x = skill_x; + bl.y = skill_y; + range = skill_get_range(skill_num,skill_lv); + if(range < 0) + range = battle_get_range(&sd->bl) - (range + 1); + if(!battle_check_range(&sd->bl,&bl,range) ) + return 0; + + pc_stopattack(sd); + + casttime=skill_castfix(&sd->bl, skill_get_cast( skill_num,skill_lv) ); + delay=skill_delayfix(&sd->bl, skill_get_delay( skill_num,skill_lv) ); + sd->state.skillcastcancel = skill_db[skill_num].castcancel; + + if(battle_config.pc_skill_log) + printf("PC %d skill use target_pos=(%d,%d) skill=%d lv=%d cast=%d\n",sd->bl.id,skill_x,skill_y,skill_num,skill_lv,casttime); + +// if(sd->skillitem == skill_num) +// casttime = delay = 0; + //メモライズ状態ならキャストタイムが1/3 + if(sc_data && sc_data[SC_MEMORIZE].timer != -1 && casttime > 0){ + casttime = casttime/3; + if((--sc_data[SC_MEMORIZE].val2)<=0) + skill_status_change_end(&sd->bl, SC_MEMORIZE, -1); + } + + if( casttime>0 ) /* 詠唱が必要 */ + clif_skillcasting( &sd->bl, + sd->bl.id, 0, skill_x,skill_y, skill_num,casttime); + + if( casttime<=0 ) /* 詠唱の無いものはキャンセルされない */ + sd->state.skillcastcancel=0; + + sd->skilltarget = 0; +/* sd->cast_target_bl = NULL; */ + tick=gettick(); + sd->canact_tick = tick + casttime + delay; + sd->canmove_tick = tick; + if(!(battle_config.pc_cloak_check_type&2) && sc_data && sc_data[SC_CLOAKING].timer != -1) + skill_status_change_end(&sd->bl,SC_CLOAKING,-1); + if(casttime > 0) { + sd->skilltimer = add_timer( tick+casttime, skill_castend_pos, sd->bl.id, 0 ); + if((skill = pc_checkskill(sd,SA_FREECAST)) > 0) { + sd->prev_speed = sd->speed; + sd->speed = sd->speed*(175 - skill*5)/100; + clif_updatestatus(sd,SP_SPEED); + } + else + pc_stop_walking(sd,0); + } + else { + sd->skilltimer = -1; + skill_castend_pos(sd->skilltimer,tick,sd->bl.id,0); + } + //マジックパワーの効果終了 + if(sc_data && sc_data[SC_MAGICPOWER].timer != -1 && skill_num != HW_MAGICPOWER) + skill_status_change_end(&sd->bl,SC_MAGICPOWER,-1); + + return 0; +} + +/*========================================== + * スキル詠唱キャンセル + *------------------------------------------ + */ +int skill_castcancel(struct block_list *bl,int type) +{ + int inf; + 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(pc_checkskill(sd,SA_FREECAST) > 0) { + sd->speed = sd->prev_speed; + clif_updatestatus(sd,SP_SPEED); + } + if(!type) { + if((inf = skill_get_inf( sd->skillid )) == 2 || inf == 32) + ret=delete_timer( sd->skilltimer, skill_castend_pos ); + else + ret=delete_timer( sd->skilltimer, skill_castend_id ); + if(ret<0) + printf("delete timer error : skillid : %d\n",sd->skillid); + } + else { + if((inf = skill_get_inf( sd->skillid_old )) == 2 || inf == 32) + ret=delete_timer( sd->skilltimer, skill_castend_pos ); + else + ret=delete_timer( sd->skilltimer, skill_castend_id ); + if(ret<0) + printf("delete timer error : skillid : %d\n",sd->skillid_old); + } + sd->skilltimer=-1; + clif_skillcastcancel(bl); + } + + return 0; + }else if(bl->type==BL_MOB){ + struct mob_data *md=(struct mob_data *)bl; + nullpo_retr(0, md); + if( md->skilltimer!=-1 ){ + if((inf = skill_get_inf( md->skillid )) == 2 || inf == 32) + ret=delete_timer( md->skilltimer, mobskill_castend_pos ); + else + ret=delete_timer( md->skilltimer, mobskill_castend_id ); + md->skilltimer=-1; + clif_skillcastcancel(bl); + } + if(ret<0) + printf("delete timer error : skillid : %d\n",md->skillid); + return 0; + } + return 1; +} +/*========================================= + * ブランディッシュスピア 初期範囲決定 + *---------------------------------------- + */ +void skill_brandishspear_first(struct square *tc,int dir,int x,int y){ + + nullpo_retv(tc); + + if(dir == 0){ + tc->val1[0]=x-2; + tc->val1[1]=x-1; + tc->val1[2]=x; + tc->val1[3]=x+1; + tc->val1[4]=x+2; + tc->val2[0]= + tc->val2[1]= + tc->val2[2]= + tc->val2[3]= + tc->val2[4]=y-1; + } + else if(dir==2){ + tc->val1[0]= + tc->val1[1]= + tc->val1[2]= + tc->val1[3]= + tc->val1[4]=x+1; + tc->val2[0]=y+2; + tc->val2[1]=y+1; + tc->val2[2]=y; + tc->val2[3]=y-1; + tc->val2[4]=y-2; + } + else if(dir==4){ + tc->val1[0]=x-2; + tc->val1[1]=x-1; + tc->val1[2]=x; + tc->val1[3]=x+1; + tc->val1[4]=x+2; + tc->val2[0]= + tc->val2[1]= + tc->val2[2]= + tc->val2[3]= + tc->val2[4]=y+1; + } + else if(dir==6){ + tc->val1[0]= + tc->val1[1]= + tc->val1[2]= + tc->val1[3]= + tc->val1[4]=x-1; + tc->val2[0]=y+2; + tc->val2[1]=y+1; + tc->val2[2]=y; + tc->val2[3]=y-1; + tc->val2[4]=y-2; + } + else if(dir==1){ + tc->val1[0]=x-1; + tc->val1[1]=x; + tc->val1[2]=x+1; + tc->val1[3]=x+2; + tc->val1[4]=x+3; + tc->val2[0]=y-4; + tc->val2[1]=y-3; + tc->val2[2]=y-1; + tc->val2[3]=y; + tc->val2[4]=y+1; + } + else if(dir==3){ + tc->val1[0]=x+3; + tc->val1[1]=x+2; + tc->val1[2]=x+1; + tc->val1[3]=x; + tc->val1[4]=x-1; + tc->val2[0]=y-1; + tc->val2[1]=y; + tc->val2[2]=y+1; + tc->val2[3]=y+2; + tc->val2[4]=y+3; + } + else if(dir==5){ + tc->val1[0]=x+1; + tc->val1[1]=x; + tc->val1[2]=x-1; + tc->val1[3]=x-2; + tc->val1[4]=x-3; + tc->val2[0]=y+3; + tc->val2[1]=y+2; + tc->val2[2]=y+1; + tc->val2[3]=y; + tc->val2[4]=y-1; + } + else if(dir==7){ + tc->val1[0]=x-3; + tc->val1[1]=x-2; + tc->val1[2]=x-1; + tc->val1[3]=x; + tc->val1[4]=x+1; + tc->val2[1]=y; + tc->val2[0]=y+1; + tc->val2[2]=y-1; + tc->val2[3]=y-2; + tc->val2[4]=y-3; + } + +} + +/*========================================= + * ブランディッシュスピア 方向判定 範囲拡張 + *----------------------------------------- + */ +void skill_brandishspear_dir(struct square *tc,int dir,int are){ + + int c; + + nullpo_retv(tc); + + for(c=0;c<5;c++){ + if(dir==0){ + tc->val2[c]+=are; + }else if(dir==1){ + tc->val1[c]-=are; tc->val2[c]+=are; + }else if(dir==2){ + tc->val1[c]-=are; + }else if(dir==3){ + tc->val1[c]-=are; tc->val2[c]-=are; + }else if(dir==4){ + tc->val2[c]-=are; + }else if(dir==5){ + tc->val1[c]+=are; tc->val2[c]-=are; + }else if(dir==6){ + tc->val1[c]+=are; + }else if(dir==7){ + tc->val1[c]+=are; tc->val2[c]+=are; + } + } +} + +/*========================================== + * ディボーション 有効確認 + *------------------------------------------ + */ +void skill_devotion(struct map_session_data *md,int target) +{ + // 総確認 + int n; + + nullpo_retv(md); + + for(n=0;n<5;n++){ + if(md->dev.val1[n]){ + struct map_session_data *sd = map_id2sd(md->dev.val1[n]); + // 相手が見つからない // 相手をディボしてるのが自分じゃない // 距離が離れてる + if( sd == NULL || (sd->sc_data && (md->bl.id != sd->sc_data[SC_DEVOTION].val1)) || skill_devotion3(&md->bl,md->dev.val1[n])){ + skill_devotion_end(md,sd,n); + } + } + } +} +void skill_devotion2(struct block_list *bl,int crusader) +{ + // 被ディボーションが歩いた時の距離チェック + struct map_session_data *sd = map_id2sd(crusader); + + nullpo_retv(bl); + + if(sd) skill_devotion3(&sd->bl,bl->id); +} +int skill_devotion3(struct block_list *bl,int target) +{ + // クルセが歩いた時の距離チェック + struct map_session_data *md; + struct map_session_data *sd; + int n,r=0; + + nullpo_retr(1, bl); + + if( (md = (struct map_session_data *)bl) == NULL || (sd = map_id2sd(target)) == NULL ) + return 1; + else + r = distance(bl->x,bl->y,sd->bl.x,sd->bl.y); + + if(pc_checkskill(sd,CR_DEVOTION)+6 < r){ // 許容範囲を超えてた + for(n=0;n<5;n++) + if(md->dev.val1[n]==target) + md->dev.val2[n]=0; // 離れた時は、糸を切るだけ + clif_devotion(md,sd->bl.id); + return 1; + } + return 0; +} + +void skill_devotion_end(struct map_session_data *md,struct map_session_data *sd,int target) +{ + // クルセと被ディボキャラのリセット + nullpo_retv(md); + nullpo_retv(sd); + + md->dev.val1[target]=md->dev.val2[target]=0; + if(sd && sd->sc_data){ + // skill_status_change_end(sd->bl,SC_DEVOTION,-1); + sd->sc_data[SC_DEVOTION].val1=0; + sd->sc_data[SC_DEVOTION].val2=0; + clif_status_change(&sd->bl,SC_DEVOTION,0); + clif_devotion(md,sd->bl.id); + } +} +/*========================================== + * オートスペル + *------------------------------------------ + */ +int skill_autospell(struct map_session_data *sd,int skillid) +{ + int skilllv; + int maxlv=1,lv; + + nullpo_retr(0, sd); + + skilllv = pc_checkskill(sd,SA_AUTOSPELL); + + if(skillid==MG_NAPALMBEAT) maxlv=3; + else if(skillid==MG_COLDBOLT || skillid==MG_FIREBOLT || skillid==MG_LIGHTNINGBOLT){ + if(skilllv==2) maxlv=1; + else if(skilllv==3) maxlv=2; + else if(skilllv>=4) maxlv=3; + } + else if(skillid==MG_SOULSTRIKE){ + if(skilllv==5) maxlv=1; + else if(skilllv==6) maxlv=2; + else if(skilllv>=7) maxlv=3; + } + else if(skillid==MG_FIREBALL){ + if(skilllv==8) maxlv=1; + else if(skilllv>=9) maxlv=2; + } + else if(skillid==MG_FROSTDIVER) maxlv=1; + else return 0; + + if(maxlv > (lv=pc_checkskill(sd,skillid))) + maxlv = lv; + + skill_status_change_start(&sd->bl,SC_AUTOSPELL,skilllv,skillid,maxlv,0, // val1:スキルID val2:使用最大Lv + skill_get_time(SA_AUTOSPELL,skilllv),0);// にしてみたけどbscriptが書き易い・・・? + return 0; +} + +/*========================================== + * ギャングスターパラダイス判定処理(foreachinarea) + *------------------------------------------ + */ + +static int skill_gangster_count(struct block_list *bl,va_list ap) +{ + int *c; + struct map_session_data *sd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + sd=(struct map_session_data*)bl; + c=va_arg(ap,int *); + + if(sd && c && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0) + (*c)++; + return 0; +} + +static int skill_gangster_in(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + sd=(struct map_session_data*)bl; + if(sd && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0) + sd->state.gangsterparadise=1; + return 0; +} + +static int skill_gangster_out(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + sd=(struct map_session_data*)bl; + if(sd && sd->state.gangsterparadise) + sd->state.gangsterparadise=0; + return 0; +} + +int skill_gangsterparadise(struct map_session_data *sd ,int type) +{ + int range=1; + int c=0; + + nullpo_retr(0, sd); + + if(pc_checkskill(sd,RG_GANGSTER) <= 0) + return 0; + + if(type==1) {/* 座った時の処理 */ + map_foreachinarea(skill_gangster_count,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC,&c); + if(c > 0) {/*ギャングスター成功したら自分にもギャングスター属性付与*/ + map_foreachinarea(skill_gangster_in,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC); + sd->state.gangsterparadise = 1; + } + return 0; + } + else if(type==0) {/* 立ち上がったときの処理 */ + map_foreachinarea(skill_gangster_count,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC,&c); + if(c < 1) + map_foreachinarea(skill_gangster_out,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC); + sd->state.gangsterparadise = 0; + return 0; + } + return 0; +} +/*========================================== + * 寒いジョーク・スクリーム判定処理(foreachinarea) + *------------------------------------------ + */ +int skill_frostjoke_scream(struct block_list *bl,va_list ap) +{ + struct block_list *src; + int skillnum,skilllv; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, src=va_arg(ap,struct block_list*)); + + skillnum=va_arg(ap,int); + skilllv=va_arg(ap,int); + tick=va_arg(ap,unsigned int); + + if(src == bl)//自分には効かない + return 0; + + if(battle_check_target(src,bl,BCT_ENEMY) > 0) + skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick); + else if(battle_check_target(src,bl,BCT_PARTY) > 0) { + if(rand()%100 < 10)//PTメンバにも低確率でかかる(とりあえず10%) + skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick); + } + + return 0; +} + +/*========================================== + *アブラカダブラの使用スキル決定(決定スキルがダメなら0を返す) + *------------------------------------------ + */ +int skill_abra_dataset(int skilllv) +{ + int skill = rand()%331; + //dbに基づくレベル・確率判定 + if(skill_abra_db[skill].req_lv > skilllv || rand()%10000 >= skill_abra_db[skill].per) return 0; + //NPCスキルはダメ + if(skill >= NPC_PIERCINGATT && skill <= NPC_SUMMONMONSTER) return 0; + //演奏スキルはダメ + if(skill_is_danceskill(skill)) return 0; + + return skill; +} + +/*========================================== + * + *------------------------------------------ + */ +int skill_attack_area(struct block_list *bl,va_list ap) +{ + struct block_list *src,*dsrc; + int atk_type,skillid,skilllv,flag,type; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + atk_type = va_arg(ap,int); + if((src=va_arg(ap,struct block_list*)) == NULL) + return 0; + if((dsrc=va_arg(ap,struct block_list*)) == NULL) + return 0; + skillid=va_arg(ap,int); + skilllv=va_arg(ap,int); + tick=va_arg(ap,unsigned int); + flag=va_arg(ap,int); + type=va_arg(ap,int); + + if(battle_check_target(dsrc,bl,type) > 0) + skill_attack(atk_type,src,dsrc,bl,skillid,skilllv,tick,flag); + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int skill_clear_element_field(struct block_list *bl) +{ + struct mob_data *md=NULL; + struct map_session_data *sd=NULL; + int i,skillid; + + nullpo_retr(0, bl); + + if(bl->type==BL_MOB) + md=(struct mob_data *)bl; + if(bl->type==BL_PC) + sd=(struct map_session_data *)bl; + + for(i=0;i<MAX_MOBSKILLUNITGROUP;i++){ + if(sd){ + skillid=sd->skillunit[i].skill_id; + if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR) + skill_delunitgroup(&sd->skillunit[i]); + }else if(md){ + skillid=md->skillunit[i].skill_id; + if(skillid==SA_DELUGE||skillid==SA_VOLCANO||skillid==SA_VIOLENTGALE||skillid==SA_LANDPROTECTOR) + skill_delunitgroup(&md->skillunit[i]); + } + } + return 0; +} +/*========================================== + * ランドプロテクターチェック(foreachinarea) + *------------------------------------------ + */ +int skill_landprotector(struct block_list *bl, va_list ap ) +{ + int skillid; + int *alive; + struct skill_unit *unit; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + skillid=va_arg(ap,int); + alive=va_arg(ap,int *); + if((unit=(struct skill_unit *)bl) == NULL) + return 0; + + if(skillid==SA_LANDPROTECTOR){ + skill_delunit(unit); + }else{ + if(alive && unit->group->skill_id==SA_LANDPROTECTOR) + (*alive)=0; + } + return 0; +} +/*========================================== + * イドゥンの林檎の回復処理(foreachinarea) + *------------------------------------------ + */ +int skill_idun_heal(struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + struct skill_unit_group *sg; + int heal; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, unit = va_arg(ap,struct skill_unit *)); + nullpo_retr(0, sg = unit->group); + + heal=30+sg->skill_lv*5+((sg->val1)>>16)*5+((sg->val1)&0xfff)/2; + + if(bl->type == BL_SKILL || bl->id == sg->src_id) + return 0; + + if(bl->type == BL_PC || bl->type == BL_MOB){ + clif_skill_nodamage(&unit->bl,bl,AL_HEAL,heal,1); + battle_heal(NULL,bl,heal,0,0); + } + return 0; +} + +/*========================================== + * 指定範囲内でsrcに対して有効なターゲットのblの数を数える(foreachinarea) + *------------------------------------------ + */ +int skill_count_target(struct block_list *bl, va_list ap ){ + struct block_list *src; + int *c; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + if((src = va_arg(ap,struct block_list *)) == NULL) + return 0; + if((c = va_arg(ap,int *)) == NULL) + return 0; + if(battle_check_target(src,bl,BCT_ENEMY) > 0) + (*c)++; + return 0; +} +/*========================================== + * トラップ範囲処理(foreachinarea) + *------------------------------------------ + */ +int skill_trap_splash(struct block_list *bl, va_list ap ) +{ + struct block_list *src; + int tick; + int splash_count; + struct skill_unit *unit; + struct skill_unit_group *sg; + struct block_list *ss; + int i; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, src = va_arg(ap,struct block_list *)); + nullpo_retr(0, unit = (struct skill_unit *)src); + nullpo_retr(0, sg = unit->group); + nullpo_retr(0, ss = map_id2bl(sg->src_id)); + + tick = va_arg(ap,int); + splash_count = va_arg(ap,int); + + if(battle_check_target(src,bl,BCT_ENEMY) > 0){ + switch(sg->unit_id){ + case 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x94: /* ショックウェーブトラップ */ + skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick); + break; + case 0x8f: /* ブラストマイン */ + case 0x98: /* クレイモアートラップ */ + for(i=0;i<splash_count;i++){ + skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,(sg->val2)?0x0500:0); + } + case 0x97: /* フリージングトラップ */ + skill_attack(BF_WEAPON, ss,src,bl,sg->skill_id,sg->skill_lv,tick,(sg->val2)?0x0500:0); + break; + default: + break; + } + } + + return 0; +} +/*---------------------------------------------------------------------------- + * ステータス異常 + *---------------------------------------------------------------------------- + */ + +/*========================================== + * ステータス異常タイマー範囲処理 + *------------------------------------------ + */ +int skill_status_change_timer_sub(struct block_list *bl, va_list ap ) +{ + struct block_list *src; + int type; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, src=va_arg(ap,struct block_list*)); + type=va_arg(ap,int); + tick=va_arg(ap,unsigned int); + + if(bl->type!=BL_PC && bl->type!=BL_MOB) + return 0; + + switch( type ){ + case SC_SIGHT: /* サイト */ + case SC_CONCENTRATE: + if( (*battle_get_option(bl))&6 ){ + skill_status_change_end( bl, SC_HIDING, -1); + skill_status_change_end( bl, SC_CLOAKING, -1); + } + break; + case SC_RUWACH: /* ルアフ */ + if( (*battle_get_option(bl))&6 ){ + skill_status_change_end( bl, SC_HIDING, -1); + skill_status_change_end( bl, SC_CLOAKING, -1); + if(battle_check_target( src,bl, BCT_ENEMY ) > 0) { + struct status_change *sc_data = battle_get_sc_data(bl); + skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,sc_data[type].val1,tick,0); + } + } + break; + } + return 0; +} + +/*========================================== + * ステータス異常終了 + *------------------------------------------ + */ +int skill_status_change_end(struct block_list* bl, int type, int tid) +{ + struct status_change* sc_data; + int opt_flag=0, calc_flag = 0; + short *sc_count, *option, *opt1, *opt2, *opt3; + + nullpo_retr(0, bl); + if(bl->type!=BL_PC && bl->type!=BL_MOB) { + if(battle_config.error_log) + printf("skill_status_change_end: neither MOB nor PC !\n"); + return 0; + } + nullpo_retr(0, sc_data = battle_get_sc_data(bl)); + nullpo_retr(0, sc_count = battle_get_sc_count(bl)); + nullpo_retr(0, option = battle_get_option(bl)); + nullpo_retr(0, opt1 = battle_get_opt1(bl)); + nullpo_retr(0, opt2 = battle_get_opt2(bl)); + nullpo_retr(0, opt3 = battle_get_opt3(bl)); + + if ((*sc_count) > 0 && sc_data[type].timer != -1 && (sc_data[type].timer == tid || tid == -1)) { + + if (tid == -1) // タイマから呼ばれていないならタイマ削除をする + delete_timer(sc_data[type].timer,skill_status_change_timer); + + /* 該当の異常を正常に戻す */ + sc_data[type].timer=-1; + (*sc_count)--; + + switch(type){ /* 異常の種類ごとの処理 */ + case SC_PROVOKE: /* プロボック */ + case SC_CONCENTRATE: /* 集中力向上 */ + case SC_BLESSING: /* ブレッシング */ + case SC_ANGELUS: /* アンゼルス */ + case SC_INCREASEAGI: /* 速度上昇 */ + case SC_DECREASEAGI: /* 速度減少 */ + case SC_SIGNUMCRUCIS: /* シグナムクルシス */ + case SC_HIDING: + case SC_TWOHANDQUICKEN: /* 2HQ */ + case SC_ADRENALINE: /* アドレナリンラッシュ */ + case SC_ENCPOISON: /* エンチャントポイズン */ + case SC_IMPOSITIO: /* インポシティオマヌス */ + case SC_GLORIA: /* グロリア */ + case SC_LOUD: /* ラウドボイス */ + case SC_QUAGMIRE: /* クァグマイア */ + case SC_PROVIDENCE: /* プロヴィデンス */ + case SC_SPEARSQUICKEN: /* スピアクイッケン */ + case SC_VOLCANO: + case SC_DELUGE: + case SC_VIOLENTGALE: + case SC_ETERNALCHAOS: /* エターナルカオス */ + case SC_DRUMBATTLE: /* 戦太鼓の響き */ + case SC_NIBELUNGEN: /* ニーベルングの指輪 */ + case SC_SIEGFRIED: /* 不死身のジークフリード */ + case SC_WHISTLE: /* 口笛 */ + case SC_ASSNCROS: /* 夕陽のアサシンクロス */ + case SC_HUMMING: /* ハミング */ + case SC_DONTFORGETME: /* 私を忘れないで */ + case SC_FORTUNE: /* 幸運のキス */ + case SC_SERVICE4U: /* サービスフォーユー */ + case SC_EXPLOSIONSPIRITS: // 爆裂波動 + case SC_STEELBODY: // 金剛 + case SC_DEFENDER: + case SC_SPEEDPOTION0: /* 増速ポーション */ + case SC_SPEEDPOTION1: + case SC_SPEEDPOTION2: + case SC_APPLEIDUN: /* イドゥンの林檎 */ + case SC_RIDING: + case SC_BLADESTOP_WAIT: + case SC_AURABLADE: /* オーラブレード */ + case SC_PARRYING: /* パリイング */ + case SC_CONCENTRATION: /* コンセントレーション */ + case SC_TENSIONRELAX: /* テンションリラックス */ + case SC_ASSUMPTIO: /* アシャンプティオ */ + case SC_WINDWALK: /* ウインドウォーク */ + case SC_TRUESIGHT: /* トゥルーサイト */ + case SC_SPIDERWEB: /* スパイダーウェッブ */ + case SC_MAGICPOWER: /* 魔法力増幅 */ + case SC_CHASEWALK: + case SC_ATKPOT: /* attack potion [Valaris] */ + case SC_MATKPOT: /* magic attack potion [Valaris] */ + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + case SC_MELTDOWN: /* メルトダウン */ + calc_flag = 1; + break; + case SC_BERSERK: /* バーサーク */ + calc_flag = 1; + clif_status_change(bl,SC_INCREASEAGI,0); /* アイコン消去 */ + break; + case SC_DEVOTION: /* ディボーション */ + { + struct map_session_data *md = map_id2sd(sc_data[type].val1); + sc_data[type].val1=sc_data[type].val2=0; + skill_devotion(md,bl->id); + calc_flag = 1; + } + break; + case SC_BLADESTOP: + { + struct status_change *t_sc_data = battle_get_sc_data((struct block_list *)sc_data[type].val4); + //片方が切れたので相手の白刃状態が切れてないのなら解除 + if(t_sc_data && t_sc_data[SC_BLADESTOP].timer!=-1) + skill_status_change_end((struct block_list *)sc_data[type].val4,SC_BLADESTOP,-1); + + if(sc_data[type].val2==2) + clif_bladestop((struct block_list *)sc_data[type].val3,(struct block_list *)sc_data[type].val4,0); + } + break; + case SC_DANCING: + { + struct map_session_data *dsd; + struct status_change *d_sc_data; + if(sc_data[type].val4 && (dsd=map_id2sd(sc_data[type].val4))){ + d_sc_data = dsd->sc_data; + //合奏で相手がいる場合相手のval4を0にする + if(d_sc_data && d_sc_data[type].timer!=-1) + d_sc_data[type].val4=0; + } + } + calc_flag = 1; + break; + case SC_GRAFFITI: + { + struct skill_unit_group *sg=(struct skill_unit_group *)sc_data[type].val4; //val4がグラフィティのgroup_id + if(sg) + skill_delunitgroup(sg); + } + break; + case SC_NOCHAT: //チャット禁止状態 + { + struct map_session_data *sd=NULL; + if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){ + sd->status.manner = 0; + clif_updatestatus(sd,SP_MANNER); + } + } + break; + case SC_SPLASHER: /* ベナムスプラッシャー */ + { + struct block_list *src=map_id2bl(sc_data[type].val3); + if(src && tid!=-1){ + //自分にダメージ&周囲3*3にダメージ + skill_castend_damage_id(src, bl,sc_data[type].val2,sc_data[type].val1,gettick(),0 ); + } + } + break; + case SC_SELFDESTRUCTION: /* 自爆 */ + { + //自分のダメージは0にして + struct mob_data *md=NULL; + if(bl->type == BL_MOB && (md=(struct mob_data*)bl)) + skill_castend_damage_id(bl, bl,sc_data[type].val2,sc_data[type].val1,gettick(),0 ); + } + break; + /* option1 */ + case SC_FREEZE: + sc_data[type].val3 = 0; + break; + + /* option2 */ + case SC_POISON: /* 毒 */ + case SC_BLIND: /* 暗黒 */ + case SC_CURSE: + calc_flag = 1; + break; + } + + if(bl->type==BL_PC && type<SC_SENDMAX) + clif_status_change(bl,type,0); /* アイコン消去 */ + + switch(type){ /* 正常に戻るときなにか処理が必要 */ + case SC_STONE: + case SC_FREEZE: + case SC_STAN: + case SC_SLEEP: + *opt1 = 0; + opt_flag = 1; + break; + + case SC_POISON: + case SC_CURSE: + case SC_SILENCE: + case SC_BLIND: + *opt2 &= ~(1<<(type-SC_POISON)); + opt_flag = 1; + break; + + case SC_SIGNUMCRUCIS: + *opt2 &= ~0x40; + opt_flag = 1; + break; + + case SC_HIDING: + case SC_CLOAKING: + *option &= ~((type == SC_HIDING) ? 2 : 4); + opt_flag = 1 ; + break; + + case SC_CHASEWALK: + *option &= ~16388; + opt_flag = 1 ; + break; + + case SC_SIGHT: + *option &= ~1; + opt_flag = 1; + break; + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + *option &= ~4096; + opt_flag = 1; + break; + case SC_RUWACH: + *option &= ~8192; + opt_flag = 1; + break; + + //opt3 + case SC_TWOHANDQUICKEN: /* 2HQ */ + case SC_SPEARSQUICKEN: /* スピアクイッケン */ + case SC_CONCENTRATION: /* コンセントレーション */ + *opt3 &= ~1; + break; + case SC_OVERTHRUST: /* オーバースラスト */ + *opt3 &= ~2; + break; + case SC_ENERGYCOAT: /* エナジーコート */ + *opt3 &= ~4; + break; + case SC_EXPLOSIONSPIRITS: // 爆裂波動 + *opt3 &= ~8; + break; + case SC_STEELBODY: // 金剛 + *opt3 &= ~16; + break; + case SC_BLADESTOP: /* 白刃取り */ + *opt3 &= ~32; + break; + case SC_BERSERK: /* バーサーク */ + *opt3 &= ~128; + break; + case SC_MARIONETTE: /* マリオネットコントロール */ + *opt3 &= ~1024; + break; + case SC_ASSUMPTIO: /* アスムプティオ */ + *opt3 &= ~2048; + break; + } + + if (night_flag == 1 && (*opt2 & STATE_BLIND) == 0 && bl->type == BL_PC) { // by [Yor] + *opt2 |= STATE_BLIND; + opt_flag = 1; + } + + if(opt_flag) /* optionの変更を伝える */ + clif_changeoption(bl); + + if (bl->type == BL_PC && calc_flag) + pc_calcstatus((struct map_session_data *)bl,0); /* ステータス再計算 */ + } + + return 0; +} +/*========================================== + * ステータス異常終了タイマー + *------------------------------------------ + */ +int skill_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; //使ってない? + + if( (bl=map_id2bl(id)) == NULL ) + return 0; //該当IDがすでに消滅しているというのはいかにもありそうなのでスルーしてみる + nullpo_retr(0, sc_data=battle_get_sc_data(bl)); + + if(bl->type==BL_PC) + sd=(struct map_session_data *)bl; + + //sc_count=battle_get_sc_count(bl); //使ってない? + + if(sc_data[type].timer != tid) { + if(battle_config.error_log) + printf("skill_status_change_timer %d != %d\n",tid,sc_data[type].timer); + } + + switch(type){ /* 特殊な処理になる場合 */ + case SC_MAXIMIZEPOWER: /* マキシマイズパワー */ + case SC_CLOAKING: /* クローキング */ + case SC_CHASEWALK: + if(sd){ + if( sd->status.sp > 0 ){ /* SP切れるまで持続 */ + sd->status.sp--; + clif_updatestatus(sd,SP_SP); + sc_data[type].timer=add_timer( /* タイマー再設定 */ + sc_data[type].val2+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + break; + + case SC_HIDING: /* ハイディング */ + if(sd){ /* SPがあって、時間制限の間は持続 */ + if( sd->status.sp > 0 && (--sc_data[type].val2)>0 ){ + if(sc_data[type].val2 % (sc_data[type].val1+3) ==0 ){ + sd->status.sp--; + clif_updatestatus(sd,SP_SP); + } + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 1000+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + break; + + case SC_SIGHT: /* サイト */ + { + const int range=7; + map_foreachinarea( skill_status_change_timer_sub, + bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,0, + bl,type,tick); + + if( (--sc_data[type].val2)>0 ){ + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 250+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + break; + case SC_RUWACH: /* ルアフ */ + { + const int range=5; + map_foreachinarea( skill_status_change_timer_sub, + bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,0, + bl,type,tick); + + if( (--sc_data[type].val2)>0 ){ + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 250+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + break; + + case SC_SIGNUMCRUCIS: /* シグナムクルシス */ + { + int race = battle_get_race(bl); + if(race == 6 || battle_check_undead(race,battle_get_elem_type(bl))) { + sc_data[type].timer=add_timer(1000*600+tick,skill_status_change_timer, bl->id, data ); + return 0; + } + } + break; + + case SC_PROVOKE: /* プロボック/オートバーサーク */ + if(sc_data[type].val2!=0){ /* オートバーサーク(1秒ごとにHPチェック) */ + if(sd && sd->status.hp>sd->status.max_hp>>2) /* 停止 */ + break; + sc_data[type].timer=add_timer( 1000+tick,skill_status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_WATERBALL: /* ウォーターボール */ + { + struct block_list *target=map_id2bl(sc_data[type].val2); + if(target==NULL || target->prev==NULL) + break; + skill_attack(BF_MAGIC,bl,bl,target,WZ_WATERBALL,sc_data[type].val1,tick,0); + if((--sc_data[type].val3)>0) { + sc_data[type].timer=add_timer( 150+tick,skill_status_change_timer, bl->id, data ); + return 0; + } + } + break; + + case SC_ENDURE: /* インデュア */ + if(sd && sd->special_state.infinite_endure) { + sc_data[type].timer=add_timer( 1000*600+tick,skill_status_change_timer, bl->id, data ); + sc_data[type].val2=1; + return 0; + } + break; + + case SC_DISSONANCE: /* 不協和音 */ + if( (--sc_data[type].val2)>0){ + struct skill_unit *unit= + (struct skill_unit *)sc_data[type].val4; + struct block_list *src; + + if(!unit || !unit->group) + break; + src=map_id2bl(unit->group->src_id); + if(!src) + break; + skill_attack(BF_MISC,src,&unit->bl,bl,unit->group->skill_id,sc_data[type].val1,tick,0); + sc_data[type].timer=add_timer(skill_get_time2(unit->group->skill_id,unit->group->skill_lv)+tick, + skill_status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_LULLABY: /* 子守唄 */ + if( (--sc_data[type].val2)>0){ + struct skill_unit *unit= + (struct skill_unit *)sc_data[type].val4; + if(!unit || !unit->group || unit->group->src_id==bl->id) + break; + skill_additional_effect(bl,bl,unit->group->skill_id,sc_data[type].val1,BF_LONG|BF_SKILL|BF_MISC,tick); + sc_data[type].timer=add_timer(skill_get_time(unit->group->skill_id,unit->group->skill_lv)/10+tick, + skill_status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_STONE: + if(sc_data[type].val2 != 0) { + short *opt1 = battle_get_opt1(bl); + sc_data[type].val2 = 0; + sc_data[type].val4 = 0; + battle_stopwalking(bl,1); + if(opt1) { + *opt1 = 1; + clif_changeoption(bl); + } + sc_data[type].timer=add_timer(1000+tick,skill_status_change_timer, bl->id, data ); + return 0; + } + else if( (--sc_data[type].val3) > 0) { + int hp = battle_get_max_hp(bl); + if((++sc_data[type].val4)%5 == 0 && battle_get_hp(bl) > hp>>2) { + hp = hp/100; + if(hp < 1) hp = 1; + if(bl->type == BL_PC) + pc_heal((struct map_session_data *)bl,-hp,0); + else if(bl->type == BL_MOB){ + struct mob_data *md; + if((md=((struct mob_data *)bl)) == NULL) + break; + md->hp -= hp; + } + } + sc_data[type].timer=add_timer(1000+tick,skill_status_change_timer, bl->id, data ); + return 0; + } + break; + case SC_POISON: + if(sc_data[SC_SLOWPOISON].timer == -1) { + if( (--sc_data[type].val3) > 0) { + int hp = battle_get_max_hp(bl); + if(battle_get_hp(bl) > hp>>2) { + if(bl->type == BL_PC) { + hp = 3 + hp*3/200; + pc_heal((struct map_session_data *)bl,-hp,0); + } + else if(bl->type == BL_MOB) { + struct mob_data *md; + if((md=((struct mob_data *)bl)) == NULL) + break; + hp = 3 + hp/200; + md->hp -= hp; + } + } + sc_data[type].timer=add_timer(1000+tick,skill_status_change_timer, bl->id, data ); + } + } + else + sc_data[type].timer=add_timer(1000+tick,skill_status_change_timer, bl->id, data ); + break; + case SC_TENSIONRELAX: /* テンションリラックス */ + if(sd){ /* SPがあって、HPが満タンでなければ継続 */ + if( sd->status.sp > 12 && sd->status.max_hp > sd->status.hp ){ + if(sc_data[type].val2 % (sc_data[type].val1+3) ==0 ){ + sd->status.sp -= 12; + clif_updatestatus(sd,SP_SP); + } + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 10000+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + if(sd->status.max_hp <= sd->status.hp) + skill_status_change_end(&sd->bl,SC_TENSIONRELAX,-1); + } + break; + + /* 時間切れ無し?? */ + case SC_AETERNA: + case SC_TRICKDEAD: + case SC_RIDING: + case SC_FALCON: + case SC_WEIGHT50: + case SC_WEIGHT90: + case SC_MAGICPOWER: /* 魔法力増幅 */ + case SC_REJECTSWORD: /* リジェクトソード */ + case SC_MEMORIZE: /* メモライズ */ + case SC_BROKNWEAPON: + case SC_BROKNARMOR: + if(sc_data[type].timer==tid) + sc_data[type].timer=add_timer( 1000*600+tick,skill_status_change_timer, bl->id, data ); + return 0; + + case SC_DANCING: //ダンススキルの時間SP消費 + { + int s=0; + if(sd){ + if(sd->status.sp > 0 && (--sc_data[type].val3)>0){ + switch(sc_data[type].val1){ + case BD_RICHMANKIM: /* ニヨルドの宴 3秒にSP1 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き 3秒にSP1 */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 3秒にSP1 */ + case BD_SIEGFRIED: /* 不死身のジークフリード 3秒にSP1 */ + case BA_DISSONANCE: /* 不協和音 3秒でSP1 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス 3秒でSP1 */ + case DC_UGLYDANCE: /* 自分勝手なダンス 3秒でSP1 */ + s=3; + break; + case BD_LULLABY: /* 子守歌 4秒にSP1 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 4秒にSP1 */ + case BD_ROKISWEIL: /* ロキの叫び 4秒にSP1 */ + case DC_FORTUNEKISS: /* 幸運のキス 4秒でSP1 */ + s=4; + break; + case BD_INTOABYSS: /* 深淵の中に 5秒にSP1 */ + case BA_WHISTLE: /* 口笛 5秒でSP1 */ + case DC_HUMMING: /* ハミング 5秒でSP1 */ + case BA_POEMBRAGI: /* ブラギの詩 5秒でSP1 */ + case DC_SERVICEFORYOU: /* サービスフォーユー 5秒でSP1 */ + s=5; + break; + case BA_APPLEIDUN: /* イドゥンの林檎 6秒でSP1 */ + s=6; + break; + case DC_DONTFORGETME: /* 私を忘れないで… 10秒でSP1 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら 10秒でSP1? */ + s=10; + break; + } + if(s && ((sc_data[type].val3 % s) == 0)){ + sd->status.sp--; + clif_updatestatus(sd,SP_SP); + } + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 1000+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + } + break; + case SC_BERSERK: /* バーサーク */ + if(sd){ /* HPが100以上なら継続 */ + if( (sd->status.hp - sd->status.hp/100) > 100 ){ + sd->status.hp -= sd->status.hp/100; + clif_updatestatus(sd,SP_HP); + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 15000+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + break; + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + if(sd){ + time_t timer; + if(time(&timer) < ((sc_data[type].val2) + 3600)){ //1時間たっていないので継続 + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 10000+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + break; + case SC_NOCHAT: //チャット禁止状態 + if(sd && battle_config.muting_players){ + time_t timer; + if((++sd->status.manner) && time(&timer) < ((sc_data[type].val2) + 60*(0-sd->status.manner))){ //開始からstatus.manner分経ってないので継続 + clif_updatestatus(sd,SP_MANNER); + sc_data[type].timer=add_timer( /* タイマー再設定(60秒) */ + 60000+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + } + break; + case SC_SELFDESTRUCTION: /* 自爆 */ + if(--sc_data[type].val3>0){ + struct mob_data *md; + if(bl->type==BL_MOB && (md=(struct mob_data *)bl) && md->speed > 250){ + md->speed -= 250; + md->next_walktime=tick; + } + sc_data[type].timer=add_timer( /* タイマー再設定 */ + 1000+tick, skill_status_change_timer, + bl->id, data); + return 0; + } + break; + } + + return skill_status_change_end( bl,type,tid ); +} + +/*========================================== + * ステータス異常終了 + *------------------------------------------ + */ +int skill_encchant_eremental_end(struct block_list *bl,int type) +{ + struct status_change *sc_data; + + nullpo_retr(0, bl); + nullpo_retr(0, sc_data=battle_get_sc_data(bl)); + + if( type!=SC_ENCPOISON && sc_data[SC_ENCPOISON].timer!=-1 ) /* エンチャントポイズン解除 */ + skill_status_change_end(bl,SC_ENCPOISON,-1); + if( type!=SC_ASPERSIO && sc_data[SC_ASPERSIO].timer!=-1 ) /* アスペルシオ解除 */ + skill_status_change_end(bl,SC_ASPERSIO,-1); + if( type!=SC_FLAMELAUNCHER && sc_data[SC_FLAMELAUNCHER].timer!=-1 ) /* フレイムランチャ解除 */ + skill_status_change_end(bl,SC_FLAMELAUNCHER,-1); + if( type!=SC_FROSTWEAPON && sc_data[SC_FROSTWEAPON].timer!=-1 ) /* フロストウェポン解除 */ + skill_status_change_end(bl,SC_FROSTWEAPON,-1); + if( type!=SC_LIGHTNINGLOADER && sc_data[SC_LIGHTNINGLOADER].timer!=-1 ) /* ライトニングローダー解除 */ + skill_status_change_end(bl,SC_LIGHTNINGLOADER,-1); + if( type!=SC_SEISMICWEAPON && sc_data[SC_SEISMICWEAPON].timer!=-1 ) /* サイスミックウェポン解除 */ + skill_status_change_end(bl,SC_SEISMICWEAPON,-1); + + return 0; +} +/*========================================== + * ステータス異常開始 + *------------------------------------------ + */ +int skill_status_change_start(struct block_list *bl, int type, int val1, int val2, int val3, int val4, int tick, int flag) +{ + struct map_session_data *sd = NULL; + struct status_change* sc_data; + short *sc_count, *option, *opt1, *opt2, *opt3; + int opt_flag = 0, calc_flag = 0,updateflag = 0, race, mode, elem, undead_flag; + int scdef=0; + + nullpo_retr(0, bl); + if(bl->type == BL_SKILL) + return 0; + nullpo_retr(0, sc_data=battle_get_sc_data(bl)); + nullpo_retr(0, sc_count=battle_get_sc_count(bl)); + nullpo_retr(0, option=battle_get_option(bl)); + nullpo_retr(0, opt1=battle_get_opt1(bl)); + nullpo_retr(0, opt2=battle_get_opt2(bl)); + nullpo_retr(0, opt3=battle_get_opt3(bl)); + + + race=battle_get_race(bl); + mode=battle_get_mode(bl); + elem=battle_get_elem_type(bl); + undead_flag=battle_check_undead(race,elem); + + if(type == SC_AETERNA && (sc_data[SC_STONE].timer != -1 || sc_data[SC_FREEZE].timer != -1) ) + return 0; + + switch(type){ + case SC_STONE: + case SC_FREEZE: + scdef=3+battle_get_mdef(bl)+battle_get_luk(bl)/3; + break; + case SC_STAN: + case SC_SILENCE: + case SC_POISON: + scdef=3+battle_get_vit(bl)+battle_get_luk(bl)/3; + break; + case SC_SLEEP: + case SC_BLIND: + scdef=3+battle_get_int(bl)+battle_get_luk(bl)/3; + break; + case SC_CURSE: + scdef=3+battle_get_luk(bl); + break; + +// case SC_CONFUSION: + default: + scdef=0; + } + if(scdef>=100) + return 0; + if(bl->type==BL_PC){ + sd=(struct map_session_data *)bl; + if( sd && type == SC_ADRENALINE && !(skill_get_weapontype(BS_ADRENALINE)&(1<<sd->status.weapon))) + return 0; + + if(SC_STONE<=type && type<=SC_BLIND){ /* カードによる耐性 */ + if( sd && sd->reseff[type-SC_STONE] > 0 && rand()%10000<sd->reseff[type-SC_STONE]){ + if(battle_config.battle_log) + printf("PC %d skill_sc_start: cardによる異常耐性発動\n",sd->bl.id); + return 0; + } + } + } + else if(bl->type == BL_MOB) { + } + else { + if(battle_config.error_log) + printf("skill_status_change_start: neither MOB nor PC !\n"); + return 0; + } + + if(type==SC_FREEZE && undead_flag && !(flag&1)) + return 0; + + if((type == SC_ADRENALINE || type == SC_WEAPONPERFECTION || type == SC_OVERTHRUST) && + sc_data[type].timer != -1 && sc_data[type].val2 && !val2) + return 0; + + if(mode & 0x20 && (type==SC_STONE || type==SC_FREEZE || + type==SC_STAN || type==SC_SLEEP || type==SC_SILENCE || type==SC_QUAGMIRE || type == SC_DECREASEAGI || type == SC_SIGNUMCRUCIS || type == SC_PROVOKE || + (type == SC_BLESSING && (undead_flag || race == 6))) && !(flag&1)){ + /* ボスには効かない(ただしカードによる効果は適用される) */ + return 0; + } + if(type==SC_FREEZE || type==SC_STAN || type==SC_SLEEP) + battle_stopwalking(bl,1); + + if(sc_data[type].timer != -1){ /* すでに同じ異常になっている場合タイマ解除 */ + if(sc_data[type].val1 > val1 && type != SC_COMBO && type != SC_DANCING && type != SC_DEVOTION && + type != SC_SPEEDPOTION0 && type != SC_SPEEDPOTION1 && type != SC_SPEEDPOTION2 + && type != SC_ATKPOT && type != SC_MATKPOT) // added atk and matk potions [Valaris] + return 0; + if(type >=SC_STAN && type <= SC_BLIND) + return 0;/* 継ぎ足しができない状態異常である時は状態異常を行わない */ + if(type == SC_GRAFFITI){ //異常中にもう一度状態異常になった時に解除してから再度かかる + skill_status_change_end(bl,type,-1); + }else{ + (*sc_count)--; + delete_timer(sc_data[type].timer, skill_status_change_timer); + sc_data[type].timer = -1; + } + } + + switch(type){ /* 異常の種類ごとの処理 */ + case SC_PROVOKE: /* プロボック */ + calc_flag = 1; + if(tick <= 0) tick = 1000; /* (オートバーサーク) */ + break; + case SC_ENDURE: /* インデュア */ + if(tick <= 0) tick = 1000 * 60; + break; + case SC_CONCENTRATE: /* 集中力向上 */ + calc_flag = 1; + break; + case SC_BLESSING: /* ブレッシング */ + { + if(bl->type == BL_PC || (!undead_flag && race != 6)) { + if(sc_data[SC_CURSE].timer!=-1 ) + skill_status_change_end(bl,SC_CURSE,-1); + if(sc_data[SC_STONE].timer!=-1 && sc_data[SC_STONE].val2 == 0) + skill_status_change_end(bl,SC_STONE,-1); + } + calc_flag = 1; + } + break; + case SC_ANGELUS: /* アンゼルス */ + calc_flag = 1; + break; + case SC_INCREASEAGI: /* 速度上昇 */ + calc_flag = 1; + if(sc_data[SC_DECREASEAGI].timer!=-1 ) + skill_status_change_end(bl,SC_DECREASEAGI,-1); + if(sc_data[SC_WINDWALK].timer!=-1 ) /* ウインドウォーク */ + skill_status_change_end(bl,SC_WINDWALK,-1); + break; + case SC_DECREASEAGI: /* 速度減少 */ + calc_flag = 1; + if(sc_data[SC_INCREASEAGI].timer!=-1 ) + skill_status_change_end(bl,SC_INCREASEAGI,-1); + break; + case SC_SIGNUMCRUCIS: /* シグナムクルシス */ + calc_flag = 1; +// val2 = 14 + val1; + val2 = 10 + val1*2; + tick = 600*1000; + clif_emotion(bl,4); + break; + case SC_SLOWPOISON: + if(sc_data[SC_POISON].timer == -1 ) + return 0; + break; + case SC_TWOHANDQUICKEN: /* 2HQ */ + *opt3 |= 1; + calc_flag = 1; + break; + case SC_ADRENALINE: /* アドレナリンラッシュ */ + calc_flag = 1; + break; + case SC_WEAPONPERFECTION: /* ウェポンパーフェクション */ + if(battle_config.party_skill_penaly && !val2) tick /= 5; + break; + case SC_OVERTHRUST: /* オーバースラスト */ + *opt3 |= 2; + if(battle_config.party_skill_penaly && !val2) tick /= 10; + break; + case SC_MAXIMIZEPOWER: /* マキシマイズパワー(SPが1減る時間,val2にも) */ + if(bl->type == BL_PC) + val2 = tick; + else + tick = 5000*val1; + break; + case SC_ENCPOISON: /* エンチャントポイズン */ + calc_flag = 1; + val2=(((val1 - 1) / 2) + 3)*100; /* 毒付与確率 */ + skill_encchant_eremental_end(bl,SC_ENCPOISON); + break; + case SC_POISONREACT: /* ポイズンリアクト */ + break; + case SC_IMPOSITIO: /* インポシティオマヌス */ + calc_flag = 1; + break; + case SC_ASPERSIO: /* アスペルシオ */ + skill_encchant_eremental_end(bl,SC_ASPERSIO); + break; + case SC_SUFFRAGIUM: /* サフラギム */ + case SC_BENEDICTIO: /* 聖体 */ + case SC_MAGNIFICAT: /* マグニフィカート */ + case SC_AETERNA: /* エーテルナ */ + break; + case SC_ENERGYCOAT: /* エナジーコート */ + *opt3 |= 4; + break; + case SC_MAGICROD: + val2 = val1*20; + break; + case SC_KYRIE: /* キリエエレイソン */ + val2 = battle_get_max_hp(bl) * (val1 * 2 + 10) / 100;/* 耐久度 */ + val3 = (val1 / 2 + 5); /* 回数 */ +// -- moonsoul (added to undo assumptio status if target has it) + if(sc_data[SC_ASSUMPTIO].timer!=-1 ) + skill_status_change_end(bl,SC_ASSUMPTIO,-1); + break; + case SC_MINDBREAKER: + calc_flag = 1; + if(tick <= 0) tick = 1000; /* (オートバーサーク) */ + case SC_GLORIA: /* グロリア */ + calc_flag = 1; + break; + case SC_LOUD: /* ラウドボイス */ + calc_flag = 1; + break; + case SC_TRICKDEAD: /* 死んだふり */ + break; + case SC_QUAGMIRE: /* クァグマイア */ + calc_flag = 1; + if(sc_data[SC_CONCENTRATE].timer!=-1 ) /* 集中力向上解除 */ + skill_status_change_end(bl,SC_CONCENTRATE,-1); + if(sc_data[SC_INCREASEAGI].timer!=-1 ) /* 速度上昇解除 */ + skill_status_change_end(bl,SC_INCREASEAGI,-1); + if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 ) + skill_status_change_end(bl,SC_TWOHANDQUICKEN,-1); + if(sc_data[SC_SPEARSQUICKEN].timer!=-1 ) + skill_status_change_end(bl,SC_SPEARSQUICKEN,-1); + if(sc_data[SC_ADRENALINE].timer!=-1 ) + skill_status_change_end(bl,SC_ADRENALINE,-1); + if(sc_data[SC_LOUD].timer!=-1 ) + skill_status_change_end(bl,SC_LOUD,-1); + if(sc_data[SC_TRUESIGHT].timer!=-1 ) /* トゥルーサイト */ + skill_status_change_end(bl,SC_TRUESIGHT,-1); + if(sc_data[SC_WINDWALK].timer!=-1 ) /* ウインドウォーク */ + skill_status_change_end(bl,SC_WINDWALK,-1); + if(sc_data[SC_CARTBOOST].timer!=-1 ) /* カートブースト */ + skill_status_change_end(bl,SC_CARTBOOST,-1); + break; + case SC_FLAMELAUNCHER: /* フレームランチャー */ + skill_encchant_eremental_end(bl,SC_FLAMELAUNCHER); + break; + case SC_FROSTWEAPON: /* フロストウェポン */ + skill_encchant_eremental_end(bl,SC_FROSTWEAPON); + break; + case SC_LIGHTNINGLOADER: /* ライトニングローダー */ + skill_encchant_eremental_end(bl,SC_LIGHTNINGLOADER); + break; + case SC_SEISMICWEAPON: /* サイズミックウェポン */ + skill_encchant_eremental_end(bl,SC_SEISMICWEAPON); + break; + case SC_DEVOTION: /* ディボーション */ + calc_flag = 1; + break; + case SC_PROVIDENCE: /* プロヴィデンス */ + calc_flag = 1; + val2=val1*5; + break; + case SC_REFLECTSHIELD: + val2=10+val1*3; + break; + case SC_STRIPWEAPON: + case SC_STRIPSHIELD: + case SC_STRIPARMOR: + case SC_STRIPHELM: + case SC_CP_WEAPON: + case SC_CP_SHIELD: + case SC_CP_ARMOR: + case SC_CP_HELM: + break; + + case SC_AUTOSPELL: /* オートスペル */ + val4 = 5 + val1*2; + break; + + case SC_VOLCANO: + calc_flag = 1; + val3 = val1*10; + val4 = val1>=5?20: (val1==4?19: (val1==3?17: ( val1==2?14:10 ) ) ); + break; + case SC_DELUGE: + calc_flag = 1; + val3 = val1>=5?15: (val1==4?14: (val1==3?12: ( val1==2?9:5 ) ) ); + val4 = val1>=5?20: (val1==4?19: (val1==3?17: ( val1==2?14:10 ) ) ); + break; + case SC_VIOLENTGALE: + calc_flag = 1; + val3 = val1*3; + val4 = val1>=5?20: (val1==4?19: (val1==3?17: ( val1==2?14:10 ) ) ); + break; + + case SC_SPEARSQUICKEN: /* スピアクイッケン */ + calc_flag = 1; + val2 = 20+val1; + *opt3 |= 1; + break; + case SC_COMBO: + break; + case SC_BLADESTOP_WAIT: /* 白刃取り(待ち) */ + break; + case SC_BLADESTOP: /* 白刃取り */ + if(val2==2) clif_bladestop((struct block_list *)val3,(struct block_list *)val4,1); + *opt3 |= 32; + break; + + case SC_LULLABY: /* 子守唄 */ + val2 = 11; + break; + case SC_RICHMANKIM: + break; + case SC_ETERNALCHAOS: /* エターナルカオス */ + calc_flag = 1; + break; + case SC_DRUMBATTLE: /* 戦太鼓の響き */ + calc_flag = 1; + val2 = (val1+1)*25; + val3 = (val1+1)*2; + break; + case SC_NIBELUNGEN: /* ニーベルングの指輪 */ + calc_flag = 1; + val2 = (val1+2)*50; + val3 = (val1+2)*25; + break; + case SC_ROKISWEIL: /* ロキの叫び */ + break; + case SC_INTOABYSS: /* 深淵の中に */ + break; + case SC_SIEGFRIED: /* 不死身のジークフリード */ + calc_flag = 1; + val2 = 40 + val1*5; + val3 = val1*10; + break; + case SC_DISSONANCE: /* 不協和音 */ + val2 = 10; + break; + case SC_WHISTLE: /* 口笛 */ + calc_flag = 1; + break; + case SC_ASSNCROS: /* 夕陽のアサシンクロス */ + calc_flag = 1; + break; + case SC_POEMBRAGI: /* ブラギの詩 */ + break; + case SC_APPLEIDUN: /* イドゥンの林檎 */ + calc_flag = 1; + break; + case SC_UGLYDANCE: /* 自分勝手なダンス */ + val2 = 10; + break; + case SC_HUMMING: /* ハミング */ + calc_flag = 1; + break; + case SC_DONTFORGETME: /* 私を忘れないで */ + calc_flag = 1; + if(sc_data[SC_INCREASEAGI].timer!=-1 ) /* 速度上昇解除 */ + skill_status_change_end(bl,SC_INCREASEAGI,-1); + if(sc_data[SC_TWOHANDQUICKEN].timer!=-1 ) + skill_status_change_end(bl,SC_TWOHANDQUICKEN,-1); + if(sc_data[SC_SPEARSQUICKEN].timer!=-1 ) + skill_status_change_end(bl,SC_SPEARSQUICKEN,-1); + if(sc_data[SC_ADRENALINE].timer!=-1 ) + skill_status_change_end(bl,SC_ADRENALINE,-1); + if(sc_data[SC_ASSNCROS].timer!=-1 ) + skill_status_change_end(bl,SC_ASSNCROS,-1); + if(sc_data[SC_TRUESIGHT].timer!=-1 ) /* トゥルーサイト */ + skill_status_change_end(bl,SC_TRUESIGHT,-1); + if(sc_data[SC_WINDWALK].timer!=-1 ) /* ウインドウォーク */ + skill_status_change_end(bl,SC_WINDWALK,-1); + if(sc_data[SC_CARTBOOST].timer!=-1 ) /* カートブースト */ + skill_status_change_end(bl,SC_CARTBOOST,-1); + break; + case SC_FORTUNE: /* 幸運のキス */ + calc_flag = 1; + break; + case SC_SERVICE4U: /* サービスフォーユー */ + calc_flag = 1; + break; + case SC_DANCING: /* ダンス/演奏中 */ + calc_flag = 1; + val3= tick / 1000; + tick = 1000; + break; + + case SC_EXPLOSIONSPIRITS: // 爆裂波動 + calc_flag = 1; + val2 = 75 + 25*val1; + *opt3 |= 8; + break; + case SC_STEELBODY: // 金剛 + calc_flag = 1; + *opt3 |= 16; + break; + case SC_EXTREMITYFIST: /* 阿修羅覇凰拳 */ + break; + case SC_AUTOCOUNTER: + val3 = val4 = 0; + break; + + case SC_SPEEDPOTION0: /* 増速ポーション */ + case SC_SPEEDPOTION1: + case SC_SPEEDPOTION2: + calc_flag = 1; + tick = 1000 * tick; + val2 = 5*(2+type-SC_SPEEDPOTION0); + break; + + /* atk & matk potions [Valaris] */ + case SC_ATKPOT: + case SC_MATKPOT: + calc_flag = 1; + tick = 1000 * tick; + break; + case SC_WEDDING: //結婚用(結婚衣裳になって歩くのが遅いとか) + { + time_t timer; + + calc_flag = 1; + tick = 10000; + if(!val2) + val2 = time(&timer); + } + break; + case SC_NOCHAT: //チャット禁止状態 + { + time_t timer; + + if(!battle_config.muting_players) + break; + + tick = 60000; + if(!val2) + val2 = time(&timer); + updateflag = SP_MANNER; + } + break; + case SC_SELFDESTRUCTION: //自爆 + clif_skillcasting(bl,bl->id, bl->id,0,0,331,skill_get_time(val2,val1)); + val3 = tick / 1000; + tick = 1000; + break; + + /* option1 */ + case SC_STONE: /* 石化 */ + if(!(flag&2)) { + int sc_def = battle_get_mdef(bl)*200; + tick = tick - sc_def; + } + val3 = tick/1000; + if(val3 < 1) val3 = 1; + tick = 5000; + val2 = 1; + break; + case SC_SLEEP: /* 睡眠 */ + if(!(flag&2)) { +// int sc_def = 100 - (battle_get_int(bl) + battle_get_luk(bl)/3); +// tick = tick * sc_def / 100; +// if(tick < 1000) tick = 1000; + tick = 30000;//睡眠はステータス耐性に関わらず30秒 + } + break; + case SC_FREEZE: /* 凍結 */ + if(!(flag&2)) { + int sc_def = 100 - battle_get_mdef(bl); + tick = tick * sc_def / 100; + } + break; + case SC_STAN: /* スタン(val2にミリ秒セット) */ + if(!(flag&2)) { + int sc_def = 100 - (battle_get_vit(bl) + battle_get_luk(bl)/3); + tick = tick * sc_def / 100; + } + break; + + /* option2 */ + case SC_POISON: /* 毒 */ + calc_flag = 1; + if(!(flag&2)) { + int sc_def = 100 - (battle_get_vit(bl) + battle_get_luk(bl)/5); + tick = tick * sc_def / 100; + } + val3 = tick/1000; + if(val3 < 1) val3 = 1; + tick = 1000; + break; + case SC_SILENCE: /* 沈黙(レックスデビーナ) */ + if(!(flag&2)) { + int sc_def = 100 - battle_get_vit(bl); + tick = tick * sc_def / 100; + } + break; + case SC_BLIND: /* 暗黒 */ + calc_flag = 1; + if(!(flag&2)) { + int sc_def = battle_get_lv(bl)/10 + battle_get_int(bl)/15; + tick = 30000 - sc_def; + } + break; + case SC_CURSE: + calc_flag = 1; + if(!(flag&2)) { + int sc_def = 100 - battle_get_vit(bl); + tick = tick * sc_def / 100; + } + break; + + /* option */ + case SC_HIDING: /* ハイディング */ + calc_flag = 1; + if(bl->type == BL_PC) { + val2 = tick / 1000; /* 持続時間 */ + tick = 1000; + } + break; + case SC_CHASEWALK: + case SC_CLOAKING: /* クローキング */ + if(bl->type == BL_PC) + val2 = tick; + else + tick = 5000*val1; + break; + case SC_SIGHT: /* サイト/ルアフ */ + case SC_RUWACH: + val2 = tick/250; + tick = 10; + break; + + /* セーフティウォール、ニューマ */ + case SC_SAFETYWALL: case SC_PNEUMA: + tick=((struct skill_unit *)val2)->group->limit; + break; + + /* アンクル */ + case SC_ANKLE: + break; + + /* ウォーターボール */ + case SC_WATERBALL: + tick=150; + if(val1>5) //レベルが5以上の場合は25発に制限(1発目はすでに打ってるので-1) + val3=5*5-1; + else + val3= (val1|1)*(val1|1)-1; + break; + + /* スキルじゃない/時間に関係しない */ + case SC_RIDING: + calc_flag = 1; + tick = 600*1000; + break; + case SC_FALCON: + case SC_WEIGHT50: + case SC_WEIGHT90: + case SC_BROKNWEAPON: + case SC_BROKNARMOR: + tick=600*1000; + break; + + case SC_AUTOGUARD: + { + int i,t; + for(i=val2=0;i<val1;i++) { + t = 5-(i>>1); + val2 += (t < 0)? 1:t; + } + } + break; + + case SC_DEFENDER: + calc_flag = 1; + val2 = 5 + val1*15; + break; + + case SC_KEEPING: + case SC_BARRIER: + case SC_HALLUCINATION: + break; + case SC_CONCENTRATION: /* コンセントレーション */ + *opt3 |= 1; + calc_flag = 1; + break; + case SC_TENSIONRELAX: /* テンションリラックス */ + calc_flag = 1; + if(bl->type == BL_PC) { + tick = 10000; + } + break; + case SC_AURABLADE: /* オーラブレード */ + case SC_PARRYING: /* パリイング */ +// case SC_ASSUMPTIO: /* */ + case SC_HEADCRUSH: /* ヘッドクラッシュ */ + case SC_JOINTBEAT: /* ジョイントビート */ +// case SC_MARIONETTE: /* マリオネットコントロール */ + + //とりあえず手抜き + break; + +// -- moonsoul (for new upper class related skill status effects) +/* + case SC_AURABLADE: + val2 = val1*10; + break; + case SC_PARRYING: + val2=val1*3; + break; + case SC_CONCENTRATION: + calc_flag=1; + val2=val1*10; + val3=val1*5; + break; + case SC_TENSIONRELAX: +// val2 = 10; +// val3 = 15; + break; + case SC_BERSERK: + calc_flag=1; + break; + case SC_ASSUMPTIO: + if(sc_data[SC_KYRIE].timer!=-1 ) + skill_status_change_end(bl,SC_KYRIE,-1); + break; +*/ + case SC_WINDWALK: /* ウインドウォーク */ + calc_flag = 1; + val2 = (val1 / 2); //Flee上昇率 + break; + case SC_BERSERK: /* バーサーク */ + if(sd){ + sd->status.sp = 0; + clif_updatestatus(sd,SP_SP); + clif_status_change(bl,SC_INCREASEAGI,1); /* アイコン表示 */ + } + *opt3 |= 128; + tick = 1000; + calc_flag = 1; + break; + case SC_ASSUMPTIO: /* アスムプティオ */ + *opt3 |= 2048; + break; + case SC_MARIONETTE: /* マリオネットコントロール */ + *opt3 |= 1024; + break; + case SC_MELTDOWN: /* メルトダウン */ + case SC_CARTBOOST: /* カートブースト */ + case SC_TRUESIGHT: /* トゥルーサイト */ + case SC_SPIDERWEB: /* スパイダーウェッブ */ + case SC_MAGICPOWER: /* 魔法力増幅 */ + calc_flag = 1; + break; + case SC_REJECTSWORD: /* リジェクトソード */ + val2 = 3; //3回攻撃を跳ね返す + break; + case SC_MEMORIZE: /* メモライズ */ + val2 = 3; //3回詠唱を1/3にする + break; + case SC_GRAFFITI: /* グラフィティ */ + { + struct skill_unit_group *sg = skill_unitsetting(bl,RG_GRAFFITI,val1,val2,val3,0); + if(sg) + val4 = (int)sg; + } + break; + case SC_SPLASHER: /* ベナムスプラッシャー */ + break; + default: + if(battle_config.error_log) + printf("UnknownStatusChange [%d]\n", type); + return 0; + } + + if(bl->type==BL_PC && type<SC_SENDMAX) + clif_status_change(bl,type,1); /* アイコン表示 */ + + /* optionの変更 */ + switch(type){ + case SC_STONE: + case SC_FREEZE: + case SC_STAN: + case SC_SLEEP: + battle_stopattack(bl); /* 攻撃停止 */ + skill_stop_dancing(bl,0); /* 演奏/ダンスの中断 */ + { /* 同時に掛からないステータス異常を解除 */ + int i; + for(i = SC_STONE; i <= SC_SLEEP; i++){ + if(sc_data[i].timer != -1){ + (*sc_count)--; + delete_timer(sc_data[i].timer, skill_status_change_timer); + sc_data[i].timer = -1; + } + } + } + if(type == SC_STONE) + *opt1 = 6; + else + *opt1 = type - SC_STONE + 1; + opt_flag = 1; + break; + case SC_POISON: + case SC_CURSE: + case SC_SILENCE: + case SC_BLIND: + *opt2 |= 1<<(type-SC_POISON); + opt_flag = 1; + break; + case SC_SIGNUMCRUCIS: + *opt2 |= 0x40; + opt_flag = 1; + break; + case SC_HIDING: + case SC_CLOAKING: + battle_stopattack(bl); /* 攻撃停止 */ + *option |= ((type==SC_HIDING)?2:4); + opt_flag =1 ; + break; + case SC_CHASEWALK: + battle_stopattack(bl); /* 攻撃停止 */ + *option |= 16388; + opt_flag =1 ; + break; + case SC_SIGHT: + *option |= 1; + opt_flag = 1; + break; + case SC_RUWACH: + *option |= 8192; + opt_flag = 1; + break; + case SC_WEDDING: + *option |= 4096; + opt_flag = 1; + } + + if(opt_flag) /* optionの変更 */ + clif_changeoption(bl); + + (*sc_count)++; /* ステータス異常の数 */ + + sc_data[type].val1 = val1; + sc_data[type].val2 = val2; + sc_data[type].val3 = val3; + sc_data[type].val4 = val4; + /* タイマー設定 */ + sc_data[type].timer = add_timer( + gettick() + tick, skill_status_change_timer, bl->id, type); + + if(bl->type==BL_PC && calc_flag) + pc_calcstatus(sd,0); /* ステータス再計算 */ + + if(bl->type==BL_PC && updateflag) + clif_updatestatus(sd,updateflag); /* ステータスをクライアントに送る */ + + return 0; +} +/*========================================== + * ステータス異常全解除 + *------------------------------------------ + */ +int skill_status_change_clear(struct block_list *bl, int type) +{ + struct status_change* sc_data; + short *sc_count, *option, *opt1, *opt2, *opt3; + int i; + + nullpo_retr(0, bl); + nullpo_retr(0, sc_data = battle_get_sc_data(bl)); + nullpo_retr(0, sc_count = battle_get_sc_count(bl)); + nullpo_retr(0, option = battle_get_option(bl)); + nullpo_retr(0, opt1 = battle_get_opt1(bl)); + nullpo_retr(0, opt2 = battle_get_opt2(bl)); + nullpo_retr(0, opt3 = battle_get_opt3(bl)); + + if (*sc_count == 0) + return 0; + for(i = 0; i < MAX_STATUSCHANGE; i++){ + if(sc_data[i].timer != -1){ /* 異常があるならタイマーを削除する */ +/* + delete_timer(sc_data[i].timer, skill_status_change_timer); + sc_data[i].timer = -1; + + if (!type && i < SC_SENDMAX) + clif_status_change(bl, i, 0); +*/ + + skill_status_change_end(bl, i, -1); + } + } + *sc_count = 0; + *opt1 = 0; + *opt2 = 0; + *opt3 = 0; + *option &= OPTION_MASK; + + if (night_flag == 1 && type == BL_PC) // by [Yor] + *opt2 |= STATE_BLIND; + + if(!type || type&2) + clif_changeoption(bl); + + return 0; +} + +/* クローキング検査(周りに移動不可能地帯があるか) */ +int skill_check_cloaking(struct block_list *bl) +{ + struct map_session_data *sd=NULL; + static int dx[]={-1, 0, 1,-1, 1,-1, 0, 1}; + static int dy[]={-1,-1,-1, 0, 0, 1, 1, 1}; + int end=1,i; + + nullpo_retr(0, bl); + + if(pc_checkskill(sd,AS_CLOAKING)>2) + return 0; + if(bl->type == BL_PC && battle_config.pc_cloak_check_type&1) + return 0; + if(bl->type == BL_MOB && battle_config.monster_cloak_check_type&1) + return 0; + for(i=0;i<sizeof(dx)/sizeof(dx[0]);i++){ + int c=map_getcell(bl->m,bl->x+dx[i],bl->y+dy[i]); + if(c==1 || c==5) end=0; + } + if(end){ + skill_status_change_end(bl, SC_CLOAKING, -1); + *battle_get_option(bl)&=~4; /* 念のための処理 */ + } + return end; +} + +/* + *---------------------------------------------------------------------------- + * スキルユニット + *---------------------------------------------------------------------------- + */ + +/*========================================== + * 演奏/ダンススキルかどうか判定 + * 引数 スキルID + * 戻り ダンスじゃない=0 合奏=2 それ以外のダンス=1 + *------------------------------------------ + */ +int skill_is_danceskill(int id) +{ + int i; + switch(id){ + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + i=2; + break; + case BA_DISSONANCE: /* 不協和音 */ + case BA_FROSTJOKE: /* 寒いジョーク */ + case BA_WHISTLE: /* 口笛 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス */ + case BA_POEMBRAGI: /* ブラギの詩 */ + case BA_APPLEIDUN: /* イドゥンの林檎 */ + case DC_UGLYDANCE: /* 自分勝手なダンス */ + case DC_SCREAM: /* スクリーム */ + case DC_HUMMING: /* ハミング */ + case DC_DONTFORGETME: /* 私を忘れないで… */ + case DC_FORTUNEKISS: /* 幸運のキス */ + case DC_SERVICEFORYOU: /* サービスフォーユー */ + i=1; + break; + default: + i=0; + } + return i; +} + +/*========================================== + * 演奏/ダンスをやめる + * flag 1で合奏中なら相方にユニットを任せる + * + *------------------------------------------ + */ +void skill_stop_dancing(struct block_list *src, int flag) +{ + struct status_change* sc_data; + struct skill_unit_group* group; + + nullpo_retv(src); + + sc_data=battle_get_sc_data(src); + if(sc_data && sc_data[SC_DANCING].timer==-1) + return; + group=(struct skill_unit_group *)sc_data[SC_DANCING].val2; //ダンスのスキルユニットIDはval2に入ってる + if(group && src->type==BL_PC && sc_data && sc_data[SC_DANCING].val4){ //合奏中断 + struct map_session_data* dsd=map_id2sd(sc_data[SC_DANCING].val4); //相方のsd取得 + if(flag){ //ログアウトなど片方が落ちても演奏が継続される + if(dsd && src->id == group->src_id){ //グループを持ってるPCが落ちる + group->src_id=sc_data[SC_DANCING].val4; //相方にグループを任せる + if(flag&1) //ログアウト + dsd->sc_data[SC_DANCING].val4=0; //相方の相方を0にして合奏終了→通常のダンス状態 + if(flag&2) //ハエ飛びなど + return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり + }else if(dsd && dsd->bl.id == group->src_id){ //相方がグループを持っているPCが落ちる(自分はグループを持っていない) + if(flag&1) //ログアウト + dsd->sc_data[SC_DANCING].val4=0; //相方の相方を0にして合奏終了→通常のダンス状態 + if(flag&2) //ハエ飛びなど + return; //合奏もダンス状態も終了させない&スキルユニットは置いてけぼり + } + skill_status_change_end(src,SC_DANCING,-1);//自分のステータスを終了させる + //そしてグループは消さない&消さないのでステータス計算もいらない? + return; + }else{ + if(dsd && src->id == group->src_id){ //グループを持ってるPCが止める + skill_status_change_end((struct block_list *)dsd,SC_DANCING,-1);//相手のステータスを終了させる + } + if(dsd && dsd->bl.id == group->src_id){ //相方がグループを持っているPCが止める(自分はグループを持っていない) + skill_status_change_end(src,SC_DANCING,-1);//自分のステータスを終了させる + } + } + } + if(flag&2 && group && src->type==BL_PC){ //ハエで飛んだときとかはユニットも飛ぶ + struct map_session_data *sd = (struct map_session_data *)src; + skill_unit_move_unit_group(group, sd->bl.m,(sd->to_x - sd->bl.x),(sd->to_y - sd->bl.y)); + return; + } + skill_delunitgroup(group); + if(src->type==BL_PC) + pc_calcstatus((struct map_session_data *)src,0); +} + +/*========================================== + * スキルユニット初期化 + *------------------------------------------ + */ +struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y) +{ + struct skill_unit *unit; + + nullpo_retr(NULL, group); + nullpo_retr(NULL, unit=&group->unit[idx]); + + if(!unit->alive) + group->alive_count++; + + unit->bl.id=map_addobject(&unit->bl); + unit->bl.type=BL_SKILL; + unit->bl.m=group->map; + unit->bl.x=x; + unit->bl.y=y; + unit->group=group; + unit->val1=unit->val2=0; + unit->alive=1; + + map_addblock(&unit->bl); + clif_skill_setunit(unit); + return unit; +} + +int skill_unit_timer_sub_ondelete( struct block_list *bl, va_list ap ); +/*========================================== + * スキルユニット削除 + *------------------------------------------ + */ +int skill_delunit(struct skill_unit *unit) +{ + struct skill_unit_group *group; + int range; + + nullpo_retr(0, unit); + if(!unit->alive) + return 0; + nullpo_retr(0, group=unit->group); + + /* onlimitイベント呼び出し */ + skill_unit_onlimit( unit,gettick() ); + + /* ondeleteイベント呼び出し */ + range=group->range; + map_foreachinarea( skill_unit_timer_sub_ondelete, unit->bl.m, + unit->bl.x-range,unit->bl.y-range,unit->bl.x+range,unit->bl.y+range,0, + &unit->bl,gettick() ); + + clif_skill_delunit(unit); + + unit->group=NULL; + unit->alive=0; + map_delobjectnofree(unit->bl.id); + if(group->alive_count>0 && (--group->alive_count)<=0) + skill_delunitgroup(group); + + return 0; +} +/*========================================== + * スキルユニットグループ初期化 + *------------------------------------------ + */ +static int skill_unit_group_newid=10; +struct skill_unit_group *skill_initunitgroup(struct block_list *src, + int count,int skillid,int skilllv,int unit_id) +{ + int i; + struct skill_unit_group *group=NULL, *list=NULL; + int maxsug=0; + + nullpo_retr(NULL, src); + + if(src->type==BL_PC){ + list=((struct map_session_data *)src)->skillunit; + maxsug=MAX_SKILLUNITGROUP; + }else if(src->type==BL_MOB){ + list=((struct mob_data *)src)->skillunit; + maxsug=MAX_MOBSKILLUNITGROUP; + }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){ + printf("skill_initunitgroup: error unit group !\n"); + exit(1); + } + + group->src_id=src->id; + group->party_id=battle_get_party_id(src); + group->guild_id=battle_get_guild_id(src); + group->group_id=skill_unit_group_newid++; + if(skill_unit_group_newid<=0) + skill_unit_group_newid=10; + 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->range=0; + group->limit=10000; + group->interval=1000; + group->tick=gettick(); + group->valstr=NULL; + + if( skill_is_danceskill(skillid) ){ + struct map_session_data *sd = NULL; + if(src->type==BL_PC && (sd=(struct map_session_data *)src) ){ + sd->skillid_dance=skillid; + sd->skilllv_dance=skilllv; + } + skill_status_change_start(src,SC_DANCING,skillid,(int)group,0,0,skill_get_time(skillid,skilllv)+1000,0); + switch(skillid){ //合奏スキルは相方をダンス状態にする + case BD_LULLABY: /* 子守歌 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の混沌 */ + case BD_DRUMBATTLEFIELD: /* 戦太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニーベルングの指輪 */ + case BD_ROKISWEIL: /* ロキの叫び */ + case BD_INTOABYSS: /* 深淵の中に */ + case BD_SIEGFRIED: /* 不死身のジークフリード */ + case BD_RAGNAROK: /* 神々の黄昏 */ + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + { + int range=1; + int c=0; + if(sd){ + map_foreachinarea(skill_check_condition_use_sub,sd->bl.m, + sd->bl.x-range,sd->bl.y-range, + sd->bl.x+range,sd->bl.y+range,BL_PC,&sd->bl,&c); + } + } + } + } + return group; +} + +/*========================================== + * スキルユニットグループ削除 + *------------------------------------------ + */ +int skill_delunitgroup(struct skill_unit_group *group) +{ + struct block_list *src; + int i; + + nullpo_retr(0, group); + if(group->unit_count<=0) + return 0; + + src=map_id2bl(group->src_id); + if( skill_is_danceskill(group->skill_id) ){ //ダンススキルはダンス状態を解除する + if(src) + skill_status_change_end(src,SC_DANCING,-1); + } + + group->alive_count=0; + if(group->unit!=NULL){ + for(i=0;i<group->unit_count;i++) + if(group->unit[i].alive) + skill_delunit(&group->unit[i]); + } + if(group->valstr!=NULL){ + map_freeblock(group->valstr); + group->valstr=NULL; + } + + map_freeblock(group->unit); /* free()の替わり */ + group->unit=NULL; + group->src_id=0; + group->group_id=0; + group->unit_count=0; + return 0; +} + +/*========================================== + * スキルユニットグループ全削除 + *------------------------------------------ + */ +int skill_clear_unitgroup(struct block_list *src) +{ + struct skill_unit_group *group=NULL; + int maxsug=0; + + nullpo_retr(0, src); + + if(src->type==BL_PC){ + group=((struct map_session_data *)src)->skillunit; + maxsug=MAX_SKILLUNITGROUP; + }else if(src->type==BL_MOB){ + group=((struct mob_data *)src)->skillunit; + maxsug=MAX_MOBSKILLUNITGROUP; + }else if(src->type==BL_PET){ // [Valaris] + group=((struct pet_data *)src)->skillunit; + maxsug=MAX_MOBSKILLUNITGROUP; + } + if(group){ + int i; + for(i=0;i<maxsug;i++) + if(group[i].group_id>0 && group[i].src_id == src->id) + skill_delunitgroup(&group[i]); + } + return 0; +} + +/*========================================== + * スキルユニットグループの被影響tick検索 + *------------------------------------------ + */ +struct skill_unit_group_tickset *skill_unitgrouptickset_search( + struct block_list *bl,int group_id) +{ + int i,j=0,k,s=group_id%MAX_SKILLUNITGROUPTICKSET; + struct skill_unit_group_tickset *set=NULL; + + nullpo_retr(0, bl); + + if(bl->type==BL_PC){ + set=((struct map_session_data *)bl)->skillunittick; + }else{ + set=((struct mob_data *)bl)->skillunittick; + } + if(set==NULL) + return 0; + for(i=0;i<MAX_SKILLUNITGROUPTICKSET;i++) + if( set[(k=(i+s)%MAX_SKILLUNITGROUPTICKSET)].group_id == group_id ) + return &set[k]; + else if( set[k].group_id==0 ) + j=k; + + return &set[j]; +} + +/*========================================== + * スキルユニットグループの被影響tick削除 + *------------------------------------------ + */ +int skill_unitgrouptickset_delete(struct block_list *bl,int group_id) +{ + int i,s=group_id%MAX_SKILLUNITGROUPTICKSET; + struct skill_unit_group_tickset *set=NULL,*ts; + + nullpo_retr(0, bl); + + if(bl->type==BL_PC){ + set=((struct map_session_data *)bl)->skillunittick; + }else{ + set=((struct mob_data *)bl)->skillunittick; + } + + if(set!=NULL){ + + for(i=0;i<MAX_SKILLUNITGROUPTICKSET;i++) + if( (ts=&set[(i+s)%MAX_SKILLUNITGROUPTICKSET])->group_id == group_id ) + ts->group_id=0; + + } + return 0; +} + +/*========================================== + * スキルユニットタイマー発動処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_timer_sub_onplace( struct block_list *bl, va_list ap ) +{ + struct block_list *src; + struct skill_unit *su; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + src=va_arg(ap,struct block_list*); + + tick=va_arg(ap,unsigned int); + su = (struct skill_unit *)src; + + if( su && su->alive ) { + struct skill_unit_group *sg; + sg = su->group; + if(sg && battle_check_target(src,bl,sg->target_flag )>0) + skill_unit_onplace( su, bl, tick ); + } + return 0; +} + +/*========================================== + * スキルユニットタイマー削除処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_timer_sub_ondelete( struct block_list *bl, va_list ap ) +{ + struct block_list *src; + struct skill_unit *su; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + src=va_arg(ap,struct block_list*); + + tick=va_arg(ap,unsigned int); + su = (struct skill_unit *)src; + + if( su && su->alive ){ + struct skill_unit_group *sg; + sg = su->group; + if( sg && battle_check_target(src,bl,sg->target_flag )>0 ) + skill_unit_ondelete( su, bl, tick ); + } + return 0; +} + +/*========================================== + * スキルユニットタイマー処理用(foreachobject) + *------------------------------------------ + */ +int skill_unit_timer_sub( struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + int range; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, unit=(struct skill_unit *)bl); + nullpo_retr(0, group=unit->group); + tick=va_arg(ap,unsigned int); + + if(!unit->alive) + return 0; + + range=(unit->range!=0)?unit->range:group->range; + + /* onplaceイベント呼び出し */ + if(unit->alive && unit->range>=0){ + map_foreachinarea( skill_unit_timer_sub_onplace, bl->m, + bl->x-range,bl->y-range,bl->x+range,bl->y+range,0, + bl,tick); + if(group->unit_id == 0xaa && DIFF_TICK(tick,group->tick)>=6000*group->val2){ + map_foreachinarea( skill_idun_heal, bl->m, + bl->x-range,bl->y-range,bl->x+range,bl->y+range,0,unit); + group->val2++; + } + } + /* 時間切れ削除 */ + if(unit->alive && + (DIFF_TICK(tick,group->tick)>=group->limit || DIFF_TICK(tick,group->tick)>=unit->limit) ){ + switch(group->unit_id){ + + + + + + + case 0x8f: /* ブラストマイン */ + group->unit_id = 0x8c; + clif_changelook(bl,LOOK_BASE,group->unit_id); + group->limit=DIFF_TICK(tick+1500,group->tick); + unit->limit=DIFF_TICK(tick+1500,group->tick); + break; + case 0x90: /* スキッドトラップ */ + case 0x91: /* アンクルスネア */ + case 0x93: /* ランドマイン */ + case 0x94: /* ショックウェーブトラップ */ + case 0x95: /* サンドマン */ + case 0x96: /* フラッシャー */ + case 0x97: /* フリージングトラップ */ + case 0x98: /* クレイモアートラップ */ + case 0x99: /* トーキーボックス */ + { + struct block_list *src=map_id2bl(group->src_id); + if(group->unit_id == 0x91 && group->val2); + else{ + if(src && src->type==BL_PC){ + struct item item_tmp; + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid=1065; + item_tmp.identify=1; + map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,NULL,NULL,NULL,0); // 罠返還 + } + } + } + default: + skill_delunit(unit); + } + } + + if(group->unit_id == 0x8d) { + unit->val1 -= 5; + if(unit->val1 <= 0 && unit->limit + group->tick > tick + 700) + unit->limit = DIFF_TICK(tick+700,group->tick); + } + + return 0; +} +/*========================================== + * スキルユニットタイマー処理 + *------------------------------------------ + */ +int skill_unit_timer( int tid,unsigned int tick,int id,int data) +{ + map_freeblock_lock(); + + map_foreachobject( skill_unit_timer_sub, BL_SKILL, tick ); + + map_freeblock_unlock(); + + return 0; +} + +/*========================================== + * スキルユニット移動時処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_out_all_sub( struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + struct block_list *src; + int range; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, src=va_arg(ap,struct block_list*)); + nullpo_retr(0, unit=(struct skill_unit *)bl); + nullpo_retr(0, group=unit->group); + + tick=va_arg(ap,unsigned int); + + if(!unit->alive || src->prev==NULL) + return 0; + + range=(unit->range!=0)?unit->range:group->range; + + if( range<0 || battle_check_target(bl,src,group->target_flag )<=0 ) + return 0; + + if( src->x >= bl->x-range && src->x <= bl->x+range && + src->y >= bl->y-range && src->y <= bl->y+range ) + skill_unit_onout( unit, src, tick ); + + return 0; +} + + +/*========================================== + * スキルユニット移動時処理 + *------------------------------------------ + */ +int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range) +{ + nullpo_retr(0, bl); + + if( bl->prev==NULL ) + return 0; + + if(range<7) + range=7; + map_foreachinarea( skill_unit_out_all_sub, + bl->m,bl->x-range,bl->y-range,bl->x+range,bl->y+range,BL_SKILL, + bl,tick ); + + return 0; +} + +/*========================================== + * スキルユニット移動時処理用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_move_sub( struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + struct block_list *src; + int range; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, unit=(struct skill_unit *)bl); + nullpo_retr(0, src=va_arg(ap,struct block_list*)); + + tick=va_arg(ap,unsigned int); + + if(!unit->alive || src->prev==NULL) + return 0; + + if((group=unit->group) == NULL) + return 0; + range=(unit->range!=0)?unit->range:group->range; + + if( range<0 || battle_check_target(bl,src,group->target_flag )<=0 ) + return 0; + + if( src->x >= bl->x-range && src->x <= bl->x+range && + src->y >= bl->y-range && src->y <= bl->y+range ) + skill_unit_onplace( unit, src, tick ); + else + skill_unit_onout( unit, src, tick ); + + return 0; +} + +/*========================================== + * スキルユニット移動時処理 + *------------------------------------------ + */ +int skill_unit_move( struct block_list *bl,unsigned int tick,int range) +{ + nullpo_retr(0, bl); + + if( bl->prev==NULL ) + return 0; + + if(range<7) + range=7; + map_foreachinarea( skill_unit_move_sub, + bl->m,bl->x-range,bl->y-range,bl->x+range,bl->y+range,BL_SKILL, + bl,tick ); + + return 0; +} + +/*========================================== + * スキルユニット自体の移動時処理(foreachinarea) + *------------------------------------------ + */ +int skill_unit_move_unit_group_sub( struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + struct block_list *src; + int range; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, src=va_arg(ap,struct block_list*)); + nullpo_retr(0, unit=(struct skill_unit *)src); + nullpo_retr(0, group=unit->group); + + tick=va_arg(ap,unsigned int); + + if(!unit->alive || bl->prev==NULL) + return 0; + + range=(unit->range!=0)?unit->range:group->range; + + if( range<0 || battle_check_target(src,bl,group->target_flag )<=0 ) + return 0; + if( bl->x >= src->x-range && bl->x <= src->x+range && + bl->y >= src->y-range && bl->y <= src->y+range ) + skill_unit_onplace( unit, bl, tick ); + else + skill_unit_onout( unit, bl, tick ); + return 0; +} + +/*========================================== + * スキルユニット自体の移動時処理 + * 引数はグループと移動量 + *------------------------------------------ + */ +int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy) +{ + nullpo_retr(0, group); + + if( group->unit_count<=0) + return 0; + + if(group->unit!=NULL){ + if(!battle_config.unit_movement_type){ + int i; + for(i=0;i<group->unit_count;i++){ + struct skill_unit *unit=&group->unit[i]; + if(unit->alive && !(m==unit->bl.m && dx==0 && dy==0)){ + int range=unit->range; + map_delblock(&unit->bl); + unit->bl.m = m; + unit->bl.x += dx; + unit->bl.y += dy; + map_addblock(&unit->bl); + clif_skill_setunit(unit); + if(range>0){ + if(range<7) + range=7; + map_foreachinarea( skill_unit_move_unit_group_sub, unit->bl.m, + unit->bl.x-range,unit->bl.y-range,unit->bl.x+range,unit->bl.y+range,0, + &unit->bl,gettick() ); + } + } + } + }else{ + int i,j, *r_flag, *s_flag, *m_flag; + struct skill_unit *unit1; + struct skill_unit *unit2; + r_flag = (int *) malloc(sizeof(int) * group->unit_count); + s_flag = (int *) malloc(sizeof(int) * group->unit_count); + m_flag = (int *) malloc(sizeof(int) * group->unit_count); + memset(r_flag,0, sizeof(int) * group->unit_count);// 継承フラグ + memset(s_flag,0, sizeof(int) * group->unit_count);// 継承フラグ + memset(m_flag,0, sizeof(int) * group->unit_count);// 継承フラグ + + //先にフラグを全部決める + for(i=0;i<group->unit_count;i++){ + int move_check=0;// かぶりフラグ + unit1=&group->unit[i]; + for(j=0;j<group->unit_count;j++){ + unit2=&group->unit[j]; + if(unit1->bl.m==m && unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){ + //移動先にユニットがかぶってたら + s_flag[i]=1;// 移動前のユニットナンバーの継承フラグon + r_flag[j]=1;// かぶるユニットナンバーの残留フラグon + move_check=1;//ユニットがかぶった。 + break; + } + } + if(!move_check)// ユニットがかぶってなかったら + m_flag[i]=1;// 移動前ユニットナンバーの移動フラグon + } + + //フラグに基づいてユニット移動 + for(i=0;i<group->unit_count;i++){ + unit1=&group->unit[i]; + if(m_flag[i]){// 移動フラグがonで + if(!r_flag[i]){// 残留フラグがoffなら + //単純移動(rangeも継承の必要無し) + int range=unit1->range; + map_delblock(&unit1->bl); + unit1->bl.m = m; + unit1->bl.x += dx; + unit1->bl.y += dy; + map_addblock(&unit1->bl); + clif_skill_setunit(unit1); + if(range > 0){ + if(range < 7) + range = 7; + map_foreachinarea( skill_unit_move_unit_group_sub, unit1->bl.m, + unit1->bl.x-range,unit1->bl.y-range,unit1->bl.x+range,unit1->bl.y+range,0, + &unit1->bl,gettick() ); + } + }else{// 残留フラグがonなら + //空ユニットになるので、継承可能なユニットを探す + for(j=0;j<group->unit_count;j++){ + unit2=&group->unit[j]; + if(s_flag[j] && !r_flag[j]){ + // 継承移動(range継承付き) + int range=unit1->range; + map_delblock(&unit2->bl); + unit2->bl.m = m; + unit2->bl.x = unit1->bl.x + dx; + unit2->bl.y = unit1->bl.y + dy; + unit2->range = unit1->range; + map_addblock(&unit2->bl); + clif_skill_setunit(unit2); + if(range > 0){ + if(range < 7) + range = 7; + map_foreachinarea( skill_unit_move_unit_group_sub, unit2->bl.m, + unit2->bl.x-range,unit2->bl.y-range,unit2->bl.x+range,unit2->bl.y+range,0, + &unit2->bl,gettick() ); + } + s_flag[j]=0;// 継承完了したのでoff + break; + } + } + } + } + } + free(r_flag); + free(s_flag); + free(m_flag); + } + } + return 0; +} + +/*---------------------------------------------------------------------------- + * アイテム合成 + *---------------------------------------------------------------------------- + */ + +/*========================================== + * アイテム合成可能判定 + *------------------------------------------ + */ +int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger ) +{ + int i,j; + + nullpo_retr(0, sd); + + if(nameid<=0) + return 0; + + for(i=0;i<MAX_SKILL_PRODUCE_DB;i++){ + if(skill_produce_db[i].nameid == nameid ) + break; + } + if( i >= MAX_SKILL_PRODUCE_DB ) /* データベースにない */ + return 0; + + if(trigger>=0){ + if(trigger==32 || trigger==16 || trigger==64){ + if(skill_produce_db[i].itemlv!=trigger) /* ファーマシー*ポーション類と溶鉱炉*鉱石以外はだめ */ + return 0; + }else{ + if(skill_produce_db[i].itemlv>=16) /* 武器以外はだめ */ + return 0; + if( itemdb_wlv(nameid)>trigger ) /* 武器Lv判定 */ + return 0; + } + } + if( (j=skill_produce_db[i].req_skill)>0 && pc_checkskill(sd,j)<=0 ) + return 0; /* スキルが足りない */ + + for(j=0;j<5;j++){ + int id,x,y; + if( (id=skill_produce_db[i].mat_id[j]) <= 0 ) /* これ以上は材料要らない */ + continue; + if(skill_produce_db[i].mat_amount[j] <= 0) { + if(pc_search_inventory(sd,id) < 0) + return 0; + } + else { + for(y=0,x=0;y<MAX_INVENTORY;y++) + if( sd->status.inventory[y].nameid == id ) + x+=sd->status.inventory[y].amount; + if(x<skill_produce_db[i].mat_amount[j]) /* アイテムが足りない */ + return 0; + } + } + return i+1; +} + +/*========================================== + * アイテム合成可能判定 + *------------------------------------------ + */ +int skill_produce_mix( struct map_session_data *sd, + int nameid, int slot1, int slot2, int slot3 ) +{ + 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)) ) /* 条件不足 */ + return 0; + idx--; + slot[0]=slot1; + slot[1]=slot2; + slot[2]=slot3; + + /* 埋め込み処理 */ + for(i=0,sc=0,ele=0;i<3;i++){ + int j; + if( slot[i]<=0 ) + continue; + j = pc_search_inventory(sd,slot[i]); + if(j < 0) /* 不正パケット(アイテム存在)チェック */ + continue; + if(slot[i]==1000){ /* 星のかけら */ + pc_delitem(sd,j,1,1); + sc++; + } + if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* 属性石 */ + 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<5;i++){ + int j,id,x; + if( (id=skill_produce_db[idx].mat_id[i]) <= 0 ) + continue; + x=skill_produce_db[idx].mat_amount[i]; /* 必要な個数 */ + do{ /* 2つ以上のインデックスにまたがっているかもしれない */ + int y=0; + j = pc_search_inventory(sd,id); + + if(j >= 0){ + y = sd->status.inventory[j].amount; + if(y>x)y=x; /* 足りている */ + pc_delitem(sd,j,y,0); + }else { + if(battle_config.error_log) + printf("skill_produce_mix: material item error\n"); + } + + x-=y; /* まだ足りない個数を計算 */ + }while( j>=0 && x>0 ); /* 材料を消費するか、エラーになるまで繰り返す */ + } + + /* 確率判定 */ + equip = itemdb_isequip(nameid); + if(!equip) { + if(skill_produce_db[idx].req_skill==AM_PHARMACY) { + if((nameid >= 501 && nameid <= 506) || (nameid >= 545 && nameid <= 547) || nameid == 525) + make_per = 2000 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300 + pc_checkskill(sd,AM_POTIONPITCHER)*100; + else if(nameid == 970) + make_per = 1500 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300; + else if(nameid == 7135) + make_per = 1000 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300 + pc_checkskill(sd,AM_DEMONSTRATION)*100; + else if(nameid == 7136) + make_per = 1000 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300 + pc_checkskill(sd,AM_ACIDTERROR)*100; + else if(nameid == 7137) + make_per = 1000 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300 + pc_checkskill(sd,AM_CANNIBALIZE)*100; + else if(nameid == 7138) + make_per = 1000 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300 + pc_checkskill(sd,AM_SPHEREMINE)*100; + else if(nameid == 7139) + make_per = 1000 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300 + pc_checkskill(sd,AM_CP_WEAPON)*100 + + pc_checkskill(sd,AM_CP_SHIELD)*100 + pc_checkskill(sd,AM_CP_ARMOR)*100 + pc_checkskill(sd,AM_CP_HELM)*100; + else + make_per = 1000 + sd->status.base_level*30 + sd->paramc[3]*20 + sd->paramc[4]*15 + pc_checkskill(sd,AM_LEARNINGPOTION)*100 + pc_checkskill(sd,AM_PHARMACY)*300; + } + else { + if(nameid == 998) + make_per = 2000 + sd->status.base_level*30 + sd->paramc[4]*20 + sd->paramc[5]*10 + pc_checkskill(sd,skill_produce_db[idx].req_skill)*600; + else if(nameid == 985) + make_per = 1000 + sd->status.base_level*30 + sd->paramc[4]*20 + sd->paramc[5]*10 + (pc_checkskill(sd,skill_produce_db[idx].req_skill)-1)*500; + else + make_per = 1000 + sd->status.base_level*30 + sd->paramc[4]*20 + sd->paramc[5]*10 + pc_checkskill(sd,skill_produce_db[idx].req_skill)*500; + } + } + else { + int add_per; + if(pc_search_inventory(sd,989) >= 0) add_per = 750; + else if(pc_search_inventory(sd,988) >= 0) add_per = 500; + else if(pc_search_inventory(sd,987) >= 0) add_per = 250; + else if(pc_search_inventory(sd,986) >= 0) add_per = 0; + else add_per = -500; + if(ele) add_per -= 500; + add_per -= sc*500; + wlv = itemdb_wlv(nameid); + make_per = ((250 + sd->status.base_level*15 + sd->paramc[4]*10 + sd->paramc[5]*5 + pc_checkskill(sd,skill_produce_db[idx].req_skill)*500 + + add_per) * (100 - (wlv - 1)*20))/100 + pc_checkskill(sd,BS_WEAPONRESEARCH)*100 + ((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100 : 0); + } + + if(make_per < 1) make_per = 1; + + if(skill_produce_db[idx].req_skill==AM_PHARMACY) { + if( battle_config.pp_rate!=100 ) + make_per=make_per*battle_config.pp_rate/100; + } + else { + if( battle_config.wp_rate!=100 ) /* 確率補正 */ + make_per=make_per*battle_config.wp_rate/100; + } + +// if(battle_config.etc_log) +// printf("make rate = %d\n",make_per); + + if(rand()%10000 < make_per){ + /* 成功 */ + 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; /* 属性とつよさ */ + *((unsigned long *)(&tmp_item.card[2]))=sd->char_id; /* キャラID */ + } + else if((battle_config.produce_item_name_input && skill_produce_db[idx].req_skill!=AM_PHARMACY) || + (battle_config.produce_potion_name_input && skill_produce_db[idx].req_skill==AM_PHARMACY)) { + tmp_item.card[0]=0x00fe; + tmp_item.card[1]=0; + *((unsigned long *)(&tmp_item.card[2]))=sd->char_id; /* キャラID */ + } + + if(skill_produce_db[idx].req_skill!=AM_PHARMACY && skill_produce_db[idx].req_skill!=WS_CREATECOIN) { //武器製造の場合 + clif_produceeffect(sd,0,nameid);/* 武器製造エフェクトパケット */ + clif_misceffect(&sd->bl,3); /* 他人にも成功を通知(精錬成功エフェクトと同じでいいの?) */ + } + else if(skill_produce_db[idx].req_skill==AM_PHARMACY){ //ファーマシーの場合 + clif_produceeffect(sd,2,nameid);/* 製薬エフェクトパケット */ + clif_misceffect(&sd->bl,5); /* 他人にも成功を通知*/ + }else{ + clif_produceeffect(sd,0,nameid);/* 不明なのでとりあえず製造エフェクトパケット */ + clif_misceffect(&sd->bl,3); /* 他人にも成功を通知*/ + } + + 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); + } + } + else { + if(skill_produce_db[idx].req_skill!=AM_PHARMACY) { //武器製造の場合 + clif_produceeffect(sd,1,nameid);/* 武器製造失敗エフェクトパケット */ + clif_misceffect(&sd->bl,2); /* 他人にも失敗を通知 */ + } + else if(skill_produce_db[idx].req_skill==AM_PHARMACY){ //ファーマシーの場合 + clif_produceeffect(sd,3,nameid);/* 製薬失敗エフェクトパケット */ + clif_misceffect(&sd->bl,6); /* 他人にも失敗を通知*/ + }else{ + clif_produceeffect(sd,1,nameid);/* 不明なのでとりあえず製造失敗エフェクトパケット */ + clif_misceffect(&sd->bl,2); /* 他人にも失敗を通知*/ + } + } + 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; + *((unsigned long *)(&tmp_item.card[2]))=sd->char_id; /* キャラID */ + } + 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; +} + +/*---------------------------------------------------------------------------- + * 初期化系 + */ + +/*========================================== + * スキル関係ファイル読み込み + * skill_db.txt スキルデータ + * skill_cast_db.txt スキルの詠唱時間とディレイデータ + * produce_db.txt アイテム作成スキル用データ + * create_arrow_db.txt 矢作成スキル用データ + * abra_db.txt アブラカダブラ発動スキルデータ + *------------------------------------------ + */ +int skill_readdb(void) +{ + int i,j,k,l,m; + FILE *fp; + char line[1024],*p; + char *filename[]={"db/produce_db.txt","db/produce_db2.txt"}; + + /* スキルデータベース */ + memset(skill_db,0,sizeof(skill_db)); + fp=fopen("db/skill_db.txt","r"); + if(fp==NULL){ + printf("can't read db/skill_db.txt\n"); + return 1; + } + while(fgets(line,1020,fp)){ + char *split[50], *split2[MAX_SKILL_LEVEL]; + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<14 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(split[13]==NULL || j<14) + continue; + + i=atoi(split[0]); + if(i<0 || i>MAX_SKILL_DB) + continue; + +/* printf("skill id=%d\n",i); */ + memset(split2,0,sizeof(split2)); + for(j=0,p=split[1];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].range[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + skill_db[i].hit=atoi(split[2]); + skill_db[i].inf=atoi(split[3]); + skill_db[i].pl=atoi(split[4]); + skill_db[i].nk=atoi(split[5]); + skill_db[i].max=atoi(split[6]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[7];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].num[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + if(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; + memset(split2,0,sizeof(split2)); + for(j=0,p=split[13];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].blewcount[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + } + fclose(fp); + printf("read db/skill_db.txt done\n"); + + fp=fopen("db/skill_require_db.txt","r"); + if(fp==NULL){ + printf("can't read db/skill_require_db.txt\n"); + return 1; + } + while(fgets(line,1020,fp)){ + char *split[51], *split2[MAX_SKILL_LEVEL]; + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<30 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(split[29]==NULL || j<30) + continue; + + i=atoi(split[0]); + if(i<0 || i>MAX_SKILL_DB) + continue; + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[1];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].hp[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[2];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].mhp[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[3];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].sp[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[4];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].hp_rate[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[5];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].sp_rate[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[6];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].zeny[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[7];j<32 && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<32 && split2[k];k++) { + l = atoi(split2[k]); + if(l == 99) { + skill_db[i].weapon = 0xffffffff; + break; + } + else + skill_db[i].weapon |= 1<<l; + } + + if( 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],"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; + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[9];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].spiritball[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + skill_db[i].itemid[0]=atoi(split[10]); + skill_db[i].amount[0]=atoi(split[11]); + skill_db[i].itemid[1]=atoi(split[12]); + skill_db[i].amount[1]=atoi(split[13]); + skill_db[i].itemid[2]=atoi(split[14]); + skill_db[i].amount[2]=atoi(split[15]); + skill_db[i].itemid[3]=atoi(split[16]); + skill_db[i].amount[3]=atoi(split[17]); + skill_db[i].itemid[4]=atoi(split[18]); + skill_db[i].amount[4]=atoi(split[19]); + skill_db[i].itemid[5]=atoi(split[20]); + skill_db[i].amount[5]=atoi(split[21]); + skill_db[i].itemid[6]=atoi(split[22]); + skill_db[i].amount[6]=atoi(split[23]); + skill_db[i].itemid[7]=atoi(split[24]); + skill_db[i].amount[7]=atoi(split[25]); + skill_db[i].itemid[8]=atoi(split[26]); + skill_db[i].amount[8]=atoi(split[27]); + skill_db[i].itemid[9]=atoi(split[28]); + skill_db[i].amount[9]=atoi(split[29]); + } + fclose(fp); + printf("read db/skill_require_db.txt done\n"); + + /* キャスティングデータベース */ + fp=fopen("db/skill_cast_db.txt","r"); + if(fp==NULL){ + printf("can't read db/skill_cast_db.txt\n"); + return 1; + } + while(fgets(line,1020,fp)){ + char *split[50], *split2[MAX_SKILL_LEVEL]; + memset(split,0,sizeof(split)); // [Valaris] thanks to fov + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<5 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(split[4]==NULL || j<5) + continue; + + i=atoi(split[0]); + if(i<0 || i>MAX_SKILL_DB) + continue; + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[1];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].cast[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[2];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].delay[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[3];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].upkeep_time[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[4];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].upkeep_time2[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + } + fclose(fp); + printf("read db/skill_cast_db.txt done\n"); + + /* 製造系スキルデータベース */ + memset(skill_produce_db,0,sizeof(skill_produce_db)); + for(m=0;m<2;m++){ + fp=fopen(filename[m],"r"); + if(fp==NULL){ + if(m>0) + continue; + printf("can't read %s\n",filename[m]); + 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)); + for(j=0,p=line;j<13 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(split[0]==NULL) + 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<5;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); + printf("read %s done (count=%d)\n",filename[m],k); + } + + memset(skill_arrow_db,0,sizeof(skill_arrow_db)); + fp=fopen("db/create_arrow_db.txt","r"); + if(fp==NULL){ + printf("can't read db/create_arrow_db.txt\n"); + 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)); + for(j=0,p=line;j<13 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(split[0]==NULL) + 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); + printf("read db/create_arrow_db.txt done (count=%d)\n",k); + + memset(skill_abra_db,0,sizeof(skill_abra_db)); + fp=fopen("db/abra_db.txt","r"); + if(fp==NULL){ + printf("can't read db/abra_db.txt\n"); + return 1; + } + k=0; + while(fgets(line,1020,fp)){ + char *split[16]; + if(line[0]=='/' && line[1]=='/') + continue; + memset(split,0,sizeof(split)); + for(j=0,p=line;j<13 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(split[0]==NULL) + 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); + printf("read db/abra_db.txt done (count=%d)\n",k); + + fp=fopen("db/skill_castnodex_db.txt","r"); + if(fp==NULL){ + printf("can't read db/skill_castnodex_db.txt\n"); + return 1; + } + while(fgets(line,1020,fp)){ + char *split[50], *split2[MAX_SKILL_LEVEL]; + memset(split,0,sizeof(split)); + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<2 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + + i=atoi(split[0]); + if(i<0 || i>MAX_SKILL_DB) + continue; + + memset(split2,0,sizeof(split2)); + for(j=0,p=split[1];j<MAX_SKILL_LEVEL && p;j++){ + split2[j]=p; + p=strchr(p,':'); + if(p) *p++=0; + } + for(k=0;k<MAX_SKILL_LEVEL;k++) + skill_db[i].castnodex[k]=(split2[k])? atoi(split2[k]):atoi(split2[0]); + } + fclose(fp); + printf("read db/skill_castnodex_db.txt done\n"); + + return 0; +} + +void skill_reload(void) +{ + /* + + <empty skill database> + <?> + + */ + + do_init_skill(); +} + +/*========================================== + * スキル関係初期化処理 + *------------------------------------------ + */ +int do_init_skill(void) +{ + skill_readdb(); + + add_timer_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_status_change_timer,"skill_status_change_timer"); + 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 0000000..6cb3d88 --- /dev/null +++ b/src/map/skill.h @@ -0,0 +1,842 @@ +// $Id: skill.h,v 1.5 2004/09/25 05:32:19 MouseJstr Exp $ +#ifndef _SKILL_H_ +#define _SKILL_H_ + +#include "map.h" + +#define MAX_SKILL_DB 450 +#define MAX_SKILL_PRODUCE_DB 150 +#define MAX_SKILL_ARROW_DB 150 +#define MAX_SKILL_ABRA_DB 350 + +// スキルデータベース +struct skill_db { + 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]; +}; +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 +}; +extern struct skill_name_db skill_names[]; + +// アイテム作成データベース +struct skill_produce_db { + int nameid, trigger; + int req_skill,itemlv; + int mat_id[5],mat_amount[5]; +}; +extern struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; + +// 矢作成データベース +struct skill_arrow_db { + int nameid, trigger; + int cre_id[5],cre_amount[5]; +}; +extern struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; + +// アブラカダブラデータベース +struct skill_abra_db { + int nameid; + int req_lv; + int per; +}; +extern struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; + +struct block_list; +struct map_session_data; +struct skill_unit; +struct skill_unit_group; + +int do_init_skill(void); + +// スキルデータベースへのアクセサ +int skill_get_hit( int id ); +int skill_get_inf( int id ); +int skill_get_pl( int id ); +int skill_get_nk( int id ); +int skill_get_max( int id ); +int skill_get_range( int id , int lv ); +int skill_get_hp( int id ,int lv ); +int skill_get_mhp( int id ,int lv ); +int skill_get_sp( int id ,int lv ); +int skill_get_zeny( int id ,int lv ); +int skill_get_num( int id ,int lv ); +int skill_get_cast( int id ,int lv ); +int skill_get_delay( int id ,int lv ); +int skill_get_time( int id ,int lv ); +int skill_get_time2( int id ,int lv ); +int skill_get_castdef( int id ); +int skill_get_weapontype( int id ); +int skill_get_unit_id(int id,int flag); +int skill_get_inf2( int id ); +int skill_get_maxcount( int id ); +int skill_get_blewcount( int id ,int lv ); + +// スキルの使用 +int skill_use_id( struct map_session_data *sd, int target_id, + int skill_num,int skill_lv); +int skill_use_pos( struct map_session_data *sd, + int skill_x, int skill_y, int skill_num, int skill_lv); + +int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map); + +int skill_cleartimerskill(struct block_list *src); +int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag); + +// 追加効果 +int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick); + +// ユニットスキル +struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y); +int skill_delunit(struct skill_unit *unit); +struct skill_unit_group *skill_initunitgroup(struct block_list *src, + int count,int skillid,int skilllv,int unit_id); +int skill_delunitgroup(struct skill_unit_group *group); +struct skill_unit_group_tickset *skill_unitgrouptickset_search( + struct block_list *bl,int group_id); +int skill_unitgrouptickset_delete(struct block_list *bl,int group_id); +int skill_clear_unitgroup(struct block_list *src); + +int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl, + int damage,unsigned int tick); + +int skill_castfix( struct block_list *bl, int time ); +int skill_delayfix( struct block_list *bl, int time ); +int skill_check_unit_range(int m,int x,int y,int range,int skillid); +int skill_check_unit_range2(int m,int x,int y,int range); +// -- moonsoul (added skill_check_unit_cell) +int skill_check_unit_cell(int skillid,int m,int x,int y,int unit_id); +int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range); +int skill_unit_move( struct block_list *bl,unsigned int tick,int range); +int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy); + +struct skill_unit_group *skill_check_dancing( struct block_list *src ); +void skill_stop_dancing(struct block_list *src, int flag); + +// 詠唱キャンセル +int skill_castcancel(struct block_list *bl,int type); + +int skill_gangsterparadise(struct map_session_data *sd ,int type); +void skill_brandishspear_first(struct square *tc,int dir,int x,int y); +void skill_brandishspear_dir(struct square *tc,int dir,int are); +int skill_autospell(struct map_session_data *md,int skillid); +void skill_devotion(struct map_session_data *md,int target); +void skill_devotion2(struct block_list *bl,int crusader); +int skill_devotion3(struct block_list *bl,int target); +void skill_devotion_end(struct map_session_data *md,struct map_session_data *sd,int target); + +#define skill_calc_heal(bl,skill_lv) (( battle_get_lv(bl)+battle_get_int(bl) )/8 *(4+ skill_lv*8)) + +// その他 +int skill_check_cloaking(struct block_list *bl); +int skill_is_danceskill(int id); + +// ステータス異常 +int skill_status_change_start(struct block_list *bl,int type,int val1,int val2,int val3,int val4,int tick,int flag); +int skill_status_change_timer(int tid, unsigned int tick, int id, int data); +int skill_encchant_eremental_end(struct block_list *bl, int type); +int skill_status_change_end( struct block_list* bl , int type,int tid ); +int skill_status_change_clear(struct block_list *bl,int type); + + +// アイテム作成 +int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger ); +int skill_produce_mix( struct map_session_data *sd, + int nameid, int slot1, int slot2, int slot3 ); + +int skill_arrow_create( struct map_session_data *sd,int nameid); + +// mobスキルのため +int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); +int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); +int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag); + +// スキル攻撃一括処理 +int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc, + struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); + +void skill_reload(void); + +enum { + ST_NONE,ST_HIDING,ST_CLOAKING,ST_HIDDEN,ST_RIDING,ST_FALCON,ST_CART,ST_SHIELD,ST_SIGHT,ST_EXPLOSIONSPIRITS, + ST_RECOV_WEIGHT_RATE,ST_MOVE_ENABLE,ST_WATER, +}; + +enum { // struct map_session_data の status_changeの番号テーブル +// SC_SENDMAX未満はクライアントへの通知あり。 +// 2-2次職の値はなんかめちゃくちゃっぽいので暫定。たぶん変更されます。 + SC_SENDMAX =128, + SC_PROVOKE = 0, + SC_ENDURE = 1, + SC_TWOHANDQUICKEN = 2, + SC_CONCENTRATE = 3, + SC_HIDING = 4, + SC_CLOAKING = 5, + SC_ENCPOISON = 6, + SC_POISONREACT = 7, + SC_QUAGMIRE = 8, + SC_ANGELUS = 9, + SC_BLESSING =10, + SC_SIGNUMCRUCIS =11, + SC_INCREASEAGI =12, + SC_DECREASEAGI =13, + SC_SLOWPOISON =14, + SC_IMPOSITIO =15, + SC_SUFFRAGIUM =16, + SC_ASPERSIO =17, + SC_BENEDICTIO =18, + SC_KYRIE =19, + SC_MAGNIFICAT =20, + SC_GLORIA =21, + SC_AETERNA =22, + SC_ADRENALINE =23, + SC_WEAPONPERFECTION =24, + SC_OVERTHRUST =25, + SC_MAXIMIZEPOWER =26, + SC_RIDING =27, + SC_FALCON =28, + SC_TRICKDEAD =29, + SC_LOUD =30, + SC_ENERGYCOAT =31, + SC_HALLUCINATION =34, + SC_WEIGHT50 =35, + SC_WEIGHT90 =36, + SC_SPEEDPOTION0 =37, + SC_SPEEDPOTION1 =38, + SC_SPEEDPOTION2 =39, + SC_STRIPWEAPON =50, + SC_STRIPSHIELD =51, + SC_STRIPARMOR =52, + SC_STRIPHELM =53, + SC_CP_WEAPON =54, + SC_CP_SHIELD =55, + SC_CP_ARMOR =56, + SC_CP_HELM =57, + SC_AUTOGUARD =58, + SC_REFLECTSHIELD =59, + SC_DEVOTION =60, + SC_PROVIDENCE =61, + SC_DEFENDER =62, + SC_AUTOSPELL =65, + SC_SPEARSQUICKEN =68, + SC_EXPLOSIONSPIRITS =86, + SC_STEELBODY =87, + SC_COMBO =89, + SC_FLAMELAUNCHER =90, + SC_FROSTWEAPON =91, + SC_LIGHTNINGLOADER =92, + SC_SEISMICWEAPON =93, + SC_AURABLADE =103, /* オーラブレード */ + SC_PARRYING =104, /* パリイング */ + SC_CONCENTRATION =105, /* コンセントレーション */ + SC_TENSIONRELAX =106, /* テンションリラックス */ + SC_BERSERK =107, /* バーサーク */ + SC_ASSUMPTIO =110, /* アシャンプティオ */ + SC_MAGICPOWER =113, /* 魔法力増幅 */ + SC_TRUESIGHT =115, /* トゥルーサイト */ + SC_WINDWALK =116, /* ウインドウォーク */ + SC_MELTDOWN =117, /* メルトダウン */ + SC_CARTBOOST =118, /* カートブースト */ + SC_REJECTSWORD =120, /* リジェクトソード */ + SC_MARIONETTE =121, /* マリオネットコントロール */ + SC_HEADCRUSH =124, /* ヘッドクラッシュ */ + SC_JOINTBEAT =125, /* ジョイントビート */ + + SC_STONE =128, + SC_FREEZE =129, + SC_STAN =130, + SC_SLEEP =131, + SC_POISON =132, + SC_CURSE =133, + SC_SILENCE =134, + SC_CONFUSION =135, + SC_BLIND =136, + SC_DIVINA = SC_SILENCE, + + SC_SAFETYWALL =140, + SC_PNEUMA =141, + SC_WATERBALL =142, + SC_ANKLE =143, + SC_DANCING =144, + SC_KEEPING =145, + SC_BARRIER =146, + + SC_MAGICROD =149, + SC_SIGHT =150, + SC_RUWACH =151, + SC_AUTOCOUNTER =152, + SC_VOLCANO =153, + SC_DELUGE =154, + SC_VIOLENTGALE =155, + SC_BLADESTOP_WAIT =156, + SC_BLADESTOP =157, + SC_EXTREMITYFIST =158, + SC_GRAFFITI =159, + + SC_LULLABY =160, + SC_RICHMANKIM =161, + SC_ETERNALCHAOS =162, + SC_DRUMBATTLE =163, + SC_NIBELUNGEN =164, + SC_ROKISWEIL =165, + SC_INTOABYSS =166, + SC_SIEGFRIED =167, + SC_DISSONANCE =168, + SC_WHISTLE =169, + SC_ASSNCROS =170, + SC_POEMBRAGI =171, + SC_APPLEIDUN =172, + SC_UGLYDANCE =173, + SC_HUMMING =174, + SC_DONTFORGETME =175, + SC_FORTUNE =176, + SC_SERVICE4U =177, + + SC_SPIDERWEB =180, /* スパイダーウェッブ */ + SC_MEMORIZE =181, /* メモライズ */ + + SC_WEDDING =187, //結婚用(結婚衣裳になって歩くのが遅いとか) + SC_NOCHAT =188, //赤エモ状態 + SC_SPLASHER =189, /* ベナムスプラッシャー */ + SC_SELFDESTRUCTION =190, /* 自爆 */ + + +// Used by English Team + SC_BROKNARMOR =32, + SC_BROKNWEAPON =33, + SC_SIGHTTRASHER =73, + SC_BASILICA =125, + SC_ENSEMBLE =159, + SC_FOGWALL =178, + SC_GOSPEL =179, + SC_LANDPROTECTOR =182, + SC_ADAPTATION =183, + SC_CHASEWALK =184, + SC_ATKPOT =185, // [Valaris] + SC_MATKPOT =186, // [Valaris] + SC_MINDBREAKER =191, + SC_SPELLBREAKER =192, + +// -- testing various SC effects +// SC_AURABLADE =81, +// SC_CONCENTRATION =83, +// SC_TENSIONRELAX =84, +// SC_BERSERK =85, +// SC_CALLSPIRITS =100, +// SC_PARRYING =100, +// SC_FREECAST =101, +// SC_ABSORBSPIRIT =102, +// SC_ASSUMPTIO =114, +// SC_SHARPSHOOT =127, +// SC_GANGSTER =184, +// SC_CANNIBALIZE =186, +// SC_SPHEREMINE =187, +// SC_METEOSTORM =189, +// SC_CASTCANCEL =190, +// SC_SPIDERWEB =191, +}; +extern int SkillStatusChangeTable[]; + +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, + + WE_MALE = 334, + WE_FEMALE, + WE_CALLPARTNER, + + NPC_SELFDESTRUCTION2 = 331, + NPC_DARKCROSS = 338, + + LK_AURABLADE = 355, + LK_PARRYING, + LK_CONCENTRATION, + LK_TENSIONRELAX, + LK_BERSERK, + LK_FURY, + HP_ASSUMPTIO, + HP_BASILICA, + HP_MEDITATIO, + HW_SOULDRAIN, + HW_MAGICCRASHER, + HW_MAGICPOWER, + PA_PRESSURE, + PA_SACRIFICE, + PA_GOSPEL, + CH_PALMSTRIKE, + CH_TIGERFIST, + CH_CHAINCRUSH, + PF_HPCONVERSION, + PF_SOULCHANGE, + PF_SOULBURN, + ASC_KATAR, + ASC_HALLUCINATION, + ASC_EDP, + ASC_BREAKER, + SN_SIGHT, + SN_FALCONASSAULT, + SN_SHARPSHOOTING, + SN_WINDWALK, + WS_MELTDOWN, + WS_CREATECOIN, + WS_CREATENUGGET, + WS_CARTBOOST, + WS_SYSTEMCREATE, + ST_CHASEWALK, + ST_REJECTSWORD, + ST_STEALBACKPACK, + CR_ALCHEMY, + CR_SYNTHESISPOTION, + CG_ARROWVULCAN, + CG_MOONLIT, + CG_MARIONETTE, + LK_SPIRALPIERCE, + LK_HEADCRUSH, + LK_JOINTBEAT, + HW_NAPALMVULCAN, + CH_SOULCOLLECT, + PF_MINDBREAKER, + PF_MEMORIZE, + PF_FOGWALL, + PF_SPIDERWEB, + ASC_METEORASSAULT, + ASC_CDP, + WE_BABY, + WE_CALLPARENT, + WE_CALLBABY, + TK_RUN, + TK_READYSTORM, + TK_STORMKICK, + TK_READYDOWN, + TK_DOWNKICK, + TK_READYTURN, + TK_TURNKICK, + TK_READYCOUNTER, + TK_COUNTER, + TK_DODGE, + TK_JUMPKICK, + TK_HPTIME, + TK_SPTIME, + TK_POWER, + TK_SEVENWIND, + TK_HIGHJUMP, + SG_FEEL, + SG_SUN_WARM, + SG_MOON_WARM, + SG_STAR_WARM, + SG_SUN_COMFORT, + SG_MOON_COMFORT, + SG_STAR_COMFORT, + SG_HATE, + SG_SUN_ANGER, + SG_MOON_ANGER, + SG_STAR_ANGER, + SG_SUN_BLESS, + SG_MOON_BLESS, + SG_STAR_BLESS, + SG_DEVIL, + SG_FRIEND, + SG_KNOWLEDGE, + SG_FUSION, + SL_ALCHEMIST, + AM_BERSERKPITCHER, + SL_MONK, + SL_STAR, + SL_SAGE, + SL_CRUSADER, + SL_SUPERNOVICE, + SL_KNIGHT, + SL_WIZARD, + SL_PRIEST, + SL_BARDDANCER, + SL_ROGUE, + SL_ASSASIN, + SL_BLACKSMITH, + BS_ADRENALINE2, + SL_HUNTER, + SL_SOULLINKER, + SL_KAIZEL, + SL_KAAHI, + SL_KAUPE, + SL_KAITE, + SL_KAINA, + SL_STIN, + SL_STUN, + SL_SMA, + SL_SWOO, + SL_SKE, + SL_SKA, + + GD_APPROVAL=10000, + GD_KAFRACONTACT, + GD_GUARDIANRESEARCH, + GD_CHARISMA, + GD_EXTENSION, +}; + +#endif + diff --git a/src/map/storage.c b/src/map/storage.c new file mode 100644 index 0000000..696a74e --- /dev/null +++ b/src/map/storage.c @@ -0,0 +1,576 @@ +// $Id: storage.c,v 1.3 2004/09/25 02:05:22 MouseJstr Exp $ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "db.h" +#include "itemdb.h" +#include "clif.h" +#include "intif.h" +#include "pc.h" +#include "storage.h" +#include "guild.h" +#include "nullpo.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +static struct dbt *storage_db; +static struct dbt *guild_storage_db; + +/*========================================== + * 倉庫内アイテムソート + *------------------------------------------ + */ +int storage_comp_item(const void *_i1, const void *_i2){ +struct item *i1=(struct item *)_i1; +struct item *i2=(struct item *)_i2; + + if (i1->nameid == i2->nameid) { + return 0; + } else if (!(i1->nameid) || !(i1->amount)){ + return 1; + } else if (!(i2->nameid) || !(i2->amount)){ + return -1; + } else { + 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=numdb_init(); + guild_storage_db=numdb_init(); + return 1; +} + +void do_final_storage(void) // map.c::do_final()から呼ばれる +{ +} + +struct storage *account2storage(int account_id) +{ + struct storage *stor; + stor=numdb_search(storage_db,account_id); + if(stor == NULL) { + stor = calloc(sizeof(struct storage), 1); + if(stor == NULL){ + printf("storage: out of memory!\n"); + exit(0); + } + memset(stor,0,sizeof(struct storage)); + stor->account_id=account_id; + numdb_insert(storage_db,stor->account_id,stor); + } + return stor; +} + +// Just to ask storage, without creation +struct storage *account2storage2(int account_id) { + return numdb_search(storage_db, account_id); +} + +int storage_delete(int account_id) +{ + struct storage *stor = numdb_search(storage_db,account_id); + if(stor) { + numdb_erase(storage_db,account_id); + free(stor); + } + return 0; +} + +/*========================================== + * カプラ倉庫を開く + *------------------------------------------ + */ +int storage_storageopen(struct map_session_data *sd) +{ + struct storage *stor; + + nullpo_retr(0, sd); + + if((stor = numdb_search(storage_db,sd->status.account_id)) != NULL) { + stor->storage_status = 1; + sd->state.storage_flag = 0; + clif_storageitemlist(sd,stor); + clif_storageequiplist(sd,stor); + clif_updatestorageamount(sd,stor); + return 0; + } else + intif_request_storage(sd->status.account_id); + + return 1; +} + +/*========================================== + * カプラ倉庫へアイテム追加 + *------------------------------------------ + */ +int storage_additem(struct map_session_data *sd,struct storage *stor,struct item *item_data,int amount) +{ + struct item_data *data; + int i; + + nullpo_retr(1, sd); + nullpo_retr(1, stor); + nullpo_retr(1, item_data); + + if(item_data->nameid <= 0 || amount <= 0) + return 1; + nullpo_retr(1, data = itemdb_search(item_data->nameid)); + + i=MAX_STORAGE; + if(!itemdb_isequip2(data)){ + // 装備品ではないので、既所有品なら個数のみ変化させる + for(i=0;i<MAX_STORAGE;i++){ + if(stor->storage[i].nameid == item_data->nameid && + stor->storage[i].card[0] == item_data->card[0] && stor->storage[i].card[1] == item_data->card[1] && + stor->storage[i].card[2] == item_data->card[2] && stor->storage[i].card[3] == item_data->card[3]){ + if(stor->storage[i].amount+amount > MAX_AMOUNT) + return 1; + stor->storage[i].amount+=amount; + clif_storageitemadded(sd,stor,i,amount); + break; + } + } + } + if(i>=MAX_STORAGE){ + // 装備品か未所有品だったので空き欄へ追加 + for(i=0;i<MAX_STORAGE;i++){ + if(stor->storage[i].nameid==0){ + memcpy(&stor->storage[i],item_data,sizeof(stor->storage[0])); + stor->storage[i].amount=amount; + stor->storage_amount++; + clif_storageitemadded(sd,stor,i,amount); + clif_updatestorageamount(sd,stor); + break; + } + } + if(i>=MAX_STORAGE) + return 1; + } + return 0; +} +/*========================================== + * カプラ倉庫アイテムを減らす + *------------------------------------------ + */ +int storage_delitem(struct map_session_data *sd,struct storage *stor,int n,int amount) +{ + nullpo_retr(1, sd); + nullpo_retr(1, stor); + + if(stor->storage[n].nameid==0 || stor->storage[n].amount<amount) + return 1; + + stor->storage[n].amount-=amount; + if(stor->storage[n].amount==0){ + memset(&stor->storage[n],0,sizeof(stor->storage[0])); + stor->storage_amount--; + clif_updatestorageamount(sd,stor); + } + clif_storageitemremoved(sd,n,amount); + + return 0; +} +/*========================================== + * カプラ倉庫へ入れる + *------------------------------------------ + */ +int storage_storageadd(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage(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 + if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0) + // remove item from inventory + pc_delitem(sd,index,amount,0); + } // valid amount + }// valid index + }// storage not full & storage open + + return 0; +} + +/*========================================== + * カプラ倉庫から出す + *------------------------------------------ + */ +int storage_storageget(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + int flag; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage(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 + clif_additem(sd,0,0,flag); + } // valid amount + }// valid index + }// storage open + + return 0; +} +/*========================================== + * カプラ倉庫へカートから入れる + *------------------------------------------ + */ +int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage(sd->status.account_id)); + + if( (stor->storage_amount <= MAX_STORAGE) && (stor->storage_status == 1) ) { // storage not full & storage open + if(index>=0 && index<MAX_INVENTORY) { // valid index + if( (amount <= sd->status.cart[index].amount) && (amount > 0) ) { //valid amount + if(storage_additem(sd,stor,&sd->status.cart[index],amount)==0) + pc_cart_delitem(sd,index,amount,0); + } // valid amount + }// valid index + }// storage not full & storage open + + return 0; +} + +/*========================================== + * カプラ倉庫からカートへ出す + *------------------------------------------ + */ +int storage_storagegettocart(struct map_session_data *sd,int index,int amount) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage(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; +} + + +/*========================================== + * カプラ倉庫を閉じる + *------------------------------------------ + */ +int storage_storageclose(struct map_session_data *sd) +{ + struct storage *stor; + + nullpo_retr(0, sd); + nullpo_retr(0, stor=account2storage(sd->status.account_id)); + + stor->storage_status=0; + sd->state.storage_flag = 0; + clif_storageclose(sd); + + sortage_sortitem(stor); + return 0; +} + +/*========================================== + * ログアウト時開いているカプラ倉庫の保存 + *------------------------------------------ + */ +int storage_storage_quit(struct map_session_data *sd) +{ + struct storage *stor; + + nullpo_retr(0, sd); + + stor = numdb_search(storage_db,sd->status.account_id); + if(stor) stor->storage_status = 0; + + return 0; +} + +int storage_storage_save(struct map_session_data *sd) +{ + struct storage *stor; + + nullpo_retr(0, sd); + + stor=numdb_search(storage_db,sd->status.account_id); + if(stor) intif_send_storage(stor); + + return 0; +} + +struct guild_storage *guild2storage(int guild_id) +{ + struct guild_storage *gs = NULL; + if(guild_search(guild_id) != NULL) { + gs=numdb_search(guild_storage_db,guild_id); + if(gs == NULL) { + gs = calloc(sizeof(struct guild_storage), 1); + if(gs==NULL){ + printf("storage: out of memory!\n"); + exit(0); + } + gs->guild_id=guild_id; + numdb_insert(guild_storage_db,gs->guild_id,gs); + } + } + return gs; +} + +int guild_storage_delete(int guild_id) +{ + struct guild_storage *gstor = numdb_search(guild_storage_db,guild_id); + if(gstor) { + numdb_erase(guild_storage_db,guild_id); + free(gstor); + } + return 0; +} + +int storage_guild_storageopen(struct map_session_data *sd) +{ + struct guild_storage *gstor; + + nullpo_retr(0, sd); + + if(sd->status.guild_id <= 0) + return 2; + if((gstor = numdb_search(guild_storage_db,sd->status.guild_id)) != NULL) { + if(gstor->storage_status) + return 1; + gstor->storage_status = 1; + sd->state.storage_flag = 1; + 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; + + i=MAX_GUILD_STORAGE; + if(!itemdb_isequip2(data)){ + // 装備品ではないので、既所有品なら個数のみ変化させる + for(i=0;i<MAX_GUILD_STORAGE;i++){ + if(stor->storage[i].nameid == item_data->nameid && + stor->storage[i].card[0] == item_data->card[0] && stor->storage[i].card[1] == item_data->card[1] && + stor->storage[i].card[2] == item_data->card[2] && stor->storage[i].card[3] == item_data->card[3]){ + if(stor->storage[i].amount+amount > MAX_AMOUNT) + return 1; + stor->storage[i].amount+=amount; + clif_guildstorageitemadded(sd,stor,i,amount); + break; + } + } + } + if(i>=MAX_GUILD_STORAGE){ + // 装備品か未所有品だったので空き欄へ追加 + for(i=0;i<MAX_GUILD_STORAGE;i++){ + if(stor->storage[i].nameid==0){ + memcpy(&stor->storage[i],item_data,sizeof(stor->storage[0])); + stor->storage[i].amount=amount; + stor->storage_amount++; + clif_guildstorageitemadded(sd,stor,i,amount); + clif_updateguildstorageamount(sd,stor); + break; + } + } + if(i>=MAX_GUILD_STORAGE) + return 1; + } + 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); + + 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=guild2storage(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 + 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=guild2storage(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); + } // 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=guild2storage(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=guild2storage(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_storageclose(struct map_session_data *sd) +{ + struct guild_storage *stor; + + nullpo_retr(0, sd); + + if((stor=guild2storage(sd->status.guild_id)) != NULL) { + intif_send_guild_storage(sd->status.account_id,stor); + stor->storage_status = 0; + sd->state.storage_flag = 0; + sortage_gsortitem(stor); + } + clif_storageclose(sd); + + return 0; +} + +int storage_guild_storage_quit(struct map_session_data *sd,int flag) +{ + struct guild_storage *stor; + + nullpo_retr(0, sd); + + stor = numdb_search(guild_storage_db,sd->status.guild_id); + if(stor) { + if(!flag) + intif_send_guild_storage(sd->status.account_id,stor); + stor->storage_status = 0; + sd->state.storage_flag = 0; + } + + return 0; +} diff --git a/src/map/storage.h b/src/map/storage.h new file mode 100644 index 0000000..489741c --- /dev/null +++ b/src/map/storage.h @@ -0,0 +1,38 @@ +// $Id: storage.h,v 1.3 2004/09/25 05:32:19 MouseJstr Exp $ +#ifndef _STORAGE_H_ +#define _STORAGE_H_ + +#include "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); +struct storage *account2storage(int account_id); +struct storage *account2storage2(int account_id); +int storage_delete(int account_id); +int storage_storage_quit(struct map_session_data *sd); +int storage_storage_save(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_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 0000000..da43d67 --- /dev/null +++ b/src/map/trade.c @@ -0,0 +1,255 @@ +#include <stdio.h> +#include <string.h> + +#include "clif.h" +#include "itemdb.h" +#include "map.h" +#include "trade.h" +#include "pc.h" +#include "npc.h" +#include "battle.h" +#include "nullpo.h" + +/*========================================== + * 取引要請を相手に送る + *------------------------------------------ + */ +void trade_traderequest(struct map_session_data *sd,int target_id) +{ + struct map_session_data *target_sd; + + nullpo_retv(sd); + + if((target_sd = map_id2sd(target_id)) != NULL){ + if(!battle_config.invite_request_check) { + if(target_sd->guild_invite>0 || target_sd->party_invite>0){ + clif_tradestart(sd,2); // 相手はPT要請中かGuild要請中 + return; + } + } + if((target_sd->trade_partner !=0) || (sd->trade_partner !=0)) { + trade_tradecancel(sd); //person is in another trade + } + else{ + if(sd->bl.m != target_sd->bl.m + || (sd->bl.x - target_sd->bl.x <= -5 || sd->bl.x - target_sd->bl.x >= 5) + || (sd->bl.y - target_sd->bl.y <= -5 || sd->bl.y - target_sd->bl.y >= 5)) { + clif_tradestart(sd,0); //too far + } + else if(sd!=target_sd) { + target_sd->trade_partner = sd->status.account_id; + sd->trade_partner = target_sd->status.account_id; + clif_traderequest(target_sd,sd->status.name); + } + } + } + else{ + clif_tradestart(sd,1); //character does not exist + } +} + +/*========================================== + * 取引要請 + *------------------------------------------ + */ +void trade_tradeack(struct map_session_data *sd,int type) +{ + struct map_session_data *target_sd; + + nullpo_retv(sd); + + if((target_sd = map_id2sd(sd->trade_partner)) != NULL){ + clif_tradestart(target_sd,type); + clif_tradestart(sd,type); + if(type == 4){ // Cancel + sd->deal_locked =0; + sd->trade_partner=0; + target_sd->deal_locked=0; + target_sd->trade_partner=0; + } + if(sd->npc_id != 0) + npc_event_dequeue(sd); + if(target_sd->npc_id != 0) + npc_event_dequeue(target_sd); + } +} + +/*========================================== + * アイテム追加 + *------------------------------------------ + */ +void trade_tradeadditem(struct map_session_data *sd,int index,int amount) +{ + struct map_session_data *target_sd; + int trade_i; + int trade_weight=0; + int c; + + nullpo_retv(sd); + + if(((target_sd = map_id2sd(sd->trade_partner)) != NULL) && (sd->deal_locked < 1)){ + if(index<2 || index>=MAX_INVENTORY+2){ + if(index == 0 && amount > 0 && amount <= sd->status.zeny){ + sd->deal_zeny=amount; + clif_tradeadditem(sd,target_sd,0,amount); + } + }else if(amount <= sd->status.inventory[index-2].amount && amount > 0){ + for(trade_i=0; trade_i<10;trade_i++){ + if(sd->deal_item_amount[trade_i] == 0){ + trade_weight+=sd->inventory_data[index-2]->weight*amount; + if(target_sd->weight + trade_weight > target_sd->max_weight){ + clif_tradeitemok(sd,index,0,1); //fail to add item -- the player was over weighted. + amount = 0; // [MouseJstr] + }else{ + for(c=0; c==trade_i-1;c++){ // re-deal exploit protection [Valaris] + if(sd->deal_item_index[c]==index) { + trade_tradecancel(sd); + return; + } + } + sd->deal_item_index[trade_i] =index; + sd->deal_item_amount[trade_i]+=amount; + clif_tradeitemok(sd,index,amount,0); //success to add item + clif_tradeadditem(sd,target_sd,index,amount); + } + break; + }else{ + trade_weight+=sd->inventory_data[sd->deal_item_index[trade_i]-2]->weight*sd->deal_item_amount[trade_i]; + } + } + } + } +} + +/*========================================== + * アイテム追加完了(ok押し) + *------------------------------------------ + */ +void trade_tradeok(struct map_session_data *sd) +{ + struct map_session_data *target_sd; + int trade_i; + + nullpo_retv(sd); + + for(trade_i=0;trade_i<10;trade_i++) { + if(sd->deal_item_amount[trade_i]>sd->status.inventory[sd->deal_item_index[trade_i]-2].amount || + sd->deal_item_amount[trade_i]<0) { + trade_tradecancel(sd); + return; + } + + } + + if((target_sd = map_id2sd(sd->trade_partner)) != NULL){ + sd->deal_locked=1; + clif_tradeitemok(sd,0,0,0); + clif_tradedeal_lock(sd,0); + clif_tradedeal_lock(target_sd,1); + } +} + +/*========================================== + * 取引キャンセル + *------------------------------------------ + */ +void trade_tradecancel(struct map_session_data *sd) +{ + struct map_session_data *target_sd; + int trade_i; + + nullpo_retv(sd); + + if((target_sd = map_id2sd(sd->trade_partner)) != NULL){ + for(trade_i=0; trade_i<10;trade_i++) { //give items back (only virtual) + if(sd->deal_item_amount[trade_i] != 0) { + clif_additem(sd,sd->deal_item_index[trade_i]-2,sd->deal_item_amount[trade_i],0); + sd->deal_item_index[trade_i] =0; + sd->deal_item_amount[trade_i]=0; + } + if(target_sd->deal_item_amount[trade_i] != 0) { + clif_additem(target_sd,target_sd->deal_item_index[trade_i]-2,target_sd->deal_item_amount[trade_i],0); + target_sd->deal_item_index[trade_i] =0; + target_sd->deal_item_amount[trade_i]=0; + } + } + if(sd->deal_zeny) { + 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->deal_locked =0; + sd->trade_partner=0; + target_sd->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; + + nullpo_retv(sd); + + if((target_sd = map_id2sd(sd->trade_partner)) != NULL){ + if( (sd->deal_locked >=1) && (target_sd->deal_locked >=1) ){ // both have pressed 'ok' + if(sd->deal_locked < 2) {sd->deal_locked=2;} // set locked to 2 + if(target_sd->deal_locked==2) { // the other one pressed 'trade' too + for(trade_i=0; trade_i<10;trade_i++) { + if(sd->deal_item_amount[trade_i] != 0) { + int n=sd->deal_item_index[trade_i]-2; + int flag; + flag = pc_additem(target_sd,&sd->status.inventory[n],sd->deal_item_amount[trade_i]); + if(flag==0) + pc_delitem(sd,n,sd->deal_item_amount[trade_i],1); + else + clif_additem(sd,n,sd->deal_item_amount[trade_i],0); + sd->deal_item_index[trade_i] =0; + sd->deal_item_amount[trade_i]=0; + } + if(target_sd->deal_item_amount[trade_i] != 0) { + int n=target_sd->deal_item_index[trade_i]-2; + int flag; + flag = pc_additem(sd,&target_sd->status.inventory[n],target_sd->deal_item_amount[trade_i]); + if(flag==0) + pc_delitem(target_sd,n,target_sd->deal_item_amount[trade_i],1); + else + clif_additem(target_sd,n,target_sd->deal_item_amount[trade_i],0); + target_sd->deal_item_index[trade_i] =0; + target_sd->deal_item_amount[trade_i]=0; + } + } + if(sd->deal_zeny) { + sd->status.zeny -= sd->deal_zeny; + clif_updatestatus(sd,SP_ZENY); + target_sd->status.zeny += sd->deal_zeny; + clif_updatestatus(target_sd,SP_ZENY); + sd->deal_zeny=0; + } + if(target_sd->deal_zeny) { + target_sd->status.zeny -= target_sd->deal_zeny; + clif_updatestatus(target_sd,SP_ZENY); + sd->status.zeny += target_sd->deal_zeny; + clif_updatestatus(sd,SP_ZENY); + target_sd->deal_zeny=0; + } + sd->deal_locked =0; + sd->trade_partner=0; + target_sd->deal_locked=0; + target_sd->trade_partner=0; + clif_tradecompleted(sd,0); + clif_tradecompleted(target_sd,0); + } + } + } +} diff --git a/src/map/trade.h b/src/map/trade.h new file mode 100644 index 0000000..01cbce7 --- /dev/null +++ b/src/map/trade.h @@ -0,0 +1,13 @@ +// $Id: trade.h,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ +#ifndef _TRADE_H_ +#define _TRADE_H_ + +#include "map.h" +void trade_traderequest(struct map_session_data *sd,int target_id); +void trade_tradeack(struct map_session_data *sd,int type); +void trade_tradeadditem(struct map_session_data *sd,int index,int amount); +void trade_tradeok(struct map_session_data *sd); +void trade_tradecancel(struct map_session_data *sd); +void trade_tradecommit(struct map_session_data *sd); + +#endif // _TRADE_H_ diff --git a/src/map/vending.c b/src/map/vending.c new file mode 100644 index 0000000..189584d --- /dev/null +++ b/src/map/vending.c @@ -0,0 +1,163 @@ +// $Id: vending.c,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ +#include <stdio.h> +#include <string.h> + +#include "clif.h" +#include "itemdb.h" +#include "map.h" +#include "vending.h" +#include "pc.h" +#include "skill.h" +#include "battle.h" +#include "nullpo.h" + +/*========================================== + * 露店閉鎖 + *------------------------------------------ +*/ +void vending_closevending(struct map_session_data *sd) +{ + + nullpo_retv(sd); + + sd->vender_id=0; + clif_closevendingboard(&sd->bl,0); +} + +/*========================================== + * 露店アイテムリスト要求 + *------------------------------------------ + */ +void vending_vendinglistreq(struct map_session_data *sd,int id) +{ + struct map_session_data *vsd; + + nullpo_retv(sd); + + if( (vsd=map_id2sd(id)) == NULL ) + return; + if(vsd->vender_id==0) + return; + clif_vendinglist(sd,id,vsd->vending); +} + +/*========================================== + * 露店アイテム購入 + *------------------------------------------ + */ +void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p) +{ + int i,j,w,z,new=0,blank,vend_list[12]; + short amount,index; + struct map_session_data *vsd=map_id2sd(id); + + nullpo_retv(sd); + + blank=pc_inventoryblank(sd); + + if(vsd==NULL) + return; + if(vsd->vender_id==0) + return; + if(vsd->vender_id==sd->bl.id) + return; + for(i=0,w=z=0;8+4*i<len;i++){ + amount=*(short*)(p+4*i); + index=*(short*)(p+2+4*i)-2; +/* + if(amount < 0) return; //add + for(j=0;j<vsd->vend_num;j++) + if(0<vsd->vending[j].amount && amount<=vsd->vending[j].amount && vsd->vending[j].index==index) + break; +*/ +//ADD_start + for(j=0;j < vsd->vend_num;j++) { + if(0 < vsd->vending[j].amount && vsd->vending[j].index==index) { + if(amount > vsd->vending[j].amount || amount <= 0) { + clif_buyvending(sd,index,vsd->vending[j].amount,4); + return; + } + if(amount <= vsd->vending[j].amount) break; + } + } +//ADD_end + if(j==vsd->vend_num) + return; // 売り切れ + vend_list[i]=j; + z+=vsd->vending[j].value*amount; + if(z > sd->status.zeny){ + clif_buyvending(sd,index,amount,1); + return; // zeny不足 + } + w+=itemdb_weight(vsd->status.cart[index].nameid)*amount; + if(w+sd->weight > sd->max_weight){ + clif_buyvending(sd,index,amount,2); + return; // 重量超過 + } + switch(pc_checkadditem(sd,vsd->status.cart[index].nameid,amount)){ + case ADDITEM_EXIST: + break; + case ADDITEM_NEW: + new++; + if(new > blank) + return; // 種類数超過 + break; + case ADDITEM_OVERAMOUNT: + return; // アイテム数超過 + } + } + if(z < 0 || z > MAX_ZENY){ //Zeny Bug Fixed by Darkchild + clif_tradecancelled(sd); + clif_tradecancelled(vsd); + return; + } + pc_payzeny(sd,z); + pc_getzeny(vsd,z); + for(i=0;8+4*i<len;i++){ + amount=*(short*)(p+4*i); + index=*(short*)(p+2+4*i)-2; + if(amount < 0) break; //add + pc_additem(sd,&vsd->status.cart[index],amount); + vsd->vending[vend_list[i]].amount-=amount; + pc_cart_delitem(vsd,index,amount,0); + clif_vendingreport(vsd,index,amount); + } +} + +/*========================================== + * 露店開設 + *------------------------------------------ + */ +void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p) +{ + int i; + + nullpo_retv(sd); + + if(!pc_checkskill(sd,MC_VENDING) || !pc_iscarton(sd)) { // cart skill and cart check [Valaris] + clif_skill_fail(sd,MC_VENDING,0,0); + return; + } + + if(flag){ + for(i=0;85+8*i<len;i++){ + sd->vending[i].index=*(short*)(p+8*i)-2; + sd->vending[i].amount=*(short*)(p+2+8*i); + sd->vending[i].value=*(int*)(p+4+8*i); + if(sd->vending[i].value>battle_config.vending_max_value)sd->vending[i].value=battle_config.vending_max_value; + // カート内のアイテム数と販売するアイテム数に相違があったら中止 + 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; + } + } + sd->vender_id=sd->bl.id; + sd->vend_num=i; + strcpy(sd->message,message); + 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 0000000..41aa731 --- /dev/null +++ b/src/map/vending.h @@ -0,0 +1,12 @@ +// $Id: vending.h,v 1.2 2004/09/25 05:32:19 MouseJstr Exp $ +#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/mra.patch b/src/mra.patch new file mode 100644 index 0000000..b8ae365 --- /dev/null +++ b/src/mra.patch @@ -0,0 +1,69 @@ +diff -u -r athena/src/map/clif.c athenanew/src/map/clif.c +--- athena/src/map/clif.c 2005-04-16 17:07:03.000000000 +0000 ++++ athenanew/src/map/clif.c 2005-05-21 18:25:01.121659080 +0000 +@@ -3208,17 +3208,19 @@ + * アイテム追加成功/失敗 + *------------------------------------------ + */ +-int clif_tradeitemok(struct map_session_data *sd,int index,int fail) ++int clif_tradeitemok(struct map_session_data *sd,int index,int amount,int fail) + { + int fd; + + nullpo_retr(0, sd); + + fd=sd->fd; +- WFIFOW(fd,0)=0xea; ++ WFIFOW(fd,0)=0x1b1; ++ //WFIFOW(fd,0)=0xea; + WFIFOW(fd,2)=index; +- WFIFOB(fd,4)=fail; +- WFIFOSET(fd,packet_len_table[0xea]); ++ WFIFOW(fd,4)=amount; ++ WFIFOB(fd,6)=fail; ++ WFIFOSET(fd,packet_len_table[0x1b1]); + + return 0; + } +diff -u -r athena/src/map/clif.h athenanew/src/map/clif.h +--- athena/src/map/clif.h 2005-04-16 17:06:56.000000000 +0000 ++++ athenanew/src/map/clif.h 2005-05-21 18:25:33.040806632 +0000 +@@ -97,7 +97,7 @@ + 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_tradeitemok(struct map_session_data *sd,int index,int amount,int fail); + int clif_tradedeal_lock(struct map_session_data *sd,int fail); + int clif_tradecancelled(struct map_session_data *sd); + int clif_tradecompleted(struct map_session_data *sd,int fail); +diff -u -r athena/src/map/trade.c athenanew/src/map/trade.c +--- athena/src/map/trade.c 2005-04-16 17:08:06.000000000 +0000 ++++ athenanew/src/map/trade.c 2005-05-21 18:26:46.750601040 +0000 +@@ -98,7 +98,7 @@ + if(sd->deal_item_amount[trade_i] == 0){ + trade_weight+=sd->inventory_data[index-2]->weight*amount; + if(target_sd->weight + trade_weight > target_sd->max_weight){ +- clif_tradeitemok(sd,index,1); //fail to add item -- the player was over weighted. ++ clif_tradeitemok(sd,index,0,1); //fail to add item -- the player was over weighted. + amount = 0; // [MouseJstr] + }else{ + for(c=0; c==trade_i-1;c++){ // re-deal exploit protection [Valaris] +@@ -109,7 +109,7 @@ + } + sd->deal_item_index[trade_i] =index; + sd->deal_item_amount[trade_i]+=amount; +- clif_tradeitemok(sd,index,0); //success to add item ++ clif_tradeitemok(sd,index,amount,0); //success to add item + clif_tradeadditem(sd,target_sd,index,amount); + } + break; +@@ -143,7 +143,7 @@ + + if((target_sd = map_id2sd(sd->trade_partner)) != NULL){ + sd->deal_locked=1; +- clif_tradeitemok(sd,0,0); ++ clif_tradeitemok(sd,0,0,0); + clif_tradedeal_lock(sd,0); + clif_tradedeal_lock(target_sd,1); + } diff --git a/src/tmw-server.dev b/src/tmw-server.dev new file mode 100644 index 0000000..8098eb2 --- /dev/null +++ b/src/tmw-server.dev @@ -0,0 +1,169 @@ +[Project]
+FileName=tmw-server.dev
+Name=tmw-server
+UnitCount=12
+Type=0
+Ver=1
+ObjFiles=
+Includes=
+Libs=
+PrivateResource=
+ResourceIncludes=
+MakeIncludes=
+Compiler=
+CppCompiler=
+Linker=
+IsCpp=1
+Icon=
+ExeOutput=
+ObjectOutput=
+OverrideOutput=0
+OverrideOutputName=
+HostApplication=
+Folders=
+CommandLine=
+UseCustomMakefile=0
+CustomMakefile=
+IncludeVersionInfo=0
+SupportXPThemes=0
+CompilerSet=0
+CompilerSettings=
+
+[Unit1]
+FileName=src\char\char.c
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit2]
+FileName=src\char\char.h
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit3]
+FileName=src\char\int_guild.c
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit4]
+FileName=src\char\int_guild.h
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit5]
+FileName=src\char\int_party.c
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit6]
+FileName=src\char\int_party.h
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit7]
+FileName=src\char\int_pet.c
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit8]
+FileName=src\char\int_pet.h
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit9]
+FileName=src\char\int_storage.c
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit10]
+FileName=src\char\int_storage.h
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit11]
+FileName=src\char\inter.c
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[Unit12]
+FileName=src\char\inter.h
+CompileCpp=1
+Folder=tmw-server
+Compile=1
+Link=1
+Priority=1000
+OverrideBuildCmd=0
+BuildCmd=
+
+[VersionInfo]
+Major=0
+Minor=1
+Release=1
+Build=1
+LanguageID=1033
+CharsetID=1252
+CompanyName=
+FileVersion=
+FileDescription=Developed using the Dev-C++ IDE
+InternalName=
+LegalCopyright=
+LegalTrademarks=
+OriginalFilename=
+ProductName=
+ProductVersion=
+AutoIncBuildNr=0
+
diff --git a/src/tmw-server.layout b/src/tmw-server.layout new file mode 100644 index 0000000..6a8b2b4 --- /dev/null +++ b/src/tmw-server.layout @@ -0,0 +1,43 @@ +[Editor_0]
+CursorCol=23
+CursorRow=94
+TopLine=85
+LeftChar=1
+Open=0
+Top=0
+[Editors]
+Focused=-1
+Order=
+[Editor_1]
+Open=0
+Top=0
+[Editor_2]
+Open=0
+Top=0
+[Editor_3]
+Open=0
+Top=0
+[Editor_4]
+Open=0
+Top=0
+[Editor_5]
+Open=0
+Top=0
+[Editor_6]
+Open=0
+Top=0
+[Editor_7]
+Open=0
+Top=0
+[Editor_8]
+Open=0
+Top=0
+[Editor_9]
+Open=0
+Top=0
+[Editor_10]
+Open=0
+Top=0
+[Editor_11]
+Open=0
+Top=0
diff --git a/src/tool/Makefile b/src/tool/Makefile new file mode 100644 index 0000000..8734041 --- /dev/null +++ b/src/tool/Makefile @@ -0,0 +1,6 @@ +all:
+ $(CC) -o adduser adduser.c
+
+clean:
+ rm -f adduser
+ rm -f *.exe
diff --git a/src/tool/adduser.c b/src/tool/adduser.c new file mode 100644 index 0000000..1219540 --- /dev/null +++ b/src/tool/adduser.c @@ -0,0 +1,96 @@ +/* + 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/backup b/src/tool/backup new file mode 100644 index 0000000..2b5a958 --- /dev/null +++ b/src/tool/backup @@ -0,0 +1,100 @@ +#!/usr/bin/perl
+
+##########################################################################
+# Athena用データバックアップツール
+#
+# Athenaの各種データファイル*.txtをバックアップするツール
+#
+#-------------------------------------------------------------------------
+# 設定方法
+# 実行する時のカレントフォルダからのデータへのパス、ファイルのリストを
+# 正しく設定します。バックアップ先のフォルダは自動作成されないので、
+# 自分で作成しておく必要があります。
+# フォルダの最後の「/」は省略できません。
+#
+# フォルダは引数でも指定できます。例>./backup ../save/ ./backup_data/
+# フォルダの最後の「/」は省略できません。
+#
+# 実行するとバックアップ先のフォルダへ、ファイル名に現在の日付と時刻を
+# つけてファイルをコピーします。
+#
+# * toolフォルダ内にbackup_dataフォルダを作成し、
+# athena.shの中に「./tool/backup ./save/ ./tool/backup_data/」
+# という行を追加すると、athenaを起動するたびにバックアップが取れます
+#
+# 復元するときは引数に「-r 日付と時刻」を指定します。
+# またその後ろにフォルダを指定することも出来ます
+# 例1> ./backup -r 200309191607
+# 例2> ./backup -r 200309191607 ../save ./backup_data/
+# この例では2003/09/19の16:07分にバックアップしたデータを復元しています
+#
+# 復元するとき、Athenaディレクトリにあるデータは *.bak に名前を変更して
+# 残しているので、いらない場合は rm *.bak などで消してください。
+#
+##########################################################################
+
+$sdir="../save/"; #バックアップ元(Athenaのディレクトリ/save/)
+$tdir="./backup_data/"; #バックアップ先
+
+@files=( #ファイルのリスト
+ "account","athena","storage","party","guild","castle","pet"
+);
+
+
+#-------------------------------設定ここまで-----------------------------
+
+
+
+
+
+
+
+
+
+
+
+if($ARGV[0]=~/^\-r$/i || $ARGV[0]=~/\-\-(recover|restore)/i){
+ #復元処理
+
+ $file=$ARGV[1];
+ $sdir=$ARGV[2]||$sdir;
+ $tdir=$ARGV[3]||$tdir;
+ &restorecopy($_) foreach @files;
+ exit(0);
+}
+
+#バックアップ処理
+$sdir=$ARGV[0]||$sdir;
+$tdir=$ARGV[1]||$tdir;
+
+unless( -d $tdir ){
+ print "$0: \"$tdir\" : No such directory\n";
+ exit(1);
+}
+
+(undef,$min,$hour,$day,$month,$year)=localtime;
+
+$file=sprintf("%04d%02d%02d%02d%02d",
+ $year+1900, $month+1, $day, $hour, $min );
+
+&backupcopy($_) foreach @files;
+exit(0);
+
+sub backupcopy {
+ my($name)= @_;
+ system("cp $sdir$name.txt $tdir$name$file.txt");
+}
+
+sub restorecopy {
+ my($name)= @_;
+ unless( -f "$sdir$name.txt" ){
+ printf("$0: \"$sdir$name.txt\" not found!\n");
+ return 0;
+ }
+ unless( -f "$tdir$name$file.txt" ){
+ printf("$0: \"$tdir$name$file.txt\" not found!\n");
+ return 0;
+ }
+ rename "$sdir$name.txt","$sdir$name.bak";
+ system("cp $tdir$name$file.txt $sdir$name.txt");
+}
diff --git a/src/tool/cgi/addaccount.cgi b/src/tool/cgi/addaccount.cgi new file mode 100644 index 0000000..7d1788c --- /dev/null +++ b/src/tool/cgi/addaccount.cgi @@ -0,0 +1,204 @@ +#!/usr/bin/perl + +#========================================================================= +# addaccount.cgi ver.1.00 +# ladminをラップした、アカウントを作成するCGI。 +# ladmin ver.1.04での動作を確認。 +# +# ** 設定方法 ** +# +# - 下の$ladmin変数にladminへのパスを設定すること。 +# - UNIX系OSで使用する場合はladminと共に改行コードを変換すること、また +# ファイル先頭行をperlの正しいパスにすること。例> $ which perl +# - サーバープログラムやブラウザによっては $cgiuri にこのファイルへの +# 完全なURIをセットしなければならない場合もある。 +# - perlにパスが通っていない場合は $perl をperlへの正しいパスにすること。 +# - 他は普通のCGIと同じです。(実行権やcgi-binフォルダなど) +# +# ** その他 ** +# addaccount.cgi をブラウザで開くとサンプルHTML(そのまま使えます)が +# 開きます。また、このcgiはブラウザから送られるAccept-Languageが +# jaで始まっていればメッセージの一部を日本語に変換します。 +# (IEならインターネットオプションの言語設定で一番上に日本語を置く) +# それ以外の場合は英語のまま出力します。 +#------------------------------------------------------------------------- + +my($ladmin) = "../ladmin"; # ladminのパス(おそらく変更が必要) + +my($cgiuri) = "./addaccount.cgi"; # このファイルのURI +my($perl) = "perl"; # perlのコマンド名 + + + +#--------------------------- 設定ここまで -------------------------------- + + + + + + +use strict; +use CGI; + +my($cgi)= new CGI; +my(%langconv)=( + 'Athena login-server administration tool.*' => '', + 'logged on.*' => '', +); + +# ----- 日本語環境なら変換テーブルをセット ----- +if($ENV{'HTTP_ACCEPT_LANGUAGE'}=~/^ja/){ + my(%tmp)=( + 'Account \[(.+)\] is successfully created.*' + => 'アカウント "$1" を作成しました.', + 'Account \[(.+)\] creation failed\. same account exists.*' + => 'アカウント "$1" は既に存在します.', + 'Illeagal charactor found in UserID.*' + => 'IDの中に不正な文字があります.', + 'Illeagal charactor found in Password.*' + => 'Passwordの中に不正な文字があります.', + 'input UserID 4-24 bytes.' + => 'IDは半角4〜24文字で入力してください.', + 'input Password 4-24 bytes.' + => 'Passwordは半角4〜24文字で入力してください.', + 'Illeagal gender.*' + => '性別がおかしいです.', + 'Cant connect to login server.*' + => 'ログインサーバーに接続できません.', + 'login error.*' + => 'ログインサーバーへの管理者権限ログインに失敗しました', + "Can't execute ladmin.*" + => 'ladminの実行に失敗しました', + 'UserID "(.+)" is already used.*' + => 'ID "$1" は既に使用されています.', + 'You can use UserID \"(.+)\".*' + => 'ID "$1" は使用可能です.', + + 'account making' =>'アカウント作成', + '\>UserID' =>'>ID', + '\>Password' =>'>パスワード', + '\>Gender' =>'>性別', + '\>Male' =>'>男性', + '\>Female' =>'>女性', + '\"Make Account\"' =>'"アカウント作成"', + '\"Check UserID\"' =>'"IDのチェック"', + ); + map { $langconv{$_}=$tmp{$_}; } keys (%tmp); +} + +# ----- 追加 ----- +if( $cgi->param("addaccount") ){ + my($userid)= $cgi->param("userid"); + my($passwd)= $cgi->param("passwd"); + my($gender)= lc(substr($cgi->param("gender"),0,1)); + if(length($userid)<4 || length($userid)>24){ + HttpError("input UserID 4-24 bytes."); + } + if(length($passwd)<4 || length($passwd)>24){ + HttpError("input Password 4-24 bytes."); + } + if($userid=~/[^0-9A-Za-z\@\_\-\']/){ + HttpError("Illeagal charactor found in UserID."); + } + if($passwd=~/[\x00-\x1f\x80-\xff\']/){ + HttpError("Illeagal charactor found in Password."); + } + if($gender!~/[mf]/){ + HttpError("Gender error."); + } + open PIPE,"$perl $ladmin --add $userid $gender $passwd |" + or HttpError("Can't execute ladmin."); + my(@msg)=<PIPE>; + close PIPE; + HttpMsg(@msg); +} +# ----- 存在チェック ----- +elsif( $cgi->param("check") ){ + my($userid)= $cgi->param("userid"); + if(length($userid)<4 || length($userid)>24){ + HttpError("input UserID 4-24 bytes."); + } + if($userid=~/[^0-9A-Za-z\@\_\-\']/){ + HttpError("Illeagal charactor found in UserID."); + } + open PIPE,"$perl $ladmin --search --regex \\b$userid\\b |" + or HttpError("Can't execute ladmin."); + my(@msg)=<PIPE>; + close PIPE; + if(scalar(@msg)==6 && (split /[\s\0]+/,substr($msg[4],11,24))[0] eq $userid){ + HttpMsg("NG : UserID \"$userid\" is already used."); + }elsif(scalar(@msg)==5){ + HttpMsg("OK : You can use UserID \"$userid\""); + } + HttpError("ladmin error ?\n---output---\n",@msg); +} + +# ----- フォーム ----- +else{ + print LangConv( <<"EOM" ); +Content-type: text/html\n +<html> + <head> + <title>Athena account making cgi</title> + </head> + <body> + <h1>Athena account making cgi</h1> + <form action="$cgiuri" method="post"> + <table border=2> + <tr> + <th>UserID</th> + <td><input name="userid" size=24 maxlength=24></td> + </tr> + <tr> + <th>Password</th> + <td><input name="passwd" size=24 maxlength=24 type="password"></td> + </tr> + <tr> + <th>Gender</th> + <td> + <input type="radio" name="gender" value="male">Male + <input type="radio" name="gender" value="female">Female + </td> + </tr> + <tr> + <td colspan=2> + <input type="submit" name="addaccount" value="Make Account"> + <input type="submit" name="check" value="Check UserID"> + </td> + </tr> + </table> + </form> + </body> +</html> +EOM + exit; +} + +sub LangConv { + my(@lst)= @_; + my($a,$b,@out)=(); + foreach $a(@lst){ + foreach $b(keys %langconv){ + $a=~s/$b/$langconv{$b}/g; + my($rep1)=$1; + $a=~s/\$1/$rep1/g; + } + push @out,$a; + } + return @out; +} + +sub HttpMsg { + my($msg)=join("", LangConv(@_)); + $msg=~s/\n/<br>\n/g; + print LangConv("Content-type: text/html\n\n"),$msg; + exit; +} + +sub HttpError { + my($msg)=join("", LangConv(@_)); + $msg=~s/\n/<br>\n/g; + print LangConv("Content-type: text/html\n\n"),$msg; + exit; +} + diff --git a/src/tool/checkversion b/src/tool/checkversion new file mode 100644 index 0000000..1351652 --- /dev/null +++ b/src/tool/checkversion @@ -0,0 +1,85 @@ +#!/usr/bin/perl -w
+
+##########################################################################
+# INFORMATION TOOL ABOUT THE SERVERS VERSION OF ATHENA
+#
+# By connection on a server, this software display the version of the
+# designed server.
+#-------------------------------------------------------------------------
+# Usages:
+# ./checkversion IP:port
+# ./checkversion IP port
+# perl checkversion IP:port
+# perl checkversion IP port
+#
+# note: default port: 6900
+#
+# When successfull, the software return the value 0.
+#
+##########################################################################
+
+#------------------------- start of configuration ------------------------
+
+$connecttimeout = 10; # Connection Timeout (in seconds)
+
+#-------------------------- End of configuration -------------------------
+
+use IO::Socket;
+
+unless($ARGV[0]) {
+ print "USAGE: $0 server_ip:port\n";
+ exit(1);
+}
+
+$server = $ARGV[0];
+$port = $ARGV[1];
+$port = $1 if ($server =~ s/:(\d+)//);
+$port ||= 6900;
+
+# Connection to the server
+my($so,$er) = ();
+eval{
+ $so = IO::Socket::INET->new(
+ PeerAddr=> $server,
+ PeerPort=> $port,
+ Proto => "tcp",
+ Timeout => $connecttimeout) or $er = 1;
+};
+
+if($er || $@) {
+ print "Can't not connect to server [$server:$port] !\n";
+ exit(2);
+}
+
+# Request for the server version
+print $so pack("v",30000); # 0x7530
+$so->flush();
+
+# Receiving of the answer of the server
+if (read($so,$buf,10) < 10) {
+ print "Invalid answer. It isn't an athena server or it is a too old version.\n";
+ exit(5);
+}
+
+# Sending end of connection to the server
+print $so pack("v",30002); # 0x7532
+$so->flush();
+
+# Analyse of the answer
+my($ret,$maver,$miver,$rev,$dev,$mod,$type,$mdver) = unpack("v c6 v",$buf);
+
+if ($ret != 30001) { # 0x7531
+ print "Invalid answer. It isn't an athena server or it is a too old version.\n";
+ exit(6);
+}
+
+my(@stype) = ();
+foreach $i(0..3) {
+ push @stype,(("login","char","inter","map")[$i]) if( $type & (1<<$i) );
+}
+print " ".join("/",@stype)." server [$server:$port].\n";
+printf " Athena version %s-%d.%d", ("stable","dev")[$dev], $maver,$miver;
+printf " revision %d",$rev if $rev;
+printf "%s%d\n",("","-mod")[$mod],$mdver;
+
+exit(0);
diff --git a/src/tool/convert.c b/src/tool/convert.c new file mode 100644 index 0000000..16631c9 --- /dev/null +++ b/src/tool/convert.c @@ -0,0 +1,296 @@ +#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; // 新規データ + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%[^,],%d,%d%n",p->memo_point[i].map,&tmp_int[0],&tmp_int[1],&len); + if(set!=3) + return 0; + p->memo_point[i].x=tmp_int[0]; + p->memo_point[i].y=tmp_int[1]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0],&tmp_int[1],&tmp_int[2],&tmp_int[3], + &tmp_int[4],&tmp_int[5],&tmp_int[6], + &tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10],&len); + if(set!=11) + return 0; + p->inventory[i].id=tmp_int[0]; + p->inventory[i].nameid=tmp_int[1]; + p->inventory[i].amount=tmp_int[2]; + p->inventory[i].equip=tmp_int[3]; + p->inventory[i].identify=tmp_int[4]; + p->inventory[i].refine=tmp_int[5]; + p->inventory[i].attribute=tmp_int[6]; + p->inventory[i].card[0]=tmp_int[7]; + p->inventory[i].card[1]=tmp_int[8]; + p->inventory[i].card[2]=tmp_int[9]; + p->inventory[i].card[3]=tmp_int[10]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0],&tmp_int[1],&tmp_int[2],&tmp_int[3], + &tmp_int[4],&tmp_int[5],&tmp_int[6], + &tmp_int[7],&tmp_int[8],&tmp_int[9],&tmp_int[10],&len); + if(set!=11) + return 0; + p->cart[i].id=tmp_int[0]; + p->cart[i].nameid=tmp_int[1]; + p->cart[i].amount=tmp_int[2]; + p->cart[i].equip=tmp_int[3]; + p->cart[i].identify=tmp_int[4]; + p->cart[i].refine=tmp_int[5]; + p->cart[i].attribute=tmp_int[6]; + p->cart[i].card[0]=tmp_int[7]; + p->cart[i].card[1]=tmp_int[8]; + p->cart[i].card[2]=tmp_int[9]; + p->cart[i].card[3]=tmp_int[10]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + set=sscanf(str+next,"%d,%d%n", + &tmp_int[0],&tmp_int[1],&len); + if(set!=2) + return 0; + p->skill[tmp_int[0]].id=tmp_int[0]; + p->skill[tmp_int[0]].lv=tmp_int[1]; + next+=len; + if(str[next]==' ') + next++; + } + next++; + for(i=0;str[next] && str[next]!='\t' && str[next]!='\n' && str[next]!='\r';i++){ //global_reg実装以前のathena.txt互換のため一応'\n'チェック + set=sscanf(str+next,"%[^,],%d%n", + p->global_reg[i].str,&p->global_reg[i].value,&len); + if(set!=2) + return 0; + next+=len; + if(str[next]==' ') + next++; + } + p->global_reg_num=i; + return 1; +} + +int mmo_char_convert(char *fname1,char *fname2) +{ + char line[65536]; + int ret; + struct mmo_charstatus char_dat; + FILE *ifp,*ofp; + + ifp=fopen(fname1,"r"); + ofp=fopen(fname2,"w"); + if(ifp==NULL) { + printf("file not found %s\n",fname1); + return 0; + } + if(ofp==NULL) { + printf("file open error %s\n",fname2); + return 0; + } + while(fgets(line,65535,ifp)){ + memset(&char_dat,0,sizeof(struct mmo_charstatus)); + ret=mmo_char_fromstr(line,&char_dat); + if(ret){ + mmo_char_tostr(line,&char_dat); + fprintf(ofp,"%s" 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/tool/getlogincount b/src/tool/getlogincount new file mode 100644 index 0000000..6a20992 --- /dev/null +++ b/src/tool/getlogincount @@ -0,0 +1,122 @@ +#!/usr/bin/perl -w
+
+##########################################################################
+# INFORMATION TOOL ABOUT THE # OF ONLINE PLAYERS ON ATHENA SERVERS
+#
+# By connection on the athena login-server, this software displays the
+# number of online players.
+#
+#-------------------------------------------------------------------------
+# Software usage:
+# Configure the IP, the port and a valid account of the server.
+# After, use at your choice:
+# ./getlogincount - display the number of online players on all servers.
+# ./getlogincount --premier or
+# ./getlogincount --first -- display the number of online players of the
+# first server in the received list.
+# ./getlogincount [servername] -- display the number of online players
+# of the specified server.
+#
+# If successfull, the software return the value 0.
+#
+##########################################################################
+
+#------------------------------ CONFIGURATION ----------------------------
+
+$loginserverip = "127.0.0.1"; # IP of the login-server
+$loginserverport = 6900; # port of the login-server
+$loginaccount = "s1"; # a valid account name
+$loginpasswd = "p1"; # the password of the valid account name
+
+$connecttimeout = 10; # Connection timeout (in seconds)
+
+#-------------------------------------------------------------------------
+
+use IO::Socket;
+
+my($sname) = $ARGV[0];
+if (!defined($sname)) {
+ $sname = "";
+}
+
+# Connection to the login-server
+my($so,$er) = ();
+eval{
+ $so = IO::Socket::INET->new(
+ PeerAddr=> $loginserverip,
+ PeerPort=> $loginserverport,
+ Proto => "tcp",
+ Timeout => $connecttimeout) or $er=1;
+};
+if($er || $@){
+ print "Can't not connect to the login-server [${loginserverip}:$loginserverport] !\n";
+ exit(2);
+}
+
+# Request to connect on login-server
+print $so pack("v V a24 a24 C",0x0064,9,$loginaccount,$loginpasswd,3);
+$so->flush();
+
+# Fail to connect
+if(unpack("v", &soread(\$so,2)) != 0x0069) {
+ print "Login error.\n";
+ exit(3);
+}
+
+# Get length of the received packet
+my($plen) = unpack("v",&soread(\$so,2))-4;
+
+# Suppress information of the account (we need only information about the servers)
+&soread(\$so,43);
+$plen -= 43;
+
+# Check about the number of online servers
+if ($plen < 32) {
+ printf "No server is connected to login-server.\n";
+ exit(1);
+}
+
+# Read information of the servers
+my(@slist) = ();
+for(;$plen > 0;$plen -= 32) {
+ my($name,$count) = unpack("x6 a20 V",&soread(\$so,32));
+ $name = substr($name,0,index($name,"\0"));
+ push @slist, [ $name, $count ];
+}
+
+# Display the result
+if($sname eq "--first" || $sname eq "--premier") { # If we ask only for the first server
+ printf "%-20s : %5d\n",$slist[0][0],$slist[0][1];
+} elsif ($sname eq "") { # If we ask for all servers
+ foreach $i(@slist) {
+ printf "%-20s : %5d\n",$i->[0],$i->[1];
+ }
+} else { # If we ask for a specified server (by its name)
+ my($flag) = 1;
+ foreach $i(@slist) {
+ if($i->[0] eq $sname) {
+ printf "%-20s : %5d\n",$i->[0],$i->[1];
+ $flag = 0;
+ }
+ }
+ if($flag) { # If the server doesn't exist
+ printf "The server [$sname] doesn't exist.\n";
+ exit(1);
+ }
+}
+
+# End of the software
+$so->shutdown(2);
+$so->close();
+exit(0);
+
+# Sub-function: get data from the socket
+sub soread {
+ my($so,$len) = @_;
+ my($sobuf);
+ if(read($$so,$sobuf,$len) < $len) {
+ print "Socket read error.\n";
+ exit(5);
+ }
+ return $sobuf;
+};
diff --git a/src/tool/ladmin b/src/tool/ladmin new file mode 100644 index 0000000..e3319d5 --- /dev/null +++ b/src/tool/ladmin @@ -0,0 +1,3793 @@ +#!/usr/bin/perl
+use POSIX;
+##########################################################################
+# EAthena login-server remote administration tool
+# New ladamin by [Yor]
+##########################################################################
+#-------------------------------INSTRUCTIONS------------------------------
+# Set the 4 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)
+#-------------------------------------------------------------------------
+my($loginserverip) = "127.0.0.1"; # IP of login-server
+my($loginserverport) = 6900; # Port of login-server
+my($loginserveradminpassword) = "admin"; # Administration password
+my($connecttimeout) = 10; # Timeout of connection (in seconds)
+my($passenc) = 2; # Encoding type of the password
+my($defaultlanguage) = "E"; # Default language (F: Fran軋is/E: English)
+ # (if it's not 'F', default is English)
+
+#-------------------------------------------------------------------------
+# 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.
+# Same command of banset, except that 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: 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.
+# Same command of 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: 23:59:59
+# timeset <account_name> 0
+# Gives an unlimited validity limit (0 = unlimited).
+#
+# unban/unbanish <account name>
+# Unban an account.
+# Same command of banset 0.
+#
+# unblock <account name>
+# Set state 0 (Account ok) to an account.
+# Same command of state <account_name> 0.
+#
+# version
+# Display the version of the login-server.
+#
+# who <account name>
+# Displays complete information of an account.
+#
+#-------------------------------------------------------------------------
+# Possibilities to execute ladmin in command line by usage of the software with a parameter:
+# ./ladmin --mode param1 ...
+#
+# --makesymlink -- Create the symbolic links for a use in shell
+# --add <account_name> <sex> <password> -- Create an account with the default email (or -a)
+# --ban yyyy/mm/dd hh:mm:ss <account_name> -- Change the final date of a banishment of an account (or -b)
+# --banadd <account_name> <modifier> -- Add or substract time from the final date of a banishment of an account (or - ba)
+# --banset <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the final date of a banishment of an account (or -bs)
+# --banset <account_name> 0 -- Unbanish an account (or -bs)
+# --block <account_name> -- Set state 5 to an account (or -bl)
+# --check <account_name> <password> -- Check the validity of a password for an account (or -check)
+# --create <account_name> <sex> <email> <password> -- Create an account with email (or -c)
+# --del <account_name> -- Remove an account (or -d)
+# --email <account_name> <email> -- Modify an email of an account (or -e)
+# --getcount -- Give the number of players online on all char-servers (or -g)
+# --gm <account_name> <GM_level> -- Change the GM level of an account (or -gm)
+# --id <account_name> -- Give the id of an account (or -i)
+# --info <account_id> -- Display complete information of an account (or -info)
+# --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 (-lang).
+# --list [First_id [Last_id]] -- Display a list of accounts (or -l)
+# --listBan [start_id [end_id]] -- Display a list of accounts with state or banished (or -lBan)
+# --listGM [First_id [Last_id]] -- Display a list of GM accounts (or -lGM)
+# --listOK [start_id [end_id]] -- Display a list of accounts without state and not banished (or -lOK)
+# --memo <account_name> <memo> -- Modify the memo of an account (or -e)
+# --name <account_id> -- Give the name of an account (or -n)
+# --passwd <account_name> <new_password> -- Change the password of an account (or -p)
+# --reloadGM -- Reload GM configuration file (or -r)
+# --search <expression> -- Seek accounts (or -s)
+# --search -e/-r/--expr/--regex <expression> -- Seek accounts by REGEX (or -s)
+# --sex <account_name> <sex> -- Change the sex of an account (or -sex)
+# --state <account_name> <new_state> <error_message_#7> -- Change the state of an account (or -t)
+# --timeadd <account_name> <modifier> -- Add or substract time from the validity limit of an account (or - ta)
+# --timeset <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the validify limit of an account (or -ts)
+# --timeset <account_name> 0 -- Give a unlimited validity limit (or -ts)
+# --unban/unbanish <account_name> -- Unban an account (or -uba)
+# --unblock <account_name> -- Set state 0 to an account (or -ubl)
+# --version -- Display the version of the login-server (or -v)
+# --who <account_name> -- Display complete information of an account (or -w)
+#
+# <example> ./ladmin --addaccount testname Male testpass
+#
+#-------------------------------------------------------------------------
+# Possibilities to execute ladmin with symbolic links in Shell
+# To create the symbolic links, execute ladmin with the '-- makesymlink' option.
+#
+# addaccount <account_name> <sex> <password> -- Create an account with the default email
+# banaccount yyyy/mm/dd hh:mm:ss <account_name> -- Change the final date of a banishment of an account
+# banaddaccount <account_name> <modifier> -- Add or substract time from the final date of a banishment of an account
+# bansetaccount <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the final date of a banishment of an account
+# bansetaccount <account_name> 0 -- Unbanish an account
+# blockaccount <account_name> -- Set state 5 (blocked by the GM Team) to an account
+# checkaccount <account_name> <password> -- Check the validity of a password for an account
+# createaccount <account_name> <sex> <email> <password> -- Create an account with email
+# delaccount <account_name> -- Remove an account
+# emailaccount <account_name> <email> -- Modify an email of an account
+# getcount -- Give the number of players online on all char-servers
+# gmaccount <account_name> <GM_level> -- Change the GM level of an account
+# idaccount <account_name> -- Give the id of an account
+# infoaccount <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).
+# ladminlanguage <language> -- Change the language of displaying.
+# listaccount [First_id [Last_id]] -- Display a list of accounts
+# listBanaccount [start_id [end_id]] -- Display a list of accounts with state or banished
+# listGMaccount [First_id [Last_id]] -- Display a list of GM accounts
+# listOKaccount [start_id [end_id]] -- Display a list of accounts without state and not banished
+# loginserverversion -- Display the version of the login-server
+# memoaccount <account_name> <memo> -- Modify the memo of an account
+# nameaccount <account_id> -- Give the name of an account
+# passwdaccount <account_name> <new_password> -- Change the password of an account
+# reloadGM -- Reload GM configuration file
+# searchaccount <expression> -- Seek accounts
+# searchaccount -e/-r/--expr/--regex <expression> -- Seek accounts by REGEX
+# sexaccount <account_name> <sex> -- Change the sex of an account (or -sex)
+# stateaccount <account_name> <new_state> <error_message_#7> -- Change the state of an account
+# timeaddaccount <account_name> <modifier> -- Add or substract time from the validity limit of an account
+# timesetaccount <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the validify limit of an account
+# timesetaccount <account_name> 0 -- Give a unlimited validity limit
+# unbanaccount <account_name> -- Unban an account
+# unblockaccount <account_name> -- Set state 0 (Account ok) to an account
+# whoaccount <account_name> -- Display complete information of an account
+# <exemple> ./addaccount testname Male testpass
+#
+#-------------------------------------------------------------------------
+# About the encoding:
+#
+# The Digest::MD5 module is necessary to use the encrypted password system.
+# When the software cannot found the Digest::MD5 module,
+# encoding is automatically disabled ($passenc=0), which allows
+# to use this program in any cases.
+#
+#-------------------------------------------------------------------------
+# How to use ladmin with UNIX:
+#
+# You excecute ladmin as a standard command.
+# <Example of preparation to have an access to ladmin>
+# $ mv ladmin ladmin_org
+# $ nkf -eLu ladmin_org > ladmin
+# $ chmod 700 ladmin
+# <Example to start directly ladmin>
+# $ perl ladmin
+#
+##########################################################################
+
+
+use strict;
+use IO::Socket;
+use Term::ReadLine;
+eval { use POSIX qw(:termios_h); };
+eval { use Digest::MD5 qw(md5); } if $passenc;
+$passenc = 0 if($@);
+
+my($ver) = "1.00";
+
+# Start of termios
+my($termios, $orgterml, $termlecho, $termlnoecho) = ();
+eval{
+ $termios = POSIX::Termios->new();
+ $termios->getattr(fileno(STDIN));
+ $orgterml = $termios->getlflag();
+ $termlecho = ECHO | ECHOK | ICANON;
+ $termlnoecho = $orgterml & ~$termlecho;
+};
+
+# Modification of termios for the displaying of passwords (no displays for pressed keys)
+sub cbreak() {
+ if ($termios) {
+ $termios->setlflag($termlnoecho);
+ $termios->setcc(VTIME, 1);
+ $termios->setattr(fileno(STDIN), TCSANOW);
+ }
+}
+# Modification of termios to return at the normal displaying (after input of the passwords)
+sub cooked() {
+ if ($termios) {
+ $termios->setlflag($orgterml);
+ $termios->setcc(VTIME,0);
+ $termios->setattr(fileno(STDIN),TCSANOW);
+ }
+}
+END{ cooked() }
+
+if ($defaultlanguage eq "F") {
+ print "Outil d'administration distance de eAthena V.$ver\n";
+} else {
+ print "EAthena login-server administration tool V.$ver\n";
+}
+
+# Creation of the symbolic links for call of the program in line command of the shell
+if ($ARGV[0] eq "--makesymlink") {
+ symlink $0, "loginserverversion";
+ symlink $0, "addaccount";
+ symlink $0, "banaccount";
+ symlink $0, "banaddaccount";
+ symlink $0, "bansetaccount";
+ symlink $0, "blockaccount";
+ symlink $0, "checkaccount";
+ symlink $0, "createaccount";
+ symlink $0, "delaccount";
+ symlink $0, "emailaccount";
+ symlink $0, "getcount";
+ symlink $0, "gmaccount";
+ symlink $0, "idaccount";
+ symlink $0, "infoaccount";
+ symlink $0, "kami";
+ symlink $0, "kamib";
+ symlink $0, "ladminlanguage";
+ symlink $0, "listaccount";
+ symlink $0, "listBanaccount";
+ symlink $0, "listGMaccount";
+ symlink $0, "listOKaccount";
+ symlink $0, "memoaccount";
+ symlink $0, "nameaccount";
+ symlink $0, "passwdaccount";
+ symlink $0, "reloadGM";
+ symlink $0, "searchaccount";
+ symlink $0, "sexaccount";
+ symlink $0, "stateaccount";
+ symlink $0, "timeaddaccount";
+ symlink $0, "timesetaccount";
+ symlink $0, "unbanaccount";
+ symlink $0, "unblockaccount";
+ symlink $0, "whoaccount";
+ if ($defaultlanguage eq "F") {
+ print "Liens symbliques cr鳬s.\n";
+ } else {
+ print "Symbolic links created.\n";
+ }
+ exit(0);
+}
+
+# Connection to the login-server
+my($so,$er) = ();
+eval{
+ $so = IO::Socket::INET->new(
+ PeerAddr=> $loginserverip,
+ PeerPort=> $loginserverport,
+# Proto => "tcp",
+ Timeout => $connecttimeout) or $er = 1;
+};
+if ($er || $@) {
+ if ($defaultlanguage eq "F") {
+ print "\nImpossible de se connecter au serveur de login [${loginserverip}:$loginserverport] !\n";
+ } else {
+ print "\nImpossible to have a connection with the login-server [${loginserverip}:$loginserverport] !\n";
+ }
+ print "$!\n"; # Displaying of the error
+ exit(2);
+}
+
+# Sending the administration password
+if ($passenc == 0) {
+ print $so pack("v2a24",0x7918,0,$loginserveradminpassword);
+ $so->flush();
+} else {
+ print $so pack("v",0x791a);
+ $so->flush();
+ my($buf) = readso(4);
+ if (unpack("v",$buf) != 0x01dc) {
+ if ($defaultlanguage eq "F") {
+ print "Erreur au login (馗hec de la cr饌tion de la clef md5).\n";
+ } else {
+ print "Error at login (failure of the md5 key creation).\n";
+ }
+ }
+ $buf = readso(unpack("x2v",$buf)-4);
+ my($md5bin) = md5(($passenc == 1) ? $buf.$loginserveradminpassword : $loginserveradminpassword.$buf);
+ print $so pack("v2a16",0x7918,$passenc,$md5bin);
+ $so->flush();
+}
+
+# Waiting of the server reply
+my($buf) = readso(3);
+
+if (unpack("v",$buf) != 0x7919 || unpack("x2c",$buf) != 0) {
+ if ($defaultlanguage eq "F") {
+ print "Erreur de login:\n";
+ print " - mot de passe incorrect,\n";
+ print " - syst鑪e d'administration non activ, ou\n";
+ print " - IP non autoris馥.\n";
+ } else {
+ print "Error at login:\n";
+ print " - incorrect password,\n";
+ print " - administration system not activated, or\n";
+ print " - unauthorised IP.\n";
+ }
+ quit();
+ exit(4);
+}
+
+if ($defaultlanguage eq "F") {
+ print "Connexion 騁ablie.\n";
+} else {
+ print "Established connection.\n";
+}
+
+#-------------------------------------------------------------------------
+# Here are checked the command lines with arguments and symbolic links (no prompt)
+
+if ($0 =~ /addaccount$/ ||
+ (($ARGV[0] eq "-a" || $ARGV[0] eq "--add") && ((shift @ARGV), 1))) {
+ my($r) = addaccount($ARGV[0], $ARGV[1], $ARGV[2]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /banaccount$/ || $0 =~ /banishaccount$/ ||
+ (($ARGV[0] eq "-b" || $ARGV[0] eq "--ban" || $ARGV[0] eq "--banish") && ((shift @ARGV), 1))) {
+ my($r) = bansetaccount($ARGV[1], $ARGV[2], $ARGV[0]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /banaddaccount$/ ||
+ (($ARGV[0] eq "-ba" || $ARGV[0] eq "--banadd") && ((shift @ARGV), 1))) {
+ my($r) = banaddaccount($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /bansetaccount$/ ||
+ (($ARGV[0] eq "-bs" || $ARGV[0] eq "--banset") && ((shift @ARGV), 1))) {
+ my($r) = bansetaccount($ARGV[0], $ARGV[1], $ARGV[2]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /blockaccount$/ ||
+ (($ARGV[0] eq "-bl" || $ARGV[0] eq "--block") && ((shift @ARGV), 1))) {
+ my($r) = changestate($ARGV[0], 5, "");
+ quit();
+ exit($r);
+} elsif ($0 =~ /checkaccount$/ ||
+ (($ARGV[0] eq "-check" || $ARGV[0] eq "--check") && ((shift @ARGV), 1))) {
+ my($r) = checkaccount($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /createaccount$/ ||
+ (($ARGV[0] eq "-c" || $ARGV[0] eq "--create") && ((shift @ARGV), 1))) {
+ my($r) = createaccount($ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /delaccount$/ ||
+ (($ARGV[0] eq "-d" || $ARGV[0] eq "--del") && ((shift @ARGV), 1))) {
+ my($r) = delaccount($ARGV[0]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /emailaccount$/ ||
+ (($ARGV[0] eq "-e" || $ARGV[0] eq "--email") && ((shift @ARGV), 1))) {
+ my($r) = changeemail($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /getcount$/ ||
+ (($ARGV[0] eq "-g" || $ARGV[0] eq "--getcount") && ((shift @ARGV), 1))) {
+ my($r) = getlogincount();
+ quit();
+ exit($r);
+} elsif ($0 =~ /gmaccount$/ ||
+ (($ARGV[0] eq "-gm" || $ARGV[0] eq "--gm") && ((shift @ARGV), 1))) {
+ my($r) = changegmlevel($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /id$/ ||
+ (($ARGV[0] eq "-i" || $ARGV[0] eq "--id") && ((shift @ARGV), 1))) {
+ my($r) = idaccount($ARGV[0]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /infoaccount$/ ||
+ (($ARGV[0] eq "-info" || $ARGV[0] eq "--info") && ((shift @ARGV), 1))) {
+ my($r) = infoaccount($ARGV[0]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /kami$/ ||
+ (($ARGV[0] eq "-kami" || $ARGV[0] eq "--kami") && ((shift @ARGV), 1))) {
+ my($r) = sendbroadcast(0, $ARGV[0]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /kamib$/ ||
+ (($ARGV[0] eq "-kamib" || $ARGV[0] eq "--kamib") && ((shift @ARGV), 1))) {
+ my($r) = sendbroadcast(0x10, $ARGV[0]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /ladminlanguage$/ ||
+ (($ARGV[0] eq "-lang" || $ARGV[0] eq "--language") && ((shift @ARGV), 1))) {
+ my($r) = changelanguage($ARGV[0]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /listaccount$/ ||
+ (($ARGV[0] eq "-l" || $ARGV[0] eq "--list") && ((shift @ARGV), 1))) {
+ my($r) = listaccount(int($ARGV[0]), int($ARGV[1]), 0); # 0: to list all
+ quit();
+ exit($r);
+} elsif ($0 =~ /listBanaccount$/ ||
+ (($ARGV[0] eq "-lBan" || $ARGV[0] eq "--listBan") && ((shift @ARGV), 1))) {
+ my($r) = listaccount(int($ARGV[0]), int($ARGV[1]), 3); # 3: to list only accounts with state or banished
+ quit();
+ exit($r);
+} elsif ($0 =~ /listGMaccount$/ ||
+ (($ARGV[0] eq "-lGM" || $ARGV[0] eq "--listGM") && ((shift @ARGV), 1))) {
+ my($r) = listaccount(int($ARGV[0]), int($ARGV[1]), 1); # 1: to list only GM
+ quit();
+ exit($r);
+} elsif ($0 =~ /listOKaccount$/ ||
+ (($ARGV[0] eq "-lOK" || $ARGV[0] eq "--listOK") && ((shift @ARGV), 1))) {
+ my($r) = listaccount(int($ARGV[0]), int($ARGV[1]), 4); # 4: to list only accounts without state and not banished
+ quit();
+ exit($r);
+} elsif ($0 =~ /loginserverversion$/ ||
+ (($ARGV[0] eq "-v" || $ARGV[0] eq "--version") && ((shift @ARGV), 1))) {
+ my($r) = checkloginversion();
+ quit();
+ exit($r);
+} elsif ($0 =~ /memoaccount$/ ||
+ (($ARGV[0] eq "-m" || $ARGV[0] eq "--memo") && ((shift @ARGV), 1))) {
+ my($r) = changememo($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /nameaccount$/ ||
+ (($ARGV[0] eq "-n" || $ARGV[0] eq "--name") && ((shift @ARGV), 1))) {
+ my($r) = nameaccount(int($ARGV[0]));
+ quit();
+ exit($r);
+} elsif ($0 =~ /passwdaccount$/ ||
+ (($ARGV[0] eq "-p" || $ARGV[0] eq "--passwd") && ((shift @ARGV), 1))) {
+ my($r) = changepasswd($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /reloadGM$/ ||
+ (($ARGV[0] eq "-r" || $ARGV[0] eq "--reloadGM") && ((shift @ARGV), 1))) {
+ my($r) = reloadGM();
+ quit();
+ exit($r);
+} elsif ($0 =~ /searchaccount$/ ||
+ (($ARGV[0] eq "-s" || $ARGV[0] eq "--search") && ((shift @ARGV), 1))) {
+ my($r) = searchaccount($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /sexaccount$/ ||
+ (($ARGV[0] eq "-sex" || $ARGV[0] eq "--sex") && ((shift @ARGV), 1))) {
+ my($r) = changesex($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /stateaccount$/ ||
+ (($ARGV[0] eq "-t" || $ARGV[0] eq "--state") && ((shift @ARGV), 1))) {
+ my($r) = changestate($ARGV[0], $ARGV[1], $ARGV[2]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /timeaddaccount$/ ||
+ (($ARGV[0] eq "-ta" || $ARGV[0] eq "--timeadd") && ((shift @ARGV), 1))) {
+ my($r) = timeaddaccount($ARGV[0], $ARGV[1]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /timesetaccount$/ ||
+ (($ARGV[0] eq "-ts" || $ARGV[0] eq "--timeset") && ((shift @ARGV), 1))) {
+ my($r) = timesetaccount($ARGV[0], $ARGV[1], $ARGV[2]);
+ quit();
+ exit($r);
+} elsif ($0 =~ /unbanaccount$/ || $0 =~ /unbanishaccount$/ ||
+ (($ARGV[0] eq "-uba" || $ARGV[0] eq "--unban" || $ARGV[0] eq "--unbanish") && ((shift @ARGV), 1))) {
+ my($r) = bansetaccount($ARGV[0], 0, "");
+ quit();
+ exit($r);
+} elsif ($0 =~ /unblockaccount$/ ||
+ (($ARGV[0] eq "-ubl" || $ARGV[0] eq "--unblock") && ((shift @ARGV), 1))) {
+ my($r) = changestate($ARGV[0], 0, "");
+ quit();
+ exit($r);
+} elsif ($0 =~ /whoaccount$/ ||
+ (($ARGV[0] eq "-w" || $ARGV[0] eq "--who") && ((shift @ARGV), 1))) {
+ my($r) = whoaccount($ARGV[0]);
+ quit();
+ exit($r);
+}
+
+#-------------------------------------------------------------------------
+if ($defaultlanguage eq "F") {
+ print "Lecture de la version du serveur de login...\n";
+} else {
+ print "Reading of the version of the login-server...\n";
+}
+checkloginversion();
+
+# Set the prompt line
+my($term) = new Term::ReadLine "ladmin";
+
+# Here begin the infinite loop to read prompts
+while(1) {
+ # Displaying of the prompt
+ print "\n";
+ if ($defaultlanguage eq "F") {
+ printf "\033[32mPour afficher les commandes, tapez 'Entr馥'.\033[0m\n";
+ } else {
+ printf "\033[32mTo list the commands, type 'enter'.\033[0m\n";
+ }
+ my($cmd) = $term->readline("ladmin> ");
+ # split and recovery of the input
+ chomp $cmd; # remove cariage return
+ $cmd =~ s/\x1b\[\d*\w//g; # remove (esc)[(number)(1alpha) = screen control sequence
+ $cmd =~ s/[\x00-\x1f]//g; # remove control char
+ my($command, $parameters) = split /\s+/,$cmd,2; # extract command and parameters
+ $command = lc($command); # command in lowercase
+ my(@paramlist) = split /\s+/,$parameters; # get list of parameters
+
+ if ($command eq "?" || $command eq "") {
+ $command = "aide" if ($defaultlanguage eq "F");
+ $command = "help" if ($defaultlanguage ne "F");
+ }
+
+ # Analyse of the command
+ eval {
+# help
+ if ("aide" =~ /^\Q$command/ && $command ne "a") { # check 1 letter command: 'aide' or 'add'?
+ displayhelp("aide", $paramlist[0]);
+ } elsif ("help" =~ /^\Q$command/) {
+ displayhelp("help", $paramlist[0]);
+
+# general commands
+ } elsif ("add" =~ /^\Q$command/ && $command ne "a") { # check 1 letter command: 'aide' or 'add'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)\s+(.*)/)) {
+ addaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> <sex> <password>
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ addaccount($paramlist[0], $paramlist[1], ""); # <account_name> <sex> <password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)\s+(.*)/)) {
+ addaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> <sex> <password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ addaccount($paramlist[0], $paramlist[1], ""); # <account_name> <sex> <password>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ addaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> <sex> <password>
+ }
+
+ } elsif ($command eq "ban" || ("banish" =~ /^\Q$command/ && length($command) >= 4)) {
+ if (@paramlist = ($parameters =~ m/^(\S+)\s+(\S+)\s+"(.*)"/)) { # yyyy/mm/dd hh:mm:ss <account_name>
+ bansetaccount($paramlist[2], $paramlist[0], $paramlist[1]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^(\S+)\s+(\S+)\s+'(.*)'/)) { # yyyy/mm/dd hh:mm:ss <account_name>
+ bansetaccount($paramlist[2], $paramlist[0], $paramlist[1]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } else {
+ @paramlist = split /\s+/,$parameters,3; # yyyy/mm/dd hh:mm:ss <account_name>
+ bansetaccount($paramlist[2], $paramlist[0], $paramlist[1]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ }
+
+ } elsif (("banadd" =~ /^\Q$command/ || $command eq "ba") && $command ne "b") { # check 1 letter command: 'ba' or 'bs'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ banaddaccount($paramlist[0], $paramlist[1]); # <account_name> <modifier>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ banaddaccount($paramlist[0], $paramlist[1]); # <account_name> <modifier>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ banaddaccount($paramlist[0], $paramlist[1]); # <account_name> <modifier>
+ }
+
+ } elsif (("banset" =~ /^\Q$command/ || $command eq "bs") && $command ne "b") { # check 1 letter command: 'ba' or 'bs'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)\s+(\S+)/)) {
+ bansetaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ bansetaccount($paramlist[0], $paramlist[1], "23:59:59"); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)\s+(\S+)/)) {
+ bansetaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ bansetaccount($paramlist[0], $paramlist[1], "23:59:59"); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ bansetaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ }
+
+ } elsif ("block" =~ /^\Q$command/ && length($command) >= 2) {
+ if (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ changestate($paramlist[0], 5, ""); # <account_name> <new_state> <error_message_#7>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ changestate($paramlist[0], 5, ""); # <account_name> <new_state> <error_message_#7>
+ } else {
+ @paramlist = split /\s+/,$parameters,1;
+ changestate($paramlist[0], 5, ""); # <account_name> <new_state> <error_message_#7>
+ }
+
+ } elsif ("check" =~ /^\Q$command/ && $command ne "c") { # check 1 letter command: 'check' or 'create'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(.*)/)) {
+ checkaccount($paramlist[0], $paramlist[1]); # <account_name> <password>
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ checkaccount($paramlist[0], ""); # <account_name> <password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(.*)/)) {
+ checkaccount($paramlist[0], $paramlist[1]); # <account_name> <password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ checkaccount($paramlist[0], ""); # <account_name> <password>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ checkaccount($paramlist[0], $paramlist[1]); # <account_name> <password>
+ }
+
+ } elsif ("create" =~ /^\Q$command/ && $command ne "c") { # check 1 letter command: 'check' or 'create'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)\s+(\S+)\s+(.*)/)) {
+ createaccount($paramlist[0], $paramlist[1], $paramlist[2], $paramlist[3]); # <account_name> <sex> <email> <password>
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)\s+(\S+)/)) {
+ createaccount($paramlist[0], $paramlist[1], $paramlist[2], ""); # <account_name> <sex> <email> <password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)\s+(\S+)\s+(.*)/)) {
+ createaccount($paramlist[0], $paramlist[1], $paramlist[2], $paramlist[3]); # <account_name> <sex> <email> <password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)\s+(\S+)/)) {
+ createaccount($paramlist[0], $paramlist[1], $paramlist[2], ""); # <account_name> <sex> <email> <password>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ createaccount($paramlist[0], $paramlist[1], $paramlist[2], $paramlist[3]); # <account_name> <sex> <email> <password>
+ }
+
+ } elsif ("del" =~ /^\Q$command/ || "delete" =~ /^\Q$command/) {
+ if (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ delaccount($paramlist[0]); # <account_name>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ delaccount($paramlist[0]); # <account_name>
+ } else {
+ @paramlist = split /\s+/,$parameters,1;
+ delaccount($paramlist[0]); # <account_name>
+ }
+
+ } elsif ("email" =~ /^\Q$command/ && $command ne "e") { # check 1 letter command: 'email', 'end' or 'exit'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ changeemail($paramlist[0], $paramlist[1]); # <account_name> <email>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ changeemail($paramlist[0], $paramlist[1]); # <account_name> <email>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ changeemail($paramlist[0], $paramlist[1]); # <account_name> <email>
+ }
+
+ } elsif ("getcount" =~ /^\Q$command/ && $command ne "g") { # check 1 letter command: 'getcount' or 'gm'?
+ getlogincount();
+
+ } elsif ("gm" =~ /^\Q$command/ && $command ne "g") { # check 1 letter command: 'getcount' or 'gm'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ changegmlevel($paramlist[0], int($paramlist[1])); # <account_name> <GM_level>
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ changegmlevel($paramlist[0], 0); # <account_name> <GM_level>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ changegmlevel($paramlist[0], int($paramlist[1])); # <account_name> <GM_level>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ changegmlevel($paramlist[0], 0); # <account_name> <GM_level>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ changegmlevel($paramlist[0], int($paramlist[1])); # <account_name> <GM_level>
+ }
+
+ } elsif ("id" =~ /^\Q$command/ && $command ne "i") { # check 1 letter command: 'id' or 'info'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ idaccount($paramlist[0]); # <account_name>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ idaccount($paramlist[0]); # <account_name>
+ } else {
+ @paramlist = split /\s+/,$parameters,1;
+ idaccount($paramlist[0]); # <account_name>
+ }
+
+ } elsif ("info" =~ /^\Q$command/ && $command ne "i") { # check 1 letter command: 'id' or 'info'?
+ infoaccount(int($paramlist[0])); # <account_id>
+
+ } elsif ($command eq "kami") { # check all letters command: 'kami' or 'kamib'?
+ @paramlist = split /\s+/,$parameters,1;
+ sendbroadcast(0, $paramlist[0]); # <type> <message>
+
+ } elsif ($command eq "kamib") { # check all letters command: 'kami' or 'kamib'?
+ @paramlist = split /\s+/,$parameters,1;
+ sendbroadcast(0x10, $paramlist[0]); # <type> <message>
+
+ } elsif ("language" =~ /^\Q$command/ && $command ne "l") { # check 1 letter command: 'list' or 'language'?
+ changelanguage($paramlist[0]); # <language>
+
+ } elsif (("list" =~ /^\Q$command/ || $command eq "ls") && $command ne "l") { # check 1 letter command: 'list' or 'language'?
+ listaccount(int($paramlist[0]), int($paramlist[1]), 0); # [start_id [end_id]] 0: to list all
+
+ } elsif (("listban" =~ /^\Q$command/ || $command eq "lsban") && $command ne "l") { # need to specificaly write Ban to have this list # check 1 letter command: 'list' or 'language'?
+ listaccount(int($paramlist[0]), int($paramlist[1]), 3); # [start_id [end_id]] 3: to list only accounts with state or banished
+
+ } elsif (("listgm" =~ /^\Q$command/ || $command eq "lsgm") && $command ne "l") { # need to specificaly write GM to have this list # check 1 letter command: 'list' or 'language'?
+ listaccount(int($paramlist[0]), int($paramlist[1]), 1); # [start_id [end_id]] 1: to list only GM
+
+ } elsif (("listok" =~ /^\Q$command/ || $command eq "lsok") && $command ne "l") { # need to specificaly write OK to have this list # check 1 letter command: 'list' or 'language'?
+ listaccount(int($paramlist[0]), int($paramlist[1]), 4); # [start_id [end_id]] 4: to list only accounts without state and not banished
+
+ } elsif ("memo" =~ /^\Q$command/) {
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(.*)/)) {
+ changememo($paramlist[0], $paramlist[1]); # <account_name> <memo>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(.*)/)) {
+ changememo($paramlist[0], $paramlist[1]); # <account_name> <memo>
+ } else {
+ @paramlist = split /\s+/,$parameters,2;
+ changememo($paramlist[0], $paramlist[1]); # <account_name> <memo>
+ }
+
+ } elsif ("name" =~ /^\Q$command/) {
+ nameaccount(int($paramlist[0])); # <account_id>
+
+ } elsif ("passwd" =~ /^\Q$command/ || "password" =~ /^\Q$command/) {
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(.*)/)) {
+ changepasswd($paramlist[0], $paramlist[1]); # <account_name> <new_password>
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ changepasswd($paramlist[0], ""); # <account_name> <new_password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(.*)/)) {
+ changepasswd($paramlist[0], $paramlist[1]); # <account_name> <new_password>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ changepasswd($paramlist[0], ""); # <account_name> <new_password>
+ } else {
+ @paramlist = split /\s+/,$parameters,2;
+ changepasswd($paramlist[0], $paramlist[1]); # <account_name> <new_password>
+ }
+
+ } elsif ("reloadgm" =~ /^\Q$command/) {
+ reloadGM();
+
+ } elsif ("search" =~ /^\Q$command/ && $command ne "s" && # check 1 letter command: 'search', 'state' or 'sex'?
+ $command ne "se") { # check 2 letters command: 'search' or 'sex'?
+ if (@paramlist = ($parameters =~ m/^(-{1,2}[re]\S*)\s+(.*)/)) {
+ searchaccount($paramlist[0], $paramlist[1]); # -r/-e/--expr/--regex <expression> | <expression>
+ } else {
+ @paramlist = split /\s+/,$parameters,1;
+ searchaccount($paramlist[0], ""); # -r/-e/--expr/--regex <expression> | <expression>
+ }
+
+ } elsif ("sex" =~ /^\Q$command/ && $command ne "s" && # check 1 letter command: 'search', 'state' or 'sex'?
+ $command ne "se") { # check 2 letters command: 'search' or 'sex'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ changesex($paramlist[0], $paramlist[1]); # <account_name> <sex>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ changesex($paramlist[0], $paramlist[1]); # <account_name> <sex>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ changesex($paramlist[0], $paramlist[1]); # <account_name> <sex>
+ }
+
+ } elsif ("state" =~ /^\Q$command/ && $command ne "s") { # check 1 letter command: 'search', 'state' or 'sex'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\d+)\s+(.*)/)) {
+ changestate($paramlist[0], int($paramlist[1]), $paramlist[2]); # <account_name> <new_state> <error_message_#7>
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"\s+(\d+)/)) {
+ changestate($paramlist[0], int($paramlist[1]), ""); # <account_name> <new_state> <error_message_#7>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\d+)\s+(.*)/)) {
+ changestate($paramlist[0], int($paramlist[1]), $paramlist[2]); # <account_name> <new_state> <error_message_#7>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\d+)/)) {
+ changestate($paramlist[0], int($paramlist[1]), ""); # <account_name> <new_state> <error_message_#7>
+ } else {
+ @paramlist = split /\s+/,$parameters,3;
+ changestate($paramlist[0], int($paramlist[1]), $paramlist[2]); # <account_name> <new_state> <error_message_#7>
+ }
+
+ } elsif (("timeadd" =~ /^\Q$command/ || $command eq "ta") && $command ne "t") { # check 1 letter command: 'ta' or 'ts'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ timeaddaccount($paramlist[0], $paramlist[1]); # <account_name> <modifier>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ timeaddaccount($paramlist[0], $paramlist[1]); # <account_name> <modifier>
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ timeaddaccount($paramlist[0], $paramlist[1]); # <account_name> <modifier>
+ }
+
+ } elsif (("timeset" =~ /^\Q$command/ || $command eq "ts") && $command ne "t") { # check 1 letter command: 'ta' or 'ts'?
+ if (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)\s+(\S+)/)) {
+ timesetaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^"(.*)"\s+(\S+)/)) {
+ timesetaccount($paramlist[0], $paramlist[1], "23:59:59"); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)\s+(\S+)/)) {
+ timesetaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'\s+(\S+)/)) {
+ timesetaccount($paramlist[0], $paramlist[1], "23:59:59"); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } else {
+ @paramlist = split /\s+/,$parameters;
+ timesetaccount($paramlist[0], $paramlist[1], $paramlist[2]); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ }
+
+ } elsif ($command eq "unban" || ("unbanish" =~ /^\Q$command/ && length($command) >= 4)) {
+ if (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ bansetaccount($paramlist[0], 0, ""); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ bansetaccount($paramlist[0], 0, ""); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ } else {
+ @paramlist = split /\s+/,$parameters,1;
+ bansetaccount($paramlist[0], 0, ""); # <account_name> yyyy/mm/dd [hh:mm:ss]
+ }
+
+ } elsif ("unblock" =~ /^\Q$command/ && length($command) >= 4) {
+ if (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ changestate($paramlist[0], 0, ""); # <account_name> <new_state> <error_message_#7>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ changestate($paramlist[0], 0, ""); # <account_name> <new_state> <error_message_#7>
+ } else {
+ @paramlist = split /\s+/,$parameters,1;
+ changestate($paramlist[0], 0, ""); # <account_name> <new_state> <error_message_#7>
+ }
+
+ } elsif ("version" =~ /^\Q$command/) {
+ checkloginversion();
+
+ } elsif ("who" =~ /^\Q$command/) {
+ if (@paramlist = ($parameters =~ m/^"(.*)"/)) {
+ whoaccount($paramlist[0]); # <account_name>
+ } elsif (@paramlist = ($parameters =~ m/^'(.*)'/)) {
+ whoaccount($paramlist[0]); # <account_name>
+ } else {
+ @paramlist = split /\s+/,$parameters,1;
+ whoaccount($paramlist[0]); # <account_name>
+ }
+
+# quit
+ } elsif ("quit" =~ /^\Q$command/ ||
+ (("end" =~ /^\Q$command/ || "exit" =~ /^\Q$command/) && $command ne "e")) { # check 1 letter command: 'email', 'end' or 'exit'?
+ last;
+
+# unknown command
+ } elsif ($command) {
+ if ($defaultlanguage eq "F") {
+ print "Commande inconnue [".$command."]\n";
+ } else {
+ print "Unknown command [".$command."]\n";
+ }
+ }
+# $term->addhistory($cmd) if $command;
+ };
+ if ($@) {
+ if ($defaultlanguage eq "F") {
+ print "Erreur [".$command."]\n$@";
+ } else {
+ print "Error [".$command."]\n$@";
+ }
+ }
+};
+
+# End of the software
+quit();
+
+if ($defaultlanguage eq "F") {
+ print "Au revoir.\n";
+} else {
+ print "Bye.\n";
+}
+exit(0);
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Displaying of the version of the login-server
+sub checkloginversion() {
+ print $so pack("v",30000); # 0x7530
+ $so->flush();
+ $buf = readso(10);
+ # Analyse du Packet
+ my($ret, $maver, $miver, $rev, $dev, $mod, $type, $mdver) = unpack("vc6v", $buf);
+ if ($ret != 30001) { #0x7531
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ exit(6);
+ }
+
+ print " Login-Server [$loginserverip:$loginserverport]\n";
+ printf " eAthena version %s-%d.%d", ("stable", "dev")[$dev], $maver, $miver;
+ printf " revision %d", $rev if $rev;
+ printf "%s%d.\n", ("", "-mod")[$mod], $mdver;
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Displaying of the help
+sub displayhelp() {
+ my($help, $receivedcommand) = @_;
+
+ my($command) = lc($receivedcommand); # command in lowercase
+
+ if ($command eq "") {
+ $command = "not a command"; # any value that is not a command
+ }
+
+ if ($command eq "?") {
+ $command = "aide" if ($defaultlanguage eq "F");
+ $command = "help" if ($defaultlanguage ne "F");
+ }
+
+ if ($help eq "aide") {
+ if ("aide" =~ /^\Q$command/ && $command ne "a") { # check 1 letter command: 'aide' or 'add'?
+ printf "aide/help/?\n";
+ printf " Affiche la description des commandes\n";
+ printf "aide/help/? [commande]\n";
+ printf " Affiche la description de la commande specifi馥\n";
+ } elsif ("help" =~ /^\Q$command/) {
+ 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";
+ } elsif ("add" =~ /^\Q$command/ && $command ne "a") { # check 1 letter command: 'aide' or 'add'?
+ printf "add <nomcompte> <sexe> <motdepasse>\n";
+ printf " Cr馥 un compte avec l'email par d馭aut (a\@a.com).\n";
+ printf " Concernant le sexe, seule la premi鑽e lettre compte (F ou M).\n";
+ printf " L'e-mail est a\@a.com (e-mail par d馭aut). 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";
+ } elsif ($command eq "ban" || ("banish" =~ /^\Q$command/ && length($command) >= 4)) {
+ printf "ban/banish aaaa/mm/jj hh:mm:ss <nomcompte>\n";
+ printf " Change la date de fin de bannissement d'un compte.\n";
+ printf " La diff駻ence avec banset est la position du nom du compte.\n";
+ } elsif (("banadd" =~ /^\Q$command/ || $command eq "ba") && $command ne "b") { # check 1 letter command: 'ba' or 'bs'?
+ 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駑ent modifi:\n";
+ printf " a ou y: ann馥\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麥e 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";
+ } elsif (("banset" =~ /^\Q$command/ || $command eq "bs") && $command ne "b") { # check 1 letter command: 'ba' or 'bs'?
+ 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馭aut: 23:59:59\n";
+ printf "banset <nomcompte> 0\n";
+ printf " D饕anni un compte (0 = de-banni).\n";
+ } elsif ("block" =~ /^\Q$command/ && length($command) >= 2) {
+ 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'駲uivalent de state <nom_compte> 5.\n";
+ } elsif ("check" =~ /^\Q$command/ && $command ne "c") { # check 1 letter command: 'check' or 'create'?
+ printf "check <nomcompte> <motdepasse>\n";
+ printf " V駻ifie 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騁hode que vous poss馘ez pour savoir\n";
+ printf " si un mot de passe est le bon. L'autre m騁hode est\n";
+ printf " d'avoir un acc鑚 ('physique') au fichier des comptes.\n";
+ } elsif ("create" =~ /^\Q$command/ && $command ne "c") { # check 1 letter command: 'check' or 'create'?
+ 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";
+ } elsif ("del" =~ /^\Q$command/ || "delete" =~ /^\Q$command/) {
+ printf "del <nomcompte>\n";
+ printf " Supprime un compte.\n";
+ printf " La commande demande confirmation. Apr鑚 confirmation, le compte est d騁ruit.\n";
+ } elsif ("email" =~ /^\Q$command/ && $command ne "e") { # check 1 letter command: 'email', 'end' or 'exit'?
+ printf "email <nomcompte> <email>\n";
+ printf " Modifie l'e-mail d'un compte.\n";
+ } elsif ("getcount" =~ /^\Q$command/ && $command ne "g") { # check 1 letter command: 'getcount' or 'gm'?
+ printf "getcount\n";
+ printf " Donne le nombre de joueurs en ligne par serveur de char.\n";
+ } elsif ("gm" =~ /^\Q$command/ && $command ne "g") { # check 1 letter command: 'getcount' or 'gm'?
+ printf "gm <nomcompte> [Niveau_GM]\n";
+ printf " Modifie le niveau de GM d'un compte.\n";
+ printf " Valeur par d馭aut: 0 (suppression du niveau de GM).\n";
+ printf " <exemple> gm nomtest 80\n";
+ } elsif ("id" =~ /^\Q$command/ && $command ne "i") { # check 1 letter command: 'id' or 'info'?
+ printf "id <nomcompte>\n";
+ printf " Donne l'id d'un compte.\n";
+ } elsif ("info" =~ /^\Q$command/ && $command ne "i") { # check 1 letter command: 'id' or 'info'?
+ printf "info <idcompte>\n";
+ printf " Affiche les informations sur un compte.\n";
+ } elsif ($command eq "kami") { # check all letters command: 'kami' or 'kamib'?
+ printf "kami <message>\n";
+ printf " Envoi un message g駭駻al sur tous les serveurs de map (en jaune).\n";
+ } elsif ($command eq "kamib") { # check all letters command: 'kami' or 'kamib'?
+ printf "kamib <message>\n";
+ printf " Envoi un message g駭駻al sur tous les serveurs de map (en bleu).\n";
+ } elsif ("language" =~ /^\Q$command/ && $command ne "l") { # check 1 letter command: 'list' or 'language'?
+ printf("language <langue>\n");
+ printf(" Change la langue d'affichage.\n");
+ printf(" Langues possibles: 'Fran軋is' ou 'English'.\n");
+ } elsif (("list" =~ /^\Q$command/ || $command eq "ls") && $command ne "l") { # check 1 letter command: 'list' or 'language'?
+ 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駱art et de fin.\n";
+ printf " La recherche par nom n'est pas possible avec cette commande.\n";
+ printf " <example> list 10 9999999\n";
+ } elsif (("listban" =~ /^\Q$command/ || $command eq "lsban") && $command ne "l") { # need to specificaly write Ban to have this list # check 1 letter command: 'list' or 'language'?
+ printf "listBan/lsBan [Premier_id [Dernier_id]]\n";
+ printf " Comme list/ls, mais seulement pour les comptes GM avec un statut ou bannis.\n";
+ } elsif (("listgm" =~ /^\Q$command/ || $command eq "lsgm") && $command ne "l") { # need to specificaly write GM to have this list # check 1 letter command: 'list' or 'language'?
+ printf "listGM/lsGM [Premier_id [Dernier_id]]\n";
+ printf " Comme list/ls, mais seulement pour les comptes GM.\n";
+ } elsif (("listok" =~ /^\Q$command/ || $command eq "lsok") && $command ne "l") { # need to specificaly write OK to have this list # check 1 letter command: 'list' or 'language'?
+ printf "listOK/lsOK [Premier_id [Dernier_id]]\n";
+ printf " Comme list/ls, mais seulement pour les comptes sans statut et non bannis.\n";
+ } elsif ("memo" =~ /^\Q$command/) {
+ printf "memo <nomcompte> <memo>\n";
+ printf " Modifie le m駑o d'un compte.\n";
+ printf " 'memo': Il peut avoir jusqu' 253 caract鑽es (avec des espaces ou non).\n";
+ } elsif ("name" =~ /^\Q$command/) {
+ printf "name <idcompte>\n";
+ printf " Donne le nom d'un compte.\n";
+ } elsif ("passwd" =~ /^\Q$command/ || "password" =~ /^\Q$command/) {
+ 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";
+ } elsif ("reloadgm" =~ /^\Q$command/) {
+ printf "reloadGM\n";
+ printf " Reload GM configuration file\n";
+ } elsif ("search" =~ /^\Q$command/ && $command ne "s" && # check 1 letter command: 'search', 'state' or 'sex'?
+ $command ne "se") { # check 2 letters command: 'search' or 'sex'?
+ 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鑽e.\n";
+ printf " Affiche les comptes dont les noms correspondent.\n";
+ } elsif ("sex" =~ /^\Q$command/ && $command ne "s" && # check 1 letter command: 'search', 'state' or 'sex'?
+ $command ne "se") { # check 2 letters command: 'search' or 'sex'?
+ printf "sex <nomcompte> <sexe>\n";
+ printf " Modifie le sexe d'un compte.\n";
+ printf " <exemple> sex testname Male\n";
+ } elsif ("state" =~ /^\Q$command/ && $command ne "s") { # check 1 letter command: 'search', 'state' or 'sex'?
+ printf "state <nomcompte> <nouveaustatut> <message_erreur_7>\n";
+ printf " Change le statut d'un compte.\n";
+ printf " 'nouveaustatut': Le statut est le m麥e que celui du packet 0x006a + 1.\n";
+ printf " les possibilit駸 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";
+ } elsif (("timeadd" =~ /^\Q$command/ || $command eq "ta") && $command ne "t") { # check 1 letter command: 'ta' or 'ts'?
+ 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駑ent modifi:\n";
+ printf " a ou y: ann馥\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麥e temps.\n";
+ printf "NOTE: Vous ne pouvez pas modifier une limite de validit illimit馥. Si vous\n";
+ printf " d駸irez le faire, c'est que vous voulez probablement cr馥r un limite de\n";
+ printf " validit limit馥. Donc, en premier, fix une limite de valitid.\n";
+ } elsif (("timeset" =~ /^\Q$command/ || $command eq "ts") && $command ne "t") { # check 1 letter command: 'ta' or 'ts'?
+ printf "timeset <nomcompte> aaaa/mm/jj [hh:mm:ss]\n";
+ printf " Change la limite de validit d'un compte.\n";
+ printf " Heure par d馭aut: 23:59:59\n";
+ printf "timeset <nomcompte> 0\n";
+ printf " Donne une limite de validit illimit馥 (0 = illimit馥).\n";
+ } elsif ($command eq "unban" || ("unbanish" =~ /^\Q$command/ && length($command) >= 4)) {
+ printf "unban/unbanish <nom compte>\n";
+ printf " Ote le banissement d'un compte.\n";
+ printf " La commande est l'駲uivalent de banset <nom_compte> 0.\n";
+ } elsif ("unblock" =~ /^\Q$command/ && length($command) >= 4) {
+ printf "unblock <nom compte>\n";
+ printf " Place le status d'un compte 0 (Compte ok).\n";
+ printf " La commande est l'駲uivalent de state <nom_compte> 0.\n";
+ } elsif ("version" =~ /^\Q$command/) {
+ printf "version\n";
+ printf " Affiche la version du login-serveur.\n";
+ } elsif ("who" =~ /^\Q$command/) {
+ printf "who <nomcompte>\n";
+ printf " Affiche les informations sur un compte.\n";
+ } elsif ("quit" =~ /^\Q$command/ ||
+ (("end" =~ /^\Q$command/ || "exit" =~ /^\Q$command/) && $command ne "e")) { # check 1 letter command: 'email', 'end' or 'exit'?\n";
+ printf "quit/end/exit\n";
+ printf " Fin du programme d'administration.\n";
+ } else {
+ if ($receivedcommand ne "") {
+ printf "Commande inconnue [%s] pour l'aide. Affichage de toutes les commandes.\n", $receivedcommand;
+ }
+ print << "ENDOFAIDE";
+ aide/help/? -- Affiche cet aide
+ aide/help/? [commande] -- Affiche l'aide de la commande
+ add <nomcompte> <sexe> <motdepasse> -- Cr馥 un compte (sans email)
+ ban/banish aaaa/mm/jj hh:mm:ss <nomcompte>-- Change la date finale de banismnt
+ banadd/ba <nomcompte> <modificateur> -- Ajout/soustrait du temps la
+ exemple: ba moncompte +1m-2mn1s-2y date finale de banissement
+ banset/bs <nomcompte> aaaa/mm/jj [hh:mm:ss] -- Change la date fin de banisemnt
+ banset/bs <nomcompte> 0 -- D-banis un compte.
+ block <nom compte> -- Mets le status d'un compte 5 (blocked by the GM Team)
+ check <nomcompte> <motdepasse> -- V駻ifie un mot de passe d'un compte
+ create <nomcompte> <sexe> <email> <motdepasse> -- Cr馥 un compte (avec email)
+ del <nomcompte> -- Supprime un compte
+ email <nomcompte> <email> -- Modifie l'e-mail d'un compte
+ getcount -- Donne le nb de joueurs en ligne
+ gm <nomcompte> [Niveau_GM] -- Modifie le niveau de GM d'un compte
+ id <nomcompte> -- Donne l'id d'un compte
+ info <idcompte> -- Affiche les infos sur un compte
+ kami <message> -- Envoi un message g駭駻al (en jaune)
+ kamib <message> -- Envoi un message g駭駻al (en bleu)
+ language <langue> -- Change la langue d'affichage.
+ list/ls [Premier_id [Dernier_id] ] -- Affiche une liste de comptes
+ listBan/lsBan [Premier_id [Dernier_id] ]-- Affiche une liste de comptes
+ avec un statut ou bannis
+ listGM/lsGM [Premier_id [Dernier_id] ] -- Affiche une liste de comptes GM
+ listOK/lsOK [Premier_id [Dernier_id] ] -- Affiche une liste de comptes
+ sans status et non bannis
+ memo <nomcompte> <memo> -- Modifie le memo d'un compte
+ name <idcompte> -- Donne le nom d'un compte
+ passwd <nomcompte> <nouveaumotdepasse> -- Change le mot de passe d'un compte
+ quit/end/exit -- Fin du programme d'administation
+ reloadGM -- Recharger le fichier de config des GM
+ search <expression> -- Cherche des comptes
+ search -e/-r/--expr/--regex <expression> -- Cherche des comptes par REGEX
+ sex <nomcompte> <sexe> -- Modifie le sexe d'un compte
+ state <nomcompte> <nouveaustatut> <messageerr7> -- Change le statut d'1 compte
+ timeadd/ta <nomcompte> <modificateur> -- Ajout/soustrait du temps la
+ exemple: ta moncompte +1m-2mn1s-2y limite de validit
+ timeset/ts <nomcompte> aaaa/mm/jj [hh:mm:ss] -- Change la limite de validit
+ timeset/ts <nomcompte> 0 -- limite de validit = illimit馥
+ unban/unbanish <nom compte> -- Ote le banissement d'un compte
+ unblock <nom compte> -- Mets le status d'un compte 0 (Compte ok)
+ version -- Donne la version du login-serveur
+ who <nomcompte> -- Affiche les infos sur un compte
+ENDOFAIDE
+ printf(" Note: Pour les noms de compte avec des espaces, tapez \"<nom compte>\" (ou ').\n");
+ }
+ } else {
+ if ("aide" =~ /^\Q$command/ && $command ne "a") { # check 1 letter command: 'aide' or 'add'?
+ 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";
+ } elsif ("help" =~ /^\Q$command/) {
+ 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";
+ } elsif ("add" =~ /^\Q$command/ && $command ne "a") { # check 1 letter command: 'aide' or 'add'?
+ 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";
+ } elsif ($command eq "ban" || ("banish" =~ /^\Q$command/ && length($command) >= 4)) {
+ 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 " The difference with banset is the position of the account name.\n";
+ } elsif (("banadd" =~ /^\Q$command/ || $command eq "ba") && $command ne "b") { # check 1 letter command: 'ba' or 'bs'?
+ 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";
+ } elsif (("banset" =~ /^\Q$command/ || $command eq "bs") && $command ne "b") { # check 1 letter command: 'ba' or 'bs'?
+ 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: 23:59:59\n";
+ printf "banset <account_name> 0\n";
+ printf " Set a non-banished account (0 = unbanished).\n";
+ } elsif ("block" =~ /^\Q$command/ && length($command) >= 2) {
+ printf "block <account name>\n";
+ printf " Set state 5 (You have been blocked by the GM Team) to an account.\n";
+ printf " Same command of state <account_name> 5.\n";
+ } elsif ("check" =~ /^\Q$command/ && $command ne "c") { # check 1 letter command: 'check' or 'create'?
+ 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";
+ } elsif ("create" =~ /^\Q$command/ && $command ne "c") { # check 1 letter command: 'check' or 'create'?
+ 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";
+ } elsif ("del" =~ /^\Q$command/ || "delete" =~ /^\Q$command/) {
+ printf "del <account_name>\n";
+ printf " Remove an account.\n";
+ printf " This order requires confirmation. After confirmation, the account is deleted.\n";
+ } elsif ("email" =~ /^\Q$command/ && $command ne "e") { # check 1 letter command: 'email', 'end' or 'exit'?
+ printf "email <account_name> <email>\n";
+ printf " Modify the e-mail of an account.\n";
+ } elsif ("getcount" =~ /^\Q$command/ && $command ne "g") { # check 1 letter command: 'getcount' or 'gm'?
+ printf "getcount\n";
+ printf " Give the number of players online on all char-servers.\n";
+ } elsif ("gm" =~ /^\Q$command/ && $command ne "g") { # check 1 letter command: 'getcount' or 'gm'?
+ 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";
+ } elsif ("id" =~ /^\Q$command/ && $command ne "i") { # check 1 letter command: 'id' or 'info'?
+ printf "id <account_name>\n";
+ printf " Give the id of an account.\n";
+ } elsif ("info" =~ /^\Q$command/ && $command ne "i") { # check 1 letter command: 'id' or 'info'?
+ printf "info <account_id>\n";
+ printf " Display complete information of an account.\n";
+ } elsif ($command eq "kami") { # check all letters command: 'kami' or 'kamib'?
+ printf "kami <message>\n";
+ printf " Sends a broadcast message on all map-server (in yellow).\n";
+ } elsif ($command eq "kamib") { # check all letters command: 'kami' or 'kamib'?
+ printf "kamib <message>\n";
+ printf " Sends a broadcast message on all map-server (in blue).\n";
+ } elsif ("language" =~ /^\Q$command/ && $command ne "l") { # check 1 letter command: 'list' or 'language'?
+ printf("language <language>\n");
+ printf(" Change the language of displaying.\n");
+ printf(" Possible languages: Fran軋is or English.\n");
+ } elsif (("list" =~ /^\Q$command/ || $command eq "ls") && $command ne "l") { # check 1 letter command: 'list' or 'language'?
+ 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";
+ } elsif (("listban" =~ /^\Q$command/ || $command eq "lsban") && $command ne "l") { # need to specificaly write Ban to have this list # check 1 letter command: 'list' or 'language'?
+ printf "listBan/lsBan [start_id [end_id]]\n";
+ printf " Like list/ls, but only for accounts with state or banished.\n";
+ } elsif (("listgm" =~ /^\Q$command/ || $command eq "lsgm") && $command ne "l") { # need to specificaly write GM to have this list # check 1 letter command: 'list' or 'language'?
+ printf "listGM/lsGM [start_id [end_id]]\n";
+ printf " Like list/ls, but only for GM accounts.\n";
+ } elsif (("listok" =~ /^\Q$command/ || $command eq "lsok") && $command ne "l") { # need to specificaly write OK to have this list # check 1 letter command: 'list' or 'language'?
+ printf "listOK/lsOK [start_id [end_id]]\n";
+ printf " Like list/ls, but only for accounts without state and not banished.\n";
+ } elsif ("memo" =~ /^\Q$command/) {
+ 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";
+ } elsif ("name" =~ /^\Q$command/) {
+ printf "name <account_id>\n";
+ printf " Give the name of an account.\n";
+ } elsif ("passwd" =~ /^\Q$command/ || "password" =~ /^\Q$command/) {
+ 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";
+ } elsif ("reloadgm" =~ /^\Q$command/) {
+ printf "reloadGM\n";
+ printf " Reload GM configuration file\n";
+ } elsif ("search" =~ /^\Q$command/ && $command ne "s" && # check 1 letter command: 'search', 'state' or 'sex'?
+ $command ne "se") { # check 2 letters command: 'search' or 'sex'?
+ 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";
+ } elsif ("sex" =~ /^\Q$command/ && $command ne "s" && # check 1 letter command: 'search', 'state' or 'sex'?
+ $command ne "se") { # check 2 letters command: 'search' or 'sex'?
+ printf "sex <account_name> <sex>\n";
+ printf " Modify the sex of an account.\n";
+ printf " <example> sex testname Male\n";
+ } elsif ("state" =~ /^\Q$command/ && $command ne "s") { # check 1 letter command: 'search', 'state' or 'sex'?
+ 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";
+ } elsif (("timeadd" =~ /^\Q$command/ || $command eq "ta") && $command ne "t") { # check 1 letter command: 'ta' or 'ts'?
+ 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";
+ } elsif (("timeset" =~ /^\Q$command/ || $command eq "ts") && $command ne "t") { # check 1 letter command: 'ta' or 'ts'?
+ printf "timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n";
+ printf " Changes the validity limit of an account.\n";
+ printf " Default time: 23:59:59\n";
+ printf "timeset <account_name> 0\n";
+ printf " Gives an unlimited validity limit (0 = unlimited).\n";
+ } elsif ($command eq "unban" || ("unbanish" =~ /^\Q$command/ && length($command) >= 4)) {
+ printf "unban/unbanish <account name>\n";
+ printf " Remove the banishment of an account.\n";
+ printf " This command works like banset <account_name> 0.\n";
+ } elsif ("unblock" =~ /^\Q$command/ && length($command) >= 4) {
+ 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";
+ } elsif ("version" =~ /^\Q$command/) {
+ printf "version\n";
+ printf " Display the version of the login-server.\n";
+ } elsif ("who" =~ /^\Q$command/) {
+ printf "who <account_name>\n";
+ printf " Displays complete information of an account.\n";
+ } elsif ("quit" =~ /^\Q$command/ ||
+ (("end" =~ /^\Q$command/ || "exit" =~ /^\Q$command/) && $command ne "e")) { # check 1 letter command: 'email', 'end' or 'exit'?\n";
+ printf "quit/end/exit\n";
+ printf " End of the program of administration.\n";
+ } else {
+ if ($receivedcommand ne "") {
+ printf "Unknown command [%s] for help. Displaying of all commands.\n", $receivedcommand;
+ }
+ print << "ENDOFHELP";
+ aide/help/? -- Display this help
+ aide/help/? [command] -- Display the help of the command
+ add <account_name> <sex> <password> -- Create an account with default email
+ ban/banish yyyy/mm/dd hh:mm:ss <account_name> -- Change final date of a ban
+ banadd/ba <account_name> <modifier> -- Add or substract time from the final
+ example: ba apple +1m-2mn1s-2y date of a banishment of an account
+ banset/bs <account_name> yyyy/mm/dd [hh:mm:ss] -- Change final date of a ban
+ banset/bs <account_name> 0 -- Un-banish an account
+ block <account name> -- Set state 5 (blocked by the GM Team) to an account
+ check <account_name> <password> -- Check the validity of a password
+ create <account_name> <sex> <email> <passwrd> -- Create an account with email
+ del <account_name> -- Remove an account
+ email <account_name> <email> -- Modify an email of an account
+ getcount -- Give the number of players online
+ gm <account_name> [GM_level] -- Modify the GM level of an account
+ id <account_name> -- Give the id of an account
+ info <account_id> -- Display all information of an account
+ kami <message> -- Sends a broadcast message (in yellow)
+ kamib <message> -- Sends a broadcast message (in blue)
+ language <language> -- Change the language of displaying.
+ list/ls [First_id [Last_id]] -- Display a list of accounts
+ listBan/lsBan [First_id [Last_id]] -- Display a list of accounts
+ with state or banished
+ listGM/lsGM [First_id [Last_id]] -- Display a list of GM accounts
+ listOK/lsOK [First_id [Last_id]] -- Display a list of accounts
+ without state and not banished
+ memo <account_name> <memo> -- Modify the memo of an account
+ name <account_id> -- Give the name of an account
+ passwd <account_name> <new_password> -- Change the password of an account
+ quit/end/exit -- End of the program of administation
+ reloadGM -- Reload GM configuration file
+ search <expression> -- Seek accounts
+ search -e/-r/--expr/--regex <expressn> -- Seek accounts by regular-expression
+ sex <nomcompte> <sexe> -- Modify the sex of an account
+ state <account_name> <new_state> <error_message_#7> -- Change the state
+ timeadd/ta <account_name> <modifier> -- Add or substract time from the
+ example: ta apple +1m-2mn1s-2y validity limit of an account
+ timeset/ts <account_name> yyyy/mm/dd [hh:mm:ss] -- Change the validify limit
+ timeset/ts <account_name> 0 -- Give a unlimited validity limit
+ unban/unbanish <account name> -- Remove the banishment of an account
+ unblock <account name> -- Set state 0 (Account ok) to an account
+ version -- Gives the version of the login-server
+ who <account_name> -- Display all information of an account
+ENDOFHELP
+ printf(" Note: To use spaces in an account name, type \"<account name>\" (or ').\n");
+ }
+ }
+
+ return 0;
+}
+#--------------------------------------------------------------------------
+
+# Sub-function: Displaying of the accounts list
+sub listaccount() {
+ my($st, $ed, $listflag) = @_;
+ my($i);
+ my($n) = (0);
+ # 0123456789 01 01234567890123456789012301234 012345 0123456789012345678901234567
+ if ($defaultlanguage eq "F") {
+ print " id_compte GM nom_utilisateur sexe count statut\n";
+ } else {
+ print "account_id GM user_name sex count state\n";
+ }
+ print "-------------------------------------------------------------------------------\n";
+ while(1) {
+ print $so pack("vV2", 0x7920, $st, $ed);
+ $so->flush();
+ $buf = readso(4);
+ if (unpack("v", $buf) != 0x7921) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ exit(10);
+ }
+ my($len) = unpack("x2v", $buf);
+ last if ($len <= 4);
+ for($i = 4; $i < $len; $i += 38) {
+ my(@dat) = unpack("VCa24cVV", readso(38));
+ $st = $dat[0] + 1;
+ if ($listflag == 0 ||
+ ($listflag == 1 && $dat[1] > 0) || # check GM flag
+ ($listflag == 3 && $dat[5] != 0) || # check with state or banished
+ ($listflag == 4 && $dat[5] == 0)) { # check without state and not banished
+ printf "%10d %2s %-24s%-5s %6d %-27s\n", $dat[0],
+ ($dat[1] == 0 ? " " : $dat[1]),
+ $dat[2],
+ ($defaultlanguage eq "F" ? ("Femme","Male","Servr")[$dat[3]] : ("Femal","Male","Servr")[$dat[3]]),
+ $dat[4],
+ (($defaultlanguage eq "F" ? "Compte Ok" : "Account OK"),
+ "Unregistered ID",
+ "Incorrect Password",
+ "This ID is expired",
+ "Rejected from Server",
+ "Blocked by the GM Team", # You have been blocked by the GM Team
+ "Your EXE file is too old", # Your Game's EXE file is not the latest version
+ "Banishement or\n Prohibited to login until %s", # You are Prohibited to log in until %s
+ "Server is over populated", # Server is jammed due to over populated
+ "No MSG",
+ "This ID is totally erased")[$dat[5] == 100 ? 10 : $dat[5]]; # This ID has been totally erased
+ $n++;
+ }
+ }
+ }
+ if ($defaultlanguage eq "F") {
+ if ($n == 0) {
+ print "Aucun compte trouv.\n";
+ } elsif ($n == 1) {
+ print "1 compte trouv.\n";
+ } else {
+ print "$n comptes trouv駸.\n";
+ }
+ } else {
+ if ($n == 0) {
+ print "No account found.\n";
+ } elsif ($n == 1) {
+ print "1 account found.\n";
+ } else {
+ print "$n accounts found.\n";
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: add an account with the default e-mail
+sub addaccount() {
+ my($userid, $sex, $passwd) = @_;
+ if ($userid eq "" || !defined($userid)) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> add nomtest Male motdepassetest\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> add testname Male testpass\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+# if ($userid =~ /[^A-Za-z0-9\@-_]/) {
+# if ($defaultlanguage eq "F") {
+# print "Caract鑽e interdit trouv dans le nom du compte ".$`."[${&}]${'}\n";
+# } else {
+# print "Illegal character found in the account name ".$`."[${&}]${'}\n";
+# }
+# return 101;
+# }
+ $sex = uc(substr($sex, 0, 1));
+ if ($sex !~ /^[MF]$/) {
+ if ($defaultlanguage eq "F") {
+ print "Sexe incorrect [$sex]. Entrez M ou F svp.\n";
+ } else {
+ print "Illegal gender [$sex]. Please input M or F.\n";
+ }
+ return 103;
+ }
+ if ($passwd eq "") {
+ return 108 if (($passwd = typepasswd()) eq "");
+ }
+ if (verify_password($passwd) == 0) {
+ return 104;
+ }
+ print $so pack("va24a24a1a40", 0x7930, $userid, $passwd, $sex, "");
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7931) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 106;
+ }
+ $buf = readso(28);
+ if (unpack("V", $buf) == -1 || unpack("V", $buf) == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec la cr饌tion du compte [$userid]. Un compte identique existe d駛.\n";
+ } else {
+ print "Account [$userid] creation failed. Same account already exists.\n";
+ }
+ return 107;
+ } else {
+ if ($defaultlanguage eq "F") {
+ printf "Compte [$userid] cr鳬 avec succ鑚 [id: %d].\n", unpack("V",$buf);
+ } else {
+ printf "Account [$userid] is successfully created [id: %d].\n", unpack("V",$buf);
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: add an account with an e-mail
+sub createaccount() {
+ my($userid, $sex, $email, $passwd) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> create nomtest Male mon\@email.com motdepassetest\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> create testname Male my\@mail.com testpass\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+# if ($userid =~ /[^A-Za-z0-9\@-_]/) {
+# if ($defaultlanguage eq "F") {
+# print "Caract鑽e interdit trouv dans le nom du compte ".$`."[${&}]${'}\n";
+# } else {
+# print "Illegal character found in the account name ".$`."[${&}]${'}\n";
+# }
+# return 101;
+# }
+ $sex = uc(substr($sex, 0, 1));
+ if ($sex !~ /^[MF]$/) {
+ if ($defaultlanguage eq "F") {
+ print "Sexe incorrect [$sex]. Entrez M ou F svp.\n";
+ } else {
+ print "Illegal gender [$sex]. Please input M or F.\n";
+ }
+ return 103;
+ }
+ if (length($email) < 3) {
+ if ($defaultlanguage eq "F") {
+ print "Email trop courte [$email]. Entrez une e-mail valide svp.\n";
+ } else {
+ print "Email is too short [$email]. Please input a valid e-mail.\n";
+ }
+ return 109;
+ }
+ if (length($email) > 39) {
+ if ($defaultlanguage eq "F") {
+ print "Email trop longue [$email]. Entrez une e-mail de 39 caract鑽es maximum svp.\n";
+ } else {
+ print "Email is too long [$email]. Please input an e-mail with 39 bytes at the most.\n";
+ }
+ return 109;
+ }
+ if (verify_email($email) == 0) {
+ if ($defaultlanguage eq "F") {
+ print "Email incorrecte [$email]. Entrez une e-mail valide svp.\n";
+ } else {
+ print "Invalid email [$email]. Please input a valid e-mail.\n";
+ }
+ return 109;
+ }
+ if ($passwd eq "") {
+ return 108 if (($passwd = typepasswd()) eq "");
+ }
+ if (verify_password($passwd) == 0) {
+ return 104;
+ }
+ print $so pack("va24a24a1a40", 0x7930, $userid, $passwd, $sex, $email);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7931) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 106;
+ }
+ $buf = readso(28);
+ if (unpack("V", $buf) == -1 || unpack("V", $buf) == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec la cr饌tion du compte [$userid]. Un compte identique existe d駛.\n";
+ } else {
+ print "Account [$userid] creation failed. Same account already exists.\n";
+ }
+ return 107;
+ } else {
+ if ($defaultlanguage eq "F") {
+ printf "Compte [$userid] cr鳬 avec succ鑚 [id: %d].\n", unpack("V",$buf);
+ } else {
+ printf "Account [$userid] is successfully created [id: %d].\n", unpack("V",$buf);
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: deletion of an account
+sub delaccount() {
+ my($userid) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> del nomtestasupprimer\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> del testnametodelete\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ if ($defaultlanguage eq "F") {
+ print "** Etes-vous vraiment sr de vouloir SUPPRIMER le compte [$userid]? (o/n) ";
+ } else {
+ print "** Are you really sure to DELETE account [$userid]? (y/n) ";
+ }
+ if (lc(substr(<STDIN>, 0, 1)) !~ /[oy]/) {
+ if ($defaultlanguage eq "F") {
+ print "Suppression annul馥\n.";
+ } else {
+ print "Deletion canceled\n";
+ }
+ return 121;
+ }
+ print $so pack("va24", 0x7932, $userid);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7933) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 122;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec de la suppression du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Account [$userid] deletion failed. Account doesn't exist.\n";
+ }
+ return 123;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Compte [$name][id: $id2] SUPPRIME avec succ鑚.\n";
+ } else {
+ print "Account [$name][id: $id2] is successfully DELETED.\n";
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: modification of a password
+sub changepasswd() {
+ my($userid, $passwd) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> passwd nomtest nouveaumotdepasse\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> passwd testname newpassword\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ if ($passwd eq "") {
+ return 134 if (($passwd = typepasswd()) eq "");
+ }
+ if (verify_password($passwd) == 0) {
+ return 131;
+ }
+ print $so pack("va24a24", 0x7934, $userid,$passwd);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7935) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 132;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec de la modification du mot de passe du compte [$userid].\n";
+ print "Le compte [$userid] n'existe pas.\n";
+ } else {
+ print "Account [$userid] password changing failed.\n";
+ print "Account [$userid] doesn't exist.\n";
+ }
+ return 133;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Modification du mot de passe du compte [$name][id: $id2] r騏ssie.\n";
+ } else {
+ print "Account [$name][id: $id2] password successfully changed.\n";
+ }
+ }
+ return 130;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: modification of an account e-mail
+sub changeemail() {
+ my($userid, $email) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> email testname nouveauemail\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> email testname newemail\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ if (length($email) < 3) {
+ if ($defaultlanguage eq "F") {
+ print "Email trop courte [$email]. Entrez une e-mail valide svp.\n";
+ } else {
+ print "Email is too short [$email]. Please input a valid e-mail.\n";
+ }
+ return 109;
+ }
+ if (length($email) > 39) {
+ if ($defaultlanguage eq "F") {
+ print "Email trop longue [$email]. Entrez une e-mail de 39 caract鑽es maximum svp.\n";
+ } else {
+ print "Email is too long [$email]. Please input an e-mail with 39 bytes at the most.\n";
+ }
+ return 109;
+ }
+ if (verify_email($email) == 0) {
+ if ($defaultlanguage eq "F") {
+ print "Email incorrect [$email]. Entrez une e-mail valide svp.\n";
+ } else {
+ print "Invalid email [$email]. Please input a valid e-mail.\n";
+ }
+ return 109;
+ }
+ print $so pack("va24a40", 0x7940, $userid, $email);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7941) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 162;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec de la modification de l'e-mail du compte [$userid].\n";
+ print "Le compte [$userid] n'existe pas.\n";
+ } else {
+ print "Account [$userid] e-mail changing failed.\n";
+ print "Account [$userid] doesn't exist.\n";
+ }
+ return 133;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Modification de l'e-mail du compte [$name][id: $id2] r騏ssie.\n";
+ } else {
+ print "Account [$name][id: $id2] e-mail successfully changed.\n";
+ }
+ }
+ return 160;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: search of accounts
+sub searchaccount() {
+ my($p1, $p2) = @_;
+ my($exp) = ("");
+ if ($p1 eq "-e" || $p1 eq "-r" || $p1 eq "--regex" || $p1 eq "--expr") {
+ if ($p2 eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez une expression r馮uli鑽e ou utilisez 'ls' pour avoir tous les comptes.\n";
+ } else {
+ print "Input a regular expression or use 'ls' to obtain all accounts.\n";
+ }
+ return 141;
+ }
+ $exp = $p2;
+ } else {
+ if ($p1 eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez une chane ou utilisez 'ls' pour avoir tous les comptes.\n";
+ } else {
+ print "Input a string or use 'ls' to obtain all accounts.\n";
+ }
+ return 141;
+ }
+ my($c) = 0;
+ $exp = lc($p1);
+ $exp =~ s/([\@])/\\$1/g;
+ $c += $exp =~ s/([\-\[\]])/\\$1/g;
+ $c += $exp =~ s/([\*\?])/.$1/g;
+ $c += $exp =~ s/\\\[(.)\\\-(.)\\\]/[$1-$2]/g;
+ $exp = "^$exp\$" if $c;
+ }
+ if (eval{ "" =~ /$exp/; }, $@) {
+ if ($defaultlanguage eq "F") {
+ print "Expression r馮uli鑽e non reconnue.\n";
+ } else {
+ print "Regular-Expression compiling failed.\n";
+ }
+ return 141;
+ }
+ my($i);
+ my($n, $st) = (0, 0);
+ # 0123456789 01 01234567890123456789012301234 012345 0123456789012345678901234567
+ if ($defaultlanguage eq "F") {
+ print " id_compte GM nom_utilisateur sexe count statut\n";
+ } else {
+ print "account_id GM user_name sex count state\n";
+ }
+ print "-------------------------------------------------------------------------------\n";
+ while(1) {
+ print $so pack("vV2", 0x7920, $st, 0);
+ $so->flush();
+ $buf = readso(4);
+ if (unpack("v", $buf) != 0x7921) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ exit(10);
+ }
+ my($len) = unpack("x2v", $buf);
+ last if ($len <= 4);
+ for($i = 4; $i < $len; $i += 38) {
+ my(@dat) = unpack("VCa24cVV", readso(38));
+ $st = $dat[0] + 1;
+ next if (lc($dat[2]) !~ /$exp/);
+ printf "%10d %2s %-24s%-5s %6d %-27s\n", $dat[0],
+ ($dat[1] == 0 ? " " : $dat[1]),
+ $dat[2],
+ ($defaultlanguage eq "F" ? ("Femme","Male","Servr")[$dat[3]] : ("Femal","Male","Servr")[$dat[3]]),
+ $dat[4],
+ (($defaultlanguage eq "F" ? "Compte Ok" : "Account OK"),
+ "Unregistered ID",
+ "Incorrect Password",
+ "This ID is expired",
+ "Rejected from Server",
+ "Blocked by the GM Team", # You have been blocked by the GM Team
+ "Your EXE file is too old", # Your Game's EXE file is not the latest version
+ "Banishement or\n Prohibited to login until %s", # You are Prohibited to log in until %s
+ "Server is over populated", # Server is jammed due to over populated
+ "No MSG",
+ "This ID is totally erased")[$dat[5] == 100 ? 10 : $dat[5]]; # This ID has been totally erased
+ $n++;
+ }
+ }
+ if ($defaultlanguage eq "F") {
+ if ($n == 0) {
+ print "Aucun compte trouv.\n";
+ } elsif ($n == 1) {
+ print "1 compte trouv.\n";
+ } else {
+ print "$n comptes trouv駸.\n";
+ }
+ } else {
+ if ($n == 0) {
+ print "No account found.\n";
+ } elsif ($n == 1) {
+ print "1 account found.\n";
+ } else {
+ print "$n accounts found.\n";
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: modify the sex of an account
+sub changesex() {
+ my($userid, $sex) = @_;
+ if ($userid eq "" || !defined($userid)) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> sex nomtest Male\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> sex testname Male\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+# if ($userid =~ /[^A-Za-z0-9\@-_]/) {
+# if ($defaultlanguage eq "F") {
+# print "Caract鑽e interdit trouv dans le nom du compte ".$`."[${&}]${'}\n";
+# } else {
+# print "Illegal character found in the account name ".$`."[${&}]${'}\n";
+# }
+# return 101;
+# }
+ $sex = uc(substr($sex, 0, 1));
+ if ($sex !~ /^[MF]$/) {
+ if ($defaultlanguage eq "F") {
+ print "Sexe incorrect [$sex]. Entrez M ou F svp.\n";
+ } else {
+ print "Illegal gender [$sex]. Please input M or F.\n";
+ }
+ return 103;
+ }
+ print $so pack("va24a1", 0x793c, $userid, $sex);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x793d) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement du sexe du compte [$userid].\n";
+ print "Le compte n'existe pas ou le sexe est d駛 celui demand.\n";
+ } else {
+ print "Account [$userid] sex changing failed.\n";
+ print "Account doesn't exist or the sex is already the good sex.\n";
+ }
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Sexe du compte [$name][id: $id2] chang avec succ鑚.\n";
+ } else {
+ print "Account [$name][id: $id2] sex successfully changed.\n";
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: modify the GM level of an account
+sub changegmlevel() {
+ my($userid, $gm_level) = @_;
+ if ($userid eq "" || !defined($userid)) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> gm nomtest 80\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> gm testname 80\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+# if ($userid =~ /[^A-Za-z0-9\@-_]/) {
+# if ($defaultlanguage eq "F") {
+# print "Caract鑽e interdit trouv dans le nom du compte ".$`."[${&}]${'}\n";
+# } else {
+# print "Illegal character found in the account name ".$`."[${&}]${'}\n";
+# }
+# return 101;
+# }
+ $gm_level = int($gm_level);
+ if ($gm_level < 0 || $gm_level > 99) {
+ if ($defaultlanguage eq "F") {
+ print "Niveau de GM incorrect [$gm_level]. Entrez une valeur de 0 99 svp.\n";
+ } else {
+ print "Illegal GM level [$gm_level]. Please input a value from 0 to 99.\n";
+ }
+ return 103;
+ }
+ print $so pack("va24C", 0x793e, $userid, $gm_level);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x793f) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement du niveau de GM du compte [$userid].\n";
+ print "Le compte n'existe pas, le niveau de GM est d駛 celui demand,\n";
+ print "ou il est impossible de modifier le fichier des comptes GM.\n";
+ } else {
+ print "Account [$userid] GM level changing failed.\n";
+ print "Account doesn't exist, the GM level is already the good GM level,\n";
+ print "or it's impossible to modify the GM accounts file.\n";
+ }
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Niveau de GM du compte [$name][id: $id2] chang avec succ鑚.\n";
+ } else {
+ print "Account [$name][id: $id2] GM level successfully changed.\n";
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Modification of a state
+sub changestate {
+ my($userid, $s, $error_message) = @_;
+ # Valid values: 0: ok, or value of the 0x006a packet + 1
+ if ($s eq "" || (($s < 0 || $s > 9) && $s != 100)) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez une des valeurs suivantes svp:\n";
+ print " 0 = Compte ok 6 = Your Game's EXE file is not the latest version\n";
+ } else {
+ print "Please input one of these values:\n";
+ print " 0 = Account ok 6 = Your Game's EXE file is not the latest version\n";
+ }
+ print " 1 = Unregistered ID 7 = You are Prohibited to log in until %s\n";
+ print " 2 = Incorrect Password 8 = Server is jammed due to over populated\n";
+ print " 3 = This ID is expired 9 = No MSG\n";
+ print " 4 = Rejected from Server 100 = This ID has been totally erased\n";
+ print " 5 = You have been blocked by the GM Team\n";
+ if ($defaultlanguage eq "F") {
+ print "<exemples> state nomtest 5\n";
+ print " state nomtest 7 fin de votre ban\n";
+ print " block <nom du compte>\n";
+ print " unblock <nom du compte>\n";
+ } else {
+ print "<examples> state testname 5\n";
+ print " state testname 7 end of your ban\n";
+ print " block <account name>\n";
+ print " unblock <account name>\n";
+ }
+ return 151;
+ }
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemples> state nomtest 5\n";
+ print " state nomtest 7 fin de votre ban\n";
+ print " block <nom du compte>\n";
+ print " unblock <nom du compte>\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<examples> state testname 5\n";
+ print " state testname 7 end of your ban\n";
+ print " block <account name>\n";
+ print " unblock <account name>\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ if ($s != 7) {
+ $error_message = "-";
+ } else {
+ if (length($error_message) < 1) {
+ if ($defaultlanguage eq "F") {
+ print "Message d'erreur trop court. Entrez un message de 1-19 caract鑽es.\n";
+ } else {
+ print "Error message is too short. Please input a message of 1-19 bytes.\n";
+ }
+ return 102;
+ }
+ if (length($error_message) > 19) {
+ if ($defaultlanguage eq "F") {
+ print "Message d'erreur trop long. Entrez un message de 1-19 caract鑽es.\n";
+ } else {
+ print "Error message is too long. Please input a message of 1-19 bytes.\n";
+ }
+ return 102;
+ }
+ }
+ print $so pack("va24Va20", 0x7936, $userid, $s, $error_message);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7937) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(32);
+ my(@dat) = unpack("Va24V", $buf);
+ while (length($dat[1]) > 0 && substr($dat[1], length($dat[1])-1, 1) eq chr(0)) {
+ chop($dat[1]);
+ };
+ if ($dat[0] != -1 && $dat[0] != 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Statut du compte [$dat[1]][id: $dat[0]] chang avec succ鑚 en [";
+ } else {
+ print "Account [$dat[1]][id: $dat[0]] state successfully changed in [";
+ }
+ print ((($defaultlanguage eq "F" ? "Compte Ok" : "Account OK"),
+ "Unregistered ID",
+ "Incorrect Password",
+ "This ID is expired",
+ "Rejected from Server",
+ "You have been blocked by the GM Team",
+ "Your Game's EXE file is not the latest version",
+ "You are Prohibited to log in until %s",
+ "Server is jammed due to over populated",
+ "No MSG",
+ "This ID has been totally erased")[$dat[2] == 100 ? 10 : $dat[2]]);
+ print "].\n";
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement du statut du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Account [$userid] state changing failed. Account doesn't exist.\n";
+ }
+ }
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Displaying of the number of online players
+sub getlogincount {
+ # Request to the login-server
+ print $so pack("v", 0x7938);
+ $so->flush();
+
+ $buf = readso(4);
+ # Connection failed
+ if (unpack("v", $buf) != 0x7939) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ exit(3);
+ }
+
+ # Get length of the received packet
+ my($len) = unpack("x2v", $buf) - 4;
+
+ # Read information of the servers
+ if ($len < 1) {
+ if ($defaultlanguage eq "F") {
+ printf " Aucun serveur n'est connect au login serveur.\n";
+ } else {
+ printf " No server is connected to the login-server.\n";
+ }
+ } else {
+ my(@slist) = ();
+ for(; $len > 0; $len -= 32) {
+ my($name, $count) = unpack("x6 a20 V", readso(32));
+ $name = substr($name, 0, index($name, "\0"));
+ push @slist, [ $name, $count ];
+ }
+ # Displaying of result
+ my($i);
+ if ($defaultlanguage eq "F") {
+ printf " Nombre de joueurs en ligne (serveur: nb):\n";
+ } else {
+ printf " Number of online players (server: number).\n";
+ }
+ foreach $i(@slist) {
+ printf " %-20s : %5d\n", $i->[0], $i->[1];
+ }
+ }
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Modification of a memo field
+sub changememo {
+ my($userid, $memo) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> memo nomtest nouveau memo\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> memo testname new memo\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ if (length($memo) > 254) {
+ if ($defaultlanguage eq "F") {
+ print "M駑o trop long (".length($memo)." caract鑽es).\n";
+ print "Entrez un m駑o de 254 caract鑽es maximum svp.\n";
+ } else {
+ print "Memo is too long (".length($memo)." characters).\n";
+ print "Please input a memo of 254 bytes at the maximum.\n";
+ }
+ return 102;
+ }
+ if (length($memo) == 0) {
+ print $so pack("va24v", 0x7942, $userid, 0);
+ } else {
+ print $so pack("va24va".length($memo), 0x7942, $userid, length($memo), $memo);
+ }
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7943) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement du m駑o du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Account [$userid] memo changing failed. Account doesn't exist.\n";
+ }
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "M駑o du compte [$name][id: $id2] chang avec succ鑚.\n";
+ } else {
+ print "Account [$name][id: $id2] memo successfully changed.\n";
+ }
+ }
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Request to obtain an account id
+sub idaccount() {
+ my($userid) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> id nomtest\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> id testname\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ print $so pack("va24", 0x7944, $userid);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7945) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 122;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Impossible de trouver l'id du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Unabled to find the account [$userid] id. Account doesn't exist.\n";
+ }
+ return 123;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Le compte [$name] a pour id: $id2.\n";
+ } else {
+ print "The account [$name] have the id: $id2.\n";
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Request to obtain an account name
+sub nameaccount() {
+ my($id) = @_;
+ if ($id < 0) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un id ayant une valeur positive svp.\n";
+ } else {
+ print "Please input a positive value for the id.\n";
+ }
+ return 136;
+ }
+ print $so pack("vV", 0x7946, $id);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7947) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 122;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if (length($name) == 0 || $name eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Impossible de trouver le nom du compte [id: $id2]. Le compte n'existe pas.\n";
+ } else {
+ print "Unabled to find the account [id: $id2] name. Account doesn't exist.\n";
+ }
+ return 123;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Le compte [id: $id2] a pour nom: $name.\n";
+ } else {
+ print "The account [id: $id2] have the name: $name.\n";
+ }
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Set a validity limit of an account
+sub timesetaccount() {
+ my($userid, $date, $time) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple>: timeset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n";
+ print " timeset <nom_du_compte> 0 (0 = illimit)\n";
+ printf " Heure par d馭aut [hh:mm:ss]: 23:59:59\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example>: timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n";
+ print " timeset <account_name> 0 (0 = unlimited)\n";
+ printf " Default time [hh:mm:ss]: 23:59:59\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ my($year, $month, $day) = split(/[.\-\/]/, $date);
+ my($hour, $minute, $second) = split(/:/, $time);
+ if ($time eq "") {
+ $hour = 23;
+ $minute = 59;
+ $second = 59;
+ }
+ my($timestamp);
+ if ($year eq "" ||
+ ($year != 0 && ($month eq "" || $day eq "" || $hour eq "" || $minute eq "" || $second eq ""))) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n";
+ } else {
+ print "Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n";
+ }
+ return 102;
+ }
+ if ($year == 0) {
+ $timestamp = 0;
+ } else {
+ if ($year < 70) {
+ $year = $year + 100;
+ }
+ if ($year >= 1900) {
+ $year = $year - 1900;
+ }
+ if ($month < 1 || $month > 12) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un mois correct svp (entre 1 et 12).\n";
+ } else {
+ print "Please give a correct value for the month (from 1 to 12).\n";
+ }
+ return 102;
+ }
+ $month = $month - 1;
+ if ($day < 1 || $day > 31) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un jour correct svp (entre 1 et 31).\n";
+ } else {
+ print "Please give a correct value for the day (from 1 to 31).\n";
+ }
+ return 102;
+ }
+ if ((($month == 3 || $month == 5 || $month == 8 || $month == 10) && $day > 30) ||
+ ($month == 1 && $day > 29)) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un jour correct en fonction du mois svp.\n";
+ } else {
+ print "Please give a correct value for a day of this month.\n";
+ }
+ return 102;
+ }
+ if ($hour < 0 || $hour > 23) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez une heure correcte svp (entre 0 et 23).\n";
+ } else {
+ print "Please give a correct value for the hour (from 0 to 23).\n";
+ }
+ return 102;
+ }
+ if ($minute < 0 || $minute > 59) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez des minutes correctes svp (entre 0 et 59).\n";
+ } else {
+ print "Please give a correct value for the minutes (from 0 to 59).\n";
+ }
+ return 102;
+ }
+ if ($second < 0 || $second > 59) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez des secondes correctes svp (entre 0 et 59).\n";
+ } else {
+ print "Please give a correct value for the seconds (from 0 to 59).\n";
+ }
+ return 102;
+ }
+ $timestamp = POSIX::mktime($second, $minute, $hour, $day, $month, $year, 0, 0, -1); # -1: no winter/summer time modification
+ if ($timestamp == undef) {
+ if ($defaultlanguage eq "F") {
+ print "Date incorrecte.\n";
+ print "Ajoutez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n";
+ } else {
+ print "Invalid date.\n";
+ print "Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n";
+ }
+ return 102;
+ }
+ }
+
+ print $so pack("va24V", 0x7948, $userid, $timestamp);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7949) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(32);
+ my(@dat) = unpack("Va24V", $buf);
+ while (length($dat[1]) > 0 && substr($dat[1], length($dat[1])-1, 1) eq chr(0)) {
+ chop($dat[1]);
+ };
+ if ($dat[0] != -1 && $dat[0] != 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Limite de validit du compte [$dat[1]][id: $dat[0]] chang馥 avec succ鑚 ".
+ ($dat[2] == 0 ? "en [illimit饐.\n" : "pour 黎re jusqu'au ".(POSIX::ctime($dat[2])));
+ } else {
+ print "Validity Limit of the account [$dat[1]][id: $dat[0]] successfully changed ".
+ ($dat[2] == 0 ? "to [unlimited].\n" : "to be until ".(POSIX::ctime($dat[2])));
+ }
+ # localtime($dat[2]) is also possible to display instead of POSIX::ctime.
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement de la validit du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Account [$userid] validity limit changing failed. Account doesn't exist.\n";
+ }
+ }
+
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Add/substract time to the validity limit of an account
+sub timeaddaccount() {
+ my($userid, $modif) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print " <exemple> timeadd nomtest +1m-2mn1s-6y\n";
+ print " Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n";
+ print " et 6 ans dans le m麥e temps.\n";
+ } else {
+ print "Please input an account name.\n";
+ print " <example> timeadd testname +1m-2mn1s-6y\n";
+ print " this example adds 1 month and 1 second, and substracts 2 minutes\n";
+ print " and 6 years at the same time.\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ my($year, $month, $day) = (0, 0 ,0);
+ my($hour, $minute, $second) = (0, 0 ,0);
+
+ $modif = lc($modif);
+ while (length($modif) > 0) {
+ my($value) = int($modif);
+ if ($value == 0) {
+ $modif = substr($modif, 1);
+ } else {
+ if (substr($modif, 0, 1) =~ /[\-\+]/) {
+ $modif = substr($modif, 1);
+ }
+ while (length($modif) > 0 && substr($modif, 0, 1) =~ /[0-9]/) {
+ $modif = substr($modif, 1);
+ }
+ if (index($modif, "s") == 0) {
+ $second = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "mn") == 0) {
+ $minute = $value;
+ $modif = substr($modif, 2);
+ } elsif (index($modif, "h") == 0) {
+ $hour = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "d") == 0 || index($modif, "j") == 0) {
+ $day = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "m") == 0) {
+ $month = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "y") == 0 || index($modif, "a") == 0) {
+ $year = $value;
+ $modif = substr($modif, 1);
+ } else {
+ $modif = substr($modif, 1);
+ }
+ }
+ }
+
+ if ($defaultlanguage eq "F") {
+ print " ann馥: $year\n";
+ print " mois: $month\n";
+ print " jour: $day\n";
+ print " heure: $hour\n";
+ print " minute: $minute\n";
+ print " seconde: $second\n";
+ } else {
+ print " year: $year\n";
+ print " month: $month\n";
+ print " day: $day\n";
+ print " hour: $hour\n";
+ print " minute: $minute\n";
+ print " second: $second\n";
+ }
+
+ if ($year == 0 && $month == 0 && $day == 0 && $hour == 0 && $minute == 0 && $second == 0) {
+ if ($defaultlanguage eq "F") {
+ print "Vous devez entrer un ajustement avec cette commande, svp:\n";
+ print " Valeur d'ajustement (-1, 1, +1, etc...)\n";
+ print " Element modifi:\n";
+ print " a ou y: ann馥\n";
+ print " m: mois\n";
+ print " j ou d: jour\n";
+ print " h: heure\n";
+ print " mn: minute\n";
+ print " s: seconde\n";
+ print " <exemple> timeadd nomtest +1m-2mn1s-6y\n";
+ print " Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n";
+ print " et 6 ans dans le m麥e temps.\n";
+ } else {
+ print "Please give an adjustment with this command:\n";
+ print " Adjustment value (-1, 1, +1, etc...)\n";
+ print " Modified element:\n";
+ print " a or y: year\n";
+ print " m: month\n";
+ print " j or d: day\n";
+ print " h: hour\n";
+ print " mn: minute\n";
+ print " s: second\n";
+ print " <example> timeadd testname +1m-2mn1s-6y\n";
+ print " this example adds 1 month and 1 second, and substracts 2 minutes\n";
+ print " and 6 years at the same time.\n";
+ }
+ return 137;
+ }
+ if ($year > 127 || $year < -127) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement d'ann馥s correct (de -127 127), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the years (from -127 to 127).\n";
+ }
+ return 137;
+ }
+ if ($month > 255 || $month < -255) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de mois correct (de -255 255), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the months (from -255 to 255).\n";
+ }
+ return 137;
+ }
+ if ($day > 32767 || $day < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de jours correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the days (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+ if ($hour > 32767 || $hour < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement d'heures correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the hours (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+ if ($minute > 32767 || $minute < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de minutes correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the minutes (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+ if ($second > 32767 || $second < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de secondes correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the seconds (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+
+ print $so pack("va24vvvvvv", 0x7950, $userid, $year, $month, $day, $hour, $minute, $second);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7951) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(32);
+ my(@dat) = unpack("Va24V", $buf);
+ while (length($dat[1]) > 0 && substr($dat[1], length($dat[1])-1, 1) eq chr(0)) {
+ chop($dat[1]);
+ };
+ if ($dat[0] == -1 || $dat[0] == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement de la validit du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Account [$userid] validity limit changing failed. Account doesn't exist.\n";
+ }
+ } elsif ($dat[2] == 0) {
+ if ($defaultlanguage eq "F") {
+ print "Limite de validit du compte [$dat[1]][id: $dat[0]] inchang馥.\n";
+ print "Le compte a une validit illimit馥 ou\n";
+ print "la modification est impossible avec les ajustements demand駸.\n";
+ } else {
+ print "Validity limit of the account [$dat[1]][id: $dat[0]] unchanged.\n";
+ print "The account have an unlimited validity limit or\n";
+ print "the changing is impossible with the proposed adjustments.\n";
+ }
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Limite de validit du compte [$dat[1]][id: $dat[0]] chang馥 avec succ鑚 ".
+ ($dat[2] == 0 ? "en [illimit饐.\n" : "pour 黎re jusqu'au ".(POSIX::ctime($dat[2])));
+ } else {
+ print "Validity limit of the account [$dat[1]][id: $dat[0]] successfully changed ".
+ ($dat[2] == 0 ? "to [unlimited].\n" : "to be until ".(POSIX::ctime($dat[2])));
+ }
+ # localtime($dat[2]) is also possible to display instead of POSIX::ctime.
+ }
+
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Set the final date of a banishment of an account
+sub bansetaccount() {
+ my($userid, $date, $time) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple>: banset <nom_du_compte> aaaa/mm/jj [hh:mm:ss]\n";
+ print " banset <nom_du_compte> 0 (0 = d-bani)\n";
+ print " ban/banish aaaa/mm/jj hh:mm:ss <nom du compte>\n";
+ print " unban/unbanish <nom du compte>\n";
+ printf " Heure par d馭aut [hh:mm:ss]: 23:59:59\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n";
+ print " banset <account_name> 0 (0 = un-banished)\n";
+ print " ban/banish yyyy/mm/dd hh:mm:ss <account name>\n";
+ print " unban/unbanish <account name>\n";
+ printf " Default time [hh:mm:ss]: 23:59:59\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ my($year, $month, $day) = split(/[.\-\/]/, $date);
+ my($hour, $minute, $second) = split(/:/, $time);
+ if ($time eq "") {
+ $hour = 23;
+ $minute = 59;
+ $second = 59;
+ }
+ my($timestamp);
+ if ($year eq "" ||
+ ($year != 0 && ($month eq "" || $day eq "" || $hour eq "" || $minute eq "" || $second eq ""))) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n";
+ } else {
+ print "Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n";
+ }
+ return 102;
+ }
+ if ($year == 0) {
+ $timestamp = 0;
+ } else {
+ if ($year < 70) {
+ $year = $year + 100;
+ }
+ if ($year >= 1900) {
+ $year = $year - 1900;
+ }
+ if ($month < 1 || $month > 12) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un mois correct svp (entre 1 et 12).\n";
+ } else {
+ print "Please give a correct value for the month (from 1 to 12).\n";
+ }
+ return 102;
+ }
+ $month = $month - 1;
+ if ($day < 1 || $day > 31) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un jour correct svp (entre 1 et 31).\n";
+ } else {
+ print "Please give a correct value for the day (from 1 to 31).\n";
+ }
+ return 102;
+ }
+ if ((($month == 3 || $month == 5 || $month == 8 || $month == 10) && $day > 30) ||
+ ($month == 1 && $day > 29)) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un jour correct en fonction du mois svp.\n";
+ } else {
+ print "Please give a correct value for a day of this month.\n";
+ }
+ return 102;
+ }
+ if ($hour < 0 || $hour > 23) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez une heure correcte svp (entre 0 et 23).\n";
+ } else {
+ print "Please give a correct value for the hour (from 0 to 23).\n";
+ }
+ return 102;
+ }
+ if ($minute < 0 || $minute > 59) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez des minutes correctes svp (entre 0 et 59).\n";
+ } else {
+ print "Please give a correct value for the minutes (from 0 to 59).\n";
+ }
+ return 102;
+ }
+ if ($second < 0 || $second > 59) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez des secondes correctes svp (entre 0 et 59).\n";
+ } else {
+ print "Please give a correct value for the seconds (from 0 to 59).\n";
+ }
+ return 102;
+ }
+ $timestamp = POSIX::mktime($second, $minute, $hour, $day, $month, $year, 0, 0, -1); # -1: no winter/summer time modification
+ if ($timestamp == undef) {
+ if ($defaultlanguage eq "F") {
+ print "Date incorrecte.\n";
+ print "Ajoutez 0 ou une date et une heure svp (format: 0 ou aaaa/mm/jj hh:mm:ss).\n";
+ } else {
+ print "Invalid date.\n";
+ print "Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n";
+ }
+ return 102;
+ }
+ }
+
+ print $so pack("va24V", 0x794a, $userid, $timestamp);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x794b) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(32);
+ my(@dat) = unpack("Va24V", $buf);
+ while (length($dat[1]) > 0 && substr($dat[1], length($dat[1])-1, 1) eq chr(0)) {
+ chop($dat[1]);
+ };
+ if ($dat[0] != -1 && $dat[0] != 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Date finale de banissement du compte [$dat[1]][id: $dat[0]] chang馥 avec succ鑚 ".
+ ($dat[2] == 0 ? "en [d-bannie].\n" : "pour 黎re jusqu'au ".(POSIX::ctime($dat[2])));
+ } else {
+ print "Final date of banishment of the account [$dat[1]][id: $dat[0]] successfully changed ".
+ ($dat[2] == 0 ? "to [unbanished].\n" : "to be until ".(POSIX::ctime($dat[2])));
+ }
+ # localtime($dat[2]) is also possible to display instead of POSIX::ctime.
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement de la date finale de banissement du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Account [$userid] final date of banishment changing failed. Account doesn't exist.\n";
+ }
+ }
+
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Add/substract time to the final date of a banishment of an account
+sub banaddaccount() {
+ my($userid, $modif) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print " <exemple> banadd nomtest +1m-2mn1s-6y\n";
+ print " Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n";
+ print " et 6 ans dans le m麥e temps.\n";
+ } else {
+ print "Please input an account name.\n";
+ print " <example> banadd testname +1m-2mn1s-6y\n";
+ print " this example adds 1 month and 1 second, and substracts 2 minutes\n";
+ print " and 6 years at the same time.\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ my($year, $month, $day) = (0, 0 ,0);
+ my($hour, $minute, $second) = (0, 0 ,0);
+
+ $modif = lc($modif);
+ while (length($modif) > 0) {
+ my($value) = int($modif);
+ if ($value == 0) {
+ $modif = substr($modif, 1);
+ } else {
+ if (substr($modif, 0, 1) =~ /[\-\+]/) {
+ $modif = substr($modif, 1);
+ }
+ while (length($modif) > 0 && substr($modif, 0, 1) =~ /[0-9]/) {
+ $modif = substr($modif, 1);
+ }
+ if (index($modif, "s") == 0) {
+ $second = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "mn") == 0) {
+ $minute = $value;
+ $modif = substr($modif, 2);
+ } elsif (index($modif, "h") == 0) {
+ $hour = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "d") == 0 || index($modif, "j") == 0) {
+ $day = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "m") == 0) {
+ $month = $value;
+ $modif = substr($modif, 1);
+ } elsif (index($modif, "y") == 0 || index($modif, "a") == 0) {
+ $year = $value;
+ $modif = substr($modif, 1);
+ } else {
+ $modif = substr($modif, 1);
+ }
+ }
+ }
+
+ if ($defaultlanguage eq "F") {
+ print " ann馥: $year\n";
+ print " mois: $month\n";
+ print " jour: $day\n";
+ print " heure: $hour\n";
+ print " minute: $minute\n";
+ print " seconde: $second\n";
+ } else {
+ print " year: $year\n";
+ print " month: $month\n";
+ print " day: $day\n";
+ print " hour: $hour\n";
+ print " minute: $minute\n";
+ print " second: $second\n";
+ }
+
+ if ($year == 0 && $month == 0 && $day == 0 && $hour == 0 && $minute == 0 && $second == 0) {
+ if ($defaultlanguage eq "F") {
+ print "Vous devez entrer un ajustement avec cette commande, svp:\n";
+ print " Valeur d'ajustement (-1, 1, +1, etc...)\n";
+ print " Element modifi:\n";
+ print " a ou y: ann馥\n";
+ print " m: mois\n";
+ print " j ou d: jour\n";
+ print " h: heure\n";
+ print " mn: minute\n";
+ print " s: seconde\n";
+ print " <exemple> banadd nomtest +1m-2mn1s-6y\n";
+ print " Cette exemple ajoute 1 mois et 1 seconde, et soustrait 2 minutes\n";
+ print " et 6 ans dans le m麥e temps.\n";
+ } else {
+ print "Please give an adjustment with this command:\n";
+ print " Adjustment value (-1, 1, +1, etc...)\n";
+ print " Modified element:\n";
+ print " a or y: year\n";
+ print " m: month\n";
+ print " j or d: day\n";
+ print " h: hour\n";
+ print " mn: minute\n";
+ print " s: second\n";
+ print " <example> banadd testname +1m-2mn1s-6y\n";
+ print " this example adds 1 month and 1 second, and substracts 2 minutes\n";
+ print " and 6 years at the same time.\n";
+ }
+ return 137;
+ }
+ if ($year > 127 || $year < -127) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement d'ann馥s correct (de -127 127), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the years (from -127 to 127).\n";
+ }
+ return 137;
+ }
+ if ($month > 255 || $month < -255) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de mois correct (de -255 255), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the months (from -255 to 255).\n";
+ }
+ return 137;
+ }
+ if ($day > 32767 || $day < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de jours correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the days (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+ if ($hour > 32767 || $hour < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement d'heures correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the hours (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+ if ($minute > 32767 || $minute < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de minutes correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the minutes (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+ if ($second > 32767 || $second < -32767) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un ajustement de secondes correct (de -32767 32767), svp.\n";
+ } else {
+ print "Please give a correct adjustment for the seconds (from -32767 to 32767).\n";
+ }
+ return 137;
+ }
+
+ print $so pack("va24vvvvvv", 0x794c, $userid, $year, $month, $day, $hour, $minute, $second);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x794d) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(32);
+ my(@dat) = unpack("Va24V", $buf);
+ while (length($dat[1]) > 0 && substr($dat[1], length($dat[1])-1, 1) eq chr(0)) {
+ chop($dat[1]);
+ };
+ if ($dat[0] == -1 || $dat[0] == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Echec du changement de la date finale de banissement du compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Account [$userid] final date of banishment changing failed. Account doesn't exist.\n";
+ }
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Date finale de banissement du compte [$dat[1]][id: $dat[0]] chang馥 avec succ鑚 ".
+ ($dat[2] == 0 ? "en [d-bannie].\n" : "pour 黎re jusqu'au ".(POSIX::ctime($dat[2])));
+ } else {
+ print "Final date of banishment of the account [$dat[1]][id: $dat[0]] successfully changed ".
+ ($dat[2] == 0 ? "to [unbanished].\n" : "to be until ".(POSIX::ctime($dat[2])));
+ }
+ # localtime($dat[2]) is also possible to display instead of POSIX::ctime.
+ }
+
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Request to displaying information about an account (by its name)
+sub whoaccount() {
+ my($userid) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> who nomtest\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> who testname\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+
+ print $so pack("va24", 0x7952, $userid);
+ $so->flush();
+
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7953) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 122;
+ }
+ my($id2, $GM_level, $name, $sex, $count, $status, $error_message, $last_login, $last_ip, $email, $validite, $ban_date, $memo_size) = unpack("VCa24cVVa20a24a16a40VVv", readso(148));
+ my($memo) = "";
+ if ($memo_size > 0) {
+ $memo = unpack("a".$memo_size, readso($memo_size));
+ }
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ while (length($error_message) > 0 && substr($error_message, length($error_message)-1, 1) eq chr(0)) {
+ chop($error_message);
+ };
+ while (length($last_login) > 0 && substr($last_login, length($last_login)-1, 1) eq chr(0)) {
+ chop($last_login);
+ };
+ while (length($last_ip) > 0 && substr($last_ip, length($last_ip)-1, 1) eq chr(0)) {
+ chop($last_ip);
+ };
+ while (length($email) > 0 && substr($email, length($email)-1, 1) eq chr(0)) {
+ chop($email);
+ };
+ while (length($memo) > 0 && substr($memo, length($memo)-1, 1) eq chr(0)) {
+ chop($memo);
+ };
+
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Impossible de trouver le compte [$userid]. Le compte n'existe pas.\n";
+ } else {
+ print "Unabled to find the account [$userid]. Account doesn't exist.\n";
+ }
+ return 123;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Le compte [$userid] a les caract駻istiques suivantes:\n";
+ } else {
+ print "The account [$userid] is set with:\n";
+ }
+ if ($GM_level == 0) {
+ print " Id: $id2 (non-GM)\n";
+ } else {
+ if ($defaultlanguage eq "F") {
+ print " Id: $id2 (GM niveau $GM_level)\n";
+ } else {
+ print " Id: $id2 (GM level $GM_level)\n";
+ }
+ }
+ if ($defaultlanguage eq "F") {
+ print " Nom: '$name'\n";
+ print " Sexe: ".("Femme", "Male", "Serveur")[$sex]."\n";
+ } else {
+ print " Name: '$name'\n";
+ print " Sex: ".("Female", "Male", "Server")[$sex]."\n";
+ }
+ print " E-mail: $email\n";
+ if ($status == 7) {
+ print " Statut: 7 [You are Prohibited to log in until $error_message]\n";
+ } else {
+ print " Statut: $status [".(
+ ($defaultlanguage eq "F" ? "Compte Ok" : "Account OK"),
+ "Unregistered ID",
+ "Incorrect Password",
+ "This ID is expired",
+ "Rejected from Server",
+ "You have been blocked by the GM Team",
+ "Your Game's EXE file is not the latest version",
+ "You are Prohibited to log in until %s",
+ "Server is jammed due to over populated",
+ "No MSG",
+ "This ID is totally erased")[$status == 100 ? 10 : $status]."]\n";
+ }
+ if ($defaultlanguage eq "F") {
+ print " Banissement: ".($ban_date == 0 ? "non banni.\n" : "jusqu'au ".(POSIX::ctime($ban_date)));
+ print " Compteur: $count connexion".("s", "")[$count > 1 ? 0 : 1]."\n";
+ print " Derni鑽e connexion le: $last_login (ip: $last_ip)\n";
+ print " Limite de validit: ".($validite == 0 ? "illimit.\n" : "jusqu'au ".(POSIX::ctime($validite)));
+ } else {
+ print " Banishment: ".($ban_date == 0 ? "not banished.\n" : "until ".(POSIX::ctime($ban_date)));
+ print " Count: $count connection".("s", "")[$count > 1 ? 0 : 1]."\n";
+ print " Last connection at: $last_login (ip: $last_ip)\n";
+ print " Validity limit: ".($validite == 0 ? "unlimited.\n" : "until ".(POSIX::ctime($validite)));
+ }
+ print " Memo: '$memo'\n";
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Request to displaying information about an account (by its id)
+sub infoaccount() {
+ my($id) = @_;
+ if ($id < 0) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un id ayant une valeur positive svp.\n";
+ } else {
+ print "Please input a positive value for the id.\n";
+ }
+ return 136;
+ }
+
+ print $so pack("vV", 0x7954, $id);
+ $so->flush();
+
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x7953) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 122;
+ }
+ my($id2, $GM_level, $name, $sex, $count, $status, $error_message, $last_login, $last_ip, $email, $validite, $ban_date, $memo_size) = unpack("VCa24cVVa20a24a16a40VVv", readso(148));
+ my($memo) = "";
+ if ($memo_size > 0) {
+ $memo = unpack("a".$memo_size, readso($memo_size));
+ }
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ while (length($error_message) > 0 && substr($error_message, length($error_message)-1, 1) eq chr(0)) {
+ chop($error_message);
+ };
+ while (length($last_login) > 0 && substr($last_login, length($last_login)-1, 1) eq chr(0)) {
+ chop($last_login);
+ };
+ while (length($last_ip) > 0 && substr($last_ip, length($last_ip)-1, 1) eq chr(0)) {
+ chop($last_ip);
+ };
+ while (length($email) > 0 && substr($email, length($email)-1, 1) eq chr(0)) {
+ chop($email);
+ };
+ while (length($memo) > 0 && substr($memo, length($memo)-1, 1) eq chr(0)) {
+ chop($memo);
+ };
+
+ if (length($name) == 0 || $name eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Impossible de trouver le nom du compte [id: $id2]. Le compte n'existe pas.\n";
+ } else {
+ print "Unabled to find the account [id: $id2] name. Account doesn't exist.\n";
+ }
+ return 123;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Le compte [id: $id2] a les caract駻istiques suivantes:\n";
+ } else {
+ print "The account [id: $id2] is set with:\n";
+ }
+ if ($GM_level == 0) {
+ print " Id: $id2 (non-GM)\n";
+ } else {
+ if ($defaultlanguage eq "F") {
+ print " Id: $id2 (GM niveau $GM_level)\n";
+ } else {
+ print " Id: $id2 (GM level $GM_level)\n";
+ }
+ }
+ if ($defaultlanguage eq "F") {
+ print " Nom: '$name'\n";
+ print " Sexe: ".("Femme", "Male", "Serveur")[$sex]."\n";
+ } else {
+ print " Name: '$name'\n";
+ print " Sex: ".("Female", "Male", "Server")[$sex]."\n";
+ }
+ print " E-mail: $email\n";
+ if ($status == 7) {
+ print " Statut: 7 [You are Prohibited to log in until $error_message]\n";
+ } else {
+ print " Statut: $status [".(
+ ($defaultlanguage eq "F" ? "Compte Ok" : "Account OK"),
+ "Unregistered ID",
+ "Incorrect Password",
+ "This ID is expired",
+ "Rejected from Server",
+ "You have been blocked by the GM Team",
+ "Your Game's EXE file is not the latest version",
+ "You are Prohibited to log in until %s",
+ "Server is jammed due to over populated",
+ "No MSG",
+ "This ID is totally erased")[$status == 100 ? 10 : $status]."]\n";
+ }
+ if ($defaultlanguage eq "F") {
+ print " Banissement: ".($ban_date == 0 ? "non banni.\n" : "jusqu'au ".(POSIX::ctime($ban_date)));
+ print " Compteur: $count connexion".("s", "")[$count > 1 ? 0 : 1]."\n";
+ print " Derni鑽e connexion le: $last_login (ip: $last_ip)\n";
+ print " Limite de validit: ".($validite == 0 ? "illimit.\n" : "jusqu'au ".(POSIX::ctime($validite)));
+ } else {
+ print " Banishment: ".($ban_date == 0 ? "not banished.\n" : "until ".(POSIX::ctime($ban_date)));
+ print " Count: $count connection".("s", "")[$count > 1 ? 0 : 1]."\n";
+ print " Last connection at: $last_login (ip: $last_ip)\n";
+ print " Validity limit: ".($validite == 0 ? "unlimited.\n" : "until ".(POSIX::ctime($validite)));
+ }
+ print " Memo: '$memo'\n";
+ }
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Check the validity of a password
+# (Note: never send back a password with login-server!! security of passwords)
+sub checkaccount() {
+ my($userid, $passwd) = @_;
+ if ($userid eq "") {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un nom de compte svp.\n";
+ print "<exemple> check testname motdepasse\n";
+ } else {
+ print "Please input an account name.\n";
+ print "<example> check testname password\n";
+ }
+ return 136;
+ }
+ if (verify_accountname($userid) == 0) {
+ return 102;
+ }
+ if ($passwd eq "") {
+ return 134 if (($passwd = typepasswd()) eq "");
+ }
+ if (verify_password($passwd) == 0) {
+ return 131;
+ }
+ print $so pack("va24a24", 0x793a, $userid,$passwd);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x793b) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 132;
+ }
+ $buf = readso(28);
+ my($id2, $name) = unpack("Va24", $buf);
+ while (length($name) > 0 && substr($name, length($name)-1, 1) eq chr(0)) {
+ chop($name);
+ };
+ if ($id2 == -1 || $id2 == 4294967295) {
+ if ($defaultlanguage eq "F") {
+ print "Le compte [$userid] n'existe pas ou le mot de passe est incorrect.\n";
+ } else {
+ print "The account [$userid] doesn't exist or the password is incorrect.\n";
+ }
+ return 133;
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Le mot de passe donn correspond bien au compte [$name][id: $id2].\n";
+ } else {
+ print "The proposed password is correct for the account [$name][id: $id2].\n";
+ }
+ }
+ return 130;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Request to login-server to reload GM configuration file
+sub reloadGM() {
+ print $so pack("v", 0x7955);
+ $so->flush();
+ if ($defaultlanguage eq "F") {
+ print "Demande de recharger le fichier de configuration des GM envoy馥.\n";
+ print "V駻ifiez les comptes GM actuels (apr鑚 rechargement):\n";
+ } else {
+ print "Request to reload the GM configuration file sended.\n";
+ print "Check the actual GM accounts (after reloading):\n";
+ }
+ &listaccount(0, 0, 1); # 1: to list only GM
+ return 180;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Send a broadcast message
+sub sendbroadcast() {
+ my($type, $message) = @_;
+ if ($message eq "" || length($message) == 0) {
+ if ($defaultlanguage eq "F") {
+ print "Entrez un message svp.\n";
+ if ($type == 0) {
+ print "<exemple> kami un message\n";
+ } else {
+ print "<exemple> kamib un message\n";
+ }
+ } else {
+ print "Please input a message.\n";
+ if ($type == 0) {
+ print "<example> kami a message\n";
+ } else {
+ print "<example> kamib a message\n";
+ }
+ }
+ return 136;
+ }
+
+ print $so pack("vvVa".length($message), 0x794e, $type, length($message), $message);
+ $so->flush();
+ $buf = readso(2);
+ if (unpack("v", $buf) != 0x794f) {
+ if ($defaultlanguage eq "F") {
+ print "Probl鑪e de connexion au serveur (r駱onse incorrecte).\n";
+ } else {
+ print "Connection error to the server (incorrect answer).\n";
+ }
+ return 152;
+ }
+ $buf = readso(2);
+ my($answer) = unpack("v", $buf);
+ if ($answer == -1 || $answer == 65535) {
+ if ($defaultlanguage eq "F") {
+ print "Echec de l'envoi du message. Aucun server de char en ligne.\n";
+ } else {
+ print "Message sending failed. No online char-server.\n";
+ }
+ } else {
+ if ($defaultlanguage eq "F") {
+ print "Message transmis au server de logins avec succ鑚.\n";
+ } else {
+ print "Message successfully sended to login-server.\n";
+ }
+ }
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Change language of displaying
+sub changelanguage() {
+ my($language) = @_;
+ if ($language eq "" || length($language) == 0) {
+ if ($defaultlanguage == 'F') {
+ printf("Entrez une langue svp.\n");
+ printf("<exemple> language english\n");
+ printf(" language fran軋is\n");
+ } else {
+ printf("Please input a language.\n");
+ printf("<example> language english\n");
+ printf(" language fran軋is\n");
+ }
+ return 136;
+ }
+
+ $language = uc(substr($language, 0, 1));
+ if ($language =~ /^[EF]$/) {
+ $defaultlanguage = $language;
+ if ($defaultlanguage == 'F') {
+ printf("Changement de la langue d'affichage en Fran軋is.\n");
+ } else {
+ printf("Displaying language changed to English.\n");
+ }
+ } else {
+ if ($defaultlanguage == 'F') {
+ printf("Langue non param騁r馥 (langues possibles: 'Fran軋is' ou 'English').\n");
+ } else {
+ printf("Undefined language (possible languages: Fran軋is or English).\n");
+ }
+ }
+
+ return 0;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: sending 'end of connection' packet
+sub quit() {
+ print $so pack("v", 0x7532);
+ $so->flush();
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Get datas from the socket
+sub readso() {
+ my($len) = shift;
+ my($buf);
+ if (read($so, $buf, $len) < $len) {
+ if ($defaultlanguage eq "F") {
+ print "Erreur de lecture sur la Socket.\n";
+ } else {
+ print "Socket read error.\n";
+ }
+ exit(3);
+ }
+ return $buf;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Input of a password
+sub typepasswd {
+ my($passwd1, $passwd2);
+ cbreak();
+ if ($defaultlanguage eq "F") {
+ print "Entrez le mot de passe > "; $passwd1 = <STDIN>; chomp($passwd1); print "\n";
+ print "R-entrez le mot de passe > "; $passwd2 = <STDIN>; chomp($passwd2); print "\n";
+ } else {
+ print "Type the password > "; $passwd1 = <STDIN>; chomp($passwd1); print "\n";
+ print "Verify the password > "; $passwd2 = <STDIN>; chomp($passwd2); print "\n";
+ }
+ cooked();
+ if ($passwd1 ne $passwd2) {
+ if ($defaultlanguage eq "F") {
+ print "Erreur de v駻ification du mot de passe: Saisissez le m麥e mot de passe svp.\n";
+ } else {
+ print "Password verification failed. Please input same password.\n";
+ }
+ return "";
+ }
+ return $passwd1;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Return ordonal text of a number
+sub makeordinal {
+ my($c) = shift;
+ if ($defaultlanguage eq "F") {
+ if ($c < 1) {
+ return $c;
+ }
+ return $c.("er", "鑪e")[$c == 1 ? 0 : 1];
+ } else {
+ if ($c % 10 < 4 && $c % 10 != 0 && ($c < 10 || $c > 20)) {
+ return $c.("st","nd","rd")[$c % 10 - 1];
+ }
+ return $c."th";
+ }
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Test of the validity of an account name (return 0 if incorrect, and 1 if ok)
+sub verify_accountname {
+ my($account_name) = @_; # Get the account_name
+ if ($account_name =~ /[\x00-\x1f]/) { # remove control char
+ my($c) = length($`) + 1;
+ if ($defaultlanguage eq "F") {
+ print "Caract鑽e interdit trouv dans le nom du compte (".makeordinal($c)." caract鑽e).\n";
+ } else {
+ print "Illegal character found in the account name (".makeordinal($c)." character).\n";
+ }
+ return 0;
+ }
+ if (length($account_name) < 4) {
+ if ($defaultlanguage eq "F") {
+ print "Nom du compte trop court. Entrez un nom de compte de 4-23 caract鑽es.\n";
+ } else {
+ print "Account name is too short. Please input an account name of 4-23 bytes.\n";
+ }
+ return 0;
+ }
+ if (length($account_name) > 23) {
+ if ($defaultlanguage eq "F") {
+ print "Nom du compte trop long. Entrez un nom de compte de 4-23 caract鑽es.\n";
+ } else {
+ print "Account name is too long. Please input an account name of 4-23 bytes.\n";
+ }
+ return 0;
+ }
+ return 1;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Test of the validity of password (return 0 if incorrect, and 1 if ok)
+sub verify_password {
+ my($password) = @_; # Get the password
+ if ($password =~ /[\x00-\x1f]/) {
+ my($c) = length($`) + 1;
+ if ($defaultlanguage eq "F") {
+ print "Caract鑽e interdit trouv dans le mot de passe (".makeordinal($c)." caract鑽e).\n";
+ } else {
+ print "Illegal character found in the password (".makeordinal($c)." character).\n";
+ }
+ return 0;
+ }
+ if (length($password) < 4) {
+ if ($defaultlanguage eq "F") {
+ print "Mot de passe trop court. Entrez un mot de passe de 4-23 caract鑽es.\n";
+ } else {
+ print "Password is too short. Please input a password of 4-23 bytes.\n";
+ }
+ return 0;
+ }
+ if (length($password) > 23) {
+ if ($defaultlanguage eq "F") {
+ print "Mot de passe trop long. Entrez un mot de passe de 4-23 caract鑽es.\n";
+ } else {
+ print "Password is too long. Please input a password of 4-23 bytes.\n";
+ }
+ return 0;
+ }
+ return 1;
+}
+
+#--------------------------------------------------------------------------
+
+# Sub-function: Test of the validity of an e-mail (return 0 if incorrect, and 1 if ok)
+sub verify_email {
+ my($email) = @_; # Get the e-mail
+ # To ignore a '.' before the @ (wanadoo, a provider, do that)
+ $email =~ s/\.\@/\@/;
+ # If the e-mail is void, it's not correct -> return 0
+ if ($email eq '') {
+ return(0);
+ }
+ # If the e-mail have no "@", it's not correct -> return 0
+ if ($email !~ /\@/) {
+ return(0);
+ }
+ # If the e-mail have a ",", a space, a tab or a ";", it's not correct -> return 0
+ if ($email =~ /[\,|\s|\;]/) {
+ return(0)
+ };
+ # IF
+ # (the e-mail contains 2 "@", or ".." or "@." or starts or finishes by a ".")
+ # OR IF
+ # (the e-mail doesn't contain "@localhost" AND
+ # - it doesn't contain characters followed by "@" itself followed by letters itself followed by "." and 2 or more letters
+ # - or an IP address)
+ # -> so, it's not good ! (finish !)
+ if ($email =~ /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)|(\.$)/ ||
+ ($email !~ /^.+\@localhost$/ &&
+ $email !~ /^.+\@\[?(\w|[-.])+\.[a-zA-Z]{2,3}|[0-9]{1,3}\]?$/)) {
+ return(0); # non-valid email
+ } else {
+ # If not, the e-email address is correct
+ return(1); # valid email
+ }
+}
\ No newline at end of file diff --git a/src/tool/mapcheck.sh b/src/tool/mapcheck.sh new file mode 100644 index 0000000..337884c --- /dev/null +++ b/src/tool/mapcheck.sh @@ -0,0 +1,34 @@ +#!/bin/sh +echo "============================================" +echo "= map server status checker... =" +echo "============================================" +./map-server.exe & +sleep 40 + +while [ 0 ] +do + pcpu=` top -n 1| grep map-server | awk '{print $9}' | awk 'BEGIN{FS="."} {print $1}' ` + if [ "$pcpu" -gt 80 ];then + echo "============================================" + echo "map server is more than 80% (now $pcpu%)" + echo "============================================" + ppid=` ps -a | grep map-server | awk '{print $1}' ` + kill $ppid + ./map-server.exe & + sleep 40 + else + pmapct=` ps -a| grep map-server | wc -l ` + if [ "$pmapct" -eq 0 ];then + echo "============================================" + echo "map server is not running..." + echo "restart map server..." + echo "============================================" + ./map-server.exe & + sleep 40 + #echo "test" + else + echo "map server is ok (now $pcpu%)..." + sleep 5 + fi + fi +done
\ No newline at end of file diff --git a/src/tool/mapchecker.sh b/src/tool/mapchecker.sh new file mode 100644 index 0000000..7250c34 --- /dev/null +++ b/src/tool/mapchecker.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +athena_dir="/home/athena/658/" + +while [ true ] ; do + +if [ ` ps fauxw | grep map-server | grep -v grep | wc -l ` -eq 0 ];then + #echo `date` " -- map-server crashed - restarting" + echo `date` " -- map-server crashed - restarting" >> /var/log/athena_status.log + killall -9 map-server + cd $athena_dir + nohup ./map-server ./conf/map_athena.conf ./inter_athena.conf & + sleep 240 + #sleep 40 #for fast pc's remove the "#" at the beginning of the line and delete the line above +fi + + +if [ ` ps fauxw | grep map-server | grep -v grep | awk '{print $3}' | awk 'BEGIN{FS="."} {print $1}' ` -gt 10 ];then + #echo `date` " -- mapserver cpuload over 10 - restarting" + echo `date` " -- mapserver cpuload over 10 - restarting" >> /var/log/athena_status.log + killall -9 map-server + cd $athena_dir + nohup ./map-server ./conf/map_athena.conf ./inter_athena.conf & + sleep 240 + #sleep 40 #for fast pc's remove the "#" at the beginning of the line and delete the line above + #echo `date` " -- restarted" + echo `date` " -- restarted" >> /var/log/athena_status.log +fi + +if [ ` ps fauxw | grep char-server | grep -v grep | wc -l ` -eq 0 ];then + #echo `date` " -- char server crashed - restarting" + echo `date` " -- char server crashed - restarting" >> /var/log/athena_status.log + killall -9 char-server + cd $athena_dir + nohup ./char-server ./conf/char_athena.conf ./conf/inter_athena.conf & + #echo `date` " -- restarted" + echo `date` " -- restarted" >> /var/log/athena_status.log + +fi + +if [ ` ps fauxw | grep login-server | grep -v grep | wc -l ` -eq 0 ];then + #echo `date` " -- login server crashed - restarting" + echo `date` " -- login server crashed - restarting" >> /var/log/athena_status.log + killall -9 login-server + cd $athena_dir + nohup ./login-server ./conf/login_athena.conf & + #echo `date` " -- restarted" + echo `date` " -- restarted" >> /var/log/athena_status.log + +fi + + +#echo `date` " -- everything is fine" +echo `date` " -- everything is fine" >> /var/log/athena_status.log +sleep 30 +done diff --git a/src/txt-converter/char/GNUmakefile b/src/txt-converter/char/GNUmakefile new file mode 100644 index 0000000..b88df26 --- /dev/null +++ b/src/txt-converter/char/GNUmakefile @@ -0,0 +1,13 @@ +all: char-converter
+sql: char-converter
+
+COMMON_OBJ = ../../common/core.o ../../common/socket.o ../../common/timer.o ../../common/db.o ../../common/malloc.o
+
+char-converter: char-converter.o strlib.o $(COMMON_OBJ)
+ $(CC) -o ../../../$@ $^ $(LIB_S)
+
+char-converter.o: char-converter.c char.h strlib.h
+strlib.o: strlib.c strlib.h
+clean:
+ rm -f *.o ../../../char-converter
+
diff --git a/src/txt-converter/char/Makefile b/src/txt-converter/char/Makefile new file mode 100644 index 0000000..b88df26 --- /dev/null +++ b/src/txt-converter/char/Makefile @@ -0,0 +1,13 @@ +all: char-converter
+sql: char-converter
+
+COMMON_OBJ = ../../common/core.o ../../common/socket.o ../../common/timer.o ../../common/db.o ../../common/malloc.o
+
+char-converter: char-converter.o strlib.o $(COMMON_OBJ)
+ $(CC) -o ../../../$@ $^ $(LIB_S)
+
+char-converter.o: char-converter.c char.h strlib.h
+strlib.o: strlib.c strlib.h
+clean:
+ rm -f *.o ../../../char-converter
+
diff --git a/src/txt-converter/char/char-converter.c b/src/txt-converter/char/char-converter.c new file mode 100644 index 0000000..44f6d29 --- /dev/null +++ b/src/txt-converter/char/char-converter.c @@ -0,0 +1,842 @@ +// $Id: char-converter.c,v 1.1.1.1 2004/09/10 17:45:03 MagicalTux Exp $ +// original : char2.c 2003/03/14 11:58:35 Rev.1.5 + +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> + + +#define STORAGE_MEMINC 16 + +#include "char.h" +#include "strlib.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +char pet_txt[256]="save/pet.txt"; +char storage_txt[256]="save/storage.txt"; + +MYSQL mysql_handle; +MYSQL_RES* sql_res ; +MYSQL_ROW sql_row ; +int sql_fields, sql_cnt; +char tmp_sql[65535]; + +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"; + +struct storage *storage=NULL; + +struct mmo_map_server server[MAX_MAP_SERVERS]; +int server_fd[MAX_MAP_SERVERS]; + +int login_fd; +char userid[24]; +char passwd[24]; +char server_name[20]; +char login_ip_str[16]; +int login_port = 6900; +char char_ip_str[16]; +int char_ip; +int char_port = 6121; +int char_maintenance; +int char_new; +char char_txt[256]; + +char t_name[256]; + +#define CHAR_STATE_WAITAUTH 0 +#define CHAR_STATE_AUTHOK 1 +struct char_session_data{ + int state; + int account_id, login_id1, login_id2, sex; + int found_char[9]; +}; + +#define AUTH_FIFO_SIZE 256 +struct { + int account_id, char_id, login_id1, char_pos, delflag, sex; +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos=0; + +int char_id_count=100000; +struct mmo_charstatus *char_dat; +int char_num, char_max; +int max_connect_user=0; +int autosave_interval=DEFAULT_AUTOSAVE_INTERVAL; + +// テハア タァト。(conf ニトタマキホコホナヘ タ郛ウチ、 ー。エノ) +struct point start_point={"new_1-1.gat", 53,111}; + + + +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, 24); + 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_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 tmp_sql[65535]; + MYSQL_RES* sql_res ; + MYSQL_ROW sql_row ; + + 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 `pet` WHERE `pet_id`='%d'",pet_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + sql_res = mysql_store_result(&mysql_handle) ; + sql_row = mysql_fetch_row(sql_res); //row fetching + if (!sql_row) //no row -> insert + sprintf(tmp_sql,"INSERT INTO `pet` (`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')", + p->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); + else //row reside -> updating + sprintf(tmp_sql, "UPDATE `pet` 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'", + 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); + mysql_free_result(sql_res) ; //resource free + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + + printf ("pet dump success! - %d:%s\n", pet_id, p->name); + + return 0; +} + +int storage_tosql(int account_id,struct storage *p){ + // id -> DB dump + // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} + int i,j; + + j=0; + + //printf ("starting storage dump to DB - id: %d\n", account_id); + + //delete old data. + sprintf(tmp_sql,"DELETE FROM `storage` WHERE `account_id`='%d'",account_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + + //printf ("all storage item was deleted ok\n"); + + for(i=0;i<MAX_STORAGE;i++) { + //printf ("save storage num: %d (%d:%d)\n",i, p->storage[i].nameid , p->storage[i].amount); + + if( (p->storage[i].nameid) && (p->storage[i].amount) ){ + sprintf(tmp_sql,"INSERT INTO `storage` (`account_id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`card0`,`card1`,`card2`,`card3`,`broken`) VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + p->account_id, p->storage[i].nameid, p->storage[i].amount, p->storage[i].equip, + p->storage[i].identify, p->storage[i].refine, p->storage[i].attribute, + p->storage[i].card[0], p->storage[i].card[1], p->storage[i].card[2], p->storage[i].card[3], p->storage[i].broken ); + //printf ("%s\n",tmp_sql); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error - %s\n", mysql_error(&mysql_handle) ); + } + j++; + } + } + + printf ("storage dump to DB - id: %d (total: %d)\n", account_id, j); + return 0; +} +// char to storage +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 0; + if(str[next]=='\n' || str[next]=='\r') + return 1; + next++; + for(i=0;str[next] && str[next]!='\t';i++){ + if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { + 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]; + p->storage[i].broken = tmp_int[11]; + 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]; + p->storage[i].broken = 0; + next += len; + if (str[next] == ' ') + next++; + } + + else return 0; + } + return 1; +} + +///////////////////////////////// +int mmo_char_fromstr(char *str, struct mmo_charstatus *p) { + int tmp_int[256]; + int set, next, len, i; + + // initilialise character + memset(p, '\0', sizeof(struct mmo_charstatus)); + + // If it's not char structure of version 1008 and after + if ((set = sscanf(str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%[^,],%d,%d\t%[^,],%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_int[6], &tmp_int[7], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], &tmp_int[39], &next)) != 43) { + tmp_int[39] = 0; // partner id + // If not char structure from version 384 to 1007 + if ((set = sscanf(str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%[^,],%d,%d\t%[^,],%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_int[6], &tmp_int[7], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], &tmp_int[26], + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], &next)) != 42) { + // It's char structure of a version before 384 + tmp_int[26] = 0; // pet id + set = sscanf(str, "%d\t%d,%d\t%[^\t]\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d,%d\t%d,%d,%d,%d,%d,%d\t%d,%d" + "\t%d,%d,%d\t%d,%d\t%d,%d,%d\t%d,%d,%d,%d,%d" + "\t%[^,],%d,%d\t%[^,],%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], p->name, // + &tmp_int[3], &tmp_int[4], &tmp_int[5], + &tmp_int[6], &tmp_int[7], &tmp_int[8], + &tmp_int[9], &tmp_int[10], &tmp_int[11], &tmp_int[12], + &tmp_int[13], &tmp_int[14], &tmp_int[15], &tmp_int[16], &tmp_int[17], &tmp_int[18], + &tmp_int[19], &tmp_int[20], + &tmp_int[21], &tmp_int[22], &tmp_int[23], // + &tmp_int[24], &tmp_int[25], // + &tmp_int[27], &tmp_int[28], &tmp_int[29], + &tmp_int[30], &tmp_int[31], &tmp_int[32], &tmp_int[33], &tmp_int[34], + p->last_point.map, &tmp_int[35], &tmp_int[36], // + p->save_point.map, &tmp_int[37], &tmp_int[38], &next); + set += 2; + //printf("char: old char data ver.1\n"); + // Char structure of version 1007 or older + } else { + set++; + //printf("char: old char data ver.2\n"); + } + // Char structure of version 1008+ + } else { + //printf("char: new char data ver.3\n"); + } + if (set != 43) + return 0; + + p->char_id = tmp_int[0]; + p->account_id = tmp_int[1]; + p->char_num = tmp_int[2]; + p->class = tmp_int[3]; + p->base_level = tmp_int[4]; + p->job_level = tmp_int[5]; + p->base_exp = tmp_int[6]; + p->job_exp = tmp_int[7]; + p->zeny = tmp_int[8]; + p->hp = tmp_int[9]; + p->max_hp = tmp_int[10]; + p->sp = tmp_int[11]; + p->max_sp = tmp_int[12]; + p->str = tmp_int[13]; + p->agi = tmp_int[14]; + p->vit = tmp_int[15]; + p->int_ = tmp_int[16]; + p->dex = tmp_int[17]; + p->luk = tmp_int[18]; + p->status_point = tmp_int[19]; + p->skill_point = tmp_int[20]; + p->option = tmp_int[21]; + p->karma = tmp_int[22]; + p->manner = tmp_int[23]; + p->party_id = tmp_int[24]; + p->guild_id = tmp_int[25]; + p->pet_id = tmp_int[26]; + p->hair = tmp_int[27]; + p->hair_color = tmp_int[28]; + p->clothes_color = tmp_int[29]; + p->weapon = tmp_int[30]; + p->shield = tmp_int[31]; + p->head_top = tmp_int[32]; + p->head_mid = tmp_int[33]; + p->head_bottom = tmp_int[34]; + p->last_point.x = tmp_int[35]; + p->last_point.y = tmp_int[36]; + p->save_point.x = tmp_int[37]; + p->save_point.y = tmp_int[38]; + p->partner_id = tmp_int[39]; + + // Some checks + for(i = 0; i < char_num; i++) { + if (char_dat[i].char_id == p->char_id) { + printf("\033[1;31mmmo_auth_init: ******Error: a character has an identical id to another.\n"); + printf(" character id #%d -> new character not readed.\n", p->char_id); + printf(" Character saved in log file.\033[0m\n"); + return -1; + } else if (strcmp(char_dat[i].name, p->name) == 0) { + printf("\033[1;31mmmo_auth_init: ******Error: character name already exists.\n"); + printf(" character name '%s' -> new character not readed.\n", p->name); + printf(" Character saved in log file.\033[0m\n"); + return -2; + } + } + + if (str[next] == '\n' || str[next] == '\r') + return 1; // 新規データ + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + set = sscanf(str+next, "%[^,],%d,%d%n", p->memo_point[i].map, &tmp_int[0], &tmp_int[1], &len); + if (set != 3) + return -3; + p->memo_point[i].x = tmp_int[0]; + p->memo_point[i].y = tmp_int[1]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { + // do nothing, it's ok + } else if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) { + tmp_int[11] = 0; // broken doesn't exist in this version -> 0 + } else // invalid structure + return -4; + p->inventory[i].id = tmp_int[0]; + p->inventory[i].nameid = tmp_int[1]; + p->inventory[i].amount = tmp_int[2]; + p->inventory[i].equip = tmp_int[3]; + p->inventory[i].identify = tmp_int[4]; + p->inventory[i].refine = tmp_int[5]; + p->inventory[i].attribute = tmp_int[6]; + p->inventory[i].card[0] = tmp_int[7]; + p->inventory[i].card[1] = tmp_int[8]; + p->inventory[i].card[2] = tmp_int[9]; + p->inventory[i].card[3] = tmp_int[10]; + p->inventory[i].broken = tmp_int[11]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[11], &len) == 12) { + // do nothing, it's ok + } else if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n", + &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], + &tmp_int[4], &tmp_int[5], &tmp_int[6], + &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) { + tmp_int[11] = 0; // broken doesn't exist in this version -> 0 + } else // invalid structure + return -5; + p->cart[i].id = tmp_int[0]; + p->cart[i].nameid = tmp_int[1]; + p->cart[i].amount = tmp_int[2]; + p->cart[i].equip = tmp_int[3]; + p->cart[i].identify = tmp_int[4]; + p->cart[i].refine = tmp_int[5]; + p->cart[i].attribute = tmp_int[6]; + p->cart[i].card[0] = tmp_int[7]; + p->cart[i].card[1] = tmp_int[8]; + p->cart[i].card[2] = tmp_int[9]; + p->cart[i].card[3] = tmp_int[10]; + p->cart[i].broken = tmp_int[11]; + next += len; + if (str[next] == ' ') + next++; + } + + next++; + + for(i = 0; str[next] && str[next] != '\t'; i++) { + set = sscanf(str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len); + if (set != 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実装以前のathena.txt互換のため一応'\n'チェック + set = sscanf(str + next, "%[^,],%d%n", p->global_reg[i].str, &p->global_reg[i].value, &len); + if (set != 2) { + // because some scripts are not correct, the str can be "". So, we must check that. + // If it's, we must not refuse the character, but just this REG value. + // Character line will have something like: nov_2nd_cos,9 ,9 nov_1_2_cos_c,1 (here, ,9 is not good) + if (str[next] == ',' && sscanf(str + next, ",%d%n", &p->global_reg[i].value, &len) == 1) + i--; + else + return -7; + } + next += len; + if (str[next] == ' ') + next++; + } + p->global_reg_num = i; + + return 1; +} + +//========================================================================================================== +int mmo_char_tosql(int char_id, struct mmo_charstatus *p){ + int i,save_flag; + + save_flag = char_id; + printf("request save char data... (%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`) + sprintf(tmp_sql ,"INSERT INTO `char` SET `char_id`='%d', `account_id`='%d', `char_num`='%d', `name`='%s', `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'", + 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->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, + p->last_point.map, p->last_point.x, p->last_point.y, + p->save_point.map, p->save_point.x, p->save_point.y, p->partner_id + ); + + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (update `char`)- %s\n", mysql_error(&mysql_handle) ); + } + + //`memo` (`memo_id`,`char_id`,`map`,`x`,`y`) + sprintf(tmp_sql,"DELETE FROM `memo` WHERE `char_id`='%d'",char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `memo`)- %s\n", mysql_error(&mysql_handle) ); + } + + //insert here. + for(i=0;i<10;i++){ + if(p->memo_point[i].map[0]){ + sprintf(tmp_sql,"INSERT INTO `memo`(`char_id`,`map`,`x`,`y`) VALUES ('%d', '%s', '%d', '%d')", + char_id, p->memo_point[i].map, p->memo_point[i].x, p->memo_point[i].y); + if(mysql_query(&mysql_handle, tmp_sql) ) + printf("DB server Error (insert `memo`)- %s\n", mysql_error(&mysql_handle) ); + } + } + //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken`) + sprintf(tmp_sql,"DELETE FROM `inventory` WHERE `char_id`='%d'",char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `inventory`)- %s\n", mysql_error(&mysql_handle) ); + } + + //insert here. + for(i=0;i<MAX_INVENTORY;i++){ + if(p->inventory[i].nameid){ + sprintf(tmp_sql,"INSERT INTO `inventory`(`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken`)" + " VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", + p->inventory[i].id, char_id,p->inventory[i].nameid, p->inventory[i].amount, p->inventory[i].equip, + p->inventory[i].identify, p->inventory[i].refine, p->inventory[i].attribute, + p->inventory[i].card[0], p->inventory[i].card[1], p->inventory[i].card[2], p->inventory[i].card[3], p->inventory[i].broken); + if(mysql_query(&mysql_handle, tmp_sql) ) + printf("DB server Error (insert `inventory`)- %s\n", mysql_error(&mysql_handle) ); + } + } + + //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken`) + sprintf (tmp_sql, "DELETE FROM `cart_inventory` WHERE `char_id`='%d'", char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `cart_inventory`)- %s\n", mysql_error(&mysql_handle) ); + } + + //insert here. + for(i=0;i<MAX_CART;i++){ + if(p->cart[i].nameid){ + sprintf(tmp_sql,"INSERT INTO `cart_inventory`(`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `broken`)" + " VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d' )", + p->cart[i].id, char_id, p->cart[i].nameid, p->cart[i].amount, p->cart[i].equip, + p->cart[i].identify, p->cart[i].refine, p->cart[i].attribute, + p->cart[i].card[0], p->cart[i].card[1], p->cart[i].card[2], p->cart[i].card[3], p->cart[i].broken ); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (insert `cart_inventory`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + + + //`skill` (`char_id`, `id`, `lv`) + sprintf(tmp_sql,"DELETE FROM `skill` WHERE `char_id`='%d'",char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `skill`)- %s\n", mysql_error(&mysql_handle) ); + } + + //insert here. + for(i=0;i<MAX_SKILL;i++){ + if(p->skill[i].id){ + if (p->skill[i].id && p->skill[i].flag!=1) { + sprintf(tmp_sql,"INSERT INTO `skill`(`char_id`,`id`, `lv`) VALUES ('%d', '%d', '%d')", + char_id, p->skill[i].id, (p->skill[i].flag==0)?p->skill[i].lv:p->skill[i].flag-2); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (insert `skill`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + } + //`global_reg_value` (`char_id`, `str`, `value`) + sprintf(tmp_sql,"DELETE FROM `global_reg_value` WHERE `char_id`='%d'",char_id); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (delete `global_reg_value`)- %s\n", mysql_error(&mysql_handle) ); + } + + //insert here. + for(i=0;i<p->global_reg_num;i++){ + if(p->global_reg[i].value !=0){ + sprintf(tmp_sql,"INSERT INTO `global_reg_value` (`char_id`, `str`, `value`) VALUES ('%d', '%s','%d')", + char_id, p->global_reg[i].str, p->global_reg[i].value); + if(mysql_query(&mysql_handle, tmp_sql) ) { + printf("DB server Error (insert `global_reg_value`)- %s\n", mysql_error(&mysql_handle) ); + } + } + } + + printf("saving char is done... (%d)\n",char_id); + save_flag = 0; + + return 0; +} +//========================================================================================================== + +int mmo_char_init(void){ + char line[65536]; + struct s_pet *p; + int ret; + int i=0,set,tmp_int[2], c= 0; + char input; + FILE *fp; + + + //DB connection initialized + mysql_init(&mysql_handle); + printf("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 + printf("%s\n",mysql_error(&mysql_handle)); + exit(1); + } + else { + printf ("connect success! (inter server)\n"); + } + + + + printf("Warning : Make sure you backup your databases before continuing!\n"); + printf("\nDo you wish to convert your Character Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y'){ + printf("\nConverting Character Database...\n"); + fp=fopen("save/athena.txt","r"); + char_dat=malloc(sizeof(char_dat[0])*256); + char_max=256; + if(fp==NULL) + return 0; + while(fgets(line, 65535, fp)){ + if(char_num>=char_max){ + char_max+=256; + char_dat=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]); + if(ret){ + mmo_char_tosql(char_dat[char_num].char_id , &char_dat[char_num]); + printf ("convert %d -> DB\n",char_dat[char_num].char_id); + if(char_dat[char_num].char_id>=char_id_count) + char_id_count=char_dat[char_num].char_id+1; + char_num++; + } + } + printf("char data convert end\n"); + fclose(fp); + } + + while(getchar() != '\n'); + printf("\nDo you wish to convert your Storage Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') { + printf("\nConverting Storage Database...\n"); + fp=fopen(storage_txt,"r"); + if(fp==NULL){ + printf("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=malloc(sizeof(struct storage)); + }else{ + 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("\nDo you wish to convert your Pet Database to SQL? (y/n) : "); + input=getchar(); + if(input == 'y' || input == 'Y') { + printf("\nConverting Pet Database...\n"); + if( (fp=fopen(pet_txt,"r")) ==NULL ) + return 1; + + p=malloc(sizeof(struct s_pet)); + while(fgets(line, sizeof(line), fp)){ + if(p==NULL){ + printf("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{ + printf("int_pet: broken data [%s] line %d\n", pet_txt, c); + } + c++; + } + fclose(fp); + } + + return 0; +} +int inter_config_read(const char *cfgName) { + printf ("start reading interserver configuration: %s\n",cfgName); + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + if(fp==NULL){ + printf("file not found: %s\n", cfgName); + return 1; + } + while(fgets(line, 1020, fp)){ + i=sscanf(line,"%[^:]:%s", w1, w2); + if(i!=2) + continue; + if(strcmpi(w1,"storage_txt")==0){ + printf ("set storage_txt : %s\n",w2); + strncpy(storage_txt, w2, sizeof(storage_txt)); + } else if(strcmpi(w1,"pet_txt")==0){ + printf ("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); + 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); + } + } + fclose(fp); + + printf ("success reading interserver configuration\n"); + + return 0; +} + +int char_config_read(const char *cfgName) { + printf ("start reading interserver configuration: %s\n",cfgName); + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + if(fp==NULL){ + printf("file not found: %s\n", cfgName); + return 1; + } + + while(fgets(line, 1020, fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + + i=sscanf(line,"%[^:]:%s", w1, w2); + if(i!=2) + continue; + if(strcmpi(w1,"char_txt")==0){ + printf ("set char_txt : %s\n",w2); + strcpy(char_txt, w2); + } + } + fclose(fp); + printf("reading configure done.....\n"); + + 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_cfgName); + + mmo_char_init(); + printf ("all conversion success!\n"); + exit (0); + return 0; +} + + diff --git a/src/txt-converter/char/char.h b/src/txt-converter/char/char.h new file mode 100644 index 0000000..4712b4d --- /dev/null +++ b/src/txt-converter/char/char.h @@ -0,0 +1,38 @@ +#include "../../common/core.h" +#include "../../common/socket.h" +#include "../../common/timer.h" +#include "../common/mmo.h" +#include "../../common/version.h" +#include "../../common/db.h" + +#ifndef _CHAR_H_ +#define _CHAR_H_ + +#define MAX_MAP_SERVERS 30 + +#define CHAR_CONF_NAME "conf/char_athena.conf" + +#define UNKNOWN_CHAR_NAME "Unknown" + +#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 + +struct mmo_map_server{ + long ip; + short port; + int users; + char map[MAX_MAP_PER_SERVER][16]; +}; + +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); + +extern int autosave_interval; + +#endif + +#include "inter.h" +#include "int_pet.h" +#include "int_guild.h" +#include "int_party.h" +#include "int_storage.h" diff --git a/src/txt-converter/char/int_guild.h b/src/txt-converter/char/int_guild.h new file mode 100644 index 0000000..2ea8594 --- /dev/null +++ b/src/txt-converter/char/int_guild.h @@ -0,0 +1,10 @@ +#ifndef _INT_GUILD_H_ +#define _INT_GUILD_H_ + +int inter_guild_init(); +int inter_guild_save(); +int inter_guild_parse_frommap(int fd); + +extern char guild_txt[256]; + +#endif diff --git a/src/txt-converter/char/int_party.h b/src/txt-converter/char/int_party.h new file mode 100644 index 0000000..036db1a --- /dev/null +++ b/src/txt-converter/char/int_party.h @@ -0,0 +1,11 @@ +#ifndef _INT_PARTY_H_ +#define _INT_PARTY_H_ + +int inter_party_init(); +int inter_party_save(); + +int inter_party_parse_frommap(int fd); + +extern char party_txt[256]; + +#endif diff --git a/src/txt-converter/char/int_pet.h b/src/txt-converter/char/int_pet.h new file mode 100644 index 0000000..27ba4fc --- /dev/null +++ b/src/txt-converter/char/int_pet.h @@ -0,0 +1,12 @@ +#ifndef _INT_PET_H_ +#define _INT_PET_H_ + +int inter_pet_init(); +int inter_pet_save(); +int inter_pet_delete(int pet_id); + +int inter_pet_parse_frommap(int fd); + +//extern char pet_txt[256]; + +#endif diff --git a/src/txt-converter/char/int_storage.h b/src/txt-converter/char/int_storage.h new file mode 100644 index 0000000..3572ae5 --- /dev/null +++ b/src/txt-converter/char/int_storage.h @@ -0,0 +1,11 @@ +#ifndef _INT_STORAGE_H_ +#define _INT_STORAGE_H_ + +int inter_storage_init(); +int inter_storage_save(); + +int inter_storage_parse_frommap(int fd); + +//extern char storage_txt[256]; + +#endif diff --git a/src/txt-converter/char/inter.h b/src/txt-converter/char/inter.h new file mode 100644 index 0000000..ee39944 --- /dev/null +++ b/src/txt-converter/char/inter.h @@ -0,0 +1,28 @@ +#ifndef _INTER_H_ +#define _INTER_H_ + +int inter_init(const char *file); +int inter_save(); +int inter_parse_frommap(int fd); + +int inter_check_length(int fd,int length); + +#define inter_cfgName "conf/inter_athena.conf" + + +//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 int db_server_port; +extern char db_server_ip[16]; +extern char db_server_id[32]; +extern char db_server_pw[32]; +extern char db_server_logindb[32]; + +#endif diff --git a/src/txt-converter/char/strlib.c b/src/txt-converter/char/strlib.c new file mode 100644 index 0000000..60803c1 --- /dev/null +++ b/src/txt-converter/char/strlib.c @@ -0,0 +1,66 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "strlib.h" + +//----------------------------------------------- +// string lib. +unsigned char* jstrescape (unsigned char* pt) { + //copy from here + unsigned char * ptr; + int i =0, j=0; + + //copy string to temporary + ptr = malloc(J_MAX_MALLOC_SIZE); + strcpy (ptr,pt); + + while (ptr[i] != '\0') { + switch (ptr[i]) { + case '\'': + pt[j++] = '\\'; + pt[j++] = ptr[i++]; + break; + default: + pt[j++] = ptr[i++]; + } + } + pt[j++] = '\0'; + free (ptr); + return (unsigned char*) &pt[0]; +} + +unsigned char* jstrescapecpy (unsigned char* pt,unsigned char* spt) { + //copy from here + int i =0, j=0; + + while (spt[i] != '\0') { + switch (spt[i]) { + case '\'': + pt[j++] = '\\'; + pt[j++] = spt[i++]; + break; + default: + pt[j++] = spt[i++]; + } + } + pt[j++] = '\0'; + return (unsigned char*) &pt[0]; +} +int jmemescapecpy (unsigned char* pt,unsigned 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; + default: + pt[j++] = spt[i++]; + } + } + // copy size is 0 ~ (j-1) + return j; +} diff --git a/src/txt-converter/char/strlib.h b/src/txt-converter/char/strlib.h new file mode 100644 index 0000000..442cfac --- /dev/null +++ b/src/txt-converter/char/strlib.h @@ -0,0 +1,9 @@ +#ifndef _J_STR_H_ +#define _J_STR_H_ +#define J_MAX_MALLOC_SIZE 65535 +//string functions. +//code by Jioh L. Jung +unsigned char* jstrescape (unsigned char* pt); +unsigned char* jstrescapecpy (unsigned char* pt,unsigned char* spt); +int jmemescapecpy (unsigned char* pt,unsigned char* spt, int size); +#endif diff --git a/src/txt-converter/common/mmo.h b/src/txt-converter/common/mmo.h new file mode 100644 index 0000000..df42783 --- /dev/null +++ b/src/txt-converter/common/mmo.h @@ -0,0 +1,280 @@ +// Original : mmo.h 2003/03/14 12:07:02 Rev.1.7 + +#ifndef _MMO_H_ +#define _MMO_H_ + +#include <time.h> + +#ifdef CYGWIN +// txtやlogなどの書き出すファイルの改行コード +#define RETCODE "\r\n" // (CR/LF:Windows系) +#else +#define RETCODE "\n" // (LF:Unix系) +#endif + +#define FIFOSIZE_SERVERLINK 128*1024 + +#define MAX_MAP_PER_SERVER 512 +#define MAX_INVENTORY 100 +#define MAX_AMOUNT 30000 +#define MAX_ZENY 1000000000 // 1G zeny +#define MAX_CART 100 +#define MAX_SKILL 450 +#define GLOBAL_REG_NUM 96 +#define ACCOUNT_REG_NUM 16 +#define ACCOUNT_REG2_NUM 16 +#define DEFAULT_WALK_SPEED 150 +#define MIN_WALK_SPEED 0 +#define MAX_WALK_SPEED 1000 +#define MAX_STORAGE 100 +#define MAX_GUILD_STORAGE 1000 +#define MAX_PARTY 12 +#define MAX_GUILD 56 // increased max guild members to accomodate for +2 increase for extension levels [Valaris] +#define MAX_GUILDPOSITION 56 // increased max guild positions to accomodate for all members [Valaris] +#define MAX_GUILDEXPLUSION 32 +#define MAX_GUILDALLIANCE 16 +#define MAX_GUILDSKILL 8 +#define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris] +#define MAX_GUILDLEVEL 50 + +#define MIN_HAIR_STYLE 0 +#define MAX_HAIR_STYLE 20 +#define MIN_HAIR_COLOR 0 +#define MAX_HAIR_COLOR 9 +#define MIN_CLOTH_COLOR 0 +#define MAX_CLOTH_COLOR 4 + +// 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 + +#define CHAR_CONF_NAME "conf/char_athena.conf" + +struct item { + int id; + short nameid; + short amount; + unsigned short equip; + char identify; + char refine; + char attribute; + short card[4]; + short broken; +}; +struct point{ + char map[24]; + short x,y; +}; +struct skill { + unsigned short id,lv,flag; +}; +struct global_reg { + char str[32]; + int value; +}; +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[24]; + char rename_flag; + char incuvate; +}; + +struct mmo_charstatus { + int char_id; + int account_id; + int partner_id; + + int base_exp,job_exp,zeny; + + short class; + short status_point,skill_point; + int hp,max_hp,sp,max_sp; + short option,karma,manner; + short hair,hair_color,clothes_color; + int party_id,guild_id,pet_id; + + short weapon,shield; + short head_top,head_mid,head_bottom; + + char name[24]; + unsigned char base_level,job_level; + short str,agi,vit,int_,dex,luk; + unsigned char char_num,sex; + + struct point last_point,save_point,memo_point[10]; + struct item inventory[MAX_INVENTORY],cart[MAX_CART]; + struct skill skill[MAX_SKILL]; + int global_reg_num; + struct global_reg global_reg[GLOBAL_REG_NUM]; + int account_reg_num; + struct global_reg account_reg[ACCOUNT_REG_NUM]; + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +}; + +struct storage { + int account_id; + short storage_status; + short storage_amount; + struct item storage[MAX_STORAGE]; +}; + +struct guild_storage { + int guild_id; + short storage_status; + short storage_amount; + struct item storage[MAX_GUILD_STORAGE]; +}; + +struct map_session_data; + +struct gm_account { + int account_id; + int level; +}; + +struct party_member { + int account_id; + char name[24],map[24]; + int leader,online,lv; + struct map_session_data *sd; +}; +struct party { + int party_id; + char name[24]; + int exp; + int item; + struct party_member member[MAX_PARTY]; +}; + +struct guild_member { + int account_id, char_id; + short hair,hair_color,gender,class,lv; + int exp,exp_payper; + short online,position; + int rsv1,rsv2; + char name[24]; + struct map_session_data *sd; +}; +struct guild_position { + char name[24]; + int mode; + int exp_mode; +}; +struct guild_alliance { + int opposition; + int guild_id; + char name[24]; +}; +struct guild_explusion { + char name[24]; + char mes[40]; + char acc[40]; + int account_id; + int rsv1,rsv2,rsv3; +}; +struct guild_skill { + int id,lv; +}; +struct guild { + int guild_id; + short guild_lv, connect_member, max_member, average_lv; + int exp,next_exp,skill_point,castle_id; + char name[24],master[24]; + struct guild_member member[MAX_GUILD]; + struct guild_position position[MAX_GUILDPOSITION]; + char mes1[60],mes2[120]; + int emblem_len,emblem_id; + char emblem_data[2048]; + struct guild_alliance alliance[MAX_GUILDALLIANCE]; + struct guild_explusion explusion[MAX_GUILDEXPLUSION]; + struct guild_skill skill[MAX_GUILDSKILL]; +}; +struct guild_castle { + int castle_id; + char map_name[24]; + char castle_name[24]; + int guild_id; + int economy; + int defense; + int triggerE; + int triggerD; + int nextTime; + int payTime; + int createTime; + int visibleC; + int visibleG0; + int visibleG1; + int visibleG2; + int visibleG3; + int visibleG4; + int visibleG5; + int visibleG6; + int visibleG7; + int Ghp0; // added Guardian HP [Valaris] + int Ghp1; + int Ghp2; + int Ghp3; + int Ghp4; + int Ghp5; + int Ghp6; + int Ghp7; + int GID0; + int GID1; + int GID2; + int GID3; + int GID4; + int GID5; + int GID6; + int GID7; // end addition [Valaris] + +}; +struct square { + int val1[5]; + int val2[5]; +}; + +enum { + GBI_EXP =1, // ギルドのEXP + GBI_GUILDLV =2, // ギルドのLv + GBI_SKILLPOINT =3, // ギルドのスキルポイント + GBI_SKILLLV =4, // ギルドスキルLv + + GMI_POSITION =0, // メンバーの役職変更 + GMI_EXP =1, // メンバーのEXP + +}; + +#ifndef strcmpi +#define strcmpi strcasecmp +#endif +#ifndef stricmp +#define stricmp strcasecmp +#endif +#ifndef strncmpi +#define strncmpi strncasecmp +#endif +#ifndef strnicmp +#define strnicmp strncasecmp +#endif + +#endif // _MMO_H_ diff --git a/src/txt-converter/login/GNUmakefile b/src/txt-converter/login/GNUmakefile new file mode 100644 index 0000000..965a0e0 --- /dev/null +++ b/src/txt-converter/login/GNUmakefile @@ -0,0 +1,11 @@ +all: login-converter
+sql: login-converter
+
+COMMON_OBJ = ../../common/core.o ../../common/socket.o ../../common/timer.o ../../common/db.o ../../common/malloc.o
+COMMON_H = ../../common/core.h ../../common/socket.h ../../common/timer.h ../../common/mmo.h ../../common/version.h ../../common/db.h ../../common/malloc.h
+
+login-converter: login-converter.o ../../login_sql/md5calc.o ../../login_sql/strlib.o $(COMMON_OBJ)
+ $(CC) -o ../../../$@ $^ $(LIB_S)
+login-converter.o: login-converter.c ../../login_sql/login.h ../../login_sql/md5calc.h ../../login_sql/strlib.h $(COMMON_H)
+clean:
+ rm -f *.o ../../../login-converter
diff --git a/src/txt-converter/login/Makefile b/src/txt-converter/login/Makefile new file mode 100644 index 0000000..965a0e0 --- /dev/null +++ b/src/txt-converter/login/Makefile @@ -0,0 +1,11 @@ +all: login-converter
+sql: login-converter
+
+COMMON_OBJ = ../../common/core.o ../../common/socket.o ../../common/timer.o ../../common/db.o ../../common/malloc.o
+COMMON_H = ../../common/core.h ../../common/socket.h ../../common/timer.h ../../common/mmo.h ../../common/version.h ../../common/db.h ../../common/malloc.h
+
+login-converter: login-converter.o ../../login_sql/md5calc.o ../../login_sql/strlib.o $(COMMON_OBJ)
+ $(CC) -o ../../../$@ $^ $(LIB_S)
+login-converter.o: login-converter.c ../../login_sql/login.h ../../login_sql/md5calc.h ../../login_sql/strlib.h $(COMMON_H)
+clean:
+ rm -f *.o ../../../login-converter
diff --git a/src/txt-converter/login/login-converter.c b/src/txt-converter/login/login-converter.c new file mode 100644 index 0000000..c0055da --- /dev/null +++ b/src/txt-converter/login/login-converter.c @@ -0,0 +1,252 @@ +// $Id: login-converter.c,v 1.1.1.1 2004/09/10 17:45:03 MagicalTux Exp $ +// original : login2.c 2003/01/28 02:29:17 Rev.1.1.1.1 +// login data file to mysql conversion utility. +// +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <string.h> +#include <arpa/inet.h> + +#include <mysql.h> + +#include "../../common/core.h" +#include "../../common/socket.h" +#include "../../login/login.h" +#include "../../common/mmo.h" +#include "../../common/version.h" +#include "../../common/db.h" + +int account_id_count = START_ACCOUNT_NUM; +int server_num; +int new_account_flag = 0; +int login_port = 6900; + +struct mmo_char_server server[MAX_SERVERS]; +int server_fd[MAX_SERVERS]; + +#define AUTH_FIFO_SIZE 256 +struct { + int account_id,login_id1,login_id2; + int sex,delflag; +} auth_fifo[AUTH_FIFO_SIZE]; +int auth_fifo_pos=0; +struct { + 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 = Your are Prohibited to log in until %s (packet 0x006a) + time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban) + time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + char last_ip[16]; // save of last IP of connection + char memo[255]; // a memo field + int account_reg2_num; + struct global_reg account_reg2[ACCOUNT_REG2_NUM]; +} *auth_dat; +int auth_num=0,auth_max=0; + +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"; + +int isGM(int account_id) +{ + struct gm_account *p; + p = numdb_search(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 = numdb_init(); + printf("gm_account: read start\n"); + + if( (fp=fopen("conf/GM_account.txt","r"))==NULL ) + return 1; + while(fgets(line,sizeof(line),fp)){ + if(line[0] == '/' && line[1] == '/') + continue; + p=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); + } + else { + if(p->level > 99) + p->level = 99; + numdb_insert(gm_account_db,p->account_id,p); + } + c++; + } + fclose(fp); + printf("gm_account: read done (%d gm account ID)\n",c); + return 0; +} + +int mmo_auth_init(void) +{ + MYSQL mysql_handle; + char tmpsql[1024]; + MYSQL_RES* sql_res ; + MYSQL_ROW sql_row ; + + 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"); + + 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]; + + fp=fopen("save/account.txt","r"); + auth_dat=malloc(sizeof(auth_dat[0])*256); + auth_max=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; +} + +// アカウントデ??ベ?スの書き込み +void nowork(void) +{ + //null +} + +int login_config_read(const char *cfgName){ + int i; + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp=fopen(cfgName,"r"); + + if(fp==NULL){ + printf("file not found: %s\n", cfgName); + return 1; + } + printf ("start reading configuration...\n"); + 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); + } + } + fclose(fp); + printf ("End reading configuration...\n"); + return 0; +} + +int do_init(int argc,char **argv) +{ + char input; + login_config_read( (argc>1)?argv[1]:LOGIN_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(); + + exit(0); +} + + diff --git a/src/webserver/Makefile b/src/webserver/Makefile new file mode 100644 index 0000000..f664a7d --- /dev/null +++ b/src/webserver/Makefile @@ -0,0 +1,26 @@ +cc = gcc
+
+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 0000000..c80f7bd --- /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 0000000..edcabf1 --- /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 0000000..ad050db --- /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 0000000..c3a4b92 --- /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> « Portal »</font></td></tr></tbody></table></td></tr></tbody>" + "</table></td></tr></tbody></table>\n"; + + sprintf(output, "<title>%s</title>\n%s\n", title, text); + + return output; +} + + + +char *html_start_form(char *location, char *action) +{ + memset(output, 0x0, 10000); + sprintf(output, "<form action=\"%s\" method=\"%s\">", location, action); + return output; + + +} + + +char *html_end_forum(void) +{ + return "</form>"; +} + + + diff --git a/src/webserver/logs.c b/src/webserver/logs.c new file mode 100644 index 0000000..405b488 --- /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 0000000..5936255 --- /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 0000000..2b0002a --- /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 0000000..a6492e3 --- /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 0000000..be900a1 --- /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 0000000..8e54a81 --- /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/xand1_1.patch b/src/xand1_1.patch new file mode 100644 index 0000000..5715664 --- /dev/null +++ b/src/xand1_1.patch @@ -0,0 +1,132 @@ +--- npc/tulimshar/monster_guide.txt 2005-08-05 23:03:44.144096000 +0200 ++++ npc/tulimshar/monster_guide.txt.new 2005-08-08 14:03:51.154608000 +0200 +@@ -1,91 +1,3 @@ +-new_3-1.gat,53,185,0 script ConquestMob0 -1,{ +-OnInit: +-// all monsters ingame by 31.Jul 2005 sorted by map, monsterID +-areamonster "new_1-1.gat",15,17,105,103,"RedScorpion",1004, 1,"ConquestMob-new_1-1::OnGuardianDied1004"; +-areamonster "new_1-1.gat",15,17,105,103,"GreenSlime",1005, 50,"ConquestMob-new_1-1::OnGuardianDied1005"; +-areamonster "new_1-1.gat",15,17,105,103,"GiantMaggot",1006, 30,"ConquestMob-new_1-1::OnGuardianDied1006"; +-areamonster "new_2-1.gat",31,31,90,97,"RedSlime",1008, 24,"ConquestMob-new_2-1::OnGuardianDied1008"; +-areamonster "new_2-1.gat",53,34,96,36,"RedSlime",1008, 6,"ConquestMob-new_2-1::OnGuardianDied1008b"; +-areamonster "new_2-1.gat",31,31,90,97,"BlackScorpion",1009, 15,"ConquestMob-new_2-1::OnGuardianDied1009"; +-areamonster "new_2-1.gat",84,52,93,91,"BlackScorpion",1009, 5,"ConquestMob-new_2-1::OnGuardianDied1009a"; +-areamonster "new_3-1.gat",22,42,142,79,"Maggot",1002, 35,"ConquestMob-new_3-1::OnGuardianDied1002"; +-areamonster "new_3-1.gat",22,42,142,79,"Scorpion",1003, 10,"ConquestMob-new_3-1::OnGuardianDied1003"; +-areamonster "new_5-1.gat",32,32,90,100,"YellowSlime",1007, 20,"ConquestMob-new_5-1::OnGuardianDied1007"; +-areamonster "new_5-1.gat",88,33,98,42,"RedSlime",1008, 3,"ConquestMob-new_5-1::OnGuardianDied1008a"; +-areamonster "new_5-1.gat",32,32,90,100,"Spider",1012, 8,"ConquestMob-new_5-1::OnGuardianDied1012"; +-areamonster "new_5-1.gat",81,32,85,38,"Spider",1012, 2,"ConquestMob-new_5-1::OnGuardianDied1012a"; +-areamonster "new_7-1.gat",22,27,176,174,"Snake",1010, 15,"ConquestMob-new_7-1::OnGuardianDied1010"; +-break; +-} +-new_1-1.gat,53,185,0 script ConquestMob-new_1-1 -1,{ +-// event when mob dies +-OnGuardianDied1004: +- if (MPQUEST == 1) set Mobpt,Mobpt+42; +- areamonster "new_1-1.gat",15,17,105,103,"RedScorpion",1004, 1,"ConquestMob-new_1-1::OnGuardianDied1004"; +- break; +-OnGuardianDied1005: +- if (MPQUEST == 1) set Mobpt,Mobpt+5; +- areamonster "new_1-1.gat",15,17,105,103,"GreenSlime",1005, 1,"ConquestMob-new_1-1::OnGuardianDied1005"; +- break; +-OnGuardianDied1006: +- if (MPQUEST == 1) set Mobpt,Mobpt+14; +- areamonster "new_1-1.gat",15,17,105,103,"GiantMaggot",1006, 1,"ConquestMob-new_1-1::OnGuardianDied1006"; +- break; +-} +-new_2-1.gat,53,185,0 script ConquestMob-new_2-1 -1,{ +-OnGuardianDied1008: +- if (MPQUEST == 1) set Mobpt,Mobpt+18; +- areamonster "new_2-1.gat",31,31,90,97,"RedSlime",1008, 1,"ConquestMob-new_2-1::OnGuardianDied1008"; +- break; +-OnGuardianDied1008b: +- if (MPQUEST == 1) set Mobpt,Mobpt+18; +- areamonster "new_2-1.gat",53,34,96,36,"RedSlime",1008, 1,"ConquestMob-new_2-1::OnGuardianDied1008b"; +- break; +-OnGuardianDied1009: +- if (MPQUEST == 1) set Mobpt,Mobpt+45; +- areamonster "new_2-1.gat",31,31,90,97,"BlackScorpion",1009, 1,"ConquestMob-new_2-1::OnGuardianDied1009"; +- break; +-OnGuardianDied1009a: +- if (MPQUEST == 1) set Mobpt,Mobpt+45; +- areamonster "new_2-1.gat",84,52,93,91,"BlackScorpion",1009, 1,"ConquestMob-new_2-1::OnGuardianDied1009a"; +- break; +-} +-new_3-1.gat,53,185,0 script ConquestMob-new_3-1 -1,{ +-OnGuardianDied1002: +- if (MPQUEST == 1) set Mobpt,Mobpt+1; +- areamonster "new_3-1.gat",22,42,142,79,"Maggot",1002, 1,"ConquestMob-new_3-1::OnGuardianDied1002"; +- break; +-OnGuardianDied1003: +- if (MPQUEST == 1) set Mobpt,Mobpt+2; +- areamonster "new_3-1.gat",22,42,142,79,"Scorpion",1003, 1,"ConquestMob-new_3-1::OnGuardianDied1003"; +- break; +-} +-new_5-1.gat,53,185,0 script ConquestMob-new_5-1 -1,{ +-OnGuardianDied1007: +- if (MPQUEST == 1) set Mobpt,Mobpt+9; +- areamonster "new_5-1.gat",32,32,90,100,"YellowSlime",1007, 1,"ConquestMob-new_5-1::OnGuardianDied1007"; +- break; +-// 3 Red Slimes guard treasure +-OnGuardianDied1008a: +- if (MPQUEST == 1) set Mobpt,Mobpt+18; +- areamonster "new_5-1.gat",88,33,98,42,"RedSlime",1008, 1,"ConquestMob-new_5-1::OnGuardianDied1008a"; +- break; +-OnGuardianDied1012: +- if (MPQUEST == 1) set Mobpt,Mobpt+56; +- areamonster "new_5-1.gat",32,32,90,100,"Spider",1012, 1,"ConquestMob-new_5-1::OnGuardianDied1012"; +- break; +-// 2 spiders guard entrance to treasure +-OnGuardianDied1012a: +- if (MPQUEST == 1) set Mobpt,Mobpt+56; +- areamonster "new_5-1.gat",81,32,85,38,"Spider",1012, 1,"ConquestMob-new_5-1::OnGuardianDied1012a"; +- break; +-} +-new_7-1.gat,53,185,0 script ConquestMob-new_7-1 -1,{ +-OnGuardianDied1010: +- if (MPQUEST == 1) set Mobpt,Mobpt+51; +- areamonster "new_7-1.gat",22,27,176,174,"Snake",1010, 1,"ConquestMob-new_7-1::OnGuardianDied1010"; +- break; +-} + new_3-1.gat,46,66,0 script MonsterGuide 102,{ + if(MPQUEST == 0) goto Register; + mes "[Monster Guide]"; +--- conf/map_athena.conf 2005-07-18 14:57:41.000000000 +0200 ++++ conf/map_athena.conf.new 2005-08-08 14:03:04.888080000 +0200 +@@ -57,7 +57,6 @@ + + // NPCs + +-npc: npc/tulimshar/monsters.txt + npc: npc/tulimshar/barber.txt + npc: npc/tulimshar/monster_guide.txt + npc: npc/tulimshar/ptsrewards.txt +@@ -68,10 +67,15 @@ + npc: npc/tulimshar/warps.txt + npc: npc/tulimshar/shop.txt + ++npc: npc/monsters/monsters-new_1-1.txt ++npc: npc/monsters/monsters-new_2-1.txt ++npc: npc/monsters/monsters-new_3-1.txt ++npc: npc/monsters/monsters-new_5-1.txt ++npc: npc/monsters/monsters-new_7-1.txt ++ + npc: npc/tonori/cave.txt + npc: npc/tonori/miners.txt + npc: npc/tonori/warps.txt +-npc: npc/tonori/monsters.txt + + npc: npc/guide.txt + npc: npc/nekkio.txt +--- db/mob_db.txt 2005-07-15 10:25:21.000000000 +0200 ++++ db/mob_db.txt.new 2005-08-08 14:23:23.680617600 +0200 +@@ -10,6 +10,7 @@ + 1007,Yellow_slime,Yellow Slime,1,400,0,20,2,1,20,25,2,7,10,8,2,1,34,1,1,1,1,0,20,131,1400,1800,672,480,534,200,519,100,501,350,502,250,522,10,909,0,909,0,0,0,0,0,0,0,,,,,, + 1008,Red_slime,Red Slime,1,400,0,65,56,1,30,50,2,7,15,10,2,1,25,1,1,1,1,0,20,135,1300,1500,672,480,1201,300,509,110,521,200,523,40,525,80,535,750,528,250,531,150,0,0,0,0,,,,,, + 1009,BlackScorpion,Black Scorpion,1,1100,0,200,70,1,80,90,4,6,20,20,10,10,35,10,1,1,1,0,20,133,1000,1500,672,480,523,150,509,100,518,800,909,0,909,0,909,0,909,0,0,0,0,0,0,0,,,,,, +-1010,Snake,Snake,1,2000,0,172,70,1,150,80,4,6,20,20,10,10,35,10,2,1,1,0,20,133,1000,1500,672,480,0,0,0,0,0,0,909,0,909,0,909,0,909,0,0,0,0,0,0,0,,,,,, ++1010,Snake,Snake,1,2200,0,250,120,1,150,80,4,6,15,25,10,10,45,5,2,1,1,0,20,133,1000,1500,672,480,524,50,527,500,0,0,909,0,909,0,909,0,909,0,0,0,0,0,0,0,,,,,, + 1011,Fire_Goblin,Fire Goblin,1,50,0,3,2,1,7,10,0,5,1,1,1,0,6,30,1,1,1,3,21,129,800,1872,672,480,505,800,501,150,518,800,502,150,521,70,522,1,909,0,0,0,0,0,0,0,,,,,, + 1012,Spider,Spider,1,2000,0,230,90,1,150,80,4,6,20,20,10,10,35,10,2,1,1,0,25,175,1000,1500,672,480,537,500,535,100,0,0,909,0,909,0,909,0,909,0,0,0,0,0,0,0,,,,,, ++1013,SpiderRage,Raging Spider,1,3000,0,450,170,1,230,150,5,2,35,30,10,2,40,10,2,1,1,0,25,175,700,1500,672,480,537,1000,535,5000,0,0,909,0,909,0,909,0,909,0,0,0,0,0,0,0,,,,,, |