summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/char/GNUmakefile17
-rw-r--r--src/char/Makefile17
-rw-r--r--src/char/char.c3417
-rw-r--r--src/char/char.h32
-rw-r--r--src/char/int_guild.c1446
-rw-r--r--src/char/int_guild.h16
-rw-r--r--src/char/int_party.c595
-rw-r--r--src/char/int_party.h14
-rw-r--r--src/char/int_pet.c364
-rw-r--r--src/char/int_pet.h13
-rw-r--r--src/char/int_storage.c500
-rw-r--r--src/char/int_storage.h16
-rw-r--r--src/char/inter.c563
-rw-r--r--src/char/inter.h20
-rw-r--r--src/char_sql/GNUmakefile20
-rw-r--r--src/char_sql/Makefile20
-rw-r--r--src/char_sql/char.c3017
-rw-r--r--src/char_sql/char.h82
-rw-r--r--src/char_sql/int_guild.c1597
-rw-r--r--src/char_sql/int_guild.h10
-rw-r--r--src/char_sql/int_party.c755
-rw-r--r--src/char_sql/int_party.h8
-rw-r--r--src/char_sql/int_pet.c324
-rw-r--r--src/char_sql/int_pet.h12
-rw-r--r--src/char_sql/int_storage.c360
-rw-r--r--src/char_sql/int_storage.h13
-rw-r--r--src/char_sql/inter.c577
-rw-r--r--src/char_sql/inter.h46
-rw-r--r--src/char_sql/itemdb.c247
-rw-r--r--src/char_sql/itemdb.h34
-rw-r--r--src/char_sql/make.sh11
-rw-r--r--src/char_sql/readme.txt250
-rw-r--r--src/char_sql/strlib.c79
-rw-r--r--src/char_sql/strlib.h10
-rw-r--r--src/common/GNUmakefile14
-rw-r--r--src/common/Makefile14
-rw-r--r--src/common/core.c152
-rw-r--r--src/common/core.h12
-rw-r--r--src/common/db.c501
-rw-r--r--src/common/db.h47
-rw-r--r--src/common/grfio.c948
-rw-r--r--src/common/grfio.h16
-rw-r--r--src/common/lock.c37
-rw-r--r--src/common/lock.h8
-rw-r--r--src/common/malloc.c44
-rw-r--r--src/common/malloc.h25
-rw-r--r--src/common/mmo.h311
-rw-r--r--src/common/nullpo.c90
-rw-r--r--src/common/nullpo.h222
-rw-r--r--src/common/showmsg.c71
-rw-r--r--src/common/showmsg.h48
-rw-r--r--src/common/socket.c594
-rw-r--r--src/common/socket.h104
-rw-r--r--src/common/timer.c312
-rw-r--r--src/common/timer.h45
-rw-r--r--src/common/utils.c108
-rw-r--r--src/common/utils.h33
-rw-r--r--src/common/version.h27
-rw-r--r--src/ladmin/GNUmakefile14
-rw-r--r--src/ladmin/Makefile14
-rw-r--r--src/ladmin/ladmin.c4385
-rw-r--r--src/ladmin/ladmin.h11
-rw-r--r--src/ladmin/md5calc.c237
-rw-r--r--src/ladmin/md5calc.h8
-rw-r--r--src/login/GNUmakefile13
-rw-r--r--src/login/Makefile13
-rw-r--r--src/login/login.c3775
-rw-r--r--src/login/login.h38
-rw-r--r--src/login/md5calc.c237
-rw-r--r--src/login/md5calc.h8
-rw-r--r--src/login_sql/GNUmakefile17
-rw-r--r--src/login_sql/Makefile17
-rw-r--r--src/login_sql/login.c1762
-rw-r--r--src/login_sql/login.h41
-rw-r--r--src/login_sql/make.sh6
-rw-r--r--src/login_sql/md5calc.c236
-rw-r--r--src/login_sql/md5calc.h7
-rw-r--r--src/login_sql/readme.txt120
-rw-r--r--src/login_sql/strlib.c58
-rw-r--r--src/login_sql/strlib.h9
-rw-r--r--src/login_sql/timer.h43
-rw-r--r--src/map/GNUmakefile73
-rw-r--r--src/map/Makefile73
-rw-r--r--src/map/Makefile.win3293
-rw-r--r--src/map/atcommand.c7660
-rw-r--r--src/map/atcommand.h243
-rw-r--r--src/map/battle.c5387
-rw-r--r--src/map/battle.h345
-rw-r--r--src/map/chat.c373
-rw-r--r--src/map/chat.h22
-rw-r--r--src/map/chrif.c1016
-rw-r--r--src/map/chrif.h29
-rw-r--r--src/map/clif.c9712
-rw-r--r--src/map/clif.h297
-rw-r--r--src/map/guild.c1554
-rw-r--r--src/map/guild.h87
-rw-r--r--src/map/intif.c1119
-rw-r--r--src/map/intif.h60
-rw-r--r--src/map/itemdb.c882
-rw-r--r--src/map/itemdb.h84
-rw-r--r--src/map/log.c243
-rw-r--r--src/map/log.h27
-rw-r--r--src/map/mail.c324
-rw-r--r--src/map/mail.h9
-rw-r--r--src/map/map.c2208
-rw-r--r--src/map/map.h724
-rw-r--r--src/map/mob.c4269
-rw-r--r--src/map/mob.h139
-rw-r--r--src/map/npc.c2274
-rw-r--r--src/map/npc.h52
-rw-r--r--src/map/party.c644
-rw-r--r--src/map/party.h47
-rw-r--r--src/map/path.c404
-rw-r--r--src/map/pc.c7499
-rw-r--r--src/map/pc.h186
-rw-r--r--src/map/pet.c1651
-rw-r--r--src/map/pet.h69
-rw-r--r--src/map/script.c6769
-rw-r--r--src/map/script.h39
-rw-r--r--src/map/skill.c10682
-rw-r--r--src/map/skill.h854
-rw-r--r--src/map/storage.c595
-rw-r--r--src/map/storage.h39
-rw-r--r--src/map/trade.c286
-rw-r--r--src/map/trade.h13
-rw-r--r--src/map/vending.c170
-rw-r--r--src/map/vending.h12
-rw-r--r--src/txt-converter/char/GNUmakefile13
-rw-r--r--src/txt-converter/char/Makefile13
-rw-r--r--src/txt-converter/char/char-converter.c842
-rw-r--r--src/txt-converter/char/char.h39
-rw-r--r--src/txt-converter/char/int_guild.h10
-rw-r--r--src/txt-converter/char/int_party.h11
-rw-r--r--src/txt-converter/char/int_pet.h12
-rw-r--r--src/txt-converter/char/int_storage.h11
-rw-r--r--src/txt-converter/char/strlib.c66
-rw-r--r--src/txt-converter/char/strlib.h9
-rw-r--r--src/txt-converter/common/inter.h28
-rw-r--r--src/txt-converter/common/mmo.h280
-rw-r--r--src/txt-converter/login/GNUmakefile11
-rw-r--r--src/txt-converter/login/Makefile11
-rw-r--r--src/txt-converter/login/login-converter.c258
142 files changed, 100305 insertions, 0 deletions
diff --git a/src/char/GNUmakefile b/src/char/GNUmakefile
new file mode 100644
index 000000000..f3164e0ec
--- /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/showmsg.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 ../common/showmsg.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 000000000..228dc1945
--- /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/showmsg.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 ../common/showmsg.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 000000000..e6cf4393e
--- /dev/null
+++ b/src/char/char.c
@@ -0,0 +1,3417 @@
+// $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 "showmsg.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";
+int login_ip_set_ = 0;
+char login_ip_str[16];
+int login_ip;
+int login_port = 6900;
+int char_ip_set_ = 0;
+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]="save/athena.txt";
+char backup_txt[1024]="save/backup.txt"; //By zanetheinsane
+char friends_txt[1024]="save/friends.txt"; // davidsiaw
+char backup_txt_flag = 0; // The backup_txt file was created because char deletion bug existed. Now it's finish and that take a lot of time to create a second file when there are a lot of characters. => option By [Yor]
+char unknown_char_name[1024] = "Unknown";
+char char_log_filename[1024] = "log/char.log";
+char db_path[1024]="db";
+//Added for lan support
+char lan_map_ip[128];
+int subneti[4];
+int subnetmaski[4];
+int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor]
+int char_name_option = 0; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor]
+char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor]
+
+int log_char = 1; // loggin char or not [devil]
+int log_inter = 1; // loggin inter or not [devil]
+
+struct char_session_data{
+ int account_id, login_id1, login_id2, sex;
+ int found_char[9];
+ char email[40]; // e-mail (default: a@a.com) by [Yor]
+ time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+};
+
+//Added for Mugendai's I'm Alive mod
+int imalive_on=0;
+int imalive_time=60;
+//Added by Mugendai for GUI
+int flush_on=1;
+int flush_time=100;
+
+#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;
+
+// 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_players_max;
+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
+
+struct online_chars {
+ int char_id;
+ int server;
+} *online_chars;
+
+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;
+ 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;
+}
+
+/*---------------------------------------------------
+ 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<20;i++)
+ {
+ str_p += sprintf(str_p, ",%d,%s", p->friend_id[i],p->friend_name[i]);
+ }
+ return 0;
+}
+
+//-------------------------------------------------
+// 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 ",
+ 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]);
+ }
+ *(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 ",
+ 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]);
+ }
+ *(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) {
+ } else // invalid structure
+ return -4;
+ p->inventory[i].id = tmp_int[0];
+ p->inventory[i].nameid = tmp_int[1];
+ p->inventory[i].amount = tmp_int[2];
+ p->inventory[i].equip = tmp_int[3];
+ p->inventory[i].identify = tmp_int[4];
+ p->inventory[i].refine = tmp_int[5];
+ p->inventory[i].attribute = tmp_int[6];
+ p->inventory[i].card[0] = tmp_int[7];
+ p->inventory[i].card[1] = tmp_int[8];
+ p->inventory[i].card[2] = tmp_int[9];
+ p->inventory[i].card[3] = tmp_int[10];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t'; i++) {
+ if (sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3],
+ &tmp_int[4], &tmp_int[5], &tmp_int[6],
+ &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &tmp_int[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) {
+ } else // invalid structure
+ return -5;
+ p->cart[i].id = tmp_int[0];
+ p->cart[i].nameid = tmp_int[1];
+ p->cart[i].amount = tmp_int[2];
+ p->cart[i].equip = tmp_int[3];
+ p->cart[i].identify = tmp_int[4];
+ p->cart[i].refine = tmp_int[5];
+ p->cart[i].attribute = tmp_int[6];
+ p->cart[i].card[0] = tmp_int[7];
+ p->cart[i].card[1] = tmp_int[8];
+ p->cart[i].card[2] = tmp_int[9];
+ p->cart[i].card[3] = tmp_int[10];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t'; i++) {
+ if (sscanf(str + next, "%d,%d%n", &tmp_int[0], &tmp_int[1], &len) != 2)
+ return -6;
+ p->skill[tmp_int[0]].id = tmp_int[0];
+ p->skill[tmp_int[0]].lv = tmp_int[1];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ next++;
+
+ for(i = 0; str[next] && str[next] != '\t' && str[next] != '\n' && str[next] != '\r'; i++) { // global_reg実装以前の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 friend list
+//---------------------------------
+
+int parse_friend_txt(struct mmo_charstatus *p)
+{
+ char line[1024];
+ int i,cid=0,temp[20];
+ FILE *fp;
+
+ // Open the file and look for the ID
+ fp = fopen(friends_txt, "r");
+
+
+ while(fgets(line, sizeof(line)-1, fp)) {
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ sscanf(line, "%d,%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%[^,],%d,%s",&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<20; i++)
+ p->friend_id[i] = temp[i];
+
+ fclose(fp);
+ return 0;
+}
+
+//---------------------------------
+// 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(struct online_chars) * 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].char_id = -1;
+ online_chars[i].server = -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].char_id = -1;
+ online_chars[i].server = -1;
+ }
+ }
+
+ ret = mmo_char_fromstr(line, &char_dat[char_num]);
+
+ // Initialize friends list
+ parse_friend_txt(&char_dat[char_num]); // 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;
+ 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],f_line[1024];
+ int i, j, k;
+ int lock;
+ FILE *fp,*f_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);
+ }
+
+ // 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]]);
+ fprintf(f_fp, "%s" RETCODE, f_line);
+ }
+
+ lock_fclose(f_fp, friends_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] >= 9) { // hair color (dat[31] can not be negativ)
+ char_log("Make new char error (invalid values): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE,
+ fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]);
+ return -1;
+ }
+
+ // check individual stat value
+ for(i = 24; i <= 29; i++) {
+ if (dat[i] < 1 || dat[i] > 9) {
+ char_log("Make new char error (invalid stat value: not between 1 to 9): (connection #%d, account: %d) slot %d, name: %s, stats: %d+%d+%d+%d+%d+%d=%d, hair: %d, hair color: %d" RETCODE,
+ fd, sd->account_id, dat[30], dat, dat[24], dat[25], dat[26], dat[27], dat[28], dat[29], dat[24] + dat[25] + dat[26] + dat[27] + dat[28] + dat[29], dat[33], dat[31]);
+ return -1;
+ }
+ }
+
+ 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(struct online_chars) * 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].char_id = -1;
+ online_chars[j].server = -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[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].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[online_players_max];
+
+ // don't return here if we display nothing, because server[j].users is updated in the first loop.
+
+ // Get number of online players, id of each online players, and verify if a server is offline
+ players = 0;
+ for (i = 0; i < online_players_max; i++) {
+ if (online_chars[i].char_id != -1) {
+ // check if map-server is online
+ j = online_chars[i].server;
+ if (j == -1) {
+ online_chars[i].char_id = -1;
+ continue;
+ } else if (server_fd[j] < 0) {
+ server[j].users = 0;
+ online_chars[i].char_id = -1;
+ online_chars[i].server = -1;
+ continue;
+ }
+ // check if the character is twice or more in the list
+ // (multiple map-servers and player have successfully connected twice!)
+ for(j = i + 1; j < online_players_max; j++)
+ if (online_chars[i].char_id == online_chars[j].char_id) {
+ k = online_chars[j].server;
+ if (k != -1 && server_fd[k] >= 0 && server[k].users > 0)
+ server[k].users--;
+ online_chars[j].char_id = -1;
+ online_chars[j].server = -1;
+ }
+ // search position of character in char_dat and sort online characters.
+ for(j = 0; j < char_num; j++) {
+ if (char_dat[j].char_id == online_chars[i].char_id) {
+ id[players] = j;
+ // use sorting option
+ switch (online_sorting_option) {
+ case 1: // by name (without case sensitive)
+ for(k = 0; k < players; k++)
+ if (stricmp(char_dat[j].name, char_dat[id[k]].name) < 0 ||
+ // if same name, we sort with case sensitive.
+ (stricmp(char_dat[j].name, char_dat[id[k]].name) == 0 &&
+ strcmp(char_dat[j].name, char_dat[id[k]].name) < 0)) {
+ for(l = players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[players]
+ break;
+ }
+ break;
+ case 2: // by zeny
+ for(k = 0; k < players; k++)
+ if (char_dat[j].zeny < char_dat[id[k]].zeny ||
+ // if same number of zenys, we sort by name.
+ (char_dat[j].zeny == char_dat[id[k]].zeny &&
+ stricmp(char_dat[j].name, char_dat[id[k]].name) < 0)) {
+ for(l = players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[players]
+ break;
+ }
+ break;
+ case 3: // by base level
+ for(k = 0; k < players; k++)
+ if (char_dat[j].base_level < char_dat[id[k]].base_level ||
+ // if same base level, we sort by base exp.
+ (char_dat[j].base_level == char_dat[id[k]].base_level &&
+ char_dat[j].base_exp < char_dat[id[k]].base_exp)) {
+ for(l = players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[players]
+ break;
+ }
+ break;
+ case 4: // by job (and job level)
+ for(k = 0; k < players; k++)
+ if (char_dat[j].class < char_dat[id[k]].class ||
+ // if same job, we sort by job level.
+ (char_dat[j].class == char_dat[id[k]].class &&
+ char_dat[j].job_level < char_dat[id[k]].job_level) ||
+ // if same job and job level, we sort by job exp.
+ (char_dat[j].class == char_dat[id[k]].class &&
+ char_dat[j].job_level == char_dat[id[k]].job_level &&
+ char_dat[j].job_exp < char_dat[id[k]].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
+ for(k = 0; k < players; k++)
+ if (stricmp(char_dat[j].last_point.map, char_dat[id[k]].last_point.map) < 0 ||
+ // if same map name, we sort by name.
+ (stricmp(char_dat[j].last_point.map, char_dat[id[k]].last_point.map) == 0 &&
+ stricmp(char_dat[j].name, char_dat[id[k]].name) < 0)) {
+ for(l = players; l > k; l--)
+ id[l] = id[l-1];
+ id[k] = j; // id[players]
+ break;
+ }
+ break;
+ default: // 0 or invalid value: no sorting
+ break;
+ }
+ players++;
+ break;
+ }
+ }
+ }
+ }
+
+ if (online_display_option == 0) // we display nothing, so return
+ return;
+
+ // write files
+ fp = fopen(online_txt_filename, "w");
+ if (fp != NULL) {
+ fp2 = fopen(online_html_filename, "w");
+ if (fp2 != NULL) {
+ // get time
+ time(&time_server); // get time in seconds since 1/1/1970
+ datetime = localtime(&time_server); // convert seconds in structure
+ strftime(temp, sizeof(temp), "%d %b %Y %X", datetime); // like sprintf, but only for date/time (05 dec 2003 15:12:52)
+ // write heading
+ fprintf(fp2, "<HTML>\n");
+ fprintf(fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n", online_refresh_html); // update on client explorer every x seconds
+ fprintf(fp2, " <HEAD>\n");
+ fprintf(fp2, " <TITLE>Online Players on %s</TITLE>\n", server_name);
+ fprintf(fp2, " </HEAD>\n");
+ fprintf(fp2, " <BODY>\n");
+ fprintf(fp2, " <H3>Online Players on %s (%s):</H3>\n", server_name, temp);
+ fprintf(fp, "Online Players on %s (%s):\n", server_name, temp);
+ fprintf(fp, "\n");
+
+ for (i = 0; i < players; i++) {
+ // if it's the first player
+ if (i == 0) {
+ j = 0; // count the number of characters for the txt version and to set the separate line
+ fprintf(fp2, " <table border=\"1\" cellspacing=\"1\">\n");
+ fprintf(fp2, " <tr>\n");
+ if ((online_display_option & 1) || (online_display_option & 64)) {
+ fprintf(fp2, " <td><b>Name</b></td>\n");
+ if (online_display_option & 64) {
+ fprintf(fp, "Name "); // 30
+ j += 30;
+ } else {
+ fprintf(fp, "Name "); // 25
+ j += 25;
+ }
+ }
+ if ((online_display_option & 6) == 6) {
+ fprintf(fp2, " <td><b>Job (levels)</b></td>\n");
+ fprintf(fp, "Job Levels "); // 27
+ j += 27;
+ } else if (online_display_option & 2) {
+ fprintf(fp2, " <td><b>Job</b></td>\n");
+ fprintf(fp, "Job "); // 19
+ j += 19;
+ } else if (online_display_option & 4) {
+ fprintf(fp2, " <td><b>Levels</b></td>\n");
+ fprintf(fp, " Levels "); // 8
+ j += 8;
+ }
+ if (online_display_option & 24) { // 8 or 16
+ fprintf(fp2, " <td><b>Location</b></td>\n");
+ if (online_display_option & 16) {
+ fprintf(fp, "Location ( x , y ) "); // 23
+ j += 23;
+ } else {
+ fprintf(fp, "Location "); // 13
+ j += 13;
+ }
+ }
+ if (online_display_option & 32) {
+ fprintf(fp2, " <td ALIGN=CENTER><b>zenys</b></td>\n");
+ fprintf(fp, " Zenys "); // 16
+ j += 16;
+ }
+ fprintf(fp2, " </tr>\n");
+ fprintf(fp, "\n");
+ for (k = 0; k < j; k++)
+ fprintf(fp, "-");
+ fprintf(fp, "\n");
+ }
+ fprintf(fp2, " <tr>\n");
+ // get id of the character (more speed)
+ j = id[i];
+ // displaying the character name
+ if ((online_display_option & 1) || (online_display_option & 64)) { // without/with 'GM' display
+ strcpy(temp, char_dat[j].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; k < strlen(temp); k++) {
+ switch(temp[k]) {
+ case '<': // <
+ fprintf(fp2, "&lt;");
+ break;
+ case '>': // >
+ fprintf(fp2, "&gt;");
+ break;
+ default:
+ fprintf(fp2, "%c", temp[k]);
+ break;
+ };
+ }
+ if ((online_display_option & 64) && l >= online_gm_display_min_level)
+ fprintf(fp2, "</b> (GM)");
+ fprintf(fp2, "</td>\n");
+ }
+ // displaying of the job
+ if (online_display_option & 6) {
+ char * jobname = job_name(char_dat[j].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, 17);
+ strncpy(temp, char_dat[j].last_point.map, 16);
+ 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);
+ } else {
+ fprintf(fp2, " <td>%s</td>\n", temp);
+ fprintf(fp, "%-12s ", temp);
+ }
+ }
+ // displaying nimber of zenys
+ if (online_display_option & 32) {
+ // write number of zenys
+ if (char_dat[j].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");
+ }
+ // If we display at least 1 player
+ if (players > 0) {
+ fprintf(fp2, " </table>\n");
+ fprintf(fp, "\n");
+ }
+
+ // Displaying number of online players
+ if (players == 0) {
+ fprintf(fp2, " <p>No user is online.</p>\n");
+ fprintf(fp, "No user is online.\n");
+ // 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=1;
+ if(session[fd]->eof){
+ for(i = 0; i < MAX_MAP_SERVERS; i++)
+ if (server_fd[i] == fd) {
+ printf("Map-server %d has disconnected.\n", i);
+ server_fd[i] = -1;
+ }
+ close(fd);
+ delete_session(fd);
+ create_online_files();
+ 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;
+
+ // 認証要
+ // Send character data to map-server
+ 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サーバー上のユーザー数受信
+ // Recieve alive message from map-server
+ 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 < online_players_max; i++)
+ if (online_chars[i].server == id) {
+ online_chars[i].char_id = -1;
+ online_chars[i].server = -1;
+ }
+ // add online players in the list by [Yor]
+ j = 0;
+ for(i = 0; i < server[id].users; i++) {
+ for(; j < online_players_max; j++)
+ if (online_chars[j].char_id == -1) {
+ online_chars[j].char_id = RFIFOL(fd,6+i*4);
+ online_chars[j].server = id;
+ //printf("%d\n", online_chars[j].char_id);
+ break;
+ }
+ // no available slots...
+ if (j == online_players_max) {
+ // create 256 new slots
+ online_players_max += 256;
+ online_chars = realloc(online_chars, sizeof(struct online_chars) * online_players_max);
+ if (!online_chars) {
+ printf("out of memory: parse_frommap - online_chars (realloc).\n");
+ exit(1);
+ }
+ for( ; j < online_players_max; j++) {
+ online_chars[j].char_id = -1;
+ online_chars[j].server = -1;
+ }
+ // save data
+ j = online_players_max - 256;
+ online_chars[j].char_id = RFIFOL(fd,6+i*4);
+ online_chars[j].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);
+ 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))
+ 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;
+}
+
+// Console Command Parser [Wizputer]
+int parse_console(char *buf) {
+ char *type,*command;
+
+ type = (char *)malloc(64);
+ command = (char *)malloc(64);
+
+ memset(type,0,64);
+ memset(command,0,64);
+
+ printf("Console: %s\n",buf);
+
+ if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 )
+ sscanf(buf,"%[^\n]",type);
+
+ printf("Type of command: %s || Command: %s \n",type,command);
+
+ free(buf);
+ free(type);
+ free(command);
+
+ 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) {
+ 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]);
+ 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]);
+ 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, "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, "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,"imalive_on")==0) { //Added by Mugendai for I'm Alive mod
+ imalive_on = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"imalive_time")==0) { //Added by Mugendai for I'm Alive mod
+ imalive_time = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"flush_on")==0) { //Added by Mugendai for GUI
+ flush_on = atoi(w2); //Added by Mugendai for GUI
+ } else if(strcmpi(w1,"flush_time")==0) { //Added by Mugendai for GUI
+ flush_time = atoi(w2); //Added by Mugendai for GUI
+ } 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);
+ 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,"db_path")==0) {
+ strcpy(db_path,w2);
+ } 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);
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+//-----------------------------------------------------
+//I'm Alive Alert
+//Used to output 'I'm Alive' every few seconds
+//Intended to let frontends know if the app froze
+//-----------------------------------------------------
+int imalive_timer(int tid, unsigned int tick, int id, int data){
+ printf("I'm Alive\n");
+ return 0;
+}
+
+//-----------------------------------------------------
+//Flush stdout
+//stdout buffer needs flushed to be seen in GUI
+//-----------------------------------------------------
+int flush_timer(int tid, unsigned int tick, int id, int data){
+ fflush(stdout);
+ return 0;
+}
+
+void do_final(void) {
+ int i;
+
+ // write online players files with no player
+ for(i = 0; i < online_players_max; i++) {
+ online_chars[i].char_id = -1;
+ online_chars[i].server = -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);
+
+ 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)
+ printf("Multiple interfaces detected.. using %s as our IP address\n", buf);
+ else
+ printf("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)
+ printf("Firewall detected.. edit lan_support.conf and char_athena.conf");
+ }
+
+ login_ip = inet_addr(login_ip_str);
+ char_ip = inet_addr(char_ip_str);
+
+ for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ memset(&server[i], 0, sizeof(struct mmo_map_server));
+ server_fd[i] = -1;
+ }
+
+ online_players_max = 256;
+ online_chars = calloc(sizeof(struct online_chars) * 256, 1);
+ if (!online_chars) {
+ printf("out of memory: do_init (calloc).\n");
+ exit(1);
+ }
+ for(i = 0; i < online_players_max; i++) {
+ online_chars[i].char_id = -1;
+ online_chars[i].server = -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);
+
+ //Added for Mugendais I'm Alive mod
+ if (imalive_on)
+ add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000);
+
+ //Added by Mugendai for GUI support
+ if (flush_on)
+ add_timer_interval(gettick()+10, flush_timer,0,0,flush_time);
+
+
+
+ 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
+ }
+
+ 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);
+
+ return 0;
+}
diff --git a/src/char/char.h b/src/char/char.h
new file mode 100644
index 000000000..6c3b6a615
--- /dev/null
+++ b/src/char/char.h
@@ -0,0 +1,32 @@
+// $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;
+extern char db_path[];
+
+#endif
diff --git a/src/char/int_guild.c b/src/char/int_guild.c
new file mode 100644
index 000000000..a0ba5becb
--- /dev/null
+++ b/src/char/int_guild.c
@@ -0,0 +1,1446 @@
+// $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];
+ char path[1024];
+
+ sprintf(path, "%s%s", db_path, "/exp_guild.txt");
+ fp = fopen(path, "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 < 20; 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++;
+ }
+ }
+ if(c) 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 < 20; 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);
+
+ if(log_inter)
+ inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE,
+ name, g->guild_id, master->name, master->account_id);
+
+ return 0;
+}
+
+// ギルド情報要求
+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);
+
+ if(log_inter)
+ 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);
+ if(log_inter)
+ 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 000000000..8e9ce7681
--- /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 000000000..499ac025b
--- /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 000000000..34ed7e8f4
--- /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 000000000..ab563693c
--- /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 000000000..ea241024d
--- /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 000000000..ee88d2b63
--- /dev/null
+++ b/src/char/int_storage.c
@@ -0,0 +1,500 @@
+// $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 ",
+ p->storage[i].id,p->storage[i].nameid,p->storage[i].amount,p->storage[i].equip,
+ p->storage[i].identify,p->storage[i].refine,p->storage[i].attribute,
+ p->storage[i].card[0],p->storage[i].card[1],p->storage[i].card[2],p->storage[i].card[3]);
+ f++;
+ }
+
+ *(str_p++)='\t';
+
+ *str_p='\0';
+ if(!f)
+ str[0]=0;
+ return 0;
+}
+
+// 文字列を倉庫データに変換
+int storage_fromstr(char *str,struct storage *p)
+{
+ int tmp_int[256];
+ int set,next,len,i;
+
+ set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next);
+ p->storage_amount=tmp_int[1];
+
+ if(set!=2)
+ return 1;
+ if(str[next]=='\n' || str[next]=='\r')
+ return 0;
+ next++;
+ for(i=0;str[next] && str[next]!='\t';i++){
+ 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];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ else if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3],
+ &tmp_int[4], &tmp_int[5], &tmp_int[6],
+ &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) {
+ p->storage[i].id = tmp_int[0];
+ p->storage[i].nameid = tmp_int[1];
+ p->storage[i].amount = tmp_int[2];
+ p->storage[i].equip = tmp_int[3];
+ p->storage[i].identify = tmp_int[4];
+ p->storage[i].refine = tmp_int[5];
+ p->storage[i].attribute = tmp_int[6];
+ p->storage[i].card[0] = tmp_int[7];
+ p->storage[i].card[1] = tmp_int[8];
+ p->storage[i].card[2] = tmp_int[9];
+ p->storage[i].card[3] = tmp_int[10];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ else return 1;
+ }
+ return 0;
+}
+
+int guild_storage_tostr(char *str,struct guild_storage *p)
+{
+ int i,f=0;
+ char *str_p = str;
+ str_p+=sprintf(str,"%d,%d\t",p->guild_id,p->storage_amount);
+
+ for(i=0;i<MAX_GUILD_STORAGE;i++)
+ if( (p->storage[i].nameid) && (p->storage[i].amount) ){
+ str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d ",
+ p->storage[i].id,p->storage[i].nameid,p->storage[i].amount,p->storage[i].equip,
+ p->storage[i].identify,p->storage[i].refine,p->storage[i].attribute,
+ p->storage[i].card[0],p->storage[i].card[1],p->storage[i].card[2],p->storage[i].card[3]);
+ f++;
+ }
+
+ *(str_p++)='\t';
+
+ *str_p='\0';
+ if(!f)
+ str[0]=0;
+ return 0;
+}
+
+int guild_storage_fromstr(char *str,struct guild_storage *p)
+{
+ int tmp_int[256];
+ int set,next,len,i;
+
+ set=sscanf(str,"%d,%d%n",&tmp_int[0],&tmp_int[1],&next);
+ p->storage_amount=tmp_int[1];
+
+ if(set!=2)
+ return 1;
+ if(str[next]=='\n' || str[next]=='\r')
+ return 0;
+ next++;
+ for(i=0;str[next] && str[next]!='\t';i++){
+ 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];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ else if(sscanf(str + next, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n",
+ &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3],
+ &tmp_int[4], &tmp_int[5], &tmp_int[6],
+ &tmp_int[7], &tmp_int[8], &tmp_int[9], &tmp_int[10], &len) == 11) {
+ p->storage[i].id = tmp_int[0];
+ p->storage[i].nameid = tmp_int[1];
+ p->storage[i].amount = tmp_int[2];
+ p->storage[i].equip = tmp_int[3];
+ p->storage[i].identify = tmp_int[4];
+ p->storage[i].refine = tmp_int[5];
+ p->storage[i].attribute = tmp_int[6];
+ p->storage[i].card[0] = tmp_int[7];
+ p->storage[i].card[1] = tmp_int[8];
+ p->storage[i].card[2] = tmp_int[9];
+ p->storage[i].card[3] = tmp_int[10];
+ next += len;
+ if (str[next] == ' ')
+ next++;
+ }
+
+ else return 1;
+ }
+ return 0;
+}
+
+// アカウントから倉庫データインデックスを得る(新規倉庫追加可能)
+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 000000000..1b85831b4
--- /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 000000000..f85d37a49
--- /dev/null
+++ b/src/char/inter.c
@@ -0,0 +1,563 @@
+// $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", &reg->account_id, &n ) != 1 || reg->account_id <= 0)
+ return 1;
+
+ for(j = 0, p += n; j < ACCOUNT_REG_NUM; j++, p += n) {
+ if (sscanf(p, "%[^,],%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);
+ } else if(strcmpi(w1,"log_inter")==0) {
+ log_inter = atoi(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 000000000..897188054
--- /dev/null
+++ b/src/char/inter.h
@@ -0,0 +1,20 @@
+// $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];
+extern int log_inter;
+
+#endif
diff --git a/src/char_sql/GNUmakefile b/src/char_sql/GNUmakefile
new file mode 100644
index 000000000..c2e8afe35
--- /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/showmsg.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/db.h ../common/malloc.h ../common/showmsg.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 ../common/showmsg.h
+inter.o: inter.c inter.h int_party.h int_guild.h int_storage.h int_pet.h ../common/mmo.h char.h ../common/socket.h ../common/showmsg.h
+int_party.o: int_party.c int_party.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/timer.h ../common/db.h ../common/showmsg.h
+int_guild.o: int_guild.c int_guild.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/db.h ../common/showmsg.h
+int_storage.o: int_storage.c int_storage.h char.h itemdb.h ../common/showmsg.h
+int_pet.o: int_pet.c int_pet.h inter.h char.h ../common/mmo.h ../common/socket.h ../common/db.h ../common/showmsg.h
+strlib.o: strlib.c strlib.h ../common/showmsg.h
+itemdb.o: itemdb.c itemdb.h ../common/db.h ../common/mmo.h ../common/showmsg.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 000000000..23376a5bc
--- /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/showmsg.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/db.h ../common/malloc.h ../common/showmsg.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 ../common/showmsg.h
+inter.o: inter.c inter.h int_party.h int_guild.h int_storage.h int_pet.h ../common/mmo.h char.h ../common/socket.h ../common/showmsg.h
+int_party.o: int_party.c int_party.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/timer.h ../common/db.h ../common/showmsg.h
+int_guild.o: int_guild.c int_guild.h inter.h ../common/mmo.h char.h ../common/socket.h ../common/db.h ../common/showmsg.h
+int_storage.o: int_storage.c int_storage.h char.h itemdb.h ../common/showmsg.h
+int_pet.o: int_pet.c int_pet.h inter.h char.h ../common/mmo.h ../common/socket.h ../common/db.h ../common/showmsg.h
+strlib.o: strlib.c strlib.h ../common/showmsg.h
+itemdb.o: itemdb.c itemdb.h ../common/db.h ../common/mmo.h ../common/showmsg.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 000000000..a96bb59fa
--- /dev/null
+++ b/src/char_sql/char.c
@@ -0,0 +1,3017 @@
+// $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 "../common/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";
+int login_ip_set_ = 0;
+char login_ip_str[128];
+int login_ip;
+int login_port = 6900;
+int char_ip_set_ = 0;
+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]
+
+int log_char = 1; // loggin char or not [devil]
+int log_inter = 1; // loggin inter or not [devil]
+
+char lan_map_ip[128]; // Lan map ip added by kashy
+int subnetmaski[4]; // Subnetmask added by kashy
+char unknown_char_name[1024] = "Unknown";
+char db_path[1024]="db";
+
+//Added for Mugendai's I'm Alive mod
+int imalive_on=0;
+int imalive_time=60;
+//Added by Mugendai for GUI
+int flush_on=1;
+int flush_time=100;
+
+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;
+
+int console = 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];
+ 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];
+ 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];
+ 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];
+ 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` "
+ "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]);
+ 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]);
+ 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].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', `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].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`)"
+ " VALUES ( '%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]);
+ //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`)"
+ " VALUES ('%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]);
+ //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`"
+ "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]);
+ }
+ 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`"
+ "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]);
+ }
+ 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) {
+ int char_id, jobclass, skill_point, class;
+ sql_row = mysql_fetch_row(sql_res);
+ char_id = atoi(sql_row[0]);
+ jobclass = atoi(sql_row[1]);
+ skill_point = atoi(sql_row[2]);
+ 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;
+}
+
+// Console Command Parser [Wizputer]
+int parse_console(char *buf) {
+ char *type,*command;
+
+ type = (char *)malloc(64);
+ command = (char *)malloc(64);
+
+ memset(type,0,64);
+ memset(command,0,64);
+
+ printf("Console: %s\n",buf);
+
+ if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 )
+ sscanf(buf,"%[^\n]",type);
+
+ printf("Type of command: %s || Command: %s \n",type,command);
+
+ free(buf);
+ free(type);
+ free(command);
+
+ return 0;
+}
+
+// MAP send all
+int mapif_sendall(unsigned char *buf, unsigned int len) {
+ int i, c;
+ int fd;
+
+ c = 0;
+ for(i = 0; i < MAX_MAP_SERVERS; i++) {
+ if ((fd = server_fd[i]) >= 0) {
+ 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);
+ 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);
+ }else if(strcmpi(w1,"db_path")==0){
+ strcpy(db_path,w2);
+ //Map server option to use SQL db or not
+ }else if(strcmpi(w1,"use_sql_db")==0){ // added for sql item_db read for char server [Valaris]
+ 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) {
+ 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]);
+ 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]);
+ 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,"imalive_on")==0){ //Added by Mugendai for I'm Alive mod
+ imalive_on = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"imalive_time")==0){ //Added by Mugendai for I'm Alive mod
+ imalive_time = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"flush_on")==0){ //Added by Mugendai for GUI
+ flush_on = atoi(w2); //Added by Mugendai for GUI
+ } else if(strcmpi(w1,"flush_time")==0){ //Added by Mugendai for GUI
+ flush_time = atoi(w2); //Added by Mugendai for GUI
+ } else if(strcmpi(w1,"log_char")==0){ //log char or not [devil]
+ log_char = atoi(w2);
+ } else if (strcmpi(w1, "unknown_char_name") == 0) {
+ strcpy(unknown_char_name, w2);
+ unknown_char_name[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);
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ }
+ }
+ fclose(fp);
+
+//Read ItemDB
+ do_init_itemdb();
+
+ return 0;
+}
+
+//-----------------------------------------------------
+//I'm Alive Alert
+//Used to output 'I'm Alive' every few seconds
+//Intended to let frontends know if the app froze
+//-----------------------------------------------------
+int imalive_timer(int tid, unsigned int tick, int id, int data){
+ printf("I'm Alive\n");
+ return 0;
+}
+
+//-----------------------------------------------------
+//Flush stdout
+//stdout buffer needs flushed to be seen in GUI
+//-----------------------------------------------------
+int flush_timer(int tid, unsigned int tick, int id, int data){
+ fflush(stdout);
+ return 0;
+}
+
+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);
+
+ 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)
+ printf("Multiple interfaces detected.. using %s as our IP address\n", buf);
+ else
+ printf("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)
+ printf("Firewall detected.. edit lan_support.conf and char_athena.conf");
+ }
+
+ 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);
+
+ add_timer_func_list(map_anti_freeze_system, "map_anti_freeze_system");
+ //Added for Mugendais I'm Alive mod
+ if(imalive_on)
+ add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000);
+
+ //Added by Mugendai for GUI support
+ if(flush_on)
+ add_timer_interval(gettick()+10, flush_timer,0,0,flush_time);
+
+ 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();
+
+ if ( console ) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ 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 000000000..85f779db2
--- /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];
+};
+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 db_path[];
+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 000000000..3bbba5fd3
--- /dev/null
+++ b/src/char_sql/int_guild.c
@@ -0,0 +1,1597 @@
+//
+// 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<20;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++;
+ }
+ }
+ if(c) 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<20;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);
+
+ if(log_inter)
+ inter_log("guild %s (id=%d) created by master %s (id=%d)" RETCODE,
+ name, g->guild_id, master->name, master->account_id );
+
+
+ return 0;
+}
+// Return guild info to client
+int mapif_parse_GuildInfo(int fd,int guild_id)
+{
+ struct guild *g;
+ 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=guild_pt;
+ int i;
+ 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=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=guild_pt;
+ int i,alv,c;
+
+ if(g==NULL||g->guild_id<=0)
+ return 0;
+
+ inter_guild_fromsql(guild_id,g);
+
+ 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=guild_pt;
+ if(g==NULL)
+ return 0;
+ inter_guild_fromsql(guild_id,g);
+
+ // 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);
+
+ if(log_inter)
+ 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=guild_pt;
+// int dd=*((int *)data);
+ short dw=*((short *)data);
+
+ if(g==NULL||g->guild_id<=0)
+ return 0;
+ inter_guild_fromsql(guild_id,g);
+ 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=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=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=guild_pt;
+ int idx=skill_num-10000;
+
+ inter_guild_fromsql(guild_id,g);
+
+ 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=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=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);
+ if(log_inter)
+ 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 000000000..6a933d7d3
--- /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 000000000..12e2385c9
--- /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 000000000..131b9bf98
--- /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 000000000..1c94723f6
--- /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])+1; //should SET MAX existing PET ID + 1 [Lupus]
+ 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 000000000..d90154143
--- /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 000000000..7277466c1
--- /dev/null
+++ b/src/char_sql/int_storage.c
@@ -0,0 +1,360 @@
+//
+// 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];
+ 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];
+ 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` 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_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];
+ 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];
+ 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` 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_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 000000000..8aac5ccee
--- /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 000000000..2922674ff
--- /dev/null
+++ b/src/char_sql/inter.c
@@ -0,0 +1,577 @@
+//
+// 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) {
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ printf ("start reading interserver configuration: %s\n",cfgName);
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ printf("file not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line, 1020, fp)){
+ 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,"log_inter")==0){
+ log_inter = atoi(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 000000000..791797ff4
--- /dev/null
+++ b/src/char_sql/inter.h
@@ -0,0 +1,46 @@
+#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];
+
+extern int log_inter;
+
+#endif
diff --git a/src/char_sql/itemdb.c b/src/char_sql/itemdb.c
new file mode 100644
index 000000000..96f567a57
--- /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 000000000..a7b534231
--- /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 000000000..128b5b909
--- /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 000000000..5f54418ac
--- /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 000000000..61439d7de
--- /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 000000000..3321996e1
--- /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 000000000..d1a8ee0c6
--- /dev/null
+++ b/src/common/GNUmakefile
@@ -0,0 +1,14 @@
+txt sql all: core.o socket.o timer.o grfio.o db.o lock.o nullpo.o malloc.o showmsg.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
+showmsg.o: showmsg.c showmsg.h
+
+clean:
+ rm -f *.o
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644
index 000000000..831f3b5db
--- /dev/null
+++ b/src/common/Makefile
@@ -0,0 +1,14 @@
+txt sql all: core.o socket.o timer.o grfio.o db.o lock.o nullpo.o malloc.o showmsg.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
+showmsg.o: showmsg.c showmsg.h
+
+clean:
+ rm -f *.o
diff --git a/src/common/core.c b/src/common/core.c
new file mode 100644
index 000000000..fe17db2d8
--- /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 _WIN32
+#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 _WIN32
+ 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 000000000..ea9d9f375
--- /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 000000000..1f00ffd6b
--- /dev/null
+++ b/src/common/db.c
@@ -0,0 +1,501 @@
+// $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 "mmo.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 (strnicmp(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 000000000..523d36cf5
--- /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 000000000..28389f5d8
--- /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(strcmpi(filelist[hash].fn,fname)==0)
+ break;
+ }
+
+ return (hash>=0)? &filelist[hash] : NULL;
+}
+
+/*==========================================
+ * File List : Filelist add
+ *------------------------------------------
+ */
+#define FILELIST_ADDS 1024 // number increment of file lists `
+
+static FILELIST* filelist_add(FILELIST *entry)
+{
+ int hash;
+
+ if (filelist_entrys>=FILELIST_LIMIT) {
+ 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=strrchr(fname,'.'))!=NULL){
+ for(lop=0;lop<4;lop++) {
+ if(strcmpi(period_ptr,".gnd\0.gat\0.act\0.str"+lop*5)==0)
+ break;
+ }
+ srclen=getlong(grf_filelist+ofs2)-getlong(grf_filelist+ofs2+8)-715;
+ if(lop==4) {
+ for(lop=10,srccount=1;srclen>=lop;lop=lop*10,srccount++);
+ } else {
+ srccount=0;
+ }
+ } else {
+ srccount=0;
+ }
+
+ aentry.srclen = srclen;
+ aentry.srclen_aligned = getlong(grf_filelist+ofs2+4)-37579;
+ aentry.declen = getlong(grf_filelist+ofs2+8);
+ aentry.srcpos = getlong(grf_filelist+ofs2+13)+0x2e;
+ aentry.cycle = srccount;
+ aentry.type = type;
+ strncpy(aentry.fn,fname,sizeof(aentry.fn)-1);
+#ifdef GRFIO_LOCAL
+ aentry.gentry = -(gentry+1); // As Flag for making it a negative number carrying out the first time LocalFileCheck
+#else
+ aentry.gentry = gentry+1; // With no first time LocalFileCheck
+#endif
+ filelist_modify(&aentry);
+ }
+ ofs = ofs2 + 17;
+ }
+ 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 000000000..d9690f886
--- /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 000000000..7b9e1ad9c
--- /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 000000000..235e9eea3
--- /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 000000000..bc40c6a3f
--- /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 000000000..8caa59b6b
--- /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 000000000..9b349ff44
--- /dev/null
+++ b/src/common/mmo.h
@@ -0,0 +1,311 @@
+// $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" // _WIN32
+
+#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 20 // increased max guild skills because of new skills [Sara-chan]
+#define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris]
+#define MAX_GUILDLEVEL 50
+
+#define 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];
+};
+
+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];
+
+ // Friends list vars
+ int friend_id[20];
+ char friend_name[20][24];
+};
+
+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 _WIN32
+#ifndef strcmpi
+#define strcmpi strcasecmp
+#endif
+#ifndef stricmp
+#define stricmp strcasecmp
+#endif
+#ifndef strncmpi
+#define strncmpi strncasecmp
+#endif
+#ifndef strnicmp
+#define strnicmp strncasecmp
+#endif
+#ifndef strrchr
+#define strrchr rindex
+#endif
+
+#endif
+
+#endif // _MMO_H_
diff --git a/src/common/nullpo.c b/src/common/nullpo.c
new file mode 100644
index 000000000..0e2f53664
--- /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 000000000..6365cf02d
--- /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 _WIN32
+#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/showmsg.c b/src/common/showmsg.c
new file mode 100644
index 000000000..06daaa42e
--- /dev/null
+++ b/src/common/showmsg.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "showmsg.h"
+
+int _ShowMessage(const char *string, int flag){ // by MC Cameri
+ /*
+ _ShowMessage MUST be used instead of printf as of 10/24/2004.
+ Return: 0 = Successful, 1 = Failed.
+ */
+// int ret = 0;
+ char prefix[40];
+ char *output;
+ if (strlen(string) <= 0) {
+ ShowMessage("Empty string passed to ShowMessage().\n",MSG_ERROR);
+ return 1;
+ }
+ switch (flag) {
+ case MSG_STATUS: //Bright Green (To inform about good things)
+ strcpy(prefix,"\033[1;32m[Status]\033[0;0m: ");
+ break;
+/* //Do we really need this now? [MC Cameri]
+ case MSG_SQL: //Bright Violet (For dumping out anything related with SQL)
+ strcpy(prefix,"\033[1;35m[SQL]\033[0;0m: ");
+ break;
+*/
+ case MSG_INFORMATION: //Bright Blue (Variable information)
+ strcpy(prefix,"\033[1;34m[Info]\033[0;0m: ");
+ break;
+ case MSG_NOTICE: //Bright White (Less than a warning)
+ strcpy(prefix,"\033[1;29m[Notice]\033[0;0m: ");
+ break;
+ case MSG_WARNING: //Bright Yellow
+ strcpy(prefix,"\033[1;33m[Warning]\033[0;0m: ");
+ break;
+ case MSG_ERROR: //Bright Red (Regular errors)
+ strcpy(prefix,"\033[1;31m[Error]\033[0;0m: ");
+ break;
+ case MSG_FATALERROR: //Bright Red (Fatal errors, abort(); if possible)
+ strcpy(prefix,"\033[1;31m[Fatal Error]\033[0;0m: ");
+ break;
+ default:
+ ShowMessage("In function ShowMessage() -> Invalid flag passed.\n",MSG_ERROR);
+ return 1;
+ }
+ output = (char*)malloc(sizeof(char)*(strlen(prefix)+strlen(string))+1);
+ if (output == NULL) {
+ return 1;
+// abort(); // Kill server?
+ }
+ strcpy(output,prefix);
+ strcat(output,string);
+ printf(output);
+ fflush(stdout);
+/*
+ if ((core_config.debug_output_level > -1) && (flag >= core_config.debug_output_level)) {
+ FILE *fp;
+ fp=fopen(OUTPUT_MESSAGES_LOG,"a");
+ if (fp == NULL) {
+ printf("\033[1;31m[Error]\033[0;0m: Could not open \033[1;29m%s\033[0;0m, file not found.\n",OUTPUT_MESSAGES_LOG);
+ fflush(stdout);
+ return;
+ }
+ StripColor(output);
+ strcpy(output,"\r");
+ fwrite(output,strlen(output),1,fp);
+ fclose(fp);
+ }
+*/
+ return 0;
+}
diff --git a/src/common/showmsg.h b/src/common/showmsg.h
new file mode 100644
index 000000000..9ecd90ec3
--- /dev/null
+++ b/src/common/showmsg.h
@@ -0,0 +1,48 @@
+#ifndef _SHOWMSG_H_
+#define _SHOWMSG_H_
+
+/* MSG_XX */
+ #define ShowMsg(string,flag) _ShowMessage(string,flag)
+ #define DisplayMsg(string,flag) _ShowMessage(string,flag)
+ #define ShowMessage(string,flag) _ShowMessage(string,flag)
+
+/* MSG_STATUS */
+ #define ShowStatus(string) _ShowMessage(string,MSG_STATUS)
+ #define DisplayStatus(string) _ShowMessage(string,MSG_STATUS)
+
+/* MSG_SQL*/
+// #define ShowSQL(string) _ShowMessage(string,MSG_SQL)
+// #define DisplaySQL(string) _ShowMessage(string,MSG_SQL)
+
+/* MSG_INFORMATION */
+ #define ShowInfo(string) _ShowMessage(string,MSG_INFORMATION)
+ #define DisplayInfo(string) _ShowMessage(string,MSG_INFORMATION)
+// #define ShowInformation(string) _ShowMessage(string,MSG_INFORMATION)
+// #define DisplayInformation(string) _ShowMessage(string,MSG_INFORMATION)
+
+/* MSG_NOTICE */
+ #define ShowNotice(string) _ShowMessage(string,MSG_NOTICE)
+ #define DisplayNotice(string) _ShowMessage(string,MSG_NOTICE)
+
+/* */
+ #define ShowWarning(string) _ShowMessage(string,MSG_WARNING)
+ #define DisplayWarning(string) _ShowMessage(string,MSG_WARNING)
+// #define Warn(string) _ShowMessage(string,MSG_WARNING)
+
+/* MSG_ERROR */
+ #define ShowError(string) _ShowMessage(string,MSG_ERROR)
+ #define DisplayError(string) _ShowMessage(string,MSG_ERROR)
+// #define OutputError(string) _ShowMessage(string,MSG_ERROR)
+
+/* MSG_FATALERROR */
+ #define ShowFatalError(string) _ShowMessage(string,MSG_FATALERROR)
+ #define DisplayFatalError(string) _ShowMessage(string,MSG_ERROR)
+// #define Terminate(string) _ShowMessage(string,MSG_FATALERROR)
+// #define Kill(string) _ShowMessage(string,MSG_FATALERROR)
+// #define AbortEx(string) _ShowMessage(string,MSG_FATALERROR)
+
+enum msg_type {MSG_STATUS,/* MSG_SQL, */MSG_INFORMATION,MSG_NOTICE,MSG_WARNING,MSG_ERROR,MSG_FATALERROR};
+
+extern int _ShowMessage(const char *string, int flag);
+
+#endif
diff --git a/src/common/socket.c b/src/common/socket.c
new file mode 100644
index 000000000..c0d85b5ff
--- /dev/null
+++ b/src/common/socket.c
@@ -0,0 +1,594 @@
+// original : core.c 2003/02/26 18:03:12 Rev 1.7
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+#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 <net/if.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/ioctl.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;
+
+#ifndef TCP_FRAME_LEN
+#define TCP_FRAME_LEN 1053
+#endif
+
+struct socket_data *session[FD_SETSIZE];
+
+static int null_parse(int fd);
+static int (*default_func_parse)(int) = null_parse;
+
+static int null_console_parse(char *buf);
+static int (*default_console_parse)(char*) = null_console_parse;
+
+/*======================================
+ * CORE : Set function
+ *--------------------------------------
+ */
+void set_defaultparse(int (*defaultparse)(int))
+{
+ default_func_parse = defaultparse;
+}
+
+#ifdef NSOCKET
+static void setsocketopts(int fd)
+{
+ int yes = 1; // reuse fix
+
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes);
+#ifdef SO_REUSEPORT
+ setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes);
+#endif
+ setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes);
+
+ setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &wfifo_size , sizeof(rfifo_size ));
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rfifo_size , sizeof(rfifo_size ));
+}
+
+#endif /* NSOCKET */
+/*======================================
+ * 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 _WIN32
+ 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 _WIN32
+ 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;
+#ifndef NSOCKET
+ int yes = 1; // reuse fix
+#endif /* not NSOCKET */
+
+ //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;
+
+#ifndef NSOCKET
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes);
+#ifdef SO_REUSEPORT
+ setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes);
+#endif
+ setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes);
+#else /* NSOCKET */
+ setsocketopts(fd);
+#endif /* NSOCKET */
+
+ if(fd==-1)
+ perror("accept");
+ else
+ FD_SET(fd,&readfds);
+
+#ifdef _WIN32
+ {
+ 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;
+#ifndef NSOCKET
+ int yes = 1; // reuse fix
+#endif /* not NSOCKET */
+
+ fd = socket( AF_INET, SOCK_STREAM, 0 );
+ if(fd_max<=fd) fd_max=fd+1;
+
+#ifdef _WIN32
+ {
+ unsigned long val = 1;
+ ioctlsocket(fd, FIONBIO, &val);
+ }
+#else
+ result = fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+
+#ifndef NSOCKET
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes);
+#ifdef SO_REUSEPORT
+ setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes);
+#endif
+ setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes);
+#else /* NSOCKET */
+ setsocketopts(fd);
+#endif /* NSOCKET */
+
+ 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;
+}
+
+// Console Reciever [Wizputer]
+int console_recieve(int i) {
+ int n;
+ char *buf;
+
+ CREATE(buf, char , 64);
+
+ memset(buf,0,sizeof(64));
+
+ n = read(0, buf , 64);
+ if ( n < 0 )
+ printf("Console input read error\n");
+ else
+ session[0]->func_console(buf);
+ return 0;
+}
+
+void set_defaultconsoleparse(int (*defaultparse)(char*))
+{
+ default_console_parse = defaultparse;
+}
+
+static int null_console_parse(char *buf)
+{
+ printf("null_console_parse : %s\n",buf);
+ return 0;
+}
+
+// Console Input [Wizputer]
+int start_console(void) {
+ FD_SET(0,&readfds);
+
+ CREATE(session[0], struct socket_data, 1);
+ if(session[0]==NULL){
+ printf("out of memory : start_console\n");
+ exit(1);
+ }
+
+ memset(session[0],0,sizeof(*session[0]));
+
+ session[0]->func_recv = console_recieve;
+ session[0]->func_console = default_console_parse;
+
+ return 0;
+}
+
+int make_connection(long ip,int port)
+{
+ struct sockaddr_in server_address;
+ int fd;
+ int result;
+#ifndef NSOCKET
+ int yes = 1; // reuse fix
+#endif /* not NSOCKET */
+
+ fd = socket( AF_INET, SOCK_STREAM, 0 );
+#ifndef NSOCKET
+ if(fd_max<=fd) fd_max=fd+1;
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof yes);
+#ifdef SO_REUSEPORT
+ setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&yes,sizeof yes);
+#endif
+ setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char *)&yes,sizeof yes);
+#else /* NSOCKET */
+ if(fd_max<=fd)
+ fd_max=fd+1;
+
+ setsocketopts(fd);
+#endif /* NSOCKET */
+
+ server_address.sin_family = AF_INET;
+ server_address.sin_addr.s_addr = ip;
+ server_address.sin_port = htons(port);
+
+#ifdef _WIN32
+ {
+ 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];
+#ifdef NSOCKET
+ if (s == NULL || s->wdata == NULL)
+ return 0;
+#endif /* NSOCKET */
+ 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);
+#ifdef NSOCKET
+ if (s->wdata_size > (TCP_FRAME_LEN))
+ send_from_fifo(fd);
+#endif /* NSOCKET */
+ 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;
+}
+
+
+unsigned int addr_[16]; // ip addresses of local host (host byte order)
+unsigned int naddr_ = 0; // # of ip addresses
+
+int Net_Init(void)
+{
+#ifdef _WIN32
+ char** a;
+ unsigned int i;
+ char fullhost[255];
+ struct hostent* hent;
+
+ /* Start up the windows networking */
+ WSADATA wsaData;
+
+ if ( WSAStartup(WINSOCK_VERSION, &wsaData) != 0 ) {
+ printf("SYSERR: WinSock not available!\n");
+ exit(1);
+ }
+
+ if(gethostname(fullhost, sizeof(fullhost)) == SOCKET_ERROR) {
+ printf("Ugg.. no hostname defined!\n");
+ return 0;
+ }
+
+ // XXX This should look up the local IP addresses in the registry
+ // instead of calling gethostbyname. However, the way IP addresses
+ // are stored in the registry is annoyingly complex, so I'll leave
+ // this as T.B.D.
+ hent = gethostbyname(fullhost);
+ if (hent == NULL) {
+ printf("Cannot resolve our own hostname to a IP address");
+ return 0;
+ }
+
+ a = hent->h_addr_list;
+ for(i = 0; a[i] != 0 && i < 16; ++i) {
+ unsigned long addr1 = ntohl(*(unsigned long*) a[i]);
+ addr_[i] = addr1;
+ }
+ naddr_ = i;
+#else
+ int pos;
+ int fdes = socket(AF_INET, SOCK_STREAM, 0);
+ char buf[16 * sizeof(struct ifreq)];
+ struct ifconf ic;
+
+ // The ioctl call will fail with Invalid Argument if there are more
+ // interfaces than will fit in the buffer
+ ic.ifc_len = sizeof(buf);
+ ic.ifc_buf = buf;
+ if(ioctl(fdes, SIOCGIFCONF, &ic) == -1) {
+ printf("SIOCGIFCONF failed!\n");
+ return 0;
+ }
+
+ for(pos = 0; pos < ic.ifc_len;)
+ {
+ struct ifreq * ir = (struct ifreq *) (ic.ifc_buf + pos);
+
+ struct sockaddr_in * a = (struct sockaddr_in *) &(ir->ifr_addr);
+
+ if(a->sin_family == AF_INET) {
+ u_long ad = ntohl(a->sin_addr.s_addr);
+ if(ad != INADDR_LOOPBACK) {
+ addr_[naddr_ ++] = ad;
+ if(naddr_ == 16)
+ break;
+ }
+ }
+
+#if defined(_AIX) || defined(__APPLE__)
+ pos += ir->ifr_addr.sa_len; // For when we port athena to run on Mac's :)
+ pos += sizeof(ir->ifr_name);
+#else
+ pos += sizeof(struct ifreq);
+#endif
+ }
+
+#endif
+
+ return(0);
+}
diff --git a/src/common/socket.h b/src/common/socket.h
new file mode 100644
index 000000000..c9b3b1077
--- /dev/null
+++ b/src/common/socket.h
@@ -0,0 +1,104 @@
+// original : core.h 2003/03/14 11:55:25 Rev 1.4
+
+#ifndef _SOCKET_H_
+#define _SOCKET_H_
+
+#include <stdio.h>
+
+#ifdef _WIN32
+#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);
+ int (*func_console)(char*);
+ void* session_data;
+};
+
+// Data prototype declaration
+
+#ifdef _WIN32
+
+ #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);
+
+int start_console(void);
+
+void set_defaultparse(int (*defaultparse)(int));
+void set_defaultconsoleparse(int (*defaultparse)(char*));
+
+int Net_Init(void);
+
+extern unsigned int addr_[16]; // ip addresses of local host (host byte order)
+extern unsigned int naddr_; // # of ip addresses
+
+
+#endif // _SOCKET_H_
diff --git a/src/common/timer.c b/src/common/timer.c
new file mode 100644
index 000000000..b2289e4f8
--- /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 _WIN32
+#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(_WIN32)
+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 000000000..1a9e68ca5
--- /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 000000000..00aed7ee4
--- /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 _WIN32
+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 000000000..e350df5e8
--- /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 _WIN32
+ 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 000000000..25d2847b4
--- /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 000000000..879edaeea
--- /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/showmsg.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h ../common/showmsg.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 000000000..74f211f22
--- /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/showmsg.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h ../common/showmsg.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 000000000..8bc8f7599
--- /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(&timestamp));
+ 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(&timestamp));
+ 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(&timestamp));
+ 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(&timestamp));
+ 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 000000000..314c4b22e
--- /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 000000000..d1632127e
--- /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 000000000..da6b509bd
--- /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 000000000..300737cbf
--- /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/showmsg.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 ../common/showmsg.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 000000000..d48c0cc24
--- /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/showmsg.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 ../common/showmsg.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 000000000..4d75dbd60
--- /dev/null
+++ b/src/login/login.c
@@ -0,0 +1,3775 @@
+// $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 "../common/core.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "login.h"
+#include "../common/mmo.h"
+#include "../common/version.h"
+#include "../common/db.h"
+#include "../common/lock.h"
+
+#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;
+
+//Added for Mugendai's I'm Alive mod
+int imalive_on=0;
+int imalive_time=60;
+//Added by Mugendai for GUI
+int flush_on=1;
+int flush_time=100;
+
+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;
+
+int console = 0;
+
+//------------------------------
+// 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(&timestamp);
+ timestamp_temp = mktime(tmtime);
+ if (timestamp_temp != -1 && (timestamp_temp + 3600) >= timestamp) // check possible value and overflow (and avoid summer/winter hour)
+ auth_dat[i].connect_until_time = timestamp_temp;
+ else
+ auth_dat[i].connect_until_time = 0; // unlimited
+ }
+
+ strncpy(auth_dat[i].last_ip, "-", 16);
+
+ strncpy(auth_dat[i].memo, "-", 255);
+
+ auth_dat[i].account_reg2_num = 0;
+
+ auth_num++;
+
+ return (account_id_count - 1);
+}
+
+//---------------------------------------
+// Check/authentification of a connection
+//---------------------------------------
+int mmo_auth(struct mmo_account* account, int fd) {
+ 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 1; // 1 = Incorrect Password
+ }
+ 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(&timestamp);
+ tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6);
+ tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8);
+ tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10);
+ tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12);
+ tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14);
+ tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16);
+ timestamp = mktime(tmtime);
+ if (timestamp != -1) {
+ if (timestamp <= time(NULL))
+ timestamp = 0;
+ if (auth_dat[i].ban_until_time != timestamp) {
+ if (timestamp != 0) {
+ unsigned char buf[16];
+ char tmpstr[2048];
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ login_log("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s)." RETCODE,
+ server[id].name, acc, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip);
+ WBUFW(buf,0) = 0x2731;
+ WBUFL(buf,2) = auth_dat[i].account_id;
+ WBUFB(buf,6) = 1; // 0: change of statut, 1: ban
+ WBUFL(buf,7) = 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(&timestamp));
+ i = search_account_index(account_name);
+ if (i != -1) {
+ memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24);
+ login_log("'ladmin': Change of a validity limit (account: %s, new validity: %d (%s), ip: %s)" RETCODE,
+ auth_dat[i].userid, timestamp, (timestamp == 0 ? "unlimited" : tmpstr), ip);
+ auth_dat[i].connect_until_time = timestamp;
+ WFIFOL(fd,2) = auth_dat[i].account_id;
+ mmo_auth_sync();
+ } else {
+ memcpy(WFIFOP(fd,6), account_name, 24);
+ login_log("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %d (%s), ip: %s)" RETCODE,
+ account_name, timestamp, (timestamp == 0 ? "unlimited" : tmpstr), ip);
+ }
+ WFIFOL(fd,30) = 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(&timestamp));
+ i = search_account_index(account_name);
+ if (i != -1) {
+ memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24);
+ WFIFOL(fd,2) = auth_dat[i].account_id;
+ login_log("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %d (%s), ip: %s)" RETCODE,
+ auth_dat[i].userid, timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip);
+ if (auth_dat[i].ban_until_time != timestamp) {
+ if (timestamp != 0) {
+ unsigned char buf[16];
+ WBUFW(buf,0) = 0x2731;
+ WBUFL(buf,2) = auth_dat[i].account_id;
+ WBUFB(buf,6) = 1; // 0: change of statut, 1: ban
+ WBUFL(buf,7) = 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(&timestamp);
+ tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26);
+ tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28);
+ tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30);
+ tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32);
+ tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34);
+ tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36);
+ timestamp = mktime(tmtime);
+ if (timestamp != -1) {
+ if (timestamp <= time(NULL))
+ timestamp = 0;
+ strftime(tmpstr, 24, date_format, localtime(&timestamp));
+ login_log("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" RETCODE,
+ auth_dat[i].userid, (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), timestamp, (timestamp == 0 ? "no banishment" : tmpstr), ip);
+ if (auth_dat[i].ban_until_time != timestamp) {
+ if (timestamp != 0) {
+ unsigned char buf[16];
+ WBUFW(buf,0) = 0x2731;
+ WBUFL(buf,2) = auth_dat[i].account_id;
+ WBUFB(buf,6) = 1; // 0: change of statut, 1: ban
+ WBUFL(buf,7) = 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(&timestamp);
+ tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,26);
+ tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,28);
+ tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,30);
+ tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,32);
+ tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,34);
+ tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,36);
+ timestamp = mktime(tmtime);
+ if (timestamp != -1) {
+ strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].connect_until_time));
+ strftime(tmpstr2, 24, date_format, localtime(&timestamp));
+ login_log("'ladmin': Adjustment of a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %d (%s), ip: %s)" RETCODE,
+ auth_dat[i].userid, auth_dat[i].connect_until_time, (auth_dat[i].connect_until_time == 0 ? "unlimited" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), timestamp, (timestamp == 0 ? "unlimited" : tmpstr2), ip);
+ auth_dat[i].connect_until_time = timestamp;
+ mmo_auth_sync();
+ WFIFOL(fd,30) = (unsigned long)auth_dat[i].connect_until_time;
+ } else {
+ strftime(tmpstr, 24, date_format, localtime(&auth_dat[i].connect_until_time));
+ login_log("'ladmin': Impossible to adjust a validity limit (account: %s, %d (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)" RETCODE,
+ auth_dat[i].userid, auth_dat[i].connect_until_time, (auth_dat[i].connect_until_time == 0 ? "unlimited" : tmpstr), (short)RFIFOW(fd,26), (short)RFIFOW(fd,28), (short)RFIFOW(fd,30), (short)RFIFOW(fd,32), (short)RFIFOW(fd,34), (short)RFIFOW(fd,36), ip);
+ WFIFOL(fd,30) = 0;
+ }
+ }
+ } else {
+ memcpy(WFIFOP(fd,6), account_name, 24);
+ login_log("'ladmin': Attempt to adjust the validity limit of an unknown account (account: %s, ip: %s)" RETCODE,
+ account_name, ip);
+ WFIFOL(fd,30) = 0;
+ }
+ }
+ WFIFOSET(fd,34);
+ RFIFOSKIP(fd,38);
+ break;
+
+ case 0x7952: // Request about informations of an account (by account name)
+ if (RFIFOREST(fd) < 26)
+ return 0;
+ WFIFOW(fd,0) = 0x7953;
+ WFIFOL(fd,2) = -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;
+}
+
+// Console Command Parser [Wizputer]
+int parse_console(char *buf) {
+ char *type,*command;
+
+ type = (char *)malloc(64);
+ command = (char *)malloc(64);
+
+ memset(type,0,64);
+ memset(command,0,64);
+
+ printf("Console: %s\n",buf);
+
+ if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 )
+ sscanf(buf,"%[^\n]",type);
+
+ printf("Type of command: %s || Command: %s \n",type,command);
+
+ free(buf);
+ free(type);
+ free(command);
+
+ 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);
+ } else if(strcmpi(w1,"imalive_on")==0) { //Added by Mugendai for I'm Alive mod
+ imalive_on = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"imalive_time")==0) { //Added by Mugendai for I'm Alive mod
+ imalive_time = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"flush_on")==0) { //Added by Mugendai for GUI
+ flush_on = atoi(w2); //Added by Mugendai for GUI
+ } else if(strcmpi(w1,"flush_time")==0) { //Added by Mugendai for GUI
+ flush_time = atoi(w2); //Added by Mugendai for GUI
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ }
+ }
+ }
+ 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));
+ }
+ }
+}
+
+//-----------------------------------------------------
+//I'm Alive Alert
+//Used to output 'I'm Alive' every few seconds
+//Intended to let frontends know if the app froze
+//-----------------------------------------------------
+int imalive_timer(int tid, unsigned int tick, int id, int data){
+ printf("I'm Alive\n");
+ return 0;
+}
+
+//-----------------------------------------------------
+//Flush stdout
+//stdout buffer needs flushed to be seen in GUI
+//-----------------------------------------------------
+int flush_timer(int tid, unsigned int tick, int id, int data){
+ fflush(stdout);
+ return 0;
+}
+
+//--------------------------------------
+// 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;
+
+ //Added for Mugendais I'm Alive mod
+ if(imalive_on)
+ add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000);
+
+ //Added by Mugendai for GUI support
+ if(flush_on)
+ add_timer_interval(gettick()+10, flush_timer,0,0,flush_time);
+
+ 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
+
+ if ( console ) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ login_log("The login-server is ready (Server is listening on the port %d)." RETCODE, login_port);
+
+ 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 000000000..b810a5412
--- /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 000000000..abe868d1b
--- /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 000000000..87fa79f76
--- /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 000000000..6fbb30865
--- /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/showmsg.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h ../common/showmsg.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 000000000..f17a7a3c7
--- /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/showmsg.o
+COMMON_H = ../common/core.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/db.h ../common/malloc.h ../common/showmsg.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 000000000..50465eb3c
--- /dev/null
+++ b/src/login_sql/login.c
@@ -0,0 +1,1762 @@
+// $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;
+
+//Added for Mugendai's I'm Alive mod
+int imalive_on=0;
+int imalive_time=60;
+//Added by Mugendai for GUI
+int flush_on=1;
+int flush_time=100;
+
+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];
+
+int console = 0;
+
+//-----------------------------------------------------
+
+#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) {
+ int i, fd;
+
+ //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");
+
+ 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 BINARY `%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
+ {
+ int encpasswdok = 0;
+
+ if (atoi(sql_row[9]) == -3) {
+ //id is banned
+ mysql_free_result(sql_res);
+ return -3;
+ } else if (atoi(sql_row[9]) == -2) { //dynamic ban
+ //id is banned
+ mysql_free_result(sql_res);
+ //add IP list.
+ return -2;
+ }
+
+ if (use_md5_passwds) {
+ MD5_String(account->passwd,user_password);
+ } else {
+ jstrescapecpy(user_password, account->passwd);
+ }
+ printf("account id ok encval:%d\n",account->passwdenc);
+#ifdef PASSWORDENC
+ if (account->passwdenc > 0) {
+ int j = account->passwdenc;
+ printf ("start md5calc..\n");
+ if (j > 2)
+ j = 1;
+ do {
+ if (j == 1) {
+ sprintf(md5str, "%s%s", md5key,sql_row[2]);
+ } else if (j == 2) {
+ sprintf(md5str, "%s%s", sql_row[2], md5key);
+ } else
+ md5str[0] = 0;
+ 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 BINARY `%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 BINARY `%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(&timestamp);
+ tmtime->tm_year = tmtime->tm_year + (short)RFIFOW(fd,6);
+ tmtime->tm_mon = tmtime->tm_mon + (short)RFIFOW(fd,8);
+ tmtime->tm_mday = tmtime->tm_mday + (short)RFIFOW(fd,10);
+ tmtime->tm_hour = tmtime->tm_hour + (short)RFIFOW(fd,12);
+ tmtime->tm_min = tmtime->tm_min + (short)RFIFOW(fd,14);
+ tmtime->tm_sec = tmtime->tm_sec + (short)RFIFOW(fd,16);
+ timestamp = mktime(tmtime);
+ if (timestamp != -1) {
+ if (timestamp <= time(NULL))
+ timestamp = 0;
+ if (tmptime != timestamp) {
+ if (timestamp != 0) {
+ unsigned char buf[16];
+ WBUFW(buf,0) = 0x2731;
+ WBUFL(buf,2) = acc;
+ WBUFB(buf,6) = 1; // 0: change of statut, 1: ban
+ WBUFL(buf,7) = 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 BINARY `%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;
+ {
+ unsigned char* server_name;
+ 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]);
+ 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;
+}
+
+// Console Command Parser [Wizputer]
+int parse_console(char *buf) {
+ char *type,*command;
+
+ type = (char *)malloc(64);
+ command = (char *)malloc(64);
+
+ memset(type,0,64);
+ memset(command,0,64);
+
+ printf("Console: %s\n",buf);
+
+ if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 )
+ sscanf(buf,"%[^\n]",type);
+
+ printf("Type of command: %s || Command: %s \n",type,command);
+
+ free(buf);
+ free(type);
+ free(command);
+
+ 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){
+ 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);
+
+ {
+ 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,"imalive_on")==0) { //Added by Mugendai for I'm Alive mod
+ imalive_on = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"imalive_time")==0) { //Added by Mugendai for I'm Alive mod
+ imalive_time = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"flush_on")==0) { //Added by Mugendai for GUI
+ flush_on = atoi(w2); //Added by Mugendai for GUI
+ } else if(strcmpi(w1,"flush_time")==0) { //Added by Mugendai for GUI
+ flush_time = atoi(w2); //Added by Mugendai for GUI
+ }
+ 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);
+ }
+ else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ }
+ }
+ 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];
+ FILE *fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ printf("file not found: %s\n",cfgName);
+ exit(1);
+ }
+ printf("reading configure: %s\n", cfgName);
+ while(fgets(line, sizeof(line)-1, fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+ if (strcmpi(w1, "login_db") == 0) {
+ strcpy(login_db, w2);
+ }
+ //add for DB connection
+ else if(strcmpi(w1,"login_server_ip")==0){
+ strcpy(login_server_ip, w2);
+ 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");
+}
+
+
+//-----------------------------------------------------
+//I'm Alive Alert
+//Used to output 'I'm Alive' every few seconds
+//Intended to let frontends know if the app froze
+//-----------------------------------------------------
+int imalive_timer(int tid, unsigned int tick, int id, int data){
+ printf("I'm Alive\n");
+ return 0;
+}
+
+//-----------------------------------------------------
+//Flush stdout
+//stdout buffer needs flushed to be seen in GUI
+//-----------------------------------------------------
+int flush_timer(int tid, unsigned int tick, int id, int data){
+ fflush(stdout);
+ return 0;
+}
+
+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);
+
+ //Added for Mugendais I'm Alive mod
+ if(imalive_on)
+ add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000);
+
+ //Added by Mugendai for GUI support
+ if(flush_on)
+ add_timer_interval(gettick()+10, flush_timer,0,0,flush_time);
+
+
+ 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);
+
+ if (console) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ 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 000000000..3c7e06980
--- /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 000000000..3e43cd11f
--- /dev/null
+++ b/src/login_sql/make.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+ rsqlt=`rm -rf *.o`
+ gcc -c login.c -I/usr/local/include/mysql/
+ gcc -c md5calc.c -I/usr/local/include/mysql/
+ gcc -c strlib.c
+ gcc -o login-server login.o strlib.o md5calc.o ../common/core.o ../common/socket.o ../common/timer.o ../common/db.o -L/usr/local/lib/mysql/ -lmysqlclient -lz
diff --git a/src/login_sql/md5calc.c b/src/login_sql/md5calc.c
new file mode 100644
index 000000000..d8272c413
--- /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 000000000..04fb2d8c5
--- /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 000000000..fa02912f7
--- /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 000000000..b1cb79b71
--- /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 000000000..5d58a3a0c
--- /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 000000000..ed0ee2ca1
--- /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 000000000..a1a68d107
--- /dev/null
+++ b/src/map/GNUmakefile
@@ -0,0 +1,73 @@
+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 ../common/showmsg.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 sqlobj/log.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 ../common/showmsg.h
+txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+txtobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.h ../common/showmsg.h
+txtobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.h ../common/showmsg.h
+txtobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+
+sqlobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h log.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+sqlobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h log.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+sqlobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h log.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h log.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mail.o: mail.c mail.h ../common/showmsg.h
+sqlobj/log.o: log.c log.h map.h ../common/nullpo.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 000000000..cc5abc73c
--- /dev/null
+++ b/src/map/Makefile
@@ -0,0 +1,73 @@
+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 ../common/showmsg.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 sqlobj/log.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 ../common/showmsg.h
+txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+txtobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.h ../common/showmsg.h
+txtobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.h ../common/showmsg.h
+txtobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+
+sqlobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h log.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+sqlobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h log.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+sqlobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h log.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h log.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mail.o: mail.c mail.h ../common/showmsg.h
+sqlobj/log.o: log.c log.h map.h ../common/nullpo.h
+
+clean:
+ rm -rf *.o ../../map-server ../../map-server_sql sqlobj txtobj
diff --git a/src/map/Makefile.win32 b/src/map/Makefile.win32
new file mode 100644
index 000000000..df6782c84
--- /dev/null
+++ b/src/map/Makefile.win32
@@ -0,0 +1,93 @@
+# grab a copy of http://www.winimage.com/zLibDll/zlib122.zip
+# and put safely into a subdirectory someplace
+# then point ZLIBDIR at whereever you put it
+#
+
+all: txt sql
+
+txt: txtobj map-server
+
+sql: sqlobj map-server_sql
+
+txtobj:
+ mkdir txtobj
+
+sqlobj:
+ mkdir sqlobj
+
+ZLIBDIR = C:/athena/zlib122
+PACKETDEF = -DPACKETVER=5 -DNEW_006b
+# OPT = /MDd /D_DEBUG
+OPT =
+LINKOPT = /debug /SUBSYSTEM:CONSOLE
+# OPT = /O2
+CFLAGS = $(OPT) /nologo /I../common /I$(ZLIBDIR) $(PACKETDEF) /D_WIN32
+
+COMMON_OBJ = ../common/core.o ../common/socket.o ../common/timer.o ../common/grfio.o ../common/db.o ../common/lock.o ../common/nullpo.o ../common/malloc.o ../common/showmsg.o
+LIBS = "WSOCK32.LIB"
+
+# "WSOCK32.LIB" "USER32.LIB" "ADVAPI32.LIB" "MSVCRT.LIB" "OLDNAMES.LIB" "KERNEL32.LIB"
+
+TXTOBJS = txtobj/map.o txtobj/chrif.o txtobj/clif.o txtobj/pc.o txtobj/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) $(ZLIBDIR)/inflate.o $(ZLIBDIR)/adler32.o $(ZLIBDIR)/crc32.o $(ZLIBDIR)/inftrees.o $(ZLIBDIR)/zutil.o $(ZLIBDIR)/inffast.o
+
+map-server: $(TXTOBJS)
+ link $(LINKOPT) /out:../../$@.exe $(TXTOBJS) $(LIBS)
+
+map-server_sql: 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)
+ link $(LINKOPT) /out:../../$@.exe $> $(LIBS)
+
+txtobj/%.o: %.c
+ Cl /c $(CFLAGS) -DTXT_ONLY /Fo$@ $<
+
+sqlobj/%.o: %.c
+ Cl /c $(CFLAGS) /Fo$@ $<
+
+%.o: %.c
+ Cl /c $(CFLAGS) /Fo$@ $<
+
+txtobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+txtobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+txtobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+txtobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+txtobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+txtobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+txtobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+txtobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h ../common/mmo.h ../common/showmsg.h
+txtobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h ../common/mmo.h ../common/showmsg.h
+txtobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+txtobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+
+sqlobj/map.o: map.c map.h chrif.h clif.h npc.h pc.h mob.h chat.h skill.h itemdb.h storage.h party.h pet.h atcommand.h log.h ../common/core.h ../common/timer.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chrif.o: chrif.c map.h battle.h chrif.h clif.h intif.h pc.h npc.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/clif.o: clif.c map.h chrif.h clif.h mob.h intif.h pc.h npc.h itemdb.h chat.h script.h storage.h party.h guild.h atcommand.h pet.h atcommand.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/version.h ../common/showmsg.h
+sqlobj/pc.o: pc.c map.h clif.h intif.h pc.h npc.h mob.h itemdb.h battle.h skill.h script.h party.h guild.h pet.h trade.h storage.h chat.h vending.h log.h ../common/timer.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/npc.o: npc.c map.h npc.h clif.h pc.h script.h mob.h itemdb.h battle.h ../common/db.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/chat.o: chat.c map.h clif.h pc.h chat.h ../common/db.h ../common/mmo.h ../common/showmsg.h
+sqlobj/path.o: path.c map.h battle.h ../common/mmo.h ../common/showmsg.h
+sqlobj/itemdb.o: itemdb.c map.h battle.h itemdb.h ../common/db.h ../common/grfio.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mob.o: mob.c map.h clif.h intif.h pc.h mob.h skill.h battle.h npc.h itemdb.h log.h ../common/timer.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/script.o: script.c itemdb.h map.h pc.h mob.h clif.h intif.h npc.h script.h storage.h skill.h pet.h battle.h log.h ../common/timer.h ../common/socket.h ../common/db.h ../common/mmo.h ../common/lock.h ../common/showmsg.h
+sqlobj/storage.o: storage.c itemdb.h pc.h clif.h intif.h storage.h guild.h ../common/mmo.h ../common/db.h ../common/showmsg.h
+sqlobj/skill.o: skill.c skill.h map.h clif.h pc.h mob.h battle.h itemdb.h script.h log.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/atcommand.o: atcommand.c atcommand.h itemdb.h pc.h map.h skill.h clif.h mob.h intif.h battle.h storage.h guild.h pet.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/battle.o: battle.c battle.h skill.h map.h mob.h pc.h pet.h guild.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/intif.o: intif.c intif.h chrif.h clif.h party.h guild.h storage.h map.h battle.h pet.h ../common/socket.h ../common/mmo.h ../common/showmsg.h
+sqlobj/trade.o: trade.c trade.h clif.h itemdb.h map.h pc.h npc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/party.o: party.c party.h clif.h intif.h pc.h map.h battle.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/vending.o: vending.c vending.h clif.h itemdb.h map.h pc.h log.h ../common/mmo.h ../common/showmsg.h
+sqlobj/guild.o: guild.c guild.h storage.h battle.h clif.h intif.h pc.h npc.h map.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/pet.o: pet.c pet.h map.h clif.h chrif.h intif.h pc.h itemdb.h battle.h mob.h npc.h script.h ../common/db.h ../common/socket.h ../common/timer.h ../common/mmo.h ../common/showmsg.h
+sqlobj/mail.o: mail.c mail.h ../common/showmsg.h
+sqlobj/log.o: log.c log.h map.h ../common/nullpo.h
+
+clean:
+ rm -rf *.o ../../map-server ../../map-server_sql sqlobj txtobj
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
new file mode 100644
index 000000000..a4c6fc745
--- /dev/null
+++ b/src/map/atcommand.c
@@ -0,0 +1,7660 @@
+// $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(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(send); // by davidsiaw
+ATCOMMAND_FUNC(setbattleflag); // by MouseJstr
+ATCOMMAND_FUNC(unmute); // [Valaris]
+ATCOMMAND_FUNC(uptime); // by MC Cameri
+
+#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_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_Send, "@send", 60, atcommand_send },
+ { AtCommand_SetBattleFlag, "@setbattleflag", 60, atcommand_setbattleflag },
+ { AtCommand_UnMute, "@unmute", 60, atcommand_unmute }, // [Valaris]
+ { AtCommand_UpTime, "@uptime", 0, atcommand_uptime }, // by MC Cameri
+
+#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
+ *------------------------------------------
+ */
+
+/*==========================================
+ * @send (used for testing packet sends from the client)
+ *------------------------------------------
+ */
+int atcommand_send(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int a = atoi(message);
+ if (a)
+ {
+ unsigned char buf[1024];
+ switch(a)
+ {
+ case 1:
+ WBUFW(buf,0)=0x18d;
+ case 2:
+ WBUFW(buf,0)=0x18e;
+ case 3:
+ WBUFW(buf,0)=0x18f;
+ case 4:
+ WBUFW(buf,0)=0x190;
+ }
+
+
+ }
+ return 0;
+}
+
+/*==========================================
+ * @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 = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message)
+ return -1;
+ memset(character, '\0', sizeof character);
+ if (sscanf(message, "%99[^\n]", character) < 1)
+ return -1;
+ if(strncmp(sd->status.name,character,24)==0)
+ return -1;
+
+ intif_where(sd->status.account_id,character);
+
+ if ((pl_sd = map_nick2sd(character)) == NULL) {
+ snprintf(output, sizeof output, "%s %d %d",
+ sd->mapname, sd->bl.x, sd->bl.y);
+ clif_displaymessage(fd, output);
+ return -1;
+ }
+ snprintf(output, sizeof output, "%s %s %d %d",
+ character, pl_sd->mapname, pl_sd->bl.x, pl_sd->bl.y);
+ clif_displaymessage(fd, output);
+
+ 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 = NULL;
+
+ nullpo_retr(-1, sd);
+
+ 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;
+ }
+
+ memset(character, '\0', sizeof character);
+ if (sscanf(message, "%99[^\n]", character) < 1)
+ return -1;
+ if(strncmp(sd->status.name,character,24)==0)
+ return -1;
+
+ intif_jumpto(sd->status.account_id,character);
+ 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", &param1, &param2, &param3) < 1 || param1 < 0 || param2 < 0 || param3 < 0) {
+ clif_displaymessage(fd, "Please, enter at least a option (usage: @option <param1:0+> <param2:0+> <param3:0+>).");
+ return -1;
+ }
+
+ sd->opt1 = param1;
+ sd->opt2 = param2;
+ if (!(sd->status.option & CART_MASK) && param3 & CART_MASK) {
+ 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 = NULL;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%99[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @recall <char name>).");
+ return -1;
+ }
+
+ memset(character, '\0', sizeof character);
+ if(sscanf(message, "%99[^\n]", character) < 1)
+ return -1;
+ if(strncmp(sd->status.name,character,24)==0)
+ return -1;
+
+ intif_charmovereq(sd,character,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].attribute == 1) {
+ sd->status.inventory[i].attribute = 0;
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ count++;
+ }
+ }
+
+ if (count > 0) {
+ clif_misceffect(&sd->bl, 3);
+ clif_equiplist(sd);
+ clif_displaymessage(fd, msg_table[107]); // All items have been repaired.
+ } else {
+ clif_displaymessage(fd, msg_table[108]); // No item need to be repaired.
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Removed @nuke for now in favor of alchemist marine sphere skill [Valaris]
+int atcommand_nuke(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ 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;
+}
+
+/*==========================================
+ * @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 %[^\r\n]", &newlev, user) != 2) {
+ clif_displaymessage(fd, "usage: @adjgmlvl <lvl> <user>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) user)) == NULL)
+ return -1;
+
+ pc_set_gm_level(pl_sd->status.account_id, newlev);
+
+ return 0;
+}
+
+
+/*==========================================
+ * @trade by [MouseJstr]
+ *
+ * Open a trade window with a remote player
+ *
+ * If I have to jump to a remote player one more time, I am
+ * gonna scream!
+ *------------------------------------------
+ */
+int
+atcommand_trade(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+
+ if (!message || !*message)
+ return -1;
+ if((pl_sd=map_nick2sd((char *) message)) != NULL) {
+ trade_traderequest(sd, pl_sd->bl.id);
+ return 0;
+ }
+ return -1;
+}
+
+/*==========================================
+ * @setbattleflag by [MouseJstr]
+ *
+ * set a battle_config flag without having to reboot
+ */
+int
+atcommand_setbattleflag(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char flag[128], value[128];
+
+ if (!message || !*message || sscanf(message, "%s %s", flag, value) != 2) {
+ clif_displaymessage(fd, "usage: @setbattleflag <flag> <value>.");
+ return -1;
+ }
+
+ if (battle_set_value(flag, value) == 0)
+ clif_displaymessage(fd, "unknown battle_config flag");
+ else
+ clif_displaymessage(fd, "battle_config set as requested");
+
+ return 0;
+}
+
+
+/*===========================
+ * @unmute [Valaris]
+ *===========================
+*/
+int atcommand_unmute(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ 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;
+}
+
+/*==========================================
+ * @uptime by MC Cameri
+ *------------------------------------------
+ */
+int
+atcommand_uptime(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char output[200];
+ long seconds = 0, day = 24*60*60, hour = 60*60,
+ minute = 60, days = 0, hours = 0, minutes = 0;
+
+ seconds = (gettick()-ticks)/CLOCKS_PER_SEC;
+ days = seconds/day;
+ seconds -= (seconds/day>0)?(seconds/day)*day:0;
+ hours = seconds/hour;
+ seconds -= (seconds/hour>0)?(seconds/hour)*hour:0;
+ minutes = seconds/minute;
+ seconds -= (seconds/minute>0)?(seconds/minute)*minute:0;
+
+ snprintf(output, sizeof(output), msg_table[245], days, hours, minutes, seconds);
+ clif_displaymessage(fd,output);
+ 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)
+{
+ char name[24],text[80];
+
+ if(!battle_config.mail_system)
+ return 0;
+
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message) {
+ clif_displaymessage(sd->fd,"You must specify a recipient and a message.");
+ return 0;
+ }
+
+ if ((sscanf(message, "\"%[^\"]\" %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 000000000..72d57af6a
--- /dev/null
+++ b/src/map/atcommand.h
@@ -0,0 +1,243 @@
+// $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_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, // MouseJstr
+ AtCommand_AdjCmdLvl, // MouseJstr
+ AtCommand_Trade, // MouseJstr
+ AtCommand_Send,
+ AtCommand_SetBattleFlag,
+ AtCommand_UnMute,
+ AtCommand_UpTime,
+ // 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 000000000..b5f7009d8
--- /dev/null
+++ b/src/map/battle.c
@@ -0,0 +1,5387 @@
+// $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);
+ 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;
+ int breakrate=1;
+ if(sd->status.weapon && sd->status.weapon!=11) {
+ 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) {
+ if(pc_breakweapon(sd)==1)
+ wd = battle_calc_pc_weapon_attack(src,target,skill_num,skill_lv,wflag);
+ }
+ }
+ }
+
+ 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;
+ MATK_FIX( 100+skill_lv*30, 100 );
+ 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=50;
+ 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);
+}
+
+static const struct {
+ char str[128];
+ int *val;
+} battle_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
+};
+
+int battle_set_value(char *w1, char *w2) {
+ int i;
+ for(i = 0; i < sizeof(battle_data) / (sizeof(battle_data[0])); i++)
+ if (strcmpi(w1, battle_data[i].str) == 0) {
+ *battle_data[i].val = battle_config_switch(w2);
+ return 1;
+ }
+ return 0;
+}
+
+void battle_set_defaults() {
+ 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
+}
+
+void battle_validate_conf() {
+ if(battle_config.flooritem_lifetime < 1000)
+ battle_config.flooritem_lifetime = LIFETIME_FLOORITEM*1000;
+ if(battle_config.restart_hp_rate < 0)
+ battle_config.restart_hp_rate = 0;
+ else if(battle_config.restart_hp_rate > 100)
+ battle_config.restart_hp_rate = 100;
+ if(battle_config.restart_sp_rate < 0)
+ battle_config.restart_sp_rate = 0;
+ else if(battle_config.restart_sp_rate > 100)
+ battle_config.restart_sp_rate = 100;
+ if(battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_healhp_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_healsp_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL)
+ battle_config.natural_heal_skill_interval=NATURAL_HEAL_INTERVAL;
+ if(battle_config.natural_heal_weight_rate < 50)
+ battle_config.natural_heal_weight_rate = 50;
+ if(battle_config.natural_heal_weight_rate > 101)
+ battle_config.natural_heal_weight_rate = 101;
+ battle_config.monster_max_aspd = 2000 - battle_config.monster_max_aspd*10;
+ if(battle_config.monster_max_aspd < 10)
+ battle_config.monster_max_aspd = 10;
+ if(battle_config.monster_max_aspd > 1000)
+ battle_config.monster_max_aspd = 1000;
+ battle_config.max_aspd = 2000 - battle_config.max_aspd*10;
+ if(battle_config.max_aspd < 10)
+ battle_config.max_aspd = 10;
+ if(battle_config.max_aspd > 1000)
+ battle_config.max_aspd = 1000;
+ if(battle_config.max_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
+}
+
+/*==========================================
+ * 設定ファイルを読み込む
+ *------------------------------------------
+ */
+int battle_config_read(const char *cfgName)
+{
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ static int count = 0;
+
+ if ((count++) == 0)
+ battle_set_defaults();
+
+ fp = fopen(cfgName,"r");
+ if (fp == NULL) {
+ printf("file not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line,1020,fp)){
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ if (sscanf(line, "%[^:]:%s", w1, w2) != 2)
+ continue;
+ battle_set_value(w1, w2);
+ if (strcmpi(w1, "import") == 0)
+ battle_config_read(w2);
+ }
+ fclose(fp);
+
+ if (--count == 0) {
+ battle_validate_conf();
+ 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 000000000..d4f680509
--- /dev/null
+++ b/src/map/battle.h
@@ -0,0 +1,345 @@
+// $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;
+
+extern int battle_config_read(const char *cfgName);
+extern void battle_validate_conf();
+extern void battle_set_defaults();
+extern int battle_set_value(char *, char *);
+
+#endif
diff --git a/src/map/chat.c b/src/map/chat.c
new file mode 100644
index 000000000..e5e9646d1
--- /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 000000000..15559f188
--- /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 000000000..0c456f128
--- /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 _WIN32
+#include <winsock.h>
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/types.h>
+#include <time.h>
+
+#include "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(&timestamp));
+ 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 000000000..3408c89ef
--- /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 000000000..99ccbb8c0
--- /dev/null
+++ b/src/map/clif.c
@@ -0,0 +1,9712 @@
+// $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 _WIN32
+#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"
+#include "mmo.h"
+
+#ifdef MEMWATCH
+#include "memwatch.h"
+#endif
+
+#define STATE_BLIND 0x10
+
+/* Packet Database */
+struct packet_db packet_db[MAX_PACKET_DB];
+
+// 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];
+
+/*==========================================
+ * Clif Parsing Functions
+ *------------------------------------------
+ */
+void clif_parse_WantToConnection(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_LoadEndAck(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TickSend(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_WalkToXY(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_QuitGame(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GetCharNameRequest(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GlobalMessage(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_MapMove(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ChangeDir(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_Emotion(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_HowManyConnections(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ActionRequest(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_Restart(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_Wis(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMmessage(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TakeItem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_DropItem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_UseItem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_EquipItem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_UnequipItem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcClicked(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcBuyListSend(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_CreateChatRoom(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ChatAddMember(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ChatRoomStatusChange(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ChangeChatOwner(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_KickFromChat(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ChatLeave(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TradeRequest(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TradeAck(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TradeAddItem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TradeOk(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TradeCancel(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_TradeCommit(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_StopAttack(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_PutItemToCart(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_RemoveOption(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ChangeCart(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_StatusUp(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_SkillUp(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_UseSkillToId(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_UseSkillToPos(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_UseSkillMap(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_RequestMemo(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ProduceMix(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcStringInput(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ItemIdentify(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_SelectArrow(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_AutoSpell(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_UseCard(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_InsertCard(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_SolveCharName(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ResetChar(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_LGMmessage(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_MoveToKafra(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_MoveToKafraFromCart(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_MoveFromKafraToCart(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_CloseKafra(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_CreateParty(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_CreateParty2(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_PartyInvite(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_LeaveParty(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_RemovePartyMember(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_PartyChangeOption(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_PartyMessage(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_CloseVending(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_VendingListReq(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_PurchaseReq(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_OpenVending(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_CreateGuild(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildCheckMaster(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildRequestInfo(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildChangePositionInfo(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildChangeMemberPosition(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildChangeNotice(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildInvite(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildLeave(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildExplusion(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildMessage(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildRequestAlliance(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildReplyAlliance(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildDelAlliance(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildOpposition(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GuildBreak(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_PetMenu(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_CatchPet(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_SelectEgg(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_SendEmotion(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_ChangePetName(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMKick(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMHide(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMReqNoChatCount(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_sn_doridori(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_sn_explosionspirits(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_wisexin(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_wisall(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_wisexlist(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMkillall(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMsummon(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_GMshift(int fd,struct map_session_data *sd, int cmd);
+void clif_parse_debug(int fd,struct map_session_data *sd, int cmd);
+
+struct {
+ void (*func)();
+ char *name;
+} clif_parse_func[]={
+ {clif_parse_WantToConnection,"wanttoconnection"},
+ {clif_parse_LoadEndAck,"loadendack"},
+ {clif_parse_TickSend,"ticksend"},
+ {clif_parse_WalkToXY,"walktoxy"},
+ {clif_parse_QuitGame,"quitgame"},
+ {clif_parse_GetCharNameRequest,"getcharnamerequest"},
+ {clif_parse_GlobalMessage,"globalmessage"},
+ {clif_parse_MapMove,"mapmove"},
+ {clif_parse_ChangeDir,"changedir"},
+ {clif_parse_Emotion,"emotion"},
+ {clif_parse_HowManyConnections,"howmanyconnections"},
+ {clif_parse_ActionRequest,"actionrequest"},
+ {clif_parse_Restart,"restart"},
+ {clif_parse_Wis,"wis"},
+ {clif_parse_GMmessage,"gmmessage"},
+ {clif_parse_TakeItem,"takeitem"},
+ {clif_parse_DropItem,"dropitem"},
+ {clif_parse_UseItem,"useitem"},
+ {clif_parse_EquipItem,"equipitem"},
+ {clif_parse_UnequipItem,"unequipitem"},
+ {clif_parse_NpcClicked,"npcclicked"},
+ {clif_parse_NpcBuySellSelected,"npcbuysellselected"},
+ {clif_parse_NpcBuyListSend,"npcbuylistsend"},
+ {clif_parse_NpcSellListSend,"npcselllistsend"},
+ {clif_parse_CreateChatRoom,"createchatroom"},
+ {clif_parse_ChatAddMember,"chataddmember"},
+ {clif_parse_ChatRoomStatusChange,"chatroomstatuschange"},
+ {clif_parse_ChangeChatOwner,"changechatowner"},
+ {clif_parse_KickFromChat,"kickfromchat"},
+ {clif_parse_ChatLeave,"chatleave"},
+ {clif_parse_TradeRequest,"traderequest"},
+ {clif_parse_TradeAck,"tradeack"},
+ {clif_parse_TradeAddItem,"tradeadditem"},
+ {clif_parse_TradeOk,"tradeok"},
+ {clif_parse_TradeCancel,"tradecancel"},
+ {clif_parse_TradeCommit,"tradecommit"},
+ {clif_parse_StopAttack,"stopattack"},
+ {clif_parse_PutItemToCart,"putitemtocart"},
+ {clif_parse_GetItemFromCart,"getitemfromcart"},
+ {clif_parse_RemoveOption,"removeoption"},
+ {clif_parse_ChangeCart,"changecart"},
+ {clif_parse_StatusUp,"statusup"},
+ {clif_parse_SkillUp,"skillup"},
+ {clif_parse_UseSkillToId,"useskilltoid"},
+ {clif_parse_UseSkillToPos,"useskilltopos"},
+ {clif_parse_UseSkillMap,"useskillmap"},
+ {clif_parse_RequestMemo,"requestmemo"},
+ {clif_parse_ProduceMix,"producemix"},
+ {clif_parse_NpcSelectMenu,"npcselectmenu"},
+ {clif_parse_NpcNextClicked,"npcnextclicked"},
+ {clif_parse_NpcAmountInput,"npcamountinput"},
+ {clif_parse_NpcStringInput,"npcstringinput"},
+ {clif_parse_NpcCloseClicked,"npccloseclicked"},
+ {clif_parse_ItemIdentify,"itemidentify"},
+ {clif_parse_SelectArrow,"selectarrow"},
+ {clif_parse_AutoSpell,"autospell"},
+ {clif_parse_UseCard,"usecard"},
+ {clif_parse_InsertCard,"insertcard"},
+ {clif_parse_SolveCharName,"solvecharname"},
+ {clif_parse_ResetChar,"resetchar"},
+ {clif_parse_LGMmessage,"lgmmessage"},
+ {clif_parse_MoveToKafra,"movetokafra"},
+ {clif_parse_MoveFromKafra,"movefromkafra"},
+ {clif_parse_MoveToKafraFromCart,"movetokafrafromcart"},
+ {clif_parse_MoveFromKafraToCart,"movefromkafratocart"},
+ {clif_parse_CloseKafra,"closekafra"},
+ {clif_parse_CreateParty,"createparty"},
+ {clif_parse_CreateParty2,"createparty2"},
+ {clif_parse_PartyInvite,"partyinvite"},
+ {clif_parse_ReplyPartyInvite,"replypartyinvite"},
+ {clif_parse_LeaveParty,"leaveparty"},
+ {clif_parse_RemovePartyMember,"removepartymember"},
+ {clif_parse_PartyChangeOption,"partychangeoption"},
+ {clif_parse_PartyMessage,"partymessage"},
+ {clif_parse_CloseVending,"closevending"},
+ {clif_parse_VendingListReq,"vendinglistreq"},
+ {clif_parse_PurchaseReq,"purchasereq"},
+ {clif_parse_OpenVending,"openvending"},
+ {clif_parse_CreateGuild,"createguild"},
+ {clif_parse_GuildCheckMaster,"guildcheckmaster"},
+ {clif_parse_GuildRequestInfo,"guildrequestinfo"},
+ {clif_parse_GuildChangePositionInfo,"guildchangepositioninfo"},
+ {clif_parse_GuildChangeMemberPosition,"guildchangememberposition"},
+ {clif_parse_GuildRequestEmblem,"guildrequestemblem"},
+ {clif_parse_GuildChangeEmblem,"guildchangeemblem"},
+ {clif_parse_GuildChangeNotice,"guildchangenotice"},
+ {clif_parse_GuildInvite,"guildinvite"},
+ {clif_parse_GuildReplyInvite,"guildreplyinvite"},
+ {clif_parse_GuildLeave,"guildleave"},
+ {clif_parse_GuildExplusion,"guildexplusion"},
+ {clif_parse_GuildMessage,"guildmessage"},
+ {clif_parse_GuildRequestAlliance,"guildrequestalliance"},
+ {clif_parse_GuildReplyAlliance,"guildreplyalliance"},
+ {clif_parse_GuildDelAlliance,"guilddelalliance"},
+ {clif_parse_GuildOpposition,"guildopposition"},
+ {clif_parse_GuildBreak,"guildbreak"},
+ {clif_parse_PetMenu,"petmenu"},
+ {clif_parse_CatchPet,"catchpet"},
+ {clif_parse_SelectEgg,"selectegg"},
+ {clif_parse_SendEmotion,"sendemotion"},
+ {clif_parse_ChangePetName,"changepetname"},
+ {clif_parse_GMKick,"gmkick"},
+ {clif_parse_GMHide,"gmhide"},
+ {clif_parse_GMReqNoChat,"gmreqnochat"},
+ {clif_parse_GMReqNoChatCount,"gmreqnochatcount"},
+ {clif_parse_sn_doridori,"sndoridori"},
+ {clif_parse_sn_explosionspirits,"snexplosionspirits"},
+ {clif_parse_wisexin,"wisexin"},
+ {clif_parse_wisexlist,"wisexlist"},
+ {clif_parse_wisall,"wisall"},
+ {clif_parse_GMkillall,"killall"},
+ {clif_parse_GMsummon,"summon"},
+ {clif_parse_GMshift,"shift"},
+ {clif_parse_debug,"debug"},
+
+ {NULL,NULL}
+};
+
+/*==========================================
+ * 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 {
+ 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) {
+ 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) {
+ 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;
+ 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;
+ 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) {
+ memcpy(WFIFOP(sd->fd,0), buf, len);
+ WFIFOSET(sd->fd,len);
+ }
+ }
+ }
+ }
+ break;
+ case SELF:
+ sd = (struct map_session_data *)bl;
+ 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;
+ 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) {
+ 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;
+ 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_db[0x73].len);
+
+ 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_db[0x81].len);
+
+ 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_db[0xb3].len);
+
+ 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_db[0x9e].len;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+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_db[0x9e].len, &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_db[0xa1].len, &fitem->bl, AREA);
+ } else {
+ memcpy(WFIFOP(fd,0), buf, 6);
+ WFIFOSET(fd,packet_db[0xa1].len);
+ }
+
+ 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_db[0x80].len, bl, AREA);
+ } else {
+ WBUFB(buf,6) = type;
+ clif_send(buf, packet_db[0x80].len, 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_db[0x80].len);
+
+ 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_db[0x78].len;
+ }
+
+#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_db[0x78].len;
+#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_db[0x1d8].len;
+#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_db[0x7b].len;
+ }
+
+#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_db[0x7b].len;
+#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_db[0x1da].len;
+#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_db[0x1b0].len,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_db[0x1b0].len,&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_db[0x1a4].len);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=3;
+ WBUFL(buf,3)=md->bl.id;
+ WBUFL(buf,7)=nameid;
+
+ clif_send(buf,packet_db[0x1a4].len,&md->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * MOB表示1
+ *------------------------------------------
+ */
+static int clif_mob0078(struct mob_data *md, unsigned char *buf)
+{
+ int level;
+
+ memset(buf,0,packet_db[0x78].len);
+
+ 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_db[0x78].len;
+}
+
+/*==========================================
+ * MOB表示2
+ *------------------------------------------
+ */
+static int clif_mob007b(struct mob_data *md, unsigned char *buf) {
+ int level;
+
+ memset(buf,0,packet_db[0x7b].len);
+
+ 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_db[0x7b].len;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_npc0078(struct npc_data *nd, unsigned char *buf) {
+ struct guild *g;
+
+ nullpo_retr(0, nd);
+
+ memset(buf,0,packet_db[0x78].len);
+
+ 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_db[0x78].len;
+}
+
+// NPC Walking [Valaris]
+static int clif_npc007b(struct npc_data *nd, unsigned char *buf) {
+ struct guild *g;
+
+ nullpo_retr(0, nd);
+
+ memset(buf,0,packet_db[0x7b].len);
+
+ WBUFW(buf,0)=0x7b;
+ 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;
+ }
+
+ WBUFL(buf,22)=gettick();
+ WBUFPOS2(buf,50,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y);
+ WBUFB(buf,56)=5;
+ WBUFB(buf,57)=5;
+
+ return packet_db[0x7b].len;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_pet0078(struct pet_data *pd, unsigned char *buf) {
+ int view,level;
+
+ nullpo_retr(0, pd);
+
+ memset(buf,0,packet_db[0x78].len);
+
+ 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_db[0x78].len;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int clif_pet007b(struct pet_data *pd, unsigned char *buf) {
+ int view,level;
+
+ nullpo_retr(0, pd);
+
+ memset(buf,0,packet_db[0x7b].len);
+
+ 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_db[0x7b].len;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+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_db[0x1e1].len;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+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_db[0x192].len);
+
+ 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_db[0x119].len);
+
+ 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_db[0x119].len, &sd->bl, SELF);
+
+ memset(buf, 0, packet_db[0x7c].len);
+
+ 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_db[0x7c].len, &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_db[0x79].len, &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_db[0x1d9].len, &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_db[0x7c].len);
+
+ 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_db[0x7c].len,&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_db[0x7c].len);
+
+ 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_db[0x7c].len,&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_db[0x7c].len);
+
+ 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_db[0x7c].len,&pd->bl,AREA);
+ }
+
+ len = clif_pet0078(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_movepet(struct pet_data *pd) {
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, pd);
+
+ len = clif_pet007b(pd,buf);
+ clif_send(buf,len,&pd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ * npc walking [Valaris]
+ *------------------------------------------
+ */
+int clif_movenpc(struct npc_data *nd) {
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ len = clif_npc007b(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_servertick(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOW(fd,0)=0x7f;
+ WFIFOL(fd,2)=sd->server_tick;
+ WFIFOSET(fd,packet_db[0x7f].len);
+
+ 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_db[0x87].len);
+
+ 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_db[0x91].len);
+
+ 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_db[0x92].len);
+
+ 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_db[0x88].len, 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_db[0xc4].len);
+
+ 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_db[0xb5].len);
+
+ 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_db[0xb6].len);
+
+ 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_db[0x142].len);
+
+ 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_db[0x1d4].len);
+
+ 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_db[0x144].len);
+
+ 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_db[0x1b3].len);
+
+ 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;
+ 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_db[0xa0].len);
+ 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_db[0xaf].len);
+
+ 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;
+ 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;
+ 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;
+ 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_db[0x1ab].len,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_db[0xc3].len,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_db[0x1d7].len,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_db[0x1d7].len,bl,AREA);
+ } else {
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_db[0xc3].len,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_db[0xbd].len);
+
+ 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_db[0x013c].len);
+
+ 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_db[0x013b].len);
+
+ 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_db[0xbc].len);
+
+ 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_db[0xaa].len);
+
+ 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_db[0xac].len);
+
+ 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_db[0x19b].len,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_db[0x1f3].len);
+
+ WBUFW(buf,0) = 0x1f3;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf, packet_db[0x1f3].len, 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_db[0x119].len,bl,AREA_WOS);
+ clif_spawnpc(sd);
+ } else
+ clif_send(buf,packet_db[0x119].len,bl,AREA);
+ } else
+ clif_send(buf,packet_db[0x119].len,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_db[0xa8].len);
+ }
+ 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_db[0xa8].len);
+#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_db[0x1c8].len,&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_db[0xd6].len);
+
+ 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_db[0xd8].len);
+ WFIFOSET(fd,packet_db[0xd8].len);
+ } else {
+ clif_send(buf,packet_db[0xd8].len,*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_db[0xda].len);
+
+ 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_db[0xdc].len,&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_db[0xe1].len*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_db[0xdd].len,&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_db[0xe5].len);
+
+ 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_db[0xe7].len);
+
+ 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
+ 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_db[0xe9].len);
+
+ return 0;
+}
+
+/*==========================================
+ * アイテム追加成功/失敗
+ *------------------------------------------
+ */
+int clif_tradeitemok(struct map_session_data *sd,int index,int fail)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+
+ fd=sd->fd;
+ WFIFOW(fd,0)=0xea;
+ WFIFOW(fd,2)=index;
+ WFIFOB(fd,4)=fail;
+ WFIFOSET(fd,packet_db[0xea].len);
+
+ 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_db[0xec].len);
+
+ 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_db[0xee].len);
+
+ 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_db[0xf0].len);
+
+ 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_db[0xf2].len);
+
+ 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
+ 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_db[0xf4].len);
+
+ 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_db[0xf2].len);
+
+ 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
+ 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_db[0xf4].len);
+
+ 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_db[0xf6].len);
+
+ 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_db[0xf8].len);
+
+ 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_db[0x1e1].len);
+ }
+ 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;
+}
+
+// npc walking [Valaris]
+int clif_fixnpcpos(struct npc_data *nd)
+{
+ unsigned char buf[256];
+ int len;
+
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK){
+ len = clif_npc007b(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+ } else {
+ len = clif_npc0078(nd,buf);
+ clif_send(buf,len,&nd->bl,AREA);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 通常攻撃エフェクト&ダメージ
+ *------------------------------------------
+ */
+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_db[0x8a].len,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_db[0x9d].len);
+}
+/*==========================================
+ * 場所スキルエフェクトが視界に入る
+ *------------------------------------------
+ */
+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_db[0x11f].len);
+ 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_db[0x11f].len);
+#else
+ memset(WFIFOP(fd,0),0,packet_db[0x1c9].len);
+ 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_db[0x1c9].len);
+#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_db[0x120].len);
+ 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_db[0x1ac].len,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;
+}
+// npc walking [Valaris]
+int clif_npcoutsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=va_arg(ap,struct npc_data*));
+
+ if(bl->type==BL_PC && (sd = (struct map_session_data*) bl)){
+ clif_clearchar_id(nd->bl.id,0,sd->fd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int clif_petinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct pet_data *pd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ pd=va_arg(ap,struct pet_data*);
+ if(bl->type==BL_PC && (sd = (struct map_session_data *)bl)){
+ clif_getareachar_pet(sd,pd);
+ }
+
+ return 0;
+}
+// npc walking [Valaris]
+int clif_npcinsight(struct block_list *bl,va_list ap)
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+
+ nd=va_arg(ap,struct npc_data*);
+ if(bl->type==BL_PC && (sd = (struct map_session_data *)bl)){
+ clif_getareachar_npc(sd,nd);
+ }
+
+ 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_db[0x147].len);
+
+ 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_db[0x10e].len);
+
+ 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_db[0x13e].len, 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_db[0x1b9].len, 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_db[0x110].len);
+
+ 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_db[0x114].len,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_db[0x1de].len,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_db[0x115].len,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_db[0x11a].len,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_db[0x117].len,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_db[0x11f].len);
+ 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_db[0x11f].len,&unit->bl,AREA);
+#else
+ memset(WBUFP(buf, 0),0,packet_db[0x1c9].len);
+ 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_db[0x1c9].len,&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_db[0x120].len,&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_db[0x11c].len);
+ 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_db[0x11e].len);
+ 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_db[0x189].len);
+ 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_db[0x18c].len,&sd->bl,PARTY_AREA);
+ else{
+ memcpy(WFIFOP(sd->fd,0),buf,packet_db[0x18c].len);
+ WFIFOSET(sd->fd,packet_db[0x18c].len);
+ }
+ 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_db[0x196].len,bl,AREA);
+ return 0;
+}
+
+/*==========================================
+ * Send message (modified by [Yor])
+ *------------------------------------------
+ */
+int clif_displaymessage(const int fd, char* mes)
+{
+ //Console [Wizputer]
+ if ( fd == 0 )
+ printf("console: %s\n",mes);
+ else {
+ int len_mes = strlen(mes);
+
+ if (len_mes > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
+ 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_db[0x13d].len);
+
+ 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_db[0x148].len,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_db[0x199].len);
+
+ 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_db[0x19a].len);
+ } 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_db[0x19a].len,&sd->bl,AREA);
+ else
+ clif_send(buf,packet_db[0x19a].len,&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_db[0x199].len,&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_db[0x188].len);
+
+ 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_db[0x98].len);
+ 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_db[0x194].len);
+ }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_db[0x17d].len);
+ 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_db[0x179].len);
+ 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].attribute==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_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_db[0x147].len);
+ 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;
+ 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_db[0x124].len);
+ 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_db[0x125].len);
+
+ 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;
+ 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_db[0x12d].len);
+
+ 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_db[0x131].len);
+ WFIFOSET(fd,packet_db[0x131].len);
+ }else{
+ clif_send(buf,packet_db[0x131].len,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_db[0x132].len);
+ WFIFOSET(fd,packet_db[0x132].len);
+ }else{
+ clif_send(buf,packet_db[0x132].len,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;
+ 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_db[0x135].len);
+
+ 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].attribute==1) // Prevent unidentified and broken items from being sold [Valaris]
+ continue;
+ data = itemdb_search(sd->status.cart[index].nameid);
+ WBUFB(buf,16+n*22)=data->type;
+ if(data->view_id > 0)
+ WBUFW(buf,17+n*22)=data->view_id;
+ else
+ WBUFW(buf,17+n*22)=sd->status.cart[index].nameid;
+ WBUFB(buf,19+n*22)=sd->status.cart[index].identify;
+ WBUFB(buf,20+n*22)=sd->status.cart[index].attribute;
+ WBUFB(buf,21+n*22)=sd->status.cart[index].refine;
+ 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_db[0x137].len);
+
+ 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_db[0xfa].len);
+ 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_db[0xfe].len);
+ 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_db[0xfd].len);
+ 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_db[0x101].len,&sd->bl,PARTY);
+ else {
+ memcpy(WFIFOP(sd->fd,0),buf,packet_db[0x101].len);
+ WFIFOSET(sd->fd,packet_db[0x101].len);
+ }
+ 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_db[0x105].len,&sd->bl,PARTY);
+ } else if (sd!=NULL) {
+ memcpy(WFIFOP(sd->fd,0),buf,packet_db[0x105].len);
+ WFIFOSET(sd->fd,packet_db[0x105].len);
+ }
+ 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_db[0x107].len,&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_db[0x106].len,&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_db[0x104].len,&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_db[0x139].len);
+ 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_db[0x18f].len);
+ 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_db[0x19e].len);
+
+ 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_db[0x1a0].len);
+
+ 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_db[0x1a4].len);
+
+ 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_db[0x1a2].len);
+
+ 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_db[0x1aa].len);
+
+ 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_db[0x1aa].len,&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_db[0x1a4].len);
+
+ WBUFW(buf,0)=0x1a4;
+ WBUFB(buf,2)=4;
+ WBUFL(buf,3)=bl->id;
+ WBUFL(buf,7)=param;
+
+ clif_send(buf,packet_db[0x1a4].len,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_db[0x1a4].len);
+
+ 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_db[0x1a4].len,&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_db[0x1a3].len);
+
+ 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_db[0x1cd].len);
+ 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_db[0x1cf].len,&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_db[0x1d0].len,&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_db[0x1d2].len,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_db[0x1d1].len,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_db[0x192].len,&bl,AREA);
+ else
+ clif_send(buf,packet_db[0x192].len,&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_db[0x10c].len,&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_db[0x10a].len);
+ 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_db[0x10b].len);
+ 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_db[0x167].len);
+ 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_db[0x16c].len);
+ 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_db[0x16c].len);
+ 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_db[0x16d].len,&sd->bl,GUILD);
+ }else
+ clif_send(buf,packet_db[0x16d].len,&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_db[0x14e].len);
+ 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_db[WFIFOW(fd,0)].len);
+ 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,up=1;
+ 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);
+ if(g->skill[i].lv < guild_skill_get_max(id)) {
+ //Kafra and Guardian changed to require Approval [Sara]
+ if (g->skill[i].id == GD_KAFRACONTACT && guild_checkskill(g,GD_APPROVAL) <= 0)
+ up = 0;
+ else if (g->skill[i].id == GD_GUARDIANRESEARCH && guild_checkskill(g,GD_APPROVAL) <= 0)
+ up = 0;
+ //Glory skill requirements -- Pretty sure correct [Sara]
+ else if (g->skill[i].id == GD_LEADERSHIP && guild_checkskill(g,GD_GLORYGUILD) <= 0)
+ up = 0;
+ else if (g->skill[i].id == GD_GLORYWOUNDS && guild_checkskill(g,GD_GLORYGUILD) <= 0)
+ up = 0;
+ else if (g->skill[i].id == GD_SOULCOLD && guild_checkskill(g,GD_GLORYWOUNDS) <= 0)
+ up = 0;
+ else if (g->skill[i].id == GD_HAWKEYES && guild_checkskill(g,GD_LEADERSHIP) <= 0)
+ up = 0;
+ //Activated skill requirements -- Just guesses [Sara]
+ else if (g->skill[i].id == GD_BATTLEORDER && guild_checkskill(g,GD_APPROVAL) <= 0)
+ up = 0;
+ else if (g->skill[i].id == GD_REGENERATION && guild_checkskill(g,GD_APPROVAL) <= 0)
+ up = 0;
+ else if (g->skill[i].id == GD_RESTORE && guild_checkskill(g,GD_REGENERATION) <= 0)
+ up = 0;
+ else if (g->skill[i].id == GD_EMERGENCYCALL && guild_checkskill(g,GD_APPROVAL) <= 0)
+ up = 0;
+ if (g->skill[i].id == GD_GUARDUP && guild_checkskill(g,GD_GUARDIANRESEARCH) <= 0)
+ up = 0;
+ //Unadded yet? Has extension description in kRO tables
+ else if (g->skill[i].id == GD_DEVELOPMENT)
+ up = 0;
+ else
+ up = 1;
+ }
+ else {
+ up = 0;
+ }
+ WFIFOB(fd,c*37+42)= up;
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*37+6;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ return 0;
+}
+/*==========================================
+ * ギルド告知送信
+ *------------------------------------------
+ */
+int clif_guild_notice(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, g);
+
+ fd=sd->fd;
+ if(*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_db[0x16f].len);
+ 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_db[0x16a].len);
+ 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_db[0x169].len);
+ 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_db[0x15a].len,&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_db[0x15c].len,&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_db[0x171].len);
+ 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_db[0x173].len);
+ 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_db[0x184].len);
+ 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_db[0x181].len);
+ 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_db[0x185].len,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_db[0x15e].len);
+ 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_db[0xc0].len,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_db[0x191].len,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_db[0x1ea].len, 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_db[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_db[0x8a].len, &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_db[0xcd].len);
+ return 0;
+}
+
+int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type)
+{
+ int fd;
+ nullpo_retr(0, tsd);
+
+ if(type)
+ clif_GM_kickack(sd,tsd->status.account_id);
+ tsd->opt1 = tsd->opt2 = 0;
+ fd = tsd->fd;
+ WFIFOW(fd,0) = 0x18b;
+ WFIFOW(fd,2) = 0;
+ WFIFOSET(fd,packet_db[0x18b].len);
+ clif_setwaitclose(fd);
+
+ 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_db[0xd1].len);
+
+ 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_db[0xd2].len);
+
+ 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_db[0x1d3].len);
+
+ 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_db[0x1f3].len);
+
+ 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_db[0x1f3].len, bl, SELF);
+ else if (!flag)
+ clif_send(buf, packet_db[0x1f3].len, bl, AREA);
+
+ return 0;
+
+}
+// ------------
+// clif_parse_*
+// ------------
+// パケット読み取って色々操作
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WantToConnection(int fd, struct map_session_data *sd, int cmd)
+{
+ struct map_session_data *old_sd;
+ int account_id,char_id,login_id1,sex;
+ unsigned int client_tick;
+
+ if (sd) {
+ if (battle_config.error_log)
+ printf("clif_parse_WantToConnection : invalid request?\n");
+ return;
+ }
+
+ 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;
+
+ account_id = RFIFOL(fd,packet_db[cmd].pos[0]);
+ char_id = RFIFOL(fd,packet_db[cmd].pos[1]);
+ login_id1 = RFIFOL(fd,packet_db[cmd].pos[2]);
+ client_tick = RFIFOL(fd,packet_db[cmd].pos[3]);
+ sex = RFIFOB(fd,packet_db[cmd].pos[4]);
+
+ pc_setnewpc(sd,account_id,char_id,login_id1,client_tick,sex,fd);
+ if((old_sd=map_id2sd(account_id)) != NULL){
+ // if same account already connected, we disconnect the 2 sessions
+ old_sd->new_fd=fd;
+ } else {
+ map_addiddb(&sd->bl);
+ }
+
+ chrif_authreq(sd);
+
+ WFIFOL(fd,0)=sd->bl.id;
+ WFIFOSET(fd,4);
+
+ return;
+}
+
+/*==========================================
+ * 007d クライアント側マップ読み込み完了
+ * map侵入時に必要なデータを全て送りつける
+ *------------------------------------------
+ */
+void clif_parse_LoadEndAck(int fd,struct map_session_data *sd, int cmd)
+{
+// 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].attribute==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].attribute==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, int cmd) {
+ nullpo_retv(sd);
+
+ sd->client_tick=RFIFOL(fd,packet_db[cmd].pos[0]);
+ sd->server_tick=gettick();
+ clif_servertick(sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_WalkToXY(int fd, struct map_session_data *sd, int cmd) {
+ 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);
+
+ x = RFIFOB(fd,packet_db[cmd].pos[0])*4+(RFIFOB(fd,packet_db[cmd].pos[0]+1)>>6);
+ y = ((RFIFOB(fd,packet_db[cmd].pos[0]+1)&0x3f)<<4)+(RFIFOB(fd,packet_db[cmd].pos[0]+2)>>4);
+
+ pc_walktoxy(sd,x,y);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_QuitGame(int fd, struct map_session_data *sd, int cmd) {
+ 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_db[0x18b].len);
+ 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_db[0x18b].len);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd, int cmd) {
+ struct block_list *bl;
+ int account_id;
+
+ account_id = RFIFOL(fd,packet_db[cmd].pos[0]);
+ 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_db[0x195].len);
+ break;
+ }
+ }
+ WFIFOSET(fd,packet_db[0x95].len);
+ }
+ break;
+ case BL_PET:
+ memcpy(WFIFOP(fd,6), ((struct pet_data*)bl)->name, 24);
+ WFIFOSET(fd,packet_db[0x95].len);
+ break;
+ case BL_NPC:
+ memcpy(WFIFOP(fd,6), ((struct npc_data*)bl)->name, 24);
+ WFIFOSET(fd,packet_db[0x95].len);
+ 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_db[0x195].len);
+ } else {
+ WFIFOSET(fd,packet_db[0x95].len);
+ }
+ } 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_db[0x195].len);
+ } else {
+ WFIFOSET(fd,packet_db[0x95].len);
+ }
+ }
+ 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, int cmd) { // 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,packet_db[cmd].pos[0]) + 128);
+ memset(buf, '\0', RFIFOW(fd,packet_db[cmd].pos[0]) + 4);
+
+ if ((is_atcommand(fd, sd, RFIFOP(fd, packet_db[cmd].pos[1]), 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, packet_db[cmd].pos[1]), 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, packet_db[cmd].pos[1])) == 0)
+ strcpy(message, " This player sends a void name and a void message.");
+ else
+ sprintf(message, " This player sends (name:message): '%s'.", RFIFOP(fd, packet_db[cmd].pos[1]));
+ 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,packet_db[cmd].pos[0]));
+ 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, int cmd) {
+// /m /mapmove (as @rura GM command)
+ char mapname[32];
+ int x,y;
+
+ if (battle_config.atc_gmonly == 0 ||
+ pc_isGM(sd) >= get_atcommand_level(AtCommand_MapMove)) {
+ memcpy(mapname,RFIFOP(fd,packet_db[cmd].pos[0]),16);
+ mapname[16]=0;
+ x=RFIFOW(fd,packet_db[cmd].pos[1]);
+ y=RFIFOW(fd,packet_db[cmd].pos[2]);
+ pc_setpos(sd,mapname,x,y,2);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChangeDir(int fd, struct map_session_data *sd, int cmd) {
+ short headdir,dir;
+ unsigned char buf[64];
+
+ nullpo_retv(sd);
+
+ headdir=RFIFOW(fd,packet_db[cmd].pos[0]);
+ dir=RFIFOB(fd,packet_db[cmd].pos[1]);
+ pc_setdir(sd,dir,headdir);
+
+ WFIFOW(fd,0)=0x9c;
+ WFIFOL(fd,2)=sd->bl.id;
+ WFIFOW(fd,6)=headdir;
+ WFIFOB(fd,8)=dir;
+ if (sd->disguise > 23 && sd->disguise < 4001) // mob disguises [Valaris]
+ clif_send(buf, packet_db[0x9c].len, &sd->bl, AREA);
+ else
+ clif_send(buf, packet_db[0x9c].len, &sd->bl, AREA_WOS);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_Emotion(int fd, struct map_session_data *sd, int cmd) {
+ int emotion;
+ unsigned char buf[64];
+
+ nullpo_retv(sd);
+
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) {
+ emotion=RFIFOB(fd,packet_db[cmd].pos[0]);
+ WBUFW(buf,0) = 0xc0;
+ WBUFL(buf,2) = sd->bl.id;
+ WBUFB(buf,6) = emotion;
+ clif_send(buf, packet_db[0xc0].len, &sd->bl, AREA);
+ } else
+ clif_skill_fail(sd, 1, 0, 1);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_HowManyConnections(int fd, struct map_session_data *sd, int cmd) {
+ WFIFOW(fd,0) = 0xc2;
+ WFIFOL(fd,2) = map_getusers();
+ WFIFOSET(fd,packet_db[0xc2].len);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ActionRequest(int fd, struct map_session_data *sd, int cmd) {
+ 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);
+
+ target_id = RFIFOL(fd,packet_db[cmd].pos[0]);
+ action_type = RFIFOB(fd,packet_db[cmd].pos[1]);
+
+ 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_db[0x8a].len, &sd->bl, AREA);
+ break;
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_Restart(int fd, struct map_session_data *sd, int cmd) {
+ int restarttype=RFIFOB(fd,packet_db[cmd].pos[0]);
+
+ nullpo_retv(sd);
+
+ switch(restarttype){
+ 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_db[0x018b].len);
+ }
+ break;
+ }
+}
+
+/*==========================================
+ * Transmission of a wisp (S 0096 <len>.w <nick>.24B <message>.?B)
+ *------------------------------------------
+ */
+void clif_parse_Wis(int fd, struct map_session_data *sd, int cmd) { // S 0096 <len>.w <nick>.24B <message>.?B // rewritten by [Yor]
+ int len=RFIFOW(fd,packet_db[cmd].pos[0]);
+
+ if( sd && sd->sc_data &&
+ (sd->sc_data[SC_BERSERK].timer!=-1 || //バーサーク時は会話も不可
+ sd->sc_data[SC_NOCHAT].timer!=-1 ) ) //チャット禁止
+ return;
+
+ intif_wis_message(sd,RFIFOP(fd,packet_db[cmd].pos[1]),RFIFOP(fd,packet_db[cmd].pos[2]),len-packet_db[cmd].pos[2]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_GMmessage(int fd, struct map_session_data *sd, int cmd) {
+// /b
+ int len;
+ nullpo_retv(sd);
+
+ len=RFIFOW(fd,packet_db[cmd].pos[0]);
+ if (battle_config.atc_gmonly == 0 ||
+ pc_isGM(sd) >= get_atcommand_level(AtCommand_Broadcast))
+ intif_GMmessage(RFIFOP(fd,packet_db[cmd].pos[1]),len-packet_db[cmd].pos[1],0);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_TakeItem(int fd, struct map_session_data *sd, int cmd) {
+ struct flooritem_data *fitem;
+ int map_object_id;
+
+ nullpo_retv(sd);
+
+ map_object_id = RFIFOL(fd,packet_db[cmd].pos[0]);
+ 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 cmd) {
+ 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;
+
+ item_index = RFIFOW(fd,packet_db[cmd].pos[0])-2;
+ item_amount = RFIFOW(fd,packet_db[cmd].pos[1]);
+
+ pc_dropitem(sd, item_index, item_amount);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_UseItem(int fd, struct map_session_data *sd, int cmd) {
+ 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);
+
+ pc_useitem(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_EquipItem(int fd,struct map_session_data *sd, int cmd)
+{
+ int index;
+
+ nullpo_retv(sd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+ index = RFIFOW(fd,packet_db[cmd].pos[0])-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) { // 未鑑定
+ 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,packet_db[cmd].pos[1])=0x8000; // 矢を無理やり装備できるように(−−;
+ pc_equipitem(sd,index,RFIFOW(fd,packet_db[cmd].pos[1]));
+ } else
+ pet_equipitem(sd,index);
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_UnequipItem(int fd,struct map_session_data *sd, int cmd)
+{
+ int index;
+
+ nullpo_retv(sd);
+
+ if(pc_isdead(sd)) {
+ clif_clearchar_area(&sd->bl,1);
+ return;
+ }
+
+ index = packet_db[cmd].pos[0]-2;
+ if(sd->status.inventory[index].attribute == 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].attribute == 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,RFIFOW(fd,packet_db[cmd].pos[0])-2,0);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcClicked(int fd,struct map_session_data *sd, int cmd)
+{
+ 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,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd, int cmd)
+{
+ npc_buysellsel(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOB(fd,packet_db[cmd].pos[1]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcBuyListSend(int fd,struct map_session_data *sd, int cmd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+
+ n = (RFIFOW(fd,packet_db[cmd].pos[0])-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,packet_db[cmd].pos[1]);
+
+ fail = npc_buylist(sd,n,item_list);
+
+ WFIFOW(fd,0)=0xca;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_db[0xca].len);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd, int cmd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+
+ n = (RFIFOW(fd,packet_db[cmd].pos[0])-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,packet_db[cmd].pos[1]);
+
+ fail = npc_selllist(sd,n,item_list);
+
+ WFIFOW(fd,0)=0xcb;
+ WFIFOB(fd,2)=fail;
+ WFIFOSET(fd,packet_db[0xcb].len);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_CreateChatRoom(int fd,struct map_session_data *sd, int cmd)
+{
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 4){
+ int len = RFIFOW(fd,packet_db[cmd].pos[0]);
+ chat_createchat(sd,RFIFOW(fd,packet_db[cmd].pos[1]),RFIFOB(fd,packet_db[cmd].pos[2]),RFIFOP(fd,packet_db[cmd].pos[3]),RFIFOP(fd,packet_db[cmd].pos[4]),len-packet_db[cmd].pos[4]);
+ }
+ else
+ clif_skill_fail(sd,1,0,3);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatAddMember(int fd,struct map_session_data *sd, int cmd)
+{
+ chat_joinchat(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOP(fd,packet_db[cmd].pos[1]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatRoomStatusChange(int fd,struct map_session_data *sd, int cmd)
+{
+ int len = RFIFOW(fd,packet_db[cmd].pos[0]);
+ chat_changechatstatus(sd,RFIFOW(fd,packet_db[cmd].pos[1]),RFIFOB(fd,packet_db[cmd].pos[2]),RFIFOP(fd,packet_db[cmd].pos[3]),RFIFOP(fd,packet_db[cmd].pos[4]),len-packet_db[cmd].pos[4]);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChangeChatOwner(int fd,struct map_session_data *sd, int cmd)
+{
+ chat_changechatowner(sd,RFIFOP(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_KickFromChat(int fd,struct map_session_data *sd, int cmd)
+{
+ chat_kickchat(sd,RFIFOP(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_ChatLeave(int fd,struct map_session_data *sd, int cmd)
+{
+ chat_leavechat(sd);
+}
+
+/*==========================================
+ * 取引要請を相手に送る
+ *------------------------------------------
+ */
+void clif_parse_TradeRequest(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 1){
+ trade_traderequest(sd,RFIFOL(sd->fd,packet_db[cmd].pos[0]));
+ }
+ else
+ clif_skill_fail(sd,1,0,0);
+}
+
+/*==========================================
+ * 取引要請
+ *------------------------------------------
+ */
+void clif_parse_TradeAck(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ trade_tradeack(sd,RFIFOB(sd->fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ * アイテム追加
+ *------------------------------------------
+ */
+void clif_parse_TradeAddItem(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ trade_tradeadditem(sd,RFIFOW(sd->fd,packet_db[cmd].pos[0]),RFIFOL(sd->fd,packet_db[cmd].pos[1]));
+}
+
+/*==========================================
+ * アイテム追加完了(ok押し)
+ *------------------------------------------
+ */
+void clif_parse_TradeOk(int fd,struct map_session_data *sd, int cmd)
+{
+ trade_tradeok(sd);
+}
+
+/*==========================================
+ * 取引キャンセル
+ *------------------------------------------
+ */
+void clif_parse_TradeCancel(int fd,struct map_session_data *sd, int cmd)
+{
+ trade_tradecancel(sd);
+}
+
+/*==========================================
+ * 取引許諾(trade押し)
+ *------------------------------------------
+ */
+void clif_parse_TradeCommit(int fd,struct map_session_data *sd, int cmd)
+{
+ trade_tradecommit(sd);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_StopAttack(int fd,struct map_session_data *sd, int cmd)
+{
+ pc_stopattack(sd);
+}
+
+/*==========================================
+ * カートへアイテムを移す
+ *------------------------------------------
+ */
+void clif_parse_PutItemToCart(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0)
+ return;
+ pc_putitemtocart(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2,RFIFOL(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * カートからアイテムを出す
+ *------------------------------------------
+ */
+void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0) return;
+ pc_getitemfromcart(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2,RFIFOL(fd,packet_db[cmd].pos[1]));
+}
+
+/*==========================================
+ * 付属品(鷹,ペコ,カート)をはずす
+ *------------------------------------------
+ */
+void clif_parse_RemoveOption(int fd,struct map_session_data *sd, int cmd)
+{
+ 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, int cmd)
+{
+ pc_setcart(sd,RFIFOW(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ * ステータスアップ
+ *------------------------------------------
+ */
+void clif_parse_StatusUp(int fd,struct map_session_data *sd, int cmd)
+{
+ pc_statusup(sd,RFIFOW(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ * スキルレベルアップ
+ *------------------------------------------
+ */
+void clif_parse_SkillUp(int fd,struct map_session_data *sd, int cmd)
+{
+ pc_skillup(sd,RFIFOW(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ * スキル使用(ID指定)
+ *------------------------------------------
+ */
+void clif_parse_UseSkillToId(int fd, struct map_session_data *sd, int cmd) {
+ int skillnum, skilllv,lv, target_id;
+ unsigned int tick=gettick();
+
+ nullpo_retv(sd);
+
+ if (sd->chatID || sd->npc_id != 0 || sd->vender_id != 0)
+ return;
+
+ skilllv = RFIFOW(fd,packet_db[cmd].pos[0]);
+ skillnum = RFIFOW(fd,packet_db[cmd].pos[1]);
+ target_id = RFIFOL(fd,packet_db[cmd].pos[2]);
+
+ if(skillnotok(skillnum, sd))
+ return;
+
+ 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 cmd) {
+ int skillnum, skilllv, lv, x, y;
+ unsigned int tick = gettick();
+
+ nullpo_retv(sd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0) return;
+ if(sd->chatID) return;
+
+ skilllv = RFIFOW(fd,packet_db[cmd].pos[0]);
+ skillnum = RFIFOW(fd,packet_db[cmd].pos[1]);
+ x = RFIFOW(fd,packet_db[cmd].pos[2]);
+ y = RFIFOW(fd,packet_db[cmd].pos[3]);
+
+ if(packet_db[cmd].pos[4]){
+ if(pc_issit(sd)){
+ clif_skill_fail(sd,skillnum,0,0);
+ return;
+ }
+ memcpy(sd->message,RFIFOP(fd,packet_db[cmd].pos[4]),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, int cmd)
+{
+ nullpo_retv(sd);
+
+ 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,packet_db[cmd].pos[0]),RFIFOP(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * メモ要求
+ *------------------------------------------
+ */
+void clif_parse_RequestMemo(int fd,struct map_session_data *sd, int cmd)
+{
+ pc_memo(sd,-1);
+}
+/*==========================================
+ * アイテム合成
+ *------------------------------------------
+ */
+void clif_parse_ProduceMix(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ sd->state.produce_flag = 0;
+ skill_produce_mix(sd,RFIFOW(fd,packet_db[cmd].pos[0]),RFIFOW(fd,packet_db[cmd].pos[1]),RFIFOW(fd,packet_db[cmd].pos[2]),RFIFOW(fd,packet_db[cmd].pos[3]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ sd->npc_menu=RFIFOB(fd,packet_db[cmd].pos[1]);
+ npc_scriptcont(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd, int cmd)
+{
+ npc_scriptcont(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd, int cmd)
+{
+ 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,packet_db[cmd].pos[1]); //fixed by Lupus. npc_amount is (int) but was RFIFOL changing it to (unsigned int)
+#undef RFIFOL_
+
+ npc_scriptcont(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcStringInput(int fd,struct map_session_data *sd, int cmd)
+{
+ int len = RFIFOW(fd,packet_db[cmd].pos[0]);
+
+ nullpo_retv(sd);
+
+ if(len-packet_db[cmd].pos[2]+1 >= sizeof(sd->npc_str)){
+ printf("clif: input string too long !\n");
+ memcpy(sd->npc_str,RFIFOP(fd,packet_db[cmd].pos[2]),sizeof(sd->npc_str));
+ sd->npc_str[sizeof(sd->npc_str)-1]=0;
+ }else
+ strncpy(sd->npc_str,RFIFOP(fd,packet_db[cmd].pos[2]),256);
+ npc_scriptcont(sd,RFIFOL(fd,packet_db[cmd].pos[1]));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd, int cmd)
+{
+ npc_scriptcont(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+
+/*==========================================
+ * アイテム鑑定
+ *------------------------------------------
+ */
+void clif_parse_ItemIdentify(int fd,struct map_session_data *sd, int cmd)
+{
+ pc_item_identify(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2);
+}
+/*==========================================
+ * 矢作成
+ *------------------------------------------
+ */
+void clif_parse_SelectArrow(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ sd->state.make_arrow_flag = 0;
+ skill_arrow_create(sd,RFIFOW(fd,packet_db[cmd].pos[0]));
+}
+/*==========================================
+ * オートスペル受信
+ *------------------------------------------
+ */
+void clif_parse_AutoSpell(int fd,struct map_session_data *sd, int cmd)
+{
+ skill_autospell(sd,RFIFOW(fd,packet_db[cmd].pos[0]));
+}
+/*==========================================
+ * カード使用
+ *------------------------------------------
+ */
+void clif_parse_UseCard(int fd,struct map_session_data *sd, int cmd)
+{
+ clif_use_card(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2);
+}
+/*==========================================
+ * カード挿入装備選択
+ *------------------------------------------
+ */
+void clif_parse_InsertCard(int fd,struct map_session_data *sd, int cmd)
+{
+ pc_insert_card(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2,RFIFOW(fd,packet_db[cmd].pos[1])-2);
+}
+
+/*==========================================
+ * 0193 キャラID名前引き
+ *------------------------------------------
+ */
+void clif_parse_SolveCharName(int fd,struct map_session_data *sd, int cmd)
+{
+ int char_id;
+
+ char_id = RFIFOL(fd,packet_db[cmd].pos[0]);
+ clif_solved_charname(sd,char_id);
+}
+
+/*==========================================
+ * 0197 /resetskill /resetstate
+ *------------------------------------------
+ */
+void clif_parse_ResetChar(int fd, struct map_session_data *sd, int cmd) {
+ nullpo_retv(sd);
+
+ if (battle_config.atc_gmonly == 0 || pc_isGM(sd) >= get_atcommand_level(AtCommand_ResetState)) {
+ switch(RFIFOW(fd,packet_db[cmd].pos[0])){
+ case 0:
+ pc_resetstate(sd);
+ break;
+ case 1:
+ pc_resetskill(sd);
+ break;
+ }
+ }
+}
+
+/*==========================================
+ * 019c /lb等
+ *------------------------------------------
+ */
+void clif_parse_LGMmessage(int fd, struct map_session_data *sd, int cmd) {
+ int len = RFIFOW(fd,packet_db[cmd].pos[0]);
+ 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) = len;
+ memcpy(WBUFP(buf,4), RFIFOP(fd,packet_db[cmd].pos[1]), len-packet_db[cmd].pos[1]);
+ clif_send(buf, len, &sd->bl, ALL_SAMEMAP);
+ }
+}
+
+/*==========================================
+ * カプラ倉庫へ入れる
+ *------------------------------------------
+ */
+void clif_parse_MoveToKafra(int fd, struct map_session_data *sd, int cmd) {
+ int item_index, item_amount;
+
+ nullpo_retv(sd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0)
+ return;
+ item_index = RFIFOW(fd,packet_db[cmd].pos[0])-2;
+ item_amount = RFIFOL(fd,packet_db[cmd].pos[1]);
+
+ 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 cmd) {
+ int item_index,item_amount;
+
+ nullpo_retv(sd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0) return;
+ item_index = RFIFOW(fd,packet_db[cmd].pos[0])-1;
+ item_amount = RFIFOL(fd,packet_db[cmd].pos[1]);
+
+ 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, int cmd) {
+ nullpo_retv(sd);
+
+ if (sd->npc_id != 0 || sd->vender_id != 0 || sd->trade_partner != 0) return;
+
+ if(sd->state.storage_flag)
+ storage_guild_storageaddfromcart(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2,RFIFOL(fd,packet_db[cmd].pos[1]));
+ else
+ storage_storageaddfromcart(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2,RFIFOL(fd,packet_db[cmd].pos[1]));
+}
+
+/*==========================================
+ * カプラ倉庫から出す
+ *------------------------------------------
+ */
+void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd, int cmd) {
+ nullpo_retv(sd);
+
+ if(sd->npc_id!=0 || sd->vender_id != 0) return;
+ if(sd->state.storage_flag)
+ storage_guild_storagegettocart(sd,RFIFOW(fd,packet_db[cmd].pos[0])-1,RFIFOL(fd,packet_db[cmd].pos[1]));
+ else
+ storage_storagegettocart(sd,RFIFOW(fd,packet_db[cmd].pos[0])-1,RFIFOL(fd,packet_db[cmd].pos[1]));
+}
+
+/*==========================================
+ * カプラ倉庫を閉じる
+ *------------------------------------------
+ */
+void clif_parse_CloseKafra(int fd, struct map_session_data *sd, int cmd) {
+ 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, int cmd) {
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7){
+ party_create(sd,RFIFOP(fd,packet_db[cmd].pos[0]));
+ }
+ else
+ clif_skill_fail(sd,1,0,4);
+}
+
+/*==========================================
+ * パーティを作る
+ *------------------------------------------
+ */
+void clif_parse_CreateParty2(int fd,struct map_session_data *sd, int cmd)
+{
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 7){
+ party_create(sd,RFIFOP(fd,packet_db[cmd].pos[0]));
+ }
+ else
+ clif_skill_fail(sd,1,0,4);
+}
+
+/*==========================================
+ * パーティに勧誘
+ *------------------------------------------
+ */
+void clif_parse_PartyInvite(int fd,struct map_session_data *sd, int cmd)
+{
+ party_invite(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+/*==========================================
+ * パーティ勧誘返答
+ *------------------------------------------
+ */
+void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd, int cmd)
+{
+ if(battle_config.basic_skill_check == 0 || pc_checkskill(sd,NV_BASIC) >= 5){
+ party_reply_invite(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOL(fd,packet_db[cmd].pos[1]));
+ }
+ else {
+ party_reply_invite(sd,RFIFOL(fd,packet_db[cmd].pos[0]),-1);
+ clif_skill_fail(sd,1,0,4);
+ }
+}
+/*==========================================
+ * パーティ脱退要求
+ *------------------------------------------
+ */
+void clif_parse_LeaveParty(int fd,struct map_session_data *sd, int cmd)
+{
+ party_leave(sd);
+}
+/*==========================================
+ * パーティ除名要求
+ *------------------------------------------
+ */
+void clif_parse_RemovePartyMember(int fd,struct map_session_data *sd, int cmd)
+{
+ party_removemember(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOP(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * パーティ設定変更要求
+ *------------------------------------------
+ */
+void clif_parse_PartyChangeOption(int fd,struct map_session_data *sd, int cmd)
+{
+ party_changeoption(sd,RFIFOW(fd,packet_db[cmd].pos[0]),RFIFOW(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * パーティメッセージ送信要求
+ *------------------------------------------
+ */
+void clif_parse_PartyMessage(int fd, struct map_session_data *sd, int cmd) {
+ nullpo_retv(sd);
+
+ if (is_atcommand(fd, sd, RFIFOP(fd, packet_db[cmd].pos[1]),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,packet_db[cmd].pos[1]),RFIFOW(fd,packet_db[cmd].pos[0])-packet_db[cmd].pos[1]);
+}
+
+/*==========================================
+ * 露店閉鎖
+ *------------------------------------------
+ */
+void clif_parse_CloseVending(int fd,struct map_session_data *sd, int cmd)
+{
+ vending_closevending(sd);
+}
+
+/*==========================================
+ * 露店アイテムリスト要求
+ *------------------------------------------
+ */
+void clif_parse_VendingListReq(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ vending_vendinglistreq(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+ if(sd->npc_id)
+ npc_event_dequeue(sd);
+}
+
+/*==========================================
+ * 露店アイテム購入
+ *------------------------------------------
+ */
+void clif_parse_PurchaseReq(int fd,struct map_session_data *sd, int cmd)
+{
+ vending_purchasereq(sd,RFIFOW(fd,packet_db[cmd].pos[0]),RFIFOL(fd,packet_db[cmd].pos[1]),RFIFOP(fd,packet_db[cmd].pos[2]));
+}
+
+/*==========================================
+ * 露店開設
+ *------------------------------------------
+ */
+void clif_parse_OpenVending(int fd,struct map_session_data *sd, int cmd)
+{
+ vending_openvending(sd,RFIFOW(fd,packet_db[cmd].pos[0]),RFIFOP(fd,packet_db[cmd].pos[1]),RFIFOB(fd,packet_db[cmd].pos[2]),RFIFOP(fd,packet_db[cmd].pos[3]));
+}
+
+/*==========================================
+ * ギルドを作る
+ *------------------------------------------
+ */
+void clif_parse_CreateGuild(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_create(sd,RFIFOP(fd,packet_db[cmd].pos[0]));
+}
+/*==========================================
+ * ギルドマスターかどうか確認
+ *------------------------------------------
+ */
+void clif_parse_GuildCheckMaster(int fd,struct map_session_data *sd, int cmd)
+{
+ clif_guild_masterormember(sd);
+}
+/*==========================================
+ * ギルド情報要求
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestInfo(int fd,struct map_session_data *sd, int cmd)
+{
+ switch(RFIFOL(fd,packet_db[cmd].pos[0])){
+ 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,packet_db[cmd].pos[0]));
+ break;
+ }
+}
+/*==========================================
+ * ギルド役職変更
+ *------------------------------------------
+ */
+void clif_parse_GuildChangePositionInfo(int fd,struct map_session_data *sd, int cmd)
+{
+ int i;
+ for(i=4;i<RFIFOW(fd,packet_db[cmd].pos[0]);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 cmd)
+{
+ int i;
+
+ nullpo_retv(sd);
+
+ for(i=4;i<RFIFOW(fd,packet_db[cmd].pos[0]);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, int cmd)
+{
+ struct guild *g=guild_search(RFIFOL(fd,packet_db[cmd].pos[0]));
+ if(g!=NULL)
+ clif_guild_emblem(sd,g);
+}
+
+/*==========================================
+ * ギルドエンブレム変更
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_change_emblem(sd,RFIFOW(fd,packet_db[cmd].pos[0])-packet_db[cmd].pos[1],RFIFOP(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * ギルド告知変更
+ *------------------------------------------
+ */
+void clif_parse_GuildChangeNotice(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_change_notice(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOP(fd,packet_db[cmd].pos[1]),RFIFOP(fd,packet_db[cmd].pos[2]));
+}
+
+/*==========================================
+ * ギルド勧誘
+ *------------------------------------------
+ */
+void clif_parse_GuildInvite(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_invite(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+/*==========================================
+ * ギルド勧誘返信
+ *------------------------------------------
+ */
+void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_reply_invite(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOB(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * ギルド脱退
+ *------------------------------------------
+ */
+void clif_parse_GuildLeave(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_leave(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOL(fd,packet_db[cmd].pos[1]),RFIFOL(fd,packet_db[cmd].pos[2]),RFIFOP(fd,packet_db[cmd].pos[3]));
+}
+/*==========================================
+ * ギルド追放
+ *------------------------------------------
+ */
+void clif_parse_GuildExplusion(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_explusion(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOL(fd,packet_db[cmd].pos[1]),RFIFOL(fd,packet_db[cmd].pos[2]),RFIFOP(fd,packet_db[cmd].pos[3]));
+}
+
+/*==========================================
+ * ギルド会話
+ *------------------------------------------
+ */
+void clif_parse_GuildMessage(int fd,struct map_session_data *sd, int cmd) {
+ nullpo_retv(sd);
+
+ if (is_atcommand(fd, sd, RFIFOP(fd, packet_db[cmd].pos[1]), 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,packet_db[cmd].pos[1]),RFIFOW(fd,packet_db[cmd].pos[0])-packet_db[cmd].pos[1]);
+}
+
+/*==========================================
+ * ギルド同盟要求
+ *------------------------------------------
+ */
+void clif_parse_GuildRequestAlliance(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_reqalliance(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+/*==========================================
+ * ギルド同盟要求返信
+ *------------------------------------------
+ */
+void clif_parse_GuildReplyAlliance(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_reply_reqalliance(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOL(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * ギルド関係解消
+ *------------------------------------------
+ */
+void clif_parse_GuildDelAlliance(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_delalliance(sd,RFIFOL(fd,packet_db[cmd].pos[0]),RFIFOL(fd,packet_db[cmd].pos[1]));
+}
+/*==========================================
+ * ギルド敵対
+ *------------------------------------------
+ */
+void clif_parse_GuildOpposition(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_opposition(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+/*==========================================
+ * ギルド解散
+ *------------------------------------------
+ */
+void clif_parse_GuildBreak(int fd,struct map_session_data *sd, int cmd)
+{
+ guild_break(sd,RFIFOP(fd,packet_db[cmd].pos[0]));
+}
+
+// pet
+void clif_parse_PetMenu(int fd,struct map_session_data *sd, int cmd)
+{
+ pet_menu(sd,RFIFOB(fd,packet_db[cmd].pos[0]));
+}
+void clif_parse_CatchPet(int fd,struct map_session_data *sd, int cmd)
+{
+ pet_catch_process2(sd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+
+void clif_parse_SelectEgg(int fd,struct map_session_data *sd, int cmd)
+{
+ pet_select_egg(sd,RFIFOW(fd,packet_db[cmd].pos[0])-2);
+}
+
+void clif_parse_SendEmotion(int fd,struct map_session_data *sd, int cmd)
+{
+ nullpo_retv(sd);
+
+ if(sd->pd)
+ clif_pet_emotion(sd->pd,RFIFOL(fd,packet_db[cmd].pos[0]));
+}
+
+void clif_parse_ChangePetName(int fd,struct map_session_data *sd, int cmd)
+{
+ pet_change_name(sd,RFIFOP(fd,packet_db[cmd].pos[0]));
+}
+
+// Kick (right click menu for GM "(name) force to quit")
+void clif_parse_GMKick(int fd, struct map_session_data *sd, int cmd) {
+ struct block_list *target;
+ int tid = RFIFOL(fd,packet_db[cmd].pos[0]);
+
+ 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);
+ }
+}
+
+/*==========================================
+ * GM funtions
+ *------------------------------------------
+ */
+void clif_parse_GMHide(int fd,struct map_session_data *sd, int cmd) {
+ nullpo_retv(sd);
+
+ if(pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide)) {
+ if(sd->status.option&0x40){
+ sd->status.option&=~0x40;
+ clif_displaymessage(fd,"invisible off!");
+ }else{
+ sd->status.option|=0x40;
+ clif_displaymessage(fd,"invisible!");
+ }
+ clif_changeoption(&sd->bl);
+ }
+}
+
+/*==========================================
+ * GMによるチャット禁止時間付与
+ *------------------------------------------
+ */
+void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd, int cmd)
+{
+ int tid = RFIFOL(fd,packet_db[cmd].pos[0]);
+ int type = RFIFOB(fd,packet_db[cmd].pos[1]);
+ int limit = RFIFOW(fd,packet_db[cmd].pos[2]);
+ 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_db[0x14b].len);
+ 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 cmd)
+{
+ int tid = RFIFOL(fd,packet_db[cmd].pos[0]);
+
+ WFIFOW(fd,0)=0x1e0;
+ WFIFOL(fd,2)=tid;
+ snprintf(WFIFOP(fd,6),24,"%d",tid);
+// memcpy(WFIFOP(fd,6),"TESTNAME",24);
+ WFIFOSET(fd,packet_db[0x1e0].len);
+
+ return;
+}
+
+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_db[0x1d1].len);
+
+ return 0;
+}
+
+/*==========================================
+ * スパノビの/doridoriによるSPR2倍
+ *------------------------------------------
+ */
+void clif_parse_sn_doridori(int fd, struct map_session_data *sd, int cmd) {
+ if (sd)
+ sd->doridori_counter = 1;
+
+ return;
+}
+/*==========================================
+ * スパノビの爆裂波動
+ *------------------------------------------
+ */
+void clif_parse_sn_explosionspirits(int fd, struct map_session_data *sd, int cmd)
+{
+ 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;
+}
+
+/*==========================================
+ * Friends List (davidsiaw)
+ *------------------------------------------
+ */
+void clif_friends_list_send(struct map_session_data *sd)
+{
+ unsigned char buf[1024];
+ int i,n=0;
+
+ // Packet name
+ WBUFW(buf,0)=0x201;
+
+ // check all 20 friends. stop at 0
+ for (i=0;i<20;i++)
+ {
+ if (!sd->status.friend_id[i])
+ break;
+
+ WBUFL(buf,4 + 32 * n + 1)=sd->status.friend_id[i];
+ //WBUFB(buf,4 + 32 * n + 5)=(online[n])?0:1; // <- We don't know this yet. I'd reckon its 5 but... i could be wrong.
+
+ memcpy(WBUFP(buf,4 + 32 * n + 8),&sd->status.friend_name[i],23);
+
+ n++;
+ }
+
+ WBUFW(buf,2) = 4 + 32 * n;
+
+ memcpy(WFIFOP(sd->fd,0), buf, sizeof(buf));
+ WFIFOSET(sd->fd,sizeof(buf));
+ //clif_send(buf,WBUFW(buf,2),&sd->bl,SELF);
+
+}
+void clif_parse_friends_list_add(int fd,struct map_session_data *sd)
+{
+ struct map_session_data *f_sd = map_nick2sd(RFIFOP(fd,2));
+
+ int i=0;
+
+ while (sd->status.friend_id[i]) { // Find an empty slot
+
+ if (sd->status.friend_id[i] == f_sd->status.char_id) {
+ clif_displaymessage(fd,"Friend already exists"); // Friend already exists
+ return;
+ }
+ i++;
+ if (i==20) {
+ clif_displaymessage(fd,"Friends list is full.");
+ return;
+ }
+
+ }
+
+ sd->status.friend_id[i] = f_sd->status.char_id;
+ memcpy(sd->status.friend_name[i],f_sd->status.name,23);
+ clif_displaymessage(fd,"Friend added.");
+
+ for (i=i+1; i<20; i++)
+ {
+ sd->status.friend_id[i] = 0;
+ }
+
+ clif_friends_list_send(sd);
+
+ //printf("clif_parse_friends_list_add");
+
+}
+void clif_parse_friends_list_remove(int fd,struct map_session_data *sd)
+{
+ // 0x203 </o> <ID to be removed W 4B>
+
+ int id = RFIFOL(fd,3);
+ char mes[80];
+
+ int i=0;
+
+
+ while (sd->status.friend_id[i] != id)
+ {
+ sprintf(mes, "Compare: %d and %d", id, sd->status.friend_id[i]);
+ clif_displaymessage(fd,mes);
+ i++;
+ if (i==20)
+ {
+ clif_displaymessage (fd,"Name not found in list");
+ clif_friends_list_send(sd);
+ return;
+ }
+ }
+
+ sd->status.friend_id[i] = 0;
+ memcpy(sd->status.friend_name[i],"",23);
+
+ // move all chars up
+
+ for (i=i+1; i<20; i++)
+ {
+ if(sd->status.friend_name[i])
+ {
+ sd->status.friend_id[i] = sd->status.friend_id[i-1];
+ memcpy(sd->status.friend_name[i],sd->status.friend_name[i-1],23);
+ }
+ else
+ {
+ sd->status.friend_id[i] = 0;
+ memcpy(sd->status.friend_name[i],"",23);
+ }
+
+ }
+
+ clif_friends_list_send(sd);
+
+}
+
+/*==========================================
+ * Whisper Ignoring [ /ex /in ]
+ *------------------------------------------
+ */
+int pstrcmp(const void *a, const void *b)
+{
+ return strcmp((char *)a, (char *)b);
+}
+void clif_parse_wisexin(int fd,struct map_session_data *sd, int cmd)
+{
+ int type = RFIFOB(fd,packet_db[cmd].pos[1]);
+ int i,flag=1;
+
+ if(sd){
+ qsort(sd->wis_refusal[0], MAX_WIS_REFUSAL, sizeof(sd->wis_refusal[0]), pstrcmp);
+ if(type==0){ //ex
+ for(i=0;i<MAX_WIS_REFUSAL;i++){ //すでに追加されていれば何もしない
+ if(strcmp(sd->wis_refusal[i],RFIFOP(fd,packet_db[cmd].pos[0]))==0){
+ flag=0;
+ clif_wisexin(sd, type, flag);
+ return;
+ }
+ }
+ for(i=0;i<MAX_WIS_REFUSAL;i++){ //空の拒否リストに追加(とりあえず)
+ if(sd->wis_refusal[i][0]==0){
+ memcpy(sd->wis_refusal[i],RFIFOP(fd,packet_db[cmd].pos[0]),24);
+ flag=0;
+ break;
+ }
+ }
+ if(flag==1){
+ memcpy(sd->wis_refusal[MAX_WIS_REFUSAL-1],RFIFOP(fd,packet_db[cmd].pos[0]),24);
+ flag=0;
+ }
+ }else{ //in
+ for(i=0;i<MAX_WIS_REFUSAL;i++){ //一致する拒否リストを空に
+ if(strcmp(sd->wis_refusal[i],RFIFOP(fd,packet_db[cmd].pos[0]))==0){
+ sd->wis_refusal[i][0]=0;
+ flag=0;
+ }
+ }
+ sd->wis_all = 0;
+ }
+ clif_wisexin(sd, type, flag);
+ }
+ return;
+}
+
+/*==========================================
+ * View Whisper List [ /ex ]
+ *------------------------------------------
+ */
+void clif_parse_wisexlist(int fd,struct map_session_data *sd, int cmd)
+{
+ int i,j=0,count=0;
+
+ if(sd){
+ qsort(sd->wis_refusal[0], MAX_WIS_REFUSAL, sizeof(sd->wis_refusal[0]), pstrcmp);
+ for(i=0;i<MAX_WIS_REFUSAL;i++){ //中身があるのを数える
+ if(sd->wis_refusal[i][0]!=0)
+ count++;
+ }
+ WFIFOW(fd,0)=0xd4;
+ WFIFOW(fd,2)=4+(24*count);
+ for(i=0;i<MAX_WIS_REFUSAL;i++){
+ if(sd->wis_refusal[i][0]!=0){
+ memcpy(WFIFOP(fd,4+j*24),sd->wis_refusal[i],24);
+ j++;
+ }
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+ if(count>=MAX_WIS_REFUSAL) //満タンなら最後の1個を消す
+ sd->wis_refusal[MAX_WIS_REFUSAL-1][0]=0;
+ }
+
+ return;
+}
+
+/*==========================================
+ * Block and Allow all Whispers /exall /inall
+ *------------------------------------------
+ */
+void clif_parse_wisall(int fd,struct map_session_data *sd, int cmd)
+{
+ int type = RFIFOB(fd,packet_db[cmd].pos[0]);
+ int i;
+
+ if(sd){
+ if(type==0) //exall
+ sd->wis_all = 1;
+ else{ //inall
+ for(i=0;i<MAX_WIS_REFUSAL;i++) //拒否リストを空に
+ sd->wis_refusal[i][0]=0;
+ sd->wis_all = 0;
+ }
+ clif_wisall(sd, type, 0);
+ }
+}
+
+/*==========================================
+ * /killall
+ *------------------------------------------
+ */
+void clif_parse_GMkillall(int fd,struct map_session_data *sd, int cmd)
+{
+ char message[50];
+
+ nullpo_retv(sd);
+
+ strncpy(message,sd->status.name,24);
+ is_atcommand(fd,sd,strcat(message," : @kickall"),0);
+ return;
+}
+
+/*==========================================
+ * /summon
+ *------------------------------------------
+ */
+void clif_parse_GMsummon(int fd,struct map_session_data *sd, int cmd)
+{
+ char message[100];
+
+ nullpo_retv(sd);
+
+ strncpy(message,sd->status.name,24);
+ strcat(message," : @recall ");
+ strncat(message,RFIFOP(fd,packet_db[cmd].pos[0]),24);
+ is_atcommand(fd,sd,message,0);
+ return;
+}
+/*==========================================
+ * /shift
+ *------------------------------------------
+ */
+void clif_parse_GMshift(int fd,struct map_session_data *sd, int cmd)
+{
+ char message[100];
+
+ nullpo_retv(sd);
+
+ strncpy(message,sd->status.name,24);
+ strcat(message," : @jumpto ");
+ strncat(message,RFIFOP(fd,packet_db[cmd].pos[0]),24);
+ is_atcommand(fd,sd,message,0);
+ return;
+}
+/*==========================================
+ * Debugging
+ *------------------------------------------
+ */
+void clif_parse_debug(int fd,struct map_session_data *sd, int cmd)
+{
+ int i;
+
+ printf("packet debug 0x%4X\n",cmd);
+ printf("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
+ for(i=0;i<packet_db[cmd].len;i++){
+ if((i&15)==0)
+ printf("\n%04X ",i);
+ printf("%02X ",RFIFOB(fd,i));
+ }
+ printf("\n");
+}
+
+/*==========================================
+ * クライアントからのパケット解析
+ * socket.cのdo_parsepacketから呼び出される
+ *------------------------------------------
+ */
+static int clif_parse(int fd) {
+ int packet_len = 0, cmd;
+ struct map_session_data *sd;
+
+ sd = session[fd]->session_data;
+
+ // 接続が切れてるので後始末
+ if (!chrif_isconnect() || session[fd]->eof) { // char鯖に繋がってない間は接続禁止 (!chrif_isconnect())
+ if (sd && sd->state.auth) {
+ clif_quitsave(fd, sd);
+ } 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
+ }
+ close(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;
+ }
+
+ // ゲーム用以外パケットか、認証を終える前に0072以外が来たら、切断する
+ if(cmd>=MAX_PACKET_DB || packet_db[cmd].len==0 ||
+ ((!sd || (sd && sd->state.auth==0)) && packet_db[cmd].func!=clif_parse_WantToConnection) ){
+
+ close(fd);
+ session[fd]->eof = 1;
+ if(packet_db[cmd].len==0) {
+ printf("clif_parse : %d %d %x\n",fd,packet_db[cmd].len,cmd);
+ printf("%x length 0 packet disconnect %d\n",cmd,fd);
+ }
+ return 0;
+ }
+
+ // パケット長を計算
+ packet_len = packet_db[cmd].len;
+ if(packet_len==-1){
+ if(RFIFOREST(fd)<4)
+ return 0; // 可変長パケットで長さの所までデータが来てない
+ packet_len = RFIFOW(fd,2);
+ if(packet_len<4 || packet_len>32768){
+ close(fd);
+ 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_db[cmd].func){
+ // パケット処理
+ packet_db[cmd].func(fd,sd,cmd);
+ } 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;
+}
+
+/*==========================================
+ * Read the Packet Database file.
+ *------------------------------------------
+ */
+static int packetdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int cmd,j;
+ char *str[32],*p,*str2[32],*p2;
+
+ memset(packet_db,0,sizeof(packet_db));
+
+ if( (fp=fopen("db/packet_db.txt","r"))==NULL ){
+ printf("can't read db/packet_db.txt\n");
+ exit(1);
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<4 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ cmd=strtol(str[0],(char **)NULL,0);
+ if(cmd<=0 || cmd>=MAX_PACKET_DB)
+ continue;
+
+ if(str[1]==NULL){
+ printf("packet_db: packet len error\n");
+ exit(1);
+ }
+ packet_db[cmd].len = atoi(str[1]);
+
+ if(str[2]==NULL){
+ ln++;
+ continue;
+ }
+ for(j=0;j<sizeof(clif_parse_func)/sizeof(clif_parse_func[0]);j++){
+ if(clif_parse_func[j].name == NULL){
+ printf("packet_db: %d 0x%x no func %s\n",ln+1,cmd,str[2]);
+ exit(1);
+ }
+ if( strcmp(str[2],clif_parse_func[j].name)==0){
+ packet_db[cmd].func=clif_parse_func[j].func;
+ break;
+ }
+ }
+ if(str[3]==NULL){
+ printf("packet_db: packet error\n");
+ exit(1);
+ }
+ for(j=0,p2=str[3];p2;j++){
+ str2[j]=p2;
+ p2=strchr(p2,':');
+ if(p2) *p2++=0;
+ packet_db[cmd].pos[j]=atoi(str2[j]);
+ }
+
+ ln++;
+// if(packet_db[cmd].len > 2 && packet_db[cmd].pos[0] == 0)
+// printf("packet_db:? %d 0x%x %d %s %p\n",ln,cmd,packet_db[cmd].len,str[2],packet_db[cmd].func);
+ }
+ fclose(fp);
+ printf("read db/packet_db.txt done (Packets=%d)\n",ln);
+ return 0;
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int do_init_clif(void) {
+ int i;
+
+ packetdb_readdb();
+ set_defaultparse(clif_parse);
+ for(i = 0; i < 10; i++) {
+ if (make_listen_port(map_port))
+ break;
+#ifdef _WIN32
+ 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 000000000..5a04d41d6
--- /dev/null
+++ b/src/map/clif.h
@@ -0,0 +1,297 @@
+// $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 _WIN32
+#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"
+
+#define MAX_PACKET_DB 0x220
+
+struct packet_db {
+ short len;
+ void (*func)();
+ short pos[20];
+};
+
+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_movenpc(struct npc_data *nd); // [Valaris]
+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_fixnpcpos(struct npc_data *nd); // [Valaris]
+int clif_npcbuysell(struct map_session_data*,int); //self
+int clif_buylist(struct map_session_data*,struct npc_data*); //self
+int clif_selllist(struct map_session_data*); //self
+int clif_scriptmes(struct map_session_data*,int,char*); //self
+int clif_scriptnext(struct map_session_data*,int); //self
+int clif_scriptclose(struct map_session_data*,int); //self
+int clif_scriptmenu(struct map_session_data*,int,char*); //self
+int clif_scriptinput(struct map_session_data*,int); //self
+int clif_scriptinputstr(struct map_session_data *sd,int npcid); // self
+int clif_cutin(struct map_session_data*,char*,int); //self
+int clif_viewpoint(struct map_session_data*,int,int,int,int,int,int); //self
+int clif_additem(struct map_session_data*,int,int,int); //self
+int clif_delitem(struct map_session_data*,int,int); //self
+int clif_updatestatus(struct map_session_data*,int); //self
+int clif_changestatus(struct block_list*,int,int); //area
+int clif_damage(struct block_list *,struct block_list *,unsigned int,int,int,int,int,int,int); // area
+#define clif_takeitem(src,dst) clif_damage(src,dst,0,0,0,0,0,1,0)
+int clif_changelook(struct block_list *,int,int); // area
+int clif_arrowequip(struct map_session_data *sd,int val); //self
+int clif_arrow_fail(struct map_session_data *sd,int type); //self
+int clif_arrow_create_list(struct map_session_data *sd); //self
+int clif_statusupack(struct map_session_data *,int,int,int); // self
+int clif_equipitemack(struct map_session_data *,int,int,int); // self
+int clif_unequipitemack(struct map_session_data *,int,int,int); // self
+int clif_misceffect(struct block_list*,int); // area
+int clif_misceffect2(struct block_list *bl,int type);
+int clif_changeoption(struct block_list*); // area
+int clif_useitemack(struct map_session_data*,int,int,int); // self
+
+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 fail);
+int clif_tradedeal_lock(struct map_session_data *sd,int fail);
+int clif_tradecancelled(struct map_session_data *sd);
+int clif_tradecompleted(struct map_session_data *sd,int fail);
+
+// storage
+#include "storage.h"
+int clif_storageitemlist(struct map_session_data *sd,struct storage *stor);
+int clif_storageequiplist(struct map_session_data *sd,struct storage *stor);
+int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor);
+int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount);
+int clif_storageitemremoved(struct map_session_data *sd,int index,int amount);
+int clif_storageclose(struct map_session_data *sd);
+int clif_guildstorageitemlist(struct map_session_data *sd,struct guild_storage *stor);
+int clif_guildstorageequiplist(struct map_session_data *sd,struct guild_storage *stor);
+int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor);
+int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount);
+
+int clif_pcinsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_pcoutsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_mobinsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_moboutsight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_petoutsight(struct block_list *bl,va_list ap);
+int clif_petinsight(struct block_list *bl,va_list ap);
+int clif_npcoutsight(struct block_list *bl,va_list ap);
+int clif_npcinsight(struct block_list *bl,va_list ap);
+
+int clif_class_change(struct block_list *bl,int class,int type);
+int clif_mob_class_change(struct mob_data *md,int class);
+int clif_mob_equip(struct mob_data *md,int nameid); // [Valaris]
+
+int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range);
+int clif_skillinfoblock(struct map_session_data *sd);
+int clif_skillup(struct map_session_data *sd,int skill_num);
+
+int clif_skillcasting(struct block_list* bl,
+ int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime);
+int clif_skillcastcancel(struct block_list* bl);
+int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype);
+int clif_skill_damage(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_nodamage(struct block_list *src,struct block_list *dst,
+ int skill_id,int heal,int fail);
+int clif_skill_poseffect(struct block_list *src,int skill_id,
+ int val,int x,int y,int tick);
+int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst);
+int clif_skill_warppoint(struct map_session_data *sd,int skill_num,
+ const char *map1,const char *map2,const char *map3,const char *map4);
+int clif_skill_memo(struct map_session_data *sd,int flag);
+int clif_skill_teleportmessage(struct map_session_data *sd,int flag);
+int clif_skill_produce_mix_list(struct map_session_data *sd,int trigger);
+
+int clif_produceeffect(struct map_session_data *sd,int flag,int nameid);
+
+int clif_skill_setunit(struct skill_unit *unit);
+int clif_skill_delunit(struct skill_unit *unit);
+
+int clif_01ac(struct block_list *bl);
+
+int clif_autospell(struct map_session_data *sd,int skilllv);
+int clif_devotion(struct map_session_data *sd,int 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);
+
+//friends list
+void clif_friends_list_send(struct map_session_data *sd);
+void clif_parse_friends_list_add(int fd,struct map_session_data *sd);
+void clif_parse_friends_list_remove(int fd,struct map_session_data *sd);
+
+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 000000000..164cf9827
--- /dev/null
+++ b/src/map/guild.c
@@ -0,0 +1,1554 @@
+// $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"
+#include "skill.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) { // Modified for new skills [Sara]
+ if (id==GD_BATTLEORDER) return 4;
+ else if (id==GD_REGENERATION) return 4;
+ else if (id==GD_RESTORE) return 4;
+ else if (id==GD_EMERGENCYCALL) return 4;
+ else 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) { // Modified for new skills [Sara]
+ if (id==GD_EXTENSION) return 10;
+ else if (id==GD_REGENERATION) return 3;
+ else return 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,int flag)
+{
+ 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 || flag&1) &&
+ 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,flag);
+ }
+ 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 000000000..46842464f
--- /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 flag);
+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 000000000..a2bdbe7ee
--- /dev/null
+++ b/src/map/intif.c
@@ -0,0 +1,1119 @@
+// $Id: intif.c,v 1.2 2004/09/25 05:32:18 MouseJstr Exp $
+#include <sys/types.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "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,int flag)
+{
+ WFIFOW(inter_fd, 0)=0x303c;
+ WFIFOL(inter_fd, 2)=guild_id;
+ WFIFOL(inter_fd, 6)=skill_num;
+ WFIFOL(inter_fd,10)=account_id;
+ WFIFOL(inter_fd,14)=flag;
+ WFIFOSET(inter_fd,18);
+ 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;
+}
+
+/*==========================================
+ * 指定した名前のキャラの場所要求
+ *------------------------------------------
+ */
+int intif_charposreq(int account_id,char *name,int flag)
+{
+ WFIFOW(inter_fd,0)=0x3090;
+ WFIFOL(inter_fd,2)=account_id;
+ memcpy(WFIFOP(inter_fd,6),name,24);
+ WFIFOB(inter_fd,30)=flag;
+ WFIFOSET(inter_fd,31);
+ return 0;
+}
+
+/*==========================================
+ * 指定した名前のキャラの場所に移動する
+ * @jumpto
+ *------------------------------------------
+ */
+int intif_jumpto(int account_id,char *name)
+{
+ intif_charposreq(account_id,name,1);
+ //printf("intif_jumpto: %d %s\n",account_id,name);
+ return 0;
+}
+
+/*==========================================
+ * 指定した名前のキャラの場所表示する
+ * @where
+ *------------------------------------------
+ */
+int intif_where(int account_id,char *name)
+{
+ intif_charposreq(account_id,name,0);
+ //printf("intif_where: %d %s\n",account_id,name);
+ return 0;
+}
+
+/*==========================================
+ * 指定した名前のキャラを呼び寄せる
+ * flag=0 あなたに逢いたい
+ * flag=1 @recall
+ *------------------------------------------
+ */
+int intif_charmovereq(struct map_session_data *sd,char *name,int flag)
+{
+ nullpo_retr(0,sd);
+
+ //printf("intif_charmovereq: %d %s\n",sd->status.account_id,name);
+ if(name==NULL)
+ return -1;
+
+ WFIFOW(inter_fd,0)=0x3092;
+ WFIFOL(inter_fd,2)=sd->status.account_id;
+ memcpy(WFIFOP(inter_fd,6),name,24);
+ WFIFOB(inter_fd,30)=flag;
+ memcpy(WFIFOP(inter_fd,31),sd->mapname,16);
+ WFIFOW(inter_fd,47)=sd->bl.x;
+ WFIFOW(inter_fd,49)=sd->bl.y;
+ WFIFOSET(inter_fd,51);
+ return 0;
+}
+/*==========================================
+ * 対象IDにメッセージを送信
+ *------------------------------------------
+ */
+int intif_displaymessage(int account_id, char* mes)
+{
+ int len = 6+strlen(mes)+1;
+ WFIFOW(inter_fd,0) = 0x3093;
+ WFIFOW(inter_fd,2) = len;
+ WFIFOL(inter_fd,4) = account_id;
+ strncpy(WFIFOP(inter_fd,8), mes, len-6);
+ WFIFOSET(inter_fd, len );
+
+ 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 id=RFIFOL(fd,4);
+ int i,j=0;
+
+// if(battle_config.etc_log)
+// printf("intif_parse_wismessage: %d %s %s %s\n",id,RFIFOP(fd,6),RFIFOP(fd,30),RFIFOP(fd,54) );
+
+ sd=map_nick2sd(RFIFOP(fd,32)); // 送信先を探す
+ if(sd!=NULL){
+ for(i=0;i<MAX_WIS_REFUSAL;i++){ //拒否リストに名前があるかどうか判定してあれば拒否
+ if(strcmp(sd->wis_refusal[i],RFIFOP(fd,8))==0){
+ j++;
+ break;
+ }
+ }
+ if(sd->wis_all)
+ intif_wis_replay(id,3); // 受信拒否
+ else if(j>0)
+ intif_wis_replay(id,2); // 受信拒否
+ else{
+ clif_wis_message(sd->fd,RFIFOP(fd,8),RFIFOP(fd,56),RFIFOW(fd,2)-56);
+ intif_wis_replay(id,0); // 送信成功
+ }
+ }else
+ intif_wis_replay(id,1); // そんな人いません
+ 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 000000000..bb5fcd379
--- /dev/null
+++ b/src/map/intif.h
@@ -0,0 +1,60 @@
+// $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 flag);
+int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag);
+int intif_guild_notice(int guild_id, const char *mes1, const char *mes2);
+int intif_guild_emblem(int guild_id, int len, const char *data);
+int intif_guild_castle_dataload(int castle_id, int index);
+int intif_guild_castle_datasave(int castle_id, int index, int value);
+
+int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id,
+ short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name);
+int intif_request_petdata(int account_id, int char_id, int pet_id);
+int intif_save_petdata(int account_id, struct s_pet *p);
+int intif_delete_petdata(int pet_id);
+
+int intif_jumpto(int account_id,char *name);
+int intif_where(int account_id,char *name);
+int intif_charmovereq(struct map_session_data *sd,char *name,int flag);
+
+#endif
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
new file mode 100644
index 000000000..095e053c2
--- /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 000000000..05ecc572f
--- /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/log.c b/src/map/log.c
new file mode 100644
index 000000000..b8997a7df
--- /dev/null
+++ b/src/map/log.c
@@ -0,0 +1,243 @@
+// Logging functions by Azndragon & Codemaster
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "map.h"
+#include "nullpo.h"
+#include "log.h"
+
+struct Log_Config log_config;
+
+int log_branch(struct map_session_data *sd)
+{
+ #ifndef TXT_ONLY
+ nullpo_retr(0, sd);
+ sprintf(tmp_sql, "INSERT INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%s')", log_config.log_branch_db, sd->status.account_id, sd->status.char_id, sd->status.name, sd->mapname);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ #endif
+ return 0;
+}
+
+int log_drop(struct map_session_data *sd, int monster_id, int *log_drop)
+{
+ #ifndef TXT_ONLY
+ nullpo_retr(0, sd);
+ sprintf(tmp_sql, "INSERT INTO `%s` (`drop_date`, `kill_char_id`, `monster_id`, `item1`, `item2`, `item3`, `item4`, `item5`, `item6`, `item7`, `item8`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s') ", log_config.log_drop_db, sd->status.char_id, monster_id, log_drop[0], log_drop[1], log_drop[2], log_drop[3], log_drop[4], log_drop[5], log_drop[6], log_drop[7], sd->mapname);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ #endif
+ return 0;
+}
+
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp)
+{
+ #ifndef TXT_ONLY
+ nullpo_retr(0, sd);
+ sprintf(tmp_sql, "INSERT INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], sd->mapname);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ #endif
+ return 0;
+}
+
+int log_present(struct map_session_data *sd, int source_type, int nameid)
+{
+ #ifndef TXT_ONLY
+ nullpo_retr(0, sd);
+ sprintf(tmp_sql, "INSERT INTO `%s` (`present_date`, `src_id`, `account_id`, `char_id`, `char_name`, `nameid`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%s', '%d', '%s') ", log_config.log_present_db, source_type, sd->status.account_id, sd->status.char_id, sd->status.name, nameid, sd->mapname);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ #endif
+ return 0;
+}
+
+int log_produce(struct map_session_data *sd, int nameid, int slot1, int slot2, int slot3, int success)
+{
+ #ifndef TXT_ONLY
+ nullpo_retr(0, sd);
+ sprintf(tmp_sql, "INSERT INTO `%s` (`produce_date`, `account_id`, `char_id`, `char_name`, `nameid`, `slot1`, `slot2`, `slot3`, `map`, `success`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%s', '%d') ", log_config.log_produce_db, sd->status.account_id, sd->status.char_id, sd->status.name, nameid, slot1, slot2, slot3, sd->mapname, success);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ #endif
+ return 0;
+}
+
+int log_refine(struct map_session_data *sd, int n, int success)
+{
+ #ifndef TXT_ONLY
+ int log_card[4];
+ int item_level;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(success == 0)
+ item_level = 0;
+ else
+ item_level = sd->status.inventory[n].refine + 1;
+
+ for(i=0;i<4;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+ sprintf(tmp_sql, "INSERT INTO `%s` (`refine_date`, `account_id`, `char_id`, `char_name`, `nameid`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`, `success`, `item_level`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%d', '%d')", log_config.log_refine_db, sd->status.account_id, sd->status.char_id, sd->status.name, sd->status.inventory[n].nameid, sd->status.inventory[n].refine, log_card[0], log_card[1], log_card[2], log_card[3], sd->mapname, success, item_level);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ #endif
+ return 0;
+}
+
+int log_trade(struct map_session_data *sd, struct map_session_data *target_sd, int n,int amount)
+{
+ #ifndef TXT_ONLY
+ int log_nameid, log_amount, log_refine, log_card[4];
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount>=0)
+ {
+ log_nameid = sd->status.inventory[n].nameid;
+ log_amount = sd->status.inventory[n].amount;
+ log_refine = sd->status.inventory[n].refine;
+
+ for(i=0;i<4;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+ sprintf(tmp_sql, "INSERT INTO `%s` (`trade_date`, `src_account_id`, `src_char_id`, `src_char_name`, `des_account_id`, `des_char_id`, `des_char_name`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", log_config.log_trade_db, sd->status.account_id, sd->status.char_id, sd->status.name, target_sd->status.account_id, target_sd->status.char_id, target_sd->status.name, log_nameid, log_amount, log_refine, log_card[0], log_card[1], log_card[2], log_card[3], sd->mapname);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ }
+ #endif
+ return 0;
+}
+
+int log_vend(struct map_session_data *sd,struct map_session_data *vsd,int n,int amount, int zeny)
+{
+ #ifndef TXT_ONLY
+ int log_nameid, log_amount, log_refine, log_card[4];
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL)
+ return 1;
+
+ if(sd->status.inventory[n].amount>=0)
+ {
+ log_nameid = sd->status.inventory[n].nameid;
+ log_amount = sd->status.inventory[n].amount;
+ log_refine = sd->status.inventory[n].refine;
+
+ for(i=0;i<4;i++)
+ log_card[i] = sd->status.inventory[n].card[i];
+
+ sprintf(tmp_sql, "INSERT INTO `%s` (`vend_date`, `vend_account_id`, `vend_char_id`, `vend_char_name`, `buy_account_id`, `buy_char_id`, `buy_char_name`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`, `zeny`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s', '%d')", log_config.log_vend_db, sd->status.account_id, sd->status.char_id, sd->status.name, vsd->status.account_id, vsd->status.char_id, vsd->status.name, log_nameid, log_amount, log_refine, log_card[0], log_card[1], log_card[2], log_card[3], sd->mapname, zeny);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ }
+ #endif
+ return 0;
+}
+
+int log_zeny(struct map_session_data *sd, struct map_session_data *target_sd,int amount)
+{
+ #ifndef TXT_ONLY
+ nullpo_retr(0, sd);
+
+ sprintf(tmp_sql,"INSERT INTO `%s` (`trade_date`, `src_account_id`, `src_char_id`, `src_char_name`, `des_account_id`, `des_char_id`, `des_char_name`, `map`, `zeny`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%d', '%s', '%s', '%d')", log_config.log_trade_db, sd->status.account_id, sd->status.char_id, sd->status.name, target_sd->status.account_id, target_sd->status.char_id, target_sd->status.name, sd->mapname, sd->deal_zeny);
+ if(mysql_query(&mmysql_handle, tmp_sql))
+ printf("DB server Error - %s\n",mysql_error(&mmysql_handle));
+ #endif
+ return 0;
+}
+
+int log_config_read(char *cfgName)
+{
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ if((fp = fopen(cfgName, "r")) == NULL)
+ {
+ printf("Log configuration file not found at: %s\n", cfgName);
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line) -1, fp))
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2)
+ {
+ if(strcmpi(w1,"log_branch") == 0) {
+ log_config.branch = (atoi(w2));
+ } else if(strcmpi(w1,"log_drop") == 0) {
+ log_config.drop = (atoi(w2));
+ } else if(strcmpi(w1,"log_mvpdrop") == 0) {
+ log_config.mvpdrop = (atoi(w2));
+ } else if(strcmpi(w1,"log_present") == 0) {
+ log_config.present = (atoi(w2));
+ } else if(strcmpi(w1,"log_produce") == 0) {
+ log_config.produce = (atoi(w2));
+ } else if(strcmpi(w1,"log_refine") == 0) {
+ log_config.refine = (atoi(w2));
+ } else if(strcmpi(w1,"log_trade") == 0) {
+ log_config.trade = (atoi(w2));
+ } else if(strcmpi(w1,"log_vend") == 0) {
+ log_config.vend = (atoi(w2));
+ } else if(strcmpi(w1,"log_zeny") == 0) {
+ if(log_config.trade != 1)
+ log_config.zeny = 0;
+ else
+ log_config.zeny = (atoi(w2));
+ }
+
+ else if(strcmpi(w1, "log_branch_db") == 0) {
+ strcpy(log_config.log_branch_db, w2);
+ if(log_config.branch == 1)
+ printf("Logging Dead Branch Usage to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_drop_db") == 0) {
+ strcpy(log_config.log_drop_db, w2);
+ if(log_config.drop == 1)
+ printf("Logging Item Drops to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_mvpdrop_db") == 0) {
+ strcpy(log_config.log_mvpdrop_db, w2);
+ if(log_config.mvpdrop == 1)
+ printf("Logging MVP Drops to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_present_db") == 0) {
+ strcpy(log_config.log_present_db, w2);
+ if(log_config.present == 1)
+ printf("Logging Present Usage & Results to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_produce_db") == 0) {
+ strcpy(log_config.log_produce_db, w2);
+ if(log_config.produce == 1)
+ printf("Logging Producing to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_refine_db") == 0) {
+ strcpy(log_config.log_refine_db, w2);
+ if(log_config.refine == 1)
+ printf("Logging Refining to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_trade_db") == 0) {
+ strcpy(log_config.log_trade_db, w2);
+ if(log_config.trade == 1)
+ {
+ printf("Logging Item Trades");
+ if(log_config.zeny == 1)
+ printf("and Zeny Trades");
+ printf(" to table `%s`\n", w2);
+ }
+ } else if(strcmpi(w1, "log_vend_db") == 0) {
+ strcpy(log_config.log_vend_db, w2);
+ if(log_config.vend == 1)
+ printf("Logging Vending to table `%s`\n", w2);
+ }
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
diff --git a/src/map/log.h b/src/map/log.h
new file mode 100644
index 000000000..7047d980c
--- /dev/null
+++ b/src/map/log.h
@@ -0,0 +1,27 @@
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#ifndef TXT_ONLY
+
+extern char db_server_logdb[32];
+
+#endif //NOT TXT_ONLY
+
+int log_branch(struct map_session_data *sd);
+int log_drop(struct map_session_data *sd, int monster_id, int *log_drop);
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp);
+int log_present(struct map_session_data *sd, int source_type, int nameid);
+int log_produce(struct map_session_data *sd, int nameid, int slot1, int slot2, int slot3, int success);
+int log_refine(struct map_session_data *sd, int n, int success);
+int log_trade(struct map_session_data *sd,struct map_session_data *target_sd,int n,int amount);
+int log_vend(struct map_session_data *sd,struct map_session_data *vsd,int n,int amount,int zeny);
+int log_zeny(struct map_session_data *sd, struct map_session_data *target_sd,int amount);
+
+int log_config_read(char *cfgName);
+
+extern struct Log_Config {
+ int branch, drop, mvpdrop, present, produce, refine, trade, vend, zeny;
+ char log_branch_db[32], log_drop_db[32], log_mvpdrop_db[32], log_present_db[32], log_produce_db[32], log_refine_db[32], log_trade_db[32], log_vend_db[32];
+} log_config;
+
+#endif
diff --git a/src/map/mail.c b/src/map/mail.c
new file mode 100644
index 000000000..c6155a04d
--- /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)
+{
+ struct map_session_data *sd = NULL;
+ int i;
+
+ 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;
+ }
+
+ 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 000000000..cfa86c6bb
--- /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 000000000..217f36c88
--- /dev/null
+++ b/src/map/map.c
@@ -0,0 +1,2208 @@
+// $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 _WIN32
+#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"
+#include "log.h"
+
+#ifdef MEMWATCH
+#include "memwatch.h"
+#endif
+
+unsigned long ticks = 0; // by MC Cameri
+
+#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";
+
+char log_db[32] = "log";
+char log_db_ip[16] = "127.0.0.1";
+char log_db_id[32] = "ragnarok";
+char log_db_pw[32] = "ragnarok";
+int log_db_port = 3306;
+
+char gm_db[32] = "login";
+char gm_db_level[32] = "level";
+char gm_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]
+
+//Added for Mugendai's I'm Alive mod
+int imalive_on=0;
+int imalive_time=60;
+//Added by Mugendai for GUI
+int flush_on=1;
+int flush_time=100;
+
+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
+
+int console = 0;
+/*==========================================
+ * 全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 {float high[4]; int type;} *p=NULL;
+ 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=*(int*)(gat+6);
+ ys=map[m].ys=*(int*)(gat+10);
+ map[m].gat = (unsigned char *)aCalloc(s = map[m].xs * map[m].ys,sizeof(unsigned char));
+ 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*20+14);
+ 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 = (struct block_list **)aCalloc(1,size);
+ map[m].block_mob = (struct block_list **)aCalloc(1,size);
+ size = map[m].bxs*map[m].bys*sizeof(int);
+ map[m].block_count = (int *)aCalloc(1,size);
+ map[m].block_mob_count=(int *)aCalloc(1,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;
+}
+
+static int map_ip_set_ = 0;
+static int char_ip_set_ = 0;
+
+/*==========================================
+ * Console Command Parser [Wizputer]
+ *------------------------------------------
+ */
+int parse_console(char *buf) {
+ char *type,*command,*map, *buf2;
+ int x = 0, y = 0;
+ int m, n;
+ struct map_session_data *sd;
+
+ sd = calloc(sizeof(*sd), 1);
+
+ sd->fd = 0;
+ strcpy( sd->status.name , "console");
+
+ type = (char *)malloc(64);
+ command = (char *)malloc(64);
+ map = (char *)malloc(64);
+ buf2 = (char *)malloc(72);
+
+ memset(type,0,64);
+ memset(command,0,64);
+ memset(map,0,64);
+ memset(buf2,0,72);
+
+ if ( ( n = sscanf(buf, "%[^:]:%[^:]:%99s %d %d[^\n]", type , command , map , &x , &y )) < 5 )
+ if ( ( n = sscanf(buf, "%[^:]:%[^\n]", type , command )) < 2 )
+ n = sscanf(buf,"%[^\n]",type);
+
+ if ( n == 5 ) {
+ if (x <= 0) {
+ x = rand() % 399 + 1;
+ sd->bl.x = x;
+ } else {
+ sd->bl.x = x;
+ }
+
+ if (y <= 0) {
+ y = rand() % 399 + 1;
+ sd->bl.y = y;
+ } else {
+ sd->bl.y = y;
+ }
+
+ m = map_mapname2mapid(map);
+ if ( m >= 0 )
+ sd->bl.m = m;
+ else {
+ printf("Console: Unknown map\n");
+ goto end;
+ }
+ }
+
+ printf("Type of command: %s || Command: %s || Map: %s Coords: %d %d\n",type,command,map,x,y);
+
+ if ( strcmpi("admin",type) == 0 && n == 5 ) {
+ sprintf(buf2,"console: %s",command);
+ if( is_atcommand(sd->fd,sd,buf2,99) == AtCommand_None )
+ printf("Console: not atcommand\n");
+ } else if ( strcmpi("server",type) == 0 && n == 2 ) {
+ if ( strcmpi("shutdown", command) == 0 || strcmpi("exit",command) == 0 || strcmpi("quit",command) == 0 ) {
+ exit(0);
+ }
+ } else if ( strcmpi("help",type) == 0 ) {
+ printf("To use GM commands:\n");
+ printf("admin:<gm command>:<map of \"gm\"> <x> <y>\n");
+ printf("You can use any GM command that doesn't require the GM.\n");
+ printf("No using @item or @warp however you can use @charwarp\n");
+ printf("The <map of \"gm\"> <x> <y> is for commands that need coords of the GM\n");
+ printf("IE: @spawn\n");
+ printf("To shutdown the server:\n");
+ printf("server:shutdown\n");
+ }
+
+ end:
+ free(buf);
+ free(type);
+ free(command);
+ free(map);
+ free(buf2);
+ free(sd);
+
+ return 0;
+}
+
+/*==========================================
+ * 設定ファイルを読み込む
+ *------------------------------------------
+ */
+int map_config_read(char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+ struct hostent *h = NULL;
+
+ fp = fopen(cfgName,"r");
+ if (fp == NULL) {
+ 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) {
+ 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]);
+ sprintf(w2,"%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]);
+ }
+ chrif_setip(w2);
+ } else if (strcmpi(w1, "char_port") == 0) {
+ chrif_setport(atoi(w2));
+ } else if (strcmpi(w1, "map_ip") == 0) {
+ map_ip_set_ = 1;
+ h = gethostbyname (w2);
+ if (h != NULL) {
+ 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);
+ } else if (strcmpi(w1, "console") == 0) {
+ if(strcmpi(w2,"on") == 0 || strcmpi(w2,"yes") == 0 )
+ console = 1;
+ } else if(strcmpi(w1,"imalive_on")==0){ //Added by Mugendai for I'm Alive mod
+ imalive_on = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"imalive_time")==0){ //Added by Mugendai for I'm Alive mod
+ imalive_time = atoi(w2); //Added by Mugendai for I'm Alive mod
+ } else if(strcmpi(w1,"flush_on")==0){ //Added by Mugendai for GUI
+ flush_on = atoi(w2); //Added by Mugendai for GUI
+ } else if(strcmpi(w1,"flush_time")==0){ //Added by Mugendai for GUI
+ flush_time = atoi(w2); //Added by Mugendai for GUI
+ }
+
+ }
+ }
+ 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 log_sql_init(void){
+
+ mysql_init(&mmysql_handle);
+
+ //DB connection start
+ printf("\033[1;29m[SQL]\033[0;0m: Connecting to Log Database \033[1;29m%s\033[0;0m At \033[1;29m%s\033[0;0m...\n",log_db,log_db_ip);
+ if(!mysql_real_connect(&mmysql_handle, log_db_ip, log_db_id, log_db_pw,
+ log_db ,log_db_port, (char *)NULL, 0)) {
+ //pointer check
+ printf("\033[1;29m[SQL Error]\033[0;0m: %s\n",mysql_error(&mmysql_handle));
+ exit(1);
+ } else {
+ printf("\033[1;29m[SQL]\033[0;0m: Successfully \033[1;32mconnected\033[0;0m to Database \033[1;29m%s\033[0;0m.\n", log_db);
+ }
+
+ 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);
+ } else if(strcmpi(w1,"gm_db_level")==0){
+ strcpy(gm_db_level,w2);
+ } else if(strcmpi(w1,"gm_db_account_id")==0){
+ strcpy(gm_db_account_id,w2);
+ } else if(strcmpi(w1,"gm_db")==0){
+ strcpy(gm_db,w2);
+ //Map Server SQL DB
+ } else if(strcmpi(w1,"map_server_ip")==0){
+ strcpy(map_server_ip, w2);
+ 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);
+ } else if(strcmpi(w1,"log_db")==0) {
+ strcpy(log_db, w2);
+ } else if(strcmpi(w1,"log_db_ip")==0) {
+ strcpy(log_db_ip, w2);
+ } else if(strcmpi(w1,"log_db")==0) {
+ strcpy(log_db, w2);
+ } else if(strcmpi(w1,"log_db_id")==0) {
+ strcpy(log_db_id, w2);
+ } else if(strcmpi(w1,"log_db_pw")==0) {
+ strcpy(log_db_pw, w2);
+ } else if(strcmpi(w1,"log_db_port")==0) {
+ log_db_port = atoi(w2);
+ }
+ }
+ 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 */
+
+//-----------------------------------------------------
+//I'm Alive Alert
+//Used to output 'I'm Alive' every few seconds
+//Intended to let frontends know if the app froze
+//-----------------------------------------------------
+int imalive_timer(int tid, unsigned int tick, int id, int data){
+ printf("I'm Alive\n");
+ return 0;
+}
+
+//-----------------------------------------------------
+//Flush stdout
+//stdout buffer needs flushed to be seen in GUI
+//-----------------------------------------------------
+int flush_timer(int tid, unsigned int tick, int id, int data){
+ fflush(stdout);
+ return 0;
+}
+
+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";
+ unsigned char *LOG_CONF_NAME="conf/log_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);
+
+ if ((naddr_ == 0) && (map_ip_set_ == 0 || char_ip_set_ == 0)) {
+ printf("\nUnable to determine your IP address... please edit\n");
+ printf("the map_athena.conf file and set it.\n");
+ printf("(127.0.0.1 is valid if you have no network interface)\n");
+ }
+
+ if (map_ip_set_ == 0 || char_ip_set_ == 0) {
+ // The map server should know what IP address it is running on
+ // - MouseJstr
+ int localaddr = ntohl(addr_[0]);
+ unsigned char *ptr = (unsigned char *) &localaddr;
+ char buf[16];
+ sprintf(buf, "%d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);;
+ if (naddr_ != 1)
+ printf("Multiple interfaces detected.. using %s as our IP address\n", buf);
+ else
+ printf("Defaulting to %s as our IP address\n", buf);
+ if (map_ip_set_ == 0)
+ clif_setip(buf);
+ if (char_ip_set_ == 0)
+ chrif_setip(buf);
+
+ if (ptr[0] == 192 && ptr[1] == 168)
+ printf("\nFirewall detected.. \n edit lan_support.conf and map_athena.conf\n\n");
+ }
+
+ 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);
+ log_config_read(LOG_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");
+
+ //Added by Mugendai for GUI support
+ if (flush_on)
+ {
+ add_timer_interval(gettick()+10, flush_timer,0,0,flush_time);
+ }
+
+#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();
+
+ if (log_config.branch || log_config.drop || log_config.mvpdrop ||
+ log_config.present || log_config.produce || log_config.refine ||
+ log_config.trade)
+ {
+ log_sql_init();
+ }
+#endif /* not TXT_ONLY */
+
+ npc_event_do_oninit(); // npcのOnInitイベント実行
+
+ if ( console ) {
+ set_defaultconsoleparse(parse_console);
+ start_console();
+ }
+
+ if (battle_config.pk_mode == 1)
+ printf("The server is running in \033[1;31mPK Mode\033[0m.\n");
+
+ //Added for Mugendais I'm Alive mod
+ if (imalive_on)
+ add_timer_interval(gettick()+10, imalive_timer,0,0,imalive_time*1000);
+
+ printf("The map-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", map_port);
+ ticks = gettick();
+
+
+ return 0;
+}
diff --git a/src/map/map.h b/src/map/map.h
new file mode 100644
index 000000000..6e7f6c7e5
--- /dev/null
+++ b/src/map/map.h
@@ -0,0 +1,724 @@
+// $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 MAX_WIS_REFUSAL 14
+
+#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;
+ unsigned short amount;
+ unsigned 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;
+
+ char wis_refusal[MAX_WIS_REFUSAL][24]; //Refuse Whispers
+ int wis_all; //Ignore all Whispers
+
+ 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
+ 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;
+ int walktimer; // [Valaris]
+ short to_x,to_y; // [Valaris]
+ struct walkpath_data walkpath;
+ unsigned int next_walktime;
+ unsigned int canmove_tick;
+
+ struct { // [Valaris]
+ unsigned state : 8;
+ unsigned change_walk_target : 1;
+ unsigned walk_easy : 1;
+ } state;
+
+ 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);
+
+extern unsigned long ticks;
+
+#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 char gm_db[32];
+extern char gm_db_level[32];
+extern char gm_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 000000000..46e367371
--- /dev/null
+++ b/src/map/mob.c
@@ -0,0 +1,4269 @@
+// $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"
+#include "log.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;
+}
+
+/*==========================================
+ * The disregard ID is added to mob.
+ *------------------------------------------
+ */
+int mob_exclusion_add(struct mob_data *md,int type,int id)
+{
+ nullpo_retr(0, md);
+
+ if(type==1)
+ md->exclusion_src=id;
+ if(type==2)
+ md->exclusion_party=id;
+ if(type==3)
+ md->exclusion_guild=id;
+
+ return 0;
+}
+
+/*==========================================
+ * The disregard ID of mob is checked. (TAGE?)
+ *------------------------------------------
+ */
+int mob_exclusion_check(struct mob_data *md,struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+ nullpo_retr(0, md);
+
+ if(sd->bl.type==BL_PC){
+ if(md->exclusion_src && md->exclusion_src==sd->bl.id)
+ return 1;
+ if(md->exclusion_party && md->exclusion_party==sd->status.party_id)
+ return 2;
+ if(md->exclusion_guild && md->exclusion_guild==sd->status.guild_id)
+ return 3;
+ }
+ return 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)) {
+ int log_item[8] = {0};
+ 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;
+ log_item[i] = ditem->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);
+ }
+
+ #ifndef TXT_ONLY
+ if(log_config.drop > 0)
+ log_drop(mvp_sd, md->class, log_item);
+ #endif
+
+ 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 log_mvp[2] = {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);
+ log_mvp[1] = mexp;
+ 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);
+ log_mvp[0] = item.nameid;
+ if(mvp_sd->weight*2 > mvp_sd->max_weight)
+ map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1);
+ else if((ret = pc_additem(mvp_sd,&item,1))) {
+ clif_additem(sd,0,0,ret);
+ map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1);
+ }
+ break;
+ }
+ #ifndef TXT_ONLY
+ if(log_config.mvpdrop > 0)
+ log_mvpdrop(mvp_sd, md->class, log_mvp);
+ #endif
+ }
+
+ } // [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 000000000..2ec71d90c
--- /dev/null
+++ b/src/map/mob.h
@@ -0,0 +1,139 @@
+// $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_exclusion_add(struct mob_data *md,int type,int id);
+int mob_exclusion_check(struct mob_data *md,struct map_session_data *sd);
+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 000000000..1c089a2c9
--- /dev/null
+++ b/src/map/npc.c
@@ -0,0 +1,2274 @@
+// $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; // 時計イベント用
+
+static int npc_walktimer(int,unsigned int,int,int); // [Valaris]
+static int npc_walktoxy_sub(struct npc_data *nd); // [Valaris]
+
+/*==========================================
+ * NPCの無効化/有効化
+ * npc_enable
+ * npc_enable_sub 有効時にOnTouchイベントを実行
+ *------------------------------------------
+ */
+int npc_enable_sub( struct block_list *bl, va_list ap )
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+ char *name=(char *)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 && strcmpi(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 && strcmpi(name,p)==0 ) {
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ (*c)++;
+ }
+
+ return 0;
+}
+int npc_event_do(const 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 && strnicmp("::OnTimer",p,8)==0 ){
+ sscanf(&p[9],"%s",temp);
+ tick=atoi(temp);
+
+ strcpy( event, ev->nd->name);
+ strcat( event, p);
+
+ if (option!=0) {
+ npc_addeventtimer(ev->nd,tick,event);
+ } else {
+ npc_deleventtimer(ev->nd,event);
+ }
+ }
+ return 0;
+}
+int npc_do_ontimer(int npc_id, 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 (strnicmp(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 && strnicmp("::OnCommand",p,10)==0 ){
+ sscanf(&p[11],"%s",temp);
+
+ if (strcmp(command,temp)==0)
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ }
+
+ return 0;
+}
+
+int npc_command(struct map_session_data *sd,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;
+
+}
+
+// [Valaris] NPC Walking
+
+/*==========================================
+ * Time calculation concerning one step next to npc
+ *------------------------------------------
+ */
+static int calc_next_walk_step(struct npc_data *nd)
+{
+ nullpo_retr(0, nd);
+
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len)
+ return -1;
+ if(nd->walkpath.path[nd->walkpath.path_pos]&1)
+ return battle_get_speed(&nd->bl)*14/10;
+ return battle_get_speed(&nd->bl);
+}
+
+
+/*==========================================
+ * npc Walk processing
+ *------------------------------------------
+ */
+static int npc_walk(struct npc_data *nd,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, nd);
+
+ nd->state.state=MS_IDLE;
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len || nd->walkpath.path_pos!=data)
+ return 0;
+
+ nd->walkpath.path_half ^= 1;
+ if(nd->walkpath.path_half==0){
+ nd->walkpath.path_pos++;
+ if(nd->state.change_walk_target){
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+ }
+ else {
+ if(nd->walkpath.path[nd->walkpath.path_pos]>=8)
+ return 1;
+
+ x = nd->bl.x;
+ y = nd->bl.y;
+ ctype = map_getcell(nd->bl.m,x,y);
+ if(ctype == 1 || ctype == 5) {
+ npc_stop_walking(nd,1);
+ return 0;
+ }
+ nd->dir=nd->walkpath.path[nd->walkpath.path_pos];
+ dx = dirx[nd->dir];
+ dy = diry[nd->dir];
+
+ ctype = map_getcell(nd->bl.m,x+dx,y+dy);
+ if(ctype == 1 || ctype == 5) {
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+
+ moveblock = ( x/BLOCK_SIZE != (x+dx)/BLOCK_SIZE || y/BLOCK_SIZE != (y+dy)/BLOCK_SIZE);
+
+ nd->state.state=MS_WALK;
+ map_foreachinmovearea(clif_npcoutsight,nd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,dx,dy,BL_PC,nd);
+
+ x += dx;
+ y += dy;
+
+ if(moveblock) map_delblock(&nd->bl);
+ nd->bl.x = x;
+ nd->bl.y = y;
+ if(moveblock) map_addblock(&nd->bl);
+
+ map_foreachinmovearea(clif_npcinsight,nd->bl.m,x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE,-dx,-dy,BL_PC,nd);
+ nd->state.state=MS_IDLE;
+ }
+ if((i=calc_next_walk_step(nd))>0){
+ i = i>>1;
+ if(i < 1 && nd->walkpath.path_half == 0)
+ i = 1;
+ nd->walktimer=add_timer(tick+i,npc_walktimer,nd->bl.id,nd->walkpath.path_pos);
+ nd->state.state=MS_WALK;
+
+ if(nd->walkpath.path_pos>=nd->walkpath.path_len)
+ clif_fixnpcpos(nd); // When npc stops, retransmission current of a position.
+
+ }
+ return 0;
+}
+
+int npc_changestate(struct npc_data *nd,int state,int type)
+{
+ int i;
+
+ nullpo_retr(0, nd);
+
+ if(nd->walktimer != -1)
+ delete_timer(nd->walktimer,npc_walktimer);
+ nd->walktimer=-1;
+ nd->state.state=state;
+
+ switch(state){
+ case MS_WALK:
+ if((i=calc_next_walk_step(nd))>0){
+ i = i>>2;
+ nd->walktimer=add_timer(gettick()+i,npc_walktimer,nd->bl.id,0);
+ }
+ else
+ nd->state.state=MS_IDLE;
+ break;
+ case MS_DELAY:
+ nd->walktimer=add_timer(gettick()+type,npc_walktimer,nd->bl.id,0);
+ break;
+
+ }
+
+ return 0;
+}
+
+static int npc_walktimer(int tid,unsigned int tick,int id,int data)
+{
+ struct npc_data *nd;
+
+ nd=(struct npc_data*)map_id2bl(id);
+ if(nd == NULL || nd->bl.type != BL_NPC)
+ return 1;
+
+ if(nd->walktimer != tid){
+ return 0;
+ }
+
+ nd->walktimer=-1;
+
+ if(nd->bl.prev == NULL)
+ return 1;
+
+ switch(nd->state.state){
+ case MS_WALK:
+ npc_walk(nd,tick,data);
+ break;
+ case MS_DELAY:
+ npc_changestate(nd,MS_IDLE,0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static int npc_walktoxy_sub(struct npc_data *nd)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, nd);
+
+ if(path_search(&wpd,nd->bl.m,nd->bl.x,nd->bl.y,nd->to_x,nd->to_y,nd->state.walk_easy))
+ return 1;
+ memcpy(&nd->walkpath,&wpd,sizeof(wpd));
+
+ nd->state.change_walk_target=0;
+ npc_changestate(nd,MS_WALK,0);
+
+ clif_movenpc(nd);
+
+ return 0;
+}
+
+int npc_walktoxy(struct npc_data *nd,int x,int y,int easy)
+{
+ struct walkpath_data wpd;
+
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK && path_search(&wpd,nd->bl.m,nd->bl.x,nd->bl.y,x,y,0) )
+ return 1;
+
+ nd->state.walk_easy = easy;
+ nd->to_x=x;
+ nd->to_y=y;
+ if(nd->state.state == MS_WALK) {
+ nd->state.change_walk_target=1;
+ } else {
+ return npc_walktoxy_sub(nd);
+ }
+
+ return 0;
+}
+
+int npc_stop_walking(struct npc_data *nd,int type)
+{
+ nullpo_retr(0, nd);
+
+ if(nd->state.state == MS_WALK || nd->state.state == MS_IDLE) {
+ int dx=0,dy=0;
+
+ nd->walkpath.path_len=0;
+ if(type&4){
+ dx=nd->to_x-nd->bl.x;
+ if(dx<0)
+ dx=-1;
+ else if(dx>0)
+ dx=1;
+ dy=nd->to_y-nd->bl.y;
+ if(dy<0)
+ dy=-1;
+ else if(dy>0)
+ dy=1;
+ }
+ nd->to_x=nd->bl.x+dx;
+ nd->to_y=nd->bl.y+dy;
+ if(dx!=0 || dy!=0){
+ npc_walktoxy_sub(nd);
+ return 0;
+ }
+ npc_changestate(nd,MS_IDLE,0);
+ }
+ if(type&0x01)
+ clif_fixnpcpos(nd);
+ if(type&0x02) {
+ int delay=battle_get_dmotion(&nd->bl);
+ unsigned int tick = gettick();
+ if(nd->canmove_tick < tick)
+ nd->canmove_tick = tick + delay;
+ }
+
+ 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;
+ nd->walktimer=-1;
+
+ //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_walktimer,"npc_walktimer"); // [Valaris]
+ add_timer_func_list(npc_event_timer,"npc_event_timer");
+ add_timer_func_list(npc_event_do_clock,"npc_event_do_clock");
+ add_timer_func_list(npc_timerevent,"npc_timerevent");
+
+ //exit(1);
+
+ return 0;
+}
diff --git a/src/map/npc.h b/src/map/npc.h
new file mode 100644
index 000000000..9bc7a6c96
--- /dev/null
+++ b/src/map/npc.h
@@ -0,0 +1,52 @@
+// $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_walktoxy(struct npc_data *nd,int x,int y,int easy); // npc walking [Valaris]
+int npc_stop_walking(struct npc_data *nd,int type);
+int npc_changestate(struct npc_data *nd,int state,int type);
+
+int npc_get_new_npc_id(void);
+
+void npc_addsrcfile(char *);
+void npc_delsrcfile(char *);
+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 000000000..d433fa3b8
--- /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 000000000..e1a03fcd1
--- /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 000000000..51143c943
--- /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 000000000..34a1e9c72
--- /dev/null
+++ b/src/map/pc.c
@@ -0,0 +1,7499 @@
+// $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"
+#include "log.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);
+
+ if(sd->bl.type!=BL_PC )
+ return 0;
+
+/* p = numdb_search(gm_account_db, sd->status.account_id);
+ if (p == NULL)
+ return 0;
+ return p->level;*/
+
+ //For console [Wizputer]
+ if ( sd->fd == 0 )
+ return 99;
+
+ for(i = 0; i < GM_num; i++)
+ if (gm_account[i].account_id == sd->status.account_id)
+ return gm_account[i].level;
+ return 0;
+
+}
+
+int pc_iskiller(struct map_session_data *src, struct map_session_data *target) {
+ nullpo_retr(0, src);
+
+ if(src->bl.type!=BL_PC )
+ return 0;
+ if (src->special_state.killer)
+ return 1;
+
+ if(target->bl.type!=BL_PC )
+ return 0;
+ if (target->special_state.killable)
+ return 1;
+
+ return 0;
+}
+
+
+int pc_set_gm_level(int account_id, int level) {
+ int i;
+ for (i = 0; i < GM_num; i++) {
+ if (account_id == gm_account[i].account_id) {
+ gm_account[i].level = level;
+ return 0;
+ }
+ }
+
+ GM_num++;
+ gm_account = 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].attribute==1){
+ item=sd->inventory_data[i];
+ sd->status.inventory[i].attribute=1;
+ pc_unequipitem(sd,i,0);
+ sprintf(output, "%s has broken.",item->jname);
+ clif_emotion(&sd->bl,23);
+ clif_displaymessage(sd->fd, output);
+ clif_equiplist(sd);
+ return 1;
+ }
+ }
+
+ 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].attribute==1){
+ item=sd->inventory_data[i];
+ sd->status.inventory[i].attribute=1;
+ pc_unequipitem(sd,i,0);
+ sprintf(output, "%s has broken.",item->jname);
+ clif_emotion(&sd->bl,23);
+ clif_displaymessage(sd->fd, output);
+ clif_equiplist(sd);
+ }
+ }
+
+ 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));
+
+ // パーティー関係の初期化
+ 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);
+
+ //printf("pc: OnPCLogin event done. (%d events)\n", npc_event_doall("OnPCLogin") );
+ if (npc_name2id("PCLoginEvent"))
+ run_script(npc_name2id("PCLoginEvent")->u.scr.script,0,sd->bl.id,npc_name2id("PCLoginEvent")->bl.id); // PCLoginNPC
+ // Send friends list
+ clif_friends_list_send(sd);
+
+ // 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++){
+ if(i==331) continue;
+ 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_CHASEWALK].timer!=-1)
+ sd->speed = sd->speed*(135-sd->sc_data[SC_CHASEWALK].val1*5)/100; // slow down by chasewalk
+ 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;
+
+#ifndef TXT_ONLY
+ if((log_config.branch > 0) && (nameid == 604))
+ log_branch(sd);
+#endif
+
+ 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) && sd->status.base_exp+base_exp >= pc_nextbaseafter(sd)) {
+ base_exp = pc_nextbaseafter(sd) - sd->status.base_exp;
+ if (base_exp < 0)
+ base_exp = 0;
+ }
+
+ sd->status.base_exp += base_exp;
+ if(sd->status.base_exp < 0)
+ sd->status.base_exp = 0;
+
+ while(pc_checkbaselevelup(sd)) ;
+
+ clif_updatestatus(sd,SP_BASEEXP);
+ if(!battle_config.multi_level_up && pc_nextjobafter(sd) && sd->status.job_exp+job_exp >= pc_nextjobafter(sd)) {
+ job_exp = pc_nextjobafter(sd) - sd->status.job_exp;
+ if (job_exp < 0)
+ job_exp = 0;
+ }
+
+ sd->status.job_exp += job_exp;
+ if(sd->status.job_exp < 0)
+ sd->status.job_exp = 0;
+
+ while(pc_checkjoblevelup(sd)) ;
+
+ clif_updatestatus(sd,SP_JOBEXP);
+
+ if(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,0);
+ return 0;
+ }
+
+ if( sd->status.skill_point>0 &&
+ sd->status.skill[skill_num].id!=0 &&
+ sd->status.skill[skill_num].lv < skill_get_max(skill_num) )
+ {
+ 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++){
+ if(i==331) continue;
+ 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=88;
+ }
+
+ 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]);
+ if(sd->status.class >= 4001 && sd->status.class <= 4024)
+ sd->status.status_point+=40;
+// 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].attribute==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].attribute==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'",gm_db_account_id,gm_db_level,gm_db,gm_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 000000000..befa690fd
--- /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 000000000..8bc17003a
--- /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 000000000..b4ccf5ccb
--- /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 000000000..534e50cd1
--- /dev/null
+++ b/src/map/script.c
@@ -0,0 +1,6769 @@
+// $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 _WIN32
+#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"
+#include "log.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]
+int buildin_npcspeed(struct script_state *st); // [Valaris]
+int buildin_npcwalkto(struct script_state *st); // [Valaris]
+int buildin_npcstop(struct script_state *st); // [Valaris]
+
+
+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"},
+ {buildin_npcspeed,"npcspeed","i"}, // [Valaris]
+ {buildin_npcwalkto,"npcwalkto","ii"}, // [Valaris]
+ {buildin_npcstop,"npcstop",""}, // [Valaris]
+ {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);
+ #ifndef TXT_ONLY
+ if(log_config.present > 0)
+ log_present(sd, -nameid, nameid);
+ #endif //USE_SQL
+ 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].attribute==1){
+ brokencounter++;
+ if(num==brokencounter){
+ id=sd->status.inventory[i].nameid;
+ break;
+ }
+ }
+ }
+
+ push_val(st->stack,C_INT,id);
+
+ return 0;
+}
+
+/*==========================================
+ * repair [Valaris]
+ *------------------------------------------
+ */
+int buildin_repair(struct script_state *st)
+{
+ int i,num;
+ int repaircounter=0;
+ struct map_session_data *sd;
+
+
+ sd=script_rid2sd(st);
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute==1){
+ repaircounter++;
+ if(num==repaircounter){
+ sd->status.inventory[i].attribute=0;
+ clif_equiplist(sd);
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ clif_misceffect(&sd->bl, 3);
+ clif_displaymessage(sd->fd,"Item has been repaired.");
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 装備チェック
+ *------------------------------------------
+ */
+int buildin_getequipisequiped(struct script_state *st)
+{
+ int i,num;
+ struct map_session_data *sd;
+
+ num=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ sd=script_rid2sd(st);
+ 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;
+
+ #ifndef TXT_ONLY
+ if(log_config.refine > 0)
+ log_refine(sd, i, 1);
+ #endif //USE_SQL
+
+ 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) {
+ #ifndef TXT_ONLY
+ if(log_config.refine > 0)
+ log_refine(sd, i, 0);
+ #endif //USE_SQL
+
+ 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,flag=0;
+ struct map_session_data *sd;
+ int i=0;
+
+ id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ level=conv_num(st,& (st->stack->stack_data[st->start+3]));
+ if( st->end>st->start+4 )
+ flag=conv_num(st,&(st->stack->stack_data[st->start+4]) );
+ sd=script_rid2sd(st);
+ for(i=0;i<level;i++)
+ guild_skillup(sd,id,flag);
+
+ return 0;
+}
+/*==========================================
+ * スキルレベル所得
+ *------------------------------------------
+ */
+int buildin_getskilllv(struct script_state *st)
+{
+ int id=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ push_val(st->stack,C_INT, pc_checkskill( script_rid2sd(st) ,id) );
+ return 0;
+}
+/*==========================================
+ * getgdskilllv(Guild_ID, Skill_ID);
+ * skill_id = 10000 : GD_APPROVAL
+ * 10001 : GD_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;
+}
+// change npc walkspeed [Valaris]
+int buildin_npcspeed(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0;
+
+ x=conv_num(st,& (st->stack->stack_data[st->start+2]));
+
+ if(nd) {
+ nd->speed=x;
+ }
+
+ return 0;
+}
+// make an npc walk to a position [Valaris]
+int buildin_npcwalkto(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0,y=0;
+
+ x=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ y=conv_num(st,& (st->stack->stack_data[st->start+3]));
+
+ if(nd) {
+ npc_walktoxy(nd,x,y,0);
+ }
+
+ return 0;
+}
+// stop an npc's movement [Valaris]
+int buildin_npcstop(struct script_state *st)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd) {
+ if(nd->state.state==MS_WALK)
+ npc_stop_walking(nd,1);
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * getlook char info. getlook(arg)
+ *------------------------------------------
+ */
+int buildin_getlook(struct script_state *st){
+ int type,val;
+ struct map_session_data *sd;
+ sd=script_rid2sd(st);
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ val=-1;
+ switch(type){
+ case LOOK_HAIR: //1
+ val=sd->status.hair;
+ break;
+ case LOOK_WEAPON: //2
+ val=sd->status.weapon;
+ break;
+ case LOOK_HEAD_BOTTOM: //3
+ val=sd->status.head_bottom;
+ break;
+ case LOOK_HEAD_TOP: //4
+ val=sd->status.head_top;
+ break;
+ case LOOK_HEAD_MID: //5
+ val=sd->status.head_mid;
+ break;
+ case LOOK_HAIR_COLOR: //6
+ val=sd->status.hair_color;
+ break;
+ case LOOK_CLOTHES_COLOR: //7
+ val=sd->status.clothes_color;
+ break;
+ case LOOK_SHIELD: //8
+ val=sd->status.shield;
+ break;
+ case LOOK_SHOES: //9
+ break;
+ }
+
+ push_val(st->stack,C_INT,val);
+ return 0;
+}
+
+/*==========================================
+ * get char save point. argument: 0- map name, 1- x, 2- y
+ *------------------------------------------
+*/
+int buildin_getsavepoint(struct script_state *st)
+{
+ int x,y,type;
+ char *mapname;
+ struct map_session_data *sd;
+
+ sd=script_rid2sd(st);
+
+ type=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ 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:
+ {
+ long long res = i1 * i2;
+ if (res > 2147483647 )
+ i1 = 2147483647;
+ else
+ 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 000000000..c0c4ba20f
--- /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 000000000..de4ce3624
--- /dev/null
+++ b/src/map/skill.c
@@ -0,0 +1,10682 @@
+// $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"
+#include "log.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?
+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(bl->type == BL_PC && 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);
+ }
+ 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(skillnotok(skill_num, 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;
+
+ 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;
+
+ if(skill_num==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(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:
+ 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_CHASEWALK:
+ if(sd){
+ if( sd->status.sp > 19+sc_data[SC_CHASEWALK].val1*3){
+ sd->status.sp-=(19+(sc_data[SC_CHASEWALK].val1*3)); // update sp cost [Celest]
+ 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: /* クローキング */
+ calc_flag = 1; // [Celest]
+ 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);
+ sd=(struct map_session_data *)bl; //missing sd [Found by Celest, commited by Aria]
+
+ 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;
+}
+
+int skill_type_cloaking(struct block_list *bl)
+{
+ 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(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;
+ }
+ 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 */
+ }
+
+ #ifndef TXT_ONLY
+ if(log_config.produce > 0)
+ log_produce(sd,nameid,slot1,slot2,slot3,1);
+ #endif //USE_SQL
+
+ 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 {
+ #ifndef TXT_ONLY
+ if(log_config.produce > 0)
+ log_produce(sd,nameid,slot1,slot2,slot3,0);
+ #endif //USE_SQL
+
+ 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 000000000..468e5d3af
--- /dev/null
+++ b/src/map/skill.h
@@ -0,0 +1,854 @@
+// $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_type_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 skillnotok(int skillid, struct map_session_data *sd);
+
+// アイテム作成
+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=10001,
+ GD_GUARDIANRESEARCH=10002,
+ GD_CHARISMA=10003,
+ GD_GUARDUP=10003,
+ GD_EXTENSION=10004,
+ GD_GLORYGUILD=10005,
+ GD_LEADERSHIP=10006,
+ GD_GLORYWOUNDS=10007,
+ GD_SOULCOLD=10008,
+ GD_HAWKEYES=10009,
+ GD_BATTLEORDER=10010,
+ GD_REGENERATION=10011,
+ GD_RESTORE=10012,
+ GD_EMERGENCYCALL=10013,
+ GD_DEVELOPMENT=10014,
+};
+
+#endif
+
diff --git a/src/map/storage.c b/src/map/storage.c
new file mode 100644
index 000000000..b10b73bb0
--- /dev/null
+++ b/src/map/storage.c
@@ -0,0 +1,595 @@
+// $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_storageopen2(struct map_session_data *sd, struct map_session_data *pl_sd)
+{
+ struct storage *stor;
+ if(sd == NULL || pl_sd == NULL)
+ {
+ printf("storage_storageopen nullpo\n");
+ return 0;
+ }
+
+ if((stor = numdb_search(storage_db,pl_sd->status.account_id)) != NULL)
+ {
+ clif_storageitemlist(sd,stor);
+ clif_storageequiplist(sd,stor);
+ clif_updatestorageamount(sd,stor);
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * カプラ倉庫へアイテム追加
+ *------------------------------------------
+ */
+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 000000000..4e89e657a
--- /dev/null
+++ b/src/map/storage.h
@@ -0,0 +1,39 @@
+// $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_storageopen2(struct map_session_data *sd,struct map_session_data *pl_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 000000000..41a89cd7a
--- /dev/null
+++ b/src/map/trade.c
@@ -0,0 +1,286 @@
+#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"
+#include "log.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((pc_isGM(sd) < 60) && (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,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,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);
+ 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;
+
+ //Dupe Fix by mark
+ if (sd->status.inventory[n].amount < sd->deal_item_amount[trade_i])
+ sd->deal_item_amount[trade_i] = sd->status.inventory[n].amount;
+ //End Dupe Fix
+
+ #ifndef TXT_ONLY
+ if(log_config.trade > 0)
+ log_trade(sd,target_sd,n,sd->deal_item_amount[trade_i]);
+ #endif //USE_SQL
+
+ 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;
+
+ //Dupe Fix by mark
+ if (target_sd->status.inventory[n].amount < target_sd->deal_item_amount[trade_i])
+ target_sd->deal_item_amount[trade_i] = target_sd->status.inventory[n].amount;
+ //End Dupe Fix
+
+ #ifndef TXT_ONLY
+ if(log_config.trade > 0)
+ log_trade(target_sd,sd,n,target_sd->deal_item_amount[trade_i]);
+ #endif //USE_SQL
+
+ 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) {
+ #ifndef TXT_ONLY
+ if (log_config.trade > 0 && log_config.zeny > 0)
+ log_zeny(sd, target_sd, sd->deal_zeny);
+ #endif //USE_SQL
+ 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) {
+ #ifndef TXT_ONLY
+ if (log_config.trade > 0 && log_config.zeny > 0)
+ log_zeny(target_sd, sd, target_sd->deal_zeny);
+ #endif //USE_SQL
+ 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 000000000..d796df8e6
--- /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 000000000..703b1b9b9
--- /dev/null
+++ b/src/map/vending.c
@@ -0,0 +1,170 @@
+// $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"
+#include "log.h"
+
+/*==========================================
+ * 露店閉鎖
+ *------------------------------------------
+*/
+void vending_closevending(struct map_session_data *sd)
+{
+
+ nullpo_retv(sd);
+
+ sd->vender_id=0;
+ clif_closevendingboard(&sd->bl,0);
+}
+
+/*==========================================
+ * 露店アイテムリスト要求
+ *------------------------------------------
+ */
+void vending_vendinglistreq(struct map_session_data *sd,int id)
+{
+ struct map_session_data *vsd;
+
+ nullpo_retv(sd);
+
+ if( (vsd=map_id2sd(id)) == NULL )
+ return;
+ if(vsd->vender_id==0)
+ return;
+ clif_vendinglist(sd,id,vsd->vending);
+}
+
+/*==========================================
+ * 露店アイテム購入
+ *------------------------------------------
+ */
+void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p)
+{
+ int i,j,w,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);
+
+ #ifndef TXT_ONLY
+ if(log_config.vend > 0)
+ log_vend(vsd,sd,index,amount,z);
+ #endif
+
+ 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 000000000..e5b958386
--- /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/txt-converter/char/GNUmakefile b/src/txt-converter/char/GNUmakefile
new file mode 100644
index 000000000..56723ca5a
--- /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 000000000..b88df2610
--- /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 000000000..c3aa25573
--- /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) {
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ printf ("Start reading interserver configuration: %s\n",cfgName);
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ printf("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line, 1020, fp)){
+ 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("Reading interserver configuration: Done\n");
+
+ return 0;
+}
+
+int char_config_read(const char *cfgName) {
+ int i;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ printf ("Start reading char-server configuration: %s\n",cfgName);
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ printf("File not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while(fgets(line, 1020, fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ i=sscanf(line,"%[^:]:%s", w1, w2);
+ if(i!=2)
+ continue;
+ if(strcmpi(w1,"char_txt")==0){
+ printf ("set char_txt : %s\n",w2);
+ strcpy(char_txt, w2);
+ }
+ }
+ fclose(fp);
+ printf("Reading configuration: 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_CONF_NAME);
+
+ mmo_char_init();
+ printf ("Everything's been converted!\n");
+ exit (0);
+}
+
+
diff --git a/src/txt-converter/char/char.h b/src/txt-converter/char/char.h
new file mode 100644
index 000000000..bfbe6d75c
--- /dev/null
+++ b/src/txt-converter/char/char.h
@@ -0,0 +1,39 @@
+#include "../../common/core.h"
+#include "../../common/socket.h"
+#include "../../common/timer.h"
+#include "../common/mmo.h"
+#include "../common/inter.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 000000000..7cc8cc60e
--- /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 000000000..6f55871eb
--- /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 000000000..9bfe3b41c
--- /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 000000000..dece251fd
--- /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/strlib.c b/src/txt-converter/char/strlib.c
new file mode 100644
index 000000000..114983d2e
--- /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 000000000..3ab10c41e
--- /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/inter.h b/src/txt-converter/common/inter.h
new file mode 100644
index 000000000..1bb295c34
--- /dev/null
+++ b/src/txt-converter/common/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_CONF_NAME "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/common/mmo.h b/src/txt-converter/common/mmo.h
new file mode 100644
index 000000000..733874572
--- /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 300
+#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 000000000..9f34e143a
--- /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 000000000..965a0e07d
--- /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 000000000..9dfdfa170
--- /dev/null
+++ b/src/txt-converter/login/login-converter.c
@@ -0,0 +1,258 @@
+// $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"
+#include "../common/inter.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 = You are Prohibited to log in until %s (packet 0x006a)
+ time_t ban_until_time; // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
+ time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+ char last_ip[16]; // save of last IP of connection
+ char memo[255]; // a memo field
+ int account_reg2_num;
+ struct global_reg account_reg2[ACCOUNT_REG2_NUM];
+} *auth_dat;
+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("Starting reading gm_account\n");
+
+ if( (fp=fopen("conf/GM_account.txt","r"))==NULL )
+ return 1;
+ while(fgets(line,sizeof(line),fp)){
+ if(line[0] == '/' || line[1] == '/' || line[2] == '/')
+ continue;
+
+ p=malloc(sizeof(struct gm_account));
+ if(p==NULL){
+ printf("gm_account: out of memory!\n");
+ exit(0);
+ }
+
+ if(sscanf(line,"%d %d",&p->account_id,&p->level) != 2 || p->level <= 0) {
+ printf("gm_account: broken data [conf/GM_account.txt] line %d\n",c);
+ continue;
+ }
+ else {
+ if(p->level > 99)
+ p->level = 99;
+ numdb_insert(gm_account_db,p->account_id,p);
+ c++;
+ printf("GM ID: %d Level: %d\n",p->account_id,p->level);
+ }
+ }
+ fclose(fp);
+ printf("%d ID of gm_accounts read.\n",c);
+ return 0;
+}
+
+int mmo_auth_init(void)
+{
+ MYSQL mysql_handle;
+ char tmpsql[1024];
+ MYSQL_RES* sql_res ;
+ MYSQL_ROW sql_row ;
+ FILE *fp;
+ int account_id, logincount, user_level, state, n, i;
+ char line[2048], userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048];
+ time_t ban_until_time;
+ time_t connect_until_time;
+ char t_uid[256];
+
+ mysql_init(&mysql_handle);
+ if(!mysql_real_connect(&mysql_handle, db_server_ip, db_server_id, db_server_pw,
+ db_server_logindb ,db_server_port, (char *)NULL, 0)) {
+ //pointer check
+ printf("%s\n",mysql_error(&mysql_handle));
+ exit(1);
+ }
+ else {
+ printf ("Connect: Success!\n");
+ }
+ printf ("Convert start...\n");
+
+
+ fp=fopen("save/account.txt","r");
+ auth_dat=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 ("ムonvert 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;
+
+ printf ("Start reading interserver configuration: %s\n",cfgName);
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ printf("File not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while(fgets(line, 1020, fp)){
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ i=sscanf(line,"%[^:]:%s", w1, w2);
+ if(i!=2)
+ continue;
+
+ //add for DB connection
+ if(strcmpi(w1,"db_server_ip")==0){
+ strcpy(db_server_ip, w2);
+ printf ("set db_server_ip : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_port")==0){
+ db_server_port=atoi(w2);
+ printf ("set db_server_port : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_id")==0){
+ strcpy(db_server_id, w2);
+ printf ("set db_server_id : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_pw")==0){
+ strcpy(db_server_pw, w2);
+ printf ("set db_server_pw : %s\n",w2);
+ }
+ else if(strcmpi(w1,"db_server_logindb")==0){
+ strcpy(db_server_logindb, w2);
+ printf ("set db_server_logindb : %s\n",w2);
+ }
+ }
+ fclose(fp);
+ printf ("End reading interserver configuration...\n");
+ return 0;
+}
+
+int do_init(int argc,char **argv)
+{
+ char input;
+ login_config_read( (argc>1)?argv[1]:INTER_CONF_NAME );
+ read_gm_account();
+
+ printf("\nWarning : Make sure you backup your databases before continuing!\n");
+ printf("\nDo you wish to convert your Login Database to SQL? (y/n) : ");
+ input=getchar();
+ if(input == 'y' || input == 'Y')
+ mmo_auth_init();
+ printf ("Everything's been converted!\n");
+ exit (0);
+}
+
+