diff options
Diffstat (limited to 'src')
53 files changed, 8552 insertions, 3183 deletions
diff --git a/src/char/char.c b/src/char/char.c index 0f23744c0..90e954002 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -1793,7 +1793,7 @@ int count_users(void) // 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 136 //Max size (for WFIFOHEAD calls) +#define MAX_CHAR_BUF 140 //Max size (for WFIFOHEAD calls) int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { unsigned short offset = 0; @@ -1828,7 +1828,7 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed; WBUFW(buf,52) = p->class_; WBUFW(buf,54) = p->hair; - WBUFW(buf,56) = p->option&0x7E80020 ? 0 : p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? + WBUFW(buf,56) = p->option&0x20 ? 0 : p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? WBUFW(buf,58) = p->base_level; WBUFW(buf,60) = min(p->skill_point, INT16_MAX); WBUFW(buf,62) = p->head_bottom; @@ -1861,6 +1861,10 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) WBUFL(buf,128) = p->robe; offset += 4; #endif +#if PACKETVER >= 20110928 + WBUFL(buf,132) = 0; // change slot feature (0 = disabled, otherwise enabled) + offset += 4; +#endif return 106+offset; } diff --git a/src/char/int_party.c b/src/char/int_party.c index b340aa9cf..2ae9e5319 100644 --- a/src/char/int_party.c +++ b/src/char/int_party.c @@ -309,29 +309,29 @@ int mapif_party_created(int fd,int account_id, int char_id, struct party *p) } // パ?ティ情報見つからず -int mapif_party_noinfo(int fd, int party_id) { - WFIFOHEAD(fd, 8); +static void mapif_party_noinfo(int fd, int party_id, int char_id) +{ + WFIFOHEAD(fd, 12); WFIFOW(fd,0) = 0x3821; - WFIFOW(fd,2) = 8; - WFIFOL(fd,4) = party_id; - WFIFOSET(fd,8); - ShowWarning("int_party: info not found %d\n", party_id); - - return 0; + WFIFOW(fd,2) = 12; + WFIFOL(fd,4) = char_id; + WFIFOL(fd,8) = party_id; + WFIFOSET(fd,12); + ShowWarning("int_party: info not found (party_id=%d char_id=%d)\n", party_id, char_id); } // パ?ティ情報まとめ送り -int mapif_party_info(int fd, struct party *p) { - unsigned char buf[2048]; - +static void mapif_party_info(int fd, struct party* p, int char_id) +{ + unsigned char buf[8 + sizeof(struct party)]; WBUFW(buf,0) = 0x3821; - memcpy(buf + 4, p, sizeof(struct party)); - WBUFW(buf,2) = 4 + sizeof(struct party); + WBUFW(buf,2) = 8 + sizeof(struct party); + WBUFL(buf,4) = char_id; + memcpy(WBUFP(buf,8), p, sizeof(struct party)); if (fd < 0) mapif_sendall(buf, WBUFW(buf,2)); else mapif_send(fd, buf, WBUFW(buf,2)); - return 0; } // パ?ティメンバ追加可否 @@ -472,25 +472,23 @@ int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct part int_party_calc_state(p); idb_put(party_db, p->party.party_id, p); + mapif_party_info(fd, &p->party, 0); mapif_party_created(fd, leader->account_id, leader->char_id, &p->party); - mapif_party_info(fd, &p->party); return 0; } // パ?ティ情報要求 -int mapif_parse_PartyInfo(int fd, int party_id) +static void mapif_parse_PartyInfo(int fd, int party_id, int char_id) { struct party_data *p; p = (struct party_data*)idb_get(party_db, party_id); if (p != NULL) - mapif_party_info(fd, &p->party); + mapif_party_info(fd, &p->party, char_id); else { - mapif_party_noinfo(fd, party_id); + mapif_party_noinfo(fd, party_id, char_id); } - - return 0; } // パーティ追加要求 @@ -524,8 +522,8 @@ int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member int_party_check_lv(p); } + mapif_party_info(-1, &p->party, 0); mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 0); - mapif_party_info(-1, &p->party); return 0; } @@ -574,7 +572,7 @@ int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) int_party_check_lv(p); } if (party_check_empty(&p->party) == 0) - mapif_party_info(-1, &p->party); + mapif_party_info(-1, &p->party, 0); return 0; } } @@ -679,7 +677,7 @@ int inter_party_parse_frommap(int fd) { RFIFOHEAD(fd); switch(RFIFOW(fd,0)) { case 0x3020: mapif_parse_CreateParty(fd, (char*)RFIFOP(fd,4), RFIFOB(fd,28), RFIFOB(fd,29), (struct party_member*)RFIFOP(fd,30)); break; - case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break; + case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2), RFIFOL(fd,6)); break; case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,4), (struct party_member*)RFIFOP(fd,8)); break; case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break; case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; diff --git a/src/char/inter.c b/src/char/inter.c index 8d9cac99f..298ed38a5 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -52,7 +52,7 @@ int inter_send_packet_length[]={ int inter_recv_packet_length[]={ -1,-1, 7,-1, -1,13,36, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3000-0x300f 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, //0x3010-0x301f - -1, 6,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f + -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, //0x3020-0x302f Party -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, //0x3030-0x303f 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3040-0x304f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3050-0x305f diff --git a/src/char_sql/char.c b/src/char_sql/char.c index 30800d95b..19ea01f66 100644 --- a/src/char_sql/char.c +++ b/src/char_sql/char.c @@ -1591,7 +1591,7 @@ int count_users(void) // 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 136 //Max size (for WFIFOHEAD calls) +#define MAX_CHAR_BUF 140 //Max size (for WFIFOHEAD calls) int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { unsigned short offset = 0; @@ -1626,7 +1626,7 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed; WBUFW(buf,52) = p->class_; WBUFW(buf,54) = p->hair; - WBUFW(buf,56) = p->option&0x7E80020 ? 0 : p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? + WBUFW(buf,56) = p->option&0x20 ? 0 : p->weapon; //When the weapon is sent and your option is riding, the client crashes on login!? WBUFW(buf,58) = p->base_level; WBUFW(buf,60) = min(p->skill_point, INT16_MAX); WBUFW(buf,62) = p->head_bottom; @@ -1659,6 +1659,10 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) WBUFL(buf,128) = p->robe; offset += 4; #endif +#if PACKETVER >= 20110928 + WBUFL(buf,132) = 0; // change slot feature (0 = disabled, otherwise enabled) + offset += 4; +#endif return 106+offset; } diff --git a/src/char_sql/int_party.c b/src/char_sql/int_party.c index 07dbcdb7d..d62e9f6a7 100644 --- a/src/char_sql/int_party.c +++ b/src/char_sql/int_party.c @@ -42,7 +42,10 @@ static int int_party_check_lv(struct party_data *p) { p->min_lv = UINT_MAX; p->max_lv = 0; for(i=0;i<MAX_PARTY;i++){ - if(!p->party.member[i].online) + /** + * - If not online OR if it's a family party and this is the child (doesn't affect exp range) + **/ + if(!p->party.member[i].online || p->party.member[i].char_id == p->family ) continue; lv=p->party.member[i].lv; @@ -75,7 +78,13 @@ static void int_party_calc_state(struct party_data *p) if(p->party.member[i].online) p->party.count++; } - if(p->size == 3) { + if( p->size == 2 && ( char_child(p->party.member[0].char_id,p->party.member[1].char_id) || char_child(p->party.member[1].char_id,p->party.member[2].char_id) ) ) { + //Child should be able to share with either of their parents [RoM] + if(p->party.member[0].class_&0x2000) //first slot is the child? + p->family = p->party.member[0].char_id; + else + p->family = p->party.member[1].char_id; + } else if( p->size == 3 ) { //Check Family State. p->family = char_family( p->party.member[0].char_id, @@ -357,29 +366,29 @@ int mapif_party_created(int fd,int account_id,int char_id,struct party *p) } // パーティ情報見つからず -int mapif_party_noinfo(int fd,int party_id) +static void mapif_party_noinfo(int fd, int party_id, int char_id) { - WFIFOHEAD(fd,8); - WFIFOW(fd,0)=0x3821; - WFIFOW(fd,2)=8; - WFIFOL(fd,4)=party_id; - WFIFOSET(fd,8); - ShowWarning("int_party: info not found %d\n",party_id); - return 0; + WFIFOHEAD(fd, 12); + WFIFOW(fd,0) = 0x3821; + WFIFOW(fd,2) = 12; + WFIFOL(fd,4) = char_id; + WFIFOL(fd,8) = party_id; + WFIFOSET(fd,12); + ShowWarning("int_party: info not found (party_id=%d char_id=%d)\n", party_id, char_id); } // パーティ情報まとめ送り -int mapif_party_info(int fd,struct party *p) +static void mapif_party_info(int fd, struct party* p, int char_id) { - unsigned char buf[5+sizeof(struct party)]; - WBUFW(buf,0)=0x3821; - WBUFW(buf,2)=4+sizeof(struct party); - memcpy(buf+4,p,sizeof(struct party)); + unsigned char buf[8 + sizeof(struct party)]; + WBUFW(buf,0) = 0x3821; + WBUFW(buf,2) = 8 + sizeof(struct party); + WBUFL(buf,4) = char_id; + memcpy(WBUFP(buf,8), p, sizeof(struct party)); if(fd<0) mapif_sendall(buf,WBUFW(buf,2)); else mapif_send(fd,buf,WBUFW(buf,2)); - return 0; } // パーティメンバ追加可否 int mapif_party_memberadded(int fd, int party_id, int account_id, int char_id, int flag) { @@ -506,8 +515,8 @@ int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct part //Add party to db int_party_calc_state(p); idb_put(party_db_, p->party.party_id, p); + mapif_party_info(fd, &p->party, 0); mapif_party_created(fd,leader->account_id,leader->char_id,&p->party); - mapif_party_info(fd,&p->party); } else { //Failed to create party. aFree(p); mapif_party_created(fd,leader->account_id,leader->char_id,NULL); @@ -516,16 +525,15 @@ int mapif_parse_CreateParty(int fd, char *name, int item, int item2, struct part return 0; } // パーティ情報要求 -int mapif_parse_PartyInfo(int fd,int party_id) +static void mapif_parse_PartyInfo(int fd, int party_id, int char_id) { struct party_data *p; p = inter_party_fromsql(party_id); if (p) - mapif_party_info(fd,&p->party); + mapif_party_info(fd, &p->party, char_id); else - mapif_party_noinfo(fd,party_id); - return 0; + mapif_party_noinfo(fd, party_id, char_id); } // パーティ追加要求 int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member) @@ -550,7 +558,7 @@ int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member p->party.member[i].leader = 0; if (p->party.member[i].online) p->party.count++; p->size++; - if (p->size == 3) //Check family state. + if (p->size == 2 || p->size == 3) // Check family state. And also accept either of their Parents. [RoM] int_party_calc_state(p); else //Check even share range. if (member->lv < p->min_lv || member->lv > p->max_lv || p->family) { @@ -558,8 +566,8 @@ int mapif_parse_PartyAddMember(int fd, int party_id, struct party_member *member int_party_check_lv(p); } + mapif_party_info(-1, &p->party, 0); mapif_party_memberadded(fd, party_id, member->account_id, member->char_id, 0); - mapif_party_info(-1, &p->party); inter_party_tosql(&p->party, PS_ADDMEMBER, i); return 0; @@ -633,7 +641,7 @@ int mapif_parse_PartyLeave(int fd, int party_id, int account_id, int char_id) } if (party_check_empty(p) == 0) - mapif_party_info(-1,&p->party); + mapif_party_info(-1, &p->party, 0); return 0; } // When member goes to other map or levels up. @@ -746,7 +754,7 @@ int inter_party_parse_frommap(int fd) RFIFOHEAD(fd); switch(RFIFOW(fd,0)) { case 0x3020: mapif_parse_CreateParty(fd, (char*)RFIFOP(fd,4), RFIFOB(fd,28), RFIFOB(fd,29), (struct party_member*)RFIFOP(fd,30)); break; - case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2)); break; + case 0x3021: mapif_parse_PartyInfo(fd, RFIFOL(fd,2), RFIFOL(fd,6)); break; case 0x3022: mapif_parse_PartyAddMember(fd, RFIFOL(fd,4), (struct party_member*)RFIFOP(fd,8)); break; case 0x3023: mapif_parse_PartyChangeOption(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOW(fd,10), RFIFOW(fd,12)); break; case 0x3024: mapif_parse_PartyLeave(fd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; diff --git a/src/char_sql/int_storage.c b/src/char_sql/int_storage.c index 0719685ae..e4d2395f1 100644 --- a/src/char_sql/int_storage.c +++ b/src/char_sql/int_storage.c @@ -17,7 +17,7 @@ #define STORAGE_MEMINC 16 -/// Save guild_storage data to sql +/// Save storage data to sql int storage_tosql(int account_id, struct storage_data* p) { memitemdata_to_sql(p->items, MAX_STORAGE, account_id, TABLE_STORAGE); @@ -25,7 +25,7 @@ int storage_tosql(int account_id, struct storage_data* p) } #ifndef TXT_SQL_CONVERT -/// Load guild_storage data to mem +/// Load storage data to mem int storage_fromsql(int account_id, struct storage_data* p) { StringBuf buf; diff --git a/src/char_sql/inter.c b/src/char_sql/inter.c index 97c48bdf1..50f05f6a3 100644 --- a/src/char_sql/inter.c +++ b/src/char_sql/inter.c @@ -47,7 +47,7 @@ char main_chat_nick[16] = "Main"; int inter_recv_packet_length[] = { -1,-1, 7,-1, -1,13,36, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3000- 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- - -1, 6,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- + -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- 5, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- -1,-1,10,10, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus] @@ -580,7 +580,6 @@ int mapif_parse_WisToGM(int fd) { unsigned char buf[2048]; // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B - ShowDebug("Sent packet back!\n"); memcpy(WBUFP(buf,0), RFIFOP(fd,0), RFIFOW(fd,2)); WBUFW(buf, 0) = 0x3803; mapif_sendall(buf, RFIFOW(fd,2)); diff --git a/src/common/core.c b/src/common/core.c index d528861c2..5b00f228b 100644 --- a/src/common/core.c +++ b/src/common/core.c @@ -180,26 +180,22 @@ const char* get_svn_revision(void) /*====================================== * CORE : Display title + * ASCII By CalciumKid 1/12/2011 *--------------------------------------*/ static void display_title(void) { //ClearScreen(); // clear screen and go up/left (0, 0 position in text) ShowMessage("\n"); - ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BT_YELLOW" eAthena Development Team presents "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" ______ __ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ _ \\/\\ \\__/\\ \\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" __\\ \\ \\_\\ \\ \\ ,_\\ \\ \\___ __ ___ __ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" /'__`\\ \\ __ \\ \\ \\/\\ \\ _ `\\ /'__`\\/' _ `\\ /'__`\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" /\\ __/\\ \\ \\/\\ \\ \\ \\_\\ \\ \\ \\ \\/\\ __//\\ \\/\\ \\/\\ \\_\\.\\_ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" \\ \\____\\\\ \\_\\ \\_\\ \\__\\\\ \\_\\ \\_\\ \\____\\ \\_\\ \\_\\ \\__/.\\_\\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" \\/____/ \\/_/\\/_/\\/__/ \\/_/\\/_/\\/____/\\/_/\\/_/\\/__/\\/_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" _ _ _ _ _ _ _ _ _ _ _ _ _ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ / \\ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" ( e | n | g | l | i | s | h ) ( A | t | h | e | n | a ) "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_XXBL" ("CL_BOLD" "CL_XXBL")"CL_CLL""CL_NORMAL"\n"); - ShowMessage(""CL_WTBL" (=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=)"CL_CLL""CL_NORMAL"\n\n"); + ShowMessage(""CL_PASS" "CL_BOLD" "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BT_WHITE" RAthena Development Team presents "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BOLD" ____ ___ __ __ "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BOLD" / __ \\/ | / /_/ /_ ___ ____ ____ _ "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BOLD" / /_/ / /| |/ __/ __ \\/ _ \\/ __ \\/ __ `/ "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BOLD" / _, _/ ___ / /_/ / / / __/ / / / /_/ / "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BOLD" /_/ |_/_/ |_\\__/_/ /_/\\___/_/ /_/\\___,_/ "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BOLD" "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_GREEN" http://rathena.org/board/ "CL_PASS""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_PASS" "CL_BOLD" "CL_PASS""CL_CLL""CL_NORMAL"\n"); ShowInfo("SVN Revision: '"CL_WHITE"%s"CL_RESET"'.\n", get_svn_revision()); } diff --git a/src/common/mmo.h b/src/common/mmo.h index 49a738b08..f8ef63390 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -41,10 +41,11 @@ // 20100803 - 2010-08-03aRagexeRE+ - 0x6b, 0x6d, 0x827, 0x828, 0x829, 0x82a, 0x82b, 0x82c, 0x842, 0x843 // 20101124 - 2010-11-24aRagexeRE+ - 0x856, 0x857, 0x858 // 20110111 - 2011-01-11aRagexeRE+ - 0x6b, 0x6d +// 20110928 - 2011-09-28aRagexeRE+ - 0x6b, 0x6d #ifndef PACKETVER - #define PACKETVER 20081126 - //#define PACKETVER 20100707 + #define PACKETVER 20110609 + //#define PACKETVER 20100730 #endif // backward compatible PACKETVER 8 and 9 #if PACKETVER == 8 @@ -175,6 +176,7 @@ enum item_types { IT_UNKNOWN2,//9 IT_AMMO, //10 IT_DELAYCONSUME,//11 + IT_THROWWEAPON= 17,//17 IT_CASH = 18, IT_MAX }; @@ -645,21 +647,27 @@ enum { JOB_STAR_GLADIATOR, JOB_STAR_GLADIATOR2, JOB_SOUL_LINKER, - + /** + * 3.1 (2.1) + **/ JOB_RUNE_KNIGHT = 4054, JOB_WARLOCK, JOB_RANGER, - JOB_ARCHBISHOP, + JOB_ARCH_BISHOP, JOB_MECHANIC, JOB_GUILLOTINE_CROSS, - - JOB_RUNE_KNIGHT_H, - JOB_WARLOCK_H, - JOB_RANGER_H, - JOB_ARCHBISHOP_H, - JOB_MECHANIC_H, - JOB_GUILLOTINE_CROSS_H, - + /** + * 3.1 (2.1 Trans) + **/ + JOB_RUNE_KNIGHT_T, + JOB_WARLOCK_T, + JOB_RANGER_T, + JOB_ARCH_BISHOP_T, + JOB_MECHANIC_T, + JOB_GUILLOTINE_CROSS_T, + /** + * 3.2 (2.2) + **/ JOB_ROYAL_GUARD, JOB_SORCERER, JOB_MINSTREL, @@ -667,42 +675,27 @@ enum { JOB_SURA, JOB_GENETIC, JOB_SHADOW_CHASER, - - JOB_ROYAL_GUARD_H, - JOB_SORCERER_H, - JOB_MINSTREL_H, - JOB_WANDERER_H, - JOB_SURA_H, - JOB_GENETIC_H, - JOB_SHADOW_CHASER_H, - + /** + * 3.2 (2.2 Trans) + **/ + JOB_ROYAL_GUARD_T, + JOB_SORCERER_T, + JOB_MINSTREL_T, + JOB_WANDERER_T, + JOB_SURA_T, + JOB_GENETIC_T, + JOB_SHADOW_CHASER_T, + /** + * 3.x Mounts / Vehicles + **/ JOB_RUNE_KNIGHT2, - JOB_RUNE_KNIGHT_H2, + JOB_RUNE_KNIGHT_T2, JOB_ROYAL_GUARD2, - JOB_ROYAL_GUARD_H2, + JOB_ROYAL_GUARD_T2, JOB_RANGER2, - JOB_RANGER_H2, + JOB_RANGER_T2, JOB_MECHANIC2, - JOB_MECHANIC_H2, - - JOB_BABY_RUNE = 4096, - JOB_BABY_WARLOCK, - JOB_BABY_RANGER, - JOB_BABY_BISHOP, - JOB_BABY_MECHANIC, - JOB_BABY_CROSS, - JOB_BABY_GUARD, - JOB_BABY_SORCERER, - JOB_BABY_MINSTREL, - JOB_BABY_WANDERER, - JOB_BABY_SURA, - JOB_BABY_GENETIC, - JOB_BABY_CHASER, - - JOB_BABY_RUNE2, - JOB_BABY_GUARD2, - JOB_BABY_RANGER2, - JOB_BABY_MECHANIC2, + JOB_MECHANIC_T2, JOB_MAX, }; diff --git a/src/common/showmsg.c b/src/common/showmsg.c index fc1badd26..a69c73ba2 100644 --- a/src/common/showmsg.c +++ b/src/common/showmsg.c @@ -4,6 +4,8 @@ #include "../common/cbasetypes.h" #include "../common/strlib.h" // StringBuf #include "showmsg.h" +#include "core.h" //[Ind] - For SERVER_TYPE +#include "version.h" //[Ind] - For SERVER_TYPE values #include <stdio.h> #include <string.h> @@ -51,6 +53,8 @@ int stdout_with_ansisequence = 0; int msg_silent = 0; //Specifies how silent the console is. +int console_msg_log = 0;//[Ind] msg error logging + /////////////////////////////////////////////////////////////////////////////// /// static/dynamic buffer for the messages @@ -684,6 +688,29 @@ int _vShowMessage(enum msg_type flag, const char *string, va_list ap) return 1; } if( + ( flag == MSG_WARNING && console_msg_log&1 ) || + ( ( flag == MSG_ERROR || flag == MSG_SQL ) && console_msg_log&2 ) || + ( flag == MSG_DEBUG && console_msg_log&4 ) ) {//[Ind] + FILE *log = NULL; + if( (log = fopen(SERVER_TYPE == ATHENA_SERVER_MAP ? "./log/map-msg_log.log" : "./log/unknown.log","a+")) ) { + char timestring[255]; + time_t curtime; + time(&curtime); + strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime)); + fprintf(log,"(%s) [ %s ] : ", + timestring, + flag == MSG_WARNING ? "Warning" : + flag == MSG_ERROR ? "Error" : + flag == MSG_SQL ? "SQL Error" : + flag == MSG_DEBUG ? "Debug" : + "Unknown"); + va_copy(apcopy, ap); + vfprintf(log,string,apcopy); + va_end(apcopy); + fclose(log); + } + } + if( (flag == MSG_INFORMATION && msg_silent&1) || (flag == MSG_STATUS && msg_silent&2) || (flag == MSG_NOTICE && msg_silent&4) || diff --git a/src/common/showmsg.h b/src/common/showmsg.h index 998296b66..5f80a4312 100644 --- a/src/common/showmsg.h +++ b/src/common/showmsg.h @@ -67,6 +67,7 @@ extern int stdout_with_ansisequence; //If the color ansi sequences are to be used. [flaviojs] extern int msg_silent; //Specifies how silent the console is. [Skotlex] +extern int console_msg_log; //Specifies what error messages to log. [Ind] extern char timestamp_format[20]; //For displaying Timestamps [Skotlex] enum msg_type { diff --git a/src/common/socket.c b/src/common/socket.c index b522ac607..4f7afd8fd 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -647,6 +647,14 @@ int WFIFOSET(int fd, size_t len) ShowFatalError("WFIFOSET: Packet 0x%x is too big. (len=%u, max=%u)\n", (*(uint16*)(s->wdata + s->wdata_size)), (unsigned int)len, 0xFFFF); exit(EXIT_FAILURE); } + else if( len == 0 ) + { + // abuses the fact, that the code that did WFIFOHEAD(fd,0), already wrote + // the packet type into memory, even if it could have overwritten vital data + // this can happen when a new packet was added on map-server, but packet len table was not updated + ShowWarning("WFIFOSET: Attempted to send zero-length packet, most likely 0x%04x (please report this).\n", WFIFOW(fd,0)); + return 0; + } if( !s->flag.server && len > socket_max_client_packet ) {// see declaration of socket_max_client_packet for details @@ -1222,6 +1230,7 @@ int socket_getips(uint32* ips, int max) void socket_init(void) { char *SOCKET_CONF_FILENAME = "conf/packet_athena.conf"; + unsigned int rlim_cur = FD_SETSIZE; #ifdef WIN32 {// Start up windows networking @@ -1265,6 +1274,7 @@ void socket_init(void) if( err == EPERM ) errmsg = "permission denied"; ShowWarning("socket_init: failed to set socket limit to %d, setting to maximum allowed (original limit=%d, current limit=%d, maximum allowed=%d, error=%s).\n", FD_SETSIZE, rlim_ori, (int)rlp.rlim_cur, (int)rlp.rlim_max, errmsg); + rlim_cur = rlp.rlim_cur; } } } @@ -1294,6 +1304,8 @@ void socket_init(void) add_timer_func_list(connect_check_clear, "connect_check_clear"); add_timer_interval(gettick()+1000, connect_check_clear, 0, 0, 5*60*1000); #endif + + ShowInfo("Server supports up to '"CL_WHITE"%u"CL_RESET"' concurrent connections.\n", rlim_cur); } diff --git a/src/common/sql.h b/src/common/sql.h index 5b318ab4d..898e2c778 100644 --- a/src/common/sql.h +++ b/src/common/sql.h @@ -14,7 +14,10 @@ #define SQL_SUCCESS 0 #define SQL_NO_DATA 100 - +// macro definition to determine whether the mySQL engine is running on InnoDB (rather than MyISAM) +// uncomment this line if the your mySQL tables have been changed to run on InnoDB +// this macro will adjust how logs are recorded in the database to accommodate the change +//#define SQL_INNODB /// Data type identifier. /// String, enum and blob data types need the buffer length specified. diff --git a/src/map/Makefile.in b/src/map/Makefile.in index 09e92dde2..492300c3d 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -34,7 +34,9 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \ storage.h skill.h atcommand.h battle.h battleground.h \ intif.h trade.h party.h vending.h guild.h pet.h \ log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h \ - buyingstore.h searchstore.h duel.h + buyingstore.h searchstore.h duel.h \ + config/Core.h config/Renewal.h config/Secure.h config/Data/Const.h \ + config/Skills/General.h config/Skills/Mage_Classes.h config/Skills/Swordsman_Classes.h HAVE_MYSQL=@HAVE_MYSQL@ ifeq ($(HAVE_MYSQL),yes) diff --git a/src/map/atcommand.c b/src/map/atcommand.c index b1a395afa..9290f844a 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -1255,7 +1255,7 @@ ACMD_FUNC(jobchange) if (!message || !*message || sscanf(message, "%d %d", &job, &upper) < 1) { int i, found = 0; - const struct { char name[22]; int id; } jobs[] = { + const struct { char name[24]; int id; } jobs[] = { { "novice", 0 }, { "swordsman", 1 }, { "mage", 2 }, @@ -1332,43 +1332,30 @@ ACMD_FUNC(jobchange) { "soul linker", 4049 }, { "rune knight", 4054 }, { "warlock", 4055 }, - { "ranger", 4056 }, + { "ranger", 4056 }, { "arch bishop", 4057 }, { "mechanic", 4058 }, - { "guillotine cross", 4059 }, - { "rune knight2", 4060 }, - { "warlock2", 4061 }, - { "ranger2", 4062 }, - { "arch bishop2", 4063 }, - { "mechanic2", 4064 }, - { "guillotine cross2", 4065 }, + { "guillotine", 4059 }, + { "rune knight (Trans)", 4060 }, + { "warlock (Trans)", 4061 }, + { "ranger (Trans)", 4062 }, + { "arch bishop (Trans)", 4063 }, + { "mechanic (Trans)", 4064 }, + { "guillotine (Trans)", 4065 }, { "royal guard", 4066 }, { "sorcerer", 4067 }, { "minstrel", 4068 }, { "wanderer", 4069 }, - { "sura", 4070 }, + { "sura", 4070 }, { "genetic", 4071 }, { "shadow chaser", 4072 }, - { "royal guard2", 4073 }, - { "sorcerer2", 4074 }, - { "minstrel2", 4075 }, - { "wanderer2", 4076 }, - { "sura2", 4077 }, - { "genetic2", 4078 }, - { "shadow chaser2", 4079 }, - { "baby rune knight", 4096 }, - { "baby warlock", 4097 }, - { "baby ranger", 4098 }, - { "baby arch bishop", 4099 }, - { "baby mechanic", 4100 }, - { "baby guillotine cross", 4101 }, - { "baby royal guard", 4102 }, - { "baby sorcerer", 4103 }, - { "baby minstrel", 4104 }, - { "baby wanderer", 4105 }, - { "baby sura", 4106 }, - { "baby genetic", 4107 }, - { "baby shadow chaser", 4108 }, + { "royal guard (Trans)", 4073 }, + { "sorcerer (Trans)", 4074 }, + { "minstrel (Trans)", 4075 }, + { "wanderer (Trans)", 4076 }, + { "sura (Trans)", 4077 }, + { "genetic (Trans)", 4078 }, + { "shadow chaser (Trans)", 4079 }, }; for (i=0; i < ARRAYLENGTH(jobs); i++) { @@ -1393,50 +1380,53 @@ ACMD_FUNC(jobchange) clif_displaymessage(fd, "----- High Novice / High 1st Class -----"); clif_displaymessage(fd, "4001 Novice High 4002 Swordman High 4003 Mage High 4004 Archer High"); clif_displaymessage(fd, "4005 Acolyte High 4006 Merchant High 4007 Thief High"); - clif_displaymessage(fd, "----- Transcendent Class -----"); + clif_displaymessage(fd, "----- Transcendent 2nd Class -----"); clif_displaymessage(fd, "4008 Lord Knight 4009 High Priest 4010 High Wizard 4011 Whitesmith"); clif_displaymessage(fd, "4012 Sniper 4013 Assassin Cross 4015 Paladin 4016 Champion"); clif_displaymessage(fd, "4017 Professor 4018 Stalker 4019 Creator 4020 Clown"); clif_displaymessage(fd, "4021 Gypsy"); - clif_displaymessage(fd, "----- 3rd Class (Regular 3rd) -----"); + clif_displaymessage(fd, "----- 3rd Class (Regular to 3rd) -----"); clif_displaymessage(fd, "4054 Rune Knight 4055 Warlock 4056 Ranger 4057 Arch Bishop"); clif_displaymessage(fd, "4058 Mechanic 4059 Guillotine Cross 4066 Royal Guard 4067 Sorcerer"); clif_displaymessage(fd, "4068 Minstrel 4069 Wanderer 4070 Sura 4071 Genetic"); clif_displaymessage(fd, "4072 Shadow Chaser"); - clif_displaymessage(fd, "----- 3rd Class (Trans 3rd) -----"); + clif_displaymessage(fd, "----- 3rd Class (Transcendent to 3rd) -----"); clif_displaymessage(fd, "4060 Rune Knight 4061 Warlock 4062 Ranger 4063 Arch Bishop"); clif_displaymessage(fd, "4064 Mechanic 4065 Guillotine Cross 4073 Royal Guard 4074 Sorcerer"); clif_displaymessage(fd, "4075 Minstrel 4076 Wanderer 4077 Sura 4078 Genetic"); clif_displaymessage(fd, "4079 Shadow Chaser"); clif_displaymessage(fd, "----- Expanded Class -----"); - clif_displaymessage(fd, " 23 Super Novice 24 Gunslinger 25 Ninja 4046 Taekwon"); - clif_displaymessage(fd, "4047 Star Gladiator 4049 Soul Linker"); - clif_displaymessage(fd, "---- Baby 1st / 2nd Class ----"); + clif_displaymessage(fd, " 23 Super Novice 24 Gunslinger 25 Ninja 26 Xmas"); + clif_displaymessage(fd, " 27 Summer 4046 Taekwon 4047 Star Gladiator 4049 Soul Linker"); + //clif_displaymessage(fd, "4050 Gangsi 4051 Death Knight 4052 Dark Collector"); + clif_displaymessage(fd, "---- 1st And 2nd Baby Class ----"); clif_displaymessage(fd, "4023 Baby Novice 4024 Baby Swordsman 4025 Baby Mage 4026 Baby Archer"); clif_displaymessage(fd, "4027 Baby Acolyte 4028 Baby Merchant 4029 Baby Thief 4030 Baby Knight"); clif_displaymessage(fd, "4031 Baby Priest 4032 Baby Wizard 4033 Baby Blacksmith 4034 Baby Hunter"); clif_displaymessage(fd, "4035 Baby Assassin 4037 Baby Crusader 4038 Baby Monk 4039 Baby Sage"); clif_displaymessage(fd, "4040 Baby Rogue 4041 Baby Alchemist 4042 Baby Bard 4043 Baby Dancer"); clif_displaymessage(fd, "4045 Super Baby"); - clif_displaymessage(fd, "---- Baby 3rd Class ----"); - clif_displaymessage(fd, "4096 Baby Rune Knight 4097 Baby Warlock 4098 Baby Ranger"); - clif_displaymessage(fd, "4099 Baby Arch Bishop 4100 Baby Mechanic 4101 Baby Guillotine Cross"); - clif_displaymessage(fd, "4102 Baby Royal Guard 4103 Baby Sorcerer 4104 Baby Minstrel"); - clif_displaymessage(fd, "4105 Baby Wanderer 4106 Baby Sura 4107 Baby Genetic"); - clif_displaymessage(fd, "4108 Baby Shadow Chaser"); - clif_displaymessage(fd, "[upper]: -1 (default) to automatically determine the 'level', 0 to force normal job, 1 to force high job."); + //clif_displaymessage(fd, "---- 3rd Baby Class ----"); + //clif_displaymessage(fd, "4096 Baby Rune Knight 4097 Baby Warlock 4098 Baby Ranger"); + //clif_displaymessage(fd, "4099 Baby Arch Bishop 4100 Baby Mechanic 4101 Baby Guillotine Cross"); + //clif_displaymessage(fd, "4102 Baby Royal Guard 4103 Baby Sorcerer 4104 Baby Minstrel"); + //clif_displaymessage(fd, "4105 Baby Wanderer 4106 Baby Sura 4107 Baby Genetic"); + //clif_displaymessage(fd, "4108 Baby Shadow Chaser"); + //clif_displaymessage(fd, "---- Mounts, Modes, And Others ----"); + //clif_displaymessage(fd, " 13 Knight (Peco) 21 Crusader (Peco) 22 Wedding 26 Christmas"); + //clif_displaymessage(fd, " 27 Summer 4014 Lord Knight (Peco) 4022 Paladin (Peco) 4036 Baby Knight (Peco)"); + //clif_displaymessage(fd, "4044 Baby Crusader (Peco) 4048 Star Gladiator (Union) 4080 Rune Knight (Dragon)"); + //clif_displaymessage(fd, "4081 Rune Knight Trans (Dragon) 4082 Royal Guard (Gryphon)"); + //clif_displaymessage(fd, "4083 Royal Guard Trans (Gryphon) 4084 Ranger (Warg) 4085 Ranger Trans (Warg)"); + //clif_displaymessage(fd, "4086 Mechanic (Mado) 4087 Mechanic Trans (Mado)"); return -1; } } - switch(job) - { - case 13: case 21: case 4014: case 4022: case 4036: - case 4044: case 4080: case 4081: case 4082: case 4083: - case 4084: case 4085: case 4086: case 4087: case 4109: - case 4110: case 4111: case 4112: - return 0; // Deny direct transformation into dummy jobs - } + if (job == 13 || job == 21 || job == 22 || job == 26 || job == 27 + || job == 4014 || job == 4022 || job == 4036 || job == 4044 || job == 4048 + ) // Deny direct transformation into dummy jobs + return 0; if (pcdb_checkid(job)) { @@ -1448,49 +1438,56 @@ ACMD_FUNC(jobchange) } } else { clif_displaymessage(fd, "Please, enter job ID (usage: @job/@jobchange <job name/ID>)."); - clif_displaymessage(fd, "----- Novice / 1st Class -----"); - clif_displaymessage(fd, " 0 Novice 1 Swordman 2 Mage 3 Archer"); - clif_displaymessage(fd, " 4 Acolyte 5 Merchant 6 Thief"); - clif_displaymessage(fd, "----- 2nd Class -----"); - clif_displaymessage(fd, " 7 Knight 8 Priest 9 Wizard 10 Blacksmith"); - clif_displaymessage(fd, " 11 Hunter 12 Assassin 14 Crusader 15 Monk"); - clif_displaymessage(fd, " 16 Sage 17 Rogue 18 Alchemist 19 Bard"); - clif_displaymessage(fd, " 20 Dancer"); - clif_displaymessage(fd, "----- High Novice / High 1st Class -----"); - clif_displaymessage(fd, "4001 Novice High 4002 Swordman High 4003 Mage High 4004 Archer High"); - clif_displaymessage(fd, "4005 Acolyte High 4006 Merchant High 4007 Thief High"); - clif_displaymessage(fd, "----- Transcendent Class -----"); - clif_displaymessage(fd, "4008 Lord Knight 4009 High Priest 4010 High Wizard 4011 Whitesmith"); - clif_displaymessage(fd, "4012 Sniper 4013 Assassin Cross 4015 Paladin 4016 Champion"); - clif_displaymessage(fd, "4017 Professor 4018 Stalker 4019 Creator 4020 Clown"); - clif_displaymessage(fd, "4021 Gypsy"); - clif_displaymessage(fd, "----- 3rd Class (Regular 3rd) -----"); - clif_displaymessage(fd, "4054 Rune Knight 4055 Warlock 4056 Ranger 4057 Arch Bishop"); - clif_displaymessage(fd, "4058 Mechanic 4059 Guillotine Cross 4066 Royal Guard 4067 Sorcerer"); - clif_displaymessage(fd, "4068 Minstrel 4069 Wanderer 4070 Sura 4071 Genetic"); - clif_displaymessage(fd, "4072 Shadow Chaser"); - clif_displaymessage(fd, "----- 3rd Class (Trans 3rd) -----"); - clif_displaymessage(fd, "4060 Rune Knight 4061 Warlock 4062 Ranger 4063 Arch Bishop"); - clif_displaymessage(fd, "4064 Mechanic 4065 Guillotine Cross 4073 Royal Guard 4074 Sorcerer"); - clif_displaymessage(fd, "4075 Minstrel 4076 Wanderer 4077 Sura 4078 Genetic"); - clif_displaymessage(fd, "4079 Shadow Chaser"); - clif_displaymessage(fd, "----- Expanded Class -----"); - clif_displaymessage(fd, " 23 Super Novice 24 Gunslinger 25 Ninja 4046 Taekwon"); - clif_displaymessage(fd, "4047 Star Gladiator 4049 Soul Linker"); - clif_displaymessage(fd, "---- Baby 1st / 2nd Class ----"); - clif_displaymessage(fd, "4023 Baby Novice 4024 Baby Swordsman 4025 Baby Mage 4026 Baby Archer"); - clif_displaymessage(fd, "4027 Baby Acolyte 4028 Baby Merchant 4029 Baby Thief 4030 Baby Knight"); - clif_displaymessage(fd, "4031 Baby Priest 4032 Baby Wizard 4033 Baby Blacksmith 4034 Baby Hunter"); - clif_displaymessage(fd, "4035 Baby Assassin 4037 Baby Crusader 4038 Baby Monk 4039 Baby Sage"); - clif_displaymessage(fd, "4040 Baby Rogue 4041 Baby Alchemist 4042 Baby Bard 4043 Baby Dancer"); - clif_displaymessage(fd, "4045 Super Baby"); - clif_displaymessage(fd, "---- Baby 3rd Class ----"); - clif_displaymessage(fd, "4096 Baby Rune Knight 4097 Baby Warlock 4098 Baby Ranger"); - clif_displaymessage(fd, "4099 Baby Arch Bishop 4100 Baby Mechanic 4101 Baby Guillotine Cross"); - clif_displaymessage(fd, "4102 Baby Royal Guard 4103 Baby Sorcerer 4104 Baby Minstrel"); - clif_displaymessage(fd, "4105 Baby Wanderer 4106 Baby Sura 4107 Baby Genetic"); - clif_displaymessage(fd, "4108 Baby Shadow Chaser"); - clif_displaymessage(fd, "[upper]: -1 (default) to automatically determine the 'level', 0 to force normal job, 1 to force high job."); + clif_displaymessage(fd, "----- Novice / 1st Class -----"); + clif_displaymessage(fd, " 0 Novice 1 Swordman 2 Mage 3 Archer"); + clif_displaymessage(fd, " 4 Acolyte 5 Merchant 6 Thief"); + clif_displaymessage(fd, "----- 2nd Class -----"); + clif_displaymessage(fd, " 7 Knight 8 Priest 9 Wizard 10 Blacksmith"); + clif_displaymessage(fd, " 11 Hunter 12 Assassin 14 Crusader 15 Monk"); + clif_displaymessage(fd, " 16 Sage 17 Rogue 18 Alchemist 19 Bard"); + clif_displaymessage(fd, " 20 Dancer"); + clif_displaymessage(fd, "----- High Novice / High 1st Class -----"); + clif_displaymessage(fd, "4001 Novice High 4002 Swordman High 4003 Mage High 4004 Archer High"); + clif_displaymessage(fd, "4005 Acolyte High 4006 Merchant High 4007 Thief High"); + clif_displaymessage(fd, "----- Transcendent 2nd Class -----"); + clif_displaymessage(fd, "4008 Lord Knight 4009 High Priest 4010 High Wizard 4011 Whitesmith"); + clif_displaymessage(fd, "4012 Sniper 4013 Assassin Cross 4015 Paladin 4016 Champion"); + clif_displaymessage(fd, "4017 Professor 4018 Stalker 4019 Creator 4020 Clown"); + clif_displaymessage(fd, "4021 Gypsy"); + clif_displaymessage(fd, "----- 3rd Class (Regular to 3rd) -----"); + clif_displaymessage(fd, "4054 Rune Knight 4055 Warlock 4056 Ranger 4057 Arch Bishop"); + clif_displaymessage(fd, "4058 Mechanic 4059 Guillotine Cross 4066 Royal Guard 4067 Sorcerer"); + clif_displaymessage(fd, "4068 Minstrel 4069 Wanderer 4070 Sura 4071 Genetic"); + clif_displaymessage(fd, "4072 Shadow Chaser"); + clif_displaymessage(fd, "----- 3rd Class (Transcendent to 3rd) -----"); + clif_displaymessage(fd, "4060 Rune Knight 4061 Warlock 4062 Ranger 4063 Arch Bishop"); + clif_displaymessage(fd, "4064 Mechanic 4065 Guillotine Cross 4073 Royal Guard 4074 Sorcerer"); + clif_displaymessage(fd, "4075 Minstrel 4076 Wanderer 4077 Sura 4078 Genetic"); + clif_displaymessage(fd, "4079 Shadow Chaser"); + clif_displaymessage(fd, "----- Expanded Class -----"); + clif_displaymessage(fd, " 23 Super Novice 24 Gunslinger 25 Ninja 26 Xmas"); + clif_displaymessage(fd, " 27 Summer 4046 Taekwon 4047 Star Gladiator 4049 Soul Linker"); + //clif_displaymessage(fd, "4050 Gangsi 4051 Death Knight 4052 Dark Collector"); + clif_displaymessage(fd, "---- 1st And 2nd Baby Class ----"); + clif_displaymessage(fd, "4023 Baby Novice 4024 Baby Swordsman 4025 Baby Mage 4026 Baby Archer"); + clif_displaymessage(fd, "4027 Baby Acolyte 4028 Baby Merchant 4029 Baby Thief 4030 Baby Knight"); + clif_displaymessage(fd, "4031 Baby Priest 4032 Baby Wizard 4033 Baby Blacksmith 4034 Baby Hunter"); + clif_displaymessage(fd, "4035 Baby Assassin 4037 Baby Crusader 4038 Baby Monk 4039 Baby Sage"); + clif_displaymessage(fd, "4040 Baby Rogue 4041 Baby Alchemist 4042 Baby Bard 4043 Baby Dancer"); + clif_displaymessage(fd, "4045 Super Baby"); + //clif_displaymessage(fd, "---- 3rd Baby Class ----"); + //clif_displaymessage(fd, "4096 Baby Rune Knight 4097 Baby Warlock 4098 Baby Ranger"); + //clif_displaymessage(fd, "4099 Baby Arch Bishop 4100 Baby Mechanic 4101 Baby Guillotine Cross"); + //clif_displaymessage(fd, "4102 Baby Royal Guard 4103 Baby Sorcerer 4104 Baby Minstrel"); + //clif_displaymessage(fd, "4105 Baby Wanderer 4106 Baby Sura 4107 Baby Genetic"); + //clif_displaymessage(fd, "4108 Baby Shadow Chaser"); + //clif_displaymessage(fd, "---- Mounts, Modes, And Others ----"); + //clif_displaymessage(fd, " 13 Knight (Peco) 21 Crusader (Peco) 22 Wedding 26 Christmas"); + //clif_displaymessage(fd, " 27 Summer 4014 Lord Knight (Peco) 4022 Paladin (Peco) 4036 Baby Knight (Peco)"); + //clif_displaymessage(fd, "4044 Baby Crusader (Peco) 4048 Star Gladiator (Union) 4080 Rune Knight (Dragon)"); + //clif_displaymessage(fd, "4081 Rune Knight Trans (Dragon) 4082 Royal Guard (Gryphon)"); + //clif_displaymessage(fd, "4083 Royal Guard Trans (Gryphon) 4084 Ranger (Warg) 4085 Ranger Trans (Warg)"); + //clif_displaymessage(fd, "4086 Mechanic (Mado) 4087 Mechanic Trans (Mado)"); return -1; } @@ -1710,8 +1707,7 @@ ACMD_FUNC(item) if(log_config.enable_logs&0x400) log_pick_pc(sd, "A", item_id, number, NULL); - if (!flag) - clif_displaymessage(fd, msg_txt(18)); // Item created. + clif_displaymessage(fd, msg_txt(18)); // Item created. return 0; } @@ -1786,8 +1782,7 @@ ACMD_FUNC(item2) if(log_config.enable_logs&0x400) log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp); - if (!flag) - clif_displaymessage(fd, msg_txt(18)); // Item created. + clif_displaymessage(fd, msg_txt(18)); // Item created. } else { clif_displaymessage(fd, msg_txt(19)); // Invalid item ID or name. return -1; @@ -2929,7 +2924,7 @@ ACMD_FUNC(displaystatus) if (i < 2) flag = 1; if (i < 3) tick = 0; - clif_status_change(&sd->bl, type, flag, tick); + clif_status_change(&sd->bl, type, flag, tick, 0, 0, 0); return 0; } @@ -3070,7 +3065,7 @@ ACMD_FUNC(zeny) *------------------------------------------*/ ACMD_FUNC(param) { - int i, value = 0, new_value; + int i, value = 0, new_value, max; const char* param[] = { "str", "agi", "vit", "int", "dex", "luk" }; short* status[6]; //we don't use direct initialization because it isn't part of the c standard. @@ -3099,16 +3094,16 @@ ACMD_FUNC(param) status[4] = &sd->status.dex; status[5] = &sd->status.luk; - if(value < 0 && *status[i] <= -value) - { - new_value = 1; - } - else if(SHRT_MAX - *status[i] < value) - { - new_value = SHRT_MAX; - } + if( battle_config.atcommand_max_stat_bypass ) + max = SHRT_MAX; else - { + max = pc_maxparameter(sd); + + if(value < 0 && *status[i] <= -value) { + new_value = 1; + } else if(max - *status[i] < value) { + new_value = max; + } else { new_value = *status[i] + value; } @@ -3150,7 +3145,10 @@ ACMD_FUNC(stat_all) value = pc_maxparameter(sd); max = pc_maxparameter(sd); } else { - max = SHRT_MAX; + if( battle_config.atcommand_max_stat_bypass ) + max = SHRT_MAX; + else + max = pc_maxparameter(sd); } count = 0; @@ -4218,6 +4216,7 @@ ACMD_FUNC(reloadmobdb) mob_reload(); read_petdb(); merc_reload(); + read_mercenarydb(); clif_displaymessage(fd, msg_txt(98)); // Monster database has been reloaded. return 0; @@ -4231,6 +4230,7 @@ ACMD_FUNC(reloadskilldb) nullpo_retr(-1, sd); skill_reload(); merc_skill_reload(); + read_mercenary_skilldb(); clif_displaymessage(fd, msg_txt(99)); // Skill database has been reloaded. return 0; @@ -4482,8 +4482,11 @@ ACMD_FUNC(mapinfo) strcat(atcmd_output, "Fireworks | "); if (map[m_id].flag.leaves) strcat(atcmd_output, "Leaves | "); - if (map[m_id].flag.rain) - strcat(atcmd_output, "Rain | "); + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //if (map[m_id].flag.rain) + // strcat(atcmd_output, "Rain | "); if (map[m_id].flag.nightenabled) strcat(atcmd_output, "Displays Night | "); clif_displaymessage(fd, atcmd_output); @@ -4597,7 +4600,26 @@ ACMD_FUNC(mapinfo) ACMD_FUNC(mount_peco) { nullpo_retr(-1, sd); - + if( pc_checkskill(sd,RK_DRAGONTRAINING) > 0 ) { + if( !(sd->sc.option&OPTION_DRAGON1) ) { + clif_displaymessage(sd->fd,"You have mounted your Dragon"); + pc_setoption(sd, sd->sc.option|OPTION_DRAGON1); + } else { + clif_displaymessage(sd->fd,"You have released your Dragon"); + pc_setoption(sd, sd->sc.option&~OPTION_DRAGON1); + } + return 0; + } + if( (sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC ) { + if( !(sd->sc.option&OPTION_MADOGEAR) ) { + clif_displaymessage(sd->fd,"You have mounted your Mado Gear"); + pc_setoption(sd, sd->sc.option|OPTION_MADOGEAR); + } else { + clif_displaymessage(sd->fd,"You have released your Mado Gear"); + pc_setoption(sd, sd->sc.option&~OPTION_MADOGEAR); + } + return 0; + } if (!pc_isriding(sd)) { // if actually no peco if (!pc_checkskill(sd, KN_RIDING)) { @@ -6204,24 +6226,26 @@ ACMD_FUNC(autolootitem) return 0; } - +/** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ /*========================================== * It is made to rain. *------------------------------------------*/ -ACMD_FUNC(rain) -{ - nullpo_retr(-1, sd); - if (map[sd->bl.m].flag.rain) { - map[sd->bl.m].flag.rain=0; - clif_weather(sd->bl.m); - clif_displaymessage(fd, "The rain has stopped."); - } else { - map[sd->bl.m].flag.rain=1; - clif_weather(sd->bl.m); - clif_displaymessage(fd, "It is made to rain."); - } - return 0; -} +//ACMD_FUNC(rain) +//{ +// nullpo_retr(-1, sd); +// if (map[sd->bl.m].flag.rain) { +// map[sd->bl.m].flag.rain=0; +// clif_weather(sd->bl.m); +// clif_displaymessage(fd, "The rain has stopped."); +// } else { +// map[sd->bl.m].flag.rain=1; +// clif_weather(sd->bl.m); +// clif_displaymessage(fd, "It is made to rain."); +// } +// return 0; +//} /*========================================== * It is made to snow. @@ -6360,7 +6384,10 @@ ACMD_FUNC(fireworks) ACMD_FUNC(clearweather) { nullpo_retr(-1, sd); - map[sd->bl.m].flag.rain=0; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //map[sd->bl.m].flag.rain=0; map[sd->bl.m].flag.snow=0; map[sd->bl.m].flag.sakura=0; map[sd->bl.m].flag.clouds=0; @@ -7850,13 +7877,72 @@ ACMD_FUNC(fakename) } /*========================================== - * @mapflag [flag name] [1|0|on|off] [map name] by Lupus - * => Shows information about the map flags [map name] - * Also set flags + * Ragnarok Resources *------------------------------------------*/ -ACMD_FUNC(mapflag) -{ -// WIP +ACMD_FUNC(mapflag) { +#define checkflag( cmd ) if ( map[ sd->bl.m ].flag.cmd ) clif_displaymessage(sd->fd,#cmd) +#define setflag( cmd ) \ + if ( strcmp( flag_name , #cmd ) == 0 && ( flag == 0 || flag == 1 ) ){\ + map[ sd->bl.m ].flag.cmd = flag;\ + sprintf(atcmd_output,"[ @mapflag ] %s flag has been set to %s",#cmd,flag?"On":"Off");\ + clif_displaymessage(sd->fd,atcmd_output);\ + return 0;\ + } + unsigned char flag_name[100]; + int flag=9,i; + nullpo_retr(-1, sd); + memset(flag_name, '\0', sizeof(flag_name)); + + if (!message || !*message || (sscanf(message, "%99s %d", flag_name, &flag) < 1)) { + clif_displaymessage(sd->fd,"Enabled Mapflags in this map:"); + clif_displaymessage(sd->fd,"----------------------------------"); + checkflag(autotrade); checkflag(allowks); checkflag(nomemo); checkflag(noteleport); + checkflag(noreturn); checkflag(monster_noteleport); checkflag(nosave); checkflag(nobranch); + checkflag(noexppenalty); checkflag(pvp); checkflag(pvp_noparty); checkflag(pvp_noguild); + checkflag(pvp_nightmaredrop); checkflag(pvp_nocalcrank); checkflag(gvg_castle); checkflag(gvg); + checkflag(gvg_dungeon); checkflag(gvg_noparty); checkflag(battleground);checkflag(nozenypenalty); + checkflag(notrade); checkflag(noskill); checkflag(nowarp); checkflag(nowarpto); + checkflag(noicewall); checkflag(snow); checkflag(clouds); checkflag(clouds2); + checkflag(fog); checkflag(fireworks); checkflag(sakura); checkflag(leaves); + checkflag(nogo); checkflag(nobaseexp); + checkflag(nojobexp); checkflag(nomobloot); checkflag(nomvploot); checkflag(nightenabled); + checkflag(restricted); checkflag(nodrop); checkflag(novending); checkflag(loadevent); + checkflag(nochat); checkflag(partylock); checkflag(guildlock); checkflag(src4instance); + clif_displaymessage(sd->fd," "); + clif_displaymessage(sd->fd,"Usage: \"@mapflag monster_teleport 1\" (0=Off 1=On)"); + clif_displaymessage(sd->fd,"Use: \"@mapflag available\" to list the available mapflags"); + return 1; + } + for (i = 0; flag_name[i]; i++) flag_name[i] = tolower(flag_name[i]); //lowercase + + setflag(autotrade); setflag(allowks); setflag(nomemo); setflag(noteleport); + setflag(noreturn); setflag(monster_noteleport);setflag(nosave); setflag(nobranch); + setflag(noexppenalty); setflag(pvp); setflag(pvp_noparty); setflag(pvp_noguild); + setflag(pvp_nightmaredrop); setflag(pvp_nocalcrank); setflag(gvg_castle); setflag(gvg); + setflag(gvg_dungeon); setflag(gvg_noparty); setflag(battleground); setflag(nozenypenalty); + setflag(notrade); setflag(noskill); setflag(nowarp); setflag(nowarpto); + setflag(noicewall); setflag(snow); setflag(clouds); setflag(clouds2); + setflag(fog); setflag(fireworks); setflag(sakura); setflag(leaves); + setflag(nogo); setflag(nobaseexp); + setflag(nojobexp); setflag(nomobloot); setflag(nomvploot); setflag(nightenabled); + setflag(restricted); setflag(nodrop); setflag(novending); setflag(loadevent); + setflag(nochat); setflag(partylock); setflag(guildlock); setflag(src4instance); + + clif_displaymessage(sd->fd,"Invalid flag name or flag"); + clif_displaymessage(sd->fd,"Usage: \"@mapflag monster_teleport 1\" (0=Off | 1=On)"); + clif_displaymessage(sd->fd,"Available Flags:"); + clif_displaymessage(sd->fd,"----------------------------------"); + clif_displaymessage(sd->fd,"town, autotrade, allowks, nomemo, noteleport, noreturn, monster_noteleport, nosave,"); + clif_displaymessage(sd->fd,"nobranch, noexppenalty, pvp, pvp_noparty, pvp_noguild, pvp_nightmaredrop,"); + clif_displaymessage(sd->fd,"pvp_nocalcrank, gvg_castle, gvg, gvg_dungeon, gvg_noparty, battleground,"); + clif_displaymessage(sd->fd,"nozenypenalty, notrade, noskill, nowarp, nowarpto, noicewall, snow, clouds, clouds2,"); + clif_displaymessage(sd->fd,"fog, fireworks, sakura, leaves, nogo, nobaseexp, nojobexp, nomobloot,"); + clif_displaymessage(sd->fd,"nomvploot, nightenabled, restricted, nodrop, novending, loadevent, nochat, partylock,"); + clif_displaymessage(sd->fd,"guildlock, src4instance"); + +#undef checkflag +#undef setflag + return 0; } @@ -8072,6 +8158,7 @@ ACMD_FUNC(reject) *-----------------------------------*/ ACMD_FUNC(cash) { + char output[128]; int value; nullpo_retr(-1, sd); @@ -8082,17 +8169,27 @@ ACMD_FUNC(cash) if( !strcmpi(command+1,"cash") ) { - if( value > 0 ) + if( value > 0 ) { pc_getcash(sd, value, 0); - else + sprintf(output, msg_txt(505), value, sd->cashPoints); + clif_disp_onlyself(sd, output, strlen(output)); + } else { pc_paycash(sd, -value, 0); + sprintf(output, msg_txt(410), value, sd->cashPoints); + clif_disp_onlyself(sd, output, strlen(output)); + } } else { // @points - if( value > 0 ) + if( value > 0 ) { pc_getcash(sd, 0, value); - else + sprintf(output, msg_txt(506), value, sd->kafraPoints); + clif_disp_onlyself(sd, output, strlen(output)); + } else { pc_paycash(sd, -value, -value); + sprintf(output, msg_txt(411), -value, sd->kafraPoints); + clif_disp_onlyself(sd, output, strlen(output)); + } } return 0; @@ -8668,8 +8765,17 @@ ACMD_FUNC(font) return 0; } - - +ACMD_FUNC(new_mount) { + clif_displaymessage(sd->fd,"NOTICE: If you crash with mount your LUA is outdated"); + if( !(sd->sc.option&OPTION_MOUNTING) ) { + clif_displaymessage(sd->fd,"You have mounted."); + pc_setoption(sd, sd->sc.option|OPTION_MOUNTING); + } else { + clif_displaymessage(sd->fd,"You have released your mount"); + pc_setoption(sd, sd->sc.option&~OPTION_MOUNTING); + } + return 0; +} /*========================================== * atcommand_info[] structure definition *------------------------------------------*/ @@ -8972,6 +9078,10 @@ AtCommandInfo atcommand_info[] = { { "delitem", 60,60, atcommand_delitem }, { "charcommands", 1,1, atcommand_commands }, { "font", 1,1, atcommand_font }, + /** + * For Testing Purposes, not going to be here after we're done. + **/ + { "newmount", 0,99, atcommand_new_mount }, }; @@ -9112,11 +9222,21 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message info = get_atcommandinfo_byname(command); if( info == NULL || info->func == NULL || ( type && ((*atcmd_msg == atcommand_symbol && pc_isGM(sd) < info->level) || (*atcmd_msg == charcommand_symbol && pc_isGM(sd) < info->level2)) ) ) { + if( pc_isGM(sd) ) { sprintf(output, msg_txt(153), command); // "%s is Unknown Command." clif_displaymessage(fd, output); return true; + } else + return false; } + //Log atcommands + if( log_config.gm && info->level >= log_config.gm && *atcmd_msg == atcommand_symbol ) + log_atcommand(sd, atcmd_msg); + //Log Charcommands + else if( log_config.gm && info->level2 >= log_config.gm && *atcmd_msg == charcommand_symbol && ssd != NULL ) + log_atcommand(sd, message); + //Attempt to use the command if( strcmpi("adjgmlvl",command+1) && ssd ) { lv = ssd->gmlevel; ssd->gmlevel = sd->gmlevel; } if ( (info->func(fd, (*atcmd_msg == atcommand_symbol) ? sd : ssd, command, params) != 0) ) @@ -9126,14 +9246,6 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message } if( strcmpi("adjgmlvl",command+1) && ssd ) ssd->gmlevel = lv; - //Log atcommands - if( log_config.gm && info->level >= log_config.gm && *atcmd_msg == atcommand_symbol ) - log_atcommand(sd, atcmd_msg); - - //Log Charcommands - if( log_config.gm && info->level2 >= log_config.gm && *atcmd_msg == charcommand_symbol && ssd != NULL ) - log_atcommand(sd, message); - return true; } diff --git a/src/map/battle.c b/src/map/battle.c index f338a50bb..8b0aa22fb 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -118,7 +118,7 @@ static int battle_getenemy_sub(struct block_list *bl, va_list ap) if (bl->id == target->id) return 0; - if (*c >= 24) + if (*c >= 23) return 0; if (status_isdead(bl)) return 0; @@ -136,8 +136,47 @@ struct block_list* battle_getenemy(struct block_list *target, int type, int rang int c = 0; memset(bl_list, 0, sizeof(bl_list)); map_foreachinrange(battle_getenemy_sub, target, range, type, bl_list, &c, target); - if (c == 0 || c > 24) + if (c == 0 ) return NULL; + if( c >= 24 ) + c = 23; + return bl_list[rand()%c]; +} +static int battle_getenemyarea_sub(struct block_list *bl, va_list ap) +{ + struct block_list **bl_list, *src; + int *c, ignore_id; + + bl_list = va_arg(ap, struct block_list **); + c = va_arg(ap, int *); + src = va_arg(ap, struct block_list *); + ignore_id = va_arg(ap, int); + + if( bl->id == src->id || bl->id == ignore_id ) + return 0; // Ignores Caster and a possible pre-target + if( *c >= 23 ) + return 0; + if( status_isdead(bl) ) + return 0; + if( battle_check_target(src, bl, BCT_ENEMY) > 0 ) + { // Is Enemy!... + bl_list[(*c)++] = bl; + return 1; + } + return 0; +} + +// Pick a random enemy +struct block_list* battle_getenemyarea(struct block_list *src, int x, int y, int range, int type, int ignore_id) +{ + struct block_list *bl_list[24]; + int c = 0; + memset(bl_list, 0, sizeof(bl_list)); + map_foreachinarea(battle_getenemyarea_sub, src->m, x - range, y - range, x + range, y + range, type, bl_list, &c, src, ignore_id); + if( c == 0 ) + return NULL; + if( c >= 24 ) + c = 23; return bl_list[rand()%c]; } @@ -212,7 +251,6 @@ int battle_delay_damage (unsigned int tick, int amotion, struct block_list *src, return 0; } - int battle_attr_ratio(int atk_elem,int def_type, int def_lv) { @@ -265,9 +303,6 @@ int battle_attr_fix(struct block_list *src, struct block_list *target, int damag if( tsc->data[SC_SPIDERWEB]->val2 == 0 ) status_change_end(target, SC_SPIDERWEB, INVALID_TIMER); } - if( atk_elem == ELE_HOLY && tsc && tsc->count && tsc->data[SC_ORATIO] ) - ratio += tsc->data[SC_ORATIO]->val2; - return damage*ratio/100; } @@ -319,6 +354,20 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag d->dmg_lv = ATK_BLOCK; return 0; } + if( sc->data[SC_WHITEIMPRISON] && skill_num != HW_GRAVITATION && skill_num != PA_PRESSURE ) { // Gravitation and Pressure do damage without removing the effect + if( skill_num == MG_NAPALMBEAT || + skill_num == MG_SOULSTRIKE || + skill_num == WL_SOULEXPANSION || + (skill_num && skill_get_ele(skill_num, skill_lv) == ELE_GHOST) || + (!skill_num && (status_get_status_data(src))->rhw.ele == ELE_GHOST) + ) + status_change_end(bl,SC_WHITEIMPRISON,-1); // Those skills do damage and removes effect + else + { + d->dmg_lv = ATK_BLOCK; + return 0; + } + } if( sc->data[SC_SAFETYWALL] && (flag&(BF_SHORT|BF_MAGIC))==BF_SHORT ) { @@ -337,7 +386,13 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag d->dmg_lv = ATK_BLOCK; return 0; } - + if( sc->data[SC_WEAPONBLOCKING] && flag&(BF_SHORT|BF_WEAPON) && rand()%100 < sc->data[SC_WEAPONBLOCKING]->val2 ) + { + clif_skill_nodamage(bl,src,GC_WEAPONBLOCKING,1,1); + d->dmg_lv = ATK_NONE; + sc_start2(bl,SC_COMBO,100,GC_WEAPONBLOCKING,src->id,2000); + return 0; + } if( (sce=sc->data[SC_AUTOGUARD]) && flag&BF_WEAPON && !(skill_get_nk(skill_num)&NK_NO_CARDFIX_ATK) && rand()%100 < sce->val2 ) { int delay; @@ -361,26 +416,7 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag clif_skill_nodamage(bl, bl, LK_PARRYING, sce->val1,1); return 0; } - - if((sce=sc->data[SC_MILLENNIUMSHIELD]) && damage > 0) { - if(sce->val2 > 0) - { - sce->val3 -= damage; - if( sce->val3 <= 0 ) - { // Reduce remaining shields and create new one. - sc_start(bl,SC_STUN,15,0,1000); - sce->val3 = 1000; - sce->val2--; - if( sd ) - clif_millenniumshield(sd,sce->val2); - } - - damage = 0; // Nullify damage even if shield is destroyed. - } - if(sce->val2 <= 0) - status_change_end(bl, SC_MILLENNIUMSHIELD, INVALID_TIMER); - } - + if(sc->data[SC_DODGE] && !sc->opt1 && (flag&BF_LONG || sc->data[SC_SPURT]) && rand()%100 < 20) { @@ -434,7 +470,7 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag } //Finally damage reductions.... - if( sc->data[SC_ASSUMPTIO] && skill_num != RK_DRAGONBREATH ) + if( sc->data[SC_ASSUMPTIO] ) { if( map_flag_vs(bl->m) ) damage = damage*2/3; //Receive 66% damage @@ -447,11 +483,10 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag damage=damage*(100-sc->data[SC_DEFENDER]->val2)/100; if(sc->data[SC_ADJUSTMENT] && - (flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON) && - skill_num != RK_DRAGONBREATH) + (flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON)) damage -= 20*damage/100; - if(sc->data[SC_FOGWALL] && skill_num != RK_DRAGONBREATH) { + if(sc->data[SC_FOGWALL]) { if(flag&BF_SKILL) //25% reduction damage -= 25*damage/100; else if ((flag&(BF_LONG|BF_WEAPON)) == (BF_LONG|BF_WEAPON)) @@ -510,10 +545,18 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag status_change_end(bl, SC_REJECTSWORD, INVALID_TIMER); } + //Finally added to remove the status of immobile when aimedbolt is used. [Jobbie] + if( skill_num == RA_AIMEDBOLT && (sc->data[SC_BITE] || sc->data[SC_ANKLE] || sc->data[SC_ELECTRICSHOCKER]) ) + { + status_change_end(bl, SC_BITE, -1); + status_change_end(bl, SC_ANKLE, -1); + status_change_end(bl, SC_ELECTRICSHOCKER, -1); + } + //Finally Kyrie because it may, or not, reduce damage to 0. if((sce = sc->data[SC_KYRIE]) && damage > 0){ sce->val2-=damage; - if(flag&BF_WEAPON || skill_num == TF_THROWSTONE || skill_num == RK_DRAGONBREATH){ + if(flag&BF_WEAPON || skill_num == TF_THROWSTONE){ if(sce->val2>=0) damage=0; else @@ -523,13 +566,6 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag status_change_end(bl, SC_KYRIE, INVALID_TIMER); } - if((sce = sc->data[SC_STONEHARDSKIN]) && damage > 0) - { - sce->val2-=damage; // Reduce Stone Skin's HP by damage taken. - if( sce->val2 <= 0 ) - status_change_end(bl, SC_STONEHARDSKIN, INVALID_TIMER); - } - if (!damage) return 0; //Probably not the most correct place, but it'll do here @@ -569,9 +605,11 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag break; } } + if( sc->data[SC_POISONINGWEAPON] && skill_num != GC_VENOMPRESSURE && (flag&BF_WEAPON) && damage > 0 && rand()%100 < sc->data[SC_POISONINGWEAPON]->val3 ) + sc_start(bl,sc->data[SC_POISONINGWEAPON]->val2,100,sc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON,sc->data[SC_POISONINGWEAPON]->val1)); } - if (battle_config.pk_mode && sd && bl->type == BL_PC && damage) + if (battle_config.pk_mode && sd && bl->type == BL_PC && damage && !map_flag_gvg(sd->bl.m)) { if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex] if (flag&BF_WEAPON) @@ -628,7 +666,6 @@ int battle_calc_bg_damage(struct block_list *src, struct block_list *bl, int dam case PA_PRESSURE: case HW_GRAVITATION: case NJ_ZENYNAGE: - case RK_DRAGONBREATH: break; default: if( flag&BF_SKILL ) @@ -690,9 +727,13 @@ int battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int dama case PA_PRESSURE: case HW_GRAVITATION: case NJ_ZENYNAGE: - case RK_DRAGONBREATH: break; default: + /* Uncomment if you want god-mode Emperiums at 100 defense. [Kisuka] + if (md && md->guardian_data) { + damage -= damage * (md->guardian_data->castle->defense/100) * battle_config.castle_defense_rate/100; + } + */ if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex] if (flag&BF_WEAPON) damage = damage * battle_config.gvg_weapon_damage_rate/100; @@ -747,6 +788,12 @@ int battle_addmastery(struct map_session_data *sd,struct block_list *target,int (battle_check_undead(status->race,status->def_ele) || status->race==RC_DEMON) ) damage += (skill*(int)(3+(sd->status.base_level+1)*0.05)); // submitted by orn //damage += (skill * 3); + if( (skill = pc_checkskill(sd, RA_RANGERMAIN)) > 0 && (status->race == RC_BRUTE || status->race == RC_PLANT || status->race == RC_FISH) ) + damage += (skill * 5); + if( (skill = pc_checkskill(sd,NC_RESEARCHFE)) > 0 && (status->def_ele == ELE_FIRE || status->def_ele == ELE_EARTH) ) + damage += (skill * 10); + if( (sd->sc.option&OPTION_MADOGEAR) ) + damage += 20 + 20 * pc_checkskill(sd, NC_MADOLICENCE); if((skill = pc_checkskill(sd,HT_BEASTBANE)) > 0 && (status->race==RC_BRUTE || status->race==RC_INSECT) ) { damage += (skill * 4); @@ -772,24 +819,25 @@ int battle_addmastery(struct map_session_data *sd,struct block_list *target,int case W_1HSPEAR: case W_2HSPEAR: if((skill = pc_checkskill(sd,KN_SPEARMASTERY)) > 0) { - if(!pc_isriding(sd) && !pc_isdragon(sd)) + if(!pc_isriding(sd)) damage += (skill * 4); else damage += (skill * 5); - // increase damage by level of KN_SPEARMASTERY * 10 - if (pc_checkskill(sd,RK_DRAGONTRAINING) > 0) - damage += (skill * 10); } break; case W_1HAXE: case W_2HAXE: if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) damage += (skill * 3); + if((skill = pc_checkskill(sd,NC_TRAININGAXE)) > 0) + damage += (skill * 5); break; case W_MACE: case W_2HMACE: if((skill = pc_checkskill(sd,PR_MACEMASTERY)) > 0) damage += (skill * 3); + if((skill = pc_checkskill(sd,NC_TRAININGAXE)) > 0) + damage += (skill * 5); break; case W_FIST: if((skill = pc_checkskill(sd,TK_RUN)) > 0) @@ -1022,7 +1070,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo wd.type=0; //Normal attack wd.div_=skill_num?skill_get_num(skill_num,skill_lv):1; wd.amotion=(skill_num && skill_get_inf(skill_num)&INF_GROUND_SKILL)?0:sstatus->amotion; //Amotion should be 0 for ground skills. - if(skill_num == KN_AUTOCOUNTER || skill_num == RK_DEATHBOUND) + if(skill_num == KN_AUTOCOUNTER) wd.amotion >>= 1; wd.dmotion=tstatus->dmotion; wd.blewcount=skill_get_blewcount(skill_num,skill_lv); @@ -1095,7 +1143,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo break; case KN_AUTOCOUNTER: - case RK_DEATHBOUND: wd.flag=(wd.flag&~BF_SKILLMASK)|BF_NORMAL; break; @@ -1222,7 +1269,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if (flag.cri) { wd.type = 0x0a; - flag.hit = 1; + flag.idef = flag.idef2 = flag.hit = 1; } else { //Check for Perfect Hit if(sd && sd->perfect_hit > 0 && rand()%100 < sd->perfect_hit) flag.hit = 1; @@ -1309,6 +1356,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if(sd && pc_checkskill(sd,AS_SONICACCEL)>0) hitrate += hitrate * 50 / 100; break; + case GC_VENOMPRESSURE: + hitrate += 10 + 4 * skill_lv; + break; } // Weaponry Research hidden bonus @@ -1436,15 +1486,6 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if( (i = party_foreachsamemap(party_sub_count, sd, 0)) > 1 ) // exclude the player himself [Inkfish] ATK_ADDRATE(2*skill*i); } - if(sd->status.party_id && sc && sc->data[SC_FIGHTINGSPIRIT]) - { - i = party_foreachsamemap(party_sub_count, sd, 0); - if( (sc->data[SC_FIGHTINGSPIRIT]->val2) > 0){ - ATK_ADDRATE(7*i); //Caster gets full effect. - }else{ - ATK_ADDRATE(7*i/4); //Party members get 1/4. - } - } } break; } //End default case @@ -1767,65 +1808,160 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo case NPC_VAMPIRE_GIFT: skillratio += ((skill_lv-1)%5+1)*100; break; - case RK_SONICWAVE: - skillratio += ((skill_lv + 5) * 100) * (1 + (status_get_lv(src) -100) / 200); + case RK_SONICWAVE: { + int level = status_get_lv(src); + skillratio += 400 + 100 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 100) / 200; + } break; - case RK_HUNDREDSPEAR: - { - int weight = 1, dmg = 0; - if (sd) { - short index = sd->equip_index[EQI_HAND_R]; - - if (index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON) - weight = sd->inventory_data[index]->weight; //80% of weight - } - - dmg = (600 + (skill_lv * 80) + (1000 - (weight>1000?1000:weight)) * ((1 + status_get_lv(src) - 100) / 200)); - - if(sd) // Add clashing spiral bonus damage (Skill level * 50% damage) - dmg += pc_checkskill(sd,LK_SPIRALPIERCE) * (dmg * 50 /100); - - skillratio = dmg; - break; + case RK_HUNDREDSPEAR: { + int level = status_get_lv(src); + skillratio += 500 + 40 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 100) / 200; } - case RK_WINDCUTTER: - skillratio += ((skill_lv + 2) * 50) * status_get_lv(src) / 100; break; - case RK_IGNITIONBREAK: - { - int dmg = 300; // Base maximum damage at less than 3 cells. + case RK_WINDCUTTER: { + int level = status_get_lv(src); + skillratio += 50 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 50) / 200; + } + break; + case RK_IGNITIONBREAK: { + int level = status_get_lv(src); i = distance_bl(src,target); - if( i > 7 ) - dmg -= 100; // Greather than 7 cells. (200 damage) - else if( i > 3 ) - dmg -= 50; // Greater than 3 cells, less than 7. (250 damage) - - dmg = (dmg * skill_lv) * (1+ (status_get_lv(src) - 100) / 120); - - // Elemental check, +100% damage if your element is fire. - if( sstatus->rhw.ele == ELE_FIRE ) - dmg += skill_lv * 100 / 100; - - skillratio = dmg; - break; + if( i < 2 ) + skillratio = 200 + 200 * skill_lv; + else if( i < 4 ) + skillratio = 100 + 200 * skill_lv; + else + skillratio = 100 + 100 * skill_lv; + if( level > 100 ) + skillratio += skillratio * (level - 100) / 200; + if( sstatus->rhw.ele == ELE_FIRE ) + skillratio += skillratio / 2; } + break; case RK_CRUSHSTRIKE: - if(sd) - { - short index = sd->equip_index[EQI_HAND_R]; - if( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON ) - skillratio = (sd->inventory_data[index]->wlv * (sd->status.inventory[index].refine + 6) * 100) + sd->inventory_data[index]->atk + sd->inventory_data[index]->weight; - } - break; + if( sd ) + { + short index = sd->equip_index[EQI_HAND_R]; + if( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON ) + skillratio = sstatus->rhw.atk + 100 * sd->inventory_data[index]->wlv * (sd->status.inventory[index].refine + 6); + } + break; case RK_STORMBLAST: - skillratio = ((sd?pc_checkskill(sd,RK_RUNEMASTERY):1) + (sstatus->int_ / 8)) * 100; + skillratio = 100 * (sd ? pc_checkskill(sd,RK_RUNEMASTERY) : 1) + 100 * (sstatus->int_ / 4); break; case RK_PHANTOMTHRUST: - skillratio = ((skill_lv * 50) + (sd?pc_checkskill(sd,KN_SPEARMASTERY):0) * 10) * status_get_lv(src) / 150; + skillratio = 50 * skill_lv + 10 * ( sd ? pc_checkskill(sd,KN_SPEARMASTERY) : 10); + //if( s_level > 100 ) skillratio += skillratio * s_level / 150; // Base level bonus. This is official, but is disabled until I can confirm something with was changed or not. [Rytech] + //if( s_level > 100 ) skillratio += skillratio * (s_level - 100) / 200; // Base level bonus. + break; + /** + * GC Guilotine Cross + **/ + case GC_CROSSIMPACT: + skillratio += 1050 + 50 * skill_lv; break; + case GC_PHANTOMMENACE: + skillratio += 200; + break; + case GC_COUNTERSLASH: + skillratio += 200 + (100 * skill_lv) + sstatus->agi; + break; + case GC_ROLLINGCUTTER: + skillratio += 20 * skill_lv; + break; + case GC_CROSSRIPPERSLASHER: + skillratio += 60 + 40 * skill_lv; + if( sc && sc->data[SC_ROLLINGCUTTER] ) + skillratio += 25 * sc->data[SC_ROLLINGCUTTER]->val1; + break; + /** + * Arch Bishop + **/ case AB_DUPLELIGHT_MELEE: skillratio += 10 * skill_lv; break; + /** + * Ranger + **/ + case RA_ARROWSTORM: + skillratio += 100 + 50 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case RA_AIMEDBOLT: + skillratio += 400 + 50 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( tsc && (tsc->data[SC_BITE] || tsc->data[SC_ANKLE] || tsc->data[SC_ELECTRICSHOCKER]) ) + wd.div_ = tstatus->size + 2 + rand()%2; + break; + case RA_CLUSTERBOMB: + skillratio += 100 + 100 * skill_lv; + break; + case RA_WUGDASH: + skillratio = 500; + break; + case RA_WUGSTRIKE: + skillratio = 200 * skill_lv; + break; + case RA_WUGBITE: + skillratio += 300 + 200 * skill_lv; + if ( skill_lv == 5 ) skillratio += 100; + break; + case RA_SENSITIVEKEEN: + skillratio += 50 * skill_lv; + break; + /** + * Mechanic + **/ + case NC_BOOSTKNUCKLE: + skillratio += 100 + 100 * skill_lv + sstatus->dex; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_PILEBUNKER: + skillratio += 200 + 100 * skill_lv + sstatus->str; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_VULCANARM: + skillratio = 70 * skill_lv + sstatus->dex; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_FLAMELAUNCHER: + case NC_COLDSLOWER: + skillratio += 200 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_ARMSCANNON: + switch( tstatus->size ) { + case 0: skillratio += 100 + 500 * skill_lv; break;// Small + case 1: skillratio += 100 + 400 * skill_lv; break;// Medium + case 2: skillratio += 100 + 300 * skill_lv; break;// Large + } + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + //NOTE: Their's some other factors that affects damage, but not sure how exactly. Will recheck one day. [Rytech] + break; + case NC_AXEBOOMERANG: + skillratio += 60 + 40 * skill_lv; + if( sd ) { + short index = sd->equip_index[EQI_HAND_R]; + if( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON ) + skillratio += sd->inventory_data[index]->weight / 10;// Weight is divided by 10 since 10 weight in coding make 1 whole actural weight. [Rytech] + } + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_POWERSWING: + skillratio += 80 + 20 * skill_lv + sstatus->str + sstatus->dex; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case NC_AXETORNADO: + skillratio += 100 + 100 * skill_lv + sstatus->vit; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + } ATK_RATE(skillratio); @@ -1854,6 +1990,16 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo case NJ_SYURIKEN: ATK_ADD(4*skill_lv); break; + /** + * Ranger + **/ + case RA_WUGDASH: + case RA_WUGSTRIKE: + case RA_WUGBITE: + if(sd) + ATK_ADD(30*pc_checkskill(sd, RA_TOOTHOFWUG)); + break; + } } //Div fix. @@ -1863,14 +2009,17 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if (sc) { if(sc->data[SC_TRUESIGHT]) ATK_ADDRATE(2*sc->data[SC_TRUESIGHT]->val1); - + #if RE_EDP == 0 + /** + * In RE EDP doesn't affect your final damage but your atk and weapon atk + **/ if(sc->data[SC_EDP] && skill_num != ASC_BREAKER && skill_num != ASC_METEORASSAULT && skill_num != AS_SPLASHER && - skill_num != AS_VENOMKNIFE && - skill_num != AS_GRIMTOOTH) // RE disabled Grimtooth carrying EDP. + skill_num != AS_VENOMKNIFE) ATK_ADDRATE(sc->data[SC_EDP]->val3); + #endif } switch (skill_num) { @@ -1887,6 +2036,10 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo sc->data[SC_SPIRIT]->val2 == SL_CRUSADER) ATK_ADDRATE(100); break; + case NC_AXETORNADO: + if( (sstatus->rhw.ele) == ELE_WIND || (sstatus->lhw.ele) == ELE_WIND ) + ATK_ADDRATE(50); + break; } if( sd ) @@ -1939,15 +2092,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if (!flag.idef || !flag.idef2) { //Defense reduction short vit_def; - signed short def1 = status_get_def(target); //Don't use tstatus->def1 due to skill timer reductions. + signed char def1 = status_get_def(target); //Don't use tstatus->def1 due to skill timer reductions. short def2 = (short)tstatus->def2; - if( sc && sc->data[SC_EXPIATIO] ) - { - def1 -= def1 * sc->data[SC_EXPIATIO]->val2 / 100; - def2 -= def2 * sc->data[SC_EXPIATIO]->val2 / 100; - } - if( sd ) { i = sd->ignore_def[is_boss(target)?RC_BOSS:RC_NONBOSS]; @@ -1987,6 +2134,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo if((battle_check_undead(sstatus->race,sstatus->def_ele) || sstatus->race==RC_DEMON) && //This bonus already doesnt work vs players src->type == BL_MOB && (skill=pc_checkskill(tsd,AL_DP)) > 0) vit_def += skill*(int)(3 +(tsd->status.base_level+1)*0.04); // submitted by orn + if( src->type == BL_MOB && (skill=pc_checkskill(tsd,RA_RANGERMAIN))>0 && + (sstatus->race == RC_BRUTE || sstatus->race == RC_FISH || sstatus->race == RC_PLANT) ) + vit_def += skill*5; } else { //Mob-Pet vit-eq //VIT + rnd(0,[VIT/20]^2-1) vit_def = (def2/20)*(def2/20); @@ -2399,11 +2549,10 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo struct Damage md = battle_calc_misc_attack(src, target, skill_num, skill_lv, wflag); wd.damage += md.damage; } - - if ( sc ) - { // I don't see the point in repeating the SC check now that there are more things that use it. [L0ne_W0lf] + if( sc ) { + //SG_FUSION hp penalty [Komurka] if (sc->data[SC_FUSION]) - { //SG_FUSION hp penalty [Komurka] + { int hp= sstatus->max_hp; if (sd && tsd) { hp = 8*hp/100; @@ -2413,38 +2562,21 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo hp = 2*hp/100; //2% hp loss per hit status_zap(src, hp, 0); } - - if(sc->data[SC_ENCHANTBLADE] && !skill_num && wd.flag&BF_SHORT ) - { - if (tsc && tsc->data[SC_SAFETYWALL]) - ; // Although this is suposed to be considered a magic atttack, Safety Wall still blocks it? May be impemented wrong. - else - { - struct Damage ebd = battle_calc_attack(BF_MAGIC,src,target,RK_ENCHANTBLADE,sc->data[SC_ENCHANTBLADE]->val1,wd.flag); - wd.damage += (sc->data[SC_ENCHANTBLADE]->val1 * 20 + 100) * (status_get_lv(src) / 150) + sstatus->int_ + ebd.damage; - } - } - - if(sc->data[SC_GIANTGROWTH] && !skill_num ) - { - int rate = battle_config.equip_natural_break_rate; - rate += 10; - skill_break_equip(src, EQP_WEAPON, rate, BCT_SELF); - if( rand() % 100 <= 10 ) - ATK_RATE(300); - } - - if(sc->data[SC_STONEHARDSKIN] && !skill_num) - { // SC_STRIPWEAPON will reduce damage by 25% so piggyback off that since there is no offensive status for this. - int rate = battle_config.equip_natural_break_rate; - rate += 300; //chance to break gear, or reduce attack by 25% in hte case of monsters. - if( sd ) - skill_break_equip(src,EQP_WEAPON,rate,BCT_ENEMY); - if (!sd && !(status_get_mode(src)&MD_BOSS)) - status_change_start(src,SC_STRIPWEAPON,rate,0,0,0,0,10000,0); + /** + * affecting non-skills + **/ + if( !skill_num ) { + /** + * RK Enchant Blade + **/ + if( sc->data[SC_ENCHANTBLADE] && sd && ( (flag.rh && sd->weapontype1) || (flag.lh && sd->weapontype2) ) ) { + struct Damage md = battle_calc_magic_attack(src, target, RK_ENCHANTBLADE, pc_checkskill(sd,RK_ENCHANTBLADE), wflag); + wd.damage += md.damage; + wd.flag |= md.flag; } - } + } + } return wd; } @@ -2536,6 +2668,9 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case AL_HEAL: case PR_BENEDICTIO: case PR_SANCTUARY: + /** + * Arch Bishop + **/ case AB_HIGHNESSHEAL: ad.damage = skill_calc_heal(src, target, skill_num, skill_lv, false); break; @@ -2556,17 +2691,31 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case PF_SOULBURN: ad.damage = tstatus->sp * 2; break; + /** + * Arch Bishop + **/ case AB_RENOVATIO: + //Damage calculation from iRO wiki. [Jobbie] ad.damage = (int)((15 * status_get_lv(src)) + (1.5 * sstatus->int_)); break; default: { + #if RRMODE //Renewal MATK Appliance according to doddler (?title=Renewal_Changes#Upgrade_MATK) + /** + * min: (weaponMATK+upgradeMATK) * 2 + 1.5 * statusMATK + * max: [weaponMATK+upgradeMATK+(wMatk*wLvl)/10] * 2 + 1.5 * statusMATK + * yes this formula MATCHES their site: matk_max already holds weaponmatk+upgradematk, and + * -> statusMATK holds the %Matk modifier stuff from earlier and lastly: + * -> the mdef part is not applied at this point, but later. + **/ //1:bugreport:5101 //1:bugreport:5101 + MATK_ADD((1+sstatus->matk_max) * 2 + 15/10 * sstatus->matk_min + rand()% ( sstatus->matk_max + (1 + (sstatus->matk_max*sstatus->wlv) / 10 * 2 * 10/15 * sstatus->matk_min ) )); + #else //Ancient MATK Appliance if (sstatus->matk_max > sstatus->matk_min) { MATK_ADD(sstatus->matk_min+rand()%(1+sstatus->matk_max-sstatus->matk_min)); } else { MATK_ADD(sstatus->matk_min); } - + #endif if(nk&NK_SPLASHSPLIT){ // Divide MATK in case of multiple targets skill if(mflag>0) ad.damage/= mflag; @@ -2612,6 +2761,11 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case WZ_SIGHTRASHER: skillratio += 20*skill_lv; break; +#if FIREIVY_ON + case WZ_FIREIVY: + skillratio += 20*skill_lv-15; + break; +#endif case WZ_VERMILION: skillratio += 20*skill_lv-20; break; @@ -2655,15 +2809,193 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list case NPC_EARTHQUAKE: skillratio += 100 +100*skill_lv +100*(skill_lv/2); break; + /** + * Arch Bishop + **/ case AB_JUDEX: - skillratio += ((skill_lv * 20) + 300) * status_get_lv(src) / 100; + skillratio += 180 + 20 * skill_lv; + if (skill_lv > 4) skillratio += 20; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. break; case AB_ADORAMUS: - skillratio += ((skill_lv * 100) + 500) * status_get_lv(src) / 100; + skillratio += 400 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. break; case AB_DUPLELIGHT_MAGIC: - skillratio = 200 + 20 * skill_lv; + skillratio += 100 + 20 * skill_lv; + break; + /** + * Warlock + **/ + case WL_SOULEXPANSION: + skillratio += 300 + 100 * skill_lv + sstatus->int_; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_FROSTMISTY: + skillratio += 100 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_JACKFROST: + { + struct status_change *tsc = status_get_sc(target); + if( tsc && tsc->data[SC_FREEZING] ) + { + skillratio += 900 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + } + else + skillratio += 400 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + } + break; + case WL_DRAINLIFE: + skillratio = 200 * skill_lv + sstatus->int_; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_CRIMSONROCK: + skillratio += 1200 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_HELLINFERNO: + if( status_get_element(target) == ELE_FIRE ) + skillratio = 60 * skill_lv; + else + skillratio = 240 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_COMET: { + struct status_change * sc = status_get_sc(src); + if( sc ) + i = distance_xy(target->x, target->y, sc->comet_x, sc->comet_y); + else + i = 8; + if( i < 2 ) skillratio = 2500 + 500 * skill_lv; + else + if( i < 4 ) skillratio = 1600 + 400 * skill_lv; + else + if( i < 6 ) skillratio = 1200 + 300 * skill_lv; + else + skillratio = 800 + 200 * skill_lv; + } + break; + case WL_CHAINLIGHTNING_ATK: + skillratio += 100 + 300 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_EARTHSTRAIN: + skillratio += 1900 + 100 * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WL_TETRAVORTEX_FIRE: + case WL_TETRAVORTEX_WATER: + case WL_TETRAVORTEX_WIND: + case WL_TETRAVORTEX_GROUND: + skillratio += 400 + 500 * skill_lv; break; + case WL_SUMMON_ATK_FIRE: + case WL_SUMMON_ATK_WATER: + case WL_SUMMON_ATK_WIND: + case WL_SUMMON_ATK_GROUND: + skillratio = skill_lv * (status_get_lv(src) + ( sd ? sd->status.job_level : 50 ));// This is close to official, but lacking a little info to finalize. [Rytech] + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case LG_RAYOFGENESIS: + skillratio = (skillratio + 200) * skill_lv; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + break; + case WM_METALICSOUND: + skillratio += 120 * skill_lv + 60 * ( sd? pc_checkskill(sd, WM_LESSON) : 10 ) - 100; + break; + case WM_SEVERE_RAINSTORM: + skillratio += 50 * skill_lv; + break; + case WM_REVERBERATION_MAGIC: + skillratio += 100 * (sd ? pc_checkskill(sd, WM_REVERBERATION) : 1); + break; + case SO_FIREWALK: { + struct status_change * sc = status_get_sc(src); + skillratio = 300; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_HEATER_OPTION] ) + skillratio += skillratio * sc->data[SC_HEATER_OPTION]->val3 / 100; + } + break; + case SO_ELECTRICWALK: { + struct status_change * sc = status_get_sc(src); + skillratio = 300; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_BLAST_OPTION] ) + skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100; + } + break; + case SO_EARTHGRAVE: { + struct status_change * sc = status_get_sc(src); + skillratio = ( 200 * ( sd ? pc_checkskill(sd, SA_SEISMICWEAPON) : 10 ) + sstatus->int_ * skill_lv ); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val2 / 100; + } + break; + case SO_DIAMONDDUST: { + struct status_change * sc = status_get_sc(src); + skillratio = ( 200 * ( sd ? pc_checkskill(sd, SA_FROSTWEAPON) : 10 ) + sstatus->int_ * skill_lv ); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_COOLER_OPTION] ) + skillratio += skillratio * sc->data[SC_COOLER_OPTION]->val3 / 100; + } + break; + case SO_POISON_BUSTER: { + struct status_change * sc = status_get_sc(src); + skillratio += 1100 + 300 * skill_lv; + if( sc && sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val2 / 100; + } + break; + case SO_PSYCHIC_WAVE: { + struct status_change * sc = status_get_sc(src); + skillratio += -100 + skill_lv * 70 + (sstatus->int_ * 3); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc ){ + if( sc->data[SC_HEATER_OPTION] ) + skillratio += skillratio * sc->data[SC_HEATER_OPTION]->val3 / 100; + else if(sc->data[SC_COOLER_OPTION] ) + skillratio += skillratio * sc->data[SC_COOLER_OPTION]->val3 / 100; + else if(sc->data[SC_BLAST_OPTION] ) + skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100; + else if(sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val3 / 100; + } + } + break; + case SO_VARETYR_SPEAR: { + struct status_change * sc = status_get_sc(src); + skillratio += -100 + ( 100 * ( sd ? pc_checkskill(sd, SA_LIGHTNINGLOADER) : 10 ) + sstatus->int_ * skill_lv ); + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_BLAST_OPTION] ) + skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100; + } + break; + case SO_CLOUD_KILL: { + struct status_change * sc = status_get_sc(src); + skillratio += -100 + skill_lv * 40; + if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus. + if( sc && sc->data[SC_CURSED_SOIL_OPTION] ) + skillratio += skillratio * sc->data[SC_CURSED_SOIL_OPTION]->val2 / 100; + } + break; + case GN_DEMONIC_FIRE: + if( skill_lv > 20) + { // Fire expansion Lv.2 + skillratio += 110 + 20 * (skill_lv - 20) + status_get_int(src) * 3; // Need official INT bonus. [LimitLine] + } + else if( skill_lv > 10 ) + { // Fire expansion Lv.1 + skillratio += 110 + 20 * (skill_lv - 10) / 2; + } + else + skillratio += 110 + 20 * skill_lv; + break; + } MATK_RATE(skillratio); @@ -2689,7 +3021,7 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list } if(!flag.imdef){ - short mdef = tstatus->mdef; + char mdef = tstatus->mdef; int mdef2= tstatus->mdef2; if(sd) { i = sd->ignore_mdef[is_boss(target)?RC_BOSS:RC_NONBOSS]; @@ -2701,10 +3033,19 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list //mdef2-= mdef2* i/100; } } + #if RRMODE + /** + * RE MDEF Reduction (from doddler:?title=Renewal_Changes#MDEF) + * Damage from magic = Magic Attack * 111.5/(111.5+eMDEF) + * Damage = Magic Attack * 111.5/(111.5+eMDEF) - sMDEF + **/ + ad.damage = ad.damage * ((1115/10) - mdef)/(1115/10) - mdef2; + #else if(battle_config.magic_defense_type) ad.damage = ad.damage - mdef*battle_config.magic_defense_type - mdef2; else ad.damage = ad.damage * (100-mdef)/100 - mdef2; + #endif } if (skill_num == NPC_EARTHQUAKE) @@ -2957,7 +3298,31 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * md.damage = skill_calc_heal(src,target,skill_num,skill_lv,false); break; case RK_DRAGONBREATH: - md.damage = (sstatus->hp / 50 + sstatus->max_sp / 4) * (skill_lv * status_get_lv(src)/150) * (95 + 5 * (sd?pc_checkskill(sd,RK_DRAGONTRAINING):10)) / 100; + md.damage = ((status_get_hp(src) / 50) + (status_get_max_sp(src) / 4)) * skill_lv; + if (status_get_lv(src) > 100) md.damage = md.damage * status_get_lv(src) / 150; + if (sd) md.damage = md.damage * (100 + 5 * (pc_checkskill(sd,RK_DRAGONTRAINING) - 1)) / 100; + break; + /** + * Ranger + **/ + case RA_CLUSTERBOMB: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + md.damage = (2 * skill_lv * (sstatus->dex + 100)); + if (status_get_lv(src) > 100) md.damage += md.damage * (status_get_lv(src) - 50) / 200 + 15 / 10; + md.damage = md.damage * 2;// Without BaseLv Bonus + md.damage = md.damage + (5 * sstatus->int_) + (40 * ( sd ? pc_checkskill(sd,RA_RESEARCHTRAP) : 10 ) ); + break; + /** + * Mechanic + **/ + case NC_SELFDESTRUCTION: + md.damage = (sd?pc_checkskill(sd,NC_MAINFRAME):10) * skill_lv * (status_get_sp(src) + sstatus->vit); + if (status_get_lv(src) > 100) md.damage = md.damage * status_get_lv(src) / 150;// Base level bonus. + if (sd) md.damage = md.damage + status_get_hp(src); + status_set_sp(src, 0, 0); + break; + } if (nk&NK_SPLASHSPLIT){ // Divide ATK among targets @@ -3059,11 +3424,23 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list * else if( map[target->m].flag.battleground ) md.damage=battle_calc_bg_damage(src,target,md.damage,md.div_,skill_num,skill_lv,md.flag); - if (skill_num == NJ_ZENYNAGE && sd) - { //Time to Pay Up. - if ( md.damage > sd->status.zeny ) - md.damage=sd->status.zeny; - pc_payzeny(sd, md.damage); + switch( skill_num ) { + case RA_CLUSTERBOMB: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + { + struct Damage wd; + wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag); + md.damage += wd.damage; + } + break; + case NJ_ZENYNAGE: + if( sd ) { + if ( md.damage > sd->status.zeny ) + md.damage = sd->status.zeny; + pc_payzeny(sd, md.damage); + } + break; } return md; @@ -3096,10 +3473,10 @@ struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct bl } //Calculates BF_WEAPON returned damage. -int battle_calc_return_damage(struct block_list* bl, int damage, int flag) +int battle_calc_return_damage(struct block_list* bl, struct block_list *src, int *dmg, int flag) { struct map_session_data* sd = NULL; - int rdamage = 0; + int rdamage = 0, damage = *dmg; sd = BL_CAST(BL_PC, bl); @@ -3112,10 +3489,24 @@ int battle_calc_return_damage(struct block_list* bl, int damage, int flag) if(rdamage < 1) rdamage = 1; } sc = status_get_sc(bl); - if (sc && sc->data[SC_REFLECTSHIELD]) - { - rdamage += damage * sc->data[SC_REFLECTSHIELD]->val2 / 100; - if (rdamage < 1) rdamage = 1; + if( sc && sc->count ) { + if (sc->data[SC_REFLECTSHIELD]) { + rdamage += damage * sc->data[SC_REFLECTSHIELD]->val2 / 100; + if (rdamage < 1) rdamage = 1; + } + if(sc->data[SC_DEATHBOUND] && !(src->type == BL_MOB && is_boss(src)) ) { + int dir = map_calc_dir(bl,src->x,src->y), + t_dir = unit_getdir(bl), rd1 = 0; + + if( distance_bl(src,bl) <= 0 || !map_check_dir(dir,t_dir) ) { + rd1 = min(damage,status_get_max_hp(bl)) * sc->data[SC_DEATHBOUND]->val2 / 100; // Amplify damage. + *dmg = rd1 * 30 / 100; // Received damge = 30% of amplifly damage. + clif_skill_damage(src,bl,gettick(), status_get_amotion(src), 0, -30000, 1, RK_DEATHBOUND, sc->data[SC_DEATHBOUND]->val1,6); + status_change_end(bl,SC_DEATHBOUND,-1); + rdamage += rd1; + if (rdamage < 1) rdamage = 1; + } + } } } else { if (sd && sd->long_weapon_damage_return) @@ -3318,27 +3709,17 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t damage = wd.damage + wd.damage2; if( damage > 0 && src != target ) { - if( sc && sc->data[SC_DUPLELIGHT] ) { - int skilllv = sc->data[SC_DUPLELIGHT]->val1; - if( rand()%100 < sc->data[SC_DUPLELIGHT]->val2 ) - skill_addtimerskill(src,tick+status_get_adelay(src) / 2,target->id,0,0,AB_DUPLELIGHT_MELEE,skilllv,BF_WEAPON,flag); - else if( rand()%100 < sc->data[SC_DUPLELIGHT]->val3 ) - skill_addtimerskill(src,tick+status_get_adelay(src) / 2,target->id,0,0,AB_DUPLELIGHT_MAGIC,skilllv,BF_MAGIC,flag); - } - - if(tsc && tsc->data[SC_DEATHBOUND] && !is_boss(src) && map_check_dir(map_calc_dir(src,target->x,target->y),unit_getdir(target))) - { - int skilllv = tsc->data[SC_DEATHBOUND]->val1; - clif_skill_damage(src,src, tick, 0, 0, 0, 0, RK_DEATHBOUND,-1, 1); - rdamage = wd.damage * ((500 + 100*skilllv) / 100); - wd.damage = rdamage * 30 / 100; - status_zap(target, wd.damage, 0); - skill_blown(src, src, skill_get_blewcount(RK_DEATHBOUND,skilllv), unit_getdir(src), 0); - status_change_end(target,SC_DEATHBOUND,INVALID_TIMER); + if( sc && sc->data[SC_DUPLELIGHT] && (wd.flag&BF_SHORT) && rand()%100 <= 10+2*sc->data[SC_DUPLELIGHT]->val1 ) + { // Activates it only from melee damage + int skillid; + if( rand()%2 == 1 ) + skillid = AB_DUPLELIGHT_MELEE; + else + skillid = AB_DUPLELIGHT_MAGIC; + skill_attack(skill_get_type(skillid), src, src, target, skillid, sc->data[SC_DUPLELIGHT]->val1, tick, SD_LEVEL); } - else - rdamage = battle_calc_return_damage(target, damage, wd.flag); + rdamage = battle_calc_return_damage(target,src, &damage, wd.flag); if( rdamage > 0 ) { rdelay = clif_damage(src, src, tick, wd.amotion, sstatus->dmotion, rdamage, 1, 4, 0); @@ -3399,11 +3780,6 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t } } } - if (sc && sc->data[SC_CRUSHSTRIKE]) - { - skill_castend_damage_id(src, target, RK_CRUSHSTRIKE, 1, tick, flag); - status_change_end(src,SC_CRUSHSTRIKE, INVALID_TIMER); - } if (sd) { if (wd.flag & BF_WEAPON && src != target && damage > 0) { if (battle_config.left_cardfix_to_right) @@ -3551,6 +3927,8 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f case WZ_SIGHTBLASTER: case SM_MAGNUM: case MS_MAGNUM: + case RA_DETONATOR: + case RA_SENSITIVEKEEN: state |= BCT_ENEMY; strip_enemy = 0; break; @@ -3814,6 +4192,8 @@ static const struct _battle_data { { "enable_perfect_flee", &battle_config.enable_perfect_flee, BL_PC|BL_PET, BL_NUL, BL_ALL, }, { "casting_rate", &battle_config.cast_rate, 100, 0, INT_MAX, }, { "delay_rate", &battle_config.delay_rate, 100, 0, INT_MAX, }, + { "delay_dependon_dex", &battle_config.delay_dependon_dex, 0, 0, 1, }, + { "delay_dependon_agi", &battle_config.delay_dependon_agi, 0, 0, 1, }, { "skill_delay_attack_enable", &battle_config.sdelay_attack_enable, 0, 0, 1, }, { "left_cardfix_to_right", &battle_config.left_cardfix_to_right, 0, 0, 1, }, { "skill_add_range", &battle_config.skill_add_range, 0, 0, INT_MAX, }, @@ -3940,16 +4320,14 @@ static const struct _battle_data { { "arrow_decrement", &battle_config.arrow_decrement, 1, 0, 2, }, { "max_aspd", &battle_config.max_aspd, 199, 100, 199, }, { "max_walk_speed", &battle_config.max_walk_speed, 300, 100, 100*DEFAULT_WALK_SPEED, }, - { "max_lv", &battle_config.max_lv, 99, 0, 150, }, + { "max_lv", &battle_config.max_lv, 99, 0, 127, }, { "aura_lv", &battle_config.aura_lv, 99, 0, INT_MAX, }, { "max_hp", &battle_config.max_hp, 32500, 100, 1000000000, }, { "max_sp", &battle_config.max_sp, 32500, 100, 1000000000, }, { "max_cart_weight", &battle_config.max_cart_weight, 8000, 100, 1000000, }, { "max_parameter", &battle_config.max_parameter, 99, 10, 10000, }, { "max_baby_parameter", &battle_config.max_baby_parameter, 80, 10, 10000, }, - { "max_third_parameter", &battle_config.max_third_parameter, 120, 10, 10000, }, - { "max_baby_third_parameter", &battle_config.max_baby_third_parameter, 108, 10, 10000, }, - { "max_def", &battle_config.max_def, SHRT_MAX, 0, INT_MAX, }, + { "max_def", &battle_config.max_def, 99, 0, INT_MAX, }, { "over_def_bonus", &battle_config.over_def_bonus, 0, 0, 1000, }, { "skill_log", &battle_config.skill_log, BL_NUL, BL_NUL, BL_ALL, }, { "battle_log", &battle_config.battle_log, 0, 0, 1, }, @@ -4083,6 +4461,7 @@ static const struct _battle_data { { "min_cloth_color", &battle_config.min_cloth_color, 0, 0, INT_MAX, }, { "max_cloth_color", &battle_config.max_cloth_color, 4, 0, INT_MAX, }, { "pet_hair_style", &battle_config.pet_hair_style, 100, 0, INT_MAX, }, + { "castrate_dex_scale", &battle_config.castrate_dex_scale, 150, 1, INT_MAX, }, { "area_size", &battle_config.area_size, 14, 0, INT_MAX, }, { "zeny_from_mobs", &battle_config.zeny_from_mobs, 0, 0, 1, }, { "mobs_level_up", &battle_config.mobs_level_up, 0, 0, 1, }, @@ -4200,13 +4579,11 @@ static const struct _battle_data { { "bg_magic_attack_damage_rate", &battle_config.bg_magic_damage_rate, 60, 0, INT_MAX, }, { "bg_misc_attack_damage_rate", &battle_config.bg_misc_damage_rate, 60, 0, INT_MAX, }, { "bg_flee_penalty", &battle_config.bg_flee_penalty, 20, 0, INT_MAX, }, -// MVP Decrease AGI - { "max_decagi_lv", &battle_config.max_decagi_lv, 11, 1, INT_MAX, }, - { "max_decagi_dur", &battle_config.max_decagi_dur, 120000, 1, INT_MAX, }, - { "max_decagi", &battle_config.max_decagi, 50, 0, INT_MAX, }, -// Third jobs - { "rune_block_by_skill", &battle_config.rune_block_by_skill, 1, 0, 1, }, - { "rune_block_by_status", &battle_config.rune_block_by_status, 0, 0, 1, }, + /** + * rAthena + **/ + { "max_third_parameter", &battle_config.max_third_parameter, 20, 0, INT_MAX, }, + { "atcommand_max_stat_bypass", &battle_config.atcommand_max_stat_bypass, 0, 0, 100, }, }; @@ -4253,8 +4630,8 @@ void battle_adjust_conf() battle_config.max_walk_speed = 100*DEFAULT_WALK_SPEED/battle_config.max_walk_speed; battle_config.max_cart_weight *= 10; - if(battle_config.max_def > SHRT_MAX && !battle_config.weapon_defense_type) // added by [Skotlex] - battle_config.max_def = SHRT_MAX; + if(battle_config.max_def > 100 && !battle_config.weapon_defense_type) // added by [Skotlex] + battle_config.max_def = 100; if(battle_config.min_hitrate > battle_config.max_hitrate) battle_config.min_hitrate = battle_config.max_hitrate; diff --git a/src/map/battle.h b/src/map/battle.h index 7570f77b7..41ff70850 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -35,7 +35,7 @@ struct block_list; struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int count); -int battle_calc_return_damage(struct block_list *bl, int damage, int flag); +int battle_calc_return_damage(struct block_list *bl, struct block_list *src, int *, int flag); void battle_drain(struct map_session_data *sd, struct block_list *tbl, int rdamage, int ldamage, int race, int boss); @@ -111,6 +111,7 @@ extern struct Battle_Config int enable_baseatk; int enable_perfect_flee; int cast_rate, delay_rate; + int delay_dependon_dex, delay_dependon_agi; int sdelay_attack_enable; int left_cardfix_to_right; int skill_add_range; @@ -234,7 +235,7 @@ extern struct Battle_Config int max_hp; int max_sp; int max_lv, aura_lv; - int max_parameter, max_baby_parameter, max_third_parameter, max_baby_third_parameter; + int max_parameter, max_baby_parameter; int max_cart_weight; int skill_log; int battle_log; @@ -362,6 +363,7 @@ extern struct Battle_Config int max_cloth_color; // added by [MouseJstr] int pet_hair_style; // added by [Skotlex] + int castrate_dex_scale; // added by [MouseJstr] int area_size; // added by [MouseJstr] int max_def, over_def_bonus; //added by [Skotlex] @@ -495,12 +497,9 @@ extern struct Battle_Config int bg_magic_damage_rate; int bg_misc_damage_rate; int bg_flee_penalty; - - int max_decagi_lv; - int max_decagi_dur; - int max_decagi; - int rune_block_by_skill; - int rune_block_by_status; + // rAthena + int max_third_parameter; + int atcommand_max_stat_bypass; } battle_config; void do_init_battle(void); @@ -511,4 +510,7 @@ extern void battle_set_defaults(void); int battle_set_value(const char* w1, const char* w2); int battle_get_value(const char* w1); +// +struct block_list* battle_getenemyarea(struct block_list *src, int x, int y, int range, int type, int ignore_id); + #endif /* _BATTLE_H_ */ diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index 94f390c10..8d3179745 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -56,12 +56,18 @@ bool buyingstore_setup(struct map_session_data* sd, unsigned char slots) return false; } - if( map[sd->bl.m].flag.novending || map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) - {// custom: no vending maps/cells + if( map[sd->bl.m].flag.novending ) + {// custom: no vending maps clif_displaymessage(sd->fd, msg_txt(276)); // "You can't open a shop on this map" return false; } + if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) + {// custom: no vending cells + clif_displaymessage(sd->fd, msg_txt(204)); // "You can't open a shop on this cell." + return false; + } + if( slots > MAX_BUYINGSTORE_SLOTS ) { ShowWarning("buyingstore_setup: Requested %d slots, but server supports only %d slots.\n", (int)slots, MAX_BUYINGSTORE_SLOTS); @@ -105,12 +111,18 @@ void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha return; } - if( map[sd->bl.m].flag.novending || map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) - {// custom: no vending maps/cells + if( map[sd->bl.m].flag.novending ) + {// custom: no vending maps clif_displaymessage(sd->fd, msg_txt(276)); // "You can't open a shop on this map" return; } + if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) ) + {// custom: no vending cells + clif_displaymessage(sd->fd, msg_txt(204)); // "You can't open a shop on this cell." + return; + } + weight = sd->weight; // check item list diff --git a/src/map/chrif.c b/src/map/chrif.c index d1332fab2..947cb0bdc 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -505,7 +505,13 @@ void chrif_on_ready(void) ShowStatus("Map Server is now online.\n"); chrif_state = 2; chrif_check_shutdown(); - + /** + * while we're not fully ready + **/ + ShowMessage(""CL_XXBL""CL_BT_YELLOW"============= WARNING ============="CL_XXBL""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_XXBL"- "CL_BT_YELLOW"This version is under development and shouldn't be used as a real server"CL_XXBL""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_XXBL"- "CL_BT_YELLOW"For bugs, comments and suggestions: http://ro-resources.net "CL_XXBL""CL_CLL""CL_NORMAL"\n"); + ShowMessage(""CL_XXBL"- "CL_BT_YELLOW"Thank you for trying out"CL_XXBL""CL_CLL""CL_NORMAL"\n"); //If there are players online, send them to the char-server. [Skotlex] send_users_tochar(); diff --git a/src/map/clif.c b/src/map/clif.c index a8cfa48cd..75c00187e 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -63,35 +63,60 @@ static inline int itemtype(int type) return ( type == IT_PETEGG ) ? IT_WEAPON : type; } -#define WBUFPOS(p,pos,x,y,dir) \ - do { \ - uint8 *__p = (p); \ - __p+=(pos); \ - __p[0] = (uint8)((x)>>2); \ - __p[1] = (uint8)(((x)<<6) | (((y)>>4)&0x3f)); \ - __p[2] = (uint8)(((y)<<4) | ((dir)&0xf)); \ - } while(0) + +static inline void WBUFPOS(uint8* p, unsigned short pos, short x, short y, unsigned char dir) +{ + p += pos; + p[0] = (uint8)(x>>2); + p[1] = (uint8)((x<<6) | ((y>>4)&0x3f)); + p[2] = (uint8)((y<<4) | (dir&0xf)); +} + + // client-side: x0+=sx0*0.0625-0.5 and y0+=sy0*0.0625-0.5 -#define WBUFPOS2(p,pos,x0,y0,x1,y1,sx0,sy0) \ - do { \ - uint8 *__p = (p); \ - __p+=(pos); \ - __p[0]=(uint8)((x0)>>2); \ - __p[1]=(uint8)(((x0)<<6) | (((y0)>>4)&0x3f)); \ - __p[2]=(uint8)(((y0)<<4) | (((x1)>>6)&0x0f)); \ - __p[3]=(uint8)(((x1)<<2) | (((y1)>>8)&0x03)); \ - __p[4]=(uint8)(y1); \ - __p[5]=(uint8)(((sx0)<<4) | ((sy0)&0x0f)); \ - } while(0) - -#define WFIFOPOS(fd,pos,x,y,dir) WBUFPOS(WFIFOP(fd,pos),0,x,y,dir) -#define WFIFOPOS2(fd,pos,x0,y0,x1,y1,sx0,sy0) WBUFPOS2(WFIFOP(fd,pos),0,x0,y0,x1,y1,sx0,sy0) +static inline void WBUFPOS2(uint8* p, unsigned short pos, short x0, short y0, short x1, short y1, unsigned char sx0, unsigned char sy0) +{ + p += pos; + p[0] = (uint8)(x0>>2); + p[1] = (uint8)((x0<<6) | ((y0>>4)&0x3f)); + p[2] = (uint8)((y0<<4) | ((x1>>6)&0x0f)); + p[3] = (uint8)((x1<<2) | ((y1>>8)&0x03)); + p[4] = (uint8)y1; + p[5] = (uint8)((sx0<<4) | (sy0&0x0f)); +} + + +static inline void WFIFOPOS(int fd, unsigned short pos, short x, short y, unsigned char dir) +{ + WBUFPOS(WFIFOP(fd,pos), 0, x, y, dir); +} + + +inline void WFIFOPOS2(int fd, unsigned short pos, short x0, short y0, short x1, short y1, unsigned char sx0, unsigned char sy0) +{ + WBUFPOS2(WFIFOP(fd,pos), 0, x0, y0, x1, y1, sx0, sy0); +} + //To idenfity disguised characters. -#define disguised(bl) ((bl)->type==BL_PC && ((TBL_PC*)bl)->disguise) +static inline bool disguised(struct block_list* bl) +{ + return (bool)( bl->type == BL_PC && ((TBL_PC*)bl)->disguise ); +} + + +//Guarantees that the given string does not exceeds the allowed size, as well as making sure it's null terminated. [Skotlex] +static inline unsigned int mes_len_check(char* mes, unsigned int len, unsigned int max) +{ + if( len > max ) + len = max; + + mes[len-1] = '\0'; + + return len; +} + -//Guarantees that the given string does not exceeds the allowed size, as well as making sure it's null terminated. [Skotlex\] -#define mes_len_check(mes, len, max) if (len > max) { mes[max-1] = '\0'; len = max; } else mes[len-1] = '\0'; static char map_ip_str[128]; static uint32 map_ip; static uint32 bind_ip = INADDR_ANY; @@ -129,7 +154,8 @@ void clif_setbindip(const char* ip) } /*========================================== - * map鯖のport設定 + * Sets map port to 'port' + * is run from map.c upon loading map server configuration *------------------------------------------*/ void clif_setport(uint16 port) { @@ -137,7 +163,7 @@ void clif_setport(uint16 port) } /*========================================== - * map鯖のip読み出し + * Returns map server IP *------------------------------------------*/ uint32 clif_getip(void) { @@ -159,7 +185,7 @@ uint32 clif_refresh_ip(void) } /*========================================== - * map鯖のport読み出し + * Returns map port which is set by clif_setport() *------------------------------------------*/ uint16 clif_getport(void) { @@ -185,7 +211,14 @@ static inline unsigned char clif_bl_type(struct block_list *bl) { #endif /*========================================== - * clif_sendでAREA*指定時用 + * sub process of clif_send + * Called from a map_foreachinarea (grabs all players in specific area and subjects them to this function) + * In order to send area-wise packets, such as: + * - AREA : everyone nearby your area + * - AREA_WOSC (AREA WITHOUT SAME CHAT) : Not run for people in the same chat as yours + * - AREA_WOC (AREA WITHOUT CHAT) : Not run for people inside a chat + * - AREA_WOS (AREA WITHOUT SELF) : Not run for self + * - AREA_CHAT_WOC : Everyone in the area of your chat without a chat *------------------------------------------*/ int clif_send_sub(struct block_list *bl, va_list ap) { @@ -248,7 +281,8 @@ int clif_send_sub(struct block_list *bl, va_list ap) } /*========================================== - * + * Packet Delegation (called on all packets that require data to be sent to more than one client) + * functions that are sent solely to one use whose ID it posses use WFIFOSET *------------------------------------------*/ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type) { @@ -529,11 +563,10 @@ int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target return 0; } -// -// パケット作って送信 -// + /*========================================== - * + * Tells client that its player is fully loaded and that it can proceed to the map screen + * Provides client with player position and facing direction *------------------------------------------*/ int clif_authok(struct map_session_data *sd) { @@ -632,7 +665,8 @@ int clif_dropflooritem(struct flooritem_data* fitem) } /*========================================== - * + * Server tells client to remove item of ID ('fitem->bl.id') from FD player area + * If FD is 0 it tells all clients nearby this item that it is gone *------------------------------------------*/ int clif_clearflooritem(struct flooritem_data *fitem, int fd) { @@ -1080,7 +1114,8 @@ static void clif_setdisguise(struct block_list *bl, unsigned char *buf,int len) } /*========================================== - * クラスチェンジ typeはMobの場合は1で他は0? + * Acronym for 'clif_mob_class_change' used to tell clients around the monster that it's identity changed + * for example, it's run when a pupa transforms into a creamy. *------------------------------------------*/ int clif_class_change(struct block_list *bl,int class_,int type) { @@ -1099,7 +1134,7 @@ int clif_class_change(struct block_list *bl,int class_,int type) } /*========================================== - * + * Server tells client to display (sd->spiritball) amount of spiritballs on target of account id (sd->bl.id) *------------------------------------------*/ static void clif_spiritball_single(int fd, struct map_session_data *sd) { @@ -1111,7 +1146,8 @@ static void clif_spiritball_single(int fd, struct map_session_data *sd) } /*========================================== - * + * Run when player changes map / refreshes + * Tells its client to display all weather settings being used by this map *------------------------------------------*/ static void clif_weather_check(struct map_session_data *sd) { @@ -1123,7 +1159,10 @@ static void clif_weather_check(struct map_session_data *sd) || map[m].flag.fireworks || map[m].flag.sakura || map[m].flag.leaves - || map[m].flag.rain + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //|| map[m].flag.rain || map[m].flag.clouds2) { if (map[m].flag.snow) @@ -1143,11 +1182,16 @@ static void clif_weather_check(struct map_session_data *sd) clif_specialeffect_single(&sd->bl, 163, fd); if (map[m].flag.leaves) clif_specialeffect_single(&sd->bl, 333, fd); - if (map[m].flag.rain) - clif_specialeffect_single(&sd->bl, 161, fd); + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //if (map[m].flag.rain) + // clif_specialeffect_single(&sd->bl, 161, fd); } } - +/** + * Run when the weather on a map changes, throws all players in map id 'm' to clif_weather_check function + **/ void clif_weather(int m) { struct s_mapiterator* iter; @@ -1161,7 +1205,9 @@ void clif_weather(int m) } mapit_free(iter); } - +/** + * Main function to spawn a unit on the client (player/mob/pet/etc) + **/ int clif_spawn(struct block_list *bl) { unsigned char buf[128]; @@ -1193,8 +1239,10 @@ int clif_spawn(struct block_list *bl) clif_specialeffect(bl,421,AREA); if( sd->bg_id && map[sd->bl.m].flag.battleground ) clif_sendbgemblem_area(sd); - if(sd->sc.data[SC_MILLENNIUMSHIELD] && sd->sc.data[SC_MILLENNIUMSHIELD]->val2 > 0) // Ensure that we have shields to display. - clif_millenniumshield(sd,sd->sc.data[SC_MILLENNIUMSHIELD]->val2); + if( sd->sc.option&OPTION_MOUNTING ) { + //New Mounts are not complaint to the original method, so we gotta tell this guy that he is mounting. + clif_status_load_notick(&sd->bl,SI_ALL_RIDING,2,1,0,0); + } } break; case BL_MOB: @@ -1850,7 +1898,7 @@ int clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int } /*========================================== - * + * Server tells client to display cutin of name 'image' to client, in position 'type' (255, etc) *------------------------------------------*/ int clif_cutin(struct map_session_data* sd, const char* image, int type) { @@ -1922,7 +1970,8 @@ static void clif_addcards(unsigned char* buf, struct item* item) } /*========================================== - * + * Server tells client he got item of index 'n' and amount 'amount', + * when 'fail' is 1 it tells the client it failed to receive said item *------------------------------------------*/ int clif_additem(struct map_session_data *sd, int n, int amount, int fail) { @@ -1991,7 +2040,7 @@ int clif_additem(struct map_session_data *sd, int n, int amount, int fail) } /*========================================== - * + * *------------------------------------------*/ int clif_dropitem(struct map_session_data *sd,int n,int amount) { @@ -2434,8 +2483,9 @@ int clif_guild_xy_remove(struct map_session_data *sd) } /*========================================== - * ステータスを送りつける - * 表示専用数字はこの中で計算して送る + * Server tells client that data 'type' has changed and sends it's new value + * For example, when server updates the client max weight, say, due to higher STR, + * It calls this function with SP_MAXWEIGHT type *------------------------------------------*/ int clif_updatestatus(struct map_session_data *sd,int type) { @@ -2514,31 +2564,35 @@ int clif_updatestatus(struct map_session_data *sd,int type) WFIFOL(fd,4)=sd->battle_status.amotion; break; case SP_ATK1: - WFIFOL(fd,4)=sd->battle_status.batk; + WFIFOL(fd,4)=sd->battle_status.batk +sd->battle_status.rhw.atk +sd->battle_status.lhw.atk; break; case SP_DEF1: - WFIFOL(fd,4)=sd->battle_status.def2; + WFIFOL(fd,4)=sd->battle_status.def; break; case SP_MDEF1: - WFIFOL(fd,4)= sd->battle_status.mdef2; + WFIFOL(fd,4)=sd->battle_status.mdef; break; case SP_ATK2: - WFIFOL(fd,4)=sd->battle_status.rhw.atk + sd->battle_status.rhw.atk2 + sd->battle_status.equipment_atk; + WFIFOL(fd,4)=sd->battle_status.rhw.atk2 + sd->battle_status.lhw.atk2; break; case SP_DEF2: - WFIFOL(fd,4)=sd->battle_status.def; + WFIFOL(fd,4)=sd->battle_status.def2; break; case SP_MDEF2: - WFIFOL(fd,4)=sd->battle_status.mdef; + //negative check (in case you have something like Berserk active) + len = sd->battle_status.mdef2 - (sd->battle_status.vit>>1); + if (len < 0) len = 0; + WFIFOL(fd,4)= len; + len = 8; break; case SP_CRITICAL: WFIFOL(fd,4)=sd->battle_status.cri/10; break; case SP_MATK1: - WFIFOL(fd,4)=sd->weapon_matk + sd->battle_status.rhw.atk2 + sd->equipment_matk; + WFIFOL(fd,4)=sd->battle_status.matk_max; break; case SP_MATK2: - WFIFOL(fd,4)=sd->battle_status.status_matk; + WFIFOL(fd,4)=sd->battle_status.matk_min; break; @@ -2563,7 +2617,9 @@ int clif_updatestatus(struct map_session_data *sd,int type) WFIFOL(fd,4)=pc_nextjobexp(sd); break; - // 00be 終了 + /** + * SP_U<STAT> are used to update the amount of points necessary to increase that stat + **/ case SP_USTR: case SP_UAGI: case SP_UVIT: @@ -2575,14 +2631,15 @@ int clif_updatestatus(struct map_session_data *sd,int type) len=5; break; - // 013a 終了 + /** + * Tells the client how far it is allowed to attack (weapon range) + **/ case SP_ATTACKRANGE: WFIFOW(fd,0)=0x13a; WFIFOW(fd,2)=sd->battle_status.rhw.range; len=4; break; - // 0141 終了 case SP_STR: WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; @@ -2672,7 +2729,7 @@ int clif_changestatus(struct block_list *bl,int type,int val) } /*========================================== - * + * Updates BL unit view data to nearby clients *------------------------------------------*/ void clif_changelook(struct block_list *bl,int type,int val) { @@ -2865,14 +2922,17 @@ int clif_initialstatus(struct map_session_data *sd) WBUFB(buf,14)=min(sd->status.luk, UINT8_MAX); WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK,1); - WBUFW(buf,16) = sd->battle_status.batk; - WBUFW(buf,18) = sd->battle_status.rhw.atk + sd->battle_status.lhw.atk2 + sd->battle_status.equipment_atk; //atk bonus - WBUFW(buf,20) = sd->weapon_matk + sd->battle_status.rhw.atk2 + sd->equipment_matk; - WBUFW(buf,22) = sd->battle_status.status_matk; - WBUFW(buf,24) = sd->battle_status.def2; - WBUFW(buf,26) = sd->battle_status.def; // def - WBUFW(buf,28) = sd->battle_status.mdef2; - WBUFW(buf,30) = sd->battle_status.mdef; // mdef + WBUFW(buf,16) = sd->battle_status.batk + sd->battle_status.rhw.atk + sd->battle_status.lhw.atk; + WBUFW(buf,18) = sd->battle_status.rhw.atk2 + sd->battle_status.lhw.atk2; //atk bonus + WBUFW(buf,20) = sd->battle_status.matk_max; + WBUFW(buf,22) = sd->battle_status.matk_min; + WBUFW(buf,24) = sd->battle_status.def; // def + WBUFW(buf,26) = sd->battle_status.def2; + WBUFW(buf,28) = sd->battle_status.mdef; // mdef + fd = sd->battle_status.mdef2 - (sd->battle_status.vit>>1); + if (fd < 0) fd = 0; //Negative check for Frenzy'ed characters. + WBUFW(buf,30) = fd; + fd = sd->fd; WBUFW(buf,32) = sd->battle_status.hit; WBUFW(buf,34) = sd->battle_status.flee; WBUFW(buf,36) = sd->battle_status.flee2/10; @@ -2892,20 +2952,11 @@ int clif_initialstatus(struct map_session_data *sd) clif_updatestatus(sd,SP_ATTACKRANGE); clif_updatestatus(sd,SP_ASPD); - clif_updatestatus(sd,SP_ATK1); - clif_updatestatus(sd,SP_ATK2); - clif_updatestatus(sd,SP_MATK1); - clif_updatestatus(sd,SP_MATK2); - clif_updatestatus(sd,SP_DEF1); - clif_updatestatus(sd,SP_DEF2); - clif_updatestatus(sd,SP_MDEF1); - clif_updatestatus(sd,SP_MDEF2); - return 0; } /*========================================== - *矢装備 + * Server tells client item idx 'val' is meant to be shown in equipment's window arrow slot *------------------------------------------*/ int clif_arrowequip(struct map_session_data *sd,int val) { @@ -2947,7 +2998,8 @@ int clif_arrow_fail(struct map_session_data *sd,int type) } /*========================================== - * 作成可能 矢リスト送信 + * Server tells client to display a window similar to Mangifier (item) one + * Server populates the window with avilable arrow crafting options according to player's inventory *------------------------------------------*/ int clif_arrow_create_list(struct map_session_data *sd) { @@ -2983,7 +3035,7 @@ int clif_arrow_create_list(struct map_session_data *sd) } /*========================================== - * + * Server tells client his response regarding the earlier request to increase status *------------------------------------------*/ int clif_statusupack(struct map_session_data *sd,int type,int ok,int val) { @@ -3069,7 +3121,7 @@ int clif_misceffect(struct block_list* bl,int type) } /*========================================== - * 表示オプション変更 + * Server tells BL unit and all nearby clients that his unit-view options (e.g. stone curse appearance) changed *------------------------------------------*/ int clif_changeoption(struct block_list* bl) { @@ -3462,7 +3514,7 @@ void clif_tradestart(struct map_session_data* sd, uint8 type) } /*========================================== - * 相手方からのアイテム追加 + * Server tells 'tsd' player client info on the items 'sd' player just added to the trade window *------------------------------------------*/ void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount) { @@ -3526,7 +3578,9 @@ void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd } /*========================================== - * アイテム追加成功/失敗 + * Server tells client on the status of it's OK request + * fail 1 : the other person did 'ok' + * fail 0 : you did 'ok' *------------------------------------------*/ void clif_tradeitemok(struct map_session_data* sd, int index, int fail) { @@ -3542,7 +3596,9 @@ void clif_tradeitemok(struct map_session_data* sd, int index, int fail) } /*========================================== - * 取り引きok押し + * Server tells client on the status of it's lock request + * fail 1 : The other trader lock request + * fail 0 : Your lock request *------------------------------------------*/ void clif_tradedeal_lock(struct map_session_data* sd, int fail) { @@ -3557,7 +3613,7 @@ void clif_tradedeal_lock(struct map_session_data* sd, int fail) } /*========================================== - * 取り引きがキャンセルされました + * Server tells client it's trade request was cancelled *------------------------------------------*/ void clif_tradecancelled(struct map_session_data* sd) { @@ -3571,7 +3627,7 @@ void clif_tradecancelled(struct map_session_data* sd) } /*========================================== - * 取り引き完了 + * Server tells client the final status on his trade request *------------------------------------------*/ void clif_tradecompleted(struct map_session_data* sd, int fail) { @@ -3586,7 +3642,7 @@ void clif_tradecompleted(struct map_session_data* sd, int fail) } /*========================================== - * カプラ倉庫のアイテム数を更新 + * Server tells client it's quantity of items in storage changed *------------------------------------------*/ void clif_updatestorageamount(struct map_session_data* sd, int amount) { @@ -3603,7 +3659,7 @@ void clif_updatestorageamount(struct map_session_data* sd, int amount) } /*========================================== - * カプラ倉庫にアイテムを追加する + * Server tells client it's status on his request to add a item to storage *------------------------------------------*/ void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount) { @@ -3658,7 +3714,7 @@ void clif_updateguildstorageamount(struct map_session_data* sd, int amount) } /*========================================== - * カプラ倉庫からアイテムを取り去る + * Server tells client its status on his request to remove a item from storage *------------------------------------------*/ void clif_storageitemremoved(struct map_session_data* sd, int index, int amount) { @@ -3675,7 +3731,7 @@ void clif_storageitemremoved(struct map_session_data* sd, int index, int amount) } /*========================================== - * カプラ倉庫を閉じる + * Server tells client his storage was closed *------------------------------------------*/ void clif_storageclose(struct map_session_data* sd) { @@ -3688,9 +3744,9 @@ void clif_storageclose(struct map_session_data* sd) WFIFOW(fd,0) = 0xf8; // Storage Closed WFIFOSET(fd,packet_len(0xf8)); } - +int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3); /*========================================== - * PC表示 + * Server tells 'sd' player client the abouts of 'dstsd' player *------------------------------------------*/ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd) { @@ -3714,7 +3770,10 @@ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_d if(dstsd->spiritball > 0) clif_spiritball_single(sd->fd, dstsd); - + if( dstsd->sc.option&OPTION_MOUNTING ) { + //New Mounts are not complaint to the original method, so we gotta tell this guy that I'm mounting. + clif_status_load_single(sd->fd,dstsd->bl.id,SI_ALL_RIDING,2,1,0,0); + } if( (sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting. (sd->bg_id && sd->bg_id == dstsd->bg_id) || //BattleGround (battle_config.disp_hpmeter && (gmlvl = pc_isGM(sd)) >= battle_config.disp_hpmeter && gmlvl >= pc_isGM(dstsd)) ) @@ -3796,7 +3855,10 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl) //Modifies the type of damage according to status changes [Skotlex] //Aegis data specifies that: 4 endure against single hit sources, 9 against multi-hit. -#define clif_calc_delay(type,div,damage,delay) ((delay)==0&&(damage)>0?((div)>1?9:4):type) +static inline int clif_calc_delay(int type, int div, int damage, int delay) +{ + return ( delay == 0 && damage > 0 ) ? ( div > 1 ? 9 : 4 ) : type; +} /*========================================== * Estimates walk delay based on the damage criteria. [Skotlex] @@ -3894,11 +3956,12 @@ int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tic WBUFL(buf,2) = -src->id; if (disguised(dst)) WBUFL(buf,6) = dst->id; - if(damage > 0) WBUFW(buf,22) = -1; #if PACKETVER < 20071113 + if(damage > 0) WBUFW(buf,22) = -1; if(damage2 > 0) WBUFW(buf,27) = -1; #else - if(damage2 > 0) WBUFW(buf,29) = -1; + if(damage > 0) WBUFL(buf,22) = -1; + if(damage2 > 0) WBUFL(buf,29) = -1; #endif clif_send(buf,packet_len(cmd),src,SELF); } @@ -4019,7 +4082,7 @@ void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fi } /*========================================== - * 場所スキルエフェクトが視界に入る + * Server tells client 'sd' of all nearby skill units (e.g. safety wall) *------------------------------------------*/ static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill_unit *unit) { @@ -4059,7 +4122,7 @@ static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill } /*========================================== - * 場所スキルエフェクトが視界から消える + * Server tells client to remove unit of id 'unit->bl.id' *------------------------------------------*/ static void clif_clearchar_skillunit(struct skill_unit *unit, int fd) { @@ -4075,7 +4138,7 @@ static void clif_clearchar_skillunit(struct skill_unit *unit, int fd) } /*========================================== - * 場所スキルエフェクト削除 + * Server tells all clients in sight of 'unit->bl.id' ID to remove itself from sight (delete) *------------------------------------------*/ void clif_skill_delunit(struct skill_unit *unit) { @@ -4220,7 +4283,7 @@ int clif_insight(struct block_list *bl,va_list ap) } /*========================================== - * スキルリストを送信する + * Server tells the client information on 'sd' player's skill tree *------------------------------------------*/ int clif_skillinfoblock(struct map_session_data *sd) { @@ -4257,7 +4320,9 @@ int clif_skillinfoblock(struct map_session_data *sd) return 1; } - +/** + * Server tells client 'sd' to add skill of id 'id' to it's skill tree (e.g. with Ice Falcion item) + **/ int clif_addskill(struct map_session_data *sd, int id ) { int fd; @@ -4308,7 +4373,7 @@ int clif_deleteskill(struct map_session_data *sd, int id) } /*========================================== - * スキル割り振り通知 + * Server tells client it's skill of id 'skill_num' level changed *------------------------------------------*/ int clif_skillup(struct map_session_data *sd,int skill_num) { @@ -4419,8 +4484,7 @@ int clif_skillcastcancel(struct block_list* bl) /// btype==5 "no shout" MsgStringTable[164] /// btype==6 "no PKing" MsgStringTable[165] /// btype==7 "no alligning" MsgStringTable[383] -/// btype==8: "Insufficient level for joining a Party" -/// btype>=9: Ignored. +/// btype>=8: ignored /// if(skill_id==AL_WARP) "not enough skill level" MsgStringTable[214] /// if(skill_id==TF_STEAL) "steal failed" MsgStringTable[205] /// if(skill_id==TF_POISON) "envenom failed" MsgStringTable[207] @@ -4436,38 +4500,7 @@ int clif_skillcastcancel(struct block_list* bl) /// type==8 "blue gemstone needed" MsgStringTable[247] /// type==9 "overweight" MsgStringTable[580] /// type==10 "skill failed" MsgStringTable[285] -/// type==11 "This skill can't be used on that object" -/// type==12 "You can't use skill because you have exceeded the number Ansila possession limit" -/// type==13 "need Holy Water" -/// type==14 "need Ancilla to cast skill" -/// type==15 "Can't be duplicated with certain distance" -/// type==16 "In order to use this skill, you need other skill" -/// type==17 "This skill can't be used alone" -/// type==18 "This skill can be used to certain direction only" -/// type==19 "Can't summon anymore" -/// type==20 "There is no summoned sphere" -/// type==21 "There exists no usable imitaion skill" -/// type==22 "You can't reuse this skill" -/// type==23 "Skill can't be used in this state" -/// type==24 "Paintbrush is needed" -/// type==25 "available only on the dragon" -/// type==26 "Skill can't be used on designated spot " -/// type==27 "Assistant SP is not enough" -/// type==31 "Can only be used for linked to weapon blocking" -/// type==32 "Need a weapon coated with poison of a guillotine cross v" -/// type==33 "Can only be used while riding Madogear" -/// type==37 "Load a Cannon Ball" -/// type==40 "Can only be used in Hovering state" -/// type==43 "Need a Guillotine Poison" -/// type==50 "Can't be used while on Magic Gear" -/// type==51 "Need a Magic Book" -/// type==52 "Feel sleepy since Magic Book is too difficult to understand" -/// type==53 "Not enough saved point" -/// type==54 "Can't read a Magic Book anymore" -/// type==57 "usable only when cart is put on" -/// type==60 "Can't cast anymore" -/// type==71 "[ITEMID] need AMOUNT" -/// type==72 "Need to put on [ITEMID] in order to use" +/// type>=11 ignored /// /// if(success!=0) doesn't display any of the previous messages /// Note: when this packet is received an unknown flag is always set to 0, @@ -4496,13 +4529,11 @@ int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype) if(skill_id == TF_POISON && battle_config.display_skill_fail&8) return 0; - // This is the correct packet structure. - // If you need to pack 2 numbers into bytes 4~7 (btype) use MakeDWord [Gepard] WFIFOHEAD(fd,packet_len(0x110)); WFIFOW(fd,0) = 0x110; WFIFOW(fd,2) = skill_id; WFIFOL(fd,4) = btype; - WFIFOB(fd,8) = 0; + WFIFOB(fd,8) = 0;// success WFIFOB(fd,9) = type; WFIFOSET(fd,packet_len(0x110)); @@ -4677,7 +4708,7 @@ int clif_skill_damage2(struct block_list *src,struct block_list *dst,unsigned in */ /*========================================== - * 支援/回復スキルエフェクト + * Server tells client(s) that 'src' casted a skill of nodamage type (e.g. heal) on 'dst' *------------------------------------------*/ int clif_skill_nodamage(struct block_list *src,struct block_list *dst,int skill_id,int heal,int fail) { @@ -4710,7 +4741,8 @@ int clif_skill_nodamage(struct block_list *src,struct block_list *dst,int skill_ } /*========================================== - * 場所スキルエフェクト + * Server tells client 'src' to display effect of skill id 'skill_id' on location 'x' and 'y' + * 'val' is used for information that varies from skill to skill, usually it's the skill level *------------------------------------------*/ int clif_skill_poseffect(struct block_list *src,int skill_id,int val,int x,int y,int tick) { @@ -4736,7 +4768,7 @@ int clif_skill_poseffect(struct block_list *src,int skill_id,int val,int x,int y } /*========================================== - * 場所スキルエフェクト表示 + * Tells all client's nearby 'unit' sight range that it spawned *------------------------------------------*/ //FIXME: this is just an AREA version of clif_getareachar_skillunit() void clif_skill_setunit(struct skill_unit *unit) @@ -4774,7 +4806,7 @@ void clif_skill_setunit(struct skill_unit *unit) } /*========================================== - * ワープ場所選択 + * Used to display 'teleport' and 'warp portal' information on it's respective dialogs *------------------------------------------*/ void clif_skill_warppoint(struct map_session_data* sd, short skill_num, short skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4) { @@ -4841,7 +4873,7 @@ void clif_skill_teleportmessage(struct map_session_data *sd, int type) } /*========================================== - * モンスター情報 + * Server tells client to display 'estimation' (Sense) information for monster (bl) 'dst' *------------------------------------------*/ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) { @@ -4877,14 +4909,15 @@ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) return 0; } /*========================================== - * アイテム合成可能リスト + * Server tells client to display a window similar to Mangifier (item) one + * Server populates the window with avilable crafting options according to skill used to call this *------------------------------------------*/ -int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger) +int clif_skill_produce_mix_list(struct map_session_data *sd, int skillid , int trigger) { int i,c,view,fd; nullpo_ret(sd); - if(sd->menuskill_id == AM_PHARMACY) + if(sd->menuskill_id == skillid) return 0; //Avoid resending the menu twice or more times... fd=sd->fd; WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8); @@ -4904,7 +4937,7 @@ int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger) WFIFOW(fd, 2)=c*8+8; WFIFOSET(fd,WFIFOW(fd,2)); if(c > 0) { - sd->menuskill_id = AM_PHARMACY; + sd->menuskill_id = skillid; sd->menuskill_val = trigger; return 1; } @@ -4975,9 +5008,9 @@ int clif_status_load(struct block_list *bl,int type, int flag) return 0; } /*========================================== - * 状態異常アイコン/メッセージ表示 + * Server tell's BL and nearby clients of his status change *------------------------------------------*/ -int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick) +int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick,int val1, int val2, int val3) { unsigned char buf[32]; @@ -4992,8 +5025,7 @@ int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick type == SI_TENSIONRELAX || type == SI_LANDENDOW || type == SI_AUTOBERSERK || type == SI_BUMP || type == SI_READYSTORM || type == SI_READYDOWN || type == SI_READYTURN || type == SI_READYCOUNTER || type == SI_DODGE || - type == SI_DEVIL || type == SI_NIGHT || type == SI_INTRAVISION || - type == SI_CLOAKING) + type == SI_DEVIL || type == SI_NIGHT || type == SI_INTRAVISION) tick=0; if( battle_config.display_status_timers && tick>0 ) @@ -5006,9 +5038,9 @@ int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick if( battle_config.display_status_timers && tick>0 ) { WBUFL(buf,9)=tick; - WBUFL(buf,13)=0; - WBUFL(buf,17)=0; - WBUFL(buf,21)=0; + WBUFL(buf,13) = val1; + WBUFL(buf,17) = val2; + WBUFL(buf,21) = val3; } clif_send(buf,packet_len(WBUFW(buf,0)),bl,AREA); return 0; @@ -5041,7 +5073,6 @@ int clif_displaymessage(const int fd, const char* mes) } /*========================================== - * 天の声を送信する * Send broadcast message in yellow or blue (without font formatting). * S 009A <len>.W <message>.?B *------------------------------------------*/ @@ -5065,7 +5096,8 @@ int clif_broadcast(struct block_list* bl, const char* mes, int len, int type, en } /*========================================== - * グローバルメッセージ + * Displays a message on a 'bl' to all it's nearby clients + * Used by npc_globalmessage *------------------------------------------*/ void clif_GlobalMessage(struct block_list* bl, const char* message) { @@ -5088,7 +5120,7 @@ void clif_GlobalMessage(struct block_list* bl, const char* message) WBUFW(buf,0)=0x8d; WBUFW(buf,2)=len+8; WBUFL(buf,4)=bl->id; - strncpy((char *) WBUFP(buf,8),message,len); + safestrncpy((char *) WBUFP(buf,8),message,len); clif_send((unsigned char *) buf,WBUFW(buf,2),bl,ALL_CLIENT); } @@ -5097,7 +5129,7 @@ void clif_GlobalMessage(struct block_list* bl, const char* message) *------------------------------------------*/ void clif_MainChatMessage(const char* message) { - char buf[200]; + uint8 buf[200]; int len; if(!message) @@ -5111,8 +5143,8 @@ void clif_MainChatMessage(const char* message) WBUFW(buf,0)=0x8d; WBUFW(buf,2)=len+8; WBUFL(buf,4)=0; - strncpy((char *) WBUFP(buf,8),message,len); - clif_send((unsigned char *) buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT); + safestrncpy((char *) WBUFP(buf,8),message,len); + clif_send(buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT); } /*========================================== @@ -5138,7 +5170,8 @@ int clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned lo return 0; } /*========================================== - * HPSP回復エフェクトを送信する + * Server tells self client to heal self for 'val', is either SP_HP or SP_SP + * It displays these green and blue heal numbers that show up at your body and go up until they fade away *------------------------------------------*/ int clif_heal(int fd,int type,int val) { @@ -5152,7 +5185,7 @@ int clif_heal(int fd,int type,int val) } /*========================================== - * 復活する + * Server tells nearby clients of 'bl' that it ressurected (and plays ress effect) *------------------------------------------*/ int clif_resurrection(struct block_list *bl,int type) { @@ -5200,7 +5233,8 @@ void clif_map_type(struct map_session_data* sd, enum map_type type) } /*========================================== - * PVP実装?(仮) + * Server tells client on it's pvp rank and map status, + * (it controls the counter on the bottom right of the map existent in pvp rooms) *------------------------------------------*/ int clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type) { @@ -5249,7 +5283,8 @@ void clif_map_property_mapall(int map, enum map_property property) } /*========================================== - * 精錬エフェクトを送信する + * Server tells client the status on refine of item index 'index' from refine 'val' + * Message displayed depends on 'fail' (broken(red) or success(blue)) *------------------------------------------*/ void clif_refine(int fd, int fail, int index, int val) { @@ -5318,7 +5353,8 @@ int clif_wis_end(int fd, int flag) } /*========================================== - * キャラID名前引き結果を送信する + * Server tells client that char id 'charid' is to be assigned the name of 'name' + * This is used when client requests the server the name written in a item, e.g. crafted alche potions *------------------------------------------*/ int clif_solved_charname(int fd, int charid, const char* name) { @@ -5331,7 +5367,7 @@ int clif_solved_charname(int fd, int charid, const char* name) } /*========================================== - * カードの挿入可能リストを返す + * Server tells client to list all items that may be worn by card item of index 'idx' *------------------------------------------*/ int clif_use_card(struct map_session_data *sd,int idx) { @@ -5381,7 +5417,7 @@ int clif_use_card(struct map_session_data *sd,int idx) return 0; } /*========================================== - * カードの挿入終了 + * Server tells client his status on the previous clif_use_card (failed or OK) *------------------------------------------*/ int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag) { @@ -5400,7 +5436,7 @@ int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int } /*========================================== - * 鑑定可能アイテムリスト送信 + * Server tells client it's list of unidentified items *------------------------------------------*/ int clif_item_identify_list(struct map_session_data *sd) { @@ -5429,7 +5465,7 @@ int clif_item_identify_list(struct map_session_data *sd) } /*========================================== - * 鑑定結果 + * Server tells client his item of index 'idx' has been identified *------------------------------------------*/ int clif_item_identified(struct map_session_data *sd,int idx,int flag) { @@ -5447,7 +5483,7 @@ int clif_item_identified(struct map_session_data *sd,int idx,int flag) } /*========================================== - * 修理可能アイテムリスト送信 + * Server tells client the list of broken items *------------------------------------------*/ int clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd) { @@ -5547,7 +5583,9 @@ int clif_item_refine_list(struct map_session_data *sd) } /*========================================== - * アイテムによる一時的なスキル効果 + * Server tells client to display the 'green skill name' at the top of the screen + target cursor, + * for skill 'skillid' of 'skilllv' level + * Used for example when player uses a skill scroll (e.g. Fire Bolt Scroll) *------------------------------------------*/ int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv) { @@ -5571,7 +5609,7 @@ int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv) } /*========================================== - * カートにアイテム追加 + * Server tells client it's status on trying to add item of index 'n' and amount 'amount' to it's cart *------------------------------------------*/ int clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail) { @@ -5621,7 +5659,7 @@ int clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail) } /*========================================== - * カートからアイテム削除 + * Server tells client it's status on trying to remove item of index 'n' and amount 'amount' from it's cart to invent *------------------------------------------*/ int clif_cart_delitem(struct map_session_data *sd,int n,int amount) { @@ -5963,43 +6001,54 @@ void clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd) WFIFOSET(fd,packet_len(cmd)); } -/*========================================== - * Party invitation result. - * R 00fd <nick>.24S <flag>.B - * R 02c5 <nick>.24S <flag>.L - * Flag values are: - * 0 -> char is already in a party - * 1 -> party invite was rejected - * 2 -> party invite was accepted - * 3 -> party is full - * 4 -> char of the same account already joined the party - *------------------------------------------*/ -void clif_party_inviteack(struct map_session_data* sd, const char* nick, int flag) + +/// Party invite result. +/// R 00fd <nick>.24S <result>.B +/// R 02c5 <nick>.24S <result>.L +/// result=0 : char is already in a party -> MsgStringTable[80] +/// result=1 : party invite was rejected -> MsgStringTable[81] +/// result=2 : party invite was accepted -> MsgStringTable[82] +/// result=3 : party is full -> MsgStringTable[83] +/// result=4 : char of the same account already joined the party -> MsgStringTable[608] +/// result=5 : char blocked party invite -> MsgStringTable[1324] (since 20070904) +/// result=7 : char is not online or doesn't exist -> MsgStringTable[71] (since 20070904) +/// result=8 : (%s) TODO instance related? -> MsgStringTable[1388] (since 20080527) +/// return=9 : TODO map prohibits party joining? -> MsgStringTable[1871] (since 20110205) +void clif_party_inviteack(struct map_session_data* sd, const char* nick, int result) { int fd; nullpo_retv(sd); fd=sd->fd; +#if PACKETVER < 20070904 + if( result == 7 ) { + clif_displaymessage(fd, msg_txt(3)); + return; + } +#endif + #if PACKETVER < 20070821 WFIFOHEAD(fd,packet_len(0xfd)); WFIFOW(fd,0) = 0xfd; safestrncpy((char*)WFIFOP(fd,2),nick,NAME_LENGTH); - WFIFOB(fd,26) = flag; + WFIFOB(fd,26) = result; WFIFOSET(fd,packet_len(0xfd)); #else WFIFOHEAD(fd,packet_len(0x2c5)); WFIFOW(fd,0) = 0x2c5; safestrncpy((char*)WFIFOP(fd,2),nick,NAME_LENGTH); - WFIFOL(fd,26) = flag; + WFIFOL(fd,26) = result; WFIFOSET(fd,packet_len(0x2c5)); #endif } + /*========================================== - * パーティ設定送信 - * flag & 0x001=exp変更ミス - * 0x010=item変更ミス - * 0x100=一人にのみ送信 + * Server tells client (and it's party members) of a change in the party settings + * 'Flag' Options + * - 0x01 (exp) + * - 0x10 (item) + * - 0x100 (party member logged in / was added to party) *------------------------------------------*/ int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag) { @@ -6034,7 +6083,7 @@ int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag) return 0; } /*========================================== - * パーティ脱退(脱退前に呼ぶこと) + * Server tells party members of party 'p' that 'sd' player left *------------------------------------------*/ int clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int account_id, const char* name, int flag) { @@ -6056,15 +6105,14 @@ int clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int a WBUFL(buf,2)=account_id; memcpy(WBUFP(buf,6),name,NAME_LENGTH); WBUFB(buf,30)=flag&0x0f; - if((flag&0xf0)==0) clif_send(buf,packet_len(0x105),&sd->bl,PARTY); - else + else clif_send(buf,packet_len(0x105),&sd->bl,SELF); return 0; } /*========================================== - * パーティメッセージ送信 + * Server deploys a message to all party members, called from party.c:party_recv_message() *------------------------------------------*/ int clif_party_message(struct party_data* p, int account_id, const char* mes, int len) { @@ -6086,7 +6134,7 @@ int clif_party_message(struct party_data* p, int account_id, const char* mes, in return 0; } /*========================================== - * パーティ座標通知 + * Server tells all party members of 'sd' player that 'sd' player location changed *------------------------------------------*/ int clif_party_xy(struct map_session_data *sd) { @@ -6119,7 +6167,7 @@ int clif_party_xy_single(int fd, struct map_session_data *sd) /*========================================== - * パーティHP通知 + * Server tells nearby party members of 'sd' that his hp bar has updated *------------------------------------------*/ int clif_party_hp(struct map_session_data *sd) { @@ -6224,7 +6272,7 @@ int clif_hpmeter_sub(struct block_list *bl, va_list ap) } /*========================================== - * GMへ場所とHP通知 + * Server tells all nearby gms to 'sd' that 'sd' hp bar was updated *------------------------------------------*/ int clif_hpmeter(struct map_session_data *sd) { @@ -6237,7 +6285,7 @@ int clif_hpmeter(struct map_session_data *sd) } /*========================================== - * パーティ場所移動(未使用) + * (?) Server tells 'sd' party members that 'sd' state 'changed' *------------------------------------------*/ void clif_party_move(struct party* p, struct map_session_data* sd, int online) { @@ -6258,7 +6306,8 @@ void clif_party_move(struct party* p, struct map_session_data* sd, int online) clif_send(buf,packet_len(0x104),&sd->bl,PARTY); } /*========================================== - * 攻撃するために移動が必要 + * Server tells client to attack bl, if not in range of attack (rhw.range) it'll move to bl + * called from unit.c *------------------------------------------*/ int clif_movetoattack(struct map_session_data *sd,struct block_list *bl) { @@ -6280,7 +6329,7 @@ int clif_movetoattack(struct map_session_data *sd,struct block_list *bl) return 0; } /*========================================== - * 製造エフェクト + * Server tells client to display produce effect (refine-like), success or failure depends on 'flag' *------------------------------------------*/ int clif_produceeffect(struct map_session_data* sd,int flag,int nameid) { @@ -6331,7 +6380,7 @@ int clif_pet_roulette(struct map_session_data *sd,int data) } /*========================================== - * pet卵リスト作成 + * Server tells client to list it's eggs (used in hatching window to select a egg) *------------------------------------------*/ int clif_sendegg(struct map_session_data *sd) { @@ -6461,7 +6510,7 @@ int clif_pet_food(struct map_session_data *sd,int foodid,int fail) } /*========================================== - * オートスペル リスト送信 + * Server tells client to display autospell (Sage Skill) skill selection list *------------------------------------------*/ int clif_autospell(struct map_session_data *sd,int skilllv) { @@ -6549,7 +6598,7 @@ void clif_devotion(struct block_list *src, struct map_session_data *tsd) } /*========================================== - * 氣球 + * Server tells clients nearby 'sd' (and himself) to display 'sd->spiritball' number of spiritballs on 'sd' *------------------------------------------*/ int clif_spiritball(struct map_session_data *sd) { @@ -6581,7 +6630,8 @@ int clif_combo_delay(struct block_list *bl,int wait) return 0; } /*========================================== - *白刃取り + * Server tells client to display blade stop animation 'link' from 'src' to 'dst_id' (account id of target) + * active toggles the state *------------------------------------------*/ void clif_bladestop(struct block_list *src, int dst_id, int active) { @@ -6598,7 +6648,7 @@ void clif_bladestop(struct block_list *src, int dst_id, int active) } /*========================================== - * MVPエフェクト + * Server tells clients nearby 'sd' (and itself) to display MvP killed effect on 'sd' player *------------------------------------------*/ int clif_mvp_effect(struct map_session_data *sd) { @@ -6612,7 +6662,7 @@ int clif_mvp_effect(struct map_session_data *sd) return 0; } /*========================================== - * MVPアイテム所得 + * Server tells client to display mvp drop prize info to player 'sd' for item id 'nameid' *------------------------------------------*/ int clif_mvp_item(struct map_session_data *sd,int nameid) { @@ -6631,7 +6681,7 @@ int clif_mvp_item(struct map_session_data *sd,int nameid) return 0; } /*========================================== - * MVP経験値所得 + * Server tells client to display mvp exp prize to player 'sd' for amount 'exp' *------------------------------------------*/ int clif_mvp_exp(struct map_session_data *sd, unsigned int exp) { @@ -6693,7 +6743,7 @@ void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g) /*========================================== - * ギルドメンバログイン通知 + * Server tells all members of 'g' guild that member of index 'idx' is online or offline (flag 1:0) *------------------------------------------*/ int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag) { @@ -6755,7 +6805,7 @@ int clif_guild_send_onlineinfo(struct map_session_data *sd) } /*========================================== - * ギルドマスター通知(14dへの応答) + * Tells 'sd' whether he is the guild master of his guild or not (relies on sd->state.gmaster_flag) *------------------------------------------*/ int clif_guild_masterormember(struct map_session_data *sd) { @@ -6814,7 +6864,7 @@ int clif_guild_basicinfo(struct map_session_data *sd) } /*========================================== - * ギルド同盟/敵対情報 + * Server tells client 'sd' it's guild alliances *------------------------------------------*/ int clif_guild_allianceinfo(struct map_session_data *sd) { @@ -6843,7 +6893,7 @@ int clif_guild_allianceinfo(struct map_session_data *sd) } /*========================================== - * ギルドメンバーリスト + * Server tells client it's guild member list *------------------------------------------*/ int clif_guild_memberlist(struct map_session_data *sd) { @@ -6873,7 +6923,7 @@ int clif_guild_memberlist(struct map_session_data *sd) WFIFOL(fd,c*104+22)=(int)cap_value(m->exp,0,INT32_MAX); WFIFOL(fd,c*104+26)=m->online; WFIFOL(fd,c*104+30)=m->position; - memset(WFIFOP(fd,c*104+34),0,50); // メモ? + memset(WFIFOP(fd,c*104+34),0,50); //[Ind] - This is displayed in the 'note' column but being you can't edit it it's sent empty. memcpy(WFIFOP(fd,c*104+84),m->name,NAME_LENGTH); c++; } @@ -6882,7 +6932,7 @@ int clif_guild_memberlist(struct map_session_data *sd) return 0; } /*========================================== - * ギルド役職名リスト + * Server tell client it's guild position list *------------------------------------------*/ int clif_guild_positionnamelist(struct map_session_data *sd) { @@ -6905,7 +6955,7 @@ int clif_guild_positionnamelist(struct map_session_data *sd) return 0; } /*========================================== - * ギルド役職情報リスト + * Server tell client about it's guild position permissions and tax *------------------------------------------*/ int clif_guild_positioninfolist(struct map_session_data *sd) { @@ -6931,7 +6981,7 @@ int clif_guild_positioninfolist(struct map_session_data *sd) return 0; } /*========================================== - * ギルド役職変更通知 + * Server tells client about position 'idx' information, being it changed *------------------------------------------*/ int clif_guild_positionchanged(struct guild *g,int idx) { @@ -6952,7 +7002,7 @@ int clif_guild_positionchanged(struct guild *g,int idx) return 0; } /*========================================== - * ギルドメンバ変更通知 + * Server tells client about a specific guild member index that changed *------------------------------------------*/ int clif_guild_memberpositionchanged(struct guild *g,int idx) { @@ -6971,7 +7021,7 @@ int clif_guild_memberpositionchanged(struct guild *g,int idx) return 0; } /*========================================== - * ギルドエンブレム送信 + * Server tells client about this new cool emblem a specific guild got *------------------------------------------*/ int clif_guild_emblem(struct map_session_data *sd,struct guild *g) { @@ -7076,7 +7126,7 @@ int clif_guild_notice(struct map_session_data* sd, struct guild* g) } /*========================================== - * ギルドメンバ勧誘 + * Server tells client 'sd' that guild 'g' wants to invite him *------------------------------------------*/ int clif_guild_invite(struct map_session_data *sd,struct guild *g) { @@ -7116,7 +7166,7 @@ int clif_guild_inviteack(struct map_session_data *sd,int flag) } /*========================================== - * ギルドメンバ脱退通知 + * Server tells guild members of 'sd' that he left his guild for a reason *------------------------------------------*/ int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes) { @@ -7132,7 +7182,7 @@ int clif_guild_leave(struct map_session_data *sd,const char *name,const char *me } /*========================================== - * ギルドメンバ追放通知 + * Server tells guild members of 'sd' that 'name' of account id 'account_id' was expelled for reason 'mes' *------------------------------------------*/ void clif_guild_expulsion(struct map_session_data* sd, const char* name, const char* mes, int account_id) { @@ -7155,7 +7205,7 @@ void clif_guild_expulsion(struct map_session_data* sd, const char* name, const c } /*========================================== - * ギルド追放メンバリスト + * Server tells client on sd's guild expulsion records *------------------------------------------*/ void clif_guild_expulsionlist(struct map_session_data* sd) { @@ -7197,29 +7247,35 @@ void clif_guild_expulsionlist(struct map_session_data* sd) WFIFOSET(fd,WFIFOW(fd,2)); } -/*========================================== - * ギルド会話 - *------------------------------------------*/ -int clif_guild_message(struct guild *g,int account_id,const char *mes,int len) -{ + +/// Notification of a guild chat message (ZC_GUILD_CHAT) +/// 017f <packet len>.W <message>.?B +void clif_guild_message(struct guild *g,int account_id,const char *mes,int len) +{// TODO: account_id is not used, candidate for deletion? [Ai4rei] struct map_session_data *sd; - unsigned char *buf; + uint8 buf[256]; - buf = (unsigned char*)aMallocA((len + 4)*sizeof(unsigned char)); + if( len == 0 ) + { + return; + } + else if( len > sizeof(buf)-5 ) + { + ShowWarning("clif_guild_message: Truncated message '%s' (len=%d, max=%d, guild_id=%d).\n", mes, len, sizeof(buf)-5, g->guild_id); + len = sizeof(buf)-5; + } WBUFW(buf, 0) = 0x17f; - WBUFW(buf, 2) = len + 4; - memcpy(WBUFP(buf,4), mes, len); + WBUFW(buf, 2) = len + 5; + safestrncpy((char*)WBUFP(buf,4), mes, len+1); if ((sd = guild_getavailablesd(g)) != NULL) clif_send(buf, WBUFW(buf,2), &sd->bl, GUILD_NOBG); +} - if(buf) aFree(buf); - return 0; -} /*========================================== - * ギルドスキル割り振り通知 + * Server tells client 'sd' that his guild skill 'skill_num' gone to level 'lv' *------------------------------------------*/ int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv) { @@ -7239,7 +7295,7 @@ int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv) return 0; } /*========================================== - * ギルド同盟要請 + * Server tells client 'sd' that 'account_id' from guild name 'name' wants to invite 'sd's guild for alliance *------------------------------------------*/ int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name) { @@ -7278,7 +7334,7 @@ int clif_guild_allianceack(struct map_session_data *sd,int flag) return 0; } /*========================================== - * ギルド関係解消通知 + * Server tells client 'sd' that guild_id is either in or out of it's alliance list (depend on flag) *------------------------------------------*/ int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag) { @@ -7331,7 +7387,7 @@ int clif_guild_oppositionack(struct map_session_data *sd,int flag) }*/ /*========================================== - * ギルド解散通知 + * Server tells client 'sd' that guild broke because of 'flag' reason *------------------------------------------*/ int clif_guild_broken(struct map_session_data *sd,int flag) { @@ -7348,7 +7404,7 @@ int clif_guild_broken(struct map_session_data *sd,int flag) } /*========================================== - * エモーション + * Server tells all nearby clients of 'bl' to display emoticon number 'type' *------------------------------------------*/ void clif_emotion(struct block_list *bl,int type) { @@ -7363,7 +7419,7 @@ void clif_emotion(struct block_list *bl,int type) } /*========================================== - * トーキーボックス + * Server tells all clients nearby 'bl' that he stepped in a talkie box (and displays the message) *------------------------------------------*/ void clif_talkiebox(struct block_list* bl, const char* talkie) { @@ -7377,7 +7433,7 @@ void clif_talkiebox(struct block_list* bl, const char* talkie) } /*========================================== - * 結婚エフェクト + * Server tells bl and nearby clients to display marriage effect *------------------------------------------*/ void clif_wedding_effect(struct block_list *bl) { @@ -7390,7 +7446,7 @@ void clif_wedding_effect(struct block_list *bl) clif_send(buf, packet_len(0x1ea), bl, AREA); } /*========================================== - * ?なたに逢いたい使用時名前叫び + * Server tells client 'sd' to create a warp to call his partner (wedding skill) *------------------------------------------*/ void clif_callpartner(struct map_session_data *sd) @@ -7459,18 +7515,9 @@ void clif_parse_ReqMarriage(int fd, struct map_session_data *sd) /*========================================== * *------------------------------------------*/ -int clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len) +void clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len) { - int fd; - nullpo_ret(sd); - fd = sd->fd; - if (!fd || !len) return 0; //Disconnected player. - WFIFOHEAD(fd, len+5); - WFIFOW(fd, 0) = 0x17f; - WFIFOW(fd, 2) = len + 5; - memcpy(WFIFOP(fd,4), mes, len); - WFIFOSET(fd, WFIFOW(fd,2)); - return 1; + clif_disp_message(&sd->bl, mes, len, SELF); } /*========================================== @@ -7478,13 +7525,22 @@ int clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len) *------------------------------------------*/ void clif_disp_message(struct block_list* src, const char* mes, int len, enum send_target target) { - unsigned char buf[1024]; - if (!len) return; + unsigned char buf[256]; + + if( len == 0 ) + { + return; + } + else if( len > sizeof(buf)-5 ) + { + ShowWarning("clif_disp_message: Truncated message '%s' (len=%d, max=%d, aid=%d).\n", mes, len, sizeof(buf)-5, src->id); + len = sizeof(buf)-5; + } + WBUFW(buf, 0) = 0x17f; WBUFW(buf, 2) = len + 5; - memcpy(WBUFP(buf,4), mes, len); + safestrncpy((char*)WBUFP(buf,4), mes, len+1); clif_send(buf, WBUFW(buf,2), src, target); - return; } /*========================================== @@ -7556,41 +7612,41 @@ void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, } /*========================================== - * Wis拒否許可応答 + * ? Unknown functionality : not called anywhere *------------------------------------------*/ -int clif_wisexin(struct map_session_data *sd,int type,int flag) -{ - int fd; - - nullpo_ret(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xd1)); - WFIFOW(fd,0)=0xd1; - WFIFOB(fd,2)=type; - WFIFOB(fd,3)=flag; - WFIFOSET(fd,packet_len(0xd1)); - - return 0; -} +//int clif_wisexin(struct map_session_data *sd,int type,int flag) +//{ +// int fd; +// +// nullpo_ret(sd); +// +// fd=sd->fd; +// WFIFOHEAD(fd,packet_len(0xd1)); +// WFIFOW(fd,0)=0xd1; +// WFIFOB(fd,2)=type; +// WFIFOB(fd,3)=flag; +// WFIFOSET(fd,packet_len(0xd1)); +// +// return 0; +//} /*========================================== - * Wis全拒否許可応答 + * ? Unknown functionality : not called anywhere *------------------------------------------*/ -int clif_wisall(struct map_session_data *sd,int type,int flag) -{ - int fd; - - nullpo_ret(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xd2)); - WFIFOW(fd,0)=0xd2; - WFIFOB(fd,2)=type; - WFIFOB(fd,3)=flag; - WFIFOSET(fd,packet_len(0xd2)); - - return 0; -} +//int clif_wisall(struct map_session_data *sd,int type,int flag) +//{ +// int fd; +// +// nullpo_ret(sd); +// +// fd=sd->fd; +// WFIFOHEAD(fd,packet_len(0xd2)); +// WFIFOW(fd,0)=0xd2; +// WFIFOB(fd,2)=type; +// WFIFOB(fd,3)=flag; +// WFIFOSET(fd,packet_len(0xd2)); +// +// return 0; +//} /*========================================== * Play a BGM! [Rikter/Yommy] @@ -7609,7 +7665,8 @@ void clif_playBGM(struct map_session_data* sd, const char* name) } /*========================================== - * サウンドエフェクト + * Server tells 'bl' to play a .wav music file in client's /wav/ folder named 'name' + * functionality of 'type' is unclear. it's normally sent as '0' *------------------------------------------*/ void clif_soundeffect(struct map_session_data* sd, struct block_list* bl, const char* name, int type) { @@ -7707,12 +7764,12 @@ int clif_messagecolor(struct block_list* bl, unsigned long color, const char* ms } // messages (from mobs/npcs) [Valaris] -int clif_message(struct block_list* bl, const char* msg) +void clif_message(struct block_list* bl, const char* msg) { unsigned short msg_len = strlen(msg) + 1; uint8 buf[256]; - nullpo_ret(bl); + nullpo_retv(bl); if( msg_len > sizeof(buf)-8 ) { @@ -7723,11 +7780,9 @@ int clif_message(struct block_list* bl, const char* msg) WBUFW(buf,0) = 0x8d; WBUFW(buf,2) = msg_len + 8; WBUFL(buf,4) = bl->id; - memcpy(WBUFP(buf,8), msg, msg_len); + safestrncpy((char*)WBUFP(buf,8), msg, msg_len); clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC); - - return 0; } // refresh the client's screen, getting rid of any effects @@ -8001,29 +8056,27 @@ void clif_slide(struct block_list *bl, int x, int y) /*------------------------------------------ * @me command by lordalfa, rewritten implementation by Skotlex *------------------------------------------*/ -int clif_disp_overhead(struct map_session_data *sd, const char* mes) +void clif_disp_overhead(struct map_session_data *sd, const char* mes) { unsigned char buf[256]; //This should be more than sufficient, the theorical max is CHAT_SIZE + 8 (pads and extra inserted crap) int len_mes = strlen(mes)+1; //Account for \0 - if (len_mes + 8 >= 256) { + if (len_mes > sizeof(buf)-8) { ShowError("clif_disp_overhead: Message too long (length %d)\n", len_mes); - len_mes = 247; //Trunk it to avoid problems. + len_mes = sizeof(buf)-8; //Trunk it to avoid problems. } // send message to others WBUFW(buf,0) = 0x8d; WBUFW(buf,2) = len_mes + 8; // len of message + 8 (command+len+id) WBUFL(buf,4) = sd->bl.id; - memcpy(WBUFP(buf,8), mes, len_mes); + safestrncpy((char*)WBUFP(buf,8), mes, len_mes); clif_send(buf, WBUFW(buf,2), &sd->bl, AREA_CHAT_WOC); // send back message to the speaker WBUFW(buf,0) = 0x8e; WBUFW(buf, 2) = len_mes + 4; - memcpy(WBUFP(buf,4), mes, len_mes); + safestrncpy((char*)WBUFP(buf,4), mes, len_mes); clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); - - return 0; } /*========================== @@ -8457,7 +8510,7 @@ static int clif_guess_PacketVer(int fd, int get_previous, int *error) // ------------ // clif_parse_* // ------------ -// パケット読み取って色々操作 +// Parses incoming (player) connection /*========================================== * *------------------------------------------*/ @@ -8546,8 +8599,9 @@ void clif_parse_WantToConnection(int fd, TBL_PC* sd) } /*========================================== - * 007d クライアント側マップ読み込み完了 - * map侵入時に必要なデータを全て送りつける + * 007d : Server/Client tells that he is able to proceed + * This is run by both server (from pc.c) and client (on map load/refresh + * (teleport/warping in same map also triggers this) *------------------------------------------*/ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) { @@ -8711,9 +8765,6 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) if (sd->sc.option&OPTION_RIDING) clif_status_load(&sd->bl, SI_RIDING, 1); - if (sd->sc.option&OPTION_DRAGON) - clif_status_load(&sd->bl, SI_RIDING, 1); - if(sd->status.manner < 0) sc_start(&sd->bl,SC_NOCHAT,100,0,0); @@ -8932,7 +8983,7 @@ void clif_parse_WalkToXY(int fd, struct map_session_data *sd) return; } - if (sd->sc.opt1 && (sd->sc.opt1 == OPT1_STONEWAIT || sd->sc.opt1 == OPT1_BURNING)) + if (sd->sc.opt1 && sd->sc.opt1 == OPT1_STONEWAIT) ; //You CAN walk on this OPT1 value. else if( sd->progressbar.npc_id ) clif_progressbar_abort(sd); @@ -9021,9 +9072,11 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd) const char* text = (char*)RFIFOP(fd,4); int textlen = RFIFOW(fd,2) - 4; - char *name, *message; + char *name, *message, *fakename = NULL; int namelen, messagelen; + bool is_fake; + // validate packet and retrieve name and message if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) ) return; @@ -9040,21 +9093,35 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd) return; sd->cantalk_tick = gettick() + battle_config.min_chat_delay; } - + /** + * Fake Name Design by FatalEror (bug report #9) + **/ + if( ( is_fake = ( sd->fakename[0] ) ) ) { + fakename = (char*) malloc(strlen(sd->fakename)+messagelen+3); + strcpy(fakename, sd->fakename); + strcat(fakename, " : "); + strcat(fakename, message); + textlen = strlen(fakename) + 1; + } // send message to others (using the send buffer for temp. storage) WFIFOHEAD(fd, 8 + textlen); WFIFOW(fd,0) = 0x8d; WFIFOW(fd,2) = 8 + textlen; WFIFOL(fd,4) = sd->bl.id; - safestrncpy((char*)WFIFOP(fd,8), text, textlen); + safestrncpy((char*)WFIFOP(fd,8), is_fake ? fakename : 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 - memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); - WFIFOW(fd,0) = 0x8e; + if( is_fake ) { + WFIFOW(fd,0) = 0x8e; + WFIFOW(fd,2) = textlen + 4; + safestrncpy((char*)WFIFOP(fd,4), fakename, textlen); + } else { + memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2)); + WFIFOW(fd,0) = 0x8e; + } WFIFOSET(fd, WFIFOW(fd,2)); - #ifdef PCRE_SUPPORT // trigger listening npcs map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl); @@ -9182,8 +9249,7 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, if (sd->sc.count && (sd->sc.data[SC_TRICKDEAD] || sd->sc.data[SC_AUTOCOUNTER] || - sd->sc.data[SC_BLADESTOP] || - sd->sc.data[SC_DEATHBOUND])) + sd->sc.data[SC_BLADESTOP])) return; pc_stop_walking(sd, 1); @@ -9229,7 +9295,7 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, return; } - if (sd->ud.skilltimer != INVALID_TIMER || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING)) + if (sd->ud.skilltimer != INVALID_TIMER || sd->sc.opt1) break; if (sd->sc.count && ( @@ -9454,7 +9520,7 @@ void clif_parse_Broadcast(int fd, struct map_session_data* sd) return; // as the length varies depending on the command used, just block unreasonably long strings - mes_len_check(msg, len, CHAT_SIZE_MAX); + len = mes_len_check(msg, len, CHAT_SIZE_MAX); intif_broadcast(msg, len, 0); @@ -9525,7 +9591,6 @@ void clif_parse_DropItem(int fd, struct map_session_data *sd) if (sd->sc.count && ( sd->sc.data[SC_AUTOCOUNTER] || sd->sc.data[SC_BLADESTOP] || - sd->sc.data[SC_DEATHBOUND] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOITEM) )) break; @@ -9552,7 +9617,7 @@ void clif_parse_UseItem(int fd, struct map_session_data *sd) return; } - if (sd->sc.opt1 > 0 && sd->sc.opt1 != OPT1_STONEWAIT && sd->sc.opt1 != OPT1_BURNING) + if (sd->sc.opt1 > 0 && sd->sc.opt1 != OPT1_STONEWAIT) return; //This flag enables you to use items while in an NPC. [Skotlex] @@ -9591,7 +9656,7 @@ void clif_parse_EquipItem(int fd,struct map_session_data *sd) if(sd->npc_id) { if (sd->npc_id != sd->npc_item_flag) return; - } else if (sd->state.storage_flag || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING)) + } else if (sd->state.storage_flag || sd->sc.opt1) ; //You can equip/unequip stuff while storage is open/under status changes else if (pc_cant_act(sd)) return; @@ -9840,7 +9905,7 @@ static void clif_noask_sub(struct map_session_data *src, struct map_session_data } /*========================================== - * 取引要請を相手に送る + * Client tells server to send a trade request to char id RFIFOL(fd,2) *------------------------------------------*/ void clif_parse_TradeRequest(int fd,struct map_session_data *sd) { @@ -9867,7 +9932,7 @@ void clif_parse_TradeRequest(int fd,struct map_session_data *sd) } /*========================================== - * 取引要請 + * Client tells server he replied to a trade request sent to him *------------------------------------------*/ void clif_parse_TradeAck(int fd,struct map_session_data *sd) { @@ -9875,7 +9940,7 @@ void clif_parse_TradeAck(int fd,struct map_session_data *sd) } /*========================================== - * アイテム追加 + * Client tells server to add RFIFOL(fd,4) quantity of item index RFIFOW(fd,2) *------------------------------------------*/ void clif_parse_TradeAddItem(int fd,struct map_session_data *sd) { @@ -9889,7 +9954,7 @@ void clif_parse_TradeAddItem(int fd,struct map_session_data *sd) } /*========================================== - * アイテム追加完了(ok押し) + * Client tells server player he is done adding items to his trade window *------------------------------------------*/ void clif_parse_TradeOk(int fd,struct map_session_data *sd) { @@ -9897,7 +9962,7 @@ void clif_parse_TradeOk(int fd,struct map_session_data *sd) } /*========================================== - * 取引キャンセル + * Client tells server player cancelled the trade *------------------------------------------*/ void clif_parse_TradeCancel(int fd,struct map_session_data *sd) { @@ -9905,7 +9970,7 @@ void clif_parse_TradeCancel(int fd,struct map_session_data *sd) } /*========================================== - * 取引許諾(trade押し) + * Client tells server player 'locked' the trade screen (can't add/remove items) *------------------------------------------*/ void clif_parse_TradeCommit(int fd,struct map_session_data *sd) { @@ -9921,7 +9986,7 @@ void clif_parse_StopAttack(int fd,struct map_session_data *sd) } /*========================================== - * カートへアイテムを移す + * Client tells server player dragged (RFIFOL(fd,4))x of item idx RIFOFW(fd,2)-2 to cart *------------------------------------------*/ void clif_parse_PutItemToCart(int fd,struct map_session_data *sd) { @@ -9932,7 +9997,7 @@ void clif_parse_PutItemToCart(int fd,struct map_session_data *sd) pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); } /*========================================== - * カートからアイテムを出す + * Client tells server to take y (RFIFOL(fd,4)) amount of item (idx:RFIFOW(fd,2)-2) from cart and add to inventory *------------------------------------------*/ void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd) { @@ -9942,16 +10007,18 @@ void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd) } /*========================================== - * 付属品(鷹,ペコ,カート)をはずす + * Client tells server the user hit the 'OFF' button in the equip window (appears when mounting, with falcon, etc) *------------------------------------------*/ void clif_parse_RemoveOption(int fd,struct map_session_data *sd) { - //Can only remove Cart/Riding/Falcon. - pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON)); + /** + * Attempts to remove these options when this function is called (will remove all available) + **/ + pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR)); } /*========================================== - * チェンジカート + * Client tells server the user selected cart type 'type', comes from cart selection screen (Change Cart Skill) *------------------------------------------*/ void clif_parse_ChangeCart(int fd,struct map_session_data *sd) { @@ -9971,7 +10038,7 @@ void clif_parse_ChangeCart(int fd,struct map_session_data *sd) } /*========================================== - * ステータスアップ + * Client tells Server to process a /str, /vit, etc(others) *------------------------------------------*/ void clif_parse_StatusUp(int fd,struct map_session_data *sd) { @@ -9979,7 +10046,7 @@ void clif_parse_StatusUp(int fd,struct map_session_data *sd) } /*========================================== - * スキルレベルアップ + * Client tells server to level up skill (RFIFOW(fd,2)) by 1 *------------------------------------------*/ void clif_parse_SkillUp(int fd,struct map_session_data *sd) { @@ -10059,7 +10126,7 @@ static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct } /*========================================== - * スキル使用(ID指定) + * Client tells server he'd like to use skill of id 'skillnum' and level 'skilllv' on 'target_id' *------------------------------------------*/ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) { @@ -10115,7 +10182,7 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) { if( sd->skillitem != skillnum ) { - clif_skill_fail(sd, skillnum, 0x04, 0); + clif_skill_fail(sd, skillnum, 4, 0); return; } } @@ -10166,7 +10233,7 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) } /*========================================== - * スキル使用(場所指定) + * Client tells server he'd like to use AoE skill id 'skillnum' of level 'skilllv' on 'x','y' location *------------------------------------------*/ void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, short skilllv, short skillnum, short x, short y, int skillmoreinfo) { @@ -10276,7 +10343,7 @@ void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd) ); } /*========================================== - * スキル使用(map指定) + * (?) I *think* this one is for skills cast on self, not entirely sure *------------------------------------------*/ void clif_parse_UseSkillMap(int fd, struct map_session_data* sd) { @@ -10297,7 +10364,7 @@ void clif_parse_UseSkillMap(int fd, struct map_session_data* sd) skill_castend_map(sd,skill_num,map_name); } /*========================================== - * メモ要求 + * Client tells server he did '/memo' *------------------------------------------*/ void clif_parse_RequestMemo(int fd,struct map_session_data *sd) { @@ -10305,13 +10372,12 @@ void clif_parse_RequestMemo(int fd,struct map_session_data *sd) pc_memo(sd,-1); } /*========================================== - * アイテム合成 + * Client tells server he selected something from a crafting window (e.g. pharmacy) *------------------------------------------*/ void clif_parse_ProduceMix(int fd,struct map_session_data *sd) { - if (sd->menuskill_id != AM_PHARMACY) + if( sd->menuskill_id != -1 && sd->menuskill_id != AM_PHARMACY && sd->menuskill_id != RK_RUNEMASTERY ) 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); @@ -10344,7 +10410,7 @@ void clif_parse_Cooking(int fd,struct map_session_data *sd) sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * 武器修理 + * Client tells server he selected something in his 'repair item list' *------------------------------------------*/ void clif_parse_RepairItem(int fd, struct map_session_data *sd) { @@ -10449,7 +10515,7 @@ void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd) } /*========================================== - * アイテム鑑定 + * Client tells server he selected something in his 'magnifier item list' *------------------------------------------*/ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) { @@ -10466,23 +10532,35 @@ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * 矢作成 + * Client tells server he selected something in his 'arrow crafting list' *------------------------------------------*/ void clif_parse_SelectArrow(int fd,struct map_session_data *sd) { - if (sd->menuskill_id != AC_MAKINGARROW) - 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); sd->menuskill_val = sd->menuskill_id = 0; return; } - skill_arrow_create(sd,RFIFOW(fd,2)); + switch( sd->menuskill_id ) { + case AC_MAKINGARROW: + skill_arrow_create(sd,RFIFOW(fd,2)); + break; + case WL_READING_SB: + skill_spellbook(sd,RFIFOW(fd,2)); + break; + case GC_POISONINGWEAPON: + skill_poisoningweapon(sd,RFIFOW(fd,2)); + break; + case NC_MAGICDECOY: + skill_magicdecoy(sd,RFIFOW(fd,2)); + break; + } + sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * オートスペル受信 + * Client tells server he selected something in his 'autospell skill list' *------------------------------------------*/ void clif_parse_AutoSpell(int fd,struct map_session_data *sd) { @@ -10492,7 +10570,7 @@ void clif_parse_AutoSpell(int fd,struct map_session_data *sd) sd->menuskill_val = sd->menuskill_id = 0; } /*========================================== - * カード使用 + * Client tells server he clicked on a card item, requests the can-add-to list *------------------------------------------*/ void clif_parse_UseCard(int fd,struct map_session_data *sd) { @@ -10501,7 +10579,7 @@ void clif_parse_UseCard(int fd,struct map_session_data *sd) clif_use_card(sd,RFIFOW(fd,2)-2); } /*========================================== - * カード挿入装備選択 + * Client tells server he selected something in his 'carding list' (the one that lists all items you got that can receive that card) *------------------------------------------*/ void clif_parse_InsertCard(int fd,struct map_session_data *sd) { @@ -10511,7 +10589,7 @@ void clif_parse_InsertCard(int fd,struct map_session_data *sd) } /*========================================== - * 0193 キャラID名前引き + * 0193 : Client asks server for nick reference to a specific char id *------------------------------------------*/ void clif_parse_SolveCharName(int fd, struct map_session_data *sd) { @@ -10558,7 +10636,7 @@ void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd) return; // as the length varies depending on the command used, just block unreasonably long strings - mes_len_check(msg, len, CHAT_SIZE_MAX); + len = mes_len_check(msg, len, CHAT_SIZE_MAX); clif_broadcast(&sd->bl, msg, len, 0, ALL_SAMEMAP); @@ -10570,7 +10648,7 @@ void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd) } /*========================================== - * カプラ倉庫へ入れる + * Client tells server to move (item_amount) quantity of item idx (item_index) from inventory to storage *------------------------------------------*/ void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) { @@ -10592,7 +10670,7 @@ void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) } /*========================================== - * カプラ倉庫から出す + * Client tells server to move (item_amount) quantity of item idx (item_index) from storage to inventory *------------------------------------------*/ void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) { @@ -10609,7 +10687,7 @@ void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) } /*========================================== - * カプラ倉庫へカートから入れる + * Client tells server to move RFIFOL(fd,4) quantity of item idx RFIFOW(fd,2) from cart to storage *------------------------------------------*/ void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) { @@ -10626,7 +10704,7 @@ void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) } /*========================================== - * カプラ倉庫から出す + * Client tells server to move RFIFOL(fd,4) quantity of item idx RFIFOW(fd,2) from storage to cart *------------------------------------------*/ void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) { @@ -10643,7 +10721,7 @@ void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) } /*========================================== - * カプラ倉庫を閉じる + * Client tells server to close the kafra *------------------------------------------*/ void clif_parse_CloseKafra(int fd, struct map_session_data *sd) { @@ -10774,7 +10852,7 @@ void clif_parse_ReplyPartyInvite2(int fd,struct map_session_data *sd) } /*========================================== - * パーティ脱退要求 + * Client tells server to remove itself from it's party *------------------------------------------*/ void clif_parse_LeaveParty(int fd, struct map_session_data *sd) { @@ -10787,7 +10865,7 @@ void clif_parse_LeaveParty(int fd, struct map_session_data *sd) } /*========================================== - * パーティ除名要求 + * Client tells server to remove player account id RFIFOL(fd,2) with char name RFIFOP(fd,6) from his party *------------------------------------------*/ void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) { @@ -10800,7 +10878,11 @@ void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) } /*========================================== - * パーティ設定変更要求 + * Client tells server to change it's party configuration + * - clients before 20090603 + * -- It only may toggle exp sharing + * - 20090603 or newer + * -- It may toggle exp (RFIFOW(fd,2) and item sharing options (RFIFOB(fd,6) and RFIFOB(fd,7)) *------------------------------------------*/ void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) { @@ -11075,10 +11157,14 @@ void clif_parse_OpenVending(int fd, struct map_session_data* sd) if( sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM ) return; - if( map[sd->bl.m].flag.novending || map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOVENDING) ) { + if( map[sd->bl.m].flag.novending ) { clif_displaymessage (sd->fd, msg_txt(276)); // "You can't open a shop on this map" return; } + if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOVENDING) ) { + clif_displaymessage (sd->fd, msg_txt(204)); // "You can't open a shop on this cell." + return; + } if( message[0] == '\0' ) // invalid input return; @@ -11250,7 +11336,7 @@ void clif_parse_GuildChangeNotice(int fd, struct map_session_data* sd) } /*========================================== - * ギルド勧誘 + * Client tells server to invite account id RFIFOL(fd,2) to his guild *------------------------------------------*/ void clif_parse_GuildInvite(int fd,struct map_session_data *sd) { @@ -11274,7 +11360,8 @@ void clif_parse_GuildInvite(int fd,struct map_session_data *sd) } /*========================================== - * ギルド勧誘返信 + * Client tells server his reply on the request from guild ID RFIFOL(fd,2), + * - based on RFIFOB(fd,6) which is either 1 (accept) or 0 (reject) *------------------------------------------*/ void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) { @@ -11282,7 +11369,7 @@ void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) } /*========================================== - * ギルド脱退 + * Client tells server he wants to leave his current guild *------------------------------------------*/ void clif_parse_GuildLeave(int fd,struct map_session_data *sd) { @@ -11350,7 +11437,7 @@ void clif_parse_GuildMessage(int fd, struct map_session_data* sd) } /*========================================== - * ギルド同盟要求 + * Client tells server he'd like to send a alliance request to account id RFIFOL(fd,2) *------------------------------------------*/ void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) { @@ -11377,7 +11464,8 @@ void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) } /*========================================== - * ギルド同盟要求返信 + * Client tells server his response to the alliance request from, + * Guild ID RFIFOL(fd,2) based on RFIFOL(fd,6) which is 1 (accepted) or 0 (rejected) *------------------------------------------*/ void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) { @@ -11385,7 +11473,8 @@ void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) } /*========================================== - * ギルド関係解消 + * Client tells server he'd like to delete alliance from guild ID RFIFOL(fd,2), + * RFIFOL(fd,6) returns a 1 or 0 flag but apparently it is no longer used *------------------------------------------*/ void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) { @@ -11401,7 +11490,7 @@ void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) } /*========================================== - * ギルド敵対 + * Client tells server he'd like his guild to be set antagonist of account id RFIFOL(fd,2)'s guild *------------------------------------------*/ void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) { @@ -11428,7 +11517,7 @@ void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) } /*========================================== - * ギルド解散 + * Client tells server he'd like to break (delete) his own guild *------------------------------------------*/ void clif_parse_GuildBreak(int fd, struct map_session_data *sd) { @@ -11475,7 +11564,7 @@ void clif_parse_ChangePetName(int fd, struct map_session_data *sd) } /*========================================== - * /kill <???> + * /kill <account_id> * (or right click menu for GM "(name) force to quit") * S 00cc <id>.L *------------------------------------------*/ @@ -11996,7 +12085,7 @@ void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd) } /*========================================== - * Wis拒否リスト + * Client tells server he'd like the server to list him his ignore list *------------------------------------------*/ void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd) { @@ -12014,7 +12103,7 @@ void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd) } /*========================================== - * スパノビの/doridoriによるSPR2倍 + * Client tells server he did a /doridori *------------------------------------------*/ void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd) { @@ -12048,7 +12137,7 @@ void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *sd) if( ( sd->class_&MAPID_UPPERMASK ) == MAPID_SUPER_NOVICE ) { unsigned int next = pc_nextbaseexp(sd); - + if( next == 0 ) next = pc_thisbaseexp(sd); if( next ) { int percent = (int)( ( (float)sd->status.base_exp/(float)next )*1000. ); @@ -13142,6 +13231,12 @@ void clif_Auction_results(struct map_session_data *sd, short count, short pages, WFIFOSET(fd,WFIFOW(fd,2)); } + +/// Result from request to add an item (ZC_ACK_AUCTION_ADD_ITEM) +/// 0256 <index>.W <result>.B +/// result: +/// 0 = success +/// 1 = failure static void clif_Auction_setitem(int fd, int index, bool fail) { WFIFOHEAD(fd,packet_len(0x256)); @@ -13151,6 +13246,13 @@ static void clif_Auction_setitem(int fd, int index, bool fail) WFIFOSET(fd,packet_len(0x256)); } + +/// Request to initialize 'new auction' data (CZ_AUCTION_CREATE) +/// 024b <type>.W +/// type: +/// 0 = create (any other action in auction window) +/// 1 = cancel (cancel pressed on register tab) +/// ? = junk, uninitialized value (ex. when switching between list filters) void clif_parse_Auction_cancelreg(int fd, struct map_session_data *sd) { if( sd->auction.amount > 0 ) @@ -13159,6 +13261,9 @@ void clif_parse_Auction_cancelreg(int fd, struct map_session_data *sd) sd->auction.amount = 0; } + +/// Request to add an item to the action (CZ_AUCTION_ADD_ITEM) +/// 024c <index>.W <count>.L void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) { int idx = RFIFOW(fd,2) - 2; @@ -13174,8 +13279,8 @@ void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) return; } - if( amount != 1 || amount < 0 || amount > sd->status.inventory[idx].amount ) - { // By client, amount is allways set to 1. Maybe this is a future implementation. + if( amount != 1 || amount > sd->status.inventory[idx].amount ) + { // By client, amount is always set to 1. Maybe this is a future implementation. ShowWarning("Character %s trying to set invalid amount in auctions.\n", sd->status.name); return; } @@ -13224,7 +13329,7 @@ void clif_Auction_message(int fd, unsigned char flag) /// result: /// 0 = You have ended the auction /// 1 = You cannot end the auction -/// 2 = Bid number is incorrect +/// 2 = Auction ID is incorrect void clif_Auction_close(int fd, unsigned char flag) { WFIFOHEAD(fd,packet_len(0x25e)); @@ -13233,6 +13338,9 @@ void clif_Auction_close(int fd, unsigned char flag) WFIFOSET(fd,packet_len(0x25e)); } + +/// Request to add an auction (CZ_AUCTION_ADD) +/// 024d <now money>.L <max money>.L <delete hour>.W void clif_parse_Auction_register(int fd, struct map_session_data *sd) { struct auction_data auction; @@ -13277,9 +13385,9 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd) auction.auction_id = 0; auction.seller_id = sd->status.char_id; - safestrncpy(auction.seller_name, sd->status.name, NAME_LENGTH); + safestrncpy(auction.seller_name, sd->status.name, sizeof(auction.seller_name)); auction.buyer_id = 0; - memset(&auction.buyer_name, '\0', NAME_LENGTH); + memset(auction.buyer_name, '\0', sizeof(auction.buyer_name)); if( sd->status.inventory[sd->auction.index].nameid == 0 || sd->status.inventory[sd->auction.index].amount < sd->auction.amount ) { @@ -13293,7 +13401,7 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd) return; } - safestrncpy(auction.item_name, item->jname, ITEM_NAME_LENGTH); + safestrncpy(auction.item_name, item->jname, sizeof(auction.item_name)); auction.type = item->type; memcpy(&auction.item, &sd->status.inventory[sd->auction.index], sizeof(struct item)); auction.item.amount = 1; @@ -13309,6 +13417,9 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd) } } + +/// Cancels an auction (CZ_AUCTION_ADD_CANCEL) +/// 024e <auction id>.L void clif_parse_Auction_cancel(int fd, struct map_session_data *sd) { unsigned int auction_id = RFIFOL(fd,2); @@ -13316,6 +13427,9 @@ void clif_parse_Auction_cancel(int fd, struct map_session_data *sd) intif_Auction_cancel(sd->status.char_id, auction_id); } + +/// Closes an auction (CZ_AUCTION_REQ_MY_SELL_STOP) +/// 025d <auction id>.L void clif_parse_Auction_close(int fd, struct map_session_data *sd) { unsigned int auction_id = RFIFOL(fd,2); @@ -13323,6 +13437,9 @@ void clif_parse_Auction_close(int fd, struct map_session_data *sd) intif_Auction_close(sd->status.char_id, auction_id); } + +/// Places a bid on an auction (CZ_AUCTION_BUY) +/// 024f <auction id>.L <money>.L void clif_parse_Auction_bid(int fd, struct map_session_data *sd) { unsigned int auction_id = RFIFOL(fd,2); @@ -13345,23 +13462,34 @@ void clif_parse_Auction_bid(int fd, struct map_session_data *sd) } } -/*------------------------------------------ - * Auction Search - * S 0251 <search type>.w <search price>.l <search text>.24B <page number>.w - * Search Type: 0 Armor 1 Weapon 2 Card 3 Misc 4 By Text 5 By Price 6 Sell 7 Buy - *------------------------------------------*/ + +/// Auction Search (CZ_AUCTION_ITEM_SEARCH) +/// 0251 <search type>.W <auction id>.L <search text>.24B <page number>.W +/// search type: +/// 0 = armor +/// 1 = weapon +/// 2 = card +/// 3 = misc +/// 4 = name search +/// 5 = auction id search void clif_parse_Auction_search(int fd, struct map_session_data* sd) { char search_text[NAME_LENGTH]; short type = RFIFOW(fd,2), page = RFIFOW(fd,32); - int price = RFIFOL(fd,4); + int price = RFIFOL(fd,4); // FIXME: bug #5071 clif_parse_Auction_cancelreg(fd, sd); - safestrncpy(search_text, (char*)RFIFOP(fd,8), NAME_LENGTH); + safestrncpy(search_text, (char*)RFIFOP(fd,8), sizeof(search_text)); intif_Auction_requestlist(sd->status.char_id, type, price, search_text, page); } + +/// Requests list of own currently active bids or auctions (CZ_AUCTION_REQ_MY_INFO) +/// 025c <type>.W +/// type: +/// 0 = sell (own auctions) +/// 1 = buy (own bids) void clif_parse_Auction_buysell(int fd, struct map_session_data* sd) { short type = RFIFOW(fd,2) + 6; @@ -14288,7 +14416,7 @@ void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, b /// 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). +/// 3: Decremental counter (1 tick/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) @@ -14813,19 +14941,6 @@ void clif_search_store_info_click_ack(struct map_session_data* sd, short x, shor WFIFOSET(fd,packet_len(0x83d)); } -// Correct packet for RK_MILLENIUMSHIELD. Shows spirit spheres. -void clif_millenniumshield(struct map_session_data *sd, int num) -{ -#if PACKETVER >= 20081217 - unsigned char buf[10]; - - WBUFW(buf,0)=0x440; - WBUFL(buf,2)=sd->bl.id; - WBUFW(buf,6)=num; - WBUFW(buf,8)=0; - clif_send(buf,packet_len(0x440),&sd->bl,AREA); -#endif -} /// Parse function for packet debugging void clif_parse_debug(int fd,struct map_session_data *sd) @@ -14858,6 +14973,183 @@ void clif_parse_debug(int fd,struct map_session_data *sd) ShowDump(RFIFOP(fd,0), packet_len); } +/** + * Rune Knight + **/ +void clif_millenniumshield(struct map_session_data *sd, short shields ) { +#if PACKETVER >= 20081217 + unsigned char buf[10]; + + WBUFW(buf,0) = 0x440; + WBUFL(buf,2) = sd->bl.id; + WBUFW(buf,6) = shields; + WBUFW(buf,8) = 0; + clif_send(buf,packet_len(0x440),&sd->bl,AREA); +#endif +} +/** + * Warlock + **/ +/*========================================== + * Spellbook list [LimitLine/3CeAM] + *------------------------------------------*/ +int clif_spellbook_list(struct map_session_data *sd) +{ + int i, c; + int fd; + + nullpo_ret(sd); + + fd = sd->fd; + WFIFOHEAD(fd, 8 * 8 + 8); + WFIFOW(fd,0) = 0x1ad; + + for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) + { + if( itemdb_is_spellbook(sd->status.inventory[i].nameid) ) + { + WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + c ++; + } + } + + if( c > 0 ) + { + WFIFOW(fd,2) = c * 2 + 4; + WFIFOSET(fd, WFIFOW(fd, 2)); + sd->menuskill_id = WL_READING_SB; + sd->menuskill_val = c; + } + else + status_change_end(&sd->bl,SC_STOP,-1); + + return 1; +} +/** + * Mechanic + **/ +/*========================================== + * Magic Decoy Material List + *------------------------------------------*/ +int clif_magicdecoy_list(struct map_session_data *sd, int skill_lv, short x, short y) { + int i, c; + int fd; + + nullpo_ret(sd); + + fd = sd->fd; + WFIFOHEAD(fd, 8 * 8 + 8); + WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil] + + for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) { + if( itemdb_is_element(sd->status.inventory[i].nameid) ) { + WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + c ++; + } + } + if( c > 0 ) { + sd->menuskill_id = NC_MAGICDECOY; + sd->menuskill_val = skill_lv; + sd->sc.comet_x = x; + sd->sc.comet_y = y; + WFIFOW(fd,2) = c * 2 + 4; + WFIFOSET(fd, WFIFOW(fd, 2)); + } else { + clif_skill_fail(sd,NC_MAGICDECOY,0,0); + return 0; + } + + return 1; +} +/** + * Guilotine Cross + **/ +/*========================================== + * Guillotine Cross Poisons List + *------------------------------------------*/ +int clif_poison_list(struct map_session_data *sd, int skill_lv) { + int i, c; + int fd; + + nullpo_ret(sd); + + fd = sd->fd; + WFIFOHEAD(fd, 8 * 8 + 8); + WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil] + + for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) { + if( itemdb_is_poison(sd->status.inventory[i].nameid) ) { + WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + c ++; + } + } + if( c > 0 ) { + sd->menuskill_id = GC_POISONINGWEAPON; + sd->menuskill_val = skill_lv; + WFIFOW(fd,2) = c * 2 + 4; + WFIFOSET(fd, WFIFOW(fd, 2)); + } else { + clif_skill_fail(sd,GC_POISONINGWEAPON,0x2b,0); + return 0; + } + + return 1; +} +/** + * Sends a new status without a tick (currently used by the new mounts) + **/ +int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3) { + unsigned char buf[32]; + + nullpo_ret(bl); + + WBUFW(buf,0)=0x043f; + WBUFW(buf,2)=type; + WBUFL(buf,4)=bl->id; + WBUFB(buf,8)=flag; + WBUFL(buf,9) = 0; + WBUFL(buf,13) = val1; + WBUFL(buf,17) = val2; + WBUFL(buf,21) = val3; + + clif_send(buf,packet_len(WBUFW(buf,0)),bl,AREA); + return 0; +} +//Notifies FD of ID's type +int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3) { + WFIFOHEAD(fd, packet_len(0x043f)); + WFIFOW(fd,0)=0x043f; + WFIFOW(fd,2)=type; + WFIFOL(fd,4)=id; + WFIFOB(fd,8)=flag; + WFIFOL(fd,9) = 0; + WFIFOL(fd,13) = val1; + WFIFOL(fd,17) = val2; + WFIFOL(fd,21) = val3; + WFIFOSET(fd, packet_len(0x043f)); + return 0; +} +// msgstringtable.txt +// 0x291 <line>.W +void clif_msgtable(int fd, int line) { + WFIFOHEAD(fd, packet_len(0x291)); + WFIFOW(fd, 0) = 0x291; + WFIFOW(fd, 2) = line; + WFIFOSET(fd, packet_len(0x291)); +} + +// msgstringtable.txt +// 0x7e2 <line>.W <value>.L +void clif_msgtable_num(int fd, int line, int num) { +#if PACKETVER >= 20090805 + WFIFOHEAD(fd, packet_len(0x7e2)); + WFIFOW(fd, 0) = 0x7e2; + WFIFOW(fd, 2) = line; + WFIFOL(fd, 4) = num; + WFIFOSET(fd, packet_len(0x7e2)); +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -15027,7 +15319,7 @@ int clif_parse(int fd) } /*========================================== - * パケットデータベース読み込み + * Reads packet_db.txt and setups its array reference *------------------------------------------*/ static int packetdb_readdb(void) { @@ -15157,7 +15449,7 @@ static int packetdb_readdb(void) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 25, //#0x0440 - 10, 4, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -15569,6 +15861,13 @@ static int packetdb_readdb(void) if(p2) *p2++=0; k = atoi(str2[j]); // if (packet_db[packet_ver][cmd].pos[j] != k && clif_config.prefer_packet_db) // not used for now + + if( j >= MAX_PACKET_POS ) + { + ShowError("Too many positions found for packet 0x%04x (max=%d).\n", cmd, MAX_PACKET_POS); + break; + } + packet_db[packet_ver][cmd].pos[j] = k; } } diff --git a/src/map/clif.h b/src/map/clif.h index 9d1207901..8b6271075 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -27,14 +27,18 @@ struct battleground_data; struct quest; struct party_booking_ad_info; #include <stdarg.h> -// packet DB -#define MAX_PACKET_DB 0x900 -#define MAX_PACKET_VER 26 + +enum +{// packet DB + MAX_PACKET_DB = 0x900, + MAX_PACKET_VER = 26, + MAX_PACKET_POS = 20, +}; struct s_packet_db { short len; void (*func)(int, struct map_session_data *); - short pos[20]; + short pos[MAX_PACKET_POS]; }; // packet_db[SERVER] is reserved for server use @@ -339,7 +343,7 @@ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst); void clif_skill_warppoint(struct map_session_data* sd, short skill_num, short skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4); void clif_skill_memomessage(struct map_session_data* sd, int type); void clif_skill_teleportmessage(struct map_session_data* sd, int type); -int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger); +int clif_skill_produce_mix_list(struct map_session_data *sd, int skillid, int trigger); void clif_cooking_list(struct map_session_data *sd, int trigger); int clif_produceeffect(struct map_session_data* sd,int flag,int nameid); @@ -357,7 +361,7 @@ void clif_bladestop(struct block_list* src, int dst_id, int active); void clif_changemapcell(int fd, int m, int x, int y, int type, enum send_target target); int clif_status_load(struct block_list *bl,int type, int flag); -int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick); +int clif_status_change(struct block_list *bl,int type,int flag,unsigned int tick,int val1, int val2, int val3); int clif_wis_message(int fd, const char* nick, const char* mes, int mes_len); int clif_wis_end(int fd,int flag); @@ -406,7 +410,7 @@ int clif_party_created(struct map_session_data *sd,int result); int clif_party_member_info(struct party_data *p, struct map_session_data *sd); int clif_party_info(struct party_data *p, struct map_session_data *sd); void clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd); -void clif_party_inviteack(struct map_session_data* sd, const char* nick, int flag); +void clif_party_inviteack(struct map_session_data* sd, const char* nick, int result); int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag); int clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int account_id, const char* name, int flag); int clif_party_message(struct party_data* p, int account_id, const char* mes, int len); @@ -437,7 +441,7 @@ int clif_guild_memberpositionchanged(struct guild *g,int idx); int clif_guild_emblem(struct map_session_data *sd,struct guild *g); void clif_guild_emblem_area(struct block_list* bl); int clif_guild_notice(struct map_session_data *sd,struct guild *g); -int clif_guild_message(struct guild *g,int account_id,const char *mes,int len); +void clif_guild_message(struct guild *g,int account_id,const char *mes,int len); int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv); int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name); int clif_guild_allianceack(struct map_session_data *sd,int flag); @@ -468,7 +472,7 @@ int clif_font(struct map_session_data *sd); // atcommand int clif_displaymessage(const int fd,const char* mes); -int clif_disp_onlyself(struct map_session_data *sd,const char *mes,int len); +void clif_disp_onlyself(struct map_session_data *sd,const char *mes,int len); void clif_disp_message(struct block_list* src, const char* mes, int len, enum send_target target); int clif_broadcast(struct block_list *bl, const char* mes, int len, int type, enum send_target target); void clif_MainChatMessage(const char* message); //luzza @@ -502,7 +506,7 @@ void clif_weather(int m); // [Valaris] int clif_specialeffect(struct block_list* bl, int type, enum send_target target); // special effects [Valaris] void clif_specialeffect_single(struct block_list* bl, int type, int fd); int clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg); // Mob/Npc color talk [SnakeDrak] -int clif_message(struct block_list *bl, const char* msg); // messages (from mobs/npcs) [Valaris] +void clif_message(struct block_list *bl, const char* msg); // messages (from mobs/npcs) [Valaris] int clif_GM_kickack(struct map_session_data *sd,int id); void clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd); @@ -510,7 +514,7 @@ void clif_manner_message(struct map_session_data* sd, uint32 type); void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, uint8 type); int clif_timedout(struct map_session_data *sd); -int clif_disp_overhead(struct map_session_data *sd, const char* mes); +void clif_disp_overhead(struct map_session_data *sd, const char* mes); void clif_get_weapon_view(struct map_session_data* sd, unsigned short *rhand, unsigned short *lhand); @@ -625,8 +629,29 @@ void clif_search_store_info_ack(struct map_session_data* sd); void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason); void clif_open_search_store_info(struct map_session_data* sd); void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y); - -// Third jobs -void clif_millenniumshield(struct map_session_data *sd, int num); - +/** + * 3CeAM + **/ +void clif_msgtable(int fd, int line); +void clif_msgtable_num(int fd, int line, int num); +/** + * Rune Knight + **/ +void clif_millenniumshield(struct map_session_data *sd, short shields ); +/** + * Warlock + **/ +int clif_spellbook_list(struct map_session_data *sd); +/** + * Mechanic + **/ +int clif_magicdecoy_list(struct map_session_data *sd, int skill_lv, short x, short y); +/** + * Guilotine Cross + **/ +int clif_poison_list(struct map_session_data *sd, int skill_lv); +/** + * [RRInd] for the new mounts + **/ +int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3); #endif /* _CLIF_H_ */ diff --git a/src/map/config/Core.h b/src/map/config/Core.h new file mode 100644 index 000000000..5073d5262 --- /dev/null +++ b/src/map/config/Core.h @@ -0,0 +1,28 @@ +#ifndef _RRCONFIGS_ +#define _RRCONFIGS_ +/** + * Ragnarok Resources Configuration File (http://ro-resources.net) + * The following settings are applied upon compiling the program, + * therefore any settings you disable will not even be added to the program + * making these settings the most performance-effiecient possible + **/ + +/** + * @INFO: RREmu Settings Core + * - For detailed guidance on these check http://trac.ro-resources.net/wiki/CoreConfiguration + **/ + +/** + * No settings past this point + **/ +#include "./Renewal.h" +#include "./Secure.h" +#include "./Skills/General.h" +/** + * Constants come last; so they process anything that could've been modified in early includes + **/ +#include "./Data/Const.h" +/** + * End of File + **/ +#endif diff --git a/src/map/config/Data/Const.h b/src/map/config/Data/Const.h new file mode 100644 index 000000000..31f68fdbc --- /dev/null +++ b/src/map/config/Data/Const.h @@ -0,0 +1,33 @@ +#ifndef _RRCONFIGS_CONST_ +#define _RRCONFIGS_CONST_ +/** + * Ragnarok Resources Configuration File (http://ro-resources.net) + * The following settings are applied upon compiling the program, + * therefore any settings you disable will not even be added to the program + * making these settings the most performance-effiecient possible + **/ + +/** + * @INFO: This file holds constants that aims at making code smoother and more efficient + */ + +/** + * "Constants" + **/ +#define CONST_CASTRATE_SCALE ( RECASTING ? RECASTING_VMIN : battle_config.castrate_dex_scale ) +#define CONST_CASTRATE_CALC ( RECASTING ? ((status_get_dex(bl)*2)+status_get_int(bl)) : status_get_dex(bl) ) + +/** + * "Sane Checks" to save you from compiling with cool bugs + **/ +#if SECURE_NPCTIMEOUT_INTERVAL <= 0 + #error SECURE_NPCTIMEOUT_INTERVAL should be at least 1 (1s) +#endif +#if SECURE_NPCTIMEOUT < 0 + #error SECURE_NPCTIMEOUT cannot be lower than 0 +#endif + +/** + * End of File + **/ +#endif diff --git a/src/map/config/Renewal.h b/src/map/config/Renewal.h new file mode 100644 index 000000000..a1ab30427 --- /dev/null +++ b/src/map/config/Renewal.h @@ -0,0 +1,53 @@ +#ifndef _RRCONFIGS_RE_ +#define _RRCONFIGS_RE_ +/** + * Ragnarok Resources Configuration File (http://ro-resources.net) + * The following settings are applied upon compiling the program, + * therefore any settings you disable will not even be added to the program + * making these settings the most performance-effiecient possible + **/ + +/** + * @INFO: This file holds general-purpose renewal settings, for class-specific ones check /src/map/config/Skills folder + **/ + +/** + * Game Server Mode + * @values: 1 or 0 + * 1 : renewal support, such as renewal-exclusive formulas + * -> Note some features may be enabled/disabled at this file despite this setting being ON + * 0 : renewal support disabled, use original formulas + **/ +#define RRMODE 1 + +/** + * Renewal Cast Time + * @values: 1 (enabled) or 0 (disabled) + * 1 : Cast Time is decreased by DEX*2+INT, 20% of the cast time is not reduced by stats, + * - for example, on a skill whose cast time is 10s, only 8s may be reduced. other 2s are + * - part of a "fixed cast time" that is only reduced by special items and skills (such as + * - Arch Bishop's Sacrament skill). + * 0 : the old cast time method, influenced by dex, items and skills. + **/ +#define RECASTING 1 + +/** + * Renewal Cast Time : Variable-Free + * - Value required for no variable cast time with stats. + * - Formula: (casterDex x 2) + (casterInt) + * Default: 530 + **/ +#define RECASTING_VMIN 530 + +/** + * Renewal Enchant Deadly Poison Change + * - In RE EDP no longer increases final damage by 400%. + * - it increases your weapon atk and your stat atk + * - it doesn't affect grimtooth + **/ +#define RE_EDP 1 + +/** + * End of File + **/ +#endif diff --git a/src/map/config/Secure.h b/src/map/config/Secure.h new file mode 100644 index 000000000..6f3da3b90 --- /dev/null +++ b/src/map/config/Secure.h @@ -0,0 +1,36 @@ +#ifndef _RRCONFIGS_SECURE_ +#define _RRCONFIGS_SECURE_ +/** + * Ragnarok Resources Configuration File (http://ro-resources.net) + * The following settings are applied upon compiling the program, + * therefore any settings you disable will not even be added to the program + * making these settings the most performance-effiecient possible + **/ + +/** + * @INFO: This file holds optional security settings + **/ + +/** + * Optional NPC Dialog Timer + * When enabled all npcs dialog will 'timeout' if user is on idle for longer than the amount of seconds allowed + * - On 'timeout' the npc dialog window changes it's next/menu to a 'close' button + * @values + * - ? : Desired idle time in seconds (e.g. 10) + * - 0 : Disabled + **/ +#define SECURE_NPCTIMEOUT 0 + +/** + * (Secure) Optional NPC Dialog Timer + * @requirement : SECURE_NPCTIMEOUT must be enabled + * Minimum Interval Between timeout checks in seconds + * Default: 1s + **/ +#define SECURE_NPCTIMEOUT_INTERVAL 1 + + +/** + * End of File + **/ +#endif diff --git a/src/map/config/Skills/General.h b/src/map/config/Skills/General.h new file mode 100644 index 000000000..7869c0cc9 --- /dev/null +++ b/src/map/config/Skills/General.h @@ -0,0 +1,23 @@ +#ifndef _RRCONFIGS_SKILLS_GENERAL_ +#define _RRCONFIGS_SKILLS_GENERAL_ +/** + * Ragnarok Resources Configuration File (http://ro-resources.net) + * The following settings are applied upon compiling the program, + * therefore any settings you disable will not even be added to the program + * making these settings the most performance-effiecient possible + **/ + +/** + * Default Magical Reflection Behavior + * - When reflecting, reflected damage depends on gears caster is wearing, not target + * - When disabled damage depends on gears target is wearing, not caster. + * @values 1 (enabled) or 0 (disabled) + **/ +#define RR_MAGIC_REFLECTION 1 + +/** + * No settings past this point + **/ +#include "Mage_Classes.h" +#include "Swordsman_Classes.h" +#endif diff --git a/src/map/config/Skills/Mage_Classes.h b/src/map/config/Skills/Mage_Classes.h new file mode 100644 index 000000000..26cd1d03c --- /dev/null +++ b/src/map/config/Skills/Mage_Classes.h @@ -0,0 +1,19 @@ +#ifndef _RRCONFIGS_SKILLS_MAGE_ +#define _RRCONFIGS_SKILLS_MAGE_ +/** + * Ragnarok Resources Configuration File (http://ro-resources.net) + * The following settings are applied upon compiling the program, + * therefore any settings you disable will not even be added to the program + * making these settings the most performance-effiecient possible + **/ + +/** + * (Wizard/HW/Warlock) enable Fire Ivy skill? (1 OR 0) + * Default: 0 (disabled) + **/ +#define FIREIVY_ON 0 + +/** + * No settings past this point + **/ +#endif diff --git a/src/map/config/Skills/Swordsman_Classes.h b/src/map/config/Skills/Swordsman_Classes.h new file mode 100644 index 000000000..3792fa5a3 --- /dev/null +++ b/src/map/config/Skills/Swordsman_Classes.h @@ -0,0 +1,19 @@ +#ifndef _RRCONFIGS_SKILLS_SWORDS_ +#define _RRCONFIGS_SKILLS_SWORDS_ +/** + * Ragnarok Resources Configuration File (http://ro-resources.net) + * The following settings are applied upon compiling the program, + * therefore any settings you disable will not even be added to the program + * making these settings the most performance-effiecient possible + **/ + +/** + * (Rune Knight) the maximum rune items a character may have of the same type + * Default: 20 + **/ +#define MAX_RUNE 20 + +/** + * No settings past this point + **/ +#endif diff --git a/src/map/guild.c b/src/map/guild.c index 0b77aa9a1..8c5988dc8 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -52,13 +52,14 @@ struct guild_expcache { }; static struct eri *expcache_ers; //For handling of guild exp payment. +#define MAX_GUILD_SKILL_REQUIRE 5 struct{ int id; int max; struct{ short id; short lv; - }need[6]; + }need[MAX_GUILD_SKILL_REQUIRE]; } guild_skill_tree[MAX_GUILDSKILL]; int guild_payexp_timer(int tid, unsigned int tick, int id, intptr_t data); @@ -124,7 +125,7 @@ static bool guild_read_guildskill_tree_db(char* split[], int columns, int curren guild_skill_tree[id].max = 1; } - for( k = 0; k < 5; k++ ) + for( k = 0; k < MAX_GUILD_SKILL_REQUIRE; k++ ) { guild_skill_tree[id].need[k].id = atoi(split[k*2+2]); guild_skill_tree[id].need[k].lv = atoi(split[k*2+3]); @@ -147,7 +148,7 @@ int guild_check_skill_require(struct guild *g,int id) if (idx < 0 || idx >= MAX_GUILDSKILL) return 0; - for(i=0;i<5;i++) + for(i=0;i<MAX_GUILD_SKILL_REQUIRE;i++) { if(guild_skill_tree[idx].need[i].id == 0) break; if(guild_skill_tree[idx].need[i].lv > guild_checkskill(g,guild_skill_tree[idx].need[i].id)) @@ -322,10 +323,14 @@ int guild_send_xy_timer_sub(DBKey key,void *data,va_list ap) nullpo_ret(g); + if( !g->connect_member ) + {// no members connected to this guild so do not iterate + return 0; + } + for(i=0;i<g->max_member;i++){ - //struct map_session_data* sd = g->member[i].sd; - struct map_session_data* sd = map_charid2sd(g->member[i].char_id); // temporary crashfix - if( sd != NULL && (sd->guild_x != sd->bl.x || sd->guild_y != sd->bl.y) && !sd->bg_id ) + struct map_session_data* sd = g->member[i].sd; + if( sd != NULL && sd->fd && (sd->guild_x != sd->bl.x || sd->guild_y != sd->bl.y) && !sd->bg_id ) { clif_guild_xy(sd); sd->guild_x = sd->bl.x; @@ -1943,7 +1948,7 @@ void do_init_guild(void) sv_readdb(db_path, "castle_db.txt", ',', 4, 5, -1, &guild_read_castledb); memset(guild_skill_tree,0,sizeof(guild_skill_tree)); - sv_readdb(db_path, "guild_skill_tree.txt", ',', 12, 12, -1, &guild_read_guildskill_tree_db); //guild skill tree [Komurka] + sv_readdb(db_path, "guild_skill_tree.txt", ',', 2+MAX_GUILD_SKILL_REQUIRE*2, 2+MAX_GUILD_SKILL_REQUIRE*2, -1, &guild_read_guildskill_tree_db); //guild skill tree [Komurka] add_timer_func_list(guild_payexp_timer,"guild_payexp_timer"); add_timer_func_list(guild_send_xy_timer, "guild_send_xy_timer"); diff --git a/src/map/intif.c b/src/map/intif.c index 1b69c0837..9fea3be43 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -381,14 +381,15 @@ int intif_create_party(struct party_member *member,char *name,int item,int item2 return 0; } // パーティ情報要求 -int intif_request_partyinfo(int party_id) +int intif_request_partyinfo(int party_id, int char_id) { if (CheckForCharServer()) return 0; - WFIFOHEAD(inter_fd,6); + WFIFOHEAD(inter_fd,10); WFIFOW(inter_fd,0) = 0x3021; WFIFOL(inter_fd,2) = party_id; - WFIFOSET(inter_fd,6); + WFIFOL(inter_fd,6) = char_id; + WFIFOSET(inter_fd,10); return 0; } // パーティ追加要求 @@ -1007,15 +1008,15 @@ int intif_parse_PartyCreated(int fd) // パーティ情報 int intif_parse_PartyInfo(int fd) { - if( RFIFOW(fd,2)==8){ - ShowWarning("intif: party noinfo %d\n",RFIFOL(fd,4)); - party_recv_noinfo(RFIFOL(fd,4)); + if( RFIFOW(fd,2) == 12 ){ + ShowWarning("intif: party noinfo (char_id=%d party_id=%d)\n", RFIFOL(fd,4), RFIFOL(fd,8)); + party_recv_noinfo(RFIFOL(fd,8), RFIFOL(fd,4)); return 0; } - if( RFIFOW(fd,2)!=sizeof(struct party)+4 ) - ShowError("intif: party info : data size error %d %d %d\n",RFIFOL(fd,4),RFIFOW(fd,2),sizeof(struct party)+4); - party_recv_info((struct party *)RFIFOP(fd,4)); + if( RFIFOW(fd,2) != 8+sizeof(struct party) ) + ShowError("intif: party info : data size error (char_id=%d party_id=%d packet_len=%d expected_len=%d)\n", RFIFOL(fd,4), RFIFOL(fd,8), RFIFOW(fd,2), 8+sizeof(struct party)); + party_recv_info((struct party *)RFIFOP(fd,8), RFIFOL(fd,4)); return 0; } // パーティ追加通知 diff --git a/src/map/intif.h b/src/map/intif.h index 73dc5b863..1df990108 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -30,7 +30,7 @@ int intif_send_guild_storage(int account_id, struct guild_storage *gstor); int intif_create_party(struct party_member *member,char *name,int item,int item2); -int intif_request_partyinfo(int party_id); +int intif_request_partyinfo(int party_id, int char_id); int intif_party_addmember(int party_id,struct party_member *member); int intif_party_changeoption(int party_id, int account_id, int exp, int item); diff --git a/src/map/itemdb.c b/src/map/itemdb.c index e8afebdfe..3fc0a5704 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -663,46 +663,6 @@ static bool itemdb_read_itemdelay(char* str[], int columns, int current) return true; } -/*================================================================== - * Reads item stacking restrictions - *----------------------------------------------------------------*/ -static bool itemdb_read_stack(char* fields[], int columns, int current) -{// <item id>,<stack limit amount>,<type> - unsigned short nameid, amount; - unsigned int type; - struct item_data* id; - - nameid = (unsigned short)strtoul(fields[0], NULL, 10); - - if( ( id = itemdb_exists(nameid) ) == NULL ) - { - ShowWarning("itemdb_read_stack: Unknown item id '%hu'.\n", nameid); - return false; - } - - if( !itemdb_isstackable2(id) ) - { - ShowWarning("itemdb_read_stack: Item id '%hu' is not stackable.\n", nameid); - return false; - } - - amount = (unsigned short)strtoul(fields[1], NULL, 10); - type = strtoul(fields[2], NULL, 10); - - if( !amount ) - {// ignore - return true; - } - - id->stack.amount = amount; - id->stack.inventory = (type&1)!=0; - id->stack.cart = (type&2)!=0; - id->stack.storage = (type&4)!=0; - id->stack.guildstorage = (type&8)!=0; - - return true; -} - /// Reads items allowed to be sold in buying stores static bool itemdb_read_buyingstore(char* fields[], int columns, int current) @@ -746,7 +706,35 @@ static int itemdb_gendercheck(struct item_data *id) return (battle_config.ignore_items_gender) ? 2 : id->sex; } - +/** + * [RRInd] + * For backwards compatibility, in Renewal mode, MATK from weapons comes from the atk slot + * We use a ':' delimiter which, if not found, assumes the weapon does not provide any matk. + **/ +void itemdb_rr_split_atoi(char *str, int *atk, int *matk) { + int i, val[2]; + + for (i=0; i<2; i++) { + if (!str) break; + val[i] = atoi(str); + str = strchr(str,':'); + if (str) + *str++=0; + } + if( i == 0 ) { + *atk = *matk = 0; + return;//no data found + } + if( i == 1 ) {//Single Value, we assume it's the ATK + *atk = val[0]; + *matk = 0; + return; + } + //We assume we have 2 values. + *atk = val[0]; + *matk = val[1]; + return; +} /*========================================== * processes one itemdb entry *------------------------------------------*/ @@ -776,7 +764,7 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr 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 ) + if( id->type < 0 || id->type == IT_UNKNOWN || id->type == IT_UNKNOWN2 || ( id->type > IT_THROWWEAPON && 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; @@ -813,7 +801,11 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr id->value_buy, id->value_sell, nameid, id->jname); id->weight = atoi(str[6]); +#if RRMODE + itemdb_rr_split_atoi(str[7],&id->atk,&id->matk); +#else id->atk = atoi(str[7]); +#endif id->def = atoi(str[8]); id->range = atoi(str[9]); id->slot = atoi(str[10]); @@ -875,7 +867,14 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr *------------------------------------------*/ static int itemdb_readdb(void) { - const char* filename[] = { "item_db.txt", "item_db2.txt" }; + /** + * ro-resources inheritance: item_db -> item_db_re -> item_db2 (user customs) + **/ +#if RRMODE + const char* filename[] = { "item_db.txt","item_db_re.txt","item_db2.txt" }; +#else + const char* filename[] = { "item_db.txt","item_db2.txt" }; +#endif int fi; for( fi = 0; fi < ARRAYLENGTH(filename); ++fi ) @@ -987,7 +986,11 @@ static int itemdb_readdb(void) *======================================*/ static int itemdb_read_sqldb(void) { +#if RRMODE + const char* item_db_name[] = { item_db_db, item_db_re_db, item_db2_db }; +#else const char* item_db_name[] = { item_db_db, item_db2_db }; +#endif int fi; for( fi = 0; fi < ARRAYLENGTH(item_db_name); ++fi ) @@ -1046,7 +1049,6 @@ static void itemdb_read(void) sv_readdb(db_path, "item_noequip.txt", ',', 2, 2, -1, &itemdb_read_noequip); sv_readdb(db_path, "item_trade.txt", ',', 3, 3, -1, &itemdb_read_itemtrade); sv_readdb(db_path, "item_delay.txt", ',', 2, 2, MAX_ITEMDELAYS, &itemdb_read_itemdelay); - sv_readdb(db_path, "item_stack.txt", ',', 3, 3, -1, &itemdb_read_stack); sv_readdb(db_path, "item_buyingstore.txt", ',', 1, 1, -1, &itemdb_read_buyingstore); } @@ -1090,8 +1092,8 @@ void itemdb_reload(void) struct s_mapiterator* iter; struct map_session_data* sd; - int i; - + int i,d,k; + // clear the previous itemdb data for( i = 0; i < ARRAYLENGTH(itemdb_array); ++i ) if( itemdb_array[i] ) @@ -1100,9 +1102,37 @@ void itemdb_reload(void) itemdb_other->clear(itemdb_other, itemdb_final_sub); memset(itemdb_array, 0, sizeof(itemdb_array)); - + // read new data itemdb_read(); + + //Epoque's awesome @reloaditemdb fix - thanks! [Ind] + //- Fixes the need of a @reloadmobdb after a @reloaditemdb to re-link monster drop data + for( i = 0; i < MAX_MOB_DB; i++ ) { + struct mob_db *entry; + if( !((i < 1324 || i > 1363) && (i < 1938 || i > 1946)) ) + continue; + entry = mob_db(i); + for(d = 0; d < MAX_MOB_DROP; d++) { + struct item_data *id; + if( !entry->dropitem[d].nameid ) + continue; + id = itemdb_search(entry->dropitem[d].nameid); + + for (k = 0; k < MAX_SEARCH; k++) { + if (id->mob[k].chance <= entry->dropitem[d].p) + break; + } + + if (k == MAX_SEARCH) + continue; + + if (id->mob[k].id != i) + memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0])); + id->mob[k].chance = entry->dropitem[d].p; + id->mob[k].id = i; + } + } // readjust itemdb pointer cache for each player iter = mapit_geteachpc(); diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 6a201008e..801b81be8 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -5,14 +5,20 @@ #define _ITEMDB_H_ #include "../common/mmo.h" // ITEM_NAME_LENGTH +#include "map.h" //RRMODE #define MAX_RANDITEM 11000 // The maximum number of item delays -#define MAX_ITEMDELAYS 18 +#define MAX_ITEMDELAYS 10 #define MAX_SEARCH 5 //Designed for search functions, species max number of matches to display. +/** + * Arch Bishop + **/ +#define ITEMID_ANCILLA 12333 + #define ITEMID_YELLOW_GEMSTONE 715 #define ITEMID_RED_GEMSTONE 716 #define ITEMID_BLUE_GEMSTONE 717 @@ -50,6 +56,9 @@ struct item_data { int equip; int weight; int atk; +#if RRMODE + int matk;//[RRInd] -- used in RE for matk +#endif int def; int range; int slot; @@ -61,7 +70,7 @@ struct item_data { //Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command // some script commands should be revised as well... unsigned int class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2) - unsigned class_upper : 3; //Specifies if the upper-type can equip it (bitfield, 1: normal, 2: upper, 3: baby) + unsigned class_upper : 4; //Specifies if the upper-type can equip it (bitfield, 1: normal, 2: upper, 3: baby,4:third) struct { unsigned short chance; int id; @@ -78,14 +87,6 @@ struct item_data { unsigned autoequip: 1; unsigned buyingstore : 1; } flag; - struct - {// item stacking limitation - unsigned short amount; - unsigned int inventory:1; - unsigned int cart:1; - unsigned int storage:1; - unsigned int guildstorage:1; - } stack; short gm_lv_trade_override; //GM-level to override trade_restriction }; @@ -151,4 +152,35 @@ void itemdb_reload(void); void do_final_itemdb(void); int do_init_itemdb(void); +/** + * Rune Knight + **/ +enum { + ITEMID_NAUTHIZ = 12725, + ITEMID_RAIDO, + ITEMID_BERKANA, + ITEMID_ISA, + ITEMID_OTHILA, + ITEMID_URUZ, + ITEMID_THURISAZ, + ITEMID_WYRD, + ITEMID_HAGALAZ, +} rune_list; +#define itemdb_is_rune(n) (n >= ITEMID_NAUTHIZ && n <= ITEMID_HAGALAZ) +/** + * Warlock + **/ +#define itemdb_is_spellbook(n) (n >= 6188 && n <= 6205) +/** + * Ranger + **/ +#define ITEMID_TRAP_ALLOY 7940 +/** + * Mechanic + **/ +#define itemdb_is_element(n) (n >= 990 && n <= 993) +/** + * Guilotine Cross + **/ +#define itemdb_is_poison(n) (n >= 12717 && n <= 12724) #endif /* _ITEMDB_H_ */ diff --git a/src/map/log.c b/src/map/log.c index 72cfe46d3..af7ae3121 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -1,6 +1,7 @@ // Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder +#include "../common/sql.h" // SQL_INNODB #include "../common/strlib.h" #include "../common/nullpo.h" #include "../common/showmsg.h" @@ -21,6 +22,15 @@ struct Log_Config log_config; char timestring[255]; time_t curtime; +#ifdef SQL_INNODB +// database is using an InnoDB engine so do not use DELAYED +#define LOG_QUERY "INSERT" +#else +// database is using a MyISAM engine so use DELAYED +#define LOG_QUERY "INSERT DELAYED" +#endif + + //FILTER OPTIONS //0 = Don't log //1 = Log any item @@ -70,7 +80,7 @@ int log_branch(struct map_session_data *sd) { SqlStmt* stmt; stmt = SqlStmt_Malloc(logmysql_handle); - if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', ?, '%s')", log_config.log_branch_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) ) + if( SQL_SUCCESS != SqlStmt_Prepare(stmt, LOG_QUERY " INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', ?, '%s')", log_config.log_branch_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) ) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, sd->status.name, strnlen(sd->status.name, NAME_LENGTH)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) { @@ -107,14 +117,14 @@ int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int a if( log_config.sql_logs ) { if( itm == NULL ) { //We log common item - if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", + if (SQL_ERROR == Sql_Query(logmysql_handle, LOG_QUERY " INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", log_config.log_pick_db, sd->status.char_id, type, nameid, amount, mapindex_id2name(sd->mapindex)) ) { Sql_ShowDebug(logmysql_handle); return 0; } } else { //We log Extended item - if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", + if (SQL_ERROR == Sql_Query(logmysql_handle, LOG_QUERY " INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", log_config.log_pick_db, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapindex_id2name(sd->mapindex)) ) { Sql_ShowDebug(logmysql_handle); @@ -162,14 +172,14 @@ int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, if( log_config.sql_logs ) { if( itm == NULL ) { //We log common item - if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", + if (SQL_ERROR == Sql_Query(logmysql_handle, LOG_QUERY " INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')", log_config.log_pick_db, md->class_, type, nameid, amount, mapname) ) { Sql_ShowDebug(logmysql_handle); return 0; } } else { //We log Extended item - if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", + if (SQL_ERROR == Sql_Query(logmysql_handle, LOG_QUERY " INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')", log_config.log_pick_db, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname) ) { Sql_ShowDebug(logmysql_handle); @@ -208,7 +218,7 @@ int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *s #ifndef TXT_ONLY if( log_config.sql_logs ) { - if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')", + if (SQL_ERROR == Sql_Query(logmysql_handle, LOG_QUERY " INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')", log_config.log_zeny_db, sd->status.char_id, src_sd->status.char_id, type, amount, mapindex_id2name(sd->mapindex)) ) { Sql_ShowDebug(logmysql_handle); @@ -240,7 +250,7 @@ int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp) #ifndef TXT_ONLY if( log_config.sql_logs ) { - if (SQL_ERROR == Sql_Query(logmysql_handle, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", + if (SQL_ERROR == Sql_Query(logmysql_handle, LOG_QUERY " INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex)) ) { Sql_ShowDebug(logmysql_handle); @@ -276,7 +286,7 @@ int log_atcommand(struct map_session_data* sd, const char* message) SqlStmt* stmt; stmt = SqlStmt_Malloc(logmysql_handle); - if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES (NOW(), '%d', '%d', ?, '%s', ?)", log_config.log_gm_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) ) + if( SQL_SUCCESS != SqlStmt_Prepare(stmt, LOG_QUERY " INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES (NOW(), '%d', '%d', ?, '%s', ?)", log_config.log_gm_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) ) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, sd->status.name, strnlen(sd->status.name, NAME_LENGTH)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (char*)message, safestrnlen(message, 255)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) @@ -314,7 +324,7 @@ int log_npc(struct map_session_data* sd, const char* message) { SqlStmt* stmt; stmt = SqlStmt_Malloc(logmysql_handle); - if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES (NOW(), '%d', '%d', ?, '%s', ?)", log_config.log_npc_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) ) + if( SQL_SUCCESS != SqlStmt_Prepare(stmt, LOG_QUERY " INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES (NOW(), '%d', '%d', ?, '%s', ?)", log_config.log_npc_db, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex) ) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, sd->status.name, strnlen(sd->status.name, NAME_LENGTH)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (char*)message, safestrnlen(message, 255)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) @@ -365,7 +375,7 @@ int log_chat(const char* type, int type_id, int src_charid, int src_accid, const SqlStmt* stmt; stmt = SqlStmt_Malloc(logmysql_handle); - if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', ?, ?)", log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y) + if( SQL_SUCCESS != SqlStmt_Prepare(stmt, LOG_QUERY " INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', ?, ?)", log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (char*)dst_charname, safestrnlen(dst_charname, NAME_LENGTH)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (char*)message, safestrnlen(message, CHAT_SIZE_MAX)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) diff --git a/src/map/map.c b/src/map/map.c index a4b419f65..5dab6634d 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -68,6 +68,7 @@ Sql* mmysql_handle; int db_use_sqldbs = 0; char item_db_db[32] = "item_db"; char item_db2_db[32] = "item_db2"; +char item_db_re_db[32] = "item_db_re"; char mob_db_db[32] = "mob_db"; char mob_db2_db[32] = "mob_db2"; @@ -1565,6 +1566,8 @@ int map_quit(struct map_session_data *sd) if (sd->npc_id) npc_event_dequeue(sd); + if( sd->bg_id ) + bg_team_leave(sd,1); npc_script_event(sd, NPCE_LOGOUT); //Unit_free handles clearing the player related data, @@ -1584,9 +1587,6 @@ int map_quit(struct map_session_data *sd) status_change_end(&sd->bl, SC_WEIGHT50, INVALID_TIMER); status_change_end(&sd->bl, SC_WEIGHT90, INVALID_TIMER); if (battle_config.debuff_on_logout&1) { - status_change_end(&sd->bl, SC_DECREASEAGI, INVALID_TIMER); - status_change_end(&sd->bl, SC_BENEDICTIO, INVALID_TIMER); - status_change_end(&sd->bl, SC_AETERNA, INVALID_TIMER); status_change_end(&sd->bl, SC_ORCISH, INVALID_TIMER); status_change_end(&sd->bl, SC_STRIPWEAPON, INVALID_TIMER); status_change_end(&sd->bl, SC_STRIPARMOR, INVALID_TIMER); @@ -1594,8 +1594,6 @@ int map_quit(struct map_session_data *sd) status_change_end(&sd->bl, SC_STRIPHELM, INVALID_TIMER); status_change_end(&sd->bl, SC_EXTREMITYFIST, INVALID_TIMER); status_change_end(&sd->bl, SC_EXPLOSIONSPIRITS, INVALID_TIMER); - status_change_end(&sd->bl, SC_JOINTBEAT, INVALID_TIMER); - status_change_end(&sd->bl, SC_MINDBREAKER, INVALID_TIMER); if(sd->sc.data[SC_REGENERATION] && sd->sc.data[SC_REGENERATION]->val4) status_change_end(&sd->bl, SC_REGENERATION, INVALID_TIMER); //TO-DO Probably there are way more NPC_type negative status that are removed @@ -1606,61 +1604,12 @@ int map_quit(struct map_session_data *sd) } if (battle_config.debuff_on_logout&2) { - status_change_end(&sd->bl, SC_MAGNIFICAT, INVALID_TIMER); status_change_end(&sd->bl, SC_MAXIMIZEPOWER, INVALID_TIMER); status_change_end(&sd->bl, SC_MAXOVERTHRUST, INVALID_TIMER); - status_change_end(&sd->bl, SC_AURABLADE, INVALID_TIMER); - status_change_end(&sd->bl, SC_PARRYING, INVALID_TIMER); - status_change_end(&sd->bl, SC_CONCENTRATION, INVALID_TIMER); - status_change_end(&sd->bl, SC_TENSIONRELAX, INVALID_TIMER); - status_change_end(&sd->bl, SC_MAGICPOWER, INVALID_TIMER); - status_change_end(&sd->bl, SC_EDP, INVALID_TIMER); - status_change_end(&sd->bl, SC_TRUESIGHT, INVALID_TIMER); - status_change_end(&sd->bl, SC_WINDWALK, INVALID_TIMER); - status_change_end(&sd->bl, SC_MELTDOWN, INVALID_TIMER); - status_change_end(&sd->bl, SC_CARTBOOST, INVALID_TIMER); - status_change_end(&sd->bl, SC_MEMORIZE, INVALID_TIMER); - status_change_end(&sd->bl, SC_DEVOTION, INVALID_TIMER); - status_change_end(&sd->bl, SC_SACRIFICE, INVALID_TIMER); status_change_end(&sd->bl, SC_STEELBODY, INVALID_TIMER); status_change_end(&sd->bl, SC_PRESERVE, INVALID_TIMER); status_change_end(&sd->bl, SC_KAAHI, INVALID_TIMER); - status_change_end(&sd->bl, SC_KAUPE, INVALID_TIMER); - status_change_end(&sd->bl, SC_DOUBLECAST, INVALID_TIMER); - status_change_end(&sd->bl, SC_SHRINK, INVALID_TIMER); - status_change_end(&sd->bl, SC_SIGHTBLASTER, INVALID_TIMER); status_change_end(&sd->bl, SC_SPIRIT, INVALID_TIMER); - status_change_end(&sd->bl, SC_KAITE, INVALID_TIMER); - status_change_end(&sd->bl, SC_UTSUSEMI, INVALID_TIMER); - status_change_end(&sd->bl, SC_BUNSINJYUTSU, INVALID_TIMER); - status_change_end(&sd->bl, SC_SUITON, INVALID_TIMER); - // Third jobs - status_change_end(&sd->bl, SC_MILLENNIUMSHIELD, INVALID_TIMER); - status_change_end(&sd->bl, SC_DEATHBOUND, INVALID_TIMER); - status_change_end(&sd->bl, SC_REFRESH, INVALID_TIMER); - status_change_end(&sd->bl, SC_STONEHARDSKIN, INVALID_TIMER); - //status_change_end(&sd->bl, SC_CLOAKINGEXCEED, INVALID_TIMER); - //status_change_end(&sd->bl, SC_HALLUCINATIONWALK_POSTDELAY, INVALID_TIMER); - //status_change_end(&sd->bl, SC_WEAPONBLOCKING_POSTDELAY, INVALID_TIMER); - //status_change_end(&sd->bl, SC_ROLLINGCUTTER, INVALID_TIMER); - //status_change_end(&sd->bl, SC_ELECTRICSHOCKER, INVALID_TIMER); - //status_change_end(&sd->bl, SC_WUGDASH, INVALID_TIMER); - //status_change_end(&sd->bl, SC_WUGBITE, INVALID_TIMER); - //status_change_end(&sd->bl, SC_CAMOUFLAGE, INVALID_TIMER); - //status_change_end(&sd->bl, SC_MAGNETICFIELD, INVALID_TIMER); - //status_change_end(&sd->bl, SC_NEUTRALBARRIER, INVALID_TIMER); - //status_change_end(&sd->bl, SC_NEUTRALBARRIER_MASTER, INVALID_TIMER); - //status_change_end(&sd->bl, SC_STEALTHFIELD_MASTER, INVALID_TIMER); - //status_change_end(&sd->bl, SC_SHADOWFORM, INVALID_TIMER); - //status_change_end(&sd->bl, SC_INVISIBILITY, INVALID_TIMER); - //status_change_end(&sd->bl, SC_RAISINGDRAGON, INVALID_TIMER); - //status_change_end(&sd->bl, SC_NOEQUIPACCESSARY, INVALID_TIMER); - //status_change_end(&sd->bl, SC_MANHOLE, INVALID_TIMER); - //status_change_end(&sd->bl, SC_PROPERTYWALK, INVALID_TIMER); - //status_change_end(&sd->bl, SC_DEEP_SLEEP, INVALID_TIMER); - //status_change_end(&sd->bl, SC_WARMER, INVALID_TIMER); - //status_change_end(&sd->bl, SC_GN_TRAINING_SWORD, INVALID_TIMER); - //status_change_end(&sd->bl, SC_GN_REMODELING_CART, INVALID_TIMER); } } @@ -3259,6 +3208,9 @@ int map_config_read(char *cfgName) if (strcmpi(w1, "import") == 0) map_config_read(w2); else + if (strcmpi(w1, "console_msg_log") == 0) + console_msg_log = atoi(w2);//[Ind] + else ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName); } @@ -3580,41 +3532,39 @@ void do_abort(void) /*====================================================== * Map-Server Version Screen [MC Cameri] *------------------------------------------------------*/ -void map_helpscreen(int flag) -{ - puts("Usage: map-server [options]"); - puts("Options:"); - puts(CL_WHITE" Commands\t\t\tDescription"CL_RESET); - puts("-----------------------------------------------------------------------------"); - puts(" --help, --h, --?, /? Displays this help screen"); - puts(" --map-config <file> Load map-server configuration from <file>"); - puts(" --battle-config <file> Load battle configuration from <file>"); - puts(" --atcommand-config <file> Load atcommand configuration from <file>"); - puts(" --script-config <file> Load script configuration from <file>"); - puts(" --msg-config <file> Load message configuration from <file>"); - puts(" --grf-path-file <file> Load grf path file configuration from <file>"); - puts(" --sql-config <file> Load inter-server configuration from <file>"); - puts(" (SQL Only)"); - puts(" --log-config <file> Load logging configuration from <file>"); - puts(" (SQL Only)"); - puts(" --version, --v, -v, /v Displays the server's version"); - puts("\n"); - if (flag) exit(EXIT_FAILURE); +static void map_helpscreen(bool do_exit) +{ + ShowInfo("Usage: %s [options]\n", SERVER_NAME); + ShowInfo("\n"); + ShowInfo("Options:\n"); + ShowInfo(" -?, -h [--help]\t\tDisplays this help screen.\n"); + ShowInfo(" -v [--version]\t\tDisplays the server's version.\n"); + ShowInfo(" --run-once\t\t\tCloses server after loading (testing).\n"); + ShowInfo(" --map-config <file>\t\tAlternative map-server configuration.\n"); + ShowInfo(" --battle-config <file>\tAlternative battle configuration.\n"); + ShowInfo(" --atcommand-config <file>\tAlternative atcommand configuration.\n"); + ShowInfo(" --script-config <file>\tAlternative script configuration.\n"); + ShowInfo(" --msg-config <file>\t\tAlternative message configuration.\n"); + ShowInfo(" --grf-path <file>\t\tAlternative GRF path configuration.\n"); + ShowInfo(" --inter-config <file>\t\tAlternative inter-server configuration.\n"); + ShowInfo(" --log-config <file>\t\tAlternative logging configuration.\n"); + if( do_exit ) + exit(EXIT_SUCCESS); } /*====================================================== * Map-Server Version Screen [MC Cameri] *------------------------------------------------------*/ -void map_versionscreen(int flag) +static void map_versionscreen(bool do_exit) { - ShowInfo(CL_WHITE "eAthena version %d.%02d.%02d, Athena Mod version %d" CL_RESET"\n", - ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, - ATHENA_MOD_VERSION); - ShowInfo(CL_GREEN "Website/Forum:" CL_RESET "\thttp://eathena.deltaanime.net/\n"); - ShowInfo(CL_GREEN "IRC Channel:" CL_RESET "\tirc://irc.deltaanime.net/#athena\n"); - ShowInfo("\nOpen " CL_WHITE "readme.html" CL_RESET " for more information."); - if (ATHENA_RELEASE_FLAG) ShowNotice("This version is not for release.\n"); - if (flag) exit(EXIT_FAILURE); + ShowInfo(CL_WHITE"RAthena version %d.%02d.%02d, Athena Mod version %d" CL_RESET"\n", ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, ATHENA_MOD_VERSION); + ShowInfo(CL_GREEN"Website/Forum:"CL_RESET"\thttp://rathena.org/\n"); + ShowInfo(CL_GREEN"IRC Channel:"CL_RESET"\tirc://irc.rizon.net/#rthena\n"); + ShowInfo("Open "CL_WHITE"readme.html"CL_RESET" for more information.\n"); + if(ATHENA_RELEASE_FLAG) + ShowNotice("This version is not for release.\n"); + if( do_exit ) + exit(EXIT_SUCCESS); } /*====================================================== @@ -3645,6 +3595,16 @@ void do_shutdown(void) } } +static bool map_arg_next_value(const char* option, int i, int argc) +{ + if( i >= argc-1 ) + { + ShowWarning("Missing value for option '%s'.\n", option); + return false; + } + + return true; +} int do_init(int argc, char *argv[]) { @@ -3665,31 +3625,67 @@ int do_init(int argc, char *argv[]) srand(gettick()); - for (i = 1; i < argc ; i++) { - if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "--h") == 0 || strcmp(argv[i], "--?") == 0 || strcmp(argv[i], "/?") == 0) - map_helpscreen(1); - else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "--v") == 0 || strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "/v") == 0) - map_versionscreen(1); - else if (strcmp(argv[i], "--map_config") == 0 || strcmp(argv[i], "--map-config") == 0) - MAP_CONF_NAME=argv[i+1]; - else if (strcmp(argv[i],"--battle_config") == 0 || strcmp(argv[i],"--battle-config") == 0) - BATTLE_CONF_FILENAME = argv[i+1]; - else if (strcmp(argv[i],"--atcommand_config") == 0 || strcmp(argv[i],"--atcommand-config") == 0) - ATCOMMAND_CONF_FILENAME = argv[i+1]; - else if (strcmp(argv[i],"--script_config") == 0 || strcmp(argv[i],"--script-config") == 0) - SCRIPT_CONF_NAME = argv[i+1]; - else if (strcmp(argv[i],"--msg_config") == 0 || strcmp(argv[i],"--msg-config") == 0) - MSG_CONF_NAME = argv[i+1]; - else if (strcmp(argv[i],"--grf_path_file") == 0 || strcmp(argv[i],"--grf-path-file") == 0) - GRF_PATH_FILENAME = argv[i+1]; -#ifndef TXT_ONLY - else if (strcmp(argv[i],"--inter_config") == 0 || strcmp(argv[i],"--inter-config") == 0) - INTER_CONF_NAME = argv[i+1]; -#endif - else if (strcmp(argv[i],"--log_config") == 0 || strcmp(argv[i],"--log-config") == 0) - LOG_CONF_NAME = argv[i+1]; - else if (strcmp(argv[i],"--run_once") == 0) // close the map-server as soon as its done.. for testing [Celest] - runflag = 0; + for( i = 1; i < argc ; i++ ) + { + const char* arg = argv[i]; + + if( arg[0] != '-' && ( arg[0] != '/' || arg[1] == '-' ) ) + {// -, -- and / + ShowError("Unknown option '%s'.\n", argv[i]); + exit(EXIT_FAILURE); + } + else if( (++arg)[0] == '-' ) + {// long option + arg++; + + if( strcmp(arg, "help") == 0 ) + map_helpscreen(true); + else if( strcmp(arg, "version") == 0 ) + map_versionscreen(true); + else if( strcmp(arg, "map-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + MAP_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "battle-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + BATTLE_CONF_FILENAME = argv[++i]; + } else if( strcmp(arg, "atcommand-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + ATCOMMAND_CONF_FILENAME = argv[++i]; + } else if( strcmp(arg, "script-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + SCRIPT_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "msg-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + MSG_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "grf-path-file") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + GRF_PATH_FILENAME = argv[++i]; + } else if( strcmp(arg, "inter-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + INTER_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "log-config") == 0 ) { + if( map_arg_next_value(arg, i, argc) ) + LOG_CONF_NAME = argv[++i]; + } else if( strcmp(arg, "run-once") == 0 ) // close the map-server as soon as its done.. for testing [Celest] + runflag = CORE_ST_STOP; + else { + ShowError("Unknown option '%s'.\n", argv[i]); + exit(EXIT_FAILURE); + } + } + else switch( arg[0] ) + {// short option + case '?': + case 'h': + map_helpscreen(true); + break; + case 'v': + map_versionscreen(true); + break; + default: + ShowError("Unknown option '%s'.\n", argv[i]); + exit(EXIT_FAILURE); + } } map_config_read(MAP_CONF_NAME); diff --git a/src/map/map.h b/src/map/map.h index 6fbb36cdc..51678d590 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -10,6 +10,11 @@ #include "../common/mapindex.h" #include "../common/db.h" +/** + * [ro-resources.net] + **/ +#include "./config/Core.h" + #include <stdarg.h> struct npc_data; @@ -68,17 +73,17 @@ enum E_MAPSERVER_ST #define JOBL_UPPER 0x1000 //4096 #define JOBL_BABY 0x2000 //8192 -#define JOBL_3 0x4000 //16384 +#define JOBL_THIRD 0x4000 //16384 //for filtering and quick checking. #define MAPID_UPPERMASK 0x0fff #define MAPID_BASEMASK 0x00ff +#define MAPID_THIRDMASK (JOBL_THIRD|MAPID_UPPERMASK) //First Jobs //Note the oddity of the novice: //Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too... enum { MAPID_NOVICE = 0x0, -//1st classes MAPID_SWORDMAN, MAPID_MAGE, MAPID_ARCHER, @@ -92,7 +97,7 @@ enum { MAPID_XMAS, MAPID_SUMMER, //2_1 classes - MAPID_SUPER_NOVICE = JOBL_2_1|MAPID_NOVICE, + MAPID_SUPER_NOVICE = JOBL_2_1|0x0, MAPID_KNIGHT, MAPID_WIZARD, MAPID_HUNTER, @@ -101,29 +106,15 @@ enum { MAPID_ASSASSIN, MAPID_STAR_GLADIATOR, //2_2 classes - MAPID_CRUSADER = JOBL_2_2|MAPID_SWORDMAN, + MAPID_CRUSADER = JOBL_2_2|0x1, MAPID_SAGE, MAPID_BARDDANCER, MAPID_MONK, MAPID_ALCHEMIST, MAPID_ROGUE, MAPID_SOUL_LINKER, -//3_1 classes - MAPID_RUNE_KNIGHT = JOBL_3|JOBL_2_1|MAPID_SWORDMAN, - MAPID_WARLOCK, - MAPID_RANGER, - MAPID_ARCHBISHOP, - MAPID_MECHANIC, - MAPID_GUILLOTINE_CROSS, -//3_2 classes - MAPID_ROYAL_GUARD = JOBL_3|JOBL_2_2|MAPID_SWORDMAN, - MAPID_SORCERER, - MAPID_MINSTRELWANDERER, - MAPID_SURA, - MAPID_GENETIC, - MAPID_SHADOW_CHASER, -//1st, advanced - MAPID_NOVICE_HIGH = JOBL_UPPER|MAPID_NOVICE, +//1-1, advanced + MAPID_NOVICE_HIGH = JOBL_UPPER|0x0, MAPID_SWORDMAN_HIGH, MAPID_MAGE_HIGH, MAPID_ARCHER_HIGH, @@ -131,35 +122,21 @@ enum { MAPID_MERCHANT_HIGH, MAPID_THIEF_HIGH, //2_1 advanced - MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|MAPID_SWORDMAN, + MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|0x1, MAPID_HIGH_WIZARD, MAPID_SNIPER, MAPID_HIGH_PRIEST, MAPID_WHITESMITH, MAPID_ASSASSIN_CROSS, //2_2 advanced - MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|MAPID_SWORDMAN, + MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|0x1, MAPID_PROFESSOR, MAPID_CLOWNGYPSY, MAPID_CHAMPION, MAPID_CREATOR, MAPID_STALKER, -//3_1 advanced - MAPID_RUNE_KNIGHT_H = JOBL_3|JOBL_UPPER|JOBL_2_1|MAPID_SWORDMAN, - MAPID_WARLOCK_H, - MAPID_RANGER_H, - MAPID_ARCHBISHOP_H, - MAPID_MECHANIC_H, - MAPID_GUILLOTINE_CROSS_H, -//3_2 advanced - MAPID_ROYAL_GUARD_H = JOBL_3|JOBL_UPPER|JOBL_2_2|MAPID_SWORDMAN, - MAPID_SORCERER_H, - MAPID_MINSTRELWANDERER_H, - MAPID_SURA_H, - MAPID_GENETIC_H, - MAPID_SHADOW_CHASER_H, -//1st baby - MAPID_BABY = JOBL_BABY|MAPID_NOVICE, +//1-1 baby + MAPID_BABY = JOBL_BABY|0x0, MAPID_BABY_SWORDMAN, MAPID_BABY_MAGE, MAPID_BABY_ARCHER, @@ -168,7 +145,7 @@ enum { MAPID_BABY_THIEF, MAPID_BABY_TAEKWON, //2_1 baby - MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|MAPID_NOVICE, + MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|0x0, MAPID_BABY_KNIGHT, MAPID_BABY_WIZARD, MAPID_BABY_HUNTER, @@ -177,27 +154,38 @@ enum { MAPID_BABY_ASSASSIN, MAPID_BABY_STAR_GLADIATOR, //2_2 baby - MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|MAPID_SWORDMAN, + MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|0x1, MAPID_BABY_SAGE, MAPID_BABY_BARDDANCER, MAPID_BABY_MONK, MAPID_BABY_ALCHEMIST, MAPID_BABY_ROGUE, MAPID_BABY_SOUL_LINKER, -//3_1 baby - MAPID_BABY_RUNE = JOBL_3|JOBL_BABY|JOBL_2_1|MAPID_SWORDMAN, - MAPID_BABY_WARLOCK, - MAPID_BABY_RANGER, - MAPID_BABY_BISHOP, - MAPID_BABY_MECHANIC, - MAPID_BABY_CROSS, -//3_2 baby - MAPID_BABY_GUARD = JOBL_3|JOBL_BABY|JOBL_2_2|MAPID_SWORDMAN, - MAPID_BABY_SORCERER, - MAPID_BABY_MINSTRELWANDERER, - MAPID_BABY_SURA, - MAPID_BABY_GENETIC, - MAPID_BABY_CHASER + MAPID_RUNE_KNIGHT = JOBL_THIRD|JOBL_2_1|0x1, + MAPID_WARLOCK, + MAPID_RANGER, + MAPID_ARCH_BISHOP, + MAPID_MECHANIC, + MAPID_GUILLOTINE_CROSS, + MAPID_ROYAL_GUARD = JOBL_THIRD|JOBL_2_2|0x1, + MAPID_SORCERER, + MAPID_MINSTRELWANDERER, + MAPID_SURA, + MAPID_GENETIC, + MAPID_SHADOW_CHASER, + MAPID_RUNE_KNIGHT_T = JOBL_THIRD|JOBL_UPPER|JOBL_2_1|0x1, + MAPID_WARLOCK_T, + MAPID_RANGER_T, + MAPID_ARCH_BISHOP_T, + MAPID_MECHANIC_T, + MAPID_GUILLOTINE_CROSS_T, + MAPID_ROYAL_GUARD_T = JOBL_THIRD|JOBL_UPPER|JOBL_2_2|0x1, + MAPID_SORCERER_T, + MAPID_MINSTRELWANDERER_T, + MAPID_SURA_T, + MAPID_GENETIC_T, + MAPID_SHADOW_CHASER_T, + }; //Max size for inputs to Graffiti, Talkie Box and Vending text prompts @@ -313,7 +301,7 @@ struct spawn_data { signed short xs,ys; unsigned short num; //Number of mobs using this structure unsigned short active; //Number of mobs that are already spawned (for mob_remove_damaged: no) - unsigned int delay1,delay2; //Min delay before respawning after spawn/death + unsigned int delay1,delay2; //Spawn delay (fixed base + random variance) struct { unsigned int size :2; //Holds if mob has to be tiny/large unsigned int ai :2; //Holds if mob is special ai. @@ -380,7 +368,7 @@ enum _sp { SP_WEAPON_ATK,SP_WEAPON_ATK_RATE, // 1081-1082 SP_DELAYRATE,SP_HP_DRAIN_RATE_RACE,SP_SP_DRAIN_RATE_RACE, // 1083-1085 SP_IGNORE_MDEF_RATE,SP_IGNORE_DEF_RATE,SP_SKILL_HEAL2,SP_ADDEFF_ONSKILL, //1086-1089 - SP_ADD_HEAL_RATE,SP_ADD_HEAL2_RATE,SP_FIXEDCASTRATE,SP_BASE_MATK,SP_WEAPON_MATK,SP_EQUIPMENT_MATK, //1090-1095 + SP_ADD_HEAL_RATE,SP_ADD_HEAL2_RATE, //1090-1091 SP_RESTART_FULL_RECOVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005 SP_NO_CASTCANCEL2,SP_NO_MISC_DAMAGE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR, SP_UNBREAKABLE_HELM, // 2006-2010 @@ -520,7 +508,10 @@ struct map_data { unsigned fireworks : 1; unsigned sakura : 1; // [Valaris] unsigned leaves : 1; // [Valaris] - unsigned rain : 1; // [Valaris] + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //unsigned rain : 1; // [Valaris] unsigned nogo : 1; // [Valaris] unsigned nobaseexp : 1; // [Lorky] added by Lupus unsigned nojobexp : 1; // [Lorky] @@ -687,7 +678,6 @@ int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex] 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); @@ -711,8 +701,6 @@ extern char *SCRIPT_CONF_NAME; extern char *MSG_CONF_NAME; extern char *GRF_PATH_FILENAME; -extern char *map_server_dns; - //Useful typedefs from jA [Skotlex] typedef struct map_session_data TBL_PC; typedef struct npc_data TBL_NPC; @@ -741,6 +729,7 @@ extern Sql* logmysql_handle; extern char item_db_db[32]; extern char item_db2_db[32]; +extern char item_db_re_db[32]; extern char mob_db_db[32]; extern char mob_db2_db[32]; diff --git a/src/map/mercenary.h b/src/map/mercenary.h index 1cc689208..f040a3913 100644 --- a/src/map/mercenary.h +++ b/src/map/mercenary.h @@ -69,6 +69,12 @@ int mercenary_kills(struct mercenary_data *md); int mercenary_checkskill(struct mercenary_data *md, int skill_id); +/** + * atcommand.c required + **/ +int read_mercenarydb(void); +int read_mercenary_skilldb(void); + int do_init_mercenary(void); #endif /* _MERCENARY_H_ */ diff --git a/src/map/mob.c b/src/map/mob.c index 49ca6eb23..df547c77d 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -1311,7 +1311,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) return false; // Abnormalities - if((md->sc.opt1 > 0 && md->sc.opt1 != OPT1_STONEWAIT && md->sc.opt1 != OPT1_BURNING) || md->sc.data[SC_BLADESTOP]) + if((md->sc.opt1 > 0 && md->sc.opt1 != OPT1_STONEWAIT) || md->sc.data[SC_BLADESTOP]) { //Should reset targets. md->target_id = md->attacked_id = 0; return false; @@ -2163,8 +2163,12 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) merc_hom_gainexp(tmpsd[i]->hd, base_exp); if(base_exp || job_exp) { - if( md->dmglog[i].flag != MDLF_PET || battle_config.pet_attack_exp_to_master ) + if( md->dmglog[i].flag != MDLF_PET || battle_config.pet_attack_exp_to_master ) { +#if RRMODE + party_renewal_exp_mod(&base_exp,&job_exp,tmpsd[i]->status.base_level,md->level); +#endif pc_gainexp(tmpsd[i], &md->bl, base_exp, job_exp, false); + } } if(zeny) // zeny from mobs [Valaris] pc_getzeny(tmpsd[i], zeny); @@ -2185,6 +2189,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); struct item_drop *ditem; int drop_rate; +#if RRMODE + int drop_modifier = mvp_sd ? party_renewal_drop_mod(mvp_sd->status.base_level - md->level) : + second_sd ? party_renewal_drop_mod(second_sd->status.base_level - md->level) : + third_sd ? party_renewal_drop_mod(third_sd->status.base_level - md->level) : 100; +#endif dlist->m = md->bl.m; dlist->x = md->bl.x; dlist->y = md->bl.y; @@ -2226,7 +2235,10 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) // Increase drop rate if user has SC_ITEMBOOST if (sd && sd->sc.data[SC_ITEMBOOST]) // now rig the drop rate to never be over 90% unless it is originally >90%. drop_rate = max(drop_rate,cap_value((int)(0.5+drop_rate*(sd->sc.data[SC_ITEMBOOST]->val1)/100.),0,9000)); - +#if RRMODE + if( drop_modifier != 100 ) + drop_rate = drop_rate * drop_modifier / 100; +#endif // attempt to drop the item if (rand() % 10000 >= drop_rate) continue; @@ -2437,8 +2449,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) delete_timer(md->deletetimer,mob_timer_delete); md->deletetimer = INVALID_TIMER; } - - mob_deleteslave(md); + /** + * Only loops if necessary (e.g. a poring would never need to loop) + **/ + if( md->can_summon ) + mob_deleteslave(md); map_freeblock_unlock(); @@ -2700,6 +2715,10 @@ int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id) if(mobdb_checkid(value[0]) == 0) return 0; + /** + * Flags this monster is able to summon; saves a worth amount of memory upon deletion + **/ + md2->can_summon = 1; while(count < 5 && mobdb_checkid(value[count])) count++; if(count < 1) return 0; @@ -3574,7 +3593,8 @@ static bool mob_parse_dbrow(char** str) db->dropitem[i].p = 0; //No drop. continue; } - type = itemdb_type(db->dropitem[i].nameid); + id = itemdb_search(db->dropitem[i].nameid); + type = id->type; rate = atoi(str[k+1]); if( (class_ >= 1324 && class_ <= 1363) || (class_ >= 1938 && class_ <= 1946) ) { //Treasure box drop rates [Skotlex] @@ -3618,7 +3638,6 @@ static bool mob_parse_dbrow(char** str) //calculate and store Max available drop chance of the item 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 == -1 || (id->maxchance < db->dropitem[i].p) ) { id->maxchance = db->dropitem[i].p; //item has bigger drop chance or sold in shops } diff --git a/src/map/mob.h b/src/map/mob.h index 9e86b8d63..2c6d882c9 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -161,6 +161,11 @@ struct mob_data { short skillidx; unsigned int skilldelay[MAX_MOBSKILL]; char npc_event[EVENT_NAME_LENGTH]; + /** + * Did this monster summon something? + * Used to flag summon deletions, saves a worth amount of memory + **/ + bool can_summon : 1; }; diff --git a/src/map/npc.c b/src/map/npc.c index 897475f73..880626344 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -96,7 +96,7 @@ static struct script_event_s struct view_data* npc_get_viewdata(int class_) { //Returns the viewdata for normal npc classes. - if (class_ == INVISIBLE_CLASS) + if( class_ == HIDDEN_WARP_CLASS || class_ == INVISIBLE_CLASS ) return &npc_viewdb[0]; if (npcdb_checkid(class_) || class_ == WARP_CLASS) return &npc_viewdb[class_]; @@ -199,7 +199,33 @@ struct npc_data* npc_name2id(const char* name) { return (struct npc_data *) strdb_get(npcname_db, name); } - +/** + * For the Secure NPC Timeout option (check config/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT +/** + * Timer to check for idle time and timeout the dialog if necessary + **/ +int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t data) { + struct map_session_data* sd = NULL; + if( (sd = map_id2sd(id)) == NULL || !sd->npc_id ) + return 0;//Not logged in anymore OR no longer attached to a npc + + if( DIFF_TICK(tick,sd->npc_idle_tick) > (SECURE_NPCTIMEOUT*1000) ) { + /** + * If we still have the NPC script attached, tell it to stop. + **/ + if( sd->st ) + sd->st->state = END; + /** + * This guy's been idle for longer than allowed, close him. + **/ + clif_scriptclose(sd,sd->npc_id); + } else //Create a new instance of ourselves to continue + sd->npc_idle_timer = add_timer(gettick() + (SECURE_NPCTIMEOUT_INTERVAL*1000),npc_rr_secure_timeout_timer,sd->bl.id,0); + return 0; +} +#endif /*========================================== * イベントキューのイベント処理 *------------------------------------------*/ @@ -1114,6 +1140,15 @@ int npc_scriptcont(struct map_session_data* sd, int id) return 1; } } + /** + * For the Secure NPC Timeout option (check config/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + /** + * Update the last NPC iteration + **/ + sd->npc_idle_tick = gettick(); +#endif run_script_main(sd->st); return 0; @@ -1389,14 +1424,28 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_list, struct npc_data* nd) { char npc_ev[EVENT_NAME_LENGTH]; - int i, idx; + char card_slot[NAME_LENGTH]; + int i, j, idx; int key_nameid = 0; int key_amount = 0; + int key_refine = 0; + int key_attribute = 0; + int key_identify = 0; + int key_card = 0; // discard old contents script_cleararray_pc(sd, "@sold_nameid", (void*)0); script_cleararray_pc(sd, "@sold_quantity", (void*)0); + script_cleararray_pc(sd, "@sold_refine", (void*)0); + script_cleararray_pc(sd, "@sold_attribute", (void*)0); + script_cleararray_pc(sd, "@sold_identify", (void*)0); + for( j = 0; MAX_SLOTS > j; j++ ) + {// clear each of the card slot entries + snprintf(card_slot, sizeof(card_slot), "@sold_card%d", j + 1); + script_cleararray_pc(sd, card_slot, (void*)0); + } + // save list of to be sold items for( i = 0; i < n; i++ ) { @@ -1404,6 +1453,19 @@ static int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* script_setarray_pc(sd, "@sold_nameid", i, (void*)(intptr_t)sd->status.inventory[idx].nameid, &key_nameid); script_setarray_pc(sd, "@sold_quantity", i, (void*)(intptr_t)item_list[i*2+1], &key_amount); + + if( itemdb_isequip(sd->status.inventory[idx].nameid) ) + {// process equipment based information into the arrays + script_setarray_pc(sd, "@sold_refine", i, (void*)(intptr_t)sd->status.inventory[idx].refine, &key_refine); + script_setarray_pc(sd, "@sold_attribute", i, (void*)(intptr_t)sd->status.inventory[idx].attribute, &key_attribute); + script_setarray_pc(sd, "@sold_identify", i, (void*)(intptr_t)sd->status.inventory[idx].identify, &key_identify); + + for( j = 0; MAX_SLOTS > j; j++ ) + {// store each of the cards from the equipment in the array + snprintf(card_slot, sizeof(card_slot), "@sold_card%d", j + 1); + script_setarray_pc(sd, card_slot, i, (void*)(intptr_t)sd->status.inventory[idx].card[j], &key_card); + } + } } // invoke event @@ -3058,8 +3120,11 @@ static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, con map[m].flag.sakura=state; else if (!strcmpi(w3,"leaves")) map[m].flag.leaves=state; - else if (!strcmpi(w3,"rain")) - map[m].flag.rain=state; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //else if (!strcmpi(w3,"rain")) + // map[m].flag.rain=state; else if (!strcmpi(w3,"nightenabled")) map[m].flag.nightenabled=state; else if (!strcmpi(w3,"nogo")) diff --git a/src/map/npc.h b/src/map/npc.h index 29e3d79cb..ac411697e 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -69,15 +69,19 @@ struct npc_data { #define START_NPC_NUM 110000000 -#define WARP_CLASS 45 -#define WARP_DEBUG_CLASS 722 -#define FLAG_CLASS 722 -#define INVISIBLE_CLASS 32767 +enum actor_classes +{ + WARP_CLASS = 45, + HIDDEN_WARP_CLASS = 139, // server-side only, never sent to client + WARP_DEBUG_CLASS = 722, + FLAG_CLASS = 722, + INVISIBLE_CLASS = 32767, +}; #define MAX_NPC_CLASS 1000 //Checks if a given id is a valid npc id. [Skotlex] //Since new npcs are added all the time, the max valid value is the one before the first mob (Scorpion = 1001) -#define npcdb_checkid(id) ( ( (id) >= 46 && (id) <= 125) || (id) == 139 || ( (id) > 400 && (id) < MAX_NPC_CLASS ) || (id) == INVISIBLE_CLASS ) +#define npcdb_checkid(id) ( ( (id) >= 46 && (id) <= 125) || (id) == HIDDEN_WARP_CLASS || ( (id) > 400 && (id) < MAX_NPC_CLASS ) || (id) == INVISIBLE_CLASS ) #ifdef PCRE_SUPPORT void npc_chat_finalize(struct npc_data* nd); @@ -153,4 +157,11 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po extern struct npc_data* fake_nd; +/** + * For the Secure NPC Timeout option (check config/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT +int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t data); +#endif + #endif /* _NPC_H_ */ diff --git a/src/map/party.c b/src/map/party.c index 88f53bcb0..a8a524046 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -28,8 +28,8 @@ #include <string.h> -static DBMap* party_db; // int party_id -> struct party_data* -static DBMap* party_booking_db; // Party Booking [Spiria] +static DBMap* party_db; // int party_id -> struct party_data* (releases data) +static DBMap* party_booking_db; // int char_id -> struct party_booking_ad_info* (releases data) // Party Booking [Spiria] static unsigned long party_booking_nextid = 1; int party_send_xy_timer(int tid, unsigned int tick, int id, intptr_t data); @@ -38,18 +38,36 @@ int party_send_xy_timer(int tid, unsigned int tick, int id, intptr_t data); * Fills the given party_member structure according to the sd provided. * Used when creating/adding people to a party. [Skotlex] *------------------------------------------*/ -static void party_fill_member(struct party_member *member, struct map_session_data *sd) +static void party_fill_member(struct party_member* member, struct map_session_data* sd, unsigned int leader) { member->account_id = sd->status.account_id; member->char_id = sd->status.char_id; - memcpy(member->name, sd->status.name, NAME_LENGTH); + safestrncpy(member->name, sd->status.name, NAME_LENGTH); member->class_ = sd->status.class_; member->map = sd->mapindex; member->lv = sd->status.base_level; member->online = 1; - member->leader = 0; + member->leader = leader; } + +/// Get the member_id of a party member. +/// Return -1 if not in party. +int party_getmemberid(struct party_data* p, struct map_session_data* sd) +{ + int member_id; + nullpo_retr(-1, p); + if( sd == NULL ) + return -1;// no player + ARR_FIND(0, MAX_PARTY, member_id, + p->party.member[member_id].account_id == sd->status.account_id && + p->party.member[member_id].char_id == sd->status.char_id); + if( member_id == MAX_PARTY ) + return -1;// not found + return member_id; +} + + /*========================================== * Request an available sd of this party *------------------------------------------*/ @@ -72,6 +90,8 @@ static TBL_PC* party_sd_check(int party_id, int account_id, int char_id) if (!(sd && sd->status.char_id == char_id)) return NULL; + if( sd->status.party_id == 0 ) + sd->status.party_id = party_id;// auto-join if not in a party if (sd->status.party_id != party_id) { //If player belongs to a different party, kick him out. intif_party_leave(party_id,account_id,char_id); @@ -143,8 +163,7 @@ int party_create(struct map_session_data *sd,char *name,int item,int item2) sd->party_creating = true; - party_fill_member(&leader, sd); - leader.leader = 1; + party_fill_member(&leader, sd, 1); intif_create_party(&leader,name,item,item2); return 0; @@ -175,63 +194,26 @@ void party_created(int account_id,int char_id,int fail,int party_id,char *name) } -int party_request_info(int party_id) +int party_request_info(int party_id, int char_id) { - return intif_request_partyinfo(party_id); + return intif_request_partyinfo(party_id, char_id); } -/// Checks if each char having a party actually belongs to that party. -/// If check fails, the char gets marked as 'not in a party'. -int party_check_member(struct party *p) +/// Invoked (from char-server) when the party info is not found. +int party_recv_noinfo(int party_id, int char_id) { - int i; - struct map_session_data *sd; - struct s_mapiterator* iter; - - nullpo_ret(p); + struct map_session_data* sd; - iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) + party_broken(party_id); + if( char_id != 0 )// requester { - if( sd->status.party_id != p->party_id ) - continue; - - ARR_FIND( 0, MAX_PARTY, i, p->member[i].account_id == sd->status.account_id && p->member[i].char_id == sd->status.char_id ); - if( i == MAX_PARTY ) - { - ShowWarning("party_check_member: '%s' (acc:%d) is not member of party '%s' (id:%d)\n",sd->status.name,sd->status.account_id,p->name,p->party_id); + sd = map_charid2sd(char_id); + if( sd && sd->status.party_id == party_id ) sd->status.party_id = 0; - } - } - mapit_free(iter); - - return 0; -} - -/// Marks all chars belonging to this party as 'not in a party'. -int party_recv_noinfo(int party_id) -{ - struct map_session_data *sd; - struct s_mapiterator* iter; - - iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) - { - if( sd->status.party_id == party_id ) - sd->status.party_id = 0; // erase party } - mapit_free(iter); - return 0; } -static void* create_party(DBKey key, va_list args) -{ - struct party_data *p; - p=(struct party_data *)aCalloc(1,sizeof(struct party_data)); - return p; -} - static void party_check_state(struct party_data *p) { int i; @@ -259,42 +241,92 @@ static void party_check_state(struct party_data *p) } } -int party_recv_info(struct party *sp) +int party_recv_info(struct party* sp, int char_id) { - struct party_data *p; + struct party_data* p; + struct party_member* member; + struct map_session_data* sd; + int removed[MAX_PARTY];// member_id in old data + int removed_count = 0; + int added[MAX_PARTY];// member_id in new data + int added_count = 0; int i; - bool party_new = false; + int member_id; nullpo_ret(sp); - p = (struct party_data*)idb_ensure(party_db, sp->party_id, create_party); - if (!p->party.party_id) //party just received. + p = (struct party_data*)idb_get(party_db, sp->party_id); + if( p != NULL )// diff members + { + for( member_id = 0; member_id < MAX_PARTY; ++member_id ) + { + member = &p->party.member[member_id]; + if( member->char_id == 0 ) + continue;// empty + ARR_FIND(0, MAX_PARTY, i, + sp->member[i].account_id == member->account_id && + sp->member[i].char_id == member->char_id); + if( i == MAX_PARTY ) + removed[removed_count++] = member_id; + } + for( member_id = 0; member_id < MAX_PARTY; ++member_id ) + { + member = &sp->member[member_id]; + if( member->char_id == 0 ) + continue;// empty + ARR_FIND(0, MAX_PARTY, i, + p->party.member[i].account_id == member->account_id && + p->party.member[i].char_id == member->char_id); + if( i == MAX_PARTY ) + added[added_count++] = member_id; + } + } + else + { + for( member_id = 0; member_id < MAX_PARTY; ++member_id ) + if( sp->member[member_id].char_id != 0 ) + added[added_count++] = member_id; + CREATE(p, struct party_data, 1); + idb_put(party_db, sp->party_id, p); + } + while( removed_count > 0 )// no longer in party { - party_new = true; - party_check_member(sp); + member_id = removed[--removed_count]; + sd = p->data[member_id].sd; + if( sd == NULL ) + continue;// not online + party_member_withdraw(sp->party_id, sd->status.account_id, sd->status.char_id); } - memcpy(&p->party,sp,sizeof(struct party)); + memcpy(&p->party, sp, sizeof(struct party)); memset(&p->state, 0, sizeof(p->state)); memset(&p->data, 0, sizeof(p->data)); - for(i=0;i<MAX_PARTY;i++){ - if (!p->party.member[i].account_id) - continue; - p->data[i].sd = party_sd_check(p->party.party_id, p->party.member[i].account_id, p->party.member[i].char_id); + for( member_id = 0; member_id < MAX_PARTY; member_id++ ) + { + member = &p->party.member[member_id]; + if ( member->char_id == 0 ) + continue;// empty + p->data[member_id].sd = party_sd_check(sp->party_id, member->account_id, member->char_id); } party_check_state(p); - if (party_new) { - //Send party data to all players. - struct map_session_data *sd; - for(i=0;i<MAX_PARTY;i++){ - sd = p->data[i].sd; - if(!sd) continue; - clif_charnameupdate(sd); //Update other people's display. [Skotlex] - clif_party_member_info(p,sd); - clif_party_option(p,sd,0x100); - clif_party_info(p,NULL); - } + while( added_count > 0 )// new in party + { + member_id = added[--added_count]; + sd = p->data[member_id].sd; + if( sd == NULL ) + continue;// not online + clif_charnameupdate(sd); //Update other people's display. [Skotlex] + clif_party_member_info(p,sd); + clif_party_option(p,sd,0x100); + clif_party_info(p,NULL); + if( p->instance_id != 0 ) + clif_instance_join(sd->fd, p->instance_id); + } + if( char_id != 0 )// requester + { + sd = map_charid2sd(char_id); + if( sd && sd->status.party_id == sp->party_id && party_getmemberid(p,sd) == -1 ) + sd->status.party_id = 0;// was not in the party } - return 0; } @@ -306,8 +338,8 @@ int party_invite(struct map_session_data *sd,struct map_session_data *tsd) nullpo_ret(sd); if( ( p = party_search(sd->status.party_id) ) == NULL ) return 0; - if( tsd == NULL) { //TODO: Find the correct reply packet. - clif_displaymessage(sd->fd, msg_txt(3)); + if( tsd == NULL) { + clif_party_inviteack(sd, "", 7); return 0; } @@ -385,7 +417,7 @@ void party_reply_invite(struct map_session_data *sd,int party_id,int flag) if( flag == 1 && !sd->party_creating && !sd->party_joining ) {// accepted and allowed sd->party_joining = true; - party_fill_member(&member, sd); + party_fill_member(&member, sd, 0); intif_party_addmember(sd->party_invite, &member); } else @@ -407,7 +439,7 @@ void party_member_joined(struct map_session_data *sd) int i; if (!p) { - party_request_info(sd->status.party_id); + party_request_info(sd->status.party_id, sd->status.char_id); return; } ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd->status.account_id && p->party.member[i].char_id == sd->status.char_id ); @@ -456,14 +488,6 @@ int party_member_added(int party_id,int account_id,int char_id, int flag) sd->status.party_id = party_id; - ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == 0 ); - if (i < MAX_PARTY) { - //TODO: This is a hack to allow the following clif calls to send the data to the new player. - //The correct player data is set when party_recv_info arrives soon afterwards. - party_fill_member(&p->party.member[i], sd); - p->data[i].sd = sd; - } - clif_party_member_info(p,sd); clif_party_option(p,sd,0x100); clif_party_info(p,sd); @@ -542,7 +566,7 @@ int party_member_withdraw(int party_id, int account_id, int char_id) ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id ); if( i < MAX_PARTY ) { - clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x00); + clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->data[i], 0, sizeof(p->data[0])); p->party.count--; @@ -849,11 +873,16 @@ int party_send_xy_timer(int tid, unsigned int tick, int id, intptr_t data) for( p = (struct party_data*)iter->first(iter,NULL); iter->exists(iter); p = (struct party_data*)iter->next(iter,NULL) ) { int i; + + if( !p->party.count ) + {// no online party members so do not iterate + continue; + } + // for each member of this party, for( i = 0; i < MAX_PARTY; i++ ) { - //struct map_session_data* sd = p->data[i].sd; - struct map_session_data* sd = map_charid2sd(p->party.member[i].char_id); //temporary crashfix + struct map_session_data* sd = p->data[i].sd; if( !sd ) continue; if( p->data[i].x != sd->bl.x || p->data[i].y != sd->bl.y ) @@ -888,13 +917,64 @@ int party_send_xy_clear(struct party_data *p) } return 0; } - +#if RRMODE +/** + * Renewal Drop Earning Modifier + **/ +int party_renewal_drop_mod(int diff) { + if( diff >= -10 && diff <= 5 ) + return 100;//no change. + if( diff > 0 ) { + if( diff > 5 && diff < 10 ) + return 90; + if( diff > 9 && diff < 15 ) + return 75; + if( diff > 14 && diff < 30 ) + return 60; + } else { + if( diff <= -10 && diff <= -14 ) + return 75;//75% + } + //other chases: 50% + return 50; +} +/** + * Renewal Experience Earning Mode + **/ +void party_renewal_exp_mod(unsigned int *base_exp, unsigned int *job_exp, int lvl, int moblvl) { + int diff = lvl - moblvl, boost = 0; + //-2 ~ +5: 100% + if( diff >= -2 && diff <= 5 ) + return;//we don't change anything, it's 100% boost + //-3 ~ -10: +5% boost for each + if( diff >= -10 && diff <= -3 ) + boost = 100 + (( -diff * 5 ) - 15 ); + // 40% boost if difference is <= -10 + else if ( diff <= -10 ) + boost = 40; + else { + boost = ( diff > 5 && diff < 11 ) ? 95 : + ( diff > 10 && diff < 16 ) ? 90 : + ( diff > 15 && diff < 21 ) ? 85 : + ( diff > 20 && diff < 26 ) ? 60 : + ( diff > 25 && diff < 31 ) ? 35 : + 10; + } + if( *base_exp ) + *base_exp = (unsigned int)cap_value(*base_exp * boost / 100, 1, UINT_MAX); + if( *job_exp ) + *job_exp = (unsigned int)cap_value(*job_exp * boost / 100, 1, UINT_MAX); + return; +} +#endif // exp share and added zeny share [Valaris] int party_exp_share(struct party_data* p, struct block_list* src, unsigned int base_exp, unsigned int job_exp, int zeny) { struct map_session_data* sd[MAX_PARTY]; unsigned int i, c; - +#if RRMODE + int src_lvl = status_get_lv(src); +#endif nullpo_ret(p); // count the number of players eligible for exp sharing @@ -921,8 +1001,10 @@ int party_exp_share(struct party_data* p, struct block_list* src, unsigned int b zeny = (unsigned int) cap_value(zeny * bonus/100, INT_MIN, INT_MAX); } - for (i = 0; i < c; i++) - { + for (i = 0; i < c; i++) { +#if RRMODE + party_renewal_exp_mod(&base_exp,&job_exp,sd[i]->status.base_level,src_lvl); +#endif pc_gainexp(sd[i], src, base_exp, job_exp, false); if (zeny) // zeny from mobs [Valaris] pc_getzeny(sd[i],zeny); diff --git a/src/map/party.h b/src/map/party.h index 1c59197b3..4918d9a3a 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -51,11 +51,12 @@ void do_init_party(void); void do_final_party(void); struct party_data* party_search(int party_id); struct party_data* party_searchname(const char* str); +int party_getmemberid(struct party_data* p, struct map_session_data* sd); struct map_session_data* party_getavailablesd(struct party_data *p); int party_create(struct map_session_data *sd,char *name, int item, int item2); void party_created(int account_id,int char_id,int fail,int party_id,char *name); -int party_request_info(int party_id); +int party_request_info(int party_id, int char_id); int party_invite(struct map_session_data *sd,struct map_session_data *tsd); void party_member_joined(struct map_session_data *sd); int party_member_added(int party_id,int account_id,int char_id,int flag); @@ -63,8 +64,8 @@ int party_leave(struct map_session_data *sd); int party_removemember(struct map_session_data *sd,int account_id,char *name); int party_member_withdraw(int party_id,int account_id,int char_id); void party_reply_invite(struct map_session_data *sd,int party_id,int flag); -int party_recv_noinfo(int party_id); -int party_recv_info(struct party *sp); +int party_recv_noinfo(int party_id, int char_id); +int party_recv_info(struct party* sp, int char_id); int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv); int party_broken(int party_id); int party_optionchanged(int party_id,int account_id,int exp,int item,int flag); @@ -91,4 +92,9 @@ 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); +#if RRMODE +void party_renewal_exp_mod(unsigned int *base_exp, unsigned int *job_exp, int lvl, int moblvl); +int party_renewal_drop_mod(int diff); +#endif + #endif /* _PARTY_H_ */ diff --git a/src/map/pc.c b/src/map/pc.c index 440c684f4..21588a12d 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -395,7 +395,7 @@ int pc_makesavestatus(struct map_session_data *sd) //Only copy the Cart/Peco/Falcon options, the rest are handled via //status change load/saving. [Skotlex] - sd->status.option = sd->sc.option&(OPTION_CART|OPTION_FALCON|OPTION_RIDING|OPTION_DRAGON); + sd->status.option = sd->sc.option&(OPTION_CART|OPTION_FALCON|OPTION_RIDING|OPTION_DRAGON|OPTION_WUGRIDER|OPTION_WUG|OPTION_MADOGEAR|OPTION_MOUNTING); if (sd->sc.data[SC_JAILED]) { //When Jailed, do not move last point. @@ -765,10 +765,14 @@ int pc_isequip(struct map_session_data *sd,int n) //Not equipable by class. [Skotlex] if (!(1<<(sd->class_&MAPID_BASEMASK)&item->class_base[(sd->class_&JOBL_2_1)?1:((sd->class_&JOBL_2_2)?2:0)])) return 0; - - //Not equipable by upper class. [Skotlex] - if(!(1<<((sd->class_&JOBL_UPPER)?1:((sd->class_&JOBL_BABY)?2:0))&item->class_upper)) + //Not usable by upper class. [Inkfish] + while( 1 ) { + if( item->class_upper&1 && !(sd->class_&(JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) ) break; + if( item->class_upper&2 && sd->class_&(JOBL_UPPER|JOBL_THIRD) ) break; + if( item->class_upper&4 && sd->class_&JOBL_BABY ) break; + if( item->class_upper&8 && sd->class_&JOBL_THIRD ) break; return 0; + } return 1; } @@ -825,7 +829,17 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->invincible_timer = INVALID_TIMER; sd->npc_timer_id = INVALID_TIMER; sd->pvp_timer = INVALID_TIMER; - + /** + * For the Secure NPC Timeout option (check config/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + /** + * Initialize to defaults/expected + **/ + sd->npc_idle_timer = INVALID_TIMER; + sd->npc_idle_tick = tick; +#endif + sd->canuseitem_tick = tick; sd->canusecashfood_tick = tick; sd->canequip_tick = tick; @@ -915,7 +929,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim if (battle_config.display_version == 1){ char buf[256]; - sprintf(buf, "eAthena SVN version: %s", get_svn_revision()); + sprintf(buf, "SVN version: %s", get_svn_revision()); clif_displaymessage(sd->fd, buf); } @@ -1814,14 +1828,14 @@ int pc_bonus(struct map_session_data *sd,int type,int val) break; case SP_BASE_ATK: if(sd->state.lr_flag != 2) { - bonus = status->equipment_atk + val; - status->equipment_atk = cap_value(bonus, 0, USHRT_MAX); + bonus = status->batk + val; + status->batk = cap_value(bonus, 0, USHRT_MAX); } break; case SP_DEF1: if(sd->state.lr_flag != 2) { bonus = status->def + val; - status->def = cap_value(bonus, SHRT_MIN, SHRT_MAX); + status->def = cap_value(bonus, CHAR_MIN, CHAR_MAX); } break; case SP_DEF2: @@ -1833,7 +1847,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val) case SP_MDEF1: if(sd->state.lr_flag != 2) { bonus = status->mdef + val; - status->mdef = cap_value(bonus, SHRT_MIN, SHRT_MAX); + status->mdef = cap_value(bonus, CHAR_MIN, CHAR_MAX); } break; case SP_MDEF2: @@ -1925,10 +1939,6 @@ int pc_bonus(struct map_session_data *sd,int type,int val) if(sd->state.lr_flag != 2) sd->castrate+=val; break; - case SP_FIXEDCASTRATE: - if(sd->state.lr_flag != 2) - sd->fixedcastrate+=val; - break; case SP_MAXHPRATE: if(sd->state.lr_flag != 2) sd->hprate+=val; @@ -1976,7 +1986,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val) break; case SP_ASPD_RATE: //Stackable increase - Made it linear as per rodatazone if(sd->state.lr_flag != 2) - status->aspd_rate -= 10 * val; + status->aspd_rate -= 10*val; break; case SP_HP_RECOV_RATE: if(sd->state.lr_flag != 2) @@ -2010,14 +2020,6 @@ int pc_bonus(struct map_session_data *sd,int type,int val) if(sd->state.lr_flag != 2) sd->matk_rate += val; break; - case SP_WEAPON_MATK: - if(sd->state.lr_flag != 2) - sd->weapon_matk += val; - break; - case SP_EQUIPMENT_MATK: - if(sd->state.lr_flag != 2) - sd->equipment_matk += val; - break; case SP_IGNORE_DEF_ELE: if(val >= ELE_MAX) { ShowError("pc_bonus: SP_IGNORE_DEF_ELE: Invalid element %d\n", val); @@ -2713,23 +2715,6 @@ int pc_bonus2(struct map_session_data *sd,int type,int type2,int val) } break; - case SP_FIXEDCASTRATE: - if(sd->state.lr_flag == 2) - break; - ARR_FIND(0, ARRAYLENGTH(sd->fixedskillcast), i, sd->fixedskillcast[i].id == 0 || sd->fixedskillcast[i].id == type2); - if (i == ARRAYLENGTH(sd->fixedskillcast)) - { //Better mention this so the array length can be updated. [Skotlex] - ShowDebug("run_script: bonus2 bFixedCastRate reached it's limit (%d skills per character), bonus skill %d (+%d%%) lost.\n", ARRAYLENGTH(sd->fixedskillcast), type2, val); - break; - } - if(sd->fixedskillcast[i].id == type2) - sd->fixedskillcast[i].val += val; - else { - sd->fixedskillcast[i].id = type2; - sd->fixedskillcast[i].val = val; - } - break; - case SP_HP_LOSS_RATE: if(sd->state.lr_flag != 2) { sd->hp_loss.value = type2; @@ -3199,25 +3184,19 @@ int pc_modifysellvalue(struct map_session_data *sd,int orig_value) int pc_checkadditem(struct map_session_data *sd,int nameid,int amount) { int i; - struct item_data* data; nullpo_ret(sd); if(amount > MAX_AMOUNT) return ADDITEM_OVERAMOUNT; - data = itemdb_search(nameid); - - if(!itemdb_isstackable2(data)) + if(!itemdb_isstackable(nameid)) return ADDITEM_NEW; - if( data->stack.inventory && amount > data->stack.amount ) - return ADDITEM_OVERAMOUNT; - for(i=0;i<MAX_INVENTORY;i++){ // FIXME: This does not consider the checked item's cards, thus could check a wrong slot for stackability. if(sd->status.inventory[i].nameid==nameid){ - if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) ) + if(sd->status.inventory[i].amount+amount > MAX_AMOUNT) return ADDITEM_OVERAMOUNT; return ADDITEM_EXIST; } @@ -3411,16 +3390,18 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount) return 5; data = itemdb_search(item_data->nameid); - - if( data->stack.inventory && amount > data->stack.amount ) - {// item stack limitation - return 7; - } - w = data->weight*amount; if(sd->weight + w > sd->max_weight) return 2; - + if( itemdb_is_rune(item_data->nameid) ) { + int rune = pc_search_inventory(sd,item_data->nameid); + if( ( rune >= 0 && sd->status.inventory[rune].amount + amount > MAX_RUNE ) || + ( rune == -1 && amount > MAX_RUNE ) + ) { + clif_msgtable(sd->fd,0x61b); + return 1; + } + } i = MAX_INVENTORY; if( itemdb_isstackable2(data) && item_data->expire_time == 0 ) @@ -3429,7 +3410,7 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount) { if( sd->status.inventory[i].nameid == item_data->nameid && memcmp(&sd->status.inventory[i].card, &item_data->card, sizeof(item_data->card)) == 0 ) { - if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) ) + if( amount > MAX_AMOUNT - sd->status.inventory[i].amount ) return 5; sd->status.inventory[i].amount += amount; clif_additem(sd,i,amount,0); @@ -3615,7 +3596,6 @@ int pc_isUseitem(struct map_session_data *sd,int n) //Not consumable item if( item->type != IT_HEALING && item->type != IT_USABLE && item->type != IT_CASH ) return 0; - // This contradicts official behavior. Items with no scripts should be consumed regardless. [L0ne_W0lf] if( !item->script ) //if it has no script, you can't really consume it! return 0; @@ -3694,6 +3674,7 @@ int pc_isUseitem(struct map_session_data *sd,int n) if( nameid == 12243 && sd->md->db->lv < 80 ) return 0; break; + case 12213: //Neuralizer if( !map[sd->bl.m].flag.reset ) return 0; @@ -3703,7 +3684,15 @@ int pc_isUseitem(struct map_session_data *sd,int n) if( nameid >= 12153 && nameid <= 12182 && sd->md != NULL ) return 0; // Mercenary Scrolls - if (nameid >= 12725 && nameid <= 12733 && !pc_isUseitem_check_runeskill(sd, sd->status.inventory[n].nameid) ) + /** + * Only Rune Knights may use runes + **/ + if( itemdb_is_rune(nameid) && (sd->class_&MAPID_THIRDMASK) != MAPID_RUNE_KNIGHT ) + return 0; + /** + * Only GCross may use poisons + **/ + else if( itemdb_is_poison(nameid) && (sd->class_&MAPID_THIRDMASK) != MAPID_GUILLOTINE_CROSS ) return 0; //added item_noequip.txt items check by Maya&[Lupus] @@ -3729,13 +3718,14 @@ int pc_isUseitem(struct map_session_data *sd,int n) (item->class_base[sd->class_&JOBL_2_1?1:(sd->class_&JOBL_2_2?2:0)]) )) return 0; - - //Not usable by upper class. [Skotlex] - if(!( - (1<<(sd->class_&JOBL_UPPER?1:(sd->class_&JOBL_BABY?2:0))) & - item->class_upper - )) + //Not usable by upper class. [Inkfish] + while( 1 ) { + if( item->class_upper&1 && !(sd->class_&(JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) ) break; + if( item->class_upper&2 && sd->class_&(JOBL_UPPER|JOBL_THIRD) ) break; + if( item->class_upper&4 && sd->class_&JOBL_BABY ) break; + if( item->class_upper&8 && sd->class_&JOBL_THIRD ) break; return 0; + } //Dead Branch & Bloody Branch & Porings Box if((log_config.branch > 0) && (nameid == 604 || nameid == 12103 || nameid == 12109)) @@ -3744,45 +3734,6 @@ int pc_isUseitem(struct map_session_data *sd,int n) return 1; } - -int pc_isUseitem_check_runeskill(TBL_PC *sd, int nameid) -{ - struct { - int runeid; - int skillid; - } rune2skill_table[] = { - { 12725, RK_REFRESH }, - { 12726, RK_CRUSHSTRIKE }, - { 12727, RK_MILLENNIUMSHIELD }, - { 12728, RK_VITALITYACTIVATION }, - { 12729, RK_FIGHTINGSPIRIT }, - { 12730, RK_ABUNDANCE }, - { 12731, RK_GIANTGROWTH }, - { 12732, RK_STORMBLAST }, - { 12733, RK_STONEHARDSKIN }, - }; - - int i; - int skillid; - - nullpo_retr(0, sd); - - ARR_FIND(0, ARRAYLENGTH(rune2skill_table), i, rune2skill_table[i].runeid == nameid); - if ( i == ARRAYLENGTH(rune2skill_table) ) { - ShowError("pc_isUseitem_check_runeskill: rune %d skill not found.\n", nameid); - return 0; - } - - skillid = rune2skill_table[i].skillid; - if ( battle_config.rune_block_by_skill && skillnotok(skillid, sd) ) - return 0; - if ( battle_config.rune_block_by_status && status_skill2sc(skillid) != SC_NONE && sd->sc.data[status_skill2sc(skillid)] ) - return 0; - - return 1; -} - - /*========================================== * アイテムを使う *------------------------------------------*/ @@ -3904,11 +3855,6 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun return 1; data = itemdb_search(item_data->nameid); - if( data->stack.cart && amount > data->stack.amount ) - {// item stack limitation - return 1; - } - if( !itemdb_cancartstore(item_data, pc_isGM(sd)) ) { // Check item trade restrictions [Skotlex] clif_displaymessage (sd->fd, msg_txt(264)); @@ -3929,7 +3875,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun if( i < MAX_CART ) {// item already in cart, stack it - if( amount > MAX_AMOUNT - sd->status.cart[i].amount || ( data->stack.cart && amount > data->stack.amount - sd->status.cart[i].amount ) ) + if(sd->status.cart[i].amount+amount > MAX_AMOUNT) return 1; // no room sd->status.cart[i].amount+=amount; @@ -4575,21 +4521,6 @@ int pc_jobid2mapid(unsigned short b_class) case JOB_ALCHEMIST: return MAPID_ALCHEMIST; case JOB_ROGUE: return MAPID_ROGUE; case JOB_SOUL_LINKER: return MAPID_SOUL_LINKER; - //3_1 classes - case JOB_RUNE_KNIGHT: return MAPID_RUNE_KNIGHT; - case JOB_WARLOCK: return MAPID_WARLOCK; - case JOB_RANGER: return MAPID_RANGER; - case JOB_ARCHBISHOP: return MAPID_ARCHBISHOP; - case JOB_MECHANIC: return MAPID_MECHANIC; - case JOB_GUILLOTINE_CROSS: return MAPID_GUILLOTINE_CROSS; - //3_2 classes - case JOB_ROYAL_GUARD: return MAPID_ROYAL_GUARD; - case JOB_SORCERER: return MAPID_SORCERER; - case JOB_MINSTREL: - case JOB_WANDERER: return MAPID_MINSTRELWANDERER; - case JOB_SURA: return MAPID_SURA; - case JOB_GENETIC: return MAPID_GENETIC; - case JOB_SHADOW_CHASER: return MAPID_SHADOW_CHASER; //1st: advanced case JOB_NOVICE_HIGH: return MAPID_NOVICE_HIGH; case JOB_SWORDMAN_HIGH: return MAPID_SWORDMAN_HIGH; @@ -4613,21 +4544,6 @@ int pc_jobid2mapid(unsigned short b_class) case JOB_CHAMPION: return MAPID_CHAMPION; case JOB_CREATOR: return MAPID_CREATOR; case JOB_STALKER: return MAPID_STALKER; - //3_1 advanced - case JOB_RUNE_KNIGHT_H: return MAPID_RUNE_KNIGHT_H; - case JOB_WARLOCK_H: return MAPID_WARLOCK_H; - case JOB_RANGER_H: return MAPID_RANGER_H; - case JOB_ARCHBISHOP_H: return MAPID_ARCHBISHOP_H; - case JOB_MECHANIC_H: return MAPID_MECHANIC_H; - case JOB_GUILLOTINE_CROSS_H:return MAPID_GUILLOTINE_CROSS_H; - //3_2 advanced - case JOB_ROYAL_GUARD_H: return MAPID_ROYAL_GUARD_H; - case JOB_SORCERER_H: return MAPID_SORCERER_H; - case JOB_MINSTREL_H: - case JOB_WANDERER_H: return MAPID_MINSTRELWANDERER_H; - case JOB_SURA_H: return MAPID_SURA_H; - case JOB_GENETIC_H: return MAPID_GENETIC_H; - case JOB_SHADOW_CHASER_H: return MAPID_SHADOW_CHASER_H; //1-1 baby case JOB_BABY: return MAPID_BABY; case JOB_BABY_SWORDMAN: return MAPID_BABY_SWORDMAN; @@ -4652,21 +4568,36 @@ int pc_jobid2mapid(unsigned short b_class) case JOB_BABY_MONK: return MAPID_BABY_MONK; case JOB_BABY_ALCHEMIST: return MAPID_BABY_ALCHEMIST; case JOB_BABY_ROGUE: return MAPID_BABY_ROGUE; - //3_1 baby classes - case JOB_BABY_RUNE: return MAPID_BABY_RUNE; - case JOB_BABY_WARLOCK: return MAPID_BABY_WARLOCK; - case JOB_BABY_RANGER: return MAPID_BABY_RANGER; - case JOB_BABY_BISHOP: return MAPID_BABY_BISHOP; - case JOB_BABY_MECHANIC: return MAPID_BABY_MECHANIC; - case JOB_BABY_CROSS: return MAPID_BABY_CROSS; - //3_2 baby classes - case JOB_BABY_GUARD: return MAPID_BABY_GUARD; - case JOB_BABY_SORCERER: return MAPID_BABY_SORCERER; - case JOB_BABY_WANDERER: - case JOB_BABY_MINSTREL: return MAPID_BABY_MINSTRELWANDERER; - case JOB_BABY_SURA: return MAPID_BABY_SURA; - case JOB_BABY_GENETIC: return MAPID_BABY_GENETIC; - case JOB_BABY_CHASER: return MAPID_BABY_CHASER; + //3.1 non-trans + case JOB_RUNE_KNIGHT: return MAPID_RUNE_KNIGHT; + case JOB_WARLOCK: return MAPID_WARLOCK; + case JOB_RANGER: return MAPID_RANGER; + case JOB_ARCH_BISHOP: return MAPID_ARCH_BISHOP; + case JOB_MECHANIC: return MAPID_MECHANIC; + case JOB_GUILLOTINE_CROSS: return MAPID_GUILLOTINE_CROSS; + //3.1 trans + case JOB_RUNE_KNIGHT_T: return MAPID_RUNE_KNIGHT_T; + case JOB_WARLOCK_T: return MAPID_WARLOCK_T; + case JOB_RANGER_T: return MAPID_RANGER_T; + case JOB_ARCH_BISHOP_T: return MAPID_ARCH_BISHOP_T; + case JOB_MECHANIC_T: return MAPID_MECHANIC_T; + case JOB_GUILLOTINE_CROSS_T:return MAPID_GUILLOTINE_CROSS_T; + //3.2 non-trans + case JOB_ROYAL_GUARD: return MAPID_ROYAL_GUARD; + case JOB_SORCERER: return MAPID_SORCERER; + case JOB_MINSTREL: return MAPID_MINSTRELWANDERER; + case JOB_WANDERER: return MAPID_MINSTRELWANDERER; + case JOB_SURA: return MAPID_SURA; + case JOB_GENETIC: return MAPID_GENETIC; + case JOB_SHADOW_CHASER: return MAPID_SHADOW_CHASER; + //3.2 trans + case JOB_ROYAL_GUARD_T: return MAPID_ROYAL_GUARD_T; + case JOB_SORCERER_T: return MAPID_SORCERER_T; + case JOB_MINSTREL_T: return MAPID_MINSTRELWANDERER_T; + case JOB_WANDERER_T: return MAPID_MINSTRELWANDERER_T; + case JOB_SURA_T: return MAPID_SURA_T; + case JOB_GENETIC_T: return MAPID_GENETIC_T; + case JOB_SHADOW_CHASER_T: return MAPID_SHADOW_CHASER_T; default: return -1; } @@ -4708,20 +4639,6 @@ int pc_mapid2jobid(unsigned short class_, int sex) case MAPID_ALCHEMIST: return JOB_ALCHEMIST; case MAPID_ROGUE: return JOB_ROGUE; case MAPID_SOUL_LINKER: return JOB_SOUL_LINKER; - //3_1 classes - case MAPID_RUNE_KNIGHT: return JOB_RUNE_KNIGHT; - case MAPID_WARLOCK: return JOB_WARLOCK; - case MAPID_RANGER: return JOB_RANGER; - case MAPID_ARCHBISHOP: return JOB_ARCHBISHOP; - case MAPID_MECHANIC: return JOB_MECHANIC; - case MAPID_GUILLOTINE_CROSS:return JOB_GUILLOTINE_CROSS; - //3_2 classes - case MAPID_ROYAL_GUARD: return JOB_ROYAL_GUARD; - case MAPID_SORCERER: return JOB_SORCERER; - case MAPID_MINSTRELWANDERER:return sex?JOB_MINSTREL:JOB_WANDERER; - case MAPID_SURA: return JOB_SURA; - case MAPID_GENETIC: return JOB_GENETIC; - case MAPID_SHADOW_CHASER: return JOB_SHADOW_CHASER; //1st: advanced case MAPID_NOVICE_HIGH: return JOB_NOVICE_HIGH; case MAPID_SWORDMAN_HIGH: return JOB_SWORDMAN_HIGH; @@ -4744,20 +4661,6 @@ int pc_mapid2jobid(unsigned short class_, int sex) case MAPID_CHAMPION: return JOB_CHAMPION; case MAPID_CREATOR: return JOB_CREATOR; case MAPID_STALKER: return JOB_STALKER; - //3_1 advanced - case MAPID_RUNE_KNIGHT_H: return JOB_RUNE_KNIGHT_H; - case MAPID_WARLOCK_H: return JOB_WARLOCK_H; - case MAPID_RANGER_H: return JOB_RANGER_H; - case MAPID_ARCHBISHOP_H: return JOB_ARCHBISHOP_H; - case MAPID_MECHANIC_H: return JOB_MECHANIC_H; - case MAPID_GUILLOTINE_CROSS_H: return JOB_GUILLOTINE_CROSS_H; - //3_2 advanced - case MAPID_ROYAL_GUARD_H: return JOB_ROYAL_GUARD_H; - case MAPID_SORCERER_H: return JOB_SORCERER_H; - case MAPID_MINSTRELWANDERER_H: return sex?JOB_MINSTREL_H:JOB_WANDERER_H; - case MAPID_SURA_H: return JOB_SURA_H; - case MAPID_GENETIC_H: return JOB_GENETIC_H; - case MAPID_SHADOW_CHASER_H: return JOB_SHADOW_CHASER_H; //1-1 baby case MAPID_BABY: return JOB_BABY; case MAPID_BABY_SWORDMAN: return JOB_BABY_SWORDMAN; @@ -4781,20 +4684,34 @@ int pc_mapid2jobid(unsigned short class_, int sex) case MAPID_BABY_MONK: return JOB_BABY_MONK; case MAPID_BABY_ALCHEMIST: return JOB_BABY_ALCHEMIST; case MAPID_BABY_ROGUE: return JOB_BABY_ROGUE; - //3_1 baby classes - case MAPID_BABY_RUNE: return JOB_BABY_RUNE; - case MAPID_BABY_WARLOCK: return JOB_BABY_WARLOCK; - case MAPID_BABY_RANGER: return JOB_BABY_RANGER; - case MAPID_BABY_BISHOP: return JOB_BABY_BISHOP; - case MAPID_BABY_MECHANIC: return JOB_BABY_MECHANIC; - case MAPID_BABY_CROSS: return JOB_BABY_CROSS; - //3_2 baby classes - case MAPID_BABY_GUARD: return JOB_BABY_GUARD; - case MAPID_BABY_SORCERER: return JOB_BABY_SORCERER; - case MAPID_BABY_MINSTRELWANDERER: return sex?JOB_BABY_MINSTREL:JOB_BABY_WANDERER; - case MAPID_BABY_SURA: return JOB_BABY_SURA; - case MAPID_BABY_GENETIC: return JOB_BABY_GENETIC; - case MAPID_BABY_CHASER: return JOB_BABY_CHASER; + //3.1 non-trans + case MAPID_RUNE_KNIGHT: return JOB_RUNE_KNIGHT; + case MAPID_WARLOCK: return JOB_WARLOCK; + case MAPID_RANGER: return JOB_RANGER; + case MAPID_ARCH_BISHOP: return JOB_ARCH_BISHOP; + case MAPID_MECHANIC: return JOB_MECHANIC; + case MAPID_GUILLOTINE_CROSS:return JOB_GUILLOTINE_CROSS; + //3.1 trans + case MAPID_RUNE_KNIGHT_T: return JOB_RUNE_KNIGHT_T; + case MAPID_WARLOCK_T: return JOB_WARLOCK_T; + case MAPID_RANGER_T: return JOB_RANGER_T; + case MAPID_ARCH_BISHOP_T: return JOB_ARCH_BISHOP_T; + case MAPID_MECHANIC_T: return JOB_MECHANIC_T; + case MAPID_GUILLOTINE_CROSS_T:return JOB_GUILLOTINE_CROSS_T; + //3.2 non-trans + case MAPID_ROYAL_GUARD: return JOB_ROYAL_GUARD; + case MAPID_SORCERER: return JOB_SORCERER; + case MAPID_MINSTRELWANDERER:return sex?JOB_MINSTREL:JOB_WANDERER; + case MAPID_SURA: return JOB_SURA; + case MAPID_GENETIC: return JOB_GENETIC; + case MAPID_SHADOW_CHASER: return JOB_SHADOW_CHASER; + //3.2 trans + case MAPID_ROYAL_GUARD_T: return JOB_ROYAL_GUARD_T; + case MAPID_SORCERER_T: return JOB_SORCERER_T; + case MAPID_MINSTRELWANDERER_T:return sex?JOB_MINSTREL_T:JOB_WANDERER_T; + case MAPID_SURA_T: return JOB_SURA_T; + case MAPID_GENETIC_T: return JOB_GENETIC_T; + case MAPID_SHADOW_CHASER_T: return JOB_SHADOW_CHASER_T; default: return -1; } @@ -4922,86 +4839,9 @@ const char* job_name(int class_) return msg_txt(617); case JOB_SOUL_LINKER: return msg_txt(618); - - case JOB_RUNE_KNIGHT: - case JOB_WARLOCK: - case JOB_RANGER: - case JOB_ARCHBISHOP: - case JOB_MECHANIC: - case JOB_GUILLOTINE_CROSS: - return msg_txt(625 - JOB_RUNE_KNIGHT +class_); - - case JOB_RUNE_KNIGHT_H: - case JOB_WARLOCK_H: - case JOB_RANGER_H: - case JOB_ARCHBISHOP_H: - case JOB_MECHANIC_H: - case JOB_GUILLOTINE_CROSS_H: - return msg_txt(625 - JOB_RUNE_KNIGHT_H +class_); - - case JOB_ROYAL_GUARD: - case JOB_SORCERER: - case JOB_MINSTREL: - case JOB_WANDERER: - case JOB_SURA: - case JOB_GENETIC: - case JOB_SHADOW_CHASER: - return msg_txt(631 - JOB_ROYAL_GUARD +class_); - - case JOB_ROYAL_GUARD_H: - case JOB_SORCERER_H: - case JOB_MINSTREL_H: - case JOB_WANDERER_H: - case JOB_SURA_H: - case JOB_GENETIC_H: - case JOB_SHADOW_CHASER_H: - return msg_txt(631 - JOB_ROYAL_GUARD_H +class_); - - case JOB_RUNE_KNIGHT2: - case JOB_RUNE_KNIGHT_H2: - return msg_txt(625); - - case JOB_ROYAL_GUARD2: - case JOB_ROYAL_GUARD_H2: - return msg_txt(631); - - case JOB_RANGER2: - case JOB_RANGER_H2: - return msg_txt(627); - - case JOB_MECHANIC2: - case JOB_MECHANIC_H2: - return msg_txt(629); - - case JOB_BABY_RUNE: - case JOB_BABY_WARLOCK: - case JOB_BABY_RANGER: - case JOB_BABY_BISHOP: - case JOB_BABY_MECHANIC: - case JOB_BABY_CROSS: - case JOB_BABY_GUARD: - case JOB_BABY_SORCERER: - case JOB_BABY_MINSTREL: - case JOB_BABY_WANDERER: - case JOB_BABY_SURA: - case JOB_BABY_GENETIC: - case JOB_BABY_CHASER: - return msg_txt(638 - JOB_BABY_RUNE +class_); - - case JOB_BABY_RUNE2: - return msg_txt(638); - - case JOB_BABY_GUARD2: - return msg_txt(644); - - case JOB_BABY_RANGER2: - return msg_txt(640); - - case JOB_BABY_MECHANIC2: - return msg_txt(642); - + default: - return msg_txt(651); + return msg_txt(650); } } @@ -5150,7 +4990,7 @@ int pc_checkjoblevelup(struct map_session_data *sd) status_calc_pc(sd,0); clif_misceffect(&sd->bl,1); if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) - clif_status_change(&sd->bl,SI_DEVIL, 1, 0); //Permanent blind effect from SG_DEVIL. + clif_status_change(&sd->bl,SI_DEVIL, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL. npc_script_event(sd, NPCE_JOBLVUP); return 1; @@ -5191,7 +5031,6 @@ int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int { float nextbp=0, nextjp=0; unsigned int nextb=0, nextj=0; - int leveldiff = src ? ( sd->status.base_level - status_get_lv(src) ) : 0, modifier = 100; nullpo_ret(sd); if(sd->bl.prev == NULL || pc_isdead(sd)) @@ -5230,52 +5069,6 @@ int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int } } - /*Adjust exp by mob level difference vs player level. - Higher level creatures give more exp, lower level creatures give less - and you'll get normal experience if they're very close to your level. - - -2 to +5 = 100% - -3 = 105% - -4 = 110% - -5 = 115% - -6 = 120% - -7 = 125% - -8 = 130% - -9 = 135% - -10 = 140% - -11 or lower = 40% - - +6 to +10 = 95% - +11 to +15 = 90% - +15 to +20 = 85% - +21 to +25 = 60% - +26 to +30 = 35% - +31 or higher = 10% */ - - if (leveldiff >= 6 && leveldiff <= 10) - modifier = -5; - if (leveldiff >= 11 && leveldiff <= 15) - modifier = -10; - if (leveldiff >= 16 && leveldiff <= 20) - modifier = -15; - if (leveldiff >= 21 && leveldiff <= 25) - modifier = -40; - if (leveldiff >= 26 && leveldiff <= 30) - modifier = -65; - if (leveldiff > 30) - modifier = -90; - if (leveldiff <= -3 && leveldiff >= -10) - modifier = ((leveldiff * -5)-10); - if (leveldiff <= -10) - modifier = -60; - - if ( modifier < 0 ) - modifier = modifier * -1; - if ( modifier != 100 ) { - base_exp = (int)((float)base_exp - (((float)modifier/100) * (float)base_exp)); - job_exp = (int)((float)job_exp - (((float)modifier/100) * (float)job_exp)); - } - //Cap exp to the level up requirement of the previous level when you are at max level, otherwise cap at UINT_MAX (this is required for some S. Novice bonuses). [Skotlex] if (base_exp) { nextb = nextb?UINT_MAX:pc_thisbaseexp(sd); @@ -5410,12 +5203,7 @@ int pc_gets_status_point(int level) if (battle_config.use_statpoint_table) //Use values from "db/statpoint.txt" return (statp[level+1] - statp[level]); else //Default increase - { - if (level < 100) - return ((level+15) / 5); - else - return ((level+130) / 10); - } + return ((level+15) / 5); } /// Returns the number of stat points needed to change the specified stat by val. @@ -5429,18 +5217,17 @@ int pc_need_status_point(struct map_session_data* sd, int type, int val) return 0; low = pc_getstat(sd,type); - if ( low >= pc_maxparameter(sd) && val > 0 ) - return 0; // Official servers show '0' when max is reached high = low + val; if ( val < 0 ) swap(low, high); for ( ; low < high; low++ ) - if( low < 100 ) - sp += ( 1 + (low + 9) / 10 ); - else - sp += ( 16 + 4*((low - 100) / 5) ); +#if RRMODE //Renewal Stat Cost Formula + sp += (low < 100) ? (2 + (low - 1) / 10) : (16 + 4 * ((low - 100) / 5)); +#else + sp += ( 1 + (low + 9) / 10 ); +#endif return sp; } @@ -5789,9 +5576,16 @@ int pc_resetskill(struct map_session_data* sd, int flag) i &= ~OPTION_CART; if( i&OPTION_FALCON && pc_checkskill(sd, HT_FALCON) ) i &= ~OPTION_FALCON; - if( i&OPTION_DRAGON && pc_checkskill(sd, KN_RIDING) ) //RK_DRAGONTRAINING not needed for riding (bug?), assuming KN_RIDING is. - i&=~OPTION_DRAGON; - + if( i&OPTION_DRAGON && pc_checkskill(sd, RK_DRAGONTRAINING) ) + i &= ~OPTION_DRAGON; + if( i&OPTION_WUG && pc_checkskill(sd, RA_WUGMASTERY) ) + i &= ~OPTION_WUG; + if( i&OPTION_WUGRIDER && pc_checkskill(sd, RA_WUGRIDER) ) + i &= ~OPTION_WUGRIDER; + if( i&OPTION_MADOGEAR && ( sd->class_&MAPID_THIRDMASK ) == MAPID_MECHANIC ) + i &= ~OPTION_MADOGEAR; + if( i&OPTION_MOUNTING ) + i &= ~OPTION_MOUNTING; if( i != sd->sc.option ) pc_setoption(sd, i); @@ -6565,13 +6359,6 @@ int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp) sp -= sp * sd->sc.data[SC_CRITICALWOUND]->val2 / 100; } - if (sd->sc.data[SC_VITALITYACTIVATION]) - { - hp += hp * sd->sc.data[SC_VITALITYACTIVATION]->val2 / 100; //HP +50% - sp -= sp * sd->sc.data[SC_VITALITYACTIVATION]->val3 / 100; //SP -50% - } - - return status_heal(&sd->bl, hp, sp, 1); } @@ -6690,14 +6477,6 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) clif_updatestatus(sd,SP_JOBEXP); clif_updatestatus(sd,SP_NEXTJOBEXP); - //New job may have new max_parameter, so update stat points needed to raise a stat - clif_updatestatus(sd,SP_USTR); - clif_updatestatus(sd,SP_UAGI); - clif_updatestatus(sd,SP_UVIT); - clif_updatestatus(sd,SP_UINT); - clif_updatestatus(sd,SP_UDEX); - clif_updatestatus(sd,SP_ULUK); - for(i=0;i<EQI_MAX;i++) { if(sd->equip_index[i] >= 0) if(!pc_isequip(sd,sd->equip_index[i])) @@ -6726,9 +6505,14 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) i&=~OPTION_CART; if(i&OPTION_FALCON && !pc_checkskill(sd, HT_FALCON)) i&=~OPTION_FALCON; - if(i&OPTION_DRAGON && !pc_checkskill(sd, KN_RIDING)) //RK_DRAGONTRAINING not needed for riding (bug?), assuming KN_RIDING is. + if( i&OPTION_DRAGON && !pc_checkskill(sd,RK_DRAGONTRAINING) ) i&=~OPTION_DRAGON; - + if( i&OPTION_WUGRIDER && !pc_checkskill(sd,RA_WUGMASTERY) ) + i&=~OPTION_WUGRIDER; + if( i&OPTION_WUG && !pc_checkskill(sd,RA_WUGMASTERY) ) + i&=~OPTION_WUG; + if( i&OPTION_MADOGEAR ) //You do not need a skill for this. + i&=~OPTION_MADOGEAR; if(i != sd->sc.option) pc_setoption(sd, i); @@ -6856,19 +6640,15 @@ int pc_setoption(struct map_session_data *sd,int type) sd->sc.option=type; clif_changeoption(&sd->bl); - if (((type&OPTION_RIDING && !(p_type&OPTION_RIDING)) // Knight and Crusader/Royal Guard - || (type&OPTION_DRAGON && !(p_type&OPTION_DRAGON))) // Rune Knight Dragon - && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN) - { //We are going to mount. [Skotlex] + if( (type&OPTION_RIDING && !(p_type&OPTION_RIDING)) || (type&OPTION_DRAGON && !(p_type&OPTION_DRAGON) && pc_checkskill(sd,RK_DRAGONTRAINING) > 0) ) + { // Mounting clif_status_load(&sd->bl,SI_RIDING,1); - status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds. + status_calc_pc(sd,0); } - else if (((!(type&OPTION_RIDING) && p_type&OPTION_RIDING) //Knight and Crusader/Royal Guard - || (!(type&OPTION_DRAGON) && p_type&OPTION_DRAGON)) // Rune Knight Dragon - && (sd->class_&MAPID_BASEMASK) == MAPID_SWORDMAN) - { //We are going to dismount. + else if( (!(type&OPTION_RIDING) && p_type&OPTION_RIDING) || (!(type&OPTION_DRAGON) && p_type&OPTION_DRAGON && pc_checkskill(sd,RK_DRAGONTRAINING) > 0) ) + { // Dismount clif_status_load(&sd->bl,SI_RIDING,0); - status_calc_pc(sd,0); //Mounting/Umounting affects walk and attack speeds. + status_calc_pc(sd,0); } if(type&OPTION_CART && !(p_type&OPTION_CART)) @@ -6885,11 +6665,49 @@ int pc_setoption(struct map_session_data *sd,int type) status_calc_pc(sd,0); //Remove speed penalty. } + if (type&OPTION_MOUNTING && !(p_type&OPTION_MOUNTING)) { + clif_status_load_notick(&sd->bl,SI_ALL_RIDING,2,1,0,0); + status_calc_pc(sd,0); + } else if (!(type&OPTION_MOUNTING) && p_type&OPTION_MOUNTING) { + clif_status_load_notick(&sd->bl,SI_ALL_RIDING,0,0,0,0); + status_calc_pc(sd,0); + } + + if (type&OPTION_FALCON && !(p_type&OPTION_FALCON)) //Falcon ON clif_status_load(&sd->bl,SI_FALCON,1); else if (!(type&OPTION_FALCON) && p_type&OPTION_FALCON) //Falcon OFF clif_status_load(&sd->bl,SI_FALCON,0); + if( (sd->class_&MAPID_THIRDMASK) == MAPID_RANGER ) { + if( type&OPTION_WUGRIDER && !(p_type&OPTION_WUGRIDER) ) { // Mounting + clif_status_load(&sd->bl,SI_WUGRIDER,1); + status_calc_pc(sd,0); + } else if( !(type&OPTION_WUGRIDER) && p_type&OPTION_WUGRIDER ) { // Dismount + clif_status_load(&sd->bl,SI_WUGRIDER,0); + status_calc_pc(sd,0); + } + } + if( (sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC ) { + if( type&OPTION_MADOGEAR && !(p_type&OPTION_MADOGEAR) ) { + status_calc_pc(sd, 0); + status_change_end(&sd->bl,SC_MAXIMIZEPOWER,-1); + status_change_end(&sd->bl,SC_OVERTHRUST,-1); + status_change_end(&sd->bl,SC_WEAPONPERFECTION,-1); + status_change_end(&sd->bl,SC_ADRENALINE,-1); + status_change_end(&sd->bl,SC_CARTBOOST,-1); + status_change_end(&sd->bl,SC_MELTDOWN,-1); + status_change_end(&sd->bl,SC_MAXOVERTHRUST,-1); + } else if( !(type&OPTION_MADOGEAR) && p_type&OPTION_MADOGEAR ) { + status_calc_pc(sd, 0); + status_change_end(&sd->bl,SC_SHAPESHIFT,-1); + status_change_end(&sd->bl,SC_HOVERING,-1); + status_change_end(&sd->bl,SC_ACCELERATION,-1); + status_change_end(&sd->bl,SC_OVERHEAT_LIMITPOINT,-1); + status_change_end(&sd->bl,SC_OVERHEAT,-1); + } + } + if (type&OPTION_FLYING && !(p_type&OPTION_FLYING)) new_look = JOB_STAR_GLADIATOR2; else if (!(type&OPTION_FLYING) && p_type&OPTION_FLYING) @@ -6976,29 +6794,13 @@ int pc_setriding(TBL_PC* sd, int flag) if( pc_checkskill(sd,KN_RIDING) > 0 ) // ライディングスキル所持 pc_setoption(sd, sd->sc.option|OPTION_RIDING); } else if( pc_isriding(sd) ){ - pc_setoption(sd, sd->sc.option&~OPTION_RIDING); + pc_setoption(sd, sd->sc.option&~OPTION_RIDING); } return 0; } /*========================================== - * Enable Riding Dragons for Rune Knight class. - *------------------------------------------*/ -int pc_setdragon(TBL_PC* sd, int flag, int color) -{ - int dragon[5] = {OPTION_DRAGON1,OPTION_DRAGON2,OPTION_DRAGON3,OPTION_DRAGON4,OPTION_DRAGON5}; - - if( flag ){ - if( pc_checkskill(sd,KN_RIDING) > 0 ) //Possible to rent dragons without RK_DRAGONTRAINING; Source, iRO. (Bug?) - pc_setoption(sd, sd->sc.option|dragon[color]); - } else if( pc_isdragon(sd) ){ - pc_setoption(sd, sd->sc.option&~OPTION_DRAGON); - } - - return 0; -} -/*========================================== * アイテムドロップ可不可判定 *------------------------------------------*/ int pc_candrop(struct map_session_data *sd,struct item *item) @@ -8149,6 +7951,30 @@ void pc_setstand(struct map_session_data *sd){ sd->state.dead_sit = sd->vd.dead_sit = 0; } +/** + * Mechanic (MADO GEAR) + **/ +void pc_overheat(struct map_session_data *sd, int val) { + int heat = val, skill, + limit[] = { 10, 20, 28, 46, 66 }; + + if( !(sd->sc.option&OPTION_MADOGEAR) || sd->sc.data[SC_OVERHEAT] ) + return; // already burning + + skill = cap_value(pc_checkskill(sd,NC_MAINFRAME),0,4); + if( sd->sc.data[SC_OVERHEAT_LIMITPOINT] ) { + heat += sd->sc.data[SC_OVERHEAT_LIMITPOINT]->val1; + status_change_end(&sd->bl,SC_OVERHEAT_LIMITPOINT,-1); + } + + heat = max(0,heat); // Avoid negative HEAT + if( heat >= limit[skill] ) + sc_start(&sd->bl,SC_OVERHEAT,100,0,1000); + else + sc_start(&sd->bl,SC_OVERHEAT_LIMITPOINT,100,heat,30000); + + return; +} int pc_split_str(char *str,char **val,int num) { int i; @@ -8408,7 +8234,11 @@ int pc_readdb(void) // スキルツリ? memset(statp,0,sizeof(statp)); i=1; +#if RRMODE + sprintf(line, "%s/statpoint_renewal.txt", db_path); +#else sprintf(line, "%s/statpoint.txt", db_path); +#endif fp=fopen(line,"r"); if(fp == NULL){ ShowWarning("Can't read '"CL_WHITE"%s"CL_RESET"'... Generating DB.\n",line); @@ -8427,7 +8257,11 @@ int pc_readdb(void) i++; } fclose(fp); + #if RRMODE + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint_renewal.txt"); + #else ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","statpoint.txt"); + #endif } // generate the remaining parts of the db if necessary k = battle_config.use_statpoint_table; //save setting diff --git a/src/map/pc.h b/src/map/pc.h index 4619af878..16c31e69c 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -21,6 +21,9 @@ #define MAX_PC_SKILL_REQUIRE 5 #define MAX_PC_FEELHATE 3 +//For Warlock +#define MAX_SPELLBOOK 10 + struct weapon_data { int atkmods[3]; // all the variables except atkmods get zero'ed in each call of status_calc_pc @@ -249,7 +252,7 @@ struct map_session_data { struct { //skillatk raises bonus dmg% of skills, skillheal increases heal%, skillblown increases bonus blewcount for some skills. unsigned short id; short val; - } skillatk[MAX_PC_BONUS], skillheal[5], skillheal2[5], skillblown[MAX_PC_BONUS], skillcast[MAX_PC_BONUS], fixedskillcast[MAX_PC_BONUS]; + } skillatk[MAX_PC_BONUS], skillheal[5], skillheal2[5], skillblown[MAX_PC_BONUS], skillcast[MAX_PC_BONUS]; struct { short value; int rate; @@ -307,9 +310,9 @@ struct map_session_data { // zeroed vars end here. - int castrate,fixedcastrate,delayrate,hprate,sprate,dsprate; + int castrate,delayrate,hprate,sprate,dsprate; int hprecov_rate,sprecov_rate; - int matk_rate,weapon_matk,equipment_matk; + int matk_rate; int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate; int itemid; @@ -405,6 +408,13 @@ struct map_session_data { bool changed; // if true, should sync with charserver on next mailbox request } mail; + // Reading SpellBook + struct { + unsigned short skillid; + unsigned char level; + unsigned char points; + } rsb[MAX_SPELLBOOK]; + //Quest log system [Kevin] [Inkfish] int num_quests; int avail_quests; @@ -420,7 +430,24 @@ struct map_session_data { unsigned int bg_id; unsigned short user_font; - int produce_itemusedid; //used to determine the type of item used when creating items via script. + /** + * For the Secure NPC Timeout option (check config/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + /** + * ID of the timer + * @info + * - value is -1 (INVALID_TIMER constant) when not being used + * - timer is cancelled upon closure of the current npc's instance + **/ + int npc_idle_timer; + /** + * Tick on the last recorded NPC iteration (next/menu/whatever) + * @info + * - It is updated on every NPC iteration as mentioned above + **/ + unsigned int npc_idle_tick; +#endif // temporary debugging of bug #3504 const char* delunit_prevfile; @@ -428,7 +455,8 @@ struct map_session_data { }; //Update this max as necessary. 55 is the value needed for Super Baby currently -#define MAX_SKILL_TREE 77 +//Raised to 75 due to 3rds +#define MAX_SKILL_TREE 75 //Total number of classes (for data storage) #define CLASS_COUNT (JOB_MAX - JOB_NOVICE_HIGH + JOB_MAX_BASIC) @@ -528,7 +556,7 @@ enum equip_index { #define pc_issit(sd) ( (sd)->vd.dead_sit == 2 ) #define pc_isidle(sd) ( (sd)->chatID || (sd)->state.vending || (sd)->state.buyingstore || DIFF_TICK(last_tick, (sd)->idletime) >= battle_config.idle_no_share ) #define pc_istrading(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->state.trading ) -#define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chatID || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag ) +#define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chatID || (sd)->sc.opt1 || (sd)->state.trading || (sd)->state.storage_flag ) #define pc_setdir(sd,b,h) ( (sd)->ud.dir = (b) ,(sd)->head_dir = (h) ) #define pc_setchatid(sd,n) ( (sd)->chatID = n ) #define pc_ishiding(sd) ( (sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) ) @@ -540,8 +568,12 @@ enum equip_index { #define pc_isinvisible(sd) ( (sd)->sc.option&OPTION_INVISIBLE ) #define pc_is50overweight(sd) ( (sd)->weight*100 >= (sd)->max_weight*battle_config.natural_heal_weight_rate ) #define pc_is90overweight(sd) ( (sd)->weight*10 >= (sd)->max_weight*9 ) -#define pc_maxparameter(sd) ( ((sd)->class_&JOBL_3 ? ((sd)->class_&JOBL_BABY ? battle_config.max_baby_third_parameter : battle_config.max_third_parameter) : ((sd)->class_&JOBL_BABY ? battle_config.max_baby_parameter : battle_config.max_parameter)) ) -#define pc_isdragon(sd) ( (sd)->sc.option&OPTION_DRAGON ) +#define pc_maxparameter(sd) ( (sd)->class_&JOBL_THIRD ? battle_config.max_third_parameter : (sd)->class_&JOBL_BABY ? battle_config.max_baby_parameter : battle_config.max_parameter ) +/** + * Ranger + **/ +#define pc_iswug(sd) ( (sd)->sc.option&OPTION_WUG ) +#define pc_isridingwug(sd) ( (sd)->sc.option&OPTION_WUGRIDER ) #define pc_stop_walking(sd, type) unit_stop_walking(&(sd)->bl, type) #define pc_stop_attack(sd) unit_stop_attack(&(sd)->bl) @@ -554,8 +586,7 @@ enum equip_index { ( \ ( (class_) >= JOB_NOVICE && (class_) < JOB_MAX_BASIC ) \ || ( (class_) >= JOB_NOVICE_HIGH && (class_) <= JOB_SOUL_LINKER ) \ -|| ( (class_) >= JOB_RUNE_KNIGHT && (class_) <= JOB_MECHANIC_H2 ) \ -|| ( (class_) >= JOB_BABY_RUNE && (class_) < JOB_MAX ) \ +|| ( (class_) >= JOB_RUNE_KNIGHT && (class_) < JOB_MAX ) \ ) int pc_class2idx(int class_); @@ -667,7 +698,6 @@ int pc_equipitem(struct map_session_data*,int,int); int pc_unequipitem(struct map_session_data*,int,int); int pc_checkitem(struct map_session_data*); int pc_useitem(struct map_session_data*,int); -int pc_isUseitem_check_runeskill(TBL_PC *sd, int nameid); int pc_skillatk_bonus(struct map_session_data *sd, int skill_num); int pc_skillheal_bonus(struct map_session_data *sd, int skill_num); @@ -686,7 +716,6 @@ int pc_setfalcon(struct map_session_data* sd, int flag); int pc_setriding(struct map_session_data* sd, int flag); int pc_changelook(struct map_session_data *,int,int); int pc_equiplookall(struct map_session_data *sd); -int pc_setdragon(struct map_session_data* sd, int flag, int color); int pc_readparam(struct map_session_data*,int); int pc_setparam(struct map_session_data*,int,int); @@ -792,5 +821,8 @@ void pc_inventory_rental_add(struct map_session_data *sd, int seconds); int pc_read_motd(void); // [Valaris] int pc_disguise(struct map_session_data *sd, int class_); - +/** + * Mechanic (Mado Gear) + **/ +void pc_overheat(struct map_session_data *sd, int val); #endif /* _PC_H_ */ diff --git a/src/map/script.c b/src/map/script.c index 0f3c09e1e..2ffcad83c 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -171,8 +171,16 @@ enum { LABEL_NEXTLINE=1,LABEL_START }; static unsigned char* script_buf = NULL; static int script_pos = 0, script_size = 0; -#define GETVALUE(buf,i) ((int)MakeDWord(MakeWord((buf)[i],(buf)[i+1]),MakeWord((buf)[i+2],0))) -#define SETVALUE(buf,i,n) ((buf)[i]=GetByte(n,0),(buf)[i+1]=GetByte(n,1),(buf)[i+2]=GetByte(n,2)) +static inline int GETVALUE(const unsigned char* buf, int i) +{ + return (int)MakeDWord(MakeWord(buf[i], buf[i+1]), MakeWord(buf[i+2], 0)); +} +static inline void SETVALUE(unsigned char* buf, int i, int n) +{ + buf[i] = GetByte(n, 0); + buf[i+1] = GetByte(n, 1); + buf[i+2] = GetByte(n, 2); +} // String buffer structures. // str_data stores string information @@ -317,7 +325,10 @@ enum { MF_FOG, MF_SAKURA, MF_LEAVES, - MF_RAIN, //20 + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //MF_RAIN, //20 // 21 free MF_NOGO = 22, MF_CLOUDS, @@ -3357,7 +3368,18 @@ static void script_detach_state(struct script_state* st, bool dequeue_event) { sd->st = st->bk_st; sd->npc_id = st->bk_npcid; - + /** + * For the Secure NPC Timeout option (check config/Secure.h) [RR] + **/ + #if SECURE_NPCTIMEOUT + /** + * We're done with this NPC session, so we cancel the timer (if existent) and move on + **/ + if( sd->npc_idle_timer != INVALID_TIMER ) { + delete_timer(sd->npc_idle_timer,npc_rr_secure_timeout_timer); + sd->npc_idle_timer = INVALID_TIMER; + } + #endif if(st->bk_st) { //Remove tag for removal. @@ -3399,6 +3421,14 @@ static void script_attach_state(struct script_state* st) } sd->st = st; sd->npc_id = st->oid; +/** + * For the Secure NPC Timeout option (check config/Secure.h) [RR] + **/ +#if SECURE_NPCTIMEOUT + if( sd->npc_idle_timer == INVALID_TIMER ) + sd->npc_idle_timer = add_timer(gettick() + (SECURE_NPCTIMEOUT_INTERVAL*1000),npc_rr_secure_timeout_timer,sd->bl.id,0); + sd->npc_idle_tick = gettick(); +#endif } } @@ -7022,8 +7052,7 @@ BUILDIN_FUNC(bonus) case SP_ADD_SKILL_BLOW: case SP_CASTRATE: case SP_ADDEFF_ONSKILL: - case SP_FIXEDCASTRATE: - // these bonuses support skill names + // these bonuses support skill names val1 = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) ); break; default: @@ -7426,7 +7455,7 @@ BUILDIN_FUNC(setoption) flag = script_getnum(st,3); else if( !option ){// Request to remove everything. flag = 0; - option = OPTION_CART|OPTION_FALCON|OPTION_RIDING|OPTION_DRAGON; + option = OPTION_CART|OPTION_FALCON|OPTION_RIDING; } if( flag ){// Add option if( option&OPTION_WEDDING && !battle_config.wedding_modifydisplay ) @@ -7542,7 +7571,7 @@ BUILDIN_FUNC(checkriding) if( sd == NULL ) return 0;// no player attached, report source - if( pc_isriding(sd) || pc_isdragon(sd) ) + if( pc_isriding(sd) || sd->sc.option&OPTION_MOUNTING ) script_pushint(st, 1); else script_pushint(st, 0); @@ -7571,31 +7600,6 @@ BUILDIN_FUNC(setriding) return 0; } -/// Sets if the player is riding a dragon. -/// <flag> defaults to 1 -/// <color> defaults to 0 -/// -/// setdragon <flag>{,<color>}; -/// setdragon <flag>; -/// setdragon; -BUILDIN_FUNC(setdragon) -{ - int flag = 1, color = 0; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - if( script_hasdata(st,2) ) - flag = script_getnum(st,2); - if( script_hasdata(st,3) ) - color = cap_value(script_getnum(st,3),0,4); - - pc_setdragon(sd, flag, color); - return 0; -} - /// Sets the save point of the player. /// /// save "<map name>",<x>,<y> @@ -7787,31 +7791,9 @@ BUILDIN_FUNC(produce) sd = script_rid2sd(st); if( sd == NULL ) return 0; - - if( script_hasdata(st,3) ) - { // only used with Rune Knights RK_RUNEMASTERY as part of the calculation. - struct item_data* id = NULL; - struct script_data* data; - - data = script_getdata(st,3); - get_val(st, data); - - if( data_isstring(data) ) - id = itemdb_searchname(conv_str(st, data)); - else - id = itemdb_exists(conv_num(st, data)); - - if( id == NULL ) - { - ShowError("buildin_produce: Invalid item '%s'.\n", script_getstr(st,3)); - return 1; - } - else - sd->produce_itemusedid = id->nameid; - } trigger=script_getnum(st,2); - clif_skill_produce_mix_list(sd, trigger); + clif_skill_produce_mix_list(sd, -1, trigger); return 0; } /*========================================== @@ -9186,7 +9168,7 @@ BUILDIN_FUNC(roclass) } /*========================================== - *携帯卵孵化機使用 + * Tells client to open a hatching window, used for pet incubator *------------------------------------------*/ BUILDIN_FUNC(birthpet) { @@ -9634,6 +9616,7 @@ BUILDIN_FUNC(getmapflag) switch(i) { case MF_NOMEMO: script_pushint(st,map[m].flag.nomemo); break; case MF_NOTELEPORT: script_pushint(st,map[m].flag.noteleport); break; + case MF_NOSAVE: script_pushint(st,map[m].flag.nosave); break; case MF_NOBRANCH: script_pushint(st,map[m].flag.nobranch); break; case MF_NOPENALTY: script_pushint(st,map[m].flag.noexppenalty); break; case MF_NOZENYPENALTY: script_pushint(st,map[m].flag.nozenypenalty); break; @@ -9642,23 +9625,26 @@ BUILDIN_FUNC(getmapflag) case MF_PVP_NOGUILD: script_pushint(st,map[m].flag.pvp_noguild); break; case MF_GVG: script_pushint(st,map[m].flag.gvg); break; case MF_GVG_NOPARTY: script_pushint(st,map[m].flag.gvg_noparty); break; - case MF_GVG_DUNGEON: script_pushint(st,map[m].flag.gvg_dungeon); break; - case MF_GVG_CASTLE: script_pushint(st,map[m].flag.gvg_castle); break; case MF_NOTRADE: script_pushint(st,map[m].flag.notrade); break; - case MF_NODROP: script_pushint(st,map[m].flag.nodrop); break; case MF_NOSKILL: script_pushint(st,map[m].flag.noskill); break; case MF_NOWARP: script_pushint(st,map[m].flag.nowarp); break; + case MF_PARTYLOCK: script_pushint(st,map[m].flag.partylock); break; case MF_NOICEWALL: script_pushint(st,map[m].flag.noicewall); break; case MF_SNOW: script_pushint(st,map[m].flag.snow); break; - case MF_CLOUDS: script_pushint(st,map[m].flag.clouds); break; - case MF_CLOUDS2: script_pushint(st,map[m].flag.clouds2); break; case MF_FOG: script_pushint(st,map[m].flag.fog); break; - case MF_FIREWORKS: script_pushint(st,map[m].flag.fireworks); break; case MF_SAKURA: script_pushint(st,map[m].flag.sakura); break; case MF_LEAVES: script_pushint(st,map[m].flag.leaves); break; - case MF_RAIN: script_pushint(st,map[m].flag.rain); break; - case MF_NIGHTENABLED: script_pushint(st,map[m].flag.nightenabled); break; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //case MF_RAIN: script_pushint(st,map[m].flag.rain); break; case MF_NOGO: script_pushint(st,map[m].flag.nogo); break; + case MF_CLOUDS: script_pushint(st,map[m].flag.clouds); break; + case MF_CLOUDS2: script_pushint(st,map[m].flag.clouds2); break; + case MF_FIREWORKS: script_pushint(st,map[m].flag.fireworks); break; + case MF_GVG_CASTLE: script_pushint(st,map[m].flag.gvg_castle); break; + case MF_GVG_DUNGEON: script_pushint(st,map[m].flag.gvg_dungeon); break; + case MF_NIGHTENABLED: script_pushint(st,map[m].flag.nightenabled); break; case MF_NOBASEEXP: script_pushint(st,map[m].flag.nobaseexp); break; case MF_NOJOBEXP: script_pushint(st,map[m].flag.nojobexp); break; case MF_NOMOBLOOT: script_pushint(st,map[m].flag.nomobloot); break; @@ -9668,12 +9654,13 @@ BUILDIN_FUNC(getmapflag) case MF_NIGHTMAREDROP: script_pushint(st,map[m].flag.pvp_nightmaredrop); break; case MF_RESTRICTED: script_pushint(st,map[m].flag.restricted); break; case MF_NOCOMMAND: script_pushint(st,map[m].nocommand); break; + case MF_NODROP: script_pushint(st,map[m].flag.nodrop); break; case MF_JEXP: script_pushint(st,map[m].jexp); break; case MF_BEXP: script_pushint(st,map[m].bexp); break; case MF_NOVENDING: script_pushint(st,map[m].flag.novending); break; case MF_LOADEVENT: script_pushint(st,map[m].flag.loadevent); break; case MF_NOCHAT: script_pushint(st,map[m].flag.nochat); break; - case MF_PARTYLOCK: script_pushint(st,map[m].flag.partylock); break; + case MF_NOEXPPENALTY: script_pushint(st,map[m].flag.noexppenalty ); break; case MF_GUILDLOCK: script_pushint(st,map[m].flag.guildlock); break; case MF_TOWN: script_pushint(st,map[m].flag.town); break; case MF_AUTOTRADE: script_pushint(st,map[m].flag.autotrade); break; @@ -9681,7 +9668,7 @@ BUILDIN_FUNC(getmapflag) case MF_MONSTER_NOTELEPORT: script_pushint(st,map[m].flag.monster_noteleport); break; case MF_PVP_NOCALCRANK: script_pushint(st,map[m].flag.pvp_nocalcrank); break; case MF_BATTLEGROUND: script_pushint(st,map[m].flag.battleground); break; - case MF_RESET: script_pushint(st,map[m].flag.reset); break; + case MF_RESET: script_pushint(st,map[m].flag.reset); break; } } @@ -9702,56 +9689,61 @@ BUILDIN_FUNC(setmapflag) m = map_mapname2mapid(str); if(m >= 0) { switch(i) { - case MF_NOMEMO: map[m].flag.nomemo=1; break; - case MF_NOTELEPORT: map[m].flag.noteleport=1; break; - case MF_NOBRANCH: map[m].flag.nobranch=1; break; - case MF_NOPENALTY: map[m].flag.noexppenalty=1; map[m].flag.nozenypenalty=1; break; - case MF_NOZENYPENALTY: map[m].flag.nozenypenalty=1; break; - case MF_PVP: map[m].flag.pvp=1; break; - case MF_PVP_NOPARTY: map[m].flag.pvp_noparty=1; break; - case MF_PVP_NOGUILD: map[m].flag.pvp_noguild=1; break; - case MF_GVG: map[m].flag.gvg=1; break; - case MF_GVG_NOPARTY: map[m].flag.gvg_noparty=1; break; - case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon=1; break; - case MF_GVG_CASTLE: map[m].flag.gvg_castle=1; break; - case MF_NOTRADE: map[m].flag.notrade=1; break; - case MF_NODROP: map[m].flag.nodrop=1; break; - case MF_NOSKILL: map[m].flag.noskill=1; break; - case MF_NOWARP: map[m].flag.nowarp=1; break; - case MF_NOICEWALL: map[m].flag.noicewall=1; break; - case MF_SNOW: map[m].flag.snow=1; break; - case MF_CLOUDS: map[m].flag.clouds=1; break; - case MF_CLOUDS2: map[m].flag.clouds2=1; break; - case MF_FOG: map[m].flag.fog=1; break; - case MF_FIREWORKS: map[m].flag.fireworks=1; break; - case MF_SAKURA: map[m].flag.sakura=1; break; - case MF_LEAVES: map[m].flag.leaves=1; break; - case MF_RAIN: map[m].flag.rain=1; break; - case MF_NIGHTENABLED: map[m].flag.nightenabled=1; break; - case MF_NOGO: map[m].flag.nogo=1; break; - case MF_NOBASEEXP: map[m].flag.nobaseexp=1; break; - case MF_NOJOBEXP: map[m].flag.nojobexp=1; break; - case MF_NOMOBLOOT: map[m].flag.nomobloot=1; break; - case MF_NOMVPLOOT: map[m].flag.nomvploot=1; break; - case MF_NORETURN: map[m].flag.noreturn=1; break; - case MF_NOWARPTO: map[m].flag.nowarpto=1; break; - case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop=1; break; - case MF_RESTRICTED: map[m].flag.restricted=1; break; + case MF_NOMEMO: map[m].flag.nomemo = 1; break; + case MF_NOTELEPORT: map[m].flag.noteleport = 1; break; + case MF_NOSAVE: map[m].flag.nosave = 1; break; + case MF_NOBRANCH: map[m].flag.nobranch = 1; break; + case MF_NOPENALTY: map[m].flag.noexppenalty = 1; map[m].flag.nozenypenalty = 1; break; + case MF_NOZENYPENALTY: map[m].flag.nozenypenalty = 1; break; + case MF_PVP: map[m].flag.pvp = 1; break; + case MF_PVP_NOPARTY: map[m].flag.pvp_noparty = 1; break; + case MF_PVP_NOGUILD: map[m].flag.pvp_noguild = 1; break; + case MF_GVG: map[m].flag.gvg = 1; break; + case MF_GVG_NOPARTY: map[m].flag.gvg_noparty = 1; break; + case MF_NOTRADE: map[m].flag.notrade = 1; break; + case MF_NOSKILL: map[m].flag.noskill = 1; break; + case MF_NOWARP: map[m].flag.nowarp = 1; break; + case MF_PARTYLOCK: map[m].flag.partylock = 1; break; + case MF_NOICEWALL: map[m].flag.noicewall = 1; break; + case MF_SNOW: map[m].flag.snow = 1; break; + case MF_FOG: map[m].flag.fog = 1; break; + case MF_SAKURA: map[m].flag.sakura = 1; break; + case MF_LEAVES: map[m].flag.leaves = 1; break; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //case MF_RAIN: map[m].flag.rain = 1; break; + case MF_NOGO: map[m].flag.nogo = 1; break; + case MF_CLOUDS: map[m].flag.clouds = 1; break; + case MF_CLOUDS2: map[m].flag.clouds2 = 1; break; + case MF_FIREWORKS: map[m].flag.fireworks = 1; break; + case MF_GVG_CASTLE: map[m].flag.gvg_castle = 1; break; + case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon = 1; break; + case MF_NIGHTENABLED: map[m].flag.nightenabled = 1; break; + case MF_NOBASEEXP: map[m].flag.nobaseexp = 1; break; + case MF_NOJOBEXP: map[m].flag.nojobexp = 1; break; + case MF_NOMOBLOOT: map[m].flag.nomobloot = 1; break; + case MF_NOMVPLOOT: map[m].flag.nomvploot = 1; break; + case MF_NORETURN: map[m].flag.noreturn = 1; break; + case MF_NOWARPTO: map[m].flag.nowarpto = 1; break; + case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop = 1; break; + case MF_RESTRICTED: map[m].flag.restricted = 1; break; case MF_NOCOMMAND: map[m].nocommand = (!val || atoi(val) <= 0) ? 100 : atoi(val); break; + case MF_NODROP: map[m].flag.nodrop = 1; break; case MF_JEXP: map[m].jexp = (!val || atoi(val) < 0) ? 100 : atoi(val); break; case MF_BEXP: map[m].bexp = (!val || atoi(val) < 0) ? 100 : atoi(val); break; - case MF_NOVENDING: map[m].flag.novending=1; break; - case MF_LOADEVENT: map[m].flag.loadevent=1; break; - case MF_NOCHAT: map[m].flag.nochat=1; break; - case MF_PARTYLOCK: map[m].flag.partylock=1; break; - case MF_GUILDLOCK: map[m].flag.guildlock=1; break; - case MF_TOWN: map[m].flag.town=1; break; - case MF_AUTOTRADE: map[m].flag.autotrade=1; break; - case MF_ALLOWKS: map[m].flag.allowks=1; break; - case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport=1; break; - case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank=1; break; - case MF_BATTLEGROUND: map[m].flag.battleground = (!val || atoi(val) < 0 || atoi(val) > 2) ? 1 : atoi(val); break; - case MF_RESET: map[m].flag.reset=1; break; + case MF_NOVENDING: map[m].flag.novending = 1; break; + case MF_LOADEVENT: map[m].flag.loadevent = 1; break; + case MF_NOCHAT: map[m].flag.nochat = 1; break; + case MF_NOEXPPENALTY: map[m].flag.noexppenalty = 1; break; + case MF_GUILDLOCK: map[m].flag.guildlock = 1; break; + case MF_TOWN: map[m].flag.town = 1; break; + case MF_AUTOTRADE: map[m].flag.autotrade = 1; break; + case MF_ALLOWKS: map[m].flag.allowks = 1; break; + case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport = 1; break; + case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank = 1; break; + case MF_BATTLEGROUND: (!val || atoi(val) < 0 || atoi(val) > 2) ? 1 : atoi(val); break; + case MF_RESET: map[m].flag.reset = 1; break; } } @@ -9768,57 +9760,61 @@ BUILDIN_FUNC(removemapflag) m = map_mapname2mapid(str); if(m >= 0) { switch(i) { - case MF_NOMEMO: map[m].flag.nomemo=0; break; - case MF_NOTELEPORT: map[m].flag.noteleport=0; break; - case MF_NOSAVE: map[m].flag.nosave=0; break; - case MF_NOBRANCH: map[m].flag.nobranch=0; break; - case MF_NOPENALTY: map[m].flag.noexppenalty=0; map[m].flag.nozenypenalty=0; break; - case MF_PVP: map[m].flag.pvp=0; break; - case MF_PVP_NOPARTY: map[m].flag.pvp_noparty=0; break; - case MF_PVP_NOGUILD: map[m].flag.pvp_noguild=0; break; - case MF_GVG: map[m].flag.gvg=0; break; - case MF_GVG_NOPARTY: map[m].flag.gvg_noparty=0; break; - case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon=0; break; - case MF_GVG_CASTLE: map[m].flag.gvg_castle=0; break; - case MF_NOZENYPENALTY: map[m].flag.nozenypenalty=0; break; - case MF_NOTRADE: map[m].flag.notrade=0; break; - case MF_NODROP: map[m].flag.nodrop=0; break; - case MF_NOSKILL: map[m].flag.noskill=0; break; - case MF_NOWARP: map[m].flag.nowarp=0; break; - case MF_NOICEWALL: map[m].flag.noicewall=0; break; - case MF_SNOW: map[m].flag.snow=0; break; - case MF_CLOUDS: map[m].flag.clouds=0; break; - case MF_CLOUDS2: map[m].flag.clouds2=0; break; - case MF_FOG: map[m].flag.fog=0; break; - case MF_FIREWORKS: map[m].flag.fireworks=0; break; - case MF_SAKURA: map[m].flag.sakura=0; break; - case MF_LEAVES: map[m].flag.leaves=0; break; - case MF_RAIN: map[m].flag.rain=0; break; - case MF_NIGHTENABLED: map[m].flag.nightenabled=0; break; - case MF_NOGO: map[m].flag.nogo=0; break; - case MF_NOBASEEXP: map[m].flag.nobaseexp=0; break; - case MF_NOJOBEXP: map[m].flag.nojobexp=0; break; - case MF_NOMOBLOOT: map[m].flag.nomobloot=0; break; - case MF_NOMVPLOOT: map[m].flag.nomvploot=0; break; - case MF_NORETURN: map[m].flag.noreturn=0; break; - case MF_NOWARPTO: map[m].flag.nowarpto=0; break; - case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop=0; break; - case MF_RESTRICTED: map[m].flag.restricted=0; break; - case MF_NOCOMMAND: map[m].nocommand=0; break; - case MF_JEXP: map[m].jexp=100; break; - case MF_BEXP: map[m].bexp=100; break; - case MF_NOVENDING: map[m].flag.novending=0; break; - case MF_LOADEVENT: map[m].flag.loadevent=0; break; - case MF_NOCHAT: map[m].flag.nochat=0; break; - case MF_PARTYLOCK: map[m].flag.partylock=0; break; - case MF_GUILDLOCK: map[m].flag.guildlock=0; break; - case MF_TOWN: map[m].flag.town=0; break; - case MF_AUTOTRADE: map[m].flag.autotrade=0; break; - case MF_ALLOWKS: map[m].flag.allowks=0; break; - case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport=0; break; - case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank=0; break; - case MF_BATTLEGROUND: map[m].flag.battleground=0; break; - case MF_RESET: map[m].flag.reset=0; break; + case MF_NOMEMO: map[m].flag.nomemo = 0; break; + case MF_NOTELEPORT: map[m].flag.noteleport = 0; break; + case MF_NOSAVE: map[m].flag.nosave = 0; break; + case MF_NOBRANCH: map[m].flag.nobranch = 0; break; + case MF_NOPENALTY: map[m].flag.noexppenalty = 0; map[m].flag.nozenypenalty = 0; break; + case MF_NOZENYPENALTY: map[m].flag.nozenypenalty = 0; break; + case MF_PVP: map[m].flag.pvp = 0; break; + case MF_PVP_NOPARTY: map[m].flag.pvp_noparty = 0; break; + case MF_PVP_NOGUILD: map[m].flag.pvp_noguild = 0; break; + case MF_GVG: map[m].flag.gvg = 0; break; + case MF_GVG_NOPARTY: map[m].flag.gvg_noparty = 0; break; + case MF_NOTRADE: map[m].flag.notrade = 0; break; + case MF_NOSKILL: map[m].flag.noskill = 0; break; + case MF_NOWARP: map[m].flag.nowarp = 0; break; + case MF_PARTYLOCK: map[m].flag.partylock = 0; break; + case MF_NOICEWALL: map[m].flag.noicewall = 0; break; + case MF_SNOW: map[m].flag.snow = 0; break; + case MF_FOG: map[m].flag.fog = 0; break; + case MF_SAKURA: map[m].flag.sakura = 0; break; + case MF_LEAVES: map[m].flag.leaves = 0; break; + /** + * No longer available, keeping here just in case it's back someday. [Ind] + **/ + //case MF_RAIN: map[m].flag.rain = 0; break; + case MF_NOGO: map[m].flag.nogo = 0; break; + case MF_CLOUDS: map[m].flag.clouds = 0; break; + case MF_CLOUDS2: map[m].flag.clouds2 = 0; break; + case MF_FIREWORKS: map[m].flag.fireworks = 0; break; + case MF_GVG_CASTLE: map[m].flag.gvg_castle = 0; break; + case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon = 0; break; + case MF_NIGHTENABLED: map[m].flag.nightenabled = 0; break; + case MF_NOBASEEXP: map[m].flag.nobaseexp = 0; break; + case MF_NOJOBEXP: map[m].flag.nojobexp = 0; break; + case MF_NOMOBLOOT: map[m].flag.nomobloot = 0; break; + case MF_NOMVPLOOT: map[m].flag.nomvploot = 0; break; + case MF_NORETURN: map[m].flag.noreturn = 0; break; + case MF_NOWARPTO: map[m].flag.nowarpto = 0; break; + case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop = 0; break; + case MF_RESTRICTED: map[m].flag.restricted = 0; break; + case MF_NOCOMMAND: map[m].nocommand = 0; break; + case MF_NODROP: map[m].flag.nodrop = 0; break; + case MF_JEXP: map[m].jexp = 0; break; + case MF_BEXP: map[m].bexp = 0; break; + case MF_NOVENDING: map[m].flag.novending = 0; break; + case MF_LOADEVENT: map[m].flag.loadevent = 0; break; + case MF_NOCHAT: map[m].flag.nochat = 0; break; + case MF_NOEXPPENALTY: map[m].flag.noexppenalty = 0; break; + case MF_GUILDLOCK: map[m].flag.guildlock = 0; break; + case MF_TOWN: map[m].flag.town = 0; break; + case MF_AUTOTRADE: map[m].flag.autotrade = 0; break; + case MF_ALLOWKS: map[m].flag.allowks = 0; break; + case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport = 0; break; + case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank = 0; break; + case MF_BATTLEGROUND: map[m].flag.battleground = 0; break; + case MF_RESET: map[m].flag.reset = 0; break; } } @@ -12583,6 +12579,792 @@ BUILDIN_FUNC(charisalpha) return 0; } +//======================================================= +// charisupper <str>, <index> +//------------------------------------------------------- +BUILDIN_FUNC(charisupper) +{ + const char *str = script_getstr(st,2); + int pos = script_getnum(st,3); + + int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISUPPER( str[pos] ) : 0; + + script_pushint(st,val); + return 0; +} + +//======================================================= +// charislower <str>, <index> +//------------------------------------------------------- +BUILDIN_FUNC(charislower) +{ + const char *str = script_getstr(st,2); + int pos = script_getnum(st,3); + + int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISLOWER( str[pos] ) : 0; + + script_pushint(st,val); + return 0; +} + +//======================================================= +// charat <str>, <index> +//------------------------------------------------------- +BUILDIN_FUNC(charat) +{ + const char *str = script_getstr(st,2); + int pos = script_getnum(st,3); + char *output; + + output = (char*)aMallocA(2*sizeof(char)); + output[0] = '\0'; + + if(str && pos >= 0 && (unsigned int)pos < strlen(str)) + sprintf(output, "%c", str[pos]); + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// setchar <string>, <char>, <index> +//------------------------------------------------------- +BUILDIN_FUNC(setchar) +{ + const char *str = script_getstr(st,2); + const char *c = script_getstr(st,3); + int index = script_getnum(st,4); + char *output; + size_t len = strlen(str); + + output = (char*)aMallocA(len + 1); + memcpy(output, str, len); + output[len] = '\0'; + + if(index >= 0 && index < len) + output[index] = c[0]; + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// insertchar <string>, <char>, <index> +//------------------------------------------------------- +BUILDIN_FUNC(insertchar) +{ + const char *str = script_getstr(st,2); + const char *c = script_getstr(st,3); + int index = script_getnum(st,4); + char *output; + size_t len = strlen(str); + + if(index < 0) + index = 0; + else if(index > len) + index = len; + + output = (char*)aMallocA(len + 2); + + memcpy(output, str, index); + output[index] = c[0]; + memcpy(&output[index+1], &str[index], len - index); + output[len+1] = '\0'; + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// delchar <string>, <index> +//------------------------------------------------------- +BUILDIN_FUNC(delchar) +{ + const char *str = script_getstr(st,2); + int index = script_getnum(st,3); + char *output; + size_t len = strlen(str); + + if(index < 0 || index > len) { + //return original + ++len; + output = (char*)aMallocA(len); + memcpy(output, str, len); + script_pushstr(st, output); + return 0; + } + + output = (char*)aMallocA(len); + + memcpy(output, str, index); + memcpy(&output[index], &str[index+1], len - index); + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// strtoupper <str> +//------------------------------------------------------- +BUILDIN_FUNC(strtoupper) +{ + const char *str = script_getstr(st,2); + char *output; + int i = 0; + + output = (char*)aMallocA(strlen(str) + 1); + + while(str[i] != '\0') + output[i++] = TOUPPER(str[i]); + output[i] = '\0'; + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// strtolower <str> +//------------------------------------------------------- +BUILDIN_FUNC(strtolower) +{ + const char *str = script_getstr(st,2); + char *output; + int i = 0; + + output = (char*)aMallocA(strlen(str) + 1); + + while(str[i] != '\0') + output[i++] = TOLOWER(str[i]); + output[i] = '\0'; + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// substr <str>, <start>, <end> +//------------------------------------------------------- +BUILDIN_FUNC(substr) +{ + const char *str = script_getstr(st,2); + char *output; + int start = script_getnum(st,3); + int end = script_getnum(st,4); + + int len = 0; + + if(start >= 0 && end < strlen(str) && start <= end) { + len = end - start + 1; + output = (char*)aMallocA(len + 1); + memcpy(output, &str[start], len); + } else + output = (char*)aMallocA(1); + + output[len] = '\0'; + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// explode <dest_string_array>, <str>, <delimiter> +// Note: delimiter is limited to 1 char +//------------------------------------------------------- +BUILDIN_FUNC(explode) +{ + struct script_data* data = script_getdata(st, 2); + const char *str = script_getstr(st,3); + const char delimiter = script_getstr(st, 4)[0]; + int32 id; + size_t len = strlen(str); + int i = 0, j = 0; + int start; + + + char *temp; + const char* name; + + TBL_PC* sd = NULL; + + temp = (char*)aMallocA(len + 1); + + if( !data_isreference(data) ) + { + ShowError("script:explode: not a variable\n"); + script_reportdata(data); + st->state = END; + return 1;// not a variable + } + + id = reference_getid(data); + start = reference_getindex(data); + name = reference_getname(data); + + if( not_array_variable(*name) ) + { + ShowError("script:explode: illegal scope\n"); + script_reportdata(data); + st->state = END; + return 1;// not supported + } + + if( !is_string_variable(name) ) + { + ShowError("script:explode: not string array\n"); + script_reportdata(data); + st->state = END; + return 1;// data type mismatch + } + + if( not_server_variable(*name) ) + { + sd = script_rid2sd(st); + if( sd == NULL ) + return 0;// no player attached + } + + while(str[i] != '\0') { + if(str[i] == delimiter && start < 127) { //break at delimiter but ignore after reaching last array index + temp[j] = '\0'; + set_reg(st, sd, reference_uid(id, start++), name, (void*)temp, reference_getref(data)); + j = 0; + ++i; + } else { + temp[j++] = str[i++]; + } + } + //set last string + temp[j] = '\0'; + set_reg(st, sd, reference_uid(id, start), name, (void*)temp, reference_getref(data)); + + aFree(temp); + return 0; +} + +//======================================================= +// implode <string_array> +// implode <string_array>, <glue> +//------------------------------------------------------- +BUILDIN_FUNC(implode) +{ + struct script_data* data = script_getdata(st, 2); + const char *glue = NULL, *name, *temp; + int32 glue_len = 0, array_size, id; + size_t len = 0; + int i, k = 0; + + TBL_PC* sd = NULL; + + char *output; + + if( !data_isreference(data) ) + { + ShowError("script:implode: not a variable\n"); + script_reportdata(data); + st->state = END; + return 1;// not a variable + } + + id = reference_getid(data); + name = reference_getname(data); + + if( not_array_variable(*name) ) + { + ShowError("script:implode: illegal scope\n"); + script_reportdata(data); + st->state = END; + return 1;// not supported + } + + if( !is_string_variable(name) ) + { + ShowError("script:implode: not string array\n"); + script_reportdata(data); + st->state = END; + return 1;// data type mismatch + } + + if( not_server_variable(*name) ) + { + sd = script_rid2sd(st); + if( sd == NULL ) + return 0;// no player attached + } + + //count chars + array_size = getarraysize(st, id, reference_getindex(data), is_string_variable(name), reference_getref(data)) - 1; + + if(array_size == -1) //empty array check (AmsTaff) + { + ShowWarning("script:implode: array length = 0\n"); + output = (char*)aMallocA(sizeof(char)*5); + sprintf(output,"%s","NULL"); + } else { + for(i = 0; i <= array_size; ++i) { + temp = (char*) get_val2(st, reference_uid(id, i), reference_getref(data)); + len += strlen(temp); + script_removetop(st, -1, 0); + } + + //allocate mem + if( script_hasdata(st,3) ) { + glue = script_getstr(st,3); + glue_len = strlen(glue); + len += glue_len * (array_size); + } + output = (char*)aMallocA(len + 1); + + //build output + for(i = 0; i < array_size; ++i) { + temp = (char*) get_val2(st, reference_uid(id, i), reference_getref(data)); + len = strlen(temp); + memcpy(&output[k], temp, len); + k += len; + if(glue_len != 0) { + memcpy(&output[k], glue, glue_len); + k += glue_len; + } + script_removetop(st, -1, 0); + } + temp = (char*) get_val2(st, reference_uid(id, array_size), reference_getref(data)); + len = strlen(temp); + memcpy(&output[k], temp, len); + k += len; + script_removetop(st, -1, 0); + + output[k] = '\0'; + } + + script_pushstr(st, output); + return 0; +} + +//======================================================= +// sprintf(<format>, ...); +// Implements C sprintf, except format %n. The resulting string is +// returned, instead of being saved in variable by reference. +//------------------------------------------------------- +BUILDIN_FUNC(sprintf) +{ + unsigned int len, argc = 0, arg = 0, buf2_len = 0; + const char* format; + char* p; + char* q; + char* buf = NULL; + char* buf2 = NULL; + struct script_data* data; + StringBuf final_buf; + + // Fetch init data + format = script_getstr(st, 2); + argc = script_lastdata(st)-2; + len = strlen(format); + + // Skip parsing, where no parsing is required. + if(len==0){ + script_pushconststr(st,""); + return 0; + } + + // Pessimistic alloc + CREATE(buf, char, len+1); + + // Need not be parsed, just solve stuff like %%. + if(argc==0){ + sprintf(buf, format); + script_pushstrcopy(st, buf); + aFree(buf); + return 0; + } + + safestrncpy(buf, format, len+1); + + // Issue sprintf for each parameter + StringBuf_Init(&final_buf); + q = buf; + while((p = strchr(q, '%'))!=NULL){ + if(p!=q){ + len = p-q+1; + if(buf2_len<len){ + RECREATE(buf2, char, len); + buf2_len = len; + } + safestrncpy(buf2, q, len); + StringBuf_AppendStr(&final_buf, buf2); + q = p; + } + p = q+1; + if(*p=='%'){ // %% + StringBuf_AppendStr(&final_buf, "%"); + q+=2; + continue; + } + if(*p=='n'){ // %n + ShowWarning("buildin_sprintf: Format %%n not supported! Skipping...\n"); + script_reportsrc(st); + q+=2; + continue; + } + if(arg>=argc){ + ShowError("buildin_sprintf: Not enough arguments passed!\n"); + if(buf) aFree(buf); + if(buf2) aFree(buf2); + StringBuf_Destroy(&final_buf); + script_pushconststr(st,""); + return 1; + } + if((p = strchr(q+1, '%'))==NULL){ + p = strchr(q, 0); // EOS + } + len = p-q+1; + if(buf2_len<len){ + RECREATE(buf2, char, len); + buf2_len = len; + } + safestrncpy(buf2, q, len); + q = p; + + // Note: This assumes the passed value being the correct + // type to the current format specifier. If not, the server + // probably crashes or returns anything else, than expected, + // but it would behave in normal code the same way so it's + // the scripter's responsibility. + data = script_getdata(st, arg+3); + if(data_isstring(data)){ // String + StringBuf_Printf(&final_buf, buf2, script_getstr(st, arg+3)); + }else if(data_isint(data)){ // Number + StringBuf_Printf(&final_buf, buf2, script_getnum(st, arg+3)); + }else if(data_isreference(data)){ // Variable + char* name = reference_getname(data); + if(name[strlen(name)-1]=='$'){ // var Str + StringBuf_Printf(&final_buf, buf2, script_getstr(st, arg+3)); + }else{ // var Int + StringBuf_Printf(&final_buf, buf2, script_getnum(st, arg+3)); + } + }else{ // Unsupported type + ShowError("buildin_sprintf: Unknown argument type!\n"); + if(buf) aFree(buf); + if(buf2) aFree(buf2); + StringBuf_Destroy(&final_buf); + script_pushconststr(st,""); + return 1; + } + arg++; + } + + // Append anything left + if(*q){ + StringBuf_AppendStr(&final_buf, q); + } + + // Passed more, than needed + if(arg<argc){ + ShowWarning("buildin_sprintf: Unused arguments passed.\n"); + script_reportsrc(st); + } + + script_pushstrcopy(st, StringBuf_Value(&final_buf)); + + if(buf) aFree(buf); + if(buf2) aFree(buf2); + StringBuf_Destroy(&final_buf); + + return 0; +} + +//======================================================= +// sscanf(<str>, <format>, ...); +// Implements C sscanf. +//------------------------------------------------------- +BUILDIN_FUNC(sscanf){ + unsigned int argc, arg = 0, len; + struct script_data* data; + struct map_session_data* sd = NULL; + const char* str; + const char* format; + const char* p; + const char* q; + char* buf = NULL; + char* buf_p; + char* ref_str = NULL; + int ref_int; + + // Get data + str = script_getstr(st, 2); + format = script_getstr(st, 3); + argc = script_lastdata(st)-3; + + len = strlen(format); + CREATE(buf, char, len*2+1); + + // Issue sscanf for each parameter + *buf = 0; + q = format; + while((p = strchr(q, '%'))){ + if(p!=q){ + strncat(buf, q, (size_t)(p-q)); + q = p; + } + p = q+1; + if(*p=='*' || *p=='%'){ // Skip + strncat(buf, q, 2); + q+=2; + continue; + } + if(arg>=argc){ + ShowError("buildin_sscanf: Not enough arguments passed!\n"); + script_pushint(st, -1); + if(buf) aFree(buf); + if(ref_str) aFree(ref_str); + return 1; + } + if((p = strchr(q+1, '%'))==NULL){ + p = strchr(q, 0); // EOS + } + len = p-q; + strncat(buf, q, len); + q = p; + + // Validate output + data = script_getdata(st, arg+4); + if(!data_isreference(data) || !reference_tovariable(data)){ + ShowError("buildin_sscanf: Target argument is not a variable!\n"); + script_pushint(st, -1); + if(buf) aFree(buf); + if(ref_str) aFree(ref_str); + return 1; + } + buf_p = reference_getname(data); + if(not_server_variable(*buf_p) && (sd = script_rid2sd(st))==NULL){ + script_pushint(st, -1); + if(buf) aFree(buf); + if(ref_str) aFree(ref_str); + return 0; + } + + // Save value if any + if(buf_p[strlen(buf_p)-1]=='$'){ // String + if(ref_str==NULL){ + CREATE(ref_str, char, strlen(str)+1); + } + if(sscanf(str, buf, ref_str)==0){ + break; + } + set_reg(st, sd, add_str(buf_p), buf_p, (void *)(ref_str), reference_getref(data)); + }else{ // Number + if(sscanf(str, buf, &ref_int)==0){ + break; + } + set_reg(st, sd, add_str(buf_p), buf_p, (void *)(ref_int), reference_getref(data)); + } + arg++; + + // Disable used format (%... -> %*...) + buf_p = strchr(buf, 0); + memmove(buf_p-len+2, buf_p-len+1, len); + *(buf_p-len+1) = '*'; + } + + // Passed more, than needed + if(arg<argc){ + ShowWarning("buildin_sscanf: Unused arguments passed.\n"); + script_reportsrc(st); + } + + script_pushint(st, arg); + if(buf) aFree(buf); + if(ref_str) aFree(ref_str); + + return 0; +} + +//======================================================= +// strpos(<haystack>, <needle>) +// strpos(<haystack>, <needle>, <offset>) +// +// Implements PHP style strpos. Adapted from code from +// http://www.daniweb.com/code/snippet313.html, Dave Sinkula +//------------------------------------------------------- +BUILDIN_FUNC(strpos) { + const char *haystack = script_getstr(st,2); + const char *needle = script_getstr(st,3); + int i; + size_t len; + + if( script_hasdata(st,4) ) + i = script_getnum(st,4); + else + i = 0; + + if ( strlen(needle) == 0 ) { + script_pushint(st, -1); + return 0; + } + + len = strlen(haystack); + for ( ; i < len; ++i ) { + if ( haystack[i] == *needle ) { + // matched starting char -- loop through remaining chars + const char *h, *n; + for ( h = &haystack[i], n = needle; *h && *n; ++h, ++n ) { + if ( *h != *n ) { + break; + } + } + if ( !*n ) { // matched all of 'needle' to null termination + script_pushint(st, i); + return 0; + } + } + } + script_pushint(st, -1); + return 0; +} + +//=============================================================== +// replacestr <input>, <search>, <replace>{, <usecase>{, <count>}} +// +// Note: Finds all instances of <search> in <input> and replaces +// with <replace>. If specified will only replace as many +// instances as specified in <count>. By default will be case +// sensitive. +//--------------------------------------------------------------- +BUILDIN_FUNC(replacestr) +{ + const char *input = script_getstr(st, 2); + const char *find = script_getstr(st, 3); + const char *replace = script_getstr(st, 4); + size_t inputlen = strlen(input); + size_t findlen = strlen(find); + struct StringBuf output; + bool usecase = true; + + int count = 0; + int numFinds = 0; + int i = 0, f = 0; + + if(findlen == 0) { + ShowError("script:replacestr: Invalid search length.\n"); + st->state = END; + return 1; + } + + if(script_hasdata(st, 5)) { + if(script_isint(st,5)) + usecase = script_getnum(st, 5) != 0; + else { + ShowError("script:replacestr: Invalid usecase value. Expected int got string\n"); + st->state = END; + return 1; + } + } + + if(script_hasdata(st, 6)) { + if(script_isint(st,6)) + count = script_getnum(st, 6); + else { + ShowError("script:replacestr: Invalid count value. Expected int got string\n"); + st->state = END; + return 1; + } + } + + StringBuf_Init(&output); + + for(; i < inputlen; i++) { + if(count && count == numFinds) { //found enough, stop looking + break; + } + + for(f = 0; f <= findlen; f++) { + if(f == findlen) { //complete match + numFinds++; + StringBuf_AppendStr(&output, replace); + + i += findlen - 1; + break; + } else { + if(usecase) { + if((i + f) > inputlen || input[i + f] != find[f]) { + StringBuf_Printf(&output, "%c", input[i]); + break; + } + } else { + if(((i + f) > inputlen || input[i + f] != find[f]) && TOUPPER(input[i+f]) != TOUPPER(find[f])) { + StringBuf_Printf(&output, "%c", input[i]); + break; + } + } + } + } + } + + //append excess after enough found + if(i < inputlen) + StringBuf_AppendStr(&output, &(input[i])); + + script_pushstrcopy(st, StringBuf_Value(&output)); + StringBuf_Destroy(&output); + return 0; +} + +//======================================================== +// countstr <input>, <search>{, <usecase>} +// +// Note: Counts the number of times <search> occurs in +// <input>. By default will be case sensitive. +//-------------------------------------------------------- +BUILDIN_FUNC(countstr) +{ + const char *input = script_getstr(st, 2); + const char *find = script_getstr(st, 3); + size_t inputlen = strlen(input); + size_t findlen = strlen(find); + bool usecase = true; + + int numFinds = 0; + int i = 0, f = 0; + + if(findlen == 0) { + ShowError("script:countstr: Invalid search length.\n"); + st->state = END; + return 1; + } + + if(script_hasdata(st, 4)) { + if(script_isint(st,4)) + usecase = script_getnum(st, 4) != 0; + else { + ShowError("script:countstr: Invalid usecase value. Expected int got string\n"); + st->state = END; + return 1; + } + } + + for(; i < inputlen; i++) { + for(f = 0; f <= findlen; f++) { + if(f == findlen) { //complete match + numFinds++; + i += findlen - 1; + break; + } else { + if(usecase) { + if((i + f) > inputlen || input[i + f] != find[f]) { + break; + } + } else { + if(((i + f) > inputlen || input[i + f] != find[f]) && TOUPPER(input[i+f]) != TOUPPER(find[f])) { + break; + } + } + } + } + } + script_pushint(st, numFinds); + return 0; +} + + /// Changes the display name and/or display class of the npc. /// Returns 0 is successful, 1 if the npc does not exist. /// @@ -13122,7 +13904,7 @@ BUILDIN_FUNC(setitemscript) n=script_getnum(st,4); i_data = itemdb_exists(item_id); - if (!i_data || script==NULL || script[0]!='{') { + if (!i_data || script==NULL || ( script[0] && script[0]!='{' )) { script_pushint(st,0); return 0; } @@ -13140,7 +13922,7 @@ BUILDIN_FUNC(setitemscript) if(*dstscript) script_free_code(*dstscript); - *dstscript = parse_script(script, "script_setitemscript", 0, 0); + *dstscript = script[0] ? parse_script(script, "script_setitemscript", 0, 0) : NULL; script_pushint(st,1); return 0; } @@ -14991,87 +15773,133 @@ BUILDIN_FUNC(searchstores) searchstore_open(sd, uses, effect); return 0; } - -/// Returns the successful use of a Rune Knight Runestone. -/// -/// SuccessRuneUse() -/// -BUILDIN_FUNC(successruneuse) +/// Displays a number as large digital clock. +/// showdigit <value>[,<type>]; +BUILDIN_FUNC(showdigit) { - struct item_data* id = NULL; + unsigned int type = 0; + int value; struct map_session_data* sd; - struct script_data* data; if( ( sd = script_rid2sd(st) ) == NULL ) - return 0; // no player attached, report source - - data = script_getdata(st,2); - get_val(st, data); // convert into value in case of a variable - - if( data_isstring(data) ) - id = itemdb_searchname(conv_str(st, data)); - else - id = itemdb_exists(conv_num(st, data)); - - if( id == NULL ) { - ShowError("buildin_successruneuse: Invalid item '%s'.\n", script_getstr(st,2)); - script_pushint(st,0); - return 1; + return 0; } - if( (sd->class_&~(JOBL_UPPER|JOBL_BABY)) == MAPID_RUNE_KNIGHT ) + value = script_getnum(st,2); + + if( script_hasdata(st,3) ) { - int skilllv = pc_checkskill(sd,RK_RUNEMASTERY); - int i = (sd->status.dex + sd->status.luk ) / 20 + (skilllv?55+skilllv:0) + 30; + type = script_getnum(st,3); - if (rand() % 100 < i) - script_pushint(st, 1); - else + if( type > 3 ) { - script_pushint(st, 0); + ShowError("buildin_showdigit: Invalid type %u.\n", type); + return 1; + } + } - i = rand() % 100; // reroll for fail effects - if( i < 3 ) - { - long damage = (1000 * id->weight) - (sd->battle_status.mdef + sd->battle_status.mdef2); - clif_damage(&sd->bl, &sd->bl, gettick(), 0, 0, damage, 0, 0, 0); - status_damage(&sd->bl, &sd->bl, damage, 0, 0, 0); - } - else if( i < 13 ) - { // Random status effect - struct { - sc_type type; - int duration; - } effects[] = { - { SC_FREEZE, 30000 }, - { SC_STUN, 5000 }, - { SC_SLEEP, 20000 }, - { SC_SILENCE, 20000 }, - { SC_BLIND, 20000 }, - }; - i = rand()%ARRAYLENGTH(effects); // redesignate i to random status effect+duration. - sc_start(&sd->bl, effects[i].type, 100, 1, effects[i].duration); - } - else if( i < 15 ) - pc_randomwarp(sd, CLR_TELEPORT); - else if( i < 18 ) - ; // Unknown effect, however weight of the item used is taken into account. - else if( i < 19 ) - { - if (!status_isimmune(&sd->bl)) - status_percent_heal(&sd->bl, 100, 100); + clif_showdigit(sd, (unsigned char)type, value); + return 0; +} +/** + * Rune Knight + **/ +BUILDIN_FUNC(makerune) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + clif_skill_produce_mix_list(sd,RK_RUNEMASTERY,24); + sd->itemid = script_getnum(st,2); + return 0; +} +/** + * checkdragon() returns 1 if mounting a dragon or 0 otherwise. + **/ +BUILDIN_FUNC(checkdragon) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( sd->sc.option&OPTION_DRAGON ) + script_pushint(st,1); + else + script_pushint(st,0); + return 0; +} +/** + * setdragon({optional Color}) returns 1 on success or 0 otherwise + * - Toggles the dragon on a RK if he can mount; + * @param Color - when not provided uses the green dragon; + * - 1 : Green Dragon + * - 2 : Brown Dragon + * - 3 : Gray Dragon + * - 4 : Blue Dragon + * - 5 : Red Dragon + **/ +BUILDIN_FUNC(setdragon) { + TBL_PC* sd; + int color = script_hasdata(st,2) ? script_getnum(st,2) : 0; + unsigned int option = OPTION_DRAGON1; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( !pc_checkskill(sd,RK_DRAGONTRAINING) || (sd->class_&MAPID_THIRDMASK) != MAPID_RUNE_KNIGHT ) + script_pushint(st,0);//Doesn't have the skill or it's not a Rune Knight + else if ( sd->sc.option&OPTION_DRAGON ) {//Is mounted; release + pc_setoption(sd, sd->sc.option&~OPTION_DRAGON); + script_pushint(st,1); + } else {//Not mounted; Mount now. + if( color ) { + option = ( color == 1 ? OPTION_DRAGON1 : + color == 2 ? OPTION_DRAGON2 : + color == 3 ? OPTION_DRAGON3 : + color == 4 ? OPTION_DRAGON4 : + color == 5 ? OPTION_DRAGON5 : 0); + if( !option ) { + ShowWarning("script_setdragon: Unknown Color %d used; changing to green (1)\n",color); + option = OPTION_DRAGON1; } - else if( i >= 20 ) - ; // Unknown effect } + pc_setoption(sd, sd->sc.option|option); + script_pushint(st,1); } - else - script_pushint(st, 0); + return 0; +} +/** + * ismounting() returns 1 if mounting a new mount or 0 otherwise + **/ +BUILDIN_FUNC(ismounting) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( sd->sc.option&OPTION_MOUNTING ) + script_pushint(st,1); + else + script_pushint(st,0); return 0; } +/** + * setmounting() returns 1 on success or 0 otherwise + * - Toggles new mounts on a player when he can mount + * - Will fail if the player is mounting a non-new mount, e.g. dragon, peco, wug, etc. + * - Will unmount the player is he is already mounting + **/ +BUILDIN_FUNC(setmounting) { + TBL_PC* sd; + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + if( sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR) ) + script_pushint(st,0);//can't mount with one of these + else { + if( sd->sc.option&OPTION_MOUNTING ) + pc_setoption(sd, sd->sc.option&~OPTION_MOUNTING);//release mount + else + pc_setoption(sd, sd->sc.option|OPTION_MOUNTING);//mount + script_pushint(st,1);//in both cases, return 1. + } + return 0; +} // declarations that were supposed to be exported from npc_chat.c #ifdef PCRE_SUPPORT BUILDIN_FUNC(defpattern); @@ -15180,7 +16008,6 @@ struct script_function buildin_func[] = { BUILDIN_DEF(setfalcon,"?"), BUILDIN_DEF(checkfalcon,""), BUILDIN_DEF(setriding,"?"), - BUILDIN_DEF(setdragon,"??"), BUILDIN_DEF(checkriding,""), BUILDIN_DEF2(savepoint,"save","sii"), BUILDIN_DEF(savepoint,"sii"), @@ -15190,7 +16017,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(openstorage,""), BUILDIN_DEF(guildopenstorage,""), BUILDIN_DEF(itemskill,"vi"), - BUILDIN_DEF(produce,"i?"), + BUILDIN_DEF(produce,"i"), BUILDIN_DEF(cooking,"i"), BUILDIN_DEF(monster,"siisii?"), BUILDIN_DEF(getmobdrops,"i"), @@ -15354,6 +16181,22 @@ struct script_function buildin_func[] = { BUILDIN_DEF(unequip,"i"), // unequip command [Spectre] BUILDIN_DEF(getstrlen,"s"), //strlen [Valaris] BUILDIN_DEF(charisalpha,"si"), //isalpha [Valaris] + BUILDIN_DEF(charat,"si"), + BUILDIN_DEF(setchar,"ssi"), + BUILDIN_DEF(insertchar,"ssi"), + BUILDIN_DEF(delchar,"si"), + BUILDIN_DEF(strtoupper,"s"), + BUILDIN_DEF(strtolower,"s"), + BUILDIN_DEF(charisupper, "si"), + BUILDIN_DEF(charislower, "si"), + BUILDIN_DEF(substr,"sii"), + BUILDIN_DEF(explode, "rss"), + BUILDIN_DEF(implode, "r?"), + BUILDIN_DEF(sprintf,"s*"), // [Mirei] + BUILDIN_DEF(sscanf,"ss*"), // [Mirei] + BUILDIN_DEF(strpos,"ss?"), + BUILDIN_DEF(replacestr,"sss??"), + BUILDIN_DEF(countstr,"ss?"), BUILDIN_DEF(setnpcdisplay,"sv??"), BUILDIN_DEF(compare,"ss"), // Lordalfa - To bring strstr to scripting Engine. BUILDIN_DEF(getiteminfo,"ii"), //[Lupus] returns Items Buy / sell Price, etc info @@ -15437,7 +16280,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(pushpc,"ii"), BUILDIN_DEF(buyingstore,"i"), BUILDIN_DEF(searchstores,"ii"), - BUILDIN_DEF(successruneuse,"?"), + BUILDIN_DEF(showdigit,"i?"), // WoE SE BUILDIN_DEF(agitstart2,""), BUILDIN_DEF(agitend2,""), @@ -15469,7 +16312,14 @@ struct script_function buildin_func[] = { BUILDIN_DEF(instance_npcname,"s?"), BUILDIN_DEF(has_instance,"s?"), BUILDIN_DEF(instance_warpall,"sii?"), - + /** + * 3rd-related + **/ + BUILDIN_DEF(makerune,"i"), + BUILDIN_DEF(checkdragon,""),//[Ind] + BUILDIN_DEF(setdragon,"?"),//[Ind] + BUILDIN_DEF(ismounting,""),//[Ind] + BUILDIN_DEF(setmounting,""),//[Ind] //Quest Log System [Inkfish] BUILDIN_DEF(setquest, "i"), BUILDIN_DEF(erasequest, "i"), diff --git a/src/map/skill.c b/src/map/skill.c index 26d812d86..a7e75b6f4 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -37,7 +37,7 @@ #include <stdlib.h> #include <string.h> #include <time.h> -#include <math.h> + #define SKILLUNITTIMER_INTERVAL 100 @@ -59,13 +59,28 @@ struct s_skill_db skill_db[MAX_SKILL_DB]; struct s_skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; struct s_skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; struct s_skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; +//Warlock +struct s_skill_spellbook_db { + int nameid; + int skillid; + int points; +}; + +struct s_skill_spellbook_db skill_spellbook_db[MAX_SKILL_SPELLBOOK_DB]; +//Guillotine Cross +struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB]; struct s_skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT]; int firewall_unit_pos; int icewall_unit_pos; - +int earthstrain_unit_pos; +//early declaration +int skill_stasis_check(struct block_list *bl, int src_id, int skillid); //Since only mob-casted splash skills can hit ice-walls -#define splash_target(bl) (bl->type==BL_MOB?BL_SKILL|BL_CHAR:BL_CHAR) +static inline int splash_target(struct block_list* bl) +{ + return ( bl->type == BL_MOB ) ? BL_SKILL|BL_CHAR : BL_CHAR; +} /// Returns the id of the skill, or 0 if not found. int skill_name2id(const char* name) @@ -143,9 +158,7 @@ int skill_get_itemqty(int id, int idx) { skill_get (skill_db[id].amount[idx], int skill_get_zeny( int id ,int lv ) { skill_get (skill_db[id].zeny[lv-1], id, lv); } int skill_get_num( int id ,int lv ) { skill_get (skill_db[id].num[lv-1], id, lv); } int skill_get_cast( int id ,int lv ) { skill_get (skill_db[id].cast[lv-1], id, lv); } -int skill_get_fixedcast( int id ,int lv ) { skill_get (skill_db[id].fixedcast[lv-1], id, lv); } int skill_get_delay( int id ,int lv ) { skill_get (skill_db[id].delay[lv-1], id, lv); } -int skill_get_cooldown( int id ,int lv ) { skill_get (skill_db[id].cooldown[lv-1], id, lv); } int skill_get_walkdelay( int id ,int lv ) { skill_get (skill_db[id].walkdelay[lv-1], id, lv); } int skill_get_time( int id ,int lv ) { skill_get (skill_db[id].upkeep_time[lv-1], id, lv); } int skill_get_time2( int id ,int lv ) { skill_get (skill_db[id].upkeep_time2[lv-1], id, lv); } @@ -169,6 +182,7 @@ int skill_get_unit_target( int id ) { skill_get (skill_db[id].unit_target& int skill_get_unit_bl_target( int id ) { skill_get (skill_db[id].unit_target&BL_ALL, id, 1); } int skill_get_unit_flag( int id ) { skill_get (skill_db[id].unit_flag, id, 1); } int skill_get_unit_layout_type( int id ,int lv ){ skill_get (skill_db[id].unit_layout_type[lv-1], id, lv); } +int skill_get_cooldown( int id ,int lv ) { skill_get (skill_db[id].cooldown[lv-1], id, lv); } int skill_tree_get_max(int id, int b_class) { @@ -240,6 +254,12 @@ int skill_get_range2 (struct block_list *bl, int id, int lv) case MA_CHARGEARROW: case SN_FALCONASSAULT: case HT_POWER: + /** + * Ranger + **/ + case RA_ARROWSTORM: + case RA_AIMEDBOLT: + case RA_WUGBITE: if( bl->type == BL_PC ) range += pc_checkskill((TBL_PC*)bl, AC_VULTURE); else @@ -260,6 +280,36 @@ int skill_get_range2 (struct block_list *bl, int id, int lv) if (bl->type == BL_PC) range = skill_get_range(NJ_SHADOWJUMP,pc_checkskill((TBL_PC*)bl,NJ_SHADOWJUMP)); break; + /** + * Warlock + **/ + case WL_WHITEIMPRISON: + case WL_SOULEXPANSION: + case WL_FROSTMISTY: + case WL_MARSHOFABYSS: + case WL_SIENNAEXECRATE: + case WL_DRAINLIFE: + case WL_CRIMSONROCK: + case WL_HELLINFERNO: + case WL_COMET: + case WL_CHAINLIGHTNING: + case WL_TETRAVORTEX: + case WL_RELEASE: + if( bl->type == BL_PC ) + range += pc_checkskill((TBL_PC*)bl, WL_RADIUS); + break; + /** + * Ranger Bonus + **/ + case HT_LANDMINE: + case HT_FREEZINGTRAP: + case HT_BLASTMINE: + case HT_CLAYMORETRAP: + case RA_CLUSTERBOMB: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + if( bl->type == BL_PC ) + range += (1 + pc_checkskill((TBL_PC*)bl, RA_RESEARCHTRAP))/2; } if( !range && bl->type != BL_PC ) @@ -269,14 +319,10 @@ int skill_get_range2 (struct block_list *bl, int id, int lv) int skill_calc_heal(struct block_list *src, struct block_list *target, int skill_id, int skill_lv, bool heal) { - int skill, hp, mod = 100; + int skill, hp; struct map_session_data *sd = BL_CAST(BL_PC, src); struct map_session_data *tsd = BL_CAST(BL_PC, target); struct status_change* sc; - struct status_data *status; - bool FullCalc = false; - - status = status_get_status_data(src); switch( skill_id ) { @@ -292,83 +338,43 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, int skill hp = (skill_lv>6)?666:skill_lv*100; break; default: - FullCalc = true; // Enables full calculation, which adds heal variance. if (skill_lv >= battle_config.max_heal_lv) return battle_config.max_heal; - - // iRO Wiki states as of 2011/08/22: - // heal = ( [(Base Level + INT) / 5] ?30 ) ?(Heal Level / 10) ?(1 + (Modifiers / 100)) + MATK - // fixme: Does not match up with iRO's heal, level 1 or level 10 - // with 219 matk + HP_MEDITATO (10) (no gear), level 1 = 361; level 10 = 1839 - if( skill_id == AB_HIGHNESSHEAL ) { - skill = pc_checkskill(sd,AL_HEAL); - if( skill < 0 ) - skill = 10; - } - else - skill = skill_lv; - - // Calculate base heal rate - hp = ( ( ( status_get_lv(src) + status_get_int(src) ) / 5) * 30 ) * skill / 10; - - // Increment heal by skill/type modifiers + #if RRMODE + /** + * Renewal Heal Formula (from Doddler) + * TODO: whats that( 1+ %Modifier / 100 ) ? currently using 'x1' (100/100) until found out + * - Min = ( [ ( BaseLvl + INT ) / 5 ] * 30 ) * (1+( %Modifier / 100)) * (HealLvl * 0.1) + StatusMATK + EquipMATK - [(WeaponMATK * WeaponLvl) / 10] + * - Max = ( [ ( BaseLvl + INT ) / 5 ] * 30 ) * (1+( %Modifier / 100)) * (HealLvl * 0.1) + StatusMATK + EquipMATK + [(WeaponMATK * WeaponLvl) / 10] + **/ + hp = ( ( ( ( status_get_lv(src) + status_get_int(src) ) / 5 ) * 30 ) * ( skill_lv / 10 ) + status_get_matk_min(src) + status_get_matk_max(src) - ( ( status_get_matk_max(src) * status_get_wlv(src) ) / 10 ) ) + rand()%( ( ( ( status_get_lv(src) + status_get_int(src) ) / 5 ) * 30 ) * ( skill_lv / 10 ) + status_get_matk_min(src) + status_get_matk_max(src) + ( ( status_get_matk_max(src) * status_get_wlv(src) ) / 10 ) ); + #else + hp = ( status_get_lv(src) + status_get_int(src) ) / 8 * (4 + ( skill_id == AB_HIGHNESSHEAL ? ( sd ? pc_checkskill(sd,AL_HEAL) : 10 ) : skill_lv ) * 8); + #endif if( sd && ((skill = pc_checkskill(sd, HP_MEDITATIO)) > 0) ) - mod += skill * 2; + hp += hp * skill * 2 / 100; else if( src->type == BL_HOM && (skill = merc_hom_checkskill(((TBL_HOM*)src), HLIF_BRAIN)) > 0 ) - mod += skill * 2; + hp += hp * skill * 2 / 100; break; - } + } + + if( ( (target && target->type == BL_MER) || !heal ) && skill_id != NPC_EVILLAND ) + hp >>= 1; - // Increment heal by item-based modifiers if( sd && (skill = pc_skillheal_bonus(sd, skill_id)) ) - mod += skill; + hp += hp*skill/100; + if( tsd && (skill = pc_skillheal2_bonus(tsd, skill_id)) ) - mod += skill; + hp += hp*skill/100; sc = status_get_sc(target); if( sc && sc->count ) { if( sc->data[SC_CRITICALWOUND] && heal ) // Critical Wound has no effect on offensive heal. [Inkfish] - mod -= sc->data[SC_CRITICALWOUND]->val2; + hp -= hp * sc->data[SC_CRITICALWOUND]->val2/100; if( sc->data[SC_INCHEALRATE] && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN ) - mod += sc->data[SC_INCHEALRATE]->val1; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish] - if( sc->data[SC_VITALITYACTIVATION] && heal && skill_id != BA_APPLEIDUN ) - mod += sc->data[SC_VITALITYACTIVATION]->val2; + hp += hp * sc->data[SC_INCHEALRATE]->val1/100; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish] } - - // Adjust the HP recovered rate by adding all of the modifiers together. - hp = hp * mod / 100; - - // Get weapon level and weapon matk for variance calculations if it's a player. - // Mobs have their own variance, we've assumed: - // Weapon Level = 1 - // Weapom_Matk = ? - // Need more information before that can be implemented. - if( sd && FullCalc) - { - int matk = 0; - int smatk = sd->battle_status.status_matk; - int ematk = sd->equipment_matk; - int wmatk = 0; - int wlv = 1; - int index = sd->equip_index[EQI_HAND_R]; - - if( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON ) - wlv = sd->inventory_data[index]->wlv; - - wmatk = (int)( sd->weapon_matk + ( (float)( ( rand() % 20 ) - 10 ) / 100 * wlv * sd->weapon_matk ) + ( sd->battle_status.rhw.atk2 ) ); - matk = ( smatk + wmatk + ematk ); - - hp += matk; - } - - // mercenaries only take half-effectiveness from heals. - if( ( (target && target->type == BL_MER) || !heal ) && skill_id != NPC_EVILLAND ) - hp >>= 1; - - // Give Highness Heal it's extra heal - if( skill_id == AB_HIGHNESSHEAL ) - hp = hp * (170 + 30 * skill_lv) / 100; return hp; } @@ -430,6 +436,9 @@ int skillnotok (int skillid, struct map_session_data *sd) if(map[m].flag.restricted && map[m].zone && skill_get_nocast (skillid) & (8*map[m].zone)) return 1; + if( sd->sc.option&OPTION_MOUNTING ) + return 1;//You can't use skills while in the new mounts (The client doesn't let you, this is to make cheat-safe) + switch (skillid) { case AL_WARP: if(map[m].flag.nowarp) { @@ -462,6 +471,12 @@ int skillnotok (int skillid, struct map_session_data *sd) return 1; } break; + case GC_DARKILLUSION: + if( map_flag_gvg(m) ) { + clif_skill_fail(sd,skillid,0,0); + return 1; + } + break; case GD_EMERGENCYCALL: if ( !(battle_config.emergency_call&((agit_flag || agit2_flag)?2:1)) || @@ -472,6 +487,23 @@ int skillnotok (int skillid, struct map_session_data *sd) return 1; } break; + case BS_GREED: + case WS_CARTBOOST: + case BS_HAMMERFALL: + case BS_ADRENALINE: + case MC_CARTREVOLUTION: + case MC_MAMMONITE: + case WS_MELTDOWN: + case MG_SIGHT: + case TF_HIDING: + /** + * These skills cannot be used while in mado gear (credits to Xantara) + **/ + if(sd->sc.option&OPTION_MADOGEAR) { + clif_skill_fail(sd,skillid,0,0); + return 1; + } + break; } return (map[m].flag.noskill); } @@ -523,6 +555,8 @@ struct s_skill_unit_layout* skill_get_unit_layout (int skillid, int skilllv, str return &skill_unit_layout [firewall_unit_pos + dir]; else if (skillid == WZ_ICEWALL) return &skill_unit_layout [icewall_unit_pos + dir]; + else if( skillid == WL_EARTHSTRAIN ) //Warlock + return &skill_unit_layout [earthstrain_unit_pos + dir]; ShowError("skill_get_unit_layout: unknown unit layout for skill %d (level %d)\n", skillid, skilllv); return &skill_unit_layout[0]; // default 1x1 layout @@ -639,6 +673,9 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int rate=(sd->status.job_level+9)/10; skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skill<rate)?skill:rate,tick,SD_LEVEL); } + // Automatic trigger of Warg Strike [Jobbie] + if( pc_iswug(sd) && (sd->status.weapon == W_BOW || sd->status.weapon == W_FIST) && (skill=pc_checkskill(sd,RA_WUGSTRIKE)) > 0 && rand()%1000 <= sstatus->luk*10/3+1 ) + skill_castend_damage_id(src,bl,RA_WUGSTRIKE,skill,tick,0); // Gank if(dstmd && sd->status.weapon != W_BOW && (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 && @@ -735,7 +772,14 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int case WZ_METEOR: sc_start(bl,SC_STUN,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); break; - +#if FIREIVY_ON + //case WZ_FIREIVY: + // Testing for Fire Ivy + //sc_start(bl,SC_STUN,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); + //sc_start(bl,SC_STUN,(5*skilllv+35),skilllv,skill_get_time2(skillid,skilllv)); + //sc_start(bl,SC_BURNING,(8*skilllv+35),skilllv,skill_get_time2(skillid,skilllv)); + //break; +#endif case WZ_VERMILION: sc_start(bl,SC_BLIND,4*skilllv,skilllv,skill_get_time2(skillid,skilllv)); break; @@ -981,20 +1025,104 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int case NPC_CRITICALWOUND: sc_start(bl,SC_CRITICALWOUND,100,skilllv,skill_get_time2(skillid,skilllv)); break; + /** + * Rune Knight + **/ + case RK_HUNDREDSPEAR: + if( !sd || pc_checkskill(sd,KN_SPEARBOOMERANG) == 0 ) + break; // Spear Boomerang auto cast chance only works if you have mastered Spear Boomerang. + rate = 10 + 3 * skilllv; + if( rand()%100 < rate ) + skill_castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0); + break; case RK_WINDCUTTER: sc_start(bl,SC_FEAR,3+2*skilllv,skilllv,skill_get_time(skillid,skilllv)); break; case RK_DRAGONBREATH: - sc_start4(bl,SC_BURNING,15,skilllv,src->id,0,0,skill_get_time(skillid,skilllv)); + sc_start4(bl,SC_BURNING,5+5*skilllv,skilllv,1000,src->id,0,skill_get_time(skillid,skilllv)); break; + /** + * Arch Bishop + **/ case AB_ADORAMUS: - if ( sd ) + if( tsc && !tsc->data[SC_DECREASEAGI] ) //Prevent duplicate agi-down effect. + sc_start(bl, SC_ADORAMUS, 100, skilllv, skill_get_time(skillid, skilllv)); + break; + /** + * Warlock + **/ + case WL_CRIMSONROCK: + sc_start(bl, SC_STUN, 40, skilllv, skill_get_time(skillid, skilllv)); + break; + case WL_COMET: + sc_start4(bl,SC_BURNING,100,skilllv,1000,src->id,0,skill_get_time(skillid,skilllv)); + break; + case WL_EARTHSTRAIN: { - int rate = (skilllv*4) + sd->status.job_level / 2; - sc_start(bl, SC_BLIND, rate, skilllv, skill_get_time(skillid, skilllv)); - sc_start(bl, SC_ADORAMUS, rate, skilllv, skill_get_time2(skillid, skilllv)); + int rate = 0, i; + const int pos[5] = { EQP_WEAPON, EQP_HELM, EQP_SHIELD, EQP_ARMOR, EQP_ACC }; + rate = 6 * skilllv + sstatus->dex / 10 + (sd? sd->status.job_level / 4 : 0) - tstatus->dex /5;// The tstatus->dex / 5 part is unofficial, but players gotta have some kind of way to have resistance. [Rytech] + //rate -= rate * tstatus->dex / 200; // Disabled until official resistance is found. + + for( i = 0; i < skilllv; i++ ) + skill_strip_equip(bl,pos[i],rate,skilllv,skill_get_time2(skillid,skilllv)); + } + break; + case WL_JACKFROST: + sc_start(bl,SC_FREEZE,100,skilllv,skill_get_time(skillid,skilllv)); + break; + /** + * Ranger + **/ + case RA_WUGBITE: + sc_start(bl, SC_BITE, 70, skilllv, skill_get_time(skillid, skilllv) + (sd ? pc_checkskill(sd,RA_TOOTHOFWUG) * 1000 : 0)); // Need official chance. + break; + case RA_SENSITIVEKEEN: + if( rand()%100 < 8 * skilllv ) + skill_castend_damage_id(src, bl, RA_WUGBITE, sd ? pc_checkskill(sd, RA_WUGBITE):skilllv, tick, SD_ANIMATION); + break; + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + if( dstmd && !(dstmd->status.mode&MD_BOSS) ) + sc_start2(bl,SC_ELEMENTALCHANGE,100,skilllv,skill_get_ele(skillid,skilllv),skill_get_time2(skillid,skilllv)); + break; + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: + sc_start(bl, (skillid == RA_FIRINGTRAP) ? SC_BURNING:SC_FREEZING, 40 + 10 * skilllv, skilllv, skill_get_time2(skillid, skilllv)); + break; + /** + * Mechanic + **/ + case NC_PILEBUNKER: + if( rand()%100 < 5 + 15*skilllv ) + { //Deactivatable Statuses: Kyrie Eleison, Auto Guard, Steel Body, Assumptio, and Millennium Shield + status_change_end(bl, SC_KYRIE, -1); + status_change_end(bl, SC_AUTOGUARD, -1); + status_change_end(bl, SC_STEELBODY, -1); + status_change_end(bl, SC_ASSUMPTIO, -1); + status_change_end(bl, SC_MILLENNIUMSHIELD, -1); } break; + case NC_FLAMELAUNCHER: + sc_start4(bl, SC_BURNING, 50 + 10 * skilllv, skilllv, 1000, src->id, 0, skill_get_time2(skillid, skilllv)); + break; + case NC_COLDSLOWER: + sc_start(bl, SC_FREEZE, 10 * skilllv, skilllv, skill_get_time(skillid, skilllv)); + sc_start(bl, SC_FREEZING, 20 + 10 * skilllv, skilllv, skill_get_time2(skillid, skilllv)); + break; + case NC_POWERSWING: + sc_start(bl, SC_STUN, 5*skilllv, skilllv, skill_get_time(skillid, skilllv)); + if( rand()%100 < 5*skilllv ) + skill_castend_damage_id(src, bl, NC_AXEBOOMERANG, pc_checkskill(sd, NC_AXEBOOMERANG), tick, 1); + break; + /** + * Guilotine Cross + **/ + case GC_WEAPONCRUSH: + skill_castend_nodamage_id(src,bl,skillid,skilllv,tick,BCT_ENEMY); + break; } if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai) @@ -1105,7 +1233,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if ( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, rate); + clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0); } } } @@ -1381,7 +1509,7 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list * if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if ( battle_config.display_status_timers && dstsd ) - clif_status_change(bl, SI_ACTIONDELAY, 1, rate); + clif_status_change(bl, SI_ACTIONDELAY, 1, rate, 0, 0, 0); } } } @@ -1516,7 +1644,7 @@ int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int return 0; sc = status_get_sc(bl); - if (!sc) + if (!sc || sc->option&OPTION_MADOGEAR ) //Mado Gear cannot be divested [Ind] return 0; for (i = 0; i < ARRAYLENGTH(pos); i++) { @@ -1531,8 +1659,8 @@ int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int } return where?1:0; } - - +//Early declaration +static int skill_area_temp[8]; /*========================================================================= Used to knock back players, monsters, traps, etc - 'count' is the number of squares to knock back @@ -1721,8 +1849,14 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds sc->data[SC_SPIRIT]->val4 = dsrc->id; } } + /** + * Official Magic Reflection Behavior : damage reflected depends on gears caster wears, not target + **/ + #if RR_MAGIC_REFLECTION + if( dmg.dmg_lv != ATK_MISS )//Wiz SL cancelled and consumed fragment + dmg = battle_calc_attack(BF_MAGIC,bl,bl,skillid,skilllv,flag&0xFFF); + #endif } - if(sc && sc->data[SC_MAGICROD] && src == dsrc) { int sp = skill_get_sp(skillid,skilllv); dmg.damage = dmg.damage2 = 0; @@ -1744,19 +1878,8 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds if( damage > 0 && dmg.flag&BF_WEAPON && src != bl && ( src == dsrc || ( dsrc->type == BL_SKILL && ( skillid == SG_SUN_WARM || skillid == SG_MOON_WARM || skillid == SG_STAR_WARM ) ) ) && skillid != WS_CARTTERMINATION ) - { - if( sc && sc->data[SC_DEATHBOUND] && !is_boss(bl) && map_check_dir(map_calc_dir(src,bl->x,bl->y),unit_getdir(bl))) - { - int skilllv = sc->data[SC_DEATHBOUND]->val1; - clif_skill_damage(src,src, tick, 0, 0, 0, 0, RK_DEATHBOUND,-1, 1); - rdamage = damage * ((500 + 100*skilllv) / 100); - damage = rdamage * 70 / 100; - skill_blown(src, src, skill_get_blewcount(skillid,skilllv), unit_getdir(src), 0); - status_change_end(dsrc,SC_DEATHBOUND,INVALID_TIMER); - } - else - rdamage = battle_calc_return_damage(bl, damage, dmg.flag); - } + rdamage = battle_calc_return_damage(bl,src, &damage, dmg.flag); + //Skill hit type type=(skillid==0)?5:skill_get_hit(skillid); @@ -1872,7 +1995,6 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds case NPC_CRITICALSLASH: case TF_DOUBLE: case GS_CHAINACTION: - case RK_DEATHBOUND: dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2); break; @@ -1882,7 +2004,31 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds else // the central target doesn't display an animation dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skillid, -2, 5); // needs -2(!) as skill level break; - + /** + * Warlock + **/ + case WL_HELLINFERNO: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skillid,-2,6); + break; + case WL_SOULEXPANSION: + case WL_COMET: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skillid,skilllv,8); + break; + case WL_CHAINLIGHTNING_ATK: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING,-2,6); + break; + case WL_TETRAVORTEX_FIRE: + case WL_TETRAVORTEX_WATER: + case WL_TETRAVORTEX_WIND: + case WL_TETRAVORTEX_GROUND: + dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_TETRAVORTEX_FIRE,-2,type); + break; + /** + * Arch Bishop + **/ + case AB_DUPLELIGHT_MELEE: + case AB_DUPLELIGHT_MAGIC: + dmg.amotion = 300; default: if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit. type = 5; @@ -1897,8 +2043,22 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds && (!sc || !sc->data[SC_PRESERVE]) && damage < tsd->battle_status.hp) { //Updated to not be able to copy skills if the blow will kill you. [Skotlex] - if ((tsd->status.skill[skillid].id == 0 || tsd->status.skill[skillid].flag == SKILL_FLAG_PLAGIARIZED) && - can_copy(tsd,skillid,bl)) // Split all the check into their own function [Aru] + int copy_skill = skillid; + /** + * Copy Referal: dummy skills should point to their source upon copying + **/ + switch( skillid ) { + case AB_DUPLELIGHT_MELEE: + case AB_DUPLELIGHT_MAGIC: + copy_skill = AB_DUPLELIGHT; + break; + case WL_CHAINLIGHTNING_ATK: + copy_skill = WL_CHAINLIGHTNING; + break; + } + + if ((tsd->status.skill[copy_skill].id == 0 || tsd->status.skill[copy_skill].flag == SKILL_FLAG_PLAGIARIZED) && + can_copy(tsd,copy_skill,bl)) // Split all the check into their own function [Aru] { int lv = skilllv; if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED){ @@ -1911,11 +2071,11 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds if ((type = pc_checkskill(tsd,RG_PLAGIARISM)) < lv) lv = type; - tsd->cloneskill_id = skillid; - pc_setglobalreg(tsd, "CLONE_SKILL", skillid); + tsd->cloneskill_id = copy_skill; + pc_setglobalreg(tsd, "CLONE_SKILL", copy_skill); pc_setglobalreg(tsd, "CLONE_SKILL_LV", lv); - tsd->status.skill[skillid].id = skillid; + tsd->status.skill[skillid].id = copy_skill; tsd->status.skill[skillid].lv = lv; tsd->status.skill[skillid].flag = SKILL_FLAG_PLAGIARIZED; clif_addskill(tsd,skillid); @@ -1949,6 +2109,12 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds if( damage > 0 ) //Counter status effects [Skotlex] skill_counter_additional_effect(src,bl,skillid,skilllv,dmg.flag,tick); } + // Hell Inferno burning status only starts if Fire part hits. + if( skillid == WL_HELLINFERNO && dmg.damage > 0 ) + sc_start4(bl,SC_BURNING,55+5*skilllv,skilllv,1000,src->id,0,skill_get_time(skillid,skilllv)); + // Apply knock back chance in SC_TRIANGLESHOT skill. + else if( skillid == SC_TRIANGLESHOT && rand()%100 > (1 + skilllv) ) + dmg.blewcount = 0; //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex] //Reflected spells do not bounce back (bl == dsrc since it only happens for direct skills) @@ -1960,6 +2126,8 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds case MG_FIREWALL: direction = unit_getdir(bl); break; // backwards case WZ_STORMGUST: direction = rand()%8; break; // randomly case PR_SANCTUARY: direction = unit_getdir(bl); break; // backwards + case WL_CRIMSONROCK: direction = map_calc_dir(bl,skill_area_temp[4],skill_area_temp[5]); break; + } skill_blown(dsrc,bl,dmg.blewcount,direction,0); } @@ -2015,9 +2183,19 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds battle_drain(tsd, src, rdamage, rdamage, sstatus->race, is_boss(src)); skill_additional_effect(bl, src, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick); } - - if( damage > 0 && skillid == RK_CRUSHSTRIKE ) - skill_break_equip(src,EQP_WEAPON,2000,BCT_SELF); + if( damage > 0 ) { + if( skillid == RK_CRUSHSTRIKE ) // Your weapon will not be broken if you miss. + skill_break_equip(src,EQP_WEAPON,10000,BCT_SELF); + + if( skillid == GC_VENOMPRESSURE ) { + struct status_change *ssc = status_get_sc(src); + if( ssc && ssc->data[SC_POISONINGWEAPON] && rand()%100 < 70 + 5*skilllv ) { + sc_start(bl,ssc->data[SC_POISONINGWEAPON]->val2,100,ssc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON,ssc->data[SC_POISONINGWEAPON]->val1)); + status_change_end(src,SC_POISONINGWEAPON,-1); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + } + } if (!(flag&2) && ( @@ -2044,7 +2222,6 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds * ffff=自由に使用可能 * 0 =予約?B0に固定 *------------------------------------------*/ -static int skill_area_temp[8]; typedef int (*SkillFunc)(struct block_list *, struct block_list *, int, int, unsigned int, int); int skill_area_sub (struct block_list *bl, va_list ap) { @@ -2115,6 +2292,17 @@ static int skill_check_unit_range_sub (struct block_list *bl, va_list ap) case HT_CLAYMORETRAP: case HT_TALKIEBOX: case HP_BASILICA: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set) if (skillid != g_skillid && !(skill_get_inf2(g_skillid)&INF2_TRAP) && g_skillid != AS_VENOMDUST) return 0; @@ -2427,15 +2615,60 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data) } } break; - case RK_HUNDREDSPEAR: - if(src->type == BL_PC) + /** + * Warlock + **/ + case WL_CHAINLIGHTNING_ATK: { - int skill_lv = pc_checkskill((struct map_session_data *)src,KN_SPEARBOOMERANG); - if(skill_lv > 0) - skill_attack(BF_WEAPON,src,src,target,KN_SPEARBOOMERANG,skill_lv,tick,skl->flag); - } - else - skill_attack(BF_WEAPON,src,src,target,KN_SPEARBOOMERANG,1,tick,skl->flag); + struct block_list *nbl = NULL; // Next Target of Chain + skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); // Hit a Lightning on the current Target + if( skl->type > 1 ) + { // Remaining Chains Hit + nbl = battle_getenemyarea(src,target->x,target->y,2,BL_CHAR|BL_SKILL,target->id); // Search for a new Target around current one... + if( nbl == NULL && skl->x > 1 ) + { + nbl = target; + skl->x--; + } + else skl->x = 3; + } + + if( nbl ) + skill_addtimerskill(src,tick+status_get_adelay(src),nbl->id,skl->x,0,WL_CHAINLIGHTNING_ATK,skl->skill_lv,skl->type-1,skl->flag); + } + break; + case WL_TETRAVORTEX_FIRE: + case WL_TETRAVORTEX_WATER: + case WL_TETRAVORTEX_WIND: + case WL_TETRAVORTEX_GROUND: + skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); + if( skl->type >= 3 ) + { // Final Hit + status_change_end(src,SC_MAGICPOWER,-1); // Removes Magic Power + if( !status_isdead(target) ) + { // Final Status Effect + int effects[4] = { SC_BURNING, SC_FREEZING, SC_BLEEDING, SC_STUN }, + applyeffects[4] = { 0, 0, 0, 0 }, + i, j = 0, k = 0; + for( i = 1; i <= 8; i = i + i ) + { + if( skl->x&i ) + { + applyeffects[j] = effects[k]; + j++; + } + k++; + } + if( j ) + { + i = applyeffects[rand()%j]; + status_change_start(target, i, 10000, skl->skill_lv, + (i == SC_BURNING ? 1000 : 0), + (i == SC_BURNING ? src->id : 0), + 0, skill_get_time(WL_TETRAVORTEX,skl->skill_lv), 0); + } + } + } break; default: skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); @@ -2459,7 +2692,13 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data) else if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) ) skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag); break; + case WL_EARTHSTRAIN: + skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag); + break; + } + if( skl->skill_id >= WL_TETRAVORTEX_FIRE && skl->skill_id <= WL_TETRAVORTEX_GROUND ) + status_change_end(src,SC_MAGICPOWER,-1); } } while (0); //Free skl now that it is no longer needed. @@ -2660,9 +2899,43 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NPC_BLEEDING: case NPC_CRITICALWOUND: case NPC_HELLPOWER: + /** + * Rune Knight + **/ case RK_SONICWAVE: + case RK_HUNDREDSPEAR: case RK_WINDCUTTER: + /** + * Arch Bishop + **/ case AB_DUPLELIGHT_MELEE: + /** + * Ranger + **/ + case RA_AIMEDBOLT: + /** + * Mechanic + **/ + case NC_AXEBOOMERANG: + case NC_POWERSWING: + /** + * Guilotinne Cross + **/ + case GC_CROSSIMPACT: + case GC_VENOMPRESSURE: + + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + + /** + * Mechanic (MADO GEAR) + **/ + case NC_BOOSTKNUCKLE: + case NC_PILEBUNKER: + case NC_VULCANARM: + case NC_COLDSLOWER: + case NC_ARMSCANNON: + if (sd) pc_overheat(sd,1); skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); break; @@ -2731,6 +3004,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int } break; + case NC_FLAMELAUNCHER: + if (sd) pc_overheat(sd,1); case SN_SHARPSHOOTING: case MA_SHARPSHOOTING: case NJ_KAMAITACHI: @@ -2860,7 +3135,35 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NPC_PULSESTRIKE: case NPC_HELLJUDGEMENT: case NPC_VAMPIRE_GIFT: + /** + * Rune Knight + **/ + case RK_IGNITIONBREAK: + /** + * Arch Bishop + **/ case AB_JUDEX: + /** + * Warlock + **/ + case WL_SOULEXPANSION: + case WL_CRIMSONROCK: + case WL_COMET: + /** + * Ranger + **/ + case RA_ARROWSTORM: + case RA_WUGDASH: + /** + * Mechanic + **/ + case NC_SELFDESTRUCTION: + case NC_AXETORNADO: + /** + * Guilotine Cross + **/ + case GC_ROLLINGCUTTER: + case GC_COUNTERSLASH: if( flag&1 ) { //Recursive invocation // skill_area_temp[0] holds number of targets in area @@ -2887,7 +3190,10 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skill_area_temp[0] = 0; skill_area_temp[1] = bl->id; skill_area_temp[2] = 0; - + if( skillid == WL_CRIMSONROCK ) { + skill_area_temp[4] = bl->x; + skill_area_temp[5] = bl->y; + } // if skill damage should be split among targets, count them //SD_LEVEL -> Forced splash damage for Auto Blitz-Beat -> count targets //special case: Venom Splasher uses a different range for searching than for splashing @@ -2895,7 +3201,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, (skillid == AS_SPLASHER)?1:skill_get_splash(skillid, skilllv), BL_CHAR, src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count); // recursive invocation of skill_castend_damage_id() with flag|1 - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); + map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), (skillid == WL_CRIMSONROCK)?BL_CHAR|BL_SKILL:splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); //FIXME: Isn't EarthQuake a ground skill after all? if( skillid == NPC_EARTHQUAKE ) @@ -3015,10 +3321,20 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NJ_KOUENKA: case NJ_HYOUSENSOU: case NJ_HUUJIN: +#if FIREIVY_ON + case WZ_FIREIVY: +#endif + /** + * Arch Bishop + **/ case AB_ADORAMUS: case AB_RENOVATIO: case AB_HIGHNESSHEAL: case AB_DUPLELIGHT_MAGIC: + /** + * Warlock + **/ + case WL_HELLINFERNO: skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); break; @@ -3105,6 +3421,9 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int case NPC_SMOKING: case GS_FLING: case NJ_ZENYNAGE: + /** + * Rune Knight + **/ case RK_DRAGONBREATH: skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); break; @@ -3162,17 +3481,329 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int status_change_end(src, SC_HIDING, INVALID_TIMER); skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); break; + /** + * Rune Knight + **/ + case RK_PHANTOMTHRUST: + unit_setdir(src,map_calc_dir(src, bl->x, bl->y)); + clif_skill_nodamage(src,bl,skillid,skilllv,1); - case RK_HUNDREDSPEAR: - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - if(rand()%100 < (10 + 3*skilllv)) { - skill_blown(src,bl,6,-1,0); - skill_addtimerskill(src,tick+800,bl->id,0,0,skillid,skilllv,BF_WEAPON,flag); - } + skill_blown(src,bl,distance_bl(src,bl)-1,unit_getdir(src),0); + if( battle_check_target(src,bl,BCT_ENEMY) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); break; + + case RK_STORMBLAST: case RK_CRUSHSTRIKE: + if( sd ) { + if( pc_checkskill(sd,RK_RUNEMASTERY) >= ( skillid == RK_CRUSHSTRIKE ? 7 : 3 ) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else + clif_skill_fail(sd,skillid,0,0); + } else //non-sd support + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + /** + * Guilotinne Cross + **/ + case GC_DARKILLUSION: + { + short x, y; + short dir = map_calc_dir(src,bl->x,bl->y); + + if( dir > 4 ) x = -1; + else if( dir > 0 && dir < 4 ) x = 1; + else x = 0; + if( dir < 3 || dir > 5 ) y = -1; + else if( dir > 3 && dir < 5 ) y = 1; + else y = 0; + + if( unit_movepos(src, bl->x+x, bl->y+y, 1, 1) ) + { + clif_slide(src,bl->x+x,bl->y+y); + clif_fixpos(src); // the official server send these two packts. + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if( rand()%100 < 4 * skilllv ) + skill_castend_damage_id(src,bl,GC_CROSSIMPACT,skilllv,tick,flag); + } + + } + break; + + case GC_WEAPONCRUSH: + if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else if( sd ) + clif_skill_fail(sd,skillid,0x1f,0); + break; + + case GC_CROSSRIPPERSLASHER: + if( sd && !(sc && sc->data[SC_ROLLINGCUTTER]) ) + clif_skill_fail(sd,skillid,0x17,0); + else + { + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + status_change_end(src,SC_ROLLINGCUTTER,-1); + } + break; + + case GC_PHANTOMMENACE: + if( flag&1 ) + { // Only Hits Invisible Targets + struct status_change *tsc = status_get_sc(bl); + if(tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + } + break; + /** + * Warlock + **/ + case WL_CHAINLIGHTNING: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_addtimerskill(src,tick + 150,bl->id,3,0,WL_CHAINLIGHTNING_ATK,skilllv,4+skilllv,flag); + break; + case WL_DRAINLIFE: + { + int heal = skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag); + int rate = 70 + 4 * skilllv + ( sd ? sd->status.job_level : 50 ) / 5; + + heal = 8 * skilllv; + if( status_get_lv(src) > 100 ) heal = heal * status_get_lv(src) / 100; // Base level bonus. + + if( bl->type == BL_SKILL ) + heal = 0; // Don't absorb heal from Ice Walls or other skill units. + + if( heal && rand()%100 < rate ) + { + status_heal(src, heal, 0, 0); + clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1); + } + } + break; + + case WL_TETRAVORTEX: if( sd ) + { + int spheres[5] = { 0, 0, 0, 0, 0 }, + positions[5] = {-1,-1,-1,-1,-1 }, + i, j = 0, k, subskill = 0; + + for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ ) + if( sc && sc->data[i] ) + { + spheres[j] = i; + positions[j] = sc->data[i]->val2; + j++; // + } + + if( j < 4 ) + { // Need 4 spheres minimum + clif_skill_fail(sd,skillid,0,0); + break; + } + + // Sphere Sort, this time from new to old + for( i = 0; i <= j - 2; i++ ) + for( k = i + 1; k <= j - 1; k++ ) + if( positions[i] < positions[k] ) + { + swap(positions[i],positions[k]); + swap(spheres[i],spheres[k]); + } + + k = 0; + for( i = 0; i < 4; i++ ) + { + switch( sc->data[spheres[i]]->val1 ) + { + case WLS_FIRE: subskill = WL_TETRAVORTEX_FIRE; k |= 1; break; + case WLS_WIND: subskill = WL_TETRAVORTEX_WIND; k |= 4; break; + case WLS_WATER: subskill = WL_TETRAVORTEX_WATER; k |= 2; break; + case WLS_STONE: subskill = WL_TETRAVORTEX_GROUND; k |= 8; break; + } + + skill_addtimerskill(src,tick+status_get_adelay(src)*i,bl->id,k,0,subskill,skilllv,i,flag); + status_change_end(src, spheres[i], INVALID_TIMER); + } + } + break; + + case WL_RELEASE: + if( sd ) + { + int i; + // Priority is to release SpellBook + ARR_FIND(0,MAX_SPELLBOOK,i,sd->rsb[i].skillid != 0); + if( i < MAX_SPELLBOOK ) + { // SpellBook + int rsb_skillid, rsb_skilllv; + + if( skilllv > 1 ) + { + ARR_FIND(0,MAX_SPELLBOOK,i,sd->rsb[i].skillid == 0); + i--; // At skilllvl 2, Release uses the last learned skill in spellbook + } + + rsb_skillid = sd->rsb[i].skillid; + rsb_skilllv = sd->rsb[i].level; + + if( skilllv > 1 ) + sd->rsb[i].skillid = 0; // Last position - only remove it from list + else + memmove(&sd->rsb[0],&sd->rsb[1],sizeof(sd->rsb) - sizeof(sd->rsb[0])); + + if( sd->rsb[0].skillid == 0 ) + status_change_end(src, SC_READING_SB, INVALID_TIMER); + + status_change_end(src, SC_MAGICPOWER, INVALID_TIMER); + + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( !skill_check_condition_castbegin(sd,rsb_skillid,rsb_skilllv) ) + break; + + switch( skill_get_casttype(rsb_skillid) ) + { + case CAST_GROUND: + skill_castend_pos2(src,bl->x,bl->y,rsb_skillid,rsb_skilllv,tick,0); + break; + case CAST_NODAMAGE: + skill_castend_nodamage_id(src,bl,rsb_skillid,rsb_skilllv,tick,0); + break; + case CAST_DAMAGE: + skill_castend_damage_id(src,bl,rsb_skillid,rsb_skilllv,tick,0); + break; + } + + sd->ud.canact_tick = tick + skill_delayfix(src, rsb_skillid, rsb_skilllv); + clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, rsb_skillid, rsb_skilllv), 0, 0, 0); + } + else + { // Summon Balls + int j = 0, k, skele; + int spheres[5] = { 0, 0, 0, 0, 0 }, + positions[5] = {-1,-1,-1,-1,-1 }; + + for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ ) + if( sc && sc->data[i] ) + { + spheres[j] = i; + positions[j] = sc->data[i]->val2; + sc->data[i]->val2--; // Prepares for next position + j++; + } + + if( j == 0 ) + { // No Spheres + clif_skill_fail(sd,skillid,0,0); + break; + } + + // Sphere Sort + for( i = 0; i <= j - 2; i++ ) + for( k = i + 1; k <= j - 1; k++ ) + if( positions[i] > positions[k] ) + { + swap(positions[i],positions[k]); + swap(spheres[i],spheres[k]); + } + + status_change_end(src, SC_MAGICPOWER, INVALID_TIMER); + + if( skilllv == 1 ) j = 1; // Limit only to one ball + for( i = 0; i < j; i++ ) + { + skele = WL_RELEASE - 5 + sc->data[spheres[i]]->val1 - WLS_FIRE; // Convert Ball Element into Skill ATK for balls + // WL_SUMMON_ATK_FIRE, WL_SUMMON_ATK_WIND, WL_SUMMON_ATK_WATER, WL_SUMMON_ATK_GROUND + skill_addtimerskill(src,tick+status_get_adelay(src)*i,bl->id,0,0,skele,sc->data[spheres[i]]->val3,BF_MAGIC,flag|SD_LEVEL); + status_change_end(src, spheres[i], INVALID_TIMER); // Eliminate ball + } + clif_skill_nodamage(src,bl,skillid,0,1); + } + } + break; + case WL_FROSTMISTY: + { + struct status_change *tsc = status_get_sc(bl); + if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) ) + break; // Doesn't hit/cause Freezing to invisible enemy + // Causes Freezing status through walls. + sc_start(bl,status_skill2sc(skillid),20+12*skilllv+(sd ? sd->status.job_level : 50)/5,skilllv,skill_get_time(skillid,skilllv)); + // Doesn't deal damage through non-shootable walls. + if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKWALL) ) + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + } + break; + + case WL_JACKFROST: { + struct status_change *tsc = status_get_sc(bl); + if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) ) + break; // Do not hit invisible enemy + skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag); + } + break; + /** + * Ranger + **/ + case RA_WUGSTRIKE: + case RA_WUGBITE: + if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKNOREACH) ) { + if( skillid == RA_WUGSTRIKE ) { + if( sd && pc_isridingwug(sd) && !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src,bl->x,bl->y,1,1) ) + clif_slide(src, bl->x, bl->y); + } skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + } + break; + + case RA_SENSITIVEKEEN: + if( bl->type != BL_SKILL ) { // Only Hits Invisible Targets + struct status_change * tsc = status_get_sc(bl); + if(tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK) || tsc->data[SC__INVISIBILITY]) ) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + } + else + { + struct skill_unit *su = BL_CAST(BL_SKILL,bl); + struct skill_unit_group* sg; + + if( su && (sg=su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP && sg->src_id != src->id && + battle_check_target(src, map_id2bl(sg->src_id), BCT_ENEMY) > 0 ) + { + if( sd && !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) ) + { + struct item item_tmp; + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = ( sg->unit_id >= UNT_MAGENTATRAP && sg->unit_id <= UNT_CLUSTERBOMB )?ITEMID_TRAP_ALLOY:ITEMID_TRAP; + item_tmp.identify = 1; + if( item_tmp.nameid ) + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); + } + skill_delunit(su); + } + } + break; + /** + * Mechanic + **/ + case NC_INFRAREDSCAN: + if( flag&1 ) + { //TODO: Need a confirmation if the other type of hidden status is included to be scanned. [Jobbie] + if( rand()%100 < 50 ) + sc_start(bl, SC_INFRAREDSCAN, 10000, skilllv, skill_get_time(skillid, skilllv)); + status_change_end(bl, SC_HIDING, -1); + status_change_end(bl, SC_CLOAKING, -1); + status_change_end(bl, SC_CLOAKINGEXCEED, -1); // Need confirm it. + } + else + { + map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); + clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + if( sd ) pc_overheat(sd,1); + } + break; + + case NC_MAGNETICFIELD: + sc_start2(bl,SC_MAGNETICFIELD,100,skilllv,src->id,skill_get_time(skillid,skilllv)); break; case 0: if(sd) { @@ -3209,9 +3840,6 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int skill_consume_requirement(sd,skillid,skilllv,2); } - if( sd && skill_get_cooldown(skillid,skilllv) ) - skill_blockpc_start (sd, skillid, skill_get_cooldown(skillid, skilllv)); - return 0; } @@ -3268,6 +3896,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case AL_HEAL: case ALL_RESURRECTION: case PR_ASPERSIO: + /** + * Arch Bishop + **/ case AB_RENOVATIO: case AB_HIGHNESSHEAL: //Apparently only player casted skills can be offensive like this. @@ -3303,12 +3934,20 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in { case HLIF_HEAL: //[orn] case AL_HEAL: + /** + * Arch Bishop + **/ case AB_HIGHNESSHEAL: { - int heal = skill_calc_heal(src, bl, skillid, skilllv, true); + int heal = skill_calc_heal(src, bl, (skillid == AB_HIGHNESSHEAL)?AL_HEAL:skillid, (skillid == AB_HIGHNESSHEAL)?10:skilllv, true); int heal_get_jobexp; - - if( status_isimmune(bl) || (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) ) + //Highness Heal: starts at 1.5 boost + 0.5 for each level + if( skillid == AB_HIGHNESSHEAL ) { + heal = heal * ( 15 + 5 * skilllv ) / 10; + } + if( status_isimmune(bl) || + (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) || + (skillid == AL_HEAL && dstsd && dstsd->sc.option&OPTION_MADOGEAR) )//Mado is immune to AL_HEAL heal=0; if( sd && dstsd && sd->status.partner_id == dstsd->status.char_id && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0 ) @@ -3421,12 +4060,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case AL_DECAGI: case MER_DECAGI: - if (skilllv = battle_config.max_decagi_lv) - clif_skill_nodamage (src, bl, skillid, skilllv, - sc_start(bl, type, (40 + skilllv * 2 + (status_get_lv(src) + sstatus->int_)/5), (battle_config.max_decagi - 2), battle_config.max_decagi_dur)); - else - clif_skill_nodamage (src, bl, skillid, skilllv, - sc_start(bl, type, (40 + skilllv * 2 + (status_get_lv(src) + sstatus->int_)/5), skilllv, skill_get_time(skillid,skilllv))); + clif_skill_nodamage (src, bl, skillid, skilllv, + sc_start(bl, type, (40 + skilllv * 2 + (status_get_lv(src) + sstatus->int_)/5), skilllv, skill_get_time(skillid,skilllv))); break; case AL_CRUCIS: @@ -3721,7 +4356,10 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skillid, skilllv)); if (sd) skill_blockpc_start (sd, skillid, skill_get_time(skillid, skilllv)); break; - + case ASC_EDP: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv) + ( sd ? 3000 * pc_checkskill(sd,GC_RESEARCHNEWPOISON) : 0 ))); + break; case AL_INCAGI: case AL_BLESSING: case MER_INCAGI: @@ -3762,7 +4400,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case HW_MAGICPOWER: case PF_MEMORIZE: case PA_SACRIFICE: - case ASC_EDP: case PF_DOUBLECASTING: case SG_SUN_COMFORT: case SG_MOON_COMFORT: @@ -3779,17 +4416,32 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case ST_PRESERVE: case NPC_INVINCIBLE: case NPC_INVINCIBLEOFF: + /** + * Rune Knight + **/ case RK_DEATHBOUND: - case RK_MILLENNIUMSHIELD: - case RK_CRUSHSTRIKE: - case RK_GIANTGROWTH: - case RK_STONEHARDSKIN: - case RK_VITALITYACTIVATION: - case RK_ABUNDANCE: + /** + * Arch Bishop + **/ case AB_RENOVATIO: case AB_EXPIATIO: case AB_DUPLELIGHT: case AB_SECRAMENT: + /** + * Mechanic + **/ + case NC_ACCELERATION: + case NC_HOVERING: + case NC_SHAPESHIFT: + /** + * Warlock + **/ + case WL_RECOGNIZEDSPELL: + /** + * Guillotine Cross + **/ + case GC_VENOMIMPRESS: + clif_skill_nodamage(src,bl,skillid,skilllv, sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); break; @@ -4047,14 +4699,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case AM_PHARMACY: if(sd) { - clif_skill_produce_mix_list(sd,22); + clif_skill_produce_mix_list(sd,skillid,22); clif_skill_nodamage(src,bl,skillid,skilllv,1); } break; case SA_CREATECON: if(sd) { - clif_skill_produce_mix_list(sd,23); + clif_skill_produce_mix_list(sd,skillid,23); clif_skill_nodamage(src,bl,skillid,skilllv,1); } break; @@ -4075,12 +4727,32 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case ASC_METEORASSAULT: case GS_SPREADATTACK: + /** + * Rune Knight + **/ + case RK_STORMBLAST: + /** + * Mechanic + **/ + case NC_AXETORNADO: + /** + * Guilotine Cross + **/ + case GC_COUNTERSLASH: skill_area_temp[1] = 0; clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), + i = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); + if( !i && skillid == NC_AXETORNADO ) + clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); break; + case NC_EMERGENCYCOOL: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + status_change_end(src,SC_OVERHEAT_LIMITPOINT,-1); + status_change_end(src,SC_OVERHEAT,-1); + break; + case NC_INFRAREDSCAN: case NPC_EARTHQUAKE: case NPC_VAMPIRE_GIFT: case NPC_HELLJUDGEMENT: @@ -4156,7 +4828,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case BS_ADRENALINE2: case BS_WEAPONPERFECT: case BS_OVERTHRUST: - case RK_FIGHTINGSPIRIT: //Splash range in skill_db is 0, should be map-wide according to party_foreachsamemap if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { clif_skill_nodamage(bl,bl,skillid,skilllv, sc_start2(bl,type,100,skilllv,(src == bl)? 1:0,skill_get_time(skillid,skilllv))); @@ -4225,6 +4896,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in clif_skill_nodamage(src,bl,skillid,-1,status_change_end(bl, type, INVALID_TIMER)); //Hide skill-scream animation. map_freeblock_unlock(); return 0; + } else if( tsc && tsc->option&OPTION_MADOGEAR ) { + //Mado Gear cannot hide + if( sd ) clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 0; } clif_skill_nodamage(src,bl,skillid,-1,sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); break; @@ -4240,6 +4916,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in clif_walkok(sd); // So aegis has to resend the walk ok. break; case AS_CLOAKING: + case RA_CAMOUFLAGE: + /** + * Guilotine Cross + **/ + case GC_CLOAKINGEXCEED: if (tsce) { i = status_change_end(bl, type, INVALID_TIMER); @@ -4305,7 +4986,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in if(pc_steal_item(sd,bl,skilllv)) clif_skill_nodamage(src,bl,skillid,skilllv,1); else - clif_skill_fail(sd,skillid,10,0); + clif_skill_fail(sd,skillid,0x0a,0); } break; @@ -4559,6 +5240,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in case RG_STRIPARMOR: case RG_STRIPHELM: case ST_FULLSTRIP: + case GC_WEAPONCRUSH: { unsigned short location = 0; int d = 0; @@ -4577,6 +5259,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in switch (skillid) { case RG_STRIPWEAPON: + case GC_WEAPONCRUSH: location = EQP_WEAPON; break; case RG_STRIPSHIELD: @@ -4594,14 +5277,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in } //Special message when trying to use strip on FCP [Jobbie] - if( sd && skillid == ST_FULLSTRIP && tsc && tsc->data[SC_CP_WEAPON] && tsc->data[SC_CP_HELM] && tsc->data[SC_CP_ARMOR] && tsc->data[SC_CP_SHIELD] ) + if( sd && skillid == ST_FULLSTRIP && tsc && tsc->data[SC_CP_WEAPON] && tsc->data[SC_CP_HELM] && tsc->data[SC_CP_ARMOR] && tsc->data[SC_CP_SHIELD]) { clif_gospel_info(sd, 0x28); break; } //Attempts to strip at rate i and duration d - if( (i = skill_strip_equip(bl, location, i, skilllv, d)) || skillid != ST_FULLSTRIP ) + if( (i = skill_strip_equip(bl, location, i, skilllv, d)) || (skillid != ST_FULLSTRIP && skillid != GC_WEAPONCRUSH ) ) clif_skill_nodamage(src,bl,skillid,skilllv,i); //Nothing stripped. @@ -4765,7 +5448,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in clif_skill_nodamage(src,bl,skillid,skilllv,1); if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_ROGUE) //Rogue's spirit defends againt dispel. - || rand()%100 >= 50+10*skilllv) + || rand()%100 >= 50+10*skilllv + || ( tsc && tsc->option&OPTION_MADOGEAR ) )//Mado Gear is immune to dispell according to bug report 49 [Ind] { if (sd) clif_skill_fail(sd,skillid,0,0); @@ -5778,19 +6462,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in skill_castend_nodamage_id); } break; - case ALL_PARTYFLEE: - if( sd && !(flag&1) ) - { - if( !sd->status.party_id ) - { - clif_skill_fail(sd,skillid,0,0); - break; - } - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - } - else - clif_skill_nodamage(src,bl,skillid,skilllv,sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; case NPC_TALK: case ALL_WEWISH: clif_skill_nodamage(src,bl,skillid,skilllv,1); @@ -5802,271 +6473,606 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in } break; case RK_ENCHANTBLADE: - i = status_get_int(src) + status_get_lv(src) / 10; - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,i,skill_get_time(skillid,skilllv))); + clif_skill_nodamage(src,bl,skillid,skilllv,// formula not confirmed + sc_start2(bl,type,100,skilllv,100+20*skilllv/*+sstatus->int_/2+status_get_lv(bl)/10*/,skill_get_time(skillid,skilllv))); break; - case RK_IGNITIONBREAK: - if(flag&1) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - else + case RK_DRAGONHOWLING: + if( flag&1) + sc_start(bl,type,50 + 6 * skilllv,skilllv,skill_get_time(skillid,skilllv)); + else { + skill_area_temp[2] = 0; clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid,skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|SD_PREAMBLE|1, skill_castend_nodamage_id); } break; - case RK_DRAGONHOWLING: - if(flag&1) - sc_start(bl,SC_FEAR,50 + skilllv * 6,skilllv,skill_get_time2(skillid,skilllv)); - else + case RK_IGNITIONBREAK: + //case LG_EARTHDRIVE: + clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + //if( skillid == LG_EARTHDRIVE ) + //{ + // int dummy = 1; + // i = skill_get_splash(skillid,skilllv); + // map_foreachinarea(skill_cell_overlap, src->m, src->x-i, src->y-i, src->x+i, src->y+i, BL_SKILL, LG_EARTHDRIVE, &dummy, src); + //} + map_foreachinrange(skill_area_sub, bl,skill_get_splash(skillid,skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + case RK_STONEHARDSKIN: + if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 4 ) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_nodamage_id); + int heal = sstatus->hp / 4; // 25% HP + if( status_charge(bl,heal,0) ) + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start2(bl,type,100,skilllv,heal,skill_get_time(skillid,skilllv))); + else + clif_skill_fail(sd,skillid,0,0); } break; case RK_REFRESH: + if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 8 ) { - int heal = sstatus->max_hp * 25 / 100; - status_heal(bl,heal,0,0); + int heal = status_get_max_hp(bl) * 25 / 100; clif_skill_nodamage(src,bl,skillid,skilllv, sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - status_change_clear_buffs(bl,6); + status_heal(bl,heal,0,1); + status_change_clear_buffs(bl,2); } break; - case RK_STORMBLAST: - if( flag&1 ) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + + case RK_MILLENNIUMSHIELD: + if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 9 ) + { + short shields = (rand()%100<50) ? 4 : ((rand()%100<80) ? 3 : 2); + sc_start4(bl,type,100,skilllv,shields,1000,0,skill_get_time(skillid,skilllv)); + clif_millenniumshield(sd,shields); + clif_skill_nodamage(src,bl,skillid,1,1); + } + break; + + case RK_GIANTGROWTH: + case RK_VITALITYACTIVATION: + case RK_ABUNDANCE: + if( sd ) + { + int lv = 1; // RK_GIANTGROWTH + if( skillid == RK_VITALITYACTIVATION ) + lv = 2; + else if( skillid == RK_ABUNDANCE ) + lv = 6; + if( pc_checkskill(sd,RK_RUNEMASTERY) >= lv ) + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + } + break; + + case RK_FIGHTINGSPIRIT: + if( flag&1 ) { + if( src == bl ) + sc_start2(bl,type,100,skill_area_temp[5],10*(sd?pc_checkskill(sd,RK_RUNEMASTERY):10),skill_get_time(skillid,skilllv)); + else + sc_start(bl,type,100,skill_area_temp[5]/4,skill_get_time(skillid,skilllv)); + } else if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 5 ) { + if( sd->status.party_id ) { + i = party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skillid,skilllv),src,skillid,skilllv,tick,BCT_PARTY,skill_area_sub_count); + skill_area_temp[5] = 7 * i; // ATK + party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skillid,skilllv),src,skillid,skilllv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id); + } else + sc_start2(bl,type,100,7,5,skill_get_time(skillid,skilllv)); + } + clif_skill_nodamage(src,bl,skillid,1,1); + break; + /** + * Guilotine Cross + **/ + case GC_ROLLINGCUTTER: + { + short count = 1; + skill_area_temp[2] = 0; + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|SD_PREAMBLE|SD_SPLASH|1,skill_castend_damage_id); + if( tsc && tsc->data[SC_ROLLINGCUTTER] ) + { // Every time the skill is casted the status change is reseted adding a counter. + count += (short)tsc->data[SC_ROLLINGCUTTER]->val1; + if( count > 10 ) + count = 10; // Max coounter + status_change_end(bl, SC_ROLLINGCUTTER, INVALID_TIMER); + } + sc_start(bl,SC_ROLLINGCUTTER,100,count,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,src,skillid,skilllv,1); + } + break; + + case GC_WEAPONBLOCKING: + if( tsc && tsc->data[SC_WEAPONBLOCKING] ) + status_change_end(bl, SC_WEAPONBLOCKING, INVALID_TIMER); else + sc_start(bl,SC_WEAPONBLOCKING,100,skilllv,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + case GC_CREATENEWPOISON: + if( sd ) { + clif_skill_produce_mix_list(sd,skillid,25); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + } + break; + + case GC_POISONINGWEAPON: + if( sd ) { + clif_poison_list(sd,skilllv); clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_nodamage_id); } break; - case RK_PHANTOMTHRUST: - if(battle_check_target(src,bl,BCT_ENEMY) > 0 || battle_check_target(src,bl,BCT_PARTY) > 0) + + case GC_ANTIDOTE: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if( tsc ) + { + status_change_end(bl, SC_PARALYSE, INVALID_TIMER); + status_change_end(bl, SC_PYREXIA, INVALID_TIMER); + status_change_end(bl, SC_DEATHHURT, INVALID_TIMER); + status_change_end(bl, SC_LEECHESEND, INVALID_TIMER); + status_change_end(bl, SC_VENOMBLEED, INVALID_TIMER); + status_change_end(bl, SC_MAGICMUSHROOM, INVALID_TIMER); + status_change_end(bl, SC_TOXIN, INVALID_TIMER); + status_change_end(bl, SC_OBLIVIONCURSE, INVALID_TIMER); + } + break; + + case GC_PHANTOMMENACE: + clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + + case GC_HALLUCINATIONWALK: { - if(!map[bl->m].flag.gvg && !map[bl->m].flag.battleground && !(status_get_mode(bl)&MD_BOSS)) + int heal = status_get_max_hp(bl) / 10; + if( status_get_hp(bl) < heal ) { // if you haven't enough HP skill fails. + if( sd ) clif_skill_fail(sd,skillid,0x02,0); + break; + } + if( !status_charge(bl,heal,0) ) { - int x = 0, y = 0; - if(bl->x > src->x) x = 1; - else if(bl->x < src->x) x = -1; - if(bl->y >= src->y) y = 1; - else if(bl->y < src->y) y = -1; - unit_movepos(bl, src->x+x, src->y+y, 1, 0); - clif_slide(bl,src->x+x, src->y+y); + if( sd ) clif_skill_fail(sd,skillid,0x02,0); + break; } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if (battle_check_target(src,bl,BCT_ENEMY) > 0 ) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); } break; + /** + * Arch Bishop + **/ case AB_ANCILLA: - if(sd) { - if (skill_produce_mix(sd, skillid, 12333, 0, 0, 0, 1)) - clif_skill_nodamage(src,bl,skillid,skilllv,1); - else - clif_skill_fail(sd,skillid,0,0); + if( sd ) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_produce_mix(sd, skillid, ITEMID_ANCILLA, 0, 0, 0, 1); } break; + case AB_CLEMENTIA: case AB_CANTO: - if( sd == NULL || sd->status.party_id == 0 || (flag & 1) ) { - int lv = 1; - switch(skillid) { - case AB_CLEMENTIA: if( sd ) lv = pc_checkskill(sd,AL_BLESSING); break; - case AB_CANTO: if( sd ) lv = pc_checkskill(sd,AL_INCAGI); break; - } - clif_skill_nodamage(bl, bl, skillid, skilllv, - sc_start4(bl,type,100,lv,0,sd?sd->status.job_level:0,0,skill_get_time(skillid,skilllv))); + { + int bless_lv = pc_checkskill(sd,AL_BLESSING); + int agi_lv = pc_checkskill(sd,AL_INCAGI); + if( sd == NULL || sd->status.party_id == 0 || flag&1 ) + clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start(bl,type,100, + (skillid == AB_CLEMENTIA)? bless_lv : (skillid == AB_CANTO)? agi_lv : skilllv, skill_get_time(skillid,skilllv))); + else if( sd ) + party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); } + break; + + case AB_PRAEFATIO: + if( sd == NULL || sd->status.party_id == 0 || flag&1 ) + clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start4(bl, type, 100, skilllv, 0, 0, 1, skill_get_time(skillid, skilllv))); else if( sd ) party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); break; + case AB_CHEAL: if( sd == NULL || sd->status.party_id == 0 || flag&1 ) { - int lv = (sd?pc_checkskill(sd, AL_HEAL):1); if( sd && tstatus && !battle_check_undead(tstatus->race, tstatus->def_ele) ) { - int heal = skill_calc_heal(src, bl, AL_HEAL, lv, true); - if( status_isimmune(bl) ) - heal = 0; - - if( sd->status.party_id && (i = party_foreachsamemap(party_sub_count, sd, 0)) > 1 ) - heal += ((heal / 100) * (i * 10) / 4); - - clif_skill_nodamage(bl, bl, skillid, heal, 1); - status_heal(bl, heal, 0, 0); + i = skill_calc_heal(src, bl, AL_HEAL, pc_checkskill(sd, AL_HEAL), true); + status_heal(bl, i, 0, 1); + clif_skill_nodamage(bl, bl, skillid, i, 1); } } else if( sd ) party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); break; - case AB_PRAEFATIO: - if( (flag&1) || sd == NULL || sd->status.party_id == 0 ) - { - if( dstsd && dstsd->special_state.no_magic_damage ) - break; - if ( sd && sd->status.party_id && (i = party_foreachsamemap(party_sub_count, sd, 0)) > 0) - { - clif_skill_nodamage(bl, bl, skillid, skilllv, - sc_start4(bl, type, 100, skilllv, 0, 0, i, skill_get_time(skillid, skilllv))); - } - } - else - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - break; case AB_ORATIO: - if (flag&1) - sc_start(bl, type, 40+5*skilllv, skilllv, skill_get_time(skillid, skilllv)); + if( flag&1 ) + sc_start(bl, type, 40 + 5 * skilllv, skilllv, skill_get_time(skillid, skilllv)); else { + map_foreachinrange(skill_area_sub, src, skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); clif_skill_nodamage(src, bl, skillid, skilllv, 1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, flag|BCT_ENEMY|1, - skill_castend_nodamage_id); } break; + case AB_LAUDAAGNUS: + if( flag&1 || sd == NULL ) + { + if( (tsc && (tsc->data[SC_FREEZE] || tsc->data[SC_STONE] || + tsc->data[SC_BLIND]))&& (rand()%100 < 30+5*skilllv) ) + { + status_change_end(bl, SC_FREEZE, -1); + status_change_end(bl, SC_STONE, -1); + status_change_end(bl, SC_BLIND, -1); + } + // Success rate only applies to the curing effect and not stat bonus. + clif_skill_nodamage(bl, bl, skillid, skilllv, + sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); + } + else if( sd ) + party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), + src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); + break; + case AB_LAUDARAMUS: - if( (flag&1) || sd == NULL || sd->status.party_id == 0 ) + if( flag&1 || sd == NULL ) { - if( tsc && (rand()%100 < 40+10*skilllv) ) + if( (tsc && (tsc->data[SC_SLEEP] || tsc->data[SC_STUN] || + tsc->data[SC_SILENCE]))&& (rand()%100 < 30+5*skilllv) ) { - switch(skillid) - { - case AB_LAUDAAGNUS: - if( tsc->data[SC_STONE] || tsc->data[SC_FREEZE] || tsc->data[SC_BLIND] ) //TODO: Freezing, Crystallization and Burning - { - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_BLIND, INVALID_TIMER); - } - else - clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); - break; - case AB_LAUDARAMUS: - if( tsc->data[SC_STUN] || tsc->data[SC_SLEEP] || tsc->data[SC_SILENCE] ) // TODO: Howling of Mandragora, and Deep Sleep - { - status_change_end(bl, SC_STUN, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_SILENCE, INVALID_TIMER); - } - else - clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); - break; - } + status_change_end(bl, SC_SLEEP, -1); + status_change_end(bl, SC_STUN, -1); + status_change_end(bl, SC_SILENCE, -1); } + clif_skill_nodamage(bl, bl, skillid, skilllv, + sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); + //Success rate only applies to the curing effect and not stat bonus. } else if( sd ) party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); break; + case AB_CLEARANCE: + if( flag&1 || (i = skill_get_splash(skillid, skilllv)) < 1 ) + { //As of the behavior in official server Clearance is just a super version of Dispell skill. [Jobbie] + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rand()%100 >= 30 + 10 * skilllv) + { + if (sd) + clif_skill_fail(sd,skillid,0,0); + break; + } + if(status_isimmune(bl) || !tsc || !tsc->count) + break; + for(i=0;i<SC_MAX;i++) + { + if (!tsc->data[i]) + continue; + switch (i) { + case SC_WEIGHT50: case SC_WEIGHT90: case SC_HALLUCINATION: + case SC_STRIPWEAPON: case SC_STRIPSHIELD: case SC_STRIPARMOR: + case SC_STRIPHELM: case SC_CP_WEAPON: case SC_CP_SHIELD: + case SC_CP_ARMOR: case SC_CP_HELM: case SC_COMBO: + case SC_STRFOOD: case SC_AGIFOOD: case SC_VITFOOD: + case SC_INTFOOD: case SC_DEXFOOD: case SC_LUKFOOD: + case SC_HITFOOD: case SC_FLEEFOOD: case SC_BATKFOOD: + case SC_WATKFOOD: case SC_MATKFOOD: case SC_DANCING: + case SC_GUILDAURA: case SC_SPIRIT: case SC_AUTOBERSERK: + case SC_CARTBOOST: case SC_MELTDOWN: case SC_SAFETYWALL: + case SC_SMA: case SC_SPEEDUP0: case SC_NOCHAT: + case SC_ANKLE: case SC_SPIDERWEB: case SC_JAILED: + case SC_ITEMBOOST: case SC_EXPBOOST: case SC_LIFEINSURANCE: + case SC_BOSSMAPINFO: case SC_PNEUMA: case SC_AUTOSPELL: + case SC_INCHITRATE: case SC_INCATKRATE: case SC_NEN: + case SC_READYSTORM: case SC_READYDOWN: case SC_READYTURN: + case SC_READYCOUNTER:case SC_DODGE: case SC_WARM: + case SC_SPEEDUP1: case SC_AUTOTRADE: case SC_CRITICALWOUND: + case SC_JEXPBOOST: case SC_INVINCIBLE: case SC_INVINCIBLEOFF: + case SC_HELLPOWER: case SC_MANU_ATK: case SC_MANU_DEF: + case SC_SPL_ATK: case SC_SPL_DEF: case SC_MANU_MATK: + case SC_SPL_MATK: case SC_RICHMANKIM: case SC_ETERNALCHAOS: + case SC_DRUMBATTLE: case SC_NIBELUNGEN: case SC_ROKISWEIL: + case SC_INTOABYSS: case SC_SIEGFRIED: case SC_WHISTLE: + case SC_ASSNCROS: case SC_POEMBRAGI: case SC_APPLEIDUN: + case SC_HUMMING: case SC_DONTFORGETME: case SC_FORTUNE: + case SC_SERVICE4U: case SC_FOOD_STR_CASH: case SC_FOOD_AGI_CASH: + case SC_FOOD_VIT_CASH: case SC_FOOD_DEX_CASH: case SC_FOOD_INT_CASH: + case SC_FOOD_LUK_CASH: /* case SC_ELECTRICSHOCKER: case SC_BITE: + case SC__STRIPACCESSORY: case SC__ENERVATION: case SC__GROOMY: + case SC__IGNORANCE: case SC__LAZINESS: case SC__UNLUCKY: + case SC__WEAKNESS: case SC_SAVAGE_STEAK: case SC_COCKTAIL_WARG_BLOOD: + case SC_MAGNETICFIELD:case SC_MINOR_BBQ: case SC_SIROMA_ICE_TEA: + case SC_DROCERA_HERB_STEAMED: case SC_PUTTI_TAILS_NOODLES: + case SC_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER: + case SC_STEALTHFIELD_MASTER: case SC_STEALTHFIELD: */ + continue; + case SC_ASSUMPTIO: + if( bl->type == BL_MOB ) + continue; + break; + } + if(i==SC_BERSERK /*|| i==SC_SATURDAYNIGHTFEVER*/) tsc->data[i]->val2=0; //Mark a dispelled berserk to avoid setting hp to 100 by setting hp penalty to 0. + status_change_end(bl,(sc_type)i,-1); + } + break; + } + map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skillid, skilllv, tick, flag|1, skill_castend_damage_id); + break; + + case AB_SILENTIUM: + // Should the level of Lex Divina be equivalent to the level of Silentium or should the highest level learned be used? [LimitLine] + map_foreachinrange(skill_area_sub, src, skill_get_splash(skillid, skilllv), BL_CHAR, + src, PR_LEXDIVINA, skilllv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + break; + /** + * Warlock + **/ + case WL_STASIS: + if( flag&1 ) + sc_start2(bl,type,100,skilllv,src->id,skill_get_time(skillid,skilllv)); + else + { + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid, skilllv),BL_CHAR,src,skillid,skilllv,tick,(map_flag_vs(src->m)?BCT_ALL:BCT_ENEMY|BCT_SELF)|flag|1,skill_castend_nodamage_id); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + } + break; + + case WL_WHITEIMPRISON: + if( !(tsc && tsc->data[type]) && (src == bl || battle_check_target(src, bl, BCT_ENEMY)) ) + { + int rate = 50 + 3 * skilllv + ( sd? sd->status.job_level : 50 ) / 4; + i = sc_start2(bl,type,rate,skilllv,src->id,(src == bl)?skill_get_time2(skillid,skilllv):skill_get_time(skillid, skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv,i); + if( sd && i ) + skill_blockpc_start(sd,skillid,4000); // Reuse Delay only activated on success + } + else if( sd ) + clif_skill_fail(sd,skillid,0,0); + break; + + case WL_FROSTMISTY: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub,bl,skill_get_splash(skillid,skilllv),BL_CHAR|BL_SKILL,src,skillid,skilllv,tick,flag|BCT_ENEMY,skill_castend_damage_id); + break; + + case WL_JACKFROST: clif_skill_nodamage(src,bl,skillid,skilllv,1); - if( rand()%100 >= 60+10*skilllv ) + map_foreachinshootrange(skill_area_sub,bl,skill_get_splash(skillid,skilllv),BL_CHAR|BL_SKILL,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + + case WL_MARSHOFABYSS: + // Should marsh of abyss still apply half reduction to players after the 28/10 patch? [LimitLine] + clif_skill_nodamage(src, bl, skillid, skilllv, + sc_start4(bl, type, 100, skilllv, status_get_int(src), sd ? sd->status.job_level : 50, 0, + skill_get_time(skillid, skilllv))); + break; + + case WL_SIENNAEXECRATE: + if( status_isimmune(bl) || !tsc ) + break; + + if( flag&1 ) { - if (sd) + if( bl->id == skill_area_temp[1] ) + break; // Already work on this target + + if( tsc && tsc->data[SC_STONE] ) + status_change_end(bl,SC_STONE,-1); + else + status_change_start(bl,SC_STONE,10000,skilllv,0,0,1000,(8+2*skilllv)*1000,2); + } + else + { + int rate = 40 + 8 * skilllv + ( sd? sd->status.job_level : 50 ) / 4; + // IroWiki says Rate should be reduced by target stats, but currently unknown + if( rand()%100 < rate ) + { // Success on First Target + rate = 0; + if( !tsc->data[SC_STONE] ) + rate = status_change_start(bl,SC_STONE,10000,skilllv,0,0,1000,(8+2*skilllv)*1000,2); + else + { + rate = 1; + status_change_end(bl,SC_STONE,-1); + } + + if( rate ) + { + skill_area_temp[1] = bl->id; + map_foreachinrange(skill_area_sub,bl,skill_get_splash(skillid,skilllv),BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id); + } + // Doesn't send failure packet if it fails on defense. + } + else if( sd ) // Failure on Rate clif_skill_fail(sd,skillid,0,0); - break; } + break; - if(status_isimmune(bl) || !tsc || !tsc->count) - break; - for(i=0;i<SC_MAX;i++) + case WL_SUMMONFB: + case WL_SUMMONBL: + case WL_SUMMONWB: + case WL_SUMMONSTONE: { - if (!tsc->data[i]) - continue; - //Initial list of status effects NOT cancelled by Clearance. - switch (i) { - case SC_WEIGHT50: case SC_WEIGHT90: case SC_TWOHANDQUICKEN: - case SC_QUAGMIRE: case SC_SLOWPOISON: case SC_BENEDICTIO: - case SC_TRICKDEAD: case SC_HALLUCINATION: case SC_ASPDPOTION0: - case SC_ASPDPOTION1: case SC_SPEEDUP1: case SC_STRIPWEAPON: - case SC_STRIPSHIELD: case SC_STRIPARMOR: case SC_STRIPHELM: - case SC_CP_WEAPON: case SC_CP_SHIELD: case SC_CP_ARMOR: - case SC_CP_HELM: case SC_AUTOGUARD: case SC_REFLECTSHIELD: - case SC_MAGICROD: case SC_SAFETYWALL: case SC_FIREWEAPON: - case SC_WATERWEAPON: case SC_WINDWEAPON: case SC_EARTHWEAPON: - case SC_VOLCANO: case SC_DELUGE: case SC_VIOLENTGALE: - case SC_AUTOBERSERK: case SC_CARTBOOST: case SC_BLADESTOP: - case SC_ARMOR_ELEMENT: case SC_STOP: case SC_EXPLOSIONSPIRITS: - case SC_NOCHAT: case SC_PARRYING: case SC_TENSIONRELAX: - case SC_SACRIFICE: case SC_BASILICA: case SC_GUILDAURA: - case SC_BLEEDING: case SC_JOINTBEAT: case SC_FOGWALL: - case SC_SPIDERWEB: case SC_RUN: case SC_SPURT: - case SC_SHADOWWEAPON: case SC_GHOSTWEAPON: case SC_SPIRIT: - case SC_WATKFOOD: case SC_MATKFOOD: case SC_KAITE: - case SC_KAAHI: case SC_KAUPE: case SC_ONEHAND: - case SC_CHASEWALK: case SC_SLOWDOWN: case SC_DOUBLECAST: - case SC_GRAVITATION: case SC_CLOSECONFINE: case SC_CLOSECONFINE2: - case SC_UTSUSEMI: case SC_BUNSINJYUTSU: case SC_SUITON: - case SC_STRFOOD: case SC_AGIFOOD: case SC_VITFOOD: - case SC_DEXFOOD: case SC_INTFOOD: case SC_LUKFOOD: - case SC_FLEEFOOD: case SC_HITFOOD: case SC_JAILED: - case SC_SUMMER: case SC_WEDDING: case SC_DANCING: - case SC_EXPBOOST: case SC_LIFEINSURANCE: case SC_ITEMBOOST: - case SC_BOSSMAPINFO: case SC_FOOD_STR_CASH: case SC_FOOD_AGI_CASH: - case SC_FOOD_VIT_CASH: case SC_FOOD_DEX_CASH: case SC_FOOD_INT_CASH: - case SC_FOOD_LUK_CASH: case SC_MERC_FLEEUP: case SC_MERC_ATKUP: - case SC_MERC_HPUP: case SC_MERC_SPUP: case SC_MERC_HITUP: - case SC_SLOWCAST: /*case SC_CRITICALWOUND:*/ case SC_SPEEDUP0: - case SC_DEF_RATE: case SC_MDEF_RATE: case SC_INCHEALRATE: - case SC_S_LIFEPOTION: case SC_L_LIFEPOTION: case SC_INCCRI: - case SC_SPCOST_RATE: case SC_COMMONSC_RESIST: case SC_ELEMENTALCHANGE: - case SC_INCFLEE2: case SC_PNEUMA: case SC_AUTOTRADE: - case SC_KSPROTECTED: case SC_ARMOR_RESIST: case SC_HELLPOWER: - case SC_JEXPBOOST: case SC_ITEMSCRIPT: case SC_INVINCIBLE: - case SC_INVINCIBLEOFF: case SC_MANU_ATK: case SC_MANU_DEF: - case SC_SPL_ATK: case SC_SPL_DEF: case SC_MANU_MATK: - case SC_SPL_MATK: case SC_SEVENWIND: case SC_NEN: - case SC_READYSTORM: case SC_READYDOWN: case SC_READYTURN: - case SC_READYCOUNTER: case SC_DODGE: case SC_WARM: - case SC_SMA: case SC_RICHMANKIM: case SC_ETERNALCHAOS: - case SC_DRUMBATTLE: case SC_NIBELUNGEN: case SC_ROKISWEIL: - case SC_INTOABYSS: case SC_SIEGFRIED: case SC_WHISTLE: - case SC_ASSNCROS: case SC_POEMBRAGI: case SC_APPLEIDUN: - case SC_HUMMING: case SC_DONTFORGETME: case SC_FORTUNE: - case SC_SERVICE4U: case SC_PARTYFLEE: /*case SC_ANGEL_PROTECT:*/ - case SC_EPICLESIS: case SC_DEATHBOUND: case SC_FIGHTINGSPIRIT: - case SC_ABUNDANCE: case SC_MILLENNIUMSHIELD: - // not implemented - //case SC_BUCHEDENOEL: case SC_POPECOOKIE: - //case SC_SAVAGE_STEAK: case SC_COCKTAIL_WARG_BLOOD: case SC_MINOR_BBQ: - //case SC_SIROMA_ICE_TEA: case SC_DROCERA_HERB_STEAMED: case SC_PUTTI_TAILS_NOODLES: - //case SC_CRIFOOD: case SC_STR_SCROLL: case SC_INT_SCROLL: - //case SC_ACARAJE: - // - continue; - case SC_ASSUMPTIO: - if( bl->type == BL_MOB ) - continue; + short element = 0, sctype = 0, pos = -1; + struct status_change *sc = status_get_sc(src); + if( !sc ) break; + + for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ ) + { + if( !sctype && !sc->data[i] ) + sctype = i; // Take the free SC + if( sc->data[i] ) + pos = max(sc->data[i]->val2,pos); + } + + if( !sctype ) + { + if( sd ) // No free slots to put SC + clif_skill_fail(sd,skillid,0x13,0); break; } - if(i==SC_BERSERK) tsc->data[i]->val2=0; //Mark a dispelled berserk to avoid setting hp to 100 by setting hp penalty to 0. - status_change_end(bl, (sc_type)i, INVALID_TIMER); + + pos++; // Used in val2 for SC. Indicates the order of this ball + switch( skillid ) + { // Set val1. The SC element for this ball + case WL_SUMMONFB: element = WLS_FIRE; break; + case WL_SUMMONBL: element = WLS_WIND; break; + case WL_SUMMONWB: element = WLS_WATER; break; + case WL_SUMMONSTONE: element = WLS_STONE; break; + } + + sc_start4(src,sctype,100,element,pos,skilllv,0,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,0,0); } break; - case AB_SILENTIUM: - if (flag&1) - sc_start(bl,SC_SILENCE,100,skilllv,skill_get_time(skillid,skilllv)); - else { + + case WL_READING_SB: + if( sd ) + { + int i, preserved = 0, max_preserve = 4 * pc_checkskill(sd,WL_FREEZE_SP) + sstatus->int_ / 10 + sd->status.base_level / 10; + ARR_FIND(0, MAX_SPELLBOOK, i, sd->rsb[i].skillid == 0); // Search for a Free Slot + if( i == MAX_SPELLBOOK ) + { + clif_skill_fail(sd,skillid,0x04,0); + break; + } + for( i = 0; i < MAX_SPELLBOOK && sd->rsb[i].skillid; i++ ) + preserved += sd->rsb[i].points; + + if( preserved >= max_preserve ) + { + clif_skill_fail(sd,skillid,0x04,0); + break; + } + + sc_start(bl,SC_STOP,100,skilllv,-1); //Can't move while selecting a spellbook. + clif_spellbook_list(sd); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + /** + * Ranger + **/ + case RA_FEARBREEZE: + clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src, bl, skillid, skilllv, sc_start(bl, type, 100, skilllv, skill_get_time(skillid, skilllv))); + break; + + case RA_WUGMASTERY: + if( sd ) + { + if( pc_isridingwug(sd) ) + clif_skill_fail(sd,skillid,0,0); + else if( !pc_iswug(sd) ) + pc_setoption(sd,sd->sc.option|OPTION_WUG); + else + pc_setoption(sd,sd->sc.option&~OPTION_WUG); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case RA_WUGRIDER: + if( sd ) { + if( !pc_isridingwug(sd) && pc_iswug(sd) ) { + pc_setoption(sd,sd->sc.option&~OPTION_WUG); + pc_setoption(sd,sd->sc.option|OPTION_WUGRIDER); + } else if( pc_isridingwug(sd) ) { + pc_setoption(sd,sd->sc.option&~OPTION_WUGRIDER); + pc_setoption(sd,sd->sc.option|OPTION_WUG); + } else if( sd ) { + clif_skill_fail(sd,skillid,0,0); + } clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_nodamage_id); + } + break; + + case RA_WUGDASH: + if( tsce ) { + clif_skill_nodamage(src,bl,skillid,skilllv,status_change_end(bl, type, -1)); + map_freeblock_unlock(); + return 0; + } + if( sd && pc_isridingwug(sd) ) { + clif_skill_nodamage(src,bl,skillid,skilllv,sc_start4(bl,type,100,skilllv,unit_getdir(bl),0,0,1)); + clif_walkok(sd); + } + break; + + case RA_SENSITIVEKEEN: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),BL_CHAR|BL_SKILL,src,skillid,skilllv,tick,flag|BCT_ENEMY,skill_castend_damage_id); + break; + /** + * Mechanic + **/ + case NC_F_SIDESLIDE: + case NC_B_SIDESLIDE: + { + int dir = (skillid == NC_F_SIDESLIDE) ? (unit_getdir(src)+4)%8 : unit_getdir(src); + skill_blown(src,bl,skill_get_blewcount(skillid,skilllv),dir,0x1); + clif_slide(src,src->x,src->y); + clif_fixpos(src); //Aegis sent this packet + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case NC_SELFDESTRUCTION: + if( sd ) { + if( sd->sc.option&OPTION_MADOGEAR ) + pc_setoption(sd, sd->sc.option&~OPTION_MADOGEAR); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + skill_castend_damage_id(src, src, skillid, skilllv, tick, flag); + } + break; + + case NC_ANALYZE: + clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src, bl, skillid, skilllv, + sc_start(bl,type, 30 + 12 * skilllv,skilllv,skill_get_time(skillid,skilllv))); + if( sd ) pc_overheat(sd,1); + break; + + case NC_MAGNETICFIELD: + if( (i = sc_start2(bl,type,100,skilllv,src->id,skill_get_time(skillid,skilllv))) ) + { + map_foreachinrange(skill_area_sub,src,skill_get_splash(skillid,skilllv),splash_target(src),src,skillid,skilllv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill_castend_damage_id);; + clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skillid,skilllv,6); + pc_overheat(sd,1); + } + clif_skill_nodamage(src,src,skillid,skilllv,i); + break; + + case NC_REPAIR: + if( sd ) + { + int heal; + if( dstsd && (dstsd->sc.option&OPTION_MADOGEAR) ) + { + heal = dstsd->status.max_hp * (3+3*skilllv) / 100; + status_heal(bl,heal,0,2); + } else { + heal = sd->status.max_hp * (3+3*skilllv) / 100; + status_heal(src,heal,0,2); + } + + clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + clif_skill_nodamage(src, bl, skillid, skilllv, heal); + } + break; + + case NC_DISJOINT: + { + if( bl->type != BL_MOB ) break; + md = map_id2md(bl->id); + if( md && md->class_ >= 2042 && md->class_ <= 2046 ) + status_kill(bl); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); } break; @@ -6090,9 +7096,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in skill_consume_requirement(sd,skillid,skilllv,2); } - if( sd && skill_get_cooldown(skillid,skilllv) ) - skill_blockpc_start (sd, skillid, skill_get_cooldown(skillid, skilllv)); - map_freeblock_unlock(); return 0; } @@ -6277,13 +7280,15 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data) if (ud->state.running && ud->skillid == TK_JUMPKICK) flag = 1; - if (ud->walktimer != INVALID_TIMER && ud->skillid != TK_RUN) + if (ud->walktimer != INVALID_TIMER && ud->skillid != TK_RUN && ud->skillid != RA_WUGDASH) unit_stop_walking(src,1); if( !sd || sd->skillitem != ud->skillid || skill_get_delay(ud->skillid,ud->skilllv) ) ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish] + if( sd && skill_get_cooldown(ud->skillid,ud->skilllv) > 0 ) + skill_blockpc_start(sd, ud->skillid, skill_get_cooldown(ud->skillid, ud->skilllv)); if( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv)); + clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv), 0, 0, 0); if( sd ) { switch( ud->skillid ) @@ -6319,7 +7324,7 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data) sc = status_get_sc(src); if(sc && sc->count) { if(sc->data[SC_MAGICPOWER] && - ud->skillid != HW_MAGICPOWER && ud->skillid != WZ_WATERBALL) + ud->skillid != HW_MAGICPOWER && ud->skillid != WZ_WATERBALL && ud->skillid != WL_TETRAVORTEX) status_change_end(src, SC_MAGICPOWER, INVALID_TIMER); if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD && @@ -6496,7 +7501,7 @@ int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data) if( !sd || sd->skillitem != ud->skillid || skill_get_delay(ud->skillid,ud->skilllv) ) ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); if( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv)); + clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skillid, ud->skilllv), 0, 0, 0); // if( sd ) // { // switch( ud->skillid ) @@ -6628,6 +7633,9 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk case MG_SAFETYWALL: case MG_FIREWALL: case MG_THUNDERSTORM: +#if FIREIVY_ON + case WZ_FIREIVY: +#endif case AL_PNEUMA: case WZ_ICEWALL: case WZ_FIREPILLAR: @@ -6690,6 +7698,17 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk case NJ_RAIGEKISAI: case NJ_KAMAITACHI: case NPC_EVILLAND: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete). case GS_GROUNDDRIFT: //Ammo should be deleted right away. skill_unitsetting(src,skillid,skilllv,x,y,0); @@ -6937,35 +7956,119 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk } } break; - + /** + * Mechanic + **/ + case NC_COLDSLOWER: + case NC_ARMSCANNON: + /** + * Rune Knight + **/ + case RK_DRAGONBREATH: case RK_WINDCUTTER: - i = skill_get_splash(skillid, skilllv); - clif_skill_damage(src, src, tick, 0, 0, -1, 1, skillid, -1, 0); - map_foreachinarea(skill_area_sub, - src->m,x-i,y-i,x+i,y+i,(BL_CHAR|BL_SKILL), - src,skillid,skilllv,tick,flag|BCT_ENEMY|1, - skill_castend_damage_id); + i = skill_get_splash(skillid,skilllv); + map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); + break; + /** + * Guilotine Cross + **/ + case GC_POISONSMOKE: + if( !(sc && sc->data[SC_POISONINGWEAPON]) ) { + if( sd ) + clif_skill_fail(sd,skillid,0x20,0); + return 0; + } + clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skillid,skilllv,6); + skill_unitsetting(src, skillid, skilllv, x, y, flag); + status_change_end(src,SC_POISONINGWEAPON,-1); + break; + /** + * Arch Bishop + **/ + case AB_EPICLESIS: + if( (sg = skill_unitsetting(src, skillid, skilllv, x, y, 0)) ) { + i = sg->unit->range; + map_foreachinarea(skill_area_sub, src->m, x - i, y - i, x + i, y + i, BL_CHAR, src, ALL_RESURRECTION, 1, tick, flag|BCT_NOENEMY|1,skill_castend_nodamage_id); + } + break; + /** + * Warlock + **/ + case WL_COMET: + if( sc ) { + sc->comet_x = x; + sc->comet_y = y; + } + i = skill_get_splash(skillid,skilllv); + map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); break; - case RK_DRAGONBREATH: + case WL_EARTHSTRAIN: + { + int i, wave = skilllv + 4, dir = map_calc_dir(src,x,y); + int sx = x, sy = y; + + if( sc && sc->data[SC_MAGICPOWER] ) + flag = flag|2; //Store the magic power flag + + for( i = 0; i < wave; i++ ) + { + switch( dir ) + { + case 0: case 1: case 7: sy = src->y + i; break; + case 3: case 4: case 5: sy = src->y - i; break; + case 2: sx = src->x - i; break; + case 6: sx = src->x + i; break; + } + skill_addtimerskill(src,gettick() + (200 * i),0,sx,sy,skillid,skilllv,dir,flag&2); // Temp code until animation is replaced. [Rytech] + //skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skillid,skilllv,dir,flag&2); // Official steping timer, but disabled due to too much noise. + } + } + break; + /** + * Ranger + **/ + case RA_DETONATOR: i = skill_get_splash(skillid, skilllv); - map_foreachinarea(skill_area_sub, - src->m,x-i,y-i,x+i,y+i,(BL_CHAR|BL_SKILL), - src,skillid,skilllv,tick,flag|BCT_ENEMY|1, - skill_castend_damage_id); + map_foreachinarea(skill_detonator, src->m, x-i, y-i, x+i, y+i, BL_SKILL, src); + clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6); + break; + /** + * Mechanic + **/ + case NC_NEUTRALBARRIER: + case NC_STEALTHFIELD: + skill_clear_unitgroup(src); // To remove previous skills - cannot used combined + if( (sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0)) != NULL ) + { + sc_start2(src,skillid == NC_NEUTRALBARRIER ? SC_NEUTRALBARRIER_MASTER : SC_STEALTHFIELD_MASTER,100,skilllv,sg->group_id,skill_get_time(skillid,skilllv)); + if( sd ) pc_overheat(sd,1); + } break; - case AB_EPICLESIS: - if( skill_unitsetting(src, skillid, skilllv, x, y, 0) ) + case NC_SILVERSNIPER: { - i = skill_get_splash(skillid,skilllv); // Use Splash Range. - map_foreachinarea(skill_area_sub, - src->m, x-i, y-i, x+i, y+i, BL_CHAR, - src, ALL_RESURRECTION, 1, tick, flag|BCT_NOENEMY|1, - skill_castend_nodamage_id); + int class_ = 2042; + struct mob_data *md; + + md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, ""); + if( md ) + { + md->master_id = src->id; + md->special_state.ai = 3; + if( md->deletetimer != INVALID_TIMER ) + delete_timer(md->deletetimer, mob_timer_delete); + md->deletetimer = add_timer (gettick() + skill_get_time(skillid, skilllv), mob_timer_delete, md->bl.id, 0); + mob_spawn( md ); + } } break; + case NC_MAGICDECOY: + if( sd ) clif_magicdecoy_list(sd,skilllv,x,y); + break; + default: ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skillid); return 1; @@ -6981,9 +8084,6 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk skill_consume_requirement(sd,skillid,skilllv,2); } - if( sd && skill_get_cooldown(skillid,skilllv) ) - skill_blockpc_start (sd, skillid, skill_get_cooldown(skillid, skilllv)); - return 0; } @@ -7017,7 +8117,11 @@ int skill_castend_map (struct map_session_data *sd, short skill_num, const char sd->sc.data[SC_BERSERK] || sd->sc.data[SC_BASILICA] || sd->sc.data[SC_MARIONETTE] || - sd->sc.data[SC_DEATHBOUND] + /** + * Warlock + **/ + sd->sc.data[SC_WHITEIMPRISON] || + (sd->sc.data[SC_STASIS] && skill_stasis_check(&sd->bl, sd->sc.data[SC_STASIS]->val2, skill_num)) )) { skill_failed(sd); return 0; @@ -7306,6 +8410,17 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli case HT_FREEZINGTRAP: case MA_FREEZINGTRAP: case HT_BLASTMINE: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: if( map_flag_gvg(src->m) || map[src->m].flag.battleground ) limit *= 4; // longer trap times in WOE [celest] if( battle_config.vs_traps_bctall && map_flag_vs(src->m) && (src->type&battle_config.vs_traps_bctall) ) @@ -7455,6 +8570,17 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli break; } + /** + * Guilotine Cross + **/ + case GC_POISONSMOKE: + if( !(sc && sc->data[SC_POISONINGWEAPON]) ) + return NULL; + val1 = sc->data[SC_POISONINGWEAPON]->val1; // Level of Poison, to determine poisoning time + val2 = sc->data[SC_POISONINGWEAPON]->val2; // Type of Poison + limit = 4000 + 2000 * skilllv; + break; + } nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skillid,skilllv,skill_get_unit_id(skillid,flag&1)+subunt, limit, interval)); @@ -7529,6 +8655,17 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli case HT_TALKIEBOX: case HT_SKIDTRAP: case MA_SKIDTRAP: + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + case RA_CLUSTERBOMB: + case RA_MAGENTATRAP: + case RA_COBALTTRAP: + case RA_MAIZETRAP: + case RA_VERDURETRAP: + case RA_FIRINGTRAP: + case RA_ICEBOUNDTRAP: val1 = 3500; break; case GS_DESPERADO: @@ -7608,8 +8745,8 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un sc = status_get_sc(bl); - if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE) - return 0; //Hidden characters are immune to AoE skills except Heaven's Drive. [Skotlex] + if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE && sg->skill_id != WL_EARTHSTRAIN ) + return 0; //Hidden characters are immune to AoE skills except to these. [Skotlex] type = status_skill2sc(sg->skill_id); sce = (sc && type != -1)?sc->data[type]:NULL; @@ -7992,6 +9129,16 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns case UNT_FLASHER: case UNT_FREEZINGTRAP: case UNT_FIREPILLAR_ACTIVE: + /** + * Ranger + **/ + case UNT_CLUSTERBOMB: + case UNT_MAGENTATRAP: + case UNT_COBALTTRAP: + case UNT_MAIZETRAP: + case UNT_VERDURETRAP: + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick); if (sg->unit_id != UNT_FIREPILLAR_ACTIVE) clif_changetraplook(&src->bl, sg->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS); @@ -8158,6 +9305,10 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns break; case UNT_GRAVITATION: + case UNT_EARTHSTRAIN: + case UNT_FIREWALK: + case UNT_ELECTRICWALK: + case UNT_PSYCHIC_WAVE: skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; @@ -8173,60 +9324,70 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns //clif_changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE); sg->limit=DIFF_TICK(tick,sg->tick)+1500; break; + /** + * 3rd stuff + **/ + case UNT_POISONSMOKE: + if( battle_check_target(ss,bl,BCT_ENEMY) > 0 && !(tsc && tsc->data[sg->val2]) && rand()%100 < 20 ) + sc_start(bl,sg->val2,100,sg->val1,skill_get_time2(GC_POISONINGWEAPON,sg->val1)); + break; case UNT_EPICLESIS: - sg->val2--; // track when units should be healed. Initial tick heals immediately. - if ( !battle_check_undead(tstatus->race, tstatus->def_ele) && bl->type == BL_PC) - { //Effect only players who are not undead element. - - //Unknown if any status effects should prevent Epiclesis. - //if( tsc->data[SC_BERSERK] ) - // break; - - //FIXME: Apply status effect if standing in unit, should cancel upon stepping out of skill_get_splash() area. - if( tsc && !tsc->data[SC_EPICLESIS] ) - sc_start(bl, type, 100, sg->skill_lv, skill_get_time(sg->skill_id,sg->skill_lv)); - - //There's probably a better way to handle this... - if( sg->val2 < 1 ) + if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) + { + int hp, sp; + switch( sg->skill_lv ) { - int heal, sp; - struct map_session_data *sd = (struct map_session_data *)bl; - - if( tstatus->hp < tstatus->max_hp ) - { - heal = tstatus->max_hp * (((sg->skill_lv - 1) / 2) + 3) / 100; - if( tstatus->hp + heal > tstatus->max_hp ) - heal = tstatus->max_hp - tstatus->hp; - if( heal > 0 ) - { - clif_heal(sd->fd,SP_HP,heal); - status_heal(bl, heal, 0, 0); - } - } - - if( tstatus->sp < tstatus->max_sp ) - { - sp = tstatus->max_sp * (((sg->skill_lv - 1) / 2) + 2) / 100; - if( tstatus->sp + sp > tstatus->max_sp ) - sp = tstatus->max_sp - tstatus->sp; - if( sp > 0 ) - { - clif_heal(sd->fd,SP_SP,sp); - status_heal(bl, 0, sp, 0); - } - } - sg->val2 = 3; // then every three seconds after. + case 1: case 2: hp = 3; sp = 2; break; + case 3: case 4: hp = 4; sp = 3; break; + case 5: default: hp = 5; sp = 4; break; + } + hp = tstatus->max_hp * hp / 100; + sp = tstatus->max_sp * sp / 100; + status_heal(bl, hp, sp, 0); + if( tstatus->hp < tstatus->max_hp ) + clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 1); + if( tstatus->sp < tstatus->max_sp ) + clif_skill_nodamage(&src->bl, bl, MG_SRECOVERY, sp, 1); + sc_start(bl, type, 100, sg->skill_lv, sg->interval + 100); + sg->val2++; + // Reveal hidden players every 5 seconds. + if( sg->val2 >= 5 ) + { + sg->val2 = 0; + // TODO: check if other hidden status can be removed. + status_change_end(bl,SC_HIDING,-1); + status_change_end(bl,SC_CLOAKING,-1); } } + /* Enable this if kRO fix the current skill. Currently no damage on undead and demon monster. [Jobbie] + else if( battle_check_target(ss, bl, BCT_ENEMY) > 0 && battle_check_undead(tstatus->race, tstatus->def_ele) ) + skill_castend_damage_id(&src->bl, bl, sg->skill_id, sg->skill_lv, 0, 0);*/ + break; - sg->val3--; // track when units should be unhidden. Initial tick unhides units immediately. - if( sg->val3 < 1 ) - { - status_change_end(bl,SC_HIDING,-1); - status_change_end(bl,SC_CLOAKING,-1); - sg->val3 = 5; //then every five seconds after. - } + case UNT_STEALTHFIELD: + if( bl->id == sg->src_id ) + break; // Dont work on Self (video shows that) + case UNT_NEUTRALBARRIER: + sc_start(bl,type,100,sg->skill_lv,sg->interval + 100); + break; + + case UNT_DIMENSIONDOOR: + if( tsd && !map[bl->m].flag.noteleport ) + pc_randomwarp(tsd,3); + else if( bl->type == BL_MOB && battle_config.mob_warp&8 ) + unit_warp(bl,-1,-1,-1,3); + break; + + case UNT_REVERBERATION: + clif_changetraplook(&src->bl,UNT_USED_TRAPS); + map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick); + sg->limit = DIFF_TICK(tick,sg->tick) + 1500; + break; + + case UNT_SEVERE_RAINSTORM: + if( battle_check_target(&src->bl, bl, BCT_ENEMY) ) + skill_attack(BF_WEAPON,ss,&src->bl,bl,WM_SEVERE_RAINSTORM_MELEE,sg->skill_lv,tick,0); break; } @@ -8265,7 +9426,7 @@ int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned in switch(sg->unit_id){ case UNT_SAFETYWALL: case UNT_PNEUMA: - case UNT_EPICLESIS: + case UNT_EPICLESIS://Arch Bishop if (sce) status_change_end(bl, type, INVALID_TIMER); break; @@ -8274,7 +9435,6 @@ int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned in if( sce && sce->val4 == src->bl.id ) status_change_end(bl, type, INVALID_TIMER); break; - case UNT_HERMODE: //Clear Hermode if the owner moved. if (sce && sce->val3 == BCT_SELF && sce->val4 == sg->group_id) status_change_end(bl, type, INVALID_TIMER); @@ -8511,12 +9671,18 @@ static int skill_check_condition_char_sub (struct block_list *bl, va_list ap) return 1; } case AB_ADORAMUS: - { - int skilllv = pc_checkskill(sd,skillid); - if( (tsd->class_&MAPID_UPPERMASK) == MAPID_PRIEST && tsd->status.sp >= 2*skilllv) - p_sd[(*c)++]=tsd->bl.id; + { // Adoramus does not consume Blue Gemstone when there is at least 1 Priest class next to the caster + if( (tsd->class_&MAPID_UPPERMASK) == MAPID_PRIEST ) + p_sd[(*c)++] = tsd->bl.id; return 1; } + case WL_COMET: + { // Comet does not consume Red Gemstones when there is at least 1 Warlock class next to the caster + if( tsd->status.class_ == 4055 || tsd->status.class_ == 4061 ) + p_sd[(*c)++] = tsd->bl.id; + return 1; + } + default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex] { int skilllv; @@ -8568,8 +9734,11 @@ int skill_check_pc_partner (struct map_session_data *sd, short skill_id, short* return c; case AB_ADORAMUS: if( c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL ) - status_charge(&tsd->bl, 0, 2*(*skill_lv)); - return c; + { + i = 2 * (*skill_lv); + status_charge(&tsd->bl, 0, i); + } + break; default: //Warning: Assuming Ensemble skills here (for speed) if (c > 0 && sd->sc.data[SC_DANCING] && (tsd = map_id2sd(p_sd[0])) != NULL) { @@ -8588,7 +9757,7 @@ int skill_check_pc_partner (struct map_session_data *sd, short skill_id, short* memset (p_sd, 0, sizeof(p_sd)); i = map_foreachinrange(skill_check_condition_char_sub, &sd->bl, range, BL_PC, &sd->bl, &c, &p_sd, skill_id); - if (skill_id != PR_BENEDICTIO && skill_id != AB_ADORAMUS) //Apply the average lv to encore skills. + if (skill_id != PR_BENEDICTIO) //Apply the average lv to encore skills. *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners. return c; } @@ -9002,34 +10171,97 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh return 0; } break; + /** + * Arch Bishop + **/ case AB_ANCILLA: - i = pc_search_inventory(sd,12333); - if( i >= 0 && sd->status.inventory[i].amount >= 3 ) { - clif_skill_fail(sd, skill, 12, 0); - return 0; + int count = 0; + for( i = 0; i < MAX_INVENTORY; i ++ ) + if( sd->status.inventory[i].nameid == ITEMID_ANCILLA ) + count += sd->status.inventory[i].amount; + if( count >= 3 ) { + clif_skill_fail(sd, skill, 0x0c, 0); + return 0; + } } break; - case AB_EPICLESIS: // Skill should fail if items are not present. Why the curveball, RO? - if ( require.itemid[0] ) + /** + * Keeping as a note: + * Bug Report #17 provides a link to a sep-2011 changelog that shows this requirement was removed + **/ + //case AB_LAUDAAGNUS: + //case AB_LAUDARAMUS: + // if( !sd->status.party_id ) { + // clif_skill_fail(sd,skill,0,0); + // return 0; + // } + // break; + + case AB_ADORAMUS: + /** + * Warlock + **/ + case WL_COMET: + if( skill_check_pc_partner(sd,skill,&lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) ) { - i = pc_search_inventory(sd,require.itemid[0]); - if( i < 0 || sd->status.inventory[i].amount < require.amount[0] ) - { - clif_skill_fail(sd,skill,14,0); //Ancilla required - return 0; - } + //clif_skill_fail(sd,skill,0x47,require.amount[0],require.itemid[0]); + clif_skill_fail(sd,skill,0,0); + return 0; } - if ( require.itemid[1] ) + break; + case WL_SUMMONFB: + case WL_SUMMONBL: + case WL_SUMMONWB: + case WL_SUMMONSTONE: + if( sc ) { - i = pc_search_inventory(sd,require.itemid[1]); - if(i < 0 || sd->status.inventory[i].amount < require.amount[1] ) - { - clif_skill_fail(sd,skill,13,0); //Holy Water required + ARR_FIND(SC_SPHERE_1,SC_SPHERE_5+1,i,!sc->data[i]); + if( i == SC_SPHERE_5+1 ) + { // No more free slots + clif_skill_fail(sd,skill,0x13,0); return 0; } } break; + /** + * Guilotine Cross + **/ + case GC_HALLUCINATIONWALK: + if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) { + clif_skill_fail(sd,skill,0x0,0); + return 0; + } + break; + case GC_COUNTERSLASH: + case GC_WEAPONCRUSH: + if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING) ) { + clif_skill_fail(sd, skill, 0x1f, 0); + return 0; + } + break; + case GC_CROSSRIPPERSLASHER: + if( !(sc && sc->data[SC_ROLLINGCUTTER]) ) { + clif_skill_fail(sd, skill, 0x17, 0); + return 0; + } + break; + case GC_POISONSMOKE: + case GC_VENOMPRESSURE: + if( !(sc && sc->data[SC_POISONINGWEAPON]) ) { + clif_skill_fail(sd, skill, 0x20, 0); + return 0; + } + break; + /** + * Ranger + **/ + case RA_SENSITIVEKEEN: + if(!pc_iswug(sd)) { + clif_skill_fail(sd,skill,0x17,0); + return 0; + } + break; } switch(require.state) { @@ -9114,26 +10346,51 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh break; clif_skill_fail(sd,skill,0,0); return 0; + /** + * Rune Knight + **/ + case ST_RIDINGDRAGON: + if( !(sd->sc.option&OPTION_DRAGON)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } break; - case ST_DRAGON: - if(!pc_isdragon(sd)) { - clif_skill_fail(sd,skill,25,0); + /** + * Wug + **/ + case ST_WUG: + if( !(sd->sc.option&OPTION_WUG) ) { + clif_skill_fail(sd,skill,0,0); return 0; } break; -/* - case ST_WARG: - if(!pc_iswarg(sd)) { + /** + * Riding Wug + **/ + case ST_RIDINGWUG: + if( !(sd->sc.option&OPTION_WUGRIDER) ){ clif_skill_fail(sd,skill,0,0); return 0; } break; - case ST_MADOGEAR: - if(!pc_ismadogear(sd)) { - clif_skill_fail(sd,skill,33,0); + /** + * Mechanic + **/ + case ST_MADO: + if( !(sd->sc.option&OPTION_MADOGEAR) ) { + clif_skill_fail(sd,skill,0,0); return 0; } -*/ + break; + /** + * Sorcerer + **/ + //case ST_ELEMENTALSPIRIT: + // if(!sd->ed) { + // clif_skill_fail(sd,skill,0x4f,0,0); + // return 0; + // } + // break; } if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) { @@ -9208,15 +10465,16 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor return 0; } - require = skill_get_requirement(sd,skill,lv);// Adoramus need this. - // perform skill-specific checks (and actions) switch( skill ) { case PR_BENEDICTIO: - case AB_ADORAMUS: skill_check_pc_partner(sd, skill, &lv, 1, 1); break; + case AB_ADORAMUS: + //if( skill_check_pc_partner(sd,skill,&lv, 1, 2) ) + // sd->state.no_gemstone = 1; // Mark this skill as it don't consume ammo because partners gives SP + break; case AM_CANNIBALIZE: case AM_SPHEREMINE: { @@ -9236,10 +10494,38 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor } break; } + case NC_SILVERSNIPER: + case NC_MAGICDECOY: + { + int c = 0, j; + int maxcount = skill_get_maxcount(skill,lv); + int mob_class = 2042; + if( skill == NC_MAGICDECOY ) + mob_class = 2043; + + if( battle_config.land_skill_limit && maxcount > 0 && ( battle_config.land_skill_limit&BL_PC ) ) + { + if( skill == NC_MAGICDECOY ) + { + for( j = mob_class; j <= 2046; j++ ) + i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, j, skill, &c); + } + else + i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill, &c); + if( c >= maxcount ) + { + clif_skill_fail(sd , skill, 0, 0); + return 0; + } + } + } + break; } status = &sd->battle_status; + require = skill_get_requirement(sd,skill,lv); + if( require.hp > 0 && status->hp <= (unsigned int)require.hp) { clif_skill_fail(sd,skill,2,0); return 0; @@ -9272,15 +10558,8 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor clif_skill_fail(sd,skill,7,0);// red gemstone required else if( require.itemid[i] == ITEMID_BLUE_GEMSTONE ) clif_skill_fail(sd,skill,8,0);// blue gemstone required - else if( require.itemid[i] == 523 ) - clif_skill_fail(sd,skill,13,0); //Holy Water required - else if( require.itemid[i] == 12333 ) - clif_skill_fail(sd,skill,14,0); //Ancilla required else - { - //clif_skill_fail(sd,skill,71,require.amount[i],require.itemid[i]); clif_skill_fail(sd,skill,0,0); - } return 0; } } @@ -9428,6 +10707,10 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short if (sd->status.hom_id) //Don't delete items when hom is already out. continue; break; + case NC_SHAPESHIFT: + if( i < 4 ) + continue; + break; case WZ_FIREPILLAR: // celest if (lv <= 5) // no gems required at level 1-5 continue; @@ -9439,13 +10722,7 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short if( itemid_isgemstone(req.itemid[i]) && skill != HW_GANBANTEIN ) { - if( sd->special_state.no_gemstone || -// FIXME: [Inkfish] -// check partners every time trying to get requirement info? not wise -// but neither check it in castbegin -// PR_BENEDICTIO is instant cast, so you probably can store info in castbegin and use it in castend without it being modified. -// but AB_ADORAMUS has cast time. partner info may change during casting. - (skill == AB_ADORAMUS && skill_check_pc_partner(sd,skill,&lv, 1, 0) )) // Do not require Gemstone if next to another priest. + if( sd->special_state.no_gemstone ) { //Make it substract 1 gem rather than skipping the cost. if( --req.amount[i] < 1 ) req.itemid[i] = 0; @@ -9530,39 +10807,22 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short *------------------------------------------*/ int skill_castfix (struct block_list *bl, int skill_id, int skill_lv) { - int scale = 0, max_fixedReduction = 0; - int time = skill_get_cast(skill_id, skill_lv); // Skill's base cast time. - int fixedcasttime = skill_get_fixedcast(skill_id, skill_lv); // Skills fixed cast time. + int time = skill_get_cast(skill_id, skill_lv); struct map_session_data *sd; - struct status_change *sc; nullpo_ret(bl); sd = BL_CAST(BL_PC, bl); - sc = status_get_sc(bl); - - // calculate base cast time (reduced by dex and int) - if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) - { - // iRO Wiki states as of 2011/08/22: - // castTime = (1 - SQRT((DEX * 2 + INT) / 530)) * (1 - sum_castReduction/100%) * baseCast * 0.8 + (1 - max_fixedReduction/100%) * baseCast * 0.2 - // let's do (DEX * 2 + INT) here; the rest will come after. - scale = cap_value((status_get_dex(bl) * 2 + status_get_int(bl)) * 10000, 0, INT_MAX); - } - //apply variable cast rate modifiers via status effects. - if( !(skill_get_castnodex(skill_id, skill_lv)&2) && sc && sc->count ) - { - if (sc->data[SC_SLOWCAST]) - time += time * sc->data[SC_SLOWCAST]->val2 / 100; - if (sc->data[SC_SUFFRAGIUM]) - time -= time * sc->data[SC_SUFFRAGIUM]->val2 / 100; - if (sc->data[SC_MEMORIZE]) - time>>=1; - if (sc->data[SC_POEMBRAGI]) - time -= time * sc->data[SC_POEMBRAGI]->val2 / 100; + // calculate base cast time (reduced by dex) + if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) { + int scale = CONST_CASTRATE_SCALE - CONST_CASTRATE_CALC; + if( scale > 0 ) // not instant cast + time = time * scale / CONST_CASTRATE_SCALE; + else + return 0; // instant cast } - // calculate variable cast time reduced by item/card bonuses + // calculate cast time reduced by item/card bonuses if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd ) { int i; @@ -9577,51 +10837,61 @@ int skill_castfix (struct block_list *bl, int skill_id, int skill_lv) } } } - - // apply fixed cast rate modifiers via status effects. - if( !(skill_get_castnodex(skill_id, skill_lv)&2) && sc && sc->count ) - { - if( sc->data[SC_AB_SECRAMENT] && sc->data[SC_AB_SECRAMENT]->val2 > max_fixedReduction ) - max_fixedReduction = sc->data[SC_AB_SECRAMENT]->val2; - } - - // calculate fixed cast time reduced by item/card bonuses - if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd ) - { - int i; - if( sd->fixedcastrate != 100 ) - fixedcasttime = fixedcasttime * sd->fixedcastrate / 100; - for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ ) - { - if( sd->skillcast[i].id == skill_id ) - { - fixedcasttime+= fixedcasttime * sd->skillcast[i].val / 100; - break; - } - } - } - - // Apply more of the cast time formula for variable cast time post reduction. - // Now let's do (1 - SQRT(scale / 530)) * (1 - sum_castReduction/100%) - // Ensure this value is not reduced past 0. - time = cap_value((100 - (int)sqrt(scale/530.)) * time / 100, 0, INT_MAX); - - // Reduce fixedcasttime by only the highest max_fixedReduction found - if( max_fixedReduction ) - fixedcasttime -= fixedcasttime * max_fixedReduction / 100; - - // Apply the modified fixed cast time to variable cast time. - time += fixedcasttime; - // config cast time multiplier if (battle_config.cast_rate != 100) time = time * battle_config.cast_rate / 100; - // return final cast time return (time > 0) ? time : 0; } /*========================================== + * Does cast-time reductions based on sc data. + *------------------------------------------*/ +int skill_castfix_sc (struct block_list *bl, int time, int skill_id, int skill_lv) +{ + struct status_change *sc = status_get_sc(bl); +#if RECASTING + int fixed = skill_get_cast(skill_id, skill_lv); + if( fixed > 1 ) + fixed = fixed * 20 / 100; + else + fixed = 0; +#endif + if (sc && sc->count) { + if (sc->data[SC_SLOWCAST]) + time += time * sc->data[SC_SLOWCAST]->val2 / 100; + if (sc->data[SC_SUFFRAGIUM]) { + time -= time * sc->data[SC_SUFFRAGIUM]->val2 / 100; + status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER); + } + if (sc->data[SC_MEMORIZE]) { + time>>=1; + if ((--sc->data[SC_MEMORIZE]->val2) <= 0) + status_change_end(bl, SC_MEMORIZE, INVALID_TIMER); + } + if (sc->data[SC_POEMBRAGI]) + time -= time * sc->data[SC_POEMBRAGI]->val2 / 100; +#if RECASTING + /** + * AB Sacrament reduces fixed cast time by (10 x Level)% (up to 50%) + **/ + if( sc->data[SC_SECRAMENT] ) + fixed -= fixed * sc->data[SC_SECRAMENT]->val2 / 100; +#endif + } +#if RECASTING + /** + * WL_RADIUS decreases 10/15/20% fixed cast time from warlock skills + **/ + if( bl->type == BL_PC && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP && ( skill_lv = pc_checkskill((TBL_PC*)bl, WL_RADIUS) ) ) + fixed -= fixed * (5+(skill_lv*5)) / 100; + return (time > 0 || fixed > 0) ? cap_value( time , fixed , INT_MAX ) : 0; +#else + return (time > 0) ? time : 0; +#endif +} + +/*========================================== * Does delay reductions based on dex/agi, sc data, item bonuses, ... *------------------------------------------*/ int skill_delayfix (struct block_list *bl, int skill_id, int skill_lv) @@ -9657,6 +10927,23 @@ int skill_delayfix (struct block_list *bl, int skill_id, int skill_lv) if( sc && !sc->data[SC_BASILICA] ) time = 0; // There is no Delay on Basilica creation, only on cancel break; + default: + if (battle_config.delay_dependon_dex && !(delaynodex&1)) + { // if skill delay is allowed to be reduced by dex + int scale = battle_config.castrate_dex_scale - status_get_dex(bl); + if (scale > 0) + time = time * scale / battle_config.castrate_dex_scale; + else //To be capped later to minimum. + time = 0; + } + if (battle_config.delay_dependon_agi && !(delaynodex&1)) + { // if skill delay is allowed to be reduced by agi + int scale = battle_config.castrate_dex_scale - status_get_agi(bl); + if (scale > 0) + time = time * scale / battle_config.castrate_dex_scale; + else //To be capped later to minimum. + time = 0; + } } if ( sc && sc->data[SC_SPIRIT] ) @@ -10162,8 +11449,8 @@ int skill_frostjoke_scream (struct block_list *bl, va_list ap) return 0; if (bl->type == BL_PC) { struct map_session_data *sd = (struct map_session_data *)bl; - if (sd && sd->sc.option&OPTION_INVISIBLE) - return 0; + if ( sd && sd->sc.option&(OPTION_INVISIBLE|OPTION_MADOGEAR) ) + return 0;//Frost Joke / Scream cannot target invisible or MADO Gear characters [Ind] } //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex] if(battle_check_target(src,bl,BCT_ENEMY) > 0) @@ -10321,6 +11608,48 @@ int skill_greed (struct block_list *bl, va_list ap) return 0; } +//For Ranger's Detonator [Jobbie/3CeAM] +int skill_detonator(struct block_list *bl, va_list ap) +{ + struct skill_unit *unit=NULL; + struct block_list *src; + int unit_id; + + nullpo_ret(bl); + nullpo_ret(ap); + src = va_arg(ap,struct block_list *); + + if( bl->type != BL_SKILL || (unit = (struct skill_unit *)bl) == NULL || !unit->group ) + return 0; + if( unit->group->src_id != src->id ) + return 0; + + unit_id = unit->group->unit_id; + switch( unit_id ) + { //List of Hunter and Ranger Traps that can be detonate. + case UNT_BLASTMINE: + case UNT_SANDMAN: + case UNT_CLAYMORETRAP: + case UNT_TALKIEBOX: + case UNT_CLUSTERBOMB: + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: + if( unit_id == UNT_TALKIEBOX ) + { + clif_talkiebox(bl,unit->group->valstr); + unit->group->val2 = -1; + } + else + map_foreachinrange(skill_trap_splash,bl,skill_get_splash(unit->group->skill_id,unit->group->skill_lv),unit->group->bl_flag,bl,unit->group->tick); + + clif_changetraplook(bl,unit_id == UNT_FIRINGTRAP ? UNT_DUMMYSKILL : UNT_USED_TRAPS); + unit->group->unit_id = UNT_USED_TRAPS; + unit->range = -1; + unit->group->limit = DIFF_TICK(gettick(),unit->group->tick) + (unit_id == UNT_TALKIEBOX ? 5000 : 1500); + break; + } + return 0; +} /*========================================== * @@ -10487,6 +11816,15 @@ static int skill_trap_splash (struct block_list *bl, va_list ap) if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) skill_blown(src,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),-1,0); break; + case UNT_ELECTRICSHOCKER: + clif_skill_damage(src,bl,tick,0,0,-30000,1,sg->skill_id,sg->skill_lv,5); + break; + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: + case UNT_CLUSTERBOMB: + if(skill_attack(BF_MISC,ss,bl,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) + clif_skill_damage(bl,bl,tick,0,0,-30000,1,sg->skill_id,sg->skill_lv,5); + break; default: skill_attack(skill_get_type(sg->skill_id),ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); break; @@ -10554,6 +11892,37 @@ bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce return wall; } +bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce) +{ + static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; + static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; + bool wall = true; + int i; + + if( bl->type == BL_PC ) + { //Check for walls. + ARR_FIND( 0, 8, i, map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 ); + if( i == 8 ) + wall = false; + } + + if( sce ) + { + if( !wall ) + { + if( sce->val1 < 3 ) //End camouflage. + status_change_end(bl, SC_CAMOUFLAGE, -1); + else + if( sce->val3&1 ) + { //Remove wall bonus + sce->val3&=~1; + status_calc_bl(bl,SCB_SPEED); + } + } + } + + return wall; +} /*========================================== * @@ -10595,6 +11964,16 @@ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int case HP_BASILICA: skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,true); break; + /** + * Ranger + **/ + case RA_ELECTRICSHOCKER: + { + struct block_list* target = map_id2bl(group->val2); + if( target ) + status_change_end(target, SC_ELECTRICSHOCKER, -1); + } + break; default: if (group->state.song_dance&0x1) //Check for dissonance. skill_dance_overlap(unit, 1); @@ -10793,14 +12172,36 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li } } - if (group->skill_id == SG_SUN_WARM || - group->skill_id == SG_MOON_WARM || - group->skill_id == SG_STAR_WARM) { - struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_WARM]) { - sc->data[SC_WARM]->val4 = 0; - status_change_end(src, SC_WARM, INVALID_TIMER); - } + switch( group->skill_id ) { + case SG_SUN_WARM: + case SG_MOON_WARM: + case SG_STAR_WARM: + { + struct status_change *sc = NULL; + if( (sc = status_get_sc(src)) != NULL && sc->data[SC_WARM] ) { + sc->data[SC_WARM]->val4 = 0; + status_change_end(src, SC_WARM, INVALID_TIMER); + } + } + break; + case NC_NEUTRALBARRIER: + { + struct status_change *sc = NULL; + if( (sc = status_get_sc(src)) != NULL && sc->data[SC_NEUTRALBARRIER_MASTER] ) { + sc->data[SC_NEUTRALBARRIER_MASTER]->val2 = 0; + status_change_end(src,SC_NEUTRALBARRIER_MASTER,-1); + } + } + break; + case NC_STEALTHFIELD: + { + struct status_change *sc = NULL; + if( (sc = status_get_sc(src)) != NULL && sc->data[SC_STEALTHFIELD_MASTER] ) { + sc->data[SC_STEALTHFIELD_MASTER]->val2 = 0; + status_change_end(src,SC_STEALTHFIELD_MASTER,-1); + } + } + break; } if (src->type==BL_PC && group->state.ammo_consume) @@ -10969,6 +12370,14 @@ static int skill_unit_timer_sub (DBKey key, void* data, va_list ap) case UNT_FREEZINGTRAP: case UNT_CLAYMORETRAP: case UNT_TALKIEBOX: + case UNT_CLUSTERBOMB: + case UNT_MAGENTATRAP: + case UNT_COBALTTRAP: + case UNT_MAIZETRAP: + case UNT_VERDURETRAP: + case UNT_FIRINGTRAP: + case UNT_ICEBOUNDTRAP: + { struct block_list* src; if( unit->val1 > 0 && (src = map_id2bl(group->src_id)) != NULL && src->type == BL_PC ) @@ -11035,6 +12444,11 @@ static int skill_unit_timer_sub (DBKey key, void* data, va_list ap) case UNT_FREEZINGTRAP: case UNT_TALKIEBOX: case UNT_ANKLESNARE: + /** + * Ranger + **/ + case UNT_ELECTRICSHOCKER: + case UNT_CLUSTERBOMB: if( unit->val1 <= 0 ) { if( group->unit_id == UNT_ANKLESNARE && group->val2 > 0 ) skill_delunit(unit); @@ -11407,6 +12821,9 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in if (!skill_id) //A skill can be specified for some override cases. skill_id = skill_produce_db[idx].req_skill; + if( skill_id == GC_RESEARCHNEWPOISON ) + skill_id = GC_CREATENEWPOISON; + slot[0]=slot1; slot[1]=slot2; slot[2]=slot3; @@ -11428,22 +12845,27 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in ele=ele_table[slot[i]-994]; } } - - if(skill_id == RK_RUNEMASTERY) - { // Now we figure out how many runes we're going to make. :3 - int skill_lv = pc_checkskill(sd,skill_id); - if( skill_lv > 4 && skill_lv < 10) // level 5~9 can make 1~2 runes - qty=(rand()%2)+1; - else if( skill_lv == 10 ) // Level 10 can make 1~3 runes - qty=(rand()%3)+1; - - // Check to see if the amount of runes will exceed 20. - i = pc_search_inventory(sd,nameid); - if( i >= 0 && sd->status.inventory[i].amount+qty > 20 ) // Cancel creation if created stones will exceed 20. - { - clif_msg(sd,0x61b); - return 0; + if( skill_id == RK_RUNEMASTERY ) { + int temp_qty, skill_lv = pc_checkskill(sd,skill_id); + if( skill_lv == 10 ) temp_qty = 1 + rand()%3; + else if( skill_lv > 5 ) temp_qty = 1 + rand()%2; + else temp_qty = 1; + for( i = 0; i < MAX_INVENTORY; i++ ) { + if( sd->status.inventory[i].nameid == nameid ) { + if( sd->status.inventory[i].amount >= MAX_RUNE ) { + clif_msgtable(sd->fd,0x61b); + return 0; + } else { + /** + * the amount fits, say we got temp_qty 4 and 19 runes, we trim temp_qty to 1. + **/ + if( temp_qty + sd->status.inventory[i].amount >= MAX_RUNE ) + temp_qty = MAX_RUNE - sd->status.inventory[i].amount; + } + break; + } } + qty = temp_qty; } for(i=0;i<MAX_PRODUCE_RESOURCE;i++){ @@ -11451,10 +12873,7 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in if( (id=skill_produce_db[idx].mat_id[i]) <= 0 ) continue; num++; - if (skill_id == RK_RUNEMASTERY) - x=skill_produce_db[idx].mat_amount[i]; // RK_RUNEMASTERY only uses one set of required items, even if making more than 1 item - else - x=qty*skill_produce_db[idx].mat_amount[i]; + x=( skill_id == RK_RUNEMASTERY ? 1 : qty)*skill_produce_db[idx].mat_amount[i]; do{ int y=0; j = pc_search_inventory(sd,id); @@ -11499,6 +12918,10 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in make_per = (2000 + 40*status->dex + 20*status->luk); break; case AL_HOLYWATER: + /** + * Arch Bishop + **/ + case AB_ANCILLA: make_per = 100000; //100% success break; case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG] @@ -11547,57 +12970,21 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in make_per = make_per * battle_config.pp_rate / 100; break; case SA_CREATECON: // Elemental Converter Creation - case AB_ANCILLA: // Ancilla Creation make_per = 100000; // should be 100% success rate break; - case RK_RUNEMASTERY: //Information from iROWiki and RuneItemInfo.lua - { - int skill_lv = pc_checkskill(sd,skill_id); - - make_per = 5100 + 20 * skill_lv; // Base chance. - - //Take stats into account before applying non-modified values. - make_per += (status->dex / 30 + status->luk / 10) + sd->status.job_level / 10 * 100; - - switch(sd->produce_itemusedid) - { // Add success rate based on what type of stone is used. - case 12737: - make_per += 200; break; - case 12734: - make_per += 500; break; - case 12738: - make_per += 800; break; - case 12735: - make_per += 1100; break; - case 12736: - make_per += 1400; break; - default: - break; - } - sd->produce_itemusedid = 0; - - switch(nameid) - { // Reduce success rate based on what rank stone we're making. - case 12727: // Runstone_Verkana - make_per -= 2000; // S Class - break; - case 12725: // Runstone_Nosiege - case 12730: // Runstone_Urj - make_per -= 1500; // A Rank - break; - case 12728: // Runstone_Isia - case 12732: // Runstone_Pertz - make_per -= 1000; // B Rank - break; - case 12726: // Runstone_Rhydo - case 12729: // Runstone_Asir - case 12731: // Runstone_Turisus - case 12733: // Runstone_Hagalas - make_per -= 500; // C Rank - break; - } + /** + * Rune Knight + **/ + case RK_RUNEMASTERY: + make_per = 5 * (sd->itemid + pc_checkskill(sd,skill_id)) * 100; + break; + /** + * Guilotine Cross + **/ + case GC_CREATENEWPOISON: + make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON); + qty = 1+rand()%pc_checkskill(sd,GC_RESEARCHNEWPOISON); break; - } default: if (sd->menuskill_id == AM_PHARMACY && sd->menuskill_val > 10 && sd->menuskill_val <= 20) @@ -11636,6 +13023,7 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in if(make_per < 1) make_per = 1; + if(rand()%10000 < make_per || qty > 1){ //Success, or crafting multiple items. struct item tmp_item; memset(&tmp_item,0,sizeof(tmp_item)); @@ -11666,6 +13054,10 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in flag = battle_config.produce_item_name_input&0x2; break; case AL_HOLYWATER: + /** + * Arch Bishop + **/ + case AB_ANCILLA: flag = battle_config.produce_item_name_input&0x8; break; case ASC_CDP: @@ -11735,7 +13127,6 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in case AM_TWILIGHT2: case AM_TWILIGHT3: case ASC_CDP: - case RK_RUNEMASTERY: clif_produceeffect(sd,2,nameid); clif_misceffect(&sd->bl,5); break; @@ -11745,6 +13136,11 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in clif_produceeffect(sd,0,nameid); clif_misceffect(&sd->bl,3); break; + case RK_RUNEMASTERY: + case GC_CREATENEWPOISON: + clif_produceeffect(sd,2,nameid); + clif_misceffect(&sd->bl,5); + break; default: //Those that don't require a skill? if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20) { //Cooking items. @@ -11790,6 +13186,7 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in clif_misceffect(&sd->bl,2); break; case RK_RUNEMASTERY: + case GC_CREATENEWPOISON: clif_produceeffect(sd,3,nameid); clif_misceffect(&sd->bl,6); break; @@ -11846,6 +13243,125 @@ int skill_arrow_create (struct map_session_data *sd, int nameid) return 0; } +int skill_poisoningweapon( struct map_session_data *sd, int nameid) { + sc_type type; + int t_lv = 0, chance, i; + nullpo_ret(sd); + if( nameid <= 0 || (i = pc_search_inventory(sd,nameid)) < 0 || pc_delitem(sd,i,1,0,0) ) { + clif_skill_fail(sd,GC_POISONINGWEAPON,0,0); + return 0; + } + switch( nameid ) + { // t_lv used to take duration from skill_get_time2 + case PO_PARALYSE: type = SC_PARALYSE; t_lv = 1; break; + case PO_PYREXIA: type = SC_PYREXIA; t_lv = 2; break; + case PO_DEATHHURT: type = SC_DEATHHURT; t_lv = 3; break; + case PO_LEECHESEND: type = SC_LEECHESEND; t_lv = 4; break; + case PO_VENOMBLEED: type = SC_VENOMBLEED; t_lv = 6; break; + case PO_TOXIN: type = SC_TOXIN; t_lv = 7; break; + case PO_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; t_lv = 8; break; + case PO_OBLIVIONCURSE: type = SC_OBLIVIONCURSE; t_lv = 9; break; + default: + clif_skill_fail(sd,GC_POISONINGWEAPON,0,0); + return 0; + } + + chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv + sc_start4(&sd->bl,SC_POISONINGWEAPON,100,t_lv,type,chance,0,skill_get_time(GC_POISONINGWEAPON,sd->menuskill_val)); + + return 0; +} +int skill_magicdecoy(struct map_session_data *sd, int nameid) { + int x, y, i, class_, skill; + struct mob_data *md; + nullpo_ret(sd); + skill = sd->menuskill_val; + + if( nameid <= 0 || !itemdb_is_element(nameid) || (i = pc_search_inventory(sd,nameid)) < 0 || !skill || pc_delitem(sd,i,1,0,0) ) + { + clif_skill_fail(sd,NC_MAGICDECOY,0,0); + return 0; + } + + // Spawn Position + pc_delitem(sd,i,1,0,0); + x = sd->sc.comet_x; + y = sd->sc.comet_y; + sd->sc.comet_x = sd->sc.comet_y = 0; + sd->menuskill_val = 0; + + class_ = (nameid == 990 || nameid == 991) ? 2043 + nameid - 990 : (nameid == 992) ? 2046 : 2045; + + + md = mob_once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, ""); + if( md ) { + md->master_id = sd->bl.id; + md->special_state.ai = 3; + if( md->deletetimer != INVALID_TIMER ) + delete_timer(md->deletetimer, mob_timer_delete); + md->deletetimer = add_timer (gettick() + skill_get_time(NC_MAGICDECOY,skill), mob_timer_delete, md->bl.id, 0); + mob_spawn(md); + md->status.matk_min = md->status.matk_max = 250 + (50 * skill); + } + + return 0; +} +// Warlock Spellbooks. [LimitLine/3CeAM] +int skill_spellbook (struct map_session_data *sd, int nameid) { + int i, j, points, skillid, preserved = 0, max_preserve; + nullpo_ret(sd); + + if( sd->sc.data[SC_STOP] ) status_change_end(&sd->bl,SC_STOP,-1); + if( nameid <= 0 ) return 0; + + if( pc_search_inventory(sd,nameid) < 0 ) + { // User with no item on inventory + clif_skill_fail(sd,WL_READING_SB,0x04,0); + return 0; + } + + ARR_FIND(0,MAX_SPELLBOOK,j,sd->rsb[j].skillid == 0); // Search for a free slot + if( j == MAX_SPELLBOOK ) + { // No more free slots + clif_skill_fail(sd,WL_READING_SB,0x35,0); + return 0; + } + + ARR_FIND(0,MAX_SKILL_SPELLBOOK_DB,i,skill_spellbook_db[i].nameid == nameid); // Search for information of this item + if( i == MAX_SKILL_SPELLBOOK_DB ) + { // Fake nameid + clif_skill_fail(sd,WL_READING_SB,0x04,0); + return 0; + } + + skillid = skill_spellbook_db[i].skillid; + points = skill_spellbook_db[i].points; + + if( !pc_checkskill(sd,skillid) ) + { // User don't know the skill + sc_start(&sd->bl,SC_SLEEP,100,1,skill_get_time(WL_READING_SB,pc_checkskill(sd,WL_READING_SB))); + clif_skill_fail(sd,WL_READING_SB,0x34,0); + return 0; + } + + max_preserve = 4 * pc_checkskill(sd,WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10; + for( i = 0; i < MAX_SPELLBOOK && sd->rsb[i].skillid; i++ ) + preserved += sd->rsb[i].points; + + if( preserved + points >= max_preserve ) + { // No more free points + clif_skill_fail(sd,WL_READING_SB,0x04,0); + return 0; + } + + sd->rsb[j].skillid = skillid; + sd->rsb[j].level = pc_checkskill(sd,skillid); + sd->rsb[j].points = points; + sc_start2(&sd->bl,SC_READING_SB,100,0,preserved+points,-1); + + return 1; +} + /*========================================== * @@ -12023,6 +13539,7 @@ void skill_init_unit_layout (void) switch (i) { case MG_FIREWALL: case WZ_ICEWALL: + case WL_EARTHSTRAIN://Warlock // these will be handled later break; case PR_SANCTUARY: @@ -12229,6 +13746,90 @@ void skill_init_unit_layout (void) } pos++; } + earthstrain_unit_pos = pos; + for( i = 0; i < 8; i++ ) + { // For each Direction + skill_unit_layout[pos].count = 3; // Temp code being used as the official method makes too much noise in game. [Rytech] + //skill_unit_layout[pos].count = 15; // This line is here to replace the above one once gravity changes the animation. + switch( i ) + { + case 0: case 1: case 3: case 4: case 5: case 7: + { + int dx[] = {-5, 0, 5}; + int dy[] = { 0, 0, 0}; + //int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}; // Leave this here for future use. + //int dy[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + break; + case 2: + case 6: + { + int dx[] = { 0, 0, 0}; + int dy[] = {-5, 0, 5}; + //int dx[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Leave this here for future use. + //int dy[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + break; + } + pos++; + } + +} +// Stasis skill usage check. [LimitLine/3CeAM] +int skill_stasis_check(struct block_list *bl, int src_id, int skillid) +{ + int inf = 0; + if( !bl || skillid < 1 ) + return 0; // Can do it + inf = skill_get_inf2(skillid); + if( inf == INF2_SONG_DANCE || /*skill_get_inf2(skillid) == INF2_CHORUS_SKILL ||*/ inf == INF2_SPIRIT_SKILL ) + return 1; // Can't do it. + + switch( skillid ) + { + case NV_FIRSTAID: case TF_HIDING: case AS_CLOAKING: case WZ_SIGHTRASHER: + case RG_STRIPWEAPON: case RG_STRIPSHIELD: case RG_STRIPARMOR: case WZ_METEOR: + case RG_STRIPHELM: case SC_STRIPACCESSARY: case ST_FULLSTRIP: case WZ_SIGHTBLASTER: + case ST_CHASEWALK: case SC_ENERVATION: case SC_GROOMY: case WZ_ICEWALL: + case SC_IGNORANCE: case SC_LAZINESS: case SC_UNLUCKY: case WZ_STORMGUST: + case SC_WEAKNESS: case AL_RUWACH: case AL_PNEUMA: case WZ_JUPITEL: + case AL_HEAL: case AL_BLESSING: case AL_INCAGI: case WZ_VERMILION: + case AL_TELEPORT: case AL_WARP: case AL_HOLYWATER: case WZ_EARTHSPIKE: + case AL_HOLYLIGHT: case PR_IMPOSITIO: case PR_ASPERSIO: case WZ_HEAVENDRIVE: + case PR_SANCTUARY: case PR_STRECOVERY: case PR_MAGNIFICAT: case WZ_QUAGMIRE: + case ALL_RESURRECTION: case PR_LEXDIVINA: case PR_LEXAETERNA: case HW_GRAVITATION: + case PR_MAGNUS: case PR_TURNUNDEAD: case MG_SRECOVERY: case HW_MAGICPOWER: + case MG_SIGHT: case MG_NAPALMBEAT: case MG_SAFETYWALL: case HW_GANBANTEIN: + case MG_SOULSTRIKE: case MG_COLDBOLT: case MG_FROSTDIVER: case WL_DRAINLIFE: + case MG_STONECURSE: case MG_FIREBALL: case MG_FIREWALL: case WL_SOULEXPANSION: + case MG_FIREBOLT: case MG_LIGHTNINGBOLT: case MG_THUNDERSTORM: case MG_ENERGYCOAT: + case WL_WHITEIMPRISON: case WL_SUMMONFB: case WL_SUMMONBL: case WL_SUMMONWB: + case WL_SUMMONSTONE: case WL_SIENNAEXECRATE: case WL_RELEASE: case WL_EARTHSTRAIN: + case WL_RECOGNIZEDSPELL: case WL_READING_SB: case SA_MAGICROD: case SA_SPELLBREAKER: + case SA_DISPELL: case SA_FLAMELAUNCHER: case SA_FROSTWEAPON: case SA_LIGHTNINGLOADER: + case SA_SEISMICWEAPON: case SA_VOLCANO: case SA_DELUGE: case SA_VIOLENTGALE: + case SA_LANDPROTECTOR: case PF_HPCONVERSION: case PF_SOULCHANGE: case PF_SPIDERWEB: + case PF_FOGWALL: case TK_RUN: case TK_HIGHJUMP: case TK_SEVENWIND: + case SL_KAAHI: case SL_KAUPE: case SL_KAITE: +#if FIREIVY_ON + case WZ_FIREIVY: +#endif + // Skills that need to be confirmed. + case SO_FIREWALK: case SO_ELECTRICWALK: case SO_SPELLFIST: case SO_EARTHGRAVE: + case SO_DIAMONDDUST: case SO_POISON_BUSTER: case SO_PSYCHIC_WAVE: case SO_CLOUD_KILL: + case SO_STRIKING: case SO_WARMER: case SO_VACUUM_EXTREME: case SO_VARETYR_SPEAR: + case SO_ARRULLO: + return 1; // Can't do it. + + default: + return 0; // Can do it. + } + + return 0; // Can Cast anything else like Weapon Skills } /*========================================== @@ -12259,7 +13860,10 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current) i = skill_get_index(id); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].range); skill_db[i].hit = atoi(split[2]); skill_db[i].inf = atoi(split[3]); @@ -12301,7 +13905,10 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].hp); skill_split_atoi(split[2],skill_db[i].mhp); skill_split_atoi(split[3],skill_db[i].sp); @@ -12359,9 +13966,17 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[i].state = ST_RECOV_WEIGHT_RATE; else if( strcmpi(split[10],"move_enable")==0 ) skill_db[i].state = ST_MOVE_ENABLE; else if( strcmpi(split[10],"water")==0 ) skill_db[i].state = ST_WATER; - else if( strcmpi(split[10],"dragon")==0 ) skill_db[i].state = ST_DRAGON; - else if( strcmpi(split[10],"warg")==0 ) skill_db[i].state = ST_WARG; - else if( strcmpi(split[10],"madogear")==0 ) skill_db[i].state = ST_MADOGEAR; + /** + * New States + **/ + else if( strcmpi(split[10],"dragon")==0 ) skill_db[i].state = ST_RIDINGDRAGON; + else if( strcmpi(split[10],"warg")==0 ) skill_db[i].state = ST_WUG; + else if( strcmpi(split[10],"ridingwarg")==0 ) skill_db[i].state = ST_RIDINGWUG; + else if( strcmpi(split[10],"mado")==0 ) skill_db[i].state = ST_MADO; + else if( strcmpi(split[10],"elementalspirit")==0 ) skill_db[i].state = ST_ELEMENTALSPIRIT; + /** + * Unknown or no state + **/ else skill_db[i].state = ST_NONE; skill_split_atoi(split[11],skill_db[i].spiritball); @@ -12374,20 +13989,21 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) } static bool skill_parse_row_castdb(char* split[], int columns, int current) -{// SkillID,CastingTime,FixedCastingTime,AfterCastActDelay,Cooldown,AfterCastWalkDelay,Duration1,Duration2 +{// SkillID,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2 int i = atoi(split[0]); i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].cast); - skill_split_atoi(split[2],skill_db[i].fixedcast); - skill_split_atoi(split[3],skill_db[i].delay); - skill_split_atoi(split[4],skill_db[i].cooldown); - skill_split_atoi(split[5],skill_db[i].walkdelay); - skill_split_atoi(split[6],skill_db[i].upkeep_time); - skill_split_atoi(split[7],skill_db[i].upkeep_time2); - + skill_split_atoi(split[2],skill_db[i].delay); + skill_split_atoi(split[3],skill_db[i].walkdelay); + skill_split_atoi(split[4],skill_db[i].upkeep_time); + skill_split_atoi(split[5],skill_db[i].upkeep_time2); + skill_split_atoi(split[6],skill_db[i].cooldown); return true; } @@ -12397,7 +14013,10 @@ static bool skill_parse_row_castnodexdb(char* split[], int columns, int current) i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_split_atoi(split[1],skill_db[i].castnodex); if( split[2] ) // optional column skill_split_atoi(split[2],skill_db[i].delaynodex); @@ -12411,7 +14030,10 @@ static bool skill_parse_row_nocastdb(char* split[], int columns, int current) i = skill_get_index(i); if( !i ) // invalid skill id return false; - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_db[i].nocast |= atoi(split[1]); return true; @@ -12497,6 +14119,50 @@ static bool skill_parse_row_createarrowdb(char* split[], int columns, int curren return true; } +static bool skill_parse_row_spellbookdb(char* split[], int columns, int current) +{// SkillID,PreservePoints + + int skillid = atoi(split[0]), + points = atoi(split[1]), + nameid = atoi(split[2]); + + if( !skill_get_index(skillid) || !skill_get_max(skillid) ) + ShowError("spellbook_db: Invalid skill ID %d\n", skillid); + if ( !skill_get_inf(skillid) ) + ShowError("spellbook_db: Passive skills cannot be memorized (%d/%s)\n", skillid, skill_get_name(skillid)); + if( points < 1 ) + ShowError("spellbook_db: PreservePoints have to be 1 or above! (%d/%s)\n", skillid, skill_get_name(skillid)); + else + { + skill_spellbook_db[current].skillid = skillid; + skill_spellbook_db[current].points = points; + skill_spellbook_db[current].nameid = nameid; + + return true; + } + + return false; +} +static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current) +{ + int i = atoi(split[0]); + + if( !skill_get_index(i) || !skill_get_max(i) ) + { + ShowError("magicmushroom_db: Invalid skill ID %d\n", i); + return false; + } + if ( !skill_get_inf(i) ) + { + ShowError("magicmushroom_db: Passive skills cannot be casted (%d/%s)\n", i, skill_get_name(i)); + return false; + } + + skill_magicmushroom_db[current].skillid = i; + + return true; +} + static bool skill_parse_row_abradb(char* split[], int columns, int current) {// SkillID,DummyName,RequiredHocusPocusLevel,Rate @@ -12511,7 +14177,10 @@ static bool skill_parse_row_abradb(char* split[], int columns, int current) ShowError("abra_db: Passive skills cannot be casted (%d/%s)\n", i, skill_get_name(i)); return false; } - +#if FIREIVY_ON == 0 + if( i == WZ_FIREIVY ) //Disabled + return true; +#endif skill_abra_db[current].skillid = i; skill_abra_db[current].req_lv = atoi(split[2]); skill_abra_db[current].per = atoi(split[3]); @@ -12527,13 +14196,14 @@ static void skill_readdb(void) memset(skill_produce_db,0,sizeof(skill_produce_db)); memset(skill_arrow_db,0,sizeof(skill_arrow_db)); memset(skill_abra_db,0,sizeof(skill_abra_db)); - + memset(skill_spellbook_db,0,sizeof(skill_spellbook_db)); + memset(skill_magicmushroom_db,0,sizeof(skill_magicmushroom_db)); // load skill databases safestrncpy(skill_db[0].name, "UNKNOWN_SKILL", sizeof(skill_db[0].name)); safestrncpy(skill_db[0].desc, "Unknown Skill", sizeof(skill_db[0].desc)); sv_readdb(db_path, "skill_db.txt" , ',', 17, 17, MAX_SKILL_DB, skill_parse_row_skilldb); sv_readdb(db_path, "skill_require_db.txt" , ',', 32, 32, MAX_SKILL_DB, skill_parse_row_requiredb); - sv_readdb(db_path, "skill_cast_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_castdb); + sv_readdb(db_path, "skill_cast_db.txt" , ',', 7, 7, MAX_SKILL_DB, skill_parse_row_castdb); sv_readdb(db_path, "skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill_parse_row_castnodexdb); sv_readdb(db_path, "skill_nocast_db.txt" , ',', 2, 2, MAX_SKILL_DB, skill_parse_row_nocastdb); sv_readdb(db_path, "skill_unit_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_unitdb); @@ -12541,6 +14211,11 @@ static void skill_readdb(void) sv_readdb(db_path, "produce_db.txt" , ',', 4, 4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill_parse_row_producedb); sv_readdb(db_path, "create_arrow_db.txt" , ',', 1+2, 1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill_parse_row_createarrowdb); sv_readdb(db_path, "abra_db.txt" , ',', 4, 4, MAX_SKILL_ABRA_DB, skill_parse_row_abradb); + //Warlock + sv_readdb(db_path, "spellbook_db.txt" , ',', 3, 3, MAX_SKILL_SPELLBOOK_DB, skill_parse_row_spellbookdb); + //Guillotine Cross + sv_readdb(db_path, "magicmushroom_db.txt" , ',', 1, 1, MAX_SKILL_MAGICMUSHROOM_DB, skill_parse_row_magicmushroomdb); + } void skill_reload (void) diff --git a/src/map/skill.h b/src/map/skill.h index 0cadd92d7..97bd54252 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -90,8 +90,8 @@ struct s_skill_db { char desc[40]; int range[MAX_SKILL_LEVEL],hit,inf,element[MAX_SKILL_LEVEL],nk,splash[MAX_SKILL_LEVEL],max; int num[MAX_SKILL_LEVEL]; - int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL],fixedcast[MAX_SKILL_LEVEL],cooldown[MAX_SKILL_LEVEL]; - int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL]; + int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL]; + int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL],cooldown[MAX_SKILL_LEVEL]; int castcancel,cast_def_rate; int inf2,maxcount[MAX_SKILL_LEVEL],skill_type; int blewcount[MAX_SKILL_LEVEL]; @@ -239,9 +239,7 @@ int skill_get_state(int id); int skill_get_zeny( int id ,int lv ); int skill_get_num( int id ,int lv ); int skill_get_cast( int id ,int lv ); -int skill_get_fixedcast( int id ,int lv ); int skill_get_delay( int id ,int lv ); -int skill_get_cooldown( int id ,int lv ); int skill_get_walkdelay( int id ,int lv ); int skill_get_time( int id ,int lv ); int skill_get_time2( int id ,int lv ); @@ -292,6 +290,7 @@ int skill_clear_group(struct block_list *bl, int flag); int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,int damage,unsigned int tick); int skill_castfix( struct block_list *bl, int skill_id, int skill_lv); +int skill_castfix_sc( struct block_list *bl, int time, int skill_id, int skill_lv); int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv); // Skill conditions check and remove [Inkfish] @@ -369,9 +368,14 @@ enum { ST_RECOV_WEIGHT_RATE, ST_MOVE_ENABLE, ST_WATER, - ST_DRAGON, - ST_WARG, - ST_MADOGEAR + /** + * 3rd States + **/ + ST_RIDINGDRAGON, + ST_WUG, + ST_RIDINGWUG, + ST_MADO, + ST_ELEMENTALSPIRIT, }; enum e_skill { @@ -1314,7 +1318,7 @@ enum e_skill { GN_S_PHARMACY, GN_SLINGITEM_RANGEMELEEATK, - AB_SECRAMENT = 2515, + AB_SECRAMENT=2515, WM_SEVERE_RAINSTORM_MELEE, SR_HOWLINGOFLION, SR_RIDEINLIGHTNING, @@ -1505,7 +1509,7 @@ enum { UNT_EVILLAND, UNT_DARK_RUNNER, //TODO UNT_DARK_TRANSFER, //TODO - UNT_EPICLESIS, + UNT_EPICLESIS, //TODO UNT_EARTHSTRAIN, //TODO UNT_MANHOLE, //TODO UNT_DIMENSIONDOOR, //TODO @@ -1551,5 +1555,46 @@ enum { UNT_MAX = 0x190 }; - +/** + * Warlock + **/ +#define MAX_SKILL_SPELLBOOK_DB 17 +enum wl_spheres { + WLS_FIRE = 0x44, + WLS_WIND, + WLS_WATER, + WLS_STONE, +}; +int skill_spellbook (struct map_session_data *sd, int nameid); +/** + * Guilottine Cross + **/ +#define MAX_SKILL_MAGICMUSHROOM_DB 22 +struct s_skill_magicmushroom_db { + int skillid; +}; +extern struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB]; +/** + * Ranger + **/ +int skill_detonator(struct block_list *bl, va_list ap); +bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce); +/** + * Mechanic + **/ +int skill_magicdecoy(struct map_session_data *sd, int nameid); +/** + * Guiltoine Cross + **/ +int skill_poisoningweapon( struct map_session_data *sd, int nameid); +enum gx_poison { + PO_PARALYSE = 12717, + PO_LEECHESEND, + PO_OBLIVIONCURSE, + PO_DEATHHURT, + PO_TOXIN, + PO_PYREXIA, + PO_MAGICMUSHROOM, + PO_VENOMBLEED +}; #endif /* _SKILL_H_ */ diff --git a/src/map/status.c b/src/map/status.c index f704ee0d6..dd528660b 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -35,7 +35,7 @@ #include <stdlib.h> #include <memory.h> #include <string.h> -#include <math.h> + //Regen related flags. enum e_regen @@ -52,12 +52,17 @@ static int hp_coefficient2[CLASS_COUNT]; static int hp_sigma_val[CLASS_COUNT][MAX_LEVEL+1]; static int sp_coefficient[CLASS_COUNT]; static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE]; //[blackhole89] -static int shield_aspd_base[CLASS_COUNT]; static int refinebonus[MAX_REFINE_BONUS][3]; // 精錬ボーナステーブル(refine_db.txt) int percentrefinery[5][MAX_REFINE+1]; // 精錬成功率(refine_db.txt) static int atkmods[3][MAX_WEAPON_TYPE]; // 武器ATKサイズ修正(size_fix.txt) static char job_bonus[CLASS_COUNT][MAX_LEVEL]; - +#if RRMODE +enum { + SHIELD_ASPD, + RE_JOB_DB_MAX, +} RE_JOB_DB; +static int re_job_db[CLASS_COUNT][RE_JOB_DB_MAX];//[RRInd] +#endif static struct eri *sc_data_ers; //For sc_data entries static struct status_data dummy_status; @@ -400,7 +405,7 @@ void initChangeTables(void) set_sc( CASH_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED ); set_sc( CASH_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE ); - set_sc( ALL_PARTYFLEE , SC_PARTYFLEE , SI_PARTYFLEE , SCB_FLEE ); + //set_sc( ALL_PARTYFLEE , SC_INCFLEE , SI_PARTYFLEE , SCB_NONE ); set_sc( CR_SHRINK , SC_SHRINK , SI_SHRINK , SCB_NONE ); set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE ); @@ -413,30 +418,6 @@ void initChangeTables(void) add_sc( SA_ELEMENTGROUND , SC_ELEMENTALCHANGE ); add_sc( SA_ELEMENTWIND , SC_ELEMENTALCHANGE ); - set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , SI_ENCHANTBLADE , SCB_NONE ); - set_sc( RK_DEATHBOUND , SC_DEATHBOUND , SI_DEATHBOUND , SCB_NONE ); - set_sc( RK_MILLENNIUMSHIELD , SC_MILLENNIUMSHIELD , SI_BLANK , SCB_NONE ); - set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , SI_CRUSHSTRIKE , SCB_NONE ); - set_sc( RK_REFRESH , SC_REFRESH , SI_REFRESH , SCB_NONE ); - set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , SI_GIANTGROWTH , SCB_STR ); - set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , SI_STONEHARDSKIN , SCB_DEF2|SCB_MDEF2 ); - set_sc( RK_VITALITYACTIVATION, SC_VITALITYACTIVATION , SI_VITALITYACTIVATION , SCB_REGEN ); - set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , SI_FIGHTINGSPIRIT , SCB_ASPD ); - set_sc( RK_ABUNDANCE , SC_ABUNDANCE , SI_ABUNDANCE , SCB_NONE ); - - set_sc( AB_ADORAMUS , SC_ADORAMUS , SI_ADORAMUS , SCB_AGI|SCB_SPEED ); - add_sc( AB_CLEMENTIA , SC_BLESSING ); - add_sc( AB_CANTO , SC_INCREASEAGI ); - add_sc( AB_PRAEFATIO , SC_KYRIE ); - set_sc( AB_EPICLESIS , SC_EPICLESIS , SI_EPICLESIS , SCB_MAXHP ); - set_sc( AB_ORATIO , SC_ORATIO , SI_ORATIO , SCB_NONE ); - set_sc( AB_LAUDAAGNUS , SC_LAUDAAGNUS , SI_LAUDAAGNUS , SCB_VIT ); - set_sc( AB_LAUDARAMUS , SC_LAUDARAMUS , SI_LAUDARAMUS , SCB_LUK ); - set_sc( AB_RENOVATIO , SC_RENOVATIO , SI_RENOVATIO , SCB_REGEN ); - set_sc( AB_EXPIATIO , SC_EXPIATIO , SI_EXPIATIO , SCB_NONE ); - set_sc( AB_DUPLELIGHT , SC_DUPLELIGHT , SI_DUPLELIGHT , SCB_NONE ); - set_sc( AB_SECRAMENT , SC_AB_SECRAMENT , SI_AB_SECRAMENT , SCB_NONE ); - set_sc( HLIF_AVOID , SC_AVOID , SI_BLANK , SCB_SPEED ); set_sc( HLIF_CHANGE , SC_CHANGE , SI_BLANK , SCB_VIT|SCB_INT ); set_sc( HFLI_FLEET , SC_FLEET , SI_BLANK , SCB_ASPD|SCB_BATK|SCB_WATK ); @@ -470,6 +451,162 @@ void initChangeTables(void) set_sc( GD_LEADERSHIP , SC_GUILDAURA , SI_BLANK , SCB_STR|SCB_AGI|SCB_VIT|SCB_DEX ); set_sc( GD_BATTLEORDER , SC_BATTLEORDERS , SI_BLANK , SCB_STR|SCB_INT|SCB_DEX ); set_sc( GD_REGENERATION , SC_REGENERATION , SI_BLANK , SCB_REGEN ); + + /** + * Rune Knight + **/ + set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , SI_ENCHANTBLADE , SCB_NONE ); + set_sc( RK_DRAGONHOWLING , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT ); + set_sc( RK_DEATHBOUND , SC_DEATHBOUND , SI_DEATHBOUND , SCB_NONE ); + set_sc( RK_WINDCUTTER , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT ); + add_sc( RK_DRAGONBREATH , SC_BURNING ); + set_sc( RK_MILLENNIUMSHIELD , SC_MILLENNIUMSHIELD , SI_REUSE_MILLENNIUMSHIELD , SCB_NONE ); + set_sc( RK_REFRESH , SC_REFRESH , SI_REFRESH , SCB_NONE ); + set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , SI_GIANTGROWTH , SCB_STR ); + set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , SI_STONEHARDSKIN , SCB_NONE ); + set_sc( RK_VITALITYACTIVATION, SC_VITALITYACTIVATION, SI_VITALITYACTIVATION, SCB_REGEN ); + set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , SI_FIGHTINGSPIRIT , SCB_WATK|SCB_ASPD ); + set_sc( RK_ABUNDANCE , SC_ABUNDANCE , SI_ABUNDANCE , SCB_NONE ); + /** + * GC Guillotine Cross + **/ + set_sc( GC_VENOMIMPRESS , SC_VENOMIMPRESS , SI_VENOMIMPRESS , SCB_NONE ); + set_sc( GC_POISONINGWEAPON , SC_POISONINGWEAPON , SI_POISONINGWEAPON , SCB_NONE ); + set_sc( GC_WEAPONBLOCKING , SC_WEAPONBLOCKING , SI_WEAPONBLOCKING , SCB_NONE ); + set_sc( GC_CLOAKINGEXCEED , SC_CLOAKINGEXCEED , SI_CLOAKINGEXCEED , SCB_SPEED ); + set_sc( GC_HALLUCINATIONWALK , SC_HALLUCINATIONWALK, SI_HALLUCINATIONWALK, SCB_FLEE ); + set_sc( GC_ROLLINGCUTTER , SC_ROLLINGCUTTER , SI_ROLLINGCUTTER , SCB_NONE ); + /** + * Arch Bishop + **/ + set_sc( AB_ADORAMUS , SC_ADORAMUS , SI_ADORAMUS , SCB_AGI|SCB_SPEED ); + add_sc( AB_CLEMENTIA , SC_BLESSING ); + add_sc( AB_CANTO , SC_INCREASEAGI ); + set_sc( AB_EPICLESIS , SC_EPICLESIS , SI_EPICLESIS , SCB_MAXHP ); + add_sc( AB_PRAEFATIO , SC_KYRIE ); + set_sc( AB_ORATIO , SC_ORATIO , SI_ORATIO , SCB_NONE ); + set_sc( AB_LAUDAAGNUS , SC_LAUDAAGNUS , SI_LAUDAAGNUS , SCB_VIT ); + set_sc( AB_LAUDARAMUS , SC_LAUDARAMUS , SI_LAUDARAMUS , SCB_LUK ); + set_sc( AB_RENOVATIO , SC_RENOVATIO , SI_RENOVATIO , SCB_REGEN ); + set_sc( AB_EXPIATIO , SC_EXPIATIO , SI_EXPIATIO , SCB_ATK_ELE ); + set_sc( AB_DUPLELIGHT , SC_DUPLELIGHT , SI_DUPLELIGHT , SCB_NONE ); + set_sc( AB_SECRAMENT , SC_SECRAMENT , SI_SECRAMENT , SCB_NONE ); + /** + * Warlock + **/ + add_sc( WL_WHITEIMPRISON , SC_WHITEIMPRISON ); + set_sc( WL_FROSTMISTY , SC_FREEZING , SI_FROSTMISTY , SCB_ASPD|SCB_SPEED|SCB_DEF|SCB_DEF2 ); + set_sc( WL_MARSHOFABYSS , SC_MARSHOFABYSS , SI_MARSHOFABYSS , SCB_SPEED|SCB_FLEE|SCB_DEF|SCB_MDEF ); + set_sc( WL_RECOGNIZEDSPELL , SC_RECOGNIZEDSPELL , SI_RECOGNIZEDSPELL , SCB_NONE ); + set_sc( WL_STASIS , SC_STASIS , SI_STASIS , SCB_NONE ); + /** + * Ranger + **/ + set_sc( RA_FEARBREEZE , SC_FEARBREEZE , SI_FEARBREEZE , SCB_NONE ); + set_sc( RA_ELECTRICSHOCKER , SC_ELECTRICSHOCKER , SI_ELECTRICSHOCKER , SCB_NONE ); + set_sc( RA_WUGDASH , SC_WUGDASH , SI_WUGDASH , SCB_SPEED ); + set_sc( RA_CAMOUFLAGE , SC_CAMOUFLAGE , SI_CAMOUFLAGE , SCB_CRI|SCB_SPEED ); + add_sc( RA_MAGENTATRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_COBALTTRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_MAIZETRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_VERDURETRAP , SC_ELEMENTALCHANGE ); + add_sc( RA_FIRINGTRAP , SC_BURNING ); + set_sc( RA_ICEBOUNDTRAP , SC_FREEZING , SI_FROSTMISTY , SCB_NONE ); + /** + * Mechanic + **/ + set_sc( NC_ACCELERATION , SC_ACCELERATION , SI_ACCELERATION , SCB_SPEED ); + set_sc( NC_HOVERING , SC_HOVERING , SI_HOVERING , SCB_SPEED ); + set_sc( NC_SHAPESHIFT , SC_SHAPESHIFT , SI_SHAPESHIFT , SCB_DEF_ELE ); + set_sc( NC_INFRAREDSCAN , SC_INFRAREDSCAN , SI_INFRAREDSCAN , SCB_FLEE ); + set_sc( NC_ANALYZE , SC_ANALYZE , SI_ANALYZE , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 ); + set_sc( NC_MAGNETICFIELD , SC_MAGNETICFIELD , SI_MAGNETICFIELD , SCB_NONE ); + set_sc( NC_NEUTRALBARRIER , SC_NEUTRALBARRIER , SI_NEUTRALBARRIER , SCB_NONE ); + set_sc( NC_STEALTHFIELD , SC_STEALTHFIELD , SI_STEALTHFIELD , SCB_NONE ); + ///** + // * Shadow Chaser + // **/ + //set_sc( SC_REPRODUCE , SC__REPRODUCE , SI_REPRODUCE , SCB_NONE ); + //set_sc( SC_AUTOSHADOWSPELL , SC__AUTOSHADOWSPELL, SI_AUTOSHADOWSPELL , SCB_NONE ); + //set_sc( SC_SHADOWFORM , SC__SHADOWFORM , SI_SHADOWFORM , SCB_NONE ); + //set_sc( SC_BODYPAINT , SC__BODYPAINT , SI_BODYPAINTING , SCB_ASPD ); + //set_sc( SC_INVISIBILITY , SC__INVISIBILITY , SI_INVISIBILITY , SCB_ASPD|SCB_CRI|SCB_ATK_ELE ); + //set_sc( SC_DEADLYINFECT , SC__DEADLYINFECT , SI_DEADLYINFECT , SCB_NONE ); + //set_sc( SC_ENERVATION , SC__ENERVATION , SI_ENERVATION , SCB_BATK ); + //set_sc( SC_GROOMY , SC__GROOMY , SI_GROOMY , SCB_ASPD|SCB_HIT|SCB_SPEED ); + //set_sc( SC_IGNORANCE , SC__IGNORANCE , SI_IGNORANCE , SCB_NONE ); + //set_sc( SC_LAZINESS , SC__LAZINESS , SI_LAZINESS , SCB_FLEE ); + //set_sc( SC_UNLUCKY , SC__UNLUCKY , SI_UNLUCKY , SCB_CRI|SCB_FLEE2 ); + //set_sc( SC_WEAKNESS , SC__WEAKNESS , SI_WEAKNESS , SCB_FLEE2|SCB_MAXHP ); + //set_sc( SC_STRIPACCESSARY , SC__STRIPACCESSORY , SI_STRIPACCESSORY , SCB_DEX|SCB_INT|SCB_LUK ); + //set_sc( SC_MANHOLE , SC__MANHOLE , SI_MANHOLE , SCB_NONE ); + //add_sc( SC_CHAOSPANIC , SC_CHAOS ); + //set_sc( SC_BLOODYLUST , SC__BLOODYLUST , SI_BLOODYLUST , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); + ///** + // * Royal Guard + // **/ + //set_sc( LG_REFLECTDAMAGE , SC_REFLECTDAMAGE , SI_LG_REFLECTDAMAGE, SCB_NONE ); + //set_sc( LG_FORCEOFVANGUARD , SC_FORCEOFVANGUARD , SI_FORCEOFVANGUARD , SCB_MAXHP|SCB_DEF ); + //set_sc( LG_EXEEDBREAK , SC_EXEEDBREAK , SI_EXEEDBREAK , SCB_NONE ); + //set_sc( LG_PRESTIGE , SC_PRESTIGE , SI_PRESTIGE , SCB_DEF2 ); + //set_sc( LG_BANDING , SC_BANDING , SI_BANDING , SCB_DEF2|SCB_WATK );// Renewal: atk2 & def2 + //set_sc( LG_PIETY , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE ); + //set_sc( LG_EARTHDRIVE , SC_EARTHDRIVE , SI_EARTHDRIVE , SCB_DEF|SCB_ASPD ); + //set_sc( LG_INSPIRATION , SC_INSPIRATION , SI_INSPIRATION , SCB_MAXHP|SCB_WATK|SCB_HIT|SCB_VIT|SCB_AGI|SCB_STR|SCB_DEX|SCB_INT|SCB_LUK); + ///** + // * Sura + // **/ + //add_sc( SR_DRAGONCOMBO , SC_STUN ); + //add_sc( SR_EARTHSHAKER , SC_STUN ); + //set_sc( SR_CRESCENTELBOW , SC_CRESCENTELBOW , SI_CRESCENTELBOW , SCB_NONE ); + //set_sc( SR_CURSEDCIRCLE , SC_CURSEDCIRCLE_TARGET, SI_CURSEDCIRCLE_TARGET , SCB_NONE ); + //set_sc( SR_LIGHTNINGWALK , SC_LIGHTNINGWALK , SI_LIGHTNINGWALK , SCB_NONE ); + //set_sc( SR_RAISINGDRAGON , SC_RAISINGDRAGON , SI_RAISINGDRAGON , SCB_REGEN|SCB_MAXHP|SCB_MAXSP/*|SCB_ASPD*/ ); + //set_sc( SR_GENTLETOUCH_ENERGYGAIN, SC_GT_ENERGYGAIN , SI_GENTLETOUCH_ENERGYGAIN, SCB_NONE ); + //set_sc( SR_GENTLETOUCH_CHANGE , SC_GT_CHANGE , SI_GENTLETOUCH_CHANGE , SCB_BATK|SCB_ASPD|SCB_DEF|SCB_MDEF ); + //set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GT_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_DEF2|SCB_REGEN|SCB_ASPD|SCB_SPEED ); + ///** + // * Wanderer / Mistrel + // **/ + //set_sc( WA_SWING_DANCE , SC_SWINGDANCE , SI_SWINGDANCE , SCB_SPEED|SCB_ASPD ); + //set_sc( WA_SYMPHONY_OF_LOVER , SC_SYMPHONYOFLOVER , SI_SYMPHONYOFLOVERS , SCB_MDEF ); + //set_sc( WA_MOONLIT_SERENADE , SC_MOONLITSERENADE , SI_MOONLITSERENADE , SCB_MATK ); + //set_sc( MI_RUSH_WINDMILL , SC_RUSHWINDMILL , SI_RUSHWINDMILL , SCB_BATK ); + //set_sc( MI_ECHOSONG , SC_ECHOSONG , SI_ECHOSONG , SCB_DEF2 ); + //set_sc( MI_HARMONIZE , SC_HARMONIZE , SI_HARMONIZE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); + //set_sc( WM_POEMOFNETHERWORLD , SC_STOP , SI_NETHERWORLD , SCB_NONE ); + //set_sc( WM_VOICEOFSIREN , SC_VOICEOFSIREN , SI_VOICEOFSIREN , SCB_NONE ); + //set_sc( WM_LULLABY_DEEPSLEEP , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE ); + //set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , SI_SIRCLEOFNATURE , SCB_NONE ); + //set_sc( WM_GLOOMYDAY , SC_GLOOMYDAY , SI_GLOOMYDAY , SCB_FLEE|SCB_ASPD ); + //set_sc( WM_SONG_OF_MANA , SC_SONGOFMANA , SI_SONGOFMANA , SCB_NONE ); + //set_sc( WM_DANCE_WITH_WUG , SC_DANCEWITHWUG , SI_DANCEWITHWUG , SCB_ASPD ); + //set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAYNIGHTFEVER , SI_SATURDAYNIGHTFEVER , SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN ); + //set_sc( WM_LERADS_DEW , SC_LERADSDEW , SI_LERADSDEW , SCB_MAXHP ); + //set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , SI_MELODYOFSINK , SCB_BATK|SCB_MATK ); + //set_sc( WM_BEYOND_OF_WARCRY , SC_BEYONDOFWARCRY , SI_WARCRYOFBEYOND , SCB_BATK|SCB_MATK ); + //set_sc( WM_UNLIMITED_HUMMING_VOICE, SC_UNLIMITEDHUMMINGVOICE, SI_UNLIMITEDHUMMINGVOICE, SCB_NONE ); + ///** + // * Sorcerer + // **/ + //set_sc( SO_FIREWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE ); + //set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE ); + //set_sc( SO_SPELLFIST , SC_SPELLFIST , SI_SPELLFIST , SCB_NONE ); + //set_sc( SO_CLOUD_KILL , SC_POISON , SI_CLOUDKILL , SCB_NONE ); + //set_sc( SO_STRIKING , SC_STRIKING , SI_STRIKING , SCB_WATK|SCB_CRI ); + //set_sc( SO_WARMER , SC_WARMER , SI_WARMER , SCB_NONE ); + //set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SI_VACUUM_EXTREME , SCB_NONE ); + //set_sc( SO_ARRULLO , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE ); + ///** + // * Genetic + // **/ + //set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SI_CARTSBOOST , SCB_SPEED ); + //set_sc( GN_THORNS_TRAP , SC_THORNSTRAP , SI_THORNTRAP , SCB_NONE ); + //set_sc( GN_BLOOD_SUCKER , SC_BLOODSUCKER , SI_BLOODSUCKER , SCB_NONE ); + //set_sc( GN_WALLOFTHORN , SC_STOP , SI_BLANK , SCB_NONE ); + //set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_SMOKEPOWDER , SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_NONE ); + //set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_TEARGAS , SI_FIRE_EXPANSION_TEAR_GAS , SCB_NONE ); + //set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SI_MANDRAGORA , SCB_INT ); // Storing the target job rather than simply SC_SPIRIT simplifies code later on. SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST, @@ -514,7 +651,6 @@ void initChangeTables(void) StatusIconChangeTable[SC_SPL_DEF] = SI_SPL_DEF; StatusIconChangeTable[SC_MANU_MATK] = SI_MANU_MATK; StatusIconChangeTable[SC_SPL_MATK] = SI_SPL_MATK; - //Cash Items StatusIconChangeTable[SC_FOOD_STR_CASH] = SI_FOOD_STR_CASH; StatusIconChangeTable[SC_FOOD_AGI_CASH] = SI_FOOD_AGI_CASH; @@ -542,6 +678,81 @@ void initChangeTables(void) StatusIconChangeTable[SC_MERC_HPUP] = SI_MERC_HPUP; StatusIconChangeTable[SC_MERC_SPUP] = SI_MERC_SPUP; StatusIconChangeTable[SC_MERC_HITUP] = SI_MERC_HITUP; + // Warlock Spheres + StatusIconChangeTable[SC_SPHERE_1] = SI_SPHERE_1; + StatusIconChangeTable[SC_SPHERE_2] = SI_SPHERE_2; + StatusIconChangeTable[SC_SPHERE_3] = SI_SPHERE_3; + StatusIconChangeTable[SC_SPHERE_4] = SI_SPHERE_4; + StatusIconChangeTable[SC_SPHERE_5] = SI_SPHERE_5; + + StatusIconChangeTable[SC_NEUTRALBARRIER_MASTER] = SI_NEUTRALBARRIER_MASTER; + StatusIconChangeTable[SC_STEALTHFIELD_MASTER] = SI_STEALTHFIELD_MASTER; + StatusIconChangeTable[SC_OVERHEAT] = SI_OVERHEAT; + StatusIconChangeTable[SC_OVERHEAT_LIMITPOINT] = SI_OVERHEAT_LIMITPOINT; + + StatusIconChangeTable[SC_HALLUCINATIONWALK_POSTDELAY] = SI_HALLUCINATIONWALK_POSTDELAY; + StatusIconChangeTable[SC_TOXIN] = SI_TOXIN; + StatusIconChangeTable[SC_PARALYSE] = SI_PARALYSE; + StatusIconChangeTable[SC_VENOMBLEED] = SI_VENOMBLEED; + StatusIconChangeTable[SC_MAGICMUSHROOM] = SI_MAGICMUSHROOM; + StatusIconChangeTable[SC_DEATHHURT] = SI_DEATHHURT; + StatusIconChangeTable[SC_PYREXIA] = SI_PYREXIA; + StatusIconChangeTable[SC_OBLIVIONCURSE] = SI_OBLIVIONCURSE; + StatusIconChangeTable[SC_LEECHESEND] = SI_LEECHESEND; + + StatusIconChangeTable[SC_SHIELDSPELL_DEF] = SI_SHIELDSPELL_DEF; + StatusIconChangeTable[SC_SHIELDSPELL_MDEF] = SI_SHIELDSPELL_MDEF; + StatusIconChangeTable[SC_SHIELDSPELL_REF] = SI_SHIELDSPELL_REF; + StatusIconChangeTable[SC_BANDING_DEFENCE] = SI_BANDING_DEFENCE; + + StatusIconChangeTable[SC_GLOOMYDAY_SK] = SI_GLOOMYDAY; + + StatusIconChangeTable[SC_CURSEDCIRCLE_ATKER] = SI_CURSEDCIRCLE_ATKER; + + StatusIconChangeTable[SC_STOMACHACHE] = SI_STOMACHACHE; + StatusIconChangeTable[SC_MYSTERIOUS_POWDER] = SI_MYSTERIOUS_POWDER; + StatusIconChangeTable[SC_MELON_BOMB] = SI_MELON_BOMB; + StatusIconChangeTable[SC_BANANA_BOMB] = SI_BANANA_BOMB; + StatusIconChangeTable[SC_BANANA_BOMB_SITDOWN] = SI_BANANA_BOMB_SITDOWN_POSTDELAY; + + //Genetics New Food Items Status Icons + StatusIconChangeTable[SC_SAVAGE_STEAK] = SI_SAVAGE_STEAK; + StatusIconChangeTable[SC_COCKTAIL_WARG_BLOOD] = SI_COCKTAIL_WARG_BLOOD; + StatusIconChangeTable[SC_MINOR_BBQ] = SI_MINOR_BBQ; + StatusIconChangeTable[SC_SIROMA_ICE_TEA] = SI_SIROMA_ICE_TEA; + StatusIconChangeTable[SC_DROCERA_HERB_STEAMED] = SI_DROCERA_HERB_STEAMED; + StatusIconChangeTable[SC_PUTTI_TAILS_NOODLES] = SI_PUTTI_TAILS_NOODLES; + + StatusIconChangeTable[SC_BOOST500] |= SI_BOOST500; + StatusIconChangeTable[SC_FULL_SWING_K] |= SI_FULL_SWING_K; + StatusIconChangeTable[SC_MANA_PLUS] |= SI_MANA_PLUS; + StatusIconChangeTable[SC_MUSTLE_M] |= SI_MUSTLE_M; + StatusIconChangeTable[SC_LIFE_FORCE_F] |= SI_LIFE_FORCE_F; + StatusIconChangeTable[SC_EXTRACT_WHITE_POTION_Z] |= SI_EXTRACT_WHITE_POTION_Z; + StatusIconChangeTable[SC_VITATA_500] |= SI_VITATA_500; + StatusIconChangeTable[SC_EXTRACT_SALAMINE_JUICE] |= SI_EXTRACT_SALAMINE_JUICE; + + // Elemental Spirit's 'side' status change icons. + StatusIconChangeTable[SC_CIRCLE_OF_FIRE] = SI_CIRCLE_OF_FIRE; + StatusIconChangeTable[SC_FIRE_CLOAK] = SI_FIRE_CLOAK; + StatusIconChangeTable[SC_WATER_SCREEN] = SI_WATER_SCREEN; + StatusIconChangeTable[SC_WATER_DROP] = SI_WATER_DROP; + StatusIconChangeTable[SC_WIND_STEP] = SI_WIND_STEP; + StatusIconChangeTable[SC_WIND_CURTAIN] = SI_WIND_CURTAIN; + StatusIconChangeTable[SC_SOLID_SKIN] = SI_SOLID_SKIN; + StatusIconChangeTable[SC_STONE_SHIELD] = SI_STONE_SHIELD; + StatusIconChangeTable[SC_PYROTECHNIC] = SI_PYROTECHNIC; + StatusIconChangeTable[SC_HEATER] = SI_HEATER; + StatusIconChangeTable[SC_TROPIC] = SI_TROPIC; + StatusIconChangeTable[SC_AQUAPLAY] = SI_AQUAPLAY; + StatusIconChangeTable[SC_COOLER] = SI_COOLER; + StatusIconChangeTable[SC_CHILLY_AIR] = SI_CHILLY_AIR; + StatusIconChangeTable[SC_GUST] = SI_GUST; + StatusIconChangeTable[SC_BLAST] = SI_BLAST; + StatusIconChangeTable[SC_WILD_STORM] = SI_WILD_STORM; + StatusIconChangeTable[SC_PETROLOGY] = SI_PETROLOGY; + StatusIconChangeTable[SC_CURSED_SOIL] = SI_CURSED_SOIL; + StatusIconChangeTable[SC_UPHEAVAL] = SI_UPHEAVAL; //Other SC which are not necessarily associated to skills. StatusChangeFlagTable[SC_ASPDPOTION0] = SCB_ASPD; @@ -586,8 +797,6 @@ void initChangeTables(void) StatusChangeFlagTable[SC_SPCOST_RATE] |= SCB_ALL; StatusChangeFlagTable[SC_WALKSPEED] |= SCB_SPEED; StatusChangeFlagTable[SC_ITEMSCRIPT] |= SCB_ALL; - StatusChangeFlagTable[SC_FEAR] |= SCB_HIT|SCB_FLEE; - // Cash Items StatusChangeFlagTable[SC_FOOD_STR_CASH] = SCB_STR; StatusChangeFlagTable[SC_FOOD_AGI_CASH] = SCB_AGI; @@ -601,7 +810,12 @@ void initChangeTables(void) StatusChangeFlagTable[SC_MERC_HPUP] |= SCB_MAXHP; StatusChangeFlagTable[SC_MERC_SPUP] |= SCB_MAXSP; StatusChangeFlagTable[SC_MERC_HITUP] |= SCB_HIT; - +#if RE_EDP + /** + * In RE EDP increases your atk and weapon atk + **/ + StatusChangeFlagTable[SC_EDP] |= SCB_BATK|SCB_WATK; +#endif if( !battle_config.display_hallucination ) //Disable Hallucination. StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK; } @@ -627,6 +841,14 @@ static void initDummyData(void) dummy_status.mode = MD_CANMOVE; } + +//For copying a status_data structure from b to a, without overwriting current Hp and Sp +static inline void status_cpy(struct status_data* a, const struct status_data* b) +{ + memcpy((void*)&a->max_hp, (const void*)&b->max_hp, sizeof(struct status_data)-(sizeof(a->hp)+sizeof(a->sp))); +} + + /*========================================== * 精錬ボーナス *------------------------------------------*/ @@ -748,6 +970,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s status_change_end(target, SC_HIDING, INVALID_TIMER); status_change_end(target, SC_CLOAKING, INVALID_TIMER); status_change_end(target, SC_CHASEWALK, INVALID_TIMER); + status_change_end(target, SC_CAMOUFLAGE, INVALID_TIMER); if ((sce=sc->data[SC_ENDURE]) && !sce->val4) { //Endure count is only reduced by non-players on non-gvg maps. //val4 signals infinite endure. [Skotlex] @@ -765,6 +988,8 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s } if(sc->data[SC_DANCING] && (unsigned int)hp > status->max_hp>>2) status_change_end(target, SC_DANCING, INVALID_TIMER); + if(sc->data[SC_CLOAKINGEXCEED] && --(sc->data[SC_CLOAKINGEXCEED]->val2) <= 0) + status_change_end(target,SC_CLOAKINGEXCEED,-1); } unit_skillcastcancel(target, 2); } @@ -917,9 +1142,6 @@ int status_heal(struct block_list *bl,int hp,int sp, int flag) } if(sp) { - if (sc && sc->data[SC_BERSERK] && sc->data[SC_ABUNDANCE]) // SP does not regenerate during Frenzy. - sp = 0; - if((unsigned int)sp > status->max_sp - status->sp) sp = status->max_sp - status->sp; } @@ -1199,6 +1421,8 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int } if (sc->option&OPTION_CHASEWALK && skill_num != ST_CHASEWALK) return 0; + if(sc->option&OPTION_MOUNTING) + return 0;//New mounts can't attack nor use skills in the client; this check makes it cheat-safe [Ind] } if (target == NULL || target == src) //No further checking needed. return 1; @@ -1233,6 +1457,8 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int if (tsc->option&hide_flag && !(status->mode&MD_BOSS) && (sd->special_state.perfect_hiding || !(status->mode&MD_DETECTOR))) return 0; + if( tsc->data[SC_CAMOUFLAGE] && !(status->mode&(MD_BOSS|MD_DETECTOR)) && !skill_num ) + return 0; } break; case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them). @@ -1279,18 +1505,14 @@ int status_check_visibility(struct block_list *src, struct block_list *target) switch (target->type) { //Check for chase-walk/hiding/cloaking opponents. case BL_PC: - if(tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) && - !(status->mode&MD_BOSS) && - ( - ((TBL_PC*)target)->special_state.perfect_hiding || - !(status->mode&MD_DETECTOR) - )) + if( (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&MD_BOSS) && + ( ((TBL_PC*)target)->special_state.perfect_hiding || !(status->mode&MD_DETECTOR) ) ) return 0; break; default: - if (tsc && tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) && - !(status->mode&(MD_BOSS|MD_DETECTOR))) - return 0; + if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&(MD_BOSS|MD_DETECTOR)) ) + return 0; + } return 1; @@ -1299,28 +1521,42 @@ int status_check_visibility(struct block_list *src, struct block_list *target) // Basic ASPD value int status_base_amotion_pc(struct map_session_data* sd, struct status_data* status) { - int amotion, shield = 0; + int amotion; // base weapon delay amotion = (sd->status.weapon < MAX_WEAPON_TYPE) ? (aspd_base[pc_class2idx(sd->status.class_)][sd->status.weapon]) // single weapon : (aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype1] + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2])*7/10; // dual-wield - if (sd->status.shield) - shield = shield_aspd_base[pc_class2idx(sd->status.class_)] * 10; - // percentual delay reduction from stats - amotion-= ( amotion - shield ) * (4*status->agi + status->dex)/1000; - + amotion-= amotion * (4*status->agi + status->dex)/1000; + // raw delay adjustment from bAspd bonus amotion+= sd->aspd_add; - - return amotion; +#if RRMODE + /** + * Bearing a shield decreases your ASPD by a fixed value depending on your class + **/ + if( sd->status.shield ) + amotion += re_job_db[pc_class2idx(sd->status.class_)][SHIELD_ASPD]; + /** + * RE Absolute aspd modifiers + **/ + if( sd->sc.count ) { + int i; + if ( sd->sc.data[i=SC_ASPDPOTION3] || + sd->sc.data[i=SC_ASPDPOTION2] || + sd->sc.data[i=SC_ASPDPOTION1] || + sd->sc.data[i=SC_ASPDPOTION0] ) + amotion -= sd->sc.data[i]->val1*10; + } +#endif + return amotion; } static unsigned short status_base_atk(const struct block_list *bl, const struct status_data *status) { - int flag = 0, str, dex, dstr, base; + int flag = 0, str, dex, dstr; if(!(bl->type&battle_config.enable_baseatk)) return 0; @@ -1338,10 +1574,10 @@ static unsigned short status_base_atk(const struct block_list *bl, const struct flag = 1; } if (flag) { - base =str = status->dex; + str = status->dex; dex = status->str; } else { - base = str = status->str; + str = status->str; dex = status->dex; } //Normally only players have base-atk, but homunc have a different batk @@ -1350,12 +1586,33 @@ static unsigned short status_base_atk(const struct block_list *bl, const struct dstr = str/10; str += dstr*dstr; if (bl->type == BL_PC) - str = (((TBL_PC*)bl)->status.base_level*10/4 + base*10 + dex*10/5 + status->luk*10/3)/10; + str+= dex/5 + status->luk/5; return cap_value(str, 0, USHRT_MAX); } -#define status_base_matk_max(status) (status->int_+(status->int_/5)*(status->int_/5)) -#define status_base_matk_min(status) (status->int_+(status->int_/7)*(status->int_/7)) + +static inline unsigned short status_base_matk_max(const struct status_data* status) +{ + #if RRMODE + return status->matk_max;//In RE maximum MATK signs weapon matk, which we store in this var + #else //Original Max MATK Formula + return status->int_+(status->int_/5)*(status->int_/5); + #endif +} + +#if RRMODE +static inline unsigned short status_base_matk_min(const struct status_data* status, int lvl) +#else +static inline unsigned short status_base_matk_min(const struct status_data* status) +#endif +{ + #if RRMODE //Renewal MATK Formula + return status->int_+(status->int_/2)+(status->dex/5)+(status->luk/3)+(lvl/4); + #else //Original Min MATK Formula + return status->int_+(status->int_/7)*(status->int_/7); + #endif +} + //Fills in the misc data that can be calculated from the other status info (except for level) void status_calc_misc(struct block_list *bl, struct status_data *status, int level) @@ -1366,23 +1623,31 @@ void status_calc_misc(struct block_list *bl, struct status_data *status, int lev status->hit = status->flee = status->def2 = status->mdef2 = status->cri = status->flee2 = 0; - +#if RRMODE + status->matk_min = status_base_matk_min(status, level); +#else status->matk_min = status_base_matk_min(status); +#endif status->matk_max = status_base_matk_max(status); - //Renewal calculation? HIT is currently coming up too high. - status->hit += 175 + status->dex + (unsigned short)floor((double)status->luk/3) + level; - status->flee += 100 + status->agi + level; - - // Values are still off by roughly 1~3 points. Getting there though. - status->def2 += (level*10/2 + status->vit*10/2 + status->agi*10/5)/10; - if( bl->type != BL_MOB ) - status->mdef2 += (level*10/4 + status->int_*10 + status->vit*10/5 + status->dex*10/5)/10; - else - status->mdef2 = status->mdef; // Mobs use their mdef as their mdef2. - - //Status MATK = floor(Base Level/4 + INT + INT/2 + DEX/5 + LUK/3) - status->status_matk = (unsigned short)floor((double)level/4 + (double)status->int_ + (double)status->int_/2 + (double)status->dex/5 + (double)status->luk/3); +#if RRMODE //Renewal Formulas + status->hit += level + status->dex;//base level + ( every 1 dex = +1 hit ) + status->hit += status->luk / 3;//every 3 luk = +1 hit + status->flee += level + status->agi;//base level + ( every 1 agi = +1 flee ) + status->flee += status->luk/5;//every 5 luk = +1 flee + status->def2 += status->agi / 5;//every 5 agi = +1 def + status->def2 += status->vit / 2;//every 2 agi = +1 def + status->def2 += level / 2;//every 2 lvls = +1 def + status->mdef2 += status->int_ / 2;//every 2 int = +1 mdef + status->mdef2 += status->dex / 5;//every 5 dex = +1 mdef + status->mdef2 += level /4;//every 4 lvls = +1 mdef + //status->matk_min += level/4;//every 4 lvls = +1 matk +#else //Old Formulas + status->hit += level + status->dex; + status->flee += level + status->agi; + status->def2 += status->vit; + status->mdef2 += status->int_ + (status->vit>>1); +#endif if( bl->type&battle_config.enable_critical ) status->cri += status->luk*3 + 10; @@ -1399,6 +1664,10 @@ void status_calc_misc(struct block_list *bl, struct status_data *status, int lev status->batk = cap_value(temp, 0, USHRT_MAX); } else status->batk = status_base_atk(bl, status); +#if RRMODE //Renewal ATK Bonus Formula (after atk is calculated) + status->batk += status->luk / 3;//every 3 luk = +1ATK + status->batk += level / 4;//every 4 levels = +1 ATK +#endif if (status->cri) switch (bl->type) { case BL_MOB: @@ -1561,11 +1830,8 @@ int status_calc_mob_(struct mob_data* md, bool first) status->max_sp += 200 * gc->defense; status->hp = status->max_hp; status->sp = status->max_sp; - if( gc->castle_id < 24 ) - { - status->def += (gc->defense+2)/3; - status->mdef += (gc->defense+2)/3; - } + status->def += (gc->defense+2)/3; + status->mdef += (gc->defense+2)/3; } if(md->class_ != MOBID_EMPERIUM) { status->batk += status->batk * 10*md->guardian_data->guardup_lv/100; @@ -1712,6 +1978,18 @@ static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct sta return val; } +#if RRMODE +/** + * Renewal Absolute Bonus to be applied after all bonuses were applied (so % bonuses on say, skills, don't affect them) + **/ +void status_renewal_postcalc(struct status_data* status, int flag) { + if( flag&SCB_FLEE ) + status->flee += 100; + if( flag&SCB_HIT ) + status->hit += 175; + return; +} +#endif //Calculates player data from scratch without counting SC adjustments. //Should be invoked whenever players raise stats, learn passive skills or change equipment. @@ -1772,9 +2050,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100; sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100; sd->regen.state.block = 0; - sd->fixedcastrate=100; - sd->weapon_matk = 0; - sd->equipment_matk = 0; // zeroed arrays, order follows the order in pc.h. // add new arrays to the end of zeroed area in pc.h (see comments) and size here. [zzo] @@ -1818,7 +2093,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first) status->mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK); status->size = (sd->class_&JOBL_BABY)?0:1; - if (battle_config.character_size && (pc_isriding(sd) || pc_isdragon(sd))) { //[Lupus] + if (battle_config.character_size && pc_isriding(sd)) { //[Lupus] if (sd->class_&JOBL_BABY) { if (battle_config.character_size&2) status->size++; @@ -1940,7 +2215,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) int r,wlv = sd->inventory_data[index]->wlv; struct weapon_data *wd; struct weapon_atk *wa; - if (wlv >= MAX_REFINE_BONUS) wlv = MAX_REFINE_BONUS - 1; if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L) { @@ -1952,6 +2226,21 @@ int status_calc_pc_(struct map_session_data* sd, bool first) } wa->atk += sd->inventory_data[index]->atk; wa->atk2 = (r=sd->status.inventory[index].refine)*refinebonus[wlv][0]; + #if RRMODE + /** + * in RE matk_max is used as the weapon's matk. + * += is used so that two-wield weapons (in the case of, say, sinx) bonus stack. + **/ + status->matk_max += sd->inventory_data[index]->matk; + /** + * Refine Bonus + **/ + status->matk_max += sd->status.inventory[index].refine * refinebonus[wlv][0]; + /** + * In RE weapon level is used in several areas, this way we save performance + **/ + status->wlv = wlv; + #endif if((r-=refinebonus[wlv][2])>0) //Overrefine bonus. wd->overrefine = r*refinebonus[wlv][1]; @@ -1979,11 +2268,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first) } } else if(sd->inventory_data[index]->type == IT_ARMOR) { - int r = sd->status.inventory[index].refine; - int per = 0, refine=(r>0)?1:0; - for(per=1; per<r; per++) - refine += (per/4) + 1; - refinedef += refine; + refinedef += sd->status.inventory[index].refine*refinebonus[0][0]; if(sd->inventory_data[index]->script) { run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); if (!calculating) //Abort, run_script retriggered this. [Skotlex] @@ -2008,7 +2293,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first) memcpy(sd->param_equip,sd->param_bonus,sizeof(sd->param_equip)); memset(sd->param_bonus, 0, sizeof(sd->param_bonus)); - status->def += refinedef; + status->def += (refinedef+50)/100; //Parse Cards for(i=0;i<EQI_MAX-1;i++) { @@ -2103,7 +2388,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2]; sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2]; - if((pc_isriding(sd) || pc_isdragon(sd)) && + if(pc_isriding(sd) && (sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR)) { //When Riding with spear, damage modifier to mid-class becomes //same as versus large size. @@ -2312,7 +2597,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) status->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3); if((skill=pc_checkskill(sd,MO_DODGE))>0) status->flee += (skill*3)>>1; - // ----- EQUIPMENT-DEF CALCULATION ----- // Apply relative modifiers from equipment @@ -2320,15 +2604,22 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->def_rate = 0; if(sd->def_rate != 100) { i = status->def * sd->def_rate/100; +#if RRMODE status->def = cap_value(i, SHRT_MIN, SHRT_MAX); - } - +#else + status->def = cap_value(i, CHAR_MIN, CHAR_MAX); +#endif + } +#if RRMODE == 0 + /** + * The following setting does not affect Renewal Mode + **/ if (!battle_config.weapon_defense_type && status->def > battle_config.max_def) { status->def2 += battle_config.over_def_bonus*(status->def -battle_config.max_def); - status->def = (unsigned short)battle_config.max_def; + status->def = (unsigned char)battle_config.max_def; } - +#endif // ----- EQUIPMENT-MDEF CALCULATION ----- // Apply relative modifiers from equipment @@ -2336,15 +2627,22 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->mdef_rate = 0; if(sd->mdef_rate != 100) { i = status->mdef * sd->mdef_rate/100; +#if RRMODE status->mdef = cap_value(i, SHRT_MIN, SHRT_MAX); - } - +#else + status->mdef = cap_value(i, CHAR_MIN, CHAR_MAX); +#endif + } +#if RRMODE == 0 + /** + * The following setting does not affect Renewal Mode + **/ if (!battle_config.magic_defense_type && status->mdef > battle_config.max_def) { status->mdef2 += battle_config.over_def_bonus*(status->mdef -battle_config.max_def); - status->mdef = (signed short)battle_config.max_def; + status->mdef = (signed char)battle_config.max_def; } - +#endif // ----- ASPD CALCULATION ----- // Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied @@ -2362,8 +2660,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first) status->aspd_rate -= ((skill+1)/2) * 10; if(pc_isriding(sd)) status->aspd_rate += 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY); - if(pc_isdragon(sd)) - status->aspd_rate += 750-75*pc_checkskill(sd,RK_DRAGONTRAINING); // Officiak is rumoured to be 75+5*skilllv...giving you 125% ASPD? + status->adelay = 2*status->amotion; @@ -2381,8 +2678,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->max_weight += 2000*skill; if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0) sd->max_weight += 10000; - if(pc_isdragon(sd) && (skill=pc_checkskill(sd,RK_DRAGONTRAINING))>0) - sd->max_weight += 5000+2000*skill; //+200 weight per level of RK_DRAGINTRAINING if(sc->data[SC_KNOWLEDGE]) sd->max_weight += sd->max_weight*sc->data[SC_KNOWLEDGE]->val1/10; if((skill=pc_checkskill(sd,ALL_INCCARRY))>0) @@ -2408,8 +2703,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->dsprate = 0; if(sd->castrate < 0) sd->castrate = 0; - if( sd->fixedcastrate < 0 ) - sd->fixedcastrate = 0; if(sd->delayrate < 0) sd->delayrate = 0; if(sd->hprecov_rate < 0) @@ -2431,17 +2724,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->magic_addrace[RC_DRAGON]+=skill; sd->subrace[RC_DRAGON]+=skill; } - if( (skill = pc_checkskill(sd, AB_EUCHARISTICA)) > 0 ) - { - sd->right_weapon.addrace[RC_DEMON]+=skill; - sd->right_weapon.addele[ELE_DARK]+=skill; - sd->left_weapon.addrace[RC_DEMON]+=skill; - sd->left_weapon.addele[ELE_DARK]+=skill; - sd->magic_addrace[RC_DEMON]+=skill; - sd->magic_addele[ELE_DARK]+=skill; - sd->subrace[RC_DEMON]+=skill; - sd->subele[ELE_DARK]+=skill; - } if(sc->count){ if(sc->data[SC_CONCENTRATE]) @@ -2480,7 +2762,6 @@ int status_calc_pc_(struct map_session_data* sd, bool first) sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4; } } - status_cpy(&sd->battle_status, status); // ----- CLIENT-SIDE REFRESH ----- @@ -2594,8 +2875,13 @@ int status_calc_homunculus_(struct homun_data *hd, bool first) status->adelay = status->amotion; //It seems adelay = amotion for Homunculus. status_calc_misc(&hd->bl, status, hom->level); +#if RRMODE + /** + * In RE Mode matk_max is used as source of weaponMATK, but homuns don't have it -- so we swap the values here. + **/ + status->matk_max = status->matk_min; +#endif status_cpy(&hd->battle_status, status); - return 1; } @@ -2612,9 +2898,17 @@ static signed short status_calc_hit(struct block_list *,struct status_change *,i static signed short status_calc_critical(struct block_list *,struct status_change *,int); static signed short status_calc_flee(struct block_list *,struct status_change *,int); static signed short status_calc_flee2(struct block_list *,struct status_change *,int); -static signed short status_calc_def(struct block_list *,struct status_change *,int); +#if RRMODE + static short status_calc_def(struct block_list *bl, struct status_change *sc, int); +#else + static signed char status_calc_def(struct block_list *,struct status_change *,int); +#endif static signed short status_calc_def2(struct block_list *,struct status_change *,int); -static signed short status_calc_mdef(struct block_list *,struct status_change *,int); +#if RRMODE + static short status_calc_mdef(struct block_list *bl, struct status_change *sc, int); +#else + static signed char status_calc_mdef(struct block_list *,struct status_change *,int); +#endif static signed short status_calc_mdef2(struct block_list *,struct status_change *,int); static unsigned short status_calc_speed(struct block_list *,struct status_change *,int); static short status_calc_aspd_rate(struct block_list *,struct status_change *,int); @@ -2792,8 +3086,6 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str } else regen->flag&=~sce->val4; //Remove regen as specified by val4 } - if( sc->data[SC_VITALITYACTIVATION] ) - regen->flag &=~RGN_SP; } /// Recalculates parts of an object's battle status according to the specified flags. @@ -2857,7 +3149,7 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag) if(flag&SCB_LUK) { status->luk = status_calc_luk(bl, sc, b_status->luk); - flag|=SCB_BATK|SCB_CRI|SCB_FLEE2|SCB_MATK; + flag|=SCB_BATK|SCB_CRI|SCB_FLEE2; } if(flag&SCB_BATK && b_status->batk) { @@ -3048,38 +3340,27 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag) } if(flag&SCB_MATK) { - int wlv = 1, wmatk = 0; - //New matk + #if RRMODE + status->matk_min = status_base_matk_min(status,status_get_lv(bl)); + #else status->matk_min = status_base_matk_min(status); + #endif status->matk_max = status_base_matk_max(status); - // iRO Wiki states as of 2011/02/24: - // Status MATK = floor(Base Level/4 + INT + INT/2 + DEX/5 + LUK/3) - status->status_matk = status_get_lv(bl)/4 + status->int_ + status->int_/2 + status->dex/5 + status->luk/3; - - if( sd ) - { - short index = sd->equip_index[EQI_HAND_R]; - wmatk = sd->weapon_matk + sd->battle_status.rhw.atk2 + sd->equipment_matk; - if( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON ) - wlv = sd->inventory_data[index]->wlv; - } - - // Variance = ア 0.1 * Weapon Level * Base Weapon MATK - // Used in a lot of magical attack calculations still. - status->matk_min = status->status_matk + wmatk; - status->matk_max = status->status_matk + wmatk + (wmatk * wlv / 10); - if( bl->type&BL_PC && sd->matk_rate != 100 ) { //Bonuses from previous matk + #if RRMODE == 0 //Only changed in non-re [RRInd] status->matk_max = status->matk_max * sd->matk_rate/100; + #endif status->matk_min = status->matk_min * sd->matk_rate/100; } status->matk_min = status_calc_matk(bl, sc, status->matk_min); - status->matk_max = status_calc_matk(bl, sc, status->matk_max); + #if RRMODE == 0 //Only changed in non-re [RRInd] + status->matk_max = status_calc_matk(bl, sc, status->matk_max); + #endif if(sc->data[SC_MAGICPOWER]) { //Store current matk values sc->mp_matk_min = status->matk_min; @@ -3160,14 +3441,15 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag) status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion); } } - +#if RRMODE + status_renewal_postcalc(status,flag); +#endif if(flag&(SCB_VIT|SCB_MAXHP|SCB_INT|SCB_MAXSP) && bl->type&BL_REGEN) status_calc_regen(bl, status, status_get_regen_data(bl)); if(flag&SCB_REGEN && bl->type&BL_REGEN) status_calc_regen_rate(bl, status_get_regen_data(bl), sc); } - /// Recalculates parts of an object's base status and battle status according to the specified flags. /// Also sends updates to the client wherever applicable. /// @param flag bitfield of values from enum scb_flag @@ -3228,32 +3510,26 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first) clif_updatestatus(sd,SP_ASPD); if(b_status.speed != status->speed) clif_updatestatus(sd,SP_SPEED); - if(b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk || b_status.batk != status->batk - || b_status.rhw.atk2 != status->rhw.atk2 || b_status.lhw.atk2 != status->lhw.atk2 || b_status.equipment_atk != status->equipment_atk) - { + if(b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk || b_status.batk != status->batk) clif_updatestatus(sd,SP_ATK1); - clif_updatestatus(sd,SP_ATK2); - clif_updatestatus(sd,SP_MATK1); - } - if(b_status.def != status->def || b_status.def2 != status->def2) - { + if(b_status.def != status->def) clif_updatestatus(sd,SP_DEF1); + if(b_status.rhw.atk2 != status->rhw.atk2 || b_status.lhw.atk2 != status->lhw.atk2) + clif_updatestatus(sd,SP_ATK2); + if(b_status.def2 != status->def2) clif_updatestatus(sd,SP_DEF2); - } if(b_status.flee2 != status->flee2) clif_updatestatus(sd,SP_FLEE2); if(b_status.cri != status->cri) clif_updatestatus(sd,SP_CRITICAL); if(b_status.matk_max != status->matk_max) - { clif_updatestatus(sd,SP_MATK1); + if(b_status.matk_min != status->matk_min) clif_updatestatus(sd,SP_MATK2); - } - if(b_status.mdef != status->mdef || b_status.mdef2 != status->mdef2) - { + if(b_status.mdef != status->mdef) clif_updatestatus(sd,SP_MDEF1); + if(b_status.mdef2 != status->mdef2) clif_updatestatus(sd,SP_MDEF2); - } if(b_status.rhw.range != status->rhw.range) clif_updatestatus(sd,SP_ATTACKRANGE); if(b_status.max_hp != status->max_hp) @@ -3343,6 +3619,9 @@ static unsigned short status_calc_str(struct block_list *bl, struct status_chang str += ((sc->data[SC_MARIONETTE2]->val3)>>16)&0xFF; if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && str < 50) str = 50; + /** + * RK Rune Skill + **/ if(sc->data[SC_GIANTGROWTH]) str += 30; @@ -3384,6 +3663,9 @@ static unsigned short status_calc_agi(struct block_list *bl, struct status_chang agi += ((sc->data[SC_MARIONETTE2]->val3)>>8)&0xFF; if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && agi < 50) agi = 50; + /** + * Arch Bishop + **/ if(sc->data[SC_ADORAMUS]) agi -= sc->data[SC_ADORAMUS]->val2; @@ -3417,8 +3699,6 @@ static unsigned short status_calc_vit(struct block_list *bl, struct status_chang vit += sc->data[SC_MARIONETTE2]->val3&0xFF; if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && vit < 50) vit = 50; - if(sc->data[SC_LAUDAAGNUS]) - vit += sc->data[SC_LAUDAAGNUS]->val2; return (unsigned short)cap_value(vit,0,USHRT_MAX); } @@ -3529,8 +3809,6 @@ static unsigned short status_calc_luk(struct block_list *bl, struct status_chang luk += sc->data[SC_MARIONETTE2]->val4&0xFF; if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && luk < 50) luk = 50; - if(sc->data[SC_LAUDARAMUS]) - luk += sc->data[SC_LAUDARAMUS]->val2; return (unsigned short)cap_value(luk,0,USHRT_MAX); } @@ -3567,6 +3845,13 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan batk += sc->data[SC_GATLINGFEVER]->val3; if(sc->data[SC_MADNESSCANCEL]) batk += 100; +#if RE_EDP + /** + * in RE EDP increases your base atk by atk x Skill Level. + **/ + if( sc->data[SC_EDP] ) + batk = batk * sc->data[SC_EDP]->val1; +#endif return (unsigned short)cap_value(batk,0,USHRT_MAX); } @@ -3611,6 +3896,13 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100; if(sc->data[SC_MERC_ATKUP]) watk += sc->data[SC_MERC_ATKUP]->val2; +#if RE_EDP + /** + * in RE EDP increases your weapon atk by watk x Skill Level - 1 + **/ + if( sc->data[SC_EDP] && sc->data[SC_EDP]->val1 > 1 ) + watk = watk * (sc->data[SC_EDP]->val1 - 1); +#endif return (unsigned short)cap_value(watk,0,USHRT_MAX); } @@ -3649,6 +3941,8 @@ static signed short status_calc_critical(struct block_list *bl, struct status_ch critical += sc->data[SC_TRUESIGHT]->val2; if(sc->data[SC_CLOAKING]) critical += critical; + if(sc->data[SC_CAMOUFLAGE]) + critical += 100; return (short)cap_value(critical,10,SHRT_MAX); } @@ -3680,7 +3974,7 @@ static signed short status_calc_hit(struct block_list *bl, struct status_change if(sc->data[SC_MERC_HITUP]) hit += sc->data[SC_MERC_HITUP]->val2; if(sc->data[SC_FEAR]) - hit -= hit * 20/100; + hit -= hit * 20 / 100; return (short)cap_value(hit,1,SHRT_MAX); } @@ -3726,12 +4020,31 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change flee -= sc->data[SC_GATLINGFEVER]->val4; if(sc->data[SC_SPEED]) flee += 10 + sc->data[SC_SPEED]->val1 * 10; - if(sc->data[SC_PARTYFLEE]) - flee += sc->data[SC_PARTYFLEE]->val1 * 10; if(sc->data[SC_MERC_FLEEUP]) flee += sc->data[SC_MERC_FLEEUP]->val2; if(sc->data[SC_FEAR]) - flee -= flee * 20/100; + flee -= flee * 20 / 100; + if(sc->data[SC_PARALYSE]) + flee -= flee / 10; // 10% Flee reduction + if(sc->data[SC_INFRAREDSCAN]) + flee -= flee * 30 / 100; + if( sc->data[SC__LAZINESS] ) + flee -= flee * sc->data[SC__LAZINESS]->val3 / 100; + if( sc->data[SC_GLOOMYDAY] ) + flee -= flee * sc->data[SC_GLOOMYDAY]->val2 / 100; + if( sc->data[SC_HALLUCINATIONWALK] ) + flee += sc->data[SC_HALLUCINATIONWALK]->val2; + if( sc->data[SC_SATURDAYNIGHTFEVER] ) + flee -= flee * (40 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100; + if( sc->data[SC_WATER_BARRIER] ) + flee -= sc->data[SC_WATER_BARRIER]->val3; + if( sc->data[SC_WIND_STEP_OPTION] ) + flee += flee * sc->data[SC_WIND_STEP_OPTION]->val2 / 100; + if( sc->data[SC_ZEPHYR] ) + flee += flee * sc->data[SC_ZEPHYR]->val2 / 100; + if( sc->data[SC_MARSHOFABYSS] ) + flee -= (9 * sc->data[SC_MARSHOFABYSS]->val3 / 10 + sc->data[SC_MARSHOFABYSS]->val2 / 10) * (bl->type == BL_MOB ? 2 : 1); + return (short)cap_value(flee,1,SHRT_MAX); } @@ -3748,12 +4061,18 @@ static signed short status_calc_flee2(struct block_list *bl, struct status_chang return (short)cap_value(flee2,10,SHRT_MAX); } - -static signed short status_calc_def(struct block_list *bl, struct status_change *sc, int def) +#if RRMODE + static short status_calc_def(struct block_list *bl, struct status_change *sc, int def) +#else + static signed char status_calc_def(struct block_list *bl, struct status_change *sc, int def) +#endif { if(!sc || !sc->count) - return (signed short)cap_value(def,SHRT_MIN,SHRT_MAX); - +#if RRMODE + return (short)cap_value(def,SHRT_MIN,SHRT_MAX); +#else + return (signed char)cap_value(def,CHAR_MIN,CHAR_MAX); +#endif if(sc->data[SC_BERSERK]) return 0; if(sc->data[SC_SKA]) @@ -3788,8 +4107,31 @@ static signed short status_calc_def(struct block_list *bl, struct status_change def -= def * sc->data[SC_STRIPSHIELD]->val2/100; if (sc->data[SC_FLING]) def -= def * (sc->data[SC_FLING]->val2)/100; - - return (signed short)cap_value(def,SHRT_MIN,SHRT_MAX); + if( sc->data[SC_FREEZING] ) + def -= def * 3 / 10; + if( sc->data[SC_MARSHOFABYSS] ) + def -= def * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100; + if( sc->data[SC_ANALYZE] ) + def -= def * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; + if( sc->data[SC__BLOODYLUST] ) + def -= def * 55 / 100; + if( sc->data[SC_FORCEOFVANGUARD] ) + def += def * 2 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100; + if(sc->data[SC_SATURDAYNIGHTFEVER]) + def -= def * (10 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100; + if(sc->data[SC_EARTHDRIVE]) + def -= def * 25 / 100; + if( sc->data[SC_GT_CHANGE] ) + def -= def * sc->data[SC_GT_CHANGE]->val3 / 100; + if( sc->data[SC_ROCK_CRUSHER] ) + def -= def * sc->data[SC_ROCK_CRUSHER]->val2 / 100; + if( sc->data[SC_POWER_OF_GAIA] ) + def += def * sc->data[SC_POWER_OF_GAIA]->val2 / 100; +#if RRMODE + return (short)cap_value(def,SHRT_MIN,SHRT_MAX); +#else + return (signed char)cap_value(def,CHAR_MIN,CHAR_MAX); +#endif } static signed short status_calc_def2(struct block_list *bl, struct status_change *sc, int def2) @@ -3820,16 +4162,36 @@ static signed short status_calc_def2(struct block_list *bl, struct status_change + def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST ? 25 : 0 ) / 100; if(sc->data[SC_FLING]) def2 -= def2 * (sc->data[SC_FLING]->val3)/100; - if (sc->data[SC_STONEHARDSKIN]) - def2 += sc->data[SC_STONEHARDSKIN]->val3; + if( sc->data[SC_FREEZING] ) + def2 -= def2 * 3 / 10; + if(sc->data[SC_ANALYZE]) + def2 -= def2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; + if( sc->data[SC_ECHOSONG] ) + def2 += def2 * sc->data[SC_ECHOSONG]->val2/100; + if( sc->data[SC_PRESTIGE] ) + def2 += def2 * sc->data[SC_PRESTIGE]->val1 / 100; + if( sc->data[SC_SHIELDSPELL_REF] && sc->data[SC_SHIELDSPELL_REF]->val1 == 1 ) + def2 += sc->data[SC_SHIELDSPELL_REF]->val2; + if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 ) + def2 += (5 + sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2); + if( sc->data[SC_GT_REVITALIZE] ) + def2 += def2 * ( 50 + 10 * sc->data[SC_GT_REVITALIZE]->val1 ) / 100; return (short)cap_value(def2,1,SHRT_MAX); } -static signed short status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) +#if RRMODE + static short status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) +#else + static signed char status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) +#endif { if(!sc || !sc->count) - return (signed short)cap_value(mdef,SHRT_MIN,SHRT_MAX); +#if RRMODE + return (short)cap_value(mdef,SHRT_MIN,SHRT_MAX); +#else + return (signed char)cap_value(mdef,CHAR_MIN,CHAR_MAX); +#endif if(sc->data[SC_BERSERK]) return 0; @@ -3849,8 +4211,22 @@ static signed short status_calc_mdef(struct block_list *bl, struct status_change mdef += sc->data[SC_ENDURE]->val1; if(sc->data[SC_CONCENTRATION]) mdef += 1; //Skill info says it adds a fixed 1 Mdef point. - - return (signed short)cap_value(mdef,SHRT_MIN,SHRT_MAX); + if( sc->data[SC_MARSHOFABYSS] ) + mdef -= mdef * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100; + if(sc->data[SC_ANALYZE]) + mdef -= mdef * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; + if(sc->data[SC_SYMPHONYOFLOVER]) + mdef += mdef * sc->data[SC_SYMPHONYOFLOVER]->val2 / 100; + if(sc->data[SC_GT_CHANGE]) + mdef -= mdef * sc->data[SC_GT_CHANGE]->val3 / 100; + if(sc->data[SC_WATER_BARRIER]) + mdef += sc->data[SC_WATER_BARRIER]->val2; + +#if RRMODE + return (short)cap_value(mdef,SHRT_MIN,SHRT_MAX); +#else + return (signed char)cap_value(mdef,CHAR_MIN,CHAR_MAX); +#endif } static signed short status_calc_mdef2(struct block_list *bl, struct status_change *sc, int mdef2) @@ -3862,8 +4238,8 @@ static signed short status_calc_mdef2(struct block_list *bl, struct status_chang return 0; if(sc->data[SC_MINDBREAKER]) mdef2 -= mdef2 * sc->data[SC_MINDBREAKER]->val3/100; - if (sc->data[SC_STONEHARDSKIN]) - mdef2 += sc->data[SC_STONEHARDSKIN]->val3; + if(sc->data[SC_ANALYZE]) + mdef2 -= mdef2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; return (short)cap_value(mdef2,1,SHRT_MAX); } @@ -3890,11 +4266,17 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha if( sc->data[SC_FUSION] ) val = 25; - else - if( sd && pc_isriding(sd) ) - val = 25; - if( sd && pc_isdragon(sd) ) - val = 25; + else if( sd ) { + if( pc_isriding(sd) || sd->sc.option&(OPTION_DRAGON|OPTION_MOUNTING) ) + val = 25;//Same bonus + else if( sd->sc.option&OPTION_WUGRIDER ) + val = 15 + 5 * pc_checkskill(sd, RA_WUGRIDER); + else if( sd->sc.option&OPTION_MADOGEAR ) { + val = (- 10 * (5 - pc_checkskill(sd,NC_MADOLICENCE))); + if( sc->data[SC_ACCELERATION] ) + val += 25; + } + } speed_rate -= val; } @@ -3919,7 +4301,7 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha if( sc->data[SC_DECREASEAGI] ) val = max( val, 25 ); - if( sc->data[SC_QUAGMIRE] ) + if( sc->data[SC_QUAGMIRE] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) val = max( val, 50 ); if( sc->data[SC_DONTFORGETME] ) val = max( val, sc->data[SC_DONTFORGETME]->val3 ); @@ -3943,8 +4325,24 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha val = max( val, sc->data[SC_SUITON]->val3 ); if( sc->data[SC_SWOO] ) val = max( val, 300 ); - if( sc->data[SC_ADORAMUS] ) - val = max( val, 25 ); + if( sc->data[SC_FREEZING] ) + val = max( val, 70 ); + if( sc->data[SC_MARSHOFABYSS] ) + val = max( val, 40 + 10 * sc->data[SC_MARSHOFABYSS]->val1 ); + if( sc->data[SC_CAMOUFLAGE] && (sc->data[SC_CAMOUFLAGE]->val3&1) == 0 ) + val = max( val, sc->data[SC_CAMOUFLAGE]->val1 < 3 ? 300 : 25 * (6 - sc->data[SC_CAMOUFLAGE]->val1) ); + if( sc->data[SC__GROOMY] ) + val = max( val, sc->data[SC__GROOMY]->val2); + if( sc->data[SC_STEALTHFIELD_MASTER] ) + val = max( val, 30 ); + if( sc->data[SC_BANDING_DEFENCE] ) + val = max( val, sc->data[SC_BANDING_DEFENCE]->val1 );//+90% walking speed. + if( sc->data[SC_ROCK_CRUSHER_ATK] ) + val = max( val, sc->data[SC_ROCK_CRUSHER_ATK]->val2 ); + if( sc->data[SC_POWER_OF_GAIA] ) + val = max( val, sc->data[SC_POWER_OF_GAIA]->val2 ); + if( sc->data[SC_MELON_BOMB] ) + val = max( val, sc->data[SC_MELON_BOMB]->val1 ); if( sd && sd->speed_rate + sd->speed_add_rate > 0 ) // permanent item-based speedup val = max( val, sd->speed_rate + sd->speed_add_rate ); @@ -4012,7 +4410,12 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha /// Note that the scale of aspd_rate is 1000 = 100%. static short status_calc_aspd_rate(struct block_list *bl, struct status_change *sc, int aspd_rate) { +#if RRMODE == 0 + /** + * this variable is not used unless in non-RE + **/ int i; +#endif if(!sc || !sc->count) return cap_value(aspd_rate,0,SHRT_MAX); @@ -4081,12 +4484,16 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change * else if(sc->data[SC_MADNESSCANCEL]) aspd_rate -= 200; } - +#if RRMODE == 0 + /** + * in RE they give a fixed boost -- we do so along SERVICE4U in status_base_amotion_pc + **/ if(sc->data[i=SC_ASPDPOTION3] || sc->data[i=SC_ASPDPOTION2] || sc->data[i=SC_ASPDPOTION1] || sc->data[i=SC_ASPDPOTION0]) aspd_rate -= sc->data[i]->val2; +#endif if(sc->data[SC_DONTFORGETME]) aspd_rate += 10 * sc->data[SC_DONTFORGETME]->val2; if(sc->data[SC_LONGING]) @@ -4107,9 +4514,41 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change * if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ) aspd_rate += 100; } - if(sc->data[SC_FIGHTINGSPIRIT]) - aspd_rate -= sc->data[SC_FIGHTINGSPIRIT]->val3; - + if( sc->data[SC_FREEZING] ) + aspd_rate += 300; + if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] ) + aspd_rate += 500; + if( sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 ) + aspd_rate -= sc->data[SC_FIGHTINGSPIRIT]->val2; + if( sc->data[SC_PARALYSE] ) + aspd_rate += 100; + if( sc->data[SC__BODYPAINT] ) + aspd_rate += aspd_rate * (20 + 5 * sc->data[SC__BODYPAINT]->val1) / 100; + if( sc->data[SC__INVISIBILITY] ) + aspd_rate += aspd_rate * sc->data[SC__INVISIBILITY]->val2 / 100; + if( sc->data[SC__GROOMY] ) + aspd_rate += aspd_rate * sc->data[SC__GROOMY]->val2 / 100; + if( sc->data[SC_SWINGDANCE] ) + aspd_rate -= aspd_rate * sc->data[SC_SWINGDANCE]->val2 / 100; + if( sc->data[SC_DANCEWITHWUG] ) + aspd_rate -= aspd_rate * sc->data[SC_DANCEWITHWUG]->val3 / 100; + if( sc->data[SC_GLOOMYDAY] ) + aspd_rate += aspd_rate * sc->data[SC_GLOOMYDAY]->val3 / 100; + if( sc->data[SC_EARTHDRIVE] ) + aspd_rate += aspd_rate * 25 / 100; + /*As far I tested the skill there is no ASPD addition is applied. [Jobbie] */ + //if( sc->data[SC_RAISINGDRAGON] ) + // aspd_rate -= 100; //FIXME: Need official ASPD bonus of this status. [Jobbie] + if( sc->data[SC_GT_CHANGE] ) + aspd_rate -= aspd_rate * (sc->data[SC_GT_CHANGE]->val2/200) / 100; + if( sc->data[SC_GT_REVITALIZE] ) + aspd_rate -= aspd_rate * sc->data[SC_GT_REVITALIZE]->val2 / 100; + if( sc->data[SC_MELON_BOMB] ) + aspd_rate += aspd_rate * sc->data[SC_MELON_BOMB]->val1 / 100; + if( sc->data[SC_BOOST500] ) + aspd_rate -= aspd_rate * sc->data[SC_BOOST500]->val1/100; + if(sc->data[SC_EXTRACT_SALAMINE_JUICE]) + aspd_rate -= aspd_rate * sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1/100; return (short)cap_value(aspd_rate,0,SHRT_MAX); } @@ -4123,7 +4562,7 @@ static unsigned short status_calc_dmotion(struct block_list *bl, struct status_c return 0; if( sc->data[SC_CONCENTRATION] ) return 0; - if( sc->data[SC_RUN] ) + if( sc->data[SC_RUN] || sc->data[SC_WUGDASH] ) return 0; return (unsigned short)cap_value(dmotion,0,USHRT_MAX); @@ -4149,7 +4588,10 @@ static unsigned int status_calc_maxhp(struct block_list *bl, struct status_chang maxhp += maxhp * sc->data[SC_MERC_HPUP]->val2/100; if(sc->data[SC_EPICLESIS]) - maxhp += maxhp * sc->data[SC_EPICLESIS]->val2/100; + maxhp += maxhp * 5 * sc->data[SC_EPICLESIS]->val1 / 100; + if(sc->data[SC_VENOMBLEED]) + maxhp -= maxhp * 15 / 100; + return cap_value(maxhp,1,UINT_MAX); } @@ -4184,6 +4626,9 @@ static unsigned char status_calc_element(struct block_list *bl, struct status_ch return ELE_UNDEAD; if(sc->data[SC_ELEMENTALCHANGE]) return sc->data[SC_ELEMENTALCHANGE]->val2; + if(sc->data[SC_SHAPESHIFT]) + return sc->data[SC_SHAPESHIFT]->val2; + return (unsigned char)cap_value(element,0,UCHAR_MAX); } @@ -4202,6 +4647,8 @@ static unsigned char status_calc_element_lv(struct block_list *bl, struct status return 1; if(sc->data[SC_ELEMENTALCHANGE]) return sc->data[SC_ELEMENTALCHANGE]->val1; + if(sc->data[SC_SHAPESHIFT]) + return 1; return (unsigned char)cap_value(lv,1,4); } @@ -4336,7 +4783,7 @@ struct status_data *status_get_base_status(struct block_list *bl) } } -signed short status_get_def(struct block_list *bl) +signed char status_get_def(struct block_list *bl) { struct unit_data *ud; struct status_data *status = status_get_status_data(bl); @@ -4344,7 +4791,11 @@ signed short status_get_def(struct block_list *bl) ud = unit_bl2ud(bl); if (ud && ud->skilltimer != INVALID_TIMER) def -= def * skill_get_castdef(ud->skillid)/100; +#if RRMODE return cap_value(def, SHRT_MIN, SHRT_MAX); +#else + return cap_value(def, CHAR_MIN, CHAR_MAX); +#endif } unsigned short status_get_speed(struct block_list *bl) @@ -4569,22 +5020,6 @@ void status_set_viewdata(struct block_list *bl, int class_) class_ = JOB_BABY_CRUSADER2; break; } - /* - else - if (sd->sc.option&OPTION_DRAGON) // For completeness' sake. These aren't actually needed...except maybe in older clients. - switch (class_) - { - case JOB_RUNE_KNIGHT: - class_ = JOB_RUNE_KNIGHT2; - break; - case JOB_RUNE_KNIGHT_H: - class_ = JOB_RUNE_KNIGHT_H2; - break; - case JOB_BABY_RUNE: - class_ = JOB_BABY_RUNE2; - break; - } - */ sd->vd.class_ = class_; clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield); sd->vd.head_top = sd->status.head_top; @@ -4689,7 +5124,7 @@ void status_change_init(struct block_list *bl) //the flag values are the same as in status_change_start. int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int tick, int flag) { - int sc_def, tick_def = 0; + int sc_def = 0, tick_def = 0; struct status_data* status; struct status_change* sc; struct map_session_data *sd; @@ -4722,7 +5157,6 @@ int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int ti case SC_STONE: case SC_QUAGMIRE: case SC_SUITON: - case SC_ADORAMUS: return 0; } @@ -4741,7 +5175,7 @@ int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int ti sc_def = 3 +status->int_; break; case SC_DECREASEAGI: - case SC_ADORAMUS: + case SC_ADORAMUS://Arch Bishop if (sd) tick>>=1; //Half duration for players. case SC_STONE: case SC_FREEZE: @@ -4771,6 +5205,40 @@ int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int ti if (sd) //Duration greatly reduced for players. tick /= 15; //No defense against it (buff). + /** + * 3rd stuff + **/ + case SC_WHITEIMPRISON: + rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; // Lineal Reduction of Rate + //tick_def = (int)floor(log10(status_get_lv(bl)) * 10.); + break; + case SC_BURNING: + // From iROwiki : http://forums.irowiki.org/showpost.php?p=577240&postcount=583 + tick -= 50*status->luk + 60*status->int_ + 170*status->vit; + tick = max(tick,10000); // Minimum Duration 10s. + break; + case SC_FREEZING: + tick -= 1000 * ((status->vit + status->dex) / 20); + tick = max(tick,10000); // Minimum Duration 10s. + break; + case SC_OBLIVIONCURSE: + sc_def = status->int_*4/5; //FIXME: info said this is the formula of status chance. Check again pls. [Jobbie] + break; + case SC_ELECTRICSHOCKER: + case SC_BITE: + { + if( bl->type == BL_MOB ) + tick -= 1000 * (status->agi/10); + if( sd && type != SC_ELECTRICSHOCKER ) + tick >>= 1; + } + break; + case SC_CRYSTALIZE: + tick -= (1000*(status->vit/10))+(status_get_lv(bl)/50); + break; + case SC_VACUUM_EXTREME: + tick -= 50*status->str; + break; default: //Effect that cannot be reduced? Likely a buff. if (!(rand()%10000 < rate)) @@ -4873,8 +5341,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val struct status_change_entry* sce; struct status_data *status; struct view_data *vd; - int opt_flag, calc_flag, undead_flag; - int duration = tick; + int opt_flag, calc_flag, undead_flag, val_flag = 0, tick_time = 0; nullpo_ret(bl); sc = status_get_sc(bl); @@ -4889,35 +5356,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if( !sc ) return 0; //Unable to receive status changes - if( sc->data[SC_REFRESH] ) - { - if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX && type != SC_STUN ) // Immune to all common status ailments except stun (iROWiki) - return 0; - switch( type ) - { - case SC_FEAR: - case SC_BURNING: - // Not implemented yet. (kRO 3-x balance update) - //case SC_POISONINGWEAPON: - //case SC_TOXIN: - //case SC_PARALYSE: - //case SC_VENOMBLEED: - //case SC_MAGICMUSHROOM: - //case SC_DEATHHURT: - //case SC_PYREXIA: - //case SC_OBLIVIONCURSE: - //case SC_LEECHESEND: - //case SC_FROSTMISTY: - //case SC_MARSHOFABYSS: - //case SC_DEEP_SLEEP: - //case SC_COLD: - //case SC_FREEZE_SP: - //case SC_MANDRAGORA: - return 0; - } - } - - if( status_isdead(bl) ) + if( status_isdead(bl) && type != SC_NOCHAT ) // SC_NOCHAT should work even on dead characters return 0; if( bl->type == BL_MOB && type != SC_SAFETYWALL && type != SC_PNEUMA ) @@ -4940,17 +5379,27 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val //Check for inmunities / sc fails switch (type) { - case SC_FREEZE: case SC_STONE: + if(sc->data[SC_POWER_OF_GAIA]) + return 0; + case SC_FREEZE: //Undead are immune to Freeze/Stone if (undead_flag && !(flag&1)) return 0; case SC_SLEEP: case SC_STUN: - case SC_BURNING: + case SC_FREEZING: if (sc->opt1) return 0; //Cannot override other opt1 status changes. [Skotlex] + if((type == SC_FREEZE || type == SC_FREEZING) && sc->data[SC_WARMER]) + return 0; //Immune to Frozen and Freezing status if under Warmer status. [Jobbie] + break; + + case SC_BURNING: + if(sc->opt1 || sc->data[SC_FREEZING]) + return 0; break; + case SC_SIGNUMCRUCIS: //Only affects demons and undead element (but not players) if((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) @@ -4967,13 +5416,16 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_OVERTHRUST: if (sc->data[SC_MAXOVERTHRUST]) return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex] + case SC_MAXOVERTHRUST: + if( sc->option&OPTION_MADOGEAR ) + return 0;//Overthrust and Overthrust Max cannot be used on Mado Gear [Ind] break; case SC_ADRENALINE: if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE))) return 0; if (sc->data[SC_QUAGMIRE] || sc->data[SC_DECREASEAGI] || - sc->data[SC_ADORAMUS] + sc->option&OPTION_MADOGEAR //Adrenaline doesn't affect Mado Gear [Ind] ) return 0; break; @@ -4981,16 +5433,17 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE2))) return 0; if (sc->data[SC_QUAGMIRE] || - sc->data[SC_DECREASEAGI] || - sc->data[SC_ADORAMUS] + sc->data[SC_DECREASEAGI] ) return 0; break; + case SC_MAGNIFICAT: + if( sc->option&OPTION_MADOGEAR ) //Mado is immune to magnificat + break; case SC_ONEHAND: case SC_MERC_QUICKEN: case SC_TWOHANDQUICKEN: - if(sc->data[SC_DECREASEAGI] || - sc->data[SC_ADORAMUS]) + if(sc->data[SC_DECREASEAGI]) return 0; case SC_CONCENTRATE: case SC_INCREASEAGI: @@ -5001,6 +5454,8 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_ASSNCROS: if (sc->data[SC_QUAGMIRE]) return 0; + if(sc->option&OPTION_MADOGEAR) + return 0;//Mado is immune to increase agi, wind walk, cart boost, etc (others above) [Ind] break; case SC_CLOAKING: //Avoid cloaking with no wall and low skill level. [Skotlex] @@ -5152,6 +5607,10 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1) return 0; break; + case SC_CAMOUFLAGE: + if( sd && pc_checkskill(sd, RA_CAMOUFLAGE) < 3 && !skill_check_camouflage(bl,NULL) ) + return 0; + break; } //Check for BOSS resistances @@ -5160,8 +5619,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val return 0; switch (type) { case SC_BLESSING: - if (!undead_flag && status->race!=RC_DEMON) - break; case SC_DECREASEAGI: case SC_PROVOKE: case SC_COMA: @@ -5170,7 +5627,26 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_RICHMANKIM: case SC_ROKISWEIL: case SC_FOGWALL: + case SC_FREEZING: + case SC_BURNING: // Place here until we have info about its behavior on Boss-monsters. [pakpil] + case SC_MARSHOFABYSS: case SC_ADORAMUS: + + // Exploid prevention - kRO Fix + case SC_PYREXIA: + case SC_DEATHHURT: + case SC_TOXIN: + case SC_PARALYSE: + case SC_VENOMBLEED: + case SC_MAGICMUSHROOM: + case SC_OBLIVIONCURSE: + case SC_LEECHESEND: + + // Ranger Effects + case SC_BITE: + case SC_ELECTRICSHOCKER: + case SC_MAGNETICFIELD: + return 0; } } @@ -5189,7 +5665,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val break; case SC_INCREASEAGI: status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER); - status_change_end(bl, SC_ADORAMUS, INVALID_TIMER); break; case SC_QUAGMIRE: status_change_end(bl, SC_CONCENTRATE, INVALID_TIMER); @@ -5197,7 +5672,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val status_change_end(bl, SC_WINDWALK, INVALID_TIMER); //Also blocks the ones below... case SC_DECREASEAGI: - case SC_ADORAMUS: status_change_end(bl, SC_CARTBOOST, INVALID_TIMER); //Also blocks the ones below... case SC_DONTFORGETME: @@ -5208,6 +5682,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER); status_change_end(bl, SC_ONEHAND, INVALID_TIMER); status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER); + status_change_end(bl, SC_ACCELERATION, INVALID_TIMER); break; case SC_ONEHAND: //Removes the Aspd potion effect, as reported by Vicious. [Skotlex] @@ -5255,10 +5730,9 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER); break; case SC_CARTBOOST: - if(sc->data[SC_DECREASEAGI] || sc->data[SC_ADORAMUS]) + if(sc->data[SC_DECREASEAGI]) { //Cancel Decrease Agi, but take no further effect [Skotlex] status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER); - status_change_end(bl, SC_ADORAMUS, INVALID_TIMER); return 0; } break; @@ -5312,10 +5786,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_FOOD_LUK_CASH: status_change_end(bl, SC_LUKFOOD, INVALID_TIMER); break; - case SC_FIGHTINGSPIRIT: - if (sc->data[SC_FIGHTINGSPIRIT]) - status_change_end(bl, SC_FIGHTINGSPIRIT, INVALID_TIMER); - break; } //Check for overlapping fails @@ -5355,8 +5825,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_MARIONETTE2: case SC_NOCHAT: case SC_CHANGE: //Otherwise your Hp/Sp would get refilled while still within effect of the last invocation. - case SC_FEAR: - case SC_BURNING: return 0; case SC_COMBO: case SC_DANCING: @@ -5413,10 +5881,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val { case SC_DECREASEAGI: case SC_INCREASEAGI: - case SC_ADORAMUS: val2 = 2 + val1; //Agi change - if (val3) //Canto Candidus: Add joblv/10 additional AGI (8/31/2011) - val2 += (val3/10); break; case SC_ENDURE: val2 = 7; // Hit-count [Celest] @@ -5456,6 +5921,8 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_EDP: // [Celest] val2 = val1 + 2; //Chance to Poison enemies. val3 = 50*(val1+1); //Damage increase (+50 +50*lv%) + if( sd )//[Ind] - iROwiki says each level increases its duration by 3 seconds + tick += pc_checkskill(sd,GC_RESEARCHNEWPOISON)*3000; break; case SC_POISONREACT: val2=(val1+1)/2 + val1/10; // Number of counters [Skotlex] @@ -5466,17 +5933,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val break; case SC_KYRIE: val2 = status->max_hp * (val1 * 2 + 10) / 100; //%Max HP to absorb - // val4 holds current about of party memebers when casting AB_PRAEFATIO, - // as Praefatio's barrier has more health and blocks more hits than Kyrie Elesion. - if( val4 < 1 ) //== PR_KYRIE - val3 = (val1 / 2 + 5); - else - { //== AB_PRAEFATIO - val2 += val4 * 2; //Increase barrier strength per party member. - val3 = 6 + val1; - } - if( sd ) - val1 = min(val1,pc_checkskill(sd,PR_KYRIE)); // use skill level to determine barrier health. + val3 = (val1 / 2 + 5); //Hits break; case SC_MAGICPOWER: //val1: Skill lv @@ -5601,10 +6058,10 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val //val3 : Brings the skilllv (merged into val1 here) //val4 : Partner if (val1 == CG_MOONLIT) - clif_status_change(bl,SI_MOONLIT,1,tick); + clif_status_change(bl,SI_MOONLIT,1,tick,0, 0, 0); val1|= (val3<<16); val3 = tick/1000; //Tick duration - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time break; case SC_LONGING: val2 = 500-100*val1; //Aspd penalty. @@ -5612,9 +6069,14 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_EXPLOSIONSPIRITS: val2 = 75 + 25*val1; //Cri bonus break; +#if RRMODE == 0 + /** + * Only in non-RE it's var is changed + **/ case SC_ASPDPOTION0: case SC_ASPDPOTION1: case SC_ASPDPOTION2: +#endif case SC_ASPDPOTION3: val2 = 50*(2+type-SC_ASPDPOTION0); break; @@ -5635,6 +6097,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); break; case SC_NOCHAT: + // [GodLesZ] FIXME: is this correct? a hardcoded interval of 60sec? what about configuration ?_? tick = 60000; val1 = battle_config.manner_system; //Mute filters. if (sd) @@ -5666,7 +6129,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_POISON: /* 毒 */ val3 = tick/1000; //Damage iterations if(val3 < 1) val3 = 1; - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time //val4: HP damage if (bl->type == BL_PC) val4 = (type == SC_DPOISON) ? 3 + status->max_hp/50 : 3 + status->max_hp*3/200; @@ -5680,7 +6143,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_BLEEDING: val4 = tick/10000; if (!val4) val4 = 1; - tick = 10000; + tick_time = 10000; // [GodLesZ] tick time break; case SC_S_LIFEPOTION: case SC_L_LIFEPOTION: @@ -5691,7 +6154,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if( val2 < 1 ) val2 = 1; if( (val4 = tick/(val2 * 1000)) < 1 ) val4 = 1; - tick = val2 * 1000; // val2 = Seconds between heals + tick_time = val2 * 1000; // [GodLesZ] tick time break; case SC_BOSSMAPINFO: if( sd != NULL ) @@ -5705,12 +6168,12 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val val1 = boss_md->bl.id; if( (val4 = tick/1000) < 1 ) val4 = 1; - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time } break; case SC_HIDING: val2 = tick/1000; - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time val3 = 0; // unused, previously speed adjustment val4 = val1+3; //Seconds before SP substraction happen. break; @@ -5740,7 +6203,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_SIGHTBLASTER: val3 = skill_get_splash(val2, val1); //Val2 should bring the skill-id. val2 = tick/250; - tick = 10; + tick_time = 10; // [GodLesZ] tick time break; //Permanent effects. @@ -5811,7 +6274,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val val2 = 12; //SP cost val4 = 10000; //Decrease at 10secs intervals. val3 = tick/val4; - tick = val4; + tick_time = val4; // [GodLesZ] tick time break; case SC_PARRYING: val2 = 20 + val1*3; //Block Chance @@ -5834,13 +6297,13 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1); if (!val4) val4 = 10000; //Val4 holds damage interval val3 = tick/val4; //val3 holds skill duration - tick = val4; + tick_time = val4; // [GodLesZ] tick time break; case SC_GOSPEL: if(val4 == BCT_SELF) { // self effect val2 = tick/10000; - tick = 10000; + tick_time = 10000; // [GodLesZ] tick time status_change_clear_buffs(bl,3); //Remove buffs/debuffs } break; @@ -6000,11 +6463,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val break; case SC_BLESSING: if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) - { val2 = val1; - if (val3) //Clementia: Add joblv/10 additional STR/INT/DEX (8/31/2011) - val2 += (val3/10); - } else val2 = 0; //0 -> Half stat. break; @@ -6110,7 +6569,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_SKA: val2 = tick/1000; val3 = rand()%100; //Def changes randomly every second... - tick = 1000; + tick_time = 1000; // [GodLesZ] tick time break; case SC_JAILED: //Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time. @@ -6213,70 +6672,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_KAIZEL: val2 = 10*val1; //% of life to be revived with break; - case SC_MILLENNIUMSHIELD: - val2 = (rand()%100<20) ? 4 : ((rand()%100<30) ? 3 : ((rand()%100<50) ? 2 : 0)); // 20% for 4, 30% for 3, 50% for 2 - val3 = 1000; // Initial Sheild health. (Additional sheilds health are set in battle.c when shield is broken.) - if( sd && val2 > 0) - clif_millenniumshield(sd,val2); - break; - case SC_STONEHARDSKIN: - val2 = (status->hp * 20 / 100); - if( val2 > 0 ) - status_heal(bl, -val2, 0, 0); // Reduce health by 20% - if ( sd ) - val3 = (sd->status.job_level * pc_checkskill(sd,RK_RUNEMASTERY)) / 4; - break; - case SC_VITALITYACTIVATION: - val2 = 50; // Increase HP recovery effects by 50% - val3 = 50; // Reduce SP recovery effects by 50% - break; - case SC_FIGHTINGSPIRIT: // attack is handled in battle.c - //val2 holds the source of the skill (1 = caster, 0 = party member.) - //official ASPD bonus appears to be (Rune Mastery Level / 10 x 4). - val3 = 10 * (sd?pc_checkskill(sd,RK_RUNEMASTERY):1); //Kind of dirty means to implement 4aspd increase. - break; - case SC_ABUNDANCE: - val3 = tick / 10000; - if(val3 < 1) - val3 = 1; - tick = 10000; - break; - case SC_EPICLESIS: - val2 = 5 * val1; // % HP gained * level of Epiclesis cast. - break; - case SC_ORATIO: - val2 = 2 * val1; // % Damage increased by level of Oratio cast. - break; - case SC_LAUDAAGNUS: - case SC_LAUDARAMUS: - val2 = 4+val1; // Bonus status points gained - break; - case SC_RENOVATIO: - val2 = tick / 5000; // Heal every 5 seconds. - tick = 5000; - break; - case SC_EXPIATIO: - val2 = 5*val1; // DEF reduced by 5*Skill Level percent. - break; - case SC_DUPLELIGHT: - val2 = 10+2*val1; //Chance of MELEE proc - val3 = 10+2*val1; //Chance of MAGIC proc - break; - - case SC_AB_SECRAMENT: - val2 = 10*val1; //Fixed cast time reduced by 10*Skill Level - break; - case SC_FEAR: - if (tick < 2000) - val3 = 2000; - else - val3 = tick - 2000; - tick = 2000; - break; - case SC_BURNING: - val4 = tick/2000; - tick = 3000; - break; // case SC_ARMOR_ELEMENT: // case SC_ARMOR_RESIST: // Mod your resistance against elements: @@ -6310,6 +6705,487 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_SPL_MATK: val2 = 2; // Splendide group break; + /** + * General + **/ + case SC_FEAR: + val2 = 2; + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_BURNING: + val4 = tick / 2000; // Total Ticks to Burn!! + tick_time = 2000; // [GodLesZ] tick time + break; + /** + * Rune Knight + **/ + case SC_DEATHBOUND: + val2 = 500 + 100 * val1; + break; + case SC_FIGHTINGSPIRIT: + val_flag |= 1|2; + break; + case SC_ABUNDANCE: + val4 = tick / 10000; + tick_time = 10000; // [GodLesZ] tick time + break; + case SC_GIANTGROWTH: + val2 = 10; // Triple damage success rate. + break; + /** + * Arch Bishop + **/ + case SC_RENOVATIO: + val4 = tick / 5000; + tick_time = 5000; + break; + case SC_SECRAMENT: + val2 = 10 * val1; + break; + case SC_VENOMIMPRESS: + val2 = 10 * val1; + val_flag |= 1|2; + break; + case SC_POISONINGWEAPON: + val_flag |= 1|2|4; + break; + case SC_WEAPONBLOCKING: + val2 = 10 + 2 * val1; // Chance + val4 = tick / 3000; + tick_time = 3000; // [GodLesZ] tick time + val_flag |= 1|2; + break; + case SC_TOXIN: + val4 = tick / 10000; + tick_time = 10000; // [GodLesZ] tick time + break; + case SC_MAGICMUSHROOM: + val4 = tick / 4000; + tick_time = 4000; // [GodLesZ] tick time + break; + case SC_PYREXIA: + val4 = tick / 3000; + tick_time = 4000; // [GodLesZ] tick time + break; + case SC_LEECHESEND: + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_OBLIVIONCURSE: + val4 = tick / 3000; + tick_time = 3000; // [GodLesZ] tick time + break; + case SC_ROLLINGCUTTER: + val_flag |= 1; + break; + case SC_CLOAKINGEXCEED: + val2 = ( val1 + 1 ) / 2; // Hits + val3 = ( val1 - 1 ) * 10; // Walk speed + val_flag |= 1|2|4; + if (bl->type == BL_PC) + val4 |= battle_config.pc_cloak_check_type&7; + else + val4 |= battle_config.monster_cloak_check_type&7; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_HALLUCINATIONWALK: + val2 = 50 * val1; // Evasion rate of physical attacks. Flee + val3 = 10 * val1; // Evasion rate of magical attacks. + val_flag |= 1|2|4; + break; + case SC_WHITEIMPRISON: + status_change_end(bl, SC_BURNING, -1); + status_change_end(bl, SC_FREEZING, -1); + status_change_end(bl, SC_FREEZE, -1); + status_change_end(bl, SC_STONE, -1); + break; + case SC_FREEZING: + status_change_end(bl, SC_BURNING, -1); + break; + case SC_READING_SB: + // val2 = sp reduction per second + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_SPHERE_1: + case SC_SPHERE_2: + case SC_SPHERE_3: + case SC_SPHERE_4: + case SC_SPHERE_5: + if( !sd ) + return 0; // Should only work on players. + val4 = tick / 1000; + if( val4 < 1 ) + val4 = 1; + tick_time = 1000; // [GodLesZ] tick time + val_flag |= 1; + break; + case SC_SHAPESHIFT: + switch( val1 ) + { + case 1: val2 = ELE_FIRE; break; + case 2: val2 = ELE_EARTH; break; + case 3: val2 = ELE_WIND; break; + case 4: val2 = ELE_WATER; break; + } + break; + case SC_ELECTRICSHOCKER: + case SC_CRYSTALIZE: + val4 = tick / 1000; + if( val4 < 1 ) + val4 = 1; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_CAMOUFLAGE: + //val3 |= battle_config.pc_camouflage_check_type&7; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_WUGDASH: + val4 = gettick(); //Store time at which you started running. + tick = -1; + break; + case SC__SHADOWFORM: + { + //struct map_session_data * s_sd = map_id2sd(val2); + //if( s_sd ) + // s_sd->shadowform_id = bl->id; + val4 = tick / 1000; + val_flag |= 1|2|4; + tick_time = 1000; // [GodLesZ] tick time + } + break; + case SC__STRIPACCESSORY: + if (!sd) + val2 = 20; + break; + case SC__INVISIBILITY: + val2 = 50 - 10 * val1; // ASPD + val3 = 20 * val1; // CRITICAL + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + val_flag |= 1|2; + break; + case SC__ENERVATION: + val2 = 20 + 10 * val1; // ATK Reduction + val_flag |= 1|2; + if( sd ) pc_delspiritball(sd,sd->spiritball,0); + break; + case SC__GROOMY: + val2 = 20 + 10 * val1; //ASPD. Need to confirm if Movement Speed reduction is the same. [Jobbie] + val3 = 20 * val1; //HIT + val_flag |= 1|2|4; + if( sd ) + { // Removes Animals + //if( pc_isriding(sd,OPTION_RIDING|OPTION_RIDING_DRAGON|OPTION_RIDING_WUG) ) pc_setriding(sd, 0); + //if( pc_iswarg(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUG); + if( pc_isfalcon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_FALCON); + if( sd->status.pet_id > 0 ) pet_menu(sd, 3); + if( merc_is_hom_active(sd->hd) ) merc_hom_vaporize(sd,1); + if( sd->md ) merc_delete(sd->md,3); + } + break; + case SC__LAZINESS: + val2 = 10 + 10 * val1; // Cast reduction + val3 = 10 * val1; // Flee Reduction + val_flag |= 1|2|4; + break; + case SC__UNLUCKY: + val2 = 10 * val1; // Crit and Flee2 Reduction + val_flag |= 1|2|4; + break; + case SC__WEAKNESS: + val2 = 10 * val1; + val_flag |= 1|2; + skill_strip_equip(bl,EQP_WEAPON|EQP_SHIELD,100,val1,tick); + break; + case SC__BLOODYLUST: + val_flag |= 1|2; + break; + case SC_GN_CARTBOOST: + if( val1 < 3 ) + val2 = 50; + else if( val1 < 5 ) + val2 = 75; + else + val2 = 100; + break; + case SC_PROPERTYWALK: + val_flag |= 1|2; + val3 = 0; + break; + case SC_WARMER: + status_change_end(bl, SC_FREEZE, -1); + status_change_end(bl, SC_FREEZING, -1); + status_change_end(bl, SC_CRYSTALIZE, -1); + break; + case SC_STRIKING: + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_BLOODSUCKER: + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_SWINGDANCE: + val2 = 4 * val1; // Walk speed and aspd reduction. + break; + case SC_SYMPHONYOFLOVER: + case SC_RUSHWINDMILL: + case SC_ECHOSONG: + val2 = 6 * val1; + val2 += val3; //Adding 1% * Lesson Bonus + val2 += (int)(val4*2/10); //Adding 0.2% per JobLevel + break; + case SC_MOONLITSERENADE: + val2 = 10 * val1; + break; + case SC_HARMONIZE: + val2 = 3 + 2 * val1; + break; + case SC_VOICEOFSIREN: + val4 = tick / 2000; + tick_time = 2000; // [GodLesZ] tick time + break; + case SC_DEEPSLEEP: + val4 = tick / 2000; + tick_time = 2000; // [GodLesZ] tick time + break; + case SC_SIRCLEOFNATURE: + val2 = 1 + val1; //SP consume + val3 = 40 * val1; //HP recovery + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_SONGOFMANA: + val3 = 10 + (2 * val2); + val4 = tick/3000; + tick_time = 3000; // [GodLesZ] tick time + break; + case SC_SATURDAYNIGHTFEVER: + if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1); + if (!val4) val4 = 3000; + val3 = tick/val4; + tick_time = val4; // [GodLesZ] tick time + break; + case SC_GLOOMYDAY: + val2 = 3 + 2 * val1; // Flee reduction. + val3 = 3 * val1; // ASPD reduction. + break; + case SC_SITDOWN_FORCE: + case SC_BANANA_BOMB_SITDOWN: + if( sd && !pc_issit(sd) ) + { + pc_setsit(sd); + skill_sit(sd,1); + clif_sitting(bl); + } + break; + case SC_DANCEWITHWUG: + val3 = (5 * val1) + (1 * val2); //Still need official value. + break; + case SC_LERADSDEW: + val3 = (5 * val1) + (1 * val2); + break; + case SC_MELODYOFSINK: + val3 = (5 * val1) + (1 * val2); + break; + case SC_BEYONDOFWARCRY: + val3 = (5 * val1) + (1 * val2); + break; + case SC_UNLIMITEDHUMMINGVOICE: + { + struct unit_data *ud = unit_bl2ud(bl); + if( ud == NULL ) return 0; + ud->state.skillcastcancel = 0; + val3 = 15 - (2 * val2); + } + break; + case SC_REFLECTDAMAGE: + val2 = 15 + 5 * val1; + val3 = (val1==5)?20:(val1+4)*2; // SP consumption + val4 = tick/10000; + tick_time = 10000; // [GodLesZ] tick time + break; + case SC_FORCEOFVANGUARD: // This is not the official way to handle it but I think we should use it. [pakpil] + val2 = 20 + 12 * (val1 - 1); // Chance + val3 = 5 + (2 * val1); // Max rage counters + tick_time = 6000; // [GodLesZ] tick time + val_flag |= 1|2|4; + break; + case SC_EXEEDBREAK: + val1 *= 150; // 150 * skill_lv + if( sd ) + { // Chars. + struct item_data *id = sd->inventory_data[sd->equip_index[EQI_HAND_R]]; + if( id ) val1 += (id->weight/10 * id->wlv * status_get_lv(bl) / 100); // (weapon_weight * weapon_level * base_lvl)/100 + val1 += 15 * sd->status.job_level; // 15 * job_lvl + } + else // Mobs + val1 += (400 * status_get_lv(bl) / 100) + (15 * (status_get_lv(bl) / 2)); // About 1138% at mob_lvl 99. Is an aproximation to a standard weapon. [pakpil] + break; + + case SC_PRESTIGE: // Bassed on suggested formula in iRO Wiki and some test, still need more test. [pakpil] + val2 = ((status->int_ + status->luk) / 6) + 5; // Chance to evade magic damage. + val1 *= 15; // Defence added + if( sd ) + val1 += 10 * pc_checkskill(sd,CR_DEFENDER); + val_flag |= 1|2; + break; + case SC_BANDING: + tick_time = 5000; // [GodLesZ] tick time + val_flag |= 1; + break; + case SC_SHIELDSPELL_DEF: + case SC_SHIELDSPELL_MDEF: + case SC_SHIELDSPELL_REF: + val_flag |= 1|2; + break; + case SC_MAGNETICFIELD: + val3 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + break; + case SC_INSPIRATION: + if( sd ) + { + val2 = (40 * val1) + (3 * sd->status.job_level); // ATK bonus + val3 = (sd->status.job_level / 10) * 2 + 12; // All stat bonus + } + val4 = tick / 1000; + tick_time = 1000; // [GodLesZ] tick time + status_change_clear_buffs(bl,3); //Remove buffs/debuffs + break; + case SC_SPELLFIST: + case SC_CURSEDCIRCLE_ATKER: + val_flag |= 1|2|4; + break; + case SC_CRESCENTELBOW: + val2 = 94 + val1; + val_flag |= 1|2; + break; + case SC_LIGHTNINGWALK: + val1 = 88 + 2 * val1; + val_flag |= 1; + break; + case SC_RAISINGDRAGON: + val3 = tick / 5000; + tick_time = 5000; // [GodLesZ] tick time + break; + case SC_GT_CHANGE: + if( sd ) val2 = (13 * val1 / 2) * sd->status.agi; //Aspd - old formula. + val3 = 20 + 1 * val1; //Base Atk, Reduction to DEF & MDEF + break; + case SC_GT_REVITALIZE: + val2 = 5 * val1; //Custom value VIT, ASPD, SPEED bonus. + val3 = 60 + 40 * val1; //HP recovery + break; + case SC_PYROTECHNIC_OPTION: + val2 = 60; // Watk TODO: Renewal (Atk2) + val3 = 11; // % Increase damage. + val_flag |= 1|2|4; + break; + case SC_HEATER_OPTION: + val2 = 120; // Watk. TODO: Renewal (Atk2) + val3 = 33; // % Increase effects. + val4 = 3; // Change into fire element. + val_flag |= 1|2|4; + break; + case SC_TROPIC_OPTION: + val2 = 180; // Watk. TODO: Renewal (Atk2) + val3 = MG_FIREBOLT; + break; + case SC_AQUAPLAY_OPTION: + val2 = 40; // Matk. TODO: Renewal (Matk1) + val3 = 33; // % Increase effects. + val_flag |= 1|2|4; + break; + case SC_COOLER_OPTION: + val2 = 80; // % Freezing chance + val3 = 33; // % increased damage + val4 = 1; // Change into water elemet + val_flag |= 1|2|4; + break; + case SC_CHILLY_AIR_OPTION: + val2 = 120; // Matk. TODO: Renewal (Matk1) + val3 = MG_COLDBOLT; + val_flag |= 1|2; + break; + case SC_GUST_OPTION: + val2 = 33; + val_flag |= 1|2; + break; + case SC_WIND_STEP_OPTION: + val2 = 50; // % Increase speed and flee. + break; + case SC_BLAST_OPTION: + val2 = 33; + val3 = 4; + val_flag |= 1|2|4; + break; + case SC_WILD_STORM_OPTION: + val2 = MG_LIGHTNINGBOLT; + val_flag |= 1|2; + break; + case SC_PETROLOGY_OPTION: + val2 = 5; + val3 = 33; + val_flag |= 1|2|4; + break; + case SC_CURSED_SOIL_OPTION: + val2 = 10; + val3 = 33; + val4 = 2; + val_flag |= 1|2|4; + break; + case SC_UPHEAVAL_OPTION: + val2 = WZ_EARTHSPIKE; + val_flag |= 1|2; + break; + case SC_CIRCLE_OF_FIRE_OPTION: + val2 = 300; + val_flag |= 1|2; + break; + case SC_FIRE_CLOAK_OPTION: + case SC_WATER_DROP_OPTION: + case SC_WIND_CURTAIN_OPTION: + case SC_STONE_SHIELD_OPTION: + val2 = 20; // Elemental modifier. Not confirmed. + break; + case SC_CIRCLE_OF_FIRE: + case SC_FIRE_CLOAK: + case SC_WATER_DROP: + case SC_WATER_SCREEN: + case SC_WIND_CURTAIN: + case SC_WIND_STEP: + case SC_STONE_SHIELD: + case SC_SOLID_SKIN: + val2 = 10; + tick_time = 2000; // [GodLesZ] tick time + break; + case SC_WATER_BARRIER: + val2 = 40; // Increasement. Mdef1 ??? + val3 = 20; // Reductions. Atk2, Flee1, Matk1 ???? + val_flag |= 1|2|4; + break; + case SC_ZEPHYR: + val2 = 22; // Flee. + break; + case SC_TIDAL_WEAPON: + val2 = 20; // Increase Elemental's attack. + break; + case SC_ROCK_CRUSHER: + case SC_ROCK_CRUSHER_ATK: + case SC_POWER_OF_GAIA: + val2 = 33; + break; + case SC_MELON_BOMB: + case SC_BANANA_BOMB: + val1 = 15; + break; + case SC_STOMACHACHE: + val2 = 8; // SP consume. + val4 = tick / 10000; + tick_time = 10000; // [GodLesZ] tick time + break; default: if( calc_flag == SCB_NONE && StatusSkillChangeTable[type] == 0 && StatusIconChangeTable[type] == 0 ) @@ -6341,7 +7217,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_STUN: case SC_SLEEP: case SC_STONE: - case SC_BURNING: if (sd && pc_issit(sd)) //Avoid sprite sync problems. pc_setstand(sd); case SC_TRICKDEAD: @@ -6356,13 +7231,15 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_CLOSECONFINE2: case SC_ANKLE: case SC_SPIDERWEB: - case SC_FEAR: + case SC_ELECTRICSHOCKER: unit_stop_walking(bl,1); break; case SC_HIDING: case SC_CLOAKING: + case SC_CLOAKINGEXCEED: case SC_CHASEWALK: case SC_WEIGHT90: + case SC_CAMOUFLAGE: unit_stop_attack(bl); break; case SC_SILENCE: @@ -6376,11 +7253,12 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val switch(type) { //OPT1 - case SC_STONE: sc->opt1 = OPT1_STONEWAIT; break; - case SC_FREEZE: sc->opt1 = OPT1_FREEZE; break; - case SC_STUN: sc->opt1 = OPT1_STUN; break; - case SC_SLEEP: sc->opt1 = OPT1_SLEEP; break; - case SC_BURNING: sc->opt1 = OPT1_BURNING; break; + case SC_STONE: sc->opt1 = OPT1_STONEWAIT; break; + case SC_FREEZE: sc->opt1 = OPT1_FREEZE; break; + case SC_STUN: sc->opt1 = OPT1_STUN; break; + case SC_SLEEP: sc->opt1 = OPT1_SLEEP; break; + case SC_BURNING: sc->opt1 = OPT1_BURNING; break; // Burning need this to be showed correctly. [pakpil] + case SC_WHITEIMPRISON: sc->opt1 = OPT1_IMPRISON; break; //OPT2 case SC_POISON: sc->opt2 |= OPT2_POISON; break; case SC_CURSE: sc->opt2 |= OPT2_CURSE; break; @@ -6390,7 +7268,6 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_ANGELUS: sc->opt2 |= OPT2_ANGELUS; break; case SC_BLEEDING: sc->opt2 |= OPT2_BLEEDING; break; case SC_DPOISON: sc->opt2 |= OPT2_DPOISON; break; - case SC_FEAR: sc->opt2 |= OPT2_FEAR; break; //OPT3 case SC_TWOHANDQUICKEN: case SC_ONEHAND: @@ -6486,6 +7363,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val opt_flag = 2; break; case SC_CLOAKING: + case SC_CLOAKINGEXCEED: sc->option |= OPTION_CLOAK; opt_flag = 2; break; @@ -6532,11 +7410,15 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val calc_flag&=~SCB_DYE; } - if( vd && ((pcdb_checkid(vd->class_) || bl->type == BL_MER ) //Only for players sprites, client crashes if they receive this for a mob o.O [Skotlex] - || (bl->type == BL_MOB && type == SC_ORATIO)) ) // Required to show the proper status for monsters. Possible this may need an overhaul. - clif_status_change(bl,StatusIconChangeTable[type],1,duration); + if( vd && (pcdb_checkid(vd->class_) || bl->type == BL_MER || bl->type == BL_MOB ) ) + clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0); else if( sd ) //Send packet to self otherwise (disguised player?) clif_status_load(bl,StatusIconChangeTable[type],1); + /** + * used as temporary storage for scs with interval ticks, so that the actual duration is sent to the client first. + **/ + if( tick_time ) + tick = tick_time; //Don't trust the previous sce assignment, in case the SC ended somewhere between there and here. if((sce=sc->data[type])) @@ -6590,6 +7472,16 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val case SC_MERC_SPUP: status_percent_heal(bl, 0, 100); // Recover Full SP break; + /** + * Ranger + **/ + case SC_WUGDASH: + { + struct unit_data *ud = unit_bl2ud(bl); + if( ud ) + ud->state.running = unit_wugdash(bl, sd); + } + break; case SC_COMBO: switch (sce->val1) { case TK_STORMKICK: @@ -6725,8 +7617,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const return 0; if (tid == INVALID_TIMER) { - if( (type == SC_ENDURE) && sce->val4 ) - //Do not end infinite endure or speed adjustment. + if (type == SC_ENDURE && sce->val4) + //Do not end infinite endure. return 0; if (sce->timer != INVALID_TIMER) //Could be a SC with infinite duration delete_timer(sce->timer,status_change_timer); @@ -6911,7 +7803,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const } if((sce->val1&0xFFFF) == CG_MOONLIT) - clif_status_change(bl,SI_MOONLIT,0,0); + clif_status_change(bl,SI_MOONLIT,0,0,0,0,0); status_change_end(bl, SC_LONGING, INVALID_TIMER); } @@ -6919,7 +7811,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const case SC_NOCHAT: if (sd && sd->status.manner < 0 && tid != INVALID_TIMER) sd->status.manner = 0; - if (sd) + if (sd && tid == INVALID_TIMER) { clif_changestatus(&sd->bl,SP_MANNER,sd->status.manner); clif_updatestatus(sd,SP_MANNER); @@ -7056,10 +7948,88 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const status_change_end(tbl, SC_STOP, INVALID_TIMER); } break; + /** + * 3rd Stuff + **/ case SC_MILLENNIUMSHIELD: - if ( sd ) - clif_millenniumshield(sd,0); + clif_millenniumshield(sd,0); + break; + case SC_HALLUCINATIONWALK: + sc_start(bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill_get_time2(GC_HALLUCINATIONWALK,sce->val1)); + break; + case SC_WHITEIMPRISON: + if( tid == -1 ) + break; // Terminated by Damage + clif_damage(bl,bl,0,0,0,400*sce->val1,0,0,0); + status_zap(bl,400*sce->val1,0); + break; + case SC_WUGDASH: + { + struct unit_data *ud = unit_bl2ud(bl); + if (ud) { + ud->state.running = 0; + if (ud->walktimer != -1) + unit_stop_walking(bl,1); + } + } break; + case SC_ADORAMUS: + status_change_end(bl, SC_BLIND, -1); + break; + /* + case SC__SHADOWFORM: + { + struct map_session_data *s_sd = map_id2sd(sce->val2); + if( !s_sd ) + break; + s_sd->shadowform_id = 0; + } + break; + case SC_SITDOWN_FORCE: + if( sd && pc_issit(sd) ) + { + pc_setstand(sd); + clif_standing(bl,true); + } + break; + case SC_NEUTRALBARRIER_MASTER: + case SC_STEALTHFIELD_MASTER: + if( sce->val2 ) + { + struct skill_unit_group* group = skill_id2group(sce->val2); + sce->val2 = 0; + skill_delunitgroup(group); + } + break; + case SC_BANDING: + { + struct skill_unit_group *group; + if(sce->val4) + { + group = skill_id2group(sce->val4); + sce->val4 = 0; + skill_delunitgroup(group); + } + } + break; + case SC_CURSEDCIRCLE_ATKER: + if( sce->val3 ) + map_foreachinrange(status_change_timer_sub, bl, skill_get_splash(SR_CURSEDCIRCLE, sce->val1),BL_CHAR, bl, sce, SC_CURSEDCIRCLE_TARGET, gettick()); + break; + case SC_RAISINGDRAGON: + if( sd && sce->val2 && !pc_isdead(sd) ) + { + int i; + i = min(sd->spiritball,5); + pc_delspiritball(sd, sd->spiritball, 0); + status_change_end(bl, SC_EXPLOSIONSPIRITS, -1); + while( i > 0 ) + { + pc_addspiritball(sd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), 5); + --i; + } + } + break;*/ } opt_flag = 1; @@ -7068,7 +8038,6 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const case SC_FREEZE: case SC_STUN: case SC_SLEEP: - case SC_BURNING: sc->opt1 = 0; break; @@ -7084,15 +8053,13 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const case SC_SIGNUMCRUCIS: sc->opt2 &= ~OPT2_SIGNUMCRUCIS; break; - case SC_FEAR: - sc->opt2 &= ~OPT2_FEAR; - break; case SC_HIDING: sc->option &= ~OPTION_HIDE; opt_flag|= 2|4; //Check for warp trigger + AoE trigger break; case SC_CLOAKING: + case SC_CLOAKINGEXCEED: sc->option &= ~OPTION_CLOAK; opt_flag|= 2; break; @@ -7222,9 +8189,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const } //On Aegis, when turning off a status change, first goes the sc packet, then the option packet. - if( vd && ((pcdb_checkid(vd->class_) || bl->type == BL_MER ) - || (bl->type == BL_MOB && type == SC_ORATIO)) ) // Required to remove SI_ORATIO indicator from monsters. - clif_status_change(bl,StatusIconChangeTable[type],0,0); + if( vd && (pcdb_checkid(vd->class_) || bl->type == BL_MER ) ) + clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0); else if (sd) clif_status_load(bl,StatusIconChangeTable[type],0); @@ -7617,60 +8583,460 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) } break; case SC_ABUNDANCE: - if((--sce->val3) > 0) + if(--(sce->val4) > 0) { - int sp = 60; - if(sd) { - if( sd->status.sp < sd->status.max_sp ) + if( !sc->data[SC_BERSERK] ) + status_heal(bl,0,60,0); + sc_timer_next(10000+tick, status_change_timer, bl->id, data); + } + break; + + case SC_PYREXIA: + if( --(sce->val4) >= 0 ) + { + bool flag; + map_freeblock_lock(); + clif_damage(bl,bl,tick,status_get_amotion(bl),0,100,0,0,0); + status_fix_damage(NULL,bl,100,0); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) + { + if( sce->val4 == 10 ) + sc_start(bl,SC_BLIND,100,sce->val1,30000); // Blind status for the final 30 seconds + sc_timer_next(3000+tick,status_change_timer,bl->id,data); + } + return 0; + } + break; + + case SC_LEECHESEND: + if( --(sce->val4) >= 0 ) + { + bool flag; + int damage = status->max_hp/100; + if( sd && (sd->status.class_ == JOB_GUILLOTINE_CROSS || sd->status.class_ == JOB_GUILLOTINE_CROSS_T ) ) + damage += 3 * status->vit; + else + damage += 7 * status->vit; + + unit_skillcastcancel(bl,2); + + map_freeblock_lock(); + status_zap(bl,damage,0); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) { + sc_timer_next(1000 + tick, status_change_timer, bl->id, data ); + } + return 0; + } + break; + + case SC_MAGICMUSHROOM: + if( --(sce->val4) >= 0 ) + { + bool flag = 0; + int damage = status->max_hp * 3 / 100; + if( status->hp <= damage ) + damage = status->hp - 1; // Cannot Kill + + if( damage > 0 ) + { // 3% Damage each 4 seconds + map_freeblock_lock(); + status_zap(bl,damage,0); + flag = !sc->data[type]; // Killed? Should not + map_freeblock_unlock(); + } + + if( !flag ) + { // Random Skill Cast + if( sd ) { - if( sd->status.sp + sp > sd->status.max_sp ) //No overhealing SP. - sp = sd->status.max_sp - sd->status.sp; - clif_heal(sd->fd,SP_SP,sp); - status_heal(bl, 0, sp, 0); + int mushroom_skillid = 0, i; + unit_stop_attack(bl); + unit_skillcastcancel(bl,1); + do + { + i = rand() % MAX_SKILL_MAGICMUSHROOM_DB; + mushroom_skillid = skill_magicmushroom_db[i].skillid; + } + while( mushroom_skillid == 0 ); + + switch( skill_get_casttype(mushroom_skillid) ) + { // Magic Mushroom skills are buffs or area damage + case CAST_GROUND: + skill_castend_pos2(bl,bl->x,bl->y,mushroom_skillid,1,tick,0); + break; + case CAST_NODAMAGE: + skill_castend_nodamage_id(bl,bl,mushroom_skillid,1,tick,0); + break; + case CAST_DAMAGE: + skill_castend_damage_id(bl,bl,mushroom_skillid,1,tick,0); + break; + } } - } - sc_timer_next(10000+tick, status_change_timer, bl->id, data); + + clif_emotion(bl,18); + sc_timer_next(4000+tick,status_change_timer,bl->id,data); + } + return 0; + } + break; + + case SC_TOXIN: + if( --(sce->val4) >= 0 ) + { //Damage is every 10 seconds including 3%sp drain. + bool flag; + map_freeblock_lock(); + clif_damage(bl,bl,tick,status_get_amotion(bl),1,1,0,0,0); + status_damage(NULL,bl,1,status->max_sp*3/100,0,16); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) { + sc_timer_next(10000 + tick, status_change_timer, bl->id, data ); + } + return 0; + } + break; + + case SC_OBLIVIONCURSE: + if( --(sce->val4) >= 0 ) + { + clif_emotion(bl,1); + sc_timer_next(3000 + tick, status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_WEAPONBLOCKING: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl,0,3) ) + break; + sc_timer_next(3000+tick,status_change_timer,bl->id,data); + return 0; } break; + + case SC_CLOAKINGEXCEED: + if(!status_charge(bl,0,10-sce->val1)) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + case SC_RENOVATIO: - if((--sce->val2) > 0) { - int heal = status->max_hp * 3 / 100; - if( status->hp + heal > status->max_hp ) - heal = status->max_hp - status->hp; - if(heal > 0) - { - clif_heal(sd->fd,SP_HP,heal); - status_heal(bl, heal, 0, 0); + if( --(sce->val4) >= 0 ) + { + status_heal(bl, status->max_hp * 3 / 100, 0, 2); + sc_timer_next(5000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_BURNING: + if( --(sce->val4) >= 0 ) + { + struct block_list *src = map_id2bl(sce->val3); + int flag, damage = 3 * status_get_max_hp(bl) / 100; // Non Elemental Damage + if( status ) + damage += battle_attr_fix(NULL, bl, sce->val2, ELE_FIRE, status->def_ele, status->ele_lv); + + map_freeblock_lock(); + status_fix_damage(src,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,0,0)); + flag = !sc->data[type]; + map_freeblock_unlock(); + if( !flag ) {// Target still lives. [LimitLine] + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); } - sc_timer_next(5000+tick, status_change_timer, bl->id, data); return 0; } break; + case SC_FEAR: - if(sce->val3 > 0) + if( --(sce->val4) >= 0 ) { - sc_timer_next(sce->val3+tick, status_change_timer, bl->id, data); - sce->val3 = 0; + if( sce->val2 > 0 ) + sce->val2--; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; } break; - case SC_BURNING: - if(--(sce->val4) >= 0) + + case SC_SPHERE_1: + case SC_SPHERE_2: + case SC_SPHERE_3: + case SC_SPHERE_4: + case SC_SPHERE_5: + if( --(sce->val4) >= 0 ) { - int flag, hp = 0; - struct block_list *src = map_id2bl(sce->val2); + if( !status_charge(bl, 0, 1) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_READING_SB: + if( !status_charge(bl, 0, sce->val2) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC_ELECTRICSHOCKER: + if( --(sce->val4) >= 0 ) + { + status_charge(bl, 0, status->max_sp / 100 * sce->val1 ); + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_CAMOUFLAGE: + if( !status_charge(bl,0,7 - sce->val1) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC__REPRODUCE: + if(!status_charge(bl, 0, 1)) + break; + sc_timer_next(1000+tick, status_change_timer, bl->id, data); + return 0; + + case SC__SHADOWFORM: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl, 0, sce->val1 - (sce->val1 - 1)) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC__INVISIBILITY: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl, 0, (status->sp * 6 - sce->val1) / 100) )// 6% - skilllv. + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_STRIKING: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl,0, sce->val1 ) ) + break; + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; - hp = 1000+(status->max_hp*3 /100); + case SC_BLOODSUCKER: + if( --(sce->val4) >= 0 ) + { + struct block_list *src = map_id2bl(sce->val2); + int damage; + bool flag; + if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) ) + break; map_freeblock_lock(); - clif_damage(bl,bl,tick,status->amotion,0,hp,1,9,0); - status_damage(src, bl, hp, 0, 0, 1); + damage = skill_attack(skill_get_type(GN_BLOOD_SUCKER), src, src, bl, GN_BLOOD_SUCKER, sce->val1, tick, 0); + flag = !sc->data[type]; + map_freeblock_unlock(); + status_heal(src, damage, 0, 0); + clif_skill_nodamage(src, bl, GN_BLOOD_SUCKER, 0, 1); + if (!flag) { + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + } + return 0; + } + break; + + case SC_VOICEOFSIREN: + if( --(sce->val4) >= 0 ) + { + clif_emotion(bl,3); + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_DEEPSLEEP: + if( --(sce->val4) >= 0 ) + { // Recovers 1% HP/SP every 2 seconds. + status_heal(bl, status->max_hp / 100, status->max_sp / 100, 2); + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_SIRCLEOFNATURE: + if( --(sce->val4) >= 0 ) + { + if( !status_charge(bl,0,sce->val2) ) + break; + status_heal(bl, sce->val3, 0, 1); + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_SONGOFMANA: + if( --(sce->val4) >= 0 ) + { + status_heal(bl,0,sce->val3,3); + sc_timer_next(3000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + + case SC_SATURDAYNIGHTFEVER: + // 1% HP/SP drain every 3 seconds [Jobbie] + if( --(sce->val3) >= 0 ) + { + int hp = status->hp / 100; + int sp = status->sp / 100; + if( !status_charge(bl, hp, sp) ) + break; + sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_CRYSTALIZE: + if( --(sce->val4) >= 0 ) + { // Drains 2% of HP and 1% of SP every seconds. + status_charge(bl, status->max_hp * 2 / 100, status->max_sp / 100); + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_FORCEOFVANGUARD: + if( !status_charge(bl,0,20) ) + break; + sc_timer_next(6000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC_BANDING: + if( status_charge(bl, 0, 7 - sce->val1) ) + { + //if( sd ) pc_banding(sd, sce->val1); + sc_timer_next(5000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_REFLECTDAMAGE: + if( --(sce->val4) >= 0 ) { + if( !status_charge(bl,0,sce->val3) ) + break; + sc_timer_next(10000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + case SC_OVERHEAT_LIMITPOINT: + if( --(sce->val1) > 0 ) { // Cooling + sc_timer_next(30000 + tick, status_change_timer, bl->id, data); + } + break; + + case SC_OVERHEAT: + { + int flag, damage = status->max_hp / 100; // Suggestion 1% each second + if( damage >= status->hp ) damage = status->hp - 1; // Do not kill, just keep you with 1 hp minimum + map_freeblock_lock(); + status_fix_damage(NULL,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,0,0)); flag = !sc->data[type]; map_freeblock_unlock(); - if(!flag) - sc_timer_next( 3000 + tick, status_change_timer, bl->id, data); + if( !flag ) { + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + } + } + break; + + case SC_MAGNETICFIELD: + { + if( --(sce->val3) <= 0 ) + break; // Time out + if( sce->val2 == bl->id ) + { + if( !status_charge(bl,0,14 + (3 * sce->val1)) ) + break; // No more SP status should end, and in the next second will end for the other affected players + } + else + { + struct block_list *src = map_id2bl(sce->val2); + struct status_change *ssc; + if( !src || (ssc = status_get_sc(src)) == NULL || !ssc->data[SC_MAGNETICFIELD] ) + break; // Source no more under Magnetic Field + } + sc_timer_next(1000 + tick, status_change_timer, bl->id, data); + } + break; + + case SC_INSPIRATION: + if(--(sce->val4) >= 0) + { + int hp = status->max_hp * (7-sce->val1) / 100; + int sp = status->max_sp * (9-sce->val1) / 100; + + if( !status_charge(bl,hp,sp) ) break; + + sc_timer_next(1000+tick,status_change_timer,bl->id, data); return 0; } break; + + case SC_RAISINGDRAGON: + // 1% every 5 seconds [Jobbie] + if( --(sce->val3)>0 && status_charge(bl, sce->val2, 0) ) + { + if( !sc->data[type] ) return 0; + sc_timer_next(5000 + tick, status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_CIRCLE_OF_FIRE: + case SC_FIRE_CLOAK: + case SC_WATER_DROP: + case SC_WATER_SCREEN: + case SC_WIND_CURTAIN: + case SC_WIND_STEP: + case SC_STONE_SHIELD: + case SC_SOLID_SKIN: + if( !status_charge(bl,0,sce->val2) ) + { + struct block_list *s_bl = battle_get_master(bl); + if( s_bl ) + status_change_end(s_bl,type+1,-1); + status_change_end(bl,type,-1); + break; + } + sc_timer_next(2000 + tick, status_change_timer, bl->id, data); + return 0; + + case SC_STOMACHACHE: + if( --(sce->val4) > 0 ) + { + status_charge(bl,0,sce->val2); // Reduce 8 every 10 seconds. + if( sd && !pc_issit(sd) ) // Force to sit every 10 seconds. + { + pc_stop_walking(sd,1|4); + pc_stop_attack(sd); + pc_setsit(sd); + clif_sitting(bl); + } + sc_timer_next(10000 + tick, status_change_timer, bl->id, data); + } + break; + } // default for all non-handled control paths is to end the status @@ -7701,11 +9067,15 @@ int status_change_timer_sub(struct block_list* bl, va_list ap) case SC_CONCENTRATE: status_change_end(bl, SC_HIDING, INVALID_TIMER); status_change_end(bl, SC_CLOAKING, INVALID_TIMER); + status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); + status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); break; case SC_RUWACH: /* ルアフ */ - if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING])) { + if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] || tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED])) { status_change_end(bl, SC_HIDING, INVALID_TIMER); status_change_end(bl, SC_CLOAKING, INVALID_TIMER); + status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER); + status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); if(battle_check_target( src, bl, BCT_ENEMY ) > 0) skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0); } @@ -7731,7 +9101,7 @@ int status_change_timer_sub(struct block_list* bl, va_list ap) /*========================================== * Clears buffs/debuffs of a character. - * type&1 -> buffs, type&2 -> debuffs, type&4 -> sc_refresh + * type&1 -> buffs, type&2 -> debuffs *------------------------------------------*/ int status_change_clear_buffs (struct block_list* bl, int type) { @@ -7790,13 +9160,6 @@ int status_change_clear_buffs (struct block_list* bl, int type) case SC_EXPBOOST: case SC_JEXPBOOST: case SC_ITEMBOOST: - case SC_GIANTGROWTH: - case SC_REFRESH: - case SC_STONEHARDSKIN: - case SC_VITALITYACTIVATION: - case SC_FIGHTINGSPIRIT: - case SC_ABUNDANCE: - case SC_MILLENNIUMSHIELD: continue; //Debuffs that can be removed. @@ -7813,33 +9176,8 @@ int status_change_clear_buffs (struct block_list* bl, int type) case SC_STRIPSHIELD: case SC_STRIPARMOR: case SC_STRIPHELM: - //Cannot be removed by Refresh. - if (type&4) - continue; - case SC_ADORAMUS: if (!(type&2)) continue; - - //Debuffs that can be removed by Refresh.. - case SC_FEAR: - case SC_BURNING: - //case SC_POISONINGWEAPON: - //case SC_TOXIN: - //case SC_PARALYSE: - //case SC_VENOMBLEED: - //case SC_MAGICMUSHROOM: - //case SC_DEATHHURT: - //case SC_PYREXIA: - //case SC_OBLIVIONCURSE: - //case SC_LEECHESEND: - //case SC_FROSTMISTY: - //case SC_MARSHOFABYSS: - //case SC_DEEP_SLEEP: - //case SC_COLD: - //case SC_FREEZE_SP: - //case SC_MANDRAGORA: - if (!(type&4)) - continue; break; //The rest are buffs that can be removed. case SC_BERSERK: @@ -8065,6 +9403,25 @@ static int status_natural_heal_timer(int tid, unsigned int tick, int id, intptr_ * size_fix.txt - size adjustment table for weapons * refine_db.txt - refining data table *------------------------------------------*/ +#if RRMODE +static bool status_readdb_job_re(char* fields[], int columns, int current) { + int idx, class_; + unsigned int i; + + class_ = atoi(fields[0]); + + if(!pcdb_checkid(class_)) { + ShowWarning("status_readdb_job_re: Invalid job class %d specified.\n", class_); + return false; + } + idx = pc_class2idx(class_); + + for(i = 0; i < RE_JOB_DB_MAX; i++) { + re_job_db[idx][i] = atoi(fields[i+1]); + } + return true; +} +#endif static bool status_readdb_job1(char* fields[], int columns, int current) {// Job-specific values (weight, HP, SP, ASPD) int idx, class_; @@ -8088,7 +9445,6 @@ static bool status_readdb_job1(char* fields[], int columns, int current) { aspd_base[idx][i] = atoi(fields[i+5]); } - shield_aspd_base[idx] = atoi(fields[MAX_WEAPON_TYPE+5]); return true; } @@ -8151,8 +9507,9 @@ int status_readdb(void) memset(hp_coefficient2, 0, sizeof(hp_coefficient2)); memset(sp_coefficient, 0, sizeof(sp_coefficient)); memset(aspd_base, 0, sizeof(aspd_base)); - memset(shield_aspd_base, 0, sizeof(shield_aspd_base)); - +#if RRMODE + memset(re_job_db, 0, sizeof(re_job_db)); +#endif // job_db2.txt memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus @@ -8174,10 +9531,14 @@ int status_readdb(void) // read databases // - sv_readdb(db_path, "job_db1.txt", ',', 5+MAX_WEAPON_TYPE+1, 5+MAX_WEAPON_TYPE+1, -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); + 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); + +#if RRMODE + sv_readdb(db_path, "re_job_db.txt", ',', 1+RE_JOB_DB_MAX, 1+RE_JOB_DB_MAX, -1, &status_readdb_job_re); +#endif return 0; } diff --git a/src/map/status.h b/src/map/status.h index 033e7ca38..4bf0b627d 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -12,7 +12,7 @@ struct mercenary_data; struct status_change; //Use this to refer the max refinery level [Skotlex] -#define MAX_REFINE 20 +#define MAX_REFINE 10 #define MAX_REFINE_BONUS 5 extern unsigned long StatusChangeFlagTable[]; @@ -326,38 +326,251 @@ typedef enum sc_type { SC_SPL_MATK, SC_FOOD_STR_CASH, SC_FOOD_AGI_CASH, - SC_FOOD_VIT_CASH, //305 + SC_FOOD_VIT_CASH, SC_FOOD_DEX_CASH, SC_FOOD_INT_CASH, - SC_FOOD_LUK_CASH, - SC_PARTYFLEE, - - // Third Jobs - Maintaining SI order for SCs. - SC_FEAR, // 310, - SC_BURNING, - SC_ENCHANTBLADE, - SC_DEATHBOUND, + SC_FOOD_LUK_CASH,//308 + /** + * 3rd + **/ + SC_FEAR,//309 + SC_BURNING,//310 + SC_FREEZING,//311 + /** + * Rune Knight + **/ + SC_ENCHANTBLADE,//312 + SC_DEATHBOUND,//313 + SC_MILLENNIUMSHIELD, + SC_CRUSHSTRIKE,//315 SC_REFRESH, - SC_GIANTGROWTH, //315 + SC_REUSE_REFRESH, + SC_GIANTGROWTH, SC_STONEHARDSKIN, - SC_VITALITYACTIVATION, + SC_VITALITYACTIVATION,//320 + SC_STORMBLAST, SC_FIGHTINGSPIRIT, SC_ABUNDANCE, - SC_MILLENNIUMSHIELD, // 320 - // - SC_EPICLESIS = 325, + /** + * Arch Bishop + **/ + SC_ADORAMUS, + SC_EPICLESIS,//325 SC_ORATIO, SC_LAUDAAGNUS, SC_LAUDARAMUS, - SC_RENOVATIO = 332, - SC_EXPIATIO = 336, + SC_RENOVATIO, + SC_EXPIATIO,//330 SC_DUPLELIGHT, - SC_ADORAMUS = 380, - SC_AB_SECRAMENT = 451, - -// SC_ALL_RIDING = 472, - - SC_CRUSHSTRIKE = 599, + SC_SECRAMENT, + /** + * Warlock + **/ + SC_WHITEIMPRISON, + SC_MARSHOFABYSS, + SC_RECOGNIZEDSPELL,//335 + SC_STASIS, + SC_SPHERE_1, + SC_SPHERE_2, + SC_SPHERE_3, + SC_SPHERE_4,//340 + SC_SPHERE_5, + SC_READING_SB, + SC_FREEZINGSPELL, + /** + * Ranger + **/ + SC_FEARBREEZE, + SC_ELECTRICSHOCKER,//345 + SC_WUGDASH, + SC_BITE, + SC_CAMOUFLAGE, + /** + * Mechanic + **/ + SC_ACCELERATION, + SC_HOVERING,//350 + SC_SHAPESHIFT, + SC_INFRAREDSCAN, + SC_ANALYZE, + SC_MAGNETICFIELD, + SC_NEUTRALBARRIER,//355 + SC_NEUTRALBARRIER_MASTER, + SC_STEALTHFIELD, + SC_STEALTHFIELD_MASTER, + SC_OVERHEAT, + SC_OVERHEAT_LIMITPOINT,//360 + /** + * Guillotine Cross + **/ + SC_VENOMIMPRESS, + SC_POISONINGWEAPON, + SC_WEAPONBLOCKING, + SC_CLOAKINGEXCEED, + SC_HALLUCINATIONWALK,//365 + SC_HALLUCINATIONWALK_POSTDELAY, + SC_ROLLINGCUTTER, + SC_TOXIN, + SC_PARALYSE, + SC_VENOMBLEED,//370 + SC_MAGICMUSHROOM, + SC_DEATHHURT, + SC_PYREXIA, + SC_OBLIVIONCURSE, + SC_LEECHESEND,//375 + /** + * Royal Guard + **/ + SC_REFLECTDAMAGE, + SC_FORCEOFVANGUARD, + SC_SHIELDSPELL_DEF, + SC_SHIELDSPELL_MDEF, + SC_SHIELDSPELL_REF,//380 + SC_EXEEDBREAK, + SC_PRESTIGE, + SC_BANDING, + SC_BANDING_DEFENCE, + SC_EARTHDRIVE,//385 + SC_INSPIRATION, + /** + * Sorcerer + **/ + SC_SPELLFIST, + SC_CRYSTALIZE, + SC_STRIKING, + SC_WARMER,//390 + SC_VACUUM_EXTREME, + SC_PROPERTYWALK, + /** + * Minstrel / Wanderer + **/ + SC_SWINGDANCE, + SC_SYMPHONYOFLOVER, + SC_MOONLITSERENADE,//395 + SC_RUSHWINDMILL, + SC_ECHOSONG, + SC_HARMONIZE, + SC_VOICEOFSIREN, + SC_DEEPSLEEP,//400 + SC_SIRCLEOFNATURE, + SC_GLOOMYDAY, + SC_GLOOMYDAY_SK, + SC_SONGOFMANA, + SC_DANCEWITHWUG,//405 + SC_SATURDAYNIGHTFEVER, + SC_LERADSDEW, + SC_MELODYOFSINK, + SC_BEYONDOFWARCRY, + SC_UNLIMITEDHUMMINGVOICE,//410 + SC_SITDOWN_FORCE, + /** + * Sura + **/ + SC_CRESCENTELBOW, + SC_CURSEDCIRCLE_ATKER, + SC_CURSEDCIRCLE_TARGET, + SC_LIGHTNINGWALK,//415 + SC_RAISINGDRAGON, + SC_GT_ENERGYGAIN, + SC_GT_CHANGE, + SC_GT_REVITALIZE, + /** + * Genetic + **/ + SC_GN_CARTBOOST,//420 + SC_THORNSTRAP, + SC_BLOODSUCKER, + SC_SMOKEPOWDER, + SC_TEARGAS, + SC_MANDRAGORA,//425 + SC_STOMACHACHE, + SC_MYSTERIOUS_POWDER, + SC_MELON_BOMB, + SC_BANANA_BOMB, + SC_BANANA_BOMB_SITDOWN,//430 + SC_SAVAGE_STEAK, + SC_COCKTAIL_WARG_BLOOD, + SC_MINOR_BBQ, + SC_SIROMA_ICE_TEA, + SC_DROCERA_HERB_STEAMED,//435 + SC_PUTTI_TAILS_NOODLES, + SC_BOOST500, + SC_FULL_SWING_K, + SC_MANA_PLUS, + SC_MUSTLE_M,//440 + SC_LIFE_FORCE_F, + SC_EXTRACT_WHITE_POTION_Z, + SC_VITATA_500, + SC_EXTRACT_SALAMINE_JUICE, + /** + * Shadow Chaser + **/ + SC__REPRODUCE,//445 + SC__AUTOSHADOWSPELL, + SC__SHADOWFORM, + SC__BODYPAINT, + SC__INVISIBILITY, + SC__DEADLYINFECT,//450 + SC__ENERVATION, + SC__GROOMY, + SC__IGNORANCE, + SC__LAZINESS, + SC__UNLUCKY,//455 + SC__WEAKNESS, + SC__STRIPACCESSORY, + SC__MANHOLE, + SC_CHAOS, + SC__BLOODYLUST,//460 + /** + * Elemental Spirits + **/ + SC_CIRCLE_OF_FIRE, + SC_CIRCLE_OF_FIRE_OPTION, + SC_FIRE_CLOAK, + SC_FIRE_CLOAK_OPTION, + SC_WATER_SCREEN,//465 + SC_WATER_SCREEN_OPTION, + SC_WATER_DROP, + SC_WATER_DROP_OPTION, + SC_WATER_BARRIER, + SC_WIND_STEP,//470 + SC_WIND_STEP_OPTION, + SC_WIND_CURTAIN, + SC_WIND_CURTAIN_OPTION, + SC_ZEPHYR, + SC_SOLID_SKIN,//475 + SC_SOLID_SKIN_OPTION, + SC_STONE_SHIELD, + SC_STONE_SHIELD_OPTION, + SC_POWER_OF_GAIA, + SC_PYROTECHNIC,//480 + SC_PYROTECHNIC_OPTION, + SC_HEATER, + SC_HEATER_OPTION, + SC_TROPIC, + SC_TROPIC_OPTION,//485 + SC_AQUAPLAY, + SC_AQUAPLAY_OPTION, + SC_COOLER, + SC_COOLER_OPTION, + SC_CHILLY_AIR,//490 + SC_CHILLY_AIR_OPTION, + SC_GUST, + SC_GUST_OPTION, + SC_BLAST, + SC_BLAST_OPTION,//495 + SC_WILD_STORM, + SC_WILD_STORM_OPTION, + SC_PETROLOGY, + SC_PETROLOGY_OPTION, + SC_CURSED_SOIL,//500 + SC_CURSED_SOIL_OPTION, + SC_UPHEAVAL, + SC_UPHEAVAL_OPTION, + SC_TIDAL_WEAPON, + SC_TIDAL_WEAPON_OPTION,//505 + SC_ROCK_CRUSHER, + SC_ROCK_CRUSHER_ATK, SC_MAX, //Automatically updated max, used in for's to check we are within bounds. } sc_type; @@ -678,9 +891,10 @@ enum si_type { // SI_REUSE_LIMIT_F = 310, SI_INVINCIBLE = 311, SI_CASH_PLUSONLYJOBEXP = 312, - SI_PARTYFLEE = 313, +// SI_PARTYFLEE = 313, // SI_ANGEL_PROTECT = 314, -// SI_ENDURE_MDEF = 315, + + SI_ENDURE_MDEF = 315, SI_ENCHANTBLADE = 316, SI_DEATHBOUND = 317, SI_REFRESH = 318, @@ -689,24 +903,23 @@ enum si_type { SI_VITALITYACTIVATION = 321, SI_FIGHTINGSPIRIT = 322, SI_ABUNDANCE = 323, -// SI_REUSE_MILLENNIUMSHIELD = 324, -// SI_REUSE_CRUSHSTRIKE = 325, -// SI_REUSE_REFRESH = 326, -// SI_REUSE_STORMBLAST = 327, -// SI_VENOMIMPRESS = 328, + SI_REUSE_MILLENNIUMSHIELD = 324, + SI_REUSE_CRUSHSTRIKE = 325, + SI_REUSE_REFRESH = 326, + SI_REUSE_STORMBLAST = 327, + SI_VENOMIMPRESS = 328, SI_EPICLESIS = 329, SI_ORATIO = 330, SI_LAUDAAGNUS = 331, SI_LAUDARAMUS = 332, -// SI_CLOAKINGEXCEED = 333, -// SI_HALLUCINATIONWALK = 334, -// SI_HALLUCINATIONWALK_POSTDELAY = 335, + SI_CLOAKINGEXCEED = 333, + SI_HALLUCINATIONWALK = 334, + SI_HALLUCINATIONWALK_POSTDELAY = 335, SI_RENOVATIO = 336, -// SI_WEAPONBLOCKING = 337, -// SI_WEAPONBLOCKING_POSTDELAY = 338, -// SI_ROLLINGCUTTER = 339, + SI_WEAPONBLOCKING = 337, + SI_WEAPONBLOCKING_POSTDELAY = 338, + SI_ROLLINGCUTTER = 339, SI_EXPIATIO = 340, -/* SI_POISONINGWEAPON = 341, SI_TOXIN = 342, SI_PARALYSE = 343, @@ -716,9 +929,7 @@ enum si_type { SI_PYREXIA = 347, SI_OBLIVIONCURSE = 348, SI_LEECHESEND = 349, -*/ SI_DUPLELIGHT = 350, -/* SI_FROSTMISTY = 351, SI_FEARBREEZE = 352, SI_ELECTRICSHOCKER = 353, @@ -731,11 +942,11 @@ enum si_type { SI_CAMOUFLAGE = 360, SI_ACCELERATION = 361, SI_HOVERING = 362, - SI_SUMMON1 = 363, - SI_SUMMON2 = 364, - SI_SUMMON3 = 365, - SI_SUMMON4 = 366, - SI_SUMMON5 = 367, + SI_SPHERE_1 = 363, + SI_SPHERE_2 = 364, + SI_SPHERE_3 = 365, + SI_SPHERE_4 = 366, + SI_SPHERE_5 = 367, SI_MVPCARD_TAOGUNKA = 368, SI_MVPCARD_MISTRESS = 369, SI_MVPCARD_ORCHERO = 370, @@ -749,15 +960,15 @@ enum si_type { SI_NEUTRALBARRIER_MASTER = 378, SI_STEALTHFIELD = 379, SI_STEALTHFIELD_MASTER = 380, -*/ + SI_MANU_ATK = 381, SI_MANU_DEF = 382, SI_SPL_ATK = 383, SI_SPL_DEF = 384, -// SI_REPRODUCE = 385, + SI_REPRODUCE = 385, SI_MANU_MATK = 386, SI_SPL_MATK = 387, -/* + SI_STR_SCROLL = 388, SI_INT_SCROLL = 389, SI_LG_REFLECTDAMAGE = 390, @@ -771,9 +982,7 @@ enum si_type { SI_SHIELDSPELL_REF = 398, SI_BODYPAINT = 399, SI_EXEEDBREAK = 400, -*/ SI_ADORAMUS = 401, -/* SI_PRESTIGE = 402, SI_INVISIBILITY = 403, SI_DEADLYINFECT = 404, @@ -844,9 +1053,7 @@ enum si_type { SI_BLOCKING_PLAY = 469, SI_MANDRAGORA = 470, SI_ACTIVATE = 471, -*/ - SI_AB_SECRAMENT = 472, -/* + SI_SECRAMENT = 472, SI_ASSUMPTIO2 = 473, SI_TK_SEVENWIND = 474, SI_LIMIT_ODINS_RECALL = 475, @@ -946,65 +1153,7 @@ enum si_type { SI_WIND_INSIGNIA = 569, SI_EARTH_INSIGNIA = 570, SI_EQUIPED_FLOOR = 571, - SI_GUARDIAN_RECALL = 572, - SI_MORA_BUFF = 573, - SI_REUSE_LIMIT_G = 574, - SI_REUSE_LIMIT_H = 575, - SI_NEEDLE_OF_PARALYZE = 576, - SI_PAIN_KILLER = 577, - SI_G_LIFEPOTION = 578, - SI_VITALIZE_POTION = 579, - SI_LIGHT_OF_REGENE = 580, - SI_OVERED_BOOST = 581, - SI_SILENT_BREEZE = 582, - SI_ODINS_POWER = 583, - SI_STYLE_CHANGE = 584, - SI_SONIC_CLAW_POSTDELAY = 585, -//--586-595 Unused - SI_SILVERVEIN_RUSH_POSTDELAY = 596, - SI_MIDNIGHT_FRENZY_POSTDELAY = 597, - SI_GOLDENE_FERSE = 598, - SI_ANGRIFFS_MODUS = 599, - SI_TINDER_BREAKER = 600, - SI_TINDER_BREAKER_POSTDELAY = 601, - SI_CBC = 602, - SI_CBC_POSTDELAY = 603, - SI_EQC = 604, - SI_MAGMA_FLOW = 605, - SI_GRANITIC_ARMOR = 606, - SI_PYROCLASTIC = 607, - SI_VOLCANIC_ASH = 608, - SI_SPIRITS_SAVEINFO1 = 609, - SI_SPIRITS_SAVEINFO2 = 610, - SI_MAGIC_CANDY = 611, -//--612 skipped - SI_ALL_RIDING = 613, -//--614 skipped - SI_MACRO = 615, - SI_MACRO_POSTDELAY = 616, - SI_BEER_BOTTLE_CAP = 617, - SI_OVERLAPEXPUP = 618, - SI_PC_IZ_DUN05 = 619, -*/ - SI_CRUSHSTRIKE = 620, -/* - SI_MONSTER_TRANSFORM = 621, - SI_SIT = 622, - SI_ONAIR = 623, - SI_MTF_ASPD = 624, - SI_MTF_RANGEATK = 625, - SI_MTF_MATK = 626, - SI_MTF_MLEATKED = 627, - SI_MTF_CRIDAMAGE = 628, - SI_REUSE_LIMIT_MTF = 629, - SI_MACRO_PERMIT = 630, - SI_MACRO_PLAY = 631, - SI_SKF_CAST = 632, - SI_SKF_ASPD = 633, - SI_SKF_ATK = 634, - SI_SKF_MATK = 635, - SI_REWARD_PLUSONLYJOBEXP = 636, -*/ + SI_ALL_RIDING = 613,//awesome 571-613 gap, we're missing quite a few stuff here. }; // JOINTBEAT stackable ailments @@ -1125,7 +1274,7 @@ enum { OPTION_DRAGON3 = 0x01000000, OPTION_DRAGON4 = 0x02000000, OPTION_DRAGON5 = 0x04000000, - OPTION_ALL_RIDING= 0x08000000, + OPTION_MOUNTING = 0x08000000,//dull name (cuz ind named it :/) // compound constants OPTION_CART = OPTION_CART1|OPTION_CART2|OPTION_CART3|OPTION_CART4|OPTION_CART5, OPTION_DRAGON = OPTION_DRAGON1|OPTION_DRAGON2|OPTION_DRAGON3|OPTION_DRAGON4|OPTION_DRAGON5, @@ -1201,22 +1350,41 @@ struct weapon_atk { //For holding basic status (which can be modified by status changes) struct status_data { unsigned int - hp, sp, + hp, sp, // see status_cpy before adding members before hp and sp max_hp, max_sp; unsigned short str, agi, vit, int_, dex, luk, - batk, equipment_atk, - matk_min, matk_max, status_matk, + batk, + matk_min, matk_max, speed, amotion, adelay, dmotion, mode; short hit, flee, cri, flee2, - def, mdef, def2, mdef2, + def2, mdef2, +#if RRMODE + /** + * In RE def and mdef can go over 127 (signed char) limit, so in RE mode we use short + **/ + def,mdef, +#endif aspd_rate; unsigned char def_ele, ele_lv, +#if RRMODE + /** + * in RE weapon level is used in several areas, keeping it here saves performance + **/ + wlv, +#endif size, race; +#if RRMODE == 0 + /** + * In NON-RE def and mdef are not required to be short, so we keep it signed char (ancient-default) + **/ + signed char + def, mdef; +#endif struct weapon_atk rhw, lhw; //Right Hand/Left Hand Weapon. }; @@ -1278,7 +1446,8 @@ struct status_change { //TODO: See if it is possible to implement the following SC's without requiring extra parameters while the SC is inactive. unsigned char jb_flag; //Joint Beat type flag unsigned short mp_matk_min, mp_matk_max; //Previous matk min/max for ground spells (Amplify magic power) - int sg_id; //ID of the previous Storm gust that hit you + //int sg_id; //ID of the previous Storm gust that hit you + short comet_x, comet_y; // Point where src casted Comet - required to calculate damage from this point unsigned char sg_counter; //Storm gust counter (previous hits from storm gust) struct status_change_entry *data[SC_MAX]; }; @@ -1306,10 +1475,6 @@ int status_set_sp(struct block_list *bl, unsigned int sp, int flag); int status_heal(struct block_list *bl,int hp,int sp, int flag); int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp); -//Define for copying a status_data structure from b to a, without overwriting current Hp and Sp -#define status_cpy(a, b) \ - memcpy(&((a)->max_hp), &((b)->max_hp), sizeof(struct status_data)-(sizeof((a)->hp)+sizeof((a)->sp))) - struct regen_data *status_get_regen_data(struct block_list *bl); struct status_data *status_get_status_data(struct block_list *bl); struct status_data *status_get_base_status(struct block_list *bl); @@ -1329,7 +1494,7 @@ int status_get_lv(struct block_list *bl); #define status_get_luk(bl) status_get_status_data(bl)->luk #define status_get_hit(bl) status_get_status_data(bl)->hit #define status_get_flee(bl) status_get_status_data(bl)->flee -signed short status_get_def(struct block_list *bl); +signed char status_get_def(struct block_list *bl); #define status_get_mdef(bl) status_get_status_data(bl)->mdef #define status_get_flee2(bl) status_get_status_data(bl)->flee2 #define status_get_def2(bl) status_get_status_data(bl)->def2 @@ -1355,6 +1520,12 @@ unsigned char status_calc_attack_element(struct block_list *bl, struct status_ch #define status_get_race(bl) status_get_status_data(bl)->race #define status_get_size(bl) status_get_status_data(bl)->size #define status_get_mode(bl) status_get_status_data(bl)->mode +#if RRMODE + /** + * in RE weapon level is used in several areas, keeping it here saves performance + **/ + #define status_get_wlv(bl) status_get_status_data(bl)->wlv +#endif int status_get_party_id(struct block_list *bl); int status_get_guild_id(struct block_list *bl); int status_get_emblem_id(struct block_list *bl); diff --git a/src/map/storage.c b/src/map/storage.c index 116ba18c9..4eb1beb32 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -138,11 +138,6 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, data = itemdb_search(item_data->nameid); - if( data->stack.storage && amount > data->stack.amount ) - {// item stack limitation - return 1; - } - if( !itemdb_canstore(item_data, pc_isGM(sd)) ) { //Check if item is storable. [Skotlex] clif_displaymessage (sd->fd, msg_txt(264)); @@ -155,7 +150,7 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, { if( compare_item(&stor->items[i], item_data) ) {// existing items found, stack them - if( amount > MAX_AMOUNT - stor->items[i].amount || ( data->stack.storage && amount > data->stack.amount - stor->items[i].amount ) ) + if( amount > MAX_AMOUNT - stor->items[i].amount ) return 1; stor->items[i].amount += amount; clif_storageitemadded(sd,&stor->items[i],i,amount); @@ -404,11 +399,6 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto data = itemdb_search(item_data->nameid); - if( data->stack.guildstorage && amount > data->stack.amount ) - {// item stack limitation - return 1; - } - if( !itemdb_canguildstore(item_data, pc_isGM(sd)) || item_data->expire_time ) { //Check if item is storable. [Skotlex] clif_displaymessage (sd->fd, msg_txt(264)); @@ -418,7 +408,7 @@ 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->items[i], item_data)) { - if( amount > MAX_AMOUNT - stor->items[i].amount || ( data->stack.guildstorage && amount > data->stack.amount - stor->items[i].amount ) ) + if(stor->items[i].amount+amount > MAX_AMOUNT) return 1; stor->items[i].amount+=amount; clif_storageitemadded(sd,&stor->items[i],i,amount); diff --git a/src/map/unit.c b/src/map/unit.c index b51e2602d..44296ca51 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -323,9 +323,13 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag) } //To set Mob's CHASE/FOLLOW states (shouldn't be done if there's no path to reach) -#define set_mobstate(bl, flag) \ - if((bl)->type == BL_MOB && (flag)) \ - ((TBL_MOB*)(bl))->state.skillstate = ((TBL_MOB*)(bl))->state.aggressive?MSS_FOLLOW:MSS_RUSH; +static inline void set_mobstate(struct block_list* bl, int flag) +{ + struct mob_data* md = BL_CAST(BL_MOB,bl); + + if( md && flag ) + md->state.skillstate = md->state.aggressive ? MSS_FOLLOW : MSS_RUSH; +} static int unit_walktobl_sub(int tid, unsigned int tick, int id, intptr_t data) { @@ -440,7 +444,7 @@ int unit_run(struct block_list *bl) if(to_x == bl->x && to_y == bl->y) { //If you can't run forward, you must be next to a wall, so bounce back. [Skotlex] - clif_status_change(bl, SI_BUMP, 1, 0); + clif_status_change(bl, SI_BUMP, 1, 0, 0, 0, 0); //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin] unit_bl2ud(bl)->state.running = 0; @@ -448,7 +452,7 @@ int unit_run(struct block_list *bl) skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0); clif_fixpos(bl); //Why is a clif_slide (skill_blown) AND a fixpos needed? Ask Aegis. - clif_status_change(bl, SI_BUMP, 0, 0); + clif_status_change(bl, SI_BUMP, 0, 0, 0, 0, 0); return 0; } if (unit_walktoxy(bl, to_x, to_y, 1)) @@ -460,7 +464,7 @@ int unit_run(struct block_list *bl) } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1)); if (i==0) { // copy-paste from above - clif_status_change(bl, SI_BUMP, 1, 0); + clif_status_change(bl, SI_BUMP, 1, 0, 0, 0, 0); //Set running to 0 beforehand so status_change_end knows not to enable spurt [Kevin] unit_bl2ud(bl)->state.running = 0; @@ -468,7 +472,73 @@ int unit_run(struct block_list *bl) skill_blown(bl,bl,skill_get_blewcount(TK_RUN,lv),unit_getdir(bl),0); clif_fixpos(bl); - clif_status_change(bl, SI_BUMP, 0, 0); + clif_status_change(bl, SI_BUMP, 0, 0, 0, 0, 0); + return 0; + } + return 1; +} + +//Exclusive function to Wug Dash state. [Jobbie/3CeAM] +int unit_wugdash(struct block_list *bl, struct map_session_data *sd) { + struct status_change *sc = status_get_sc(bl); + short to_x,to_y,dir_x,dir_y; + int lv; + int i; + if (!(sc && sc->data[SC_WUGDASH])) + return 0; + + nullpo_ret(sd); + nullpo_ret(bl); + + if (!unit_can_move(bl)) { + status_change_end(bl,SC_WUGDASH,-1); + return 0; + } + + lv = sc->data[SC_WUGDASH]->val1; + dir_x = dirx[sc->data[SC_WUGDASH]->val2]; + dir_y = diry[sc->data[SC_WUGDASH]->val2]; + + to_x = bl->x; + to_y = bl->y; + for(i=0;i<AREA_SIZE;i++) + { + if(!map_getcell(bl->m,to_x+dir_x,to_y+dir_y,CELL_CHKPASS)) + break; + + if(sc->data[SC_WUGDASH] && map_count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC)) + break; + + to_x += dir_x; + to_y += dir_y; + } + + if(to_x == bl->x && to_y == bl->y) { + + unit_bl2ud(bl)->state.running = 0; + status_change_end(bl,SC_WUGDASH,-1); + + if( sd ){ + clif_fixpos(bl); + skill_castend_damage_id(bl, &sd->bl, RA_WUGDASH, lv, gettick(), SD_LEVEL); + } + return 0; + } + if (unit_walktoxy(bl, to_x, to_y, 1)) + return 1; + do { + to_x -= dir_x; + to_y -= dir_y; + } while (--i > 0 && !unit_walktoxy(bl, to_x, to_y, 1)); + if (i==0) { + + unit_bl2ud(bl)->state.running = 0; + status_change_end(bl,SC_WUGDASH,-1); + + if( sd ){ + clif_fixpos(bl); + skill_castend_damage_id(bl, &sd->bl, RA_WUGDASH, lv, gettick(), SD_LEVEL); + } return 0; } return 1; @@ -837,8 +907,6 @@ int unit_can_move(struct block_list *bl) sc->data[SC_CLOAKING]->val1 < 3 && !(sc->data[SC_CLOAKING]->val4&1)) || sc->data[SC_MADNESSCANCEL] || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF) - || (sc->data[SC_FEAR] && sc->data[SC_FEAR]->val3 > 0) - || sc->data[SC_DEATHBOUND] )) return 0; } @@ -990,7 +1058,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh if( !target || src->m != target->m || !src->prev || !target->prev ) return 0; - if( mob_ksprotected(src, target) ) + if( battle_config.ksprotection && sd && mob_ksprotected(src, target) ) return 0; //Normally not needed because clif.c checks for it, but the at/char/script commands don't! [Skotlex] @@ -1101,10 +1169,18 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh case SA_SPELLBREAKER: temp = 1; break; + case ST_CHASEWALK: + if (sc && sc->data[SC_CHASEWALK]) + casttime = 0; + break; case TK_RUN: if (sc && sc->data[SC_RUN]) casttime = 0; break; + case HP_BASILICA: + if( sc && sc->data[SC_BASILICA] ) + casttime = 0; // No Casting time on basilica cancel + break; case KN_CHARGEATK: { unsigned int k = (distance_bl(src,target)-1)/3; //+100% every 3 cells of distance @@ -1118,17 +1194,9 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh break; } - // Cancel status effects that lower cast time. - if( !(skill_get_castnodex(skill_num, skill_lv)&2) && sc ) - { - if( sc->data[SC_SUFFRAGIUM] ) - status_change_end(src, SC_SUFFRAGIUM, INVALID_TIMER); - if( sc->data[SC_MEMORIZE] ) - { - if ((--sc->data[SC_MEMORIZE]->val2) <= 0) - status_change_end(src, SC_MEMORIZE, INVALID_TIMER); - } - } + // moved here to prevent Suffragium from ending if skill fails + if (!(skill_get_castnodex(skill_num, skill_lv)&2)) + casttime = skill_castfix_sc(src, casttime, skill_num, skill_lv); if( casttime > 0 || temp ) { @@ -1268,17 +1336,9 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, sh unit_stop_attack(src); - // Cancel status effects that lower cast time. - if( !(skill_get_castnodex(skill_num, skill_lv)&2) && sc ) - { - if( sc->data[SC_SUFFRAGIUM] ) - status_change_end(src, SC_SUFFRAGIUM, INVALID_TIMER); - if( sc->data[SC_MEMORIZE] ) - { - if ((--sc->data[SC_MEMORIZE]->val2) <= 0) - status_change_end(src, SC_MEMORIZE, INVALID_TIMER); - } - } + // moved here to prevent Suffragium from ending if skill fails + if (!(skill_get_castnodex(skill_num, skill_lv)&2)) + casttime = skill_castfix_sc(src, casttime, skill_num, skill_lv); ud->state.skillcastcancel = castcancel&&casttime>0?1:0; if( !sd || sd->skillitem != skill_num || skill_get_cast(skill_num,skill_lv) ) @@ -2079,7 +2139,6 @@ int unit_free(struct block_list *bl, clr_type clrtype) guild_send_memberinfoshort(sd,0); pc_cleareventtimer(sd); pc_inventory_rental_clear(sd); - if( sd->bg_id ) bg_team_leave(sd,1); pc_delspiritball(sd,sd->spiritball,1); if( sd->reg ) diff --git a/src/map/unit.h b/src/map/unit.h index e349a7466..f7b072739 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -126,6 +126,10 @@ int unit_changeviewsize(struct block_list *bl,short size); // 初期化ルーチン int do_init_unit(void); int do_final_unit(void); +/** + * Ranger + **/ +int unit_wugdash(struct block_list *bl, struct map_session_data *sd); extern const short dirx[8]; extern const short diry[8]; |