diff options
author | ai4rei <ai4rei@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2010-12-26 22:36:41 +0000 |
---|---|---|
committer | ai4rei <ai4rei@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2010-12-26 22:36:41 +0000 |
commit | 6096ce0b42a3eee07dc70ae5ef489aa4c30bf515 (patch) | |
tree | e91f8afd3b7b8b80f0c55aae07f7a478f469c37a /src | |
parent | 23e1b7db5ba2277a406fc84c30e87c324a83097c (diff) | |
download | hercules-6096ce0b42a3eee07dc70ae5ef489aa4c30bf515.tar.gz hercules-6096ce0b42a3eee07dc70ae5ef489aa4c30bf515.tar.bz2 hercules-6096ce0b42a3eee07dc70ae5ef489aa4c30bf515.tar.xz hercules-6096ce0b42a3eee07dc70ae5ef489aa4c30bf515.zip |
* Merged changes from trunk [14496:14630/trunk].
git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/branches/renewal@14632 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src')
57 files changed, 2896 insertions, 2381 deletions
diff --git a/src/char/char.c b/src/char/char.c index 91ad7d33d..6dd131976 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -2,19 +2,16 @@ // For more information, see LICENCE in the main folder #include "../common/cbasetypes.h" -#include "../common/mmo.h" +#include "../common/core.h" #include "../common/db.h" #include "../common/lock.h" #include "../common/malloc.h" -#include "../common/core.h" +#include "../common/mapindex.h" +#include "../common/mmo.h" +#include "../common/showmsg.h" #include "../common/socket.h" #include "../common/strlib.h" -#include "../common/showmsg.h" #include "../common/timer.h" -#include "../common/lock.h" -#include "../common/malloc.h" -#include "../common/mapindex.h" -#include "../common/showmsg.h" #include "../common/utils.h" #include "../common/version.h" #include "inter.h" @@ -27,16 +24,8 @@ #include "char.h" #include <sys/types.h> -#ifdef WIN32 -#include <winsock2.h> -#else -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#endif #include <time.h> #include <signal.h> -#include <fcntl.h> #include <string.h> #include <stdarg.h> #include <stdio.h> @@ -51,8 +40,13 @@ char friends_txt[1024] = "save/friends.txt"; char hotkeys_txt[1024] = "save/hotkeys.txt"; char char_log_filename[1024] = "log/char.log"; -int save_log = 1; // show loading/saving messages +// show loading/saving messages +#ifndef TXT_SQL_CONVERT +int save_log = 1; +#endif +//If your code editor is having problems syntax highlighting this file, uncomment this and RECOMMENT IT BEFORE COMPILING +//#undef TXT_SQL_CONVERT #ifndef TXT_SQL_CONVERT char db_path[1024] = "db"; @@ -85,9 +79,12 @@ int email_creation = 0; // disabled by default bool name_ignoring_case = false; // 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 unknown_char_name[1024] = "Unknown"; // Name to use when the requested name cannot be determined +char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the requested name cannot be determined #define TRIM_CHARS "\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex] -char char_name_letters[1024] = ""; // list of letters/symbols authorised (or not) in a character name. by [Yor] +char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) in a character name. by [Yor] + +int char_per_account = 0; //Maximum charas per account (default unlimited) [Sirius] +int char_del_level = 0; //From which level u can delete character [Lupus] int log_char = 1; // loggin char or not [devil] int log_inter = 1; // loggin inter or not [devil] @@ -118,7 +115,7 @@ int char_num, char_max; int max_connect_user = 0; int gm_allow_level = 99; int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; -int start_zeny = 500; +int start_zeny = 0; int start_weapon = 1201; int start_armor = 2301; int guild_exp_rate = 100; @@ -173,7 +170,7 @@ struct online_char_data { int char_id; int fd; int waiting_disconnect; - short server; + short server; // -2: unknown server, -1: not connected, 0+: id of server }; static DBMap* online_char_db; // int account_id -> struct online_char_data* @@ -223,6 +220,7 @@ void set_char_online(int map_id, int char_id, int account_id) { struct online_char_data* character; + //Check to see for online conflicts character = (struct online_char_data*)idb_ensure(online_char_db, account_id, create_online_char_data); if( character->char_id != -1 && character->server > -1 && character->server != map_id ) { @@ -238,11 +236,13 @@ void set_char_online(int map_id, int char_id, int account_id) if( character->server > -1 ) server[character->server].users++; + //Get rid of disconnect timer if(character->waiting_disconnect != -1) { delete_timer(character->waiting_disconnect, chardb_waiting_disconnect); character->waiting_disconnect = -1; } + //Notify login server if (login_fd > 0 && !session[login_fd]->flag.eof) { WFIFOHEAD(login_fd,6); @@ -261,7 +261,7 @@ void set_char_offline(int char_id, int account_id) if( character->server > -1 ) if( server[character->server].users > 0 ) // Prevent this value from going negative. server[character->server].users--; - + if(character->waiting_disconnect != -1){ delete_timer(character->waiting_disconnect, chardb_waiting_disconnect); character->waiting_disconnect = -1; @@ -678,7 +678,7 @@ int mmo_char_fromstr(char *str, struct mmo_charstatus *p, struct global_reg *reg tmp_int[46] = mapindex_name2id(tmp_str[2]); } // Char structure of version 1500 (homun + mapindex maps) - memcpy(p->name, tmp_str[0], NAME_LENGTH); //Overflow protection [Skotlex] + safestrncpy(p->name, tmp_str[0], NAME_LENGTH); //Overflow protection [Skotlex] p->char_id = tmp_int[0]; p->account_id = tmp_int[1]; p->slot = tmp_int[2]; @@ -1150,16 +1150,9 @@ int mmo_char_sync_timer(int tid, unsigned int tick, int id, intptr data) return 0; } -//----------------------------------- -// Function to create a new character -//----------------------------------- -int make_new_char(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style) +int check_char_name(char * name) { - char name[NAME_LENGTH]; int i; - - safestrncpy(name, name_, NAME_LENGTH); - normalize_name(name,TRIM_CHARS); // check length of character name if( name[0] == '\0' ) @@ -1174,33 +1167,64 @@ int make_new_char(struct char_session_data* sd, char* name_, int str, int agi, i return -1; // nick reserved for internal server messages // Check Authorised letters/symbols in the name of the character - if( char_name_option == 1 ) { // only letters/symbols in char_name_letters are authorised + if( char_name_option == 1 ) + { // only letters/symbols in char_name_letters are authorised for( i = 0; i < NAME_LENGTH && name[i]; i++ ) if( strchr(char_name_letters, name[i]) == NULL ) return -2; - } else - if( char_name_option == 2 ) { // letters/symbols in char_name_letters are forbidden + } + else if( char_name_option == 2 ) + { // letters/symbols in char_name_letters are forbidden for( i = 0; i < NAME_LENGTH && name[i]; i++ ) if( strchr(char_name_letters, name[i]) != NULL ) return -2; - } // else, all letters/symbols are authorised (except control char removed before) + } // check name (already in use?) - ARR_FIND( 0, char_num, i, - (name_ignoring_case && strncmp(char_dat[i].status.name, name, NAME_LENGTH) == 0) || - (!name_ignoring_case && strncmpi(char_dat[i].status.name, name, NAME_LENGTH) == 0) ); + if( name_ignoring_case ) + { + ARR_FIND( 0, char_num, i, strncmp(char_dat[i].status.name, name, NAME_LENGTH) == 0 ); + } + else + { + ARR_FIND( 0, char_num, i, strncmpi(char_dat[i].status.name, name, NAME_LENGTH) == 0 ); + } if( i < char_num ) return -1; // name already exists + return 0; +} + +//----------------------------------- +// Function to create a new character +//----------------------------------- +int make_new_char(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style) +{ + char name[NAME_LENGTH]; + int i, flag; + + safestrncpy(name, name_, NAME_LENGTH); + normalize_name(name,TRIM_CHARS); + + flag = check_char_name(name); + if( flag < 0 ) + return flag; + //check other inputs if((slot >= MAX_CHARS) // slots - || (hair_style >= 24) // hair style - || (hair_color >= 9) // hair color || (str + agi + vit + int_ + dex + luk != 6*5 ) // stats || (str < 1 || str > 9 || agi < 1 || agi > 9 || vit < 1 || vit > 9 || int_ < 1 || int_ > 9 || dex < 1 || dex > 9 || luk < 1 || luk > 9) // individual stat values || (str + int_ != 10 || agi + luk != 10 || vit + dex != 10) ) // pairs return -2; // invalid input + // check the number of already existing chars in this account + if( char_per_account != 0 ) { + ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == -1 ); + + if( i >= char_per_account ) + return -2; // character account limit exceeded + } + // check char slot ARR_FIND( 0, char_num, i, char_dat[i].status.account_id == sd->account_id && char_dat[i].status.slot == slot ); if( i < char_num ) @@ -1563,7 +1587,7 @@ void create_online_files(void) j = id[i]; // displaying the character name if ((online_display_option & 1) || (online_display_option & 64)) { // without/with 'GM' display - strcpy(temp, char_dat[j].status.name); + safestrncpy(temp, char_dat[j].status.name, sizeof(temp)); //l = isGM(char_dat[j].status.account_id); l = 0; //FIXME: how to get the gm level? if (online_display_option & 64) { @@ -1611,7 +1635,7 @@ void create_online_files(void) // displaying of the map if (online_display_option & 24) { // 8 or 16 // prepare map name - memcpy(temp, mapindex_id2name(char_dat[j].status.last_point.map), MAP_NAME_LENGTH); + safestrncpy(temp, mapindex_id2name(char_dat[j].status.last_point.map), sizeof(temp)); // write map name if (online_display_option & 16) { // map-name AND coordinates fprintf(fp2, " <td>%s (%d, %d)</td>\n", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y); @@ -1670,10 +1694,11 @@ int count_users(void) int i, users; users = 0; - for(i = 0; i < MAX_MAP_SERVERS; i++) - if (server[i].fd >= 0) + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (server[i].fd > 0) { users += server[i].users; - + } + } return users; } @@ -1738,7 +1763,7 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) #endif #if (PACKETVER >= 20100720 && PACKETVER <= 20100727) || PACKETVER >= 20100803 mapindex_getmapname_ext(mapindex_id2name(p->last_point.map), (char*)WBUFP(buf,108)); - offset += 16; + offset += MAP_NAME_LENGTH_EXT; #endif return 106+offset; } @@ -1807,7 +1832,7 @@ int char_divorce(struct mmo_charstatus *cs) return 0; } -int char_married(int pl1,int pl2) +int char_married(int pl1, int pl2) { return (char_dat[pl1].status.char_id == char_dat[pl2].status.partner_id && char_dat[pl2].status.char_id == char_dat[pl1].status.partner_id); } @@ -1850,38 +1875,18 @@ int char_family(int cid1, int cid2, int cid3) return 0; } -//Clears the given party id from all characters. -//Since sometimes the party format changes and parties must be wiped, this -//method is required to prevent stress during the "party not found!" stages. -void char_clearparty(int party_id) -{ - int i; - for(i = 0; i < char_num; i++) - { - if (char_dat[i].status.party_id == party_id) - char_dat[i].status.party_id = 0; - } -} - //---------------------------------------------------------------------- // Force disconnection of an online player (with account value) by [Yor] //---------------------------------------------------------------------- -int disconnect_player(int account_id) +void disconnect_player(int account_id) { int i; - struct char_session_data *sd; + struct char_session_data* sd; // disconnect player if online on char-server - for(i = 0; i < fd_max; i++) { - if (session[i] && (sd = (struct char_session_data*)session[i]->session_data)) { - if (sd->account_id == account_id) { - set_eof(i); - return 1; - } - } - } - - return 0; + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == account_id ); + if( i < fd_max ) + set_eof(i); } // キャラ削除に伴うデータ削除 @@ -2005,8 +2010,8 @@ int parse_fromlogin(int fd) //printf("connect login server error : %d\n", RFIFOB(fd,2)); ShowError("Can not connect to login-server.\n"); ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); - ShowInfo("Also, please make sure your accounts file (default: accounts.txt) has those values present.\n"); - ShowInfo("The communication passwords can be changed in map_athena.conf and char_athena.conf\n"); + ShowError("Also, please make sure your accounts file (default: accounts.txt) has the correct communication username/passwords and the gender of the account is S.\n"); + ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n"); } else { ShowStatus("Connected to login-server (connection #%d).\n", fd); @@ -2099,7 +2104,7 @@ int parse_fromlogin(int fd) if (RFIFOREST(fd) < 7) return 0; { - int i, j; + int j; unsigned char buf[7]; int acc = RFIFOL(fd,2); @@ -2107,7 +2112,7 @@ int parse_fromlogin(int fd) RFIFOSKIP(fd,7); if( acc > 0 ) - { + {// TODO: Is this even possible? struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc); if( node != NULL ) node->sex = sex; @@ -2162,6 +2167,8 @@ int parse_fromlogin(int fd) // disconnect player if online on char-server disconnect_player(acc); } + + // notify all mapservers about this change WBUFW(buf,0) = 0x2b0d; WBUFL(buf,2) = acc; WBUFB(buf,6) = sex; @@ -2235,14 +2242,14 @@ int parse_fromlogin(int fd) RFIFOSKIP(fd,8 + RFIFOL(fd,4)); break; - // account_reg2変更通知 + // reply to an account_reg2 registry request case 0x2729: if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; + { //Receive account_reg2 registry, forward to map servers. unsigned char buf[13+ACCOUNT_REG2_NUM*sizeof(struct global_reg)]; memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); -// WBUFW(buf,0) = 0x2b11; WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] mapif_sendall(buf, WBUFW(buf,2)); RFIFOSKIP(fd, RFIFOW(fd,2)); @@ -2297,12 +2304,12 @@ int parse_fromlogin(int fd) RFIFOSKIP(fd,6); break; - // State change of account/ban notification (from login-server) by [Yor] + // State change of account/ban notification (from login-server) case 0x2731: if (RFIFOREST(fd) < 11) return 0; - // send to all map-servers to disconnect the player - { + + { // send to all map-servers to disconnect the player unsigned char buf[11]; WBUFW(buf,0) = 0x2b14; WBUFL(buf,2) = RFIFOL(fd,2); @@ -2312,6 +2319,7 @@ int parse_fromlogin(int fd) } // disconnect player if online on char-server disconnect_player(RFIFOL(fd,2)); + RFIFOSKIP(fd,11); break; @@ -2351,7 +2359,8 @@ int parse_fromlogin(int fd) idb_remove(auth_db, aid);// reject auth attempts from map-server } break; - + + // ip address update signal from login server case 0x2735: { unsigned char buf[2]; @@ -2362,13 +2371,14 @@ int parse_fromlogin(int fd) new_ip = host2ip(login_ip_str); if (new_ip && new_ip != login_ip) - login_ip = new_ip; //Update login up. + login_ip = new_ip; //Update login ip, too. new_ip = host2ip(char_ip_str); if (new_ip && new_ip != char_ip) { //Update ip. char_ip = new_ip; ShowInfo("Updating IP for [%s].\n", char_ip_str); + // notify login server about the change WFIFOHEAD(fd,6); WFIFOW(fd,0) = 0x2736; WFIFOL(fd,2) = htonl(char_ip); @@ -2380,7 +2390,7 @@ int parse_fromlogin(int fd) break; default: - ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", RFIFOW(fd,0)); + ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", command); set_eof(fd); return 0; } @@ -2498,7 +2508,7 @@ void char_read_fame_list(void) { fame_item.id = char_dat[id[i]].status.char_id; fame_item.fame = char_dat[id[i]].status.fame; - strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); + safestrncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); memcpy(&smith_fame_list[j],&fame_item,sizeof(struct fame_list)); j++; @@ -2513,7 +2523,7 @@ void char_read_fame_list(void) { fame_item.id = char_dat[id[i]].status.char_id; fame_item.fame = char_dat[id[i]].status.fame; - strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); + safestrncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); memcpy(&chemist_fame_list[j],&fame_item,sizeof(struct fame_list)); @@ -2527,7 +2537,7 @@ void char_read_fame_list(void) { fame_item.id = char_dat[id[i]].status.char_id; fame_item.fame = char_dat[id[i]].status.fame; - strncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); + safestrncpy(fame_item.name, char_dat[id[i]].status.name, NAME_LENGTH); memcpy(&taekwon_fame_list[j],&fame_item,sizeof(struct fame_list)); @@ -2536,6 +2546,7 @@ void char_read_fame_list(void) } DELETE_BUFFER(id); } + // Send map-servers the fame ranking lists int char_send_fame_list(int fd) { @@ -2565,7 +2576,7 @@ int char_send_fame_list(int fd) // add total packet length WBUFW(buf, 2) = len; - if(fd!=-1) + if (fd != -1) mapif_send(fd, buf, len); else mapif_sendall(buf, len); @@ -2575,7 +2586,7 @@ int char_send_fame_list(int fd) void char_update_fame_list(int type, int index, int fame) { - unsigned char buf[9]; + unsigned char buf[8]; WBUFW(buf,0) = 0x2b22; WBUFB(buf,2) = type; WBUFB(buf,3) = index; @@ -2588,14 +2599,18 @@ void char_update_fame_list(int type, int index, int fame) int char_loadName(int char_id, char* name) { int j; - for( j = 0; j < char_num && char_dat[j].status.char_id != char_id; ++j ) - ;// find char + + ARR_FIND( 0, char_num, j, char_dat[j].status.char_id == char_id ); if( j < char_num ) - strncpy(name, char_dat[j].status.name, NAME_LENGTH); + { + safestrncpy(name, char_dat[j].status.name, NAME_LENGTH); + return 1; + } else - strncpy(name, unknown_char_name, NAME_LENGTH); - - return (j < char_num) ? 1 : 0; + { + safestrncpy(name, unknown_char_name, NAME_LENGTH); + } + return 0; } int search_mapserver(unsigned short map, uint32 ip, uint16 port); @@ -2624,6 +2639,7 @@ int parse_frommap(int fd) WBUFW(buf,2) = j * 4 + 10; mapif_sendallwos(fd, buf, WBUFW(buf,2)); } + memset(&server[id], 0, sizeof(struct mmo_map_server)); server[id].fd = -1; online_char_db->foreach(online_char_db,char_db_setoffline,id); //Tag relevant chars as 'in disconnected' server. } @@ -2634,8 +2650,6 @@ int parse_frommap(int fd) while(RFIFOREST(fd) >= 2) { - //ShowDebug("Received packet 0x%4x (%d bytes) from map-server (connection %d)\n", RFIFOW(fd, 0), RFIFOREST(fd), fd); - switch(RFIFOW(fd,0)) { @@ -2727,7 +2741,7 @@ int parse_frommap(int fd) RFIFOSKIP(fd, 10); } break; - + case 0x2afe: //set MAP user count if (RFIFOREST(fd) < 4) return 0; @@ -2741,12 +2755,14 @@ int parse_frommap(int fd) case 0x2aff: //set MAP users if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; + { //TODO: When data mismatches memory, update guild/party online/offline states. + int aid, cid; + struct online_char_data* character; + server[id].users = RFIFOW(fd,4); online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown' for(i = 0; i < server[id].users; i++) { - int aid, cid; - struct online_char_data* character; aid = RFIFOL(fd,6+i*8); cid = RFIFOL(fd,6+i*8+4); character = (struct online_char_data*)idb_ensure(online_char_db, aid, create_online_char_data); @@ -2756,33 +2772,44 @@ int parse_frommap(int fd) character->account_id, character->char_id, character->server, id, aid, cid); mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 2); } - character->char_id = cid; character->server = id; + character->char_id = cid; } //If any chars remain in -2, they will be cleaned in the cleanup timer. - RFIFOSKIP(fd,6+i*8); + RFIFOSKIP(fd,RFIFOW(fd,2)); + } break; case 0x2b01: // Receive character data from map-server for saving if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; + { + int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2); + struct mmo_charstatus* cs; - ARR_FIND( 0, char_num, i, char_dat[i].status.account_id == RFIFOL(fd,4) && char_dat[i].status.char_id == RFIFOL(fd,8) ); - if( i < char_num ) + if (size - 13 != sizeof(struct mmo_charstatus)) + { + ShowError("parse_from_map (save-char): Size mismatch! %d != %d\n", size-13, sizeof(struct mmo_charstatus)); + RFIFOSKIP(fd,size); + break; + } + if( ( cs = search_character(aid, cid) ) != NULL ) { - memcpy(&char_dat[i].status, RFIFOP(fd,13), sizeof(struct mmo_charstatus)); - storage_save(char_dat[i].status.account_id, &char_dat[i].status.storage); + memcpy(cs, RFIFOP(fd,13), sizeof(struct mmo_charstatus)); + storage_save(cs->account_id, &cs->storage); } if (RFIFOB(fd,12)) - { //Flag, set character offline. [Skotlex] - set_char_offline(RFIFOL(fd,8),RFIFOL(fd,4)); + { //Flag, set character offline after saving. [Skotlex] + set_char_offline(cid, aid); + WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x2b21; //Save ack only needed on final save. - WFIFOL(fd,2) = RFIFOL(fd,4); - WFIFOL(fd,6) = RFIFOL(fd,8); + WFIFOL(fd,2) = aid; + WFIFOL(fd,6) = cid; WFIFOSET(fd,10); } - RFIFOSKIP(fd,RFIFOW(fd,2)); + RFIFOSKIP(fd,size); + } break; case 0x2b02: // req char selection @@ -2805,7 +2832,8 @@ int parse_frommap(int fd) node->login_id2 = login_id2; //node->sex = 0; node->ip = ntohl(ip); - node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server) + //node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server) + //node->gmlevel = 0; idb_put(auth_db, account_id, node); //Set char to "@ char select" in online db [Kevin] @@ -2823,22 +2851,15 @@ int parse_frommap(int fd) if (RFIFOREST(fd) < 35) return 0; { - unsigned short name; int map_id, map_fd = -1; struct online_char_data* data; struct mmo_charstatus* char_data; - name = RFIFOW(fd,18); - map_id = search_mapserver(name, ntohl(RFIFOL(fd,24)), ntohs(RFIFOW(fd,28))); //Locate mapserver by ip and port. + map_id = search_mapserver(RFIFOW(fd,18), ntohl(RFIFOL(fd,24)), ntohs(RFIFOW(fd,28))); //Locate mapserver by ip and port. if (map_id >= 0) map_fd = server[map_id].fd; - for(i = 0; i < char_num; i++) { - if (char_dat[i].status.account_id == RFIFOL(fd,2) && - char_dat[i].status.char_id == RFIFOL(fd,14)) - break; - } - char_data = i < char_num ? &char_dat[i].status : NULL; + char_data = search_character(RFIFOL(fd,2), RFIFOL(fd,14)); if (map_fd >= 0 && session[map_fd] && char_data) { //Send the map server the auth of this player. @@ -2940,6 +2961,7 @@ int parse_frommap(int fd) if( login_fd <= 0 ) result = 3; // 3-login-server offline + //FIXME: need to move this check to login server [ultramage] // else // if( acc != -1 && isGM(acc) < isGM(account_id) ) // result = 2; // 2-gm level too low @@ -3053,10 +3075,10 @@ int parse_frommap(int fd) break; case 0x2b16: // Receive rates [Wizputer] - if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8)) + if( RFIFOREST(fd) < 14 ) return 0; // Txt doesn't need this packet, so just skip it - RFIFOSKIP(fd,RFIFOW(fd,8)); + RFIFOSKIP(fd,14); break; case 0x2b17: // Character disconnected set online 0 [Wizputer] @@ -3238,7 +3260,7 @@ int lan_subnetcheck(uint32 ip) { int i; ARR_FIND( 0, subnet_count, i, (subnet[i].char_ip & subnet[i].mask) == (ip & subnet[i].mask) ); - if ( i < subnet_count ) { + if( i < subnet_count ) { ShowInfo("Subnet check [%u.%u.%u.%u]: Matches "CL_CYAN"%u.%u.%u.%u/%u.%u.%u.%u"CL_RESET"\n", CONVIP(ip), CONVIP(subnet[i].char_ip & subnet[i].mask), CONVIP(subnet[i].mask)); return subnet[i].char_ip; } else { @@ -3255,7 +3277,7 @@ int parse_char(int fd) int map_fd; struct char_session_data* sd; uint32 ipl = session[fd]->client_addr; - + sd = (struct char_session_data*)session[fd]->session_data; // disconnect any player if no login-server. @@ -3265,7 +3287,7 @@ int parse_char(int fd) if(session[fd]->flag.eof) { if( sd != NULL && sd->auth ) - { + { // already authed client struct online_char_data* data = (struct online_char_data*)idb_get(online_char_db, sd->account_id); if( data != NULL && data->fd == fd) data->fd = -1; @@ -3377,15 +3399,21 @@ int parse_char(int fd) ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] >= 0 && char_dat[sd->found_char[ch]].status.slot == slot ); if (ch == MAX_CHARS) { //Not found?? May be forged packet. + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0; // rejected from server + WFIFOSET(fd,3); break; } cd = &char_dat[sd->found_char[ch]].status; char_log("Character Selected, Account ID: %d, Character Slot: %d, Character Name: %s.\n", sd->account_id, slot, cd->name); cd->sex = sd->sex; - + + ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, cd->name); + // searching map server - i = search_mapserver(cd->last_point.map,-1,-1); + i = search_mapserver(cd->last_point.map, -1, -1); // if map is not found, we check major cities if (i < 0) { @@ -3430,6 +3458,7 @@ int parse_char(int fd) cd->last_point.map = j; } + //Send NEW auth packet [Kevin] //FIXME: is this case even possible? [ultramage] if ((map_fd = server[i].fd) < 1 || session[map_fd] == NULL) { @@ -3454,8 +3483,6 @@ int parse_char(int fd) WFIFOW(fd,26) = ntows(htons(server[i].port)); // [!] LE byte order here [!] WFIFOSET(fd,28); - ShowInfo("Character selection '%s' (account: %d, slot: %d).\n", cd->name, sd->account_id, ch); - // create temporary auth entry CREATE(node, struct auth_node, 1); node->account_id = sd->account_id; @@ -3495,8 +3522,9 @@ int parse_char(int fd) else { int len; + // send to player - WFIFOHEAD(fd,MAX_CHAR_BUF+2); + WFIFOHEAD(fd,2+MAX_CHAR_BUF); WFIFOW(fd,0) = 0x6d; len = 2 + mmo_char_tobuf(WFIFOP(fd,2), &char_dat[i].status); WFIFOSET(fd,len); @@ -3504,7 +3532,7 @@ int parse_char(int fd) // add new entry to the chars list ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] == -1 ); if( ch < MAX_CHARS ) - sd->found_char[ch] = i; // position of the new char in the char_dat[] array + sd->found_char[ch] = i; // position of the new char in the char_dat[] array } RFIFOSKIP(fd,37); @@ -3519,12 +3547,13 @@ int parse_char(int fd) { int cid = RFIFOL(fd,2); struct mmo_charstatus* cs = NULL; + ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid); memcpy(email, RFIFOP(fd,6), 40); RFIFOSKIP(fd,RFIFOREST(fd)); // hack to make the other deletion packet work if (e_mail_check(email) == 0) - strncpy(email, "a@a.com", 40); // default e-mail + safestrncpy(email, "a@a.com", sizeof(email)); // default e-mail // BEGIN HACK: "change email using the char deletion 'confirm email' menu" // if we activated email creation and email is default email @@ -3587,6 +3616,17 @@ int parse_char(int fd) // deletion process cs = &char_dat[sd->found_char[i]].status; + + //check for config char del condition [Lupus] + if( ( char_del_level > 0 && cs->base_level >= (unsigned int)char_del_level ) || ( char_del_level < 0 && cs->base_level <= (unsigned int)(-char_del_level) ) ) + { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x70; + WFIFOB(fd,2) = 1; // This character cannot be deleted. + WFIFOSET(fd,3); + break; + } + char_delete(cs); if (sd->found_char[i] != char_num - 1) { int j, k; @@ -3632,9 +3672,25 @@ int parse_char(int fd) // R 028d <account ID>.l <char ID>.l <new name>.24B case 0x28d: FIFOSD_CHECK(34); - //not implemented - RFIFOSKIP(fd,34); - break; + { + //not implemented + RFIFOSKIP(fd,34); + } + break; + //Confirm change name. + // 0x28f <char_id>.L + case 0x28f: + // 0: Sucessfull + // 1: This character's name has already been changed. You cannot change a character's name more than once. + // 2: User information is not correct. + // 3: You have failed to change this character's name. + // 4: Another user is using this character name, so please select another one. + FIFOSD_CHECK(6); + { + //not implemented + RFIFOSKIP(fd,6); + } + break; // captcha code request (not implemented) // R 07e5 <?>.w <aid>.l @@ -3752,7 +3808,6 @@ int parse_console(char* buf) return 0; } -// 全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) int mapif_sendall(unsigned char *buf, unsigned int len) { int i, c; @@ -3771,7 +3826,6 @@ int mapif_sendall(unsigned char *buf, unsigned int len) return c; } -// 自分以外の全てのMAPサーバーにデータ送信(送信したmap鯖の数を返す) int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { int i, c; @@ -3789,19 +3843,19 @@ int mapif_sendallwos(int sfd, unsigned char *buf, unsigned int len) 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[i].fd) { - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - return 1; - } + ARR_FIND( 0, MAX_MAP_SERVERS, i, fd == server[i].fd ); + if( i < MAX_MAP_SERVERS ) + { + WFIFOHEAD(fd,len); + memcpy(WFIFOP(fd,0), buf, len); + WFIFOSET(fd,len); + return 1; } } return 0; @@ -4028,7 +4082,7 @@ int char_config_read(const char *cfgName) remove_control_chars(w1); remove_control_chars(w2); if(strcmpi(w1,"timestamp_format") == 0) { - strncpy(timestamp_format, w2, 20); + safestrncpy(timestamp_format, w2, sizeof(timestamp_format)); } else if(strcmpi(w1,"console_silent")==0){ ShowInfo("Console Silent Setting: %d\n", atoi(w2)); msg_silent = atoi(w2); @@ -4036,23 +4090,21 @@ int char_config_read(const char *cfgName) } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ stdout_with_ansisequence = config_switch(w2); } else if (strcmpi(w1, "userid") == 0) { - strncpy(userid, w2, 24); + safestrncpy(userid, w2, sizeof(userid)); } else if (strcmpi(w1, "passwd") == 0) { - strncpy(passwd, w2, 24); + safestrncpy(passwd, w2, sizeof(passwd)); } else if (strcmpi(w1, "server_name") == 0) { - strncpy(server_name, w2, 20); - server_name[sizeof(server_name) - 1] = '\0'; + safestrncpy(server_name, w2, sizeof(server_name)); ShowStatus("%s server has been initialized\n", w2); } else if (strcmpi(w1, "wisp_server_name") == 0) { if (strlen(w2) >= 4) { - memcpy(wisp_server_name, w2, sizeof(wisp_server_name)); - wisp_server_name[sizeof(wisp_server_name) - 1] = '\0'; + safestrncpy(wisp_server_name, w2, sizeof(wisp_server_name)); } } else if (strcmpi(w1, "login_ip") == 0) { char ip_str[16]; login_ip = host2ip(w2); if (login_ip) { - strncpy(login_ip_str, w2, sizeof(login_ip_str)); + safestrncpy(login_ip_str, w2, sizeof(login_ip_str)); ShowStatus("Login server IP address : %s -> %s\n", w2, ip2str(login_ip, ip_str)); } } else if (strcmpi(w1, "login_port") == 0) { @@ -4061,14 +4113,14 @@ int char_config_read(const char *cfgName) char ip_str[16]; char_ip = host2ip(w2); if (char_ip){ - strncpy(char_ip_str, w2, sizeof(char_ip_str)); + safestrncpy(char_ip_str, w2, sizeof(char_ip_str)); ShowStatus("Character server IP address : %s -> %s\n", w2, ip2str(char_ip, ip_str)); } } else if (strcmpi(w1, "bind_ip") == 0) { char ip_str[16]; bind_ip = host2ip(w2); if (bind_ip) { - strncpy(bind_ip_str, w2, sizeof(bind_ip_str)); + safestrncpy(bind_ip_str, w2, sizeof(bind_ip_str)); ShowStatus("Character server binding IP address : %s -> %s\n", w2, ip2str(bind_ip, ip_str)); } } else if (strcmpi(w1, "char_port") == 0) { @@ -4082,14 +4134,14 @@ int char_config_read(const char *cfgName) } else if (strcmpi(w1, "email_creation") == 0) { email_creation = config_switch(w2); } else if (strcmpi(w1, "scdata_txt") == 0) { //By Skotlex - strcpy(scdata_txt, w2); + safestrncpy(scdata_txt, w2, sizeof(scdata_txt)); #endif } else if (strcmpi(w1, "char_txt") == 0) { - strcpy(char_txt, w2); + safestrncpy(char_txt, w2, sizeof(char_txt)); } else if (strcmpi(w1, "friends_txt") == 0) { //By davidsiaw - strcpy(friends_txt, w2); + safestrncpy(friends_txt, w2, sizeof(friends_txt)); } else if (strcmpi(w1, "hotkeys_txt") == 0) { //By davidsiaw - strcpy(hotkeys_txt, w2); + safestrncpy(hotkeys_txt, w2, sizeof(hotkeys_txt)); #ifndef TXT_SQL_CONVERT } else if (strcmpi(w1, "max_connect_user") == 0) { max_connect_user = atoi(w2); @@ -4111,10 +4163,8 @@ int char_config_read(const char *cfgName) if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3) continue; start_point.map = mapindex_name2id(map); - if (!start_point.map) { + if (!start_point.map) ShowError("Specified start_point %s not found in map-index cache.\n", map); - start_point.map = 0; - } start_point.x = x; start_point.y = y; } else if (strcmpi(w1, "start_zeny") == 0) { @@ -4132,21 +4182,25 @@ int char_config_read(const char *cfgName) } 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); + safestrncpy(unknown_char_name, w2, sizeof(unknown_char_name)); unknown_char_name[NAME_LENGTH-1] = '\0'; } else if (strcmpi(w1, "char_log_filename") == 0) { - strcpy(char_log_filename, w2); + safestrncpy(char_log_filename, w2, sizeof(char_log_filename)); } else if (strcmpi(w1, "name_ignoring_case") == 0) { name_ignoring_case = (bool)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); + safestrncpy(char_name_letters, w2, sizeof(char_name_letters)); + } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius] + char_per_account = atoi(w2); + } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus] + char_del_level = atoi(w2); // online files options } else if (strcmpi(w1, "online_txt_filename") == 0) { - strcpy(online_txt_filename, w2); + safestrncpy(online_txt_filename, w2, sizeof(online_txt_filename)); } else if (strcmpi(w1, "online_html_filename") == 0) { - strcpy(online_html_filename, w2); + safestrncpy(online_html_filename, w2, sizeof(online_html_filename)); } else if (strcmpi(w1, "online_sorting_option") == 0) { online_sorting_option = atoi(w2); } else if (strcmpi(w1, "online_display_option") == 0) { @@ -4160,7 +4214,7 @@ int char_config_read(const char *cfgName) if (online_refresh_html < 1) online_refresh_html = 1; } else if(strcmpi(w1,"db_path")==0) { - strcpy(db_path,w2); + safestrncpy(db_path, w2, sizeof(db_path)); } else if (strcmpi(w1, "console") == 0) { console = config_switch(w2); } else if (strcmpi(w1, "fame_list_alchemist") == 0) { @@ -4195,11 +4249,6 @@ int char_config_read(const char *cfgName) } #ifndef TXT_SQL_CONVERT -int chardb_final(int key, void* data, va_list va) -{ - aFree(data); - return 0; -} void do_final(void) { ShowStatus("Terminating server.\n"); @@ -4209,9 +4258,10 @@ void do_final(void) set_all_offline(-1); flush_fifos(); // write online players files with no player - online_char_db->clear(online_char_db, NULL); //clean the db... + online_char_db->clear(online_char_db, NULL); create_online_files(); - online_char_db->destroy(online_char_db, NULL); //dispose the db... + + online_char_db->destroy(online_char_db, NULL); auth_db->destroy(auth_db, NULL); if(char_dat) aFree(char_dat); @@ -4294,11 +4344,11 @@ int do_init(int argc, char **argv) else ShowStatus("Defaulting to %s as our IP address\n", ip_str); if (!login_ip) { - strcpy(login_ip_str, ip_str); + safestrncpy(login_ip_str, ip_str, sizeof(login_ip_str)); login_ip = str2ip(login_ip_str); } if (!char_ip) { - strcpy(char_ip_str, ip_str); + safestrncpy(char_ip_str, ip_str, sizeof(char_ip_str)); char_ip = str2ip(char_ip_str); } } diff --git a/src/char/char.h b/src/char/char.h index 8c8d7c554..18bb7b1b5 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -29,7 +29,6 @@ int mapif_send(int fd,unsigned char *buf, unsigned int len); int char_married(int pl1,int pl2); int char_child(int parent_id, int child_id); int char_family(int cid1, int cid2, int cid3); -void char_clearparty(int party_id); int char_log(char *fmt, ...); diff --git a/src/char/int_guild.c b/src/char/int_guild.c index 9a815200f..31cceeff0 100644 --- a/src/char/int_guild.c +++ b/src/char/int_guild.c @@ -865,8 +865,8 @@ int mapif_guild_notice(struct guild *g) WBUFW(buf,0) = 0x383e; WBUFL(buf,2) = g->guild_id; - memcpy(WBUFP(buf,6), g->mes1, 60); - memcpy(WBUFP(buf,66), g->mes2, 120); + memcpy(WBUFP(buf,6), g->mes1, MAX_GUILDMES1); + memcpy(WBUFP(buf,66), g->mes2, MAX_GUILDMES2); mapif_sendall(buf, 186); return 0; @@ -1404,8 +1404,8 @@ int mapif_parse_GuildNotice(int fd, int guild_id, const char *mes1, const char * g = (struct guild*)idb_get(guild_db, guild_id); if (g == NULL) return 0; - memcpy(g->mes1, mes1, 60); - memcpy(g->mes2, mes2, 120); + memcpy(g->mes1, mes1, MAX_GUILDMES1); + memcpy(g->mes2, mes2, MAX_GUILDMES2); return mapif_guild_notice(g); } diff --git a/src/char/int_party.c b/src/char/int_party.c index 04d638049..b340aa9cf 100644 --- a/src/char/int_party.c +++ b/src/char/int_party.c @@ -488,7 +488,6 @@ int mapif_parse_PartyInfo(int fd, int party_id) mapif_party_info(fd, &p->party); else { mapif_party_noinfo(fd, party_id); - char_clearparty(party_id); } return 0; diff --git a/src/char/int_storage.c b/src/char/int_storage.c index bae72fcf4..2a041b286 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -101,12 +101,12 @@ int guild_storage_tostr(char *str,struct guild_storage *p) 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) ){ + if( (p->items[i].nameid) && (p->items[i].amount) ){ str_p += sprintf(str_p,"%d,%d,%d,%d,%d,%d,%d", - p->storage_[i].id,p->storage_[i].nameid,p->storage_[i].amount,p->storage_[i].equip, - p->storage_[i].identify,p->storage_[i].refine,p->storage_[i].attribute); + p->items[i].id,p->items[i].nameid,p->items[i].amount,p->items[i].equip, + p->items[i].identify,p->items[i].refine,p->items[i].attribute); for(j=0; j<MAX_SLOTS; j++) - str_p += sprintf(str_p,",%d",p->storage_[i].card[j]); + str_p += sprintf(str_p,",%d",p->items[i].card[j]); str_p += sprintf(str_p," "); f++; } @@ -138,15 +138,15 @@ int guild_storage_fromstr(char *str,struct guild_storage *p) &tmp_int[0], &tmp_int[1], &tmp_int[2], &tmp_int[3], &tmp_int[4], &tmp_int[5], &tmp_int[6], tmp_str, &len) == 8) { - p->storage_[i].id = tmp_int[0]; - p->storage_[i].nameid = tmp_int[1]; - p->storage_[i].amount = tmp_int[2]; - p->storage_[i].equip = tmp_int[3]; - p->storage_[i].identify = tmp_int[4]; - p->storage_[i].refine = tmp_int[5]; - p->storage_[i].attribute = tmp_int[6]; + p->items[i].id = tmp_int[0]; + p->items[i].nameid = tmp_int[1]; + p->items[i].amount = tmp_int[2]; + p->items[i].equip = tmp_int[3]; + p->items[i].identify = tmp_int[4]; + p->items[i].refine = tmp_int[5]; + p->items[i].attribute = tmp_int[6]; for(j = 0; j < MAX_SLOTS && tmp_str[0] && sscanf(tmp_str, ",%d%[0-9,-]",&tmp_int[0], tmp_str) > 0; j++) - p->storage_[i].card[j] = tmp_int[0]; + p->items[i].card[j] = tmp_int[0]; next += len; if (str[next] == ' ') next++; @@ -371,8 +371,8 @@ int inter_guild_storage_delete(int guild_id) if(gs) { int i; for(i=0;i<gs->storage_amount;i++){ - if(gs->storage_[i].card[0] == (short)0xff00) - inter_pet_delete( MakeDWord(gs->storage_[i].card[1],gs->storage_[i].card[2]) ); + if(gs->items[i].card[0] == (short)0xff00) + inter_pet_delete( MakeDWord(gs->items[i].card[1],gs->items[i].card[2]) ); } idb_remove(guild_storage_db,guild_id); } diff --git a/src/char/inter.c b/src/char/inter.c index 051d8117a..8d9cac99f 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -210,7 +210,7 @@ static int inter_config_read(const char *cfgName) { } else if(strcmpi(w1,"log_inter")==0) { log_inter = atoi(w2); } else if(strcmpi(w1, "main_chat_nick")==0){ // Main chat nick [LuzZza] - strcpy(main_chat_nick, w2); + safestrncpy(main_chat_nick, w2, sizeof(main_chat_nick)); #endif //TXT_SQL_CONVERT } else if (strcmpi(w1, "import") == 0) { inter_config_read(w2); diff --git a/src/char_sql/char.c b/src/char_sql/char.c index 36944cd35..46b645ce6 100644 --- a/src/char_sql/char.c +++ b/src/char_sql/char.c @@ -2,17 +2,17 @@ // For more information, see LICENCE in the main folder #include "../common/cbasetypes.h" -#include "../common/strlib.h" #include "../common/core.h" -#include "../common/timer.h" -#include "../common/mmo.h" #include "../common/db.h" #include "../common/malloc.h" #include "../common/mapindex.h" +#include "../common/mmo.h" #include "../common/showmsg.h" #include "../common/socket.h" -#include "../common/version.h" +#include "../common/strlib.h" +#include "../common/timer.h" #include "../common/utils.h" +#include "../common/version.h" #include "inter.h" #include "int_guild.h" #include "int_homun.h" @@ -60,6 +60,13 @@ char friend_db[256] = "friends"; char hotkey_db[256] = "hotkey"; char quest_db[256] = "quest"; +// show loading/saving messages +#ifdef TXT_SQL_CONVERT +int save_log = 0; //Have the logs be off by default when converting +#else +int save_log = 1; +#endif + //If your code editor is having problems syntax highlighting this file, uncomment this and RECOMMENT IT BEFORE COMPILING //#undef TXT_SQL_CONVERT #ifndef TXT_SQL_CONVERT @@ -94,11 +101,11 @@ int char_maintenance = 0; bool char_new = true; int char_new_display = 0; -int name_ignoring_case = 0; // Allow or not identical name for characters but with a different case by [Yor] +bool name_ignoring_case = false; // 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 unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the requested name cannot be determined #define TRIM_CHARS "\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex] -char char_name_letters[1024] = ""; // list of letters/symbols used to authorise or not a name of a character. by [Yor] +char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) in a character name. by [Yor] int char_per_account = 0; //Maximum charas per account (default unlimited) [Sirius] int char_del_level = 0; //From which level u can delete character [Lupus] @@ -106,12 +113,6 @@ int char_del_level = 0; //From which level u can delete character [Lupus] int log_char = 1; // loggin char or not [devil] int log_inter = 1; // loggin inter or not [devil] -#ifdef TXT_SQL_CONVERT -int save_log = 0; //Have the logs be off by default when converting -#else -int save_log = 1; -#endif - // Advanced subnet check [LuzZza] struct s_subnet { uint32 mask; @@ -813,7 +814,6 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit return 0; } -#define MAX_CHAR_BUF 110 //Max size (for WFIFOHEAD calls) int mmo_char_tobuf(uint8* buf, struct mmo_charstatus* p); #ifndef TXT_SQL_CONVERT @@ -1249,7 +1249,7 @@ int check_char_name(char * name, char * esc_name) // check for reserved names if( strcmpi(name, main_chat_nick) == 0 || strcmpi(name, wisp_server_name) == 0 ) return -1; // nick reserved for internal server messages - + // Check Authorised letters/symbols in the name of the character if( char_name_option == 1 ) { // only letters/symbols in char_name_letters are authorised @@ -1264,13 +1264,14 @@ int check_char_name(char * name, char * esc_name) return -2; } + // check name (already in use?) if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) - { // check name (already in use?) + { Sql_ShowDebug(sql_handle); return -2; } if( Sql_NumRows(sql_handle) > 0 ) - return -1; // name already exists + return -1; // name already exists return 0; } @@ -1294,8 +1295,6 @@ int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int ag //check other inputs if((slot >= MAX_CHARS) // slots - || (hair_style >= 24) // hair style - || (hair_color >= 9) // hair color || (str + agi + vit + int_ + dex + luk != 6*5 ) // stats || (str < 1 || str > 9 || agi < 1 || agi > 9 || vit < 1 || vit > 9 || int_ < 1 || int_ > 9 || dex < 1 || dex > 9 || luk < 1 || luk > 9) // individual stat values || (str + int_ != 10 || agi + luk != 10 || vit + dex != 10) ) // pairs @@ -1524,27 +1523,26 @@ int delete_char_sql(int char_id) return 0; } -//========================================================================================================== - +//--------------------------------------------------------------------- +// This function return the number of online players in all map-servers +//--------------------------------------------------------------------- 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[i].fd > 0) { - users += server[i].users; - } + users = 0; + for(i = 0; i < MAX_MAP_SERVERS; i++) { + if (server[i].fd > 0) { + users += server[i].users; } - return users; } - return 0; + return users; } -/// Writes char data to the buffer in the format used by the client. -/// Used in packets 0x6b (chars info) and 0x6d (new char info) +// Writes char data to the buffer in the format used by the client. +// Used in packets 0x6b (chars info) and 0x6d (new char info) // Returns the size +#define MAX_CHAR_BUF 110 //Max size (for WFIFOHEAD calls) int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { unsigned short offset = 0; @@ -1598,15 +1596,18 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) WBUFW(buf,104) = p->slot; #if PACKETVER >= 20061023 WBUFW(buf,106) = ( p->rename > 0 ) ? 0 : 1; - offset+=2; + offset += 2; #endif #if (PACKETVER >= 20100720 && PACKETVER <= 20100727) || PACKETVER >= 20100803 mapindex_getmapname_ext(mapindex_id2name(p->last_point.map), (char*)WBUFP(buf,108)); - offset += 16; + offset += MAP_NAME_LENGTH_EXT; #endif return 106+offset; } +//---------------------------------------- +// Function to send characters to a player +//---------------------------------------- int mmo_char_send006b(int fd, struct char_session_data* sd) { int j, offset = 0; @@ -1671,9 +1672,9 @@ int char_child(int parent_id, int child_id) return 0; } -int char_family(int pl1, int pl2, int pl3) +int char_family(int cid1, int cid2, int cid3) { - if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`partner_id`,`child` FROM `%s` WHERE `char_id` IN ('%d','%d','%d')", char_db, pl1, pl2, pl3) ) + if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`partner_id`,`child` FROM `%s` WHERE `char_id` IN ('%d','%d','%d')", char_db, cid1, cid2, cid3) ) Sql_ShowDebug(sql_handle); else while( SQL_SUCCESS == Sql_NextRow(sql_handle) ) { @@ -1686,9 +1687,9 @@ int char_family(int pl1, int pl2, int pl3) Sql_GetData(sql_handle, 1, &data, NULL); partnerid = atoi(data); Sql_GetData(sql_handle, 2, &data, NULL); childid = atoi(data); - if( (pl1 == charid && ((pl2 == partnerid && pl3 == childid ) || (pl2 == childid && pl3 == partnerid))) || - (pl1 == partnerid && ((pl2 == charid && pl3 == childid ) || (pl2 == childid && pl3 == charid ))) || - (pl1 == childid && ((pl2 == charid && pl3 == partnerid) || (pl2 == partnerid && pl3 == charid ))) ) + if( (cid1 == charid && ((cid2 == partnerid && cid3 == childid ) || (cid2 == childid && cid3 == partnerid))) || + (cid1 == partnerid && ((cid2 == charid && cid3 == childid ) || (cid2 == childid && cid3 == charid ))) || + (cid1 == childid && ((cid2 == charid && cid3 == partnerid) || (cid2 == partnerid && cid3 == charid ))) ) { Sql_FreeResult(sql_handle); return childid; @@ -1698,6 +1699,20 @@ int char_family(int pl1, int pl2, int pl3) return 0; } +//---------------------------------------------------------------------- +// Force disconnection of an online player (with account value) by [Yor] +//---------------------------------------------------------------------- +void disconnect_player(int account_id) +{ + int i; + struct char_session_data* sd; + + // disconnect player if online on char-server + ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == account_id ); + if( i < fd_max ) + set_eof(i); +} + static void char_auth_ok(int fd, struct char_session_data *sd) { struct online_char_data* character; @@ -1777,17 +1792,17 @@ int parse_fromlogin(int fd) return 0; if (RFIFOB(fd,2)) { - //printf("connect login server error : %d\n", RFIFOB(fd, 2)); + //printf("connect login server error : %d\n", RFIFOB(fd,2)); ShowError("Can not connect to login-server.\n"); ShowError("The server communication passwords (default s1/p1) are probably invalid.\n"); ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n"); ShowError("The communication passwords are set in map_athena.conf and char_athena.conf\n"); - }else { + } else { ShowStatus("Connected to login-server (connection #%d).\n", fd); //Send online accounts to login server. send_accounts_tologin(-1, gettick(), 0, 0); - + // if no map-server already connected, display a message... ARR_FIND( 0, MAX_MAP_SERVERS, i, server[i].fd > 0 && server[i].map[0] ); if( i == MAX_MAP_SERVERS ) @@ -1874,19 +1889,18 @@ int parse_fromlogin(int fd) if (RFIFOREST(fd) < 7) return 0; { - unsigned char buf[16]; + unsigned char buf[7]; int acc = RFIFOL(fd,2); int sex = RFIFOB(fd,6); RFIFOSKIP(fd,7); if( acc > 0 ) - { + {// TODO: Is this even possible? int char_id[MAX_CHARS]; int class_[MAX_CHARS]; int guild_id[MAX_CHARS]; int num; - int i; char* data; struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc); @@ -1935,12 +1949,10 @@ int parse_fromlogin(int fd) inter_guild_sex_changed(guild_id[i], acc, char_id[i], sex); } Sql_FreeResult(sql_handle); - } - // disconnect player if online on char-server - ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == acc ); - if( i < fd_max ) - set_eof(i); + // disconnect player if online on char-server + disconnect_player(acc); + } // notify all mapservers about this change WBUFW(buf,0) = 0x2b0d; @@ -1960,8 +1972,8 @@ int parse_fromlogin(int fd) memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] mapif_sendall(buf, WBUFW(buf,2)); - } RFIFOSKIP(fd, RFIFOW(fd,2)); + } break; // State change of account/ban notification (from login-server) @@ -1978,9 +1990,7 @@ int parse_fromlogin(int fd) mapif_sendall(buf, 11); } // disconnect player if online on char-server - ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == RFIFOL(fd,2) ); - if( i < fd_max ) - set_eof(i); + disconnect_player(RFIFOL(fd,2)); RFIFOSKIP(fd,11); break; @@ -2032,8 +2042,8 @@ int parse_fromlogin(int fd) mapif_sendall(buf, 2); new_ip = host2ip(login_ip_str); - if (new_ip && new_ip != login_ip) //Update login ip, too. - login_ip = new_ip; + if (new_ip && new_ip != login_ip) + login_ip = new_ip; //Update login ip, too. new_ip = host2ip(char_ip_str); if (new_ip && new_ip != char_ip) @@ -2046,13 +2056,13 @@ int parse_fromlogin(int fd) WFIFOL(fd,2) = htonl(char_ip); WFIFOSET(fd,6); } - } RFIFOSKIP(fd,2); + } break; default: - ShowError("Unknown packet 0x%04x received from login server, disconnecting.\n", command); + ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", command); set_eof(fd); return 0; } @@ -2180,6 +2190,7 @@ int char_send_fame_list(int fd) mapif_send(fd, buf, len); else mapif_sendall(buf, len); + return 0; } @@ -2205,13 +2216,12 @@ int char_loadName(int char_id, char* name) else if( SQL_SUCCESS == Sql_NextRow(sql_handle) ) { Sql_GetData(sql_handle, 0, &data, &len); - memset(name, 0, NAME_LENGTH); - memcpy(name, data, min(len, NAME_LENGTH)); + safestrncpy(name, data, NAME_LENGTH); return 1; } else { - memcpy(name, unknown_char_name, NAME_LENGTH); + safestrncpy(name, unknown_char_name, NAME_LENGTH); } return 0; } @@ -2220,15 +2230,9 @@ int search_mapserver(unsigned short map, uint32 ip, uint16 port); int parse_frommap(int fd) { - int i = 0, j = 0; + int i, j; int id; - // Sometimes fd=0, and it will cause server crash. Don't know why. :( - if (fd <= 0) { - ShowError("parse_frommap error fd=%d\n", fd); - return 0; - } - ARR_FIND( 0, MAX_MAP_SERVERS, id, server[id].fd == fd ); if(id == MAX_MAP_SERVERS) set_eof(fd); @@ -2260,7 +2264,7 @@ int parse_frommap(int fd) while(RFIFOREST(fd) >= 2) { - switch(RFIFOW(fd, 0)) + switch(RFIFOW(fd,0)) { case 0x2afa: // Receiving map names list from the map-server @@ -2390,16 +2394,16 @@ int parse_frommap(int fd) return 0; { //TODO: When data mismatches memory, update guild/party online/offline states. - int i, aid, cid; + int aid, cid; struct online_char_data* character; - online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown' server[id].users = RFIFOW(fd,4); + online_char_db->foreach(online_char_db,char_db_setoffline,id); //Set all chars from this server as 'unknown' for(i = 0; i < server[id].users; i++) { aid = RFIFOL(fd,6+i*8); cid = RFIFOL(fd,6+i*8+4); character = (struct online_char_data*)idb_ensure(online_char_db, aid, create_online_char_data); - if (character->server > -1 && character->server != id) + if( character->server > -1 && character->server != id ) { ShowNotice("Set map user: Character (%d:%d) marked on map server %d, but map server %d claims to have (%d:%d) online!\n", character->account_id, character->char_id, character->server, id, aid, cid); @@ -2419,6 +2423,7 @@ int parse_frommap(int fd) { int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2); struct online_char_data* character; + if (size - 13 != sizeof(struct mmo_charstatus)) { ShowError("parse_from_map (save-char): Size mismatch! %d != %d\n", size-13, sizeof(struct mmo_charstatus)); @@ -2439,7 +2444,7 @@ int parse_frommap(int fd) } if (RFIFOB(fd,12)) - { //Flag? Set character offline after saving [Skotlex] + { //Flag, set character offline after saving. [Skotlex] set_char_offline(cid, aid); WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x2b21; //Save ack only needed on final save. @@ -2530,7 +2535,7 @@ int parse_frommap(int fd) data = (struct online_char_data*)idb_ensure(online_char_db, RFIFOL(fd,2), create_online_char_data); data->char_id = char_data->char_id; data->server = map_id; //Update server where char is. - + //Reply with an ack. WFIFOHEAD(fd,30); WFIFOW(fd,0) = 0x2b06; @@ -2738,22 +2743,17 @@ int parse_frommap(int fd) break; case 0x2b16: // Receive rates [Wizputer] - if (RFIFOREST(fd) < 6 || RFIFOREST(fd) < RFIFOW(fd,8)) + if( RFIFOREST(fd) < 14 ) return 0; { - char motd[256]; - char esc_motd[sizeof(motd)*2+1]; char esc_server_name[sizeof(server_name)*2+1]; - strncpy(motd, (char*)RFIFOP(fd,10), 255); //First copy it to make sure the motd fits. - motd[255] = '\0'; - Sql_EscapeString(sql_handle, esc_motd, motd); Sql_EscapeString(sql_handle, esc_server_name, server_name); - if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `ragsrvinfo` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d',`motd`='%s'", - fd, esc_server_name, RFIFOW(fd,2), RFIFOW(fd,4), RFIFOW(fd,6), esc_motd) ) + if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `ragsrvinfo` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d'", + fd, esc_server_name, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)) ) Sql_ShowDebug(sql_handle); - RFIFOSKIP(fd,RFIFOW(fd,8)); + RFIFOSKIP(fd,14); } break; @@ -2790,7 +2790,7 @@ int parse_frommap(int fd) { #ifdef ENABLE_SC_SAVING int count, aid, cid; - + aid = RFIFOL(fd, 4); cid = RFIFOL(fd, 8); count = RFIFOW(fd, 12); @@ -2939,7 +2939,8 @@ int search_mapserver(unsigned short map, uint32 ip, uint16 port) return -1; } -int char_mapif_init(int fd) +// char_mapifの初期化処理(現在はinter_mapif初期化のみ) +static int char_mapif_init(int fd) { return inter_mapif_init(fd); } @@ -2962,8 +2963,8 @@ int lan_subnetcheck(uint32 ip) int parse_char(int fd) { - int i, ch = 0; - char email[40]; + int i, ch; + char email[40]; unsigned short cmd; int map_fd; struct char_session_data* sd; @@ -3072,7 +3073,7 @@ int parse_char(int fd) FIFOSD_CHECK(3); { struct mmo_charstatus char_dat; - struct mmo_charstatus * cp; + struct mmo_charstatus *cd; char* data; int char_id; uint32 subnet_map_ip; @@ -3084,11 +3085,13 @@ int parse_char(int fd) if ( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'", char_db, sd->account_id, slot) || SQL_SUCCESS != Sql_NextRow(sql_handle) || SQL_SUCCESS != Sql_GetData(sql_handle, 0, &data, NULL) ) - { - //Not found?? May be forged packet. + { //Not found?? May be forged packet. Sql_ShowDebug(sql_handle); Sql_FreeResult(sql_handle); - //TODO: perhaps add some reply? (otherwise it hangs the client) + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0; // rejected from server + WFIFOSET(fd,3); break; } @@ -3097,8 +3100,8 @@ int parse_char(int fd) mmo_char_fromsql(char_id, &char_dat, true); //Have to switch over to the DB instance otherwise data won't propagate [Kevin] - cp = (struct mmo_charstatus *)idb_get(char_db_, char_id); - cp->sex = sd->sex; + cd = (struct mmo_charstatus *)idb_get(char_db_, char_id); + cd->sex = sd->sex; if (log_char) { char esc_name[NAME_LENGTH*2+1]; @@ -3111,7 +3114,7 @@ int parse_char(int fd) ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name); // searching map server - i = search_mapserver(char_dat.last_point.map, -1, -1); + i = search_mapserver(cd->last_point.map, -1, -1); // if map is not found, we check major cities if (i < 0) { @@ -3127,33 +3130,33 @@ int parse_char(int fd) break; } if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { - cp->last_point.x = 273; - cp->last_point.y = 354; + cd->last_point.x = 273; + cd->last_point.y = 354; } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { - cp->last_point.x = 120; - cp->last_point.y = 100; + cd->last_point.x = 120; + cd->last_point.y = 100; } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { - cp->last_point.x = 160; - cp->last_point.y = 94; + cd->last_point.x = 160; + cd->last_point.y = 94; } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { - cp->last_point.x = 116; - cp->last_point.y = 57; + cd->last_point.x = 116; + cd->last_point.y = 57; } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { - cp->last_point.x = 87; - cp->last_point.y = 117; + cd->last_point.x = 87; + cd->last_point.y = 117; } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { - cp->last_point.x = 94; - cp->last_point.y = 103; + cd->last_point.x = 94; + cd->last_point.y = 103; } else { - ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(char_dat.last_point.map)); + ShowInfo("Connection Closed. No map server available that has a major city, and unable to find map-server for '%s'.\n", mapindex_id2name(cd->last_point.map)); WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x81; WFIFOB(fd,2) = 1; // 01 = Server closed WFIFOSET(fd,3); break; } - ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(char_dat.last_point.map), mapindex_id2name(j)); - cp->last_point.map = j; + ShowWarning("Unable to find map-server for '%s', sending to major city '%s'.\n", mapindex_id2name(cd->last_point.map), mapindex_id2name(j)); + cd->last_point.map = j; } //Send NEW auth packet [Kevin] @@ -3174,9 +3177,9 @@ int parse_char(int fd) //Send player to map WFIFOHEAD(fd,28); WFIFOW(fd,0) = 0x71; - WFIFOL(fd,2) = cp->char_id; - mapindex_getmapname_ext(mapindex_id2name(cp->last_point.map), (char*)WFIFOP(fd,6)); - subnet_map_ip = lan_subnetcheck(ipl); + WFIFOL(fd,2) = cd->char_id; + mapindex_getmapname_ext(mapindex_id2name(cd->last_point.map), (char*)WFIFOP(fd,6)); + subnet_map_ip = lan_subnetcheck(ipl); // Advanced subnet check [LuzZza] WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : server[i].ip); WFIFOW(fd,26) = ntows(htons(server[i].port)); // [!] LE byte order here [!] WFIFOSET(fd,28); @@ -3184,7 +3187,7 @@ int parse_char(int fd) // create temporary auth entry CREATE(node, struct auth_node, 1); node->account_id = sd->account_id; - node->char_id = cp->char_id; + node->char_id = cd->char_id; node->login_id1 = sd->login_id1; node->login_id2 = sd->login_id2; node->sex = sd->sex; @@ -3219,8 +3222,6 @@ int parse_char(int fd) case -3: WFIFOB(fd,2) = 0x01; break; } WFIFOSET(fd,3); - RFIFOSKIP(fd,37); - break; } else { @@ -3253,7 +3254,6 @@ int parse_char(int fd) { int cid = RFIFOL(fd,2); - WFIFOHEAD(fd,46); ShowInfo(CL_RED"Request Char Deletion: "CL_GREEN"%d (%d)"CL_RESET"\n", sd->account_id, cid); memcpy(email, RFIFOP(fd,6), 40); RFIFOSKIP(fd,RFIFOREST(fd)); // hack to make the other deletion packet work @@ -3270,7 +3270,7 @@ int parse_char(int fd) WFIFOSET(fd,3); break; } - + // check if this char exists ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid ); if( i == MAX_CHARS ) @@ -3313,7 +3313,7 @@ int parse_char(int fd) break; // char rename request - // R 028d <account ID>.l <char ID>.l <new name>.24B + // R 028d <account ID>.l <char ID>.l <new name>.24B case 0x28d: FIFOSD_CHECK(34); { @@ -3393,7 +3393,7 @@ int parse_char(int fd) RFIFOSKIP(fd,32); break; - // log in as map-server + // login as map-server case 0x2af8: if (RFIFOREST(fd) < 60) return 0; @@ -3424,7 +3424,7 @@ int parse_char(int fd) realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); char_mapif_init(fd); } - + RFIFOSKIP(fd,60); } return 0; // avoid processing of followup packets here @@ -3487,7 +3487,6 @@ int parse_console(char* buf) return 0; } -// MAP send all int mapif_sendall(unsigned char *buf, unsigned int len) { int i, c; @@ -3665,7 +3664,7 @@ static int online_data_cleanup_sub(DBKey key, void *data, va_list ap) { struct online_char_data *character= (struct online_char_data*)data; if (character->fd != -1) - return 0; //Still connected + return 0; //Character still connected if (character->server == -2) //Unknown server.. set them offline set_char_offline(character->char_id, character->account_id); if (character->server < 0) @@ -3758,59 +3757,55 @@ void sql_config_read(const char* cfgName) continue; if(!strcmpi(w1,"char_db")) - strcpy(char_db,w2); + safestrncpy(char_db, w2, sizeof(char_db)); else if(!strcmpi(w1,"scdata_db")) - strcpy(scdata_db,w2); + safestrncpy(scdata_db, w2, sizeof(scdata_db)); else if(!strcmpi(w1,"cart_db")) - strcpy(cart_db,w2); + safestrncpy(cart_db, w2, sizeof(cart_db)); else if(!strcmpi(w1,"inventory_db")) - strcpy(inventory_db, w2); + safestrncpy(inventory_db, w2, sizeof(inventory_db)); else if(!strcmpi(w1,"charlog_db")) - strcpy(charlog_db,w2); + safestrncpy(charlog_db, w2, sizeof(charlog_db)); else if(!strcmpi(w1,"storage_db")) - strcpy(storage_db,w2); + safestrncpy(storage_db, w2, sizeof(storage_db)); else if(!strcmpi(w1,"reg_db")) - strcpy(reg_db,w2); + safestrncpy(reg_db, w2, sizeof(reg_db)); else if(!strcmpi(w1,"skill_db")) - strcpy(skill_db,w2); + safestrncpy(skill_db, w2, sizeof(skill_db)); else if(!strcmpi(w1,"interlog_db")) - strcpy(interlog_db,w2); + safestrncpy(interlog_db, w2, sizeof(interlog_db)); else if(!strcmpi(w1,"memo_db")) - strcpy(memo_db,w2); + safestrncpy(memo_db, w2, sizeof(memo_db)); else if(!strcmpi(w1,"guild_db")) - strcpy(guild_db,w2); + safestrncpy(guild_db, w2, sizeof(guild_db)); else if(!strcmpi(w1,"guild_alliance_db")) - strcpy(guild_alliance_db,w2); + safestrncpy(guild_alliance_db, w2, sizeof(guild_alliance_db)); else if(!strcmpi(w1,"guild_castle_db")) - strcpy(guild_castle_db,w2); + safestrncpy(guild_castle_db, w2, sizeof(guild_castle_db)); else if(!strcmpi(w1,"guild_expulsion_db")) - strcpy(guild_expulsion_db,w2); + safestrncpy(guild_expulsion_db, w2, sizeof(guild_expulsion_db)); else if(!strcmpi(w1,"guild_member_db")) - strcpy(guild_member_db,w2); + safestrncpy(guild_member_db, w2, sizeof(guild_member_db)); else if(!strcmpi(w1,"guild_skill_db")) - strcpy(guild_skill_db,w2); + safestrncpy(guild_skill_db, w2, sizeof(guild_skill_db)); else if(!strcmpi(w1,"guild_position_db")) - strcpy(guild_position_db,w2); + safestrncpy(guild_position_db, w2, sizeof(guild_position_db)); else if(!strcmpi(w1,"guild_storage_db")) - strcpy(guild_storage_db,w2); + safestrncpy(guild_storage_db, w2, sizeof(guild_storage_db)); else if(!strcmpi(w1,"party_db")) - strcpy(party_db,w2); + safestrncpy(party_db, w2, sizeof(party_db)); else if(!strcmpi(w1,"pet_db")) - strcpy(pet_db,w2); + safestrncpy(pet_db, w2, sizeof(pet_db)); else if(!strcmpi(w1,"mail_db")) - strcpy(mail_db,w2); + safestrncpy(mail_db, w2, sizeof(mail_db)); else if(!strcmpi(w1,"auction_db")) - strcpy(auction_db,w2); + safestrncpy(auction_db, w2, sizeof(auction_db)); else if(!strcmpi(w1,"friend_db")) - strcpy(friend_db,w2); + safestrncpy(friend_db, w2, sizeof(friend_db)); else if(!strcmpi(w1,"hotkey_db")) - strcpy(hotkey_db,w2); + safestrncpy(hotkey_db, w2, sizeof(hotkey_db)); else if(!strcmpi(w1,"quest_db")) - strcpy(quest_db,w2); -#ifndef TXT_SQL_CONVERT - else if(!strcmpi(w1,"db_path")) - strcpy(db_path,w2); -#endif + safestrncpy(quest_db,w2,sizeof(quest_db)); //support the import command, just like any other config else if(!strcmpi(w1,"import")) sql_config_read(w2); @@ -3842,30 +3837,28 @@ int char_config_read(const char* cfgName) remove_control_chars(w1); remove_control_chars(w2); if(strcmpi(w1,"timestamp_format") == 0) { - strncpy(timestamp_format, w2, 20); + safestrncpy(timestamp_format, w2, sizeof(timestamp_format)); } else if(strcmpi(w1,"console_silent")==0){ ShowInfo("Console Silent Setting: %d\n", atoi(w2)); msg_silent = atoi(w2); } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ stdout_with_ansisequence = config_switch(w2); } else if (strcmpi(w1, "userid") == 0) { - strncpy(userid, w2, 24); + safestrncpy(userid, w2, sizeof(userid)); } else if (strcmpi(w1, "passwd") == 0) { - strncpy(passwd, w2, 24); + safestrncpy(passwd, w2, sizeof(passwd)); } else if (strcmpi(w1, "server_name") == 0) { - strncpy(server_name, w2, 20); - server_name[sizeof(server_name) - 1] = '\0'; + safestrncpy(server_name, w2, sizeof(server_name)); ShowStatus("%s server has been initialized\n", w2); } else if (strcmpi(w1, "wisp_server_name") == 0) { if (strlen(w2) >= 4) { - memcpy(wisp_server_name, w2, sizeof(wisp_server_name)); - wisp_server_name[sizeof(wisp_server_name) - 1] = '\0'; + safestrncpy(wisp_server_name, w2, sizeof(wisp_server_name)); } } else if (strcmpi(w1, "login_ip") == 0) { char ip_str[16]; login_ip = host2ip(w2); if (login_ip) { - strncpy(login_ip_str, w2, sizeof(login_ip_str)); + safestrncpy(login_ip_str, w2, sizeof(login_ip_str)); ShowStatus("Login server IP address : %s -> %s\n", w2, ip2str(login_ip, ip_str)); } } else if (strcmpi(w1, "login_port") == 0) { @@ -3874,14 +3867,14 @@ int char_config_read(const char* cfgName) char ip_str[16]; char_ip = host2ip(w2); if (char_ip){ - strncpy(char_ip_str, w2, sizeof(char_ip_str)); + safestrncpy(char_ip_str, w2, sizeof(char_ip_str)); ShowStatus("Character server IP address : %s -> %s\n", w2, ip2str(char_ip, ip_str)); } } else if (strcmpi(w1, "bind_ip") == 0) { char ip_str[16]; bind_ip = host2ip(w2); if (bind_ip) { - strncpy(bind_ip_str, w2, sizeof(bind_ip_str)); + safestrncpy(bind_ip_str, w2, sizeof(bind_ip_str)); ShowStatus("Character server binding IP address : %s -> %s\n", w2, ip2str(bind_ip, ip_str)); } } else if (strcmpi(w1, "char_port") == 0) { @@ -3931,18 +3924,20 @@ int char_config_read(const char* cfgName) } 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); + safestrncpy(unknown_char_name, w2, sizeof(unknown_char_name)); unknown_char_name[NAME_LENGTH-1] = '\0'; } else if (strcmpi(w1, "name_ignoring_case") == 0) { - name_ignoring_case = config_switch(w2); + name_ignoring_case = (bool)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); + safestrncpy(char_name_letters, w2, sizeof(char_name_letters)); } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius] char_per_account = atoi(w2); } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus] char_del_level = atoi(w2); + } else if(strcmpi(w1,"db_path")==0) { + safestrncpy(db_path, w2, sizeof(db_path)); } else if (strcmpi(w1, "console") == 0) { console = config_switch(w2); } else if (strcmpi(w1, "fame_list_alchemist") == 0) { @@ -3970,16 +3965,14 @@ int char_config_read(const char* cfgName) } } fclose(fp); - + ShowInfo("Done reading %s.\n", cfgName); return 0; } void do_final(void) { - ShowInfo("Doing final stage...\n"); - //check SQL save progress. - //wait until save char complete + ShowStatus("Terminating server.\n"); set_all_offline(-1); set_all_offline_sql(); @@ -3988,22 +3981,20 @@ void do_final(void) flush_fifos(); - mapindex_final(); - if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ragsrvinfo`") ) Sql_ShowDebug(sql_handle); + char_db_->destroy(char_db_, NULL); + online_char_db->destroy(online_char_db, NULL); + auth_db->destroy(auth_db, NULL); + if (login_fd > 0) do_close(login_fd); if (char_fd > 0) do_close(char_fd); - char_db_->destroy(char_db_, NULL); - online_char_db->destroy(online_char_db, NULL); - auth_db->destroy(auth_db, NULL); Sql_Free(sql_handle); - - ShowInfo("ok! all done...\n"); + mapindex_final(); } //------------------------------ @@ -4031,7 +4022,7 @@ int do_init(int argc, char **argv) //Read map indexes mapindex_init(); start_point.map = mapindex_name2id("new_zone01"); - + char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); char_lan_config_read((argc > 3) ? argv[3] : LAN_CONF_NAME); sql_config_read(SQL_CONF_NAME); @@ -4041,7 +4032,7 @@ int do_init(int argc, char **argv) ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n"); ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n"); } - + ShowInfo("Finished reading the char-server configuration.\n"); inter_init_sql((argc > 2) ? argv[2] : inter_cfgName); // inter server テハア篳ュ @@ -4066,11 +4057,11 @@ int do_init(int argc, char **argv) else ShowStatus("Defaulting to %s as our IP address\n", ip_str); if (!login_ip) { - strcpy(login_ip_str, ip_str); + safestrncpy(login_ip_str, ip_str, sizeof(login_ip_str)); login_ip = str2ip(login_ip_str); } if (!char_ip) { - strcpy(char_ip_str, ip_str); + safestrncpy(char_ip_str, ip_str, sizeof(char_ip_str)); char_ip = str2ip(char_ip_str); } } @@ -4089,7 +4080,7 @@ int do_init(int argc, char **argv) // send a list of all online account IDs to login server add_timer_func_list(send_accounts_tologin, "send_accounts_tologin"); - add_timer_interval(gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour. + add_timer_interval(gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour // ??? add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect"); diff --git a/src/char_sql/int_guild.c b/src/char_sql/int_guild.c index 00331e8e6..0b3d77f07 100644 --- a/src/char_sql/int_guild.c +++ b/src/char_sql/int_guild.c @@ -1131,8 +1131,8 @@ 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); + memcpy(WBUFP(buf,6),g->mes1,MAX_GUILDMES1); + memcpy(WBUFP(buf,66),g->mes2,MAX_GUILDMES2); mapif_sendall(buf,186); return 0; } @@ -1826,8 +1826,8 @@ int mapif_parse_GuildNotice(int fd,int guild_id,const char *mes1,const char *mes if(g==NULL) return 0; - memcpy(g->mes1,mes1,60); - memcpy(g->mes2,mes2,120); + memcpy(g->mes1,mes1,MAX_GUILDMES1); + memcpy(g->mes2,mes2,MAX_GUILDMES2); g->save_flag |= GS_MES; //Change mes of guild return mapif_guild_notice(g); } diff --git a/src/char_sql/int_storage.c b/src/char_sql/int_storage.c index 7cab6d5d9..0719685ae 100644 --- a/src/char_sql/int_storage.c +++ b/src/char_sql/int_storage.c @@ -76,7 +76,7 @@ int storage_fromsql(int account_id, struct storage_data* p) /// Save guild_storage data to sql int guild_storage_tosql(int guild_id, struct guild_storage* p) { - memitemdata_to_sql(p->storage_, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE); + memitemdata_to_sql(p->items, MAX_GUILD_STORAGE, guild_id, TABLE_GUILD_STORAGE); ShowInfo ("guild storage save to DB - guild: %d\n", guild_id); return 0; } @@ -109,7 +109,7 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p) for( i = 0; i < MAX_GUILD_STORAGE && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i ) { - item = &p->storage_[i]; + item = &p->items[i]; Sql_GetData(sql_handle, 0, &data, NULL); item->id = atoi(data); Sql_GetData(sql_handle, 1, &data, NULL); item->nameid = atoi(data); Sql_GetData(sql_handle, 2, &data, NULL); item->amount = atoi(data); diff --git a/src/char_sql/inter.c b/src/char_sql/inter.c index 2f5c2daff..97c48bdf1 100644 --- a/src/char_sql/inter.c +++ b/src/char_sql/inter.c @@ -235,7 +235,7 @@ static int inter_config_read(const char* cfgName) else if(!strcmpi(w1,"log_inter")) log_inter = atoi(w2); else if(!strcmpi(w1,"main_chat_nick")) - strcpy(main_chat_nick, w2); + safestrncpy(main_chat_nick, w2, sizeof(main_chat_nick)); #endif //TXT_SQL_CONVERT else if(!strcmpi(w1,"import")) inter_config_read(w2); diff --git a/src/common/lock.c b/src/common/lock.c index 5cb4c3a67..643b86e5c 100644 --- a/src/common/lock.c +++ b/src/common/lock.c @@ -1,8 +1,9 @@ // Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder -#include "../common/cbasetypes.h" -#include "../common/showmsg.h" +#include "cbasetypes.h" +#include "showmsg.h" +#include "utils.h" #include "lock.h" #include <stdio.h> @@ -12,12 +13,8 @@ #include <unistd.h> #else #include <io.h> -#define F_OK 0x0 -#define R_OK 0x4 #endif -#define exists(filename) (!access(filename, F_OK)) - // 書き込みファイルの保護処理 // (書き込みが終わるまで、旧ファイルを保管しておく) diff --git a/src/common/mmo.h b/src/common/mmo.h index 6a78d840c..92adf8af0 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -108,13 +108,6 @@ #define MAX_QUEST_DB 2000 //Max quests that the server will load #define MAX_QUEST_OBJECTIVES 3 //Max quest objectives for a quest -#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 @@ -147,6 +140,10 @@ #define END_ACCOUNT_NUM 100000000 #define START_CHAR_NUM 150000 +//Guilds +#define MAX_GUILDMES1 60 +#define MAX_GUILDMES2 120 + //Base Homun skill. #define HM_SKILLBASE 8001 #define MAX_HOMUNSKILL 16 @@ -177,6 +174,7 @@ enum item_types { IT_UNKNOWN2,//9 IT_AMMO, //10 IT_DELAYCONSUME,//11 + IT_CASH = 18, IT_MAX }; @@ -240,7 +238,7 @@ struct guild_storage { int guild_id; short storage_status; short storage_amount; - struct item storage_[MAX_GUILD_STORAGE]; + struct item items[MAX_GUILD_STORAGE]; }; struct s_pet { @@ -474,7 +472,7 @@ struct guild { char name[NAME_LENGTH],master[NAME_LENGTH]; struct guild_member member[MAX_GUILD]; struct guild_position position[MAX_GUILDPOSITION]; - char mes1[60],mes2[120]; + char mes1[MAX_GUILDMES1],mes2[MAX_GUILDMES2]; int emblem_len,emblem_id; char emblem_data[2048]; struct guild_alliance alliance[MAX_GUILDALLIANCE]; diff --git a/src/common/socket.c b/src/common/socket.c index deba0e97b..89c605c9d 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -199,6 +199,10 @@ time_t stall_time = 60; uint32 addr_[16]; // ip addresses of local host (host byte order) int naddr_ = 0; // # of ip addresses +// Maximum packet size in bytes, which the client is able to handle. +// Larger packets cause a buffer overflow and stack corruption. +static size_t socket_max_client_packet = 20480; + // initial recv buffer size (this will also be the max. size) // biggest known packet: S 0153 <len>.w <emblem data>.?B -> 24x24 256 color .bmp (0153 + len.w + 1618/1654/1756 bytes) #define RFIFO_SIZE (2*1024) @@ -643,8 +647,15 @@ int WFIFOSET(int fd, size_t len) exit(EXIT_FAILURE); } + if( !s->flag.server && len > socket_max_client_packet ) + {// see declaration of socket_max_client_packet for details + ShowError("WFIFOSET: Dropped too large client packet 0x%04x (length=%u, max=%u).\n", WFIFOW(fd,0), len, socket_max_client_packet); + return 0; + } + if( !s->flag.server && s->wdata_size+len > WFIFO_MAX ) {// reached maximum write fifo size + ShowError("WFIFOSET: Maximum write buffer size for client connection %d exceeded, most likely caused by packet 0x%04x (len=%u, ip=%lu.%lu.%lu.%lu).\n", fd, WFIFOW(fd,0), len, CONVIP(s->client_addr)); set_eof(fd); return 0; } @@ -1064,6 +1075,8 @@ int socket_config_read(const char* cfgName) ddos_autoreset = atoi(w2); else if (!strcmpi(w1,"debug")) access_debug = config_switch(w2); + else if (!strcmpi(w1,"socket_max_client_packet")) + socket_max_client_packet = strtoul(w2, NULL, 0); #endif else if (!strcmpi(w1, "import")) socket_config_read(w2); diff --git a/src/common/strlib.c b/src/common/strlib.c index 7a6c134e7..019e2d629 100644 --- a/src/common/strlib.c +++ b/src/common/strlib.c @@ -12,7 +12,6 @@ #include <errno.h> - #define J_MAX_MALLOC_SIZE 65535 // escapes a string in-place (' -> \' , \ -> \\ , % -> _) @@ -657,7 +656,7 @@ int sv_split(char* str, int len, int startoff, char delim, char** out_fields, in end[0] = end[1] = '\0'; *out_fields = end + 2; } - else if( (opt&SV_TERMINATE_LF) && end[0] == '\r' ) + else if( (opt&SV_TERMINATE_CR) && end[0] == '\r' ) { if( !(opt&SV_KEEP_TERMINATOR) ) end[0] = '\0'; @@ -923,18 +922,12 @@ bool sv_readdb(const char* directory, const char* filename, char delim, int minc FILE* fp; int lines = 0; int entries = 0; - char* fields[64]; // room for 63 fields ([0] is reserved) - int columns; + char** fields; // buffer for fields ([0] is reserved) + int columns, fields_length; char path[1024], line[1024]; snprintf(path, sizeof(path), "%s/%s", directory, filename); - if( maxcols > ARRAYLENGTH(fields)-1 ) - { - ShowError("sv_readdb: Insufficient column storage in parser for file \"%s\" (want %d, have only %d). Increase the capacity in the source code please.\n", path, maxcols, ARRAYLENGTH(fields)-1); - return false; - } - // open file fp = fopen(path, "r"); if( fp == NULL ) @@ -943,6 +936,10 @@ bool sv_readdb(const char* directory, const char* filename, char delim, int minc return false; } + // allocate enough memory for the maximum requested amount of columns plus the reserved one + fields_length = maxcols+1; + fields = aMalloc(fields_length*sizeof(char*)); + // process rows one by one while( fgets(line, sizeof(line), fp) ) { @@ -954,7 +951,7 @@ bool sv_readdb(const char* directory, const char* filename, char delim, int minc if( line[0] == '\0' || line[0] == '\n' || line[0] == '\r') continue; - columns = sv_split(line, strlen(line), 0, delim, fields, ARRAYLENGTH(fields), (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF)); + columns = sv_split(line, strlen(line), 0, delim, fields, fields_length, (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF)); if( columns < mincols ) { @@ -983,6 +980,7 @@ bool sv_readdb(const char* directory, const char* filename, char delim, int minc entries++; } + aFree(fields); fclose(fp); ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, path); diff --git a/src/common/utils.c b/src/common/utils.c index c1128edb4..12123784f 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -14,7 +14,11 @@ #include <math.h> // floor() #ifdef WIN32 + #include <io.h> #include <windows.h> + #ifndef F_OK + #define F_OK 0x0 + #endif /* F_OK */ #else #include <unistd.h> #include <dirent.h> @@ -182,6 +186,11 @@ void findfile(const char *p, const char *pat, void (func)(const char*)) } #endif +bool exists(const char* filename) +{ + return !access(filename, F_OK); +} + uint8 GetByte(uint32 val, int idx) { switch( idx ) diff --git a/src/common/utils.h b/src/common/utils.h index 93563dc34..5cf3ff3cf 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -14,6 +14,7 @@ void dump(FILE* fp, const unsigned char* buffer, int length); void findfile(const char *p, const char *pat, void (func)(const char*)); +bool exists(const char* filename); //Caps values to min/max #define cap_value(a, min, max) ((a >= max) ? max : (a <= min) ? min : a) diff --git a/src/ladmin/ladmin.c b/src/ladmin/ladmin.c index 3f02a240a..d7a78c119 100644 --- a/src/ladmin/ladmin.c +++ b/src/ladmin/ladmin.c @@ -261,7 +261,7 @@ int ladmin_log(char *fmt, ...) } //--------------------------------------------- -// Function to return ordonal text of a number. +// Function to return ordinal text of a number. //--------------------------------------------- char* makeordinal(int number) { @@ -275,7 +275,6 @@ char* makeordinal(int number) } else { return "th"; } - return ""; } //----------------------------------------------------------------------------------------- diff --git a/src/login/login.c b/src/login/login.c index e888d21f6..6b3a1bcaf 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -402,7 +402,7 @@ int parse_fromchar(int fd) uint32 login_id1 = RFIFOL(fd,6); uint32 login_id2 = RFIFOL(fd,10); uint8 sex = RFIFOB(fd,14); - uint32 ip_ = ntohl(RFIFOL(fd,15)); + //uint32 ip_ = ntohl(RFIFOL(fd,15)); int request_id = RFIFOL(fd,19); RFIFOSKIP(fd,23); diff --git a/src/map/atcommand.c b/src/map/atcommand.c index d48dda0db..95b3dc33e 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -452,7 +452,7 @@ ACMD_FUNC(mapmove) clif_displaymessage(fd, msg_txt(248)); return -1; } - if (pc_setpos(sd, mapindex, x, y, 3) != 0) { + if (pc_setpos(sd, mapindex, x, y, CLR_TELEPORT) != 0) { clif_displaymessage(fd, msg_txt(1)); // Map not found. return -1; } @@ -535,7 +535,7 @@ ACMD_FUNC(jumpto) return -1; } - pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, 3); + pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, CLR_TELEPORT); sprintf(atcmd_output, msg_txt(4), pl_sd->status.name); // Jumped to %s clif_displaymessage(fd, atcmd_output); @@ -572,7 +572,7 @@ ACMD_FUNC(jump) x = y = 0; //Invalid cell, use random spot. } - pc_setpos(sd, sd->mapindex, x, y, 3); + pc_setpos(sd, sd->mapindex, x, y, CLR_TELEPORT); sprintf(atcmd_output, msg_txt(5), sd->bl.x, sd->bl.y); // Jumped to %d %d clif_displaymessage(fd, atcmd_output); return 0; @@ -1114,7 +1114,7 @@ ACMD_FUNC(load) return -1; } - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 0); + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_OUTSIGHT); clif_displaymessage(fd, msg_txt(7)); // Warping to save point.. return 0; @@ -1663,15 +1663,15 @@ ACMD_FUNC(item2) 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) { + if (item_data->type == IT_WEAPON || item_data->type == IT_ARMOR || + item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) { loop = number; get_count = 1; - if (item_data->type == 7) { + if (item_data->type == IT_PETEGG) { identify = 1; refine = 0; } - if (item_data->type == 8) + if (item_data->type == IT_PETARMOR) refine = 0; if (refine > 10) refine = 10; @@ -1777,8 +1777,10 @@ ACMD_FUNC(baselevelup) sd->status.base_level -= (unsigned int)level; clif_displaymessage(fd, msg_txt(22)); // Base level lowered. } + sd->status.base_exp = 0; clif_updatestatus(sd, SP_STATUSPOINT); clif_updatestatus(sd, SP_BASELEVEL); + clif_updatestatus(sd, SP_BASEEXP); clif_updatestatus(sd, SP_NEXTBASEEXP); status_calc_pc(sd, 0); if(sd->status.party_id) @@ -1808,11 +1810,7 @@ ACMD_FUNC(joblevelup) if ((unsigned int)level > pc_maxjoblv(sd) || (unsigned int)level > pc_maxjoblv(sd) - sd->status.job_level) // fix positiv overflow level = pc_maxjoblv(sd) - sd->status.job_level; sd->status.job_level += (unsigned int)level; - clif_updatestatus(sd, SP_JOBLEVEL); - clif_updatestatus(sd, SP_NEXTJOBEXP); sd->status.skill_point += level; - clif_updatestatus(sd, SP_SKILLPOINT); - status_calc_pc(sd, 0); clif_misceffect(&sd->bl, 1); clif_displaymessage(fd, msg_txt(24)); // Job level raised. } else { @@ -1824,18 +1822,20 @@ ACMD_FUNC(joblevelup) if ((unsigned int)level >= sd->status.job_level) // fix negativ overflow level = sd->status.job_level-1; sd->status.job_level -= (unsigned int)level; - clif_updatestatus(sd, SP_JOBLEVEL); - clif_updatestatus(sd, SP_NEXTJOBEXP); if (sd->status.skill_point < level) pc_resetskill(sd,0); //Reset skills since we need to substract more points. if (sd->status.skill_point < level) sd->status.skill_point = 0; else sd->status.skill_point -= level; - clif_updatestatus(sd, SP_SKILLPOINT); - status_calc_pc(sd, 0); clif_displaymessage(fd, msg_txt(25)); // Job level lowered. } + sd->status.job_exp = 0; + clif_updatestatus(sd, SP_JOBLEVEL); + clif_updatestatus(sd, SP_JOBEXP); + clif_updatestatus(sd, SP_NEXTJOBEXP); + clif_updatestatus(sd, SP_SKILLPOINT); + status_calc_pc(sd, 0); return 0; } @@ -2308,7 +2308,7 @@ ACMD_FUNC(go) clif_displaymessage(fd, msg_txt(248)); return -1; } - if (pc_setpos(sd, mapindex_name2id(data[town].map), data[town].x, data[town].y, 3) == 0) { + if (pc_setpos(sd, mapindex_name2id(data[town].map), data[town].x, data[town].y, CLR_TELEPORT) == 0) { clif_displaymessage(fd, msg_txt(0)); // Warped. } else { clif_displaymessage(fd, msg_txt(1)); // Map not found. @@ -3329,7 +3329,7 @@ ACMD_FUNC(recall) clif_displaymessage(fd, "You are not authorized to warp this player from its actual map."); return -1; } - pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2); + pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); sprintf(atcmd_output, msg_txt(46), pl_sd->status.name); // %s recalled! clif_displaymessage(fd, atcmd_output); @@ -3988,7 +3988,7 @@ ACMD_FUNC(recallall) pc_setstand(pl_sd); pc_setrestartvalue(pl_sd,1); } - pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2); + pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); } } } @@ -4047,7 +4047,7 @@ ACMD_FUNC(guildrecall) if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) count++; else - pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2); + pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); } } mapit_free(iter); @@ -4106,7 +4106,7 @@ ACMD_FUNC(partyrecall) if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) count++; else - pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, 2); + pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); } } mapit_free(iter); @@ -4704,7 +4704,7 @@ ACMD_FUNC(tonpc) } if ((nd = npc_name2id(npcname)) != NULL) { - if (pc_setpos(sd, map_id2index(nd->bl.m), nd->bl.x, nd->bl.y, 3) == 0) + if (pc_setpos(sd, map_id2index(nd->bl.m), nd->bl.x, nd->bl.y, CLR_TELEPORT) == 0) clif_displaymessage(fd, msg_txt(0)); // Warped. else return -1; @@ -5786,7 +5786,7 @@ ACMD_FUNC(skilltree) ent = &skill_tree[c][j]; meets = 1; - for(j=0;j<5;j++) + for(j=0;j<MAX_PC_SKILL_REQUIRE;j++) { if( ent->need[j].id && pc_checkskill(sd,ent->need[j].id) < ent->need[j].lv) { @@ -6352,7 +6352,7 @@ ACMD_FUNC(mobsearch) clif_displaymessage(fd, atcmd_output); it = mapit_geteachmob(); - while( true ) + for(;;) { TBL_MOB* md = (TBL_MOB*)mapit_next(it); if( md == NULL ) @@ -6502,7 +6502,7 @@ ACMD_FUNC(users) // count users on each map iter = mapit_getallusers(); - while( true ) + for(;;) { struct map_session_data* sd2 = (struct map_session_data*)mapit_next(iter); if( sd2 == NULL ) @@ -6585,7 +6585,7 @@ ACMD_FUNC(summon) md->master_id=sd->bl.id; md->special_state.ai=1; md->deletetimer=add_timer(tick+(duration*60000),mob_timer_delete,md->bl.id,0); - clif_misceffect2(&md->bl,344); + clif_specialeffect(&md->bl,344,AREA); mob_spawn(md); sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,md->bl.x,md->bl.y,tick); @@ -6862,26 +6862,36 @@ ACMD_FUNC(identify) *------------------------------------------*/ ACMD_FUNC(gmotd) { - char buf[CHAT_SIZE_MAX]; - FILE *fp; - nullpo_retr(-1, sd); - if((fp = fopen(motd_txt, "r"))!=NULL){ - while(fgets(buf, sizeof(buf), fp) != NULL) + char buf[CHAT_SIZE_MAX]; + size_t len; + FILE* fp; + + if( ( fp = fopen(motd_txt, "r") ) != NULL ) + { + while( fgets(buf, sizeof(buf), fp) ) + { + if( buf[0] == '/' && buf[1] == '/' ) { - int i; - if (buf[0] == '/' && buf[1] == '/') - continue; - for(i=0; buf[i]; i++){ - if(buf[i]=='\r' || buf[i]=='\n'){ - buf[i]=0; - break; - } - } - intif_broadcast(buf, strlen(buf)+1, 0); + continue; + } + + len = strlen(buf); + + while( len && ( buf[len-1] == '\r' || buf[len-1] == '\n' ) ) + {// strip trailing EOL characters + len--; + } + + if( len ) + { + buf[len] = 0; + + intif_broadcast(buf, len+1, 0); } - fclose(fp); } - return 0; + fclose(fp); + } + return 0; } ACMD_FUNC(misceffect) @@ -7071,7 +7081,7 @@ ACMD_FUNC(showmobs) clif_displaymessage(fd, atcmd_output); it = mapit_geteachmob(); - while( true ) + for(;;) { TBL_MOB* md = (TBL_MOB*)mapit_next(it); if( md == NULL ) @@ -7124,7 +7134,7 @@ ACMD_FUNC(homlevel) } status_calc_homunculus(hd,0); status_percent_heal(&hd->bl, 100, 100); - clif_misceffect2(&hd->bl,568); + clif_specialeffect(&hd->bl,568,AREA); return 0; } @@ -7390,9 +7400,6 @@ ACMD_FUNC(homshuffle) *------------------------------------------*/ ACMD_FUNC(iteminfo) { - char *itype[12] = {"Potion/Food", "BUG!", "Usable", "Etc", "Weapon", "Protection", "Card", "Egg", "Pet Acessory", "BUG!", "Arrow"}; - //, "Lure/Scroll"}; No need, type 11 items are converted to type 2 upon loading [Skotlex] - struct item_data *item_data, *item_array[MAX_SEARCH]; int i, count = 1; @@ -7417,20 +7424,20 @@ ACMD_FUNC(iteminfo) item_data = item_array[i]; sprintf(atcmd_output, "Item: '%s'/'%s'[%d] (%d) Type: %s | Extra Effect: %s", item_data->name,item_data->jname,item_data->slot,item_data->nameid, - item_data->type < 12 ? itype[item_data->type] : "BUG!", + itemdb_typename(item_data->type), (item_data->script==NULL)? "None" : "With script" ); clif_displaymessage(fd, atcmd_output); - sprintf(atcmd_output, "NPC Buy:%dz%s, Sell:%dz%s | Weight: %.1f ", item_data->value_buy, item_data->flag.value_notdc ? "(No Discount!)":"", item_data->value_sell, item_data->flag.value_notoc ? "(No Overcharge!)":"", item_data->weight/10. ); + sprintf(atcmd_output, "NPC Buy:%dz, Sell:%dz | Weight: %.1f ", item_data->value_buy, item_data->value_sell, item_data->weight/10. ); clif_displaymessage(fd, atcmd_output); - if (item_data->maxchance == 10000) - strcpy(atcmd_output, " - Available in the shops only"); + if (item_data->maxchance == -1) + strcpy(atcmd_output, " - Available in the shops only."); else if (item_data->maxchance) sprintf(atcmd_output, " - Maximal monsters drop chance: %02.02f%%", (float)item_data->maxchance / 100 ); else - strcpy(atcmd_output, " - Monsters don't drop this item"); + strcpy(atcmd_output, " - Monsters don't drop this item."); clif_displaymessage(fd, atcmd_output); } @@ -7700,7 +7707,7 @@ ACMD_FUNC(size) size = atoi(message); if(sd->state.size) { sd->state.size=0; - pc_setpos(sd, sd->mapindex, sd->bl.x, sd->bl.y, 3); + pc_setpos(sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_TELEPORT); } if(size==1) { @@ -8499,58 +8506,68 @@ ACMD_FUNC(stats) ACMD_FUNC(delitem) { char item_name[100]; - int i, number = 0, item_id, item_position, count; - struct item_data *item_data; - char output[CHAT_SIZE_MAX]; + int nameid, amount = 0, total, idx; + struct item_data* id; nullpo_retr(-1, sd); - memset(item_name, '\0', sizeof(item_name)); - memset(output, '\0', sizeof(output)); + if( !message || !*message || ( sscanf(message, "\"%99[^\"]\" %d", item_name, &amount) < 2 && sscanf(message, "%99s %d", item_name, &amount) < 2 ) || amount < 1 ) + { + clif_displaymessage(fd, "Please, enter an item name/id, a quantity and a player name (usage: #delitem <player> <item_name_or_ID> <quantity>)."); + return -1; + } - if (!message || !*message || ( - sscanf(message, "\"%99[^\"]\" %d", item_name, &number) < 2 && - sscanf(message, "%s %d", item_name, &number) < 2 - ) || number < 1) { - clif_displaymessage(fd, "Please, enter an item name/id, a quantity and a player name (usage: #delitem <item_name_or_ID> <quantity> <player>)."); + if( ( id = itemdb_searchname(item_name) ) != NULL || ( id = itemdb_exists(atoi(item_name)) ) != NULL ) + { + nameid = id->nameid; + } + else + { + clif_displaymessage(fd, msg_txt(19)); // Invalid item ID or name. 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) { - item_position = pc_search_inventory(sd, item_id); - if (item_position >= 0) { - count = 0; - for(i = 0; i < number && item_position >= 0; i++) { + total = amount; - //Logs (A)dmins items [Lupus] - if(log_config.enable_logs&0x400) - log_pick_pc(sd, "A", sd->status.inventory[item_position].nameid, -1, &sd->status.inventory[item_position]); + // delete items + while( amount && ( idx = pc_search_inventory(sd, nameid) ) != -1 ) + { + int delamount = ( amount < sd->status.inventory[idx].amount ) ? amount : sd->status.inventory[idx].amount; - pc_delitem(sd, item_position, 1, 0, 0); - count++; - item_position = pc_search_inventory(sd, item_id); // for next loop - } - sprintf(output, msg_txt(113), count); // %d item(s) removed by a GM. - clif_displaymessage(sd->fd, output); - if (number == count) - sprintf(output, msg_txt(114), count); // %d item(s) removed from the player. - else - sprintf(output, msg_txt(115), count, count, number); // %d item(s) removed. Player had only %d on %d items. - clif_displaymessage(fd, output); - } else { - clif_displaymessage(fd, msg_txt(116)); // Character does not have the item. - return -1; + if( sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET ) + {// delete pet + intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2])); + } + + //Logs (A)dmins items [Lupus] + if( log_config.enable_logs&0x400 ) + { + log_pick_pc(sd, "A", nameid, -delamount, &sd->status.inventory[idx]); } + + pc_delitem(sd, idx, delamount, 0, 0); + + amount-= delamount; + } + + // notify target + sprintf(atcmd_output, msg_txt(113), total-amount); // %d item(s) removed by a GM. + clif_displaymessage(sd->fd, atcmd_output); + + // notify source + if( amount == total ) + { + clif_displaymessage(fd, msg_txt(116)); // Character does not have the item. + } + else if( amount ) + { + sprintf(atcmd_output, msg_txt(115), total-amount, total-amount, total); // %d item(s) removed. Player had only %d on %d items. + clif_displaymessage(fd, atcmd_output); } else { - clif_displaymessage(fd, msg_txt(19)); // Invalid item ID or name. - return -1; + sprintf(atcmd_output, msg_txt(114), total); // %d item(s) removed from the player. + clif_displaymessage(fd, atcmd_output); } return 0; diff --git a/src/map/battle.c b/src/map/battle.c index 5c6da69e3..32def23f3 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -1942,8 +1942,8 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if (sc && sc->data[SC_MIRACLE]) i = 2; //Star anger else - ARR_FIND(0, 3, i, t_class == sd->hate_mob[i]); - if (i < 3 && (skill=pc_checkskill(sd,sg_info[i].anger_id))) + ARR_FIND(0, MAX_PC_FEELHATE, i, t_class == sd->hate_mob[i]); + if (i < MAX_PC_FEELHATE && (skill=pc_checkskill(sd,sg_info[i].anger_id))) { skillratio = sd->status.base_level + sstatus->dex + sstatus->luk; if (i == 2) skillratio += sstatus->str; //Star Anger @@ -4004,6 +4004,8 @@ static const struct _battle_data { { "invincible.nodamage", &battle_config.invincible_nodamage, 0, 0, 1, }, { "mob_slave_keep_target", &battle_config.mob_slave_keep_target, 0, 0, 1, }, { "autospell_check_range", &battle_config.autospell_check_range, 0, 0, 1, }, + { "client_reshuffle_dice", &battle_config.client_reshuffle_dice, 0, 0, 1, }, + { "client_sort_storage", &battle_config.client_sort_storage, 0, 0, 1, }, // BattleGround Settings { "bg_update_interval", &battle_config.bg_update_interval, 1000, 100, INT_MAX, }, { "bg_short_attack_damage_rate", &battle_config.bg_short_damage_rate, 80, 0, INT_MAX, }, diff --git a/src/map/battle.h b/src/map/battle.h index 230a86007..3abc6b0a4 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -96,6 +96,13 @@ bool battle_check_range(struct block_list *src,struct block_list *bl,int range); void battle_consume_ammo(struct map_session_data* sd, int skill, int lv); // 設定 +#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 + extern struct Battle_Config { int warp_point_debug; @@ -471,6 +478,8 @@ extern struct Battle_Config int invincible_nodamage; int mob_slave_keep_target; int autospell_check_range; //Enable range check for autospell bonus. [L0ne_W0lf] + int client_reshuffle_dice; // Reshuffle /dice + int client_sort_storage; // [BattleGround Settings] int bg_update_interval; diff --git a/src/map/battleground.c b/src/map/battleground.c index fad22c38f..1c2a90d90 100644 --- a/src/map/battleground.c +++ b/src/map/battleground.c @@ -64,7 +64,7 @@ int bg_team_warp(int bg_id, unsigned short mapindex, short x, short y) struct battleground_data *bg = bg_team_search(bg_id); if( bg == NULL ) return 0; for( i = 0; i < MAX_BG_MEMBERS; i++ ) - if( bg->members[i].sd != NULL ) pc_setpos(bg->members[i].sd, mapindex, x, y, 3); + if( bg->members[i].sd != NULL ) pc_setpos(bg->members[i].sd, mapindex, x, y, CLR_TELEPORT); return 1; } @@ -145,7 +145,7 @@ int bg_member_respawn(struct map_session_data *sd) return 0; if( bg->mapindex == 0 ) return 0; // Respawn not handled by Core - pc_setpos(sd, bg->mapindex, bg->x, bg->y, 0); + pc_setpos(sd, bg->mapindex, bg->x, bg->y, CLR_OUTSIGHT); status_revive(&sd->bl, 1, 100); return 1; // Warped diff --git a/src/map/chrif.c b/src/map/chrif.c index 40e7ca8d1..a9634d2c2 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -74,7 +74,7 @@ static const int packet_len_table[0x3d] = { // U - used, F - free //2b13: Incoming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....' //2b14: Incoming, chrif_accountban -> 'not sure: kick the player with message XY' //2b15: FREE -//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....' +//2b16: Outgoing, chrif_ragsrvinfo -> 'sends base / job / drop rates ....' //2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline' //2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!' //2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online' @@ -1229,40 +1229,18 @@ int chrif_load_scdata(int fd) /*========================================== * Send rates and motd to char server [Wizputer] - * S 2b16 <base rate>.w <job rate>.w <drop rate>.w <motd len>.w <motd>.256B + * S 2b16 <base rate>.L <job rate>.L <drop rate>.L *------------------------------------------*/ - int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate) +int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate) { - char buf[256]; - FILE *fp; - int i; - chrif_check(-1); - WFIFOHEAD(char_fd, sizeof(buf) + 10); + WFIFOHEAD(char_fd,14); WFIFOW(char_fd,0) = 0x2b16; - WFIFOW(char_fd,2) = base_rate; - WFIFOW(char_fd,4) = job_rate; - WFIFOW(char_fd,6) = drop_rate; - WFIFOW(char_fd,8) = sizeof(buf) + 10; - - if ((fp = fopen(motd_txt, "r")) != NULL) { - if (fgets(buf, sizeof(buf), fp) != NULL) - { - for(i = 0; buf[i]; i++) { - if (buf[i] == '\r' || buf[i] == '\n') { - buf[i] = 0; - break; - } - } - memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); - } - fclose(fp); - } else { - memset(buf, 0, sizeof(buf)); //No data found, send empty packets? - memcpy(WFIFOP(char_fd,10), buf, sizeof(buf)); - } - WFIFOSET(char_fd,WFIFOW(char_fd,8)); + WFIFOL(char_fd,2) = base_rate; + WFIFOL(char_fd,6) = job_rate; + WFIFOL(char_fd,10) = drop_rate; + WFIFOSET(char_fd,14); return 0; } diff --git a/src/map/clif.c b/src/map/clif.c index e55a27108..c5796555a 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -168,9 +168,9 @@ static inline unsigned char clif_bl_type(struct block_list *bl) { case BL_ITEM: return 0x2; //ITEM_TYPE case BL_SKILL: return 0x3; //SKILL_TYPE case BL_CHAT: return 0x4; //UNKNOWN_TYPE - case BL_MOB: return pcdb_checkid(((TBL_MOB*)bl)->vd->class_)?0x0:0x5; //NPC_MOB_TYPE + case BL_MOB: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x5; //NPC_MOB_TYPE case BL_NPC: return 0x6; //NPC_EVT_TYPE - case BL_PET: return 0x7; //NPC_PET_TYPE + case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE case BL_HOM: return 0x8; //NPC_HOM_TYPE case BL_MER: return 0x9; //NPC_MERSOL_TYPE // case BL_ELEM: return 0xA; //NPC_ELEMENTAL_TYPE @@ -658,7 +658,7 @@ int clif_clearflooritem(struct flooritem_data *fitem, int fd) * 3 - teleported / logged out * fd : the target client *------------------------------------------*/ -int clif_clearunit_single(int id, uint8 type, int fd) +int clif_clearunit_single(int id, clr_type type, int fd) { WFIFOHEAD(fd, packet_len(0x80)); WFIFOW(fd,0) = 0x80; @@ -676,7 +676,7 @@ int clif_clearunit_single(int id, uint8 type, int fd) * 2 - respawned * 3 - teleported / logged out *------------------------------------------*/ -int clif_clearunit_area(struct block_list* bl, uint8 type) +int clif_clearunit_area(struct block_list* bl, clr_type type) { unsigned char buf[16]; @@ -686,7 +686,7 @@ int clif_clearunit_area(struct block_list* bl, uint8 type) WBUFL(buf,2) = bl->id; WBUFB(buf,6) = type; - clif_send(buf, packet_len(0x80), bl, type == 1 ? AREA : AREA_WOS); + clif_send(buf, packet_len(0x80), bl, type == CLR_DEAD ? AREA : AREA_WOS); if(disguised(bl)) { WBUFL(buf,2) = -bl->id; @@ -699,7 +699,7 @@ int clif_clearunit_area(struct block_list* bl, uint8 type) static int clif_clearunit_delayed_sub(int tid, unsigned int tick, int id, intptr data) { struct block_list *bl = (struct block_list *)data; - clif_clearunit_area(bl, 0); + clif_clearunit_area(bl, CLR_OUTSIGHT); aFree(bl); return 0; } @@ -1059,16 +1059,6 @@ static void clif_spiritball_single(int fd, struct map_session_data *sd) WFIFOSET(fd, packet_len(0x1e1)); } -// new and improved weather display [Valaris] -static void clif_weather_sub(int fd, int id, int type) -{ - WFIFOHEAD(fd,packet_len(0x1f3)); - WFIFOW(fd,0) = 0x1f3; - WFIFOL(fd,2) = id; - WFIFOL(fd,6) = type; - WFIFOSET(fd,packet_len(0x1f3)); -} - /*========================================== * *------------------------------------------*/ @@ -1086,24 +1076,24 @@ static void clif_weather_check(struct map_session_data *sd) || map[m].flag.clouds2) { if (map[m].flag.snow) - clif_weather_sub(fd, sd->bl.id, 162); + clif_specialeffect_single(&sd->bl, 162, fd); if (map[m].flag.clouds) - clif_weather_sub(fd, sd->bl.id, 233); + clif_specialeffect_single(&sd->bl, 233, fd); if (map[m].flag.clouds2) - clif_weather_sub(fd, sd->bl.id, 516); + clif_specialeffect_single(&sd->bl, 516, fd); if (map[m].flag.fog) - clif_weather_sub(fd, sd->bl.id, 515); + clif_specialeffect_single(&sd->bl, 515, fd); if (map[m].flag.fireworks) { - clif_weather_sub(fd, sd->bl.id, 297); - clif_weather_sub(fd, sd->bl.id, 299); - clif_weather_sub(fd, sd->bl.id, 301); + clif_specialeffect_single(&sd->bl, 297, fd); + clif_specialeffect_single(&sd->bl, 299, fd); + clif_specialeffect_single(&sd->bl, 301, fd); } if (map[m].flag.sakura) - clif_weather_sub(fd, sd->bl.id, 163); + clif_specialeffect_single(&sd->bl, 163, fd); if (map[m].flag.leaves) - clif_weather_sub(fd, sd->bl.id, 333); + clif_specialeffect_single(&sd->bl, 333, fd); if (map[m].flag.rain) - clif_weather_sub(fd, sd->bl.id, 161); + clif_specialeffect_single(&sd->bl, 161, fd); } } @@ -1280,13 +1270,14 @@ int clif_homskillinfoblock(struct map_session_data *sd) void clif_homskillup(struct map_session_data *sd, int skill_num) { //[orn] struct homun_data *hd; - int fd=sd->fd, skillid; - WFIFOHEAD(fd, packet_len(0x239)); + int fd, skillid; nullpo_retv(sd); skillid = skill_num - HM_SKILLBASE; + fd=sd->fd; hd=sd->hd; + WFIFOHEAD(fd, packet_len(0x239)); WFIFOW(fd,0) = 0x239; WFIFOW(fd,2) = skill_num; WFIFOW(fd,4) = hd->homunculus.hskill[skillid].lv; @@ -1468,9 +1459,10 @@ void clif_blown(struct block_list *bl) clif_slide(bl, bl->x, bl->y); } -/*========================================== - * - *------------------------------------------*/ +/// Visually moves(slides) a character to x,y. If the target cell +/// isn't walkable, the char doesn't move at all. If the char is +/// sitting it will stand up. +/// S 0088 <gid>.L <x>.W <y>.W void clif_fixpos(struct block_list *bl) { unsigned char buf[10]; @@ -1529,9 +1521,7 @@ int clif_buylist(struct map_session_data *sd, struct npc_data *nd) if( id == NULL ) continue; WFIFOL(fd, 4+c*11) = val; - if (!id->flag.value_notdc) - val = pc_modifybuyvalue(sd,val); - WFIFOL(fd, 8+c*11) = val; + WFIFOL(fd, 8+c*11) = pc_modifybuyvalue(sd,val); WFIFOB(fd,12+c*11) = itemtype(id->type); WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid; c++; @@ -1570,9 +1560,7 @@ int clif_selllist(struct map_session_data *sd) 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; + WFIFOL(fd,10+c*10)=pc_modifysellvalue(sd,val); c++; } } @@ -2166,7 +2154,7 @@ void clif_equiplist(struct map_session_data *sd) } } -void clif_storagelist(struct map_session_data* sd, struct storage_data* stor) +void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length) { struct item_data *id; int i,n,ne; @@ -2187,21 +2175,21 @@ void clif_storagelist(struct map_session_data* sd, struct storage_data* stor) const int cmd = 28; #endif - buf = (unsigned char*)aMallocA(MAX_STORAGE * s + 4); - bufe = (unsigned char*)aMallocA(MAX_STORAGE * cmd + 4); + buf = (unsigned char*)aMallocA(items_length * s + 4); + bufe = (unsigned char*)aMallocA(items_length * cmd + 4); - for( i = 0, n = 0, ne = 0; i < MAX_STORAGE; i++ ) + for( i = 0, n = 0, ne = 0; i < items_length; i++ ) { - if( stor->items[i].nameid <= 0 ) + if( items[i].nameid <= 0 ) continue; - id = itemdb_search(stor->items[i].nameid); + id = itemdb_search(items[i].nameid); if( !itemdb_isstackable2(id) ) { //Equippable WBUFW(bufe,ne*cmd+4)=i+1; - clif_item_sub(bufe, ne*cmd+6, &stor->items[i], id, id->equip); - clif_addcards(WBUFP(bufe, ne*cmd+16), &stor->items[i]); + clif_item_sub(bufe, ne*cmd+6, &items[i], id, id->equip); + clif_addcards(WBUFP(bufe, ne*cmd+16), &items[i]); #if PACKETVER >= 20071002 - WBUFL(bufe,ne*cmd+24)=stor->items[i].expire_time; + WBUFL(bufe,ne*cmd+24)=items[i].expire_time; WBUFW(bufe,ne*cmd+28)=0; //Unknown #endif ne++; @@ -2209,93 +2197,12 @@ void clif_storagelist(struct map_session_data* sd, struct storage_data* stor) else { //Stackable WBUFW(buf,n*s+4)=i+1; - clif_item_sub(buf, n*s+6, &stor->items[i], id,-1); + clif_item_sub(buf, n*s+6, &items[i], id,-1); #if PACKETVER >= 5 - clif_addcards(WBUFP(buf,n*s+14), &stor->items[i]); + clif_addcards(WBUFP(buf,n*s+14), &items[i]); #endif #if PACKETVER >= 20080102 - WBUFL(buf,n*s+22)=stor->items[i].expire_time; -#endif - n++; - } - } - if( n ) - { -#if PACKETVER < 5 - WBUFW(buf,0)=0xa5; -#elif PACKETVER < 20080102 - WBUFW(buf,0)=0x1f0; -#else - WBUFW(buf,0)=0x2ea; -#endif - WBUFW(buf,2)=4+n*s; - clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); - } - if( ne ) - { -#if PACKETVER < 20071002 - WBUFW(bufe,0)=0xa6; -#else - WBUFW(bufe,0)=0x2d1; -#endif - WBUFW(bufe,2)=4+ne*cmd; - clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF); - } - - if( buf ) aFree(buf); - if( bufe ) aFree(bufe); -} - -//Unified storage function which sends all of the storage (requires two packets, one for equipable items and one for stackable ones. [Skotlex] -void clif_guildstoragelist(struct map_session_data *sd,struct guild_storage *stor) -{ - struct item_data *id; - int i,n,ne; - unsigned char *buf; - unsigned char *bufe; -#if PACKETVER < 5 - const int s = 10; //Entry size. -#elif PACKETVER < 20080102 - const int s = 18; -#else - const int s = 22; -#endif -#if PACKETVER < 20071002 - const int cmd = 20; -#elif PACKETVER < 20100629 - const int cmd = 26; -#else - const int cmd = 28; -#endif - - buf = (unsigned char*)aMallocA(MAX_GUILD_STORAGE * s + 4); - bufe = (unsigned char*)aMallocA(MAX_GUILD_STORAGE * cmd + 4); - - for( i = 0, n = 0, ne = 0; i < MAX_GUILD_STORAGE; i++ ) - { - if( stor->storage_[i].nameid <= 0 ) - continue; - id = itemdb_search(stor->storage_[i].nameid); - if( !itemdb_isstackable2(id) ) - { //Equippable - WBUFW(bufe,ne*cmd+4)=i+1; - clif_item_sub(bufe, ne*cmd+6, &stor->storage_[i], id, id->equip); - clif_addcards(WBUFP(bufe, ne*cmd+16), &stor->storage_[i]); -#if PACKETVER >= 20071002 - WBUFL(bufe,ne*cmd+24)=stor->storage_[i].expire_time; - WBUFW(bufe,ne*cmd+28)=0; //Unknown -#endif - ne++; - } - else - { //Stackable - WBUFW(buf,n*s+4)=i+1; - clif_item_sub(buf, n*s+6, &stor->storage_[i], id,-1); -#if PACKETVER >= 5 - clif_addcards(WBUFP(buf,n*s+14), &stor->storage_[i]); -#endif -#if PACKETVER >= 20080102 - WBUFL(buf,n*s+22)=stor->storage_[i].expire_time; + WBUFL(buf,n*s+22)=items[i].expire_time; #endif n++; } @@ -3089,23 +2996,7 @@ int clif_misceffect(struct block_list* bl,int type) return 0; } -int clif_misceffect2(struct block_list *bl, int type) -{ - unsigned char buf[24]; - - nullpo_ret(bl); - - memset(buf, 0, packet_len(0x1f3)); - - WBUFW(buf,0) = 0x1f3; - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = type; - - clif_send(buf, packet_len(0x1f3), bl, AREA); - - return 0; -} /*========================================== * 表示オプション変更 *------------------------------------------*/ @@ -3226,11 +3117,13 @@ int clif_useitemack(struct map_session_data *sd,int index,int amount,int ok) return 0; } -/*========================================== - * Inform client whether chatroom creation was successful or not - * R 00d6 <fail>.B - *------------------------------------------*/ -void clif_createchat(struct map_session_data* sd, int fail) +/// Inform client whether chatroom creation was successful or not +/// R 00d6 <flag>.B +/// flag: +/// 0 = Room has been successfully created (opens chat room) +/// 1 = Room limit exceeded +/// 2 = Same room already exists +void clif_createchat(struct map_session_data* sd, int flag) { int fd; @@ -3239,7 +3132,7 @@ void clif_createchat(struct map_session_data* sd, int fail) fd = sd->fd; WFIFOHEAD(fd,packet_len(0xd6)); WFIFOW(fd,0) = 0xd6; - WFIFOB(fd,2) = fail; + WFIFOB(fd,2) = flag; WFIFOSET(fd,packet_len(0xd6)); } @@ -3327,10 +3220,18 @@ int clif_clearchat(struct chat_data *cd,int fd) return 0; } -/*========================================== - * - *------------------------------------------*/ -int clif_joinchatfail(struct map_session_data *sd,int fail) +/// Displays message (mostly) regarding join chat +/// failures +/// R 0x00da <flag>.B +/// flag: +/// 0 = The room is already full. +/// 1 = Incorrect password, please try again. +/// 2 = You have been kicked out of the room. +/// 4 = You don't have enough money. +/// 5 = You are not the required level. +/// 6 = Too high level for this job. +/// 7 = Not the suitable job for this type of work. +int clif_joinchatfail(struct map_session_data *sd,int flag) { int fd; @@ -3340,7 +3241,7 @@ int clif_joinchatfail(struct map_session_data *sd,int fail) WFIFOHEAD(fd,packet_len(0xda)); WFIFOW(fd,0) = 0xda; - WFIFOB(fd,2) = fail; + WFIFOB(fd,2) = flag; WFIFOSET(fd,packet_len(0xda)); return 0; @@ -3393,7 +3294,7 @@ int clif_addchat(struct chat_data* cd,struct map_session_data *sd) /*========================================== * Announce the new owner - * R 00e1 <index>.l <nick>.24B + * R 00e1 <owner flag>.l <nick>.24B *------------------------------------------*/ void clif_changechatowner(struct chat_data* cd, struct map_session_data* sd) { @@ -3405,6 +3306,8 @@ void clif_changechatowner(struct chat_data* cd, struct map_session_data* sd) //FIXME: this announces a swap between positions 0 and 1 (probably not what we want) [ultramage] //FIXME: aegis sends obviously incorrect packets; need to figure out what to send to display it correctly :X //TODO: is it just owner swap, or can it do general-purpose reordering? + // It's not position, but operator flag, everyone set to 1 gets chat + // operator menu (yes, that means a chat may host multiple operators) [Ai4rei] WBUFW(buf, 0) = 0xe1; WBUFL(buf, 2) = 1; @@ -3690,41 +3593,6 @@ void clif_updateguildstorageamount(struct map_session_data* sd, int amount) } /*========================================== - * - *------------------------------------------*/ -void clif_guildstorageitemadded(struct map_session_data* sd, struct item* i, int index, int amount) -{ - int view,fd; - unsigned char *buf; -#if PACKETVER < 20090603 - const int cmd = 0xf4; -#else - const int cmd = 0x1c4; -#endif - - nullpo_retv(sd); - nullpo_retv(i); - fd=sd->fd; - view = itemdb_viewid(i->nameid); - buf = WFIFOP(fd,0); - - WFIFOHEAD(fd,packet_len(cmd)); - WBUFW(buf, 0) = cmd; // Storage item added - WBUFW(buf, 2) = index+1; // index - WBUFL(buf, 4) = amount; // amount - WBUFW(buf, 8) = ( view > 0 ) ? view : i->nameid; // id -#if PACKETVER >= 20090603 - WBUFB(buf,10) = itemdb_type(i->nameid); //type - buf = WBUFP(buf,1); //Advance 1B -#endif - WBUFB(buf,10) = i->identify; //identify flag - WBUFB(buf,11) = i->attribute; // attribute - WBUFB(buf,12) = i->refine; //refine - clif_addcards(WBUFP(buf,13), i); - WFIFOSET(fd,packet_len(cmd)); -} - -/*========================================== * カプラ倉庫からアイテムを取り去る *------------------------------------------*/ void clif_storageitemremoved(struct map_session_data* sd, int index, int amount) @@ -3885,7 +3753,8 @@ static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int da /*========================================== * Sends a 'damage' packet (src performs action on dst) - * R 008a <src ID>.l <dst ID>.l <server tick>.l <src speed>.l <dst speed>.l <param1>.w <param2>.w <type>.B <param3>.w + * R 008a <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.W <div>.W <type>.B <damage2>.W + * R 02e1 <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.L <div>.W <type>.B <damage2>.L * * type=00 damage [param1: total damage, param2: div, param3: assassin dual-wield damage] * type=01 pick up item @@ -4131,12 +4000,11 @@ void clif_skill_delunit(struct skill_unit *unit) } /*========================================== - * Unknown... trap related? - * Sent when an object gets ankle-snared + * Sent when an object gets ankle-snared (ZC_SKILL_UPDATE) * Only affects units with class [139,153] client-side * R 01ac <object id>.l *------------------------------------------*/ -void clif_01ac(struct block_list* bl) +void clif_skillunit_update(struct block_list* bl) { unsigned char buf[6]; nullpo_retv(bl); @@ -4195,7 +4063,7 @@ int clif_outsight(struct block_list *bl,va_list ap) switch(bl->type){ case BL_PC: if (sd->vd.class_ != INVISIBLE_CLASS) - clif_clearunit_single(bl->id,0,tsd->fd); + clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd); if(sd->chatID){ struct chat_data *cd; cd=(struct chat_data*)map_id2bl(sd->chatID); @@ -4213,14 +4081,14 @@ int clif_outsight(struct block_list *bl,va_list ap) break; default: if ((vd=status_get_viewdata(bl)) && vd->class_ != INVISIBLE_CLASS) - clif_clearunit_single(bl->id,0,tsd->fd); + clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd); break; } } if (sd && sd->fd) { //sd is watching tbl go out of view. if ((vd=status_get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) - clif_clearunit_single(tbl->id,0,sd->fd); + clif_clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd); } return 0; } @@ -4414,6 +4282,15 @@ int clif_skillup(struct map_session_data *sd,int skill_num) /*========================================== * スキル詠唱エフェクトを送信する + * pl: + * 0 = Yellow cast aura + * 1 = Water elemental cast aura + * 2 = Earth elemental cast aura + * 3 = Fire elemental cast aura + * 4 = Wind elemental cast aura + * 5 = Poison elemental cast aura + * 6 = White cast aura + * ? = like 0 *------------------------------------------*/ int clif_skillcasting(struct block_list* bl, int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int pl, int casttime) @@ -7081,8 +6958,8 @@ int clif_guild_notice(struct map_session_data* sd, struct guild* g) WFIFOHEAD(fd,packet_len(0x16f)); WFIFOW(fd,0) = 0x16f; - memcpy(WFIFOP(fd,2), g->mes1, 60); - memcpy(WFIFOP(fd,62), g->mes2, 120); + memcpy(WFIFOP(fd,2), g->mes1, MAX_GUILDMES1); + memcpy(WFIFOP(fd,62), g->mes2, MAX_GUILDMES2); WFIFOSET(fd,packet_len(0x16f)); return 0; } @@ -7106,7 +6983,12 @@ int clif_guild_invite(struct map_session_data *sd,struct guild *g) return 0; } /*========================================== - * ギルドメンバ勧誘結果 + * Reply to invite request + * Flag: + * 0 = Already in guild. + * 1 = Offer rejected. + * 2 = Offer accepted. + * 3 = Guild full. *------------------------------------------*/ int clif_guild_inviteack(struct map_session_data *sd,int flag) { @@ -7285,7 +7167,11 @@ int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag) return 0; } /*========================================== - * ギルド敵対結果 + * Reply to opposition request + * Flag: + * 0 = Antagonist has been set. + * 1 = Guild has too many Antagonists. + * 2 = Already set as an Antagonist. *------------------------------------------*/ int clif_guild_oppositionack(struct map_session_data *sd,int flag) { @@ -7579,20 +7465,17 @@ int clif_wisall(struct map_session_data *sd,int type,int flag) /*========================================== * Play a BGM! [Rikter/Yommy] *------------------------------------------*/ -void clif_playBGM(struct map_session_data* sd, struct block_list* bl, const char* name) +void clif_playBGM(struct map_session_data* sd, const char* name) { int fd; nullpo_retv(sd); - nullpo_retv(bl); fd = sd->fd; WFIFOHEAD(fd,packet_len(0x7fe)); WFIFOW(fd,0) = 0x7fe; safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH); WFIFOSET(fd,packet_len(0x7fe)); - - return; } /*========================================== @@ -7742,7 +7625,7 @@ int clif_refresh(struct map_session_data *sd) if( pc_issit(sd) ) clif_sitting(&sd->bl); // FIXME: just send to self, not area if( pc_isdead(sd) ) //When you refresh, resend the death packet. - clif_clearunit_single(sd->bl.id,1,sd->fd); + clif_clearunit_single(sd->bl.id,CLR_DEAD,sd->fd); else clif_changed_dir(&sd->bl, SELF); @@ -7928,6 +7811,9 @@ int clif_charnameupdate (struct map_session_data *ssd) return 0; } +/// Visually moves(instant) a character to x,y. The char moves even +/// when the target cell isn't walkable. If the char is sitting it +/// stays that way. void clif_slide(struct block_list *bl, int x, int y) { unsigned char buf[10]; @@ -8014,21 +7900,58 @@ void clif_gospel_info(struct map_session_data *sd, int type) WFIFOSET(fd, packet_len(0x215)); } + +/// Multi purpose mission information packet (ZC_STARSKILL). +/// 0x20e <mapname>.24B <monster_id>.L <star>.B <result>.B +/// result: +/// 0 = Star Gladiator %s has designed <mapname>'s as the %s. +/// star: +/// 0 = Place of the Sun +/// 1 = Place of the Moon +/// 2 = Place of the Stars +/// 1 = Star Gladiator %s's %s: <mapname> +/// star: +/// 0 = Place of the Sun +/// 1 = Place of the Moon +/// 2 = Place of the Stars +/// 10 = Star Gladiator %s has designed <mapname>'s as the %s. +/// star: +/// 0 = Target of the Sun +/// 1 = Target of the Moon +/// 2 = Target of the Stars +/// 11 = Star Gladiator %s's %s: <mapname used as monster name> +/// star: +/// 0 = Monster of the Sun +/// 1 = Monster of the Moon +/// 2 = Monster of the Stars +/// 20 = [TaeKwon Mission] Target Monster : <mapname used as monster name> (<star>%) +/// 21 = [Taming Mission] Target Monster : <mapname used as monster name> +/// 22 = [Collector Rank] Target Item : <monster_id used as item id> +/// 30 = [Sun, Moon and Stars Angel] Designed places and monsters have been reset. +/// 40 = Target HP : <monster_id used as HP> +void clif_starskill(struct map_session_data* sd, const char* mapname, int monster_id, unsigned char star, unsigned char result) +{ + int fd = sd->fd; + + WFIFOHEAD(fd,packet_len(0x20e)); + WFIFOW(fd,0) = 0x20e; + safestrncpy((char*)WFIFOP(fd,2), mapname, NAME_LENGTH); + WFIFOL(fd,26) = monster_id; + WFIFOB(fd,30) = star; + WFIFOB(fd,31) = result; + WFIFOSET(fd,packet_len(0x20e)); +} + /*========================================== * Info about Star Glaldiator save map [Komurka] * type: 1: Information, 0: Map registered *------------------------------------------*/ void clif_feel_info(struct map_session_data* sd, unsigned char feel_level, unsigned char type) { - int fd=sd->fd; + char mapname[MAP_NAME_LENGTH_EXT]; - WFIFOHEAD(fd,packet_len(0x20e)); - WFIFOW(fd,0) = 0x20e; - mapindex_getmapname_ext(mapindex_id2name(sd->feel_map[feel_level].index), (char*)WFIFOP(fd,2)); - WFIFOL(fd,26) = sd->bl.id; - WFIFOB(fd,30) = feel_level; - WFIFOB(fd,31) = type?1:0; - WFIFOSET(fd,packet_len(0x20e)); + mapindex_getmapname_ext(mapindex_id2name(sd->feel_map[feel_level].index), mapname); + clif_starskill(sd, mapname, 0, feel_level, type ? 1 : 0); } /*========================================== @@ -8037,19 +7960,18 @@ void clif_feel_info(struct map_session_data* sd, unsigned char feel_level, unsig *------------------------------------------*/ void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type) { - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x20e)); - WFIFOW(fd,0)=0x20e; - if (pcdb_checkid(class_)) - strncpy((char*)WFIFOP(fd,2),job_name(class_), NAME_LENGTH); - else if (mobdb_checkid(class_)) - strncpy((char*)WFIFOP(fd,2),mob_db(class_)->jname, NAME_LENGTH); - else //Really shouldn't happen... - memset(WFIFOP(fd,2), 0, NAME_LENGTH); - WFIFOL(fd,26)=sd->bl.id; - WFIFOB(fd,30)=hate_level; - WFIFOB(fd,31)=type?10:11; //Register/Info - WFIFOSET(fd, packet_len(0x20e)); + if( pcdb_checkid(class_) ) + { + clif_starskill(sd, job_name(class_), class_, hate_level, type ? 10 : 11); + } + else if( mobdb_checkid(class_) ) + { + clif_starskill(sd, mob_db(class_)->jname, class_, hate_level, type ? 10 : 11); + } + else + { + ShowWarning("clif_hate_info: Received invalid class %d for this packet (char_id=%d, hate_level=%u, type=%u).\n", class_, sd->status.char_id, (unsigned int)hate_level, (unsigned int)type); + } } /*========================================== @@ -8057,14 +7979,7 @@ void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int cl *------------------------------------------*/ void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress) { - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x20e)); - WFIFOW(fd,0)=0x20e; - strncpy((char*)WFIFOP(fd,2),mob_db(mob_id)->jname, NAME_LENGTH); - WFIFOL(fd,26)=mob_id; - WFIFOB(fd,30)=progress; //Message to display - WFIFOB(fd,31)=20; - WFIFOSET(fd, packet_len(0x20e)); + clif_starskill(sd, mob_db(mob_id)->jname, mob_id, progress, 20); } /*========================================== @@ -8072,14 +7987,7 @@ void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char pr *------------------------------------------*/ void clif_feel_hate_reset(struct map_session_data *sd) { - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x20e)); - WFIFOW(fd,0)=0x20e; - memset(WFIFOP(fd,2), 0, NAME_LENGTH); //Blank name as all was reset. - WFIFOL(fd,26)=sd->bl.id; - WFIFOB(fd,30)=0; //Feel/hate level: irrelevant - WFIFOB(fd,31)=30; - WFIFOSET(fd, packet_len(0x20e)); + clif_starskill(sd, "", 0, 0, 30); } /*========================================== @@ -8175,12 +8083,9 @@ void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* ts WFIFOSET(fd, WFIFOW(fd, 2)); } -/*========================================== - * View player equip request denied - * R 0291 <message>.W - * TODO: this looks like a general-purpose packet to print msgstringtable entries. - *------------------------------------------*/ -void clif_viewequip_fail(struct map_session_data* sd) +/// Display msgstringtable.txt string (ZC_MSG) +/// R 0291 <message>.W +void clif_msg(struct map_session_data* sd, unsigned short id) { int fd; nullpo_retv(sd); @@ -8188,10 +8093,16 @@ void clif_viewequip_fail(struct map_session_data* sd) WFIFOHEAD(fd, packet_len(0x291)); WFIFOW(fd, 0) = 0x291; - WFIFOW(fd, 2) = 0x54d; // This controls which message is displayed. 0x54d is the correct one. Maybe it's used for something else too? + WFIFOW(fd, 2) = id; // zero-based msgstringtable.txt index WFIFOSET(fd, packet_len(0x291)); } +/// View player equip request denied +void clif_viewequip_fail(struct map_session_data* sd) +{ + clif_msg(sd, 0x54d); +} + /// Validates one global/guild/party/whisper message packet and tries to recognize its components. /// Returns true if the packet was parsed successfully. /// Formats: 0 - <packet id>.w <packet len>.w (<name> : <message>).?B 00 @@ -8329,6 +8240,10 @@ static int clif_guess_PacketVer(int fd, int get_previous, int *error) err = n;\ //define SET_ERROR + // FIXME: If the packet is not received at once, this will FAIL. + // Figure out, when it happens, that only part of the packet is + // received, or fix the function to be able to deal with that + // case. #define CHECK_PACKET_VER() \ if( cmd != clif_config.connect_cmd[packet_ver] || packet_len != packet_db[packet_ver][cmd].len )\ ;/* not wanttoconnection or wrong length */\ @@ -8561,20 +8476,22 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) // must use foreachinarea (CIRCULAR_AREA interferes with foreachinrange) map_foreachinarea(clif_getareachar, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_ALL, sd); - //TODO: merge it with the code below - if (battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m) && sd->pd) - { //Return the pet to egg. [Skotlex] - clif_displaymessage(sd->fd, "Pets are not allowed in Guild Wars."); - pet_menu(sd, 3); //Option 3 is return to egg. - } - // pet - if(sd->pd) { - map_addblock(&sd->pd->bl); - clif_spawn(&sd->pd->bl); - clif_send_petdata(sd,sd->pd,0,0); - clif_send_petstatus(sd); -// skill_unit_move(&sd->pd->bl,gettick(),1); + if( sd->pd ) + { + if( battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m) ) + { //Return the pet to egg. [Skotlex] + clif_displaymessage(sd->fd, "Pets are not allowed in Guild Wars."); + pet_menu(sd, 3); //Option 3 is return to egg. + } + else + { + map_addblock(&sd->pd->bl); + clif_spawn(&sd->pd->bl); + clif_send_petdata(sd,sd->pd,0,0); + clif_send_petstatus(sd); +// skill_unit_move(&sd->pd->bl,gettick(),1); + } } //homunculus [blackhole89] @@ -8605,7 +8522,9 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) sd->state.connect_new = 0; clif_skillinfoblock(sd); clif_hotkeys_send(sd); + clif_updatestatus(sd,SP_BASEEXP); clif_updatestatus(sd,SP_NEXTBASEEXP); + clif_updatestatus(sd,SP_JOBEXP); clif_updatestatus(sd,SP_NEXTJOBEXP); clif_updatestatus(sd,SP_SKILLPOINT); clif_initialstatus(sd); @@ -8720,7 +8639,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris] if(pc_isdead(sd)) - clif_clearunit_area(&sd->bl, 1); + clif_clearunit_area(&sd->bl, CLR_DEAD); // Uncomment if you want to make player face in the same direction he was facing right before warping. [Skotlex] // else // clif_changed_dir(&sd->bl, SELF); @@ -8821,7 +8740,7 @@ void clif_parse_WalkToXY(int fd, struct map_session_data *sd) int cmd; if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, 1); + clif_clearunit_area(&sd->bl, CLR_DEAD); return; } @@ -8940,6 +8859,7 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd) WFIFOW(fd,2) = 8 + textlen; WFIFOL(fd,4) = sd->bl.id; safestrncpy((char*)WFIFOP(fd,8), text, textlen); + //FIXME: chat has range of 9 only clif_send(WFIFOP(fd,0), WFIFOW(fd,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC); // send back message to the speaker @@ -9063,10 +8983,10 @@ void clif_parse_ChangeDir(int fd, struct map_session_data *sd) *------------------------------------------*/ void clif_parse_Emotion(int fd, struct map_session_data *sd) { - unsigned char buf[64]; + int emoticon = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) { - if (RFIFOB(fd,2) == 34) {// prevent use of the mute emote [Valaris] + if (emoticon == E_MUTE) {// prevent use of the mute emote [Valaris] clif_skill_fail(sd, 1, 0, 1); return; } @@ -9077,11 +8997,13 @@ void clif_parse_Emotion(int fd, struct map_session_data *sd) return; } sd->emotionlasttime = time(NULL) + 1; // not more than 1 per second (using /commands the client can spam it) - - WBUFW(buf,0) = 0xc0; - WBUFL(buf,2) = sd->bl.id; - WBUFB(buf,6) = RFIFOB(fd,2); - clif_send(buf, packet_len(0xc0), &sd->bl, AREA); + + if(battle_config.client_reshuffle_dice && emoticon>=E_DICE1 && emoticon<=E_DICE6) + {// re-roll dice + emoticon = rand()%6+E_DICE1; + } + + clif_emotion(&sd->bl, emoticon); } else clif_skill_fail(sd, 1, 0, 1); } @@ -9100,7 +9022,7 @@ void clif_parse_HowManyConnections(int fd, struct map_session_data *sd) void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick) { if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, 1); + clif_clearunit_area(&sd->bl, CLR_DEAD); return; } @@ -9198,7 +9120,7 @@ void clif_parse_Restart(int fd, struct map_session_data *sd) { switch(RFIFOB(fd,2)) { case 0x00: - pc_respawn(sd,2); + pc_respawn(sd,CLR_RESPAWN); break; case 0x01: /* Rovert's Prevent logout option - Fixed [Valaris] */ @@ -9219,7 +9141,7 @@ void clif_parse_Restart(int fd, struct map_session_data *sd) /*========================================== * Validates and processes whispered messages - * S 0096 <packet len>.w <nick>.23B 00 <message>.?B + * S 0096 <packet len>.w <nick>.24B <message>.?B *------------------------------------------*/ void clif_parse_WisMessage(int fd, struct map_session_data* sd) { @@ -9416,7 +9338,7 @@ void clif_parse_TakeItem(int fd, struct map_session_data *sd) do { if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, 1); + clif_clearunit_area(&sd->bl, CLR_DEAD); break; } @@ -9451,7 +9373,8 @@ void clif_parse_DropItem(int fd, struct map_session_data *sd) { int item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2; int item_amount = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]); - do { + + for(;;) { if (pc_isdead(sd)) break; @@ -9469,7 +9392,7 @@ void clif_parse_DropItem(int fd, struct map_session_data *sd) break; return; - } while (0); + } //Because the client does not like being ignored. clif_dropitem(sd, item_index,0); @@ -9483,7 +9406,7 @@ void clif_parse_UseItem(int fd, struct map_session_data *sd) int n; if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, 1); + clif_clearunit_area(&sd->bl, CLR_DEAD); return; } @@ -9516,7 +9439,7 @@ void clif_parse_EquipItem(int fd,struct map_session_data *sd) int index; if(pc_isdead(sd)) { - clif_clearunit_area(&sd->bl,1); + clif_clearunit_area(&sd->bl,CLR_DEAD); return; } index = RFIFOW(fd,2)-2; @@ -9559,7 +9482,7 @@ void clif_parse_UnequipItem(int fd,struct map_session_data *sd) int index; if(pc_isdead(sd)) { - clif_clearunit_area(&sd->bl,1); + clif_clearunit_area(&sd->bl,CLR_DEAD); return; } @@ -9581,7 +9504,7 @@ void clif_parse_NpcClicked(int fd,struct map_session_data *sd) struct block_list *bl; if(pc_isdead(sd)) { - clif_clearunit_area(&sd->bl,1); + clif_clearunit_area(&sd->bl,CLR_DEAD); return; } @@ -9592,8 +9515,6 @@ void clif_parse_NpcClicked(int fd,struct map_session_data *sd) if (!bl) return; switch (bl->type) { case BL_MOB: - clif_parse_ActionRequest_sub(sd, 0x07, bl->id, gettick()); - break; case BL_PC: clif_parse_ActionRequest_sub(sd, 0x07, bl->id, gettick()); break; @@ -10260,6 +10181,11 @@ void clif_parse_Cooking(int fd,struct map_session_data *sd) //int type = RFIFOW(fd,2); // '1' for cooking, but what do other values mean? int nameid = RFIFOW(fd,4); + if( sd->menuskill_id != AM_PHARMACY ) + { + return; + } + if (pc_istrading(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif_skill_fail(sd,sd->ud.skillid,0,0); @@ -10379,9 +10305,15 @@ void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd) *------------------------------------------*/ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) { + short idx = RFIFOW(fd,2); + if (sd->menuskill_id != MC_IDENTIFY) return; - skill_identify(sd,RFIFOW(fd,2)-2); + if( idx == -1 ) + {// cancel pressed + return; + } + skill_identify(sd,idx-2); sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== @@ -10585,7 +10517,7 @@ void clif_parse_StoragePassword(int fd, struct map_session_data *sd) /*========================================== * Party creation request * S 00f9 <party name>.24S - * S 01e8 <party name>.24S <item1>.B <item2>.B + * S 01e8 <party name>.24S <share flag>.B <share type>.B *------------------------------------------*/ void clif_parse_CreateParty(int fd, struct map_session_data *sd) { @@ -10796,10 +10728,10 @@ void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data* sd) { short level = RFIFOW(fd,2); short mapid = RFIFOW(fd,4); - short job[6]; + short job[PARTY_BOOKING_JOBS]; int i; - for(i=0; i<6; i++) + for(i=0; i<PARTY_BOOKING_JOBS; i++) job[i] = RFIFOB(fd,6+i*2); party_booking_register(sd, level, mapid, job); @@ -10832,7 +10764,7 @@ void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data* sd) /*========================================== * more_result: 0 - no more, 1 - more *------------------------------------------*/ -void clif_PartyBookingSearchAck(int fd, unsigned long *index, int count, bool more_result) +void clif_PartyBookingSearchAck(int fd, struct party_booking_ad_info** results, int count, bool more_result) { int i, j; int size = sizeof(struct party_booking_ad_info); // structure size (48) @@ -10840,17 +10772,16 @@ void clif_PartyBookingSearchAck(int fd, unsigned long *index, int count, bool mo WFIFOHEAD(fd,size*count + 5); WFIFOW(fd,0) = 0x805; WFIFOW(fd,2) = size*count + 5; - WFIFOB(fd,4) = (bool)more_result; + WFIFOB(fd,4) = more_result; for(i=0; i<count; i++) { - pb_ad = party_booking_getdata(index[i]); - if(pb_ad == NULL) return; + pb_ad = results[i]; WFIFOL(fd,i*size+5) = pb_ad->index; memcpy(WFIFOP(fd,i*size+9),pb_ad->charname,NAME_LENGTH); WFIFOL(fd,i*size+33) = pb_ad->starttime; WFIFOW(fd,i*size+37) = pb_ad->p_detail.level; WFIFOW(fd,i*size+39) = pb_ad->p_detail.mapid; - for(j=0; j<6; j++) + for(j=0; j<PARTY_BOOKING_JOBS; j++) WFIFOW(fd,i*size+41+j*2) = pb_ad->p_detail.job[j]; } WFIFOSET(fd,WFIFOW(fd,2)); @@ -10858,7 +10789,7 @@ void clif_PartyBookingSearchAck(int fd, unsigned long *index, int count, bool mo void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data* sd) { - if(party_booking_delete(sd, false)) + if(party_booking_delete(sd)) clif_PartyBookingDeleteAck(sd, 0); } @@ -10877,10 +10808,10 @@ void clif_PartyBookingDeleteAck(struct map_session_data* sd, int flag) void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data* sd) { - short job[6]; + short job[PARTY_BOOKING_JOBS]; int i; - for(i=0; i<6; i++) + for(i=0; i<PARTY_BOOKING_JOBS; i++) job[i] = RFIFOW(fd,2+i*2); party_booking_update(sd, job); @@ -10889,32 +10820,32 @@ void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data* sd) void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad) { int i; + uint8 buf[38+PARTY_BOOKING_JOBS*2]; if(pb_ad == NULL) return; - WFIFOHEAD(sd->fd,packet_len(0x809)); - WFIFOW(sd->fd,0) = 0x809; - WFIFOL(sd->fd,2) = pb_ad->index; - memcpy(WFIFOP(sd->fd,6),pb_ad->charname,NAME_LENGTH); - WFIFOL(sd->fd,30) = pb_ad->starttime; - WFIFOW(sd->fd,34) = pb_ad->p_detail.level; - WFIFOW(sd->fd,36) = pb_ad->p_detail.mapid; - for(i=0; i<6; i++) - WFIFOW(sd->fd,38+i*2) = pb_ad->p_detail.job[i]; + WBUFW(buf,0) = 0x809; + WBUFL(buf,2) = pb_ad->index; + memcpy(WBUFP(buf,6),pb_ad->charname,NAME_LENGTH); + WBUFL(buf,30) = pb_ad->starttime; + WBUFW(buf,34) = pb_ad->p_detail.level; + WBUFW(buf,36) = pb_ad->p_detail.mapid; + for(i=0; i<PARTY_BOOKING_JOBS; i++) + WBUFW(buf,38+i*2) = pb_ad->p_detail.job[i]; - WFIFOSET(sd->fd,packet_len(0x809)); + clif_send(buf, packet_len(0x809), &sd->bl, ALL_CLIENT); } void clif_PartyBookingUpdateNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad) { int i; - uint8 buf[18]; + uint8 buf[6+PARTY_BOOKING_JOBS*2]; if(pb_ad == NULL) return; WBUFW(buf,0) = 0x80a; WBUFL(buf,2) = pb_ad->index; - for(i=0; i<6; i++) + for(i=0; i<PARTY_BOOKING_JOBS; i++) WBUFW(buf,6+i*2) = pb_ad->p_detail.job[i]; clif_send(buf,packet_len(0x80a),&sd->bl,ALL_CLIENT); // Now UPDATE all client. } @@ -10941,10 +10872,11 @@ void clif_parse_CloseVending(int fd, struct map_session_data* sd) *------------------------------------------*/ void clif_parse_VendingListReq(int fd, struct map_session_data* sd) { - vending_vendinglistreq(sd,RFIFOL(fd,2)); - if( sd->npc_id ) - npc_event_dequeue(sd); + {// using an NPC + return; + } + vending_vendinglistreq(sd,RFIFOL(fd,2)); } /*========================================== @@ -11075,6 +11007,7 @@ void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd) /*========================================== * ギルドメンバ役職変更 + * S 0155 <packet len>.W {<account id>.L <char id>.L <idx>.L} *------------------------------------------*/ void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd) { @@ -11103,6 +11036,7 @@ void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd) /*========================================== * ギルドエンブレム変更 + * S 0153 <packet len>.W <emblem data>.?B *------------------------------------------*/ void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd) { @@ -11128,7 +11062,7 @@ void clif_parse_GuildChangeNotice(int fd, struct map_session_data* sd) // compensate for some client defects when using multilanguage mode if (msg1[0] == '|' && msg1[3] == '|') msg1+= 3; // skip duplicate marker if (msg2[0] == '|' && msg2[3] == '|') msg2+= 3; // skip duplicate marker - if (msg2[0] == '|') msg2[strnlen(msg2, 120)-1] = '\0'; // delete extra space at the end of string + if (msg2[0] == '|') msg2[strnlen(msg2, MAX_GUILDMES2)-1] = '\0'; // delete extra space at the end of string guild_change_notice(sd, guild_id, msg1, msg2); } @@ -11186,7 +11120,7 @@ void clif_parse_GuildLeave(int fd,struct map_session_data *sd) /*========================================== * Request to expel a member of a guild - * S 015b <guild_id>.L <account_id>.L <char_id>.L <reason>.39B 00 + * S 015b <guild_id>.L <account_id>.L <char_id>.L <reason>.40B *------------------------------------------*/ void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd) { @@ -11458,10 +11392,11 @@ void clif_parse_GMKickAll(int fd, struct map_session_data* sd) } /*========================================== - * /shift <name> + * /remove <account name> + * /shift <char name> *------------------------------------------*/ void clif_parse_GMShift(int fd, struct map_session_data *sd) -{ +{// FIXME: remove is supposed to receive account name for clients prior 20100803RE char *player_name; int lv; @@ -11480,11 +11415,47 @@ void clif_parse_GMShift(int fd, struct map_session_data *sd) } } + +/// Warps oneself to the location of the character given by account id ( /remove <account id> ). +/// R 0843 <account id>.L +void clif_parse_GMRemove2(int fd, struct map_session_data* sd) +{ + int account_id, lv; + struct map_session_data* pl_sd; + + if( battle_config.atc_gmonly && !pc_isGM(sd) ) + { + return; + } + + if( pc_isGM(sd) < ( lv = get_atcommand_level(atcommand_jumpto) ) ) + { + return; + } + + account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); + + if( ( pl_sd = map_id2sd(account_id) ) != NULL && pc_isGM(sd) >= pc_isGM(pl_sd) ) + { + pc_warpto(sd, pl_sd); + } + + if( log_config.gm && lv >= log_config.gm ) + { + char message[32]; + + sprintf(message, "/remove %d", account_id); + log_atcommand(sd, message); + } +} + + /*========================================== - * /recall <name> + * /recall <account name> + * /summon <char name> *------------------------------------------*/ void clif_parse_GMRecall(int fd, struct map_session_data *sd) -{ +{// FIXME: recall is supposed to receive account name for clients prior 20100803RE char *player_name; int lv; @@ -11504,8 +11475,44 @@ void clif_parse_GMRecall(int fd, struct map_session_data *sd) } } + +/// Summons a character given by account id to one's own position ( /recall <account id> ) +/// R 0842 <account id>.L +void clif_parse_GMRecall2(int fd, struct map_session_data* sd) +{ + int account_id, lv; + struct map_session_data* pl_sd; + + if( battle_config.atc_gmonly && !pc_isGM(sd) ) + { + return; + } + + if( pc_isGM(sd) < ( lv = get_atcommand_level(atcommand_recall) ) ) + { + return; + } + + account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); + + if( ( pl_sd = map_id2sd(account_id) ) != NULL && pc_isGM(sd) >= pc_isGM(pl_sd) ) + { + pc_recall(sd, pl_sd); + } + + if( log_config.gm && lv >= log_config.gm ) + { + char message[32]; + + sprintf(message, "/recall %d", account_id); + log_atcommand(sd, message); + } +} + + /*========================================== * /monster /item + * R 01F3 <name>.24B *------------------------------------------*/ void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) { @@ -11663,11 +11670,11 @@ void clif_parse_GMReqAccountName(int fd, struct map_session_data *sd) /*========================================== * GM single cell type change request - * /changemaptype <x> <y> <type> + * /changemaptype <x> <y> <type:0-1> * S 0198 <x>.W <y>.W <gat>.W *------------------------------------------*/ void clif_parse_GMChangeMapType(int fd, struct map_session_data *sd) -{ +{// FIXME: type sent by client is 0 or 1 (even if you enter 2+); that suggests, that it is walkable gat attribute int x,y,type; if( battle_config.atc_gmonly && !pc_isGM(sd) ) @@ -12107,6 +12114,7 @@ void clif_parse_PVPInfo(int fd,struct map_session_data *sd) /*========================================== * /blacksmith + * S 0217 *------------------------------------------*/ void clif_parse_Blacksmith(int fd,struct map_session_data *sd) { @@ -12150,6 +12158,7 @@ int clif_fame_blacksmith(struct map_session_data *sd, int points) /*========================================== * /alchemist + * S 0218 *------------------------------------------*/ void clif_parse_Alchemist(int fd,struct map_session_data *sd) { @@ -12193,6 +12202,7 @@ int clif_fame_alchemist(struct map_session_data *sd, int points) /*========================================== * /taekwon + * S 0225 *------------------------------------------*/ void clif_parse_Taekwon(int fd,struct map_session_data *sd) { @@ -12234,7 +12244,8 @@ int clif_fame_taekwon(struct map_session_data *sd, int points) } /*========================================== - * PK Ranking table? + * /pk + * S 0237 *------------------------------------------*/ void clif_parse_RankingPk(int fd,struct map_session_data *sd) { @@ -12259,7 +12270,7 @@ void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd) if (sd->menuskill_id != SG_FEEL) return; i = sd->menuskill_val-1; - if (i<0 || i > 2) return; //Bug? + if (i<0 || i >= MAX_PC_FEELHATE) return; //Bug? sd->feel_map[i].index = map_id2index(sd->bl.m); sd->feel_map[i].m = sd->bl.m; @@ -12738,9 +12749,10 @@ void clif_parse_Mail_setattach(int fd, struct map_session_data *sd) /*------------------------------------------ * Mail Window Operation + * S 0246 <flag>.W * 0 : Switch to 'new mail' window, or Close mailbox * 1 : ??? - * 2 : ??? + * 2 : Zeny entering start *------------------------------------------*/ void clif_parse_Mail_winopen(int fd, struct map_session_data *sd) { @@ -13592,12 +13604,9 @@ void clif_parse_mercenary_action(int fd, struct map_session_data* sd) * 2 = Your mercenary soldier has been fired. * 3 = Your mercenary soldier has ran away. *------------------------------------------*/ -void clif_mercenary_message(int fd, int message) +void clif_mercenary_message(struct map_session_data* sd, int message) { - WFIFOHEAD(fd,4); - WFIFOW(fd,0) = 0x0291; - WFIFOW(fd,2) = 1266 + message; - WFIFOSET(fd,4); + clif_msg(sd, 1266 + message); } /*------------------------------------------ @@ -13959,6 +13968,23 @@ void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, b return; } +/// Displays digital clock digits on top of the screen (ZC_SHOWDIGIT). +/// type: +/// 0: Displays 'value' for 5 seconds. +/// 1: Incremental counter (1 tick/second), negated 'value' specifies start value (e.g. using -10 lets the counter start at 10). +/// 2: Decremental counter (1 tick/second), negated 'value' specifies start value (does not stop when reaching 0, but overflows). +/// 3: Decremental counter (2 ticks/second), 'value' specifies start value (stops when reaching 0, displays at most 2 digits). +/// value: +/// Except for type 3 it is interpreted as seconds for displaying as DD:HH:MM:SS, HH:MM:SS, MM:SS or SS (leftmost '00' is not displayed). +void clif_showdigit(struct map_session_data* sd, unsigned char type, int value) +{ + WFIFOHEAD(sd->fd, packet_len(0x1b1)); + WFIFOW(sd->fd,0) = 0x1b1; + WFIFOB(sd->fd,0) = type; + WFIFOL(sd->fd,0) = value; + WFIFOSET(sd->fd, packet_len(0x1b1)); +} + /*========================================== * パケットデバッグ *------------------------------------------*/ @@ -13987,10 +14013,10 @@ int clif_parse(int fd) TBL_PC* sd; int pnum; - //TODO apply deplays or disconnect based on packet throughput [FlavioJS] + //TODO apply delays or disconnect based on packet throughput [FlavioJS] // Note: "click masters" can do 80+ clicks in 10 seconds - for( pnum = 0; pnum < 3; ++pnum )// Limit max packets per cycle to 3 (delay packet spammers) [FlavioJS] + for( pnum = 0; pnum < 3; ++pnum )// Limit max packets per cycle to 3 (delay packet spammers) [FlavioJS] -- This actually aids packet spammers, but stuff like /str+ gets slow without it [Ai4rei] { // begin main client packet processing loop sd = (TBL_PC *)session[fd]->session_data; @@ -14491,6 +14517,8 @@ static int packetdb_readdb(void) {clif_parse_GMShift,"shift"}, {clif_parse_GMChangeMapType,"changemaptype"}, {clif_parse_GMRc,"rc"}, + {clif_parse_GMRecall2,"recall2"}, + {clif_parse_GMRemove2,"remove2"}, {clif_parse_NoviceDoriDori,"sndoridori"}, {clif_parse_NoviceExplosionSpirits,"snexplosionspirits"}, diff --git a/src/map/clif.h b/src/map/clif.h index d3a3679f1..ded8ece30 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -78,6 +78,101 @@ typedef enum send_target { BG_AREA_WOS, } send_target; +typedef enum emotion_type +{ + E_GASP = 0, // /! + E_WHAT, // /? + E_HO, + E_LV, + E_SWT, + E_IC, + E_AN, + E_AG, + E_CASH, // /$ + E_DOTS, // /... + E_SCISSORS, // /gawi --- 10 + E_ROCK, // /bawi + E_PAPER, // /bo + E_KOREA, + E_LV2, + E_THX, + E_WAH, + E_SRY, + E_HEH, + E_SWT2, + E_HMM, // --- 20 + E_NO1, + E_NO, // /?? + E_OMG, + E_OH, + E_X, + E_HLP, + E_GO, + E_SOB, + E_GG, + E_KIS, // --- 30 + E_KIS2, + E_PIF, + E_OK, + E_MUTE, // red /... used for muted characters + E_INDONESIA, + E_BZZ, // /bzz, /stare + E_RICE, + E_AWSM, // /awsm, /cool + E_MEH, + E_SHY, // --- 40 + E_PAT, // /pat, /goodboy + E_MP, // /mp, /sptime + E_SLUR, + E_COM, // /com, /comeon + E_YAWN, // /yawn, /sleepy + E_GRAT, // /grat, /congrats + E_HP, // /hp, /hptime + E_PHILIPPINES, + E_MALAYSIA, + E_SINGAPORE, // --- 50 + E_BRAZIL, + E_FLASH, // /fsh + E_SPIN, // /spin + E_SIGH, + E_PROUD, // /dum + E_LOUD, // /crwd + E_OHNOES, // /desp, /otl + E_DICE1, + E_DICE2, + E_DICE3, // --- 60 + E_DICE4, + E_DICE5, + E_DICE6, + E_INDIA, + E_LOOSER, + E_RUSSIA, + E_VIRGIN, + E_PHONE, + E_MAIL, + E_CHINESE, // --- 70 + E_SIGNAL, + E_SIGNAL2, + E_SIGNAL3, + E_HUM, + E_ABS, + E_OOPS, + E_SPIT, + E_ENE, + E_PANIC, + E_WHISP, // --- 80 + // + E_MAX +} emotion_type; + +typedef enum clr_type +{ + CLR_OUTSIGHT = 0, + CLR_DEAD, + CLR_RESPAWN, + CLR_TELEPORT, +} clr_type; + int clif_setip(const char* ip); void clif_setbindip(const char* ip); void clif_setport(uint16 port); @@ -92,8 +187,8 @@ int clif_charselectok(int); int clif_dropflooritem(struct flooritem_data *); int clif_clearflooritem(struct flooritem_data *,int); -int clif_clearunit_single(int id, uint8 type, int fd); -int clif_clearunit_area(struct block_list* bl, uint8 type); +int clif_clearunit_single(int id, clr_type type, int fd); +int clif_clearunit_area(struct block_list* bl, clr_type type); int clif_clearunit_delayed(struct block_list* bl, unsigned int tick); int clif_spawn(struct block_list*); //area int clif_walkok(struct map_session_data*); // self @@ -133,14 +228,13 @@ 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_changeoption2(struct block_list*); // area int clif_useitemack(struct map_session_data*,int,int,int); // self void clif_GlobalMessage(struct block_list* bl, const char* message); -void clif_createchat(struct map_session_data* sd, int fail); // self +void clif_createchat(struct map_session_data* sd, int flag); // self int clif_dispchat(struct chat_data*,int); // area or fd -int clif_joinchatfail(struct map_session_data*,int); // self +int clif_joinchatfail(struct map_session_data* sd,int flag); // self int clif_joinchatok(struct map_session_data*,struct chat_data*); // self int clif_addchat(struct chat_data*,struct map_session_data*); // chat void clif_changechatowner(struct chat_data* cd, struct map_session_data* sd); // chat @@ -158,7 +252,7 @@ void clif_talkiebox(struct block_list* bl, const char* talkie); void clif_wedding_effect(struct block_list *bl); void clif_divorced(struct map_session_data* sd, const char* name); void clif_callpartner(struct map_session_data *sd); -void clif_playBGM(struct map_session_data* sd, struct block_list* bl, const char* name); +void clif_playBGM(struct map_session_data* sd, const char* name); void clif_soundeffect(struct map_session_data* sd, struct block_list* bl, const char* name, int type); int clif_soundeffectall(struct block_list* bl, const char *name, int type, enum send_target coverage); void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick); @@ -176,14 +270,12 @@ void clif_tradecompleted(struct map_session_data* sd, int fail); // storage #include "storage.h" -void clif_storagelist(struct map_session_data* sd, struct storage_data* stor); +void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length); void clif_updatestorageamount(struct map_session_data* sd, int amount); void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount); void clif_storageitemremoved(struct map_session_data* sd, int index, int amount); void clif_storageclose(struct map_session_data* sd); -void clif_guildstoragelist(struct map_session_data* sd, struct guild_storage* stor); void clif_updateguildstorageamount(struct map_session_data* sd, int amount); -void clif_guildstorageitemadded(struct map_session_data* sd, struct item* i, int index, int amount); int clif_insight(struct block_list *,va_list); // map_forallinmovearea callback int clif_outsight(struct block_list *,va_list); // map_forallinmovearea callback @@ -218,7 +310,7 @@ int clif_produceeffect(struct map_session_data* sd,int flag,int nameid); void clif_skill_setunit(struct skill_unit *unit); void clif_skill_delunit(struct skill_unit *unit); -void clif_01ac(struct block_list* bl); +void clif_skillunit_update(struct block_list* bl); int clif_autospell(struct map_session_data *sd,int skilllv); void clif_devotion(struct block_list *src, struct map_session_data *tsd); @@ -390,6 +482,7 @@ void clif_get_weapon_view(struct map_session_data* sd, unsigned short *rhand, un int clif_party_xy_remove(struct map_session_data *sd); //Fix for minimap [Kevin] void clif_gospel_info(struct map_session_data *sd, int type); void clif_parse_ReqFeel(int fd, struct map_session_data *sd, int skilllv); +void clif_starskill(struct map_session_data* sd, const char* mapname, int monster_id, unsigned char star, unsigned char result); void clif_feel_info(struct map_session_data *sd, unsigned char feel_level, unsigned char type); void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type); void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress); @@ -409,6 +502,8 @@ void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* ts void clif_viewequip_fail(struct map_session_data* sd); void clif_equipcheckbox(struct map_session_data* sd); +void clif_msg(struct map_session_data* sd, unsigned short id); + //quest system [Kevin] [Inkfish] void clif_quest_send_list(struct map_session_data * sd); void clif_quest_send_mission(struct map_session_data * sd); @@ -450,7 +545,7 @@ void clif_Adopt_reply(struct map_session_data *sd, int type); // MERCENARIES void clif_mercenary_info(struct map_session_data *sd); void clif_mercenary_skillblock(struct map_session_data *sd); -void clif_mercenary_message(int fd, int message); +void clif_mercenary_message(struct map_session_data* sd, int message); void clif_mercenary_updatestatus(struct map_session_data *sd, int type); // RENTAL SYSTEM @@ -469,9 +564,11 @@ void clif_progressbar_abort(struct map_session_data * sd); void clif_PartyBookingRegisterAck(struct map_session_data *sd, int flag); void clif_PartyBookingDeleteAck(struct map_session_data* sd, int flag); -void clif_PartyBookingSearchAck(int fd, unsigned long *index, int count, bool more_result); +void clif_PartyBookingSearchAck(int fd, struct party_booking_ad_info** results, int count, bool more_result); void clif_PartyBookingUpdateNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad); void clif_PartyBookingDeleteNotify(struct map_session_data* sd, int index); void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad); +void clif_showdigit(struct map_session_data* sd, unsigned char type, int value); + #endif /* _CLIF_H_ */ diff --git a/src/map/guild.c b/src/map/guild.c index cdc978929..f90fc8946 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -1068,8 +1068,8 @@ int guild_notice_changed(int guild_id,const char *mes1,const char *mes2) if(g==NULL) return 0; - memcpy(g->mes1,mes1,60); - memcpy(g->mes2,mes2,120); + memcpy(g->mes1,mes1,MAX_GUILDMES1); + memcpy(g->mes2,mes2,MAX_GUILDMES2); for(i=0;i<g->max_member;i++){ if((sd=g->member[i].sd)!=NULL) diff --git a/src/map/homunculus.c b/src/map/homunculus.c index 97a3274fb..0fbd7cbff 100644 --- a/src/map/homunculus.c +++ b/src/map/homunculus.c @@ -68,7 +68,7 @@ int merc_hom_dead(struct homun_data *hd, struct block_list *src) //There's no intimacy penalties on death (from Tharis) struct map_session_data *sd = hd->master; - clif_emotion(&hd->bl, 16) ; //wah + clif_emotion(&hd->bl, E_WAH); //Delete timers when dead. merc_hom_hungry_timer_delete(hd); @@ -77,7 +77,7 @@ int merc_hom_dead(struct homun_data *hd, struct block_list *src) if (!sd) //unit remove map will invoke unit free return 3; - clif_emotion(&sd->bl, 28) ; //sob + clif_emotion(&sd->bl, E_SOB); //Remove from map (if it has no intimacy, it is auto-removed from memory) return 3; } @@ -107,7 +107,7 @@ int merc_hom_vaporize(struct map_session_data *sd, int flag) memset(hd->blockskill, 0, sizeof(hd->blockskill)); clif_hominfo(sd, sd->hd, 0); merc_save(hd); - return unit_remove_map(&hd->bl, 0); + return unit_remove_map(&hd->bl, CLR_OUTSIGHT); } //delete a homunculus, completely "killing it". @@ -119,7 +119,7 @@ int merc_hom_delete(struct homun_data *hd, int emote) sd = hd->master; if (!sd) - return unit_free(&hd->bl,1); + return unit_free(&hd->bl,CLR_DEAD); if (emote >= 0) clif_emotion(&sd->bl, emote); @@ -129,7 +129,7 @@ int merc_hom_delete(struct homun_data *hd, int emote) // Send homunculus_dead to client hd->homunculus.hp = 0; clif_hominfo(sd, hd, 0); - return unit_remove_map(&hd->bl,0); + return unit_remove_map(&hd->bl,CLR_OUTSIGHT); } int merc_hom_calc_skilltree(struct homun_data *hd) @@ -147,7 +147,7 @@ int merc_hom_calc_skilltree(struct homun_data *hd) continue; //Skill already known. if(!battle_config.skillfree) { - for(j=0;j<5;j++) + for(j=0;j<MAX_PC_SKILL_REQUIRE;j++) { if( hskill_tree[c][i].need[j].id && merc_hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv) @@ -291,7 +291,7 @@ int merc_hom_evolution(struct homun_data *hd) if(!hd->homunculusDB->evo_class || hd->homunculus.class_ == hd->homunculusDB->evo_class) { - clif_emotion(&hd->bl, 4) ; //swt + clif_emotion(&hd->bl, E_SWT); return 0 ; } sd = hd->master; @@ -317,12 +317,12 @@ int merc_hom_evolution(struct homun_data *hd) hom->luk += 10*rand(min->luk, max->luk); hom->intimacy = 500; - unit_remove_map(&hd->bl, 0); + unit_remove_map(&hd->bl, CLR_OUTSIGHT); map_addblock(&hd->bl); clif_spawn(&hd->bl); - clif_emotion(&sd->bl, 21); //no1 - clif_misceffect2(&hd->bl,568); + clif_emotion(&sd->bl, E_NO1); + clif_specialeffect(&hd->bl,568,AREA); //status_Calc flag&1 will make current HP/SP be reloaded from hom structure hom->hp = hd->battle_status.hp; @@ -362,7 +362,7 @@ int merc_hom_gainexp(struct homun_data *hd,int exp) if( hd->exp_next == 0 ) hd->homunculus.exp = 0 ; - clif_misceffect2(&hd->bl,568); + clif_specialeffect(&hd->bl,568,AREA); status_calc_homunculus(hd,0); status_percent_heal(&hd->bl, 100, 100); return 0; @@ -448,33 +448,33 @@ int merc_hom_food(struct map_session_data *sd, struct homun_data *hd) if ( hd->homunculus.hunger >= 91 ) { merc_hom_decrease_intimacy(hd, 50); - emotion = 16; + emotion = E_WAH; } else if ( hd->homunculus.hunger >= 76 ) { merc_hom_decrease_intimacy(hd, 5); - emotion = 19; + emotion = E_SWT2; } else if ( hd->homunculus.hunger >= 26 ) { merc_hom_increase_intimacy(hd, 75); - emotion = 2; + emotion = E_HO; } else if ( hd->homunculus.hunger >= 11 ) { merc_hom_increase_intimacy(hd, 100); - emotion = 2; + emotion = E_HO; } else { merc_hom_increase_intimacy(hd, 50); - emotion = 2; + emotion = E_HO; } hd->homunculus.hunger += 10; //dunno increase value for each food if(hd->homunculus.hunger > 100) hd->homunculus.hunger = 100; - clif_emotion(&hd->bl,emotion) ; + clif_emotion(&hd->bl,emotion); clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger); clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); clif_hom_food(sd,foodID,1); // Too much food :/ if(hd->homunculus.intimacy == 0) - return merc_hom_delete(sd->hd, 23); //omg + return merc_hom_delete(sd->hd, E_OMG); return 0; } @@ -500,18 +500,18 @@ static int merc_hom_hungry(int tid, unsigned int tick, int id, intptr data) hd->homunculus.hunger-- ; if(hd->homunculus.hunger <= 10) { - clif_emotion(&hd->bl, 6) ; //an + clif_emotion(&hd->bl, E_AN); } else if(hd->homunculus.hunger == 25) { - clif_emotion(&hd->bl, 20) ; //hmm + clif_emotion(&hd->bl, E_HMM); } else if(hd->homunculus.hunger == 75) { - clif_emotion(&hd->bl, 33) ; //ok + clif_emotion(&hd->bl, E_OK); } if(hd->homunculus.hunger < 0) { hd->homunculus.hunger = 0; // Delete the homunculus if intimacy <= 100 if ( !merc_hom_decrease_intimacy(hd, 100) ) - return merc_hom_delete(hd, 23); //omg + return merc_hom_delete(hd, E_OMG); clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100); } @@ -676,7 +676,7 @@ int merc_call_homunculus(struct map_session_data *sd) merc_save(hd); } else //Warp him to master. - unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,0); + unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,CLR_OUTSIGHT); return 1; } @@ -871,7 +871,7 @@ int merc_hom_shuffle(struct homun_data *hd) clif_homskillinfoblock(sd); status_calc_homunculus(hd,0); status_percent_heal(&hd->bl, 100, 100); - clif_misceffect2(&hd->bl,568); + clif_specialeffect(&hd->bl,568,AREA); return 1; } @@ -1088,7 +1088,7 @@ int read_homunculus_skilldb(void) if (minJobLevelPresent) hskill_tree[classid][j].joblv=atoi(split[3]); - for(k=0;k<5;k++){ + for(k=0;k<MAX_PC_SKILL_REQUIRE;k++){ hskill_tree[classid][j].need[k].id=atoi(split[3+k*2+minJobLevelPresent]); hskill_tree[classid][j].need[k].lv=atoi(split[3+k*2+minJobLevelPresent+1]); } diff --git a/src/map/instance.c b/src/map/instance.c index c1d43c27b..6fa3a2c0a 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -235,7 +235,7 @@ int instance_del_load(struct map_session_data* sd, va_list args) if( !sd || sd->bl.m != m ) return 0; - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 0); + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_OUTSIGHT); return 1; } @@ -439,9 +439,9 @@ void instance_check_kick(struct map_session_data *sd) if( map[m].instance_id ) { // User was on the instance map if( map[m].save.map ) - pc_setpos(sd, map[m].save.map, map[m].save.x, map[m].save.y, 3); + pc_setpos(sd, map[m].save.map, map[m].save.x, map[m].save.y, CLR_TELEPORT); else - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 3); + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); } } diff --git a/src/map/intif.c b/src/map/intif.c index ba5230bab..875baa036 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -698,8 +698,8 @@ int intif_guild_notice(int guild_id,const char *mes1,const char *mes2) WFIFOHEAD(inter_fd,186); WFIFOW(inter_fd,0)=0x303e; WFIFOL(inter_fd,2)=guild_id; - memcpy(WFIFOP(inter_fd,6),mes1,60); - memcpy(WFIFOP(inter_fd,66),mes2,120); + memcpy(WFIFOP(inter_fd,6),mes1,MAX_GUILDMES1); + memcpy(WFIFOP(inter_fd,66),mes2,MAX_GUILDMES2); WFIFOSET(inter_fd,186); return 0; } @@ -984,10 +984,7 @@ int intif_parse_LoadGuildStorage(int fd) if(battle_config.save_log) ShowInfo("intif_open_guild_storage: %d\n",RFIFOL(fd,4) ); memcpy(gstor,RFIFOP(fd,12),sizeof(struct guild_storage)); - gstor->storage_status = 1; - sd->state.storage_flag = 2; - clif_guildstoragelist(sd,gstor); - clif_updateguildstorageamount(sd,gstor->storage_amount); + storage_guild_storageopen(sd); return 0; } int intif_parse_SaveGuildStorage(int fd) @@ -1920,7 +1917,7 @@ int intif_parse_mercenary_received(int fd) if( sizeof(struct s_mercenary) != len ) { if( battle_config.etc_log ) - ShowError("intif: create mercenary data size error %d != %d\n", sizeof(struct s_homunculus), len); + ShowError("intif: create mercenary data size error %d != %d\n", sizeof(struct s_mercenary), len); return 0; } diff --git a/src/map/itemdb.c b/src/map/itemdb.c index 2096ddf9e..ce10f4f39 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -27,8 +27,6 @@ static struct item_group itemgroup_db[MAX_ITEMGROUP]; struct item_data dummy_item; //This is the default dummy item used for non-existant items. [Skotlex] -int item_delays = 0; - /*========================================== * 名前で検索用 *------------------------------------------*/ @@ -182,6 +180,27 @@ struct item_data* itemdb_exists(int nameid) return item; } +/// Returns human readable name for given item type. +/// @param type Type id to retrieve name for ( IT_* ). +const char* itemdb_typename(int type) +{ + switch(type) + { + case IT_HEALING: return "Potion/Food"; + case IT_USABLE: return "Usable"; + case IT_ETC: return "Etc."; + case IT_WEAPON: return "Weapon"; + case IT_ARMOR: return "Armor"; + case IT_CARD: return "Card"; + case IT_PETEGG: return "Pet Egg"; + case IT_PETARMOR: return "Pet Accessory"; + case IT_AMMO: return "Arrow/Ammunition"; + case IT_DELAYCONSUME: return "Delay-Consume Usable"; + case IT_CASH: return "Cash Usable"; + } + return "Unknown Type"; +} + /*========================================== * Converts the jobid from the format in itemdb * to the format used by the map server. [Skotlex] @@ -667,7 +686,7 @@ static int itemdb_read_itemtrade(void) static int itemdb_read_itemdelay(void) { FILE *fp; - int nameid, j; + int nameid, j, item_delays = 0; char line[1024], *str[10], *p; struct item_data *id; @@ -679,12 +698,12 @@ static int itemdb_read_itemdelay(void) while(fgets(line, sizeof(line), fp)) { + if (line[0] == '/' && line[1] == '/') + continue; if (item_delays == MAX_ITEMDELAYS) { - ShowError("itemdb_read_itemdelay: Too many entries specified in %s/item_delay.txt!\n", db_path); + ShowError("itemdb_read_itemdelay: Too many entries specified in %s/item_delay.txt! Increase MAX_ITEMDELAYS in itemdb.h!\n", db_path); break; } - if (line[0] == '/' && line[1] == '/') - continue; memset(str, 0, sizeof(str)); for (j = 0, p = line; j < 2 && p; j++) { str[j] = p; @@ -750,6 +769,13 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr safestrncpy(id->jname, str[2], sizeof(id->jname)); id->type = atoi(str[3]); + + if( id->type < 0 || id->type == IT_UNKNOWN || id->type == IT_UNKNOWN2 || ( id->type > IT_DELAYCONSUME && id->type < IT_CASH ) || id->type >= IT_MAX ) + {// catch invalid item types + ShowWarning("itemdb_parse_dbrow: Invalid item type %d for item %d. IT_ETC will be used.\n", id->type, nameid); + id->type = IT_ETC; + } + if (id->type == IT_DELAYCONSUME) { //Items that are consumed only after target confirmation id->type = IT_USABLE; @@ -809,8 +835,6 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr id->look = atoi(str[18]); id->flag.available = 1; - id->flag.value_notdc = 0; - id->flag.value_notoc = 0; id->view_id = 0; id->sex = itemdb_gendercheck(id); //Apply gender filtering. @@ -1075,7 +1099,10 @@ void itemdb_reload(void) // readjust itemdb pointer cache for each player iter = mapit_geteachpc(); for( sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) ) + { + memset(sd->item_delay, 0, sizeof(sd->item_delay)); // reset item delays pc_setinventorydata(sd); + } mapit_free(iter); } diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 5b07b5c1b..3fb14b2d4 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -45,7 +45,7 @@ struct item_data { int value_buy; int value_sell; int type; - int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus] + int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it, -1 denotes items sold in shops only) [Lupus] int sex; int equip; int weight; @@ -71,8 +71,6 @@ struct item_data { struct script_code *unequip_script;//Script executed once when unequipping. struct { unsigned available : 1; - unsigned value_notdc : 1; - unsigned value_notoc : 1; short no_equip; unsigned no_refine : 1; // [celest] unsigned delay_consume : 1; // Signifies items that are not consumed immediately upon double-click [Skotlex] @@ -108,14 +106,13 @@ struct item_data* itemdb_exists(int nameid); #define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available) #define itemdb_viewid(n) (itemdb_search(n)->view_id) #define itemdb_autoequip(n) (itemdb_search(n)->flag.autoequip) +const char* itemdb_typename(int type); int itemdb_group_bonus(struct map_session_data* sd, int itemid); 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 #define itemdb_canrefine(n) itemdb_search(n)->flag.no_refine //Item trade restrictions [Skotlex] int itemdb_isdropable_sub(struct item_data *, int, int); diff --git a/src/map/log.c b/src/map/log.c index 474a6bb96..bf6133684 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -27,7 +27,7 @@ time_t curtime; //Bits: || //2 - Healing items (0) //3 - Etc Items(3) + Arrows (10) -//4 - Usable Items(2) + Scrolls,Lures(11) +//4 - Usable Items(2) + Scrolls,Lures(11) + Usable Cash Items(18) //5 - Weapon(4) //6 - Shields,Armor,Headgears,Accessories,etc(5) //7 - Cards(6) @@ -45,14 +45,14 @@ int should_log_item(int filter, int nameid, int amount) if ((filter&1) || // Filter = 1, we log any item (filter&2 && item_data->type == IT_HEALING ) || (filter&4 && (item_data->type == IT_ETC || item_data->type == IT_AMMO) ) || - (filter&8 && item_data->type == IT_USABLE ) || + (filter&8 && (item_data->type == IT_USABLE || item_data->type == IT_CASH) ) || (filter&16 && item_data->type == IT_WEAPON ) || (filter&32 && item_data->type == IT_ARMOR ) || (filter&64 && item_data->type == IT_CARD ) || (filter&128 && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) || (filter&256 && item_data->value_buy >= log_config.price_items_log ) || //expensive items (filter&512 && abs(amount) >= log_config.amount_items_log ) || //big amount of items - (filter&2048 && ((item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium + (filter&2048 && ((item_data->maxchance != -1 && item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium ) return item_data->nameid; return 0; diff --git a/src/map/map.c b/src/map/map.c index 309095ff0..b052cd7bc 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -1645,7 +1645,7 @@ int map_quit(struct map_session_data *sd) if( sd->pd ) pet_lootitem_drop(sd->pd, sd); if( sd->state.storage_flag == 1 ) sd->state.storage_flag = 0; // No need to Double Save Storage on Quit. - unit_remove_map_pc(sd,3); + unit_remove_map_pc(sd,CLR_TELEPORT); if( map[sd->bl.m].instance_id ) { // Avoid map conflicts and warnings on next login @@ -1665,7 +1665,7 @@ int map_quit(struct map_session_data *sd) } } - party_booking_delete(sd, true); // Party Booking [Spiria] + party_booking_delete(sd); // Party Booking [Spiria] pc_makesavestatus(sd); pc_clean_skilltree(sd); chrif_save(sd,1); @@ -2150,7 +2150,7 @@ int map_removemobs_sub(struct block_list *bl, va_list ap) if( md->db->mexp > 0 ) return 0; - unit_free(&md->bl,0); + unit_free(&md->bl,CLR_OUTSIGHT); return 1; } @@ -2797,6 +2797,20 @@ int map_delmap(char* mapname) return 0; } +/// Initializes map flags and adjusts them depending on configuration. +void map_flags_init(void) +{ + int i; + + for( i = 0; i < map_num; i++ ) + { + memset(&map[i].flag, 0, sizeof(map[i].flag)); + + if( battle_config.pk_mode ) + map[i].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris] + } +} + #define NO_WATER 1000000 /* @@ -2892,7 +2906,7 @@ int map_readallmaps (void) int i; FILE* fp=NULL; int maps_removed = 0; - unsigned char *map_cache_buffer; // Has the uncompressed gat data of all maps, so just one allocation has to be made + unsigned char *map_cache_buffer = NULL; // Has the uncompressed gat data of all maps, so just one allocation has to be made unsigned char map_cache_decode_buffer[MAX_MAP_SIZE]; if( enable_grf ) @@ -2958,8 +2972,6 @@ int map_readallmaps (void) map[i].m = i; memset(map[i].moblist, 0, sizeof(map[i].moblist)); //Initialize moblist [Skotlex] map[i].mob_delete_timer = INVALID_TIMER; //Initialize timer [Skotlex] - if(battle_config.pk_mode) - map[i].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris] map[i].bxs = (map[i].xs + BLOCK_SIZE - 1) / BLOCK_SIZE; map[i].bys = (map[i].ys + BLOCK_SIZE - 1) / BLOCK_SIZE; @@ -2973,6 +2985,9 @@ int map_readallmaps (void) map[i].block_mob = (struct block_list**)aCalloc(size, 1); } + // intialization and configuration-dependent adjustments of mapflags + map_flags_init(); + if( !enable_grf ) { fclose(fp); @@ -3205,7 +3220,7 @@ int inter_config_read(char *cfgName) continue; if(strcmpi(w1, "main_chat_nick")==0) - strcpy(main_chat_nick, w2); + safestrncpy(main_chat_nick, w2, sizeof(main_chat_nick)); #ifndef TXT_ONLY else @@ -3365,7 +3380,7 @@ int cleanup_sub(struct block_list *bl, va_list ap) npc_unload((struct npc_data *)bl); break; case BL_MOB: - unit_free(bl,0); + unit_free(bl,CLR_OUTSIGHT); break; case BL_PET: //There is no need for this, the pet is removed together with the player. [Skotlex] diff --git a/src/map/map.h b/src/map/map.h index 9f25249e2..3b1775e46 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -296,6 +296,8 @@ enum _sp { SP_BASEJOB=119, // 100+19 - celest SP_BASECLASS=120, //Hmm.. why 100+19? I just use the next one... [Skotlex] + SP_KILLERRID=121, + SP_KILLEDRID=122, // Mercenaries SP_MERCFLEE=165, SP_MERCKILLS=189, SP_MERCFAITH=190, @@ -628,6 +630,7 @@ int cleanup_sub(struct block_list *bl, va_list ap); void map_helpscreen(int flag); // [Valaris] int map_delmap(char* mapname); +void map_flags_init(void); bool map_iwall_set(int m, int x, int y, int size, int dir, bool shootable, const char* wall_name); void map_iwall_get(struct map_session_data *sd); diff --git a/src/map/mapreg_sql.c b/src/map/mapreg_sql.c index 287aae4f9..f80be56a2 100644 --- a/src/map/mapreg_sql.c +++ b/src/map/mapreg_sql.c @@ -122,9 +122,9 @@ static void script_load_mapreg(void) return; } - SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &varname[0], 32, &length, NULL); + SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &varname[0], sizeof(varname), &length, NULL); SqlStmt_BindColumn(stmt, 1, SQLDT_INT, &index, 0, NULL, NULL); - SqlStmt_BindColumn(stmt, 2, SQLDT_STRING, &value[0], 255, NULL, NULL); + SqlStmt_BindColumn(stmt, 2, SQLDT_STRING, &value[0], sizeof(value), NULL, NULL); while ( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) { diff --git a/src/map/mercenary.c b/src/map/mercenary.c index 84974e906..79bec2da6 100644 --- a/src/map/mercenary.c +++ b/src/map/mercenary.c @@ -244,7 +244,7 @@ int merc_delete(struct mercenary_data *md, int reply) merc_contract_stop(md); if( !sd ) - return unit_free(&md->bl, 0); + return unit_free(&md->bl, CLR_OUTSIGHT); if( md->devotion_flag ) { @@ -258,8 +258,8 @@ int merc_delete(struct mercenary_data *md, int reply) case 1: mercenary_set_faith(md, -1); break; // -1 Loyalty on Mercenary killed } - clif_mercenary_message(sd->fd, reply); - return unit_remove_map(&md->bl, 0); + clif_mercenary_message(sd, reply); + return unit_remove_map(&md->bl, CLR_OUTSIGHT); } void merc_contract_stop(struct mercenary_data *md) diff --git a/src/map/mob.c b/src/map/mob.c index 413d88f26..d67b470af 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -82,7 +82,6 @@ const int mob_splendide[5] = { 1991, 1992, 1993, 1994, 1995 }; *------------------------------------------*/ static int mob_makedummymobdb(int); static int mob_spawn_guardian_sub(int tid, unsigned int tick, int id, intptr data); -int mobskill_use(struct mob_data *md,unsigned int tick,int event); int mob_skillid2skillidx(int class_,int skillid); /*========================================== @@ -276,7 +275,7 @@ int mob_get_random_id(int type, int flag, int lv) (flag&8 && mob->spawn[0].qty < 1) ) && (i++) < MAX_MOB_DB); - if(i >= MAX_MOB_DB) + if(i >= MAX_MOB_DB) // no suitable monster found, use fallback for given list class_ = mob_db_data[0]->summonper[type]; return class_; } @@ -449,7 +448,7 @@ int mob_once_spawn(struct map_session_data* sd, int m, short x, short y, const c if (class_ < 0 && battle_config.dead_branch_active) //Behold Aegis's masterful decisions yet again... //"I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" - Poki#3 - sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE, 0, 60000); + sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE|MD_ANGRY, 0, 60000); } return (md)?md->bl.id : 0; // id of last spawned mob @@ -547,7 +546,7 @@ static int mob_spawn_guardian_sub(int tid, unsigned int tick, int id, intptr dat md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); } - unit_free(&md->bl,0); //Remove guardian. + unit_free(&md->bl,CLR_OUTSIGHT); //Remove guardian. } return 0; } @@ -795,7 +794,7 @@ int mob_setdelayspawn(struct mob_data *md) unsigned int spawntime; if (!md->spawn) //Doesn't has respawn data! - return unit_free(&md->bl,1); + return unit_free(&md->bl,CLR_DEAD); spawntime = md->spawn->delay1; //Base respawn time if (md->spawn->delay2) //random variance @@ -826,7 +825,7 @@ int mob_spawn (struct mob_data *md) md->last_thinktime = tick; if (md->bl.prev != NULL) - unit_remove_map(&md->bl,2); + unit_remove_map(&md->bl,CLR_RESPAWN); else if (md->spawn && md->class_ != md->spawn->class_) { @@ -1121,7 +1120,7 @@ static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick) md->master_dist > MAX_MINCHASE ){ md->master_dist = 0; - unit_warp(&md->bl,bl->m,bl->x,bl->y,3); + unit_warp(&md->bl,bl->m,bl->x,bl->y,CLR_TELEPORT); return 1; } @@ -1761,7 +1760,7 @@ int mob_timer_delete(int tid, unsigned int tick, int id, intptr data) } //for Alchemist CANNIBALIZE [Lupus] md->deletetimer = INVALID_TIMER; - unit_free(bl, 3); + unit_free(bl, CLR_TELEPORT); } return 0; } @@ -1805,7 +1804,7 @@ int mob_respawn(int tid, unsigned int tick, int id, intptr data) void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) { - int char_id = 0, flag = 0; + int char_id = 0, flag = MDLF_NORMAL; if( damage < 0 ) return; //Do nothing for absorbed damage. @@ -1827,7 +1826,7 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) case BL_HOM: { struct homun_data *hd = (TBL_HOM*)src; - flag = 1; + flag = MDLF_HOMUN; if( hd->master ) char_id = hd->master->status.char_id; if( damage ) @@ -1846,7 +1845,7 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) case BL_PET: { struct pet_data *pd = (TBL_PET*)src; - flag = 2; + flag = MDLF_PET; if( pd->msd ) { char_id = pd->msd->status.char_id; @@ -1960,9 +1959,9 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) unsigned int base_exp,job_exp; } pt[DAMAGELOG_SIZE]; int i,temp,count,pnum=0,m=md->bl.m; + int dmgbltypes = 0; // bitfield of all bl types, that caused damage to the mob and are elligible for exp distribution unsigned int mvp_damage, tick = gettick(); - unsigned short flaghom = 1; // [Zephyrus] Does the mob only received damage from homunculus? - bool rebirth; + bool rebirth, homkillonly; status = &md->status; @@ -2022,9 +2021,9 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) count++; //Only logged into same map chars are counted for the total. if (pc_isdead(tsd)) continue; // skip dead players - if(md->dmglog[i].flag == 1 && !merc_is_hom_active(tsd->hd)) + if(md->dmglog[i].flag == MDLF_HOMUN && !merc_is_hom_active(tsd->hd)) continue; // skip homunc's share if inactive - if( md->dmglog[i].flag == 2 && (!tsd->status.pet_id || !tsd->pd) ) + if( md->dmglog[i].flag == MDLF_PET && (!tsd->status.pet_id || !tsd->pd) ) continue; // skip pet's share if inactive if(md->dmglog[i].dmg > mvp_damage) @@ -2037,10 +2036,17 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) tmpsd[i] = tsd; // record as valid damage-log entry - if(!md->dmglog[i].flag == 1 && flaghom) - flaghom = 0; // Damage received from other Types != Homunculus + switch( md->dmglog[i].flag ) + { + case MDLF_NORMAL: dmgbltypes|= BL_PC; break; + case MDLF_HOMUN: dmgbltypes|= BL_HOM; break; + case MDLF_PET: dmgbltypes|= BL_PET; break; + } } + // determines, if the monster was killed by homunculus' damage only + homkillonly = (bool)( ( dmgbltypes&BL_HOM ) && !( dmgbltypes&~BL_HOM ) ); + if(!battle_config.exp_calc_type && count > 1) { //Apply first-attacker 200% exp share bonus //TODO: Determine if this should go before calculating the MVP player instead of after. @@ -2065,9 +2071,9 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) temp = status_get_class(&md->bl); if(sd->sc.data[SC_MIRACLE]) i = 2; //All mobs are Star Targets else - ARR_FIND(0, 3, i, temp == sd->hate_mob[i] && + ARR_FIND(0, MAX_PC_FEELHATE, i, temp == sd->hate_mob[i] && (battle_config.allow_skill_without_day || sg_info[i].day_func())); - if(i<3 && (temp=pc_checkskill(sd,sg_info[i].bless_id))) + if(i<MAX_PC_FEELHATE && (temp=pc_checkskill(sd,sg_info[i].bless_id))) bonus += (i==2?20:10)*temp; } if(battle_config.mobs_level_up && md->level > md->db->lv) // [Valaris] @@ -2103,7 +2109,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) else if(md->special_state.size==2) per *=2.; - if( md->dmglog[i].flag == 2 ) + if( md->dmglog[i].flag == MDLF_PET ) per *= battle_config.pet_attack_exp_rate/100.; if(battle_config.zeny_from_mobs && md->level) { @@ -2118,12 +2124,12 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) else base_exp = (unsigned int)cap_value(md->db->base_exp * per * bonus/100. * map[m].bexp/100., 1, UINT_MAX); - if (map[m].flag.nojobexp || !md->db->job_exp || md->dmglog[i].flag == 1) //Homun earned job-exp is always lost. + if (map[m].flag.nojobexp || !md->db->job_exp || md->dmglog[i].flag == MDLF_HOMUN) //Homun earned job-exp is always lost. job_exp = 0; else job_exp = (unsigned int)cap_value(md->db->job_exp * per * bonus/100. * map[m].jexp/100., 1, UINT_MAX); - if((temp = tmpsd[i]->status.party_id )>0 && !md->dmglog[i].flag == 1) //Homun-done damage (flag 1) is not given to party + if((temp = tmpsd[i]->status.party_id )>0 && !md->dmglog[i].flag == MDLF_HOMUN) //Homun-done damage (flag 1) is not given to party { int j; for(j=0;j<pnum && pt[j].id!=temp;j++); //Locate party. @@ -2155,11 +2161,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) } } if(flag) { - if(base_exp && md->dmglog[i].flag == 1) //tmpsd[i] is null if it has no homunc. + if(base_exp && md->dmglog[i].flag == MDLF_HOMUN) //tmpsd[i] is null if it has no homunc. merc_hom_gainexp(tmpsd[i]->hd, base_exp); if(base_exp || job_exp) { - if( md->dmglog[i].flag != 2 || battle_config.pet_attack_exp_to_master ) + if( md->dmglog[i].flag != MDLF_PET || battle_config.pet_attack_exp_to_master ) pc_gainexp(tmpsd[i], &md->bl, base_exp, job_exp, false); } if(zeny) // zeny from mobs [Valaris] @@ -2241,13 +2247,13 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) } // Announce first, or else ditem will be freed. [Lance] // By popular demand, use base drop rate for autoloot code. [Skotlex] - mob_item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p, flaghom); + mob_item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p, homkillonly); } // Ore Discovery [Celest] if (sd == mvp_sd && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rand()%10000) { ditem = mob_setdropitem(itemdb_searchrandomid(IG_FINDINGORE), 1); - mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10, 0); + mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10, homkillonly); } if(sd) { @@ -2273,7 +2279,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) if (rand()%10000 >= drop_rate) continue; itemid = (sd->add_drop[i].id > 0) ? sd->add_drop[i].id : itemdb_searchrandomid(sd->add_drop[i].group); - mob_item_drop(md, dlist, mob_setdropitem(itemid,1), 0, drop_rate, 0); + mob_item_drop(md, dlist, mob_setdropitem(itemid,1), 0, drop_rate, homkillonly); } } @@ -2289,7 +2295,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) // process items looted by the mob if(md->lootitem) { for(i = 0; i < md->lootitem_count; i++) - mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000, 0); + mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000, homkillonly); } if (dlist->item) //There are drop items. add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, 0, (intptr)dlist); @@ -2305,7 +2311,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) dlist->third_charid = (third_sd ? third_sd->status.char_id : 0); dlist->item = NULL; for(i = 0; i < md->lootitem_count; i++) - mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000, 0); + mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000, homkillonly); add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, 0, (intptr)dlist); } @@ -2406,12 +2412,12 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { if( sd && battle_config.mob_npc_event_type ) { - pc_setglobalreg(sd,"killerrid",sd->bl.id); + pc_setparam(sd, SP_KILLERRID, sd->bl.id); npc_event(sd,md->npc_event,0); } else if( mvp_sd ) { - pc_setglobalreg(mvp_sd,"killerrid",sd?sd->bl.id:0); + pc_setparam(mvp_sd, SP_KILLERRID, sd?sd->bl.id:0); npc_event(mvp_sd,md->npc_event,0); } else @@ -2419,7 +2425,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) } else if( mvp_sd && !md->state.npc_killmonster ) { - pc_setglobalreg(mvp_sd,"killedrid",md->class_); + pc_setparam(mvp_sd, SP_KILLEDRID, md->class_); npc_script_event(mvp_sd, NPCE_KILLNPC); // PCKillNPC [Lance] } @@ -2491,7 +2497,7 @@ int mob_guardian_guildchange(struct block_list *bl,va_list ap) md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); } - unit_free(&md->bl,0); //Remove guardian. + unit_free(&md->bl,CLR_OUTSIGHT); //Remove guardian. } return 0; } @@ -2505,7 +2511,7 @@ int mob_guardian_guildchange(struct block_list *bl,va_list ap) md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); } - unit_free(&md->bl,0); + unit_free(&md->bl,CLR_OUTSIGHT); return 0; } @@ -2631,7 +2637,7 @@ int mob_warpslave_sub(struct block_list *bl,va_list ap) return 0; map_search_freecell(master, 0, &x, &y, range, range, 0); - unit_warp(&md->bl, master->m, x, y,2); + unit_warp(&md->bl, master->m, x, y,CLR_RESPAWN); return 1; } @@ -3409,9 +3415,7 @@ static bool mob_parse_dbrow(char** str) double exp, maxhp; struct mob_data data; - class_ = str[0] ? atoi(str[0]) : 0; - if (class_ == 0) - return false; //Leave blank lines alone... [Skotlex] + class_ = atoi(str[0]); if (class_ <= 1000 || class_ > MAX_MOB_DB) { ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB); @@ -3552,7 +3556,7 @@ static bool mob_parse_dbrow(char** str) //calculate and store Max available drop chance of the MVP item if (db->mvpitem[i].p) { id = itemdb_search(db->mvpitem[i].nameid); - if (id->maxchance == 10000 || (id->maxchance < db->mvpitem[i].p/10 + 1) ) { + if (id->maxchance == -1 || (id->maxchance < db->mvpitem[i].p/10 + 1) ) { //item has bigger drop chance or sold in shops id->maxchance = db->mvpitem[i].p/10 + 1; //reduce MVP drop info to not spoil common drop rate } @@ -3585,6 +3589,7 @@ static bool mob_parse_dbrow(char** str) ratemax = battle_config.item_drop_heal_max; break; case IT_USABLE: + case IT_CASH: rate_adjust = (status->mode&MD_BOSS) ? battle_config.item_rate_use_boss : battle_config.item_rate_use; ratemin = battle_config.item_drop_use_min; ratemax = battle_config.item_drop_use_max; @@ -3613,7 +3618,7 @@ static bool mob_parse_dbrow(char** str) if( db->dropitem[i].p && (class_ < 1324 || class_ > 1363) && (class_ < 1938 || class_ > 1946) ) { //Skip treasure chests. id = itemdb_search(db->dropitem[i].nameid); - if (id->maxchance == 10000 || (id->maxchance < db->dropitem[i].p) ) { + if (id->maxchance == -1 || (id->maxchance < db->dropitem[i].p) ) { id->maxchance = db->dropitem[i].p; //item has bigger drop chance or sold in shops } for (k = 0; k< MAX_SEARCH; k++) { @@ -3636,61 +3641,31 @@ static bool mob_parse_dbrow(char** str) /*========================================== * mob_db.txt reading *------------------------------------------*/ -static int mob_readdb(void) +static bool mob_readdb_sub(char* fields[], int columns, int current) +{ + return mob_parse_dbrow(fields); +} + +static void mob_readdb(void) { const char* filename[] = { "mob_db.txt", "mob_db2.txt" }; int fi; for( fi = 0; fi < ARRAYLENGTH(filename); ++fi ) { - uint32 lines = 0, count = 0; - char line[1024]; char path[256]; - FILE* fp; - - sprintf(path, "%s/%s", db_path, filename[fi]); - fp = fopen(path, "r"); - if(fp == NULL) { - if(fi > 0) - continue; - return -1; - } - - // process rows one by one - while(fgets(line, sizeof(line), fp)) + + if(fi > 0) { - char *str[38+2*MAX_MOB_DROP], *p, *np; - int i; - - lines++; - if(line[0] == '/' && line[1] == '/') - continue; - - for(i = 0, p = line; i < 38 + 2*MAX_MOB_DROP; i++) + sprintf(path, "%s/%s", db_path, filename[fi]); + if(!exists(path)) { - str[i] = p; - if((np = strchr(p, ',')) != NULL) { - *np = '\0'; p = np + 1; - } - } - - if(i < 38 + 2*MAX_MOB_DROP) { - ShowWarning("mob_readdb: Insufficient columns for mob with id: %d, skipping.\n", atoi(str[0])); continue; } - - if (!mob_parse_dbrow(str)) - continue; - - count++; } - fclose(fp); - - ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename[fi]); + sv_readdb(db_path, filename[fi], ',', 38+2*MAX_MOB_DROP, 38+2*MAX_MOB_DROP, -1, &mob_readdb_sub); } - - return 0; } #ifndef TXT_ONLY @@ -3753,73 +3728,42 @@ static int mob_read_sqldb(void) /*========================================== * MOB display graphic change data reading *------------------------------------------*/ -static int mob_readdb_mobavail(void) +static bool mob_readdb_mobavail(char* str[], int columns, int current) { - FILE *fp; - char line[1024]; - int ln=0; - int class_,j,k; - char *str[20],*p,*np; + int class_, k; - sprintf(line, "%s/mob_avail.txt", db_path); - if( (fp=fopen(line,"r"))==NULL ){ - ShowError("can't read %s\n", line); - return -1; - } + class_=atoi(str[0]); - while(fgets(line, sizeof(line), fp)) + if(mob_db(class_) == mob_dummy) // 値が異常なら処理しない。 { - if(line[0]=='/' && line[1]=='/') - continue; - memset(str,0,sizeof(str)); - - for(j=0,p=line;j<12;j++){ - if((np=strchr(p,','))!=NULL){ - str[j]=p; - *np=0; - p=np+1; - } else - str[j]=p; - } - - if(str[0]==NULL) - continue; - - class_=atoi(str[0]); - if (class_ == 0) - continue; //Leave blank lines alone... [Skotlex] - - if(mob_db(class_) == mob_dummy) // 値が異常なら処理しない。 - continue; - - k=atoi(str[1]); - if(k < 0) - continue; - - memset(&mob_db_data[class_]->vd, 0, sizeof(struct view_data)); - mob_db_data[class_]->vd.class_=k; - - //Player sprites - if(pcdb_checkid(k) && j>=12) { - mob_db_data[class_]->vd.sex=atoi(str[2]); - mob_db_data[class_]->vd.hair_style=atoi(str[3]); - mob_db_data[class_]->vd.hair_color=atoi(str[4]); - mob_db_data[class_]->vd.weapon=atoi(str[5]); - mob_db_data[class_]->vd.shield=atoi(str[6]); - mob_db_data[class_]->vd.head_top=atoi(str[7]); - mob_db_data[class_]->vd.head_mid=atoi(str[8]); - mob_db_data[class_]->vd.head_bottom=atoi(str[9]); - mob_db_data[class_]->option=atoi(str[10])&~(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE); - mob_db_data[class_]->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris - } - else if(str[2] && atoi(str[2]) > 0) - mob_db_data[class_]->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris] + ShowWarning("mob_readdb_mobavail: Unknown mob id %d.\n", class_); + return false; + } - ln++; + k=atoi(str[1]); + + memset(&mob_db_data[class_]->vd, 0, sizeof(struct view_data)); + mob_db_data[class_]->vd.class_=k; + + //Player sprites + if(pcdb_checkid(k) && columns==12) { + mob_db_data[class_]->vd.sex=atoi(str[2]); + mob_db_data[class_]->vd.hair_style=atoi(str[3]); + mob_db_data[class_]->vd.hair_color=atoi(str[4]); + mob_db_data[class_]->vd.weapon=atoi(str[5]); + mob_db_data[class_]->vd.shield=atoi(str[6]); + mob_db_data[class_]->vd.head_top=atoi(str[7]); + mob_db_data[class_]->vd.head_mid=atoi(str[8]); + mob_db_data[class_]->vd.head_bottom=atoi(str[9]); + mob_db_data[class_]->option=atoi(str[10])&~(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE); + mob_db_data[class_]->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"mob_avail.txt"); - return 0; + else if(columns==3) + mob_db_data[class_]->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris] + else if( columns != 2 ) + return false; + + return true; } /*========================================== @@ -3842,7 +3786,7 @@ static int mob_read_randommonster(void) for( i = 0; i < ARRAYLENGTH(mobfile) && i < MAX_RANDOMMONSTER; i++ ) { - mob_db_data[0]->summonper[i] = 1002; // 設定し忘れた場合はポリンが出るようにしておく + mob_db_data[0]->summonper[i] = 1002; // Default fallback value, in case the database does not provide one sprintf(line, "%s/%s", db_path, mobfile[i]); fp=fopen(line,"r"); if(fp==NULL){ @@ -3894,8 +3838,10 @@ static int mob_read_randommonster(void) *------------------------------------------*/ static bool mob_parse_row_chatdb(char** str, const char* source, int line, int* last_msg_id) { + char* msg; struct mob_chat *ms; int msg_id; + size_t len; msg_id = atoi(str[0]); @@ -3917,13 +3863,29 @@ static bool mob_parse_row_chatdb(char** str, const char* source, int line, int* //Color ms->color=strtoul(str[1],NULL,0); //Message - if(strlen(str[2])>(CHAT_SIZE_MAX-1)){ + msg = str[2]; + len = strlen(msg); + + while( len && ( msg[len-1]=='\r' || msg[len-1]=='\n' ) ) + {// find EOL to strip + len--; + } + + if(len>(CHAT_SIZE_MAX-1)) + { if (msg_id != *last_msg_id) { ShowError("mob_chat: readdb: Message too long! Line %d, id: %d\n", line, msg_id); *last_msg_id = msg_id; } return false; } + else if( !len ) + { + ShowWarning("mob_parse_row_chatdb: Empty message for id %d.\n", msg_id); + return false; + } + + msg[len] = 0; // strip previously found EOL strncpy(ms->msg, str[2], CHAT_SIZE_MAX); return true; @@ -3987,9 +3949,8 @@ static void mob_readchatdb(void) /*========================================== * processes one mob_skill_db entry - * @param last_mob_id ensures that only one error message per mob id is printed *------------------------------------------*/ -static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, int* last_mob_id) +static bool mob_parse_row_mobskilldb(char** str, int columns, int current) { static const struct { char str[32]; @@ -4058,6 +4019,7 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i { "around4", MST_AROUND4 }, { "around", MST_AROUND }, }; + static int last_mob_id = 0; // ensures that only one error message per mob id is printed struct mob_skill *ms, gms; int mob_id; @@ -4067,9 +4029,9 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i if (mob_id > 0 && mob_db(mob_id) == mob_dummy) { - if (mob_id != *last_mob_id) { - ShowError("mob_skill: Non existant Mob id %d at %s, line %d\n", mob_id, source, line); - *last_mob_id = mob_id; + if (mob_id != last_mob_id) { + ShowError("mob_parse_row_mobskilldb: Non existant Mob id %d\n", mob_id); + last_mob_id = mob_id; } return false; } @@ -4089,9 +4051,9 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i ARR_FIND( 0, MAX_MOBSKILL, i, (ms = &mob_db_data[mob_id]->skill[i])->skill_id == 0 ); if( i == MAX_MOBSKILL ) { - if (mob_id != *last_mob_id) { - ShowError("mob_skill: readdb: too many skills! Line %d in %d[%s]\n", line, mob_id, mob_db_data[mob_id]->sprite); - *last_mob_id = mob_id; + if (mob_id != last_mob_id) { + ShowError("mob_parse_row_mobskilldb: Too many skills for monster %d[%s]\n", mob_id, mob_db_data[mob_id]->sprite); + last_mob_id = mob_id; } return false; } @@ -4102,7 +4064,7 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i if( j < ARRAYLENGTH(state) ) ms->state = state[j].id; else { - ShowWarning("mob_skill: Unrecognized state %s at %s, line %d\n", str[2], source, line); + ShowWarning("mob_parse_row_mobskilldb: Unrecognized state %s\n", str[2]); ms->state = MSS_ANY; } @@ -4111,9 +4073,9 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i if (j<=0 || j>MAX_SKILL_DB) //fixed Lupus { if (mob_id < 0) - ShowError("Invalid Skill ID (%d) for all mobs\n", j); + ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for all mobs\n", j); else - ShowError("Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob_db_data[mob_id]->sprite); + ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob_db_data[mob_id]->sprite); return false; } ms->skill_id=j; @@ -4146,7 +4108,7 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i if( j < ARRAYLENGTH(target) ) ms->target = target[j].id; else { - ShowWarning("mob_skill: Unrecognized target %s at %s, line %d\n", str[9], source, line); + ShowWarning("mob_parse_row_mobskilldb: Unrecognized target %s for %d\n", str[9], mob_id); ms->target = MST_TARGET; } @@ -4155,13 +4117,13 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i { //Ground skill. if (ms->target > MST_AROUND) { - ShowWarning("Wrong mob skill target for ground skill %d (%s) for %s.\n", + ShowWarning("mob_parse_row_mobskilldb: Wrong mob skill target for ground skill %d (%s) for %s.\n", ms->skill_id, skill_get_name(ms->skill_id), mob_id < 0?"all mobs":mob_db_data[mob_id]->sprite); ms->target = MST_TARGET; } } else if (ms->target > MST_MASTER) { - ShowWarning("Wrong mob skill target 'around' for non-ground skill %d (%s) for %s\n.", + ShowWarning("mob_parse_row_mobskilldb: Wrong mob skill target 'around' for non-ground skill %d (%s) for %s\n.", ms->skill_id, skill_get_name(ms->skill_id), mob_id < 0?"all mobs":mob_db_data[mob_id]->sprite); ms->target = MST_TARGET; @@ -4172,7 +4134,7 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i if( j < ARRAYLENGTH(cond1) ) ms->cond1 = cond1[j].id; else { - ShowWarning("mob_skill: Unrecognized condition 1 %s at %s, line %d\n", str[10], source, line); + ShowWarning("mob_parse_row_mobskilldb: Unrecognized condition 1 %s for %d\n", str[10], mob_id); ms->cond1 = -1; } @@ -4246,7 +4208,7 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i /*========================================== * mob_skill_db.txt reading *------------------------------------------*/ -static int mob_readskilldb(void) +static void mob_readskilldb(void) { const char* filename[] = { "mob_skill_db.txt", "mob_skill_db2.txt" }; int fi; @@ -4254,130 +4216,72 @@ static int mob_readskilldb(void) if( battle_config.mob_skill_rate == 0 ) { ShowStatus("Mob skill use disabled. Not reading mob skills.\n"); - return 0; + return; } for( fi = 0; fi < ARRAYLENGTH(filename); ++fi ) { - uint32 lines = 0, count = 0; - char line[1024]; - int i; - int tmp = 0; - char path[256]; - FILE *fp; - - sprintf(path, "%s/%s", db_path, filename[fi]); - fp = fopen(path, "r"); - if( fp == NULL ) - { - ShowWarning("mob_readskilldb: File not found \"%s\", skipping.\n", path); - continue; - } - // process rows one by one - while(fgets(line, sizeof(line), fp)) + if(fi > 0) { - char *str[20], *p, *np; - int j=0; - - lines++; - if(line[0] == '/' && line[1] == '/') - continue; - memset(str, 0, sizeof(str)); - - p = line; - while( ISSPACE(*p) ) - ++p; - if( *p == '\0' ) - continue;// empty line - for(i = 0; i < 19; i++) + sprintf(path, "%s/%s", db_path, filename[fi]); + if(!exists(path)) { - str[i] = p; - if((np = strchr(p, ',')) != NULL) { - *np = '\0'; p = np + 1; j++; - } - } - - if ( j < 18 || str[18]==NULL ) - { - ShowError("mob_readskilldb: Insufficient number of fields for skill at %s, line %d\n", filename[fi], lines); continue; } - - if( !mob_parse_row_mobskilldb(str, path, lines, &tmp) ) - continue; - - count++; } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", filename[fi]); + + sv_readdb(db_path, filename[fi], ',', 19, 19, -1, &mob_parse_row_mobskilldb); } - return 0; } /*========================================== - * mob_race_db.txt reading + * mob_race2_db.txt reading *------------------------------------------*/ -static int mob_readdb_race(void) +static bool mob_readdb_race2(char* fields[], int columns, int current) { - FILE *fp; - char line[1024]; - int race,j,k; - char *str[20],*p,*np; + int race, mobid, i; - sprintf(line, "%s/mob_race2_db.txt", db_path); - if( (fp=fopen(line,"r"))==NULL ){ - ShowError("can't read %s\n", line); - return -1; - } - - while(fgets(line, sizeof(line), fp)) - { - if(line[0]=='/' && line[1]=='/') - continue; - memset(str,0,sizeof(str)); + race = atoi(fields[0]); - 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; + if (race < RC2_NONE || race >= RC2_MAX) + { + ShowWarning("mob_readdb_race2: Unknown race2 %d.\n", race); + return false; + } - race=atoi(str[0]); - if (race < RC2_NONE || race >= RC2_MAX) + for(i = 1; i<columns; i++) + { + mobid = atoi(fields[i]); + if (mob_db(mobid) == mob_dummy) + { + ShowWarning("mob_readdb_race2: Unknown mob id %d for race2 %d.\n", mobid, race); continue; - - for (j=1; j<20; j++) { - if (!str[j]) - break; - k=atoi(str[j]); - if (mob_db(k) == mob_dummy) - continue; - mob_db_data[k]->race2 = race; } + mob_db_data[mobid]->race2 = race; } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","mob_race2_db.txt"); - return 0; + return true; } -void mob_reload(void) +static void mob_load(void) { - int i; #ifndef TXT_ONLY - if(db_use_sqldbs) - mob_read_sqldb(); - else + if(db_use_sqldbs) + mob_read_sqldb(); + else #endif /* TXT_ONLY */ mob_readdb(); - mob_readdb_mobavail(); + sv_readdb(db_path, "mob_avail.txt", ',', 2, 12, -1, &mob_readdb_mobavail); mob_read_randommonster(); + mob_readchatdb(); + mob_readskilldb(); + sv_readdb(db_path, "mob_race2_db.txt", ',', 2, 20, -1, &mob_readdb_race2); +} + +void mob_reload(void) +{ + int i; //Mob skills need to be cleared before re-reading them. [Skotlex] for (i = 0; i < MAX_MOB_DB; i++) @@ -4386,9 +4290,8 @@ void mob_reload(void) memset(&mob_db_data[i]->skill,0,sizeof(mob_db_data[i]->skill)); mob_db_data[i]->maxskill=0; } - mob_readchatdb(); - mob_readskilldb(); - mob_readdb_race(); + + mob_load(); } void mob_clear_spawninfo() @@ -4410,18 +4313,7 @@ int do_init_mob(void) item_drop_ers = ers_new(sizeof(struct item_drop)); item_drop_list_ers = ers_new(sizeof(struct item_drop_list)); -#ifndef TXT_ONLY - if(db_use_sqldbs) - mob_read_sqldb(); - else -#endif /* TXT_ONLY */ - mob_readdb(); - - mob_readdb_mobavail(); - mob_read_randommonster(); - mob_readchatdb(); - mob_readskilldb(); - mob_readdb_race(); + mob_load(); add_timer_func_list(mob_delayspawn,"mob_delayspawn"); add_timer_func_list(mob_delay_item_drop,"mob_delay_item_drop"); diff --git a/src/map/mob.h b/src/map/mob.h index e8bd0a837..ce896646c 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -55,6 +55,13 @@ enum MobSkillState { MSS_ANYTARGET, }; +enum MobDamageLogFlag +{ + MDLF_NORMAL = 0, + MDLF_HOMUN, + MDLF_PET, +}; + struct mob_skill { enum MobSkillState state; short skill_id,skill_lv; diff --git a/src/map/npc.c b/src/map/npc.c index dcfa52193..4b188c177 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -176,7 +176,7 @@ int npc_enable(const char* name, int flag) if (nd->class_ == WARP_CLASS || nd->class_ == FLAG_CLASS) { //Client won't display option changes for these classes [Toms] if (nd->sc.option&(OPTION_HIDE|OPTION_INVISIBLE)) - clif_clearunit_area(&nd->bl, 0); + clif_clearunit_area(&nd->bl, CLR_OUTSIGHT); else clif_spawn(&nd->bl); } else @@ -206,7 +206,7 @@ int npc_event_dequeue(struct map_session_data* sd) if(sd->npc_id) { //Current script is aborted. if(sd->state.using_fake_npc){ - clif_clearunit_single(sd->npc_id, 0, sd->fd); + clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); sd->state.using_fake_npc = 0; } if (sd->st) { @@ -866,7 +866,7 @@ int npc_touch_areanpc(struct map_session_data* sd, int m, int x, int y) case WARP: if( pc_ishiding(sd) ) break; // hidden chars cannot use warps - pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0); + pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,CLR_OUTSIGHT); break; case SCRIPT: if( npc_ontouch_event(sd,map[m].npc[i]) > 0 && npc_ontouch2_event(sd,map[m].npc[i]) > 0 ) @@ -923,7 +923,7 @@ int npc_touch_areanpc2(struct mob_data *md) xs = map_mapindex2mapid(map[m].npc[i]->u.warp.mapindex); if( m < 0 ) break; // Cannot Warp between map servers - if( unit_warp(&md->bl, xs, map[m].npc[i]->u.warp.x, map[m].npc[i]->u.warp.y, 0) == 0 ) + if( unit_warp(&md->bl, xs, map[m].npc[i]->u.warp.x, map[m].npc[i]->u.warp.y, CLR_OUTSIGHT) == 0 ) return 1; // Warped break; case SCRIPT: @@ -1142,14 +1142,24 @@ static int npc_buylist_sub(struct map_session_data* sd, int n, unsigned short* i { char npc_ev[NAME_LENGTH*2+3]; int i; - int regkey = add_str("@bought_nameid"); - int regkey2 = add_str("@bought_quantity"); - snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnBuyItem", nd->exname); - for(i=0;i<n;i++){ - pc_setreg(sd,regkey+(i<<24),(int)item_list[i*2+1]); - pc_setreg(sd,regkey2+(i<<24),(int)item_list[i*2]); + int key_nameid = 0; + int key_amount = 0; + + // discard old contents + script_cleararray_pc(sd, "@bought_nameid", (void*)0); + script_cleararray_pc(sd, "@bought_quantity", (void*)0); + + // save list of bought items + for( i = 0; i < n; i++ ) + { + script_setarray_pc(sd, "@bought_nameid", i, (void*)(intptr)item_list[i*2+1], &key_nameid); + script_setarray_pc(sd, "@bought_quantity", i, (void*)(intptr)item_list[i*2], &key_amount); } + + // invoke event + snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnBuyItem", nd->exname); npc_event(sd, npc_ev, 0); + return 0; } /*========================================== @@ -1251,8 +1261,6 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) nd = npc_checknear(sd,map_id2bl(sd->npc_shopid)); if( nd == NULL ) return 3; - if( nd->master_nd != NULL ) //Script-based shops. - return npc_buylist_sub(sd,n,item_list,nd->master_nd); if( nd->subtype != SHOP ) return 3; @@ -1287,6 +1295,11 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) amount = item_list[i*2+0] = 1; } + if( nd->master_nd ) + {// Script-controlled shops decide by themselves, what can be bought and for what price. + continue; + } + switch( pc_checkadditem(sd,nameid,amount) ) { case ADDITEM_EXIST: @@ -1300,13 +1313,15 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) return 2; } - if( !itemdb_value_notdc(nameid) ) - value = pc_modifybuyvalue(sd,value); + value = pc_modifybuyvalue(sd,value); z += (double)value * amount; w += itemdb_weight(nameid) * amount; } + if( nd->master_nd != NULL ) //Script-based shops. + return npc_buylist_sub(sd,n,item_list,nd->master_nd); + if( z > (double)sd->status.zeny ) return 1; // Not enough Zeny if( w + sd->weight > sd->max_weight ) @@ -1356,90 +1371,139 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) return 0; } -/*========================================== - * - *------------------------------------------*/ + +/// npc_selllist for script-controlled shops +static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd) +{ + char npc_ev[NAME_LENGTH*2+3]; + int i, idx; + int key_nameid = 0; + int key_amount = 0; + + // discard old contents + script_cleararray_pc(sd, "@sold_nameid", (void*)0); + script_cleararray_pc(sd, "@sold_quantity", (void*)0); + + // save list of to be sold items + for( i = 0; i < n; i++ ) + { + idx = item_list[i*2]-2; + + script_setarray_pc(sd, "@sold_nameid", i, (void*)(intptr)sd->status.inventory[idx].nameid, &key_nameid); + script_setarray_pc(sd, "@sold_quantity", i, (void*)(intptr)item_list[i*2+1], &key_amount); + } + + // invoke event + snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnSellItem", nd->exname); + npc_event(sd, npc_ev, 0); + return 0; +} + + +/// Player item selling to npc shop. +/// +/// @param item_list 'n' pairs <index,amount> +/// @return result code for clif_parse_NpcSellListSend int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) { double z; int i,skill; struct npc_data *nd; - + nullpo_retr(1, sd); nullpo_retr(1, item_list); - if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL) + if( ( nd = npc_checknear(sd, map_id2bl(sd->npc_shopid)) ) == NULL || nd->subtype != SHOP ) + { return 1; - nd = nd->master_nd; //For OnSell triggers. + } - for(i=0,z=0;i<n;i++) { - int nameid, idx; - short qty; - idx = item_list[i*2]-2; - qty = (short)item_list[i*2+1]; - - if (idx <0 || idx >=MAX_INVENTORY || qty < 0) - break; - - nameid=sd->status.inventory[idx].nameid; - if (nameid == 0 || !sd->inventory_data[idx] || - sd->status.inventory[idx].amount < qty) - break; - - if (sd->inventory_data[idx]->flag.value_notoc) - z+=(double)qty*sd->inventory_data[idx]->value_sell; - else - z+=(double)qty*pc_modifysellvalue(sd,sd->inventory_data[idx]->value_sell); + z = 0; + + // verify the sell list + for( i = 0; i < n; i++ ) + { + int nameid, amount, idx, value; + + idx = item_list[i*2]-2; + amount = item_list[i*2+1]; - if(sd->inventory_data[idx]->type == IT_PETEGG && - sd->status.inventory[idx].card[0] == CARD0_PET) + if( idx >= MAX_INVENTORY || idx < 0 || amount < 0 ) { - if(search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0) - intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1],sd->status.inventory[idx].card[2])); + return 1; + } + + nameid = sd->status.inventory[idx].nameid; + + if( !nameid || !sd->inventory_data[idx] || sd->status.inventory[idx].amount < amount ) + { + return 1; } - if(log_config.enable_logs&0x20) //Logs items, Sold to NPC (S)hop [Lupus] - log_pick_pc(sd, "S", nameid, -qty, &sd->status.inventory[idx]); + if( nd->master_nd ) + {// Script-controlled shops decide by themselves, what can be sold and at what price. + continue; + } + + value = pc_modifysellvalue(sd, sd->inventory_data[idx]->value_sell); + + z+= (double)value*amount; + } + + if( nd->master_nd ) + {// Script-controlled shops + return npc_selllist_sub(sd, n, item_list, nd->master_nd); + } + + // delete items + for( i = 0; i < n; i++ ) + { + int nameid, amount, idx; + + idx = item_list[i*2]-2; + amount = item_list[i*2+1]; + nameid = sd->status.inventory[idx].nameid; + + //Logs items, Sold to NPC (S)hop [Lupus] + if( log_config.enable_logs&0x20 ) + log_pick_pc(sd, "S", nameid, -amount, &sd->status.inventory[idx]); + //Logs - if(nd) { - pc_setreg(sd,add_str("@sold_nameid")+(i<<24),(int)sd->status.inventory[idx].nameid); - pc_setreg(sd,add_str("@sold_quantity")+(i<<24),qty); + if( sd->inventory_data[idx]->type == IT_PETEGG && sd->status.inventory[idx].card[0] == CARD0_PET ) + { + if( search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0 ) + { + intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2])); + } } - pc_delitem(sd,idx,qty,0,6); + + pc_delitem(sd, idx, amount, 0, 6); } - if (z > MAX_ZENY) z = MAX_ZENY; + if( z > MAX_ZENY ) + z = MAX_ZENY; - if(log_config.zeny) //Logs (S)hopping Zeny [Lupus] + //Logs (S)hopping Zeny [Lupus] + if( log_config.zeny ) log_zeny(sd, "S", sd, (int)z); + //Logs - pc_getzeny(sd,(int)z); - - if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) { - if (sd->status.skill[MC_OVERCHARGE].flag != 0) + pc_getzeny(sd, (int)z); + + // custom merchant shop exp bonus + 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) { + if( skill > 0 ) + { z = z * (double)skill * (double)battle_config.shop_exp/10000.; - if (z < 1) + if( z < 1 ) z = 1; - pc_gainexp(sd,NULL,0,(int)z, false); + pc_gainexp(sd, NULL, 0, (int)z, false); } } - - if(nd) { - char npc_ev[NAME_LENGTH*2+3]; - snprintf(npc_ev, ARRAYLENGTH(npc_ev), "%s::OnSellItem", nd->exname); - npc_event(sd, npc_ev, 0); - } - - if (i<n) { - //Error/Exploit... of some sort. If we return 1, the client will not mark - //any item as deleted even though a few were sold. In such a case, we - //have no recourse but to kick them out so their inventory will refresh - //correctly on relog. [Skotlex] - if (i) set_eof(sd->fd); - return 1; - } + return 0; } @@ -1451,7 +1515,7 @@ int npc_remove_map(struct npc_data* nd) if(nd->bl.prev == NULL || nd->bl.m < 0) return 1; //Not assigned to a map. m = nd->bl.m; - clif_clearunit_area(&nd->bl,2); + clif_clearunit_area(&nd->bl,CLR_RESPAWN); npc_unsetcells(nd); map_delblock(&nd->bl); //Remove npc from map[].npc list. [Skotlex] @@ -1875,8 +1939,8 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const id->name, nameid, value, (int)(value*0.75), id->value_sell, (int)(id->value_sell*1.24), filepath, strline(buffer,start-buffer)); } //for logs filters, atcommands and iteminfo script command - if( id->maxchance <= 0 ) - id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop + if( id->maxchance == 0 ) + id->maxchance = -1; // -1 would show that the item's sold in NPC Shop items[i].nameid = nameid; items[i].value = value; @@ -2550,7 +2614,7 @@ void npc_setclass(struct npc_data* nd, short class_) if( nd->class_ == class_ ) return; - clif_clearunit_area(&nd->bl, 0);// fade out + clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);// fade out nd->class_ = class_; status_set_viewdata(&nd->bl, class_); clif_spawn(&nd->bl);// fade in @@ -3129,7 +3193,12 @@ void npc_parsesrcfile(const char* filepath) {// Incorrect map, we must skip the script info... ShowError("npc_parsesrcfile: Unknown map '%s' in file '%s', line '%d'. Skipping line...\n", mapname, filepath, strline(buffer,p-buffer)); if( strcasecmp(w2,"script") == 0 && count > 3 ) - p = npc_skip_script(p,buffer,filepath); + { + if((p = npc_skip_script(p,buffer,filepath)) == NULL) + { + break; + } + } p = strchr(p,'\n');// next line continue; } @@ -3137,7 +3206,12 @@ void npc_parsesrcfile(const char* filepath) if( m < 0 ) {// "mapname" is not assigned to this server, we must skip the script info... if( strcasecmp(w2,"script") == 0 && count > 3 ) - p = npc_skip_script(p,buffer,filepath); + { + if((p = npc_skip_script(p,buffer,filepath)) == NULL) + { + break; + } + } p = strchr(p,'\n');// next line continue; } @@ -3270,7 +3344,7 @@ int npc_reload(void) npc_unload((struct npc_data *)bl); break; case BL_MOB: - unit_free(bl,0); + unit_free(bl,CLR_OUTSIGHT); break; } } @@ -3304,6 +3378,9 @@ int npc_reload(void) npc_warp = npc_shop = npc_script = 0; npc_mob = npc_cache_mob = npc_delay_mob = 0; + // reset mapflags + map_flags_init(); + //TODO: the following code is copy-pasted from do_init_npc(); clean it up // Reloading npcs now for (nsl = npc_src_files; nsl; nsl = nsl->next) @@ -3350,7 +3427,7 @@ int do_final_npc(void) if (bl->type == BL_NPC) npc_unload((struct npc_data *)bl); else if (bl->type&(BL_MOB|BL_PET|BL_HOM|BL_MER)) - unit_free(bl, 0); + unit_free(bl, CLR_OUTSIGHT); } } diff --git a/src/map/party.c b/src/map/party.c index e6a7ffd8b..407221cde 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -30,8 +30,9 @@ static DBMap* party_db; // int party_id -> struct party_data* static DBMap* party_booking_db; // Party Booking [Spiria] +static unsigned long party_booking_nextid = 1; + int party_send_xy_timer(int tid, unsigned int tick, int id, intptr data); -bool check_party_leader(struct map_session_data *sd, struct party_data *p); // Party Booking [Spiria] /*========================================== * Fills the given party_member structure according to the sd provided. @@ -515,11 +516,6 @@ int party_leave(struct map_session_data *sd) if( i == MAX_PARTY ) return 0; - if( check_party_leader(sd, p) ){ // when party leader leaves party, cancel booking. - party_booking_delete(sd,true); - clif_PartyBookingDeleteAck(sd,0); - } - intif_party_leave(p->party.party_id,sd->status.account_id,sd->status.char_id); return 1; } @@ -665,8 +661,6 @@ bool party_changeleader(struct map_session_data *sd, struct map_session_data *ts //Update info. intif_party_leaderchange(p->party.party_id,p->party.member[tmi].account_id,p->party.member[tmi].char_id); clif_party_info(p,NULL); - party_booking_delete(sd, true); // Party Booking [Spiria] - clif_PartyBookingDeleteAck(sd, 0); // Close small window return true; } @@ -1070,52 +1064,25 @@ int party_foreachsamemap(int (*func)(struct block_list*,va_list),struct map_sess * Party Booking in KRO [Spiria] *------------------------------------------*/ -static struct party_booking_ad_info* create_party_booking_data(int party_id) +static struct party_booking_ad_info* create_party_booking_data(void) { struct party_booking_ad_info *pb_ad; CREATE(pb_ad, struct party_booking_ad_info, 1); - pb_ad->index = party_id; + pb_ad->index = party_booking_nextid++; return pb_ad; } -struct party_booking_ad_info* party_booking_getdata(unsigned long index) -{ - struct party_booking_ad_info *pb_ad; - pb_ad = (struct party_booking_ad_info*)idb_get(party_booking_db, index); - return pb_ad; -} - -bool check_party_leader(struct map_session_data *sd, struct party_data *p) -{ - int i; - - if (!sd || !sd->status.party_id) return false; - - if( p == NULL ) return false; - - ARR_FIND(0, MAX_PARTY, i, p->party.member[i].leader && p->party.member[i].online && p->data[i].sd == sd); - if(i == MAX_PARTY) return false; - - return true; -} - void party_booking_register(struct map_session_data *sd, short level, short mapid, short* job) { struct party_booking_ad_info *pb_ad; - struct party_data *p=party_search(sd->status.party_id); int i; - if (!check_party_leader(sd, p)) { - clif_PartyBookingRegisterAck(sd, 1); - return; - } - - pb_ad = (struct party_booking_ad_info*)idb_get(party_booking_db, p->party.party_id); + pb_ad = (struct party_booking_ad_info*)idb_get(party_booking_db, sd->status.char_id); if( pb_ad == NULL ) { - pb_ad = create_party_booking_data(p->party.party_id); - idb_put(party_booking_db, pb_ad->index, pb_ad); + pb_ad = create_party_booking_data(); + idb_put(party_booking_db, sd->status.char_id, pb_ad); } memcpy(pb_ad->charname,sd->status.name,NAME_LENGTH); @@ -1123,84 +1090,80 @@ void party_booking_register(struct map_session_data *sd, short level, short mapi pb_ad->p_detail.level = level; pb_ad->p_detail.mapid = mapid; - for(i=0;i<6;i++) + for(i=0;i<PARTY_BOOKING_JOBS;i++) if(job[i] != 0xFF) pb_ad->p_detail.job[i] = job[i]; else pb_ad->p_detail.job[i] = -1; clif_PartyBookingRegisterAck(sd, 0); clif_PartyBookingInsertNotify(sd, pb_ad); // Notice - clif_PartyBookingSearchAck(sd->fd, &pb_ad->index, 1, false); // Update Client! - return; } void party_booking_update(struct map_session_data *sd, short* job) { int i; - struct party_data *p=party_search(sd->status.party_id); struct party_booking_ad_info *pb_ad; - if (!check_party_leader(sd, p)) { - return; - } - - pb_ad = (struct party_booking_ad_info*)idb_get(party_booking_db, p->party.party_id); + pb_ad = (struct party_booking_ad_info*)idb_get(party_booking_db, sd->status.char_id); if( pb_ad == NULL ) return; pb_ad->starttime = (int)time(NULL);// Update time. - for(i=0;i<6;i++) + for(i=0;i<PARTY_BOOKING_JOBS;i++) if(job[i] != 0xFF) pb_ad->p_detail.job[i] = job[i]; else pb_ad->p_detail.job[i] = -1; clif_PartyBookingUpdateNotify(sd, pb_ad); - return; } void party_booking_search(struct map_session_data *sd, short level, short mapid, short job, unsigned long lastindex, short resultcount) { struct party_booking_ad_info *pb_ad; int i, count=0; - unsigned long index_list[10]; + struct party_booking_ad_info* result_list[PARTY_BOOKING_RESULTS]; bool more_result = false; DBIterator* iter = party_booking_db->iterator(party_booking_db); - memset(index_list, 0, sizeof(index_list)); + memset(result_list, 0, sizeof(result_list)); for( pb_ad = (struct party_booking_ad_info*)iter->first(iter,NULL); iter->exists(iter); pb_ad = (struct party_booking_ad_info*)iter->next(iter,NULL) ) { - if (pb_ad->index < lastindex || (pb_ad->p_detail.level < level || pb_ad->p_detail.level-15 > level)) + if (pb_ad->index < lastindex || (level && (pb_ad->p_detail.level < level-15 || pb_ad->p_detail.level > level))) continue; - if (count >= 10){ + if (count >= PARTY_BOOKING_RESULTS){ more_result = true; break; } if (mapid == 0 && job == -1) - index_list[count] = pb_ad->index; + result_list[count] = pb_ad; else if (mapid == 0) { - for(i=0; i<6; i++) + for(i=0; i<PARTY_BOOKING_JOBS; i++) if (pb_ad->p_detail.job[i] == job && job != -1) - index_list[count] = pb_ad->index; + result_list[count] = pb_ad; } else if (job == -1){ if (pb_ad->p_detail.mapid == mapid) - index_list[count] = pb_ad->index; + result_list[count] = pb_ad; + } + if( result_list[count] ) + { + count++; } - count++; } iter->destroy(iter); - clif_PartyBookingSearchAck(sd->fd, index_list, count, more_result); + clif_PartyBookingSearchAck(sd->fd, result_list, count, more_result); } -bool party_booking_delete(struct map_session_data *sd, bool force_delete) +bool party_booking_delete(struct map_session_data *sd) { - struct party_data *p=party_search(sd->status.party_id); - if (!check_party_leader(sd, p) && !force_delete) { - return false; + struct party_booking_ad_info* pb_ad; + + if((pb_ad = (struct party_booking_ad_info*)idb_get(party_booking_db, sd->status.char_id))!=NULL) + { + clif_PartyBookingDeleteNotify(sd, pb_ad->index); + idb_remove(party_booking_db,sd->status.char_id); } - clif_PartyBookingDeleteNotify(sd, sd->status.party_id); - idb_remove(party_booking_db,sd->status.party_id); return true; } diff --git a/src/map/party.h b/src/map/party.h index d396d0fe6..31e46f5ec 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -12,6 +12,9 @@ struct item; #include <stdarg.h> +#define PARTY_BOOKING_JOBS 6 +#define PARTY_BOOKING_RESULTS 10 + struct party_member_data { struct map_session_data *sd; unsigned int hp; //For HP,x,y refreshing. @@ -34,7 +37,7 @@ struct party_data { struct party_booking_detail { short level; short mapid; - short job[6]; + short job[PARTY_BOOKING_JOBS]; }; struct party_booking_ad_info { @@ -83,10 +86,9 @@ int party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_ses /*========================================== * Party Booking in KRO [Spiria] *------------------------------------------*/ -struct party_booking_ad_info* party_booking_getdata(unsigned long index); void party_booking_register(struct map_session_data *sd, short level, short mapid, short* job); void party_booking_update(struct map_session_data *sd, short* job); void party_booking_search(struct map_session_data *sd, short level, short mapid, short job, unsigned long lastindex, short resultcount); -bool party_booking_delete(struct map_session_data *sd, bool force_delete); +bool party_booking_delete(struct map_session_data *sd); #endif /* _PARTY_H_ */ diff --git a/src/map/path.c b/src/map/path.c index e2e3ae44b..bbbb99a08 100644 --- a/src/map/path.c +++ b/src/map/path.c @@ -345,7 +345,7 @@ bool path_search(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int xs = md->xs-1; // あらかじめ1減算しておく ys = md->ys-1; - while(1) + for(;;) { int e=0,f=0,dist,cost,dc[4]={0,0,0,0}; diff --git a/src/map/pc.c b/src/map/pc.c index 7ad0ecfe4..94121eba9 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -61,15 +61,13 @@ struct fame_list taekwon_fame_list[MAX_FAME_LIST]; static unsigned short equip_pos[EQI_MAX]={EQP_ACC_L,EQP_ACC_R,EQP_SHOES,EQP_GARMENT,EQP_HEAD_LOW,EQP_HEAD_MID,EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_AMMO}; #define MOTD_LINE_SIZE 128 -char motd_text[MOTD_LINE_SIZE][256]; // Message of the day buffer [Valaris] +static char motd_text[MOTD_LINE_SIZE][CHAT_SIZE_MAX]; // Message of the day buffer [Valaris] struct duel duel_list[MAX_DUEL]; int duel_count = 0; -extern int item_delays; // [Paradox924X] - //Links related info to the sd->hate_mob[]/sd->feel_map[] entries -const struct sg_data sg_info[3] = { +const struct sg_data sg_info[MAX_PC_FEELHATE] = { { SG_SUN_ANGER, SG_SUN_BLESS, SG_SUN_COMFORT, "PC_FEEL_SUN", "PC_HATE_MOB_SUN", is_day_of_sun }, { SG_MOON_ANGER, SG_MOON_BLESS, SG_MOON_COMFORT, "PC_FEEL_MOON", "PC_HATE_MOB_MOON", is_day_of_moon }, { SG_STAR_ANGER, SG_STAR_BLESS, SG_STAR_COMFORT, "PC_FEEL_STAR", "PC_HATE_MOB_STAR", is_day_of_star } @@ -532,11 +530,6 @@ int pc_setinventorydata(struct map_session_data *sd) for(i=0;i<MAX_INVENTORY;i++) { id = sd->status.inventory[i].nameid; sd->inventory_data[i] = id?itemdb_search(id):NULL; - if(sd->inventory_data[i] && sd->inventory_data[i]->delay > 0) { // Load delays - sd->item_delay[item_delays].nameid = sd->inventory_data[i]->nameid; - sd->item_delay[item_delays].tick = 0; - ++item_delays; - } } return 0; } @@ -612,7 +605,7 @@ int pc_setequipindex(struct map_session_data *sd) if( sd->status.inventory[i].equip & EQP_HAND_L ) { - if( sd->inventory_data[i] && sd->inventory_data[i]->type == 4 ) + if( sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON ) sd->weapontype2 = sd->inventory_data[i]->look; else sd->weapontype2 = 0; @@ -847,6 +840,23 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->class_ = MAPID_NOVICE; } else sd->class_ = i; + + // Checks and fixes to character status data, that are required + // in case of configuration change or stuff, which cannot be + // checked on char-server. + if( sd->status.hair < MIN_HAIR_STYLE || sd->status.hair > MAX_HAIR_STYLE ) + { + sd->status.hair = MIN_HAIR_STYLE; + } + if( sd->status.hair_color < MIN_HAIR_COLOR || sd->status.hair_color > MAX_HAIR_COLOR ) + { + sd->status.hair_color = MIN_HAIR_COLOR; + } + if( sd->status.clothes_color < MIN_CLOTH_COLOR || sd->status.clothes_color > MAX_CLOTH_COLOR ) + { + sd->status.clothes_color = MIN_CLOTH_COLOR; + } + //Initializations to null/0 unneeded since map_session_data was filled with 0 upon allocation. if(!sd->status.hp) pc_setdead(sd); sd->state.connect_new = 1; @@ -910,11 +920,11 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->hate_mob[i] = -1; // 位置の設定 - if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, 0)) != 0) { + if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, CLR_OUTSIGHT)) != 0) { ShowError ("Last_point_map %s - id %d not found (error code %d)\n", mapindex_id2name(sd->status.last_point.map), sd->status.last_point.map, i); // try warping to a default map instead (church graveyard) - if (pc_setpos(sd, mapindex_name2id(MAP_PRONTERA), 273, 354, 0) != 0) { + if (pc_setpos(sd, mapindex_name2id(MAP_PRONTERA), 273, 354, CLR_OUTSIGHT) != 0) { // if we fail again clif_authfail_fd(sd->fd, 0); return false; @@ -1037,7 +1047,7 @@ int pc_reg_received(struct map_session_data *sd) } //SG map and mob read [Komurka] - for(i=0;i<3;i++) //for now - someone need to make reading from txt/sql + for(i=0;i<MAX_PC_FEELHATE;i++) //for now - someone need to make reading from txt/sql { if ((j = pc_readglobalreg(sd,sg_info[i].feel_var))!=0) { sd->feel_map[i].index = j; @@ -1206,7 +1216,7 @@ int pc_calc_skilltree(struct map_session_data *sd) f = 1; if(!battle_config.skillfree) { - for(j = 0; j < 5; j++) { + for(j = 0; j < MAX_PC_SKILL_REQUIRE; j++) { if((k=skill_tree[c][i].need[j].id)) { if (!sd->status.skill[k].id || sd->status.skill[k].flag == 13) @@ -1303,7 +1313,7 @@ static void pc_check_skilltree(struct map_session_data *sd, int skill) if( sd->status.skill[id].id ) //Already learned continue; - for( j = 0; j < 5; j++ ) + for( j = 0; j < MAX_PC_SKILL_REQUIRE; j++ ) { if( (k = skill_tree[c][i].need[j].id) ) { @@ -1439,7 +1449,7 @@ int pc_disguise(struct map_session_data *sd, int class_) if (sd->bl.prev != NULL) { pc_stop_walking(sd, 0); - clif_clearunit_area(&sd->bl, 0); + clif_clearunit_area(&sd->bl, CLR_OUTSIGHT); } if (!class_) { @@ -1507,13 +1517,7 @@ static int pc_bonus_autospell_onskill(struct s_autospell *spell, int max, short for( i = 0; i < max && spell[i].id; i++ ) { - if( spell[i].flag == src_skill && spell[i].id == id && spell[i].lv == lv && (spell[i].card_id == card_id || spell[i].rate <= 0 || rate < 0) ) - { - if( !battle_config.autospell_stacking && spell[i].rate > 0 && rate > 0 ) - return 0; - rate += spell[i].rate; - break; - } + ; // each autospell works independently } if( i == max ) @@ -2358,6 +2362,10 @@ int pc_bonus(struct map_session_data *sd,int type,int val) if(sd->state.lr_flag != 2) sd->add_heal2_rate += val; break; + case SP_ADD_ITEM_HEAL_RATE: + if(sd->state.lr_flag != 2) + sd->itemhealrate2 += val; + break; default: ShowWarning("pc_bonus: unknown type %d %d !\n",type,val); break; @@ -3195,6 +3203,9 @@ int pc_checkadditem(struct map_session_data *sd,int nameid,int amount) nullpo_ret(sd); + if(amount > MAX_AMOUNT) + return ADDITEM_OVERAMOUNT; + if(!itemdb_isstackable(nameid)) return ADDITEM_NEW; @@ -3206,8 +3217,6 @@ int pc_checkadditem(struct map_session_data *sd,int nameid,int amount) } } - if(amount > MAX_AMOUNT) - return ADDITEM_OVERAMOUNT; return ADDITEM_NEW; } @@ -3536,7 +3545,7 @@ int pc_isUseitem(struct map_session_data *sd,int n) if( item == NULL ) return 0; //Not consumable item - if( item->type != IT_HEALING && item->type != IT_USABLE ) + if( item->type != IT_HEALING && item->type != IT_USABLE && item->type != IT_CASH ) return 0; if( !item->script ) //if it has no script, you can't really consume it! return 0; @@ -3662,7 +3671,7 @@ int pc_isUseitem(struct map_session_data *sd,int n) *------------------------------------------*/ int pc_useitem(struct map_session_data *sd,int n) { - unsigned int delay, tick = gettick(); + unsigned int tick = gettick(); int amount, i, nameid; struct script_code *script; @@ -3691,13 +3700,6 @@ int pc_useitem(struct map_session_data *sd,int n) // Store information for later use before it is lost (via pc_delitem) [Paradox924X] nameid = sd->inventory_data[n]->nameid; - delay = sd->inventory_data[n]->delay; - - if( sd->inventory_data[n]->delay > 0 ) { // Check if there is a delay on this item [Paradox924X] - ARR_FIND(0, item_delays, i, sd->item_delay[i].nameid == nameid); - if( i < item_delays && DIFF_TICK(sd->item_delay[i].tick, tick) > 0 ) - return 0; // Delay has not expired yet - } //Since most delay-consume items involve using a "skill-type" target cursor, //perform a skill-use check before going through. [Skotlex] @@ -3706,6 +3708,27 @@ int pc_useitem(struct map_session_data *sd,int n) if( sd->inventory_data[n]->flag.delay_consume && ( sd->ud.skilltimer != -1 /*|| !status_check_skilluse(&sd->bl, &sd->bl, ALL_RESURRECTION, 0)*/ ) ) return 0; + if( sd->inventory_data[n]->delay > 0 ) { // Check if there is a delay on this item [Paradox924X] + ARR_FIND(0, MAX_ITEMDELAYS, i, sd->item_delay[i].nameid == nameid || !sd->item_delay[i].nameid); + if( i < MAX_ITEMDELAYS ) + { + if( sd->item_delay[i].nameid ) + {// found + if( DIFF_TICK(sd->item_delay[i].tick, tick) > 0 ) + return 0; // Delay has not expired yet + } + else + {// not yet used item (all slots are initially empty) + sd->item_delay[i].nameid = nameid; + } + sd->item_delay[i].tick = tick + sd->inventory_data[n]->delay; + } + else + {// should not happen + ShowError("pc_useitem: Exceeded item delay array capacity! (nameid=%d, char_id=%d)\n", nameid, sd->status.char_id); + } + } + sd->itemid = sd->status.inventory[n].nameid; sd->itemindex = n; if(sd->catch_target_class != -1) //Abort pet catching. @@ -3741,10 +3764,8 @@ int pc_useitem(struct map_session_data *sd,int n) //Update item use time. sd->canuseitem_tick = tick + battle_config.item_use_interval; - if( itemdb_iscashfood(sd->status.inventory[n].nameid) ) + if( itemdb_iscashfood(nameid) ) sd->canusecashfood_tick = tick + battle_config.cashfood_use_interval; - if( delay > 0 && i < item_delays ) - sd->item_delay[i].tick = tick + delay; run_script(script,0,sd->bl.id,fake_nd->bl.id); potion_flag = 0; @@ -4038,7 +4059,7 @@ int pc_steal_coin(struct map_session_data *sd,struct block_list *target) * 1 - Invalid map index. * 2 - Map not in this map-server, and failed to locate alternate map-server. *------------------------------------------*/ -int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, uint8 clrtype) +int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, clr_type clrtype) { struct party_data *p; int m; @@ -4196,7 +4217,7 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y /*========================================== * PCのランダムワ?プ *------------------------------------------*/ -int pc_randomwarp(struct map_session_data *sd, int type) +int pc_randomwarp(struct map_session_data *sd, clr_type type) { int x,y,i=0; int m; @@ -4219,6 +4240,45 @@ int pc_randomwarp(struct map_session_data *sd, int type) return 0; } + +/// Warps one player to another. +/// @param sd player to warp. +/// @param pl_sd player to warp to. +int pc_warpto(struct map_session_data* sd, struct map_session_data* pl_sd) +{ + if( map[sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) + { + return -2; + } + + if( map[pl_sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) + { + return -3; + } + + return pc_setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, CLR_TELEPORT); +} + + +/// Recalls one player to another. +/// @param sd player to warp to. +/// @param pl_sd player to warp. +int pc_recall(struct map_session_data* sd, struct map_session_data* pl_sd) +{ + if( map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) + { + return -2; + } + + if( map[sd->bl.m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd) ) + { + return -3; + } + + return pc_setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN); +} + + /*========================================== * Records a memo point at sd's current position * pos - entry to replace, (-1: shift oldest entry out) @@ -4704,7 +4764,7 @@ int pc_follow_timer(int tid, unsigned int tick, int id, intptr data) if (!check_distance_bl(&sd->bl, tbl, 5)) unit_walktobl(&sd->bl, tbl, 5, 0); } else - pc_setpos(sd, map_id2index(tbl->m), tbl->x, tbl->y, 3); + pc_setpos(sd, map_id2index(tbl->m), tbl->x, tbl->y, CLR_TELEPORT); } sd->followtimer = add_timer( tick + 1000, // increase time a bit to loosen up map's load @@ -4767,6 +4827,7 @@ int pc_checkbaselevelup(struct map_session_data *sd) clif_updatestatus(sd,SP_STATUSPOINT); clif_updatestatus(sd,SP_BASELEVEL); + clif_updatestatus(sd,SP_BASEEXP); clif_updatestatus(sd,SP_NEXTBASEEXP); status_calc_pc(sd,0); status_percent_heal(&sd->bl,100,100); @@ -4781,7 +4842,7 @@ int pc_checkbaselevelup(struct map_session_data *sd) if (sd->state.snovice_dead_flag) sd->state.snovice_dead_flag = 0; //Reenable steelbody resurrection on dead. } else - if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON || (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR) + if( (sd->class_&MAPID_BASEMASK) == MAPID_TAEKWON ) { sc_start(&sd->bl,status_skill2sc(AL_INCAGI),100,10,600000); sc_start(&sd->bl,status_skill2sc(AL_BLESSING),100,10,600000); @@ -4814,6 +4875,7 @@ int pc_checkjoblevelup(struct map_session_data *sd) } while ((next=pc_nextjobexp(sd)) > 0 && sd->status.job_exp >= next); clif_updatestatus(sd,SP_JOBLEVEL); + clif_updatestatus(sd,SP_JOBEXP); clif_updatestatus(sd,SP_NEXTJOBEXP); clif_updatestatus(sd,SP_SKILLPOINT); status_calc_pc(sd,0); @@ -5272,8 +5334,8 @@ int pc_resetlvl(struct map_session_data* sd,int type) 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; + sd->status.base_exp=0; + sd->status.job_exp=0; if(sd->sc.option !=0) sd->sc.option = 0; @@ -5317,6 +5379,8 @@ int pc_resetlvl(struct map_session_data* sd,int type) clif_updatestatus(sd,SP_BASELEVEL); clif_updatestatus(sd,SP_JOBLEVEL); clif_updatestatus(sd,SP_STATUSPOINT); + clif_updatestatus(sd,SP_BASEEXP); + clif_updatestatus(sd,SP_JOBEXP); clif_updatestatus(sd,SP_NEXTBASEEXP); clif_updatestatus(sd,SP_NEXTJOBEXP); clif_updatestatus(sd,SP_SKILLPOINT); @@ -5498,7 +5562,7 @@ int pc_resetfeel(struct map_session_data* sd) int i; nullpo_ret(sd); - for (i=0; i<3; i++) + for (i=0; i<MAX_PC_FEELHATE; i++) { sd->feel_map[i].m = -1; sd->feel_map[i].index = 0; @@ -5563,7 +5627,7 @@ int pc_skillheal2_bonus(struct map_session_data *sd, int skill_num) return bonus; } -void pc_respawn(struct map_session_data* sd, uint8 clrtype) +void pc_respawn(struct map_session_data* sd, clr_type clrtype) { if( !pc_isdead(sd) ) return; // not applicable @@ -5582,7 +5646,7 @@ static int pc_respawn_timer(int tid, unsigned int tick, int id, intptr data) if( sd != NULL ) { sd->pvp_point=0; - pc_respawn(sd,0); + pc_respawn(sd,CLR_OUTSIGHT); } return 0; @@ -5656,7 +5720,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) } pc_setglobalreg(sd,"PC_DIE_COUNTER",sd->die_counter+1); - pc_setglobalreg(sd,"killerrid",src?src->id:0); + pc_setparam(sd, SP_KILLERRID, src?src->id:0); if( sd->state.bg_id ) { struct battleground_data *bg; @@ -5710,7 +5774,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) if (src && src->type == BL_PC) { struct map_session_data *ssd = (struct map_session_data *)src; - pc_setglobalreg(ssd, "killedrid", sd->bl.id); + pc_setparam(ssd, SP_KILLEDRID, sd->bl.id); npc_script_event(ssd, NPCE_KILLPC); if (battle_config.pk_mode&2) { @@ -5966,6 +6030,8 @@ int pc_readparam(struct map_session_data* sd,int type) case SP_KARMA: val = sd->status.karma; break; case SP_MANNER: val = sd->status.manner; break; case SP_FAME: val = sd->status.fame; break; + case SP_KILLERRID: val = sd->killerrid; break; + case SP_KILLEDRID: val = sd->killedrid; break; } return val; @@ -5992,11 +6058,15 @@ int pc_setparam(struct map_session_data *sd,int type,int val) } sd->status.base_level = (unsigned int)val; sd->status.base_exp = 0; - clif_updatestatus(sd, SP_BASELEVEL); + // clif_updatestatus(sd, SP_BASELEVEL); // Gets updated at the bottom clif_updatestatus(sd, SP_NEXTBASEEXP); clif_updatestatus(sd, SP_STATUSPOINT); clif_updatestatus(sd, SP_BASEEXP); status_calc_pc(sd, 0); + if(sd->status.party_id) + { + party_send_levelup(sd); + } break; case SP_JOBLEVEL: if ((unsigned int)val >= sd->status.job_level) { @@ -6006,11 +6076,10 @@ int pc_setparam(struct map_session_data *sd,int type,int val) } sd->status.job_level = (unsigned int)val; sd->status.job_exp = 0; - clif_updatestatus(sd, SP_JOBLEVEL); + // clif_updatestatus(sd, SP_JOBLEVEL); // Gets updated at the bottom clif_updatestatus(sd, SP_NEXTJOBEXP); clif_updatestatus(sd, SP_JOBEXP); status_calc_pc(sd, 0); - clif_updatestatus(sd,type); break; case SP_SKILLPOINT: sd->status.skill_point = val; @@ -6083,6 +6152,15 @@ int pc_setparam(struct map_session_data *sd,int type,int val) case SP_FAME: sd->status.fame = val; break; + case SP_KILLERRID: + sd->killerrid = val; + return 1; + case SP_KILLEDRID: + sd->killedrid = val; + return 1; + default: + ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type); + return 0; } clif_updatestatus(sd,type); @@ -6122,6 +6200,8 @@ int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp) // A potion produced by an Alchemist in the Fame Top 10 gets +50% effect [DracoRPG] if (potion_flag > 1) bonus += bonus*(potion_flag-1)*50/100; + //All item bonuses. + bonus += sd->itemhealrate2; //Item Group bonuses bonus += bonus*itemdb_group_bonus(sd, itemid)/100; //Individual item bonuses. @@ -6358,10 +6438,8 @@ int pc_changelook(struct map_session_data *sd,int type,int val) switch(type){ case LOOK_HAIR: //Use the battle_config limits! [Skotlex] - if (val < battle_config.min_hair_style) - val = battle_config.min_hair_style; - else if (val > battle_config.max_hair_style) - val = battle_config.max_hair_style; + val = cap_value(val, MIN_HAIR_STYLE, MAX_HAIR_STYLE); + if (sd->status.hair != val) { sd->status.hair=val; @@ -6383,10 +6461,8 @@ int pc_changelook(struct map_session_data *sd,int type,int val) sd->status.head_mid=val; break; case LOOK_HAIR_COLOR: //Use the battle_config limits! [Skotlex] - if (val < battle_config.min_hair_color) - val = battle_config.min_hair_color; - else if (val > battle_config.max_hair_color) - val = battle_config.max_hair_color; + val = cap_value(val, MIN_HAIR_COLOR, MAX_HAIR_COLOR); + if (sd->status.hair_color != val) { sd->status.hair_color=val; @@ -6396,10 +6472,8 @@ int pc_changelook(struct map_session_data *sd,int type,int val) } break; case LOOK_CLOTHES_COLOR: //Use the battle_config limits! [Skotlex] - if (val < battle_config.min_cloth_color) - val = battle_config.min_cloth_color; - else if (val > battle_config.max_cloth_color) - val = battle_config.max_cloth_color; + val = cap_value(val, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); + sd->status.clothes_color=val; break; case LOOK_SHIELD: @@ -7099,10 +7173,7 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos) if(id) { if(id->type == IT_WEAPON) { sd->status.shield = 0; - if(sd->status.inventory[n].equip == EQP_HAND_L) - sd->weapontype2 = id->look; - else - sd->weapontype2 = 0; + sd->weapontype2 = id->look; } else if(id->type == IT_ARMOR) { @@ -7934,17 +8005,61 @@ int pc_split_atoui(char* str, unsigned int* val, char sep, int max) /*========================================== * DB reading. * exp.txt - required experience values - * job_db1.txt - weight, hp, sp, aspd - * job_db2.txt - job level stat bonuses * skill_tree.txt - skill tree for every class * attr_fix.txt - elemental adjustment table - * size_fix.txt - size adjustment table for weapons - * refine_db.txt - refining data table + * statpoint.txt - status points per base level *------------------------------------------*/ +static bool pc_readdb_skilltree(char* fields[], int columns, int current) +{ + unsigned char joblv = 0, skilllv; + unsigned short skillid; + int idx, class_; + unsigned int i, offset = 3, skillidx; + + class_ = atoi(fields[0]); + skillid = (unsigned short)atoi(fields[1]); + skilllv = (unsigned char)atoi(fields[2]); + + if(columns==4+MAX_PC_SKILL_REQUIRE*2) + {// job level requirement extra column + joblv = (unsigned char)atoi(fields[3]); + offset++; + } + + if(!pcdb_checkid(class_)) + { + ShowWarning("pc_readdb_skilltree: Invalid job class %d specified.\n", class_); + return false; + } + idx = pc_class2idx(class_); + + //This is to avoid adding two lines for the same skill. [Skotlex] + ARR_FIND( 0, MAX_SKILL_TREE, skillidx, skill_tree[idx][skillidx].id == 0 || skill_tree[idx][skillidx].id == skillid ); + if( skillidx == MAX_SKILL_TREE ) + { + ShowWarning("pc_readdb_skilltree: Unable to load skill %hu into job %d's tree. Maximum number of skills per class has been reached.\n", skillid, class_); + return false; + } + else if(skill_tree[idx][skillidx].id) + { + ShowNotice("pc_readdb_skilltree: Overwriting skill %hu for job class %d.\n", skillid, class_); + } + + skill_tree[idx][skillidx].id = skillid; + skill_tree[idx][skillidx].max = skilllv; + skill_tree[idx][skillidx].joblv = joblv; + + for(i = 0; i < MAX_PC_SKILL_REQUIRE; i++) + { + skill_tree[idx][skillidx].need[i].id = atoi(fields[i*2+offset]); + skill_tree[idx][skillidx].need[i].lv = atoi(fields[i*2+offset+1]); + } + return true; +} + int pc_readdb(void) { int i,j,k; - unsigned int stat; FILE *fp; char line[24000],*p; @@ -8033,53 +8148,7 @@ int pc_readdb(void) // スキルツリ? memset(skill_tree,0,sizeof(skill_tree)); - sprintf(line, "%s/skill_tree.txt", db_path); - fp=fopen(line,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", line); - return 1; - } - - while(fgets(line, sizeof(line), fp)) - { - char *split[50]; - int f=0, m=3, idx; - if(line[0]=='/' && line[1]=='/') - continue; - for(j=0,p=line;j<14 && p;j++){ - split[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if(j<13) - continue; - if (j == 14) { - f=1; // MinJobLvl has been added - m++; - } - // check for bounds [celest] - idx = atoi(split[0]); - if(!pcdb_checkid(idx)) - continue; - idx = pc_class2idx(idx); - k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex] - ARR_FIND( 0, MAX_SKILL_TREE, j, skill_tree[idx][j].id == 0 || skill_tree[idx][j].id == k ); - if( j == MAX_SKILL_TREE ) - { - ShowWarning("Unable to load skill %d into job %d's tree. Maximum number of skills per class has been reached.\n", k, atoi(split[0])); - continue; - } - skill_tree[idx][j].id=k; - skill_tree[idx][j].max=atoi(split[2]); - if (f) skill_tree[idx][j].joblv=atoi(split[3]); - - for(k=0;k<5;k++){ - skill_tree[idx][j].need[k].id=atoi(split[k*2+m]); - skill_tree[idx][j].need[k].lv=atoi(split[k*2+m+1]); - } - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","skill_tree.txt"); + sv_readdb(db_path, "skill_tree.txt", ',', 3+MAX_PC_SKILL_REQUIRE*2, 4+MAX_PC_SKILL_REQUIRE*2, -1, &pc_readdb_skilltree); // ?性修正テ?ブル for(i=0;i<4;i++) @@ -8135,7 +8204,6 @@ int pc_readdb(void) // スキルツリ? memset(statp,0,sizeof(statp)); i=1; - stat = 45; // base points sprintf(line, "%s/statpoint.txt", db_path); fp=fopen(line,"r"); if(fp == NULL){ @@ -8144,6 +8212,7 @@ int pc_readdb(void) } else { while(fgets(line, sizeof(line), fp)) { + int stat; if(line[0]=='/' && line[1]=='/') continue; if ((stat=strtoul(line,NULL,10))<0) @@ -8157,10 +8226,9 @@ int pc_readdb(void) ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint.txt"); } // generate the remaining parts of the db if necessary - for (; i <= MAX_LEVEL; i++) { - stat += (i+15)/5; - statp[i] = stat; - } + statp[0] = 45; // seed value + for (; i <= MAX_LEVEL; i++) + statp[i] = statp[i-1] + (i-1+15)/5; return 0; } @@ -8168,30 +8236,60 @@ int pc_readdb(void) // Read MOTD on startup. [Valaris] int pc_read_motd(void) { - FILE *fp; - int ln=0,i=0; + char* buf, * ptr; + unsigned int lines = 0, entries = 0; + size_t len; + FILE* fp; + + // clear old MOTD + memset(motd_text, 0, sizeof(motd_text)); + + // read current MOTD + if( ( fp = fopen(motd_txt, "r") ) != NULL ) + { + while( entries < MOTD_LINE_SIZE && fgets(motd_text[entries], sizeof(motd_text[entries]), fp) ) + { + lines++; + + buf = motd_text[entries]; - memset(motd_text,0,sizeof(motd_text)); - if ((fp = fopen(motd_txt, "r")) != NULL) { - while ((ln < MOTD_LINE_SIZE) && fgets(motd_text[ln], sizeof(motd_text[ln])-1, fp) != NULL) { - if(motd_text[ln][0] == '/' && motd_text[ln][1] == '/') + if( buf[0] == '/' && buf[1] == '/' ) + { continue; - for(i=0; motd_text[ln][i]; i++) { - if (motd_text[ln][i] == '\r' || motd_text[ln][i]== '\n') { - if(i) - motd_text[ln][i]=0; - else - motd_text[ln][0]=' '; - ln++; - break; + } + + len = strlen(buf); + + while( len && ( buf[len-1] == '\r' || buf[len-1] == '\n' ) ) + {// strip trailing EOL characters + len--; + } + + if( len ) + { + buf[len] = 0; + + if( ( ptr = strstr(buf, " :") ) != NULL && ptr-buf >= NAME_LENGTH ) + {// crashes newer clients + ShowWarning("Found sequence '"CL_WHITE" :"CL_RESET"' on line '"CL_WHITE"%u"CL_RESET"' in '"CL_WHITE"%s"CL_RESET"'. This can cause newer clients to crash.\n", lines, motd_txt); } } + else + {// empty line + buf[0] = ' '; + buf[1] = 0; + } + entries++; } fclose(fp); + + ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, motd_txt); } else - ShowWarning("In function pc_read_motd() -> File '"CL_WHITE"%s"CL_RESET"' not found.\n", motd_txt); - + { + ShowWarning("File '"CL_WHITE"%s"CL_RESET"' not found.\n", motd_txt); + } + return 0; } diff --git a/src/map/pc.h b/src/map/pc.h index 7bf3a050f..2921b4527 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -17,6 +17,8 @@ #include "mob.h" #define MAX_PC_BONUS 10 +#define MAX_PC_SKILL_REQUIRE 5 +#define MAX_PC_FEELHATE 3 struct weapon_data { int atkmods[3]; @@ -53,6 +55,7 @@ struct weapon_data { struct s_autospell { short id, lv, rate, card_id, flag; + bool lock; // bAutoSpellOnSkill: blocks autospell from triggering again, while being executed }; struct s_addeffect { @@ -132,7 +135,6 @@ struct map_session_data { short pmap; // Previous map on Map Change struct guild *gmaster_flag; unsigned int bg_id; - unsigned skillonskill : 1; unsigned short user_font; unsigned short autobonus; //flag to indicate if an autobonus is activated. [Inkfish] } state; @@ -290,6 +292,7 @@ struct map_session_data { int crit_atk_rate; int classchange; // [Valaris] int speed_rate, speed_add_rate, aspd_add; + int itemhealrate2; // [Epoque] Increase heal rate of all healing items. unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex] short splash_range, splash_add_range; @@ -378,6 +381,8 @@ struct map_session_data { int duel_group; // duel vars [LuzZza] int duel_invite; + int killerrid, killedrid; + char away_message[128]; // [LuzZza] int cashPoints, kafraPoints; @@ -543,7 +548,7 @@ bool pc_can_give_items(int level); int pc_setrestartvalue(struct map_session_data *sd,int type); int pc_makesavestatus(struct map_session_data *); -void pc_respawn(struct map_session_data* sd, uint8 clrtype); +void pc_respawn(struct map_session_data* sd, clr_type clrtype); int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int); bool pc_authok(struct map_session_data* sd, int, time_t, int gmlevel, struct mmo_charstatus* status); void pc_authfail(struct map_session_data *); @@ -564,11 +569,12 @@ int pc_clean_skilltree(struct map_session_data *sd); #define pc_checkoverhp(sd) (sd->battle_status.hp == sd->battle_status.max_hp) #define pc_checkoversp(sd) (sd->battle_status.sp == sd->battle_status.max_sp) -int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, uint8 clrtype); +int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, clr_type clrtype); int pc_setsavepoint(struct map_session_data*,short,int,int); -int pc_randomwarp(struct map_session_data *sd,int type); +int pc_randomwarp(struct map_session_data *sd,clr_type type); +int pc_warpto(struct map_session_data* sd, struct map_session_data* pl_sd); +int pc_recall(struct map_session_data* sd, struct map_session_data* pl_sd); int pc_memo(struct map_session_data* sd, int pos); -int pc_remove_map(struct map_session_data *sd,int clrtype); int pc_checkadditem(struct map_session_data*,int,int); int pc_inventoryblank(struct map_session_data*); @@ -720,7 +726,7 @@ struct skill_tree_entry { struct { short id; unsigned char lv; - } need[5]; + } need[MAX_PC_SKILL_REQUIRE]; }; // Celest extern struct skill_tree_entry skill_tree[CLASS_COUNT][MAX_SKILL_TREE]; @@ -732,7 +738,7 @@ struct sg_data { char hate_var[NAME_LENGTH]; int (*day_func)(void); }; -extern const struct sg_data sg_info[3]; +extern const struct sg_data sg_info[MAX_PC_FEELHATE]; void pc_setinvincibletimer(struct map_session_data* sd, int val); void pc_delinvincibletimer(struct map_session_data* sd); diff --git a/src/map/pet.c b/src/map/pet.c index d02f93446..843f1cc11 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -306,7 +306,7 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); } pd->pet.incuvate = 1; - unit_free(&pd->bl,0); + unit_free(&pd->bl,CLR_OUTSIGHT); status_calc_pc(sd,0); sd->status.pet_id = 0; @@ -509,7 +509,7 @@ int pet_catch_process2(struct map_session_data* sd, int target_id) if (sd->catch_target_class == 0 && !(md->status.mode&MD_BOSS)) sd->catch_target_class = md->class_; if(i < 0 || sd->catch_target_class != md->class_) { - clif_emotion(&md->bl, 7); //mob will do /ag if wrong lure is used on them. + clif_emotion(&md->bl, E_AG); //mob will do /ag if wrong lure is used on them. clif_pet_roulette(sd,0); sd->catch_target_class = -1; return 1; @@ -523,7 +523,7 @@ int pet_catch_process2(struct map_session_data* sd, int target_id) if(rand()%10000 < pet_catch_rate) { - unit_remove_map(&md->bl,0); + unit_remove_map(&md->bl,CLR_OUTSIGHT); status_kill(&md->bl); clif_pet_roulette(sd,1); intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv, @@ -1100,7 +1100,7 @@ int pet_recovery_timer(int tid, unsigned int tick, int id, intptr data) //Detoxify is chosen for now. clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1); status_change_end(&sd->bl,pd->recovery->type,-1); - clif_emotion(&pd->bl, 33); + clif_emotion(&pd->bl, E_OK); } pd->recovery->timer = INVALID_TIMER; diff --git a/src/map/script.c b/src/map/script.c index c71debc0d..773f8d6c4 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -67,7 +67,6 @@ // - 'function FuncName;' function declarations reverting to global functions // if local label isn't found // - join callfunc and callsub's functionality -// - use script_op2name in the DEBUG_DISASM block // - remove dynamic allocation in add_word() // - remove GETVALUE / SETVALUE // - clean up the set_reg / set_val / setd_sub mess @@ -160,6 +159,9 @@ if( script_hasdata(st,n) ) \ (t)=script_getnum(st,n); +/// Maximum amount of elements in script arrays +#define SCRIPT_MAX_ARRAYSIZE 128 + #define SCRIPT_BLOCK_SIZE 512 enum { LABEL_NEXTLINE=1,LABEL_START }; @@ -210,6 +212,7 @@ DBMap* script_get_userfunc_db(){ return userfunc_db; } static DBMap* autobonus_db=NULL; // char* script -> char* bytecode struct Script_Config script_config = { + 1, // warn_func_mismatch_argtypes 1, 65535, 2048, //warn_func_mismatch_paramnum/check_cmdcount/check_gotocount 0, INT_MAX, // input_min_value/input_max_value "OnPCDieEvent", //die_event_name @@ -281,13 +284,11 @@ typedef struct script_function { extern script_function buildin_func[]; static struct linkdb_node* sleep_db;// int oid -> struct script_state* -uint32 crctab[256]; /*========================================== * ローカルプロトタイプ宣言 (必要な物のみ) *------------------------------------------*/ const char* parse_subexpr(const char* p,int limit); -void push_val(struct script_stack *stack,int type,int val); int run_func(struct script_state *st); enum { @@ -515,6 +516,44 @@ static void script_reportdata(struct script_data* data) } } + +/// Reports on the console information about the current built-in function. +static void script_reportfunc(struct script_state* st) +{ + int i, params, id; + struct script_data* data; + + if( !script_hasdata(st,0) ) + {// no stack + return; + } + + data = script_getdata(st,0); + + if( !data_isreference(data) || str_data[reference_getid(data)].type != C_FUNC ) + {// script currently not executing a built-in function or corrupt stack + return; + } + + id = reference_getid(data); + params = script_lastdata(st)-1; + + if( params > 0 ) + { + ShowDebug("Function: %s (%d parameter%s):\n", get_str(id), params, ( params == 1 ) ? "" : "s"); + + for( i = 2; i <= script_lastdata(st); i++ ) + { + script_reportdata(script_getdata(st,i)); + } + } + else + { + ShowDebug("Function: %s (no parameters)\n", get_str(id)); + } +} + + /*========================================== * エラーメッセージ出力 *------------------------------------------*/ @@ -741,8 +780,8 @@ void set_label(int l,int pos, const char* script_pos) { int i,next; - if(str_data[l].type==C_INT || str_data[l].type==C_PARAM) - { //Prevent overwriting constants values and parameters [Skotlex] + if(str_data[l].type==C_INT || str_data[l].type==C_PARAM || str_data[l].type==C_FUNC) + { //Prevent overwriting constants values, parameters and built-in functions [Skotlex] disp_error_message("set_label: invalid label name",script_pos); return; } @@ -842,7 +881,7 @@ int add_word(const char* p) disp_error_message("script:add_word: invalid word. A word consists of undercores and/or alfanumeric characters, and valid variable prefixes/postfixes.", p); // Duplicate the word - CREATE(word, char, len+1); + word = aMalloc(len+1); memcpy(word, p, len); word[len] = 0; @@ -1502,7 +1541,7 @@ const char* parse_syntax(const char* p) // function declaration - just register the name int l; l = add_word(func_name); - if( str_data[l].type == C_NOP )//## ??? [FlavioJS] + if( str_data[l].type == C_NOP )// set type only if the name did not exist before str_data[l].type = C_USERFUNC; // if, for , while の閉じ判定 @@ -1528,7 +1567,7 @@ const char* parse_syntax(const char* p) // Set the position of the function (label) l=add_word(func_name); - if( str_data[l].type == C_NOP )//## ??? [FlavioJS] + if( str_data[l].type == C_NOP )// set type only if the name did not exist before str_data[l].type = C_USERFUNC; set_label(l, script_pos, p); if( parse_options&SCRIPT_USE_LABEL_DB ) @@ -2078,54 +2117,30 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o { int i = 0,j; while(i < script_pos) { - ShowMessage("%06x ",i); + c_op op = get_com(script_buf,&i); + + ShowMessage("%06x %s", i, script_op2name(op)); j = i; - switch(get_com(script_buf,&i)) { - case C_EOL: printf("C_EOL"); break; - case C_INT: printf("C_INT %d",get_num(script_buf,&i)); break; + switch(op) { + case C_INT: + ShowMessage(" %d", get_num(script_buf,&i)); + break; case C_POS: - ShowMessage("C_POS 0x%06x",*(int*)(script_buf+i)&0xffffff); + ShowMessage(" 0x%06x", *(int*)(script_buf+i)&0xffffff); i += 3; break; case C_NAME: j = (*(int*)(script_buf+i)&0xffffff); - ShowMessage("C_NAME %s",j == 0xffffff ? "?? unknown ??" : get_str(j)); + ShowMessage(" %s", ( j == 0xffffff ) ? "?? unknown ??" : get_str(j)); i += 3; break; - case C_ARG: ShowMessage("C_ARG"); break; - case C_FUNC: ShowMessage("C_FUNC"); break; - case C_ADD: ShowMessage("C_ADD"); break; - case C_SUB: ShowMessage("C_SUB"); break; - case C_MUL: ShowMessage("C_MUL"); break; - case C_DIV: ShowMessage("C_DIV"); break; - case C_MOD: ShowMessage("C_MOD"); break; - case C_EQ: ShowMessage("C_EQ"); break; - case C_NE: ShowMessage("C_NE"); break; - case C_GT: ShowMessage("C_GT"); break; - case C_GE: ShowMessage("C_GE"); break; - case C_LT: ShowMessage("C_LT"); break; - case C_LE: ShowMessage("C_LE"); break; - case C_AND: ShowMessage("C_AND"); break; - case C_OR: ShowMessage("C_OR"); break; - case C_XOR: ShowMessage("C_XOR"); break; - case C_LAND: ShowMessage("C_LAND"); break; - case C_LOR: ShowMessage("C_LOR"); break; - case C_R_SHIFT: ShowMessage("C_R_SHIFT"); break; - case C_L_SHIFT: ShowMessage("C_L_SHIFT"); break; - case C_NEG: ShowMessage("C_NEG"); break; - case C_NOT: ShowMessage("C_NOT"); break; - case C_LNOT: ShowMessage("C_LNOT"); break; - case C_NOP: ShowMessage("C_NOP"); break; - case C_OP3: ShowMessage("C_OP3"); break; case C_STR: j = strlen(script_buf + i); - ShowMessage("C_STR %s",script_buf + i); - i+= j+1; + ShowMessage(" %s", script_buf + i); + i += j+1; break; - default: - ShowMessage("unknown"); } - ShowMessage(CL_CLL "\n"); + ShowMessage(CL_CLL"\n"); } } #endif @@ -2144,6 +2159,7 @@ TBL_PC *script_rid2sd(struct script_state *st) TBL_PC *sd=map_id2sd(st->rid); if(!sd){ ShowError("script_rid2sd: fatal error ! player not attached!\n"); + script_reportfunc(st); script_reportsrc(st); st->state = END; } @@ -2694,6 +2710,10 @@ struct script_state* script_alloc_state(struct script_code* script, int pos, int /// @param st Script state void script_free_state(struct script_state* st) { + if(st->bk_st) + {// backup was not restored + ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid); + } if( st->sleep.timer != INVALID_TIMER ) delete_timer(st->sleep.timer, run_script_timer); script_free_vars(st->stack->var_function); @@ -2977,6 +2997,94 @@ void op_1(struct script_state* st, int op) } +/// Checks the type of all arguments passed to a built-in function. +/// +/// @param st Script state whose stack arguments should be inspected. +/// @param func Built-in function for which the arguments are intended. +static void script_check_buildin_argtype(struct script_state* st, int func) +{ + char type; + int idx, invalid = 0; + script_function* sf = &buildin_func[str_data[func].val]; + + for( idx = 2; script_hasdata(st, idx); idx++ ) + { + struct script_data* data = script_getdata(st, idx); + + type = sf->arg[idx-2]; + + if( type == '?' || type == '*' ) + {// optional argument or unknown number of optional parameters ( no types are after this ) + break; + } + else if( type == 0 ) + {// more arguments than necessary ( should not happen, as it is checked before ) + ShowWarning("Found more arguments than necessary.\n"); + invalid++; + break; + } + else + { + const char* name = NULL; + + if( data_isreference(data) ) + {// get name for variables to determine the type they refer to + name = reference_getname(data); + } + + switch( type ) + { + case 'v': + if( !data_isstring(data) && !data_isint(data) && !data_isreference(data) ) + {// variant + ShowWarning("Unexpected type for argument %d. Expected string, number or variable.\n", idx-1); + script_reportdata(data); + invalid++; + } + break; + case 's': + if( !data_isstring(data) && !( data_isreference(data) && is_string_variable(name) ) ) + {// string + ShowWarning("Unexpected type for argument %d. Expected string.\n", idx-1); + script_reportdata(data); + invalid++; + } + break; + case 'i': + if( !data_isint(data) && !( data_isreference(data) && ( reference_toparam(data) || reference_toconstant(data) || !is_string_variable(name) ) ) ) + {// int ( params and constants are always int ) + ShowWarning("Unexpected type for argument %d. Expected number.\n", idx-1); + script_reportdata(data); + invalid++; + } + break; + case 'r': + if( !data_isreference(data) ) + {// variables + ShowWarning("Unexpected type for argument %d. Expected variable.\n", idx-1); + script_reportdata(data); + invalid++; + } + break; + case 'l': + if( !data_islabel(data) && !data_isfunclabel(data) ) + {// label + ShowWarning("Unexpected type for argument %d. Expected label.\n", idx-1); + script_reportdata(data); + invalid++; + } + break; + } + } + } + + if(invalid) + { + ShowDebug("Function: %s\n", get_str(func)); + script_reportsrc(st); + } +} + /// Executes a buildin command. /// Stack: C_NAME(<command>) C_ARG <arg0> <arg1> ... <argN> @@ -3012,6 +3120,11 @@ int run_func(struct script_state *st) return 1; } + if( script_config.warn_func_mismatch_argtypes ) + { + script_check_buildin_argtype(st, func); + } + if(str_data[func].func){ if (str_data[func].func(st)) //Report error script_reportsrc(st); @@ -3062,8 +3175,6 @@ int run_func(struct script_state *st) /*========================================== * script execution *------------------------------------------*/ -void run_script_main(struct script_state *st); - void run_script(struct script_code *rootscript,int pos,int rid,int oid) { struct script_state *st; @@ -3138,6 +3249,63 @@ int run_script_timer(int tid, unsigned int tick, int id, intptr data) return 0; } +/// Detaches script state from possibly attached character and restores it's previous script if any. +/// +/// @param st Script state to detach. +/// @param dequeue_event Whether to schedule any queued events, when there was no previous script. +static void script_detach_state(struct script_state* st, bool dequeue_event) +{ + struct map_session_data* sd; + + if(st->rid && (sd = map_id2sd(st->rid))!=NULL) + { + sd->st = st->bk_st; + sd->npc_id = st->bk_npcid; + + if(st->bk_st) + { + //Remove tag for removal. + st->bk_st = NULL; + st->bk_npcid = 0; + } + else if(dequeue_event) + { + npc_event_dequeue(sd); + } + } + else if(st->bk_st) + {// rid was set to 0, before detaching the script state + ShowError("script_detach_state: Found previous script state without attached player (rid=%d, oid=%d, state=%d, bk_npcid=%d)\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid); + script_reportsrc(st->bk_st); + + script_free_state(st->bk_st); + st->bk_st = NULL; + } +} + +/// Attaches script state to possibly attached character and backups it's previous script, if any. +/// +/// @param st Script state to attach. +static void script_attach_state(struct script_state* st) +{ + struct map_session_data* sd; + + if(st->rid && (sd = map_id2sd(st->rid))!=NULL) + { + if(st!=sd->st) + { + if(st->bk_st) + {// there is already a backup + ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid); + } + st->bk_st = sd->st; + st->bk_npcid = sd->npc_id; + } + sd->st = st; + sd->npc_id = st->oid; + } +} + /*========================================== * スクリプトの実行メイン部分 *------------------------------------------*/ @@ -3146,22 +3314,10 @@ void run_script_main(struct script_state *st) int cmdcount=script_config.check_cmdcount; int gotocount=script_config.check_gotocount; TBL_PC *sd; - //For backing up purposes - struct script_state *bk_st = NULL; - int bk_npcid = 0; struct script_stack *stack=st->stack; struct npc_data *nd; - sd = map_id2sd(st->rid); - - if(sd){ - if(sd->st != st){ - bk_st = sd->st; - bk_npcid = sd->npc_id; - } - sd->st = st; - sd->npc_id = st->oid; - } + script_attach_state(st); nd = map_id2nd(st->oid); if( nd && map[nd->bl.m].instance_id > 0 ) @@ -3260,43 +3416,37 @@ void run_script_main(struct script_state *st) if(st->sleep.tick > 0) { //Restore previous script - if (sd) { - sd->st = bk_st; - sd->npc_id = bk_npcid; - bk_st = NULL; //Remove tag for removal. - } + script_detach_state(st, false); //Delay execution - sd = map_id2sd(st->rid); // Refresh sd since script might have attached someone while running. [Inkfish] + sd = map_id2sd(st->rid); // Get sd since script might have attached someone while running. [Inkfish] st->sleep.charid = sd?sd->status.char_id:0; st->sleep.timer = add_timer(gettick()+st->sleep.tick, run_script_timer, st->sleep.charid, (intptr)st); linkdb_insert(&sleep_db, (void*)st->oid, st); } - else if(st->state != END && sd){ + else if(st->state != END && st->rid){ //Resume later (st is already attached to player). - if(bk_st) { + if(st->bk_st) { ShowWarning("Unable to restore stack! Double continuation!\n"); //Report BOTH scripts to see if that can help somehow. ShowDebug("Previous script (lost):\n"); - script_reportsrc(bk_st); + script_reportsrc(st->bk_st); ShowDebug("Current script:\n"); script_reportsrc(st); + + script_free_state(st->bk_st); + st->bk_st = NULL; } } else { //Dispose of script. - if (sd) + if ((sd = map_id2sd(st->rid))!=NULL) { //Restore previous stack and save char. if(sd->state.using_fake_npc){ - clif_clearunit_single(sd->npc_id, 0, sd->fd); + clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); sd->state.using_fake_npc = 0; } //Restore previous script if any. - sd->st = bk_st; - sd->npc_id = bk_npcid; - if (!bk_st) - npc_event_dequeue(sd); - else - bk_st = NULL; //Remove tag for removal. + script_detach_state(st, true); if (sd->state.reg_dirty&2) intif_saveregistry(sd,2); if (sd->state.reg_dirty&1) @@ -3305,13 +3455,6 @@ void run_script_main(struct script_state *st) script_free_state(st); st = NULL; } - - if (bk_st) - { //Remove previous script - script_free_state(bk_st); - bk_st = NULL; - } - } int script_config_read(char *cfgName) @@ -3349,6 +3492,9 @@ int script_config_read(char *cfgName) else if(strcmpi(w1,"input_max_value")==0) { script_config.input_max_value = config_switch(w2); } + else if(strcmpi(w1,"warn_func_mismatch_argtypes")==0) { + script_config.warn_func_mismatch_argtypes = config_switch(w2); + } else if(strcmpi(w1,"import")==0){ script_config_read(w2); } @@ -3401,6 +3547,74 @@ void script_add_autobonus(const char *autobonus) } } + +/// resets a temporary character array variable to given value +void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value) +{ + int key; + uint8 idx; + + if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) ) + { + ShowError("script_cleararray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id); + return; + } + + key = add_str(varname); + + if( is_string_variable(varname) ) + { + for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ ) + { + pc_setregstr(sd, reference_uid(key, idx), (const char*)value); + } + } + else + { + for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ ) + { + pc_setreg(sd, reference_uid(key, idx), (int)value); + } + } +} + + +/// sets a temporary character array variable element idx to given value +/// @param refcache Pointer to an int variable, which keeps a copy of the reference to varname and must be initialized to 0. Can be NULL if only one element is set. +void script_setarray_pc(struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache) +{ + int key; + + if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) ) + { + ShowError("script_setarray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id); + return; + } + + if( idx >= SCRIPT_MAX_ARRAYSIZE ) + { + ShowError("script_setarray_pc: Variable '%s' has invalid index '%d' (char_id=%d).\n", varname, (int)idx, sd->status.char_id); + return; + } + + key = ( refcache && refcache[0] ) ? refcache[0] : add_str(varname); + + if( is_string_variable(varname) ) + { + pc_setregstr(sd, reference_uid(key, idx), (const char*)value); + } + else + { + pc_setreg(sd, reference_uid(key, idx), (int)value); + } + + if( refcache ) + {// save to avoid repeated add_str calls + refcache[0] = key; + } +} + + /*========================================== * 終了 *------------------------------------------*/ @@ -3698,7 +3912,12 @@ BUILDIN_FUNC(menu) sd->state.menu_or_input = 1; clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); StringBuf_Destroy(&buf); - //TODO what's the maximum number of options that can be displayed and/or received? -> give warning + + if( sd->npc_menu >= 0xff ) + {// client supports only up to 254 entries; 0 is not used and 255 is reserved for cancel; excess entries are displayed but cause 'uint8' overflow + ShowWarning("buildin_menu: Too many options specified (current=%d, max=254).\n", sd->npc_menu); + script_reportsrc(st); + } } else if( sd->npc_menu == 0xff ) {// Cancel was pressed @@ -3780,6 +3999,12 @@ BUILDIN_FUNC(select) sd->state.menu_or_input = 1; clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); StringBuf_Destroy(&buf); + + if( sd->npc_menu >= 0xff ) + { + ShowWarning("buildin_select: Too many options specified (current=%d, max=254).\n", sd->npc_menu); + script_reportsrc(st); + } } else if( sd->npc_menu == 0xff ) {// Cancel was pressed @@ -3842,6 +4067,12 @@ BUILDIN_FUNC(prompt) sd->state.menu_or_input = 1; clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); StringBuf_Destroy(&buf); + + if( sd->npc_menu >= 0xff ) + { + ShowWarning("buildin_prompt: Too many options specified (current=%d, max=254).\n", sd->npc_menu); + script_reportsrc(st); + } } else if( sd->npc_menu == 0xff ) {// Cancel was pressed @@ -4100,11 +4331,11 @@ BUILDIN_FUNC(warp) y = script_getnum(st,4); if(strcmp(str,"Random")==0) - ret = pc_randomwarp(sd,3); + ret = pc_randomwarp(sd,CLR_TELEPORT); else if(strcmp(str,"SavePoint")==0 || strcmp(str,"Save")==0) - ret = pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + ret = pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); else - ret = pc_setpos(sd,mapindex_name2id(str),x,y,0); + ret = pc_setpos(sd,mapindex_name2id(str),x,y,CLR_OUTSIGHT); if( ret ) { ShowError("buildin_warp: moving player '%s' to \"%s\",%d,%d failed.\n", sd->status.name, str, x, y); @@ -4124,9 +4355,9 @@ static int buildin_areawarp_sub(struct block_list *bl,va_list ap) x=va_arg(ap,int); y=va_arg(ap,int); if(map == 0) - pc_randomwarp((TBL_PC *)bl,3); + pc_randomwarp((TBL_PC *)bl,CLR_TELEPORT); else - pc_setpos((TBL_PC *)bl,map,x,y,0); + pc_setpos((TBL_PC *)bl,map,x,y,CLR_OUTSIGHT); return 0; } BUILDIN_FUNC(areawarp) @@ -4212,12 +4443,12 @@ BUILDIN_FUNC(warpchar) return 0; if(strcmp(str, "Random") == 0) - pc_randomwarp(sd, 3); + pc_randomwarp(sd, CLR_TELEPORT); else if(strcmp(str, "SavePoint") == 0) - pc_setpos(sd, sd->status.save_point.map,sd->status.save_point.x, sd->status.save_point.y, 3); + pc_setpos(sd, sd->status.save_point.map,sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); else - pc_setpos(sd, mapindex_name2id(str), x, y, 3); + pc_setpos(sd, mapindex_name2id(str), x, y, CLR_TELEPORT); return 0; } @@ -4274,15 +4505,15 @@ BUILDIN_FUNC(warpparty) { case 0: // Random if(!map[pl_sd->bl.m].flag.nowarp) - pc_randomwarp(pl_sd,3); + pc_randomwarp(pl_sd,CLR_TELEPORT); break; case 1: // SavePointAll if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); + pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT); break; case 2: // SavePoint if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); break; case 3: // Leader for(j = 0; j < MAX_PARTY && !p->party.member[j].leader; j++); @@ -4298,12 +4529,12 @@ BUILDIN_FUNC(warpparty) continue; if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp) continue; - pc_setpos(pl_sd,mapindex,x,y,3); + pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT); } break; case 4: // m,x,y if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp) - pc_setpos(pl_sd,mapindex_name2id(str),x,y,3); + pc_setpos(pl_sd,mapindex_name2id(str),x,y,CLR_TELEPORT); break; } } @@ -4352,19 +4583,19 @@ BUILDIN_FUNC(warpguild) { case 0: // Random if(!map[pl_sd->bl.m].flag.nowarp) - pc_randomwarp(pl_sd,3); + pc_randomwarp(pl_sd,CLR_TELEPORT); break; case 1: // SavePointAll if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); + pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT); break; case 2: // SavePoint if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); break; case 3: // m,x,y if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp) - pc_setpos(pl_sd,mapindex_name2id(str),x,y,3); + pc_setpos(pl_sd,mapindex_name2id(str),x,y,CLR_TELEPORT); break; } } @@ -4588,7 +4819,7 @@ static int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isst if( isstring ) { - for( ; idx < 128; ++idx ) + for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx ) { char* str = (char*)get_val2(st, reference_uid(id, idx), ref); if( str && *str ) @@ -4598,7 +4829,7 @@ static int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isst } else { - for( ; idx < 128; ++idx ) + for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx ) { int32 num = (int32)get_val2(st, reference_uid(id, idx), ref); if( num ) @@ -4651,8 +4882,8 @@ BUILDIN_FUNC(setarray) } end = start + script_lastdata(st) - 2; - if( end > 127 ) - end = 127; + if( end >= SCRIPT_MAX_ARRAYSIZE ) + end = SCRIPT_MAX_ARRAYSIZE-1; if( is_string_variable(name) ) {// string array @@ -4714,8 +4945,8 @@ BUILDIN_FUNC(cleararray) v = (void*)script_getnum(st, 3); end = start + script_getnum(st, 4); - if( end > 127 ) - end = 127; + if( end >= SCRIPT_MAX_ARRAYSIZE ) + end = SCRIPT_MAX_ARRAYSIZE-1; for( ; start <= end; ++start ) set_reg(st, sd, reference_uid(id, start), name, v, script_getref(st,2)); @@ -4784,8 +5015,8 @@ BUILDIN_FUNC(copyarray) } count = script_getnum(st, 4); - if( count > 128 - idx1 ) - count = 128 - idx1; + if( count >= SCRIPT_MAX_ARRAYSIZE - idx1 ) + count = (SCRIPT_MAX_ARRAYSIZE-1) - idx1; if( count <= 0 || (id1 == id2 && idx1 == idx2) ) return 0;// nothing to copy @@ -4802,7 +5033,7 @@ BUILDIN_FUNC(copyarray) {// normal copy for( i = 0; i < count; ++i ) { - if( idx2 + i < 128 ) + if( idx2 + i < SCRIPT_MAX_ARRAYSIZE ) { v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2)); set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1)); @@ -4958,7 +5189,7 @@ BUILDIN_FUNC(getelementofarray) } i = script_getnum(st, 3); - if( i < 0 || i >= 128 ) + if( i < 0 || i >= SCRIPT_MAX_ARRAYSIZE ) { ShowWarning("script:getelementofarray: index out of range (%d)\n", i); script_reportdata(data); @@ -5154,48 +5385,80 @@ BUILDIN_FUNC(countitem2) *------------------------------------------*/ BUILDIN_FUNC(checkweight) { - int nameid=0,amount,i; - unsigned long weight; - TBL_PC *sd; - struct script_data *data; + int nameid, amount, slots; + unsigned int weight; + struct item_data* id = NULL; + struct map_session_data* sd; + struct script_data* data; - sd = script_rid2sd(st); - if( sd == NULL ) + if( ( sd = script_rid2sd(st) ) == NULL ) + { return 0; + } - data=script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ){ - 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); + data = script_getdata(st,2); + get_val(st, data); // convert into value in case of a variable - amount=script_getnum(st,3); - if ( amount<=0 || nameid<500 ) { //if get wrong item ID or amount<=0, don't count weight of non existing items - script_pushint(st,0); - ShowError("buildin_checkweight: Wrong item ID or amount.\n"); + if( data_isstring(data) ) + {// item name + id = itemdb_searchname(conv_str(st, data)); + } + else + {// item id + id = itemdb_exists(conv_num(st, data)); + } + + if( id == NULL ) + { + ShowError("buildin_checkweight: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was + return 1; + } + + nameid = id->nameid; + amount = script_getnum(st,3); + + if( amount < 1 ) + { + ShowError("buildin_checkweight: Invalid amount '%d'.\n", amount); return 1; } weight = itemdb_weight(nameid)*amount; - if( amount > MAX_AMOUNT || weight + sd->weight > sd->max_weight ) + + if( weight + sd->weight > sd->max_weight ) + {// too heavy script_pushint(st,0); - else if( itemdb_isstackable(nameid) ) - { - if( (i = pc_search_inventory(sd,nameid)) >= 0 ) - script_pushint(st,amount + sd->status.inventory[i].amount > MAX_AMOUNT ? 0 : 1); - else - script_pushint(st,pc_search_inventory(sd,0) >= 0 ? 1 : 0); + return 0; } - else + + switch( pc_checkadditem(sd, nameid, amount) ) { - for( i = 0; i < MAX_INVENTORY && amount; ++i ) - if( sd->status.inventory[i].nameid == 0 ) - amount--; - script_pushint(st,amount ? 0 : 1); + case ADDITEM_EXIST: + // item is already in inventory, but there is still space for the requested amount + break; + case ADDITEM_NEW: + slots = pc_inventoryblank(sd); + + if( itemdb_isstackable(nameid) ) + {// stackable + if( slots < 1 ) + { + script_pushint(st,0); + return 0; + } + } + else + {// non-stackable + if( slots < amount ) + { + script_pushint(st,0); + return 0; + } + } + break; + case ADDITEM_OVERAMOUNT: + script_pushint(st,0); + return 0; } return 0; @@ -5390,8 +5653,8 @@ BUILDIN_FUNC(getitem2) } /*========================================== - * rentitem <item id> - * rentitem "<item name>" + * rentitem <item id>,<seconds> + * rentitem "<item name>",<seconds> *------------------------------------------*/ BUILDIN_FUNC(rentitem) { @@ -5596,6 +5859,156 @@ BUILDIN_FUNC(makeitem) return 0; } + +/// Counts / deletes the current item given by idx. +/// Used by buildin_delitem_search +/// Relies on all input data being already fully valid. +static void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, bool delete_items) +{ + int delamount; + struct item* inv = &sd->status.inventory[idx]; + + delamount = ( amount[0] < inv->amount ) ? amount[0] : inv->amount; + + if( delete_items ) + { + if( sd->inventory_data[idx]->type == IT_PETEGG && inv->card[0] == CARD0_PET ) + {// delete associated pet + intif_delete_petdata(MakeDWord(inv->card[1], inv->card[2])); + } + + //Logs items, got from (N)PC scripts [Lupus] + if( log_config.enable_logs&0x40 ) + { + log_pick_pc(sd, "N", inv->nameid, -delamount, inv); + } + //Logs + + pc_delitem(sd, idx, delamount, 0, 0); + } + + amount[0]-= delamount; +} + + +/// Searches for item(s) and checks, if there is enough of them. +/// Used by delitem and delitem2 +/// Relies on all input data being already fully valid. +/// @param exact_match will also match item attributes and cards, not just name id +/// @return true when all items could be deleted, false when there were not enough items to delete +static bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool exact_match) +{ + bool delete_items = false; + int i, amount, important; + struct item* inv; + + // prefer always non-equipped items + it->equip = 0; + + // when searching for nameid only, prefer additionally + if( !exact_match ) + { + // non-refined items + it->refine = 0; + // card-less items + memset(it->card, 0, sizeof(it->card)); + } + + for(;;) + { + amount = it->amount; + important = 0; + + // 1st pass -- less important items / exact match + for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ ) + { + inv = &sd->status.inventory[i]; + + if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid ) + {// wrong/invalid item + continue; + } + + if( inv->equip != it->equip || inv->refine != it->refine ) + {// not matching attributes + important++; + continue; + } + + if( exact_match ) + { + if( inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) ) + {// not matching exact attributes + continue; + } + } + else + { + if( sd->inventory_data[i]->type == IT_PETEGG ) + { + if( inv->card[0] == CARD0_PET && CheckForCharServer() ) + {// pet which cannot be deleted + continue; + } + } + else if( memcmp(inv->card, it->card, sizeof(inv->card)) ) + {// named/carded item + important++; + continue; + } + } + + // count / delete item + buildin_delitem_delete(sd, i, &amount, delete_items); + } + + // 2nd pass -- any matching item + if( amount == 0 || important == 0 ) + {// either everything was already consumed or no items were skipped + ; + } + else for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ ) + { + inv = &sd->status.inventory[i]; + + if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid ) + {// wrong/invalid item + continue; + } + + if( sd->inventory_data[i]->type == IT_PETEGG && inv->card[0] == CARD0_PET && CheckForCharServer() ) + {// pet which cannot be deleted + continue; + } + + if( exact_match ) + { + if( inv->refine != it->refine || inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) ) + {// not matching attributes + continue; + } + } + + // count / delete item + buildin_delitem_delete(sd, i, &amount, delete_items); + } + + if( amount ) + {// not enough items + return false; + } + else if( delete_items ) + {// we are done with the work + return true; + } + else + {// get rid of the items now + delete_items = true; + } + } +} + + /// Deletes items from the target/attached player. /// Prioritizes ordinary items. /// @@ -5603,8 +6016,8 @@ BUILDIN_FUNC(makeitem) /// delitem "<item name>",<amount>{,<account id>} BUILDIN_FUNC(delitem) { - int nameid=0,amount,i,important_item=0; TBL_PC *sd; + struct item it; struct script_data *data; if( script_hasdata(st,4) ) @@ -5637,89 +6050,30 @@ BUILDIN_FUNC(delitem) st->state = END; return 1; } - nameid = id->nameid;// "<item name>" + it.nameid = id->nameid;// "<item name>" } else - nameid = conv_num(st,data);// <item id> - - amount=script_getnum(st,3); - - if( amount <= 0 ) - return 0;// nothing to do - - //1st pass - //here we won't delete items with CARDS, named items but we count them - for(i=0;i<MAX_INVENTORY;i++){ - //we don't delete wrong item or equipped item - if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid) - continue; - //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle - if(sd->inventory_data[i]->type==IT_PETEGG && - sd->status.inventory[i].card[0] == CARD0_PET) + { + it.nameid = conv_num(st,data);// <item id> + if( !itemdb_exists( it.nameid ) ) { - if (!intif_delete_petdata(MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))) - continue; //pet couldn't be sent for deletion. - } else - //is this item important? does it have cards? or Player's name? or Refined/Upgraded - if(itemdb_isspecial(sd->status.inventory[i].card[0]) || - sd->status.inventory[i].card[0] || - sd->status.inventory[i].refine) { - //this is important item, count it (except for pet eggs) - if(sd->status.inventory[i].card[0] != CARD0_PET) - important_item++; - continue; - } - - if(sd->status.inventory[i].amount>=amount){ - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.enable_logs&0x40) - log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); - - pc_delitem(sd,i,amount,0,0); - return 0; //we deleted exact amount of items. now exit - } else { - amount-=sd->status.inventory[i].amount; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.enable_logs&0x40) { - log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,sd->status.inventory[i].amount,0,0); + ShowError("script:delitem: unknown item \"%d\".\n", it.nameid); + st->state = END; + return 1; } } - //2nd pass - //now if there WERE items with CARDs/REFINED/NAMED... and if we still have to delete some items. we'll delete them finally - if (important_item>0 && amount>0) - for(i=0;i<MAX_INVENTORY;i++){ - //we don't delete wrong item - if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ) - continue; - if(sd->status.inventory[i].amount>=amount){ + it.amount=script_getnum(st,3); - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.enable_logs&0x40) - log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); - - pc_delitem(sd,i,amount,0,0); - return 0; //we deleted exact amount of items. now exit - } else { - amount-=sd->status.inventory[i].amount; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.enable_logs&0x40) - log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); + if( it.amount <= 0 ) + return 0;// nothing to do - pc_delitem(sd,i,sd->status.inventory[i].amount,0,0); - } - } + if( buildin_delitem_search(sd, &it, false) ) + {// success + return 0; + } - ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", amount, sd->status.account_id, nameid); + ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid); st->state = END; clif_scriptclose(sd, st->oid); return 1; @@ -5731,9 +6085,8 @@ BUILDIN_FUNC(delitem) /// delitem2 "<Item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>} BUILDIN_FUNC(delitem2) { - int nameid=0,amount,i=0; - int iden,ref,attr,c1,c2,c3,c4; TBL_PC *sd; + struct item it; struct script_data *data; if( script_hasdata(st,11) ) @@ -5766,59 +6119,37 @@ BUILDIN_FUNC(delitem2) st->state = END; return 1; } - nameid = id->nameid;// "<item name>" + it.nameid = id->nameid;// "<item name>" } else - nameid = conv_num(st,data);// <item id> - - amount=script_getnum(st,3); - iden=script_getnum(st,4); - ref=script_getnum(st,5); - attr=script_getnum(st,6); - c1=(short)script_getnum(st,7); - c2=(short)script_getnum(st,8); - c3=(short)script_getnum(st,9); - c4=(short)script_getnum(st,10); - - if( amount <= 0 ) - return 0;// nothing to do - - for(i=0;i<MAX_INVENTORY;i++){ - //we don't delete wrong item or equipped item - if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid || - sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref || - sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 || - sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 || - sd->status.inventory[i].card[3]!=c4) - continue; - //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle - if(sd->inventory_data[i]->type==IT_PETEGG && sd->status.inventory[i].card[0] == CARD0_PET) + { + it.nameid = conv_num(st,data);// <item id> + if( !itemdb_exists( it.nameid ) ) { - if (!intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))) - continue; //Failed to send delete the pet. + ShowError("script:delitem: unknown item \"%d\".\n", it.nameid); + st->state = END; + return 1; } + } - if(sd->status.inventory[i].amount>=amount){ - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.enable_logs&0x40) - log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); - - pc_delitem(sd,i,amount,0,0); - return 0; //we deleted exact amount of items. now exit - } else { - amount-=sd->status.inventory[i].amount; + it.amount=script_getnum(st,3); + it.identify=script_getnum(st,4); + it.refine=script_getnum(st,5); + it.attribute=script_getnum(st,6); + it.card[0]=(short)script_getnum(st,7); + it.card[1]=(short)script_getnum(st,8); + it.card[2]=(short)script_getnum(st,9); + it.card[3]=(short)script_getnum(st,10); - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.enable_logs&0x40) - log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); + if( it.amount <= 0 ) + return 0;// nothing to do - pc_delitem(sd,i,sd->status.inventory[i].amount,0,0); - } + if( buildin_delitem_search(sd, &it, true) ) + {// success + return 0; } - ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", amount, sd->status.account_id, nameid); + ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid); st->state = END; clif_scriptclose(sd, st->oid); return 1; @@ -5904,34 +6235,21 @@ BUILDIN_FUNC(getcharid) /*========================================== *指定IDのPT名取得 *------------------------------------------*/ -char *buildin_getpartyname_sub(int party_id) -{ - struct party_data *p; - - p=party_search(party_id); - - if(p!=NULL){ - char *buf; - buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); - memcpy(buf, p->party.name, NAME_LENGTH); - buf[NAME_LENGTH-1] = '\0'; - return buf; - } - - return 0; -} BUILDIN_FUNC(getpartyname) { - char *name; int party_id; + struct party_data* p; - party_id=script_getnum(st,2); - name=buildin_getpartyname_sub(party_id); - if(name != NULL) - script_pushstr(st,name); + party_id = script_getnum(st,2); + + if( ( p = party_search(party_id) ) != NULL ) + { + script_pushstrcopy(st,p->party.name); + } else + { script_pushconststr(st,"null"); - + } return 0; } /*========================================== @@ -5952,13 +6270,13 @@ BUILDIN_FUNC(getpartymember) if(p->party.member[i].account_id){ switch (type) { case 2: - mapreg_setreg(add_str("$@partymemberaid")+(j<<24),p->party.member[i].account_id); + mapreg_setreg(reference_uid(add_str("$@partymemberaid"), j),p->party.member[i].account_id); break; case 1: - mapreg_setreg(add_str("$@partymembercid")+(j<<24),p->party.member[i].char_id); + mapreg_setreg(reference_uid(add_str("$@partymembercid"), j),p->party.member[i].char_id); break; default: - mapreg_setregstr(add_str("$@partymembername$")+(j<<24),p->party.member[i].name); + mapreg_setregstr(reference_uid(add_str("$@partymembername$"), j),p->party.member[i].name); } j++; } @@ -6009,75 +6327,58 @@ BUILDIN_FUNC(getpartyleader) /*========================================== *指定IDのギルド名取得 *------------------------------------------*/ -char *buildin_getguildname_sub(int guild_id) +BUILDIN_FUNC(getguildname) { - struct guild *g=NULL; - g=guild_search(guild_id); + int guild_id; + struct guild* g; - if(g!=NULL){ - char *buf; - buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); - memcpy(buf, g->name, NAME_LENGTH); - buf[NAME_LENGTH-1] = '\0'; - return buf; + guild_id = script_getnum(st,2); + + if( ( g = guild_search(guild_id) ) != NULL ) + { + script_pushstrcopy(st,g->name); } - return NULL; -} -BUILDIN_FUNC(getguildname) -{ - char *name; - int guild_id=script_getnum(st,2); - name=buildin_getguildname_sub(guild_id); - if(name != NULL) - script_pushstr(st,name); else + { script_pushconststr(st,"null"); + } return 0; } /*========================================== *指定IDのGuildMaster名取得 *------------------------------------------*/ -char *buildin_getguildmaster_sub(int guild_id) +BUILDIN_FUNC(getguildmaster) { - struct guild *g=NULL; - g=guild_search(guild_id); + int guild_id; + struct guild* g; - if(g!=NULL){ - char *buf; - buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); - memcpy(buf, g->master, NAME_LENGTH); - buf[NAME_LENGTH-1] = '\0'; - return buf; - } + guild_id = script_getnum(st,2); - return 0; -} -BUILDIN_FUNC(getguildmaster) -{ - char *master; - int guild_id=script_getnum(st,2); - master=buildin_getguildmaster_sub(guild_id); - if(master!=0) - script_pushstr(st,master); + if( ( g = guild_search(guild_id) ) != NULL ) + { + script_pushstrcopy(st,g->member[0].name); + } else + { script_pushconststr(st,"null"); + } return 0; } BUILDIN_FUNC(getguildmasterid) { - char *master; - TBL_PC *sd=NULL; - int guild_id=script_getnum(st,2); - master=buildin_getguildmaster_sub(guild_id); - if(master!=0){ - if((sd=map_nick2sd(master)) == NULL){ - script_pushint(st,0); - return 0; - } - script_pushint(st,sd->status.char_id); - }else{ + int guild_id; + struct guild* g; + + guild_id = script_getnum(st,2); + + if( ( g = guild_search(guild_id) ) != NULL ) + { + script_pushint(st,g->member[0].char_id); + } + else + { script_pushint(st,0); } return 0; @@ -6090,7 +6391,8 @@ BUILDIN_FUNC(strcharinfo) { TBL_PC *sd; int num; - char *buf; + struct guild* g; + struct party_data* p; sd=script_rid2sd(st); if (!sd) { //Avoid crashing.... @@ -6103,18 +6405,24 @@ BUILDIN_FUNC(strcharinfo) script_pushstrcopy(st,sd->status.name); break; case 1: - buf=buildin_getpartyname_sub(sd->status.party_id); - if(buf!=0) - script_pushstr(st,buf); + if( ( p = party_search(sd->status.party_id) ) != NULL ) + { + script_pushstrcopy(st,p->party.name); + } else + { script_pushconststr(st,""); + } break; case 2: - buf=buildin_getguildname_sub(sd->status.guild_id); - if(buf != NULL) - script_pushstr(st,buf); + if( ( g = guild_search(sd->status.guild_id) ) != NULL ) + { + script_pushstrcopy(st,g->name); + } else + { script_pushconststr(st,""); + } break; case 3: script_pushconststr(st,map[sd->bl.m].name); @@ -7534,8 +7842,8 @@ BUILDIN_FUNC(getmobdrops) if( itemdb_exists(mob->dropitem[i].nameid) == NULL ) continue; - mapreg_setreg(add_str("$@MobDrop_item") + (j<<24), mob->dropitem[i].nameid); - mapreg_setreg(add_str("$@MobDrop_rate") + (j<<24), mob->dropitem[i].p); + mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), mob->dropitem[i].nameid); + mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].p); j++; } @@ -7750,8 +8058,15 @@ BUILDIN_FUNC(clone) BUILDIN_FUNC(doevent) { const char* event = script_getstr(st,2); + struct map_session_data* sd; + + if( ( sd = script_rid2sd(st) ) == NULL ) + { + return 0; + } + check_event(st, event); - npc_event(map_id2sd(st->rid),event,0); + npc_event(sd, event, 0); return 0; } /*========================================== @@ -8226,18 +8541,18 @@ BUILDIN_FUNC(getusers) flag = script_getnum(st,2); - if(flag&0x8) - {// npc - bl = map_id2bl(st->oid); - } - else if((sd = script_rid2sd(st))!=NULL) - {// pc - bl = &sd->bl; - } - switch(flag&0x07) { case 0: + if(flag&0x8) + {// npc + bl = map_id2bl(st->oid); + } + else if((sd = script_rid2sd(st))!=NULL) + {// pc + bl = &sd->bl; + } + if(bl) { val = map[bl->m].users; @@ -8657,7 +8972,7 @@ BUILDIN_FUNC(homunculus_evolution) if (sd->hd->homunculus.intimacy > 91000) merc_hom_evolution(sd->hd); else - clif_emotion(&sd->hd->bl, 4) ; //swt + clif_emotion(&sd->hd->bl, E_SWT); } return 0; } @@ -9061,23 +9376,23 @@ BUILDIN_FUNC(warpwaitingpc) if( sd == NULL ) { ShowDebug("script:warpwaitingpc: no user in chat room position 0 (cd->users=%d,%d/%d)\n", cd->users, i, n); - mapreg_setreg(add_str("$@warpwaitingpc")+(i<<24), 0); + mapreg_setreg(reference_uid(add_str("$@warpwaitingpc"), i), 0); continue;// Broken npc chat room? } - mapreg_setreg(add_str("$@warpwaitingpc")+(i<<24), sd->bl.id); + mapreg_setreg(reference_uid(add_str("$@warpwaitingpc"), i), sd->bl.id); if( strcmp(map_name,"Random") == 0 ) - pc_randomwarp(sd,3); + pc_randomwarp(sd,CLR_TELEPORT); else if( strcmp(map_name,"SavePoint") == 0 ) { if( map[sd->bl.m].flag.noteleport ) return 0;// can't teleport on this map - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 3); + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); } else - pc_setpos(sd, mapindex_name2id(map_name), x, y, 0); + pc_setpos(sd, mapindex_name2id(map_name), x, y, CLR_OUTSIGHT); } mapreg_setreg(add_str("$@warpwaitingpcnum"), i); return 0; @@ -9087,15 +9402,31 @@ BUILDIN_FUNC(warpwaitingpc) // ... // +/// Detaches a character from a script. +/// +/// @param st Script state to detach the character from. +static void script_detach_rid(struct script_state* st) +{ + if(st->rid) + { + script_detach_state(st, false); + st->rid = 0; + } +} + /*========================================== * RIDのアタッチ *------------------------------------------*/ BUILDIN_FUNC(attachrid) { int rid = script_getnum(st,2); - - if (map_id2sd(rid)) { + struct map_session_data* sd; + + if ((sd = map_id2sd(rid))!=NULL) { + script_detach_rid(st); + st->rid = rid; + script_attach_state(st); script_pushint(st,1); } else script_pushint(st,0); @@ -9106,7 +9437,7 @@ BUILDIN_FUNC(attachrid) *------------------------------------------*/ BUILDIN_FUNC(detachrid) { - st->rid=0; + script_detach_rid(st); return 0; } /*========================================== @@ -9495,7 +9826,7 @@ static int buildin_maprespawnguildid_sub_pc(struct map_session_data* sd, va_list (sd->status.guild_id != g_id && flag&2) || //Warp out outsiders (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); + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); return 1; } @@ -9969,7 +10300,7 @@ BUILDIN_FUNC(mapwarp) // Added by RoVeRT for( i=0; i < g->max_member; i++) { if(g->member[i].sd && g->member[i].sd->bl.m==m){ - pc_setpos(g->member[i].sd,index,x,y,3); + pc_setpos(g->member[i].sd,index,x,y,CLR_TELEPORT); } } } @@ -9979,7 +10310,7 @@ BUILDIN_FUNC(mapwarp) // Added by RoVeRT if(p){ for(i=0;i<MAX_PARTY; i++){ if(p->data[i].sd && p->data[i].sd->bl.m == m){ - pc_setpos(p->data[i].sd,index,x,y,3); + pc_setpos(p->data[i].sd,index,x,y,CLR_TELEPORT); } } } @@ -10142,7 +10473,7 @@ BUILDIN_FUNC(warppartner) mapindex = mapindex_name2id(str); if (mapindex) { - pc_setpos(p_sd,mapindex,x,y,0); + pc_setpos(p_sd,mapindex,x,y,CLR_OUTSIGHT); script_pushint(st,1); } else script_pushint(st,0); @@ -10356,7 +10687,7 @@ BUILDIN_FUNC(getitemslots) 2 type; 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. if = 0, then monsters don't drop it at all (rare or a quest item) - if = 10000, then this item is sold in NPC shops only + if = -1, then this item is sold in NPC shops only 4 sex; 5 equip; 6 weight; @@ -10396,7 +10727,7 @@ BUILDIN_FUNC(getiteminfo) 2 type; 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. if = 0, then monsters don't drop it at all (rare or a quest item) - if = 10000, then this item is sold in NPC shops only + if = -1, then this item is sold in NPC shops only 4 sex; 5 equip; 6 weight; @@ -10545,18 +10876,18 @@ BUILDIN_FUNC(getinventorylist) 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,reference_uid(add_str("@inventorylist_id"), j),sd->status.inventory[i].nameid); + pc_setreg(sd,reference_uid(add_str("@inventorylist_amount"), j),sd->status.inventory[i].amount); + pc_setreg(sd,reference_uid(add_str("@inventorylist_equip"), j),sd->status.inventory[i].equip); + pc_setreg(sd,reference_uid(add_str("@inventorylist_refine"), j),sd->status.inventory[i].refine); + pc_setreg(sd,reference_uid(add_str("@inventorylist_identify"), j),sd->status.inventory[i].identify); + pc_setreg(sd,reference_uid(add_str("@inventorylist_attribute"), j),sd->status.inventory[i].attribute); for (k = 0; k < MAX_SLOTS; k++) { sprintf(card_var, "@inventorylist_card%d",k+1); - pc_setreg(sd,add_str(card_var)+(j<<24),sd->status.inventory[i].card[k]); + pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]); } - pc_setreg(sd,add_str("@inventorylist_expire")+(j<<24),sd->status.inventory[i].expire_time); + pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); j++; } } @@ -10571,9 +10902,9 @@ BUILDIN_FUNC(getskilllist) 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); + pc_setreg(sd,reference_uid(add_str("@skilllist_id"), j),sd->status.skill[i].id); + pc_setreg(sd,reference_uid(add_str("@skilllist_lv"), j),sd->status.skill[i].lv); + pc_setreg(sd,reference_uid(add_str("@skilllist_flag"), j),sd->status.skill[i].flag); j++; } } @@ -10665,11 +10996,11 @@ BUILDIN_FUNC(misceffect) if(st->oid && st->oid != fake_nd->bl.id) { struct block_list *bl = map_id2bl(st->oid); if (bl) - clif_misceffect2(bl,type); + clif_specialeffect(bl,type,AREA); } else{ TBL_PC *sd=script_rid2sd(st); if(sd) - clif_misceffect2(&sd->bl,type); + clif_specialeffect(&sd->bl,type,AREA); } return 0; } @@ -10678,27 +11009,34 @@ BUILDIN_FUNC(misceffect) *------------------------------------------*/ BUILDIN_FUNC(playBGM) { - TBL_PC* sd = script_rid2sd(st); - const char* name = script_getstr(st,2); + const char* name; + struct map_session_data* sd; - if(sd) + if( ( sd = script_rid2sd(st) ) != NULL ) { - if(!st->rid) - clif_playBGM(sd,map_id2bl(st->oid),name); - else - clif_playBGM(sd,&sd->bl,name); + name = script_getstr(st,2); + + clif_playBGM(sd, name); } return 0; } -int playBGM_sub(struct block_list* bl,va_list ap) +static int playBGM_sub(struct block_list* bl,va_list ap) { - char* name = va_arg(ap,char*); + const char* name = va_arg(ap,const char*); - clif_playBGM((TBL_PC *)bl, bl, name); + clif_playBGM(BL_CAST(BL_PC, bl), name); - return 0; + return 0; +} + +static int playBGM_foreachpc_sub(struct map_session_data* sd, va_list args) +{ + const char* name = va_arg(args, const char*); + + clif_playBGM(sd, name); + return 0; } /*========================================== @@ -10706,33 +11044,29 @@ int playBGM_sub(struct block_list* bl,va_list ap) *------------------------------------------*/ BUILDIN_FUNC(playBGMall) { - struct block_list* bl; const char* name; - bl = (st->rid) ? &(script_rid2sd(st)->bl) : map_id2bl(st->oid); - if (!bl) - return 0; - name = script_getstr(st,2); - if(script_hasdata(st,7)) - { // specified part of map + if( script_hasdata(st,7) ) + {// specified part of map const char* map = script_getstr(st,3); int x0 = script_getnum(st,4); int y0 = script_getnum(st,5); int x1 = script_getnum(st,6); int y1 = script_getnum(st,7); + map_foreachinarea(playBGM_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name); } - else - if(!script_hasdata(st,7)) - { // entire map + else if( script_hasdata(st,3) ) + {// entire map const char* map = script_getstr(st,3); + map_foreachinmap(playBGM_sub, map_mapname2mapid(map), BL_PC, name); } else - { - ShowError("buildin_playBGMall: insufficient arguments for specific area broadcast.\n"); + {// entire server + map_foreachpc(&playBGM_foreachpc_sub, name); } return 0; @@ -10749,10 +11083,7 @@ BUILDIN_FUNC(soundeffect) if(sd) { - if(!st->rid) - clif_soundeffect(sd,map_id2bl(st->oid),name,type); - else - clif_soundeffect(sd,&sd->bl,name,type); + clif_soundeffect(sd,&sd->bl,name,type); } return 0; } @@ -11702,7 +12033,7 @@ BUILDIN_FUNC(summon) delete_timer(md->deletetimer, mob_timer_delete); md->deletetimer = add_timer(tick+(timeout>0?timeout*1000:60000),mob_timer_delete,md->bl.id,0); mob_spawn (md); //Now it is ready for spawning. - clif_misceffect2(&md->bl,344); + clif_specialeffect(&md->bl,344,AREA); sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); } return 0; @@ -11724,68 +12055,6 @@ BUILDIN_FUNC(isday) } /*================================================ - * Check whether another item/card has been - * equipped - used for 2/15's cards patch [celest] - *------------------------------------------------*/ -// leave this here, just in case -#if 0 -BUILDIN_FUNC(isequipped) -{ - TBL_PC *sd; - int i, j, k, id = 1; - int ret = -1; - - sd = script_rid2sd(st); - - for (i=0; id!=0; i++) { - int flag = 0; - - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - - for (j=0; j<EQI_MAX; j++) { - int index; - index = sd->equip_index[j]; - if(index < 0) continue; - if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue; - if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue; - if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue; - - if(!sd->inventory_data[index]) - continue; - - if(itemdb_type(id) != IT_CARD) { //Non card - if (sd->inventory_data[index]->nameid != id) - continue; - flag = 1; - break; - } else { //Card - if (itemdb_isspecial(sd->status.inventory[index].card[0])) - continue; - for(k=0; k<sd->inventory_data[index]->slot; k++) { - if (sd->status.inventory[index].card[k] == id) - { - flag = 1; - break; - } - } - } - if (flag) break; - } - if (ret == -1) - ret = flag; - else - ret &= flag; - if (!ret) break; - } - - script_pushint(st,ret); - return 0; -} -#endif - -/*================================================ * Check how many items/cards in the list are * equipped - used for 2/15's cards patch [celest] *------------------------------------------------*/ @@ -12051,7 +12320,7 @@ BUILDIN_FUNC(setbattleflag) const char *flag, *value; flag = script_getstr(st,2); - value = script_getstr(st,3); + value = script_getstr(st,3); // HACK: Retrieve number as string (auto-converted) for battle_set_value if (battle_set_value(flag, value) == 0) ShowWarning("buildin_setbattleflag: unknown battle_config flag '%s'\n",flag); @@ -12142,7 +12411,7 @@ BUILDIN_FUNC(setnpcdisplay) if( newname ) npc_setdisplayname(nd, newname); - if( size != -1 && size != nd->size ) + if( size != -1 && size != (int)nd->size ) nd->size = size; else size = -1; @@ -12151,7 +12420,7 @@ BUILDIN_FUNC(setnpcdisplay) npc_setclass(nd, class_); else if( size != -1 ) { // Required to update the visual size - clif_clearunit_area(&nd->bl, 0); + clif_clearunit_area(&nd->bl, CLR_OUTSIGHT); clif_spawn(&nd->bl); } @@ -12231,10 +12500,9 @@ BUILDIN_FUNC(setd) { TBL_PC *sd=NULL; char varname[100]; - const char *value, *buffer; + const char *buffer; int elem; buffer = script_getstr(st, 2); - value = script_getstr(st, 3); if(sscanf(buffer, "%99[^[][%d]", varname, &elem) < 2) elem = 0; @@ -12249,10 +12517,10 @@ BUILDIN_FUNC(setd) } } - if(varname[strlen(varname)-1] != '$') { - setd_sub(st,sd, varname, elem, (void *)atoi(value),NULL); + if( is_string_variable(varname) ) { + setd_sub(st, sd, varname, elem, (void *)script_getstr(st, 3), NULL); } else { - setd_sub(st,sd, varname, elem, (void *)value,NULL); + setd_sub(st, sd, varname, elem, (void *)script_getnum(st, 3), NULL); } return 0; @@ -12266,7 +12534,7 @@ int buildin_query_sql_sub(struct script_state* st, Sql* handle) const char* query; struct script_data* data; const char* name; - int max_rows = 128;// maximum number of rows + int max_rows = SCRIPT_MAX_ARRAYSIZE;// maximum number of rows int num_vars; int num_cols; @@ -12413,7 +12681,7 @@ BUILDIN_FUNC(getd) elem = 0; // Push the 'pointer' so it's more flexible [Lance] - push_val(st->stack, C_NAME, (elem<<24) | add_str(varname)); + push_val(st->stack, C_NAME, reference_uid(add_str(varname), elem)); return 0; } @@ -12548,6 +12816,7 @@ BUILDIN_FUNC(npcshopdelitem) { const char* npcname = script_getstr(st,2); struct npc_data* nd = npc_name2id(npcname); + unsigned int nameid; int n, i; int amount; int size; @@ -12564,7 +12833,9 @@ BUILDIN_FUNC(npcshopdelitem) // remove specified items from the shop item list for( i = 3; i < 3 + amount; i++ ) { - ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == script_getnum(st,i) ); + nameid = script_getnum(st,i); + + ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == nameid ); if( n < size ) { memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n)); @@ -13016,7 +13287,7 @@ BUILDIN_FUNC(unitwarp) bl = map_id2bl(unit_id); if( map >= 0 && bl != NULL ) - script_pushint(st, unit_warp(bl,map,x,y,0)); + script_pushint(st, unit_warp(bl,map,x,y,CLR_OUTSIGHT)); else script_pushint(st, 0); @@ -13212,17 +13483,11 @@ BUILDIN_FUNC(unitskillusepos) BUILDIN_FUNC(sleep) { int ticks; - TBL_PC* sd; ticks = script_getnum(st,2); - sd = map_id2sd(st->rid); // detach the player - if( sd && sd->npc_id == st->oid ) - { - sd->npc_id = 0; - } - st->rid = 0; + script_detach_rid(st); if( ticks <= 0 ) {// do nothing @@ -13779,9 +14044,9 @@ BUILDIN_FUNC(waitingroom2bg) for( i = 0; i < n && i < MAX_BG_MEMBERS; i++ ) { if( (sd = cd->usersd[i]) != NULL && bg_team_join(bg_id, sd) ) - mapreg_setreg(add_str("$@arenamembers") + (i<<24), sd->bl.id); + mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), sd->bl.id); else - mapreg_setreg(add_str("$@arenamembers") + (i<<24), 0); + mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), 0); } mapreg_setreg(add_str("$@arenamembersnum"), i); @@ -13814,7 +14079,7 @@ BUILDIN_FUNC(waitingroom2bg_single) if( bg_team_join(bg_id, sd) ) { - pc_setpos(sd, mapindex, x, y, 3); + pc_setpos(sd, mapindex, x, y, CLR_TELEPORT); script_pushint(st,1); } else @@ -14283,7 +14548,7 @@ BUILDIN_FUNC(instance_warpall) mapindex = map_id2index(m); for( i = 0; i < MAX_PARTY; i++ ) - if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == st->instance_id ) pc_setpos(pl_sd,mapindex,x,y,3); + if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == st->instance_id ) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT); return 0; } @@ -14451,7 +14716,8 @@ BUILDIN_FUNC(deactivatepset); BUILDIN_FUNC(deletepset); #endif - +/// script command definitions +/// for an explanation on args, see add_buildin_func struct script_function buildin_func[] = { // NPC interaction BUILDIN_DEF(mes,"s"), @@ -14463,21 +14729,21 @@ struct script_function buildin_func[] = { BUILDIN_DEF(prompt,"s*"), // BUILDIN_DEF(goto,"l"), - BUILDIN_DEF(callsub,"i*"), + BUILDIN_DEF(callsub,"l*"), BUILDIN_DEF(callfunc,"s*"), BUILDIN_DEF(return,"?"), BUILDIN_DEF(getarg,"i?"), - BUILDIN_DEF(jobchange,"i*"), + BUILDIN_DEF(jobchange,"i?"), BUILDIN_DEF(jobname,"i"), - BUILDIN_DEF(input,"v??"), + BUILDIN_DEF(input,"r??"), BUILDIN_DEF(warp,"sii"), BUILDIN_DEF(areawarp,"siiiisii"), BUILDIN_DEF(warpchar,"siii"), // [LuzZza] - BUILDIN_DEF(warpparty,"siii*"), // [Fredzilla] [Paradox924X] + BUILDIN_DEF(warpparty,"siii?"), // [Fredzilla] [Paradox924X] BUILDIN_DEF(warpguild,"siii"), // [Fredzilla] BUILDIN_DEF(setlook,"ii"), BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it - BUILDIN_DEF(set,"ii"), + BUILDIN_DEF(set,"rv"), BUILDIN_DEF(setarray,"rv*"), BUILDIN_DEF(cleararray,"rvi"), BUILDIN_DEF(copyarray,"rri"), @@ -14486,12 +14752,12 @@ struct script_function buildin_func[] = { BUILDIN_DEF(getelementofarray,"ri"), BUILDIN_DEF(getitem,"vi?"), BUILDIN_DEF(rentitem,"vi"), - BUILDIN_DEF(getitem2,"iiiiiiiii*"), - BUILDIN_DEF(getnameditem,"is"), + BUILDIN_DEF(getitem2,"viiiiiiii?"), + BUILDIN_DEF(getnameditem,"vv"), BUILDIN_DEF2(grouprandomitem,"groupranditem","i"), - BUILDIN_DEF(makeitem,"iisii"), - BUILDIN_DEF(delitem,"ii?"), - BUILDIN_DEF(delitem2,"iiiiiiiii?"), + BUILDIN_DEF(makeitem,"visii"), + BUILDIN_DEF(delitem,"vi?"), + BUILDIN_DEF(delitem2,"viiiiiiii?"), BUILDIN_DEF2(enableitemuse,"enable_items",""), BUILDIN_DEF2(disableitemuse,"disable_items",""), BUILDIN_DEF(cutin,"si"), @@ -14500,13 +14766,13 @@ struct script_function buildin_func[] = { BUILDIN_DEF(itemheal,"ii"), BUILDIN_DEF(percentheal,"ii"), BUILDIN_DEF(rand,"i?"), - BUILDIN_DEF(countitem,"i"), - BUILDIN_DEF(countitem2,"iiiiiiii"), - BUILDIN_DEF(checkweight,"ii"), - BUILDIN_DEF(readparam,"i*"), - BUILDIN_DEF(getcharid,"i*"), + BUILDIN_DEF(countitem,"v"), + BUILDIN_DEF(countitem2,"viiiiiii"), + BUILDIN_DEF(checkweight,"vi"), + BUILDIN_DEF(readparam,"i?"), + BUILDIN_DEF(getcharid,"i?"), BUILDIN_DEF(getpartyname,"i"), - BUILDIN_DEF(getpartymember,"i*"), + BUILDIN_DEF(getpartymember,"i?"), BUILDIN_DEF(getpartyleader,"i?"), BUILDIN_DEF(getguildname,"i"), BUILDIN_DEF(getguildmaster,"i"), @@ -14557,16 +14823,16 @@ struct script_function buildin_func[] = { BUILDIN_DEF(gettime,"i"), BUILDIN_DEF(gettimestr,"si"), BUILDIN_DEF(openstorage,""), - BUILDIN_DEF(guildopenstorage,"*"), + BUILDIN_DEF(guildopenstorage,""), BUILDIN_DEF(itemskill,"vi"), BUILDIN_DEF(produce,"i"), BUILDIN_DEF(cooking,"i"), - BUILDIN_DEF(monster,"siisii*"), + BUILDIN_DEF(monster,"siisii?"), BUILDIN_DEF(getmobdrops,"i"), - BUILDIN_DEF(areamonster,"siiiisii*"), + BUILDIN_DEF(areamonster,"siiiisii?"), BUILDIN_DEF(killmonster,"ss?"), BUILDIN_DEF(killmonsterall,"s?"), - BUILDIN_DEF(clone,"siisi*"), + BUILDIN_DEF(clone,"siisi????"), BUILDIN_DEF(doevent,"s"), BUILDIN_DEF(donpcevent,"s"), BUILDIN_DEF(cmdothernpc,"ss"), @@ -14581,14 +14847,14 @@ struct script_function buildin_func[] = { BUILDIN_DEF(attachnpctimer,"?"), // attached the player id to the npc timer [Celest] BUILDIN_DEF(detachnpctimer,"?"), // detached the player id from the npc timer [Celest] BUILDIN_DEF(playerattached,""), // returns id of the current attached player. [Skotlex] - BUILDIN_DEF(announce,"si*"), - BUILDIN_DEF(mapannounce,"ssi*"), - BUILDIN_DEF(areaannounce,"siiiisi*"), + BUILDIN_DEF(announce,"si?????"), + BUILDIN_DEF(mapannounce,"ssi?????"), + BUILDIN_DEF(areaannounce,"siiiisi?????"), BUILDIN_DEF(getusers,"i"), BUILDIN_DEF(getmapguildusers,"si"), BUILDIN_DEF(getmapusers,"s"), BUILDIN_DEF(getareausers,"siiii"), - BUILDIN_DEF(getareadropitem,"siiiii"), + BUILDIN_DEF(getareadropitem,"siiiiv"), BUILDIN_DEF(enablenpc,"s"), BUILDIN_DEF(disablenpc,"s"), BUILDIN_DEF(hideoffnpc,"s"), @@ -14597,7 +14863,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(sc_start2,"iiii?"), BUILDIN_DEF(sc_start4,"iiiiii?"), BUILDIN_DEF(sc_end,"i?"), - BUILDIN_DEF(getscrate,"ii*"), + BUILDIN_DEF(getscrate,"ii?"), BUILDIN_DEF(debugmes,"s"), BUILDIN_DEF2(catchpet,"pet","i"), BUILDIN_DEF2(birthpet,"bpet",""), @@ -14605,7 +14871,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(resetstatus,""), BUILDIN_DEF(resetskill,""), BUILDIN_DEF(skillpointcount,""), - BUILDIN_DEF(changebase,"i"), + BUILDIN_DEF(changebase,"i?"), BUILDIN_DEF(changesex,""), BUILDIN_DEF(waitingroom,"si??"), BUILDIN_DEF(delwaitingroom,"?"), @@ -14621,22 +14887,22 @@ struct script_function buildin_func[] = { BUILDIN_DEF(isloggedin,"i?"), BUILDIN_DEF(setmapflagnosave,"ssii"), BUILDIN_DEF(getmapflag,"si"), - BUILDIN_DEF(setmapflag,"si*"), + BUILDIN_DEF(setmapflag,"si?"), BUILDIN_DEF(removemapflag,"si"), BUILDIN_DEF(pvpon,"s"), BUILDIN_DEF(pvpoff,"s"), BUILDIN_DEF(gvgon,"s"), BUILDIN_DEF(gvgoff,"s"), - BUILDIN_DEF(emotion,"i*"), + BUILDIN_DEF(emotion,"i??"), BUILDIN_DEF(maprespawnguildid,"sii"), BUILDIN_DEF(agitstart,""), // <Agit> BUILDIN_DEF(agitend,""), BUILDIN_DEF(agitcheck,""), // <Agitcheck> BUILDIN_DEF(flagemblem,"i"), // Flag Emblem BUILDIN_DEF(getcastlename,"s"), - BUILDIN_DEF(getcastledata,"si*"), + BUILDIN_DEF(getcastledata,"si?"), BUILDIN_DEF(setcastledata,"sii"), - BUILDIN_DEF(requestguildinfo,"i*"), + BUILDIN_DEF(requestguildinfo,"i?"), BUILDIN_DEF(getequipcardcnt,"i"), BUILDIN_DEF(successremovecards,"i"), BUILDIN_DEF(failedremovecards,"ii"), @@ -14649,7 +14915,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(getmotherid,""), BUILDIN_DEF(getfatherid,""), BUILDIN_DEF(warppartner,"sii"), - BUILDIN_DEF(getitemname,"i"), + BUILDIN_DEF(getitemname,"v"), BUILDIN_DEF(getitemslots,"i"), BUILDIN_DEF(makepet,"i"), BUILDIN_DEF(getexp,"ii"), @@ -14659,9 +14925,9 @@ struct script_function buildin_func[] = { BUILDIN_DEF(classchange,"ii"), BUILDIN_DEF(misceffect,"i"), BUILDIN_DEF(playBGM,"s"), - BUILDIN_DEF(playBGMall,"s*"), + BUILDIN_DEF(playBGMall,"s?????"), BUILDIN_DEF(soundeffect,"si"), - BUILDIN_DEF(soundeffectall,"si*"), // SoundEffectAll [Codemaster] + BUILDIN_DEF(soundeffectall,"si?????"), // SoundEffectAll [Codemaster] BUILDIN_DEF(strmobinfo,"ii"), // display mob data [Valaris] BUILDIN_DEF(guardian,"siisi??"), // summon guardians BUILDIN_DEF(guardianinfo,"sii"), // display guardian data [Valaris] @@ -14674,34 +14940,34 @@ struct script_function buildin_func[] = { BUILDIN_DEF(petskillsupport,"viiii"), // [Skotlex] BUILDIN_DEF(skilleffect,"vi"), // skill effect [Celest] BUILDIN_DEF(npcskilleffect,"viii"), // npc skill effect [Valaris] - BUILDIN_DEF(specialeffect,"i*"), // npc skill effect [Valaris] - BUILDIN_DEF(specialeffect2,"i*"), // skill effect on players[Valaris] + BUILDIN_DEF(specialeffect,"i??"), // npc skill effect [Valaris] + BUILDIN_DEF(specialeffect2,"i??"), // skill effect on players[Valaris] BUILDIN_DEF(nude,""), // nude command [Valaris] - BUILDIN_DEF(mapwarp,"ssii*"), // Added by RoVeRT - BUILDIN_DEF(atcommand,"*"), // [MouseJstr] - BUILDIN_DEF(charcommand,"*"), // [MouseJstr] + BUILDIN_DEF(mapwarp,"ssii??"), // Added by RoVeRT + BUILDIN_DEF(atcommand,"s"), // [MouseJstr] + BUILDIN_DEF(charcommand,"s"), // [MouseJstr] BUILDIN_DEF(movenpc,"sii"), // [MouseJstr] - BUILDIN_DEF(message,"s*"), // [MouseJstr] - BUILDIN_DEF(npctalk,"*"), // [Valaris] + BUILDIN_DEF(message,"ss"), // [MouseJstr] + BUILDIN_DEF(npctalk,"s"), // [Valaris] BUILDIN_DEF(mobcount,"ss"), BUILDIN_DEF(getlook,"i"), BUILDIN_DEF(getsavepoint,"i"), BUILDIN_DEF(npcspeed,"i"), // [Valaris] BUILDIN_DEF(npcwalkto,"ii"), // [Valaris] BUILDIN_DEF(npcstop,""), // [Valaris] - BUILDIN_DEF(getmapxy,"siii*"), //by Lorky [Lupus] + BUILDIN_DEF(getmapxy,"rrri?"), //by Lorky [Lupus] BUILDIN_DEF(checkoption1,"i"), BUILDIN_DEF(checkoption2,"i"), BUILDIN_DEF(guildgetexp,"i"), BUILDIN_DEF(guildchangegm,"is"), BUILDIN_DEF(logmes,"s"), //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus] - BUILDIN_DEF(summon,"si*"), // summons a slave monster [Celest] + BUILDIN_DEF(summon,"si??"), // summons a slave monster [Celest] BUILDIN_DEF(isnight,""), // check whether it is night time [Celest] BUILDIN_DEF(isday,""), // check whether it is day time [Celest] BUILDIN_DEF(isequipped,"i*"), // check whether another item/card has been equipped [Celest] BUILDIN_DEF(isequippedcnt,"i*"), // check how many items/cards are being equipped [Celest] BUILDIN_DEF(cardscnt,"i*"), // check how many items/cards are being equipped in the same arm [Lupus] - BUILDIN_DEF(getrefine,"*"), // returns the refined number of the current item, or an item with index specified [celest] + BUILDIN_DEF(getrefine,""), // returns the refined number of the current item, or an item with index specified [celest] BUILDIN_DEF(night,""), // sets the server to night time BUILDIN_DEF(day,""), // sets the server to day time #ifdef PCRE_SUPPORT @@ -14711,13 +14977,13 @@ struct script_function buildin_func[] = { BUILDIN_DEF(deletepset,"i"), // Delete a pattern set [MouseJstr] #endif BUILDIN_DEF(dispbottom,"s"), //added from jA [Lupus] - BUILDIN_DEF(getusersname,"*"), + BUILDIN_DEF(getusersname,""), BUILDIN_DEF(recovery,""), BUILDIN_DEF(getpetinfo,"i"), BUILDIN_DEF(gethominfo,"i"), BUILDIN_DEF(checkequipedcard,"i"), - BUILDIN_DEF(jump_zero,"ii"), //for future jA script compatibility - BUILDIN_DEF(globalmes,"s*"), + BUILDIN_DEF(jump_zero,"il"), //for future jA script compatibility + BUILDIN_DEF(globalmes,"s?"), BUILDIN_DEF(getmapmobs,"s"), //end jA addition BUILDIN_DEF(unequip,"i"), // unequip command [Spectre] BUILDIN_DEF(getstrlen,"s"), //strlen [Valaris] @@ -14734,22 +15000,22 @@ struct script_function buildin_func[] = { // <--- [zBuffer] List of mathematics commands BUILDIN_DEF(md5,"s"), // [zBuffer] List of dynamic var commands ---> - BUILDIN_DEF(getd,"*"), - BUILDIN_DEF(setd,"*"), + BUILDIN_DEF(getd,"s"), + BUILDIN_DEF(setd,"sv"), // <--- [zBuffer] List of dynamic var commands BUILDIN_DEF(petstat,"i"), - BUILDIN_DEF(callshop,"si"), // [Skotlex] + BUILDIN_DEF(callshop,"s?"), // [Skotlex] BUILDIN_DEF(npcshopitem,"sii*"), // [Lance] BUILDIN_DEF(npcshopadditem,"sii*"), BUILDIN_DEF(npcshopdelitem,"si*"), BUILDIN_DEF(npcshopattach,"s?"), BUILDIN_DEF(equip,"i"), BUILDIN_DEF(autoequip,"ii"), - BUILDIN_DEF(setbattleflag,"ss"), + BUILDIN_DEF(setbattleflag,"si"), BUILDIN_DEF(getbattleflag,"s"), - BUILDIN_DEF(setitemscript,"is*"), //Set NEW item bonus script. Lupus + BUILDIN_DEF(setitemscript,"is?"), //Set NEW item bonus script. Lupus BUILDIN_DEF(disguise,"i"), //disguise player. Lupus - BUILDIN_DEF(undisguise,"*"), //undisguise player. Lupus + BUILDIN_DEF(undisguise,""), //undisguise player. Lupus BUILDIN_DEF(getmonsterinfo,"ii"), //Lupus BUILDIN_DEF(axtoi,"s"), BUILDIN_DEF(query_sql,"s*"), @@ -14780,10 +15046,10 @@ struct script_function buildin_func[] = { BUILDIN_DEF(warpportal,"iisii"), BUILDIN_DEF2(homunculus_evolution,"homevolution",""), //[orn] BUILDIN_DEF2(homunculus_shuffle,"homshuffle",""), //[Zephyrus] - BUILDIN_DEF(eaclass,"*"), //[Skotlex] - BUILDIN_DEF(roclass,"i*"), //[Skotlex] - BUILDIN_DEF(checkvending,"*"), - BUILDIN_DEF(checkchatting,"*"), + BUILDIN_DEF(eaclass,"?"), //[Skotlex] + BUILDIN_DEF(roclass,"i?"), //[Skotlex] + BUILDIN_DEF(checkvending,"?"), + BUILDIN_DEF(checkchatting,"?"), BUILDIN_DEF(openmail,""), BUILDIN_DEF(openauction,""), BUILDIN_DEF(checkcell,"siii"), @@ -14801,7 +15067,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(readbook,"ii"), BUILDIN_DEF(setfont,"i"), BUILDIN_DEF(areamobuseskill,"siiiiviiiii"), - BUILDIN_DEF(progressbar, "si"), + BUILDIN_DEF(progressbar,"si"), BUILDIN_DEF(pushpc,"ii"), // WoE SE BUILDIN_DEF(agitstart2,""), @@ -14812,7 +15078,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(waitingroom2bg_single,"isiis"), BUILDIN_DEF(bg_team_setxy,"iii"), BUILDIN_DEF(bg_warp,"isii"), - BUILDIN_DEF(bg_monster,"isiisi*"), + BUILDIN_DEF(bg_monster,"isiisi?"), BUILDIN_DEF(bg_monster_set_team,"ii"), BUILDIN_DEF(bg_leave,""), BUILDIN_DEF(bg_destroy,"i"), @@ -14830,7 +15096,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(instance_id,"?"), BUILDIN_DEF(instance_set_timeout,"ii?"), BUILDIN_DEF(instance_init,"i"), - BUILDIN_DEF(instance_announce,"isi*"), + BUILDIN_DEF(instance_announce,"isi?????"), BUILDIN_DEF(instance_npcname,"s?"), BUILDIN_DEF(has_instance,"s?"), BUILDIN_DEF(instance_warpall,"sii?"), @@ -14839,7 +15105,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(setquest, "i"), BUILDIN_DEF(erasequest, "i"), BUILDIN_DEF(completequest, "i"), - BUILDIN_DEF(checkquest, "i*"), + BUILDIN_DEF(checkquest, "i?"), BUILDIN_DEF(changequest, "ii"), BUILDIN_DEF(showevent, "ii"), {NULL,NULL,NULL}, diff --git a/src/map/script.h b/src/map/script.h index aa6b85140..d6ede0e8b 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -11,6 +11,7 @@ extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp; extern int potion_target; extern struct Script_Config { + unsigned warn_func_mismatch_argtypes : 1; unsigned warn_func_mismatch_paramnum : 1; int check_cmdcount; int check_gotocount; @@ -121,6 +122,9 @@ struct script_state { int tick,timer,charid; } sleep; int instance_id; + //For backing up purposes + struct script_state *bk_st; + int bk_npcid; }; struct script_reg { @@ -163,6 +167,9 @@ struct DBMap* script_get_label_db(void); struct DBMap* script_get_userfunc_db(void); void script_run_autobonus(const char *autobonus,int id, int pos); +void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value); +void script_setarray_pc(struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache); + int script_config_read(char *cfgName); int do_init_script(void); int do_final_script(void); diff --git a/src/map/skill.c b/src/map/skill.c index ccc3d96f9..3c4cbcd21 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -180,9 +180,7 @@ int skill_tree_get_max(int id, int b_class) return skill_get_max(id); } -int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); int skill_frostjoke_scream(struct block_list *bl,va_list ap); -int status_change_timer_sub(struct block_list *bl, va_list ap); int skill_attack_area(struct block_list *bl,va_list ap); struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex] int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris] @@ -734,7 +732,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int case AM_ACIDTERROR: sc_start(bl,SC_BLEEDING,(skilllv*3),skilllv,skill_get_time2(skillid,skilllv)); if (skill_break_equip(bl, EQP_ARMOR, 100*skill_get_time(skillid,skilllv), BCT_ENEMY)) - clif_emotion(bl,23); + clif_emotion(bl,E_OMG); break; case AM_DEMONSTRATION: @@ -1093,11 +1091,14 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, int s if( sd == NULL || skillid <= 0 ) return 0; - sd->state.skillonskill = 1; for( i = 0; i < ARRAYLENGTH(sd->autospell3) && sd->autospell3[i].flag; i++ ) { if( sd->autospell3[i].flag != skillid ) continue; + + if( sd->autospell3[i].lock ) + continue; // autospell already being executed + skill = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id; if( skillnotok(skill, sd) ) continue; @@ -1115,6 +1116,7 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, int s continue; sd->state.autocast = 1; + sd->autospell3[i].lock = true; skill_consume_requirement(sd,skill,skilllv,1); switch( skill_get_casttype(skill) ) { @@ -1122,6 +1124,7 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, int s case CAST_NODAMAGE: skill_castend_nodamage_id(&sd->bl, tbl, skill, skilllv, tick, 0); break; case CAST_DAMAGE: skill_castend_damage_id(&sd->bl, tbl, skill, skilllv, tick, 0); break; } + sd->autospell3[i].lock = false; sd->state.autocast = 0; } @@ -1139,7 +1142,6 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, int s } } - sd->state.skillonskill = 0; return 1; } @@ -2307,11 +2309,11 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr data) switch(skl->skill_id) { case RG_INTIMIDATE: - if (unit_warp(src,-1,-1,-1,3) == 0) { + if (unit_warp(src,-1,-1,-1,CLR_TELEPORT) == 0) { short x,y; map_search_freecell(src, 0, &x, &y, 1, 1, 0); if (target != src && !status_isdead(target)) - unit_warp(target, -1, x, y, 3); + unit_warp(target, -1, x, y, CLR_TELEPORT); } break; case BA_FROSTJOKER: @@ -3002,7 +3004,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int break; case NPC_DARKBREATH: - clif_emotion(src,7); + clif_emotion(src,E_AG); case SN_FALCONASSAULT: case PA_PRESSURE: case CR_ACIDDEMONSTRATION: @@ -3098,8 +3100,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int { if( sd->state.arrow_atk ) //Consume arrow on last invocation to this skill. battle_consume_ammo(sd, skillid, skilllv); - if( !sd->state.skillonskill ) - skill_onskillusage(sd, bl, skillid, tick); + skill_onskillusage(sd, bl, skillid, tick); skill_consume_requirement(sd,skillid,skilllv,2); } @@ -4418,9 +4419,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in if( sd->state.autocast || ( (sd->skillitem == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skilllv == 1 ) || skilllv == 3 ) { if( skilllv == 1 ) - pc_randomwarp(sd,3); + pc_randomwarp(sd,CLR_TELEPORT); else - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); break; } @@ -4430,12 +4431,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in else clif_skill_warppoint(sd,skillid,skilllv, (unsigned short)-1,sd->status.save_point.map,0,0); } else - unit_warp(bl,-1,-1,-1,3); + unit_warp(bl,-1,-1,-1,CLR_TELEPORT); break; case NPC_EXPULSION: clif_skill_nodamage(src,bl,skillid,skilllv,1); - unit_warp(bl,-1,-1,-1,3); + unit_warp(bl,-1,-1,-1,CLR_TELEPORT); break; case AL_HOLYWATER: @@ -5360,7 +5361,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case 5: // 2000HP heal, random teleported status_heal(src, 2000, 0, 0); if( !map_flag_vs(bl->m) ) - unit_warp(bl, -1,-1,-1, 3); + unit_warp(bl, -1,-1,-1, CLR_TELEPORT); break; case 6: // random 2 other effects if (count == -1) @@ -5435,7 +5436,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in { //Erase death count 1% of the casts dstsd->die_counter = 0; pc_setglobalreg(dstsd,"PC_DIE_COUNTER", 0); - clif_misceffect2(bl, 0x152); + clif_specialeffect(bl, 0x152, AREA); //SC_SPIRIT invokes status_calc_pc for us. } clif_skill_nodamage(src,bl,skillid,skilllv, @@ -5530,7 +5531,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in continue; if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH)) dx[j] = dy[j] = 0; - pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], 2); + pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN); } } if (sd) @@ -5720,8 +5721,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in { if( sd->state.arrow_atk ) //Consume arrow on last invocation to this skill. battle_consume_ammo(sd, skillid, skilllv); - if( !sd->state.skillonskill ) - skill_onskillusage(sd, bl, skillid, tick); + skill_onskillusage(sd, bl, skillid, tick); skill_consume_requirement(sd,skillid,skilllv,2); } @@ -6585,8 +6585,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk { if( sd->state.arrow_atk && !(flag&1) ) //Consume arrow if a ground skill was not invoked. [Skotlex] battle_consume_ammo(sd, skillid, skilllv); - if( !sd->state.skillonskill ) - skill_onskillusage(sd, NULL, skillid, tick); + skill_onskillusage(sd, NULL, skillid, tick); skill_consume_requirement(sd,skillid,skilllv,2); } @@ -6643,9 +6642,9 @@ int skill_castend_map (struct map_session_data *sd, short skill_num, const char { case AL_TELEPORT: if(strcmp(map,"Random")==0) - pc_randomwarp(sd,3); + pc_randomwarp(sd,CLR_TELEPORT); else if (sd->menuskill_val > 1) //Need lv2 to be able to warp here. - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); break; case AL_WARP: @@ -7259,7 +7258,7 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un if( --sg->val1 <= 0 ) skill_delunitgroup(sg); - pc_setpos(sd,m,x,y,3); + pc_setpos(sd,m,x,y,CLR_TELEPORT); sg = src->group; // avoid dangling pointer (pc_setpos can cause deletion of 'sg') } } else @@ -7267,7 +7266,7 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un { int m = map_mapindex2mapid(sg->val3); if (m < 0) break; //Map not available on this map-server. - unit_warp(bl,m,sg->val2>>16,sg->val2&0xffff,3); + unit_warp(bl,m,sg->val2>>16,sg->val2&0xffff,CLR_TELEPORT); } break; @@ -7569,7 +7568,7 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns } else sec = 3000; //Couldn't trap it? - clif_01ac(&src->bl); // mysterious packet + clif_skillunit_update(&src->bl); sg->limit = DIFF_TICK(tick,sg->tick)+sec; sg->interval = -1; src->range = 0; @@ -9307,7 +9306,7 @@ void skill_repairweapon (struct map_session_data *sd, int idx) return; } - if (itemdb_type(item->nameid)==4) + if (itemdb_type(item->nameid)==IT_WEAPON) material = materials [itemdb_wlv(item->nameid)-1]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon else material = materials [2]; // Armors consume 1 Steel @@ -9358,7 +9357,7 @@ void skill_weaponrefine (struct map_session_data *sd, int idx) struct item_data *ditem = sd->inventory_data[idx]; item = &sd->status.inventory[idx]; - if(item->nameid > 0 && ditem->type == 4) + if(item->nameid > 0 && ditem->type == IT_WEAPON) { if( item->refine >= sd->menuskill_val || item->refine >= MAX_REFINE // if it's no longer refineable @@ -9408,7 +9407,7 @@ void skill_weaponrefine (struct map_session_data *sd, int idx) clif_refine(sd->fd,1,idx,item->refine); pc_delitem(sd,idx,1,0,2); clif_misceffect(&sd->bl,2); - clif_emotion(&sd->bl, 23); + clif_emotion(&sd->bl, E_OMG); } } } @@ -10162,13 +10161,17 @@ struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, /*========================================== * *------------------------------------------*/ -int skill_delunitgroup (struct skill_unit_group *group) +int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func) { struct block_list* src; struct unit_data *ud; int i,j; - nullpo_ret(group); + if( group == NULL ) + { + ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func); + return 0; + } src=map_id2bl(group->src_id); ud = unit_bl2ud(src); @@ -10404,13 +10407,13 @@ static int skill_unit_timer_sub (DBKey key, void* data, va_list ap) sd = map_charid2sd(group->val1); group->val1 = 0; if (sd && !map[sd->bl.m].flag.nowarp) - pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,3); + pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT); } if(group->val2) { sd = map_charid2sd(group->val2); group->val2 = 0; if (sd && !map[sd->bl.m].flag.nowarp) - pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,3); + pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT); } skill_delunit(unit); } diff --git a/src/map/skill.h b/src/map/skill.h index 04f2e8d28..fc7877be3 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -272,7 +272,8 @@ struct skill_unit_group *skill_unitsetting(struct block_list* src, short skillid struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2); int skill_delunit(struct skill_unit *unit); struct skill_unit_group *skill_initunitgroup(struct block_list* src, int count, short skillid, short skilllv, int unit_id, int limit, int interval); -int skill_delunitgroup(struct skill_unit_group *group); +int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func); +#define skill_delunitgroup(group) skill_delunitgroup_(group,__FILE__,__LINE__,__func__) int skill_clear_unitgroup(struct block_list *src); int skill_clear_group(struct block_list *bl, int flag); diff --git a/src/map/status.c b/src/map/status.c index 95d7f3796..449ea8f56 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -8,6 +8,7 @@ #include "../common/malloc.h" #include "../common/utils.h" #include "../common/ers.h" +#include "../common/strlib.h" #include "map.h" #include "path.h" @@ -831,16 +832,16 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s status_change_clear(target,0); if(flag&4) //Delete from memory. (also invokes map removal code) - unit_free(target,1); + unit_free(target,CLR_DEAD); else if(flag&2) //remove from map - unit_remove_map(target,1); + unit_remove_map(target,CLR_DEAD); else { //Some death states that would normally be handled by unit_remove_map unit_stop_attack(target); unit_stop_walking(target,1); unit_skillcastcancel(target,0); - clif_clearunit_area(target,1); + clif_clearunit_area(target,CLR_DEAD); skill_unit_move(target,gettick(),4); skill_cleartimerskill(target); } @@ -1083,7 +1084,7 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int if (sc->data[SC_WINKCHARM] && target && !flag) { //Prevents skill usage - clif_emotion(src, 3); + clif_emotion(src, E_LV); return 0; } @@ -1546,8 +1547,13 @@ int status_calc_pet_(struct pet_data *pd, bool first) if (first) { memcpy(&pd->status, &pd->db->status, sizeof(struct status_data)); - pd->status.mode|= MD_CANMOVE; //so they can chase their master! + pd->status.mode = MD_CANMOVE; // pets discard all modes, except walking pd->status.speed = pd->petDB->speed; + + if(battle_config.pet_attack_support || battle_config.pet_damage_support) + {// attack support requires the pet to be able to attack + pd->status.mode|= MD_CANATTACK; + } } if (battle_config.pet_lv_rate && pd->msd) @@ -1840,6 +1846,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first) + sizeof(sd->aspd_add) + sizeof(sd->setitem_hash) + sizeof(sd->setitem_hash2) + + sizeof(sd->itemhealrate2) // shorts + sizeof(sd->splash_range) + sizeof(sd->splash_add_range) @@ -1859,8 +1866,8 @@ int status_calc_pc_(struct map_session_data* sd, bool first) // Autobonus pc_delautobonus(sd,sd->autobonus,ARRAYLENGTH(sd->autobonus),true); - pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus),true); - pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus),true); + pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus2),true); + pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus3),true); // Parse equipment. for(i=0;i<EQI_MAX-1;i++) { @@ -3091,6 +3098,9 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first) } } + if( bl->type == BL_PET ) + return; // pets are not affected by statuses + if( first && bl->type == BL_MOB ) return; // assume there will be no statuses active @@ -4915,51 +4925,51 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val return 0; // Stats only for Mercenaries break; case SC_STRFOOD: - if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 > sc->data[type]->val1) + if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 > val1) return 0; break; case SC_AGIFOOD: - if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 > sc->data[type]->val1) + if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 > val1) return 0; break; case SC_VITFOOD: - if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 > sc->data[type]->val1) + if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 > val1) return 0; break; case SC_INTFOOD: - if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 > sc->data[type]->val1) + if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 > val1) return 0; break; case SC_DEXFOOD: - if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 > sc->data[type]->val1) + if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 > val1) return 0; break; case SC_LUKFOOD: - if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 > sc->data[type]->val1) + if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 > val1) return 0; break; case SC_FOOD_STR_CASH: - if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 > sc->data[type]->val1) + if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 > val1) return 0; break; case SC_FOOD_AGI_CASH: - if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 > sc->data[type]->val1) + if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 > val1) return 0; break; case SC_FOOD_VIT_CASH: - if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 > sc->data[type]->val1) + if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 > val1) return 0; break; case SC_FOOD_INT_CASH: - if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 > sc->data[type]->val1) + if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 > val1) return 0; break; case SC_FOOD_DEX_CASH: - if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 > sc->data[type]->val1) + if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 > val1) return 0; break; case SC_FOOD_LUK_CASH: - if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > sc->data[type]->val1) + if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1) return 0; break; } @@ -5084,52 +5094,40 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val status_change_end(bl,SC_INCREASEAGI,-1); break; case SC_STRFOOD: - if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_FOOD_STR_CASH,-1); + status_change_end(bl,SC_FOOD_STR_CASH,-1); break; case SC_AGIFOOD: - if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_FOOD_AGI_CASH,-1); + status_change_end(bl,SC_FOOD_AGI_CASH,-1); break; case SC_VITFOOD: - if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_FOOD_VIT_CASH,-1); + status_change_end(bl,SC_FOOD_VIT_CASH,-1); break; case SC_INTFOOD: - if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_FOOD_INT_CASH,-1); + status_change_end(bl,SC_FOOD_INT_CASH,-1); break; case SC_DEXFOOD: - if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_FOOD_DEX_CASH,-1); + status_change_end(bl,SC_FOOD_DEX_CASH,-1); break; case SC_LUKFOOD: - if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_FOOD_LUK_CASH,-1); + status_change_end(bl,SC_FOOD_LUK_CASH,-1); break; case SC_FOOD_STR_CASH: - if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_STRFOOD,-1); + status_change_end(bl,SC_STRFOOD,-1); break; case SC_FOOD_AGI_CASH: - if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_AGIFOOD,-1); + status_change_end(bl,SC_AGIFOOD,-1); break; case SC_FOOD_VIT_CASH: - if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_VITFOOD,-1); + status_change_end(bl,SC_VITFOOD,-1); break; case SC_FOOD_INT_CASH: - if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_INTFOOD,-1); + status_change_end(bl,SC_INTFOOD,-1); break; case SC_FOOD_DEX_CASH: - if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_DEXFOOD,-1); + status_change_end(bl,SC_DEXFOOD,-1); break; case SC_FOOD_LUK_CASH: - if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 <= sc->data[type]->val1) - status_change_end(bl,SC_LUKFOOD,-1); + status_change_end(bl,SC_LUKFOOD,-1); break; } @@ -5258,7 +5256,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_SIGNUMCRUCIS: val2 = 10 + 4*val1; //Def reduction tick = -1; - clif_emotion(bl,4); + clif_emotion(bl,E_SWT); break; case SC_MAXIMIZEPOWER: val2 = tick>0?tick:60000; @@ -5475,7 +5473,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val break; case SC_CONFUSION: - clif_emotion(bl,1); + clif_emotion(bl,E_WHAT); break; case SC_BLEEDING: val4 = tick/10000; @@ -5945,7 +5943,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val int pos = (bl->x&0xFFFF)|(bl->y<<16), //Current Coordinates map = sd->mapindex; //Current Map //1. Place in Jail (val2 -> Jail Map, val3 -> x, val4 -> y - pc_setpos(sd,(unsigned short)val2,val3,val4, 3); + pc_setpos(sd,(unsigned short)val2,val3,val4, CLR_TELEPORT); //2. Set restore point (val3 -> return map, val4 return coords val3 = map; val4 = pos; @@ -6607,6 +6605,15 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid) if(sce->val2) {// erase associated land skill group = skill_id2group(sce->val2); + + if( group == NULL ) + { + ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d). Please report this! (#3504)\n", + sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid, + sd ? sd->status.char_id : 0, + mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y); + } + sce->val2 = 0; skill_delunitgroup(group); } @@ -6728,7 +6735,7 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid) break; //natural expiration. if(sd && sd->mapindex == sce->val2) - pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, 3); + pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT); break; //guess hes not in jail :P case SC_CHANGE: if (tid == -1) @@ -7668,146 +7675,124 @@ static int status_natural_heal_timer(int tid, unsigned int tick, int id, intptr return 0; } -int status_readdb(void) -{ - int i,j,class_; - FILE *fp; - char line[1024], path[1024],*p; - - sprintf(path, "%s/job_db1.txt", db_path); - fp=fopen(path,"r"); // Job-specific values (weight, HP, SP, ASPD) - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; +/*========================================== + * DB reading. + * job_db1.txt - weight, hp, sp, aspd + * job_db2.txt - job level stat bonuses + * size_fix.txt - size adjustment table for weapons + * refine_db.txt - refining data table + *------------------------------------------*/ +static bool status_readdb_job1(char* fields[], int columns, int current) +{// Job-specific values (weight, HP, SP, ASPD) + int idx, class_; + unsigned int i; + + class_ = atoi(fields[0]); + + if(!pcdb_checkid(class_)) + { + ShowWarning("status_readdb_job1: Invalid job class %d specified.\n", class_); + return false; } - i = 0; - while(fgets(line, sizeof(line), fp)) + idx = pc_class2idx(class_); + + max_weight_base[idx] = atoi(fields[1]); + hp_coefficient[idx] = atoi(fields[2]); + hp_coefficient2[idx] = atoi(fields[3]); + sp_coefficient[idx] = atoi(fields[4]); + + for(i = 0; i < MAX_WEAPON_TYPE; i++) { - //NOTE: entry MAX_WEAPON_TYPE is not counted - char* split[5 + MAX_WEAPON_TYPE]; - i++; - if(line[0]=='/' && line[1]=='/') - continue; - for(j=0,p=line; j < 5 + MAX_WEAPON_TYPE && p; j++){ - split[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if(j < 5 + MAX_WEAPON_TYPE) - { //Weapon #.MAX_WEAPON_TYPE is constantly not load. Fix to that: replace < with <= [blackhole89] - ShowDebug("%s: Not enough columns at line %d\n", path, i); - continue; - } - class_ = atoi(split[0]); - if(!pcdb_checkid(class_)) - continue; - class_ = pc_class2idx(class_); - max_weight_base[class_]=atoi(split[1]); - hp_coefficient[class_]=atoi(split[2]); - hp_coefficient2[class_]=atoi(split[3]); - sp_coefficient[class_]=atoi(split[4]); - for(j=0;j<MAX_WEAPON_TYPE;j++) - aspd_base[class_][j]=atoi(split[j+5]); + aspd_base[idx][i] = atoi(fields[i+5]); } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + return true; +} - memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus - sprintf(path, "%s/job_db2.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; +static bool status_readdb_job2(char* fields[], int columns, int current) +{ + int idx, class_, i; + + class_ = atoi(fields[0]); + + if(!pcdb_checkid(class_)) + { + ShowWarning("status_readdb_job2: Invalid job class %d specified.\n", class_); + return false; } - while(fgets(line, sizeof(line), fp)) + idx = pc_class2idx(class_); + + for(i = 1; i < columns; i++) { - char *split[MAX_LEVEL+1]; //Job Level is limited to MAX_LEVEL, so the bonuses should likewise be limited to it. [Skotlex] - if(line[0]=='/' && line[1]=='/') - continue; - for(j=0,p=line;j<MAX_LEVEL+1 && p;j++){ - split[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - class_ = atoi(split[0]); - if(!pcdb_checkid(class_)) - continue; - class_ = pc_class2idx(class_); - for(i=1;i<j && split[i];i++) - job_bonus[class_][i-1]=atoi(split[i]); - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); - - // サイズ補正テ?ブル - for(i=0;i<3;i++) - for(j=0;j<MAX_WEAPON_TYPE;j++) - atkmods[i][j]=100; - sprintf(path, "%s/size_fix.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; + job_bonus[idx][i-1] = atoi(fields[i]); } - i=0; - while(fgets(line, sizeof(line), fp)) + return true; +} + +static bool status_readdb_sizefix(char* fields[], int columns, int current) +{ + unsigned int i; + + for(i = 0; i < MAX_WEAPON_TYPE; i++) { - char *split[MAX_WEAPON_TYPE]; - if(line[0]=='/' && line[1]=='/') - continue; - if(atoi(line)<=0) - continue; - memset(split,0,sizeof(split)); - for(j=0,p=line;j<MAX_WEAPON_TYPE && p;j++){ - split[j]=p; - p=strchr(p,','); - if(p) *p++=0; - atkmods[i][j]=atoi(split[j]); - } - i++; + atkmods[current][i] = atoi(fields[i]); } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + return true; +} + +static bool status_readdb_refine(char* fields[], int columns, int current) +{ + int i; + + refinebonus[current][0] = atoi(fields[0]); // stats per safe-upgrade + refinebonus[current][1] = atoi(fields[1]); // stats after safe-limit + refinebonus[current][2] = atoi(fields[2]); // safe limit + + for(i = 0; i < MAX_REFINE; i++) + { + percentrefinery[current][i] = atoi(fields[3+i]); + } + return true; +} - // 精?デ?タテ?ブル - for(i=0;i<5;i++){ +int status_readdb(void) +{ + int i, j; + + // initialize databases to default + // + + // job_db1.txt + memset(max_weight_base, 0, sizeof(max_weight_base)); + memset(hp_coefficient, 0, sizeof(hp_coefficient)); + memset(hp_coefficient2, 0, sizeof(hp_coefficient2)); + memset(sp_coefficient, 0, sizeof(sp_coefficient)); + memset(aspd_base, 0, sizeof(aspd_base)); + + // job_db2.txt + memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus + + // size_fix.txt + for(i=0;i<ARRAYLENGTH(atkmods);i++) + for(j=0;j<MAX_WEAPON_TYPE;j++) + atkmods[i][j]=100; + + // refine_db.txt + for(i=0;i<ARRAYLENGTH(percentrefinery);i++){ for(j=0;j<MAX_REFINE; j++) - percentrefinery[i][j]=100; + percentrefinery[i][j]=100; // success chance percentrefinery[i][j]=0; //Slot MAX+1 always has 0% success chance [Skotlex] - refinebonus[i][0]=0; - refinebonus[i][1]=0; - refinebonus[i][2]=10; + refinebonus[i][0]=0; // stats per safe-upgrade + refinebonus[i][1]=0; // stats after safe-limit + refinebonus[i][2]=10; // safe limit } - sprintf(path, "%s/refine_db.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; - } - i=0; - while(fgets(line, sizeof(line), fp)) - { - char *split[MAX_REFINE+4]; - if(line[0]=='/' && line[1]=='/') - continue; - if(atoi(line)<=0) - continue; - memset(split,0,sizeof(split)); - for(j=0,p=line;j<MAX_REFINE+4 && p;j++){ - split[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - refinebonus[i][0]=atoi(split[0]); // 精?ボ?ナス - refinebonus[i][1]=atoi(split[1]); // 過?精?ボ?ナス - refinebonus[i][2]=atoi(split[2]); // 安全精?限界 - for(j=0;j<MAX_REFINE && split[j+3];j++) - percentrefinery[i][j]=atoi(split[j+3]); - i++; - } - fclose(fp); //Lupus. close this file!!! - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + // read databases + // + + sv_readdb(db_path, "job_db1.txt", ',', 5+MAX_WEAPON_TYPE, 5+MAX_WEAPON_TYPE, -1, &status_readdb_job1); + sv_readdb(db_path, "job_db2.txt", ',', 1, 1+MAX_LEVEL, -1, &status_readdb_job2); + sv_readdb(db_path, "size_fix.txt", ',', MAX_WEAPON_TYPE, MAX_WEAPON_TYPE, ARRAYLENGTH(atkmods), &status_readdb_sizefix); + sv_readdb(db_path, "refine_db.txt", ',', 3+MAX_REFINE+1, 3+MAX_REFINE+1, ARRAYLENGTH(percentrefinery), &status_readdb_refine); return 0; } diff --git a/src/map/storage.c b/src/map/storage.c index 8960f7203..eb64d3eb4 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -42,17 +42,15 @@ static int storage_comp_item(const void *_i1, const void *_i2) return -1; return i1->nameid - i2->nameid; } -/* In case someone wants to use it in the future. -static void storage_sortitem(struct storage_data* stor) -{ - nullpo_retv(stor); - qsort(stor->items, MAX_STORAGE, sizeof(struct item), storage_comp_item); -} -*/ -static void storage_gsortitem(struct guild_storage* gstor) + +static void storage_sortitem(struct item* items, unsigned int size) { - nullpo_retv(gstor); - qsort(gstor->storage_, MAX_GUILD_STORAGE, sizeof(struct item), storage_comp_item); + nullpo_retv(items); + + if( battle_config.client_sort_storage ) + { + qsort(items, size, sizeof(struct item), storage_comp_item); + } } /*========================================== @@ -104,7 +102,8 @@ int storage_storageopen(struct map_session_data *sd) } sd->state.storage_flag = 1; - clif_storagelist(sd,&sd->status.storage); + storage_sortitem(sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); + clif_storagelist(sd, sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items)); clif_updatestorageamount(sd,sd->status.storage.storage_amount); return 0; } @@ -380,7 +379,8 @@ int storage_guild_storageopen(struct map_session_data* sd) gstor->storage_status = 1; sd->state.storage_flag = 2; - clif_guildstoragelist(sd,gstor); + storage_sortitem(gstor->items, ARRAYLENGTH(gstor->items)); + clif_storagelist(sd, gstor->items, ARRAYLENGTH(gstor->items)); clif_updateguildstorageamount(sd,gstor->storage_amount); return 0; } @@ -406,11 +406,11 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto if(itemdb_isstackable2(data)){ //Stackable for(i=0;i<MAX_GUILD_STORAGE;i++){ - if(compare_item(&stor->storage_[i], item_data)) { - if(stor->storage_[i].amount+amount > MAX_AMOUNT) + if(compare_item(&stor->items[i], item_data)) { + if(stor->items[i].amount+amount > MAX_AMOUNT) return 1; - stor->storage_[i].amount+=amount; - clif_guildstorageitemadded(sd,&stor->storage_[i],i,amount); + stor->items[i].amount+=amount; + clif_storageitemadded(sd,&stor->items[i],i,amount); stor->dirty = 1; if(log_config.enable_logs&0x1000) log_pick_pc(sd, "G", item_data->nameid, -amount, item_data); @@ -419,15 +419,15 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto } } //Add item - for(i=0;i<MAX_GUILD_STORAGE && stor->storage_[i].nameid;i++); + for(i=0;i<MAX_GUILD_STORAGE && stor->items[i].nameid;i++); if(i>=MAX_GUILD_STORAGE) return 1; - memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0])); - stor->storage_[i].amount=amount; + memcpy(&stor->items[i],item_data,sizeof(stor->items[0])); + stor->items[i].amount=amount; stor->storage_amount++; - clif_guildstorageitemadded(sd,&stor->storage_[i],i,amount); + clif_storageitemadded(sd,&stor->items[i],i,amount); clif_updateguildstorageamount(sd,stor->storage_amount); stor->dirty = 1; if(log_config.enable_logs&0x1000) @@ -440,14 +440,14 @@ int guild_storage_delitem(struct map_session_data* sd, struct guild_storage* sto nullpo_retr(1, sd); nullpo_retr(1, stor); - if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount) + if(stor->items[n].nameid==0 || stor->items[n].amount<amount) return 1; - stor->storage_[n].amount-=amount; + stor->items[n].amount-=amount; if(log_config.enable_logs&0x1000) - log_pick_pc(sd, "G", stor->storage_[n].nameid, amount, &stor->storage_[n]); - if(stor->storage_[n].amount==0){ - memset(&stor->storage_[n],0,sizeof(stor->storage_[0])); + log_pick_pc(sd, "G", stor->items[n].nameid, amount, &stor->items[n]); + if(stor->items[n].amount==0){ + memset(&stor->items[n],0,sizeof(stor->items[0])); stor->storage_amount--; clif_updateguildstorageamount(sd,stor->storage_amount); } @@ -496,13 +496,13 @@ int storage_guild_storageget(struct map_session_data* sd, int index, int amount) if(index<0 || index>=MAX_GUILD_STORAGE) return 0; - if(stor->storage_[index].nameid <= 0) + if(stor->items[index].nameid <= 0) return 0; - if(amount < 1 || amount > stor->storage_[index].amount) + if(amount < 1 || amount > stor->items[index].amount) return 0; - if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0) + if((flag = pc_additem(sd,&stor->items[index],amount)) == 0) guild_storage_delitem(sd,stor,index,amount); else clif_additem(sd,0,0,flag); @@ -549,13 +549,13 @@ int storage_guild_storagegettocart(struct map_session_data* sd, int index, int a if(index<0 || index>=MAX_GUILD_STORAGE) return 0; - if(stor->storage_[index].nameid<=0) + if(stor->items[index].nameid<=0) return 0; - if(amount < 1 || amount > stor->storage_[index].amount) + if(amount < 1 || amount > stor->items[index].amount) return 0; - if(pc_cart_additem(sd,&stor->storage_[index],amount)==0) + if(pc_cart_additem(sd,&stor->items[index],amount)==0) guild_storage_delitem(sd,stor,index,amount); return 1; @@ -584,7 +584,6 @@ int storage_guild_storagesaved(int guild_id) if (stor->dirty && stor->storage_status == 0) { //Storage has been correctly saved. stor->dirty = 0; - storage_gsortitem(stor); } return 1; } diff --git a/src/map/unit.c b/src/map/unit.c index 05c0bcb16..df4efef8e 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -641,7 +641,7 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag) //Warps a unit/ud to a given map/position. //In the case of players, pc_setpos is used. //it respects the no warp flags, so it is safe to call this without doing nowarpto/nowarp checks. -int unit_warp(struct block_list *bl,short m,short x,short y,int type) +int unit_warp(struct block_list *bl,short m,short x,short y,clr_type type) { struct unit_data *ud; nullpo_ret(bl); @@ -650,7 +650,7 @@ int unit_warp(struct block_list *bl,short m,short x,short y,int type) if(bl->prev==NULL || !ud) return 1; - if (type < 0 || type == 1) + if (type == CLR_DEAD) //Type 1 is invalid, since you shouldn't warp a bl with the "death" //animation, it messes up with unit_remove_map! [Skotlex] return 1; @@ -1305,8 +1305,6 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, sh return 1; } -static int unit_attack_timer(int tid, unsigned int tick, int id, intptr data); - int unit_stop_attack(struct block_list *bl) { struct unit_data *ud = unit_bl2ud(bl); @@ -1800,7 +1798,7 @@ int unit_changeviewsize(struct block_list *bl,short size) } else return 0; if(size!=0) - clif_misceffect2(bl,421+size); + clif_specialeffect(bl,421+size, AREA); return 0; } @@ -1811,7 +1809,7 @@ int unit_changeviewsize(struct block_list *bl,short size) * Otherwise it is assumed bl is being warped. * On-Kill specific stuff is not performed here, look at status_damage for that. *------------------------------------------*/ -int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int line, const char* func) +int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, int line, const char* func) { struct unit_data *ud = unit_bl2ud(bl); struct status_change *sc = status_get_sc(bl); @@ -1960,7 +1958,7 @@ int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int l { //If logging out, this is deleted on unit_free clif_clearunit_area(bl,clrtype); map_delblock(bl); - unit_free(bl,0); + unit_free(bl,CLR_OUTSIGHT); map_freeblock_unlock(); return 0; } @@ -1973,10 +1971,10 @@ int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int l ud->canact_tick = ud->canmove_tick; //It appears HOM do reset the can-act tick. if( !hd->homunculus.intimacy && !(hd->master && !hd->master->state.active) ) { //If logging out, this is deleted on unit_free - clif_emotion(bl, 28) ; //sob + clif_emotion(bl, E_SOB); clif_clearunit_area(bl,clrtype); map_delblock(bl); - unit_free(bl,0); + unit_free(bl,CLR_OUTSIGHT); map_freeblock_unlock(); return 0; } @@ -1990,7 +1988,7 @@ int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int l { clif_clearunit_area(bl,clrtype); map_delblock(bl); - unit_free(bl,0); + unit_free(bl,CLR_OUTSIGHT); map_freeblock_unlock(); return 0; } @@ -2005,11 +2003,11 @@ int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int l return 1; } -void unit_remove_map_pc(struct map_session_data *sd, int clrtype) +void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype) { unit_remove_map(&sd->bl,clrtype); - if (clrtype == 3) clrtype = 0; //3 is the warp from logging out, but pets/homunc need to just 'vanish' instead of showing the warping out animation. + if (clrtype == CLR_TELEPORT) clrtype = CLR_OUTSIGHT; //CLR_TELEPORT is the warp from logging out, but pets/homunc need to just 'vanish' instead of showing the warping out animation. if(sd->pd) unit_remove_map(&sd->pd->bl, clrtype); @@ -2021,19 +2019,17 @@ void unit_remove_map_pc(struct map_session_data *sd, int clrtype) void unit_free_pc(struct map_session_data *sd) { - if (sd->pd) unit_free(&sd->pd->bl,0); - if (sd->hd) unit_free(&sd->hd->bl,0); - if (sd->md) unit_free(&sd->md->bl,0); - unit_free(&sd->bl,3); + if (sd->pd) unit_free(&sd->pd->bl,CLR_OUTSIGHT); + if (sd->hd) unit_free(&sd->hd->bl,CLR_OUTSIGHT); + if (sd->md) unit_free(&sd->md->bl,CLR_OUTSIGHT); + unit_free(&sd->bl,CLR_TELEPORT); } /*========================================== * Function to free all related resources to the bl * if unit is on map, it is removed using the clrtype specified - * If clrtype is <0, no saving is performed. This is only for non-authed - * objects that shouldn't be on a map yet. *------------------------------------------*/ -int unit_free(struct block_list *bl, int clrtype) +int unit_free(struct block_list *bl, clr_type clrtype) { struct unit_data *ud = unit_bl2ud( bl ); nullpo_ret(ud); @@ -2053,8 +2049,8 @@ int unit_free(struct block_list *bl, int clrtype) pc_delinvincibletimer(sd); pc_delautobonus(sd,sd->autobonus,ARRAYLENGTH(sd->autobonus),false); - pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus),false); - pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus),false); + pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus2),false); + pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus3),false); if( sd->followtimer != -1 ) pc_stop_following(sd); @@ -2138,15 +2134,12 @@ int unit_free(struct block_list *bl, int clrtype) aFree (pd->loot); pd->loot = NULL; } - if( clrtype >= 0 ) - { - if( pd->pet.intimate > 0 ) - intif_save_petdata(pd->pet.account_id,&pd->pet); - else - { //Remove pet. - intif_delete_petdata(pd->pet.pet_id); - if (sd) sd->status.pet_id = 0; - } + if( pd->pet.intimate > 0 ) + intif_save_petdata(pd->pet.account_id,&pd->pet); + else + { //Remove pet. + intif_delete_petdata(pd->pet.pet_id); + if (sd) sd->status.pet_id = 0; } if( sd ) sd->pd = NULL; @@ -2213,16 +2206,13 @@ int unit_free(struct block_list *bl, int clrtype) struct homun_data *hd = (TBL_HOM*)bl; struct map_session_data *sd = hd->master; merc_hom_hungry_timer_delete(hd); - if( clrtype >= 0 ) + if( hd->homunculus.intimacy > 0 ) + merc_save(hd); + else { - if( hd->homunculus.intimacy > 0 ) - merc_save(hd); - else - { - intif_homunculus_requestdelete(hd->homunculus.hom_id); - if( sd ) - sd->status.hom_id = 0; - } + intif_homunculus_requestdelete(hd->homunculus.hom_id); + if( sd ) + sd->status.hom_id = 0; } if( sd ) sd->hd = NULL; @@ -2232,16 +2222,13 @@ int unit_free(struct block_list *bl, int clrtype) { struct mercenary_data *md = (TBL_MER*)bl; struct map_session_data *sd = md->master; - if( clrtype >= 0 ) + if( mercenary_get_lifetime(md) > 0 ) + mercenary_save(md); + else { - if( mercenary_get_lifetime(md) > 0 ) - mercenary_save(md); - else - { - intif_mercenary_delete(md->mercenary.mercenary_id); - if( sd ) - sd->status.mer_id = 0; - } + intif_mercenary_delete(md->mercenary.mercenary_id); + if( sd ) + sd->status.mer_id = 0; } if( sd ) sd->md = NULL; diff --git a/src/map/unit.h b/src/map/unit.h index 3b82dce56..fd5a83208 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -9,6 +9,7 @@ struct block_list; struct unit_data; struct map_session_data; +#include "clif.h" // clr_type #include "map.h" // struct block_list #include "path.h" // struct walkpath_data #include "skill.h" // struct skill_timerskill, struct skill_unit_group, struct skill_unit_group_tickset @@ -81,7 +82,7 @@ int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int int unit_escape(struct block_list *bl, struct block_list *target, short dist); // 位置の強制移動(吹き飛ばしなど) int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool checkpath); -int unit_warp(struct block_list *bl, short map, short x, short y, int type); +int unit_warp(struct block_list *bl, short map, short x, short y, clr_type type); int unit_setdir(struct block_list *bl,unsigned char dir); uint8 unit_getdir(struct block_list *bl); int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag); @@ -114,11 +115,11 @@ void unit_dataset(struct block_list *bl); int unit_fixdamage(struct block_list *src,struct block_list *target,unsigned int tick,int sdelay,int ddelay,int damage,int div,int type,int damage2); // その他 struct unit_data* unit_bl2ud(struct block_list *bl); -void unit_remove_map_pc(struct map_session_data *sd, int clrtype); +void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype); void unit_free_pc(struct map_session_data *sd); #define unit_remove_map(bl,clrtype) unit_remove_map_(bl,clrtype,__FILE__,__LINE__,__func__) -int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int line, const char* func); -int unit_free(struct block_list *bl, int clrtype); +int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, int line, const char* func); +int unit_free(struct block_list *bl, clr_type clrtype); int unit_changeviewsize(struct block_list *bl,short size); // 初期化ルーチン diff --git a/src/map/vending.c b/src/map/vending.c index 67e5ebf19..90bff7399 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -275,7 +275,7 @@ void vending_openvending(struct map_session_data* sd, const char* message, bool sd->vending[i].index = index; sd->vending[i].amount = amount; - sd->vending[i].value = cap_value(value, 1, (unsigned int)battle_config.vending_max_value); + sd->vending[i].value = cap_value(value, 0, (unsigned int)battle_config.vending_max_value); i++; // item successfully added } diff --git a/src/plugins/sig.c b/src/plugins/sig.c index 1872d48ff..9db0638ca 100644 --- a/src/plugins/sig.c +++ b/src/plugins/sig.c @@ -3,6 +3,7 @@ #include <stdio.h> #include <stdlib.h> #include <signal.h> +#define __USE_GNU // required to enable strsignal on some platforms #include <string.h> #include <time.h> #include "../common/plugin.h" @@ -52,7 +53,6 @@ unsigned long (*getuptime)(); char *server_name; int crash_flag = 0; -extern const char *strsignal(int); int sig_final (); // by Gabuzomeu @@ -60,9 +60,6 @@ int sig_final (); // (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced // Programming in the UNIX Environment_. // -#ifdef WIN32 // windows don't have SIGPIPE -#define SIGPIPE SIGINT -#endif #ifndef POSIX #define compat_signal(signo, func) signal(signo, func) @@ -203,9 +200,7 @@ int sig_init () compat_signal(SIGSEGV, func); compat_signal(SIGFPE, func); compat_signal(SIGILL, func); - #ifndef __WIN32 - compat_signal(SIGBUS, func); - #endif + compat_signal(SIGBUS, func); return 1; } diff --git a/src/tool/adduser.c b/src/tool/adduser.c index 2c7461930..5f4dfb6a5 100644 --- a/src/tool/adduser.c +++ b/src/tool/adduser.c @@ -75,7 +75,7 @@ int main(int argc, char *argv[]) strcpy(username, ""); while (strlen(username) < 4 || strlen(username) > 23) { printf("Enter an username (4-23 characters): "); - scanf("%s", &username); + scanf("%s", username); username[23] = 0; remove_control_chars(username); } @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) strcpy(password, ""); while (strlen(password) < 4 || strlen(password) > 23) { printf("Enter a password (4-23 characters): "); - scanf("%s", &password); + scanf("%s", password); password[23] = 0; remove_control_chars(password); } @@ -91,7 +91,7 @@ int main(int argc, char *argv[]) strcpy(sex, ""); while (strcmp(sex, "F") != 0 && strcmp(sex, "M") != 0) { printf("Enter a gender (M for male, F for female): "); - scanf("%s", &sex); + scanf("%s", sex); } FPaccout = fopen(account_txt, "r+"); diff --git a/src/txt-converter/Makefile.in b/src/txt-converter/Makefile.in index 089d9d276..50e89e34e 100644 --- a/src/txt-converter/Makefile.in +++ b/src/txt-converter/Makefile.in @@ -10,6 +10,7 @@ LOGIN_CONVERTER_OBJ = \ ../common/obj_all/showmsg.o \ ../common/obj_all/strlib.o \ ../common/obj_all/timer.o \ + ../common/obj_all/utils.o \ ../common/obj_sql/sql.o LOGIN_CONVERTER_H = \ ../login/account.h \ @@ -23,6 +24,7 @@ LOGIN_CONVERTER_H = \ ../common/showmsg.h \ ../common/strlib.h \ ../common/timer.h \ + ../common/utils.h \ ../common/sql.h CHAR_CONVERTER_OBJ = \ |