diff options
author | malufett <malufett.eat.my.binaries@gmail.com> | 2013-01-13 01:20:04 +0800 |
---|---|---|
committer | malufett <malufett.eat.my.binaries@gmail.com> | 2013-01-13 01:20:04 +0800 |
commit | cb18f86c8b6481a338057b1b8ea0bfbd5f5acb6e (patch) | |
tree | 72069956820270d274c5ac905b5166603bb2bcb9 /src | |
parent | 2c8e3208170354cf68c0328942b0a46f18e81e73 (diff) | |
download | hercules-cb18f86c8b6481a338057b1b8ea0bfbd5f5acb6e.tar.gz hercules-cb18f86c8b6481a338057b1b8ea0bfbd5f5acb6e.tar.bz2 hercules-cb18f86c8b6481a338057b1b8ea0bfbd5f5acb6e.tar.xz hercules-cb18f86c8b6481a338057b1b8ea0bfbd5f5acb6e.zip |
Merge latest rathena commits to Hercules
Merge r17093, r17094, r17095 of rAthena to Hercules.
Welcome Hercules!! my first commit <3
Diffstat (limited to 'src')
-rw-r--r-- | src/map/clif.c | 34256 | ||||
-rw-r--r-- | src/map/script.c | 35563 | ||||
-rw-r--r-- | src/map/skill.c | 35981 | ||||
-rw-r--r-- | src/map/status.c | 22584 |
4 files changed, 64200 insertions, 64184 deletions
diff --git a/src/map/clif.c b/src/map/clif.c index 06c74a5f8..c314a6f33 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1,17128 +1,17128 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include "../common/cbasetypes.h" -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/grfio.h" -#include "../common/malloc.h" -#include "../common/nullpo.h" -#include "../common/random.h" -#include "../common/showmsg.h" -#include "../common/strlib.h" -#include "../common/utils.h" -#include "../common/ers.h" - -#include "map.h" -#include "chrif.h" -#include "pc.h" -#include "status.h" -#include "npc.h" -#include "itemdb.h" -#include "chat.h" -#include "trade.h" -#include "storage.h" -#include "script.h" -#include "skill.h" -#include "atcommand.h" -#include "intif.h" -#include "battle.h" -#include "battleground.h" -#include "mob.h" -#include "party.h" -#include "unit.h" -#include "guild.h" -#include "vending.h" -#include "pet.h" -#include "homunculus.h" -#include "instance.h" -#include "mercenary.h" -#include "elemental.h" -#include "log.h" -#include "clif.h" -#include "mail.h" -#include "quest.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <time.h> - -/* for clif_clearunit_delayed */ -static struct eri *delay_clearunit_ers; - -//#define DUMP_UNKNOWN_PACKET -//#define DUMP_INVALID_PACKET - -struct Clif_Config { - int packet_db_ver; //Preferred packet version. - int connect_cmd[MAX_PACKET_VER + 1]; //Store the connect command for all versions. [Skotlex] -} clif_config; - -struct s_packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB + 1]; - -//Converts item type in case of pet eggs. -static inline int itemtype(int type) -{ - return ( type == IT_PETEGG ) ? IT_WEAPON : type; -} - - -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 -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); -} - - -static 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); -} - - -static inline void RBUFPOS(const uint8* p, unsigned short pos, short* x, short* y, unsigned char* dir) -{ - p += pos; - - if( x ) { - x[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 ); - } - - if( y ) { - y[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 ); - } - - if( dir ) { - dir[0] = ( p[2] & 0x0f ); - } -} - - -static inline void RBUFPOS2(const uint8* p, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0) -{ - p += pos; - - if( x0 ) { - x0[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 ); - } - - if( y0 ) { - y0[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 ); - } - - if( x1 ) { - x1[0] = ( ( p[2] & 0x0f ) << 6 ) | ( p[3] >> 2 ); - } - - if( y1 ) { - y1[0] = ( ( p[3] & 0x03 ) << 8 ) | ( p[4] >> 0 ); - } - - if( sx0 ) { - sx0[0] = ( p[5] & 0xf0 ) >> 4; - } - - if( sy0 ) { - sy0[0] = ( p[5] & 0x0f ) >> 0; - } -} - - -static inline void RFIFOPOS(int fd, unsigned short pos, short* x, short* y, unsigned char* dir) -{ - RBUFPOS(RFIFOP(fd,pos), 0, x, y, dir); -} - - -static inline void RFIFOPOS2(int fd, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0) -{ - RBUFPOS2(WFIFOP(fd,pos), 0, x0, y0, x1, y1, sx0, sy0); -} - - -//To idenfity disguised characters. -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; -} - - -static char map_ip_str[128]; -static uint32 map_ip; -static uint32 bind_ip = INADDR_ANY; -static uint16 map_port = 5121; -int map_fd; - -static int clif_parse (int fd); - -/*========================================== - * Ip setting of map-server - *------------------------------------------*/ -int clif_setip(const char* ip) -{ - char ip_str[16]; - map_ip = host2ip(ip); - if (!map_ip) { - ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip); - return 0; - } - - strncpy(map_ip_str, ip, sizeof(map_ip_str)); - ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(map_ip, ip_str)); - return 1; -} - -void clif_setbindip(const char* ip) -{ - char ip_str[16]; - bind_ip = host2ip(ip); - if (bind_ip) { - ShowInfo("Map Server Bind IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(bind_ip, ip_str)); - } else { - ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip); - } -} - -/*========================================== - * Sets map port to 'port' - * is run from map.c upon loading map server configuration - *------------------------------------------*/ -void clif_setport(uint16 port) -{ - map_port = port; -} - -/*========================================== - * Returns map server IP - *------------------------------------------*/ -uint32 clif_getip(void) -{ - return map_ip; -} - -//Refreshes map_server ip, returns the new ip if the ip changed, otherwise it returns 0. -uint32 clif_refresh_ip(void) -{ - uint32 new_ip; - - new_ip = host2ip(map_ip_str); - if (new_ip && new_ip != map_ip) { - map_ip = new_ip; - ShowInfo("Updating IP resolution of [%s].\n", map_ip_str); - return map_ip; - } - return 0; -} - -/*========================================== - * Returns map port which is set by clif_setport() - *------------------------------------------*/ -uint16 clif_getport(void) -{ - return map_port; -} - -#if PACKETVER >= 20071106 -static inline unsigned char clif_bl_type(struct block_list *bl) { - switch (bl->type) { - case BL_PC: return disguised(bl)?0x1:0x0; //PC_TYPE - case BL_ITEM: return 0x2; //ITEM_TYPE - case BL_SKILL: return 0x3; //SKILL_TYPE - case BL_CHAT: return 0x4; //UNKNOWN_TYPE - case BL_MOB: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x5; //NPC_MOB_TYPE - case BL_NPC: return 0x6; //NPC_EVT_TYPE - case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE - case BL_HOM: return 0x8; //NPC_HOM_TYPE - case BL_MER: return 0x9; //NPC_MERSOL_TYPE - case BL_ELEM: return 0xa; //NPC_ELEMENTAL_TYPE - default: return 0x1; //NPC_TYPE - } -} -#endif - -/*========================================== - * 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 - *------------------------------------------*/ -static int clif_send_sub(struct block_list *bl, va_list ap) -{ - struct block_list *src_bl; - struct map_session_data *sd; - unsigned char *buf; - int len, type, fd; - - nullpo_ret(bl); - nullpo_ret(sd = (struct map_session_data *)bl); - - fd = sd->fd; - if (!fd) //Don't send to disconnected clients. - return 0; - - buf = va_arg(ap,unsigned char*); - len = va_arg(ap,int); - nullpo_ret(src_bl = va_arg(ap,struct block_list*)); - type = va_arg(ap,int); - - switch(type) - { - case AREA_WOS: - if (bl == src_bl) - return 0; - break; - case AREA_WOC: - if (sd->chatID || bl == src_bl) - return 0; - break; - case AREA_WOSC: - { - if(src_bl->type == BL_PC){ - struct map_session_data *ssd = (struct map_session_data *)src_bl; - if (ssd && sd->chatID && (sd->chatID == ssd->chatID)) - return 0; - } - else if(src_bl->type == BL_NPC) { - struct npc_data *nd = (struct npc_data *)src_bl; - if (nd && sd->chatID && (sd->chatID == nd->chat_id)) - return 0; - } - } - break; - } - - if (session[fd] == NULL) - return 0; - - WFIFOHEAD(fd, len); - if (WFIFOP(fd,0) == buf) { - ShowError("WARNING: Invalid use of clif_send function\n"); - ShowError(" Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0)); - ShowError(" Please correct your code.\n"); - // don't send to not move the pointer of the packet for next sessions in the loop - //WFIFOSET(fd,0);//## TODO is this ok? - //NO. It is not ok. There is the chance WFIFOSET actually sends the buffer data, and shifts elements around, which will corrupt the buffer. - return 0; - } - - if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - } - - return 0; -} - -/*========================================== - * 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) -{ - int i; - struct map_session_data *sd, *tsd; - struct party_data *p = NULL; - struct guild *g = NULL; - struct battleground_data *bg = NULL; - int x0 = 0, x1 = 0, y0 = 0, y1 = 0, fd; - struct s_mapiterator* iter; - - if( type != ALL_CLIENT && type != CHAT_MAINCHAT ) - nullpo_ret(bl); - - sd = BL_CAST(BL_PC, bl); - - switch(type) { - - case ALL_CLIENT: //All player clients. - iter = mapit_getallusers(); - while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL ) - { - if( packet_db[tsd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(tsd->fd, len); - memcpy(WFIFOP(tsd->fd,0), buf, len); - WFIFOSET(tsd->fd,len); - } - } - mapit_free(iter); - break; - - case ALL_SAMEMAP: //All players on the same map - iter = mapit_getallusers(); - while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL ) - { - if( bl->m == tsd->bl.m && packet_db[tsd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(tsd->fd, len); - memcpy(WFIFOP(tsd->fd,0), buf, len); - WFIFOSET(tsd->fd,len); - } - } - mapit_free(iter); - break; - - case AREA: - case AREA_WOSC: - if (sd && bl->prev == NULL) //Otherwise source misses the packet.[Skotlex] - clif_send (buf, len, bl, SELF); - case AREA_WOC: - case AREA_WOS: - map_foreachinarea(clif_send_sub, bl->m, bl->x-AREA_SIZE, bl->y-AREA_SIZE, bl->x+AREA_SIZE, bl->y+AREA_SIZE, - BL_PC, buf, len, bl, type); - break; - case AREA_CHAT_WOC: - map_foreachinarea(clif_send_sub, bl->m, bl->x-(AREA_SIZE-5), bl->y-(AREA_SIZE-5), - bl->x+(AREA_SIZE-5), bl->y+(AREA_SIZE-5), BL_PC, buf, len, bl, AREA_WOC); - break; - - case CHAT: - case CHAT_WOS: - { - struct chat_data *cd; - if (sd) { - cd = (struct chat_data*)map_id2bl(sd->chatID); - } else if (bl->type == BL_CHAT) { - cd = (struct chat_data*)bl; - } else break; - if (cd == NULL) - break; - for(i = 0; i < cd->users; i++) { - if (type == CHAT_WOS && cd->usersd[i] == sd) - continue; - if (packet_db[cd->usersd[i]->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version - if ((fd=cd->usersd[i]->fd) >0 && session[fd]) // Added check to see if session exists [PoW] - { - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - } - } - } - } - break; - - case CHAT_MAINCHAT: //[LuzZza] - iter = mapit_getallusers(); - while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL ) - { - if( tsd->state.mainchat && tsd->chatID == 0 && packet_db[tsd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(tsd->fd, len); - memcpy(WFIFOP(tsd->fd,0), buf, len); - WFIFOSET(tsd->fd,len); - } - } - mapit_free(iter); - break; - - case PARTY_AREA: - case PARTY_AREA_WOS: - x0 = bl->x - AREA_SIZE; - y0 = bl->y - AREA_SIZE; - x1 = bl->x + AREA_SIZE; - y1 = bl->y + AREA_SIZE; - case PARTY: - case PARTY_WOS: - case PARTY_SAMEMAP: - case PARTY_SAMEMAP_WOS: - if (sd && sd->status.party_id) - p = party_search(sd->status.party_id); - - if (p) { - for(i=0;i<MAX_PARTY;i++){ - if( (sd = p->data[i].sd) == NULL ) - continue; - - if( !(fd=sd->fd) ) - continue; - - if( sd->bl.id == bl->id && (type == PARTY_WOS || type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS) ) - continue; - - if( type != PARTY && type != PARTY_WOS && bl->m != sd->bl.m ) - continue; - - if( (type == PARTY_AREA || type == PARTY_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) ) - continue; - - if( packet_db[sd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - } - } - if (!enable_spy) //Skip unnecessary parsing. [Skotlex] - break; - - iter = mapit_getallusers(); - while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL ) - { - if( tsd->partyspy == p->party.party_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(tsd->fd, len); - memcpy(WFIFOP(tsd->fd,0), buf, len); - WFIFOSET(tsd->fd,len); - } - } - mapit_free(iter); - } - break; - - case DUEL: - case DUEL_WOS: - if (!sd || !sd->duel_group) break; //Invalid usage. - - iter = mapit_getallusers(); - while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL ) - { - if( type == DUEL_WOS && bl->id == tsd->bl.id ) - continue; - if( sd->duel_group == tsd->duel_group && packet_db[tsd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(tsd->fd, len); - memcpy(WFIFOP(tsd->fd,0), buf, len); - WFIFOSET(tsd->fd,len); - } - } - mapit_free(iter); - break; - - case SELF: - if (sd && (fd=sd->fd) && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - } - break; - - // New definitions for guilds [Valaris] - Cleaned up and reorganized by [Skotlex] - case GUILD_AREA: - case GUILD_AREA_WOS: - x0 = bl->x - AREA_SIZE; - y0 = bl->y - AREA_SIZE; - x1 = bl->x + AREA_SIZE; - y1 = bl->y + AREA_SIZE; - case GUILD_SAMEMAP: - case GUILD_SAMEMAP_WOS: - case GUILD: - case GUILD_WOS: - case GUILD_NOBG: - if (sd && sd->status.guild_id) - g = guild_search(sd->status.guild_id); - - if (g) { - for(i = 0; i < g->max_member; i++) { - if( (sd = g->member[i].sd) != NULL ) - { - if( !(fd=sd->fd) ) - continue; - - if( type == GUILD_NOBG && sd->bg_id ) - continue; - - if( sd->bl.id == bl->id && (type == GUILD_WOS || type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS) ) - continue; - - if( type != GUILD && type != GUILD_NOBG && type != GUILD_WOS && sd->bl.m != bl->m ) - continue; - - if( (type == GUILD_AREA || type == GUILD_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) ) - continue; - - if( packet_db[sd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - } - } - } - if (!enable_spy) //Skip unnecessary parsing. [Skotlex] - break; - - iter = mapit_getallusers(); - while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL ) - { - if( tsd->guildspy == g->guild_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(tsd->fd, len); - memcpy(WFIFOP(tsd->fd,0), buf, len); - WFIFOSET(tsd->fd,len); - } - } - mapit_free(iter); - } - break; - - case BG_AREA: - case BG_AREA_WOS: - x0 = bl->x - AREA_SIZE; - y0 = bl->y - AREA_SIZE; - x1 = bl->x + AREA_SIZE; - y1 = bl->y + AREA_SIZE; - case BG_SAMEMAP: - case BG_SAMEMAP_WOS: - case BG: - case BG_WOS: - if( sd && sd->bg_id && (bg = bg_team_search(sd->bg_id)) != NULL ) - { - for( i = 0; i < MAX_BG_MEMBERS; i++ ) - { - if( (sd = bg->members[i].sd) == NULL || !(fd = sd->fd) ) - continue; - if( sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) ) - continue; - if( type != BG && type != BG_WOS && sd->bl.m != bl->m ) - continue; - if( (type == BG_AREA || type == BG_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) ) - continue; - if( packet_db[sd->packet_ver][RBUFW(buf,0)].len ) - { // packet must exist for the client version - WFIFOHEAD(fd,len); - memcpy(WFIFOP(fd,0), buf, len); - WFIFOSET(fd,len); - } - } - } - break; - - default: - ShowError("clif_send: Unrecognized type %d\n",type); - return -1; - } - - return 0; -} - - -/// Notifies the client, that it's connection attempt was accepted. -/// 0073 <start time>.L <position>.3B <x size>.B <y size>.B (ZC_ACCEPT_ENTER) -/// 02eb <start time>.L <position>.3B <x size>.B <y size>.B <font>.W (ZC_ACCEPT_ENTER2) -void clif_authok(struct map_session_data *sd) -{ -#if PACKETVER < 20080102 - const int cmd = 0x73; -#else - const int cmd = 0x2eb; -#endif - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(cmd)); - WFIFOW(fd, 0) = cmd; - WFIFOL(fd, 2) = gettick(); - WFIFOPOS(fd, 6, sd->bl.x, sd->bl.y, sd->ud.dir); - WFIFOB(fd, 9) = 5; // ignored - WFIFOB(fd,10) = 5; // ignored -#if PACKETVER >= 20080102 - WFIFOW(fd,11) = sd->user_font; // FIXME: Font is currently not saved. -#endif - WFIFOSET(fd,packet_len(cmd)); -} - - -/// Notifies the client, that it's connection attempt was refused (ZC_REFUSE_ENTER). -/// 0074 <error code>.B -/// error code: -/// 0 = client type mismatch -/// 1 = ID mismatch -/// 2 = mobile - out of available time -/// 3 = mobile - already logged in -/// 4 = mobile - waiting state -void clif_authrefuse(int fd, uint8 error_code) -{ - WFIFOHEAD(fd,packet_len(0x74)); - WFIFOW(fd,0) = 0x74; - WFIFOB(fd,2) = error_code; - WFIFOSET(fd,packet_len(0x74)); -} - - -/// Notifies the client of a ban or forced disconnect (SC_NOTIFY_BAN). -/// 0081 <error code>.B -/// error code: -/// 0 = BAN_UNFAIR -/// 1 = server closed -> MsgStringTable[4] -/// 2 = ID already logged in -> MsgStringTable[5] -/// 3 = timeout/too much lag -> MsgStringTable[241] -/// 4 = server full -> MsgStringTable[264] -/// 5 = underaged -> MsgStringTable[305] -/// 8 = Server sill recognizes last connection -> MsgStringTable[441] -/// 9 = too many connections from this ip -> MsgStringTable[529] -/// 10 = out of available time paid for -> MsgStringTable[530] -/// 11 = BAN_PAY_SUSPEND -/// 12 = BAN_PAY_CHANGE -/// 13 = BAN_PAY_WRONGIP -/// 14 = BAN_PAY_PNGAMEROOM -/// 15 = disconnected by a GM -> if( servicetype == taiwan ) MsgStringTable[579] -/// 16 = BAN_JAPAN_REFUSE1 -/// 17 = BAN_JAPAN_REFUSE2 -/// 18 = BAN_INFORMATION_REMAINED_ANOTHER_ACCOUNT -/// 100 = BAN_PC_IP_UNFAIR -/// 101 = BAN_PC_IP_COUNT_ALL -/// 102 = BAN_PC_IP_COUNT -/// 103 = BAN_GRAVITY_MEM_AGREE -/// 104 = BAN_GAME_MEM_AGREE -/// 105 = BAN_HAN_VALID -/// 106 = BAN_PC_IP_LIMIT_ACCESS -/// 107 = BAN_OVER_CHARACTER_LIST -/// 108 = BAN_IP_BLOCK -/// 109 = BAN_INVALID_PWD_CNT -/// 110 = BAN_NOT_ALLOWED_JOBCLASS -/// ? = disconnected -> MsgStringTable[3] -void clif_authfail_fd(int fd, int type) -{ - if (!fd || !session[fd] || session[fd]->func_parse != clif_parse) //clif_authfail should only be invoked on players! - return; - - WFIFOHEAD(fd, packet_len(0x81)); - WFIFOW(fd,0) = 0x81; - WFIFOB(fd,2) = type; - WFIFOSET(fd,packet_len(0x81)); - set_eof(fd); -} - - -/// Notifies the client, whether it can disconnect and change servers (ZC_RESTART_ACK). -/// 00b3 <type>.B -/// type: -/// 1 = disconnect, char-select -/// ? = nothing -void clif_charselectok(int id, uint8 ok) -{ - struct map_session_data* sd; - int fd; - - if ((sd = map_id2sd(id)) == NULL || !sd->fd) - return; - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0xb3)); - WFIFOW(fd,0) = 0xb3; - WFIFOB(fd,2) = ok; - WFIFOSET(fd,packet_len(0xb3)); -} - -/// Makes an item appear on the ground. -/// 009e <id>.L <name id>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY) -/// 084b (ZC_ITEM_FALL_ENTRY4) -void clif_dropflooritem(struct flooritem_data* fitem) -{ - uint8 buf[17]; - int view; - - nullpo_retv(fitem); - - if (fitem->item_data.nameid <= 0) - return; - - WBUFW(buf, 0) = 0x9e; - WBUFL(buf, 2) = fitem->bl.id; - WBUFW(buf, 6) = ((view = itemdb_viewid(fitem->item_data.nameid)) > 0) ? view : fitem->item_data.nameid; - WBUFB(buf, 8) = fitem->item_data.identify; - WBUFW(buf, 9) = fitem->bl.x; - WBUFW(buf,11) = fitem->bl.y; - WBUFB(buf,13) = fitem->subx; - WBUFB(buf,14) = fitem->suby; - WBUFW(buf,15) = fitem->item_data.amount; - - clif_send(buf, packet_len(0x9e), &fitem->bl, AREA); -} - - - -/// Makes an item disappear from the ground. -/// 00a1 <id>.L (ZC_ITEM_DISAPPEAR) -void clif_clearflooritem(struct flooritem_data *fitem, int fd) -{ - unsigned char buf[16]; - - nullpo_retv(fitem); - - WBUFW(buf,0) = 0xa1; - WBUFL(buf,2) = fitem->bl.id; - - if (fd == 0) { - clif_send(buf, packet_len(0xa1), &fitem->bl, AREA); - } else { - WFIFOHEAD(fd,packet_len(0xa1)); - memcpy(WFIFOP(fd,0), buf, packet_len(0xa1)); - WFIFOSET(fd,packet_len(0xa1)); - } -} - - -/// Makes a unit (char, npc, mob, homun) disappear to one client (ZC_NOTIFY_VANISH). -/// 0080 <id>.L <type>.B -/// type: -/// 0 = out of sight -/// 1 = died -/// 2 = logged out -/// 3 = teleport -/// 4 = trickdead -void clif_clearunit_single(int id, clr_type type, int fd) -{ - WFIFOHEAD(fd, packet_len(0x80)); - WFIFOW(fd,0) = 0x80; - WFIFOL(fd,2) = id; - WFIFOB(fd,6) = type; - WFIFOSET(fd, packet_len(0x80)); -} - -/// Makes a unit (char, npc, mob, homun) disappear to all clients in area (ZC_NOTIFY_VANISH). -/// 0080 <id>.L <type>.B -/// type: -/// 0 = out of sight -/// 1 = died -/// 2 = logged out -/// 3 = teleport -/// 4 = trickdead -void clif_clearunit_area(struct block_list* bl, clr_type type) -{ - unsigned char buf[8]; - - nullpo_retv(bl); - - WBUFW(buf,0) = 0x80; - WBUFL(buf,2) = bl->id; - WBUFB(buf,6) = type; - - clif_send(buf, packet_len(0x80), bl, type == CLR_DEAD ? AREA : AREA_WOS); - - if(disguised(bl)) { - WBUFL(buf,2) = -bl->id; - clif_send(buf, packet_len(0x80), bl, SELF); - } -} - - -/// Used to make monsters with player-sprites disappear after dying -/// like normal monsters, because the client does not remove those -/// automatically. -static int clif_clearunit_delayed_sub(int tid, unsigned int tick, int id, intptr_t data) -{ - struct block_list *bl = (struct block_list *)data; - clif_clearunit_area(bl, (clr_type) id); - ers_free(delay_clearunit_ers,bl); - return 0; -} -void clif_clearunit_delayed(struct block_list* bl, clr_type type, unsigned int tick) -{ - struct block_list *tbl = ers_alloc(delay_clearunit_ers, struct block_list); - memcpy (tbl, bl, sizeof (struct block_list)); - add_timer(tick, clif_clearunit_delayed_sub, (int)type, (intptr_t)tbl); -} - -void clif_get_weapon_view(struct map_session_data* sd, unsigned short *rhand, unsigned short *lhand) -{ - if(sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER)) - { - *rhand = *lhand = 0; - return; - } - -#if PACKETVER < 4 - *rhand = sd->status.weapon; - *lhand = sd->status.shield; -#else - if (sd->equip_index[EQI_HAND_R] >= 0 && - sd->inventory_data[sd->equip_index[EQI_HAND_R]]) - { - struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_R]]; - if (id->view_id > 0) - *rhand = id->view_id; - else - *rhand = id->nameid; - } else - *rhand = 0; - - if (sd->equip_index[EQI_HAND_L] >= 0 && - sd->equip_index[EQI_HAND_L] != sd->equip_index[EQI_HAND_R] && - sd->inventory_data[sd->equip_index[EQI_HAND_L]]) - { - struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_L]]; - if (id->view_id > 0) - *lhand = id->view_id; - else - *lhand = id->nameid; - } else - *lhand = 0; -#endif -} - -//To make the assignation of the level based on limits clearer/easier. [Skotlex] -static int clif_setlevel_sub(int lv) -{ - if( lv < battle_config.max_lv ) - { - ; - } - else if( lv < battle_config.aura_lv ) - { - lv = battle_config.max_lv - 1; - } - else - { - lv = battle_config.max_lv; - } - - return lv; -} - -static int clif_setlevel(struct block_list* bl) -{ - int lv = status_get_lv(bl); - if( battle_config.client_limit_unit_lv&bl->type ) - return clif_setlevel_sub(lv); - switch( bl->type ) - { - case BL_NPC: - case BL_PET: - // npcs and pets do not have level - return 0; - } - return lv; -} - -/*========================================== - * Prepares 'unit standing/spawning' packet - *------------------------------------------*/ -static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn) -{ - struct map_session_data* sd; - struct status_change* sc = status_get_sc(bl); - struct view_data* vd = status_get_viewdata(bl); - unsigned char *buf = WBUFP(buffer,0); -#if PACKETVER < 20091103 - bool type = !pcdb_checkid(vd->class_); -#endif - unsigned short offset = 0; -#if PACKETVER >= 20091103 - const char *name; -#endif - sd = BL_CAST(BL_PC, bl); - -#if PACKETVER < 20091103 - if(type) - WBUFW(buf,0) = spawn?0x7c:0x78; - else -#endif -#if PACKETVER < 4 - WBUFW(buf,0) = spawn?0x79:0x78; -#elif PACKETVER < 7 - WBUFW(buf,0) = spawn?0x1d9:0x1d8; -#elif PACKETVER < 20080102 - WBUFW(buf,0) = spawn?0x22b:0x22a; -#elif PACKETVER < 20091103 - WBUFW(buf,0) = spawn?0x2ed:0x2ee; -#elif PACKETVER < 20101124 - WBUFW(buf,0) = spawn?0x7f8:0x7f9; -#else - WBUFW(buf,0) = spawn?0x858:0x857; -#endif - -#if PACKETVER >= 20091103 - name = status_get_name(bl); -#if PACKETVER < 20110111 - WBUFW(buf,2) = (spawn?62:63)+strlen(name); -#else - WBUFW(buf,2) = (spawn?64:65)+strlen(name); -#endif - WBUFB(buf,4) = clif_bl_type(bl); - offset+=3; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 20071106 - if (type) { //Non-player packets - WBUFB(buf,2) = clif_bl_type(bl); - offset++; - buf = WBUFP(buffer,offset); - } -#endif - WBUFL(buf, 2) = bl->id; - WBUFW(buf, 6) = status_get_speed(bl); - WBUFW(buf, 8) = (sc)? sc->opt1 : 0; - WBUFW(buf,10) = (sc)? sc->opt2 : 0; -#if PACKETVER < 20091103 - if (type&&spawn) { //uses an older and different packet structure - WBUFW(buf,12) = (sc)? sc->option : 0; - WBUFW(buf,14) = vd->hair_style; - WBUFW(buf,16) = vd->weapon; - WBUFW(buf,18) = vd->head_bottom; - WBUFW(buf,20) = vd->class_; //Pet armor (ignored by client) - WBUFW(buf,22) = vd->shield; - } else { -#endif -#if PACKETVER >= 20091103 - WBUFL(buf,12) = (sc)? sc->option : 0; - offset+=2; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 7 - if (!type) { - WBUFL(buf,12) = (sc)? sc->option : 0; - offset+=2; - buf = WBUFP(buffer,offset); - } else - WBUFW(buf,12) = (sc)? sc->option : 0; -#else - WBUFW(buf,12) = (sc)? sc->option : 0; -#endif - WBUFW(buf,14) = vd->class_; - WBUFW(buf,16) = vd->hair_style; - WBUFW(buf,18) = vd->weapon; -#if PACKETVER < 4 - WBUFW(buf,20) = vd->head_bottom; - WBUFW(buf,22) = vd->shield; -#else - WBUFW(buf,20) = vd->shield; - WBUFW(buf,22) = vd->head_bottom; -#endif -#if PACKETVER < 20091103 - } -#endif - WBUFW(buf,24) = vd->head_top; - WBUFW(buf,26) = vd->head_mid; - - if( bl->type == BL_NPC && vd->class_ == FLAG_CLASS ) - { //The hell, why flags work like this? - WBUFW(buf,22) = status_get_emblem_id(bl); - WBUFW(buf,24) = GetWord(status_get_guild_id(bl), 1); - WBUFW(buf,26) = GetWord(status_get_guild_id(bl), 0); - } - - WBUFW(buf,28) = vd->hair_color; - WBUFW(buf,30) = vd->cloth_color; - WBUFW(buf,32) = (sd)? sd->head_dir : 0; -#if PACKETVER < 20091103 - if (type&&spawn) { //End of packet 0x7c - WBUFB(buf,34) = (sd)?sd->status.karma:0; // karma - WBUFB(buf,35) = vd->sex; - WBUFPOS(buf,36,bl->x,bl->y,unit_getdir(bl)); - WBUFB(buf,39) = 0; - WBUFB(buf,40) = 0; - return packet_len(0x7c); - } -#endif -#if PACKETVER >= 20110111 - WBUFW(buf,34) = vd->robe; - offset+= 2; - buf = WBUFP(buffer,offset); -#endif - WBUFL(buf,34) = status_get_guild_id(bl); - WBUFW(buf,38) = status_get_emblem_id(bl); - WBUFW(buf,40) = (sd)? sd->status.manner : 0; -#if PACKETVER >= 20091103 - WBUFL(buf,42) = (sc)? sc->opt3 : 0; - offset+=2; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 7 - if (!type) { - WBUFL(buf,42) = (sc)? sc->opt3 : 0; - offset+=2; - buf = WBUFP(buffer,offset); - } else - WBUFW(buf,42) = (sc)? sc->opt3 : 0; -#else - WBUFW(buf,42) = (sc)? sc->opt3 : 0; -#endif - WBUFB(buf,44) = (sd)? sd->status.karma : 0; - WBUFB(buf,45) = vd->sex; - WBUFPOS(buf,46,bl->x,bl->y,unit_getdir(bl)); - WBUFB(buf,49) = (sd)? 5 : 0; - WBUFB(buf,50) = (sd)? 5 : 0; - if (!spawn) { - WBUFB(buf,51) = vd->dead_sit; - offset++; - buf = WBUFP(buffer,offset); - } - WBUFW(buf,51) = clif_setlevel(bl); -#if PACKETVER < 20091103 - if (type) //End for non-player packet - return packet_len(WBUFW(buffer,0)); -#endif -#if PACKETVER >= 20080102 - WBUFW(buf,53) = sd?sd->user_font:0; -#endif -#if PACKETVER >= 20091103 - memcpy((char*)WBUFP(buf,55), name, NAME_LENGTH); - return WBUFW(buffer,2); -#else - return packet_len(WBUFW(buffer,0)); -#endif -} - -/*========================================== - * Prepares 'unit walking' packet - *------------------------------------------*/ -static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, unsigned char* buffer) -{ - struct map_session_data* sd; - struct status_change* sc = status_get_sc(bl); - struct view_data* vd = status_get_viewdata(bl); - unsigned char* buf = WBUFP(buffer,0); -#if PACKETVER >= 7 - unsigned short offset = 0; -#endif -#if PACKETVER >= 20091103 - const char *name; -#endif - - sd = BL_CAST(BL_PC, bl); - -#if PACKETVER < 4 - WBUFW(buf, 0) = 0x7b; -#elif PACKETVER < 7 - WBUFW(buf, 0) = 0x1da; -#elif PACKETVER < 20080102 - WBUFW(buf, 0) = 0x22c; -#elif PACKETVER < 20091103 - WBUFW(buf, 0) = 0x2ec; -#elif PACKETVER < 20101124 - WBUFW(buf, 0) = 0x7f7; -#else - WBUFW(buf, 0) = 0x856; -#endif - -#if PACKETVER >= 20091103 - name = status_get_name(bl); -#if PACKETVER < 20110111 - WBUFW(buf, 2) = 69+strlen(name); -#else - WBUFW(buf, 2) = 71+strlen(name); -#endif - offset+=2; - buf = WBUFP(buffer,offset); -#endif -#if PACKETVER >= 20071106 - WBUFB(buf, 2) = clif_bl_type(bl); - offset++; - buf = WBUFP(buffer,offset); -#endif - WBUFL(buf, 2) = bl->id; - WBUFW(buf, 6) = status_get_speed(bl); - WBUFW(buf, 8) = (sc)? sc->opt1 : 0; - WBUFW(buf,10) = (sc)? sc->opt2 : 0; -#if PACKETVER < 7 - WBUFW(buf,12) = (sc)? sc->option : 0; -#else - WBUFL(buf,12) = (sc)? sc->option : 0; - offset+=2; //Shift the rest of elements by 2 bytes. - buf = WBUFP(buffer,offset); -#endif - WBUFW(buf,14) = vd->class_; - WBUFW(buf,16) = vd->hair_style; - WBUFW(buf,18) = vd->weapon; -#if PACKETVER < 4 - WBUFW(buf,20) = vd->head_bottom; - WBUFL(buf,22) = gettick(); - WBUFW(buf,26) = vd->shield; -#else - WBUFW(buf,20) = vd->shield; - WBUFW(buf,22) = vd->head_bottom; - WBUFL(buf,24) = gettick(); -#endif - WBUFW(buf,28) = vd->head_top; - WBUFW(buf,30) = vd->head_mid; - WBUFW(buf,32) = vd->hair_color; - WBUFW(buf,34) = vd->cloth_color; - WBUFW(buf,36) = (sd)? sd->head_dir : 0; -#if PACKETVER >= 20110111 - WBUFW(buf,38) = vd->robe; - offset+= 2; - buf = WBUFP(buffer,offset); -#endif - WBUFL(buf,38) = status_get_guild_id(bl); - WBUFW(buf,42) = status_get_emblem_id(bl); - WBUFW(buf,44) = (sd)? sd->status.manner : 0; -#if PACKETVER < 7 - WBUFW(buf,46) = (sc)? sc->opt3 : 0; -#else - WBUFL(buf,46) = (sc)? sc->opt3 : 0; - offset+=2; //Shift the rest of elements by 2 bytes. - buf = WBUFP(buffer,offset); -#endif - WBUFB(buf,48) = (sd)? sd->status.karma : 0; - WBUFB(buf,49) = vd->sex; - WBUFPOS2(buf,50,bl->x,bl->y,ud->to_x,ud->to_y,8,8); - WBUFB(buf,56) = (sd)? 5 : 0; - WBUFB(buf,57) = (sd)? 5 : 0; - WBUFW(buf,58) = clif_setlevel(bl); -#if PACKETVER >= 20080102 - WBUFW(buf,60) = sd?sd->user_font:0; -#endif -#if PACKETVER >= 20091103 - memcpy((char*)WBUFP(buf,62), name, NAME_LENGTH); - return WBUFW(buffer,2); -#else - return packet_len(WBUFW(buffer,0)); -#endif -} - -//Modifies the buffer for disguise characters and sends it to self. -//Used for spawn/walk packets, where the ID offset changes for packetver >=9 -static void clif_setdisguise(struct block_list *bl, unsigned char *buf,int len) -{ -#if PACKETVER >= 20091103 - WBUFB(buf,4)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE - WBUFL(buf,5)=-bl->id; -#elif PACKETVER >= 20071106 - WBUFB(buf,2)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE - WBUFL(buf,3)=-bl->id; -#else - WBUFL(buf,2)=-bl->id; -#endif - clif_send(buf, len, bl, SELF); -} - - -/// Changes sprite of an NPC object (ZC_NPCSPRITE_CHANGE). -/// 01b0 <id>.L <type>.B <value>.L -/// type: -/// unused -void clif_class_change(struct block_list *bl,int class_,int type) -{ - unsigned char buf[16]; - - nullpo_retv(bl); - - if(!pcdb_checkid(class_)) - {// player classes yield missing sprites - WBUFW(buf,0)=0x1b0; - WBUFL(buf,2)=bl->id; - WBUFB(buf,6)=type; - WBUFL(buf,7)=class_; - clif_send(buf,packet_len(0x1b0),bl,AREA); - } -} - - -/// Notifies the client of an object's spirits. -/// 01d0 <id>.L <amount>.W (ZC_SPIRITS) -/// 01e1 <id>.L <amount>.W (ZC_SPIRITS2) -static void clif_spiritball_single(int fd, struct map_session_data *sd) -{ - WFIFOHEAD(fd, packet_len(0x1e1)); - WFIFOW(fd,0)=0x1e1; - WFIFOL(fd,2)=sd->bl.id; - WFIFOW(fd,6)=sd->spiritball; - WFIFOSET(fd, packet_len(0x1e1)); -} - -/*========================================== - * Kagerou/Oboro amulet spirit - *------------------------------------------*/ -static void clif_talisman_single(int fd, struct map_session_data *sd, short type) -{ - WFIFOHEAD(fd, packet_len(0x08cf)); - WFIFOW(fd,0)=0x08cf; - WFIFOL(fd,2)=sd->bl.id; - WFIFOW(fd,6)=type; - WFIFOW(fd,8)=sd->talisman[type]; - WFIFOSET(fd, packet_len(0x08cf)); -} - -/*========================================== - * 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) -{ - int16 m = sd->bl.m; - int fd = sd->fd; - - if (map[m].flag.snow - || map[m].flag.clouds - || map[m].flag.fog - || map[m].flag.fireworks - || map[m].flag.sakura - || map[m].flag.leaves - /** - * 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) - clif_specialeffect_single(&sd->bl, 162, fd); - if (map[m].flag.clouds) - clif_specialeffect_single(&sd->bl, 233, fd); - if (map[m].flag.clouds2) - clif_specialeffect_single(&sd->bl, 516, fd); - if (map[m].flag.fog) - clif_specialeffect_single(&sd->bl, 515, fd); - if (map[m].flag.fireworks) { - clif_specialeffect_single(&sd->bl, 297, fd); - clif_specialeffect_single(&sd->bl, 299, fd); - clif_specialeffect_single(&sd->bl, 301, fd); - } - if (map[m].flag.sakura) - clif_specialeffect_single(&sd->bl, 163, fd); - if (map[m].flag.leaves) - clif_specialeffect_single(&sd->bl, 333, 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(int16 m) -{ - struct s_mapiterator* iter; - struct map_session_data *sd=NULL; - - iter = mapit_getallusers(); - for( sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) ) - { - if( sd->bl.m == m ) - clif_weather_check(sd); - } - 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]; - struct view_data *vd; - int len; - - vd = status_get_viewdata(bl); - if( !vd || vd->class_ == INVISIBLE_CLASS ) - return 0; - - /** - * Hide NPC from maya purple card. - **/ - if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) - return 0; - - len = clif_set_unit_idle(bl, buf,true); - clif_send(buf, len, bl, AREA_WOS); - if (disguised(bl)) - clif_setdisguise(bl, buf, len); - - if (vd->cloth_color) - clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS); - - switch (bl->type) - { - case BL_PC: - { - TBL_PC *sd = ((TBL_PC*)bl); - int i; - if (sd->spiritball > 0) - clif_spiritball(&sd->bl); - if(sd->state.size==SZ_BIG) // tiny/big players [Valaris] - clif_specialeffect(bl,423,AREA); - else if(sd->state.size==SZ_MEDIUM) - clif_specialeffect(bl,421,AREA); - if( sd->bg_id && map[sd->bl.m].flag.battleground ) - clif_sendbgemblem_area(sd); - 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); - } - for(i = 1; i < 5; i++){ - if( sd->talisman[i] > 0 ) - clif_talisman(sd, i); - } - #ifdef NEW_CARTS - if( sd->sc.data[SC_PUSH_CART] ) - clif_status_load_notick(&sd->bl, SI_ON_PUSH_CART, 2, sd->sc.data[SC_PUSH_CART]->val1, 0, 0); - #endif - #if PACKETVER <= 20120207 - if (sd->status.robe) - clif_refreshlook(bl,bl->id,LOOK_ROBE,sd->status.robe,AREA); - #endif - } - break; - case BL_MOB: - { - TBL_MOB *md = ((TBL_MOB*)bl); - if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris] - clif_specialeffect(&md->bl,423,AREA); - else if(md->special_state.size==SZ_MEDIUM) - clif_specialeffect(&md->bl,421,AREA); - } - break; - case BL_NPC: - { - TBL_NPC *nd = ((TBL_NPC*)bl); - if( nd->size == SZ_BIG ) - clif_specialeffect(&nd->bl,423,AREA); - else if( nd->size == SZ_MEDIUM ) - clif_specialeffect(&nd->bl,421,AREA); - } - break; - case BL_PET: - if (vd->head_bottom) - clif_pet_equip_area((TBL_PET*)bl); // needed to display pet equip properly - break; - } - return 0; -} - -/// Sends information about owned homunculus to the client (ZC_PROPERTY_HOMUN). [orn] -/// 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W -void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag) -{ - struct status_data *status; - unsigned char buf[128]; - int m_class; - - nullpo_retv(hd); - - status = &hd->battle_status; - m_class = hom_class2mapid(hd->homunculus.class_); - - memset(buf,0,packet_len(0x22e)); - WBUFW(buf,0)=0x22e; - memcpy(WBUFP(buf,2),hd->homunculus.name,NAME_LENGTH); - // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true) - WBUFB(buf,26)=(battle_config.hom_rename?0:hd->homunculus.rename_flag) | (hd->homunculus.vaporize << 1) | (hd->homunculus.hp?0:4); - WBUFW(buf,27)=hd->homunculus.level; - WBUFW(buf,29)=hd->homunculus.hunger; - WBUFW(buf,31)=(unsigned short) (hd->homunculus.intimacy / 100) ; - WBUFW(buf,33)=0; // equip id - WBUFW(buf,35)=cap_value(status->rhw.atk2+status->batk, 0, INT16_MAX); - WBUFW(buf,37)=cap_value(status->matk_max, 0, INT16_MAX); - WBUFW(buf,39)=status->hit; - if (battle_config.hom_setting&0x10) - WBUFW(buf,41)=status->luk/3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious] - else - WBUFW(buf,41)=status->cri/10; - WBUFW(buf,43)=status->def + status->vit ; - WBUFW(buf,45)=status->mdef; - WBUFW(buf,47)=status->flee; - WBUFW(buf,49)=(flag)?0:status->amotion; - if (status->max_hp > INT16_MAX) { - WBUFW(buf,51) = status->hp/(status->max_hp/100); - WBUFW(buf,53) = 100; - } else { - WBUFW(buf,51)=status->hp; - WBUFW(buf,53)=status->max_hp; - } - if (status->max_sp > INT16_MAX) { - WBUFW(buf,55) = status->sp/(status->max_sp/100); - WBUFW(buf,57) = 100; - } else { - WBUFW(buf,55)=status->sp; - WBUFW(buf,57)=status->max_sp; - } - WBUFL(buf,59)=hd->homunculus.exp; - if( ((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level) || ((m_class&HOM_S) && hd->homunculus.level >= battle_config.hom_S_max_level) ) - WBUFL(buf,63)=0; - else - WBUFL(buf,63)=hd->exp_next; - WBUFW(buf,67)=hd->homunculus.skillpts; - WBUFW(buf,69)=status_get_range(&hd->bl); - clif_send(buf,packet_len(0x22e),&sd->bl,SELF); -} - - -/// Notification about a change in homunuculus' state (ZC_CHANGESTATE_MER). -/// 0230 <type>.B <state>.B <id>.L <data>.L -/// type: -/// unused -/// state: -/// 0 = pre-init -/// 1 = intimacy -/// 2 = hunger -/// 3 = accessory? -/// ? = ignored -void clif_send_homdata(struct map_session_data *sd, int state, int param) -{ //[orn] - int fd = sd->fd; - - if ( (state == SP_INTIMATE) && (param >= 910) && (sd->hd->homunculus.class_ == sd->hd->homunculusDB->evo_class) ) - merc_hom_calc_skilltree(sd->hd, 0); - - WFIFOHEAD(fd, packet_len(0x230)); - WFIFOW(fd,0)=0x230; - WFIFOB(fd,2)=0; - WFIFOB(fd,3)=state; - WFIFOL(fd,4)=sd->hd->bl.id; - WFIFOL(fd,8)=param; - WFIFOSET(fd,packet_len(0x230)); -} - - -int clif_homskillinfoblock(struct map_session_data *sd) -{ //[orn] - struct homun_data *hd; - int fd = sd->fd; - int i,j,len=4,id; - WFIFOHEAD(fd, 4+37*MAX_HOMUNSKILL); - - hd = sd->hd; - if ( !hd ) - return 0 ; - - WFIFOW(fd,0)=0x235; - for ( i = 0; i < MAX_HOMUNSKILL; i++){ - if( (id = hd->homunculus.hskill[i].id) != 0 ){ - j = id - HM_SKILLBASE; - WFIFOW(fd,len ) = id; - WFIFOW(fd,len+2) = skill_get_inf(id); - WFIFOW(fd,len+4) = 0; - WFIFOW(fd,len+6) = hd->homunculus.hskill[j].lv; - WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[j].lv); - WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[j].lv); - safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH); - WFIFOB(fd,len+36) = (hd->homunculus.hskill[j].lv < merc_skill_tree_get_max(id, hd->homunculus.class_))?1:0; - len+=37; - } - } - WFIFOW(fd,2)=len; - WFIFOSET(fd,len); - - return 0; -} - -void clif_homskillup(struct map_session_data *sd, uint16 skill_id) -{ //[orn] - struct homun_data *hd; - int fd, idx; - nullpo_retv(sd); - idx = skill_id - HM_SKILLBASE; - - fd=sd->fd; - hd=sd->hd; - - WFIFOHEAD(fd, packet_len(0x239)); - WFIFOW(fd,0) = 0x239; - WFIFOW(fd,2) = skill_id; - WFIFOW(fd,4) = hd->homunculus.hskill[idx].lv; - WFIFOW(fd,6) = skill_get_sp(skill_id,hd->homunculus.hskill[idx].lv); - WFIFOW(fd,8) = skill_get_range2(&hd->bl, skill_id,hd->homunculus.hskill[idx].lv); - WFIFOB(fd,10) = (hd->homunculus.hskill[idx].lv < skill_get_max(hd->homunculus.hskill[idx].id)) ? 1 : 0; - WFIFOSET(fd,packet_len(0x239)); -} - -int clif_hom_food(struct map_session_data *sd,int foodid,int fail) //[orn] -{ - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x22f)); - WFIFOW(fd,0)=0x22f; - WFIFOB(fd,2)=fail; - WFIFOW(fd,3)=foodid; - WFIFOSET(fd,packet_len(0x22f)); - - return 0; -} - - -/// Notifies the client, that it is walking (ZC_NOTIFY_PLAYERMOVE). -/// 0087 <walk start time>.L <walk data>.6B -void clif_walkok(struct map_session_data *sd) -{ - int fd=sd->fd; - - WFIFOHEAD(fd, packet_len(0x87)); - WFIFOW(fd,0)=0x87; - WFIFOL(fd,2)=gettick(); - WFIFOPOS2(fd,6,sd->bl.x,sd->bl.y,sd->ud.to_x,sd->ud.to_y,8,8); - WFIFOSET(fd,packet_len(0x87)); -} - - -static void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *ud) -{ - uint8 buf[128]; - int len; - - len = clif_set_unit_walking(bl,ud,buf); - clif_send(buf,len,bl,AREA_WOS); - if (disguised(bl)) - clif_setdisguise(bl, buf, len); - - if(vd->cloth_color) - clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS); - - switch(bl->type) - { - case BL_PC: - { - TBL_PC *sd = ((TBL_PC*)bl); -// clif_movepc(sd); - if(sd->state.size==SZ_BIG) // tiny/big players [Valaris] - clif_specialeffect(&sd->bl,423,AREA); - else if(sd->state.size==SZ_MEDIUM) - clif_specialeffect(&sd->bl,421,AREA); - } - break; - case BL_MOB: - { - TBL_MOB *md = ((TBL_MOB*)bl); - if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris] - clif_specialeffect(&md->bl,423,AREA); - else if(md->special_state.size==SZ_MEDIUM) - clif_specialeffect(&md->bl,421,AREA); - } - break; - case BL_PET: - if( vd->head_bottom ) - {// needed to display pet equip properly - clif_pet_equip_area((TBL_PET*)bl); - } - break; - } -} - - -/// Notifies clients in an area, that an other visible object is walking (ZC_NOTIFY_PLAYERMOVE). -/// 0086 <id>.L <walk data>.6B <walk start time>.L -/// Note: unit must not be self -void clif_move(struct unit_data *ud) -{ - unsigned char buf[16]; - struct view_data* vd; - struct block_list* bl = ud->bl; - - vd = status_get_viewdata(bl); - if (!vd || vd->class_ == INVISIBLE_CLASS) - return; //This performance check is needed to keep GM-hidden objects from being notified to bots. - - /** - * Hide NPC from maya purple card. - **/ - if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) - return; - - if (ud->state.speed_changed) { - // Since we don't know how to update the speed of other objects, - // use the old walk packet to update the data. - ud->state.speed_changed = 0; - clif_move2(bl, vd, ud); - return; - } - - WBUFW(buf,0)=0x86; - WBUFL(buf,2)=bl->id; - WBUFPOS2(buf,6,bl->x,bl->y,ud->to_x,ud->to_y,8,8); - WBUFL(buf,12)=gettick(); - clif_send(buf, packet_len(0x86), bl, AREA_WOS); - if (disguised(bl)) - { - WBUFL(buf,2)=-bl->id; - clif_send(buf, packet_len(0x86), bl, SELF); - } -} - - -/*========================================== - * Delays the map_quit of a player after they are disconnected. [Skotlex] - *------------------------------------------*/ -static int clif_delayquit(int tid, unsigned int tick, int id, intptr_t data) -{ - struct map_session_data *sd = NULL; - - //Remove player from map server - if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player. - map_quit(sd); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -void clif_quitsave(int fd,struct map_session_data *sd) -{ - if (!battle_config.prevent_logout || - DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) - map_quit(sd); - else if (sd->fd) - { //Disassociate session from player (session is deleted after this function was called) - //And set a timer to make him quit later. - session[sd->fd]->session_data = NULL; - sd->fd = 0; - add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 0); - } -} - -/// Notifies the client of a position change to coordinates on given map (ZC_NPCACK_MAPMOVE). -/// 0091 <map name>.16B <x>.W <y>.W -void clif_changemap(struct map_session_data *sd, short map, int x, int y) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x91)); - WFIFOW(fd,0) = 0x91; - mapindex_getmapname_ext(mapindex_id2name(map), (char*)WFIFOP(fd,2)); - WFIFOW(fd,18) = x; - WFIFOW(fd,20) = y; - WFIFOSET(fd,packet_len(0x91)); -} - - -/// Notifies the client of a position change to coordinates on given map, which is on another map-server (ZC_NPCACK_SERVERMOVE). -/// 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W -void clif_changemapserver(struct map_session_data* sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x92)); - WFIFOW(fd,0) = 0x92; - mapindex_getmapname_ext(mapindex_id2name(map_index), (char*)WFIFOP(fd,2)); - WFIFOW(fd,18) = x; - WFIFOW(fd,20) = y; - WFIFOL(fd,22) = htonl(ip); - WFIFOW(fd,26) = ntows(htons(port)); // [!] LE byte order here [!] - WFIFOSET(fd,packet_len(0x92)); -} - - -void clif_blown(struct block_list *bl) -{ -//Aegis packets says fixpos, but it's unsure whether slide works better or not. -// clif_fixpos(bl); - clif_slide(bl, bl->x, bl->y); -} - - -/// Visually moves(slides) a character to x,y. If the target cell -/// isn't walkable, the char doesn't move at all. If the char is -/// sitting it will stand up (ZC_STOPMOVE). -/// 0088 <id>.L <x>.W <y>.W -void clif_fixpos(struct block_list *bl) -{ - unsigned char buf[10]; - nullpo_retv(bl); - - WBUFW(buf,0) = 0x88; - WBUFL(buf,2) = bl->id; - WBUFW(buf,6) = bl->x; - WBUFW(buf,8) = bl->y; - clif_send(buf, packet_len(0x88), bl, AREA); - - if( disguised(bl) ) - { - WBUFL(buf,2) = -bl->id; - clif_send(buf, packet_len(0x88), bl, SELF); - } -} - - -/// Displays the buy/sell dialog of an NPC shop (ZC_SELECT_DEALTYPE). -/// 00c4 <shop id>.L -void clif_npcbuysell(struct map_session_data* sd, int id) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0xc4)); - WFIFOW(fd,0)=0xc4; - WFIFOL(fd,2)=id; - WFIFOSET(fd,packet_len(0xc4)); -} - - -/// Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST). -/// 00c6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.W }* -void clif_buylist(struct map_session_data *sd, struct npc_data *nd) -{ - int fd,i,c; - - nullpo_retv(sd); - nullpo_retv(nd); - - fd = sd->fd; - WFIFOHEAD(fd, 4 + nd->u.shop.count * 11); - WFIFOW(fd,0) = 0xc6; - - c = 0; - for( i = 0; i < nd->u.shop.count; i++ ) - { - struct item_data* id = itemdb_exists(nd->u.shop.shop_item[i].nameid); - int val = nd->u.shop.shop_item[i].value; - if( id == NULL ) - continue; - WFIFOL(fd, 4+c*11) = val; - WFIFOL(fd, 8+c*11) = pc_modifybuyvalue(sd,val); - WFIFOB(fd,12+c*11) = itemtype(id->type); - WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid; - c++; - } - - WFIFOW(fd,2) = 4 + c*11; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Presents list of items, that can be sold to an NPC shop (ZC_PC_SELL_ITEMLIST). -/// 00c7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }* -void clif_selllist(struct map_session_data *sd) -{ - int fd,i,c=0,val; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4); - WFIFOW(fd,0)=0xc7; - for( i = 0; i < MAX_INVENTORY; i++ ) - { - if( sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] ) - { - if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) ) - continue; - - if( sd->status.inventory[i].expire_time ) - continue; // Cannot Sell Rental Items - - val=sd->inventory_data[i]->value_sell; - if( val < 0 ) - continue; - WFIFOW(fd,4+c*10)=i+2; - WFIFOL(fd,6+c*10)=val; - WFIFOL(fd,10+c*10)=pc_modifysellvalue(sd,val); - c++; - } - } - WFIFOW(fd,2)=c*10+4; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Displays an NPC dialog message (ZC_SAY_DIALOG). -/// 00b4 <packet len>.W <npc id>.L <message>.?B -/// Client behavior (dialog window): -/// - disable mouse targeting -/// - open the dialog window -/// - set npcid of dialog window (0 by default) -/// - if set to clear on next mes, clear contents -/// - append this text -void clif_scriptmes(struct map_session_data *sd, int npcid, const char *mes) -{ - int fd = sd->fd; - int slen = strlen(mes) + 9; - - WFIFOHEAD(fd, slen); - WFIFOW(fd,0)=0xb4; - WFIFOW(fd,2)=slen; - WFIFOL(fd,4)=npcid; - memcpy((char*)WFIFOP(fd,8), mes, slen-8); - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Adds a 'next' button to an NPC dialog (ZC_WAIT_DIALOG). -/// 00b5 <npc id>.L -/// Client behavior (dialog window): -/// - disable mouse targeting -/// - open the dialog window -/// - add 'next' button -/// When 'next' is pressed: -/// - 00B9 <npcid of dialog window>.L -/// - set to clear on next mes -/// - remove 'next' button -void clif_scriptnext(struct map_session_data *sd,int npcid) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0xb5)); - WFIFOW(fd,0)=0xb5; - WFIFOL(fd,2)=npcid; - WFIFOSET(fd,packet_len(0xb5)); -} - - -/// Adds a 'close' button to an NPC dialog (ZC_CLOSE_DIALOG). -/// 00b6 <npc id>.L -/// Client behavior: -/// - if dialog window is open: -/// - remove 'next' button -/// - add 'close' button -/// - else: -/// - enable mouse targeting -/// - close the dialog window -/// - close the menu window -/// When 'close' is pressed: -/// - enable mouse targeting -/// - close the dialog window -/// - close the menu window -/// - 0146 <npcid of dialog window>.L -void clif_scriptclose(struct map_session_data *sd, int npcid) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0xb6)); - WFIFOW(fd,0)=0xb6; - WFIFOL(fd,2)=npcid; - WFIFOSET(fd,packet_len(0xb6)); -} - -/*========================================== - * - *------------------------------------------*/ -void clif_sendfakenpc(struct map_session_data *sd, int npcid) -{ - unsigned char *buf; - int fd = sd->fd; - sd->state.using_fake_npc = 1; - - WFIFOHEAD(fd, packet_len(0x78)); - buf = WFIFOP(fd,0); - memset(WBUFP(buf,0), 0, packet_len(0x78)); - WBUFW(buf,0)=0x78; -#if PACKETVER >= 20071106 - WBUFB(buf,2) = 0; // object type - buf = WFIFOP(fd,1); -#endif - WBUFL(buf,2)=npcid; - WBUFW(buf,14)=111; - WBUFPOS(buf,46,sd->bl.x,sd->bl.y,sd->ud.dir); - WBUFB(buf,49)=5; - WBUFB(buf,50)=5; - WFIFOSET(fd, packet_len(0x78)); -} - - -/// Displays an NPC dialog menu (ZC_MENU_LIST). -/// 00b7 <packet len>.W <npc id>.L <menu items>.?B -/// Client behavior: -/// - disable mouse targeting -/// - close the menu window -/// - open the menu window -/// - add options to the menu (separated in the text by ":") -/// - set npcid of menu window -/// - if dialog window is open: -/// - remove 'next' button -/// When 'ok' is pressed: -/// - 00B8 <npcid of menu window>.L <selected option>.B -/// - close the menu window -/// When 'cancel' is pressed: -/// - 00B8 <npcid of menu window>.L <-1>.B -/// - enable mouse targeting -/// - close a bunch of windows... -/// WARNING: the 'cancel' button closes other windows besides the dialog window and the menu window. -/// Which suggests their have intertwined behavior. (probably the mouse targeting) -/// TODO investigate behavior of other windows [FlavioJS] -void clif_scriptmenu(struct map_session_data* sd, int npcid, const char* mes) -{ - int fd = sd->fd; - int slen = strlen(mes) + 9; - struct block_list *bl = NULL; - - if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m || - bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || - bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)))) - clif_sendfakenpc(sd, npcid); - - WFIFOHEAD(fd, slen); - WFIFOW(fd,0)=0xb7; - WFIFOW(fd,2)=slen; - WFIFOL(fd,4)=npcid; - memcpy((char*)WFIFOP(fd,8), mes, slen-8); - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLG). -/// 0142 <npc id>.L -/// Client behavior (inputnum window): -/// - if npcid exists in the client: -/// - open the inputnum window -/// - set npcid of inputnum window -/// When 'ok' is pressed: -/// - if inputnum window has text: -/// - if npcid exists in the client: -/// - 0143 <npcid of inputnum window>.L <atoi(text)>.L -/// - close inputnum window -void clif_scriptinput(struct map_session_data *sd, int npcid) -{ - int fd; - struct block_list *bl = NULL; - - nullpo_retv(sd); - - if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m || - bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || - bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)))) - clif_sendfakenpc(sd, npcid); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0x142)); - WFIFOW(fd,0)=0x142; - WFIFOL(fd,2)=npcid; - WFIFOSET(fd,packet_len(0x142)); -} - - -/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLGSTR). -/// 01d4 <npc id>.L -/// Client behavior (inputstr window): -/// - if npcid is 0 or npcid exists in the client: -/// - open the inputstr window -/// - set npcid of inputstr window -/// When 'ok' is pressed: -/// - if inputstr window has text and isn't an insult(manner.txt): -/// - if npcid is 0 or npcid exists in the client: -/// - 01d5 <packetlen>.W <npcid of inputstr window>.L <text>.?B -/// - close inputstr window -void clif_scriptinputstr(struct map_session_data *sd, int npcid) -{ - int fd; - struct block_list *bl = NULL; - - nullpo_retv(sd); - - if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m || - bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 || - bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)))) - clif_sendfakenpc(sd, npcid); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0x1d4)); - WFIFOW(fd,0)=0x1d4; - WFIFOL(fd,2)=npcid; - WFIFOSET(fd,packet_len(0x1d4)); -} - - -/// Marks a position on client's minimap (ZC_COMPASS). -/// 0144 <npc id>.L <type>.L <x>.L <y>.L <id>.B <color>.L -/// npc id: -/// is ignored in the client -/// type: -/// 0 = display mark for 15 seconds -/// 1 = display mark until dead or teleported -/// 2 = remove mark -/// color: -/// 0x00RRGGBB -void clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0x144)); - WFIFOW(fd,0)=0x144; - WFIFOL(fd,2)=npc_id; - WFIFOL(fd,6)=type; - WFIFOL(fd,10)=x; - WFIFOL(fd,14)=y; - WFIFOB(fd,18)=id; - WFIFOL(fd,19)=color; - WFIFOSET(fd,packet_len(0x144)); -} - - -/// Displays an illustration image. -/// 0145 <image name>.16B <type>.B (ZC_SHOW_IMAGE) -/// 01b3 <image name>.64B <type>.B (ZC_SHOW_IMAGE2) -/// type: -/// 0 = bottom left corner -/// 1 = bottom middle -/// 2 = bottom right corner -/// 3 = middle of screen, inside a movable window -/// 4 = middle of screen, movable with a close button, chrome-less -void clif_cutin(struct map_session_data* sd, const char* image, int type) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0x1b3)); - WFIFOW(fd,0)=0x1b3; - strncpy((char*)WFIFOP(fd,2),image,64); - WFIFOB(fd,66)=type; - WFIFOSET(fd,packet_len(0x1b3)); -} - - -/*========================================== - * Fills in card data from the given item and into the buffer. [Skotlex] - *------------------------------------------*/ -static void clif_addcards(unsigned char* buf, struct item* item) -{ - int i=0,j; - if( item == NULL ) { //Blank data - WBUFW(buf,0) = 0; - WBUFW(buf,2) = 0; - WBUFW(buf,4) = 0; - WBUFW(buf,6) = 0; - return; - } - if( item->card[0] == CARD0_PET ) { //pet eggs - WBUFW(buf,0) = 0; - WBUFW(buf,2) = 0; - WBUFW(buf,4) = 0; - WBUFW(buf,6) = item->card[3]; //Pet renamed flag. - return; - } - if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ) { //Forged/created items - WBUFW(buf,0) = item->card[0]; - WBUFW(buf,2) = item->card[1]; - WBUFW(buf,4) = item->card[2]; - WBUFW(buf,6) = item->card[3]; - return; - } - //Client only receives four cards.. so randomly send them a set of cards. [Skotlex] - if( MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4 ) - i = rnd()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3; - - //Normal items. - if( item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,0) = j; - else - WBUFW(buf,0) = item->card[i]; - - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,2) = j; - else - WBUFW(buf,2) = item->card[i]; - - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,4) = j; - else - WBUFW(buf,4) = item->card[i]; - - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,6) = j; - else - WBUFW(buf,6) = item->card[i]; -} - - -/// Notifies the client, about a received inventory item or the result of a pick-up request. -/// 00a0 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B (ZC_ITEM_PICKUP_ACK) -/// 029a <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L (ZC_ITEM_PICKUP_ACK2) -/// 02d4 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK3) -void clif_additem(struct map_session_data *sd, int n, int amount, int fail) -{ - int fd; -#if PACKETVER < 20061218 - const int cmd = 0xa0; -#elif PACKETVER < 20071002 - const int cmd = 0x29a; -#else - const int cmd = 0x2d4; -#endif - nullpo_retv(sd); - - fd = sd->fd; - if( !session_isActive(fd) ) //Sasuke- - return; - - WFIFOHEAD(fd,packet_len(cmd)); - if( fail ) - { - WFIFOW(fd,0)=cmd; - WFIFOW(fd,2)=n+2; - WFIFOW(fd,4)=amount; - WFIFOW(fd,6)=0; - WFIFOB(fd,8)=0; - WFIFOB(fd,9)=0; - WFIFOB(fd,10)=0; - WFIFOW(fd,11)=0; - WFIFOW(fd,13)=0; - WFIFOW(fd,15)=0; - WFIFOW(fd,17)=0; - WFIFOW(fd,19)=0; - WFIFOB(fd,21)=0; - WFIFOB(fd,22)=fail; -#if PACKETVER >= 20061218 - WFIFOL(fd,23)=0; -#endif -#if PACKETVER >= 20071002 - WFIFOW(fd,27)=0; // unknown -#endif - } - else - { - if( n < 0 || n >= MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL ) - return; - - WFIFOW(fd,0)=cmd; - WFIFOW(fd,2)=n+2; - WFIFOW(fd,4)=amount; - if (sd->inventory_data[n]->view_id > 0) - WFIFOW(fd,6)=sd->inventory_data[n]->view_id; - else - WFIFOW(fd,6)=sd->status.inventory[n].nameid; - WFIFOB(fd,8)=sd->status.inventory[n].identify; - WFIFOB(fd,9)=sd->status.inventory[n].attribute; - WFIFOB(fd,10)=sd->status.inventory[n].refine; - clif_addcards(WFIFOP(fd,11), &sd->status.inventory[n]); - WFIFOW(fd,19)=pc_equippoint(sd,n); - WFIFOB(fd,21)=itemtype(sd->inventory_data[n]->type); - WFIFOB(fd,22)=fail; -#if PACKETVER >= 20061218 - WFIFOL(fd,23)=sd->status.inventory[n].expire_time; -#endif -#if PACKETVER >= 20071002 - WFIFOW(fd,27)=0; // unknown -#endif - } - - WFIFOSET(fd,packet_len(cmd)); -} - - -/// Notifies the client, that an inventory item was deleted or dropped (ZC_ITEM_THROW_ACK). -/// 00af <index>.W <amount>.W -void clif_dropitem(struct map_session_data *sd,int n,int amount) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0xaf)); - WFIFOW(fd,0)=0xaf; - WFIFOW(fd,2)=n+2; - WFIFOW(fd,4)=amount; - WFIFOSET(fd,packet_len(0xaf)); -} - - -/// Notifies the client, that an inventory item was deleted (ZC_DELETE_ITEM_FROM_BODY). -/// 07fa <delete type>.W <index>.W <amount>.W -/// delete type: -/// 0 = Normal -/// 1 = Item used for a skill -/// 2 = Refine failed -/// 3 = Material changed -/// 4 = Moved to storage -/// 5 = Moved to cart -/// 6 = Item sold -/// 7 = Consumed by Four Spirit Analysis (SO_EL_ANALYSIS) skill -void clif_delitem(struct map_session_data *sd,int n,int amount, short reason) -{ -#if PACKETVER < 20091117 - clif_dropitem(sd,n,amount); -#else - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - - WFIFOHEAD(fd, packet_len(0x7fa)); - WFIFOW(fd,0)=0x7fa; - WFIFOW(fd,2)=reason; - WFIFOW(fd,4)=n+2; - WFIFOW(fd,6)=amount; - WFIFOSET(fd,packet_len(0x7fa)); -#endif -} - - -// Simplifies inventory/cart/storage packets by handling the packet section relevant to items. [Skotlex] -// Equip is >= 0 for equippable items (holds the equip-point, is 0 for pet -// armor/egg) -1 for stackable items, -2 for stackable items where arrows must send in the equip-point. -void clif_item_sub(unsigned char *buf, int n, struct item *i, struct item_data *id, int equip) -{ - if (id->view_id > 0) - WBUFW(buf,n)=id->view_id; - else - WBUFW(buf,n)=i->nameid; - WBUFB(buf,n+2)=itemtype(id->type); - WBUFB(buf,n+3)=i->identify; - if (equip >= 0) { //Equippable item - WBUFW(buf,n+4)=equip; - WBUFW(buf,n+6)=i->equip; - WBUFB(buf,n+8)=i->attribute; - WBUFB(buf,n+9)=i->refine; - } else { //Stackable item. - WBUFW(buf,n+4)=i->amount; - if (equip == -2 && id->equip == EQP_AMMO) - WBUFW(buf,n+6)=EQP_AMMO; - else - WBUFW(buf,n+6)=0; - } - -} -void clif_favorite_item(struct map_session_data* sd, unsigned short index); -//Unified inventory function which sends all of the inventory (requires two packets, one for equipable items and one for stackable ones. [Skotlex] -void clif_inventorylist(struct map_session_data *sd) -{ - int i,n,ne,arrow=-1; - unsigned char *buf; - unsigned char *bufe; - -#if PACKETVER < 5 - const int s = 10; //Entry size. -#elif PACKETVER < 20080102 - const int s = 18; -#else - const int s = 22; -#endif -#if PACKETVER < 20071002 - const int se = 20; -#elif PACKETVER < 20100629 - const int se = 26; -#else - const int se = 28; -#endif - - buf = (unsigned char*)aMalloc(MAX_INVENTORY * s + 4); - bufe = (unsigned char*)aMalloc(MAX_INVENTORY * se + 4); - - for( i = 0, n = 0, ne = 0; i < MAX_INVENTORY; i++ ) - { - if( sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL ) - continue; - - if( !itemdb_isstackable2(sd->inventory_data[i]) ) - { //Non-stackable (Equippable) - WBUFW(bufe,ne*se+4)=i+2; - clif_item_sub(bufe, ne*se+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i)); - clif_addcards(WBUFP(bufe, ne*se+16), &sd->status.inventory[i]); -#if PACKETVER >= 20071002 - WBUFL(bufe,ne*se+24)=sd->status.inventory[i].expire_time; - WBUFW(bufe,ne*se+28)=0; //Unknown -#endif -#if PACKETVER >= 20100629 - if (sd->inventory_data[i]->equip&EQP_VISIBLE) - WBUFW(bufe,ne*se+30)= sd->inventory_data[i]->look; - else - WBUFW(bufe,ne*se+30)=0; -#endif - ne++; - } - else - { //Stackable. - WBUFW(buf,n*s+4)=i+2; - clif_item_sub(buf, n*s+6, &sd->status.inventory[i], sd->inventory_data[i], -2); - if( sd->inventory_data[i]->equip == EQP_AMMO && sd->status.inventory[i].equip ) - arrow=i; -#if PACKETVER >= 5 - clif_addcards(WBUFP(buf, n*s+14), &sd->status.inventory[i]); -#endif -#if PACKETVER >= 20080102 - WBUFL(buf,n*s+22)=sd->status.inventory[i].expire_time; -#endif - n++; - } - } - if( n ) - { -#if PACKETVER < 5 - WBUFW(buf,0)=0xa3; -#elif PACKETVER < 20080102 - WBUFW(buf,0)=0x1ee; -#else - WBUFW(buf,0)=0x2e8; -#endif - WBUFW(buf,2)=4+n*s; - clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); - } - if( arrow >= 0 ) - clif_arrowequip(sd,arrow); - - if( ne ) - { -#if PACKETVER < 20071002 - WBUFW(bufe,0)=0xa4; -#else - WBUFW(bufe,0)=0x2d0; -#endif - WBUFW(bufe,2)=4+ne*se; - clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF); - } -#if PACKETVER >= 20111122 - for( i = 0; i < MAX_INVENTORY; i++ ) { - if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL ) - continue; - - if ( sd->status.inventory[i].favorite ) - clif_favorite_item(sd, i); - } -#endif - - if( buf ) aFree(buf); - if( bufe ) aFree(bufe); -} - -//Required when items break/get-repaired. Only sends equippable item list. -void clif_equiplist(struct map_session_data *sd) -{ - int i,n,fd = sd->fd; - unsigned char *buf; -#if PACKETVER < 20071002 - const int cmd = 20; -#elif PACKETVER < 20100629 - const int cmd = 26; -#else - const int cmd = 28; -#endif - - WFIFOHEAD(fd, MAX_INVENTORY * cmd + 4); - buf = WFIFOP(fd,0); - - for(i=0,n=0;i<MAX_INVENTORY;i++){ - if (sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL) - continue; - - if(itemdb_isstackable2(sd->inventory_data[i])) - continue; - //Equippable - WBUFW(buf,n*cmd+4)=i+2; - clif_item_sub(buf, n*cmd+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i)); - clif_addcards(WBUFP(buf, n*cmd+16), &sd->status.inventory[i]); -#if PACKETVER >= 20071002 - WBUFL(buf,n*cmd+24)=sd->status.inventory[i].expire_time; - WBUFW(buf,n*cmd+28)=0; //Unknown -#endif -#if PACKETVER >= 20100629 - if (sd->inventory_data[i]->equip&EQP_VISIBLE) - WBUFW(buf,n*cmd+30)= sd->inventory_data[i]->look; - else - WBUFW(buf,n*cmd+30)=0; -#endif - n++; - } - if (n) { -#if PACKETVER < 20071002 - WBUFW(buf,0)=0xa4; -#else - WBUFW(buf,0)=0x2d0; -#endif - WBUFW(buf,2)=4+n*cmd; - WFIFOSET(fd,WFIFOW(fd,2)); - } -} - -void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length) -{ - struct item_data *id; - int i,n,ne; - unsigned char *buf; - unsigned char *bufe; -#if PACKETVER < 5 - const int s = 10; //Entry size. -#elif PACKETVER < 20080102 - const int s = 18; -#else - const int s = 22; -#endif -#if PACKETVER < 20071002 - const int cmd = 20; -#elif PACKETVER < 20100629 - const int cmd = 26; -#else - const int cmd = 28; -#endif - - buf = (unsigned char*)aMalloc(items_length * s + 4); - bufe = (unsigned char*)aMalloc(items_length * cmd + 4); - - for( i = 0, n = 0, ne = 0; i < items_length; i++ ) - { - if( items[i].nameid <= 0 ) - continue; - id = itemdb_search(items[i].nameid); - if( !itemdb_isstackable2(id) ) - { //Equippable - WBUFW(bufe,ne*cmd+4)=i+1; - clif_item_sub(bufe, ne*cmd+6, &items[i], id, id->equip); - clif_addcards(WBUFP(bufe, ne*cmd+16), &items[i]); -#if PACKETVER >= 20071002 - WBUFL(bufe,ne*cmd+24)=items[i].expire_time; - WBUFW(bufe,ne*cmd+28)=0; //Unknown -#endif - ne++; - } - else - { //Stackable - WBUFW(buf,n*s+4)=i+1; - clif_item_sub(buf, n*s+6, &items[i], id,-1); -#if PACKETVER >= 5 - clif_addcards(WBUFP(buf,n*s+14), &items[i]); -#endif -#if PACKETVER >= 20080102 - WBUFL(buf,n*s+22)=items[i].expire_time; -#endif - n++; - } - } - if( n ) - { -#if PACKETVER < 5 - WBUFW(buf,0)=0xa5; -#elif PACKETVER < 20080102 - WBUFW(buf,0)=0x1f0; -#else - WBUFW(buf,0)=0x2ea; -#endif - WBUFW(buf,2)=4+n*s; - clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); - } - if( ne ) - { -#if PACKETVER < 20071002 - WBUFW(bufe,0)=0xa6; -#else - WBUFW(bufe,0)=0x2d1; -#endif - WBUFW(bufe,2)=4+ne*cmd; - clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF); - } - - if( buf ) aFree(buf); - if( bufe ) aFree(bufe); -} - -void clif_cartlist(struct map_session_data *sd) -{ - struct item_data *id; - int i,n,ne; - unsigned char *buf; - unsigned char *bufe; -#if PACKETVER < 5 - const int s = 10; //Entry size. -#elif PACKETVER < 20080102 - const int s = 18; -#else - const int s = 22; -#endif -#if PACKETVER < 20071002 - const int cmd = 20; -#elif PACKETVER < 20100629 - const int cmd = 26; -#else - const int cmd = 28; -#endif - - buf = (unsigned char*)aMalloc(MAX_CART * s + 4); - bufe = (unsigned char*)aMalloc(MAX_CART * cmd + 4); - - for( i = 0, n = 0, ne = 0; i < MAX_CART; i++ ) - { - if( sd->status.cart[i].nameid <= 0 ) - continue; - id = itemdb_search(sd->status.cart[i].nameid); - if( !itemdb_isstackable2(id) ) - { //Equippable - WBUFW(bufe,ne*cmd+4)=i+2; - clif_item_sub(bufe, ne*cmd+6, &sd->status.cart[i], id, id->equip); - clif_addcards(WBUFP(bufe, ne*cmd+16), &sd->status.cart[i]); -#if PACKETVER >= 20071002 - WBUFL(bufe,ne*cmd+24)=sd->status.cart[i].expire_time; - WBUFW(bufe,ne*cmd+28)=0; //Unknown -#endif - ne++; - } - else - { //Stackable - WBUFW(buf,n*s+4)=i+2; - clif_item_sub(buf, n*s+6, &sd->status.cart[i], id,-1); -#if PACKETVER >= 5 - clif_addcards(WBUFP(buf,n*s+14), &sd->status.cart[i]); -#endif -#if PACKETVER >= 20080102 - WBUFL(buf,n*s+22)=sd->status.cart[i].expire_time; -#endif - n++; - } - } - if( n ) - { -#if PACKETVER < 5 - WBUFW(buf,0)=0x123; -#elif PACKETVER < 20080102 - WBUFW(buf,0)=0x1ef; -#else - WBUFW(buf,0)=0x2e9; -#endif - WBUFW(buf,2)=4+n*s; - clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); - } - if( ne ) - { -#if PACKETVER < 20071002 - WBUFW(bufe,0)=0x122; -#else - WBUFW(bufe,0)=0x2d2; -#endif - WBUFW(bufe,2)=4+ne*cmd; - clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF); - } - - if( buf ) aFree(buf); - if( bufe ) aFree(bufe); -} - - -/// Removes cart (ZC_CARTOFF). -/// 012b -/// Client behaviour: -/// Closes the cart storage and removes all it's items from memory. -/// The Num & Weight values of the cart are left untouched and the cart is NOT removed. -void clif_clearcart(int fd) -{ - WFIFOHEAD(fd, packet_len(0x12b)); - WFIFOW(fd,0) = 0x12b; - WFIFOSET(fd, packet_len(0x12b)); - -} - - -/// Guild XY locators (ZC_NOTIFY_POSITION_TO_GUILDM) [Valaris] -/// 01eb <account id>.L <x>.W <y>.W -void clif_guild_xy(struct map_session_data *sd) -{ - unsigned char buf[10]; - - nullpo_retv(sd); - - WBUFW(buf,0)=0x1eb; - WBUFL(buf,2)=sd->status.account_id; - WBUFW(buf,6)=sd->bl.x; - WBUFW(buf,8)=sd->bl.y; - clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS); -} - -/*========================================== - * Sends x/y dot to a single fd. [Skotlex] - *------------------------------------------*/ -void clif_guild_xy_single(int fd, struct map_session_data *sd) -{ - if( sd->bg_id ) - return; - - WFIFOHEAD(fd,packet_len(0x1eb)); - WFIFOW(fd,0)=0x1eb; - WFIFOL(fd,2)=sd->status.account_id; - WFIFOW(fd,6)=sd->bl.x; - WFIFOW(fd,8)=sd->bl.y; - WFIFOSET(fd,packet_len(0x1eb)); -} - -// Guild XY locators [Valaris] -void clif_guild_xy_remove(struct map_session_data *sd) -{ - unsigned char buf[10]; - - nullpo_retv(sd); - - WBUFW(buf,0)=0x1eb; - WBUFL(buf,2)=sd->status.account_id; - WBUFW(buf,6)=-1; - WBUFW(buf,8)=-1; - clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS); -} - -/*========================================== - * - *------------------------------------------*/ -static int clif_hpmeter_sub(struct block_list *bl, va_list ap) -{ - struct map_session_data *sd, *tsd; -#if PACKETVER < 20100126 - const int cmd = 0x106; -#else - const int cmd = 0x80e; -#endif - - sd = va_arg(ap, struct map_session_data *); - tsd = (TBL_PC *)bl; - - nullpo_ret(sd); - nullpo_ret(tsd); - - if( !tsd->fd || tsd == sd ) - return 0; - - if( !pc_has_permission(tsd, PC_PERM_VIEW_HPMETER) ) - return 0; - WFIFOHEAD(tsd->fd,packet_len(cmd)); - WFIFOW(tsd->fd,0) = cmd; - WFIFOL(tsd->fd,2) = sd->status.account_id; -#if PACKETVER < 20100126 - if( sd->battle_status.max_hp > INT16_MAX ) - { //To correctly display the %hp bar. [Skotlex] - WFIFOW(tsd->fd,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100); - WFIFOW(tsd->fd,8) = 100; - } else { - WFIFOW(tsd->fd,6) = sd->battle_status.hp; - WFIFOW(tsd->fd,8) = sd->battle_status.max_hp; - } -#else - WFIFOL(tsd->fd,6) = sd->battle_status.hp; - WFIFOL(tsd->fd,10) = sd->battle_status.max_hp; -#endif - WFIFOSET(tsd->fd,packet_len(cmd)); - return 0; -} - -/*========================================== - * Server tells all players that are allowed to view HP bars - * and are nearby 'sd' that 'sd' hp bar was updated. - *------------------------------------------*/ -static int clif_hpmeter(struct map_session_data *sd) -{ - nullpo_ret(sd); - map_foreachinarea(clif_hpmeter_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd); - return 0; -} - -/// Notifies client of a character parameter change. -/// 00b0 <var id>.W <value>.L (ZC_PAR_CHANGE) -/// 00b1 <var id>.W <value>.L (ZC_LONGPAR_CHANGE) -/// 00be <status id>.W <value>.B (ZC_STATUS_CHANGE) -/// 0121 <current count>.W <max count>.W <current weight>.L <max weight>.L (ZC_NOTIFY_CARTITEM_COUNTINFO) -/// 013a <atk range>.W (ZC_ATTACK_RANGE) -/// 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS) -/// TODO: Extract individual packets. -/// FIXME: Packet lengths from packet_len(cmd) -void clif_updatestatus(struct map_session_data *sd,int type) -{ - int fd,len=8; - - nullpo_retv(sd); - - fd=sd->fd; - - if ( !session_isActive(fd) ) // Invalid pointer fix, by sasuke [Kevin] - return; - - WFIFOHEAD(fd, 14); - WFIFOW(fd,0)=0xb0; - WFIFOW(fd,2)=type; - switch(type){ - // 00b0 - case SP_WEIGHT: - pc_updateweightstatus(sd); - WFIFOHEAD(fd,14); - WFIFOW(fd,0)=0xb0; //Need to re-set as pc_updateweightstatus can alter the buffer. [Skotlex] - WFIFOW(fd,2)=type; - WFIFOL(fd,4)=sd->weight; - break; - case SP_MAXWEIGHT: - WFIFOL(fd,4)=sd->max_weight; - break; - case SP_SPEED: - WFIFOL(fd,4)=sd->battle_status.speed; - break; - case SP_BASELEVEL: - WFIFOL(fd,4)=sd->status.base_level; - break; - case SP_JOBLEVEL: - WFIFOL(fd,4)=sd->status.job_level; - break; - case SP_KARMA: // Adding this back, I wonder if the client intercepts this - [Lance] - WFIFOL(fd,4)=sd->status.karma; - break; - case SP_MANNER: - WFIFOL(fd,4)=sd->status.manner; - break; - case SP_STATUSPOINT: - WFIFOL(fd,4)=sd->status.status_point; - break; - case SP_SKILLPOINT: - WFIFOL(fd,4)=sd->status.skill_point; - break; - case SP_HIT: - WFIFOL(fd,4)=sd->battle_status.hit; - break; - case SP_FLEE1: - WFIFOL(fd,4)=sd->battle_status.flee; - break; - case SP_FLEE2: - WFIFOL(fd,4)=sd->battle_status.flee2/10; - break; - case SP_MAXHP: - WFIFOL(fd,4)=sd->battle_status.max_hp; - break; - case SP_MAXSP: - WFIFOL(fd,4)=sd->battle_status.max_sp; - break; - case SP_HP: - WFIFOL(fd,4)=sd->battle_status.hp; - // TODO: Won't these overwrite the current packet? - clif_hpmeter(sd); - if( !battle_config.party_hp_mode && sd->status.party_id ) - clif_party_hp(sd); - if( sd->bg_id ) - clif_bg_hp(sd); - break; - case SP_SP: - WFIFOL(fd,4)=sd->battle_status.sp; - break; - case SP_ASPD: - WFIFOL(fd,4)=sd->battle_status.amotion; - break; - case SP_ATK1: - WFIFOL(fd,4)=pc_leftside_atk(sd); - break; - case SP_DEF1: - WFIFOL(fd,4)=pc_leftside_def(sd); - break; - case SP_MDEF1: - WFIFOL(fd,4)=pc_leftside_mdef(sd); - break; - case SP_ATK2: - WFIFOL(fd,4)=pc_rightside_atk(sd); - break; - case SP_DEF2: - WFIFOL(fd,4)=pc_rightside_def(sd); - break; - case SP_MDEF2: { - //negative check (in case you have something like Berserk active) - int mdef2 = pc_rightside_mdef(sd); - - WFIFOL(fd,4)= -#ifndef RENEWAL - ( mdef2 < 0 ) ? 0 : -#endif - mdef2; - - } - break; - case SP_CRITICAL: - WFIFOL(fd,4)=sd->battle_status.cri/10; - break; - case SP_MATK1: - WFIFOL(fd,4)=pc_rightside_matk(sd); - break; - case SP_MATK2: - WFIFOL(fd,4)=pc_leftside_matk(sd); - break; - - - case SP_ZENY: - WFIFOW(fd,0)=0xb1; - WFIFOL(fd,4)=sd->status.zeny; - break; - case SP_BASEEXP: - WFIFOW(fd,0)=0xb1; - WFIFOL(fd,4)=sd->status.base_exp; - break; - case SP_JOBEXP: - WFIFOW(fd,0)=0xb1; - WFIFOL(fd,4)=sd->status.job_exp; - break; - case SP_NEXTBASEEXP: - WFIFOW(fd,0)=0xb1; - WFIFOL(fd,4)=pc_nextbaseexp(sd); - break; - case SP_NEXTJOBEXP: - WFIFOW(fd,0)=0xb1; - WFIFOL(fd,4)=pc_nextjobexp(sd); - break; - - /** - * 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: - case SP_UINT: - case SP_UDEX: - case SP_ULUK: - WFIFOW(fd,0)=0xbe; - WFIFOB(fd,4)=pc_need_status_point(sd,type-SP_USTR+SP_STR,1); - len=5; - break; - - /** - * 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; - - case SP_STR: - WFIFOW(fd,0)=0x141; - WFIFOL(fd,2)=type; - WFIFOL(fd,6)=sd->status.str; - WFIFOL(fd,10)=sd->battle_status.str - sd->status.str; - len=14; - break; - case SP_AGI: - WFIFOW(fd,0)=0x141; - WFIFOL(fd,2)=type; - WFIFOL(fd,6)=sd->status.agi; - WFIFOL(fd,10)=sd->battle_status.agi - sd->status.agi; - len=14; - break; - case SP_VIT: - WFIFOW(fd,0)=0x141; - WFIFOL(fd,2)=type; - WFIFOL(fd,6)=sd->status.vit; - WFIFOL(fd,10)=sd->battle_status.vit - sd->status.vit; - len=14; - break; - case SP_INT: - WFIFOW(fd,0)=0x141; - WFIFOL(fd,2)=type; - WFIFOL(fd,6)=sd->status.int_; - WFIFOL(fd,10)=sd->battle_status.int_ - sd->status.int_; - len=14; - break; - case SP_DEX: - WFIFOW(fd,0)=0x141; - WFIFOL(fd,2)=type; - WFIFOL(fd,6)=sd->status.dex; - WFIFOL(fd,10)=sd->battle_status.dex - sd->status.dex; - len=14; - break; - case SP_LUK: - WFIFOW(fd,0)=0x141; - WFIFOL(fd,2)=type; - WFIFOL(fd,6)=sd->status.luk; - WFIFOL(fd,10)=sd->battle_status.luk - sd->status.luk; - len=14; - break; - - case SP_CARTINFO: - WFIFOW(fd,0)=0x121; - WFIFOW(fd,2)=sd->cart_num; - WFIFOW(fd,4)=MAX_CART; - WFIFOL(fd,6)=sd->cart_weight; - WFIFOL(fd,10)=sd->cart_weight_max; - len=14; - break; - - default: - ShowError("clif_updatestatus : unrecognized type %d\n",type); - return; - } - WFIFOSET(fd,len); -} - - -/// Notifies client of a parameter change of an another player (ZC_PAR_CHANGE_USER). -/// 01ab <account id>.L <var id>.W <value>.L -void clif_changestatus(struct map_session_data* sd,int type,int val) -{ - unsigned char buf[12]; - - nullpo_retv(sd); - - WBUFW(buf,0)=0x1ab; - WBUFL(buf,2)=sd->bl.id; - WBUFW(buf,6)=type; - - switch(type) - { - case SP_MANNER: - WBUFL(buf,8)=val; - break; - default: - ShowError("clif_changestatus : unrecognized type %d.\n",type); - return; - } - - clif_send(buf,packet_len(0x1ab),&sd->bl,AREA_WOS); -} - - -/// Updates sprite/style properties of an object. -/// 00c3 <id>.L <type>.B <value>.B (ZC_SPRITE_CHANGE) -/// 01d7 <id>.L <type>.B <value>.L (ZC_SPRITE_CHANGE2) -void clif_changelook(struct block_list *bl,int type,int val) -{ - unsigned char buf[16]; - struct map_session_data* sd = NULL; - struct status_change* sc; - struct view_data* vd; - enum send_target target = AREA; - nullpo_retv(bl); - - sd = BL_CAST(BL_PC, bl); - sc = status_get_sc(bl); - vd = status_get_viewdata(bl); - //nullpo_ret(vd); - if( vd ) //temp hack to let Warp Portal change appearance - switch(type) - { - case LOOK_WEAPON: - if (sd) - { - clif_get_weapon_view(sd, &vd->weapon, &vd->shield); - val = vd->weapon; - } - else vd->weapon = val; - break; - case LOOK_SHIELD: - if (sd) - { - clif_get_weapon_view(sd, &vd->weapon, &vd->shield); - val = vd->shield; - } - else vd->shield = val; - break; - case LOOK_BASE: - vd->class_ = val; - if (vd->class_ == JOB_WEDDING || vd->class_ == JOB_XMAS || vd->class_ == JOB_SUMMER) - vd->weapon = vd->shield = 0; - if (vd->cloth_color && ( - (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) || - (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) || - (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette) - )) - clif_changelook(bl,LOOK_CLOTHES_COLOR,0); - break; - case LOOK_HAIR: - vd->hair_style = val; - break; - case LOOK_HEAD_BOTTOM: - vd->head_bottom = val; - break; - case LOOK_HEAD_TOP: - vd->head_top = val; - break; - case LOOK_HEAD_MID: - vd->head_mid = val; - break; - case LOOK_HAIR_COLOR: - vd->hair_color = val; - break; - case LOOK_CLOTHES_COLOR: - if (val && ( - (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) || - (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) || - (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette) - )) - val = 0; - vd->cloth_color = val; - break; - case LOOK_SHOES: -#if PACKETVER > 3 - if (sd) { - int n; - if((n = sd->equip_index[2]) >= 0 && sd->inventory_data[n]) { - if(sd->inventory_data[n]->view_id > 0) - val = sd->inventory_data[n]->view_id; - else - val = sd->status.inventory[n].nameid; - } else - val = 0; - } -#endif - //Shoes? No packet uses this.... - break; - case LOOK_BODY: - case LOOK_FLOOR: - // unknown purpose - break; - case LOOK_ROBE: -#if PACKETVER < 20110111 - return; -#else - vd->robe = val; -#endif - break; - } - - // prevent leaking the presence of GM-hidden objects - if( sc && sc->option&OPTION_INVISIBLE ) - target = SELF; - -#if PACKETVER < 4 - WBUFW(buf,0)=0xc3; - WBUFL(buf,2)=bl->id; - WBUFB(buf,6)=type; - WBUFB(buf,7)=val; - clif_send(buf,packet_len(0xc3),bl,target); -#else - WBUFW(buf,0)=0x1d7; - WBUFL(buf,2)=bl->id; - if(type == LOOK_WEAPON || type == LOOK_SHIELD) { - WBUFB(buf,6)=LOOK_WEAPON; - WBUFW(buf,7)=vd->weapon; - WBUFW(buf,9)=vd->shield; - } else { - WBUFB(buf,6)=type; - WBUFL(buf,7)=val; - } - clif_send(buf,packet_len(0x1d7),bl,target); -#endif -} - -//Sends a change-base-look packet required for traps as they are triggered. -void clif_changetraplook(struct block_list *bl,int val) -{ - unsigned char buf[32]; -#if PACKETVER < 4 - WBUFW(buf,0)=0xc3; - WBUFL(buf,2)=bl->id; - WBUFB(buf,6)=LOOK_BASE; - WBUFB(buf,7)=val; - clif_send(buf,packet_len(0xc3),bl,AREA); -#else - WBUFW(buf,0)=0x1d7; - WBUFL(buf,2)=bl->id; - WBUFB(buf,6)=LOOK_BASE; - WBUFW(buf,7)=val; - WBUFW(buf,9)=0; - clif_send(buf,packet_len(0x1d7),bl,AREA); -#endif -} - -//For the stupid cloth-dye bug. Resends the given view data to the area specified by bl. -void clif_refreshlook(struct block_list *bl,int id,int type,int val,enum send_target target) -{ - unsigned char buf[32]; -#if PACKETVER < 4 - WBUFW(buf,0)=0xc3; - WBUFL(buf,2)=id; - WBUFB(buf,6)=type; - WBUFB(buf,7)=val; - clif_send(buf,packet_len(0xc3),bl,target); -#else - WBUFW(buf,0)=0x1d7; - WBUFL(buf,2)=id; - WBUFB(buf,6)=type; - WBUFW(buf,7)=val; - WBUFW(buf,9)=0; - clif_send(buf,packet_len(0x1d7),bl,target); -#endif -} - - -/// Character status (ZC_STATUS). -/// 00bd <stpoint>.W <str>.B <need str>.B <agi>.B <need agi>.B <vit>.B <need vit>.B -/// <int>.B <need int>.B <dex>.B <need dex>.B <luk>.B <need luk>.B <atk>.W <atk2>.W -/// <matk min>.W <matk max>.W <def>.W <def2>.W <mdef>.W <mdef2>.W <hit>.W -/// <flee>.W <flee2>.W <crit>.W <aspd>.W <aspd2>.W -void clif_initialstatus(struct map_session_data *sd) -{ - int fd, mdef2; - unsigned char *buf; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xbd)); - buf=WFIFOP(fd,0); - - WBUFW(buf,0)=0xbd; - WBUFW(buf,2)=min(sd->status.status_point, INT16_MAX); - WBUFB(buf,4)=min(sd->status.str, UINT8_MAX); - WBUFB(buf,5)=pc_need_status_point(sd,SP_STR,1); - WBUFB(buf,6)=min(sd->status.agi, UINT8_MAX); - WBUFB(buf,7)=pc_need_status_point(sd,SP_AGI,1); - WBUFB(buf,8)=min(sd->status.vit, UINT8_MAX); - WBUFB(buf,9)=pc_need_status_point(sd,SP_VIT,1); - WBUFB(buf,10)=min(sd->status.int_, UINT8_MAX); - WBUFB(buf,11)=pc_need_status_point(sd,SP_INT,1); - WBUFB(buf,12)=min(sd->status.dex, UINT8_MAX); - WBUFB(buf,13)=pc_need_status_point(sd,SP_DEX,1); - WBUFB(buf,14)=min(sd->status.luk, UINT8_MAX); - WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK,1); - - WBUFW(buf,16) = pc_leftside_atk(sd); - WBUFW(buf,18) = pc_rightside_atk(sd); - WBUFW(buf,20) = pc_rightside_matk(sd); - WBUFW(buf,22) = pc_leftside_matk(sd); - WBUFW(buf,24) = pc_leftside_def(sd); - WBUFW(buf,26) = pc_rightside_def(sd); - WBUFW(buf,28) = pc_leftside_mdef(sd); - mdef2 = pc_rightside_mdef(sd); - WBUFW(buf,30) = -#ifndef RENEWAL - ( mdef2 < 0 ) ? 0 : //Negative check for Frenzy'ed characters. -#endif - mdef2; - WBUFW(buf,32) = sd->battle_status.hit; - WBUFW(buf,34) = sd->battle_status.flee; - WBUFW(buf,36) = sd->battle_status.flee2/10; - WBUFW(buf,38) = sd->battle_status.cri/10; - WBUFW(buf,40) = sd->battle_status.amotion; // aspd - WBUFW(buf,42) = 0; // always 0 (plusASPD) - - WFIFOSET(fd,packet_len(0xbd)); - - clif_updatestatus(sd,SP_STR); - clif_updatestatus(sd,SP_AGI); - clif_updatestatus(sd,SP_VIT); - clif_updatestatus(sd,SP_INT); - clif_updatestatus(sd,SP_DEX); - clif_updatestatus(sd,SP_LUK); - - clif_updatestatus(sd,SP_ATTACKRANGE); - clif_updatestatus(sd,SP_ASPD); -} - - -/// Marks an ammunition item in inventory as equipped (ZC_EQUIP_ARROW). -/// 013c <index>.W -void clif_arrowequip(struct map_session_data *sd,int val) -{ - int fd; - - nullpo_retv(sd); - - pc_stop_attack(sd); // [Valaris] - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0x013c)); - WFIFOW(fd,0)=0x013c; - WFIFOW(fd,2)=val+2; //Item ID of the arrow - WFIFOSET(fd,packet_len(0x013c)); -} - - -/// Ammunition action message (ZC_ACTION_FAILURE). -/// 013b <type>.W -/// type: -/// 0 = MsgStringTable[242]="Please equip the proper ammunition first." -/// 1 = MsgStringTable[243]="You can't Attack or use Skills because your Weight Limit has been exceeded." -/// 2 = MsgStringTable[244]="You can't use Skills because Weight Limit has been exceeded." -/// 3 = assassin, baby_assassin, assassin_cross => MsgStringTable[1040]="You have equipped throwing daggers." -/// gunslinger => MsgStringTable[1175]="Bullets have been equipped." -/// NOT ninja => MsgStringTable[245]="Ammunition has been equipped." -void clif_arrow_fail(struct map_session_data *sd,int type) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len(0x013b)); - WFIFOW(fd,0)=0x013b; - WFIFOW(fd,2)=type; - WFIFOSET(fd,packet_len(0x013b)); -} - - -/// Presents a list of items, that can be processed by Arrow Crafting (ZC_MAKINGARROW_LIST). -/// 01ad <packet len>.W { <name id>.W }* -void clif_arrow_create_list(struct map_session_data *sd) -{ - int i, c, j; - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd, MAX_SKILL_ARROW_DB*2+4); - WFIFOW(fd,0) = 0x1ad; - - for (i = 0, c = 0; i < MAX_SKILL_ARROW_DB; i++) { - if (skill_arrow_db[i].nameid > 0 && - (j = pc_search_inventory(sd, skill_arrow_db[i].nameid)) >= 0 && - !sd->status.inventory[j].equip && sd->status.inventory[j].identify) - { - if ((j = itemdb_viewid(skill_arrow_db[i].nameid)) > 0) - WFIFOW(fd,c*2+4) = j; - else - WFIFOW(fd,c*2+4) = skill_arrow_db[i].nameid; - c++; - } - } - WFIFOW(fd,2) = c*2+4; - WFIFOSET(fd, WFIFOW(fd,2)); - if (c > 0) { - sd->menuskill_id = AC_MAKINGARROW; - sd->menuskill_val = c; - } -} - - -/// Notifies the client, about the result of an status change request (ZC_STATUS_CHANGE_ACK). -/// 00bc <status id>.W <result>.B <value>.B -/// status id: -/// SP_STR ~ SP_LUK -/// result: -/// 0 = failure -/// 1 = success -void clif_statusupack(struct map_session_data *sd,int type,int ok,int val) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xbc)); - WFIFOW(fd,0)=0xbc; - WFIFOW(fd,2)=type; - WFIFOB(fd,4)=ok; - WFIFOB(fd,5)=cap_value(val,0,UINT8_MAX); - WFIFOSET(fd,packet_len(0xbc)); -} - - -/// Notifies the client about the result of a request to equip an item (ZC_REQ_WEAR_EQUIP_ACK). -/// 00aa <index>.W <equip location>.W <result>.B -/// 00aa <index>.W <equip location>.W <view id>.W <result>.B (PACKETVER >= 20100629) -/// result: -/// 0 = failure -/// 1 = success -/// 2 = failure due to low level -void clif_equipitemack(struct map_session_data *sd,int n,int pos,int ok) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xaa)); - WFIFOW(fd,0)=0xaa; - WFIFOW(fd,2)=n+2; - WFIFOW(fd,4)=pos; -#if PACKETVER < 20100629 - WFIFOB(fd,6)=ok; -#else - if (ok && sd->inventory_data[n]->equip&EQP_VISIBLE) - WFIFOW(fd,6)=sd->inventory_data[n]->look; - else - WFIFOW(fd,6)=0; - WFIFOB(fd,8)=ok; -#endif - WFIFOSET(fd,packet_len(0xaa)); -} - - -/// Notifies the client about the result of a request to take off an item (ZC_REQ_TAKEOFF_EQUIP_ACK). -/// 00ac <index>.W <equip location>.W <result>.B -/// result: -/// 0 = failure -/// 1 = success -void clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xac)); - WFIFOW(fd,0)=0xac; - WFIFOW(fd,2)=n+2; - WFIFOW(fd,4)=pos; - WFIFOB(fd,6)=ok; - WFIFOSET(fd,packet_len(0xac)); -} - - -/// Notifies clients in the area about an special/visual effect (ZC_NOTIFY_EFFECT). -/// 019b <id>.L <effect id>.L -/// effect id: -/// 0 = base level up -/// 1 = job level up -/// 2 = refine failure -/// 3 = refine success -/// 4 = game over -/// 5 = pharmacy success -/// 6 = pharmacy failure -/// 7 = base level up (super novice) -/// 8 = job level up (super novice) -/// 9 = base level up (taekwon) -void clif_misceffect(struct block_list* bl,int type) -{ - unsigned char buf[32]; - - nullpo_retv(bl); - - WBUFW(buf,0) = 0x19b; - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = type; - - clif_send(buf,packet_len(0x19b),bl,AREA); -} - - -/// Notifies clients in the area of a state change. -/// 0119 <id>.L <body state>.W <health state>.W <effect state>.W <pk mode>.B (ZC_STATE_CHANGE) -/// 0229 <id>.L <body state>.W <health state>.W <effect state>.L <pk mode>.B (ZC_STATE_CHANGE3) -void clif_changeoption(struct block_list* bl) -{ - unsigned char buf[32]; - struct status_change *sc; - struct map_session_data* sd; - - nullpo_retv(bl); - sc = status_get_sc(bl); - if (!sc) return; //How can an option change if there's no sc? - sd = BL_CAST(BL_PC, bl); - -#if PACKETVER >= 7 - WBUFW(buf,0) = 0x229; - WBUFL(buf,2) = bl->id; - WBUFW(buf,6) = sc->opt1; - WBUFW(buf,8) = sc->opt2; - WBUFL(buf,10) = sc->option; - WBUFB(buf,14) = (sd)? sd->status.karma : 0; - if(disguised(bl)) { - clif_send(buf,packet_len(0x229),bl,AREA_WOS); - WBUFL(buf,2) = -bl->id; - clif_send(buf,packet_len(0x229),bl,SELF); - WBUFL(buf,2) = bl->id; - WBUFL(buf,10) = OPTION_INVISIBLE; - clif_send(buf,packet_len(0x229),bl,SELF); - } else - clif_send(buf,packet_len(0x229),bl,AREA); -#else - WBUFW(buf,0) = 0x119; - WBUFL(buf,2) = bl->id; - WBUFW(buf,6) = sc->opt1; - WBUFW(buf,8) = sc->opt2; - WBUFW(buf,10) = sc->option; - WBUFB(buf,12) = (sd)? sd->status.karma : 0; - if(disguised(bl)) { - clif_send(buf,packet_len(0x119),bl,AREA_WOS); - WBUFL(buf,2) = -bl->id; - clif_send(buf,packet_len(0x119),bl,SELF); - WBUFL(buf,2) = bl->id; - WBUFW(buf,10) = OPTION_INVISIBLE; - clif_send(buf,packet_len(0x119),bl,SELF); - } else - clif_send(buf,packet_len(0x119),bl,AREA); -#endif -} - - -/// Displays status change effects on NPCs/monsters (ZC_NPC_SHOWEFST_UPDATE). -/// 028a <id>.L <effect state>.L <level>.L <showEFST>.L -void clif_changeoption2(struct block_list* bl) -{ - unsigned char buf[20]; - struct status_change *sc; - - sc = status_get_sc(bl); - if (!sc) return; //How can an option change if there's no sc? - - WBUFW(buf,0) = 0x28a; - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = sc->option; - WBUFL(buf,10) = clif_setlevel(bl); - WBUFL(buf,14) = sc->opt3; - if(disguised(bl)) { - clif_send(buf,packet_len(0x28a),bl,AREA_WOS); - WBUFL(buf,2) = -bl->id; - clif_send(buf,packet_len(0x28a),bl,SELF); - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = OPTION_INVISIBLE; - clif_send(buf,packet_len(0x28a),bl,SELF); - } else - clif_send(buf,packet_len(0x28a),bl,AREA); -} - - -/// Notifies the client about the result of an item use request. -/// 00a8 <index>.W <amount>.W <result>.B (ZC_USE_ITEM_ACK) -/// 01c8 <index>.W <name id>.W <id>.L <amount>.W <result>.B (ZC_USE_ITEM_ACK2) -void clif_useitemack(struct map_session_data *sd,int index,int amount,bool ok) -{ - nullpo_retv(sd); - - if(!ok) { - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xa8)); - WFIFOW(fd,0)=0xa8; - WFIFOW(fd,2)=index+2; - WFIFOW(fd,4)=amount; - WFIFOB(fd,6)=ok; - WFIFOSET(fd,packet_len(0xa8)); - } - else { -#if PACKETVER < 3 - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xa8)); - WFIFOW(fd,0)=0xa8; - WFIFOW(fd,2)=index+2; - WFIFOW(fd,4)=amount; - WFIFOB(fd,6)=ok; - WFIFOSET(fd,packet_len(0xa8)); -#else - unsigned char buf[32]; - - WBUFW(buf,0)=0x1c8; - WBUFW(buf,2)=index+2; - if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0) - WBUFW(buf,4)=sd->inventory_data[index]->view_id; - else - WBUFW(buf,4)=sd->status.inventory[index].nameid; - WBUFL(buf,6)=sd->bl.id; - WBUFW(buf,10)=amount; - WBUFB(buf,12)=ok; - clif_send(buf,packet_len(0x1c8),&sd->bl,AREA); -#endif - } -} - - -/// Inform client whether chatroom creation was successful or not (ZC_ACK_CREATE_CHATROOM). -/// 00d6 <flag>.B -/// flag: -/// 0 = Room has been successfully created (opens chat room) -/// 1 = Room limit exceeded -/// 2 = Same room already exists -void clif_createchat(struct map_session_data* sd, int flag) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0xd6)); - WFIFOW(fd,0) = 0xd6; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,packet_len(0xd6)); -} - - -/// Display a chat above the owner (ZC_ROOM_NEWENTRY). -/// 00d7 <packet len>.W <owner id>.L <char id>.L <limit>.W <users>.W <type>.B <title>.?B -/// type: -/// 0 = private (password protected) -/// 1 = public -/// 2 = arena (npc waiting room) -/// 3 = PK zone (non-clickable) -void clif_dispchat(struct chat_data* cd, int fd) -{ - unsigned char buf[128]; - uint8 type; - - if( cd == NULL || cd->owner == NULL ) - return; - - type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0 - : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3 - : 1; - - WBUFW(buf, 0) = 0xd7; - WBUFW(buf, 2) = 17 + strlen(cd->title); - WBUFL(buf, 4) = cd->owner->id; - WBUFL(buf, 8) = cd->bl.id; - WBUFW(buf,12) = cd->limit; - WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users; - WBUFB(buf,16) = type; - memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated - - if( fd ) { - WFIFOHEAD(fd,WBUFW(buf,2)); - memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2)); - WFIFOSET(fd,WBUFW(buf,2)); - } else { - clif_send(buf,WBUFW(buf,2),cd->owner,AREA_WOSC); - } -} - - -/// Chatroom properties adjustment (ZC_CHANGE_CHATROOM). -/// 00df <packet len>.W <owner id>.L <chat id>.L <limit>.W <users>.W <type>.B <title>.?B -/// type: -/// 0 = private (password protected) -/// 1 = public -/// 2 = arena (npc waiting room) -/// 3 = PK zone (non-clickable) -void clif_changechatstatus(struct chat_data* cd) -{ - unsigned char buf[128]; - uint8 type; - - if( cd == NULL || cd->usersd[0] == NULL ) - return; - - type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0 - : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3 - : 1; - - WBUFW(buf, 0) = 0xdf; - WBUFW(buf, 2) = 17 + strlen(cd->title); - WBUFL(buf, 4) = cd->owner->id; - WBUFL(buf, 8) = cd->bl.id; - WBUFW(buf,12) = cd->limit; - WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users; - WBUFB(buf,16) = type; - memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated - - clif_send(buf,WBUFW(buf,2),cd->owner,CHAT); -} - - -/// Removes the chatroom (ZC_DESTROY_ROOM). -/// 00d8 <chat id>.L -void clif_clearchat(struct chat_data *cd,int fd) -{ - unsigned char buf[32]; - - nullpo_retv(cd); - - WBUFW(buf,0) = 0xd8; - WBUFL(buf,2) = cd->bl.id; - if( fd ) { - WFIFOHEAD(fd,packet_len(0xd8)); - memcpy(WFIFOP(fd,0),buf,packet_len(0xd8)); - WFIFOSET(fd,packet_len(0xd8)); - } else { - clif_send(buf,packet_len(0xd8),cd->owner,AREA_WOSC); - } -} - - -/// Displays messages regarding join chat failures (ZC_REFUSE_ENTER_ROOM). -/// 00da <result>.B -/// result: -/// 0 = room full -/// 1 = wrong password -/// 2 = kicked -/// 3 = success (no message) -/// 4 = no enough zeny -/// 5 = too low level -/// 6 = too high level -/// 7 = unsuitable job class -void clif_joinchatfail(struct map_session_data *sd,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0xda)); - WFIFOW(fd,0) = 0xda; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,packet_len(0xda)); -} - - -/// Notifies the client about entering a chatroom (ZC_ENTER_ROOM). -/// 00db <packet len>.W <chat id>.L { <role>.L <name>.24B }* -/// role: -/// 0 = owner (menu) -/// 1 = normal -void clif_joinchatok(struct map_session_data *sd,struct chat_data* cd) -{ - int fd; - int i,t; - - nullpo_retv(sd); - nullpo_retv(cd); - - fd = sd->fd; - if (!session_isActive(fd)) - return; - t = (int)(cd->owner->type == BL_NPC); - WFIFOHEAD(fd, 8 + (28*(cd->users+t))); - WFIFOW(fd, 0) = 0xdb; - WFIFOW(fd, 2) = 8 + (28*(cd->users+t)); - WFIFOL(fd, 4) = cd->bl.id; - - if(cd->owner->type == BL_NPC){ - WFIFOL(fd, 30) = 1; - WFIFOL(fd, 8) = 0; - memcpy(WFIFOP(fd, 12), ((struct npc_data *)cd->owner)->name, NAME_LENGTH); - for (i = 0; i < cd->users; i++) { - WFIFOL(fd, 8+(i+1)*28) = 1; - memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH); - } - } else - for (i = 0; i < cd->users; i++) { - WFIFOL(fd, 8+i*28) = (i != 0 || cd->owner->type == BL_NPC); - memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH); - } - WFIFOSET(fd, WFIFOW(fd, 2)); -} - - -/// Notifies clients in a chat about a new member (ZC_MEMBER_NEWENTRY). -/// 00dc <users>.W <name>.24B -void clif_addchat(struct chat_data* cd,struct map_session_data *sd) -{ - unsigned char buf[32]; - - nullpo_retv(sd); - nullpo_retv(cd); - - WBUFW(buf, 0) = 0xdc; - WBUFW(buf, 2) = cd->users; - memcpy(WBUFP(buf, 4),sd->status.name,NAME_LENGTH); - clif_send(buf,packet_len(0xdc),&sd->bl,CHAT_WOS); -} - - -/// Announce the new owner (ZC_ROLE_CHANGE). -/// 00e1 <role>.L <nick>.24B -/// role: -/// 0 = owner (menu) -/// 1 = normal -void clif_changechatowner(struct chat_data* cd, struct map_session_data* sd) -{ - unsigned char buf[64]; - - nullpo_retv(sd); - nullpo_retv(cd); - - WBUFW(buf, 0) = 0xe1; - WBUFL(buf, 2) = 1; - memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,NAME_LENGTH); - - WBUFW(buf,30) = 0xe1; - WBUFL(buf,32) = 0; - memcpy(WBUFP(buf,36),sd->status.name,NAME_LENGTH); - - clif_send(buf,packet_len(0xe1)*2,&sd->bl,CHAT); -} - - -/// Notify about user leaving the chatroom (ZC_MEMBER_EXIT). -/// 00dd <users>.W <nick>.24B <flag>.B -/// flag: -/// 0 = left -/// 1 = kicked -void clif_leavechat(struct chat_data* cd, struct map_session_data* sd, bool flag) -{ - unsigned char buf[32]; - - nullpo_retv(sd); - nullpo_retv(cd); - - WBUFW(buf, 0) = 0xdd; - WBUFW(buf, 2) = cd->users-1; - memcpy(WBUFP(buf,4),sd->status.name,NAME_LENGTH); - WBUFB(buf,28) = flag; - - clif_send(buf,packet_len(0xdd),&sd->bl,CHAT); -} - - -/// Opens a trade request window from char 'name'. -/// 00e5 <nick>.24B (ZC_REQ_EXCHANGE_ITEM) -/// 01f4 <nick>.24B <charid>.L <baselvl>.W (ZC_REQ_EXCHANGE_ITEM2) -void clif_traderequest(struct map_session_data* sd, const char* name) -{ - int fd = sd->fd; - -#if PACKETVER < 6 - WFIFOHEAD(fd,packet_len(0xe5)); - WFIFOW(fd,0) = 0xe5; - safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH); - WFIFOSET(fd,packet_len(0xe5)); -#else - struct map_session_data* tsd = map_id2sd(sd->trade_partner); - if( !tsd ) return; - - WFIFOHEAD(fd,packet_len(0x1f4)); - WFIFOW(fd,0) = 0x1f4; - safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH); - WFIFOL(fd,26) = tsd->status.char_id; - WFIFOW(fd,30) = tsd->status.base_level; - WFIFOSET(fd,packet_len(0x1f4)); -#endif -} - - -/// Reply to a trade-request. -/// 00e7 <result>.B (ZC_ACK_EXCHANGE_ITEM) -/// 01f5 <result>.B <charid>.L <baselvl>.W (ZC_ACK_EXCHANGE_ITEM2) -/// result: -/// 0 = Char is too far -/// 1 = Character does not exist -/// 2 = Trade failed -/// 3 = Accept -/// 4 = Cancel -/// 5 = Busy -void clif_tradestart(struct map_session_data* sd, uint8 type) -{ - int fd = sd->fd; - struct map_session_data* tsd = map_id2sd(sd->trade_partner); - if( PACKETVER < 6 || !tsd ) { - WFIFOHEAD(fd,packet_len(0xe7)); - WFIFOW(fd,0) = 0xe7; - WFIFOB(fd,2) = type; - WFIFOSET(fd,packet_len(0xe7)); - } else { - WFIFOHEAD(fd,packet_len(0x1f5)); - WFIFOW(fd,0) = 0x1f5; - WFIFOB(fd,2) = type; - WFIFOL(fd,3) = tsd->status.char_id; - WFIFOW(fd,7) = tsd->status.base_level; - WFIFOSET(fd,packet_len(0x1f5)); - } -} - - -/// Notifies the client about an item from other player in current trade. -/// 00e9 <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM) -/// 080f <nameid>.W <item type>.B <amount>.L <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM2) -void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount) -{ - int fd; - unsigned char *buf; -#if PACKETVER < 20100223 - const int cmd = 0xe9; -#else - const int cmd = 0x80f; -#endif - nullpo_retv(sd); - nullpo_retv(tsd); - - fd = tsd->fd; - buf = WFIFOP(fd,0); - WFIFOHEAD(fd,packet_len(cmd)); - WBUFW(buf,0) = cmd; - if( index == 0 ) - { -#if PACKETVER < 20100223 - WBUFL(buf,2) = amount; //amount - WBUFW(buf,6) = 0; // type id -#else - WBUFW(buf,2) = 0; // type id - WBUFB(buf,4) = 0; // item type - WBUFL(buf,5) = amount; // amount - buf = WBUFP(buf,1); //Advance 1B -#endif - WBUFB(buf,8) = 0; //identify flag - WBUFB(buf,9) = 0; // attribute - WBUFB(buf,10)= 0; //refine - WBUFW(buf,11)= 0; //card (4w) - WBUFW(buf,13)= 0; //card (4w) - WBUFW(buf,15)= 0; //card (4w) - WBUFW(buf,17)= 0; //card (4w) - } - else - { - index -= 2; //index fix -#if PACKETVER < 20100223 - WBUFL(buf,2) = amount; //amount - if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0) - WBUFW(buf,6) = sd->inventory_data[index]->view_id; - else - WBUFW(buf,6) = sd->status.inventory[index].nameid; // type id -#else - if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0) - WBUFW(buf,2) = sd->inventory_data[index]->view_id; - else - WBUFW(buf,2) = sd->status.inventory[index].nameid; // type id - WBUFB(buf,4) = sd->inventory_data[index]->type; // item type - WBUFL(buf,5) = amount; // amount - buf = WBUFP(buf,1); //Advance 1B -#endif - WBUFB(buf,8) = sd->status.inventory[index].identify; //identify flag - WBUFB(buf,9) = sd->status.inventory[index].attribute; // attribute - WBUFB(buf,10)= sd->status.inventory[index].refine; //refine - clif_addcards(WBUFP(buf, 11), &sd->status.inventory[index]); - } - WFIFOSET(fd,packet_len(cmd)); -} - - -/// Notifies the client about the result of request to add an item to the current trade (ZC_ACK_ADD_EXCHANGE_ITEM). -/// 00ea <index>.W <result>.B -/// result: -/// 0 = success -/// 1 = overweight -/// 2 = trade canceled -void clif_tradeitemok(struct map_session_data* sd, int index, int fail) -{ - int fd; - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0xea)); - WFIFOW(fd,0) = 0xea; - WFIFOW(fd,2) = index; - WFIFOB(fd,4) = fail; - WFIFOSET(fd,packet_len(0xea)); -} - - -/// Notifies the client about finishing one side of the current trade (ZC_CONCLUDE_EXCHANGE_ITEM). -/// 00ec <who>.B -/// who: -/// 0 = self -/// 1 = other player -void clif_tradedeal_lock(struct map_session_data* sd, int fail) -{ - int fd; - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0xec)); - WFIFOW(fd,0) = 0xec; - WFIFOB(fd,2) = fail; - WFIFOSET(fd,packet_len(0xec)); -} - - -/// Notifies the client about the trade being canceled (ZC_CANCEL_EXCHANGE_ITEM). -/// 00ee -void clif_tradecancelled(struct map_session_data* sd) -{ - int fd; - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0xee)); - WFIFOW(fd,0) = 0xee; - WFIFOSET(fd,packet_len(0xee)); -} - - -/// Result of a trade (ZC_EXEC_EXCHANGE_ITEM). -/// 00f0 <result>.B -/// result: -/// 0 = success -/// 1 = failure -void clif_tradecompleted(struct map_session_data* sd, int fail) -{ - int fd; - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0xf0)); - WFIFOW(fd,0) = 0xf0; - WFIFOB(fd,2) = fail; - WFIFOSET(fd,packet_len(0xf0)); -} - - -/// Resets the trade window on the send side (ZC_EXCHANGEITEM_UNDO). -/// 00f1 -/// NOTE: Unknown purpose. Items are not removed until the window is -/// refreshed (ex. by putting another item in there). -void clif_tradeundo(struct map_session_data* sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0xf1)); - WFIFOW(fd,0) = 0xf1; - WFIFOSET(fd,packet_len(0xf1)); -} - - -/// Updates storage total amount (ZC_NOTIFY_STOREITEM_COUNTINFO). -/// 00f2 <current count>.W <max count>.W -void clif_updatestorageamount(struct map_session_data* sd, int amount, int max_amount) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xf2)); - WFIFOW(fd,0) = 0xf2; - WFIFOW(fd,2) = amount; - WFIFOW(fd,4) = max_amount; - WFIFOSET(fd,packet_len(0xf2)); -} - - -/// Notifies the client of an item being added to the storage. -/// 00f4 <index>.W <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE) -/// 01c4 <index>.W <amount>.L <nameid>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE2) -void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount) -{ - int view,fd; - - nullpo_retv(sd); - nullpo_retv(i); - fd=sd->fd; - view = itemdb_viewid(i->nameid); - -#if PACKETVER < 5 - WFIFOHEAD(fd,packet_len(0xf4)); - WFIFOW(fd, 0) = 0xf4; // Storage item added - WFIFOW(fd, 2) = index+1; // index - WFIFOL(fd, 4) = amount; // amount - WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id - WFIFOB(fd,10) = i->identify; //identify flag - WFIFOB(fd,11) = i->attribute; // attribute - WFIFOB(fd,12) = i->refine; //refine - clif_addcards(WFIFOP(fd,13), i); - WFIFOSET(fd,packet_len(0xf4)); -#else - WFIFOHEAD(fd,packet_len(0x1c4)); - WFIFOW(fd, 0) = 0x1c4; // Storage item added - WFIFOW(fd, 2) = index+1; // index - WFIFOL(fd, 4) = amount; // amount - WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id - WFIFOB(fd,10) = itemdb_type(i->nameid); //type - WFIFOB(fd,11) = i->identify; //identify flag - WFIFOB(fd,12) = i->attribute; // attribute - WFIFOB(fd,13) = i->refine; //refine - clif_addcards(WFIFOP(fd,14), i); - WFIFOSET(fd,packet_len(0x1c4)); -#endif -} - - -/// Notifies the client of an item being deleted from the storage (ZC_DELETE_ITEM_FROM_STORE). -/// 00f6 <index>.W <amount>.L -void clif_storageitemremoved(struct map_session_data* sd, int index, int amount) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xf6)); - WFIFOW(fd,0)=0xf6; // Storage item removed - WFIFOW(fd,2)=index+1; - WFIFOL(fd,4)=amount; - WFIFOSET(fd,packet_len(0xf6)); -} - - -/// Closes storage (ZC_CLOSE_STORE). -/// 00f8 -void clif_storageclose(struct map_session_data* sd) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xf8)); - WFIFOW(fd,0) = 0xf8; // Storage Closed - WFIFOSET(fd,packet_len(0xf8)); -} - -/*========================================== - * 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) -{ - struct block_list *d_bl; - int i; - - if( dstsd->chatID ) { - struct chat_data *cd = NULL; - if( (cd = (struct chat_data*)map_id2bl(dstsd->chatID)) && cd->usersd[0]==dstsd) - clif_dispchat(cd,sd->fd); - } else if( dstsd->state.vending ) - clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd); - else if( dstsd->state.buyingstore ) - clif_buyingstore_entry_single(sd, dstsd); - - if(dstsd->spiritball > 0) - clif_spiritball_single(sd->fd, dstsd); - for(i = 1; i < 5; i++){ - if( dstsd->talisman[i] > 0 ) - clif_talisman_single(sd->fd, dstsd, i); - } - 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); - } -#ifdef NEW_CARTS - if( dstsd->sc.data[SC_PUSH_CART] ) - clif_status_load_single(sd->fd, dstsd->bl.id, SI_ON_PUSH_CART, 2, dstsd->sc.data[SC_PUSH_CART]->val1, 0, 0); -#endif - 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 - pc_has_permission(sd, PC_PERM_VIEW_HPMETER) - ) - clif_hpmeter_single(sd->fd, dstsd->bl.id, dstsd->battle_status.hp, dstsd->battle_status.max_hp); - - // display link (sd - dstsd) to sd - ARR_FIND( 0, 5, i, sd->devotion[i] == dstsd->bl.id ); - if( i < 5 ) clif_devotion(&sd->bl, sd); - // display links (dstsd - devotees) to sd - ARR_FIND( 0, 5, i, dstsd->devotion[i] > 0 ); - if( i < 5 ) clif_devotion(&dstsd->bl, sd); - // display link (dstsd - crusader) to sd - if( dstsd->sc.data[SC_DEVOTION] && (d_bl = map_id2bl(dstsd->sc.data[SC_DEVOTION]->val1)) != NULL ) - clif_devotion(d_bl, sd); -} - -void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl) -{ - uint8 buf[128]; - struct unit_data *ud; - struct view_data *vd; - int len; - - vd = status_get_viewdata(bl); - if (!vd || vd->class_ == INVISIBLE_CLASS) - return; - - /** - * Hide NPC from maya purple card. - **/ - if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) - return; - - ud = unit_bl2ud(bl); - len = ( ud && ud->walktimer != INVALID_TIMER ) ? clif_set_unit_walking(bl,ud,buf) : clif_set_unit_idle(bl,buf,false); - clif_send(buf,len,&sd->bl,SELF); - - if (vd->cloth_color) - clif_refreshlook(&sd->bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,SELF); - - switch (bl->type) - { - case BL_PC: - { - TBL_PC* tsd = (TBL_PC*)bl; - clif_getareachar_pc(sd, tsd); - if(tsd->state.size==SZ_BIG) // tiny/big players [Valaris] - clif_specialeffect_single(bl,423,sd->fd); - else if(tsd->state.size==SZ_MEDIUM) - clif_specialeffect_single(bl,421,sd->fd); - if( tsd->bg_id && map[tsd->bl.m].flag.battleground ) - clif_sendbgemblem_single(sd->fd,tsd); - if( tsd->sc.data[SC_CAMOUFLAGE] ) - clif_status_load(bl,SI_CAMOUFLAGE,1); - } - break; - case BL_MER: // Devotion Effects - if( ((TBL_MER*)bl)->devotion_flag ) - clif_devotion(bl, sd); - break; - case BL_NPC: - { - TBL_NPC* nd = (TBL_NPC*)bl; - if( nd->chat_id ) - clif_dispchat((struct chat_data*)map_id2bl(nd->chat_id),sd->fd); - if( nd->size == SZ_BIG ) - clif_specialeffect_single(bl,423,sd->fd); - else if( nd->size == SZ_MEDIUM ) - clif_specialeffect_single(bl,421,sd->fd); - } - break; - case BL_MOB: - { - TBL_MOB* md = (TBL_MOB*)bl; - if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris] - clif_specialeffect_single(bl,423,sd->fd); - else if(md->special_state.size==SZ_MEDIUM) - clif_specialeffect_single(bl,421,sd->fd); -#if PACKETVER >= 20120404 - if( !(md->status.mode&MD_BOSS) ){ - int i; - for(i = 0; i < DAMAGELOG_SIZE; i++)// must show hp bar to all char who already hit the mob. - if( md->dmglog[i].id == sd->status.char_id ) - clif_monster_hp_bar(md, sd->fd); - } -#endif - } - break; - case BL_PET: - if (vd->head_bottom) - clif_pet_equip(sd, (TBL_PET*)bl); // needed to display pet equip properly - break; - } -} - -//Modifies the type of damage according to status changes [Skotlex] -//Aegis data specifies that: 4 endure against single hit sources, 9 against multi-hit. -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] - *------------------------------------------*/ -static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int damage, int div_) -{ - if (type == 4 || type == 9 || damage <=0) - return 0; - - if (bl->type == BL_PC) { - if (battle_config.pc_walk_delay_rate != 100) - delay = delay*battle_config.pc_walk_delay_rate/100; - } else - if (battle_config.walk_delay_rate != 100) - delay = delay*battle_config.walk_delay_rate/100; - - if (div_ > 1) //Multi-hit skills mean higher delays. - delay += battle_config.multihit_delay*(div_-1); - - return delay>0?delay:1; //Return 1 to specify there should be no noticeable delay, but you should stop walking. -} - - -/// Sends a 'damage' packet (src performs action on dst) -/// 008a <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.W <div>.W <type>.B <damage2>.W (ZC_NOTIFY_ACT) -/// 02e1 <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.L <div>.W <type>.B <damage2>.L (ZC_NOTIFY_ACT2) -/// type: -/// 0 = damage [ damage: total damage, div: amount of hits, damage2: assassin dual-wield damage ] -/// 1 = pick up item -/// 2 = sit down -/// 3 = stand up -/// 4 = damage (endure) -/// 5 = (splash?) -/// 6 = (skill?) -/// 7 = (repeat damage?) -/// 8 = multi-hit damage -/// 9 = multi-hit damage (endure) -/// 10 = critical hit -/// 11 = lucky dodge -/// 12 = (touch skill?) -int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int damage, int div, int type, int damage2) -{ - unsigned char buf[33]; - struct status_change *sc; -#if PACKETVER < 20071113 - const int cmd = 0x8a; -#else - const int cmd = 0x2e1; -#endif - - nullpo_ret(src); - nullpo_ret(dst); - - type = clif_calc_delay(type,div,damage+damage2,ddelay); - sc = status_get_sc(dst); - if(sc && sc->count) { - if(sc->data[SC_HALLUCINATION]) { - if(damage) damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100; - if(damage2) damage2 = damage2*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100; - } - } - - WBUFW(buf,0)=cmd; - WBUFL(buf,2)=src->id; - WBUFL(buf,6)=dst->id; - WBUFL(buf,10)=tick; - WBUFL(buf,14)=sdelay; - WBUFL(buf,18)=ddelay; -#if PACKETVER < 20071113 - if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) { - WBUFW(buf,22)=damage?div:0; - WBUFW(buf,27)=damage2?div:0; - } else { - WBUFW(buf,22)=min(damage, INT16_MAX); - WBUFW(buf,27)=damage2; - } - WBUFW(buf,24)=div; - WBUFB(buf,26)=type; -#else - if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) { - WBUFL(buf,22)=damage?div:0; - WBUFL(buf,29)=damage2?div:0; - } else { - WBUFL(buf,22)=damage; - WBUFL(buf,29)=damage2; - } - WBUFW(buf,26)=div; - WBUFB(buf,28)=type; -#endif - if(disguised(dst)) { - clif_send(buf,packet_len(cmd),dst,AREA_WOS); - WBUFL(buf,6) = -dst->id; - clif_send(buf,packet_len(cmd),dst,SELF); - } else - clif_send(buf,packet_len(cmd),dst,AREA); - - if(disguised(src)) { - WBUFL(buf,2) = -src->id; - if (disguised(dst)) - WBUFL(buf,6) = dst->id; -#if PACKETVER < 20071113 - if(damage > 0) WBUFW(buf,22) = -1; - if(damage2 > 0) WBUFW(buf,27) = -1; -#else - if(damage > 0) WBUFL(buf,22) = -1; - if(damage2 > 0) WBUFL(buf,29) = -1; -#endif - clif_send(buf,packet_len(cmd),src,SELF); - } - - if(src == dst) { - unit_setdir(src,unit_getdir(src)); - } - //Return adjusted can't walk delay for further processing. - return clif_calc_walkdelay(dst,ddelay,type,damage+damage2,div); -} - -/*========================================== - * src picks up dst - *------------------------------------------*/ -void clif_takeitem(struct block_list* src, struct block_list* dst) -{ - //clif_damage(src,dst,0,0,0,0,0,1,0); - unsigned char buf[32]; - - nullpo_retv(src); - nullpo_retv(dst); - - WBUFW(buf, 0) = 0x8a; - WBUFL(buf, 2) = src->id; - WBUFL(buf, 6) = dst->id; - WBUFB(buf,26) = 1; - clif_send(buf, packet_len(0x8a), src, AREA); - -} - -/*========================================== - * inform clients in area that `bl` is sitting - *------------------------------------------*/ -void clif_sitting(struct block_list* bl) -{ - unsigned char buf[32]; - nullpo_retv(bl); - - WBUFW(buf, 0) = 0x8a; - WBUFL(buf, 2) = bl->id; - WBUFB(buf,26) = 2; - clif_send(buf, packet_len(0x8a), bl, AREA); - - if(disguised(bl)) { - WBUFL(buf, 2) = - bl->id; - clif_send(buf, packet_len(0x8a), bl, SELF); - } -} - -/*========================================== - * inform clients in area that `bl` is standing - *------------------------------------------*/ -void clif_standing(struct block_list* bl) -{ - unsigned char buf[32]; - nullpo_retv(bl); - - WBUFW(buf, 0) = 0x8a; - WBUFL(buf, 2) = bl->id; - WBUFB(buf,26) = 3; - clif_send(buf, packet_len(0x8a), bl, AREA); - - if(disguised(bl)) { - WBUFL(buf, 2) = - bl->id; - clif_send(buf, packet_len(0x8a), bl, SELF); - } -} - - -/// Inform client(s) about a map-cell change (ZC_UPDATE_MAPINFO). -/// 0192 <x>.W <y>.W <type>.W <map name>.16B -void clif_changemapcell(int fd, int16 m, int x, int y, int type, enum send_target target) -{ - unsigned char buf[32]; - - WBUFW(buf,0) = 0x192; - WBUFW(buf,2) = x; - WBUFW(buf,4) = y; - WBUFW(buf,6) = type; - mapindex_getmapname_ext(map[m].name,(char*)WBUFP(buf,8)); - - if( fd ) - { - WFIFOHEAD(fd,packet_len(0x192)); - memcpy(WFIFOP(fd,0), buf, packet_len(0x192)); - WFIFOSET(fd,packet_len(0x192)); - } - else - { - struct block_list dummy_bl; - dummy_bl.type = BL_NUL; - dummy_bl.x = x; - dummy_bl.y = y; - dummy_bl.m = m; - clif_send(buf,packet_len(0x192),&dummy_bl,target); - } -} - - -/// Notifies the client about an item on floor (ZC_ITEM_ENTRY). -/// 009d <id>.L <name id>.W <identified>.B <x>.W <y>.W <amount>.W <subX>.B <subY>.B -void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fitem) -{ - int view,fd; - fd=sd->fd; - - WFIFOHEAD(fd,packet_len(0x9d)); - WFIFOW(fd,0)=0x9d; - WFIFOL(fd,2)=fitem->bl.id; - if((view = itemdb_viewid(fitem->item_data.nameid)) > 0) - WFIFOW(fd,6)=view; - else - WFIFOW(fd,6)=fitem->item_data.nameid; - WFIFOB(fd,8)=fitem->item_data.identify; - WFIFOW(fd,9)=fitem->bl.x; - WFIFOW(fd,11)=fitem->bl.y; - WFIFOW(fd,13)=fitem->item_data.amount; - WFIFOB(fd,15)=fitem->subx; - WFIFOB(fd,16)=fitem->suby; - WFIFOSET(fd,packet_len(0x9d)); -} - - -/// Notifies the client of a skill unit. -/// 011f <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B (ZC_SKILL_ENTRY) -/// 01c9 <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B <has msg>.B <msg>.80B (ZC_SKILL_ENTRY2) -static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill_unit *unit) -{ - int fd = sd->fd; - - if( unit->group->state.guildaura ) - return; - -#if PACKETVER >= 3 - if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris] - WFIFOHEAD(fd,packet_len(0x1c9)); - WFIFOW(fd, 0)=0x1c9; - WFIFOL(fd, 2)=unit->bl.id; - WFIFOL(fd, 6)=unit->group->src_id; - WFIFOW(fd,10)=unit->bl.x; - WFIFOW(fd,12)=unit->bl.y; - WFIFOB(fd,14)=unit->group->unit_id; - WFIFOB(fd,15)=1; - WFIFOB(fd,16)=1; - safestrncpy((char*)WFIFOP(fd,17),unit->group->valstr,MESSAGE_SIZE); - WFIFOSET(fd,packet_len(0x1c9)); - return; - } -#endif - WFIFOHEAD(fd,packet_len(0x11f)); - WFIFOW(fd, 0)=0x11f; - WFIFOL(fd, 2)=unit->bl.id; - WFIFOL(fd, 6)=unit->group->src_id; - WFIFOW(fd,10)=unit->bl.x; - WFIFOW(fd,12)=unit->bl.y; - if (battle_config.traps_setting&1 && skill_get_inf2(unit->group->skill_id)&INF2_TRAP) - WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps. - else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT)) - WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps. - else - WFIFOB(fd,14)=unit->group->unit_id; - WFIFOB(fd,15)=1; // ignored by client (always gets set to 1) - WFIFOSET(fd,packet_len(0x11f)); - - if(unit->group->skill_id == WZ_ICEWALL) - clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,5,SELF); -} - - -/*========================================== - * Server tells client to remove unit of id 'unit->bl.id' - *------------------------------------------*/ -static void clif_clearchar_skillunit(struct skill_unit *unit, int fd) -{ - nullpo_retv(unit); - - WFIFOHEAD(fd,packet_len(0x120)); - WFIFOW(fd, 0)=0x120; - WFIFOL(fd, 2)=unit->bl.id; - WFIFOSET(fd,packet_len(0x120)); - - if(unit->group && unit->group->skill_id == WZ_ICEWALL) - clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,SELF); -} - - -/// Removes a skill unit (ZC_SKILL_DISAPPEAR). -/// 0120 <id>.L -void clif_skill_delunit(struct skill_unit *unit) -{ - unsigned char buf[16]; - - nullpo_retv(unit); - - WBUFW(buf, 0)=0x120; - WBUFL(buf, 2)=unit->bl.id; - clif_send(buf,packet_len(0x120),&unit->bl,AREA); -} - - -/// Sent when an object gets ankle-snared (ZC_SKILL_UPDATE). -/// 01ac <id>.L -/// Only affects units with class [139,153] client-side. -void clif_skillunit_update(struct block_list* bl) -{ - unsigned char buf[6]; - nullpo_retv(bl); - - WBUFW(buf,0) = 0x1ac; - WBUFL(buf,2) = bl->id; - - clif_send(buf,packet_len(0x1ac),bl,AREA); -} - - -/*========================================== - * - *------------------------------------------*/ -static int clif_getareachar(struct block_list* bl,va_list ap) -{ - struct map_session_data *sd; - - nullpo_ret(bl); - - sd=va_arg(ap,struct map_session_data*); - - if (sd == NULL || !sd->fd) - return 0; - - switch(bl->type){ - case BL_ITEM: - clif_getareachar_item(sd,(struct flooritem_data*) bl); - break; - case BL_SKILL: - clif_getareachar_skillunit(sd,(TBL_SKILL*)bl); - break; - default: - if(&sd->bl == bl) - break; - clif_getareachar_unit(sd,bl); - break; - } - return 0; -} - -/*========================================== - * tbl has gone out of view-size of bl - *------------------------------------------*/ -int clif_outsight(struct block_list *bl,va_list ap) -{ - struct block_list *tbl; - struct view_data *vd; - TBL_PC *sd, *tsd; - tbl=va_arg(ap,struct block_list*); - if(bl == tbl) return 0; - sd = BL_CAST(BL_PC, bl); - tsd = BL_CAST(BL_PC, tbl); - - if (tsd && tsd->fd) - { //tsd has lost sight of the bl object. - switch(bl->type){ - case BL_PC: - if (sd->vd.class_ != INVISIBLE_CLASS) - clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd); - if(sd->chatID){ - struct chat_data *cd; - cd=(struct chat_data*)map_id2bl(sd->chatID); - if(cd->usersd[0]==sd) - clif_dispchat(cd,tsd->fd); - } - if( sd->state.vending ) - clif_closevendingboard(bl,tsd->fd); - if( sd->state.buyingstore ) - clif_buyingstore_disappear_entry_single(tsd, sd); - break; - case BL_ITEM: - clif_clearflooritem((struct flooritem_data*)bl,tsd->fd); - break; - case BL_SKILL: - clif_clearchar_skillunit((struct skill_unit *)bl,tsd->fd); - break; - case BL_NPC: - if( !(((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE) ) - clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd); - break; - default: - if ((vd=status_get_viewdata(bl)) && vd->class_ != INVISIBLE_CLASS) - clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd); - break; - } - } - if (sd && sd->fd) - { //sd is watching tbl go out of view. - if (((vd=status_get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) && - !(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->sc.option&OPTION_INVISIBLE))) - clif_clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd); - } - return 0; -} - -/*========================================== - * tbl has come into view of bl - *------------------------------------------*/ -int clif_insight(struct block_list *bl,va_list ap) -{ - struct block_list *tbl; - TBL_PC *sd, *tsd; - tbl=va_arg(ap,struct block_list*); - - if (bl == tbl) return 0; - - sd = BL_CAST(BL_PC, bl); - tsd = BL_CAST(BL_PC, tbl); - - if (tsd && tsd->fd) - { //Tell tsd that bl entered into his view - switch(bl->type){ - case BL_ITEM: - clif_getareachar_item(tsd,(struct flooritem_data*)bl); - break; - case BL_SKILL: - clif_getareachar_skillunit(tsd,(TBL_SKILL*)bl); - break; - default: - clif_getareachar_unit(tsd,bl); - break; - } - } - if (sd && sd->fd) - { //Tell sd that tbl walked into his view - clif_getareachar_unit(sd,tbl); - } - return 0; -} - - -/// Updates whole skill tree (ZC_SKILLINFO_LIST). -/// 010f <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }* -void clif_skillinfoblock(struct map_session_data *sd) -{ - int fd; - int i,len,id; - - nullpo_retv(sd); - - fd=sd->fd; - if (!fd) return; - - WFIFOHEAD(fd, MAX_SKILL * 37 + 4); - WFIFOW(fd,0) = 0x10f; - for ( i = 0, len = 4; i < MAX_SKILL; i++) - { - if( (id = sd->status.skill[i].id) != 0 ) - { - // workaround for bugreport:5348 - if (len + 37 > 8192) - break; - - WFIFOW(fd,len) = id; - WFIFOL(fd,len+2) = skill_get_inf(id); - WFIFOW(fd,len+6) = sd->status.skill[i].lv; - WFIFOW(fd,len+8) = skill_get_sp(id,sd->status.skill[i].lv); - WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->status.skill[i].lv); - safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH); - if(sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) - WFIFOB(fd,len+36) = (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_))? 1:0; - else - WFIFOB(fd,len+36) = 0; - len += 37; - } - } - WFIFOW(fd,2)=len; - WFIFOSET(fd,len); - - // workaround for bugreport:5348; send the remaining skills one by one to bypass packet size limit - for ( ; i < MAX_SKILL; i++) - { - if( (id = sd->status.skill[i].id) != 0 ) - { - clif_addskill(sd, id); - clif_skillinfo(sd, id, 0); - } - } -} -/** - * Server tells client 'sd' to add skill of id 'id' to it's skill tree (e.g. with Ice Falcion item) - **/ - -/// Adds new skill to the skill tree (ZC_ADD_SKILL). -/// 0111 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B -void clif_addskill(struct map_session_data *sd, int id) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - if (!fd) return; - - if( sd->status.skill[id].id <= 0 ) - return; - - WFIFOHEAD(fd, packet_len(0x111)); - WFIFOW(fd,0) = 0x111; - WFIFOW(fd,2) = id; - WFIFOL(fd,4) = skill_get_inf(id); - WFIFOW(fd,8) = sd->status.skill[id].lv; - WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[id].lv); - WFIFOW(fd,12)= skill_get_range2(&sd->bl, id,sd->status.skill[id].lv); - safestrncpy((char*)WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH); - if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT ) - WFIFOB(fd,38) = (sd->status.skill[id].lv < skill_tree_get_max(id, sd->status.class_))? 1:0; - else - WFIFOB(fd,38) = 0; - WFIFOSET(fd,packet_len(0x111)); -} - - -/// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE). -/// 0441 <skill id>.W -void clif_deleteskill(struct map_session_data *sd, int id) -{ -#if PACKETVER >= 20081217 - int fd; - - nullpo_retv(sd); - fd = sd->fd; - if( !fd ) return; - - WFIFOHEAD(fd,packet_len(0x441)); - WFIFOW(fd,0) = 0x441; - WFIFOW(fd,2) = id; - WFIFOSET(fd,packet_len(0x441)); -#endif - clif_skillinfoblock(sd); -} - - -/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE). -/// 010e <skill id>.W <level>.W <sp cost>.W <attack range>.W <upgradable>.B -void clif_skillup(struct map_session_data *sd,uint16 skill_id) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x10e)); - WFIFOW(fd,0) = 0x10e; - WFIFOW(fd,2) = skill_id; - WFIFOW(fd,4) = sd->status.skill[skill_id].lv; - WFIFOW(fd,6) = skill_get_sp(skill_id,sd->status.skill[skill_id].lv); - WFIFOW(fd,8) = skill_get_range2(&sd->bl,skill_id,sd->status.skill[skill_id].lv); - WFIFOB(fd,10) = (sd->status.skill[skill_id].lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0; - WFIFOSET(fd,packet_len(0x10e)); -} - - -/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE2). -/// 07e1 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <upgradable>.B -void clif_skillinfo(struct map_session_data *sd,int skill, int inf) -{ - const int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x7e1)); - WFIFOW(fd,0) = 0x7e1; - WFIFOW(fd,2) = skill; - WFIFOL(fd,4) = inf?inf:skill_get_inf(skill); - WFIFOW(fd,8) = sd->status.skill[skill].lv; - WFIFOW(fd,10) = skill_get_sp(skill,sd->status.skill[skill].lv); - WFIFOW(fd,12) = skill_get_range2(&sd->bl,skill,sd->status.skill[skill].lv); - if( sd->status.skill[skill].flag == SKILL_FLAG_PERMANENT ) - WFIFOB(fd,14) = (sd->status.skill[skill].lv < skill_tree_get_max(skill, sd->status.class_))? 1:0; - else - WFIFOB(fd,14) = 0; - WFIFOSET(fd,packet_len(0x7e1)); -} - - -/// Notifies clients in area, that an object is about to use a skill. -/// 013e <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L (ZC_USESKILL_ACK) -/// 07fb <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L <is disposable>.B (ZC_USESKILL_ACK2) -/// property: -/// 0 = Yellow cast aura -/// 1 = Water elemental cast aura -/// 2 = Earth elemental cast aura -/// 3 = Fire elemental cast aura -/// 4 = Wind elemental cast aura -/// 5 = Poison elemental cast aura -/// 6 = Holy elemental cast aura -/// ? = like 0 -/// is disposable: -/// 0 = yellow chat text "[src name] will use skill [skill name]." -/// 1 = no text -void clif_skillcasting(struct block_list* bl, int src_id, int dst_id, int dst_x, int dst_y, uint16 skill_id, int property, int casttime) -{ -#if PACKETVER < 20091124 - const int cmd = 0x13e; -#else - const int cmd = 0x7fb; -#endif - unsigned char buf[32]; - - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = src_id; - WBUFL(buf,6) = dst_id; - WBUFW(buf,10) = dst_x; - WBUFW(buf,12) = dst_y; - WBUFW(buf,14) = skill_id; - WBUFL(buf,16) = property<0?0:property; //Avoid sending negatives as element [Skotlex] - WBUFL(buf,20) = casttime; -#if PACKETVER >= 20091124 - WBUFB(buf,24) = 0; // isDisposable -#endif - - if (disguised(bl)) { - clif_send(buf,packet_len(cmd), bl, AREA_WOS); - WBUFL(buf,2) = -src_id; - clif_send(buf,packet_len(cmd), bl, SELF); - } else - clif_send(buf,packet_len(cmd), bl, AREA); -} - - -/// Notifies clients in area, that an object canceled casting (ZC_DISPEL). -/// 01b9 <id>.L -void clif_skillcastcancel(struct block_list* bl) -{ - unsigned char buf[16]; - - nullpo_retv(bl); - - WBUFW(buf,0) = 0x1b9; - WBUFL(buf,2) = bl->id; - clif_send(buf,packet_len(0x1b9), bl, AREA); -} - - -/// Notifies the client about the result of a skill use request (ZC_ACK_TOUSESKILL). -/// 0110 <skill id>.W <num>.L <result>.B <cause>.B -/// num (only used when skill id = NV_BASIC and cause = 0): -/// 0 = "skill failed" MsgStringTable[159] -/// 1 = "no emotions" MsgStringTable[160] -/// 2 = "no sit" MsgStringTable[161] -/// 3 = "no chat" MsgStringTable[162] -/// 4 = "no party" MsgStringTable[163] -/// 5 = "no shout" MsgStringTable[164] -/// 6 = "no PKing" MsgStringTable[165] -/// 7 = "no alligning" MsgStringTable[383] -/// ? = ignored -/// cause: -/// 0 = "not enough skill level" MsgStringTable[214] (AL_WARP) -/// "steal failed" MsgStringTable[205] (TF_STEAL) -/// "envenom failed" MsgStringTable[207] (TF_POISON) -/// "skill failed" MsgStringTable[204] (otherwise) -/// ... = @see enum useskill_fail_cause -/// ? = ignored -/// -/// if(result!=0) doesn't display any of the previous messages -/// Note: when this packet is received an unknown flag is always set to 0, -/// suggesting this is an ACK packet for the UseSkill packets and should be sent on success too [FlavioJS] -void clif_skill_fail(struct map_session_data *sd,uint16 skill_id,enum useskill_fail_cause cause,int btype) -{ - int fd; - - if (!sd) { //Since this is the most common nullpo.... - ShowDebug("clif_skill_fail: Error, received NULL sd for skill %d\n", skill_id); - return; - } - - fd=sd->fd; - if (!fd) return; - - if(battle_config.display_skill_fail&1) - return; //Disable all skill failed messages - - if(cause==USESKILL_FAIL_SKILLINTERVAL && !sd->state.showdelay) - return; //Disable delay failed messages - - if(skill_id == RG_SNATCHER && battle_config.display_skill_fail&4) - return; - - if(skill_id == TF_POISON && battle_config.display_skill_fail&8) - return; - - WFIFOHEAD(fd,packet_len(0x110)); - WFIFOW(fd,0) = 0x110; - WFIFOW(fd,2) = skill_id; - WFIFOL(fd,4) = btype; - WFIFOB(fd,8) = 0;// success - WFIFOB(fd,9) = cause; - WFIFOSET(fd,packet_len(0x110)); -} - - -/// Skill cooldown display icon (ZC_SKILL_POSTDELAY). -/// 043d <skill ID>.W <tick>.L -void clif_skill_cooldown(struct map_session_data *sd, uint16 skill_id, unsigned int tick) -{ -#if PACKETVER>=20081112 - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x43d)); - WFIFOW(fd,0) = 0x43d; - WFIFOW(fd,2) = skill_id; - WFIFOL(fd,4) = tick; - WFIFOSET(fd,packet_len(0x43d)); -#endif -} - - -/// Skill attack effect and damage. -/// 0114 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.W <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL) -/// 01de <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.L <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL2) -int clif_skill_damage(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type) -{ - unsigned char buf[64]; - struct status_change *sc; - - nullpo_ret(src); - nullpo_ret(dst); - - type = clif_calc_delay(type,div,damage,ddelay); - sc = status_get_sc(dst); - if(sc && sc->count) { - if(sc->data[SC_HALLUCINATION] && damage) - damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100; - } - -#if PACKETVER < 3 - WBUFW(buf,0)=0x114; - WBUFW(buf,2)=skill_id; - WBUFL(buf,4)=src->id; - WBUFL(buf,8)=dst->id; - WBUFL(buf,12)=tick; - WBUFL(buf,16)=sdelay; - WBUFL(buf,20)=ddelay; - if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) { - WBUFW(buf,24)=damage?div:0; - } else { - WBUFW(buf,24)=damage; - } - WBUFW(buf,26)=skill_lv; - WBUFW(buf,28)=div; - WBUFB(buf,30)=type; - if (disguised(dst)) { - clif_send(buf,packet_len(0x114),dst,AREA_WOS); - WBUFL(buf,8)=-dst->id; - clif_send(buf,packet_len(0x114),dst,SELF); - } else - clif_send(buf,packet_len(0x114),dst,AREA); - - if(disguised(src)) { - WBUFL(buf,4)=-src->id; - if (disguised(dst)) - WBUFL(buf,8)=dst->id; - if(damage > 0) - WBUFW(buf,24)=-1; - clif_send(buf,packet_len(0x114),src,SELF); - } -#else - WBUFW(buf,0)=0x1de; - WBUFW(buf,2)=skill_id; - WBUFL(buf,4)=src->id; - WBUFL(buf,8)=dst->id; - WBUFL(buf,12)=tick; - WBUFL(buf,16)=sdelay; - WBUFL(buf,20)=ddelay; - if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) { - WBUFL(buf,24)=damage?div:0; - } else { - WBUFL(buf,24)=damage; - } - WBUFW(buf,28)=skill_lv; - WBUFW(buf,30)=div; - WBUFB(buf,32)=type; - if (disguised(dst)) { - clif_send(buf,packet_len(0x1de),dst,AREA_WOS); - WBUFL(buf,8)=-dst->id; - clif_send(buf,packet_len(0x1de),dst,SELF); - } else - clif_send(buf,packet_len(0x1de),dst,AREA); - - if(disguised(src)) { - WBUFL(buf,4)=-src->id; - if (disguised(dst)) - WBUFL(buf,8)=dst->id; - if(damage > 0) - WBUFL(buf,24)=-1; - clif_send(buf,packet_len(0x1de),src,SELF); - } -#endif - - //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex] - return clif_calc_walkdelay(dst,ddelay,type,damage,div); -} - - -/// Ground skill attack effect and damage (ZC_NOTIFY_SKILL_POSITION). -/// 0115 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <x>.W <y>.W <damage>.W <level>.W <div>.W <type>.B -/* -int clif_skill_damage2(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type) -{ - unsigned char buf[64]; - struct status_change *sc; - - nullpo_ret(src); - nullpo_ret(dst); - - type = (type>0)?type:skill_get_hit(skill_id); - type = clif_calc_delay(type,div,damage,ddelay); - sc = status_get_sc(dst); - - if(sc && sc->count) { - if(sc->data[SC_HALLUCINATION] && damage) - damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100; - } - - WBUFW(buf,0)=0x115; - WBUFW(buf,2)=skill_id; - WBUFL(buf,4)=src->id; - WBUFL(buf,8)=dst->id; - WBUFL(buf,12)=tick; - WBUFL(buf,16)=sdelay; - WBUFL(buf,20)=ddelay; - WBUFW(buf,24)=dst->x; - WBUFW(buf,26)=dst->y; - if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) { - WBUFW(buf,28)=damage?div:0; - } else { - WBUFW(buf,28)=damage; - } - WBUFW(buf,30)=skill_lv; - WBUFW(buf,32)=div; - WBUFB(buf,34)=type; - clif_send(buf,packet_len(0x115),src,AREA); - if(disguised(src)) { - WBUFL(buf,4)=-src->id; - if(damage > 0) - WBUFW(buf,28)=-1; - clif_send(buf,packet_len(0x115),src,SELF); - } - if (disguised(dst)) { - WBUFL(buf,8)=-dst->id; - if (disguised(src)) - WBUFL(buf,4)=src->id; - else if(damage > 0) - WBUFW(buf,28)=-1; - clif_send(buf,packet_len(0x115),dst,SELF); - } - - //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex] - return clif_calc_walkdelay(dst,ddelay,type,damage,div); -} -*/ - - -/// Non-damaging skill effect (ZC_USE_SKILL). -/// 011a <skill id>.W <skill lv>.W <dst id>.L <src id>.L <result>.B -int clif_skill_nodamage(struct block_list *src,struct block_list *dst,uint16 skill_id,int heal,int fail) -{ - unsigned char buf[32]; - - nullpo_ret(dst); - - WBUFW(buf,0)=0x11a; - WBUFW(buf,2)=skill_id; - WBUFW(buf,4)=min(heal, INT16_MAX); - WBUFL(buf,6)=dst->id; - WBUFL(buf,10)=src?src->id:0; - WBUFB(buf,14)=fail; - - if (disguised(dst)) { - clif_send(buf,packet_len(0x11a),dst,AREA_WOS); - WBUFL(buf,6)=-dst->id; - clif_send(buf,packet_len(0x11a),dst,SELF); - } else - clif_send(buf,packet_len(0x11a),dst,AREA); - - if(src && disguised(src)) { - WBUFL(buf,10)=-src->id; - if (disguised(dst)) - WBUFL(buf,6)=dst->id; - clif_send(buf,packet_len(0x11a),src,SELF); - } - - return fail; -} - - -/// Non-damaging ground skill effect (ZC_NOTIFY_GROUNDSKILL). -/// 0117 <skill id>.W <src id>.L <level>.W <x>.W <y>.W <tick>.L -void clif_skill_poseffect(struct block_list *src,uint16 skill_id,int val,int x,int y,int tick) -{ - unsigned char buf[32]; - - nullpo_retv(src); - - WBUFW(buf,0)=0x117; - WBUFW(buf,2)=skill_id; - WBUFL(buf,4)=src->id; - WBUFW(buf,8)=val; - WBUFW(buf,10)=x; - WBUFW(buf,12)=y; - WBUFL(buf,14)=tick; - if(disguised(src)) { - clif_send(buf,packet_len(0x117),src,AREA_WOS); - WBUFL(buf,4)=-src->id; - clif_send(buf,packet_len(0x117),src,SELF); - } else - clif_send(buf,packet_len(0x117),src,AREA); -} - - -/*========================================== - * 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) -{ - unsigned char buf[128]; - - nullpo_retv(unit); - - if( unit->group->state.guildaura ) - return; - -#if PACKETVER >= 3 - if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris] - WBUFW(buf, 0)=0x1c9; - WBUFL(buf, 2)=unit->bl.id; - WBUFL(buf, 6)=unit->group->src_id; - WBUFW(buf,10)=unit->bl.x; - WBUFW(buf,12)=unit->bl.y; - WBUFB(buf,14)=unit->group->unit_id; - WBUFB(buf,15)=1; - WBUFB(buf,16)=1; - safestrncpy((char*)WBUFP(buf,17),unit->group->valstr,MESSAGE_SIZE); - clif_send(buf,packet_len(0x1c9),&unit->bl,AREA); - return; - } -#endif - WBUFW(buf, 0)=0x11f; - WBUFL(buf, 2)=unit->bl.id; - WBUFL(buf, 6)=unit->group->src_id; - WBUFW(buf,10)=unit->bl.x; - WBUFW(buf,12)=unit->bl.y; - if (unit->group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE) - WBUFB(buf,14)=unit->val2&UF_SONG?UNT_DISSONANCE:UNT_UGLYDANCE; - else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT)) - WBUFB(buf, 14) = UNT_DUMMYSKILL; // Only display the unit at center. - else - WBUFB(buf,14)=unit->group->unit_id; - WBUFB(buf,15)=1; // ignored by client (always gets set to 1) - clif_send(buf,packet_len(0x11f),&unit->bl,AREA); -} - - -/// Presents a list of available warp destinations (ZC_WARPLIST). -/// 011c <skill id>.W { <map name>.16B }*4 -void clif_skill_warppoint(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x11c)); - WFIFOW(fd,0) = 0x11c; - WFIFOW(fd,2) = skill_id; - memset(WFIFOP(fd,4), 0x00, 4*MAP_NAME_LENGTH_EXT); - if (map1 == (unsigned short)-1) strcpy((char*)WFIFOP(fd,4), "Random"); - else // normal map name - if (map1 > 0) mapindex_getmapname_ext(mapindex_id2name(map1), (char*)WFIFOP(fd,4)); - if (map2 > 0) mapindex_getmapname_ext(mapindex_id2name(map2), (char*)WFIFOP(fd,20)); - if (map3 > 0) mapindex_getmapname_ext(mapindex_id2name(map3), (char*)WFIFOP(fd,36)); - if (map4 > 0) mapindex_getmapname_ext(mapindex_id2name(map4), (char*)WFIFOP(fd,52)); - WFIFOSET(fd,packet_len(0x11c)); - - sd->menuskill_id = skill_id; - if (skill_id == AL_WARP) - sd->menuskill_val = (sd->ud.skillx<<16)|sd->ud.skilly; //Store warp position here. - else - sd->menuskill_val = skill_lv; -} - - -/// Memo message (ZC_ACK_REMEMBER_WARPPOINT). -/// 011e <type>.B -/// type: -/// 0 = "Saved location as a Memo Point for Warp skill." in color 0xFFFF00 (cyan) -/// 1 = "Skill Level is not high enough." in color 0x0000FF (red) -/// 2 = "You haven't learned Warp." in color 0x0000FF (red) -/// -/// @param sd Who receives the message -/// @param type What message -void clif_skill_memomessage(struct map_session_data* sd, int type) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x11e)); - WFIFOW(fd,0)=0x11e; - WFIFOB(fd,2)=type; - WFIFOSET(fd,packet_len(0x11e)); -} - - -/// Teleport message (ZC_NOTIFY_MAPINFO). -/// 0189 <type>.W -/// type: -/// 0 = "Unable to Teleport in this area" in color 0xFFFF00 (cyan) -/// 1 = "Saved point cannot be memorized." in color 0x0000FF (red) -/// -/// @param sd Who receives the message -/// @param type What message -void clif_skill_teleportmessage(struct map_session_data *sd, int type) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x189)); - WFIFOW(fd,0)=0x189; - WFIFOW(fd,2)=type; - WFIFOSET(fd,packet_len(0x189)); -} - - -/// Displays Sense (WZ_ESTIMATION) information window (ZC_MONSTER_INFO). -/// 018c <class>.W <level>.W <size>.W <hp>.L <def>.W <race>.W <mdef>.W <element>.W -/// <water%>.B <earth%>.B <fire%>.B <wind%>.B <poison%>.B <holy%>.B <shadow%>.B <ghost%>.B <undead%>.B -void clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) -{ - struct status_data *status; - unsigned char buf[64]; - int i;//, fix; - - nullpo_retv(sd); - nullpo_retv(dst); - - if( dst->type != BL_MOB ) - return; - - status = status_get_status_data(dst); - - WBUFW(buf, 0)=0x18c; - WBUFW(buf, 2)=status_get_class(dst); - WBUFW(buf, 4)=status_get_lv(dst); - WBUFW(buf, 6)=status->size; - WBUFL(buf, 8)=status->hp; - WBUFW(buf,12)= (battle_config.estimation_type&1?status->def:0) - +(battle_config.estimation_type&2?status->def2:0); - WBUFW(buf,14)=status->race; - WBUFW(buf,16)= (battle_config.estimation_type&1?status->mdef:0) - +(battle_config.estimation_type&2?status->mdef2:0); - WBUFW(buf,18)= status->def_ele; - for(i=0;i<9;i++) - WBUFB(buf,20+i)= (unsigned char)battle_attr_ratio(i+1,status->def_ele, status->ele_lv); -// The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex] -// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_ratio(i+1,status->def_ele, status->ele_lv))<0?0:fix); - - clif_send(buf,packet_len(0x18c),&sd->bl,sd->status.party_id>0?PARTY_SAMEMAP:SELF); -} - - -/// Presents a textual list of producable items (ZC_MAKABLEITEMLIST). -/// 018d <packet len>.W { <name id>.W { <material id>.W }*3 }* -/// material id: -/// unused by the client -void clif_skill_produce_mix_list(struct map_session_data *sd, int skill_id , int trigger) -{ - int i,c,view,fd; - nullpo_retv(sd); - - if(sd->menuskill_id == skill_id) - return; //Avoid resending the menu twice or more times... - if( skill_id == GC_CREATENEWPOISON ) - skill_id = GC_RESEARCHNEWPOISON; - - fd=sd->fd; - WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8); - WFIFOW(fd, 0)=0x18d; - - for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){ - if( skill_can_produce_mix(sd,skill_produce_db[i].nameid, trigger, 1) && - ( skill_id > 0 && skill_produce_db[i].req_skill == skill_id || skill_id < 0 ) - ){ - if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0) - WFIFOW(fd,c*8+ 4)= view; - else - WFIFOW(fd,c*8+ 4)= skill_produce_db[i].nameid; - WFIFOW(fd,c*8+ 6)= 0; - WFIFOW(fd,c*8+ 8)= 0; - WFIFOW(fd,c*8+10)= 0; - c++; - } - } - WFIFOW(fd, 2)=c*8+8; - WFIFOSET(fd,WFIFOW(fd,2)); - if(c > 0) { - sd->menuskill_id = skill_id; - sd->menuskill_val = trigger; - return; - } -} - - -/// Present a list of producable items (ZC_MAKINGITEM_LIST). -/// 025a <packet len>.W <mk type>.W { <name id>.W }* -/// mk type: -/// 1 = cooking -/// 2 = arrow -/// 3 = elemental -/// 4 = GN_MIX_COOKING -/// 5 = GN_MAKEBOMB -/// 6 = GN_S_PHARMACY -void clif_cooking_list(struct map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type) -{ - int fd; - int i, c; - int view; - - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd, 6 + 2 * MAX_SKILL_PRODUCE_DB); - WFIFOW(fd,0) = 0x25a; - WFIFOW(fd,4) = list_type; // list type - - c = 0; - for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) { - if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, qty) ) - continue; - - if( (view = itemdb_viewid(skill_produce_db[i].nameid)) > 0 ) - WFIFOW(fd, 6 + 2 * c) = view; - else - WFIFOW(fd, 6 + 2 * c) = skill_produce_db[i].nameid; - - c++; - } - - if( skill_id == AM_PHARMACY ) { // Only send it while Cooking else check for c. - WFIFOW(fd,2) = 6 + 2 * c; - WFIFOSET(fd,WFIFOW(fd,2)); - } - - if( c > 0 ) { - sd->menuskill_id = skill_id; - sd->menuskill_val = trigger; - if( skill_id != AM_PHARMACY ) { - sd->menuskill_val2 = qty; // amount. - WFIFOW(fd,2) = 6 + 2 * c; - WFIFOSET(fd,WFIFOW(fd,2)); - } - } else { - clif_menuskill_clear(sd); - if( skill_id != AM_PHARMACY ) { // AM_PHARMACY is used to Cooking. - // It fails. -#if PACKETVER >= 20090922 - clif_msg_skill(sd,skill_id,0x625); -#else - WFIFOW(fd,2) = 6 + 2 * c; - WFIFOSET(fd,WFIFOW(fd,2)); -#endif - } - } -} - - -/// Notifies clients of a status change. -/// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)] -/// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs] -void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val1, int val2, int val3) -{ - unsigned char buf[32]; - struct map_session_data *sd; - - if (type == SI_BLANK) //It shows nothing on the client... - return; - - nullpo_retv(bl); - - sd = BL_CAST(BL_PC, bl); - - if (!(status_type2relevant_bl_types(type)&bl->type)) // only send status changes that actually matter to the client - return; - -#if PACKETVER >= 20090121 - if(flag && battle_config.display_status_timers && sd) - WBUFW(buf,0)=0x43f; - else -#endif - WBUFW(buf,0)=0x196; - WBUFW(buf,2)=type; - WBUFL(buf,4)=bl->id; - WBUFB(buf,8)=flag; -#if PACKETVER >= 20090121 - if(flag && battle_config.display_status_timers && sd) - { - if (tick <= 0) - tick = 9999; // this is indeed what official servers do - - WBUFL(buf,9) = tick; - WBUFL(buf,13) = val1; - WBUFL(buf,17) = val2; - WBUFL(buf,21) = val3; - } -#endif - clif_send(buf,packet_len(WBUFW(buf,0)),bl, (sd && sd->status.option&OPTION_INVISIBLE) ? SELF : AREA); -} - -/// Send message (modified by [Yor]) (ZC_NOTIFY_PLAYERCHAT). -/// 008e <packet len>.W <message>.?B -void clif_displaymessage(const int fd, const char* mes) -{ - nullpo_retv(mes); - - //Scrapped, as these are shared by disconnected players =X [Skotlex] - if (fd == 0) - ; - else { - char *message, *line; - - message = aStrdup(mes); - line = strtok(message, "\n"); - while(line != NULL) { - // Limit message to 255+1 characters (otherwise it causes a buffer overflow in the client) - int len = strnlen(line, 255); - - if (len > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line. - WFIFOHEAD(fd, 5 + len); - WFIFOW(fd,0) = 0x8e; - WFIFOW(fd,2) = 5 + len; // 4 + len + NULL teminate - safestrncpy((char *)WFIFOP(fd,4), line, len + 1); - WFIFOSET(fd, 5 + len); - } - line = strtok(NULL, "\n"); - } - aFree(message); - } -} - -/// Send broadcast message in yellow or blue without font formatting (ZC_BROADCAST). -/// 009a <packet len>.W <message>.?B -void clif_broadcast(struct block_list* bl, const char* mes, int len, int type, enum send_target target) -{ - int lp = type ? 4 : 0; - unsigned char *buf = (unsigned char*)aMalloc((4 + lp + len)*sizeof(unsigned char)); - - WBUFW(buf,0) = 0x9a; - WBUFW(buf,2) = 4 + lp + len; - if (type == 0x10) // bc_blue - WBUFL(buf,4) = 0x65756c62; //If there's "blue" at the beginning of the message, game client will display it in blue instead of yellow. - else if (type == 0x20) // bc_woe - WBUFL(buf,4) = 0x73737373; //If there's "ssss", game client will recognize message as 'WoE broadcast'. - memcpy(WBUFP(buf, 4 + lp), mes, len); - clif_send(buf, WBUFW(buf,2), bl, target); - - if (buf) - aFree(buf); -} - -/*========================================== - * 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) { - char buf[100]; - int len; - nullpo_retv(bl); - - if(!message) - return; - - len = strlen(message)+1; - - if( len > sizeof(buf)-8 ) { - ShowWarning("clif_GlobalMessage: Truncating too long message '%s' (len=%d).\n", message, len); - len = sizeof(buf)-8; - } - - WBUFW(buf,0)=0x8d; - WBUFW(buf,2)=len+8; - WBUFL(buf,4)=bl->id; - safestrncpy((char *) WBUFP(buf,8),message,len); - clif_send((unsigned char *) buf,WBUFW(buf,2),bl,ALL_CLIENT); - -} - -/*========================================== - * Send main chat message [LuzZza] - *------------------------------------------*/ -void clif_MainChatMessage(const char* message) { - uint8 buf[200]; - int len; - - if(!message) - return; - - len = strlen(message)+1; - if (len+8 > sizeof(buf)) { - ShowDebug("clif_MainChatMessage: Received message too long (len %d): %s\n", len, message); - len = sizeof(buf)-8; - } - WBUFW(buf,0)=0x8d; - WBUFW(buf,2)=len+8; - WBUFL(buf,4)=0; - safestrncpy((char *) WBUFP(buf,8),message,len); - clif_send(buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT); -} - -/// Send broadcast message with font formatting (ZC_BROADCAST2). -/// 01c3 <packet len>.W <fontColor>.L <fontType>.W <fontSize>.W <fontAlign>.W <fontY>.W <message>.?B -void clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY, enum send_target target) -{ - unsigned char *buf = (unsigned char*)aMalloc((16 + len)*sizeof(unsigned char)); - - WBUFW(buf,0) = 0x1c3; - WBUFW(buf,2) = len + 16; - WBUFL(buf,4) = fontColor; - WBUFW(buf,8) = fontType; - WBUFW(buf,10) = fontSize; - WBUFW(buf,12) = fontAlign; - WBUFW(buf,14) = fontY; - memcpy(WBUFP(buf,16), mes, len); - clif_send(buf, WBUFW(buf,2), bl, target); - - if (buf) - aFree(buf); -} - - -/// Displays heal effect (ZC_RECOVERY). -/// 013d <var id>.W <amount>.W -/// var id: -/// 5 = HP (SP_HP) -/// 7 = SP (SP_SP) -/// ? = ignored -void clif_heal(int fd,int type,int val) -{ - WFIFOHEAD(fd,packet_len(0x13d)); - WFIFOW(fd,0)=0x13d; - WFIFOW(fd,2)=type; - WFIFOW(fd,4)=cap_value(val,0,INT16_MAX); - WFIFOSET(fd,packet_len(0x13d)); -} - - -/// Displays resurrection effect (ZC_RESURRECTION). -/// 0148 <id>.L <type>.W -/// type: -/// ignored -void clif_resurrection(struct block_list *bl,int type) -{ - unsigned char buf[16]; - - nullpo_retv(bl); - - WBUFW(buf,0)=0x148; - WBUFL(buf,2)=bl->id; - WBUFW(buf,6)=0; - - clif_send(buf,packet_len(0x148),bl,type==1 ? AREA : AREA_WOS); - if (disguised(bl)) - clif_spawn(bl); -} - - -/// Sets the map property (ZC_NOTIFY_MAPPROPERTY). -/// 0199 <type>.W -void clif_map_property(struct map_session_data* sd, enum map_property property) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x199)); - WFIFOW(fd,0)=0x199; - WFIFOW(fd,2)=property; - WFIFOSET(fd,packet_len(0x199)); -} - - -/// Set the map type (ZC_NOTIFY_MAPPROPERTY2). -/// 01d6 <type>.W -void clif_map_type(struct map_session_data* sd, enum map_type type) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x1D6)); - WFIFOW(fd,0)=0x1D6; - WFIFOW(fd,2)=type; - WFIFOSET(fd,packet_len(0x1D6)); -} - - -/// Updates PvP ranking (ZC_NOTIFY_RANKING). -/// 019a <id>.L <ranking>.L <total>.L -void clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type) -{ - if(type == 2) { - int fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x19a)); - WFIFOW(fd,0) = 0x19a; - WFIFOL(fd,2) = sd->bl.id; - WFIFOL(fd,6) = pvprank; - WFIFOL(fd,10) = pvpnum; - WFIFOSET(fd,packet_len(0x19a)); - } else { - unsigned char buf[32]; - WBUFW(buf,0) = 0x19a; - WBUFL(buf,2) = sd->bl.id; - if(sd->sc.option&(OPTION_HIDE|OPTION_CLOAK)) - WBUFL(buf,6) = UINT32_MAX; //On client displays as -- - else - WBUFL(buf,6) = pvprank; - WBUFL(buf,10) = pvpnum; - if(sd->sc.option&OPTION_INVISIBLE || sd->disguise) //Causes crashes when a 'mob' with pvp info dies. - clif_send(buf,packet_len(0x19a),&sd->bl,SELF); - else if(!type) - clif_send(buf,packet_len(0x19a),&sd->bl,AREA); - else - clif_send(buf,packet_len(0x19a),&sd->bl,ALL_SAMEMAP); - } -} - - -/*========================================== - * - *------------------------------------------*/ -void clif_map_property_mapall(int map, enum map_property property) -{ - struct block_list bl; - unsigned char buf[16]; - - bl.id = 0; - bl.type = BL_NUL; - bl.m = map; - WBUFW(buf,0)=0x199; - WBUFW(buf,2)=property; - clif_send(buf,packet_len(0x199),&bl,ALL_SAMEMAP); -} - - -/// Notifies the client about the result of a refine attempt (ZC_ACK_ITEMREFINING). -/// 0188 <result>.W <index>.W <refine>.W -/// result: -/// 0 = success -/// 1 = failure -/// 2 = downgrade -void clif_refine(int fd, int fail, int index, int val) -{ - WFIFOHEAD(fd,packet_len(0x188)); - WFIFOW(fd,0)=0x188; - WFIFOW(fd,2)=fail; - WFIFOW(fd,4)=index+2; - WFIFOW(fd,6)=val; - WFIFOSET(fd,packet_len(0x188)); -} - - -/// Notifies the client about the result of a weapon refine attempt (ZC_ACK_WEAPONREFINE). -/// 0223 <result>.L <nameid>.W -/// result: -/// 0 = "weapon upgraded: %s" MsgStringTable[911] in rgb(0,255,255) -/// 1 = "weapon upgraded: %s" MsgStringTable[912] in rgb(0,205,205) -/// 2 = "cannot upgrade %s until you level up the upgrade weapon skill" MsgStringTable[913] in rgb(255,200,200) -/// 3 = "you lack the item %s to upgrade the weapon" MsgStringTable[914] in rgb(255,200,200) -void clif_upgrademessage(int fd, int result, int item_id) -{ - WFIFOHEAD(fd,packet_len(0x223)); - WFIFOW(fd,0)=0x223; - WFIFOL(fd,2)=result; - WFIFOW(fd,6)=item_id; - WFIFOSET(fd,packet_len(0x223)); -} - - -/// Whisper is transmitted to the destination player (ZC_WHISPER). -/// 0097 <packet len>.W <nick>.24B <message>.?B -/// 0097 <packet len>.W <nick>.24B <isAdmin>.L <message>.?B (PACKETVER >= 20091104) -void clif_wis_message(int fd, const char* nick, const char* mes, int mes_len) -{ -#if PACKETVER < 20091104 - WFIFOHEAD(fd, mes_len + NAME_LENGTH + 4); - WFIFOW(fd,0) = 0x97; - WFIFOW(fd,2) = mes_len + NAME_LENGTH + 4; - safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH); - safestrncpy((char*)WFIFOP(fd,28), mes, mes_len); - WFIFOSET(fd,WFIFOW(fd,2)); -#else - WFIFOHEAD(fd, mes_len + NAME_LENGTH + 8); - WFIFOW(fd,0) = 0x97; - WFIFOW(fd,2) = mes_len + NAME_LENGTH + 8; - safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH); - WFIFOL(fd,28) = 0; // isAdmin; if nonzero, also displays text above char - // TODO: WFIFOL(fd,28) = pc_get_group_level(ssd); - safestrncpy((char*)WFIFOP(fd,32), mes, mes_len); - WFIFOSET(fd,WFIFOW(fd,2)); -#endif -} - - -/// Inform the player about the result of his whisper action (ZC_ACK_WHISPER). -/// 0098 <result>.B -/// result: -/// 0 = success to send wisper -/// 1 = target character is not loged in -/// 2 = ignored by target -/// 3 = everyone ignored by target -void clif_wis_end(int fd, int flag) -{ - WFIFOHEAD(fd,packet_len(0x98)); - WFIFOW(fd,0) = 0x98; - WFIFOW(fd,2) = flag; - WFIFOSET(fd,packet_len(0x98)); -} - - -/// Returns character name requested by char_id (ZC_ACK_REQNAME_BYGID). -/// 0194 <char id>.L <name>.24B -void clif_solved_charname(int fd, int charid, const char* name) -{ - WFIFOHEAD(fd,packet_len(0x194)); - WFIFOW(fd,0)=0x194; - WFIFOL(fd,2)=charid; - safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH); - WFIFOSET(fd,packet_len(0x194)); -} - - -/// Presents a list of items that can be carded/composed (ZC_ITEMCOMPOSITION_LIST). -/// 017b <packet len>.W { <name id>.W }* -void clif_use_card(struct map_session_data *sd,int idx) -{ - int i,c,ep; - int fd=sd->fd; - - nullpo_retv(sd); - if (idx < 0 || idx >= MAX_INVENTORY) //Crash-fix from bad packets. - return; - - if (!sd->inventory_data[idx] || sd->inventory_data[idx]->type != IT_CARD) - return; //Avoid parsing invalid item indexes (no card/no item) - - ep=sd->inventory_data[idx]->equip; - WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4); - WFIFOW(fd,0)=0x17b; - - for(i=c=0;i<MAX_INVENTORY;i++){ - int j; - - if(sd->inventory_data[i] == NULL) - continue; - if(sd->inventory_data[i]->type!=IT_WEAPON && sd->inventory_data[i]->type!=IT_ARMOR) - continue; - if(itemdb_isspecial(sd->status.inventory[i].card[0])) //Can't slot it - continue; - - if(sd->status.inventory[i].identify==0 ) //Not identified - continue; - - if((sd->inventory_data[i]->equip&ep)==0) //Not equippable on this part. - continue; - - if(sd->inventory_data[i]->type==IT_WEAPON && ep==EQP_SHIELD) //Shield card won't go on left weapon. - continue; - - ARR_FIND( 0, sd->inventory_data[i]->slot, j, sd->status.inventory[i].card[j] == 0 ); - if( j == sd->inventory_data[i]->slot ) // No room - continue; - - WFIFOW(fd,4+c*2)=i+2; - c++; - } - WFIFOW(fd,2)=4+c*2; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Notifies the client about the result of item carding/composition (ZC_ACK_ITEMCOMPOSITION). -/// 017d <equip index>.W <card index>.W <result>.B -/// result: -/// 0 = success -/// 1 = failure -void clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x17d)); - WFIFOW(fd,0)=0x17d; - WFIFOW(fd,2)=idx_equip+2; - WFIFOW(fd,4)=idx_card+2; - WFIFOB(fd,6)=flag; - WFIFOSET(fd,packet_len(0x17d)); -} - - -/// Presents a list of items that can be identified (ZC_ITEMIDENTIFY_LIST). -/// 0177 <packet len>.W { <name id>.W }* -void clif_item_identify_list(struct map_session_data *sd) -{ - int i,c; - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - - WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4); - WFIFOW(fd,0)=0x177; - for(i=c=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid > 0 && !sd->status.inventory[i].identify){ - WFIFOW(fd,c*2+4)=i+2; - c++; - } - } - if(c > 0) { - WFIFOW(fd,2)=c*2+4; - WFIFOSET(fd,WFIFOW(fd,2)); - sd->menuskill_id = MC_IDENTIFY; - sd->menuskill_val = c; - } -} - - -/// Notifies the client about the result of a item identify request (ZC_ACK_ITEMIDENTIFY). -/// 0179 <index>.W <result>.B -void clif_item_identified(struct map_session_data *sd,int idx,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x179)); - WFIFOW(fd, 0)=0x179; - WFIFOW(fd, 2)=idx+2; - WFIFOB(fd, 4)=flag; - WFIFOSET(fd,packet_len(0x179)); -} - - -/// Presents a list of items that can be repaired (ZC_REPAIRITEMLIST). -/// 01fc <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* -void clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd, int lv) -{ - int i,c; - int fd; - int nameid; - - nullpo_retv(sd); - nullpo_retv(dstsd); - - fd=sd->fd; - - WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4); - WFIFOW(fd,0)=0x1fc; - for(i=c=0;i<MAX_INVENTORY;i++){ - if((nameid=dstsd->status.inventory[i].nameid) > 0 && dstsd->status.inventory[i].attribute!=0){// && skill_can_repair(sd,nameid)){ - WFIFOW(fd,c*13+4) = i; - WFIFOW(fd,c*13+6) = nameid; - WFIFOB(fd,c*13+8) = dstsd->status.inventory[i].refine; - clif_addcards(WFIFOP(fd,c*13+9), &dstsd->status.inventory[i]); - c++; - } - } - if(c > 0) { - WFIFOW(fd,2)=c*13+4; - WFIFOSET(fd,WFIFOW(fd,2)); - sd->menuskill_id = BS_REPAIRWEAPON; - sd->menuskill_val = dstsd->bl.id; - sd->menuskill_val2 = lv; - }else - clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0); -} - - -/// Notifies the client about the result of a item repair request (ZC_ACK_ITEMREPAIR). -/// 01fe <index>.W <result>.B -/// index: -/// ignored (inventory index) -/// result: -/// 0 = Item repair success. -/// 1 = Item repair failure. -void clif_item_repaireffect(struct map_session_data *sd,int idx,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x1fe)); - WFIFOW(fd, 0)=0x1fe; - WFIFOW(fd, 2)=idx+2; - WFIFOB(fd, 4)=flag; - WFIFOSET(fd,packet_len(0x1fe)); - -} - - -/// Displays a message, that an equipment got damaged (ZC_EQUIPITEM_DAMAGED). -/// 02bb <equip location>.W <account id>.L -void clif_item_damaged(struct map_session_data* sd, unsigned short position) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x2bb)); - WFIFOW(fd,0) = 0x2bb; - WFIFOW(fd,2) = position; - WFIFOL(fd,4) = sd->bl.id; // TODO: the packet seems to be sent to other people as well, probably party and/or guild. - WFIFOSET(fd,packet_len(0x2bb)); -} - - -/// Presents a list of weapon items that can be refined [Taken from jAthena] (ZC_NOTIFY_WEAPONITEMLIST). -/// 0221 <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* -void clif_item_refine_list(struct map_session_data *sd) -{ - int i,c; - int fd; - uint16 skill_lv; - int wlv; - int refine_item[5]; - - nullpo_retv(sd); - - skill_lv = pc_checkskill(sd,WS_WEAPONREFINE); - - fd=sd->fd; - - refine_item[0] = -1; - refine_item[1] = pc_search_inventory(sd,1010); - refine_item[2] = pc_search_inventory(sd,1011); - refine_item[3] = refine_item[4] = pc_search_inventory(sd,984); - - WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4); - WFIFOW(fd,0)=0x221; - for(i=c=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].refine < skill_lv && - sd->status.inventory[i].identify && (wlv=itemdb_wlv(sd->status.inventory[i].nameid)) >=1 && - refine_item[wlv]!=-1 && !(sd->status.inventory[i].equip&EQP_ARMS)){ - WFIFOW(fd,c*13+ 4)=i+2; - WFIFOW(fd,c*13+ 6)=sd->status.inventory[i].nameid; - WFIFOB(fd,c*13+ 8)=sd->status.inventory[i].refine; - clif_addcards(WFIFOP(fd,c*13+9), &sd->status.inventory[i]); - c++; - } - } - WFIFOW(fd,2)=c*13+4; - WFIFOSET(fd,WFIFOW(fd,2)); - if (c > 0) { - sd->menuskill_id = WS_WEAPONREFINE; - sd->menuskill_val = skill_lv; - } -} - - -/// Notification of an auto-casted skill (ZC_AUTORUN_SKILL). -/// 0147 <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B -void clif_item_skill(struct map_session_data *sd,uint16 skill_id,uint16 skill_lv) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x147)); - WFIFOW(fd, 0)=0x147; - WFIFOW(fd, 2)=skill_id; - WFIFOW(fd, 4)=skill_get_inf(skill_id); - WFIFOW(fd, 6)=0; - WFIFOW(fd, 8)=skill_lv; - WFIFOW(fd,10)=skill_get_sp(skill_id,skill_lv); - WFIFOW(fd,12)=skill_get_range2(&sd->bl, skill_id,skill_lv); - safestrncpy((char*)WFIFOP(fd,14),skill_get_name(skill_id),NAME_LENGTH); - WFIFOB(fd,38)=0; - WFIFOSET(fd,packet_len(0x147)); -} - - -/// Adds an item to character's cart. -/// 0124 <index>.W <amount>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART) -/// 01c5 <index>.W <amount>.L <name id>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART2) -void clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail) -{ - int view,fd; - unsigned char *buf; - - nullpo_retv(sd); - - fd=sd->fd; - if(n<0 || n>=MAX_CART || sd->status.cart[n].nameid<=0) - return; - -#if PACKETVER < 5 - WFIFOHEAD(fd,packet_len(0x124)); - buf=WFIFOP(fd,0); - WBUFW(buf,0)=0x124; - WBUFW(buf,2)=n+2; - WBUFL(buf,4)=amount; - if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0) - WBUFW(buf,8)=view; - else - WBUFW(buf,8)=sd->status.cart[n].nameid; - WBUFB(buf,10)=sd->status.cart[n].identify; - WBUFB(buf,11)=sd->status.cart[n].attribute; - WBUFB(buf,12)=sd->status.cart[n].refine; - clif_addcards(WBUFP(buf,13), &sd->status.cart[n]); - WFIFOSET(fd,packet_len(0x124)); -#else - WFIFOHEAD(fd,packet_len(0x1c5)); - buf=WFIFOP(fd,0); - WBUFW(buf,0)=0x1c5; - WBUFW(buf,2)=n+2; - WBUFL(buf,4)=amount; - if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0) - WBUFW(buf,8)=view; - else - WBUFW(buf,8)=sd->status.cart[n].nameid; - WBUFB(buf,10)=itemdb_type(sd->status.cart[n].nameid); - WBUFB(buf,11)=sd->status.cart[n].identify; - WBUFB(buf,12)=sd->status.cart[n].attribute; - WBUFB(buf,13)=sd->status.cart[n].refine; - clif_addcards(WBUFP(buf,14), &sd->status.cart[n]); - WFIFOSET(fd,packet_len(0x1c5)); -#endif -} - - -/// Deletes an item from character's cart (ZC_DELETE_ITEM_FROM_CART). -/// 0125 <index>.W <amount>.L -void clif_cart_delitem(struct map_session_data *sd,int n,int amount) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - - WFIFOHEAD(fd,packet_len(0x125)); - WFIFOW(fd,0)=0x125; - WFIFOW(fd,2)=n+2; - WFIFOL(fd,4)=amount; - WFIFOSET(fd,packet_len(0x125)); -} - - -/// Opens the shop creation menu (ZC_OPENSTORE). -/// 012d <num>.W -/// num: -/// number of allowed item slots -void clif_openvendingreq(struct map_session_data* sd, int num) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x12d)); - WFIFOW(fd,0) = 0x12d; - WFIFOW(fd,2) = num; - WFIFOSET(fd,packet_len(0x12d)); -} - - -/// Displays a vending board to target/area (ZC_STORE_ENTRY). -/// 0131 <owner id>.L <message>.80B -void clif_showvendingboard(struct block_list* bl, const char* message, int fd) -{ - unsigned char buf[128]; - - nullpo_retv(bl); - - WBUFW(buf,0) = 0x131; - WBUFL(buf,2) = bl->id; - safestrncpy((char*)WBUFP(buf,6), message, 80); - - if( fd ) { - WFIFOHEAD(fd,packet_len(0x131)); - memcpy(WFIFOP(fd,0),buf,packet_len(0x131)); - WFIFOSET(fd,packet_len(0x131)); - } else { - clif_send(buf,packet_len(0x131),bl,AREA_WOS); - } -} - - -/// Removes a vending board from screen (ZC_DISAPPEAR_ENTRY). -/// 0132 <owner id>.L -void clif_closevendingboard(struct block_list* bl, int fd) -{ - unsigned char buf[16]; - - nullpo_retv(bl); - - WBUFW(buf,0) = 0x132; - WBUFL(buf,2) = bl->id; - if( fd ) { - WFIFOHEAD(fd,packet_len(0x132)); - memcpy(WFIFOP(fd,0),buf,packet_len(0x132)); - WFIFOSET(fd,packet_len(0x132)); - } else { - clif_send(buf,packet_len(0x132),bl,AREA_WOS); - } -} - - -/// Sends a list of items in a shop. -/// R 0133 <packet len>.W <owner id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC) -/// R 0800 <packet len>.W <owner id>.L <unique id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC2) -void clif_vendinglist(struct map_session_data* sd, int id, struct s_vending* vending) -{ - int i,fd; - int count; - struct map_session_data* vsd; -#if PACKETVER < 20100105 - const int cmd = 0x133; - const int offset = 8; -#else - const int cmd = 0x800; - const int offset = 12; -#endif - - nullpo_retv(sd); - nullpo_retv(vending); - nullpo_retv(vsd=map_id2sd(id)); - - fd = sd->fd; - count = vsd->vend_num; - - WFIFOHEAD(fd, offset+count*22); - WFIFOW(fd,0) = cmd; - WFIFOW(fd,2) = offset+count*22; - WFIFOL(fd,4) = id; -#if PACKETVER >= 20100105 - WFIFOL(fd,8) = vsd->vender_id; -#endif - - for( i = 0; i < count; i++ ) - { - int index = vending[i].index; - struct item_data* data = itemdb_search(vsd->status.cart[index].nameid); - WFIFOL(fd,offset+ 0+i*22) = vending[i].value; - WFIFOW(fd,offset+ 4+i*22) = vending[i].amount; - WFIFOW(fd,offset+ 6+i*22) = vending[i].index + 2; - WFIFOB(fd,offset+ 8+i*22) = itemtype(data->type); - WFIFOW(fd,offset+ 9+i*22) = ( data->view_id > 0 ) ? data->view_id : vsd->status.cart[index].nameid; - WFIFOB(fd,offset+11+i*22) = vsd->status.cart[index].identify; - WFIFOB(fd,offset+12+i*22) = vsd->status.cart[index].attribute; - WFIFOB(fd,offset+13+i*22) = vsd->status.cart[index].refine; - clif_addcards(WFIFOP(fd,offset+14+i*22), &vsd->status.cart[index]); - } - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Shop purchase failure (ZC_PC_PURCHASE_RESULT_FROMMC). -/// 0135 <index>.W <amount>.W <result>.B -/// result: -/// 0 = success -/// 1 = not enough zeny -/// 2 = overweight -/// 4 = out of stock -/// 5 = "cannot use an npc shop while in a trade" -/// 6 = Because the store information was incorrect the item was not purchased. -/// 7 = No sales information. -void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x135)); - WFIFOW(fd,0) = 0x135; - WFIFOW(fd,2) = index+2; - WFIFOW(fd,4) = amount; - WFIFOB(fd,6) = fail; - WFIFOSET(fd,packet_len(0x135)); -} - - -/// Shop creation success (ZC_PC_PURCHASE_MYITEMLIST). -/// 0136 <packet len>.W <owner id>.L { <price>.L <index>.W <amount>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* -void clif_openvending(struct map_session_data* sd, int id, struct s_vending* vending) -{ - int i,fd; - int count; - - nullpo_retv(sd); - - fd = sd->fd; - count = sd->vend_num; - - WFIFOHEAD(fd, 8+count*22); - WFIFOW(fd,0) = 0x136; - WFIFOW(fd,2) = 8+count*22; - WFIFOL(fd,4) = id; - for( i = 0; i < count; i++ ) - { - int index = vending[i].index; - struct item_data* data = itemdb_search(sd->status.cart[index].nameid); - WFIFOL(fd, 8+i*22) = vending[i].value; - WFIFOW(fd,12+i*22) = vending[i].index + 2; - WFIFOW(fd,14+i*22) = vending[i].amount; - WFIFOB(fd,16+i*22) = itemtype(data->type); - WFIFOW(fd,17+i*22) = ( data->view_id > 0 ) ? data->view_id : sd->status.cart[index].nameid; - WFIFOB(fd,19+i*22) = sd->status.cart[index].identify; - WFIFOB(fd,20+i*22) = sd->status.cart[index].attribute; - WFIFOB(fd,21+i*22) = sd->status.cart[index].refine; - clif_addcards(WFIFOP(fd,22+i*22), &sd->status.cart[index]); - } - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Inform merchant that someone has bought an item (ZC_DELETEITEM_FROM_MCSTORE). -/// 0137 <index>.W <amount>.W -void clif_vendingreport(struct map_session_data* sd, int index, int amount) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x137)); - WFIFOW(fd,0) = 0x137; - WFIFOW(fd,2) = index+2; - WFIFOW(fd,4) = amount; - WFIFOSET(fd,packet_len(0x137)); -} - - -/// Result of organizing a party (ZC_ACK_MAKE_GROUP). -/// 00fa <result>.B -/// result: -/// 0 = opens party window and shows MsgStringTable[77]="party successfully organized" -/// 1 = MsgStringTable[78]="party name already exists" -/// 2 = MsgStringTable[79]="already in a party" -/// 3 = cannot organize parties on this map -/// ? = nothing -void clif_party_created(struct map_session_data *sd,int result) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xfa)); - WFIFOW(fd,0)=0xfa; - WFIFOB(fd,2)=result; - WFIFOSET(fd,packet_len(0xfa)); -} - - -/// Adds new member to a party. -/// 0104 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B (ZC_ADD_MEMBER_TO_GROUP) -/// 01e9 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B <item pickup rule>.B <item share rule>.B (ZC_ADD_MEMBER_TO_GROUP2) -/// role: -/// 0 = leader -/// 1 = normal -/// state: -/// 0 = connected -/// 1 = disconnected -void clif_party_member_info(struct party_data *p, struct map_session_data *sd) -{ - unsigned char buf[81]; - int i; - - if (!sd) { //Pick any party member (this call is used when changing item share rules) - ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd != 0 ); - } else { - ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd ); - } - if (i >= MAX_PARTY) return; //Should never happen... - sd = p->data[i].sd; - - WBUFW(buf, 0) = 0x1e9; - WBUFL(buf, 2) = sd->status.account_id; - WBUFL(buf, 6) = (p->party.member[i].leader)?0:1; - WBUFW(buf,10) = sd->bl.x; - WBUFW(buf,12) = sd->bl.y; - WBUFB(buf,14) = (p->party.member[i].online)?0:1; - memcpy(WBUFP(buf,15), p->party.name, NAME_LENGTH); - memcpy(WBUFP(buf,39), sd->status.name, NAME_LENGTH); - mapindex_getmapname_ext(map[sd->bl.m].name, (char*)WBUFP(buf,63)); - WBUFB(buf,79) = (p->party.item&1)?1:0; - WBUFB(buf,80) = (p->party.item&2)?1:0; - clif_send(buf,packet_len(0x1e9),&sd->bl,PARTY); -} - - -/// Sends party information (ZC_GROUP_LIST). -/// 00fb <packet len>.W <party name>.24B { <account id>.L <nick>.24B <map name>.16B <role>.B <state>.B }* -/// role: -/// 0 = leader -/// 1 = normal -/// state: -/// 0 = connected -/// 1 = disconnected -void clif_party_info(struct party_data* p, struct map_session_data *sd) -{ - unsigned char buf[2+2+NAME_LENGTH+(4+NAME_LENGTH+MAP_NAME_LENGTH_EXT+1+1)*MAX_PARTY]; - struct map_session_data* party_sd = NULL; - int i, c; - - nullpo_retv(p); - - WBUFW(buf,0) = 0xfb; - memcpy(WBUFP(buf,4), p->party.name, NAME_LENGTH); - for(i = 0, c = 0; i < MAX_PARTY; i++) - { - struct party_member* m = &p->party.member[i]; - if(!m->account_id) continue; - - if(party_sd == NULL) party_sd = p->data[i].sd; - - WBUFL(buf,28+c*46) = m->account_id; - memcpy(WBUFP(buf,28+c*46+4), m->name, NAME_LENGTH); - mapindex_getmapname_ext(mapindex_id2name(m->map), (char*)WBUFP(buf,28+c*46+28)); - WBUFB(buf,28+c*46+44) = (m->leader) ? 0 : 1; - WBUFB(buf,28+c*46+45) = (m->online) ? 0 : 1; - c++; - } - WBUFW(buf,2) = 28+c*46; - - if(sd) { // send only to self - clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); - } else if (party_sd) { // send to whole party - clif_send(buf, WBUFW(buf,2), &party_sd->bl, PARTY); - } -} - - -/// The player's 'party invite' state, sent during login (ZC_PARTY_CONFIG). -/// 02c9 <flag>.B -/// flag: -/// 0 = allow party invites -/// 1 = auto-deny party invites -void clif_partyinvitationstate(struct map_session_data* sd) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x2c9)); - WFIFOW(fd, 0) = 0x2c9; - WFIFOB(fd, 2) = 0; // not implemented - WFIFOSET(fd, packet_len(0x2c9)); -} - - -/// Party invitation request. -/// 00fe <party id>.L <party name>.24B (ZC_REQ_JOIN_GROUP) -/// 02c6 <party id>.L <party name>.24B (ZC_PARTY_JOIN_REQ) -void clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd) -{ -#if PACKETVER < 20070821 - const int cmd = 0xfe; -#else - const int cmd = 0x2c6; -#endif - int fd; - struct party_data *p; - - nullpo_retv(sd); - nullpo_retv(tsd); - - fd=tsd->fd; - - if( (p=party_search(sd->status.party_id))==NULL ) - return; - - WFIFOHEAD(fd,packet_len(cmd)); - WFIFOW(fd,0)=cmd; - WFIFOL(fd,2)=sd->status.party_id; - memcpy(WFIFOP(fd,6),p->party.name,NAME_LENGTH); - WFIFOSET(fd,packet_len(cmd)); -} - - -/// Party invite result. -/// 00fd <nick>.24S <result>.B (ZC_ACK_REQ_JOIN_GROUP) -/// 02c5 <nick>.24S <result>.L (ZC_PARTY_JOIN_REQ_ACK) -/// 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) = 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) = result; - WFIFOSET(fd,packet_len(0x2c5)); -#endif -} - - -/// Updates party settings. -/// 0101 <exp option>.L (ZC_GROUPINFO_CHANGE) -/// 07d8 <exp option>.L <item pick rule>.B <item share rule>.B (ZC_REQ_GROUPINFO_CHANGE_V2) -/// exp option: -/// 0 = exp sharing disabled -/// 1 = exp sharing enabled -/// 2 = cannot change exp sharing -/// -/// flag: -/// 0 = send to party -/// 1 = send to sd -void clif_party_option(struct party_data *p,struct map_session_data *sd,int flag) -{ - unsigned char buf[16]; -#if PACKETVER < 20090603 - const int cmd = 0x101; -#else - const int cmd = 0x7d8; -#endif - - nullpo_retv(p); - - if(!sd && flag==0){ - int i; - for(i=0;i<MAX_PARTY && !p->data[i].sd;i++); - if (i < MAX_PARTY) - sd = p->data[i].sd; - } - if(!sd) return; - WBUFW(buf,0)=cmd; - WBUFL(buf,2)=((flag&0x01)?2:p->party.exp); -#if PACKETVER >= 20090603 - WBUFB(buf,6)=(p->party.item&1)?1:0; - WBUFB(buf,7)=(p->party.item&2)?1:0; -#endif - if(flag==0) - clif_send(buf,packet_len(cmd),&sd->bl,PARTY); - else - clif_send(buf,packet_len(cmd),&sd->bl,SELF); -} - - -/// 0105 <account id>.L <char name>.24B <result>.B (ZC_DELETE_MEMBER_FROM_GROUP). -/// result: -/// 0 = leave -/// 1 = expel -/// 2 = cannot leave party on this map -/// 3 = cannot expel from party on this map -void clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int account_id, const char* name, int flag) -{ - unsigned char buf[64]; - int i; - - nullpo_retv(p); - - if(!sd && (flag&0xf0)==0) - { - for(i=0;i<MAX_PARTY && !p->data[i].sd;i++); - if (i < MAX_PARTY) - sd = p->data[i].sd; - } - - if(!sd) return; - - WBUFW(buf,0)=0x105; - WBUFL(buf,2)=account_id; - memcpy(WBUFP(buf,6),name,NAME_LENGTH); - WBUFB(buf,30)=flag&0x0f; - if((flag&0xf0)==0) - clif_send(buf,packet_len(0x105),&sd->bl,PARTY); - else - clif_send(buf,packet_len(0x105),&sd->bl,SELF); -} - - -/// Party chat message (ZC_NOTIFY_CHAT_PARTY). -/// 0109 <packet len>.W <account id>.L <message>.?B -void clif_party_message(struct party_data* p, int account_id, const char* mes, int len) -{ - struct map_session_data *sd; - int i; - - nullpo_retv(p); - - for(i=0; i < MAX_PARTY && !p->data[i].sd;i++); - if(i < MAX_PARTY){ - unsigned char buf[1024]; - - if( len > sizeof(buf)-8 ) - { - ShowWarning("clif_party_message: Truncated message '%s' (len=%d, max=%d, party_id=%d).\n", mes, len, sizeof(buf)-8, p->party.party_id); - len = sizeof(buf)-8; - } - - sd = p->data[i].sd; - WBUFW(buf,0)=0x109; - WBUFW(buf,2)=len+8; - WBUFL(buf,4)=account_id; - safestrncpy((char *)WBUFP(buf,8), mes, len); - clif_send(buf,len+8,&sd->bl,PARTY); - } -} - - -/// Updates the position of a party member on the minimap (ZC_NOTIFY_POSITION_TO_GROUPM). -/// 0107 <account id>.L <x>.W <y>.W -void clif_party_xy(struct map_session_data *sd) -{ - unsigned char buf[16]; - - nullpo_retv(sd); - - WBUFW(buf,0)=0x107; - WBUFL(buf,2)=sd->status.account_id; - WBUFW(buf,6)=sd->bl.x; - WBUFW(buf,8)=sd->bl.y; - clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS); -} - - -/*========================================== - * Sends x/y dot to a single fd. [Skotlex] - *------------------------------------------*/ -void clif_party_xy_single(int fd, struct map_session_data *sd) -{ - WFIFOHEAD(fd,packet_len(0x107)); - WFIFOW(fd,0)=0x107; - WFIFOL(fd,2)=sd->status.account_id; - WFIFOW(fd,6)=sd->bl.x; - WFIFOW(fd,8)=sd->bl.y; - WFIFOSET(fd,packet_len(0x107)); -} - - -/// Updates HP bar of a party member. -/// 0106 <account id>.L <hp>.W <max hp>.W (ZC_NOTIFY_HP_TO_GROUPM) -/// 080e <account id>.L <hp>.L <max hp>.L (ZC_NOTIFY_HP_TO_GROUPM_R2) -void clif_party_hp(struct map_session_data *sd) -{ - unsigned char buf[16]; -#if PACKETVER < 20100126 - const int cmd = 0x106; -#else - const int cmd = 0x80e; -#endif - - nullpo_retv(sd); - - WBUFW(buf,0)=cmd; - WBUFL(buf,2)=sd->status.account_id; -#if PACKETVER < 20100126 - if (sd->battle_status.max_hp > INT16_MAX) { //To correctly display the %hp bar. [Skotlex] - WBUFW(buf,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100); - WBUFW(buf,8) = 100; - } else { - WBUFW(buf,6) = sd->battle_status.hp; - WBUFW(buf,8) = sd->battle_status.max_hp; - } -#else - WBUFL(buf,6) = sd->battle_status.hp; - WBUFL(buf,10) = sd->battle_status.max_hp; -#endif - clif_send(buf,packet_len(cmd),&sd->bl,PARTY_AREA_WOS); -} - - -/*========================================== - * Sends HP bar to a single fd. [Skotlex] - *------------------------------------------*/ -void clif_hpmeter_single(int fd, int id, unsigned int hp, unsigned int maxhp) -{ -#if PACKETVER < 20100126 - const int cmd = 0x106; -#else - const int cmd = 0x80e; -#endif - WFIFOHEAD(fd,packet_len(cmd)); - WFIFOW(fd,0) = cmd; - WFIFOL(fd,2) = id; -#if PACKETVER < 20100126 - if( maxhp > INT16_MAX ) - {// To correctly display the %hp bar. [Skotlex] - WFIFOW(fd,6) = hp/(maxhp/100); - WFIFOW(fd,8) = 100; - } else { - WFIFOW(fd,6) = hp; - WFIFOW(fd,8) = maxhp; - } -#else - WFIFOL(fd,6) = hp; - WFIFOL(fd,10) = maxhp; -#endif - WFIFOSET(fd, packet_len(cmd)); -} - -/// Notifies the client, that it's attack target is too far (ZC_ATTACK_FAILURE_FOR_DISTANCE). -/// 0139 <target id>.L <target x>.W <target y>.W <x>.W <y>.W <atk range>.W -void clif_movetoattack(struct map_session_data *sd,struct block_list *bl) -{ - int fd; - - nullpo_retv(sd); - nullpo_retv(bl); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x139)); - WFIFOW(fd, 0)=0x139; - WFIFOL(fd, 2)=bl->id; - WFIFOW(fd, 6)=bl->x; - WFIFOW(fd, 8)=bl->y; - WFIFOW(fd,10)=sd->bl.x; - WFIFOW(fd,12)=sd->bl.y; - WFIFOW(fd,14)=sd->battle_status.rhw.range; - WFIFOSET(fd,packet_len(0x139)); -} - - -/// Notifies the client about the result of an item produce request (ZC_ACK_REQMAKINGITEM). -/// 018f <result>.W <name id>.W -/// result: -/// 0 = success -/// 1 = failure -/// 2 = success (alchemist) -/// 3 = failure (alchemist) -void clif_produceeffect(struct map_session_data* sd,int flag,int nameid) -{ - int view,fd; - - nullpo_retv(sd); - - fd = sd->fd; - clif_solved_charname(fd, sd->status.char_id, sd->status.name); - WFIFOHEAD(fd,packet_len(0x18f)); - WFIFOW(fd, 0)=0x18f; - WFIFOW(fd, 2)=flag; - if((view = itemdb_viewid(nameid)) > 0) - WFIFOW(fd, 4)=view; - else - WFIFOW(fd, 4)=nameid; - WFIFOSET(fd,packet_len(0x18f)); -} - - -/// Initiates the pet taming process (ZC_START_CAPTURE). -/// 019e -void clif_catch_process(struct map_session_data *sd) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x19e)); - WFIFOW(fd,0)=0x19e; - WFIFOSET(fd,packet_len(0x19e)); -} - - -/// Displays the result of a pet taming attempt (ZC_TRYCAPTURE_MONSTER). -/// 01a0 <result>.B -/// 0 = failure -/// 1 = success -void clif_pet_roulette(struct map_session_data *sd,int data) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x1a0)); - WFIFOW(fd,0)=0x1a0; - WFIFOB(fd,2)=data; - WFIFOSET(fd,packet_len(0x1a0)); -} - - -/// Presents a list of pet eggs that can be hatched (ZC_PETEGG_LIST). -/// 01a6 <packet len>.W { <index>.W }* -void clif_sendegg(struct map_session_data *sd) -{ - int i,n=0,fd; - - nullpo_retv(sd); - - fd=sd->fd; - if (battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m)) - { //Disable pet hatching in GvG grounds during Guild Wars [Skotlex] - clif_displaymessage(fd, msg_txt(666)); - return; - } - WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4); - WFIFOW(fd,0)=0x1a6; - for(i=0,n=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->inventory_data[i]->type!=IT_PETEGG || - sd->status.inventory[i].amount<=0) - continue; - WFIFOW(fd,n*2+4)=i+2; - n++; - } - WFIFOW(fd,2)=4+n*2; - WFIFOSET(fd,WFIFOW(fd,2)); - - sd->menuskill_id = SA_TAMINGMONSTER; - sd->menuskill_val = -1; -} - - -/// Sends a specific pet data update (ZC_CHANGESTATE_PET). -/// 01a4 <type>.B <id>.L <data>.L -/// type: -/// 0 = pre-init (data = 0) -/// 1 = intimacy (data = 0~4) -/// 2 = hunger (data = 0~4) -/// 3 = accessory -/// 4 = performance (data = 1~3: normal, 4: special) -/// 5 = hairstyle -/// -/// If sd is null, the update is sent to nearby objects, otherwise it is sent only to that player. -void clif_send_petdata(struct map_session_data* sd, struct pet_data* pd, int type, int param) -{ - uint8 buf[16]; - nullpo_retv(pd); - - WBUFW(buf,0) = 0x1a4; - WBUFB(buf,2) = type; - WBUFL(buf,3) = pd->bl.id; - WBUFL(buf,7) = param; - if (sd) - clif_send(buf, packet_len(0x1a4), &sd->bl, SELF); - else - clif_send(buf, packet_len(0x1a4), &pd->bl, AREA); -} - - -/// Pet's base data (ZC_PROPERTY_PET). -/// 01a2 <name>.24B <renamed>.B <level>.W <hunger>.W <intimacy>.W <accessory id>.W <class>.W -void clif_send_petstatus(struct map_session_data *sd) -{ - int fd; - struct s_pet *pet; - - nullpo_retv(sd); - nullpo_retv(sd->pd); - - fd=sd->fd; - pet = &sd->pd->pet; - WFIFOHEAD(fd,packet_len(0x1a2)); - WFIFOW(fd,0)=0x1a2; - memcpy(WFIFOP(fd,2),pet->name,NAME_LENGTH); - WFIFOB(fd,26)=battle_config.pet_rename?0:pet->rename_flag; - WFIFOW(fd,27)=pet->level; - WFIFOW(fd,29)=pet->hungry; - WFIFOW(fd,31)=pet->intimate; - WFIFOW(fd,33)=pet->equip; -#if PACKETVER >= 20081126 - WFIFOW(fd,35)=pet->class_; -#endif - WFIFOSET(fd,packet_len(0x1a2)); -} - - -/// Notification about a pet's emotion/talk (ZC_PET_ACT). -/// 01aa <id>.L <data>.L -/// data: -/// @see CZ_PET_ACT. -void clif_pet_emotion(struct pet_data *pd,int param) -{ - unsigned char buf[16]; - - nullpo_retv(pd); - - memset(buf,0,packet_len(0x1aa)); - - WBUFW(buf,0)=0x1aa; - WBUFL(buf,2)=pd->bl.id; - if(param >= 100 && pd->petDB->talk_convert_class) { - if(pd->petDB->talk_convert_class < 0) - return; - else if(pd->petDB->talk_convert_class > 0) { - // replace mob_id component of talk/act data - param -= (pd->pet.class_ - 100)*100; - param += (pd->petDB->talk_convert_class - 100)*100; - } - } - WBUFL(buf,6)=param; - - clif_send(buf,packet_len(0x1aa),&pd->bl,AREA); -} - - -/// Result of request to feed a pet (ZC_FEED_PET). -/// 01a3 <result>.B <name id>.W -/// result: -/// 0 = failure -/// 1 = success -void clif_pet_food(struct map_session_data *sd,int foodid,int fail) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x1a3)); - WFIFOW(fd,0)=0x1a3; - WFIFOB(fd,2)=fail; - WFIFOW(fd,3)=foodid; - WFIFOSET(fd,packet_len(0x1a3)); -} - - -/// Presents a list of skills that can be auto-spelled (ZC_AUTOSPELLLIST). -/// 01cd { <skill id>.L }*7 -void clif_autospell(struct map_session_data *sd,uint16 skill_lv) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x1cd)); - WFIFOW(fd, 0)=0x1cd; - - if(skill_lv>0 && pc_checkskill(sd,MG_NAPALMBEAT)>0) - WFIFOL(fd,2)= MG_NAPALMBEAT; - else - WFIFOL(fd,2)= 0x00000000; - if(skill_lv>1 && pc_checkskill(sd,MG_COLDBOLT)>0) - WFIFOL(fd,6)= MG_COLDBOLT; - else - WFIFOL(fd,6)= 0x00000000; - if(skill_lv>1 && pc_checkskill(sd,MG_FIREBOLT)>0) - WFIFOL(fd,10)= MG_FIREBOLT; - else - WFIFOL(fd,10)= 0x00000000; - if(skill_lv>1 && pc_checkskill(sd,MG_LIGHTNINGBOLT)>0) - WFIFOL(fd,14)= MG_LIGHTNINGBOLT; - else - WFIFOL(fd,14)= 0x00000000; - if(skill_lv>4 && pc_checkskill(sd,MG_SOULSTRIKE)>0) - WFIFOL(fd,18)= MG_SOULSTRIKE; - else - WFIFOL(fd,18)= 0x00000000; - if(skill_lv>7 && pc_checkskill(sd,MG_FIREBALL)>0) - WFIFOL(fd,22)= MG_FIREBALL; - else - WFIFOL(fd,22)= 0x00000000; - if(skill_lv>9 && pc_checkskill(sd,MG_FROSTDIVER)>0) - WFIFOL(fd,26)= MG_FROSTDIVER; - else - WFIFOL(fd,26)= 0x00000000; - - WFIFOSET(fd,packet_len(0x1cd)); - sd->menuskill_id = SA_AUTOSPELL; - sd->menuskill_val = skill_lv; -} - - -/// Devotion's visual effect (ZC_DEVOTIONLIST). -/// 01cf <devoter id>.L { <devotee id>.L }*5 <max distance>.W -void clif_devotion(struct block_list *src, struct map_session_data *tsd) -{ - unsigned char buf[56]; - int i; - - nullpo_retv(src); - memset(buf,0,packet_len(0x1cf)); - - WBUFW(buf,0) = 0x1cf; - WBUFL(buf,2) = src->id; - if( src->type == BL_MER ) - { - struct mercenary_data *md = BL_CAST(BL_MER,src); - if( md && md->master && md->devotion_flag ) - WBUFL(buf,6) = md->master->bl.id; - - WBUFW(buf,26) = skill_get_range2(src, ML_DEVOTION, mercenary_checkskill(md, ML_DEVOTION)); - } - else - { - struct map_session_data *sd = BL_CAST(BL_PC,src); - if( sd == NULL ) - return; - - for( i = 0; i < 5; i++ ) - WBUFL(buf,6+4*i) = sd->devotion[i]; - WBUFW(buf,26) = skill_get_range2(src, CR_DEVOTION, pc_checkskill(sd, CR_DEVOTION)); - } - - if( tsd ) - clif_send(buf, packet_len(0x1cf), &tsd->bl, SELF); - else - clif_send(buf, packet_len(0x1cf), src, AREA); -} - -/*========================================== - * Server tells clients nearby 'sd' (and himself) to display 'sd->spiritball' number of spiritballs on 'sd' - * Notifies clients in an area of an object's spirits. - * 01d0 <id>.L <amount>.W (ZC_SPIRITS) - * 01e1 <id>.L <amount>.W (ZC_SPIRITS2) - *------------------------------------------*/ -void clif_spiritball(struct block_list *bl) { - unsigned char buf[16]; - TBL_PC *sd = BL_CAST(BL_PC,bl); - TBL_HOM *hd = BL_CAST(BL_HOM,bl); - - nullpo_retv(bl); - - WBUFW(buf, 0) = 0x1d0; - WBUFL(buf, 2) = bl->id; - WBUFW(buf, 6) = 0; //init to 0 - switch(bl->type){ - case BL_PC: WBUFW(buf, 6) = sd->spiritball; break; - case BL_HOM: WBUFW(buf, 6) = hd->homunculus.spiritball; break; - } - clif_send(buf, packet_len(0x1d0), bl, AREA); -} - - -/// Notifies clients in area of a character's combo delay (ZC_COMBODELAY). -/// 01d2 <account id>.L <delay>.L -void clif_combo_delay(struct block_list *bl,int wait) -{ - unsigned char buf[32]; - - nullpo_retv(bl); - - WBUFW(buf,0)=0x1d2; - WBUFL(buf,2)=bl->id; - WBUFL(buf,6)=wait; - clif_send(buf,packet_len(0x1d2),bl,AREA); -} - - -/// Notifies clients in area that a character has blade-stopped another (ZC_BLADESTOP). -/// 01d1 <src id>.L <dst id>.L <flag>.L -/// flag: -/// 0 = inactive -/// 1 = active -void clif_bladestop(struct block_list *src, int dst_id, int active) -{ - unsigned char buf[32]; - - nullpo_retv(src); - - WBUFW(buf,0)=0x1d1; - WBUFL(buf,2)=src->id; - WBUFL(buf,6)=dst_id; - WBUFL(buf,10)=active; - - clif_send(buf,packet_len(0x1d1),src,AREA); -} - - -/// MVP effect (ZC_MVP). -/// 010c <account id>.L -void clif_mvp_effect(struct map_session_data *sd) -{ - unsigned char buf[16]; - - nullpo_retv(sd); - - WBUFW(buf,0)=0x10c; - WBUFL(buf,2)=sd->bl.id; - clif_send(buf,packet_len(0x10c),&sd->bl,AREA); -} - - -/// MVP item reward message (ZC_MVP_GETTING_ITEM). -/// 010a <name id>.W -void clif_mvp_item(struct map_session_data *sd,int nameid) -{ - int view,fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x10a)); - WFIFOW(fd,0)=0x10a; - if((view = itemdb_viewid(nameid)) > 0) - WFIFOW(fd,2)=view; - else - WFIFOW(fd,2)=nameid; - WFIFOSET(fd,packet_len(0x10a)); -} - - -/// MVP EXP reward message (ZC_MVP_GETTING_SPECIAL_EXP). -/// 010b <exp>.L -void clif_mvp_exp(struct map_session_data *sd, unsigned int exp) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x10b)); - WFIFOW(fd,0)=0x10b; - WFIFOL(fd,2)=cap_value(exp,0,INT32_MAX); - WFIFOSET(fd,packet_len(0x10b)); -} - - -/// Dropped MVP item reward message (ZC_THROW_MVPITEM). -/// 010d -/// -/// "You are the MVP, but cannot obtain the reward because -/// you are overweight." -void clif_mvp_noitem(struct map_session_data* sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x10d)); - WFIFOW(fd,0) = 0x10d; - WFIFOSET(fd,packet_len(0x10d)); -} - - -/// Guild creation result (ZC_RESULT_MAKE_GUILD). -/// 0167 <result>.B -/// result: -/// 0 = "Guild has been created." -/// 1 = "You are already in a Guild." -/// 2 = "That Guild Name already exists." -/// 3 = "You need the neccessary item to create a Guild." -void clif_guild_created(struct map_session_data *sd,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x167)); - WFIFOW(fd,0)=0x167; - WFIFOB(fd,2)=flag; - WFIFOSET(fd,packet_len(0x167)); -} - - -/// Notifies the client that it is belonging to a guild (ZC_UPDATE_GDID). -/// 016c <guild id>.L <emblem id>.L <mode>.L <ismaster>.B <inter sid>.L <guild name>.24B -/// mode: -/// &0x01 = allow invite -/// &0x10 = allow expel -void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g) -{ - int ps,fd; - nullpo_retv(sd); - nullpo_retv(g); - - fd=sd->fd; - ps=guild_getposition(g,sd); - WFIFOHEAD(fd,packet_len(0x16c)); - WFIFOW(fd,0)=0x16c; - WFIFOL(fd,2)=g->guild_id; - WFIFOL(fd,6)=g->emblem_id; - WFIFOL(fd,10)=g->position[ps].mode; - WFIFOB(fd,14)=(bool)(sd->state.gmaster_flag==g); - WFIFOL(fd,15)=0; // InterSID (unknown purpose) - memcpy(WFIFOP(fd,19),g->name,NAME_LENGTH); - WFIFOSET(fd,packet_len(0x16c)); -} - - -/// Guild member login notice. -/// 016d <account id>.L <char id>.L <status>.L (ZC_UPDATE_CHARSTAT) -/// 01f2 <account id>.L <char id>.L <status>.L <gender>.W <hair style>.W <hair color>.W (ZC_UPDATE_CHARSTAT2) -/// status: -/// 0 = offline -/// 1 = online -void clif_guild_memberlogin_notice(struct guild *g,int idx,int flag) -{ - unsigned char buf[64]; - struct map_session_data* sd; - - nullpo_retv(g); - - WBUFW(buf, 0)=0x1f2; - WBUFL(buf, 2)=g->member[idx].account_id; - WBUFL(buf, 6)=g->member[idx].char_id; - WBUFL(buf,10)=flag; - - if( ( sd = g->member[idx].sd ) != NULL ) - { - WBUFW(buf,14) = sd->status.sex; - WBUFW(buf,16) = sd->status.hair; - WBUFW(buf,18) = sd->status.hair_color; - clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD_WOS); - } - else if( ( sd = guild_getavailablesd(g) ) != NULL ) - { - WBUFW(buf,14) = 0; - WBUFW(buf,16) = 0; - WBUFW(buf,18) = 0; - clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD); - } -} - -// Function `clif_guild_memberlogin_notice` sends info about -// logins and logouts of a guild member to the rest members. -// But at the 1st time (after a player login or map changing) -// the client won't show the message. -// So I suggest use this function for sending "first-time-info" -// to some player on entering the game or changing location. -// At next time the client would always show the message. -// The function sends all the statuses in the single packet -// to economize traffic. [LuzZza] -void clif_guild_send_onlineinfo(struct map_session_data *sd) -{ - struct guild *g; - unsigned char buf[14*128]; - int i, count=0, p_len; - - nullpo_retv(sd); - - p_len = packet_len(0x16d); - - if(!(g = guild_search(sd->status.guild_id))) - return; - - for(i=0; i<g->max_member; i++) { - - if(g->member[i].account_id > 0 && - g->member[i].account_id != sd->status.account_id) { - - WBUFW(buf,count*p_len) = 0x16d; - WBUFL(buf,count*p_len+2) = g->member[i].account_id; - WBUFL(buf,count*p_len+6) = g->member[i].char_id; - WBUFL(buf,count*p_len+10) = g->member[i].online; - count++; - } - } - - clif_send(buf, p_len*count, &sd->bl, SELF); -} - - -/// Bitmask of enabled guild window tabs (ZC_ACK_GUILD_MENUINTERFACE). -/// 014e <menu flag>.L -/// menu flag: -/// 0x00 = Basic Info (always on) -/// &0x01 = Member manager -/// &0x02 = Positions -/// &0x04 = Skills -/// &0x10 = Expulsion list -/// &0x40 = Unknown (GMENUFLAG_ALLGUILDLIST) -/// &0x80 = Notice -void clif_guild_masterormember(struct map_session_data *sd) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x14e)); - WFIFOW(fd,0) = 0x14e; - WFIFOL(fd,2) = (sd->state.gmaster_flag) ? 0xd7 : 0x57; - WFIFOSET(fd,packet_len(0x14e)); -} - - -/// Guild basic information (Territories [Valaris]) -/// 0150 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B (ZC_GUILD_INFO) -/// 01b6 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B <zeny>.L (ZC_GUILD_INFO2) -void clif_guild_basicinfo(struct map_session_data *sd) { - int fd; - struct guild *g; - - nullpo_retv(sd); - fd = sd->fd; - - if( (g = guild_search(sd->status.guild_id)) == NULL ) - return; - - WFIFOHEAD(fd,packet_len(0x1b6)); - WFIFOW(fd, 0)=0x1b6;//0x150; - WFIFOL(fd, 2)=g->guild_id; - WFIFOL(fd, 6)=g->guild_lv; - WFIFOL(fd,10)=g->connect_member; - WFIFOL(fd,14)=g->max_member; - WFIFOL(fd,18)=g->average_lv; - WFIFOL(fd,22)=(uint32)cap_value(g->exp,0,INT32_MAX); - WFIFOL(fd,26)=g->next_exp; - WFIFOL(fd,30)=0; // Tax Points - WFIFOL(fd,34)=0; // Honor: (left) Vulgar [-100,100] Famed (right) - WFIFOL(fd,38)=0; // Virtue: (down) Wicked [-100,100] Righteous (up) - WFIFOL(fd,42)=g->emblem_id; - memcpy(WFIFOP(fd,46),g->name, NAME_LENGTH); - memcpy(WFIFOP(fd,70),g->master, NAME_LENGTH); - - safestrncpy((char*)WFIFOP(fd,94),msg_txt(300+guild_checkcastles(g)),16); // "'N' castles" - WFIFOL(fd,110) = 0; // zeny - - WFIFOSET(fd,packet_len(0x1b6)); -} - - -/// Guild alliance and opposition list (ZC_MYGUILD_BASIC_INFO). -/// 014c <packet len>.W { <relation>.L <guild id>.L <guild name>.24B }* -void clif_guild_allianceinfo(struct map_session_data *sd) -{ - int fd,i,c; - struct guild *g; - - nullpo_retv(sd); - if( (g = guild_search(sd->status.guild_id)) == NULL ) - return; - - fd = sd->fd; - WFIFOHEAD(fd, MAX_GUILDALLIANCE * 32 + 4); - WFIFOW(fd, 0)=0x14c; - for(i=c=0;i<MAX_GUILDALLIANCE;i++){ - struct guild_alliance *a=&g->alliance[i]; - if(a->guild_id>0){ - WFIFOL(fd,c*32+4)=a->opposition; - WFIFOL(fd,c*32+8)=a->guild_id; - memcpy(WFIFOP(fd,c*32+12),a->name,NAME_LENGTH); - c++; - } - } - WFIFOW(fd, 2)=c*32+4; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Guild member manager information (ZC_MEMBERMGR_INFO). -/// 0154 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B }* -/// state: -/// 0 = offline -/// 1 = online -/// memo: -/// probably member's self-introduction (unused, no client UI/packets for editing it) -void clif_guild_memberlist(struct map_session_data *sd) -{ - int fd; - int i,c; - struct guild *g; - nullpo_retv(sd); - - if( (fd = sd->fd) == 0 ) - return; - if( (g = guild_search(sd->status.guild_id)) == NULL ) - return; - - WFIFOHEAD(fd, g->max_member * 104 + 4); - WFIFOW(fd, 0)=0x154; - for(i=0,c=0;i<g->max_member;i++){ - struct guild_member *m=&g->member[i]; - if(m->account_id==0) - continue; - WFIFOL(fd,c*104+ 4)=m->account_id; - WFIFOL(fd,c*104+ 8)=m->char_id; - WFIFOW(fd,c*104+12)=m->hair; - WFIFOW(fd,c*104+14)=m->hair_color; - WFIFOW(fd,c*104+16)=m->gender; - WFIFOW(fd,c*104+18)=m->class_; - WFIFOW(fd,c*104+20)=m->lv; - WFIFOL(fd,c*104+22)=(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); //[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++; - } - WFIFOW(fd, 2)=c*104+4; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Guild position name information (ZC_POSITION_ID_NAME_INFO). -/// 0166 <packet len>.W { <position id>.L <position name>.24B }* -void clif_guild_positionnamelist(struct map_session_data *sd) -{ - int i,fd; - struct guild *g; - - nullpo_retv(sd); - if( (g = guild_search(sd->status.guild_id)) == NULL ) - return; - - fd = sd->fd; - WFIFOHEAD(fd, MAX_GUILDPOSITION * 28 + 4); - WFIFOW(fd, 0)=0x166; - for(i=0;i<MAX_GUILDPOSITION;i++){ - WFIFOL(fd,i*28+4)=i; - memcpy(WFIFOP(fd,i*28+8),g->position[i].name,NAME_LENGTH); - } - WFIFOW(fd,2)=i*28+4; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Guild position information (ZC_POSITION_INFO). -/// 0160 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L }* -/// mode: -/// &0x01 = allow invite -/// &0x10 = allow expel -/// ranking: -/// TODO -void clif_guild_positioninfolist(struct map_session_data *sd) -{ - int i,fd; - struct guild *g; - - nullpo_retv(sd); - if( (g = guild_search(sd->status.guild_id)) == NULL ) - return; - - fd = sd->fd; - WFIFOHEAD(fd, MAX_GUILDPOSITION * 16 + 4); - WFIFOW(fd, 0)=0x160; - for(i=0;i<MAX_GUILDPOSITION;i++){ - struct guild_position *p=&g->position[i]; - WFIFOL(fd,i*16+ 4)=i; - WFIFOL(fd,i*16+ 8)=p->mode; - WFIFOL(fd,i*16+12)=i; - WFIFOL(fd,i*16+16)=p->exp_mode; - } - WFIFOW(fd, 2)=i*16+4; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Notifies clients in a guild about updated position information (ZC_ACK_CHANGE_GUILD_POSITIONINFO). -/// 0174 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <position name>.24B }* -/// mode: -/// &0x01 = allow invite -/// &0x10 = allow expel -/// ranking: -/// TODO -void clif_guild_positionchanged(struct guild *g,int idx) -{ - // FIXME: This packet is intended to update the clients after a - // commit of position info changes, not sending one packet per - // position. - struct map_session_data *sd; - unsigned char buf[128]; - - nullpo_retv(g); - - WBUFW(buf, 0)=0x174; - WBUFW(buf, 2)=44; // packet len - // GUILD_REG_POSITION_INFO{ - WBUFL(buf, 4)=idx; - WBUFL(buf, 8)=g->position[idx].mode; - WBUFL(buf,12)=idx; - WBUFL(buf,16)=g->position[idx].exp_mode; - memcpy(WBUFP(buf,20),g->position[idx].name,NAME_LENGTH); - // }* - if( (sd=guild_getavailablesd(g))!=NULL ) - clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD); -} - - -/// Notifies clients in a guild about updated member position assignments (ZC_ACK_REQ_CHANGE_MEMBERS). -/// 0156 <packet len>.W { <account id>.L <char id>.L <position id>.L }* -void clif_guild_memberpositionchanged(struct guild *g,int idx) -{ - // FIXME: This packet is intended to update the clients after a - // commit of member position assignment changes, not sending one - // packet per position. - struct map_session_data *sd; - unsigned char buf[64]; - - nullpo_retv(g); - - WBUFW(buf, 0)=0x156; - WBUFW(buf, 2)=16; // packet len - // MEMBER_POSITION_INFO{ - WBUFL(buf, 4)=g->member[idx].account_id; - WBUFL(buf, 8)=g->member[idx].char_id; - WBUFL(buf,12)=g->member[idx].position; - // }* - if( (sd=guild_getavailablesd(g))!=NULL ) - clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD); -} - - -/// Sends emblems bitmap data to the client that requested it (ZC_GUILD_EMBLEM_IMG). -/// 0152 <packet len>.W <guild id>.L <emblem id>.L <emblem data>.?B -void clif_guild_emblem(struct map_session_data *sd,struct guild *g) -{ - int fd; - nullpo_retv(sd); - nullpo_retv(g); - - fd = sd->fd; - if( g->emblem_len <= 0 ) - return; - - WFIFOHEAD(fd,g->emblem_len+12); - WFIFOW(fd,0)=0x152; - WFIFOW(fd,2)=g->emblem_len+12; - WFIFOL(fd,4)=g->guild_id; - WFIFOL(fd,8)=g->emblem_id; - memcpy(WFIFOP(fd,12),g->emblem_data,g->emblem_len); - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Sends update of the guild id/emblem id to everyone in the area (ZC_CHANGE_GUILD). -/// 01b4 <id>.L <guild id>.L <emblem id>.W -void clif_guild_emblem_area(struct block_list* bl) -{ - uint8 buf[12]; - - nullpo_retv(bl); - - // TODO this packet doesn't force the update of ui components that have the emblem visible - // (emblem in the flag npcs and emblem over the head in agit maps) [FlavioJS] - WBUFW(buf,0) = 0x1b4; - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = status_get_guild_id(bl); - WBUFW(buf,10) = status_get_emblem_id(bl); - clif_send(buf, 12, bl, AREA_WOS); -} - - -/// Sends guild skills (ZC_GUILD_SKILLINFO). -/// 0162 <packet len>.W <skill points>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B }* -void clif_guild_skillinfo(struct map_session_data* sd) -{ - int fd; - struct guild* g; - int i,c; - - nullpo_retv(sd); - if( (g = guild_search(sd->status.guild_id)) == NULL ) - return; - - fd = sd->fd; - WFIFOHEAD(fd, 6 + MAX_GUILDSKILL*37); - WFIFOW(fd,0) = 0x0162; - WFIFOW(fd,4) = g->skill_point; - for(i = 0, c = 0; i < MAX_GUILDSKILL; i++) - { - if(g->skill[i].id > 0 && guild_check_skill_require(g, g->skill[i].id)) - { - int id = g->skill[i].id; - int p = 6 + c*37; - WFIFOW(fd,p+0) = id; - WFIFOL(fd,p+2) = skill_get_inf(id); - WFIFOW(fd,p+6) = g->skill[i].lv; - WFIFOW(fd,p+8) = skill_get_sp(id, g->skill[i].lv); - WFIFOW(fd,p+10) = skill_get_range(id, g->skill[i].lv); - safestrncpy((char*)WFIFOP(fd,p+12), skill_get_name(id), NAME_LENGTH); - WFIFOB(fd,p+36)= (g->skill[i].lv < guild_skill_get_max(id) && sd == g->member[0].sd) ? 1 : 0; - c++; - } - } - WFIFOW(fd,2) = 6 + c*37; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Sends guild notice to client (ZC_GUILD_NOTICE). -/// 016f <subject>.60B <notice>.120B -void clif_guild_notice(struct map_session_data* sd, struct guild* g) -{ - int fd; - - nullpo_retv(sd); - nullpo_retv(g); - - fd = sd->fd; - - if ( !session_isActive(fd) ) - return; - - if(g->mes1[0] == '\0' && g->mes2[0] == '\0') - return; - - WFIFOHEAD(fd,packet_len(0x16f)); - WFIFOW(fd,0) = 0x16f; - memcpy(WFIFOP(fd,2), g->mes1, MAX_GUILDMES1); - memcpy(WFIFOP(fd,62), g->mes2, MAX_GUILDMES2); - WFIFOSET(fd,packet_len(0x16f)); -} - - -/// Guild invite (ZC_REQ_JOIN_GUILD). -/// 016a <guild id>.L <guild name>.24B -void clif_guild_invite(struct map_session_data *sd,struct guild *g) -{ - int fd; - - nullpo_retv(sd); - nullpo_retv(g); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x16a)); - WFIFOW(fd,0)=0x16a; - WFIFOL(fd,2)=g->guild_id; - memcpy(WFIFOP(fd,6),g->name,NAME_LENGTH); - WFIFOSET(fd,packet_len(0x16a)); -} - - -/// Reply to invite request (ZC_ACK_REQ_JOIN_GUILD). -/// 0169 <answer>.B -/// answer: -/// 0 = Already in guild. -/// 1 = Offer rejected. -/// 2 = Offer accepted. -/// 3 = Guild full. -void clif_guild_inviteack(struct map_session_data *sd,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x169)); - WFIFOW(fd,0)=0x169; - WFIFOB(fd,2)=flag; - WFIFOSET(fd,packet_len(0x169)); -} - - -/// Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD). -/// 015a <char name>.24B <reason>.40B -void clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes) -{ - unsigned char buf[128]; - - nullpo_retv(sd); - - WBUFW(buf, 0)=0x15a; - memcpy(WBUFP(buf, 2),name,NAME_LENGTH); - memcpy(WBUFP(buf,26),mes,40); - clif_send(buf,packet_len(0x15a),&sd->bl,GUILD_NOBG); -} - - -/// Notifies clients of a guild of an expelled member. -/// 015c <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD) -/// 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO) -void clif_guild_expulsion(struct map_session_data* sd, const char* name, const char* mes, int account_id) -{ - unsigned char buf[128]; -#if PACKETVER < 20100803 - const unsigned short cmd = 0x15c; -#else - const unsigned short cmd = 0x839; -#endif - - nullpo_retv(sd); - - WBUFW(buf,0) = cmd; - safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH); - safestrncpy((char*)WBUFP(buf,26), mes, 40); -#if PACKETVER < 20100803 - memset(WBUFP(buf,66), 0, NAME_LENGTH); // account name (not used for security reasons) -#endif - clif_send(buf, packet_len(cmd), &sd->bl, GUILD_NOBG); -} - - -/// Guild expulsion list (ZC_BAN_LIST). -/// 0163 <packet len>.W { <char name>.24B <account name>.24B <reason>.40B }* -/// 0163 <packet len>.W { <char name>.24B <reason>.40B }* (PACKETVER >= 20100803) -void clif_guild_expulsionlist(struct map_session_data* sd) -{ -#if PACKETVER < 20100803 - const int offset = NAME_LENGTH*2+40; -#else - const int offset = NAME_LENGTH+40; -#endif - int fd, i, c = 0; - struct guild* g; - - nullpo_retv(sd); - - if( (g = guild_search(sd->status.guild_id)) == NULL ) - return; - - fd = sd->fd; - - WFIFOHEAD(fd,4 + MAX_GUILDEXPULSION * offset); - WFIFOW(fd,0) = 0x163; - - for( i = 0; i < MAX_GUILDEXPULSION; i++ ) - { - struct guild_expulsion* e = &g->expulsion[i]; - - if( e->account_id > 0 ) - { - memcpy(WFIFOP(fd,4 + c*offset), e->name, NAME_LENGTH); -#if PACKETVER < 20100803 - memset(WFIFOP(fd,4 + c*offset+24), 0, NAME_LENGTH); // account name (not used for security reasons) - memcpy(WFIFOP(fd,4 + c*offset+48), e->mes, 40); -#else - memcpy(WFIFOP(fd,4 + c*offset+24), e->mes, 40); -#endif - c++; - } - } - WFIFOW(fd,2) = 4 + c*offset; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// 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; - uint8 buf[256]; - - 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 + 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); -} - - -/*========================================== - * Server tells client 'sd' that his guild skill 'skill_id' gone to level 'lv' - *------------------------------------------*/ -int clif_guild_skillup(struct map_session_data *sd,uint16 skill_id,int lv) -{// TODO: Merge with clif_skillup (same packet). - int fd; - - nullpo_ret(sd); - - fd=sd->fd; - WFIFOHEAD(fd,11); - WFIFOW(fd,0) = 0x10e; - WFIFOW(fd,2) = skill_id; - WFIFOW(fd,4) = lv; - WFIFOW(fd,6) = skill_get_sp(skill_id,lv); - WFIFOW(fd,8) = skill_get_range(skill_id,lv); - WFIFOB(fd,10) = 1; - WFIFOSET(fd,11); - return 0; -} - - -/// Request for guild alliance (ZC_REQ_ALLY_GUILD). -/// 0171 <inviter account id>.L <guild name>.24B -void clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x171)); - WFIFOW(fd,0)=0x171; - WFIFOL(fd,2)=account_id; - memcpy(WFIFOP(fd,6),name,NAME_LENGTH); - WFIFOSET(fd,packet_len(0x171)); -} - - -/// Notifies the client about the result of a alliance request (ZC_ACK_REQ_ALLY_GUILD). -/// 0173 <answer>.B -/// answer: -/// 0 = Already allied. -/// 1 = You rejected the offer. -/// 2 = You accepted the offer. -/// 3 = They have too any alliances. -/// 4 = You have too many alliances. -/// 5 = Alliances are disabled. -void clif_guild_allianceack(struct map_session_data *sd,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x173)); - WFIFOW(fd,0)=0x173; - WFIFOL(fd,2)=flag; - WFIFOSET(fd,packet_len(0x173)); -} - - -/// Notifies the client that a alliance or opposition has been removed (ZC_DELETE_RELATED_GUILD). -/// 0184 <other guild id>.L <relation>.L -/// relation: -/// 0 = Ally -/// 1 = Enemy -void clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - if (fd <= 0) - return; - WFIFOHEAD(fd,packet_len(0x184)); - WFIFOW(fd,0)=0x184; - WFIFOL(fd,2)=guild_id; - WFIFOL(fd,6)=flag; - WFIFOSET(fd,packet_len(0x184)); -} - - -/// Notifies the client about the result of a opposition request (ZC_ACK_REQ_HOSTILE_GUILD). -/// 0181 <result>.B -/// result: -/// 0 = Antagonist has been set. -/// 1 = Guild has too many Antagonists. -/// 2 = Already set as an Antagonist. -/// 3 = Antagonists are disabled. -void clif_guild_oppositionack(struct map_session_data *sd,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x181)); - WFIFOW(fd,0)=0x181; - WFIFOB(fd,2)=flag; - WFIFOSET(fd,packet_len(0x181)); -} - - -/// Adds alliance or opposition (ZC_ADD_RELATED_GUILD). -/// 0185 <relation>.L <guild id>.L <guild name>.24B -/* -void clif_guild_allianceadded(struct guild *g,int idx) -{ - unsigned char buf[64]; - WBUFW(buf,0)=0x185; - WBUFL(buf,2)=g->alliance[idx].opposition; - WBUFL(buf,6)=g->alliance[idx].guild_id; - memcpy(WBUFP(buf,10),g->alliance[idx].name,NAME_LENGTH); - clif_send(buf,packet_len(0x185),guild_getavailablesd(g),GUILD); -} -*/ - - -/// Notifies the client about the result of a guild break (ZC_ACK_DISORGANIZE_GUILD_RESULT). -/// 015e <reason>.L -/// 0 = success -/// 1 = invalid key (guild name, @see clif_parse_GuildBreak) -/// 2 = there are still members in the guild -void clif_guild_broken(struct map_session_data *sd,int flag) -{ - int fd; - - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x15e)); - WFIFOW(fd,0)=0x15e; - WFIFOL(fd,2)=flag; - WFIFOSET(fd,packet_len(0x15e)); -} - - -/// Displays emotion on an object (ZC_EMOTION). -/// 00c0 <id>.L <type>.B -/// type: -/// enum emotion_type -void clif_emotion(struct block_list *bl,int type) -{ - unsigned char buf[8]; - - nullpo_retv(bl); - - WBUFW(buf,0)=0xc0; - WBUFL(buf,2)=bl->id; - WBUFB(buf,6)=type; - clif_send(buf,packet_len(0xc0),bl,AREA); -} - - -/// Displays the contents of a talkiebox trap (ZC_TALKBOX_CHATCONTENTS). -/// 0191 <id>.L <contents>.80B -void clif_talkiebox(struct block_list* bl, const char* talkie) -{ - unsigned char buf[MESSAGE_SIZE+6]; - nullpo_retv(bl); - - WBUFW(buf,0) = 0x191; - WBUFL(buf,2) = bl->id; - safestrncpy((char*)WBUFP(buf,6),talkie,MESSAGE_SIZE); - clif_send(buf,packet_len(0x191),bl,AREA); -} - - -/// Displays wedding effect centered on an object (ZC_CONGRATULATION). -/// 01ea <id>.L -void clif_wedding_effect(struct block_list *bl) -{ - unsigned char buf[6]; - - nullpo_retv(bl); - - WBUFW(buf,0) = 0x1ea; - WBUFL(buf,2) = bl->id; - clif_send(buf, packet_len(0x1ea), bl, AREA); -} - - -/// Notifies the client of the name of the partner character (ZC_COUPLENAME). -/// 01e6 <partner name>.24B -void clif_callpartner(struct map_session_data *sd) -{ - unsigned char buf[26]; - const char *p; - - nullpo_retv(sd); - - WBUFW(buf,0) = 0x1e6; - - if( sd->status.partner_id ) - { - if( ( p = map_charid2nick(sd->status.partner_id) ) != NULL ) - { - memcpy(WBUFP(buf,2), p, NAME_LENGTH); - } - else - { - WBUFB(buf,2) = 0; - } - } - else - {// Send zero-length name if no partner, to initialize the client buffer. - WBUFB(buf,2) = 0; - } - - clif_send(buf, packet_len(0x1e6), &sd->bl, AREA); -} - - -/// Initiates the partner "taming" process [DracoRPG] (ZC_START_COUPLE). -/// 01e4 -/// This packet while still implemented by the client is no longer being officially used. -/* -void clif_marriage_process(struct map_session_data *sd) -{ - int fd; - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x1e4)); - WFIFOW(fd,0)=0x1e4; - WFIFOSET(fd,packet_len(0x1e4)); -} -*/ - - -/// Notice of divorce (ZC_DIVORCE). -/// 0205 <partner name>.24B -void clif_divorced(struct map_session_data* sd, const char* name) -{ - int fd; - nullpo_retv(sd); - - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x205)); - WFIFOW(fd,0)=0x205; - memcpy(WFIFOP(fd,2), name, NAME_LENGTH); - WFIFOSET(fd, packet_len(0x205)); -} - - -/// Marriage proposal (ZC_REQ_COUPLE). -/// 01e2 <account id>.L <char id>.L <char name>.24B -/// This packet while still implemented by the client is no longer being officially used. -/* -void clif_marriage_proposal(int fd, struct map_session_data *sd, struct map_session_data* ssd) -{ - nullpo_retv(sd); - - WFIFOHEAD(fd,packet_len(0x1e2)); - WFIFOW(fd,0) = 0x1e2; - WFIFOL(fd,2) = ssd->status.account_id; - WFIFOL(fd,6) = ssd->status.char_id; - safestrncpy((char*)WFIFOP(fd,10), ssd->status.name, NAME_LENGTH); - WFIFOSET(fd, packet_len(0x1e2)); -} -*/ - - -/*========================================== - * - *------------------------------------------*/ -void clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len) -{ - clif_disp_message(&sd->bl, mes, len, SELF); -} - -/*========================================== - * Displays a message using the guild-chat colors to the specified targets. [Skotlex] - *------------------------------------------*/ -void clif_disp_message(struct block_list* src, const char* mes, int len, enum send_target target) -{ - 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; - safestrncpy((char*)WBUFP(buf,4), mes, len+1); - clif_send(buf, WBUFW(buf,2), src, target); -} - - -/// Notifies the client about the result of a request to disconnect another player (ZC_ACK_DISCONNECT_CHARACTER). -/// 00cd <result>.L (unknown packet version or invalid information at packet_len_table) -/// 00cd <result>.B -/// result: -/// 0 = failure -/// 1 = success -void clif_GM_kickack(struct map_session_data *sd, int id) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0xcd)); - WFIFOW(fd,0) = 0xcd; - WFIFOB(fd,2) = id; // FIXME: this is not account id - WFIFOSET(fd, packet_len(0xcd)); -} - - -void clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd) -{ - int fd = tsd->fd; - - if( fd > 0 ) - clif_authfail_fd(fd, 15); - else - map_quit(tsd); - - if( sd ) - clif_GM_kickack(sd,tsd->status.account_id); -} - - -/// Displays various manner-related status messages (ZC_ACK_GIVE_MANNER_POINT). -/// 014a <result>.L -/// result: -/// 0 = "A manner point has been successfully aligned." -/// 1 = MP_FAILURE_EXHAUST -/// 2 = MP_FAILURE_ALREADY_GIVING -/// 3 = "Chat Block has been applied by GM due to your ill-mannerous action." -/// 4 = "Automated Chat Block has been applied due to Anti-Spam System." -/// 5 = "You got a good point from %s." -void clif_manner_message(struct map_session_data* sd, uint32 type) -{ - int fd; - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x14a)); - WFIFOW(fd,0) = 0x14a; - WFIFOL(fd,2) = type; - WFIFOSET(fd, packet_len(0x14a)); -} - - -/// Followup to 0x14a type 3/5, informs who did the manner adjustment action (ZC_NOTIFY_MANNER_POINT_GIVEN). -/// 014b <type>.B <GM name>.24B -/// type: -/// 0 = positive (unmute) -/// 1 = negative (mute) -void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, uint8 type) -{ - int fd; - nullpo_retv(sd); - nullpo_retv(tsd); - - fd = tsd->fd; - WFIFOHEAD(fd,packet_len(0x14b)); - WFIFOW(fd,0) = 0x14b; - WFIFOB(fd,2) = type; - safestrncpy((char*)WFIFOP(fd,3), sd->status.name, NAME_LENGTH); - WFIFOSET(fd, packet_len(0x14b)); -} - - -/// Notifies the client about the result of a request to allow/deny whispers from a player (ZC_SETTING_WHISPER_PC). -/// 00d1 <type>.B <result>.B -/// type: -/// 0 = /ex (deny) -/// 1 = /in (allow) -/// result: -/// 0 = success -/// 1 = failure -/// 2 = too many blocks -void clif_wisexin(struct map_session_data *sd,int type,int flag) -{ - int fd; - - nullpo_retv(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)); -} - -/// Notifies the client about the result of a request to allow/deny whispers from anyone (ZC_SETTING_WHISPER_STATE). -/// 00d2 <type>.B <result>.B -/// type: -/// 0 = /exall (deny) -/// 1 = /inall (allow) -/// result: -/// 0 = success -/// 1 = failure -void clif_wisall(struct map_session_data *sd,int type,int flag) -{ - int fd; - - nullpo_retv(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)); -} - - -/// Play a BGM! [Rikter/Yommy] (ZC_PLAY_NPC_BGM). -/// 07fe <bgm>.24B -void clif_playBGM(struct map_session_data* sd, const char* name) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x7fe)); - WFIFOW(fd,0) = 0x7fe; - safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH); - WFIFOSET(fd,packet_len(0x7fe)); -} - - -/// Plays/stops a wave sound (ZC_SOUND). -/// 01d3 <file name>.24B <act>.B <term>.L <npc id>.L -/// file name: -/// relative to data\wav -/// act: -/// 0 = play (once) -/// 1 = play (repeat, does not work) -/// 2 = stops all sound instances of file name (does not work) -/// term: -/// unknown purpose, only relevant to act = 1 -/// npc id: -/// The accustic direction of the sound is determined by the -/// relative position of the NPC to the player (3D sound). -void clif_soundeffect(struct map_session_data* sd, struct block_list* bl, const char* name, int type) -{ - int fd; - - nullpo_retv(sd); - nullpo_retv(bl); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x1d3)); - WFIFOW(fd,0) = 0x1d3; - safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH); - WFIFOB(fd,26) = type; - WFIFOL(fd,27) = 0; - WFIFOL(fd,31) = bl->id; - WFIFOSET(fd,packet_len(0x1d3)); -} - -void clif_soundeffectall(struct block_list* bl, const char* name, int type, enum send_target coverage) -{ - unsigned char buf[40]; - - nullpo_retv(bl); - - WBUFW(buf,0) = 0x1d3; - safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH); - WBUFB(buf,26) = type; - WBUFL(buf,27) = 0; - WBUFL(buf,31) = bl->id; - clif_send(buf, packet_len(0x1d3), bl, coverage); -} - - -/// Displays special effects (npcs, weather, etc) [Valaris] (ZC_NOTIFY_EFFECT2). -/// 01f3 <id>.L <effect id>.L -/// effect id: -/// @see doc/effect_list.txt -void clif_specialeffect(struct block_list* bl, int type, enum send_target target) -{ - unsigned char buf[24]; - - nullpo_retv(bl); - - memset(buf, 0, packet_len(0x1f3)); - - WBUFW(buf,0) = 0x1f3; - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = type; - - clif_send(buf, packet_len(0x1f3), bl, target); - - if (disguised(bl)) { - WBUFL(buf,2) = -bl->id; - clif_send(buf, packet_len(0x1f3), bl, SELF); - } -} - -void clif_specialeffect_single(struct block_list* bl, int type, int fd) -{ - WFIFOHEAD(fd,10); - WFIFOW(fd,0) = 0x1f3; - WFIFOL(fd,2) = bl->id; - WFIFOL(fd,6) = type; - WFIFOSET(fd,10); -} - - -/// Notifies clients of an special/visual effect that accepts an value (ZC_NOTIFY_EFFECT3). -/// 0284 <id>.L <effect id>.L <num data>.L -/// effect id: -/// @see doc/effect_list.txt -/// num data: -/// effect-dependent value -void clif_specialeffect_value(struct block_list* bl, int effect_id, int num, send_target target) -{ - uint8 buf[14]; - - WBUFW(buf,0) = 0x284; - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = effect_id; - WBUFL(buf,10) = num; - - clif_send(buf, packet_len(0x284), bl, target); - - if( disguised(bl) ) - { - WBUFL(buf,2) = -bl->id; - clif_send(buf, packet_len(0x284), bl, SELF); - } -} -// Modification of clif_messagecolor to send colored messages to players to chat log only (doesn't display overhead) -/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B -int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg) { - unsigned short msg_len = strlen(msg) + 1; - - WFIFOHEAD(sd->fd,msg_len + 12); - WFIFOW(sd->fd,0) = 0x2C1; - WFIFOW(sd->fd,2) = msg_len + 12; - WFIFOL(sd->fd,4) = 0; - WFIFOL(sd->fd,8) = color_table[color]; - safestrncpy((char*)WFIFOP(sd->fd,12), msg, msg_len); - clif_send(WFIFOP(sd->fd,0), WFIFOW(sd->fd,2), &sd->bl, SELF); - - return 0; -} - -/// Monster/NPC color chat [SnakeDrak] (ZC_NPC_CHAT). -/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B -void clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg) { - unsigned short msg_len = strlen(msg) + 1; - uint8 buf[256]; - color = (color & 0x0000FF) << 16 | (color & 0x00FF00) | (color & 0xFF0000) >> 16; // RGB to BGR - - nullpo_retv(bl); - - if( msg_len > sizeof(buf)-12 ) - { - ShowWarning("clif_messagecolor: Truncating too long message '%s' (len=%u).\n", msg, msg_len); - msg_len = sizeof(buf)-12; - } - - WBUFW(buf,0) = 0x2C1; - WBUFW(buf,2) = msg_len + 12; - WBUFL(buf,4) = bl->id; - WBUFL(buf,8) = color; - memcpy(WBUFP(buf,12), msg, msg_len); - - clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC); -} - -/// Public chat message [Valaris] (ZC_NOTIFY_CHAT). -/// 008d <packet len>.W <id>.L <message>.?B -void clif_message(struct block_list* bl, const char* msg) { - unsigned short msg_len = strlen(msg) + 1; - uint8 buf[256]; - nullpo_retv(bl); - - if( msg_len > sizeof(buf)-8 ) { - ShowWarning("clif_message: Truncating too long message '%s' (len=%u).\n", msg, msg_len); - msg_len = sizeof(buf)-8; - } - - WBUFW(buf,0) = 0x8d; - WBUFW(buf,2) = msg_len + 8; - WBUFL(buf,4) = bl->id; - safestrncpy((char*)WBUFP(buf,8), msg, msg_len); - - clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC); -} - -// refresh the client's screen, getting rid of any effects -void clif_refresh(struct map_session_data *sd) -{ - int i; - nullpo_retv(sd); - - clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y); - clif_inventorylist(sd); - if(pc_iscarton(sd)) { - clif_cartlist(sd); - clif_updatestatus(sd,SP_CARTINFO); - } - clif_updatestatus(sd,SP_WEIGHT); - clif_updatestatus(sd,SP_MAXWEIGHT); - clif_updatestatus(sd,SP_STR); - clif_updatestatus(sd,SP_AGI); - clif_updatestatus(sd,SP_VIT); - clif_updatestatus(sd,SP_INT); - clif_updatestatus(sd,SP_DEX); - clif_updatestatus(sd,SP_LUK); - if (sd->spiritball) - clif_spiritball_single(sd->fd, sd); - for(i = 1; i < 5; i++){ - if( sd->talisman[i] > 0 ) - clif_talisman_single(sd->fd, sd, i); - } - if (sd->vd.cloth_color) - clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF); - if(merc_is_hom_active(sd->hd)) - clif_send_homdata(sd,SP_ACK,0); - if( sd->md ) { - clif_mercenary_info(sd); - clif_mercenary_skillblock(sd); - } - if( sd->ed ) - clif_elemental_info(sd); - map_foreachinrange(clif_getareachar,&sd->bl,AREA_SIZE,BL_ALL,sd); - clif_weather_check(sd); - if( sd->chatID ) - chat_leavechat(sd,0); - if( sd->state.vending ) - clif_openvending(sd, sd->bl.id, sd->vending); - if( pc_issit(sd) ) - clif_sitting(&sd->bl); // FIXME: just send to self, not area - if( pc_isdead(sd) ) // When you refresh, resend the death packet. - clif_clearunit_single(sd->bl.id,CLR_DEAD,sd->fd); - else - clif_changed_dir(&sd->bl, SELF); - - // unlike vending, resuming buyingstore crashes the client. - buyingstore_close(sd); - - mail_clear(sd); -} - - -/// Updates the object's (bl) name on client. -/// 0095 <id>.L <char name>.24B (ZC_ACK_REQNAME) -/// 0195 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B (ZC_ACK_REQNAMEALL) -void clif_charnameack (int fd, struct block_list *bl) -{ - unsigned char buf[103]; - int cmd = 0x95, i, ps = -1; - - nullpo_retv(bl); - - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = bl->id; - - switch( bl->type ) - { - case BL_PC: - { - struct map_session_data *ssd = (struct map_session_data *)bl; - struct party_data *p = NULL; - struct guild *g = NULL; - - //Requesting your own "shadow" name. [Skotlex] - if (ssd->fd == fd && ssd->disguise) - WBUFL(buf,2) = -bl->id; - - if( ssd->fakename[0] ) - { - WBUFW(buf, 0) = cmd = 0x195; - memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH); - WBUFB(buf,30) = WBUFB(buf,54) = WBUFB(buf,78) = 0; - break; - } - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); - - if( ssd->status.party_id ) - { - p = party_search(ssd->status.party_id); - } - if( ssd->status.guild_id ) - { - if( ( g = guild_search(ssd->status.guild_id) ) != NULL ) - { - ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id); - if( i < g->max_member ) ps = g->member[i].position; - } - } - - if( !battle_config.display_party_name && g == NULL ) - {// do not display party unless the player is also in a guild - p = NULL; - } - - if (p == NULL && g == NULL) - break; - - WBUFW(buf, 0) = cmd = 0x195; - if (p) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; - - if (g && ps >= 0 && ps < MAX_GUILDPOSITION) - { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); - } else { //Assume no guild. - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; - } - } - break; - //[blackhole89] - case BL_HOM: - memcpy(WBUFP(buf,6), ((TBL_HOM*)bl)->homunculus.name, NAME_LENGTH); - break; - case BL_MER: - memcpy(WBUFP(buf,6), ((TBL_MER*)bl)->db->name, NAME_LENGTH); - break; - case BL_PET: - memcpy(WBUFP(buf,6), ((TBL_PET*)bl)->pet.name, NAME_LENGTH); - break; - case BL_NPC: - memcpy(WBUFP(buf,6), ((TBL_NPC*)bl)->name, NAME_LENGTH); - break; - case BL_MOB: - { - struct mob_data *md = (struct mob_data *)bl; - nullpo_retv(md); - - memcpy(WBUFP(buf,6), md->name, NAME_LENGTH); - if( md->guardian_data && md->guardian_data->guild_id ) - { - WBUFW(buf, 0) = cmd = 0x195; - WBUFB(buf,30) = 0; - memcpy(WBUFP(buf,54), md->guardian_data->guild_name, NAME_LENGTH); - memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH); - } - else if( battle_config.show_mob_info ) - { - char mobhp[50], *str_p = mobhp; - WBUFW(buf, 0) = cmd = 0x195; - if( battle_config.show_mob_info&4 ) - str_p += sprintf(str_p, "Lv. %d | ", md->level); - if( battle_config.show_mob_info&1 ) - str_p += sprintf(str_p, "HP: %u/%u | ", md->status.hp, md->status.max_hp); - if( battle_config.show_mob_info&2 ) - str_p += sprintf(str_p, "HP: %d%% | ", get_percentage(md->status.hp, md->status.max_hp)); - //Even thought mobhp ain't a name, we send it as one so the client - //can parse it. [Skotlex] - if( str_p != mobhp ) - { - *(str_p-3) = '\0'; //Remove trailing space + pipe. - memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH); - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; - } - } - } - break; - case BL_CHAT: //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex] -// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH); -// break; - return; - case BL_ELEM: - memcpy(WBUFP(buf,6), ((TBL_ELEM*)bl)->db->name, NAME_LENGTH); - break; - default: - ShowError("clif_charnameack: bad type %d(%d)\n", bl->type, bl->id); - return; - } - - // if no receipient specified just update nearby clients - if (fd == 0) - clif_send(buf, packet_len(cmd), bl, AREA); - else { - WFIFOHEAD(fd, packet_len(cmd)); - memcpy(WFIFOP(fd, 0), buf, packet_len(cmd)); - WFIFOSET(fd, packet_len(cmd)); - } -} - - -//Used to update when a char leaves a party/guild. [Skotlex] -//Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent. -void clif_charnameupdate (struct map_session_data *ssd) -{ - unsigned char buf[103]; - int cmd = 0x195, ps = -1, i; - struct party_data *p = NULL; - struct guild *g = NULL; - - nullpo_retv(ssd); - - if( ssd->fakename[0] ) - return; //No need to update as the party/guild was not displayed anyway. - - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = ssd->bl.id; - - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); - - if (!battle_config.display_party_name) { - if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL) - p = party_search(ssd->status.party_id); - }else{ - if (ssd->status.party_id > 0) - p = party_search(ssd->status.party_id); - } - - if( ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL ) - { - ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id); - if( i < g->max_member ) ps = g->member[i].position; - } - - if( p ) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; - - if( g && ps >= 0 && ps < MAX_GUILDPOSITION ) - { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); - } - else - { - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; - } - - // Update nearby clients - clif_send(buf, packet_len(cmd), &ssd->bl, AREA); -} - - -/// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP). -/// 01ff <id>.L <x>.W <y>.W -/// -/// Visually moves(instant) a character to x,y. The char moves even -/// when the target cell isn't walkable. If the char is sitting it -/// stays that way. -void clif_slide(struct block_list *bl, int x, int y) -{ - unsigned char buf[10]; - nullpo_retv(bl); - - WBUFW(buf, 0) = 0x01ff; - WBUFL(buf, 2) = bl->id; - WBUFW(buf, 6) = x; - WBUFW(buf, 8) = y; - clif_send(buf, packet_len(0x1ff), bl, AREA); - - if( disguised(bl) ) - { - WBUFL(buf,2) = -bl->id; - clif_send(buf, packet_len(0x1ff), bl, SELF); - } -} - - -/*------------------------------------------ - * @me command by lordalfa, rewritten implementation by Skotlex - *------------------------------------------*/ -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 > sizeof(buf)-8) { - ShowError("clif_disp_overhead: Message too long (length %d)\n", len_mes); - 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; - 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; - safestrncpy((char*)WBUFP(buf,4), mes, len_mes); - clif_send(buf, WBUFW(buf,2), &sd->bl, SELF); -} - -/*========================== - * Minimap fix [Kevin] - * Remove dot from minimap - *--------------------------*/ -void clif_party_xy_remove(struct map_session_data *sd) -{ - unsigned char buf[16]; - nullpo_retv(sd); - WBUFW(buf,0)=0x107; - WBUFL(buf,2)=sd->status.account_id; - WBUFW(buf,6)=-1; - WBUFW(buf,8)=-1; - clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS); -} - - -/// Displays a skill message (thanks to Rayce) (ZC_SKILLMSG). -/// 0215 <msg id>.L -/// msg id: -/// 0x15 = End all negative status (PA_GOSPEL) -/// 0x16 = Immunity to all status (PA_GOSPEL) -/// 0x17 = MaxHP +100% (PA_GOSPEL) -/// 0x18 = MaxSP +100% (PA_GOSPEL) -/// 0x19 = All stats +20 (PA_GOSPEL) -/// 0x1c = Enchant weapon with Holy element (PA_GOSPEL) -/// 0x1d = Enchant armor with Holy element (PA_GOSPEL) -/// 0x1e = DEF +25% (PA_GOSPEL) -/// 0x1f = ATK +100% (PA_GOSPEL) -/// 0x20 = HIT/Flee +50 (PA_GOSPEL) -/// 0x28 = Full strip failed because of coating (ST_FULLSTRIP) -/// ? = nothing -void clif_gospel_info(struct map_session_data *sd, int type) -{ - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x215)); - WFIFOW(fd,0)=0x215; - WFIFOL(fd,2)=type; - WFIFOSET(fd, packet_len(0x215)); - -} - - -/// Multi-purpose mission information packet (ZC_STARSKILL). -/// 020e <mapname>.24B <monster_id>.L <star>.B <result>.B -/// result: -/// 0 = Star Gladiator %s has designed <mapname>'s as the %s. -/// star: -/// 0 = Place of the Sun -/// 1 = Place of the Moon -/// 2 = Place of the Stars -/// 1 = Star Gladiator %s's %s: <mapname> -/// star: -/// 0 = Place of the Sun -/// 1 = Place of the Moon -/// 2 = Place of the Stars -/// 10 = Star Gladiator %s has designed <mapname>'s as the %s. -/// star: -/// 0 = Target of the Sun -/// 1 = Target of the Moon -/// 2 = Target of the Stars -/// 11 = Star Gladiator %s's %s: <mapname used as monster name> -/// star: -/// 0 = Monster of the Sun -/// 1 = Monster of the Moon -/// 2 = Monster of the Stars -/// 20 = [TaeKwon Mission] Target Monster : <mapname used as monster name> (<star>%) -/// 21 = [Taming Mission] Target Monster : <mapname used as monster name> -/// 22 = [Collector Rank] Target Item : <monster_id used as item id> -/// 30 = [Sun, Moon and Stars Angel] Designed places and monsters have been reset. -/// 40 = Target HP : <monster_id used as HP> -void clif_starskill(struct map_session_data* sd, const char* mapname, int monster_id, unsigned char star, unsigned char result) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x20e)); - WFIFOW(fd,0) = 0x20e; - safestrncpy((char*)WFIFOP(fd,2), mapname, NAME_LENGTH); - WFIFOL(fd,26) = monster_id; - WFIFOB(fd,30) = star; - WFIFOB(fd,31) = result; - WFIFOSET(fd,packet_len(0x20e)); -} - -/*========================================== - * Info about Star Glaldiator save map [Komurka] - * type: 1: Information, 0: Map registered - *------------------------------------------*/ -void clif_feel_info(struct map_session_data* sd, unsigned char feel_level, unsigned char type) -{ - char mapname[MAP_NAME_LENGTH_EXT]; - - mapindex_getmapname_ext(mapindex_id2name(sd->feel_map[feel_level].index), mapname); - clif_starskill(sd, mapname, 0, feel_level, type ? 1 : 0); -} - -/*========================================== - * Info about Star Glaldiator hate mob [Komurka] - * type: 1: Register mob, 0: Information. - *------------------------------------------*/ -void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type) -{ - if( pcdb_checkid(class_) ) - { - clif_starskill(sd, job_name(class_), class_, hate_level, type ? 10 : 11); - } - else if( mobdb_checkid(class_) ) - { - clif_starskill(sd, mob_db(class_)->jname, class_, hate_level, type ? 10 : 11); - } - else - { - ShowWarning("clif_hate_info: Received invalid class %d for this packet (char_id=%d, hate_level=%u, type=%u).\n", class_, sd->status.char_id, (unsigned int)hate_level, (unsigned int)type); - } -} - -/*========================================== - * Info about TaeKwon Do TK_MISSION mob [Skotlex] - *------------------------------------------*/ -void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress) -{ - clif_starskill(sd, mob_db(mob_id)->jname, mob_id, progress, 20); -} - -/*========================================== - * Feel/Hate reset (thanks to Rayce) [Skotlex] - *------------------------------------------*/ -void clif_feel_hate_reset(struct map_session_data *sd) -{ - clif_starskill(sd, "", 0, 0, 30); -} - - -/// Equip window (un)tick ack (ZC_CONFIG). -/// 02d9 <type>.L <value>.L -/// type: -/// 0 = open equip window -/// value: -/// 0 = disabled -/// 1 = enabled -void clif_equiptickack(struct map_session_data* sd, int flag) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x2d9)); - WFIFOW(fd, 0) = 0x2d9; - WFIFOL(fd, 2) = 0; - WFIFOL(fd, 6) = flag; - WFIFOSET(fd, packet_len(0x2d9)); -} - - -/// The player's 'view equip' state, sent during login (ZC_CONFIG_NOTIFY). -/// 02da <open equip window>.B -/// open equip window: -/// 0 = disabled -/// 1 = enabled -void clif_equipcheckbox(struct map_session_data* sd) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x2da)); - WFIFOW(fd, 0) = 0x2da; - WFIFOB(fd, 2) = (sd->status.show_equip ? 1 : 0); - WFIFOSET(fd, packet_len(0x2da)); -} - - -/// Sends info about a player's equipped items. -/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <up-viewid>.W <mid-viewid>.W <low-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.26B* (ZC_EQUIPWIN_MICROSCOPE) -/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE, PACKETVER >= 20100629) -/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20101124) -/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20110111) -void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* tsd) -{ - uint8* buf; - int i, n, fd, offset = 0; -#if PACKETVER < 20100629 - const int s = 26; -#else - const int s = 28; -#endif - nullpo_retv(sd); - nullpo_retv(tsd); - fd = sd->fd; - - WFIFOHEAD(fd, MAX_INVENTORY * s + 43); - buf = WFIFOP(fd,0); - -#if PACKETVER < 20101124 - WBUFW(buf, 0) = 0x2d7; -#else - WBUFW(buf, 0) = 0x859; -#endif - safestrncpy((char*)WBUFP(buf, 4), tsd->status.name, NAME_LENGTH); - WBUFW(buf,28) = tsd->status.class_; - WBUFW(buf,30) = tsd->vd.hair_style; - WBUFW(buf,32) = tsd->vd.head_bottom; - WBUFW(buf,34) = tsd->vd.head_mid; - WBUFW(buf,36) = tsd->vd.head_top; -#if PACKETVER >= 20110111 - WBUFW(buf,38) = tsd->vd.robe; - offset+= 2; - buf = WBUFP(buf,2); -#endif - WBUFW(buf,38) = tsd->vd.hair_color; - WBUFW(buf,40) = tsd->vd.cloth_color; - WBUFB(buf,42) = tsd->vd.sex; - - for(i=0,n=0; i < MAX_INVENTORY; i++) - { - if (tsd->status.inventory[i].nameid <= 0 || tsd->inventory_data[i] == NULL) // Item doesn't exist - continue; - if (!itemdb_isequip2(tsd->inventory_data[i])) // Is not equippable - continue; - - // Inventory position - WBUFW(buf, n*s+43) = i + 2; - // Add refine, identify flag, element, etc. - clif_item_sub(WBUFP(buf,0), n*s+45, &tsd->status.inventory[i], tsd->inventory_data[i], pc_equippoint(tsd, i)); - // Add cards - clif_addcards(WBUFP(buf, n*s+55), &tsd->status.inventory[i]); - // Expiration date stuff, if all of those are set to 0 then the client doesn't show anything related (6 bytes) - WBUFL(buf, n*s+63) = tsd->status.inventory[i].expire_time; - WBUFW(buf, n*s+67) = 0; -#if PACKETVER >= 20100629 - if (tsd->inventory_data[i]->equip&EQP_VISIBLE) - WBUFW(buf, n*s+69) = tsd->inventory_data[i]->look; - else - WBUFW(buf, n*s+69) = 0; -#endif - n++; - } - - WFIFOW(fd, 2) = 43+offset+n*s; // Set length - WFIFOSET(fd, WFIFOW(fd, 2)); -} - - -/// Display msgstringtable.txt string (ZC_MSG). -/// 0291 <message>.W -void clif_msg(struct map_session_data* sd, unsigned short id) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x291)); - WFIFOW(fd, 0) = 0x291; - WFIFOW(fd, 2) = id; // zero-based msgstringtable.txt index - WFIFOSET(fd, packet_len(0x291)); -} - - -/// Display msgstringtable.txt string and fill in a valid for %d format (ZC_MSG_VALUE). -/// 0x7e2 <message>.W <value>.L -void clif_msg_value(struct map_session_data* sd, unsigned short id, int value) -{ - int fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x7e2)); - WFIFOW(fd,0) = 0x7e2; - WFIFOW(fd,2) = id; - WFIFOL(fd,4) = value; - WFIFOSET(fd, packet_len(0x7e2)); -} - - -/// Displays msgstringtable.txt string, prefixed with a skill name. (ZC_MSG_SKILL). -/// 07e6 <skill id>.W <msg id>.L -/// -/// NOTE: Message has following format and is printed in color 0xCDCDFF (purple): -/// "[SkillName] Message" -void clif_msg_skill(struct map_session_data* sd, uint16 skill_id, int msg_id) -{ - int fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x7e6)); - WFIFOW(fd,0) = 0x7e6; - WFIFOW(fd,2) = skill_id; - WFIFOL(fd,4) = msg_id; - WFIFOSET(fd, packet_len(0x7e6)); -} - - -/// View player equip request denied -void clif_viewequip_fail(struct map_session_data* sd) -{ - clif_msg(sd, 0x54d); -} - - -/// Validates one global/guild/party/whisper message packet and tries to recognize its components. -/// Returns true if the packet was parsed successfully. -/// Formats: 0 - <packet id>.w <packet len>.w (<name> : <message>).?B 00 -/// 1 - <packet id>.w <packet len>.w <name>.24B <message>.?B 00 -static bool clif_process_message(struct map_session_data* sd, int format, char** name_, int* namelen_, char** message_, int* messagelen_) -{ - char *text, *name, *message; - unsigned int packetlen, textlen, namelen, messagelen; - int fd = sd->fd; - - *name_ = NULL; - *namelen_ = 0; - *message_ = NULL; - *messagelen_ = 0; - - packetlen = RFIFOW(fd,2); - // basic structure checks - if( packetlen < 4 + 1 ) - { // 4-byte header and at least an empty string is expected - ShowWarning("clif_process_message: Received malformed packet from player '%s' (no message data)!\n", sd->status.name); - return false; - } - - text = (char*)RFIFOP(fd,4); - textlen = packetlen - 4; - - // process <name> part of the packet - if( format == 0 ) - {// name and message are separated by ' : ' - // validate name - name = text; - namelen = strnlen(sd->status.name, NAME_LENGTH-1); // name length (w/o zero byte) - - if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name - name[namelen] != ' ' || name[namelen+1] != ':' || name[namelen+2] != ' ' ) // followed by ' : ' - { - //Hacked message, or infamous "client desynch" issue where they pick one char while loading another. - ShowWarning("clif_process_message: Player '%s' sent a message using an incorrect name! Forcing a relog...\n", sd->status.name); - set_eof(fd); // Just kick them out to correct it. - return false; - } - - message = name + namelen + 3; - messagelen = textlen - namelen - 3; // this should be the message length (w/ zero byte included) - } - else - {// name has fixed width - if( textlen < NAME_LENGTH + 1 ) - { - ShowWarning("clif_process_message: Received malformed packet from player '%s' (packet length is incorrect)!\n", sd->status.name); - return false; - } - - // validate name - name = text; - namelen = strnlen(name, NAME_LENGTH-1); // name length (w/o zero byte) - - if( name[namelen] != '\0' ) - { // only restriction is that the name must be zero-terminated - ShowWarning("clif_process_message: Player '%s' sent an unterminated name!\n", sd->status.name); - return false; - } - - message = name + NAME_LENGTH; - messagelen = textlen - NAME_LENGTH; // this should be the message length (w/ zero byte included) - } - - if( messagelen != strnlen(message, messagelen)+1 ) - { // the declared length must match real length - ShowWarning("clif_process_message: Received malformed packet from player '%s' (length is incorrect)!\n", sd->status.name); - return false; - } - // verify <message> part of the packet - if( message[messagelen-1] != '\0' ) - { // message must be zero-terminated - ShowWarning("clif_process_message: Player '%s' sent an unterminated message string!\n", sd->status.name); - return false; - } - if( messagelen > CHAT_SIZE_MAX-1 ) - { // messages mustn't be too long - // Normally you can only enter CHATBOX_SIZE-1 letters into the chat box, but Frost Joke / Dazzler's text can be longer. - // Also, the physical size of strings that use multibyte encoding can go multiple times over the chatbox capacity. - // Neither the official client nor server place any restriction on the length of the data in the packet, - // but we'll only allow reasonably long strings here. This also makes sure that they fit into the `chatlog` table. - ShowWarning("clif_process_message: Player '%s' sent a message too long ('%.*s')!\n", sd->status.name, CHAT_SIZE_MAX-1, message); - return false; - } - - *name_ = name; - *namelen_ = namelen; - *message_ = message; - *messagelen_ = messagelen; - return true; -} - -// --------------------- -// clif_guess_PacketVer -// --------------------- -// Parses a WantToConnection packet to try to identify which is the packet version used. [Skotlex] -// error codes: -// 0 - Success -// 1 - Unknown packet_ver -// 2 - Invalid account_id -// 3 - Invalid char_id -// 4 - Invalid login_id1 (reserved) -// 5 - Invalid client_tick (reserved) -// 6 - Invalid sex -// Only the first 'invalid' error that appears is used. -static int clif_guess_PacketVer(int fd, int get_previous, int *error) -{ - static int err = 1; - static int packet_ver = -1; - int cmd, packet_len, value; //Value is used to temporarily store account/char_id/sex - - if (get_previous) - {//For quick reruns, since the normal code flow is to fetch this once to identify the packet version, then again in the wanttoconnect function. [Skotlex] - if( error ) - *error = err; - return packet_ver; - } - - //By default, start searching on the default one. - err = 1; - packet_ver = clif_config.packet_db_ver; - cmd = RFIFOW(fd,0); - packet_len = RFIFOREST(fd); - -#define SET_ERROR(n) \ - if( err == 1 )\ - err = n;\ -//define SET_ERROR - - // FIXME: If the packet is not received at once, this will FAIL. - // Figure out, when it happens, that only part of the packet is - // received, or fix the function to be able to deal with that - // case. -#define CHECK_PACKET_VER() \ - if( cmd != clif_config.connect_cmd[packet_ver] || packet_len != packet_db[packet_ver][cmd].len )\ - ;/* not wanttoconnection or wrong length */\ - else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) < START_ACCOUNT_NUM || value > END_ACCOUNT_NUM )\ - { SET_ERROR(2); }/* invalid account_id */\ - else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) <= 0 )\ - { SET_ERROR(3); }/* invalid char_id */\ - /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) - don't care about login_id1 */\ - /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]) - don't care about client_tick */\ - else if( (value=(int)RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) != 0 && value != 1 )\ - { SET_ERROR(6); }/* invalid sex */\ - else\ - {\ - err = 0;\ - if( error )\ - *error = 0;\ - return packet_ver;\ - }\ -//define CHECK_PACKET_VER - - CHECK_PACKET_VER();//Default packet version found. - - for (packet_ver = MAX_PACKET_VER; packet_ver > 0; packet_ver--) - { //Start guessing the version, giving priority to the newer ones. [Skotlex] - CHECK_PACKET_VER(); - } - if( error ) - *error = err; - packet_ver = -1; - return -1; -#undef SET_ERROR -#undef CHECK_PACKET_VER -} - -// ------------ -// clif_parse_* -// ------------ -// Parses incoming (player) connection - - -/// Request to connect to map-server. -/// 0072 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER) -/// 0436 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_WantToConnection(int fd, TBL_PC* sd) -{ - struct block_list* bl; - struct auth_node* node; - int cmd, account_id, char_id, login_id1, sex; - unsigned int client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex] - int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 (by [Yor]) - - if (sd) { - ShowError("clif_parse_WantToConnection : invalid request (character already logged in)\n"); - return; - } - - // Only valid packet version get here - packet_ver = clif_guess_PacketVer(fd, 1, NULL); - - cmd = RFIFOW(fd,0); - account_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0]); - char_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1]); - login_id1 = RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]); - client_tick = RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]); - sex = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4]); - - if( packet_ver < 5 || // reject really old client versions - (packet_ver <= 9 && (battle_config.packet_ver_flag & 1) == 0) || // older than 6sept04 - (packet_ver > 9 && (battle_config.packet_ver_flag & 1<<(packet_ver-9)) == 0)) // version not allowed - {// packet version rejected - ShowInfo("Rejected connection attempt, forbidden packet version (AID/CID: '"CL_WHITE"%d/%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%s"CL_RESET"').\n", account_id, char_id, packet_ver, ip2str(session[fd]->client_addr, NULL)); - WFIFOHEAD(fd,packet_len(0x6a)); - WFIFOW(fd,0) = 0x6a; - WFIFOB(fd,2) = 5; // Your Game's EXE file is not the latest version - WFIFOSET(fd,packet_len(0x6a)); - set_eof(fd); - return; - } - - if( runflag != MAPSERVER_ST_RUNNING ) - {// not allowed - clif_authfail_fd(fd,1);// server closed - return; - } - - //Check for double login. - bl = map_id2bl(account_id); - if(bl && bl->type != BL_PC) { - ShowError("clif_parse_WantToConnection: a non-player object already has id %d, please increase the starting account number\n", account_id); - WFIFOHEAD(fd,packet_len(0x6a)); - WFIFOW(fd,0) = 0x6a; - WFIFOB(fd,2) = 3; // Rejected by server - WFIFOSET(fd,packet_len(0x6a)); - set_eof(fd); - return; - } - - if (bl || - ((node=chrif_search(account_id)) && //An already existing node is valid only if it is for this login. - !(node->account_id == account_id && node->char_id == char_id && node->state == ST_LOGIN))) - { - clif_authfail_fd(fd, 8); //Still recognizes last connection - return; - } - - CREATE(sd, TBL_PC, 1); - sd->fd = fd; - sd->packet_ver = packet_ver; - session[fd]->session_data = sd; - - pc_setnewpc(sd, account_id, char_id, login_id1, client_tick, sex, fd); - -#if PACKETVER < 20070521 - WFIFOHEAD(fd,4); - WFIFOL(fd,0) = sd->bl.id; - WFIFOSET(fd,4); -#else - WFIFOHEAD(fd,packet_len(0x283)); - WFIFOW(fd,0) = 0x283; - WFIFOL(fd,2) = sd->bl.id; - WFIFOSET(fd,packet_len(0x283)); -#endif - - chrif_authreq(sd); -} - - -/// Notification from the client, that it has finished map loading and is about to display player's character (CZ_NOTIFY_ACTORINIT). -/// 007d -void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) -{ - if(sd->bl.prev != NULL) - return; - - if (!sd->state.active) - { //Character loading is not complete yet! - //Let pc_reg_received reinvoke this when ready. - sd->state.connect_new = 0; - return; - } - - if (sd->state.rewarp) - { //Rewarp player. - sd->state.rewarp = 0; - clif_changemap(sd, sd->mapindex, sd->bl.x, sd->bl.y); - return; - } - - sd->state.warping = 0; - - // look -#if PACKETVER < 4 - clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); - clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield); -#else - clif_changelook(&sd->bl,LOOK_WEAPON,0); -#endif - - if(sd->vd.cloth_color) - clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF); - - // item - clif_inventorylist(sd); // inventory list first, otherwise deleted items in pc_checkitem show up as 'unknown item' - pc_checkitem(sd); - - // cart - if(pc_iscarton(sd)) { - clif_cartlist(sd); - clif_updatestatus(sd,SP_CARTINFO); - } - - // weight - clif_updatestatus(sd,SP_WEIGHT); - clif_updatestatus(sd,SP_MAXWEIGHT); - - // guild - // (needs to go before clif_spawn() to show guild emblems correctly) - if(sd->status.guild_id) - guild_send_memberinfoshort(sd,1); - - if(battle_config.pc_invincible_time > 0) { - if(map_flag_gvg(sd->bl.m)) - pc_setinvincibletimer(sd,battle_config.pc_invincible_time<<1); - else - pc_setinvincibletimer(sd,battle_config.pc_invincible_time); - } - - if( map[sd->bl.m].users++ == 0 && battle_config.dynamic_mobs ) - map_spawnmobs(sd->bl.m); - if( !(sd->sc.option&OPTION_INVISIBLE) ) - {// increment the number of pvp players on the map - map[sd->bl.m].users_pvp++; - } - if( map[sd->bl.m].instance_id ) - { - instance[map[sd->bl.m].instance_id].users++; - instance_check_idle(map[sd->bl.m].instance_id); - } - sd->state.debug_remove_map = 0; // temporary state to track double remove_map's [FlavioJS] - - // reset the callshop flag if the player changes map - sd->state.callshop = 0; - - map_addblock(&sd->bl); - clif_spawn(&sd->bl); - - // Party - // (needs to go after clif_spawn() to show hp bars correctly) - if(sd->status.party_id) { - party_send_movemap(sd); - clif_party_hp(sd); // Show hp after displacement [LuzZza] - } - - if( sd->bg_id ) clif_bg_hp(sd); // BattleGround System - - if(map[sd->bl.m].flag.pvp && !(sd->sc.option&OPTION_INVISIBLE)) { - if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris] - if (!map[sd->bl.m].flag.pvp_nocalcrank) - sd->pvp_timer = add_timer(gettick()+200, pc_calc_pvprank_timer, sd->bl.id, 0); - sd->pvp_rank = 0; - sd->pvp_lastusers = 0; - sd->pvp_point = 5; - sd->pvp_won = 0; - sd->pvp_lost = 0; - } - clif_map_property(sd, MAPPROPERTY_FREEPVPZONE); - } else - // set flag, if it's a duel [LuzZza] - if(sd->duel_group) - clif_map_property(sd, MAPPROPERTY_FREEPVPZONE); - - if (map[sd->bl.m].flag.gvg_dungeon) - clif_map_property(sd, MAPPROPERTY_FREEPVPZONE); //TODO: Figure out the real packet to send here. - - if( map_flag_gvg(sd->bl.m) ) - clif_map_property(sd, MAPPROPERTY_AGITZONE); - - // info about nearby objects - // must use foreachinarea (CIRCULAR_AREA interferes with foreachinrange) - map_foreachinarea(clif_getareachar, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_ALL, sd); - - // pet - if( sd->pd ) - { - if( battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m) ) - { //Return the pet to egg. [Skotlex] - clif_displaymessage(sd->fd, msg_txt(666)); - pet_menu(sd, 3); //Option 3 is return to egg. - } - else - { - map_addblock(&sd->pd->bl); - clif_spawn(&sd->pd->bl); - clif_send_petdata(sd,sd->pd,0,0); - clif_send_petstatus(sd); -// skill_unit_move(&sd->pd->bl,gettick(),1); - } - } - - //homunculus [blackhole89] - if( merc_is_hom_active(sd->hd) ) - { - map_addblock(&sd->hd->bl); - clif_spawn(&sd->hd->bl); - clif_send_homdata(sd,SP_ACK,0); - clif_hominfo(sd,sd->hd,1); - clif_hominfo(sd,sd->hd,0); //for some reason, at least older clients want this sent twice - clif_homskillinfoblock(sd); - if( battle_config.hom_setting&0x8 ) - status_calc_bl(&sd->hd->bl, SCB_SPEED); //Homunc mimic their master's speed on each map change - if( !(battle_config.hom_setting&0x2) ) - skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately - } - - if( sd->md ) { - map_addblock(&sd->md->bl); - clif_spawn(&sd->md->bl); - clif_mercenary_info(sd); - clif_mercenary_skillblock(sd); - status_calc_bl(&sd->md->bl, SCB_SPEED); // Mercenary mimic their master's speed on each map change - } - - if( sd->ed ) { - map_addblock(&sd->ed->bl); - clif_spawn(&sd->ed->bl); - clif_elemental_info(sd); - clif_elemental_updatestatus(sd,SP_HP); - clif_hpmeter_single(sd->fd,sd->ed->bl.id,sd->ed->battle_status.hp,sd->ed->battle_status.max_hp); - clif_elemental_updatestatus(sd,SP_SP); - status_calc_bl(&sd->ed->bl, SCB_SPEED); //Elemental mimic their master's speed on each map change - } - - if(sd->state.connect_new) { - int lv; - sd->state.connect_new = 0; - clif_skillinfoblock(sd); - clif_hotkeys_send(sd); - clif_updatestatus(sd,SP_BASEEXP); - clif_updatestatus(sd,SP_NEXTBASEEXP); - clif_updatestatus(sd,SP_JOBEXP); - clif_updatestatus(sd,SP_NEXTJOBEXP); - clif_updatestatus(sd,SP_SKILLPOINT); - clif_initialstatus(sd); - - if (sd->sc.option&OPTION_FALCON) - clif_status_load(&sd->bl, SI_FALCON, 1); - - if (sd->sc.option&OPTION_RIDING) - clif_status_load(&sd->bl, SI_RIDING, 1); - else if (sd->sc.option&OPTION_WUGRIDER) - clif_status_load(&sd->bl, SI_WUGRIDER, 1); - - if(sd->status.manner < 0) - sc_start(&sd->bl,SC_NOCHAT,100,0,0); - - //Auron reported that This skill only triggers when you logon on the map o.O [Skotlex] - if ((lv = pc_checkskill(sd,SG_KNOWLEDGE)) > 0) { - if(sd->bl.m == sd->feel_map[0].m - || sd->bl.m == sd->feel_map[1].m - || sd->bl.m == sd->feel_map[2].m) - sc_start(&sd->bl, SC_KNOWLEDGE, 100, lv, skill_get_time(SG_KNOWLEDGE, lv)); - } - - if(sd->pd && sd->pd->pet.intimate > 900) - clif_pet_emotion(sd->pd,(sd->pd->pet.class_ - 100)*100 + 50 + pet_hungry_val(sd->pd)); - - if(merc_is_hom_active(sd->hd)) - merc_hom_init_timers(sd->hd); - - if (night_flag && map[sd->bl.m].flag.nightenabled) { - sd->state.night = 1; - clif_status_load(&sd->bl, SI_NIGHT, 1); - } - - // Notify everyone that this char logged in [Skotlex]. - map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1); - - //Login Event - npc_script_event(sd, NPCE_LOGIN); - } else { - //For some reason the client "loses" these on warp/map-change. - clif_updatestatus(sd,SP_STR); - clif_updatestatus(sd,SP_AGI); - clif_updatestatus(sd,SP_VIT); - clif_updatestatus(sd,SP_INT); - clif_updatestatus(sd,SP_DEX); - clif_updatestatus(sd,SP_LUK); - - // abort currently running script - sd->state.using_fake_npc = 0; - sd->state.menu_or_input = 0; - sd->npc_menu = 0; - - if(sd->npc_id) - npc_event_dequeue(sd); - } - - if( sd->state.changemap ) - {// restore information that gets lost on map-change -#if PACKETVER >= 20070918 - clif_partyinvitationstate(sd); - clif_equipcheckbox(sd); -#endif - if( (battle_config.bg_flee_penalty != 100 || battle_config.gvg_flee_penalty != 100) && - (map_flag_gvg(sd->state.pmap) || map_flag_gvg(sd->bl.m) || map[sd->state.pmap].flag.battleground || map[sd->bl.m].flag.battleground) ) - status_calc_bl(&sd->bl, SCB_FLEE); //Refresh flee penalty - - if( night_flag && map[sd->bl.m].flag.nightenabled ) - { //Display night. - if( !sd->state.night ) - { - sd->state.night = 1; - clif_status_load(&sd->bl, SI_NIGHT, 1); - } - } - else if( sd->state.night ) - { //Clear night display. - sd->state.night = 0; - clif_status_load(&sd->bl, SI_NIGHT, 0); - } - - if( map[sd->bl.m].flag.battleground ) - { - clif_map_type(sd, MAPTYPE_BATTLEFIELD); // Battleground Mode - if( map[sd->bl.m].flag.battleground == 2 ) - clif_bg_updatescore_single(sd); - } - - if( map[sd->bl.m].flag.allowks && !map_flag_ks(sd->bl.m) ) - { - char output[128]; - sprintf(output, "[ Kill Steal Protection Disable. KS is allowed in this map ]"); - clif_broadcast(&sd->bl, output, strlen(output) + 1, 0x10, SELF); - } - - map_iwall_get(sd); // Updates Walls Info on this Map to Client - sd->state.changemap = false; - } - - mail_clear(sd); - - /* Guild Aura Init */ - if( sd->state.gmaster_flag ) { - guild_guildaura_refresh(sd,GD_LEADERSHIP,guild_checkskill(sd->state.gmaster_flag,GD_LEADERSHIP)); - guild_guildaura_refresh(sd,GD_GLORYWOUNDS,guild_checkskill(sd->state.gmaster_flag,GD_GLORYWOUNDS)); - guild_guildaura_refresh(sd,GD_SOULCOLD,guild_checkskill(sd->state.gmaster_flag,GD_SOULCOLD)); - guild_guildaura_refresh(sd,GD_HAWKEYES,guild_checkskill(sd->state.gmaster_flag,GD_HAWKEYES)); - } - - if( sd->state.vending ) { /* show we have a vending */ - clif_openvending(sd,sd->bl.id,sd->vending); - clif_showvendingboard(&sd->bl,sd->message,0); - } - - if(map[sd->bl.m].flag.loadevent) // Lance - npc_script_event(sd, NPCE_LOADMAP); - - if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) - clif_status_load(&sd->bl, SI_DEVIL, 1); //blindness [Komurka] - - if (sd->sc.opt2) //Client loses these on warp. - clif_changeoption(&sd->bl); - - clif_weather_check(sd); - - // For automatic triggering of NPCs after map loading (so you don't need to walk 1 step first) - if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNPC)) - npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y); - else - sd->areanpc_id = 0; - - /* it broke at some point (e.g. during a crash), so we make it visibly dead again. */ - if( !sd->status.hp && !pc_isdead(sd) && status_isdead(&sd->bl) ) - pc_setdead(sd); - - // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris] - if(pc_isdead(sd)) - clif_clearunit_area(&sd->bl, CLR_DEAD); - else { - skill_usave_trigger(sd); - clif_changed_dir(&sd->bl, SELF); - } - -// Trigger skill effects if you appear standing on them - if(!battle_config.pc_invincible_time) - skill_unit_move(&sd->bl,gettick(),1); -} - - -/// Server's tick (ZC_NOTIFY_TIME). -/// 007f <time>.L -void clif_notify_time(struct map_session_data* sd, unsigned long time) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x7f)); - WFIFOW(fd,0) = 0x7f; - WFIFOL(fd,2) = time; - WFIFOSET(fd,packet_len(0x7f)); -} - - -/// Request for server's tick. -/// 007e <client tick>.L (CZ_REQUEST_TIME) -/// 0360 <client tick>.L (CZ_REQUEST_TIME2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_TickSend(int fd, struct map_session_data *sd) -{ - sd->client_tick = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - - clif_notify_time(sd, gettick()); -} - - -/// Sends hotkey bar. -/// 02b9 { <is skill>.B <id>.L <count>.W }*27 (ZC_SHORTCUT_KEY_LIST) -/// 07d9 { <is skill>.B <id>.L <count>.W }*36 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090603) -/// 07d9 { <is skill>.B <id>.L <count>.W }*38 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090617) -void clif_hotkeys_send(struct map_session_data *sd) { -#ifdef HOTKEY_SAVING - const int fd = sd->fd; - int i; -#if PACKETVER < 20090603 - const int cmd = 0x2b9; -#else - const int cmd = 0x7d9; -#endif - if (!fd) return; - WFIFOHEAD(fd, 2+MAX_HOTKEYS*7); - WFIFOW(fd, 0) = cmd; - for(i = 0; i < MAX_HOTKEYS; i++) { - WFIFOB(fd, 2 + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill - WFIFOL(fd, 2 + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID - WFIFOW(fd, 2 + 5 + i * 7) = sd->status.hotkeys[i].lv; // skill level - } - WFIFOSET(fd, packet_len(cmd)); -#endif -} - - -/// Request to update a position on the hotkey bar (CZ_SHORTCUT_KEY_CHANGE). -/// 02ba <index>.W <is skill>.B <id>.L <count>.W -void clif_parse_Hotkey(int fd, struct map_session_data *sd) { -#ifdef HOTKEY_SAVING - unsigned short idx; - int cmd; - - cmd = RFIFOW(fd, 0); - idx = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[0]); - if (idx >= MAX_HOTKEYS) return; - - sd->status.hotkeys[idx].type = RFIFOB(fd, packet_db[sd->packet_ver][cmd].pos[1]); - sd->status.hotkeys[idx].id = RFIFOL(fd, packet_db[sd->packet_ver][cmd].pos[2]); - sd->status.hotkeys[idx].lv = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[3]); -#endif -} - - -/// Displays cast-like progress bar (ZC_PROGRESS). -/// 02f0 <color>.L <time>.L -void clif_progressbar(struct map_session_data * sd, unsigned long color, unsigned int second) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x2f0)); - WFIFOW(fd,0) = 0x2f0; - WFIFOL(fd,2) = color; - WFIFOL(fd,6) = second; - WFIFOSET(fd,packet_len(0x2f0)); -} - - -/// Removes an ongoing progress bar (ZC_PROGRESS_CANCEL). -/// 02f2 -void clif_progressbar_abort(struct map_session_data * sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x2f2)); - WFIFOW(fd,0) = 0x2f2; - WFIFOSET(fd,packet_len(0x2f2)); -} - - -/// Notification from the client, that the progress bar has reached 100% (CZ_PROGRESS). -/// 02f1 -void clif_parse_progressbar(int fd, struct map_session_data * sd) -{ - int npc_id = sd->progressbar.npc_id; - - if( gettick() < sd->progressbar.timeout && sd->st ) - sd->st->state = END; - - sd->progressbar.npc_id = sd->progressbar.timeout = 0; - npc_scriptcont(sd, npc_id); -} - - -/// Request to walk to a certain position on the current map. -/// 0085 <dest>.3B (CZ_REQUEST_MOVE) -/// 035f <dest>.3B (CZ_REQUEST_MOVE2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_WalkToXY(int fd, struct map_session_data *sd) -{ - short x, y; - - if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, CLR_DEAD); - return; - } - - if (sd->sc.opt1 && ( sd->sc.opt1 == OPT1_STONEWAIT || sd->sc.opt1 == OPT1_BURNING )) - ; //You CAN walk on this OPT1 value. - else if( sd->progressbar.npc_id ) - clif_progressbar_abort(sd); - else if (pc_cant_act(sd)) - return; - - if(sd->sc.data[SC_RUN] || sd->sc.data[SC_WUGDASH]) - return; - - pc_delinvincibletimer(sd); - - RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0], &x, &y, NULL); - - //Set last idle time... [Skotlex] - sd->idletime = last_tick; - - unit_walktoxy(&sd->bl, x, y, 4); -} - - -/// Notification about the result of a disconnect request (ZC_ACK_REQ_DISCONNECT). -/// 018b <result>.W -/// result: -/// 0 = disconnect (quit) -/// 1 = cannot disconnect (wait 10 seconds) -/// ? = ignored -void clif_disconnect_ack(struct map_session_data* sd, short result) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x18b)); - WFIFOW(fd,0) = 0x18b; - WFIFOW(fd,2) = result; - WFIFOSET(fd,packet_len(0x18b)); -} - - -/// Request to disconnect from server (CZ_REQ_DISCONNECT). -/// 018a <type>.W -/// type: -/// 0 = quit -void clif_parse_QuitGame(int fd, struct map_session_data *sd) -{ - /* Rovert's prevent logout option fixed [Valaris] */ - if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] && - (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) ) - { - set_eof(fd); - clif_disconnect_ack(sd, 0); - } else { - clif_disconnect_ack(sd, 1); - } -} - - -/// Requesting unit's name. -/// 0094 <id>.L (CZ_REQNAME) -/// 0368 <id>.L (CZ_REQNAME2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd) -{ - int id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - struct block_list* bl; - //struct status_change *sc; - - if( id < 0 && -id == sd->bl.id ) // for disguises [Valaris] - id = sd->bl.id; - - bl = map_id2bl(id); - if( bl == NULL ) - return; // Lagged clients could request names of already gone mobs/players. [Skotlex] - - if( sd->bl.m != bl->m || !check_distance_bl(&sd->bl, bl, AREA_SIZE) ) - return; // Block namerequests past view range - - // 'see people in GM hide' cheat detection - /* disabled due to false positives (network lag + request name of char that's about to hide = race condition) - sc = status_get_sc(bl); - if (sc && sc->option&OPTION_INVISIBLE && !disguised(bl) && - bl->type != BL_NPC && //Skip hidden NPCs which can be seen using Maya Purple - pc_get_group_level(sd) < battle_config.hack_info_GM_level - ) { - char gm_msg[256]; - sprintf(gm_msg, "Hack on NameRequest: character '%s' (account: %d) requested the name of an invisible target (id: %d).\n", sd->status.name, sd->status.account_id, id); - ShowWarning(gm_msg); - // information is sent to all online GMs - intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, gm_msg); - return; - } - */ - - clif_charnameack(fd, bl); -} - - -/// Validates and processes global messages -/// 008c <packet len>.W <text>.?B (<name> : <message>) 00 (CZ_REQUEST_CHAT) -/// There are various variants of this packet. -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, *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; - - if( is_atcommand(fd, sd, message, 1) ) - return; - - if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) ) - return; - - if( battle_config.min_chat_delay ) - { //[Skotlex] - if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) - 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*) aMalloc(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), 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 - if( is_fake ) { - WFIFOW(fd,0) = 0x8e; - WFIFOW(fd,2) = textlen + 4; - safestrncpy((char*)WFIFOP(fd,4), fakename, textlen); - aFree(fakename); - } 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); -#endif - - // Chat logging type 'O' / Global Chat - log_chat(LOG_CHAT_GLOBAL, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, message); -} - - -/// /mm /mapmove (as @rura GM command) (CZ_MOVETO_MAP). -/// Request to warp to a map on given coordinates. -/// 0140 <map name>.16B <x>.W <y>.W -void clif_parse_MapMove(int fd, struct map_session_data *sd) -{ - char command[MAP_NAME_LENGTH_EXT+25]; - char* map_name; - - map_name = (char*)RFIFOP(fd,2); - map_name[MAP_NAME_LENGTH_EXT-1]='\0'; - sprintf(command, "%cmapmove %s %d %d", atcommand_symbol, map_name, RFIFOW(fd,18), RFIFOW(fd,20)); - is_atcommand(fd, sd, command, 1); -} - - -/// Updates body and head direction of an object (ZC_CHANGE_DIRECTION). -/// 009c <id>.L <head dir>.W <dir>.B -/// head dir: -/// 0 = straight -/// 1 = turned CW -/// 2 = turned CCW -/// dir: -/// 0 = north -/// 1 = northwest -/// 2 = west -/// 3 = southwest -/// 4 = south -/// 5 = southeast -/// 6 = east -/// 7 = northeast -void clif_changed_dir(struct block_list *bl, enum send_target target) -{ - unsigned char buf[64]; - - WBUFW(buf,0) = 0x9c; - WBUFL(buf,2) = bl->id; - WBUFW(buf,6) = bl->type==BL_PC?((TBL_PC*)bl)->head_dir:0; - WBUFB(buf,8) = unit_getdir(bl); - - clif_send(buf, packet_len(0x9c), bl, target); - - if (disguised(bl)) { - WBUFL(buf,2) = -bl->id; - WBUFW(buf,6) = 0; - clif_send(buf, packet_len(0x9c), bl, SELF); - } -} - - -/// Request to change own body and head direction. -/// 009b <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION) -/// 0361 <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_ChangeDir(int fd, struct map_session_data *sd) -{ - unsigned char headdir, dir; - - headdir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - dir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]); - pc_setdir(sd, dir, headdir); - - clif_changed_dir(&sd->bl, AREA_WOS); -} - - -/// Request to show an emotion (CZ_REQ_EMOTION). -/// 00bf <type>.B -/// type: -/// @see enum emotion_type -void clif_parse_Emotion(int fd, struct map_session_data *sd) -{ - int emoticon = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - - if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) { - if (emoticon == E_MUTE) {// prevent use of the mute emote [Valaris] - clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1); - return; - } - // fix flood of emotion icon (ro-proxy): flood only the hacker player - if (sd->emotionlasttime + 1 >= time(NULL)) { // not more than 1 per second - sd->emotionlasttime = time(NULL); - clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1); - return; - } - sd->emotionlasttime = time(NULL); - - if(battle_config.client_reshuffle_dice && emoticon>=E_DICE1 && emoticon<=E_DICE6) - {// re-roll dice - emoticon = rnd()%6+E_DICE1; - } - - clif_emotion(&sd->bl, emoticon); - } else - clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1); -} - - -/// Amount of currently online players, reply to /w /who (ZC_USER_COUNT). -/// 00c2 <count>.L -void clif_user_count(struct map_session_data* sd, int count) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0xc2)); - WFIFOW(fd,0) = 0xc2; - WFIFOL(fd,2) = count; - WFIFOSET(fd,packet_len(0xc2)); -} - - -/// /w /who (CZ_REQ_USER_COUNT). -/// Request to display amount of currently connected players. -/// 00c1 -void clif_parse_HowManyConnections(int fd, struct map_session_data *sd) -{ - clif_user_count(sd, map_getusers()); -} - - -void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick) -{ - if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, CLR_DEAD); - return; - } - - if (sd->sc.count && - (sd->sc.data[SC_TRICKDEAD] || - sd->sc.data[SC_AUTOCOUNTER] || - sd->sc.data[SC_BLADESTOP] || - sd->sc.data[SC__MANHOLE] || - sd->sc.data[SC_CURSEDCIRCLE_ATKER] || - sd->sc.data[SC_CURSEDCIRCLE_TARGET] )) - return; - - pc_stop_walking(sd, 1); - pc_stop_attack(sd); - - if(target_id<0 && -target_id == sd->bl.id) // for disguises [Valaris] - target_id = sd->bl.id; - - switch(action_type) - { - case 0x00: // once attack - case 0x07: // continuous attack - - if( pc_cant_act(sd) || sd->sc.option&OPTION_HIDE ) - return; - - if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) ) - return; - - if( sd->sc.data[SC_BASILICA] || sd->sc.data[SC__SHADOWFORM] ) - return; - - if (!battle_config.sdelay_attack_enable && pc_checkskill(sd, SA_FREECAST) <= 0) { - if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) { - clif_skill_fail(sd, 1, USESKILL_FAIL_SKILLINTERVAL, 0); - return; - } - } - - pc_delinvincibletimer(sd); - sd->idletime = last_tick; - unit_attack(&sd->bl, target_id, action_type != 0); - break; - case 0x02: // sitdown - if (battle_config.basic_skill_check && pc_checkskill(sd, NV_BASIC) < 3) { - clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 2); - break; - } - - if(pc_issit(sd)) { - //Bugged client? Just refresh them. - clif_sitting(&sd->bl); - return; - } - - if (sd->ud.skilltimer != INVALID_TIMER || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING )) - break; - - if (sd->sc.count && ( - sd->sc.data[SC_DANCING] || - (sd->sc.data[SC_GRAVITATION] && sd->sc.data[SC_GRAVITATION]->val3 == BCT_SELF) - )) //No sitting during these states either. - break; - - pc_setsit(sd); - skill_sit(sd,1); - clif_sitting(&sd->bl); - break; - case 0x03: // standup - if (!pc_issit(sd)) { - //Bugged client? Just refresh them. - clif_standing(&sd->bl); - return; - } - pc_setstand(sd); - skill_sit(sd,0); - clif_standing(&sd->bl); - break; - } -} - - -/// Request for an action. -/// 0089 <target id>.L <action>.B (CZ_REQUEST_ACT) -/// 0437 <target id>.L <action>.B (CZ_REQUEST_ACT2) -/// action: -/// 0 = attack -/// 1 = pick up item -/// 2 = sit down -/// 3 = stand up -/// 7 = continous attack -/// 12 = (touch skill?) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_ActionRequest(int fd, struct map_session_data *sd) -{ - clif_parse_ActionRequest_sub(sd, - RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), - RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), - gettick() - ); -} - - -/// Response to the death/system menu (CZ_RESTART). -/// 00b2 <type>.B -/// type: -/// 0 = restart (respawn) -/// 1 = char-select (disconnect) -void clif_parse_Restart(int fd, struct map_session_data *sd) -{ - switch(RFIFOB(fd,2)) { - case 0x00: - pc_respawn(sd,CLR_RESPAWN); - break; - case 0x01: - /* Rovert's Prevent logout option - Fixed [Valaris] */ - if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] && - (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) ) - { //Send to char-server for character selection. - chrif_charselectreq(sd, session[fd]->client_addr); - } else { - clif_disconnect_ack(sd, 1); - } - break; - } -} - - -/// Validates and processes whispered messages (CZ_WHISPER). -/// 0096 <packet len>.W <nick>.24B <message>.?B -void clif_parse_WisMessage(int fd, struct map_session_data* sd) -{ - struct map_session_data* dstsd; - int i; - - char *target, *message; - int namelen, messagelen; - - // validate packet and retrieve name and message - if( !clif_process_message(sd, 1, &target, &namelen, &message, &messagelen) ) - return; - - if ( is_atcommand(fd, sd, message, 1) ) - return; - - if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT)) - return; - - if (battle_config.min_chat_delay) { //[Skotlex] - if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) { - return; - } - sd->cantalk_tick = gettick() + battle_config.min_chat_delay; - } - - // Chat logging type 'W' / Whisper - log_chat(LOG_CHAT_WHISPER, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, target, message); - - //-------------------------------------------------------// - // Lordalfa - Paperboy - To whisper NPC commands // - //-------------------------------------------------------// - if (target[0] && (strncasecmp(target,"NPC:",4) == 0) && (strlen(target) > 4)) - { - char* str = target+4; //Skip the NPC: string part. - struct npc_data* npc; - if ((npc = npc_name2id(str))) { - char split_data[NUM_WHISPER_VAR][CHAT_SIZE_MAX]; - char *split; - char output[256]; - - str = message; - // skip codepage indicator, if detected - if( str[0] == '|' && strlen(str) >= 4 ) - str += 3; - for( i = 0; i < NUM_WHISPER_VAR; ++i ) {// Splits the message using '#' as separators - split = strchr(str,'#'); - if( split == NULL ) { // use the remaining string - safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i])); - for( ++i; i < NUM_WHISPER_VAR; ++i ) - split_data[i][0] = '\0'; - break; - } - *split = '\0'; - safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i])); - str = split+1; - } - - for( i = 0; i < NUM_WHISPER_VAR; ++i ) { - sprintf(output, "@whispervar%d$", i); - set_var(sd,output,(char *) split_data[i]); - } - - sprintf(output, "%s::OnWhisperGlobal", npc->exname); - npc_event(sd,output,0); // Calls the NPC label - - return; - } - } else if(strcmpi(target, main_chat_nick) == 0) { // Main chat [LuzZza] - if(!sd->state.mainchat) - clif_displaymessage(fd, msg_txt(388)); // You should enable main chat with "@main on" command. - else { - // send the main message using inter-server system - intif_main_message( sd, message ); - } - - return; - } - - // searching destination character - dstsd = map_nick2sd(target); - - if (dstsd == NULL || strcmp(dstsd->status.name, target) != 0) { - // player is not on this map-server - // At this point, don't send wisp/page if it's not exactly the same name, because (example) - // if there are 'Test' player on an other map-server and 'test' player on this map-server, - // and if we ask for 'Test', we must not contact 'test' player - // so, we send information to inter-server, which is the only one which decide (and copy correct name). - intif_wis_message(sd, target, message, messagelen); - return; - } - - // if player ignores everyone - if (dstsd->state.ignoreAll) { - if (dstsd->sc.option & OPTION_INVISIBLE && pc_get_group_level(sd) < pc_get_group_level(dstsd)) - clif_wis_end(fd, 1); // 1: target character is not loged in - else - clif_wis_end(fd, 3); // 3: everyone ignored by target - return; - } - - // if player is autotrading - if( dstsd->state.autotrade == 1 ) { - char output[256]; - sprintf(output, "%s is in autotrade mode and cannot receive whispered messages.", dstsd->status.name); - clif_wis_message(fd, wisp_server_name, output, strlen(output) + 1); - return; - } - - // if player ignores the source character - ARR_FIND(0, MAX_IGNORE_LIST, i, dstsd->ignore[i].name[0] == '\0' || strcmp(dstsd->ignore[i].name, sd->status.name) == 0); - if(i < MAX_IGNORE_LIST && dstsd->ignore[i].name[0] != '\0') { // source char present in ignore list - clif_wis_end(fd, 2); // 2: ignored by target - return; - } - - // notify sender of success - clif_wis_end(fd, 0); // 0: success to send wisper - - // Normal message - clif_wis_message(dstsd->fd, sd->status.name, message, messagelen); -} - - -/// /b /nb (CZ_BROADCAST). -/// Request to broadcast a message on whole server. -/// 0099 <packet len>.W <text>.?B 00 -void clif_parse_Broadcast(int fd, struct map_session_data* sd) { - char command[CHAT_SIZE_MAX+11]; - char* msg = (char*)RFIFOP(fd,4); - unsigned int len = RFIFOW(fd,2)-4; - - // as the length varies depending on the command used, just block unreasonably long strings - mes_len_check(msg, len, CHAT_SIZE_MAX); - - sprintf(command, "%ckami %s", atcommand_symbol, msg); - is_atcommand(fd, sd, command, 1); -} - - -/// Request to pick up an item. -/// 009f <id>.L (CZ_ITEM_PICKUP) -/// 0362 <id>.L (CZ_ITEM_PICKUP2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_TakeItem(int fd, struct map_session_data *sd) -{ - struct flooritem_data *fitem; - int map_object_id; - - map_object_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - - fitem = (struct flooritem_data*)map_id2bl(map_object_id); - - do { - if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, CLR_DEAD); - break; - } - - if (fitem == NULL || fitem->bl.type != BL_ITEM || fitem->bl.m != sd->bl.m) - break; - - if( sd->sc.cant.pickup ) - break; - - if (pc_cant_act(sd)) - break; - - if (!pc_takeitem(sd, fitem)) - break; - - return; - } while (0); - // Client REQUIRES a fail packet or you can no longer pick items. - clif_additem(sd,0,0,6); -} - - -/// Request to drop an item. -/// 00a2 <index>.W <amount>.W (CZ_ITEM_THROW) -/// 0363 <index>.W <amount>.W (CZ_ITEM_THROW2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_DropItem(int fd, struct map_session_data *sd) -{ - int item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2; - int item_amount = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]); - - for(;;) { - if (pc_isdead(sd)) - break; - - if (pc_cant_act(sd)) - break; - - if (sd->sc.count && ( - sd->sc.data[SC_AUTOCOUNTER] || - sd->sc.data[SC_BLADESTOP] || - (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOITEM) - )) - break; - - if (!pc_dropitem(sd, item_index, item_amount)) - break; - - return; - } - - //Because the client does not like being ignored. - clif_dropitem(sd, item_index,0); -} - - -/// Request to use an item. -/// 00a7 <index>.W <account id>.L (CZ_USE_ITEM) -/// 0439 <index>.W <account id>.L (CZ_USE_ITEM2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_UseItem(int fd, struct map_session_data *sd) -{ - int n; - - if (pc_isdead(sd)) { - clif_clearunit_area(&sd->bl, CLR_DEAD); - return; - } - - //This flag enables you to use items while in an NPC. [Skotlex] - if (sd->npc_id) { - if (sd->npc_id != sd->npc_item_flag) - return; - } - else if (pc_istrading(sd)) - return; - - //Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex] - sd->idletime = last_tick; - n = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2; - - if(n <0 || n >= MAX_INVENTORY) - return; - if (!pc_useitem(sd,n)) - clif_useitemack(sd,n,0,false); //Send an empty ack packet or the client gets stuck. -} - - -/// Request to equip an item (CZ_REQ_WEAR_EQUIP). -/// 00a9 <index>.W <position>.W -void clif_parse_EquipItem(int fd,struct map_session_data *sd) -{ - int index; - - if(pc_isdead(sd)) { - clif_clearunit_area(&sd->bl,CLR_DEAD); - return; - } - index = RFIFOW(fd,2)-2; - if (index < 0 || index >= MAX_INVENTORY) - return; //Out of bounds check. - - if(sd->npc_id) { - if (sd->npc_id != sd->npc_item_flag) - return; - } 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; - - if(!sd->status.inventory[index].identify) { - clif_equipitemack(sd,index,0,0); // fail - return; - } - - if(!sd->inventory_data[index]) - return; - - if(sd->inventory_data[index]->type == IT_PETARMOR){ - pet_equipitem(sd,index); - return; - } - - //Client doesn't send the position for ammo. - if(sd->inventory_data[index]->type == IT_AMMO) - pc_equipitem(sd,index,EQP_AMMO); - else - pc_equipitem(sd,index,RFIFOW(fd,4)); -} - - -/// Request to take off an equip (CZ_REQ_TAKEOFF_EQUIP). -/// 00ab <index>.W -void clif_parse_UnequipItem(int fd,struct map_session_data *sd) -{ - int index; - - if(pc_isdead(sd)) { - clif_clearunit_area(&sd->bl,CLR_DEAD); - return; - } - - 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; - - index = RFIFOW(fd,2)-2; - - pc_unequipitem(sd,index,1); -} - - -/// Request to start a conversation with an NPC (CZ_CONTACTNPC). -/// 0090 <id>.L <type>.B -/// type: -/// 1 = click -void clif_parse_NpcClicked(int fd,struct map_session_data *sd) -{ - struct block_list *bl; - - if(pc_isdead(sd)) { - clif_clearunit_area(&sd->bl,CLR_DEAD); - return; - } - - if (pc_cant_act(sd)) - return; - - bl = map_id2bl(RFIFOL(fd,2)); - if (!bl) return; - switch (bl->type) { - case BL_MOB: - case BL_PC: - clif_parse_ActionRequest_sub(sd, 0x07, bl->id, gettick()); - break; - case BL_NPC: - if( bl->m != -1 )// the user can't click floating npcs directly (hack attempt) - npc_click(sd,(TBL_NPC*)bl); - break; - } -} - - -/// Selection between buy/sell was made (CZ_ACK_SELECT_DEALTYPE). -/// 00c5 <id>.L <type>.B -/// type: -/// 0 = buy -/// 1 = sell -void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd) -{ - if (sd->state.trading) - return; - npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6)); -} - - -/// Notification about the result of a purchase attempt from an NPC shop (ZC_PC_PURCHASE_RESULT). -/// 00ca <result>.B -/// result: -/// 0 = "The deal has successfully completed." -/// 1 = "You do not have enough zeny." -/// 2 = "You are over your Weight Limit." -/// 3 = "Out of the maximum capacity, you have too many items." -void clif_npc_buy_result(struct map_session_data* sd, unsigned char result) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0xca)); - WFIFOW(fd,0) = 0xca; - WFIFOB(fd,2) = result; - WFIFOSET(fd,packet_len(0xca)); -} - - -/// Request to buy chosen items from npc shop (CZ_PC_PURCHASE_ITEMLIST). -/// 00c8 <packet len>.W { <amount>.W <name id>.W }* -void clif_parse_NpcBuyListSend(int fd, struct map_session_data* sd) -{ - int n = (RFIFOW(fd,2)-4) /4; - unsigned short* item_list = (unsigned short*)RFIFOP(fd,4); - int result; - - if( sd->state.trading || !sd->npc_shopid ) - result = 1; - else - result = npc_buylist(sd,n,item_list); - - sd->npc_shopid = 0; //Clear shop data. - - clif_npc_buy_result(sd, result); -} - - -/// Notification about the result of a sell attempt to an NPC shop (ZC_PC_SELL_RESULT). -/// 00cb <result>.B -/// result: -/// 0 = "The deal has successfully completed." -/// 1 = "The deal has failed." -void clif_npc_sell_result(struct map_session_data* sd, unsigned char result) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0xcb)); - WFIFOW(fd,0) = 0xcb; - WFIFOB(fd,2) = result; - WFIFOSET(fd,packet_len(0xcb)); -} - - -/// Request to sell chosen items to npc shop (CZ_PC_SELL_ITEMLIST). -/// 00c9 <packet len>.W { <index>.W <amount>.W }* -void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd) -{ - int fail=0,n; - unsigned short *item_list; - - n = (RFIFOW(fd,2)-4) /4; - item_list = (unsigned short*)RFIFOP(fd,4); - - if (sd->state.trading || !sd->npc_shopid) - fail = 1; - else - fail = npc_selllist(sd,n,item_list); - - sd->npc_shopid = 0; //Clear shop data. - - clif_npc_sell_result(sd, fail); -} - - -/// Chatroom creation request (CZ_CREATE_CHATROOM). -/// 00d5 <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B -/// type: -/// 0 = private -/// 1 = public -void clif_parse_CreateChatRoom(int fd, struct map_session_data* sd) -{ - int len = RFIFOW(fd,2)-15; - int limit = RFIFOW(fd,4); - bool pub = (RFIFOB(fd,6) != 0); - const char* password = (char*)RFIFOP(fd,7); //not zero-terminated - const char* title = (char*)RFIFOP(fd,15); // not zero-terminated - char s_password[CHATROOM_PASS_SIZE]; - char s_title[CHATROOM_TITLE_SIZE]; - - if (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) - return; - if(battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 4) { - clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,3); - return; - } - - if( len <= 0 ) - return; // invalid input - - safestrncpy(s_password, password, CHATROOM_PASS_SIZE); - safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte - - chat_createpcchat(sd, s_title, s_password, limit, pub); -} - - -/// Chatroom join request (CZ_REQ_ENTER_ROOM). -/// 00d9 <chat ID>.L <passwd>.8B -void clif_parse_ChatAddMember(int fd, struct map_session_data* sd) -{ - int chatid = RFIFOL(fd,2); - const char* password = (char*)RFIFOP(fd,6); // not zero-terminated - - chat_joinchat(sd,chatid,password); -} - - -/// Chatroom properties adjustment request (CZ_CHANGE_CHATROOM). -/// 00de <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B -/// type: -/// 0 = private -/// 1 = public -void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data* sd) -{ - int len = RFIFOW(fd,2)-15; - int limit = RFIFOW(fd,4); - bool pub = (RFIFOB(fd,6) != 0); - const char* password = (char*)RFIFOP(fd,7); // not zero-terminated - const char* title = (char*)RFIFOP(fd,15); // not zero-terminated - char s_password[CHATROOM_PASS_SIZE]; - char s_title[CHATROOM_TITLE_SIZE]; - - if( len <= 0 ) - return; // invalid input - - safestrncpy(s_password, password, CHATROOM_PASS_SIZE); - safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte - - chat_changechatstatus(sd, s_title, s_password, limit, pub); -} - - -/// Request to change the chat room ownership (CZ_REQ_ROLE_CHANGE). -/// 00e0 <role>.L <nick>.24B -/// role: -/// 0 = owner -/// 1 = normal -void clif_parse_ChangeChatOwner(int fd, struct map_session_data* sd) -{ - chat_changechatowner(sd,(char*)RFIFOP(fd,6)); -} - - -/// Request to expel a player from chat room (CZ_REQ_EXPEL_MEMBER). -/// 00e2 <name>.24B -void clif_parse_KickFromChat(int fd,struct map_session_data *sd) -{ - chat_kickchat(sd,(char*)RFIFOP(fd,2)); -} - - -/// Request to leave the current chatroom (CZ_EXIT_ROOM). -/// 00e3 -void clif_parse_ChatLeave(int fd, struct map_session_data* sd) -{ - chat_leavechat(sd,0); -} - - -//Handles notifying asker and rejecter of what has just ocurred. -//Type is used to determine the correct msg_txt to use: -//0: -static void clif_noask_sub(struct map_session_data *src, struct map_session_data *target, int type) -{ - const char* msg; - char output[256]; - // Your request has been rejected by autoreject option. - msg = msg_txt(392); - clif_disp_onlyself(src, msg, strlen(msg)); - //Notice that a request was rejected. - snprintf(output, 256, msg_txt(393+type), src->status.name, 256); - clif_disp_onlyself(target, output, strlen(output)); -} - - -/// Request to begin a trade (CZ_REQ_EXCHANGE_ITEM). -/// 00e4 <account id>.L -void clif_parse_TradeRequest(int fd,struct map_session_data *sd) -{ - struct map_session_data *t_sd; - - t_sd = map_id2sd(RFIFOL(fd,2)); - - if(!sd->chatID && pc_cant_act(sd)) - return; //You can trade while in a chatroom. - - // @noask [LuzZza] - if(t_sd && t_sd->state.noask) { - clif_noask_sub(sd, t_sd, 0); - return; - } - - if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 1) - { - clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,0); - return; - } - - trade_traderequest(sd,t_sd); -} - - -/// Answer to a trade request (CZ_ACK_EXCHANGE_ITEM). -/// 00e6 <result>.B -/// result: -/// 3 = accepted -/// 4 = rejected -void clif_parse_TradeAck(int fd,struct map_session_data *sd) -{ - trade_tradeack(sd,RFIFOB(fd,2)); -} - - -/// Request to add an item to current trade (CZ_ADD_EXCHANGE_ITEM). -/// 00e8 <index>.W <amount>.L -void clif_parse_TradeAddItem(int fd,struct map_session_data *sd) -{ - short index = RFIFOW(fd,2); - int amount = RFIFOL(fd,4); - - if( index == 0 ) - trade_tradeaddzeny(sd, amount); - else - trade_tradeadditem(sd, index, (short)amount); -} - - -/// Request to lock items in current trade (CZ_CONCLUDE_EXCHANGE_ITEM). -/// 00eb -void clif_parse_TradeOk(int fd,struct map_session_data *sd) -{ - trade_tradeok(sd); -} - - -/// Request to cancel current trade (CZ_CANCEL_EXCHANGE_ITEM). -/// 00ed -void clif_parse_TradeCancel(int fd,struct map_session_data *sd) -{ - trade_tradecancel(sd); -} - - -/// Request to commit current trade (CZ_EXEC_EXCHANGE_ITEM). -/// 00ef -void clif_parse_TradeCommit(int fd,struct map_session_data *sd) -{ - trade_tradecommit(sd); -} - - -/// Request to stop chasing/attacking an unit (CZ_CANCEL_LOCKON). -/// 0118 -void clif_parse_StopAttack(int fd,struct map_session_data *sd) -{ - pc_stop_attack(sd); -} - - -/// Request to move an item from inventory to cart (CZ_MOVE_ITEM_FROM_BODY_TO_CART). -/// 0126 <index>.W <amount>.L -void clif_parse_PutItemToCart(int fd,struct map_session_data *sd) -{ - if (pc_istrading(sd)) - return; - if (!pc_iscarton(sd)) - return; - pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); -} - - -/// Request to move an item from cart to inventory (CZ_MOVE_ITEM_FROM_CART_TO_BODY). -/// 0127 <index>.W <amount>.L -void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd) -{ - if (!pc_iscarton(sd)) - return; - pc_getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); -} - - -/// Request to remove cart/falcon/peco/dragon (CZ_REQ_CARTOFF). -/// 012a -void clif_parse_RemoveOption(int fd,struct map_session_data *sd) -{ - /** - * Attempts to remove these options when this function is called (will remove all available) - **/ -#ifdef NEW_CARTS - pc_setoption(sd,sd->sc.option&~(OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR)); - if( sd->sc.data[SC_PUSH_CART] ) - pc_setcart(sd,0); -#else - pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR)); -#endif -} - - -/// Request to change cart's visual look (CZ_REQ_CHANGECART). -/// 01af <num>.W -void clif_parse_ChangeCart(int fd,struct map_session_data *sd) -{// TODO: State tracking? - int type; - - if( sd && pc_checkskill(sd, MC_CHANGECART) < 1 ) - return; - - type = (int)RFIFOW(fd,2); -#ifdef NEW_CARTS - if( (type == 9 && sd->status.base_level > 131) || - (type == 8 && sd->status.base_level > 121) || - (type == 7 && sd->status.base_level > 111) || - (type == 6 && sd->status.base_level > 101) || - (type == 5 && sd->status.base_level > 90) || - (type == 4 && sd->status.base_level > 80) || - (type == 3 && sd->status.base_level > 65) || - (type == 2 && sd->status.base_level > 40) || - (type == 1)) -#else - if( (type == 5 && sd->status.base_level > 90) || - (type == 4 && sd->status.base_level > 80) || - (type == 3 && sd->status.base_level > 65) || - (type == 2 && sd->status.base_level > 40) || - (type == 1)) -#endif - pc_setcart(sd,type); -} - - -/// Request to increase status (CZ_STATUS_CHANGE). -/// 00bb <status id>.W <amount>.B -/// status id: -/// SP_STR ~ SP_LUK -/// amount: -/// client sends always 1 for this, even when using /str+ and -/// the like -void clif_parse_StatusUp(int fd,struct map_session_data *sd) -{ - pc_statusup(sd,RFIFOW(fd,2)); -} - - -/// Request to increase level of a skill (CZ_UPGRADE_SKILLLEVEL). -/// 0112 <skill id>.W -void clif_parse_SkillUp(int fd,struct map_session_data *sd) -{ - pc_skillup(sd,RFIFOW(fd,2)); -} - -static void clif_parse_UseSkillToId_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id) -{ - int lv; - - if( !hd ) - return; - if( skillnotok_hom(skill_id, hd) ) - return; - if( hd->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL ) - target_id = hd->bl.id; - if( hd->ud.skilltimer != INVALID_TIMER ) - { - if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return; - } - else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 ) - return; - - lv = merc_hom_checkskill(hd, skill_id); - if( skill_lv > lv ) - skill_lv = lv; - if( skill_lv ) - unit_skilluse_id(&hd->bl, target_id, skill_id, skill_lv); -} - -static void clif_parse_UseSkillToPos_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo) -{ - int lv; - if( !hd ) - return; - if( skillnotok_hom(skill_id, hd) ) - return; - if( hd->ud.skilltimer != INVALID_TIMER ) { - if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return; - } else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 ) - return; - - if( hd->sc.data[SC_BASILICA] ) - return; - lv = merc_hom_checkskill(hd, skill_id); - if( skill_lv > lv ) - skill_lv = lv; - if( skill_lv ) - unit_skilluse_pos(&hd->bl, x, y, skill_id, skill_lv); -} - -static void clif_parse_UseSkillToId_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id) -{ - int lv; - - if( !md ) - return; - if( skillnotok_mercenary(skill_id, md) ) - return; - if( md->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL ) - target_id = md->bl.id; - if( md->ud.skilltimer != INVALID_TIMER ) - { - if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return; - } - else if( DIFF_TICK(tick, md->ud.canact_tick) < 0 ) - return; - - lv = mercenary_checkskill(md, skill_id); - if( skill_lv > lv ) - skill_lv = lv; - if( skill_lv ) - unit_skilluse_id(&md->bl, target_id, skill_id, skill_lv); -} - -static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo) -{ - int lv; - if( !md ) - return; - if( skillnotok_mercenary(skill_id, md) ) - return; - if( md->ud.skilltimer != INVALID_TIMER ) - return; - if( DIFF_TICK(tick, md->ud.canact_tick) < 0 ) - { - clif_skill_fail(md->master, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0); - return; - } - - if( md->sc.data[SC_BASILICA] ) - return; - lv = mercenary_checkskill(md, skill_id); - if( skill_lv > lv ) - skill_lv = lv; - if( skill_lv ) - unit_skilluse_pos(&md->bl, x, y, skill_id, skill_lv); -} - - -/// Request to use a targeted skill. -/// 0113 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL) -/// 0438 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) -{ - uint16 skill_id, skill_lv; - int tmp, target_id; - unsigned int tick = gettick(); - - skill_lv = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - skill_id = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]); - target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]); - - if( skill_lv < 1 ) skill_lv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex] - - tmp = skill_get_inf(skill_id); - if (tmp&INF_GROUND_SKILL || !tmp) - return; //Using a ground/passive skill on a target? WRONG. - - if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) - { - clif_parse_UseSkillToId_homun(sd->hd, sd, tick, skill_id, skill_lv, target_id); - return; - } - - if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL ) - { - clif_parse_UseSkillToId_mercenary(sd->md, sd, tick, skill_id, skill_lv, target_id); - return; - } - - // Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex] - sd->idletime = last_tick; - - if( pc_cant_act(sd) && skill_id != RK_REFRESH && !(skill_id == SR_GENTLETOUCH_CURE && (sd->sc.opt1 == OPT1_STONE || sd->sc.opt1 == OPT1_FREEZE || sd->sc.opt1 == OPT1_STUN)) ) - return; - if( pc_issit(sd) ) - return; - - if( skillnotok(skill_id, sd) ) - return; - - if( sd->bl.id != target_id && tmp&INF_SELF_SKILL ) - target_id = sd->bl.id; // never trust the client - - if( target_id < 0 && -target_id == sd->bl.id ) // for disguises [Valaris] - target_id = sd->bl.id; - - if( sd->ud.skilltimer != INVALID_TIMER ) - { - if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) - return; - } - else if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) - { - if( sd->skillitem != skill_id ) - { - clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0); - return; - } - } - - if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) ) - return; - - if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) ) - return; // On basilica only caster can use Basilica again to stop it. - - if( sd->menuskill_id ) { - if( sd->menuskill_id == SA_TAMINGMONSTER ) { - clif_menuskill_clear(sd); //Cancel pet capture. - } else if( sd->menuskill_id != SA_AUTOSPELL ) - return; //Can't use skills while a menu is open. - } - if( sd->skillitem == skill_id ) { - if( skill_lv != sd->skillitemlv ) - skill_lv = sd->skillitemlv; - if( !(tmp&INF_SELF_SKILL) ) - pc_delinvincibletimer(sd); // Target skills thru items cancel invincibility. [Inkfish] - unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv); - return; - } - - sd->skillitem = sd->skillitemlv = 0; - - if( skill_id >= GD_SKILLBASE ) { - if( sd->state.gmaster_flag ) - skill_lv = guild_checkskill(sd->state.gmaster_flag, skill_id); - else - skill_lv = 0; - } else { - tmp = pc_checkskill(sd, skill_id); - if( skill_lv > tmp ) - skill_lv = tmp; - } - - pc_delinvincibletimer(sd); - - if( skill_lv ) - unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv); -} - -/*========================================== - * Client tells server he'd like to use AoE skill id 'skill_id' of level 'skill_lv' on 'x','y' location - *------------------------------------------*/ -static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uint16 skill_lv, uint16 skill_id, short x, short y, int skillmoreinfo) -{ - unsigned int tick = gettick(); - - if( !(skill_get_inf(skill_id)&INF_GROUND_SKILL) ) - return; //Using a target skill on the ground? WRONG. - - if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) { - clif_parse_UseSkillToPos_homun(sd->hd, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo); - return; - } - - if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL ) - { - clif_parse_UseSkillToPos_mercenary(sd->md, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo); - return; - } - - //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex] - sd->idletime = last_tick; - - if( skillnotok(skill_id, sd) ) - return; - if( skillmoreinfo != -1 ) - { - if( pc_issit(sd) ) - { - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - return; - } - //You can't use Graffiti/TalkieBox AND have a vending open, so this is safe. - safestrncpy(sd->message, (char*)RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE); - } - - if( sd->ud.skilltimer != INVALID_TIMER ) - return; - - if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) { - if( sd->skillitem != skill_id ) { - clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0); - return; - } - } - - if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) ) - return; - - if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) ) - return; // On basilica only caster can use Basilica again to stop it. - - if( sd->menuskill_id ) { - if( sd->menuskill_id == SA_TAMINGMONSTER ) { - clif_menuskill_clear(sd); //Cancel pet capture. - } else if( sd->menuskill_id != SA_AUTOSPELL ) - return; //Can't use skills while a menu is open. - } - - pc_delinvincibletimer(sd); - - if( sd->skillitem == skill_id ) { - if( skill_lv != sd->skillitemlv ) - skill_lv = sd->skillitemlv; - unit_skilluse_pos(&sd->bl, x, y, skill_id, skill_lv); - } else { - int lv; - sd->skillitem = sd->skillitemlv = 0; - if( (lv = pc_checkskill(sd, skill_id)) > 0 ) { - if( skill_lv > lv ) - skill_lv = lv; - unit_skilluse_pos(&sd->bl, x, y, skill_id,skill_lv); - } - } -} - - -/// Request to use a ground skill. -/// 0116 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND) -/// 0366 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd) -{ - if (pc_cant_act(sd)) - return; - if (pc_issit(sd)) - return; - - clif_parse_UseSkillToPosSub(fd, sd, - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //skill lv - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //skill num - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y - -1 //Skill more info. - ); -} - - -/// Request to use a ground skill with text. -/// 0190 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX) -/// 0367 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd) -{ - if (pc_cant_act(sd)) - return; - if (pc_issit(sd)) - return; - - clif_parse_UseSkillToPosSub(fd, sd, - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //Skill lv - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //Skill num - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x - RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y - packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[4] //skill more info - ); -} - - -/// Answer to map selection dialog (CZ_SELECT_WARPPOINT). -/// 011b <skill id>.W <map name>.16B -void clif_parse_UseSkillMap(int fd, struct map_session_data* sd) -{ - uint16 skill_id = RFIFOW(fd,2); - char map_name[MAP_NAME_LENGTH]; - mapindex_getmapname((char*)RFIFOP(fd,4), map_name); - - if(skill_id != sd->menuskill_id) - return; - - if( pc_cant_act(sd) ) { - clif_menuskill_clear(sd); - return; - } - - pc_delinvincibletimer(sd); - skill_castend_map(sd,skill_id,map_name); -} - - -/// Request to set a memo on current map (CZ_REMEMBER_WARPPOINT). -/// 011d -void clif_parse_RequestMemo(int fd,struct map_session_data *sd) -{ - if (!pc_isdead(sd)) - pc_memo(sd,-1); -} - - -/// Answer to pharmacy item selection dialog (CZ_REQMAKINGITEM). -/// 018e <name id>.W { <material id>.W }*3 -void clif_parse_ProduceMix(int fd,struct map_session_data *sd) -{ - switch( sd->menuskill_id ) { - case -1: - case AM_PHARMACY: - case RK_RUNEMASTERY: - case GC_RESEARCHNEWPOISON: - break; - default: - 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.skill_id,USESKILL_FAIL_LEVEL,0); - clif_menuskill_clear(sd); - return; - } - if( skill_can_produce_mix(sd,RFIFOW(fd,2),sd->menuskill_val, 1) ) - skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1); - clif_menuskill_clear(sd); -} - - -/// Answer to mixing item selection dialog (CZ_REQ_MAKINGITEM). -/// 025b <mk type>.W <name id>.W -/// mk type: -/// 1 = cooking -/// 2 = arrow -/// 3 = elemental -/// 4 = GN_MIX_COOKING -/// 5 = GN_MAKEBOMB -/// 6 = GN_S_PHARMACY -void clif_parse_Cooking(int fd,struct map_session_data *sd) { - int type = RFIFOW(fd,2); - int nameid = RFIFOW(fd,4); - int amount = sd->menuskill_val2?sd->menuskill_val2:1; - if( type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY ) - return; - - if (pc_istrading(sd)) { - //Make it fail to avoid shop exploits where you sell something different than you see. - clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0); - clif_menuskill_clear(sd); - return; - } - if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, amount) ) - skill_produce_mix(sd,sd->menuskill_id,nameid,0,0,0,amount); - clif_menuskill_clear(sd); -} - - -/// Answer to repair weapon item selection dialog (CZ_REQ_ITEMREPAIR). -/// 01fd <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W -void clif_parse_RepairItem(int fd, struct map_session_data *sd) -{ - if (sd->menuskill_id != BS_REPAIRWEAPON) - 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.skill_id,USESKILL_FAIL_LEVEL,0); - clif_menuskill_clear(sd); - return; - } - skill_repairweapon(sd,RFIFOW(fd,2)); - clif_menuskill_clear(sd); -} - - -/// Answer to refine weapon item selection dialog (CZ_REQ_WEAPONREFINE). -/// 0222 <index>.L -void clif_parse_WeaponRefine(int fd, struct map_session_data *sd) -{ - int idx; - - if (sd->menuskill_id != WS_WEAPONREFINE) //Packet exploit? - 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.skill_id,USESKILL_FAIL_LEVEL,0); - clif_menuskill_clear(sd); - return; - } - idx = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - skill_weaponrefine(sd, idx-2); - clif_menuskill_clear(sd); -} - - -/// Answer to script menu dialog (CZ_CHOOSE_MENU). -/// 00b8 <npc id>.L <choice>.B -/// choice: -/// 1~254 = menu item -/// 255 = cancel -/// NOTE: If there were more than 254 items in the list, choice -/// overflows to choice%256. -void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd) -{ - int npc_id = RFIFOL(fd,2); - uint8 select = RFIFOB(fd,6); - - if( (select > sd->npc_menu && select != 0xff) || select == 0 ) - { - TBL_NPC* nd = map_id2nd(npc_id); - ShowWarning("Invalid menu selection on npc %d:'%s' - got %d, valid range is [%d..%d] (player AID:%d, CID:%d, name:'%s')!\n", npc_id, (nd)?nd->name:"invalid npc id", select, 1, sd->npc_menu, sd->bl.id, sd->status.char_id, sd->status.name); - clif_GM_kick(NULL,sd); - return; - } - - sd->npc_menu = select; - npc_scriptcont(sd,npc_id); -} - - -/// NPC dialog 'next' click (CZ_REQ_NEXT_SCRIPT). -/// 00b9 <npc id>.L -void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd) -{ - npc_scriptcont(sd,RFIFOL(fd,2)); -} - - -/// NPC numeric input dialog value (CZ_INPUT_EDITDLG). -/// 0143 <npc id>.L <value>.L -void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd) -{ - int npcid = RFIFOL(fd,2); - int amount = (int)RFIFOL(fd,6); - - sd->npc_amount = amount; - npc_scriptcont(sd, npcid); -} - - -/// NPC text input dialog value (CZ_INPUT_EDITDLGSTR). -/// 01d5 <packet len>.W <npc id>.L <string>.?B -void clif_parse_NpcStringInput(int fd, struct map_session_data* sd) -{ - int message_len = RFIFOW(fd,2)-8; - int npcid = RFIFOL(fd,4); - const char* message = (char*)RFIFOP(fd,8); - - if( message_len <= 0 ) - return; // invalid input - - safestrncpy(sd->npc_str, message, min(message_len,CHATBOX_SIZE)); - npc_scriptcont(sd, npcid); -} - - -/// NPC dialog 'close' click (CZ_CLOSE_DIALOG). -/// 0146 <npc id>.L -void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd) -{ - if (!sd->npc_id) //Avoid parsing anything when the script was done with. [Skotlex] - return; - npc_scriptcont(sd,RFIFOL(fd,2)); -} - - -/// Answer to identify item selection dialog (CZ_REQ_ITEMIDENTIFY). -/// 0178 <index>.W -/// index: -/// -1 = cancel -void clif_parse_ItemIdentify(int fd,struct map_session_data *sd) -{ - short idx = RFIFOW(fd,2); - - if (sd->menuskill_id != MC_IDENTIFY) - return; - if( idx == -1 ) {// cancel pressed - clif_menuskill_clear(sd); - return; - } - skill_identify(sd,idx-2); - clif_menuskill_clear(sd); -} - - -/// Answer to arrow crafting item selection dialog (CZ_REQ_MAKINGARROW). -/// 01ae <name id>.W -void clif_parse_SelectArrow(int fd,struct map_session_data *sd) -{ - 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.skill_id,USESKILL_FAIL_LEVEL,0); - clif_menuskill_clear(sd); - return; - } - switch( sd->menuskill_id ) { - case AC_MAKINGARROW: - skill_arrow_create(sd,RFIFOW(fd,2)); - break; - case SA_CREATECON: - skill_produce_mix(sd,SA_CREATECON,RFIFOW(fd,2),0,0,0, 1); - 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; - } - - clif_menuskill_clear(sd); -} - - -/// Answer to SA_AUTOSPELL skill selection dialog (CZ_SELECTAUTOSPELL). -/// 01ce <skill id>.L -void clif_parse_AutoSpell(int fd,struct map_session_data *sd) -{ - if (sd->menuskill_id != SA_AUTOSPELL) - return; - skill_autospell(sd,RFIFOL(fd,2)); - clif_menuskill_clear(sd); -} - - -/// Request to display item carding/composition list (CZ_REQ_ITEMCOMPOSITION_LIST). -/// 017a <card index>.W -void clif_parse_UseCard(int fd,struct map_session_data *sd) -{ - if (sd->state.trading != 0) - return; - clif_use_card(sd,RFIFOW(fd,2)-2); -} - - -/// Answer to carding/composing item selection dialog (CZ_REQ_ITEMCOMPOSITION). -/// 017c <card index>.W <equip index>.W -void clif_parse_InsertCard(int fd,struct map_session_data *sd) -{ - if (sd->state.trading != 0) - return; - pc_insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2); -} - - -/// Request of character's name by char ID. -/// 0193 <char id>.L (CZ_REQNAME_BYGID) -/// 0369 <char id>.L (CZ_REQNAME_BYGID2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_SolveCharName(int fd, struct map_session_data *sd) -{ - int charid; - - charid = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - map_reqnickdb(sd, charid); -} - - -/// /resetskill /resetstate (CZ_RESET). -/// Request to reset stats or skills. -/// 0197 <type>.W -/// type: -/// 0 = state -/// 1 = skill -void clif_parse_ResetChar(int fd, struct map_session_data *sd) { - char cmd[15]; - - if( RFIFOW(fd,2) ) - sprintf(cmd,"%cresetskill",atcommand_symbol); - else - sprintf(cmd,"%cresetstat",atcommand_symbol); - - is_atcommand(fd, sd, cmd, 1); -} - - -/// /lb /nlb (CZ_LOCALBROADCAST). -/// Request to broadcast a message on current map. -/// 019c <packet len>.W <text>.?B -void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd) -{ - char command[CHAT_SIZE_MAX+16]; - char* msg = (char*)RFIFOP(fd,4); - unsigned int len = RFIFOW(fd,2)-4; - - // as the length varies depending on the command used, just block unreasonably long strings - mes_len_check(msg, len, CHAT_SIZE_MAX); - - sprintf(command, "%clkami %s", atcommand_symbol, msg); - is_atcommand(fd, sd, command, 1); -} - - -/// Request to move an item from inventory to storage. -/// 00f3 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE) -/// 0364 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) -{ - int item_index, item_amount; - - if (pc_istrading(sd)) - return; - - item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2; - item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]); - if (item_index < 0 || item_index >= MAX_INVENTORY || item_amount < 1) - return; - - if (sd->state.storage_flag == 1) - storage_storageadd(sd, item_index, item_amount); - else - if (sd->state.storage_flag == 2) - storage_guild_storageadd(sd, item_index, item_amount); -} - - -/// Request to move an item from storage to inventory. -/// 00f5 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY) -/// 0365 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY2) -/// There are various variants of this packet, some of them have padding between fields. -void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd) -{ - int item_index, item_amount; - - item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-1; - item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]); - - if (sd->state.storage_flag == 1) - storage_storageget(sd, item_index, item_amount); - else - if(sd->state.storage_flag == 2) - storage_guild_storageget(sd, item_index, item_amount); -} - - -/// Request to move an item from cart to storage (CZ_MOVE_ITEM_FROM_CART_TO_STORE). -/// 0129 <index>.W <amount>.L -void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) -{ - if( sd->state.vending ) - return; - if (!pc_iscarton(sd)) - return; - - if (sd->state.storage_flag == 1) - storage_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4)); - else - if (sd->state.storage_flag == 2) - storage_guild_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4)); -} - - -/// Request to move an item from storage to cart (CZ_MOVE_ITEM_FROM_STORE_TO_CART). -/// 0128 <index>.W <amount>.L -void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) -{ - if( sd->state.vending ) - return; - if (!pc_iscarton(sd)) - return; - - if (sd->state.storage_flag == 1) - storage_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4)); - else - if (sd->state.storage_flag == 2) - storage_guild_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4)); -} - - -/// Request to close storage (CZ_CLOSE_STORE). -/// 00f7 -void clif_parse_CloseKafra(int fd, struct map_session_data *sd) -{ - if( sd->state.storage_flag == 1 ) - storage_storageclose(sd); - else - if( sd->state.storage_flag == 2 ) - storage_guild_storageclose(sd); -} - - -/// Displays kafra storage password dialog (ZC_REQ_STORE_PASSWORD). -/// 023a <info>.W -/// info: -/// 0 = password has not been set yet -/// 1 = storage is password-protected -/// 8 = too many wrong passwords -/// ? = ignored -/// NOTE: This packet is only available on certain non-kRO clients. -void clif_storagepassword(struct map_session_data* sd, short info) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x23a)); - WFIFOW(fd,0) = 0x23a; - WFIFOW(fd,2) = info; - WFIFOSET(fd,packet_len(0x23a)); -} - - -/// Answer to the kafra storage password dialog (CZ_ACK_STORE_PASSWORD). -/// 023b <type>.W <password>.16B <new password>.16B -/// type: -/// 2 = change password -/// 3 = check password -/// NOTE: This packet is only available on certain non-kRO clients. -void clif_parse_StoragePassword(int fd, struct map_session_data *sd) -{ - //TODO -} - - -/// Result of kafra storage password validation (ZC_RESULT_STORE_PASSWORD). -/// 023c <result>.W <error count>.W -/// result: -/// 4 = password change success -/// 5 = password change failure -/// 6 = password check success -/// 7 = password check failure -/// 8 = too many wrong passwords -/// ? = ignored -/// NOTE: This packet is only available on certain non-kRO clients. -void clif_storagepassword_result(struct map_session_data* sd, short result, short error_count) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x23c)); - WFIFOW(fd,0) = 0x23c; - WFIFOW(fd,2) = result; - WFIFOW(fd,4) = error_count; - WFIFOSET(fd,packet_len(0x23c)); -} - - -/// Party creation request -/// 00f9 <party name>.24B (CZ_MAKE_GROUP) -/// 01e8 <party name>.24B <item pickup rule>.B <item share rule>.B (CZ_MAKE_GROUP2) -void clif_parse_CreateParty(int fd, struct map_session_data *sd) -{ - char* name = (char*)RFIFOP(fd,2); - name[NAME_LENGTH-1] = '\0'; - - if( map[sd->bl.m].flag.partylock ) - {// Party locked. - clif_displaymessage(fd, msg_txt(227)); - return; - } - if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 ) - { - clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4); - return; - } - - party_create(sd,name,0,0); -} - -void clif_parse_CreateParty2(int fd, struct map_session_data *sd) -{ - char* name = (char*)RFIFOP(fd,2); - int item1 = RFIFOB(fd,26); - int item2 = RFIFOB(fd,27); - name[NAME_LENGTH-1] = '\0'; - - if( map[sd->bl.m].flag.partylock ) - {// Party locked. - clif_displaymessage(fd, msg_txt(227)); - return; - } - if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 ) - { - clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4); - return; - } - - party_create(sd,name,item1,item2); -} - - -/// Party invitation request -/// 00fc <account id>.L (CZ_REQ_JOIN_GROUP) -/// 02c4 <char name>.24B (CZ_PARTY_JOIN_REQ) -void clif_parse_PartyInvite(int fd, struct map_session_data *sd) -{ - struct map_session_data *t_sd; - - if(map[sd->bl.m].flag.partylock) - {// Party locked. - clif_displaymessage(fd, msg_txt(227)); - return; - } - - t_sd = map_id2sd(RFIFOL(fd,2)); - - if(t_sd && t_sd->state.noask) - {// @noask [LuzZza] - clif_noask_sub(sd, t_sd, 1); - return; - } - - party_invite(sd, t_sd); -} - -void clif_parse_PartyInvite2(int fd, struct map_session_data *sd) -{ - struct map_session_data *t_sd; - char *name = (char*)RFIFOP(fd,2); - name[NAME_LENGTH-1] = '\0'; - - if(map[sd->bl.m].flag.partylock) - {// Party locked. - clif_displaymessage(fd, msg_txt(227)); - return; - } - - t_sd = map_nick2sd(name); - - if(t_sd && t_sd->state.noask) - {// @noask [LuzZza] - clif_noask_sub(sd, t_sd, 1); - return; - } - - party_invite(sd, t_sd); -} - - -/// Party invitation reply -/// 00ff <party id>.L <flag>.L (CZ_JOIN_GROUP) -/// 02c7 <party id>.L <flag>.B (CZ_PARTY_JOIN_REQ_ACK) -/// flag: -/// 0 = reject -/// 1 = accept -void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd) -{ - party_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6)); -} - -void clif_parse_ReplyPartyInvite2(int fd,struct map_session_data *sd) -{ - party_reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6)); -} - - -/// Request to leave party (CZ_REQ_LEAVE_GROUP). -/// 0100 -void clif_parse_LeaveParty(int fd, struct map_session_data *sd) -{ - if(map[sd->bl.m].flag.partylock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(227)); - return; - } - party_leave(sd); -} - - -/// Request to expel a party member (CZ_REQ_EXPEL_GROUP_MEMBER). -/// 0103 <account id>.L <char name>.24B -void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) -{ - if(map[sd->bl.m].flag.partylock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(227)); - return; - } - party_removemember(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6)); -} - - -/// Request to change party options. -/// 0102 <exp share rule>.L (CZ_CHANGE_GROUPEXPOPTION) -/// 07d7 <exp share rule>.L <item pickup rule>.B <item share rule>.B (CZ_GROUPINFO_CHANGE_V2) -void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) -{ - struct party_data *p; - int i; - - if( !sd->status.party_id ) - return; - - p = party_search(sd->status.party_id); - if( p == NULL ) - return; - - ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd ); - if( i == MAX_PARTY ) - return; //Shouldn't happen - - if( !p->party.member[i].leader ) - return; - -#if PACKETVER < 20090603 - //Client can't change the item-field - party_changeoption(sd, RFIFOL(fd,2), p->party.item); -#else - party_changeoption(sd, RFIFOL(fd,2), ((RFIFOB(fd,6)?1:0)|(RFIFOB(fd,7)?2:0))); -#endif -} - - -/// Validates and processes party messages (CZ_REQUEST_CHAT_PARTY). -/// 0108 <packet len>.W <text>.?B (<name> : <message>) 00 -void clif_parse_PartyMessage(int fd, struct map_session_data* sd) -{ - const char* text = (char*)RFIFOP(fd,4); - int textlen = RFIFOW(fd,2) - 4; - - char *name, *message; - int namelen, messagelen; - - // validate packet and retrieve name and message - if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) ) - return; - - if( is_atcommand(fd, sd, message, 1) ) - return; - - if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) ) - return; - - if( battle_config.min_chat_delay ) - { //[Skotlex] - if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) - return; - sd->cantalk_tick = gettick() + battle_config.min_chat_delay; - } - - party_send_message(sd, text, textlen); -} - - -/// Changes Party Leader (CZ_CHANGE_GROUP_MASTER). -/// 07da <account id>.L -void clif_parse_PartyChangeLeader(int fd, struct map_session_data* sd) -{ - party_changeleader(sd, map_id2sd(RFIFOL(fd,2))); -} - - -/// Party Booking in KRO [Spiria] -/// - -/// Request to register a party booking advertisment (CZ_PARTY_BOOKING_REQ_REGISTER). -/// 0802 <level>.W <map id>.W { <job>.W }*6 -void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data* sd) -{ - short level = RFIFOW(fd,2); - short mapid = RFIFOW(fd,4); - short job[PARTY_BOOKING_JOBS]; - int i; - - for(i=0; i<PARTY_BOOKING_JOBS; i++) - job[i] = RFIFOB(fd,6+i*2); - - party_booking_register(sd, level, mapid, job); -} - - -/// Result of request to register a party booking advertisment (ZC_PARTY_BOOKING_ACK_REGISTER). -/// 0803 <result>.W -/// result: -/// 0 = success -/// 1 = failure -/// 2 = already registered -void clif_PartyBookingRegisterAck(struct map_session_data *sd, int flag) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x803)); - WFIFOW(fd,0) = 0x803; - WFIFOW(fd,2) = flag; - WFIFOSET(fd,packet_len(0x803)); -} - - -/// Request to search for party booking advertisments (CZ_PARTY_BOOKING_REQ_SEARCH). -/// 0804 <level>.W <map id>.W <job>.W <last index>.L <result count>.W -void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data* sd) -{ - short level = RFIFOW(fd,2); - short mapid = RFIFOW(fd,4); - short job = RFIFOW(fd,6); - unsigned long lastindex = RFIFOL(fd,8); - short resultcount = RFIFOW(fd,12); - - party_booking_search(sd, level, mapid, job, lastindex, resultcount); -} - - -/// Party booking search results (ZC_PARTY_BOOKING_ACK_SEARCH). -/// 0805 <packet len>.W <more results>.B { <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6 }* -/// more results: -/// 0 = no -/// 1 = yes -void clif_PartyBookingSearchAck(int fd, struct party_booking_ad_info** results, int count, bool more_result) -{ - int i, j; - int size = sizeof(struct party_booking_ad_info); // structure size (48) - struct party_booking_ad_info *pb_ad; - WFIFOHEAD(fd,size*count + 5); - WFIFOW(fd,0) = 0x805; - WFIFOW(fd,2) = size*count + 5; - WFIFOB(fd,4) = more_result; - for(i=0; i<count; i++) - { - pb_ad = results[i]; - WFIFOL(fd,i*size+5) = pb_ad->index; - memcpy(WFIFOP(fd,i*size+9),pb_ad->charname,NAME_LENGTH); - WFIFOL(fd,i*size+33) = pb_ad->starttime; // FIXME: This is expire time - WFIFOW(fd,i*size+37) = pb_ad->p_detail.level; - WFIFOW(fd,i*size+39) = pb_ad->p_detail.mapid; - for(j=0; j<PARTY_BOOKING_JOBS; j++) - WFIFOW(fd,i*size+41+j*2) = pb_ad->p_detail.job[j]; - } - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Request to delete own party booking advertisment (CZ_PARTY_BOOKING_REQ_DELETE). -/// 0806 -void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data* sd) -{ - if(party_booking_delete(sd)) - clif_PartyBookingDeleteAck(sd, 0); -} - - -/// Result of request to delete own party booking advertisment (ZC_PARTY_BOOKING_ACK_DELETE). -/// 0807 <result>.W -/// result: -/// 0 = success -/// 1 = success (auto-removed expired ad) -/// 2 = failure -/// 3 = nothing registered -void clif_PartyBookingDeleteAck(struct map_session_data* sd, int flag) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x807)); - WFIFOW(fd,0) = 0x807; - WFIFOW(fd,2) = flag; - WFIFOSET(fd,packet_len(0x807)); -} - - -/// Request to update party booking advertisment (CZ_PARTY_BOOKING_REQ_UPDATE). -/// 0808 { <job>.W }*6 -void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data* sd) -{ - short job[PARTY_BOOKING_JOBS]; - int i; - - for(i=0; i<PARTY_BOOKING_JOBS; i++) - job[i] = RFIFOW(fd,2+i*2); - - party_booking_update(sd, job); -} - - -/// Notification about new party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_INSERT). -/// 0809 <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6 -void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad) -{ - int i; - uint8 buf[38+PARTY_BOOKING_JOBS*2]; - - if(pb_ad == NULL) return; - - WBUFW(buf,0) = 0x809; - WBUFL(buf,2) = pb_ad->index; - memcpy(WBUFP(buf,6),pb_ad->charname,NAME_LENGTH); - WBUFL(buf,30) = pb_ad->starttime; // FIXME: This is expire time - WBUFW(buf,34) = pb_ad->p_detail.level; - WBUFW(buf,36) = pb_ad->p_detail.mapid; - for(i=0; i<PARTY_BOOKING_JOBS; i++) - WBUFW(buf,38+i*2) = pb_ad->p_detail.job[i]; - - clif_send(buf, packet_len(0x809), &sd->bl, ALL_CLIENT); -} - - -/// Notification about updated party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_UPDATE). -/// 080a <index>.L { <job>.W }*6 -void clif_PartyBookingUpdateNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad) -{ - int i; - uint8 buf[6+PARTY_BOOKING_JOBS*2]; - - if(pb_ad == NULL) return; - - WBUFW(buf,0) = 0x80a; - WBUFL(buf,2) = pb_ad->index; - for(i=0; i<PARTY_BOOKING_JOBS; i++) - WBUFW(buf,6+i*2) = pb_ad->p_detail.job[i]; - clif_send(buf,packet_len(0x80a),&sd->bl,ALL_CLIENT); // Now UPDATE all client. -} - - -/// Notification about deleted party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_DELETE). -/// 080b <index>.L -void clif_PartyBookingDeleteNotify(struct map_session_data* sd, int index) -{ - uint8 buf[6]; - - WBUFW(buf,0) = 0x80b; - WBUFL(buf,2) = index; - - clif_send(buf, packet_len(0x80b), &sd->bl, ALL_CLIENT); // Now UPDATE all client. -} - - -/// Request to close own vending (CZ_REQ_CLOSESTORE). -/// 012e -void clif_parse_CloseVending(int fd, struct map_session_data* sd) -{ - vending_closevending(sd); -} - - -/// Request to open a vending shop (CZ_REQ_BUY_FROMMC). -/// 0130 <account id>.L -void clif_parse_VendingListReq(int fd, struct map_session_data* sd) -{ - if( sd->npc_id ) - {// using an NPC - return; - } - vending_vendinglistreq(sd,RFIFOL(fd,2)); -} - - -/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC). -/// 0134 <packet len>.W <account id>.L { <amount>.W <index>.W }* -void clif_parse_PurchaseReq(int fd, struct map_session_data* sd) -{ - int len = (int)RFIFOW(fd,2) - 8; - int id = (int)RFIFOL(fd,4); - const uint8* data = (uint8*)RFIFOP(fd,8); - - vending_purchasereq(sd, id, sd->vended_id, data, len/4); - - // whether it fails or not, the buy window is closed - sd->vended_id = 0; -} - - -/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC2). -/// 0801 <packet len>.W <account id>.L <unique id>.L { <amount>.W <index>.W }* -void clif_parse_PurchaseReq2(int fd, struct map_session_data* sd) -{ - int len = (int)RFIFOW(fd,2) - 12; - int aid = (int)RFIFOL(fd,4); - int uid = (int)RFIFOL(fd,8); - const uint8* data = (uint8*)RFIFOP(fd,12); - - vending_purchasereq(sd, aid, uid, data, len/4); - - // whether it fails or not, the buy window is closed - sd->vended_id = 0; -} - - -/// Confirm or cancel the shop preparation window. -/// 012f <packet len>.W <shop name>.80B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE) -/// 01b2 <packet len>.W <shop name>.80B <result>.B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE2) -/// result: -/// 0 = canceled -/// 1 = open -void clif_parse_OpenVending(int fd, struct map_session_data* sd) -{ - short len = (short)RFIFOW(fd,2) - 85; - const char* message = (char*)RFIFOP(fd,4); - bool flag = (bool)RFIFOB(fd,84); - const uint8* data = (uint8*)RFIFOP(fd,85); - - if( sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM ) - return; - 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( vending_checknearnpc(&sd->bl) ) { - char output[150]; - sprintf(output, msg_txt(662), battle_config.min_npc_vending_distance); - clif_displaymessage(sd->fd, output); - clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); - return; - } - - if( message[0] == '\0' ) // invalid input - return; - - vending_openvending(sd, message, flag, data, len/8); -} - - -/// Guild creation request (CZ_REQ_MAKE_GUILD). -/// 0165 <char id>.L <guild name>.24B -void clif_parse_CreateGuild(int fd,struct map_session_data *sd) -{ - char* name = (char*)RFIFOP(fd,6); - name[NAME_LENGTH-1] = '\0'; - - if(map[sd->bl.m].flag.guildlock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - - guild_create(sd, name); -} - - -/// Request for guild window interface permissions (CZ_REQ_GUILD_MENUINTERFACE). -/// 014d -void clif_parse_GuildCheckMaster(int fd, struct map_session_data *sd) -{ - clif_guild_masterormember(sd); -} - - -/// Request for guild window information (CZ_REQ_GUILD_MENU). -/// 014f <type>.L -/// type: -/// 0 = basic info -/// 1 = member manager -/// 2 = positions -/// 3 = skills -/// 4 = expulsion list -/// 5 = unknown (GM_ALLGUILDLIST) -/// 6 = notice -void clif_parse_GuildRequestInfo(int fd, struct map_session_data *sd) -{ - if( !sd->status.guild_id && !sd->bg_id ) - return; - - switch( RFIFOL(fd,2) ) - { - case 0: // Basic Information Guild, hostile alliance information - clif_guild_basicinfo(sd); - clif_guild_allianceinfo(sd); - break; - case 1: // Members list, list job title - clif_guild_positionnamelist(sd); - clif_guild_memberlist(sd); - break; - case 2: // List job title, title information list - clif_guild_positionnamelist(sd); - clif_guild_positioninfolist(sd); - break; - case 3: // Skill list - clif_guild_skillinfo(sd); - break; - case 4: // Expulsion list - clif_guild_expulsionlist(sd); - break; - default: - ShowError("clif: guild request info: unknown type %d\n", RFIFOL(fd,2)); - break; - } -} - - -/// Request to update guild positions (CZ_REG_CHANGE_GUILD_POSITIONINFO). -/// 0161 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <name>.24B }* -void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd) -{ - int i; - - if(!sd->state.gmaster_flag) - return; - - for(i = 4; i < RFIFOW(fd,2); i += 40 ){ - guild_change_position(sd->status.guild_id, RFIFOL(fd,i), RFIFOL(fd,i+4), RFIFOL(fd,i+12), (char*)RFIFOP(fd,i+16)); - } -} - - -/// Request to update the position of guild members (CZ_REQ_CHANGE_MEMBERPOS). -/// 0155 <packet len>.W { <account id>.L <char id>.L <position id>.L }* -void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd) -{ - int i; - - if(!sd->state.gmaster_flag) - return; - - for(i=4;i<RFIFOW(fd,2);i+=12){ - guild_change_memberposition(sd->status.guild_id, - RFIFOL(fd,i),RFIFOL(fd,i+4),RFIFOL(fd,i+8)); - } -} - - -/// Request for guild emblem data (CZ_REQ_GUILD_EMBLEM_IMG). -/// 0151 <guild id>.L -void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd) -{ - struct guild* g; - int guild_id = RFIFOL(fd,2); - - if( (g = guild_search(guild_id)) != NULL ) - clif_guild_emblem(sd,g); -} - - -/// Validates data of a guild emblem (compressed bitmap) -static bool clif_validate_emblem(const uint8* emblem, unsigned long emblem_len) -{ - bool success; - uint8 buf[1800]; // no well-formed emblem bitmap is larger than 1782 (24 bit) / 1654 (8 bit) bytes - unsigned long buf_len = sizeof(buf); - - success = ( decode_zip(buf, &buf_len, emblem, emblem_len) == 0 && buf_len >= 18 ) // sizeof(BITMAPFILEHEADER) + sizeof(biSize) of the following info header struct - && RBUFW(buf,0) == 0x4d42 // BITMAPFILEHEADER.bfType (signature) - && RBUFL(buf,2) == buf_len // BITMAPFILEHEADER.bfSize (file size) - && RBUFL(buf,10) < buf_len // BITMAPFILEHEADER.bfOffBits (offset to bitmap bits) - ; - - return success; -} - - -/// Request to update the guild emblem (CZ_REGISTER_GUILD_EMBLEM_IMG). -/// 0153 <packet len>.W <emblem data>.?B -void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd) -{ - unsigned long emblem_len = RFIFOW(fd,2)-4; - const uint8* emblem = RFIFOP(fd,4); - - if( !emblem_len || !sd->state.gmaster_flag ) - return; - - if( !clif_validate_emblem(emblem, emblem_len) ) - { - ShowWarning("clif_parse_GuildChangeEmblem: Rejected malformed guild emblem (size=%lu, accound_id=%d, char_id=%d, guild_id=%d).\n", emblem_len, sd->status.account_id, sd->status.char_id, sd->status.guild_id); - return; - } - - guild_change_emblem(sd, emblem_len, (const char*)emblem); -} - - -/// Guild notice update request (CZ_GUILD_NOTICE). -/// 016e <guild id>.L <msg1>.60B <msg2>.120B -void clif_parse_GuildChangeNotice(int fd, struct map_session_data* sd) -{ - int guild_id = RFIFOL(fd,2); - char* msg1 = (char*)RFIFOP(fd,6); - char* msg2 = (char*)RFIFOP(fd,66); - - if(!sd->state.gmaster_flag) - return; - - // compensate for some client defects when using multilanguage mode - if (msg1[0] == '|' && msg1[3] == '|') msg1+= 3; // skip duplicate marker - if (msg2[0] == '|' && msg2[3] == '|') msg2+= 3; // skip duplicate marker - if (msg2[0] == '|') msg2[strnlen(msg2, MAX_GUILDMES2)-1] = '\0'; // delete extra space at the end of string - - guild_change_notice(sd, guild_id, msg1, msg2); -} - - -/// Guild invite request (CZ_REQ_JOIN_GUILD). -/// 0168 <account id>.L <inviter account id>.L <inviter char id>.L -void clif_parse_GuildInvite(int fd,struct map_session_data *sd) -{ - struct map_session_data *t_sd; - - if(map[sd->bl.m].flag.guildlock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - - t_sd = map_id2sd(RFIFOL(fd,2)); - - // @noask [LuzZza] - if(t_sd && t_sd->state.noask) { - clif_noask_sub(sd, t_sd, 2); - return; - } - - guild_invite(sd,t_sd); -} - - -/// Answer to guild invitation (CZ_JOIN_GUILD). -/// 016b <guild id>.L <answer>.L -/// answer: -/// 0 = refuse -/// 1 = accept -void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd) -{ - guild_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6)); -} - - -/// Request to leave guild (CZ_REQ_LEAVE_GUILD). -/// 0159 <guild id>.L <account id>.L <char id>.L <reason>.40B -void clif_parse_GuildLeave(int fd,struct map_session_data *sd) -{ - if(map[sd->bl.m].flag.guildlock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - if( sd->bg_id ) - { - clif_displaymessage(fd, msg_txt(670)); //"You can't leave battleground guilds." - return; - } - - guild_leave(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14)); -} - - -/// Request to expel a member of a guild (CZ_REQ_BAN_GUILD). -/// 015b <guild id>.L <account id>.L <char id>.L <reason>.40B -void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd) -{ - if( map[sd->bl.m].flag.guildlock || sd->bg_id ) - { // Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - guild_expulsion(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14)); -} - - -/// Validates and processes guild messages (CZ_GUILD_CHAT). -/// 017e <packet len>.W <text>.?B (<name> : <message>) 00 -void clif_parse_GuildMessage(int fd, struct map_session_data* sd) -{ - const char* text = (char*)RFIFOP(fd,4); - int textlen = RFIFOW(fd,2) - 4; - - char *name, *message; - int namelen, messagelen; - - // validate packet and retrieve name and message - if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) ) - return; - - if( is_atcommand(fd, sd, message, 1) ) - return; - - if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) ) - return; - - if( battle_config.min_chat_delay ) - { //[Skotlex] - if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) - return; - sd->cantalk_tick = gettick() + battle_config.min_chat_delay; - } - - if( sd->bg_id ) - bg_send_message(sd, text, textlen); - else - guild_send_message(sd, text, textlen); -} - - -/// Guild alliance request (CZ_REQ_ALLY_GUILD). -/// 0170 <account id>.L <inviter account id>.L <inviter char id>.L -void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) -{ - struct map_session_data *t_sd; - - if(!sd->state.gmaster_flag) - return; - - if(map[sd->bl.m].flag.guildlock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - - t_sd = map_id2sd(RFIFOL(fd,2)); - - // @noask [LuzZza] - if(t_sd && t_sd->state.noask) { - clif_noask_sub(sd, t_sd, 3); - return; - } - - guild_reqalliance(sd,t_sd); -} - - -/// Answer to a guild alliance request (CZ_ALLY_GUILD). -/// 0172 <inviter account id>.L <answer>.L -/// answer: -/// 0 = refuse -/// 1 = accept -void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd) -{ - guild_reply_reqalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6)); -} - - -/// Request to delete a guild alliance or opposition (CZ_REQ_DELETE_RELATED_GUILD). -/// 0183 <opponent guild id>.L <relation>.L -/// relation: -/// 0 = Ally -/// 1 = Enemy -void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) -{ - if(!sd->state.gmaster_flag) - return; - - if(map[sd->bl.m].flag.guildlock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - guild_delalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6)); -} - - -/// Request to set a guild as opposition (CZ_REQ_HOSTILE_GUILD). -/// 0180 <account id>.L -void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) -{ - struct map_session_data *t_sd; - - if(!sd->state.gmaster_flag) - return; - - if(map[sd->bl.m].flag.guildlock) - { //Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - - t_sd = map_id2sd(RFIFOL(fd,2)); - - // @noask [LuzZza] - if(t_sd && t_sd->state.noask) { - clif_noask_sub(sd, t_sd, 4); - return; - } - - guild_opposition(sd,t_sd); -} - - -/// Request to delete own guild (CZ_REQ_DISORGANIZE_GUILD). -/// 015d <key>.40B -/// key: -/// now guild name; might have been (intended) email, since the -/// field name and size is same as the one in CH_DELETE_CHAR. -void clif_parse_GuildBreak(int fd, struct map_session_data *sd) -{ - if( map[sd->bl.m].flag.guildlock ) - { //Guild locked. - clif_displaymessage(fd, msg_txt(228)); - return; - } - guild_break(sd,(char*)RFIFOP(fd,2)); -} - - -/// Pet -/// - -/// Request to invoke a pet menu action (CZ_COMMAND_PET). -/// 01a1 <type>.B -/// type: -/// 0 = pet information -/// 1 = feed -/// 2 = performance -/// 3 = return to egg -/// 4 = unequip accessory -void clif_parse_PetMenu(int fd, struct map_session_data *sd) -{ - pet_menu(sd,RFIFOB(fd,2)); -} - - -/// Attempt to tame a monster (CZ_TRYCAPTURE_MONSTER). -/// 019f <id>.L -void clif_parse_CatchPet(int fd, struct map_session_data *sd) -{ - pet_catch_process2(sd,RFIFOL(fd,2)); -} - - -/// Answer to pet incubator egg selection dialog (CZ_SELECT_PETEGG). -/// 01a7 <index>.W -void clif_parse_SelectEgg(int fd, struct map_session_data *sd) -{ - if (sd->menuskill_id != SA_TAMINGMONSTER || sd->menuskill_val != -1) - { - //Forged packet, disconnect them [Kevin] - clif_authfail_fd(fd, 0); - return; - } - pet_select_egg(sd,RFIFOW(fd,2)-2); - clif_menuskill_clear(sd); -} - - -/// Request to display pet's emotion/talk (CZ_PET_ACT). -/// 01a9 <data>.L -/// data: -/// is either emotion (@see enum emotion_type) or a compound value -/// (((mob id)-100)*100+(act id)*10+(hunger)) that describes an -/// entry (given in parentheses) in data\pettalktable.xml -/// act id: -/// 0 = feeding -/// 1 = hunting -/// 2 = danger -/// 3 = dead -/// 4 = normal (stand) -/// 5 = special performance (perfor_s) -/// 6 = level up (levelup) -/// 7 = performance 1 (perfor_1) -/// 8 = performance 2 (perfor_2) -/// 9 = performance 3 (perfor_3) -/// 10 = log-in greeting (connect) -/// hungry value: -/// 0 = very hungry (hungry) -/// 1 = hungry (bit_hungry) -/// 2 = satisfied (noting) -/// 3 = stuffed (full) -/// 4 = full (so_full) -void clif_parse_SendEmotion(int fd, struct map_session_data *sd) -{ - if(sd->pd) - clif_pet_emotion(sd->pd,RFIFOL(fd,2)); -} - - -/// Request to change pet's name (CZ_RENAME_PET). -/// 01a5 <name>.24B -void clif_parse_ChangePetName(int fd, struct map_session_data *sd) -{ - pet_change_name(sd,(char*)RFIFOP(fd,2)); -} - - -/// /kill (CZ_DISCONNECT_CHARACTER). -/// Request to disconnect a character. -/// 00cc <account id>.L -/// NOTE: Also sent when using GM right click menu "(name) force to quit" -void clif_parse_GMKick(int fd, struct map_session_data *sd) -{ - struct block_list *target; - int tid; - - tid = RFIFOL(fd,2); - target = map_id2bl(tid); - if (!target) { - clif_GM_kickack(sd, 0); - return; - } - - switch (target->type) { - case BL_PC: - { - char command[NAME_LENGTH+6]; - sprintf(command, "%ckick %s", atcommand_symbol, status_get_name(target)); - is_atcommand(fd, sd, command, 1); - } - break; - - /** - * This one does not invoke any atcommand, so we need to check for permissions. - */ - case BL_MOB: - { - char command[100]; - if( !pc_can_use_command(sd, "killmonster", COMMAND_ATCOMMAND)) { - clif_GM_kickack(sd, 0); - return; - } - sprintf(command, "/kick %s (%d)", status_get_name(target), status_get_class(target)); - log_atcommand(sd, command); - status_percent_damage(&sd->bl, target, 100, 0, true); // can invalidate 'target' - } - break; - - case BL_NPC: - { - char command[NAME_LENGTH+11]; - sprintf(command, "%cunloadnpc %s", atcommand_symbol, status_get_name(target)); - is_atcommand(fd, sd, command, 1); - } - break; - - default: - clif_GM_kickack(sd, 0); - } -} - - -/// /killall (CZ_DISCONNECT_ALL_CHARACTER). -/// Request to disconnect all characters. -/// 00ce -void clif_parse_GMKickAll(int fd, struct map_session_data* sd) { - char cmd[15]; - sprintf(cmd,"%ckickall",atcommand_symbol); - is_atcommand(fd, sd, cmd, 1); -} - - -/// /remove (CZ_REMOVE_AID). -/// Request to warp to a character with given login ID. -/// 01ba <account name>.24B - -/// /shift (CZ_SHIFT). -/// Request to warp to a character with given name. -/// 01bb <char name>.24B -void clif_parse_GMShift(int fd, struct map_session_data *sd) -{// FIXME: remove is supposed to receive account name for clients prior 20100803RE - char *player_name; - char command[NAME_LENGTH+8]; - - player_name = (char*)RFIFOP(fd,2); - player_name[NAME_LENGTH-1] = '\0'; - - sprintf(command, "%cjumpto %s", atcommand_symbol, player_name); - is_atcommand(fd, sd, command, 1); -} - - -/// /remove (CZ_REMOVE_AID_SSO). -/// Request to warp to a character with given account ID. -/// 0843 <account id>.L -void clif_parse_GMRemove2(int fd, struct map_session_data* sd) -{ - int account_id; - struct map_session_data* pl_sd; - - account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - if( (pl_sd = map_id2sd(account_id)) != NULL ) - { - char command[NAME_LENGTH+8]; - sprintf(command, "%cjumpto %s", atcommand_symbol, pl_sd->status.name); - is_atcommand(fd, sd, command, 1); - } -} - - -/// /recall (CZ_RECALL). -/// Request to summon a player with given login ID to own position. -/// 01bc <account name>.24B - -/// /summon (CZ_RECALL_GID). -/// Request to summon a player with given name to own position. -/// 01bd <char name>.24B -void clif_parse_GMRecall(int fd, struct map_session_data *sd) -{// FIXME: recall is supposed to receive account name for clients prior 20100803RE - char *player_name; - char command [NAME_LENGTH+8]; - - player_name = (char*)RFIFOP(fd,2); - player_name[NAME_LENGTH-1] = '\0'; - - sprintf(command, "%crecall %s", atcommand_symbol, player_name); - is_atcommand(fd, sd, command, 1); -} - - -/// /recall (CZ_RECALL_SSO). -/// Request to summon a player with given account ID to own position. -/// 0842 <account id>.L -void clif_parse_GMRecall2(int fd, struct map_session_data* sd) -{ - int account_id; - struct map_session_data* pl_sd; - - account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - if( (pl_sd = map_id2sd(account_id)) != NULL ) - { - char command[NAME_LENGTH+8]; - sprintf(command, "%crecall %s", atcommand_symbol, pl_sd->status.name); - is_atcommand(fd, sd, command, 1); - } -} - - -/// /item /monster (CZ_ITEM_CREATE). -/// Request to make items or spawn monsters. -/// 013f <item/mob name>.24B -void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) -{ - char *monster_item_name; - char command[NAME_LENGTH+10]; - - monster_item_name = (char*)RFIFOP(fd,2); - monster_item_name[NAME_LENGTH-1] = '\0'; - - // FIXME: Should look for item first, then for monster. - // FIXME: /monster takes mob_db Sprite_Name as argument - if( mobdb_searchname(monster_item_name) ) { - snprintf(command, sizeof(command)-1, "%cmonster %s", atcommand_symbol, monster_item_name); - is_atcommand(fd, sd, command, 1); - return; - } - // FIXME: Stackables have a quantity of 20. - // FIXME: Equips are supposed to be unidentified. - - if( itemdb_searchname(monster_item_name) ) { - snprintf(command, sizeof(command)-1, "%citem %s", atcommand_symbol, monster_item_name); - is_atcommand(fd, sd, command, 1); - return; - } -} - - -/// /hide (CZ_CHANGE_EFFECTSTATE). -/// 019d <effect state>.L -/// effect state: -/// TODO: Any OPTION_* ? -void clif_parse_GMHide(int fd, struct map_session_data *sd) { - char cmd[6]; - - sprintf(cmd,"%chide",atcommand_symbol); - - is_atcommand(fd, sd, cmd, 1); -} - - -/// Request to adjust player's manner points (CZ_REQ_GIVE_MANNER_POINT). -/// 0149 <account id>.L <type>.B <value>.W -/// type: -/// 0 = positive points -/// 1 = negative points -/// 2 = self mute (+10 minutes) -void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd) -{ - int id, type, value; - struct map_session_data *dstsd; - char command[NAME_LENGTH+15]; - - id = RFIFOL(fd,2); - type = RFIFOB(fd,6); - value = RFIFOW(fd,7); - - if( type == 0 ) - value = -value; - - //If type is 2 and the ids don't match, this is a crafted hacked packet! - //Disabled because clients keep self-muting when you give players public @ commands... [Skotlex] - if (type == 2 /* && (pc_get_group_level(sd) > 0 || sd->bl.id != id)*/) - return; - - dstsd = map_id2sd(id); - if( dstsd == NULL ) - return; - - sprintf(command, "%cmute %d %s", atcommand_symbol, value, dstsd->status.name); - is_atcommand(fd, sd, command, 1); -} - - -/// /rc (CZ_REQ_GIVE_MANNER_BYNAME). -/// GM adjustment of a player's manner value by -60. -/// 0212 <char name>.24B -void clif_parse_GMRc(int fd, struct map_session_data* sd) -{ - char command[NAME_LENGTH+15]; - char *name = (char*)RFIFOP(fd,2); - - name[NAME_LENGTH-1] = '\0'; - sprintf(command, "%cmute %d %s", atcommand_symbol, 60, name); - is_atcommand(fd, sd, command, 1); -} - - -/// Result of request to resolve account name (ZC_ACK_ACCOUNTNAME). -/// 01e0 <account id>.L <account name>.24B -void clif_account_name(struct map_session_data* sd, int account_id, const char* accname) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x1e0)); - WFIFOW(fd,0) = 0x1e0; - WFIFOL(fd,2) = account_id; - safestrncpy((char*)WFIFOP(fd,6), accname, NAME_LENGTH); - WFIFOSET(fd,packet_len(0x1e0)); -} - - -/// GM requesting account name (for right-click gm menu) (CZ_REQ_ACCOUNTNAME). -/// 01df <account id>.L -void clif_parse_GMReqAccountName(int fd, struct map_session_data *sd) -{ - int account_id = RFIFOL(fd,2); - - //TODO: find out if this works for any player or only for authorized GMs - clif_account_name(sd, account_id, ""); // insert account name here >_< -} - - -/// /changemaptype <x> <y> <type> (CZ_CHANGE_MAPTYPE). -/// GM single cell type change request. -/// 0198 <x>.W <y>.W <type>.W -/// type: -/// 0 = not walkable -/// 1 = walkable -void clif_parse_GMChangeMapType(int fd, struct map_session_data *sd) -{ - int x,y,type; - - if( pc_has_permission(sd, PC_PERM_USE_CHANGEMAPTYPE) ) - return; - - x = RFIFOW(fd,2); - y = RFIFOW(fd,4); - type = RFIFOW(fd,6); - - map_setgatcell(sd->bl.m,x,y,type); - clif_changemapcell(0,sd->bl.m,x,y,type,ALL_SAMEMAP); - //FIXME: once players leave the map, the client 'forgets' this information. -} - - -/// /in /ex (CZ_SETTING_WHISPER_PC). -/// Request to allow/deny whispers from a nick. -/// 00cf <nick>.24B <type>.B -/// type: -/// 0 = (/ex nick) deny speech from nick -/// 1 = (/in nick) allow speech from nick -void clif_parse_PMIgnore(int fd, struct map_session_data* sd) -{ - char* nick; - uint8 type; - int i; - - nick = (char*)RFIFOP(fd,2); // speed up - nick[NAME_LENGTH-1] = '\0'; // to be sure that the player name has at most 23 characters - type = RFIFOB(fd,26); - - if( type == 0 ) - { // Add name to ignore list (block) - if (strcmp(wisp_server_name, nick) == 0) { - clif_wisexin(sd, type, 1); // fail - return; - } - - // try to find a free spot, while checking for duplicates at the same time - ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 ); - if( i == MAX_IGNORE_LIST ) - {// no space for new entry - clif_wisexin(sd, type, 2); // too many blocks - return; - } - if( sd->ignore[i].name[0] != '\0' ) - {// name already exists - clif_wisexin(sd, type, 0); // Aegis reports success. - return; - } - - //Insert in position i - safestrncpy(sd->ignore[i].name, nick, NAME_LENGTH); - } - else - { // Remove name from ignore list (unblock) - - // find entry - ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 ); - if( i == MAX_IGNORE_LIST || sd->ignore[i].name[i] == '\0' ) - { //Not found - clif_wisexin(sd, type, 1); // fail - return; - } - // move everything one place down to overwrite removed entry - memmove(sd->ignore[i].name, sd->ignore[i+1].name, (MAX_IGNORE_LIST-i-1)*sizeof(sd->ignore[0].name)); - // wipe last entry - memset(sd->ignore[MAX_IGNORE_LIST-1].name, 0, sizeof(sd->ignore[0].name)); - } - - clif_wisexin(sd, type, 0); // success -} - - -/// /inall /exall (CZ_SETTING_WHISPER_STATE). -/// Request to allow/deny all whispers. -/// 00d0 <type>.B -/// type: -/// 0 = (/exall) deny all speech -/// 1 = (/inall) allow all speech -void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd) -{ - int type = RFIFOB(fd,2), flag; - - if( type == 0 ) - {// Deny all - if( sd->state.ignoreAll ) { - flag = 1; // fail - } else { - sd->state.ignoreAll = 1; - flag = 0; // success - } - } - else - {//Unblock everyone - if( sd->state.ignoreAll ) { - sd->state.ignoreAll = 0; - flag = 0; // success - } else { - if (sd->ignore[0].name[0] != '\0') - { //Wipe the ignore list. - memset(sd->ignore, 0, sizeof(sd->ignore)); - flag = 0; // success - } else { - flag = 1; // fail - } - } - } - - clif_wisall(sd, type, flag); -} - - -/// Whisper ignore list (ZC_WHISPER_LIST). -/// 00d4 <packet len>.W { <char name>.24B }* -void clif_PMIgnoreList(struct map_session_data* sd) -{ - int i, fd = sd->fd; - - WFIFOHEAD(fd,4+ARRAYLENGTH(sd->ignore)*NAME_LENGTH); - WFIFOW(fd,0) = 0xd4; - - for( i = 0; i < ARRAYLENGTH(sd->ignore) && sd->ignore[i].name[0]; i++ ) - { - memcpy(WFIFOP(fd,4+i*NAME_LENGTH), sd->ignore[i].name, NAME_LENGTH); - } - - WFIFOW(fd,2) = 4+i*NAME_LENGTH; - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Whisper ignore list request (CZ_REQ_WHISPER_LIST). -/// 00d3 -void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd) -{ - clif_PMIgnoreList(sd); -} - - -/// Request to invoke the /doridori recovery bonus (CZ_DORIDORI). -/// 01e7 -void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd) -{ - if (sd->state.doridori) return; - - switch (sd->class_&MAPID_UPPERMASK) - { - case MAPID_SOUL_LINKER: - case MAPID_STAR_GLADIATOR: - case MAPID_TAEKWON: - if (!sd->state.rest) - break; - case MAPID_SUPER_NOVICE: - sd->state.doridori=1; - break; - } -} - - -/// Request to invoke the effect of super novice's guardian angel prayer (CZ_CHOPOKGI). -/// 01ed -/// Note: This packet is caused by 7 lines of any text, followed by -/// the prayer and an another line of any text. The prayer is -/// defined by lines 790~793 in data\msgstringtable.txt -/// "Dear angel, can you hear my voice?" -/// "I am" (space separated player name) "Super Novice~" -/// "Help me out~ Please~ T_T" -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. ); - - if( percent && ( percent%100 ) == 0 ) - {// 10.0%, 20.0%, ..., 90.0% - sc_start(&sd->bl, status_skill2sc(MO_EXPLOSIONSPIRITS), 100, 17, skill_get_time(MO_EXPLOSIONSPIRITS, 5)); //Lv17-> +50 critical (noted by Poki) [Skotlex] - clif_skill_nodamage(&sd->bl, &sd->bl, MO_EXPLOSIONSPIRITS, 5, 1); // prayer always shows successful Lv5 cast and disregards noskill restrictions - } - } - } -} - - -/// Friends List -/// - -/// Toggles a single friend online/offline [Skotlex] (ZC_FRIENDS_STATE). -/// 0206 <account id>.L <char id>.L <state>.B -/// state: -/// 0 = online -/// 1 = offline -void clif_friendslist_toggle(struct map_session_data *sd,int account_id, int char_id, int online) -{ - int i, fd = sd->fd; - - //Seek friend. - for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id && - (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++); - - if(i == MAX_FRIENDS || sd->status.friends[i].char_id == 0) - return; //Not found - - WFIFOHEAD(fd,packet_len(0x206)); - WFIFOW(fd, 0) = 0x206; - WFIFOL(fd, 2) = sd->status.friends[i].account_id; - WFIFOL(fd, 6) = sd->status.friends[i].char_id; - WFIFOB(fd,10) = !online; //Yeah, a 1 here means "logged off", go figure... - WFIFOSET(fd, packet_len(0x206)); -} - - -//Subfunction called from clif_foreachclient to toggle friends on/off [Skotlex] -int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap) -{ - int account_id, char_id, online; - account_id = va_arg(ap, int); - char_id = va_arg(ap, int); - online = va_arg(ap, int); - clif_friendslist_toggle(sd, account_id, char_id, online); - return 0; -} - - -/// Sends the whole friends list (ZC_FRIENDS_LIST). -/// 0201 <packet len>.W { <account id>.L <char id>.L <name>.24B }* -void clif_friendslist_send(struct map_session_data *sd) -{ - int i = 0, n, fd = sd->fd; - - // Send friends list - WFIFOHEAD(fd, MAX_FRIENDS * 32 + 4); - WFIFOW(fd, 0) = 0x201; - for(i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id; i++) - { - WFIFOL(fd, 4 + 32 * i + 0) = sd->status.friends[i].account_id; - WFIFOL(fd, 4 + 32 * i + 4) = sd->status.friends[i].char_id; - memcpy(WFIFOP(fd, 4 + 32 * i + 8), &sd->status.friends[i].name, NAME_LENGTH); - } - - if (i) { - WFIFOW(fd,2) = 4 + 32 * i; - WFIFOSET(fd, WFIFOW(fd,2)); - } - - for (n = 0; n < i; n++) - { //Sending the online players - if (map_charid2sd(sd->status.friends[n].char_id)) - clif_friendslist_toggle(sd, sd->status.friends[n].account_id, sd->status.friends[n].char_id, 1); - } -} - - -/// Notification about the result of a friend add request (ZC_ADD_FRIENDS_LIST). -/// 0209 <result>.W <account id>.L <char id>.L <name>.24B -/// result: -/// 0 = MsgStringTable[821]="You have become friends with (%s)." -/// 1 = MsgStringTable[822]="(%s) does not want to be friends with you." -/// 2 = MsgStringTable[819]="Your Friend List is full." -/// 3 = MsgStringTable[820]="(%s)'s Friend List is full." -void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type) -{ - int fd; - nullpo_retv(sd); - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x209)); - WFIFOW(fd,0) = 0x209; - WFIFOW(fd,2) = type; - if (f_sd) - { - WFIFOL(fd,4) = f_sd->status.account_id; - WFIFOL(fd,8) = f_sd->status.char_id; - memcpy(WFIFOP(fd, 12), f_sd->status.name,NAME_LENGTH); - } - WFIFOSET(fd, packet_len(0x209)); -} - - -/// Asks a player for permission to be added as friend (ZC_REQ_ADD_FRIENDS). -/// 0207 <req account id>.L <req char id>.L <req char name>.24B -void clif_friendlist_req(struct map_session_data* sd, int account_id, int char_id, const char* name) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x207)); - WFIFOW(fd,0) = 0x207; - WFIFOL(fd,2) = account_id; - WFIFOL(fd,6) = char_id; - memcpy(WFIFOP(fd,10), name, NAME_LENGTH); - WFIFOSET(fd,packet_len(0x207)); -} - - -/// Request to add a player as friend (CZ_ADD_FRIENDS). -/// 0202 <name>.24B -void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd) -{ - struct map_session_data *f_sd; - int i; - - f_sd = map_nick2sd((char*)RFIFOP(fd,2)); - - // ensure that the request player's friend list is not full - ARR_FIND(0, MAX_FRIENDS, i, sd->status.friends[i].char_id == 0); - - if( i == MAX_FRIENDS ) { - clif_friendslist_reqack(sd, f_sd, 2); - return; - } - - // Friend doesn't exist (no player with this name) - if (f_sd == NULL) { - clif_displaymessage(fd, msg_txt(3)); - return; - } - - if( sd->bl.id == f_sd->bl.id ) - {// adding oneself as friend - return; - } - - // @noask [LuzZza] - if(f_sd->state.noask) { - clif_noask_sub(sd, f_sd, 5); - return; - } - - // Friend already exists - for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id != 0; i++) { - if (sd->status.friends[i].char_id == f_sd->status.char_id) { - clif_displaymessage(fd, msg_txt(671)); //"Friend already exists." - return; - } - } - - f_sd->friend_req = sd->status.char_id; - sd->friend_req = f_sd->status.char_id; - - clif_friendlist_req(f_sd, sd->status.account_id, sd->status.char_id, sd->status.name); -} - - -/// Answer to a friend add request (CZ_ACK_REQ_ADD_FRIENDS). -/// 0208 <inviter account id>.L <inviter char id>.L <result>.B -/// 0208 <inviter account id>.L <inviter char id>.L <result>.L (PACKETVER >= 6) -/// result: -/// 0 = rejected -/// 1 = accepted -void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) -{ - struct map_session_data *f_sd; - int account_id; - char reply; - - account_id = RFIFOL(fd,2); - //char_id = RFIFOL(fd,6); -#if PACKETVER < 6 - reply = RFIFOB(fd,10); -#else - reply = RFIFOL(fd,10); -#endif - - if( sd->bl.id == account_id ) - {// adding oneself as friend - return; - } - - f_sd = map_id2sd(account_id); //The account id is the same as the bl.id of players. - if (f_sd == NULL) - return; - - if (reply == 0 || !( sd->friend_req == f_sd->status.char_id && f_sd->friend_req == sd->status.char_id ) ) - clif_friendslist_reqack(f_sd, sd, 1); - else { - int i; - // Find an empty slot - for (i = 0; i < MAX_FRIENDS; i++) - if (f_sd->status.friends[i].char_id == 0) - break; - if (i == MAX_FRIENDS) { - clif_friendslist_reqack(f_sd, sd, 2); - return; - } - - f_sd->status.friends[i].account_id = sd->status.account_id; - f_sd->status.friends[i].char_id = sd->status.char_id; - memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH); - clif_friendslist_reqack(f_sd, sd, 0); - - if (battle_config.friend_auto_add) { - // Also add f_sd to sd's friendlist. - for (i = 0; i < MAX_FRIENDS; i++) { - if (sd->status.friends[i].char_id == f_sd->status.char_id) - return; //No need to add anything. - if (sd->status.friends[i].char_id == 0) - break; - } - if (i == MAX_FRIENDS) { - clif_friendslist_reqack(sd, f_sd, 2); - return; - } - - sd->status.friends[i].account_id = f_sd->status.account_id; - sd->status.friends[i].char_id = f_sd->status.char_id; - memcpy(sd->status.friends[i].name, f_sd->status.name, NAME_LENGTH); - clif_friendslist_reqack(sd, f_sd, 0); - } - } -} - - -/// Request to delete a friend (CZ_DELETE_FRIENDS). -/// 0203 <account id>.L <char id>.L -void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd) -{ - struct map_session_data *f_sd = NULL; - int account_id, char_id; - int i, j; - - account_id = RFIFOL(fd,2); - char_id = RFIFOL(fd,6); - - // Search friend - for (i = 0; i < MAX_FRIENDS && - (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++); - - if (i == MAX_FRIENDS) { - clif_displaymessage(fd, msg_txt(672)); //"Name not found in list." - return; - } - - //remove from friend's list first - if( (f_sd = map_id2sd(account_id)) && f_sd->status.char_id == char_id) { - for (i = 0; i < MAX_FRIENDS && - (f_sd->status.friends[i].char_id != sd->status.char_id || f_sd->status.friends[i].account_id != sd->status.account_id); i++); - - if (i != MAX_FRIENDS) { - // move all chars up - for(j = i + 1; j < MAX_FRIENDS; j++) - memcpy(&f_sd->status.friends[j-1], &f_sd->status.friends[j], sizeof(f_sd->status.friends[0])); - - memset(&f_sd->status.friends[MAX_FRIENDS-1], 0, sizeof(f_sd->status.friends[MAX_FRIENDS-1])); - //should the guy be notified of some message? we should add it here if so - WFIFOHEAD(f_sd->fd,packet_len(0x20a)); - WFIFOW(f_sd->fd,0) = 0x20a; - WFIFOL(f_sd->fd,2) = sd->status.account_id; - WFIFOL(f_sd->fd,6) = sd->status.char_id; - WFIFOSET(f_sd->fd, packet_len(0x20a)); - } - - } else { //friend not online -- ask char server to delete from his friendlist - if(chrif_removefriend(char_id,sd->status.char_id)) { // char-server offline, abort - clif_displaymessage(fd, msg_txt(673)); //"This action can't be performed at the moment. Please try again later." - return; - } - } - - // We can now delete from original requester - for (i = 0; i < MAX_FRIENDS && - (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++); - // move all chars up - for(j = i + 1; j < MAX_FRIENDS; j++) - memcpy(&sd->status.friends[j-1], &sd->status.friends[j], sizeof(sd->status.friends[0])); - - memset(&sd->status.friends[MAX_FRIENDS-1], 0, sizeof(sd->status.friends[MAX_FRIENDS-1])); - clif_displaymessage(fd, msg_txt(674)); //"Friend removed" - - WFIFOHEAD(fd,packet_len(0x20a)); - WFIFOW(fd,0) = 0x20a; - WFIFOL(fd,2) = account_id; - WFIFOL(fd,6) = char_id; - WFIFOSET(fd, packet_len(0x20a)); -} - - -/// /pvpinfo list (ZC_ACK_PVPPOINT). -/// 0210 <char id>.L <account id>.L <win point>.L <lose point>.L <point>.L -void clif_PVPInfo(struct map_session_data* sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x210)); - WFIFOW(fd,0) = 0x210; - WFIFOL(fd,2) = sd->status.char_id; - WFIFOL(fd,6) = sd->status.account_id; - WFIFOL(fd,10) = sd->pvp_won; // times won - WFIFOL(fd,14) = sd->pvp_lost; // times lost - WFIFOL(fd,18) = sd->pvp_point; - WFIFOSET(fd, packet_len(0x210)); -} - - -/// /pvpinfo (CZ_REQ_PVPPOINT). -/// 020f <char id>.L <account id>.L -void clif_parse_PVPInfo(int fd,struct map_session_data *sd) -{ - // TODO: Is there a way to use this on an another player (char/acc id)? - clif_PVPInfo(sd); -} - - -/// /blacksmith list (ZC_BLACKSMITH_RANK). -/// 0219 { <name>.24B }*10 { <point>.L }*10 -void clif_blacksmith(struct map_session_data* sd) -{ - int i, fd = sd->fd; - const char* name; - - WFIFOHEAD(fd,packet_len(0x219)); - WFIFOW(fd,0) = 0x219; - //Packet size limits this list to 10 elements. [Skotlex] - for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) { - if (smith_fame_list[i].id > 0) { - if (strcmp(smith_fame_list[i].name, "-") == 0 && - (name = map_charid2nick(smith_fame_list[i].id)) != NULL) - { - strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), name, NAME_LENGTH); - } else - strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), smith_fame_list[i].name, NAME_LENGTH); - } else - strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "None", 5); - WFIFOL(fd, 242 + i * 4) = smith_fame_list[i].fame; - } - for(;i < 10; i++) { //In case the MAX is less than 10. - strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "Unavailable", 12); - WFIFOL(fd, 242 + i * 4) = 0; - } - - WFIFOSET(fd, packet_len(0x219)); -} - - -/// /blacksmith (CZ_BLACKSMITH_RANK). -/// 0217 -void clif_parse_Blacksmith(int fd,struct map_session_data *sd) -{ - clif_blacksmith(sd); -} - - -/// Notification about backsmith points (ZC_BLACKSMITH_POINT). -/// 021b <points>.L <total points>.L -void clif_fame_blacksmith(struct map_session_data *sd, int points) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x21b)); - WFIFOW(fd,0) = 0x21b; - WFIFOL(fd,2) = points; - WFIFOL(fd,6) = sd->status.fame; - WFIFOSET(fd, packet_len(0x21b)); -} - - -/// /alchemist list (ZC_ALCHEMIST_RANK). -/// 021a { <name>.24B }*10 { <point>.L }*10 -void clif_alchemist(struct map_session_data* sd) -{ - int i, fd = sd->fd; - const char* name; - - WFIFOHEAD(fd,packet_len(0x21a)); - WFIFOW(fd,0) = 0x21a; - //Packet size limits this list to 10 elements. [Skotlex] - for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) { - if (chemist_fame_list[i].id > 0) { - if (strcmp(chemist_fame_list[i].name, "-") == 0 && - (name = map_charid2nick(chemist_fame_list[i].id)) != NULL) - { - memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH); - } else - memcpy(WFIFOP(fd, 2 + 24 * i), chemist_fame_list[i].name, NAME_LENGTH); - } else - memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH); - WFIFOL(fd, 242 + i * 4) = chemist_fame_list[i].fame; - } - for(;i < 10; i++) { //In case the MAX is less than 10. - memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH); - WFIFOL(fd, 242 + i * 4) = 0; - } - - WFIFOSET(fd, packet_len(0x21a)); -} - - -/// /alchemist (CZ_ALCHEMIST_RANK). -/// 0218 -void clif_parse_Alchemist(int fd,struct map_session_data *sd) -{ - clif_alchemist(sd); -} - - -/// Notification about alchemist points (ZC_ALCHEMIST_POINT). -/// 021c <points>.L <total points>.L -void clif_fame_alchemist(struct map_session_data *sd, int points) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x21c)); - WFIFOW(fd,0) = 0x21c; - WFIFOL(fd,2) = points; - WFIFOL(fd,6) = sd->status.fame; - WFIFOSET(fd, packet_len(0x21c)); -} - - -/// /taekwon list (ZC_TAEKWON_RANK). -/// 0226 { <name>.24B }*10 { <point>.L }*10 -void clif_taekwon(struct map_session_data* sd) -{ - int i, fd = sd->fd; - const char* name; - - WFIFOHEAD(fd,packet_len(0x226)); - WFIFOW(fd,0) = 0x226; - //Packet size limits this list to 10 elements. [Skotlex] - for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) { - if (taekwon_fame_list[i].id > 0) { - if (strcmp(taekwon_fame_list[i].name, "-") == 0 && - (name = map_charid2nick(taekwon_fame_list[i].id)) != NULL) - { - memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH); - } else - memcpy(WFIFOP(fd, 2 + 24 * i), taekwon_fame_list[i].name, NAME_LENGTH); - } else - memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH); - WFIFOL(fd, 242 + i * 4) = taekwon_fame_list[i].fame; - } - for(;i < 10; i++) { //In case the MAX is less than 10. - memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH); - WFIFOL(fd, 242 + i * 4) = 0; - } - WFIFOSET(fd, packet_len(0x226)); -} - - -/// /taekwon (CZ_TAEKWON_RANK). -/// 0225 -void clif_parse_Taekwon(int fd,struct map_session_data *sd) -{ - clif_taekwon(sd); -} - - -/// Notification about taekwon points (ZC_TAEKWON_POINT). -/// 0224 <points>.L <total points>.L -void clif_fame_taekwon(struct map_session_data *sd, int points) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x224)); - WFIFOW(fd,0) = 0x224; - WFIFOL(fd,2) = points; - WFIFOL(fd,6) = sd->status.fame; - WFIFOSET(fd, packet_len(0x224)); -} - - -/// /pk list (ZC_KILLER_RANK). -/// 0238 { <name>.24B }*10 { <point>.L }*10 -void clif_ranking_pk(struct map_session_data* sd) -{ - int i, fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x238)); - WFIFOW(fd,0) = 0x238; - for(i=0;i<10;i++){ - memcpy(WFIFOP(fd,i*24+2), "Unknown", NAME_LENGTH); - WFIFOL(fd,i*4+242) = 0; - } - WFIFOSET(fd, packet_len(0x238)); -} - - -/// /pk (CZ_KILLER_RANK). -/// 0237 -void clif_parse_RankingPk(int fd,struct map_session_data *sd) -{ - clif_ranking_pk(sd); -} - - -/// SG Feel save OK [Komurka] (CZ_AGREE_STARPLACE). -/// 0254 <which>.B -/// which: -/// 0 = sun -/// 1 = moon -/// 2 = star -void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd) -{ - int i; - if (sd->menuskill_id != SG_FEEL) - return; - i = sd->menuskill_val-1; - if (i<0 || i >= MAX_PC_FEELHATE) return; //Bug? - - sd->feel_map[i].index = map_id2index(sd->bl.m); - sd->feel_map[i].m = sd->bl.m; - pc_setglobalreg(sd,sg_info[i].feel_var,sd->feel_map[i].index); - -//Are these really needed? Shouldn't they show up automatically from the feel save packet? -// clif_misceffect2(&sd->bl, 0x1b0); -// clif_misceffect2(&sd->bl, 0x21f); - clif_feel_info(sd, i, 0); - clif_menuskill_clear(sd); -} - - -/// Star Gladiator's Feeling map confirmation prompt (ZC_STARPLACE). -/// 0253 <which>.B -/// which: -/// 0 = sun -/// 1 = moon -/// 2 = star -void clif_feel_req(int fd, struct map_session_data *sd, uint16 skill_lv) -{ - WFIFOHEAD(fd,packet_len(0x253)); - WFIFOW(fd,0)=0x253; - WFIFOB(fd,2)=TOB(skill_lv-1); - WFIFOSET(fd, packet_len(0x253)); - sd->menuskill_id = SG_FEEL; - sd->menuskill_val = skill_lv; -} - - -/// Request to change homunculus' name (CZ_RENAME_MER). -/// 0231 <name>.24B -void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd) -{ - merc_hom_change_name(sd,(char*)RFIFOP(fd,2)); -} - - -/// Request to warp/move homunculus/mercenary to it's owner (CZ_REQUEST_MOVETOOWNER). -/// 0234 <id>.L -void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd) -{ - int id = RFIFOL(fd,2); // Mercenary or Homunculus - struct block_list *bl = NULL; - struct unit_data *ud = NULL; - - if( sd->md && sd->md->bl.id == id ) - bl = &sd->md->bl; - else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id ) - bl = &sd->hd->bl; // Moving Homunculus - else - return; - - unit_calc_pos(bl, sd->bl.x, sd->bl.y, sd->ud.dir); - ud = unit_bl2ud(bl); - unit_walktoxy(bl, ud->to_x, ud->to_y, 4); -} - - -/// Request to move homunculus/mercenary (CZ_REQUEST_MOVENPC). -/// 0232 <id>.L <position data>.3B -void clif_parse_HomMoveTo(int fd, struct map_session_data *sd) -{ - int id = RFIFOL(fd,2); // Mercenary or Homunculus - struct block_list *bl = NULL; - short x, y; - - RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1], &x, &y, NULL); - - if( sd->md && sd->md->bl.id == id ) - bl = &sd->md->bl; // Moving Mercenary - else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id ) - bl = &sd->hd->bl; // Moving Homunculus - else - return; - - unit_walktoxy(bl, x, y, 4); -} - - -/// Request to do an action with homunculus/mercenary (CZ_REQUEST_ACTNPC). -/// 0233 <id>.L <target id>.L <action>.B -/// action: -/// always 0 -void clif_parse_HomAttack(int fd,struct map_session_data *sd) -{ - struct block_list *bl = NULL; - int id = RFIFOL(fd,2), - target_id = RFIFOL(fd,6), - action_type = RFIFOB(fd,10); - - if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id ) - bl = &sd->hd->bl; - else if( sd->md && sd->md->bl.id == id ) - bl = &sd->md->bl; - else return; - - unit_stop_attack(bl); - unit_attack(bl, target_id, action_type != 0); -} - - -/// Request to invoke a homunculus menu action (CZ_COMMAND_MER). -/// 022d <type>.W <command>.B -/// type: -/// always 0 -/// command: -/// 0 = homunculus information -/// 1 = feed -/// 2 = delete -void clif_parse_HomMenu(int fd, struct map_session_data *sd) -{ //[orn] - int cmd; - - cmd = RFIFOW(fd,0); - - if(!merc_is_hom_active(sd->hd)) - return; - - merc_menu(sd,RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[1])); -} - - -/// Request to resurrect oneself using Token of Siegfried (CZ_STANDING_RESURRECTION). -/// 0292 -void clif_parse_AutoRevive(int fd, struct map_session_data *sd) -{ - int item_position = pc_search_inventory(sd, ITEMID_TOKEN_OF_SIEGFRIED); - - if (item_position < 0) - return; - - if (sd->sc.data[SC_HELLPOWER]) //Cannot res while under the effect of SC_HELLPOWER. - return; - - if (!status_revive(&sd->bl, 100, 100)) - return; - - clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1); - pc_delitem(sd, item_position, 1, 0, 1, LOG_TYPE_CONSUME); -} - - -/// Information about character's status values (ZC_ACK_STATUS_GM). -/// 0214 <str>.B <standardStr>.B <agi>.B <standardAgi>.B <vit>.B <standardVit>.B -/// <int>.B <standardInt>.B <dex>.B <standardDex>.B <luk>.B <standardLuk>.B -/// <attPower>.W <refiningPower>.W <max_mattPower>.W <min_mattPower>.W -/// <itemdefPower>.W <plusdefPower>.W <mdefPower>.W <plusmdefPower>.W -/// <hitSuccessValue>.W <avoidSuccessValue>.W <plusAvoidSuccessValue>.W -/// <criticalSuccessValue>.W <ASPD>.W <plusASPD>.W -void clif_check(int fd, struct map_session_data* pl_sd) -{ - WFIFOHEAD(fd,packet_len(0x214)); - WFIFOW(fd, 0) = 0x214; - WFIFOB(fd, 2) = min(pl_sd->status.str, UINT8_MAX); - WFIFOB(fd, 3) = pc_need_status_point(pl_sd, SP_STR, 1); - WFIFOB(fd, 4) = min(pl_sd->status.agi, UINT8_MAX); - WFIFOB(fd, 5) = pc_need_status_point(pl_sd, SP_AGI, 1); - WFIFOB(fd, 6) = min(pl_sd->status.vit, UINT8_MAX); - WFIFOB(fd, 7) = pc_need_status_point(pl_sd, SP_VIT, 1); - WFIFOB(fd, 8) = min(pl_sd->status.int_, UINT8_MAX); - WFIFOB(fd, 9) = pc_need_status_point(pl_sd, SP_INT, 1); - WFIFOB(fd,10) = min(pl_sd->status.dex, UINT8_MAX); - WFIFOB(fd,11) = pc_need_status_point(pl_sd, SP_DEX, 1); - WFIFOB(fd,12) = min(pl_sd->status.luk, UINT8_MAX); - WFIFOB(fd,13) = pc_need_status_point(pl_sd, SP_LUK, 1); - WFIFOW(fd,14) = pl_sd->battle_status.batk+pl_sd->battle_status.rhw.atk+pl_sd->battle_status.lhw.atk; - WFIFOW(fd,16) = pl_sd->battle_status.rhw.atk2+pl_sd->battle_status.lhw.atk2; - WFIFOW(fd,18) = pl_sd->battle_status.matk_max; - WFIFOW(fd,20) = pl_sd->battle_status.matk_min; - WFIFOW(fd,22) = pl_sd->battle_status.def; - WFIFOW(fd,24) = pl_sd->battle_status.def2; - WFIFOW(fd,26) = pl_sd->battle_status.mdef; - WFIFOW(fd,28) = pl_sd->battle_status.mdef2; - WFIFOW(fd,30) = pl_sd->battle_status.hit; - WFIFOW(fd,32) = pl_sd->battle_status.flee; - WFIFOW(fd,34) = pl_sd->battle_status.flee2/10; - WFIFOW(fd,36) = pl_sd->battle_status.cri/10; - WFIFOW(fd,38) = (2000-pl_sd->battle_status.amotion)/10; // aspd - WFIFOW(fd,40) = 0; // FIXME: What is 'plusASPD' supposed to be? Maybe adelay? - WFIFOSET(fd,packet_len(0x214)); -} - - -/// /check (CZ_REQ_STATUS_GM). -/// Request character's status values. -/// 0213 <char name>.24B -void clif_parse_Check(int fd, struct map_session_data *sd) -{ - char charname[NAME_LENGTH]; - struct map_session_data* pl_sd; - - if(!pc_has_permission(sd, PC_PERM_USE_CHECK)) - return; - - safestrncpy(charname, (const char*)RFIFOP(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), sizeof(charname)); - - if( ( pl_sd = map_nick2sd(charname) ) == NULL || pc_get_group_level(sd) < pc_get_group_level(pl_sd) ) - { - return; - } - - clif_check(fd, pl_sd); -} - - - -/// MAIL SYSTEM -/// By Zephyrus -/// - -/// Notification about the result of adding an item to mail (ZC_ACK_MAIL_ADD_ITEM). -/// 0255 <index>.W <result>.B -/// result: -/// 0 = success -/// 1 = failure -void clif_Mail_setattachment(int fd, int index, uint8 flag) -{ - WFIFOHEAD(fd,packet_len(0x255)); - WFIFOW(fd,0) = 0x255; - WFIFOW(fd,2) = index; - WFIFOB(fd,4) = flag; - WFIFOSET(fd,packet_len(0x255)); -} - - -/// Notification about the result of retrieving a mail attachment (ZC_MAIL_REQ_GET_ITEM). -/// 0245 <result>.B -/// result: -/// 0 = success -/// 1 = failure -/// 2 = too many items -void clif_Mail_getattachment(int fd, uint8 flag) -{ - WFIFOHEAD(fd,packet_len(0x245)); - WFIFOW(fd,0) = 0x245; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,packet_len(0x245)); -} - - -/// Notification about the result of sending a mail (ZC_MAIL_REQ_SEND). -/// 0249 <result>.B -/// result: -/// 0 = success -/// 1 = recipinent does not exist -void clif_Mail_send(int fd, bool fail) -{ - WFIFOHEAD(fd,packet_len(0x249)); - WFIFOW(fd,0) = 0x249; - WFIFOB(fd,2) = fail; - WFIFOSET(fd,packet_len(0x249)); -} - - -/// Notification about the result of deleting a mail (ZC_ACK_MAIL_DELETE). -/// 0257 <mail id>.L <result>.W -/// result: -/// 0 = success -/// 1 = failure -void clif_Mail_delete(int fd, int mail_id, short fail) -{ - WFIFOHEAD(fd, packet_len(0x257)); - WFIFOW(fd,0) = 0x257; - WFIFOL(fd,2) = mail_id; - WFIFOW(fd,6) = fail; - WFIFOSET(fd, packet_len(0x257)); -} - - -/// Notification about the result of returning a mail (ZC_ACK_MAIL_RETURN). -/// 0274 <mail id>.L <result>.W -/// result: -/// 0 = success -/// 1 = failure -void clif_Mail_return(int fd, int mail_id, short fail) -{ - WFIFOHEAD(fd,packet_len(0x274)); - WFIFOW(fd,0) = 0x274; - WFIFOL(fd,2) = mail_id; - WFIFOW(fd,6) = fail; - WFIFOSET(fd,packet_len(0x274)); -} - - -/// Notification about new mail (ZC_MAIL_RECEIVE). -/// 024a <mail id>.L <title>.40B <sender>.24B -void clif_Mail_new(int fd, int mail_id, const char *sender, const char *title) -{ - WFIFOHEAD(fd,packet_len(0x24a)); - WFIFOW(fd,0) = 0x24a; - WFIFOL(fd,2) = mail_id; - safestrncpy((char*)WFIFOP(fd,6), title, MAIL_TITLE_LENGTH); - safestrncpy((char*)WFIFOP(fd,46), sender, NAME_LENGTH); - WFIFOSET(fd,packet_len(0x24a)); -} - - -/// Opens/closes the mail window (ZC_MAIL_WINDOWS). -/// 0260 <type>.L -/// type: -/// 0 = open -/// 1 = close -void clif_Mail_window(int fd, int flag) -{ - WFIFOHEAD(fd,packet_len(0x260)); - WFIFOW(fd,0) = 0x260; - WFIFOL(fd,2) = flag; - WFIFOSET(fd,packet_len(0x260)); -} - - -/// Lists mails stored in inbox (ZC_MAIL_REQ_GET_LIST). -/// 0240 <packet len>.W <amount>.L { <mail id>.L <title>.40B <read>.B <sender>.24B <time>.L }*amount -/// read: -/// 0 = unread -/// 1 = read -void clif_Mail_refreshinbox(struct map_session_data *sd) -{ - int fd = sd->fd; - struct mail_data *md = &sd->mail.inbox; - struct mail_message *msg; - int len, i, j; - - len = 8 + (73 * md->amount); - - WFIFOHEAD(fd,len); - WFIFOW(fd,0) = 0x240; - WFIFOW(fd,2) = len; - WFIFOL(fd,4) = md->amount; - for( i = j = 0; i < MAIL_MAX_INBOX && j < md->amount; i++ ) - { - msg = &md->msg[i]; - if (msg->id < 1) - continue; - - WFIFOL(fd,8+73*j) = msg->id; - memcpy(WFIFOP(fd,12+73*j), msg->title, MAIL_TITLE_LENGTH); - WFIFOB(fd,52+73*j) = (msg->status != MAIL_UNREAD); - memcpy(WFIFOP(fd,53+73*j), msg->send_name, NAME_LENGTH); - WFIFOL(fd,77+73*j) = (uint32)msg->timestamp; - j++; - } - WFIFOSET(fd,len); - - if( md->full ) - {// TODO: is this official? - char output[100]; - sprintf(output, "Inbox is full (Max %d). Delete some mails.", MAIL_MAX_INBOX); - clif_disp_onlyself(sd, output, strlen(output)); - } -} - - -/// Mail inbox list request (CZ_MAIL_GET_LIST). -/// 023f -void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd) -{ - struct mail_data* md = &sd->mail.inbox; - - if( md->amount < MAIL_MAX_INBOX && (md->full || sd->mail.changed) ) - intif_Mail_requestinbox(sd->status.char_id, 1); - else - clif_Mail_refreshinbox(sd); - - mail_removeitem(sd, 0); - mail_removezeny(sd, 0); -} - - -/// Opens a mail (ZC_MAIL_REQ_OPEN). -/// 0242 <packet len>.W <mail id>.L <title>.40B <sender>.24B <time>.L <zeny>.L -/// <amount>.L <name id>.W <item type>.W <identified>.B <damaged>.B <refine>.B -/// <card1>.W <card2>.W <card3>.W <card4>.W <message>.?B -void clif_Mail_read(struct map_session_data *sd, int mail_id) -{ - int i, fd = sd->fd; - - ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id); - if( i == MAIL_MAX_INBOX ) - { - clif_Mail_return(sd->fd, mail_id, 1); // Mail doesn't exist - ShowWarning("clif_parse_Mail_read: char '%s' trying to read a message not the inbox.\n", sd->status.name); - return; - } - else - { - struct mail_message *msg = &sd->mail.inbox.msg[i]; - struct item *item = &msg->item; - struct item_data *data; - int msg_len = strlen(msg->body), len; - - if( msg_len == 0 ) { - strcpy(msg->body, "(no message)"); - msg_len = strlen(msg->body); - } - - len = 101 + msg_len; - - WFIFOHEAD(fd,len); - WFIFOW(fd,0) = 0x242; - WFIFOW(fd,2) = len; - WFIFOL(fd,4) = msg->id; - safestrncpy((char*)WFIFOP(fd,8), msg->title, MAIL_TITLE_LENGTH + 1); - safestrncpy((char*)WFIFOP(fd,48), msg->send_name, NAME_LENGTH + 1); - WFIFOL(fd,72) = 0; - WFIFOL(fd,76) = msg->zeny; - - if( item->nameid && (data = itemdb_exists(item->nameid)) != NULL ) - { - WFIFOL(fd,80) = item->amount; - WFIFOW(fd,84) = (data->view_id)?data->view_id:item->nameid; - WFIFOW(fd,86) = data->type; - WFIFOB(fd,88) = item->identify; - WFIFOB(fd,89) = item->attribute; - WFIFOB(fd,90) = item->refine; - WFIFOW(fd,91) = item->card[0]; - WFIFOW(fd,93) = item->card[1]; - WFIFOW(fd,95) = item->card[2]; - WFIFOW(fd,97) = item->card[3]; - } - else // no item, set all to zero - memset(WFIFOP(fd,80), 0x00, 19); - - WFIFOB(fd,99) = (unsigned char)msg_len; - safestrncpy((char*)WFIFOP(fd,100), msg->body, msg_len + 1); - WFIFOSET(fd,len); - - if (msg->status == MAIL_UNREAD) { - msg->status = MAIL_READ; - intif_Mail_read(mail_id); - clif_parse_Mail_refreshinbox(fd, sd); - } - } -} - - -/// Request to open a mail (CZ_MAIL_OPEN). -/// 0241 <mail id>.L -void clif_parse_Mail_read(int fd, struct map_session_data *sd) -{ - int mail_id = RFIFOL(fd,2); - - if( mail_id <= 0 ) - return; - if( mail_invalid_operation(sd) ) - return; - - clif_Mail_read(sd, RFIFOL(fd,2)); -} - - -/// Request to receive mail's attachment (CZ_MAIL_GET_ITEM). -/// 0244 <mail id>.L -void clif_parse_Mail_getattach(int fd, struct map_session_data *sd) -{ - int mail_id = RFIFOL(fd,2); - int i; - bool fail = false; - - if( !chrif_isconnected() ) - return; - if( mail_id <= 0 ) - return; - if( mail_invalid_operation(sd) ) - return; - - ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id); - if( i == MAIL_MAX_INBOX ) - return; - - if( sd->mail.inbox.msg[i].zeny < 1 && (sd->mail.inbox.msg[i].item.nameid < 1 || sd->mail.inbox.msg[i].item.amount < 1) ) - return; - - if( sd->mail.inbox.msg[i].zeny + sd->status.zeny > MAX_ZENY ) - { - clif_Mail_getattachment(fd, 1); - return; - } - - if( sd->mail.inbox.msg[i].item.nameid > 0 ) - { - struct item_data *data; - unsigned int weight; - - if ((data = itemdb_exists(sd->mail.inbox.msg[i].item.nameid)) == NULL) - return; - - switch( pc_checkadditem(sd, data->nameid, sd->mail.inbox.msg[i].item.amount) ) - { - case ADDITEM_NEW: - fail = ( pc_inventoryblank(sd) == 0 ); - break; - case ADDITEM_OVERAMOUNT: - fail = true; - } - - if( fail ) - { - clif_Mail_getattachment(fd, 1); - return; - } - - weight = data->weight * sd->mail.inbox.msg[i].item.amount; - if( sd->weight + weight > sd->max_weight ) - { - clif_Mail_getattachment(fd, 2); - return; - } - } - - sd->mail.inbox.msg[i].zeny = 0; - memset(&sd->mail.inbox.msg[i].item, 0, sizeof(struct item)); - clif_Mail_read(sd, mail_id); - - intif_Mail_getattach(sd->status.char_id, mail_id); -} - - -/// Request to delete a mail (CZ_MAIL_DELETE). -/// 0243 <mail id>.L -void clif_parse_Mail_delete(int fd, struct map_session_data *sd) -{ - int mail_id = RFIFOL(fd,2); - int i; - - if( !chrif_isconnected() ) - return; - if( mail_id <= 0 ) - return; - if( mail_invalid_operation(sd) ) - return; - - ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id); - if (i < MAIL_MAX_INBOX) - { - struct mail_message *msg = &sd->mail.inbox.msg[i]; - - if( (msg->item.nameid > 0 && msg->item.amount > 0) || msg->zeny > 0 ) - {// can't delete mail without removing attachment first - clif_Mail_delete(sd->fd, mail_id, 1); - return; - } - - intif_Mail_delete(sd->status.char_id, mail_id); - } -} - - -/// Request to return a mail (CZ_REQ_MAIL_RETURN). -/// 0273 <mail id>.L <receive name>.24B -void clif_parse_Mail_return(int fd, struct map_session_data *sd) -{ - int mail_id = RFIFOL(fd,2); - int i; - - if( mail_id <= 0 ) - return; - if( mail_invalid_operation(sd) ) - return; - - ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id); - if( i < MAIL_MAX_INBOX && sd->mail.inbox.msg[i].send_id != 0 ) - intif_Mail_return(sd->status.char_id, mail_id); - else - clif_Mail_return(sd->fd, mail_id, 1); -} - - -/// Request to add an item or Zeny to mail (CZ_MAIL_ADD_ITEM). -/// 0247 <index>.W <amount>.L -void clif_parse_Mail_setattach(int fd, struct map_session_data *sd) -{ - int idx = RFIFOW(fd,2); - int amount = RFIFOL(fd,4); - unsigned char flag; - - if( !chrif_isconnected() ) - return; - if (idx < 0 || amount < 0) - return; - - flag = mail_setitem(sd, idx, amount); - clif_Mail_setattachment(fd,idx,flag); -} - - -/// Request to reset mail item and/or Zeny (CZ_MAIL_RESET_ITEM). -/// 0246 <type>.W -/// type: -/// 0 = reset all -/// 1 = remove item -/// 2 = remove zeny -void clif_parse_Mail_winopen(int fd, struct map_session_data *sd) -{ - int flag = RFIFOW(fd,2); - - if (flag == 0 || flag == 1) - mail_removeitem(sd, 0); - if (flag == 0 || flag == 2) - mail_removezeny(sd, 0); -} - - -/// Request to send mail (CZ_MAIL_SEND). -/// 0248 <packet len>.W <recipient>.24B <title>.40B <body len>.B <body>.?B -void clif_parse_Mail_send(int fd, struct map_session_data *sd) -{ - struct mail_message msg; - int body_len; - - if( !chrif_isconnected() ) - return; - if( sd->state.trading ) - return; - - if( RFIFOW(fd,2) < 69 ) { - ShowWarning("Invalid Msg Len from account %d.\n", sd->status.account_id); - return; - } - - if( DIFF_TICK(sd->cansendmail_tick, gettick()) > 0 ) - { - clif_displaymessage(sd->fd,msg_txt(675)); //"Cannot send mails too fast!!." - clif_Mail_send(fd, true); // fail - return; - } - - body_len = RFIFOB(fd,68); - - if (body_len > MAIL_BODY_LENGTH) - body_len = MAIL_BODY_LENGTH; - - if( !mail_setattachment(sd, &msg) ) - { // Invalid Append condition - clif_Mail_send(sd->fd, true); // fail - mail_removeitem(sd,0); - mail_removezeny(sd,0); - return; - } - - msg.id = 0; // id will be assigned by charserver - msg.send_id = sd->status.char_id; - msg.dest_id = 0; // will attempt to resolve name - safestrncpy(msg.send_name, sd->status.name, NAME_LENGTH); - safestrncpy(msg.dest_name, (char*)RFIFOP(fd,4), NAME_LENGTH); - safestrncpy(msg.title, (char*)RFIFOP(fd,28), MAIL_TITLE_LENGTH); - - if (msg.title[0] == '\0') { - return; // Message has no length and somehow client verification was skipped. - } - - if (body_len) - safestrncpy(msg.body, (char*)RFIFOP(fd,69), body_len + 1); - else - memset(msg.body, 0x00, MAIL_BODY_LENGTH); - - msg.timestamp = time(NULL); - if( !intif_Mail_send(sd->status.account_id, &msg) ) - mail_deliveryfail(sd, &msg); - - sd->cansendmail_tick = gettick() + 1000; // 1 Second flood Protection -} - - -/// AUCTION SYSTEM -/// By Zephyrus -/// - -/// Opens/closes the auction window (ZC_AUCTION_WINDOWS). -/// 025f <type>.L -/// type: -/// 0 = open -/// 1 = close -void clif_Auction_openwindow(struct map_session_data *sd) -{ - int fd = sd->fd; - - if( sd->state.storage_flag || sd->state.vending || sd->state.buyingstore || sd->state.trading ) - return; - - WFIFOHEAD(fd,packet_len(0x25f)); - WFIFOW(fd,0) = 0x25f; - WFIFOL(fd,2) = 0; - WFIFOSET(fd,packet_len(0x25f)); -} - - -/// Returns auction item search results (ZC_AUCTION_ITEM_REQ_SEARCH). -/// 0252 <packet len>.W <pages>.L <count>.L { <auction id>.L <seller name>.24B <name id>.W <type>.L <amount>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <now price>.L <max price>.L <buyer name>.24B <delete time>.L }* -void clif_Auction_results(struct map_session_data *sd, short count, short pages, uint8 *buf) -{ - int i, fd = sd->fd, len = sizeof(struct auction_data); - struct auction_data auction; - struct item_data *item; - int k; - - WFIFOHEAD(fd,12 + (count * 83)); - WFIFOW(fd,0) = 0x252; - WFIFOW(fd,2) = 12 + (count * 83); - WFIFOL(fd,4) = pages; - WFIFOL(fd,8) = count; - - for( i = 0; i < count; i++ ) - { - memcpy(&auction, RBUFP(buf,i * len), len); - k = 12 + (i * 83); - - WFIFOL(fd,k) = auction.auction_id; - safestrncpy((char*)WFIFOP(fd,4+k), auction.seller_name, NAME_LENGTH); - - if( (item = itemdb_exists(auction.item.nameid)) != NULL && item->view_id > 0 ) - WFIFOW(fd,28+k) = item->view_id; - else - WFIFOW(fd,28+k) = auction.item.nameid; - - WFIFOL(fd,30+k) = auction.type; - WFIFOW(fd,34+k) = auction.item.amount; // Always 1 - WFIFOB(fd,36+k) = auction.item.identify; - WFIFOB(fd,37+k) = auction.item.attribute; - WFIFOB(fd,38+k) = auction.item.refine; - WFIFOW(fd,39+k) = auction.item.card[0]; - WFIFOW(fd,41+k) = auction.item.card[1]; - WFIFOW(fd,43+k) = auction.item.card[2]; - WFIFOW(fd,45+k) = auction.item.card[3]; - WFIFOL(fd,47+k) = auction.price; - WFIFOL(fd,51+k) = auction.buynow; - safestrncpy((char*)WFIFOP(fd,55+k), auction.buyer_name, NAME_LENGTH); - WFIFOL(fd,79+k) = (uint32)auction.timestamp; - } - 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)); - WFIFOW(fd,0) = 0x256; - WFIFOW(fd,2) = index; - WFIFOB(fd,4) = 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 ) - clif_additem(sd, sd->auction.index, sd->auction.amount, 0); - - 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; - int amount = RFIFOL(fd,4); // Always 1 - struct item_data *item; - - if( sd->auction.amount > 0 ) - sd->auction.amount = 0; - - if( idx < 0 || idx >= MAX_INVENTORY ) - { - ShowWarning("Character %s trying to set invalid item index in auctions.\n", sd->status.name); - return; - } - - 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; - } - - if( (item = itemdb_exists(sd->status.inventory[idx].nameid)) != NULL && !(item->type == IT_ARMOR || item->type == IT_PETARMOR || item->type == IT_WEAPON || item->type == IT_CARD || item->type == IT_ETC) ) - { // Consumable or pets are not allowed - clif_Auction_setitem(sd->fd, idx, true); - return; - } - - if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || - !sd->status.inventory[idx].identify || - !itemdb_canauction(&sd->status.inventory[idx],pc_get_group_level(sd)) ) { // Quest Item or something else - clif_Auction_setitem(sd->fd, idx, true); - return; - } - - sd->auction.index = idx; - sd->auction.amount = amount; - clif_Auction_setitem(fd, idx + 2, false); -} - -/// Result from an auction action (ZC_AUCTION_RESULT). -/// 0250 <result>.B -/// result: -/// 0 = You have failed to bid into the auction -/// 1 = You have successfully bid in the auction -/// 2 = The auction has been canceled -/// 3 = An auction with at least one bidder cannot be canceled -/// 4 = You cannot register more than 5 items in an auction at a time -/// 5 = You do not have enough Zeny to pay the Auction Fee -/// 6 = You have won the auction -/// 7 = You have failed to win the auction -/// 8 = You do not have enough Zeny -/// 9 = You cannot place more than 5 bids at a time -void clif_Auction_message(int fd, unsigned char flag) -{ - WFIFOHEAD(fd,packet_len(0x250)); - WFIFOW(fd,0) = 0x250; - WFIFOB(fd,2) = flag; - WFIFOSET(fd,packet_len(0x250)); -} - - -/// Result of the auction close request (ZC_AUCTION_ACK_MY_SELL_STOP). -/// 025e <result>.W -/// result: -/// 0 = You have ended the auction -/// 1 = You cannot end the auction -/// 2 = Auction ID is incorrect -void clif_Auction_close(int fd, unsigned char flag) -{ - WFIFOHEAD(fd,packet_len(0x25e)); - WFIFOW(fd,0) = 0x25d; // BUG: The client identifies this packet as 0x25d (CZ_AUCTION_REQ_MY_SELL_STOP) - WFIFOW(fd,2) = 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; - struct item_data *item; - - auction.price = RFIFOL(fd,2); - auction.buynow = RFIFOL(fd,6); - auction.hours = RFIFOW(fd,10); - - // Invalid Situations... - if( sd->auction.amount < 1 ) - { - ShowWarning("Character %s trying to register auction without item.\n", sd->status.name); - return; - } - - if( auction.price >= auction.buynow ) - { - ShowWarning("Character %s trying to alter auction prices.\n", sd->status.name); - return; - } - - if( auction.hours < 1 || auction.hours > 48 ) - { - ShowWarning("Character %s trying to enter an invalid time for auction.\n", sd->status.name); - return; - } - - // Auction checks... - if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) ) - { - clif_Auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee. - return; - } - - if( auction.buynow > battle_config.auction_maximumprice ) - { // Zeny Limits - auction.buynow = battle_config.auction_maximumprice; - if( auction.price >= auction.buynow ) - auction.price = auction.buynow - 1; - } - - auction.auction_id = 0; - auction.seller_id = sd->status.char_id; - safestrncpy(auction.seller_name, sd->status.name, sizeof(auction.seller_name)); - auction.buyer_id = 0; - 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 ) - { - clif_Auction_message(fd, 2); // The auction has been canceled - return; - } - - if( (item = itemdb_exists(sd->status.inventory[sd->auction.index].nameid)) == NULL ) - { // Just in case - clif_Auction_message(fd, 2); // The auction has been canceled - return; - } - - 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; - auction.timestamp = 0; - - if( !intif_Auction_register(&auction) ) - clif_Auction_message(fd, 4); // No Char Server? lets say something to the client - else - { - int zeny = auction.hours*battle_config.auction_feeperhour; - - pc_delitem(sd, sd->auction.index, sd->auction.amount, 1, 6, LOG_TYPE_AUCTION); - sd->auction.amount = 0; - - pc_payzeny(sd, zeny, LOG_TYPE_AUCTION, NULL); - } -} - - -/// 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); - - 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); - - 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); - int bid = RFIFOL(fd,6); - - if( !pc_can_give_items(sd) ) { //They aren't supposed to give zeny [Inkfish] - clif_displaymessage(sd->fd, msg_txt(246)); - return; - } - - if( bid <= 0 ) - clif_Auction_message(fd, 0); // You have failed to bid into the auction - else if( bid > sd->status.zeny ) - clif_Auction_message(fd, 8); // You do not have enough zeny - else if ( CheckForCharServer() ) // char server is down (bugreport:1138) - clif_Auction_message(fd, 0); // You have failed to bid into the auction - else { - pc_payzeny(sd, bid, LOG_TYPE_AUCTION, NULL); - intif_Auction_bid(sd->status.char_id, sd->status.name, auction_id, bid); - } -} - - -/// 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); // FIXME: bug #5071 - - clif_parse_Auction_cancelreg(fd, sd); - - 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; - clif_parse_Auction_cancelreg(fd, sd); - - intif_Auction_requestlist(sd->status.char_id, type, 0, "", 1); -} - - -/// CASH/POINT SHOP -/// - -/// List of items offered in a cash shop (ZC_PC_CASH_POINT_ITEMLIST). -/// 0287 <packet len>.W <cash point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }* -/// 0287 <packet len>.W <cash point>.L <kafra point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }* (PACKETVER >= 20070711) -void clif_cashshop_show(struct map_session_data *sd, struct npc_data *nd) -{ - int fd,i; -#if PACKETVER < 20070711 - const int offset = 8; -#else - const int offset = 12; -#endif - - nullpo_retv(sd); - nullpo_retv(nd); - - fd = sd->fd; - sd->npc_shopid = nd->bl.id; - WFIFOHEAD(fd,offset+nd->u.shop.count*11); - WFIFOW(fd,0) = 0x287; - WFIFOW(fd,2) = offset+nd->u.shop.count*11; - WFIFOL(fd,4) = sd->cashPoints; // Cash Points -#if PACKETVER >= 20070711 - WFIFOL(fd,8) = sd->kafraPoints; // Kafra Points -#endif - - for( i = 0; i < nd->u.shop.count; i++ ) - { - struct item_data* id = itemdb_search(nd->u.shop.shop_item[i].nameid); - WFIFOL(fd,offset+0+i*11) = nd->u.shop.shop_item[i].value; - WFIFOL(fd,offset+4+i*11) = nd->u.shop.shop_item[i].value; // Discount Price - WFIFOB(fd,offset+8+i*11) = itemtype(id->type); - WFIFOW(fd,offset+9+i*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid; - } - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Cashshop Buy Ack (ZC_PC_CASH_POINT_UPDATE). -/// 0289 <cash point>.L <error>.W -/// 0289 <cash point>.L <kafra point>.L <error>.W (PACKETVER >= 20070711) -/// error: -/// 0 = The deal has successfully completed. (ERROR_TYPE_NONE) -/// 1 = The Purchase has failed because the NPC does not exist. (ERROR_TYPE_NPC) -/// 2 = The Purchase has failed because the Kafra Shop System is not working correctly. (ERROR_TYPE_SYSTEM) -/// 3 = You are over your Weight Limit. (ERROR_TYPE_INVENTORY_WEIGHT) -/// 4 = You cannot purchase items while you are in a trade. (ERROR_TYPE_EXCHANGE) -/// 5 = The Purchase has failed because the Item Information was incorrect. (ERROR_TYPE_ITEM_ID) -/// 6 = You do not have enough Kafra Credit Points. (ERROR_TYPE_MONEY) -/// 7 = You can purchase up to 10 items. -/// 8 = Some items could not be purchased. -void clif_cashshop_ack(struct map_session_data* sd, int error) -{ - int fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x289)); - WFIFOW(fd,0) = 0x289; - WFIFOL(fd,2) = sd->cashPoints; -#if PACKETVER < 20070711 - WFIFOW(fd,6) = TOW(error); -#else - WFIFOL(fd,6) = sd->kafraPoints; - WFIFOW(fd,10) = TOW(error); -#endif - WFIFOSET(fd, packet_len(0x289)); -} - - -/// Request to buy item(s) from cash shop (CZ_PC_BUY_CASH_POINT_ITEM). -/// 0288 <name id>.W <amount>.W -/// 0288 <name id>.W <amount>.W <kafra points>.L (PACKETVER >= 20070711) -/// 0288 <packet len>.W <kafra points>.L <count>.W { <amount>.W <name id>.W }.4B*count (PACKETVER >= 20100803) -void clif_parse_cashshop_buy(int fd, struct map_session_data *sd) -{ - int fail = 0; - nullpo_retv(sd); - - if( sd->state.trading || !sd->npc_shopid ) - fail = 1; - else - { -#if PACKETVER < 20101116 - short nameid = RFIFOW(fd,2); - short amount = RFIFOW(fd,4); - int points = RFIFOL(fd,6); - - fail = npc_cashshop_buy(sd, nameid, amount, points); -#else - int len = RFIFOW(fd,2); - int points = RFIFOL(fd,4); - int count = RFIFOW(fd,8); - unsigned short* item_list = (unsigned short*)RFIFOP(fd,10); - - if( len < 10 || len != 10 + count * 4) - { - ShowWarning("Player %u sent incorrect cash shop buy packet (len %u:%u)!\n", sd->status.char_id, len, 10 + count * 4); - return; - } - fail = npc_cashshop_buylist(sd,points,count,item_list); -#endif - } - - clif_cashshop_ack(sd,fail); -} - - -/// Adoption System -/// - -/// Adoption message (ZC_BABYMSG). -/// 0216 <msg>.L -/// msg: -/// 0 = "You cannot adopt more than 1 child." -/// 1 = "You must be at least character level 70 in order to adopt someone." -/// 2 = "You cannot adopt a married person." -void clif_Adopt_reply(struct map_session_data *sd, int type) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,6); - WFIFOW(fd,0) = 0x216; - WFIFOL(fd,2) = type; - WFIFOSET(fd,6); -} - - -/// Adoption confirmation (ZC_REQ_BABY). -/// 01f6 <account id>.L <char id>.L <name>.B -void clif_Adopt_request(struct map_session_data *sd, struct map_session_data *src, int p_id) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,34); - WFIFOW(fd,0) = 0x1f6; - WFIFOL(fd,2) = src->status.account_id; - WFIFOL(fd,6) = p_id; - memcpy(WFIFOP(fd,10), src->status.name, NAME_LENGTH); - WFIFOSET(fd,34); -} - - -/// Request to adopt a player (CZ_REQ_JOIN_BABY). -/// 01f9 <account id>.L -void clif_parse_Adopt_request(int fd, struct map_session_data *sd) -{ - struct map_session_data *tsd = map_id2sd(RFIFOL(fd,2)), *p_sd = map_charid2sd(sd->status.partner_id); - - if( pc_can_Adopt(sd, p_sd, tsd) ) - { - tsd->adopt_invite = sd->status.account_id; - clif_Adopt_request(tsd, sd, p_sd->status.account_id); - } -} - - -/// Answer to adopt confirmation (CZ_JOIN_BABY). -/// 01f7 <account id>.L <char id>.L <answer>.L -/// answer: -/// 0 = rejected -/// 1 = accepted -void clif_parse_Adopt_reply(int fd, struct map_session_data *sd) -{ - int p1_id = RFIFOL(fd,2); - int p2_id = RFIFOL(fd,6); - int result = RFIFOL(fd,10); - struct map_session_data* p1_sd = map_id2sd(p1_id); - struct map_session_data* p2_sd = map_id2sd(p2_id); - - int pid = sd->adopt_invite; - sd->adopt_invite = 0; - - if( p1_sd == NULL || p2_sd == NULL ) - return; // Both players need to be online - - if( pid != p1_sd->status.account_id ) - return; // Incorrect values - - if( result == 0 ) - return; // Rejected - - pc_adoption(p1_sd, p2_sd, sd); -} - - -/// Convex Mirror (ZC_BOSS_INFO). -/// 0293 <infoType>.B <x>.L <y>.L <minHours>.W <minMinutes>.W <maxHours>.W <maxMinutes>.W <monster name>.51B -/// infoType: -/// 0 = No boss on this map (BOSS_INFO_NOT). -/// 1 = Boss is alive (position update) (BOSS_INFO_ALIVE). -/// 2 = Boss is alive (initial announce) (BOSS_INFO_ALIVE_WITHMSG). -/// 3 = Boss is dead (BOSS_INFO_DEAD). -void clif_bossmapinfo(int fd, struct mob_data *md, short flag) -{ - WFIFOHEAD(fd,70); - memset(WFIFOP(fd,0),0,70); - WFIFOW(fd,0) = 0x293; - - if( md != NULL ) - { - if( md->bl.prev != NULL ) - { // Boss on This Map - if( flag ) - { - WFIFOB(fd,2) = 1; - WFIFOL(fd,3) = md->bl.x; - WFIFOL(fd,7) = md->bl.y; - } - else - WFIFOB(fd,2) = 2; // First Time - } - else if (md->spawn_timer != INVALID_TIMER) - { // Boss is Dead - const struct TimerData * timer_data = get_timer(md->spawn_timer); - unsigned int seconds; - int hours, minutes; - - seconds = DIFF_TICK(timer_data->tick, gettick()) / 1000 + 60; - hours = seconds / (60 * 60); - seconds = seconds - (60 * 60 * hours); - minutes = seconds / 60; - - WFIFOB(fd,2) = 3; - WFIFOW(fd,11) = hours; // Hours - WFIFOW(fd,13) = minutes; // Minutes - } - safestrncpy((char*)WFIFOP(fd,19), md->db->jname, NAME_LENGTH); - } - - WFIFOSET(fd,70); -} - - -/// Requesting equip of a player (CZ_EQUIPWIN_MICROSCOPE). -/// 02d6 <account id>.L -void clif_parse_ViewPlayerEquip(int fd, struct map_session_data* sd) -{ - int charid = RFIFOL(fd, 2); - struct map_session_data* tsd = map_id2sd(charid); - - if (!tsd) - return; - - if( tsd->status.show_equip || pc_has_permission(sd, PC_PERM_VIEW_EQUIPMENT) ) - clif_viewequip_ack(sd, tsd); - else - clif_viewequip_fail(sd); -} - - -/// Request to change equip window tick (CZ_CONFIG). -/// 02d8 <type>.L <value>.L -/// type: -/// 0 = open equip window -/// value: -/// 0 = disabled -/// 1 = enabled -void clif_parse_EquipTick(int fd, struct map_session_data* sd) -{ - bool flag = (bool)RFIFOL(fd,6); - sd->status.show_equip = flag; - clif_equiptickack(sd, flag); -} - - -/// Questlog System [Kevin] [Inkfish] -/// - -/// Sends list of all quest states (ZC_ALL_QUEST_LIST). -/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num -void clif_quest_send_list(struct map_session_data * sd) -{ - int fd = sd->fd; - int i; - int len = sd->avail_quests*5+8; - - WFIFOHEAD(fd,len); - WFIFOW(fd, 0) = 0x2b1; - WFIFOW(fd, 2) = len; - WFIFOL(fd, 4) = sd->avail_quests; - - for( i = 0; i < sd->avail_quests; i++ ) - { - WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id; - WFIFOB(fd, i*5+12) = sd->quest_log[i].state; - } - - WFIFOSET(fd, len); -} - - -/// Sends list of all quest missions (ZC_ALL_QUEST_MISSION). -/// 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num -void clif_quest_send_mission(struct map_session_data * sd) -{ - int fd = sd->fd; - int i, j; - int len = sd->avail_quests*104+8; - struct mob_db *mob; - - WFIFOHEAD(fd, len); - WFIFOW(fd, 0) = 0x2b2; - WFIFOW(fd, 2) = len; - WFIFOL(fd, 4) = sd->avail_quests; - - for( i = 0; i < sd->avail_quests; i++ ) - { - WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id; - WFIFOL(fd, i*104+12) = sd->quest_log[i].time - quest_db[sd->quest_index[i]].time; - WFIFOL(fd, i*104+16) = sd->quest_log[i].time; - WFIFOW(fd, i*104+20) = quest_db[sd->quest_index[i]].num_objectives; - - for( j = 0 ; j < quest_db[sd->quest_index[i]].num_objectives; j++ ) - { - WFIFOL(fd, i*104+22+j*30) = quest_db[sd->quest_index[i]].mob[j]; - WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j]; - mob = mob_db(quest_db[sd->quest_index[i]].mob[j]); - memcpy(WFIFOP(fd, i*104+28+j*30), mob?mob->jname:"NULL", NAME_LENGTH); - } - } - - WFIFOSET(fd, len); -} - - -/// Notification about a new quest (ZC_ADD_QUEST). -/// 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 -void clif_quest_add(struct map_session_data * sd, struct quest * qd, int index) -{ - int fd = sd->fd; - int i; - struct mob_db *mob; - - WFIFOHEAD(fd, packet_len(0x2b3)); - WFIFOW(fd, 0) = 0x2b3; - WFIFOL(fd, 2) = qd->quest_id; - WFIFOB(fd, 6) = qd->state; - WFIFOB(fd, 7) = qd->time - quest_db[index].time; - WFIFOL(fd, 11) = qd->time; - WFIFOW(fd, 15) = quest_db[index].num_objectives; - - for( i = 0; i < quest_db[index].num_objectives; i++ ) - { - WFIFOL(fd, i*30+17) = quest_db[index].mob[i]; - WFIFOW(fd, i*30+21) = qd->count[i]; - mob = mob_db(quest_db[index].mob[i]); - memcpy(WFIFOP(fd, i*30+23), mob?mob->jname:"NULL", NAME_LENGTH); - } - - WFIFOSET(fd, packet_len(0x2b3)); -} - - -/// Notification about a quest being removed (ZC_DEL_QUEST). -/// 02b4 <quest id>.L -void clif_quest_delete(struct map_session_data * sd, int quest_id) -{ - int fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x2b4)); - WFIFOW(fd, 0) = 0x2b4; - WFIFOL(fd, 2) = quest_id; - WFIFOSET(fd, packet_len(0x2b4)); -} - - -/// Notification of an update to the hunting mission counter (ZC_UPDATE_MISSION_HUNT). -/// 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3 -void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd, int index) -{ - int fd = sd->fd; - int i; - int len = quest_db[index].num_objectives*12+6; - - WFIFOHEAD(fd, len); - WFIFOW(fd, 0) = 0x2b5; - WFIFOW(fd, 2) = len; - WFIFOW(fd, 4) = quest_db[index].num_objectives; - - for( i = 0; i < quest_db[index].num_objectives; i++ ) - { - WFIFOL(fd, i*12+6) = qd->quest_id; - WFIFOL(fd, i*12+10) = quest_db[index].mob[i]; - WFIFOW(fd, i*12+14) = quest_db[index].count[i]; - WFIFOW(fd, i*12+16) = qd->count[i]; - } - - WFIFOSET(fd, len); -} - - -/// Request to change the state of a quest (CZ_ACTIVE_QUEST). -/// 02b6 <quest id>.L <active>.B -void clif_parse_questStateAck(int fd, struct map_session_data * sd) -{ - quest_update_status(sd, RFIFOL(fd,2), RFIFOB(fd,6)?Q_ACTIVE:Q_INACTIVE); -} - - -/// Notification about the change of a quest state (ZC_ACTIVE_QUEST). -/// 02b7 <quest id>.L <active>.B -void clif_quest_update_status(struct map_session_data * sd, int quest_id, bool active) -{ - int fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x2b7)); - WFIFOW(fd, 0) = 0x2b7; - WFIFOL(fd, 2) = quest_id; - WFIFOB(fd, 6) = active; - WFIFOSET(fd, packet_len(0x2b7)); -} - - -/// Notification about an NPC's quest state (ZC_QUEST_NOTIFY_EFFECT). -/// 0446 <npc id>.L <x>.W <y>.W <effect>.W <type>.W -/// effect: -/// 0 = none -/// 1 = exclamation mark icon -/// 2 = question mark icon -/// type: -/// 0 = yellow -/// 1 = orange -/// 2 = green -/// 3 = purple -void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, short state, short color) -{ -#if PACKETVER >= 20090218 - int fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x446)); - WFIFOW(fd, 0) = 0x446; - WFIFOL(fd, 2) = bl->id; - WFIFOW(fd, 6) = bl->x; - WFIFOW(fd, 8) = bl->y; - WFIFOW(fd, 10) = state; - WFIFOW(fd, 12) = color; - WFIFOSET(fd, packet_len(0x446)); -#endif -} - - -/// Mercenary System -/// - -/// Notification about a mercenary status parameter change (ZC_MER_PAR_CHANGE). -/// 02a2 <var id>.W <value>.L -void clif_mercenary_updatestatus(struct map_session_data *sd, int type) -{ - struct mercenary_data *md; - struct status_data *status; - int fd; - if( sd == NULL || (md = sd->md) == NULL ) - return; - - fd = sd->fd; - status = &md->battle_status; - WFIFOHEAD(fd,packet_len(0x2a2)); - WFIFOW(fd,0) = 0x2a2; - WFIFOW(fd,2) = type; - switch( type ) - { - case SP_ATK1: - { - int atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk; - WFIFOL(fd,4) = cap_value(atk, 0, INT16_MAX); - } - break; - case SP_MATK1: - WFIFOL(fd,4) = cap_value(status->matk_max, 0, INT16_MAX); - break; - case SP_HIT: - WFIFOL(fd,4) = status->hit; - break; - case SP_CRITICAL: - WFIFOL(fd,4) = status->cri/10; - break; - case SP_DEF1: - WFIFOL(fd,4) = status->def; - break; - case SP_MDEF1: - WFIFOL(fd,4) = status->mdef; - break; - case SP_MERCFLEE: - WFIFOL(fd,4) = status->flee; - break; - case SP_ASPD: - WFIFOL(fd,4) = status->amotion; - break; - case SP_HP: - WFIFOL(fd,4) = status->hp; - break; - case SP_MAXHP: - WFIFOL(fd,4) = status->max_hp; - break; - case SP_SP: - WFIFOL(fd,4) = status->sp; - break; - case SP_MAXSP: - WFIFOL(fd,4) = status->max_sp; - break; - case SP_MERCKILLS: - WFIFOL(fd,4) = md->mercenary.kill_count; - break; - case SP_MERCFAITH: - WFIFOL(fd,4) = mercenary_get_faith(md); - break; - } - WFIFOSET(fd,packet_len(0x2a2)); -} - - -/// Mercenary base status data (ZC_MER_INIT). -/// 029b <id>.L <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W -/// <name>.24B <level>.W <hp>.L <maxhp>.L <sp>.L <maxsp>.L <expire time>.L <faith>.W -/// <calls>.L <kills>.L <atk range>.W -void clif_mercenary_info(struct map_session_data *sd) -{ - int fd; - struct mercenary_data *md; - struct status_data *status; - int atk; - - if( sd == NULL || (md = sd->md) == NULL ) - return; - - fd = sd->fd; - status = &md->battle_status; - - WFIFOHEAD(fd,packet_len(0x29b)); - WFIFOW(fd,0) = 0x29b; - WFIFOL(fd,2) = md->bl.id; - - // Mercenary shows ATK as a random value between ATK ~ ATK2 - atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk; - WFIFOW(fd,6) = cap_value(atk, 0, INT16_MAX); - WFIFOW(fd,8) = cap_value(status->matk_max, 0, INT16_MAX); - WFIFOW(fd,10) = status->hit; - WFIFOW(fd,12) = status->cri/10; - WFIFOW(fd,14) = status->def; - WFIFOW(fd,16) = status->mdef; - WFIFOW(fd,18) = status->flee; - WFIFOW(fd,20) = status->amotion; - safestrncpy((char*)WFIFOP(fd,22), md->db->name, NAME_LENGTH); - WFIFOW(fd,46) = md->db->lv; - WFIFOL(fd,48) = status->hp; - WFIFOL(fd,52) = status->max_hp; - WFIFOL(fd,56) = status->sp; - WFIFOL(fd,60) = status->max_sp; - WFIFOL(fd,64) = (int)time(NULL) + (mercenary_get_lifetime(md) / 1000); - WFIFOW(fd,68) = mercenary_get_faith(md); - WFIFOL(fd,70) = mercenary_get_calls(md); - WFIFOL(fd,74) = md->mercenary.kill_count; - WFIFOW(fd,78) = md->battle_status.rhw.range; - WFIFOSET(fd,packet_len(0x29b)); -} - - -/// Mercenary skill tree (ZC_MER_SKILLINFO_LIST). -/// 029d <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }* -void clif_mercenary_skillblock(struct map_session_data *sd) -{ - struct mercenary_data *md; - int fd, i, len = 4, id, j; - - if( sd == NULL || (md = sd->md) == NULL ) - return; - - fd = sd->fd; - WFIFOHEAD(fd,4+37*MAX_MERCSKILL); - WFIFOW(fd,0) = 0x29d; - for( i = 0; i < MAX_MERCSKILL; i++ ) - { - if( (id = md->db->skill[i].id) == 0 ) - continue; - j = id - MC_SKILLBASE; - WFIFOW(fd,len) = id; - WFIFOL(fd,len+2) = skill_get_inf(id); - WFIFOW(fd,len+6) = md->db->skill[j].lv; - WFIFOW(fd,len+8) = skill_get_sp(id, md->db->skill[j].lv); - WFIFOW(fd,len+10) = skill_get_range2(&md->bl, id, md->db->skill[j].lv); - safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH); - WFIFOB(fd,len+36) = 0; // Skillable for Mercenary? - len += 37; - } - - WFIFOW(fd,2) = len; - WFIFOSET(fd,len); -} - - -/// Request to invoke a mercenary menu action (CZ_MER_COMMAND). -/// 029f <command>.B -/// 1 = mercenary information -/// 2 = delete -void clif_parse_mercenary_action(int fd, struct map_session_data* sd) -{ - int option = RFIFOB(fd,2); - if( sd->md == NULL ) - return; - - if( option == 2 ) merc_delete(sd->md, 2); -} - - -/// Mercenary Message -/// message: -/// 0 = Mercenary soldier's duty hour is over. -/// 1 = Your mercenary soldier has been killed. -/// 2 = Your mercenary soldier has been fired. -/// 3 = Your mercenary soldier has ran away. -void clif_mercenary_message(struct map_session_data* sd, int message) -{ - clif_msg(sd, 1266 + message); -} - - -/// Notification about the remaining time of a rental item (ZC_CASH_TIME_COUNTER). -/// 0298 <name id>.W <seconds>.L -void clif_rental_time(int fd, int nameid, int seconds) -{ // '<ItemName>' item will disappear in <seconds/60> minutes. - WFIFOHEAD(fd,packet_len(0x298)); - WFIFOW(fd,0) = 0x298; - WFIFOW(fd,2) = nameid; - WFIFOL(fd,4) = seconds; - WFIFOSET(fd,packet_len(0x298)); -} - - -/// Deletes a rental item from client's inventory (ZC_CASH_ITEM_DELETE). -/// 0299 <index>.W <name id>.W -void clif_rental_expired(int fd, int index, int nameid) -{ // '<ItemName>' item has been deleted from the Inventory - WFIFOHEAD(fd,packet_len(0x299)); - WFIFOW(fd,0) = 0x299; - WFIFOW(fd,2) = index+2; - WFIFOW(fd,4) = nameid; - WFIFOSET(fd,packet_len(0x299)); -} - - -/// Book Reading (ZC_READ_BOOK). -/// 0294 <book id>.L <page>.L -void clif_readbook(int fd, int book_id, int page) -{ - WFIFOHEAD(fd,packet_len(0x294)); - WFIFOW(fd,0) = 0x294; - WFIFOL(fd,2) = book_id; - WFIFOL(fd,6) = page; - WFIFOSET(fd,packet_len(0x294)); -} - - -/// Battlegrounds -/// - -/// Updates HP bar of a camp member (ZC_BATTLEFIELD_NOTIFY_HP). -/// 02e0 <account id>.L <name>.24B <hp>.W <max hp>.W -void clif_bg_hp(struct map_session_data *sd) -{ - unsigned char buf[34]; - const int cmd = 0x2e0; - nullpo_retv(sd); - - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = sd->status.account_id; - memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH); - - if( sd->battle_status.max_hp > INT16_MAX ) - { // To correctly display the %hp bar. [Skotlex] - WBUFW(buf,30) = sd->battle_status.hp/(sd->battle_status.max_hp/100); - WBUFW(buf,32) = 100; - } - else - { - WBUFW(buf,30) = sd->battle_status.hp; - WBUFW(buf,32) = sd->battle_status.max_hp; - } - - clif_send(buf, packet_len(cmd), &sd->bl, BG_AREA_WOS); -} - - -/// Updates the position of a camp member on the minimap (ZC_BATTLEFIELD_NOTIFY_POSITION). -/// 02df <account id>.L <name>.24B <class>.W <x>.W <y>.W -void clif_bg_xy(struct map_session_data *sd) -{ - unsigned char buf[36]; - nullpo_retv(sd); - - WBUFW(buf,0)=0x2df; - WBUFL(buf,2)=sd->status.account_id; - memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH); - WBUFW(buf,30)=sd->status.class_; - WBUFW(buf,32)=sd->bl.x; - WBUFW(buf,34)=sd->bl.y; - - clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS); -} - -void clif_bg_xy_remove(struct map_session_data *sd) -{ - unsigned char buf[36]; - nullpo_retv(sd); - - WBUFW(buf,0)=0x2df; - WBUFL(buf,2)=sd->status.account_id; - memset(WBUFP(buf,6), 0, NAME_LENGTH); - WBUFW(buf,30)=0; - WBUFW(buf,32)=-1; - WBUFW(buf,34)=-1; - - clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS); -} - - -/// Notifies clients of a battleground message (ZC_BATTLEFIELD_CHAT). -/// 02dc <packet len>.W <account id>.L <name>.24B <message>.?B -void clif_bg_message(struct battleground_data *bg, int src_id, const char *name, const char *mes, int len) -{ - struct map_session_data *sd; - unsigned char *buf; - if( (sd = bg_getavailablesd(bg)) == NULL ) - return; - - buf = (unsigned char*)aMalloc((len + NAME_LENGTH + 8)*sizeof(unsigned char)); - - WBUFW(buf,0) = 0x2dc; - WBUFW(buf,2) = len + NAME_LENGTH + 8; - WBUFL(buf,4) = src_id; - memcpy(WBUFP(buf,8), name, NAME_LENGTH); - memcpy(WBUFP(buf,32), mes, len); - clif_send(buf,WBUFW(buf,2), &sd->bl, BG); - - if( buf ) - aFree(buf); -} - - -/// Validates and processes battlechat messages [pakpil] (CZ_BATTLEFIELD_CHAT). -/// 0x2db <packet len>.W <text>.?B (<name> : <message>) 00 -void clif_parse_BattleChat(int fd, struct map_session_data* sd) -{ - const char* text = (char*)RFIFOP(fd,4); - int textlen = RFIFOW(fd,2) - 4; - - char *name, *message; - int namelen, messagelen; - - if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) ) - return; - - if( is_atcommand(fd, sd, message, 1) ) - return; - - if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) ) - return; - - if( battle_config.min_chat_delay ) - { - if( DIFF_TICK(sd->cantalk_tick, gettick()) > 0 ) - return; - sd->cantalk_tick = gettick() + battle_config.min_chat_delay; - } - - bg_send_message(sd, text, textlen); -} - - -/// Notifies client of a battleground score change (ZC_BATTLEFIELD_NOTIFY_POINT). -/// 02de <camp A points>.W <camp B points>.W -void clif_bg_updatescore(int16 m) -{ - struct block_list bl; - unsigned char buf[6]; - - bl.id = 0; - bl.type = BL_NUL; - bl.m = m; - - WBUFW(buf,0) = 0x2de; - WBUFW(buf,2) = map[m].bgscore_lion; - WBUFW(buf,4) = map[m].bgscore_eagle; - clif_send(buf,packet_len(0x2de),&bl,ALL_SAMEMAP); -} - -void clif_bg_updatescore_single(struct map_session_data *sd) -{ - int fd; - nullpo_retv(sd); - fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x2de)); - WFIFOW(fd,0) = 0x2de; - WFIFOW(fd,2) = map[sd->bl.m].bgscore_lion; - WFIFOW(fd,4) = map[sd->bl.m].bgscore_eagle; - WFIFOSET(fd,packet_len(0x2de)); -} - - -/// Battleground camp belong-information (ZC_BATTLEFIELD_NOTIFY_CAMPINFO). -/// 02dd <account id>.L <name>.24B <camp>.W -void clif_sendbgemblem_area(struct map_session_data *sd) -{ - unsigned char buf[33]; - nullpo_retv(sd); - - WBUFW(buf, 0) = 0x2dd; - WBUFL(buf,2) = sd->bl.id; - safestrncpy((char*)WBUFP(buf,6), sd->status.name, NAME_LENGTH); // name don't show in screen. - WBUFW(buf,30) = sd->bg_id; - clif_send(buf,packet_len(0x2dd), &sd->bl, AREA); -} - -void clif_sendbgemblem_single(int fd, struct map_session_data *sd) -{ - nullpo_retv(sd); - WFIFOHEAD(fd,32); - WFIFOW(fd,0) = 0x2dd; - WFIFOL(fd,2) = sd->bl.id; - safestrncpy((char*)WFIFOP(fd,6), sd->status.name, NAME_LENGTH); - WFIFOW(fd,30) = sd->bg_id; - WFIFOSET(fd,packet_len(0x2dd)); -} - - -/// Custom Fonts (ZC_NOTIFY_FONT). -/// 02ef <account_id>.L <font id>.W -void clif_font(struct map_session_data *sd) -{ -#if PACKETVER >= 20080102 - unsigned char buf[8]; - nullpo_retv(sd); - WBUFW(buf,0) = 0x2ef; - WBUFL(buf,2) = sd->bl.id; - WBUFW(buf,6) = sd->user_font; - clif_send(buf, packet_len(0x2ef), &sd->bl, AREA); -#endif -} - - -/*========================================== - * Instancing Window - *------------------------------------------*/ -int clif_instance(int instance_id, int type, int flag) -{ - struct map_session_data *sd; - struct party_data *p; - unsigned char buf[255]; - - if( (p = party_search(instance[instance_id].party_id)) == NULL || (sd = party_getavailablesd(p)) == NULL ) - return 0; - - switch( type ) - { - case 1: - // S 0x2cb <Instance name>.61B <Standby Position>.W - // Required to start the instancing information window on Client - // This window re-appear each "refresh" of client automatically until type 4 is send to client. - WBUFW(buf,0) = 0x02CB; - memcpy(WBUFP(buf,2),instance[instance_id].name,INSTANCE_NAME_LENGTH); - WBUFW(buf,63) = flag; - clif_send(buf,packet_len(0x02CB),&sd->bl,PARTY); - break; - case 2: - // S 0x2cc <Standby Position>.W - // To announce Instancing queue creation if no maps available - WBUFW(buf,0) = 0x02CC; - WBUFW(buf,2) = flag; - clif_send(buf,packet_len(0x02CC),&sd->bl,PARTY); - break; - case 3: - case 4: - // S 0x2cd <Instance Name>.61B <Instance Remaining Time>.L <Instance Noplayers close time>.L - WBUFW(buf,0) = 0x02CD; - memcpy(WBUFP(buf,2),instance[instance_id].name,61); - if( type == 3 ) - { - WBUFL(buf,63) = (uint32)instance[instance_id].progress_timeout; - WBUFL(buf,67) = 0; - } - else - { - WBUFL(buf,63) = 0; - WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout; - } - clif_send(buf,packet_len(0x02CD),&sd->bl,PARTY); - break; - case 5: - // S 0x2ce <Message ID>.L - // 0 = Notification (EnterLimitDate update?) - // 1 = The Memorial Dungeon expired; it has been destroyed - // 2 = The Memorial Dungeon's entry time limit expired; it has been destroyed - // 3 = The Memorial Dungeon has been removed. - // 4 = Create failure (removes the instance window) - WBUFW(buf,0) = 0x02CE; - WBUFL(buf,2) = flag; - //WBUFL(buf,6) = EnterLimitDate; - clif_send(buf,packet_len(0x02CE),&sd->bl,PARTY); - break; - } - return 0; -} - -void clif_instance_join(int fd, int instance_id) -{ - if( instance[instance_id].idle_timer != INVALID_TIMER ) - { - WFIFOHEAD(fd,packet_len(0x02CD)); - WFIFOW(fd,0) = 0x02CD; - memcpy(WFIFOP(fd,2),instance[instance_id].name,61); - WFIFOL(fd,63) = 0; - WFIFOL(fd,67) = (uint32)instance[instance_id].idle_timeout; - WFIFOSET(fd,packet_len(0x02CD)); - } - else if( instance[instance_id].progress_timer != INVALID_TIMER ) - { - WFIFOHEAD(fd,packet_len(0x02CD)); - WFIFOW(fd,0) = 0x02CD; - memcpy(WFIFOP(fd,2),instance[instance_id].name,61); - WFIFOL(fd,63) = (uint32)instance[instance_id].progress_timeout;; - WFIFOL(fd,67) = 0; - WFIFOSET(fd,packet_len(0x02CD)); - } - else - { - WFIFOHEAD(fd,packet_len(0x02CB)); - WFIFOW(fd,0) = 0x02CB; - memcpy(WFIFOP(fd,2),instance[instance_id].name,61); - WFIFOW(fd,63) = 0; - WFIFOSET(fd,packet_len(0x02CB)); - } -} - -void clif_instance_leave(int fd) -{ - WFIFOHEAD(fd,packet_len(0x02CE)); - WFIFOW(fd,0) = 0x02ce; - WFIFOL(fd,2) = 4; - WFIFOSET(fd,packet_len(0x02CE)); -} - - -/// Notifies clients about item picked up by a party member (ZC_ITEM_PICKUP_PARTY). -/// 02b8 <account id>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B -void clif_party_show_picker(struct map_session_data * sd, struct item * item_data) -{ -#if PACKETVER >= 20071002 - unsigned char buf[22]; - struct item_data* id = itemdb_search(item_data->nameid); - - WBUFW(buf,0) = 0x2b8; - WBUFL(buf,2) = sd->status.account_id; - WBUFW(buf,6) = item_data->nameid; - WBUFB(buf,8) = item_data->identify; - WBUFB(buf,9) = item_data->attribute; - WBUFB(buf,10) = item_data->refine; - clif_addcards(WBUFP(buf,11), item_data); - WBUFW(buf,19) = id->equip; // equip location - WBUFB(buf,21) = itemtype(id->type); // item type - clif_send(buf, packet_len(0x2b8), &sd->bl, PARTY_SAMEMAP_WOS); -#endif -} - - -/// Display gained exp (ZC_NOTIFY_EXP). -/// 07f6 <account id>.L <amount>.L <var id>.W <exp type>.W -/// var id: -/// SP_BASEEXP, SP_JOBEXP -/// exp type: -/// 0 = normal exp gain/loss -/// 1 = quest exp gain/loss -void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, bool quest) -{ - int fd; - - nullpo_retv(sd); - - fd = sd->fd; - - WFIFOHEAD(fd, packet_len(0x7f6)); - WFIFOW(fd,0) = 0x7f6; - WFIFOL(fd,2) = sd->bl.id; - WFIFOL(fd,6) = exp; - WFIFOW(fd,10) = type; - WFIFOW(fd,12) = quest?1:0;// Normal exp is shown in yellow, quest exp is shown in purple. - WFIFOSET(fd,packet_len(0x7f6)); -} - - -/// Displays digital clock digits on top of the screen (ZC_SHOWDIGIT). -/// type: -/// 0 = Displays 'value' for 5 seconds. -/// 1 = Incremental counter (1 tick/second), negated 'value' specifies start value (e.g. using -10 lets the counter start at 10). -/// 2 = Decremental counter (1 tick/second), negated 'value' specifies start value (does not stop when reaching 0, but overflows). -/// 3 = Decremental counter (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) -{ - WFIFOHEAD(sd->fd, packet_len(0x1b1)); - WFIFOW(sd->fd,0) = 0x1b1; - WFIFOB(sd->fd,2) = type; - WFIFOL(sd->fd,3) = value; - WFIFOSET(sd->fd, packet_len(0x1b1)); -} - - -/// Notification of the state of client command /effect (CZ_LESSEFFECT). -/// 021d <state>.L -/// state: -/// 0 = Full effects -/// 1 = Reduced effects -/// -/// NOTE: The state is used on Aegis for sending skill unit packet -/// 0x11f (ZC_SKILL_ENTRY) instead of 0x1c9 (ZC_SKILL_ENTRY2) -/// whenever possible. Due to the way the decision check is -/// constructed, this state tracking was rendered useless, -/// as the only skill unit, that is sent with 0x1c9 is -/// Graffiti. -void clif_parse_LessEffect(int fd, struct map_session_data* sd) -{ - int isLess = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - - sd->state.lesseffect = ( isLess != 0 ); -} - -/// S 07e4 <length>.w <option>.l <val>.l {<index>.w <amount>.w).4b* -void clif_parse_ItemListWindowSelected(int fd, struct map_session_data* sd) { - int n = (RFIFOW(fd,2)-12) / 4; - int type = RFIFOL(fd,4); - int flag = RFIFOL(fd,8); // Button clicked: 0 = Cancel, 1 = OK - unsigned short* item_list = (unsigned short*)RFIFOP(fd,12); - - if( sd->state.trading || sd->npc_shopid ) - return; - - if( flag == 0 || n == 0) { - clif_menuskill_clear(sd); - return; // Canceled by player. - } - - if( sd->menuskill_id != SO_EL_ANALYSIS && sd->menuskill_id != GN_CHANGEMATERIAL ) { - clif_menuskill_clear(sd); - return; // Prevent hacking. - } - - switch( type ) { - case 0: // Change Material - skill_changematerial(sd,n,item_list); - break; - case 1: // Level 1: Pure to Rough - case 2: // Level 2: Rough to Pure - skill_elementalanalysis(sd,n,type,item_list); - break; - } - clif_menuskill_clear(sd); - - return; -} - -/*========================================== - * Elemental System - *==========================================*/ -void clif_elemental_updatestatus(struct map_session_data *sd, int type) { - struct elemental_data *ed; - struct status_data *status; - int fd; - - if( sd == NULL || (ed = sd->ed) == NULL ) - return; - - fd = sd->fd; - status = &ed->battle_status; - WFIFOHEAD(fd,8); - WFIFOW(fd,0) = 0x81e; - WFIFOW(fd,2) = type; - switch( type ) { - case SP_HP: - WFIFOL(fd,4) = status->hp; - break; - case SP_MAXHP: - WFIFOL(fd,4) = status->max_hp; - break; - case SP_SP: - WFIFOL(fd,4) = status->sp; - break; - case SP_MAXSP: - WFIFOL(fd,4) = status->max_sp; - break; - } - WFIFOSET(fd,8); -} - -void clif_elemental_info(struct map_session_data *sd) { - int fd; - struct elemental_data *ed; - struct status_data *status; - - if( sd == NULL || (ed = sd->ed) == NULL ) - return; - - fd = sd->fd; - status = &ed->battle_status; - - WFIFOHEAD(fd,22); - WFIFOW(fd, 0) = 0x81d; - WFIFOL(fd, 2) = ed->bl.id; - WFIFOL(fd, 6) = status->hp; - WFIFOL(fd,10) = status->max_hp; - WFIFOL(fd,14) = status->sp; - WFIFOL(fd,18) = status->max_sp; - WFIFOSET(fd,22); -} - - -/// Buying Store System -/// - -/// Opens preparation window for buying store (ZC_OPEN_BUYING_STORE). -/// 0810 <slots>.B -void clif_buyingstore_open(struct map_session_data* sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x810)); - WFIFOW(fd,0) = 0x810; - WFIFOB(fd,2) = sd->buyingstore.slots; - WFIFOSET(fd,packet_len(0x810)); -} - - -/// Request to create a buying store (CZ_REQ_OPEN_BUYING_STORE). -/// 0811 <packet len>.W <limit zeny>.L <result>.B <store name>.80B { <name id>.W <amount>.W <price>.L }* -/// result: -/// 0 = cancel -/// 1 = open -static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data* sd) -{ - const unsigned int blocksize = 8; - uint8* itemlist; - char storename[MESSAGE_SIZE]; - unsigned char result; - int zenylimit; - unsigned int count, packet_len; - struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)]; - - packet_len = RFIFOW(fd,info->pos[0]); - - // TODO: Make this check global for all variable length packets. - if( packet_len < 89 ) - {// minimum packet length - ShowError("clif_parse_ReqOpenBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 89, packet_len, sd->bl.id); - return; - } - - zenylimit = RFIFOL(fd,info->pos[1]); - result = RFIFOL(fd,info->pos[2]); - safestrncpy(storename, (const char*)RFIFOP(fd,info->pos[3]), sizeof(storename)); - itemlist = RFIFOP(fd,info->pos[4]); - - // so that buyingstore_create knows, how many elements it has access to - packet_len-= info->pos[4]; - - if( packet_len%blocksize ) - { - ShowError("clif_parse_ReqOpenBuyingStore: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize); - return; - } - count = packet_len/blocksize; - - buyingstore_create(sd, zenylimit, result, storename, itemlist, count); -} - - -/// Notification, that the requested buying store could not be created (ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER). -/// 0812 <result>.W <total weight>.L -/// result: -/// 1 = "Failed to open buying store." (0x6cd, MSI_BUYINGSTORE_OPEN_FAILED) -/// 2 = "Total amount of then possessed items exceeds the weight limit by <weight/10-maxweight*90%>. Please re-enter." (0x6ce, MSI_BUYINGSTORE_OVERWEIGHT) -/// 8 = "No sale (purchase) information available." (0x705) -/// ? = nothing -void clif_buyingstore_open_failed(struct map_session_data* sd, unsigned short result, unsigned int weight) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x812)); - WFIFOW(fd,0) = 0x812; - WFIFOW(fd,2) = result; - WFIFOL(fd,4) = weight; - WFIFOSET(fd,packet_len(0x812)); -} - - -/// Notification, that the requested buying store was created (ZC_MYITEMLIST_BUYING_STORE). -/// 0813 <packet len>.W <account id>.L <limit zeny>.L { <price>.L <count>.W <type>.B <name id>.W }* -void clif_buyingstore_myitemlist(struct map_session_data* sd) -{ - int fd = sd->fd; - unsigned int i; - - WFIFOHEAD(fd,12+sd->buyingstore.slots*9); - WFIFOW(fd,0) = 0x813; - WFIFOW(fd,2) = 12+sd->buyingstore.slots*9; - WFIFOL(fd,4) = sd->bl.id; - WFIFOL(fd,8) = sd->buyingstore.zenylimit; - - for( i = 0; i < sd->buyingstore.slots; i++ ) - { - WFIFOL(fd,12+i*9) = sd->buyingstore.items[i].price; - WFIFOW(fd,16+i*9) = sd->buyingstore.items[i].amount; - WFIFOB(fd,18+i*9) = itemtype(itemdb_type(sd->buyingstore.items[i].nameid)); - WFIFOW(fd,19+i*9) = sd->buyingstore.items[i].nameid; - } - - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Notifies clients in area of a buying store (ZC_BUYING_STORE_ENTRY). -/// 0814 <account id>.L <store name>.80B -void clif_buyingstore_entry(struct map_session_data* sd) -{ - uint8 buf[86]; - - WBUFW(buf,0) = 0x814; - WBUFL(buf,2) = sd->bl.id; - memcpy(WBUFP(buf,6), sd->message, MESSAGE_SIZE); - - clif_send(buf, packet_len(0x814), &sd->bl, AREA_WOS); -} -void clif_buyingstore_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x814)); - WFIFOW(fd,0) = 0x814; - WFIFOL(fd,2) = pl_sd->bl.id; - memcpy(WFIFOP(fd,6), pl_sd->message, MESSAGE_SIZE); - WFIFOSET(fd,packet_len(0x814)); -} - - -/// Request to close own buying store (CZ_REQ_CLOSE_BUYING_STORE). -/// 0815 -static void clif_parse_ReqCloseBuyingStore(int fd, struct map_session_data* sd) -{ - buyingstore_close(sd); -} - - -/// Notifies clients in area that a buying store was closed (ZC_DISAPPEAR_BUYING_STORE_ENTRY). -/// 0816 <account id>.L -void clif_buyingstore_disappear_entry(struct map_session_data* sd) -{ - uint8 buf[6]; - - WBUFW(buf,0) = 0x816; - WBUFL(buf,2) = sd->bl.id; - - clif_send(buf, packet_len(0x816), &sd->bl, AREA_WOS); -} -void clif_buyingstore_disappear_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x816)); - WFIFOW(fd,0) = 0x816; - WFIFOL(fd,2) = pl_sd->bl.id; - WFIFOSET(fd,packet_len(0x816)); -} - - -/// Request to open someone else's buying store (CZ_REQ_CLICK_TO_BUYING_STORE). -/// 0817 <account id>.L -static void clif_parse_ReqClickBuyingStore(int fd, struct map_session_data* sd) -{ - int account_id; - - account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - - buyingstore_open(sd, account_id); -} - - -/// Sends buying store item list (ZC_ACK_ITEMLIST_BUYING_STORE). -/// 0818 <packet len>.W <account id>.L <store id>.L <limit zeny>.L { <price>.L <amount>.W <type>.B <name id>.W }* -void clif_buyingstore_itemlist(struct map_session_data* sd, struct map_session_data* pl_sd) -{ - int fd = sd->fd; - unsigned int i; - - WFIFOHEAD(fd,16+pl_sd->buyingstore.slots*9); - WFIFOW(fd,0) = 0x818; - WFIFOW(fd,2) = 16+pl_sd->buyingstore.slots*9; - WFIFOL(fd,4) = pl_sd->bl.id; - WFIFOL(fd,8) = pl_sd->buyer_id; - WFIFOL(fd,12) = pl_sd->buyingstore.zenylimit; - - for( i = 0; i < pl_sd->buyingstore.slots; i++ ) - { - WFIFOL(fd,16+i*9) = pl_sd->buyingstore.items[i].price; - WFIFOW(fd,20+i*9) = pl_sd->buyingstore.items[i].amount; // TODO: Figure out, if no longer needed items (amount == 0) are listed on official. - WFIFOB(fd,22+i*9) = itemtype(itemdb_type(pl_sd->buyingstore.items[i].nameid)); - WFIFOW(fd,23+i*9) = pl_sd->buyingstore.items[i].nameid; - } - - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Request to sell items to a buying store (CZ_REQ_TRADE_BUYING_STORE). -/// 0819 <packet len>.W <account id>.L <store id>.L { <index>.W <name id>.W <amount>.W }* -static void clif_parse_ReqTradeBuyingStore(int fd, struct map_session_data* sd) -{ - const unsigned int blocksize = 6; - uint8* itemlist; - int account_id; - unsigned int count, packet_len, buyer_id; - struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)]; - - packet_len = RFIFOW(fd,info->pos[0]); - - if( packet_len < 12 ) - {// minimum packet length - ShowError("clif_parse_ReqTradeBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 12, packet_len, sd->bl.id); - return; - } - - account_id = RFIFOL(fd,info->pos[1]); - buyer_id = RFIFOL(fd,info->pos[2]); - itemlist = RFIFOP(fd,info->pos[3]); - - // so that buyingstore_trade knows, how many elements it has access to - packet_len-= info->pos[3]; - - if( packet_len%blocksize ) - { - ShowError("clif_parse_ReqTradeBuyingStore: Unexpected item list size %u (account_id=%d, buyer_id=%d, block size=%u)\n", packet_len, sd->bl.id, account_id, blocksize); - return; - } - count = packet_len/blocksize; - - buyingstore_trade(sd, account_id, buyer_id, itemlist, count); -} - - -/// Notifies the buyer, that the buying store has been closed due to a post-trade condition (ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER). -/// 081a <result>.W -/// result: -/// 3 = "All items within the buy limit were purchased." (0x6cf, MSI_BUYINGSTORE_TRADE_OVERLIMITZENY) -/// 4 = "All items were purchased." (0x6d0, MSI_BUYINGSTORE_TRADE_BUYCOMPLETE) -/// ? = nothing -void clif_buyingstore_trade_failed_buyer(struct map_session_data* sd, short result) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x81a)); - WFIFOW(fd,0) = 0x81a; - WFIFOW(fd,2) = result; - WFIFOSET(fd,packet_len(0x81a)); -} - - -/// Updates the zeny limit and an item in the buying store item list (ZC_UPDATE_ITEM_FROM_BUYING_STORE). -/// 081b <name id>.W <amount>.W <limit zeny>.L -void clif_buyingstore_update_item(struct map_session_data* sd, unsigned short nameid, unsigned short amount) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x81b)); - WFIFOW(fd,0) = 0x81b; - WFIFOW(fd,2) = nameid; - WFIFOW(fd,4) = amount; // amount of nameid received - WFIFOL(fd,6) = sd->buyingstore.zenylimit; - WFIFOSET(fd,packet_len(0x81b)); -} - - -/// Deletes item from inventory, that was sold to a buying store (ZC_ITEM_DELETE_BUYING_STORE). -/// 081c <index>.W <amount>.W <price>.L -/// message: -/// "%s (%d) were sold at %dz." (0x6d2, MSI_BUYINGSTORE_TRADE_SELLCOMPLETE) -/// -/// NOTE: This function has to be called _instead_ of clif_delitem/clif_dropitem. -void clif_buyingstore_delete_item(struct map_session_data* sd, short index, unsigned short amount, int price) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x81c)); - WFIFOW(fd,0) = 0x81c; - WFIFOW(fd,2) = index+2; - WFIFOW(fd,4) = amount; - WFIFOL(fd,6) = price; // price per item, client calculates total Zeny by itself - WFIFOSET(fd,packet_len(0x81c)); -} - - -/// Notifies the seller, that a buying store trade failed (ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER). -/// 0824 <result>.W <name id>.W -/// result: -/// 5 = "The deal has failed." (0x39, MSI_DEAL_FAIL) -/// 6 = "The trade failed, because the entered amount of item %s is higher, than the buyer is willing to buy." (0x6d3, MSI_BUYINGSTORE_TRADE_OVERCOUNT) -/// 7 = "The trade failed, because the buyer is lacking required balance." (0x6d1, MSI_BUYINGSTORE_TRADE_LACKBUYERZENY) -/// ? = nothing -void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short result, unsigned short nameid) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x824)); - WFIFOW(fd,0) = 0x824; - WFIFOW(fd,2) = result; - WFIFOW(fd,4) = nameid; - WFIFOSET(fd,packet_len(0x824)); -} - - -/// Search Store Info System -/// - -/// Request to search for stores (CZ_SEARCH_STORE_INFO). -/// 0835 <packet len>.W <type>.B <max price>.L <min price>.L <name id count>.B <card count>.B { <name id>.W }* { <card>.W }* -/// type: -/// 0 = Vending -/// 1 = Buying Store -/// -/// NOTE: The client determines the item ids by specifying a name and optionally, -/// amount of card slots. If the client does not know about the item it -/// cannot be searched. -static void clif_parse_SearchStoreInfo(int fd, struct map_session_data* sd) -{ - const unsigned int blocksize = 2; - const uint8* itemlist; - const uint8* cardlist; - unsigned char type; - unsigned int min_price, max_price, packet_len, count, item_count, card_count; - struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)]; - - packet_len = RFIFOW(fd,info->pos[0]); - - if( packet_len < 15 ) - {// minimum packet length - ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 15, packet_len, sd->bl.id); - return; - } - - type = RFIFOB(fd,info->pos[1]); - max_price = RFIFOL(fd,info->pos[2]); - min_price = RFIFOL(fd,info->pos[3]); - item_count = RFIFOB(fd,info->pos[4]); - card_count = RFIFOB(fd,info->pos[5]); - itemlist = RFIFOP(fd,info->pos[6]); - cardlist = RFIFOP(fd,info->pos[6]+blocksize*item_count); - - // check, if there is enough data for the claimed count of items - packet_len-= info->pos[6]; - - if( packet_len%blocksize ) - { - ShowError("clif_parse_SearchStoreInfo: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize); - return; - } - count = packet_len/blocksize; - - if( count < item_count+card_count ) - { - ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected count=%u, count=%u, account_id=%d).\n", item_count+card_count, count, sd->bl.id); - return; - } - - searchstore_query(sd, type, min_price, max_price, (const unsigned short*)itemlist, item_count, (const unsigned short*)cardlist, card_count); -} - - -/// Results for a store search request (ZC_SEARCH_STORE_INFO_ACK). -/// 0836 <packet len>.W <is first page>.B <is next page>.B <remaining uses>.B { <store id>.L <account id>.L <shop name>.80B <nameid>.W <item type>.B <price>.L <amount>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* -/// is first page: -/// 0 = appends to existing results -/// 1 = clears previous results before displaying this result set -/// is next page: -/// 0 = no "next" label -/// 1 = "next" label to retrieve more results -void clif_search_store_info_ack(struct map_session_data* sd) -{ - const unsigned int blocksize = MESSAGE_SIZE+26; - int fd = sd->fd; - unsigned int i, start, end; - - start = sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE; - end = min(sd->searchstore.count, start+SEARCHSTORE_RESULTS_PER_PAGE); - - WFIFOHEAD(fd,7+(end-start)*blocksize); - WFIFOW(fd,0) = 0x836; - WFIFOW(fd,2) = 7+(end-start)*blocksize; - WFIFOB(fd,4) = !sd->searchstore.pages; - WFIFOB(fd,5) = searchstore_querynext(sd); - WFIFOB(fd,6) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX); - - for( i = start; i < end; i++ ) - { - struct s_search_store_info_item* ssitem = &sd->searchstore.items[i]; - struct item it; - - WFIFOL(fd,i*blocksize+ 7) = ssitem->store_id; - WFIFOL(fd,i*blocksize+11) = ssitem->account_id; - memcpy(WFIFOP(fd,i*blocksize+15), ssitem->store_name, MESSAGE_SIZE); - WFIFOW(fd,i*blocksize+15+MESSAGE_SIZE) = ssitem->nameid; - WFIFOB(fd,i*blocksize+17+MESSAGE_SIZE) = itemtype(itemdb_type(ssitem->nameid)); - WFIFOL(fd,i*blocksize+18+MESSAGE_SIZE) = ssitem->price; - WFIFOW(fd,i*blocksize+22+MESSAGE_SIZE) = ssitem->amount; - WFIFOB(fd,i*blocksize+24+MESSAGE_SIZE) = ssitem->refine; - - // make-up an item for clif_addcards - memset(&it, 0, sizeof(it)); - memcpy(&it.card, &ssitem->card, sizeof(it.card)); - it.nameid = ssitem->nameid; - it.amount = ssitem->amount; - - clif_addcards(WFIFOP(fd,i*blocksize+25+MESSAGE_SIZE), &it); - } - - WFIFOSET(fd,WFIFOW(fd,2)); -} - - -/// Notification of failure when searching for stores (ZC_SEARCH_STORE_INFO_FAILED). -/// 0837 <reason>.B -/// reason: -/// 0 = "No matching stores were found." (0x70b) -/// 1 = "There are too many results. Please enter more detailed search term." (0x6f8) -/// 2 = "You cannot search anymore." (0x706) -/// 3 = "You cannot search yet." (0x708) -/// 4 = "No sale (purchase) information available." (0x705) -void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x837)); - WFIFOW(fd,0) = 0x837; - WFIFOB(fd,2) = reason; - WFIFOSET(fd,packet_len(0x837)); -} - - -/// Request to display next page of results (CZ_SEARCH_STORE_INFO_NEXT_PAGE). -/// 0838 -static void clif_parse_SearchStoreInfoNextPage(int fd, struct map_session_data* sd) -{ - searchstore_next(sd); -} - - -/// Opens the search store window (ZC_OPEN_SEARCH_STORE_INFO). -/// 083a <type>.W <remaining uses>.B -/// type: -/// 0 = Search Stores -/// 1 = Search Stores (Cash), asks for confirmation, when clicking a store -void clif_open_search_store_info(struct map_session_data* sd) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x83a)); - WFIFOW(fd,0) = 0x83a; - WFIFOW(fd,2) = sd->searchstore.effect; -#if PACKETVER > 20100701 - WFIFOB(fd,4) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX); -#endif - WFIFOSET(fd,packet_len(0x83a)); -} - - -/// Request to close the store search window (CZ_CLOSE_SEARCH_STORE_INFO). -/// 083b -static void clif_parse_CloseSearchStoreInfo(int fd, struct map_session_data* sd) -{ - searchstore_close(sd); -} - - -/// Request to invoke catalog effect on a store from search results (CZ_SSILIST_ITEM_CLICK). -/// 083c <account id>.L <store id>.L <nameid>.W -static void clif_parse_SearchStoreInfoListItemClick(int fd, struct map_session_data* sd) -{ - unsigned short nameid; - int account_id, store_id; - struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)]; - - account_id = RFIFOL(fd,info->pos[0]); - store_id = RFIFOL(fd,info->pos[1]); - nameid = RFIFOW(fd,info->pos[2]); - - searchstore_click(sd, account_id, store_id, nameid); -} - - -/// Notification of the store position on current map (ZC_SSILIST_ITEM_CLICK_ACK). -/// 083d <xPos>.W <yPos>.W -void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y) -{ - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x83d)); - WFIFOW(fd,0) = 0x83d; - WFIFOW(fd,2) = x; - WFIFOW(fd,4) = y; - WFIFOSET(fd,packet_len(0x83d)); -} - - -/// Parse function for packet debugging. -void clif_parse_debug(int fd,struct map_session_data *sd) -{ - int cmd, packet_len; - - // clif_parse ensures, that there is at least 2 bytes of data - cmd = RFIFOW(fd,0); - - if( sd ) - { - packet_len = packet_db[sd->packet_ver][cmd].len; - - if( packet_len == 0 ) - {// unknown - packet_len = RFIFOREST(fd); - } - else if( packet_len == -1 ) - {// variable length - packet_len = RFIFOW(fd,2); // clif_parse ensures, that this amount of data is already received - } - ShowDebug("Packet debug of 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id); - } - else - { - packet_len = RFIFOREST(fd); - ShowDebug("Packet debug of 0x%04X (length %d), session #%d\n", cmd, packet_len, fd); - } - - ShowDump(RFIFOP(fd,0), packet_len); -} -/*========================================== - * Server tells client to display a window similar to Magnifier (item) one - * Server populates the window with avilable elemental converter options according to player's inventory - *------------------------------------------*/ -int clif_elementalconverter_list(struct map_session_data *sd) { - int i,c,view,fd; - - nullpo_ret(sd); - - -/// Main client packet processing function - fd=sd->fd; - WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB *2+4); - WFIFOW(fd, 0)=0x1ad; - - for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){ - if( skill_can_produce_mix(sd,skill_produce_db[i].nameid,23, 1) ){ - if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0) - WFIFOW(fd,c*2+ 4)= view; - else - WFIFOW(fd,c*2+ 4)= skill_produce_db[i].nameid; - c++; - } - } - WFIFOW(fd,2) = c*2+4; - WFIFOSET(fd, WFIFOW(fd,2)); - if (c > 0) { - sd->menuskill_id = SA_CREATECON; - sd->menuskill_val = c; - } - - return 0; -} -/** - * 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,INVALID_TIMER); - clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK, 0); - } - - return 1; -} -/** - * Mechanic - **/ -/*========================================== - * Magic Decoy Material List - *------------------------------------------*/ -int clif_magicdecoy_list(struct map_session_data *sd, uint16 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,USESKILL_FAIL_LEVEL,0); - return 0; - } - - return 1; -} -/** - * Guilotine Cross - **/ -/*========================================== - * Guillotine Cross Poisons List - *------------------------------------------*/ -int clif_poison_list(struct map_session_data *sd, uint16 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,USESKILL_FAIL_GUILLONTINE_POISON,0); - return 0; - } - - return 1; -} -int clif_autoshadowspell_list(struct map_session_data *sd) { - int fd, i, c; - nullpo_ret(sd); - fd = sd->fd; - if( !fd ) return 0; - - if( sd->menuskill_id == SC_AUTOSHADOWSPELL ) - return 0; - - WFIFOHEAD(fd, 2 * 6 + 4); - WFIFOW(fd,0) = 0x442; - for( i = 0, c = 0; i < MAX_SKILL; i++ ) - if( sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].id > 0 && - sd->status.skill[i].id < GS_GLITTERING && skill_get_type(sd->status.skill[i].id) == BF_MAGIC ) - { // Can't auto cast both Extended class and 3rd class skills. - WFIFOW(fd,8+c*2) = sd->status.skill[i].id; - c++; - } - - if( c > 0 ) { - WFIFOW(fd,2) = 8 + c * 2; - WFIFOL(fd,4) = c; - WFIFOSET(fd,WFIFOW(fd,2)); - sd->menuskill_id = SC_AUTOSHADOWSPELL; - sd->menuskill_val = c; - } else { - status_change_end(&sd->bl,SC_STOP,INVALID_TIMER); - clif_skill_fail(sd,SC_AUTOSHADOWSPELL,USESKILL_FAIL_IMITATION_SKILL_NONE,0); - } - - return 1; -} -/*=========================================== - * Skill list for Four Elemental Analysis - * and Change Material skills. - *------------------------------------------*/ -int clif_skill_itemlistwindow( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv ) -{ -#if PACKETVER >= 20090922 - int fd; - - nullpo_ret(sd); - - sd->menuskill_id = skill_id; // To prevent hacking. - sd->menuskill_val = skill_lv; - - if( skill_id == GN_CHANGEMATERIAL ) - skill_lv = 0; // Changematerial - - fd = sd->fd; - WFIFOHEAD(fd,packet_len(0x7e3)); - WFIFOW(fd,0) = 0x7e3; - WFIFOL(fd,2) = skill_lv; - WFIFOL(fd,4) = 0; - WFIFOSET(fd,packet_len(0x7e3)); - -#endif - - 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(0x043f),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 -} -/*========================================== - * used by SC_AUTOSHADOWSPELL - * RFIFOL(fd,2) - flag (currently not used) - *------------------------------------------*/ -void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) { - - if( sd->menuskill_id != SC_AUTOSHADOWSPELL ) - return; - - if( pc_istrading(sd) ) { - clif_skill_fail(sd,sd->ud.skill_id,0,0); - clif_menuskill_clear(sd); - return; - } - - skill_select_menu(sd,RFIFOW(fd,6)); - - clif_menuskill_clear(sd); -} -/*========================================== - * Kagerou/Oboro amulet spirit - *------------------------------------------*/ -void clif_talisman(struct map_session_data *sd,short type) -{ - unsigned char buf[10]; - - nullpo_retv(sd); - - WBUFW(buf,0)=0x08cf; - WBUFL(buf,2)=sd->bl.id; - WBUFW(buf,6)=type; - WBUFW(buf,8)=sd->talisman[type]; - clif_send(buf,packet_len(0x08cf),&sd->bl,AREA); -} -/// Move Item from or to Personal Tab (CZ_WHATSOEVER) [FE] -/// 0907 <index>.W -/// -/// R 0908 <index>.w <type>.b -/// type: -/// 0 = move item to personal tab -/// 1 = move item to normal tab -void clif_parse_MoveItem(int fd, struct map_session_data *sd) { -#if PACKETVER >= 20111122 - int index; - - /* can't move while dead. */ - if(pc_isdead(sd)) { - return; - } - - index = RFIFOW(fd,2)-2; - - if (index < 0 || index >= MAX_INVENTORY) - return; - - if ( sd->status.inventory[index].favorite && RFIFOB(fd, 4) == 1 ) - sd->status.inventory[index].favorite = 0; - else if( RFIFOB(fd, 4) == 0 ) - sd->status.inventory[index].favorite = 1; - else - return;/* nothing to do. */ - - clif_favorite_item(sd, index); -#endif -} - - -/// Items that are in favorite tab of inventory (ZC_ITEM_FAVORITE). -/// 0900 <index>.W <favorite>.B -void clif_favorite_item(struct map_session_data* sd, unsigned short index) { - int fd = sd->fd; - - WFIFOHEAD(fd,packet_len(0x908)); - WFIFOW(fd,0) = 0x908; - WFIFOW(fd,2) = index+2; - WFIFOL(fd,4) = (sd->status.inventory[index].favorite == 1) ? 0 : 1; - WFIFOSET(fd,packet_len(0x908)); -} - -void clif_snap( struct block_list *bl, short x, short y ) { - unsigned char buf[10]; - - WBUFW(buf,0) = 0x8d2; - WBUFL(buf,2) = bl->id; - WBUFW(buf,6) = x; - WBUFW(buf,8) = y; - - clif_send(buf,packet_len(0x8d2),bl,AREA); -} - -void clif_monster_hp_bar( struct mob_data* md, int fd ) { -#if PACKETVER >= 20120404 - WFIFOHEAD(fd,packet_len(0x977)); - - WFIFOW(fd,0) = 0x977; - WFIFOL(fd,2) = md->bl.id; - WFIFOL(fd,6) = md->status.hp; - WFIFOL(fd,10) = md->status.max_hp; - - WFIFOSET(fd,packet_len(0x977)); -#endif -} - -/*========================================== - * Main client packet processing function - *------------------------------------------*/ -static int clif_parse(int fd) -{ - int cmd, packet_ver, packet_len, err; - TBL_PC* sd; - int pnum; - - //TODO apply delays or disconnect based on packet throughput [FlavioJS] - // Note: "click masters" can do 80+ clicks in 10 seconds - - for( pnum = 0; pnum < 3; ++pnum )// Limit max packets per cycle to 3 (delay packet spammers) [FlavioJS] -- This actually aids packet spammers, but stuff like /str+ gets slow without it [Ai4rei] - { // begin main client packet processing loop - - sd = (TBL_PC *)session[fd]->session_data; - if (session[fd]->flag.eof) { - if (sd) { - if (sd->state.autotrade) { - //Disassociate character from the socket connection. - session[fd]->session_data = NULL; - sd->fd = 0; - ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", sd->status.name); - } else - if (sd->state.active) { - // Player logout display [Valaris] - ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off.\n", sd->status.name); - clif_quitsave(fd, sd); - } else { - //Unusual logout (during log on/off/map-changer procedure) - ShowInfo("Player AID:%d/CID:%d logged off.\n", sd->status.account_id, sd->status.char_id); - map_quit(sd); - } - } else { - ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip2str(session[fd]->client_addr, NULL)); - } - do_close(fd); - return 0; - } - - if (RFIFOREST(fd) < 2) - return 0; - - cmd = RFIFOW(fd,0); - - // identify client's packet version - if (sd) { - packet_ver = sd->packet_ver; - } else { - // check authentification packet to know packet version - packet_ver = clif_guess_PacketVer(fd, 0, &err); - if( err ) {// failed to identify packet version - ShowInfo("clif_parse: Disconnecting session #%d with unknown packet version%s (p:0x%04x,l:%d).\n", fd, ( - err == 1 ? "" : - err == 2 ? ", possibly for having an invalid account_id" : - err == 3 ? ", possibly for having an invalid char_id." : - /* Uncomment when checks are added in clif_guess_PacketVer. [FlavioJS] - err == 4 ? ", possibly for having an invalid login_id1." : - err == 5 ? ", possibly for having an invalid client_tick." : - */ - err == 6 ? ", possibly for having an invalid sex." : - ". ERROR invalid error code"), cmd, RFIFOREST(fd)); - WFIFOHEAD(fd,packet_len(0x6a)); - WFIFOW(fd,0) = 0x6a; - WFIFOB(fd,2) = 3; // Rejected from Server - WFIFOSET(fd,packet_len(0x6a)); - -#ifdef DUMP_INVALID_PACKET - ShowDump(RFIFOP(fd,0), RFIFOREST(fd)); -#endif - - RFIFOSKIP(fd, RFIFOREST(fd)); - set_eof(fd); - return 0; - } - } - - // filter out invalid / unsupported packets - if (cmd > MAX_PACKET_DB || packet_db[packet_ver][cmd].len == 0) { - ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", cmd, RFIFOREST(fd), fd); -#ifdef DUMP_INVALID_PACKET - ShowDump(RFIFOP(fd,0), RFIFOREST(fd)); -#endif - set_eof(fd); - return 0; - } - - // determine real packet length - packet_len = packet_db[packet_ver][cmd].len; - if (packet_len == -1) { // variable-length packet - if (RFIFOREST(fd) < 4) - return 0; - - packet_len = RFIFOW(fd,2); - if (packet_len < 4 || packet_len > 32768) { - ShowWarning("clif_parse: Received packet 0x%04x specifies invalid packet_len (%d), disconnecting session #%d.\n", cmd, packet_len, fd); -#ifdef DUMP_INVALID_PACKET - ShowDump(RFIFOP(fd,0), RFIFOREST(fd)); -#endif - set_eof(fd); - return 0; - } - } - if ((int)RFIFOREST(fd) < packet_len) - return 0; // not enough data received to form the packet - - if( packet_db[packet_ver][cmd].func == clif_parse_debug ) - packet_db[packet_ver][cmd].func(fd, sd); - else if( packet_db[packet_ver][cmd].func != NULL ) { - if( !sd && packet_db[packet_ver][cmd].func != clif_parse_WantToConnection ) - ; //Only valid packet when there is no session - else - if( sd && sd->bl.prev == NULL && packet_db[packet_ver][cmd].func != clif_parse_LoadEndAck ) - ; //Only valid packet when player is not on a map - else - if( sd && session[sd->fd]->flag.eof ) - ; //No more packets accepted - else - packet_db[packet_ver][cmd].func(fd, sd); - } -#ifdef DUMP_UNKNOWN_PACKET - else { - const char* packet_txt = "save/packet.txt"; - FILE* fp; - - if( ( fp = fopen( packet_txt , "a" ) ) != NULL ) { - if( sd ) { - fprintf(fp, "Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id); - } else { - fprintf(fp, "Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd); - } - - WriteDump(fp, RFIFOP(fd,0), packet_len); - fprintf(fp, "\n"); - fclose(fp); - } else { - ShowError("Failed to write '%s'.\n", packet_txt); - - // Dump on console instead - if( sd ) { - ShowDebug("Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id); - } else { - ShowDebug("Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd); - } - - ShowDump(RFIFOP(fd,0), packet_len); - } - } -#endif - - RFIFOSKIP(fd, packet_len); - - }; // main loop end - - return 0; -} - -/*========================================== - * Reads packet_db.txt and setups its array reference - *------------------------------------------*/ -static int packetdb_readdb(void) -{ - FILE *fp; - char line[1024]; - int ln=0; - int cmd,i,j,packet_ver; - int max_cmd=-1; - int skip_ver = 0; - int warned = 0; - char *str[64],*p,*str2[64],*p2,w1[64],w2[64]; - int packet_len_table[MAX_PACKET_DB] = { - 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0040 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -#if PACKETVER <= 20081217 - 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,110, 3, 2, -#else - 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,114, 3, 2, -#endif -#if PACKETVER < 2 - 3, 28, 19, 11, 3, -1, 9, 5, 52, 51, 56, 58, 41, 2, 6, 6, -#elif PACKETVER < 20071106 // 78-7b Lv99 effect for later Kameshima - 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, -#elif PACKETVER <= 20081217 // change in 0x78 and 0x7c - 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 42, 2, 6, 6, -#else - 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 44, 2, 6, 6, -#endif - //#0x0080 - 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 2, -1, -1, -1, 0, // 0x8b changed to 2 (was 23) - 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6, -#if PACKETVER <= 20100622 - 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6, -#else - 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 9, 4, 7, 0, -1, 6, // 0xaa changed to 9 (was 7) -#endif - 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3, - //#0x00C0 - 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 3, 2, 27, // 0xcd change to 3 (was 6) - 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1, - 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2, - 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10, - //#0x0100 - 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1, - 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16, - 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1, - 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26, - //#0x0140 - 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6, - 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42, - -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182, - 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1, - //#0x0180 - 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6, -#if PACKETVER < 1 - 90, 86, 24, 6, 30,102, 8, 4, 8, 4, 14, 10, -1, 6, 2, 6, -#else // 196 comodo icon status display for later - 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6, -#endif -#if PACKETVER < 20081126 - 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, -#else // 0x1a2 changed (35->37) - 3, 3, 37, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, -#endif - 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3, - //#0x01C0, Set 0x1d5=-1 - 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 3, 9, 9, 30, 6, 28, - 8, 14, 10, 35, 6, -1, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6, - 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1, - -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10, - //#0x0200 - 26, -1, 26, 10, 18, 26, 11, 34, 14, 36, 10, 0, 0, -1, 32, 10, // 0x20c change to 0 (was 19) - 22, 0, 26, 26, 42, 6, 6, 2, 2,282,282, 10, 10, -1, -1, 66, -#if PACKETVER < 20071106 - 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 64, 5, 71, 5, -#else // 0x22c changed - 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 65, 5, 71, 5, -#endif - 12, 26, 9, 11, -1, -1, 10, 2,282, 11, 4, 36, 6, -1, 4, 2, - //#0x0240 - -1, -1, -1, -1, -1, 3, 4, 8, -1, 3, 70, 4, 8, 12, 4, 10, - 3, 32, -1, 3, 3, 5, 5, 8, 2, 3, -1, 6, 4, 6, 4, 6, - 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0280 -#if PACKETVER < 20070711 - 0, 0, 0, 6, 14, 0, 0, -1, 6, 8, 18, 0, 0, 0, 0, 0, -#else - 0, 0, 0, 6, 14, 0, 0, -1, 10, 12, 18, 0, 0, 0, 0, 0, // 0x288, 0x289 increase by 4 (kafra points) -#endif - 0, 4, 0, 70, 10, 0, 0, 0, 8, 6, 27, 80, 0, -1, 0, 0, - 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 85, -1, -1,107, 6, -1, 7, 7, 22,191, 0, 8, 0, 0, 0, 0, - //#0x02C0 - 0, -1, 0, 0, 0, 30, 30, 0, 0, 3, 0, 65, 4, 71, 10, 0, - -1, -1, -1, 0, 29, 0, 6, -1, 10, 10, 3, 0, -1, 32, 6, 36, - 34, 33, 0, 0, 0, 0, 0, 0, -1, -1, -1, 13, 67, 59, 60, 8, - 10, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0300 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0340 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0380 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x03C0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0400 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 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, -1, 0, 0, 0, 14, 0, 0, 0, 6, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0480 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x04C0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0500 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, - //#0x0540 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0580 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x05C0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0600 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, - //#0x0640 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0680 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x06C0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0700 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, - //#0x0740 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0780 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x07C0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -#if PACKETVER < 20090617 - 6, 2, -1, 4, 4, 4, 4, 8, 8,254, 6, 8, 6, 54, 30, 54, -#else // 0x7d9 changed - 6, 2, -1, 4, 4, 4, 4, 8, 8,268, 6, 8, 6, 54, 30, 54, -#endif - 0, 15, 8, 6, -1, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 14, -1, -1, -1, 8, 25, 0, 0, 26, 0, - //#0x0800 -#if PACKETVER < 20091229 - -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 20, -#else // for Party booking ( PACKETVER >= 20091229 ) - -1, -1, 18, 4, 8, 6, 2, 4, 14, 50, 18, 6, 2, 3, 14, 20, -#endif - 3, -1, 8, -1, 86, 2, 6, 6, -1, -1, 4, 10, 10, 0, 0, 0, - 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, -1, -1, 3, 2, 66, 5, 2, 12, 6, 0, 0, - //#0x0840 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0880 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x08C0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, - 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0900 - 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - //#0x0940 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, - - }; - struct { - void (*func)(int, struct map_session_data *); - char *name; - } clif_parse_func[]={ - {clif_parse_WantToConnection,"wanttoconnection"}, - {clif_parse_LoadEndAck,"loadendack"}, - {clif_parse_TickSend,"ticksend"}, - {clif_parse_WalkToXY,"walktoxy"}, - {clif_parse_QuitGame,"quitgame"}, - {clif_parse_GetCharNameRequest,"getcharnamerequest"}, - {clif_parse_GlobalMessage,"globalmessage"}, - {clif_parse_MapMove,"mapmove"}, - {clif_parse_ChangeDir,"changedir"}, - {clif_parse_Emotion,"emotion"}, - {clif_parse_HowManyConnections,"howmanyconnections"}, - {clif_parse_ActionRequest,"actionrequest"}, - {clif_parse_Restart,"restart"}, - {clif_parse_WisMessage,"wis"}, - {clif_parse_Broadcast,"broadcast"}, - {clif_parse_TakeItem,"takeitem"}, - {clif_parse_DropItem,"dropitem"}, - {clif_parse_UseItem,"useitem"}, - {clif_parse_EquipItem,"equipitem"}, - {clif_parse_UnequipItem,"unequipitem"}, - {clif_parse_NpcClicked,"npcclicked"}, - {clif_parse_NpcBuySellSelected,"npcbuysellselected"}, - {clif_parse_NpcBuyListSend,"npcbuylistsend"}, - {clif_parse_NpcSellListSend,"npcselllistsend"}, - {clif_parse_CreateChatRoom,"createchatroom"}, - {clif_parse_ChatAddMember,"chataddmember"}, - {clif_parse_ChatRoomStatusChange,"chatroomstatuschange"}, - {clif_parse_ChangeChatOwner,"changechatowner"}, - {clif_parse_KickFromChat,"kickfromchat"}, - {clif_parse_ChatLeave,"chatleave"}, - {clif_parse_TradeRequest,"traderequest"}, - {clif_parse_TradeAck,"tradeack"}, - {clif_parse_TradeAddItem,"tradeadditem"}, - {clif_parse_TradeOk,"tradeok"}, - {clif_parse_TradeCancel,"tradecancel"}, - {clif_parse_TradeCommit,"tradecommit"}, - {clif_parse_StopAttack,"stopattack"}, - {clif_parse_PutItemToCart,"putitemtocart"}, - {clif_parse_GetItemFromCart,"getitemfromcart"}, - {clif_parse_RemoveOption,"removeoption"}, - {clif_parse_ChangeCart,"changecart"}, - {clif_parse_StatusUp,"statusup"}, - {clif_parse_SkillUp,"skillup"}, - {clif_parse_UseSkillToId,"useskilltoid"}, - {clif_parse_UseSkillToPos,"useskilltopos"}, - {clif_parse_UseSkillToPosMoreInfo,"useskilltoposinfo"}, - {clif_parse_UseSkillMap,"useskillmap"}, - {clif_parse_RequestMemo,"requestmemo"}, - {clif_parse_ProduceMix,"producemix"}, - {clif_parse_Cooking,"cooking"}, - {clif_parse_NpcSelectMenu,"npcselectmenu"}, - {clif_parse_NpcNextClicked,"npcnextclicked"}, - {clif_parse_NpcAmountInput,"npcamountinput"}, - {clif_parse_NpcStringInput,"npcstringinput"}, - {clif_parse_NpcCloseClicked,"npccloseclicked"}, - {clif_parse_ItemIdentify,"itemidentify"}, - {clif_parse_SelectArrow,"selectarrow"}, - {clif_parse_AutoSpell,"autospell"}, - {clif_parse_UseCard,"usecard"}, - {clif_parse_InsertCard,"insertcard"}, - {clif_parse_RepairItem,"repairitem"}, - {clif_parse_WeaponRefine,"weaponrefine"}, - {clif_parse_SolveCharName,"solvecharname"}, - {clif_parse_ResetChar,"resetchar"}, - {clif_parse_LocalBroadcast,"localbroadcast"}, - {clif_parse_MoveToKafra,"movetokafra"}, - {clif_parse_MoveFromKafra,"movefromkafra"}, - {clif_parse_MoveToKafraFromCart,"movetokafrafromcart"}, - {clif_parse_MoveFromKafraToCart,"movefromkafratocart"}, - {clif_parse_CloseKafra,"closekafra"}, - {clif_parse_CreateParty,"createparty"}, - {clif_parse_CreateParty2,"createparty2"}, - {clif_parse_PartyInvite,"partyinvite"}, - {clif_parse_PartyInvite2,"partyinvite2"}, - {clif_parse_ReplyPartyInvite,"replypartyinvite"}, - {clif_parse_ReplyPartyInvite2,"replypartyinvite2"}, - {clif_parse_LeaveParty,"leaveparty"}, - {clif_parse_RemovePartyMember,"removepartymember"}, - {clif_parse_PartyChangeOption,"partychangeoption"}, - {clif_parse_PartyMessage,"partymessage"}, - {clif_parse_PartyChangeLeader,"partychangeleader"}, - {clif_parse_CloseVending,"closevending"}, - {clif_parse_VendingListReq,"vendinglistreq"}, - {clif_parse_PurchaseReq,"purchasereq"}, - {clif_parse_PurchaseReq2,"purchasereq2"}, - {clif_parse_OpenVending,"openvending"}, - {clif_parse_CreateGuild,"createguild"}, - {clif_parse_GuildCheckMaster,"guildcheckmaster"}, - {clif_parse_GuildRequestInfo,"guildrequestinfo"}, - {clif_parse_GuildChangePositionInfo,"guildchangepositioninfo"}, - {clif_parse_GuildChangeMemberPosition,"guildchangememberposition"}, - {clif_parse_GuildRequestEmblem,"guildrequestemblem"}, - {clif_parse_GuildChangeEmblem,"guildchangeemblem"}, - {clif_parse_GuildChangeNotice,"guildchangenotice"}, - {clif_parse_GuildInvite,"guildinvite"}, - {clif_parse_GuildReplyInvite,"guildreplyinvite"}, - {clif_parse_GuildLeave,"guildleave"}, - {clif_parse_GuildExpulsion,"guildexpulsion"}, - {clif_parse_GuildMessage,"guildmessage"}, - {clif_parse_GuildRequestAlliance,"guildrequestalliance"}, - {clif_parse_GuildReplyAlliance,"guildreplyalliance"}, - {clif_parse_GuildDelAlliance,"guilddelalliance"}, - {clif_parse_GuildOpposition,"guildopposition"}, - {clif_parse_GuildBreak,"guildbreak"}, - {clif_parse_PetMenu,"petmenu"}, - {clif_parse_CatchPet,"catchpet"}, - {clif_parse_SelectEgg,"selectegg"}, - {clif_parse_SendEmotion,"sendemotion"}, - {clif_parse_ChangePetName,"changepetname"}, - - {clif_parse_GMKick,"gmkick"}, - {clif_parse_GMHide,"gmhide"}, - {clif_parse_GMReqNoChat,"gmreqnochat"}, - {clif_parse_GMReqAccountName,"gmreqaccname"}, - {clif_parse_GMKickAll,"killall"}, - {clif_parse_GMRecall,"recall"}, - {clif_parse_GMRecall,"summon"}, - {clif_parse_GM_Monster_Item,"itemmonster"}, - {clif_parse_GMShift,"remove"}, - {clif_parse_GMShift,"shift"}, - {clif_parse_GMChangeMapType,"changemaptype"}, - {clif_parse_GMRc,"rc"}, - {clif_parse_GMRecall2,"recall2"}, - {clif_parse_GMRemove2,"remove2"}, - - {clif_parse_NoviceDoriDori,"sndoridori"}, - {clif_parse_NoviceExplosionSpirits,"snexplosionspirits"}, - {clif_parse_PMIgnore,"wisexin"}, - {clif_parse_PMIgnoreList,"wisexlist"}, - {clif_parse_PMIgnoreAll,"wisall"}, - {clif_parse_FriendsListAdd,"friendslistadd"}, - {clif_parse_FriendsListRemove,"friendslistremove"}, - {clif_parse_FriendsListReply,"friendslistreply"}, - {clif_parse_Blacksmith,"blacksmith"}, - {clif_parse_Alchemist,"alchemist"}, - {clif_parse_Taekwon,"taekwon"}, - {clif_parse_RankingPk,"rankingpk"}, - {clif_parse_FeelSaveOk,"feelsaveok"}, - {clif_parse_debug,"debug"}, - {clif_parse_ChangeHomunculusName,"changehomunculusname"}, - {clif_parse_HomMoveToMaster,"hommovetomaster"}, - {clif_parse_HomMoveTo,"hommoveto"}, - {clif_parse_HomAttack,"homattack"}, - {clif_parse_HomMenu,"hommenu"}, - {clif_parse_StoragePassword,"storagepassword"}, - {clif_parse_Hotkey,"hotkey"}, - {clif_parse_AutoRevive,"autorevive"}, - {clif_parse_Check,"check"}, - {clif_parse_Adopt_request,"adoptrequest"}, - {clif_parse_Adopt_reply,"adoptreply"}, - // MAIL SYSTEM - {clif_parse_Mail_refreshinbox,"mailrefresh"}, - {clif_parse_Mail_read,"mailread"}, - {clif_parse_Mail_getattach,"mailgetattach"}, - {clif_parse_Mail_delete,"maildelete"}, - {clif_parse_Mail_return,"mailreturn"}, - {clif_parse_Mail_setattach,"mailsetattach"}, - {clif_parse_Mail_winopen,"mailwinopen"}, - {clif_parse_Mail_send,"mailsend"}, - // AUCTION SYSTEM - {clif_parse_Auction_search,"auctionsearch"}, - {clif_parse_Auction_buysell,"auctionbuysell"}, - {clif_parse_Auction_setitem,"auctionsetitem"}, - {clif_parse_Auction_cancelreg,"auctioncancelreg"}, - {clif_parse_Auction_register,"auctionregister"}, - {clif_parse_Auction_cancel,"auctioncancel"}, - {clif_parse_Auction_close,"auctionclose"}, - {clif_parse_Auction_bid,"auctionbid"}, - // Quest Log System - {clif_parse_questStateAck,"queststate"}, - {clif_parse_cashshop_buy,"cashshopbuy"}, - {clif_parse_ViewPlayerEquip,"viewplayerequip"}, - {clif_parse_EquipTick,"equiptickbox"}, - {clif_parse_BattleChat,"battlechat"}, - {clif_parse_mercenary_action,"mermenu"}, - {clif_parse_progressbar,"progressbar"}, - {clif_parse_SkillSelectMenu,"skillselectmenu"}, - {clif_parse_ItemListWindowSelected,"itemlistwindowselected"}, -#if PACKETVER >= 20091229 - {clif_parse_PartyBookingRegisterReq,"bookingregreq"}, - {clif_parse_PartyBookingSearchReq,"bookingsearchreq"}, - {clif_parse_PartyBookingUpdateReq,"bookingupdatereq"}, - {clif_parse_PartyBookingDeleteReq,"bookingdelreq"}, -#endif - {clif_parse_PVPInfo,"pvpinfo"}, - {clif_parse_LessEffect,"lesseffect"}, - // Buying Store - {clif_parse_ReqOpenBuyingStore,"reqopenbuyingstore"}, - {clif_parse_ReqCloseBuyingStore,"reqclosebuyingstore"}, - {clif_parse_ReqClickBuyingStore,"reqclickbuyingstore"}, - {clif_parse_ReqTradeBuyingStore,"reqtradebuyingstore"}, - // Store Search - {clif_parse_SearchStoreInfo,"searchstoreinfo"}, - {clif_parse_SearchStoreInfoNextPage,"searchstoreinfonextpage"}, - {clif_parse_CloseSearchStoreInfo,"closesearchstoreinfo"}, - {clif_parse_SearchStoreInfoListItemClick,"searchstoreinfolistitemclick"}, - /* */ - { clif_parse_MoveItem , "moveitem" }, - {NULL,NULL} - }; - - // initialize packet_db[SERVER] from hardcoded packet_len_table[] values - memset(packet_db,0,sizeof(packet_db)); - for( i = 0; i < ARRAYLENGTH(packet_len_table); ++i ) - packet_len(i) = packet_len_table[i]; - - sprintf(line, "%s/packet_db.txt", db_path); - if( (fp=fopen(line,"r"))==NULL ){ - ShowFatalError("can't read %s\n", line); - exit(EXIT_FAILURE); - } - - clif_config.packet_db_ver = MAX_PACKET_VER; - packet_ver = MAX_PACKET_VER; // read into packet_db's version by default - while( fgets(line, sizeof(line), fp) ) - { - ln++; - if(line[0]=='/' && line[1]=='/') - continue; - if (sscanf(line,"%256[^:]: %256[^\r\n]",w1,w2) == 2) - { - if(strcmpi(w1,"packet_ver")==0) { - int prev_ver = packet_ver; - skip_ver = 0; - packet_ver = atoi(w2); - if ( packet_ver > MAX_PACKET_VER ) - { //Check to avoid overflowing. [Skotlex] - if( (warned&1) == 0 ) - ShowWarning("The packet_db table only has support up to version %d.\n", MAX_PACKET_VER); - warned &= 1; - skip_ver = 1; - } - else if( packet_ver < 0 ) - { - if( (warned&2) == 0 ) - ShowWarning("Negative packet versions are not supported.\n"); - warned &= 2; - skip_ver = 1; - } - else if( packet_ver == SERVER ) - { - if( (warned&4) == 0 ) - ShowWarning("Packet version %d is reserved for server use only.\n", SERVER); - warned &= 4; - skip_ver = 1; - } - - if( skip_ver ) - { - ShowWarning("Skipping packet version %d.\n", packet_ver); - packet_ver = prev_ver; - continue; - } - // copy from previous version into new version and continue - // - indicating all following packets should be read into the newer version - memcpy(&packet_db[packet_ver], &packet_db[prev_ver], sizeof(packet_db[0])); - continue; - } else if(strcmpi(w1,"packet_db_ver")==0) { - if(strcmpi(w2,"default")==0) //This is the preferred version. - clif_config.packet_db_ver = MAX_PACKET_VER; - else // to manually set the packet DB version - clif_config.packet_db_ver = cap_value(atoi(w2), 0, MAX_PACKET_VER); - - continue; - } - } - - if( skip_ver != 0 ) - continue; // Skipping current packet version - - memset(str,0,sizeof(str)); - for(j=0,p=line;j<4 && p; ++j) - { - str[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if(str[0]==NULL) - continue; - cmd=strtol(str[0],(char **)NULL,0); - if(max_cmd < cmd) - max_cmd = cmd; - if(cmd <= 0 || cmd > MAX_PACKET_DB) - continue; - if(str[1]==NULL){ - ShowError("packet_db: packet len error\n"); - continue; - } - - packet_db[packet_ver][cmd].len = (short)atoi(str[1]); - - if(str[2]==NULL){ - packet_db[packet_ver][cmd].func = NULL; - ln++; - continue; - } - - // look up processing function by name - ARR_FIND( 0, ARRAYLENGTH(clif_parse_func), j, clif_parse_func[j].name != NULL && strcmp(str[2],clif_parse_func[j].name)==0 ); - if( j < ARRAYLENGTH(clif_parse_func) ) - packet_db[packet_ver][cmd].func = clif_parse_func[j].func; - - // set the identifying cmd for the packet_db version - if (strcmp(str[2],"wanttoconnection")==0) - clif_config.connect_cmd[packet_ver] = cmd; - - if(str[3]==NULL){ - ShowError("packet_db: packet error\n"); - exit(EXIT_FAILURE); - } - for(j=0,p2=str[3];p2;j++){ - short k; - str2[j]=p2; - p2=strchr(p2,':'); - if(p2) *p2++=0; - k = atoi(str2[j]); - // if (packet_db[packet_ver][cmd].pos[j] != k && clif_config.prefer_packet_db) // not used for now - - 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; - } - } - fclose(fp); - if(max_cmd > MAX_PACKET_DB) - { - ShowWarning("Found packets up to 0x%X, ignored 0x%X and above.\n", max_cmd, MAX_PACKET_DB); - ShowWarning("Please increase MAX_PACKET_DB and recompile.\n"); - } - if (!clif_config.connect_cmd[clif_config.packet_db_ver]) - { //Locate the nearest version that we still support. [Skotlex] - for(j = clif_config.packet_db_ver; j >= 0 && !clif_config.connect_cmd[j]; j--); - - clif_config.packet_db_ver = j?j:MAX_PACKET_VER; - } - ShowStatus("Done reading packet database from '"CL_WHITE"%s"CL_RESET"'. Using default packet version: "CL_WHITE"%d"CL_RESET".\n", "packet_db.txt", clif_config.packet_db_ver); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int do_init_clif(void) { - const char* colors[COLOR_MAX] = { "0xFF0000" }; - int i; - /** - * Setup Color Table (saves unnecessary load of strtoul on every call) - **/ - for(i = 0; i < COLOR_MAX; i++) { - color_table[i] = strtoul(colors[i],NULL,0); - color_table[i] = (color_table[i] & 0x0000FF) << 16 | (color_table[i] & 0x00FF00) | (color_table[i] & 0xFF0000) >> 16;//RGB to BGR - } - - clif_config.packet_db_ver = -1; // the main packet version of the DB - memset(clif_config.connect_cmd, 0, sizeof(clif_config.connect_cmd)); //The default connect command will be determined after reading the packet_db [Skotlex] - - memset(packet_db,0,sizeof(packet_db)); - //Using the packet_db file is the only way to set up packets now [Skotlex] - packetdb_readdb(); - - set_defaultparse(clif_parse); - if( make_listen_bind(bind_ip,map_port) == -1 ) { - ShowFatalError("can't bind game port\n"); - exit(EXIT_FAILURE); - } - - add_timer_func_list(clif_clearunit_delayed_sub, "clif_clearunit_delayed_sub"); - add_timer_func_list(clif_delayquit, "clif_delayquit"); - - delay_clearunit_ers = ers_new(sizeof(struct block_list),"clif.c::delay_clearunit_ers",ERS_OPT_CLEAR); - - return 0; -} - -void do_final_clif(void) { - ers_destroy(delay_clearunit_ers); -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/grfio.h"
+#include "../common/malloc.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "../common/utils.h"
+#include "../common/ers.h"
+
+#include "map.h"
+#include "chrif.h"
+#include "pc.h"
+#include "status.h"
+#include "npc.h"
+#include "itemdb.h"
+#include "chat.h"
+#include "trade.h"
+#include "storage.h"
+#include "script.h"
+#include "skill.h"
+#include "atcommand.h"
+#include "intif.h"
+#include "battle.h"
+#include "battleground.h"
+#include "mob.h"
+#include "party.h"
+#include "unit.h"
+#include "guild.h"
+#include "vending.h"
+#include "pet.h"
+#include "homunculus.h"
+#include "instance.h"
+#include "mercenary.h"
+#include "elemental.h"
+#include "log.h"
+#include "clif.h"
+#include "mail.h"
+#include "quest.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+/* for clif_clearunit_delayed */
+static struct eri *delay_clearunit_ers;
+
+//#define DUMP_UNKNOWN_PACKET
+//#define DUMP_INVALID_PACKET
+
+struct Clif_Config {
+ int packet_db_ver; //Preferred packet version.
+ int connect_cmd[MAX_PACKET_VER + 1]; //Store the connect command for all versions. [Skotlex]
+} clif_config;
+
+struct s_packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB + 1];
+
+//Converts item type in case of pet eggs.
+static inline int itemtype(int type)
+{
+ return ( type == IT_PETEGG ) ? IT_WEAPON : type;
+}
+
+
+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
+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);
+}
+
+
+static 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);
+}
+
+
+static inline void RBUFPOS(const uint8* p, unsigned short pos, short* x, short* y, unsigned char* dir)
+{
+ p += pos;
+
+ if( x ) {
+ x[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 );
+ }
+
+ if( y ) {
+ y[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 );
+ }
+
+ if( dir ) {
+ dir[0] = ( p[2] & 0x0f );
+ }
+}
+
+
+static inline void RBUFPOS2(const uint8* p, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0)
+{
+ p += pos;
+
+ if( x0 ) {
+ x0[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 );
+ }
+
+ if( y0 ) {
+ y0[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 );
+ }
+
+ if( x1 ) {
+ x1[0] = ( ( p[2] & 0x0f ) << 6 ) | ( p[3] >> 2 );
+ }
+
+ if( y1 ) {
+ y1[0] = ( ( p[3] & 0x03 ) << 8 ) | ( p[4] >> 0 );
+ }
+
+ if( sx0 ) {
+ sx0[0] = ( p[5] & 0xf0 ) >> 4;
+ }
+
+ if( sy0 ) {
+ sy0[0] = ( p[5] & 0x0f ) >> 0;
+ }
+}
+
+
+static inline void RFIFOPOS(int fd, unsigned short pos, short* x, short* y, unsigned char* dir)
+{
+ RBUFPOS(RFIFOP(fd,pos), 0, x, y, dir);
+}
+
+
+static inline void RFIFOPOS2(int fd, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0)
+{
+ RBUFPOS2(WFIFOP(fd,pos), 0, x0, y0, x1, y1, sx0, sy0);
+}
+
+
+//To idenfity disguised characters.
+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;
+}
+
+
+static char map_ip_str[128];
+static uint32 map_ip;
+static uint32 bind_ip = INADDR_ANY;
+static uint16 map_port = 5121;
+int map_fd;
+
+static int clif_parse (int fd);
+
+/*==========================================
+ * Ip setting of map-server
+ *------------------------------------------*/
+int clif_setip(const char* ip)
+{
+ char ip_str[16];
+ map_ip = host2ip(ip);
+ if (!map_ip) {
+ ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip);
+ return 0;
+ }
+
+ strncpy(map_ip_str, ip, sizeof(map_ip_str));
+ ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(map_ip, ip_str));
+ return 1;
+}
+
+void clif_setbindip(const char* ip)
+{
+ char ip_str[16];
+ bind_ip = host2ip(ip);
+ if (bind_ip) {
+ ShowInfo("Map Server Bind IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(bind_ip, ip_str));
+ } else {
+ ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip);
+ }
+}
+
+/*==========================================
+ * Sets map port to 'port'
+ * is run from map.c upon loading map server configuration
+ *------------------------------------------*/
+void clif_setport(uint16 port)
+{
+ map_port = port;
+}
+
+/*==========================================
+ * Returns map server IP
+ *------------------------------------------*/
+uint32 clif_getip(void)
+{
+ return map_ip;
+}
+
+//Refreshes map_server ip, returns the new ip if the ip changed, otherwise it returns 0.
+uint32 clif_refresh_ip(void)
+{
+ uint32 new_ip;
+
+ new_ip = host2ip(map_ip_str);
+ if (new_ip && new_ip != map_ip) {
+ map_ip = new_ip;
+ ShowInfo("Updating IP resolution of [%s].\n", map_ip_str);
+ return map_ip;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Returns map port which is set by clif_setport()
+ *------------------------------------------*/
+uint16 clif_getport(void)
+{
+ return map_port;
+}
+
+#if PACKETVER >= 20071106
+static inline unsigned char clif_bl_type(struct block_list *bl) {
+ switch (bl->type) {
+ case BL_PC: return disguised(bl)?0x1:0x0; //PC_TYPE
+ case BL_ITEM: return 0x2; //ITEM_TYPE
+ case BL_SKILL: return 0x3; //SKILL_TYPE
+ case BL_CHAT: return 0x4; //UNKNOWN_TYPE
+ case BL_MOB: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x5; //NPC_MOB_TYPE
+ case BL_NPC: return 0x6; //NPC_EVT_TYPE
+ case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE
+ case BL_HOM: return 0x8; //NPC_HOM_TYPE
+ case BL_MER: return 0x9; //NPC_MERSOL_TYPE
+ case BL_ELEM: return 0xa; //NPC_ELEMENTAL_TYPE
+ default: return 0x1; //NPC_TYPE
+ }
+}
+#endif
+
+/*==========================================
+ * 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
+ *------------------------------------------*/
+static int clif_send_sub(struct block_list *bl, va_list ap)
+{
+ struct block_list *src_bl;
+ struct map_session_data *sd;
+ unsigned char *buf;
+ int len, type, fd;
+
+ nullpo_ret(bl);
+ nullpo_ret(sd = (struct map_session_data *)bl);
+
+ fd = sd->fd;
+ if (!fd) //Don't send to disconnected clients.
+ return 0;
+
+ buf = va_arg(ap,unsigned char*);
+ len = va_arg(ap,int);
+ nullpo_ret(src_bl = va_arg(ap,struct block_list*));
+ type = va_arg(ap,int);
+
+ switch(type)
+ {
+ case AREA_WOS:
+ if (bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOC:
+ if (sd->chatID || bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOSC:
+ {
+ if(src_bl->type == BL_PC){
+ struct map_session_data *ssd = (struct map_session_data *)src_bl;
+ if (ssd && sd->chatID && (sd->chatID == ssd->chatID))
+ return 0;
+ }
+ else if(src_bl->type == BL_NPC) {
+ struct npc_data *nd = (struct npc_data *)src_bl;
+ if (nd && sd->chatID && (sd->chatID == nd->chat_id))
+ return 0;
+ }
+ }
+ break;
+ }
+
+ if (session[fd] == NULL)
+ return 0;
+
+ WFIFOHEAD(fd, len);
+ if (WFIFOP(fd,0) == buf) {
+ ShowError("WARNING: Invalid use of clif_send function\n");
+ ShowError(" Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0));
+ ShowError(" Please correct your code.\n");
+ // don't send to not move the pointer of the packet for next sessions in the loop
+ //WFIFOSET(fd,0);//## TODO is this ok?
+ //NO. It is not ok. There is the chance WFIFOSET actually sends the buffer data, and shifts elements around, which will corrupt the buffer.
+ return 0;
+ }
+
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 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)
+{
+ int i;
+ struct map_session_data *sd, *tsd;
+ struct party_data *p = NULL;
+ struct guild *g = NULL;
+ struct battleground_data *bg = NULL;
+ int x0 = 0, x1 = 0, y0 = 0, y1 = 0, fd;
+ struct s_mapiterator* iter;
+
+ if( type != ALL_CLIENT && type != CHAT_MAINCHAT )
+ nullpo_ret(bl);
+
+ sd = BL_CAST(BL_PC, bl);
+
+ switch(type) {
+
+ case ALL_CLIENT: //All player clients.
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case ALL_SAMEMAP: //All players on the same map
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( bl->m == tsd->bl.m && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case AREA:
+ case AREA_WOSC:
+ if (sd && bl->prev == NULL) //Otherwise source misses the packet.[Skotlex]
+ clif_send (buf, len, bl, SELF);
+ case AREA_WOC:
+ case AREA_WOS:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-AREA_SIZE, bl->y-AREA_SIZE, bl->x+AREA_SIZE, bl->y+AREA_SIZE,
+ BL_PC, buf, len, bl, type);
+ break;
+ case AREA_CHAT_WOC:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-(AREA_SIZE-5), bl->y-(AREA_SIZE-5),
+ bl->x+(AREA_SIZE-5), bl->y+(AREA_SIZE-5), BL_PC, buf, len, bl, AREA_WOC);
+ break;
+
+ case CHAT:
+ case CHAT_WOS:
+ {
+ struct chat_data *cd;
+ if (sd) {
+ cd = (struct chat_data*)map_id2bl(sd->chatID);
+ } else if (bl->type == BL_CHAT) {
+ cd = (struct chat_data*)bl;
+ } else break;
+ if (cd == NULL)
+ break;
+ for(i = 0; i < cd->users; i++) {
+ if (type == CHAT_WOS && cd->usersd[i] == sd)
+ continue;
+ if (packet_db[cd->usersd[i]->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ if ((fd=cd->usersd[i]->fd) >0 && session[fd]) // Added check to see if session exists [PoW]
+ {
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ }
+ }
+ break;
+
+ case CHAT_MAINCHAT: //[LuzZza]
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( tsd->state.mainchat && tsd->chatID == 0 && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case PARTY_AREA:
+ case PARTY_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case PARTY:
+ case PARTY_WOS:
+ case PARTY_SAMEMAP:
+ case PARTY_SAMEMAP_WOS:
+ if (sd && sd->status.party_id)
+ p = party_search(sd->status.party_id);
+
+ if (p) {
+ for(i=0;i<MAX_PARTY;i++){
+ if( (sd = p->data[i].sd) == NULL )
+ continue;
+
+ if( !(fd=sd->fd) )
+ continue;
+
+ if( sd->bl.id == bl->id && (type == PARTY_WOS || type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS) )
+ continue;
+
+ if( type != PARTY && type != PARTY_WOS && bl->m != sd->bl.m )
+ continue;
+
+ if( (type == PARTY_AREA || type == PARTY_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
+ continue;
+
+ if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( tsd->partyspy == p->party.party_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ }
+ break;
+
+ case DUEL:
+ case DUEL_WOS:
+ if (!sd || !sd->duel_group) break; //Invalid usage.
+
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( type == DUEL_WOS && bl->id == tsd->bl.id )
+ continue;
+ if( sd->duel_group == tsd->duel_group && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case SELF:
+ if (sd && (fd=sd->fd) && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ break;
+
+ // New definitions for guilds [Valaris] - Cleaned up and reorganized by [Skotlex]
+ case GUILD_AREA:
+ case GUILD_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case GUILD_SAMEMAP:
+ case GUILD_SAMEMAP_WOS:
+ case GUILD:
+ case GUILD_WOS:
+ case GUILD_NOBG:
+ if (sd && sd->status.guild_id)
+ g = guild_search(sd->status.guild_id);
+
+ if (g) {
+ for(i = 0; i < g->max_member; i++) {
+ if( (sd = g->member[i].sd) != NULL )
+ {
+ if( !(fd=sd->fd) )
+ continue;
+
+ if( type == GUILD_NOBG && sd->bg_id )
+ continue;
+
+ if( sd->bl.id == bl->id && (type == GUILD_WOS || type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS) )
+ continue;
+
+ if( type != GUILD && type != GUILD_NOBG && type != GUILD_WOS && sd->bl.m != bl->m )
+ continue;
+
+ if( (type == GUILD_AREA || type == GUILD_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
+ continue;
+
+ if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( tsd->guildspy == g->guild_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ }
+ break;
+
+ case BG_AREA:
+ case BG_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case BG_SAMEMAP:
+ case BG_SAMEMAP_WOS:
+ case BG:
+ case BG_WOS:
+ if( sd && sd->bg_id && (bg = bg_team_search(sd->bg_id)) != NULL )
+ {
+ for( i = 0; i < MAX_BG_MEMBERS; i++ )
+ {
+ if( (sd = bg->members[i].sd) == NULL || !(fd = sd->fd) )
+ continue;
+ if( sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) )
+ continue;
+ if( type != BG && type != BG_WOS && sd->bl.m != bl->m )
+ continue;
+ if( (type == BG_AREA || type == BG_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
+ continue;
+ if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ }
+ break;
+
+ default:
+ ShowError("clif_send: Unrecognized type %d\n",type);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/// Notifies the client, that it's connection attempt was accepted.
+/// 0073 <start time>.L <position>.3B <x size>.B <y size>.B (ZC_ACCEPT_ENTER)
+/// 02eb <start time>.L <position>.3B <x size>.B <y size>.B <font>.W (ZC_ACCEPT_ENTER2)
+void clif_authok(struct map_session_data *sd)
+{
+#if PACKETVER < 20080102
+ const int cmd = 0x73;
+#else
+ const int cmd = 0x2eb;
+#endif
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(cmd));
+ WFIFOW(fd, 0) = cmd;
+ WFIFOL(fd, 2) = gettick();
+ WFIFOPOS(fd, 6, sd->bl.x, sd->bl.y, sd->ud.dir);
+ WFIFOB(fd, 9) = 5; // ignored
+ WFIFOB(fd,10) = 5; // ignored
+#if PACKETVER >= 20080102
+ WFIFOW(fd,11) = sd->user_font; // FIXME: Font is currently not saved.
+#endif
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Notifies the client, that it's connection attempt was refused (ZC_REFUSE_ENTER).
+/// 0074 <error code>.B
+/// error code:
+/// 0 = client type mismatch
+/// 1 = ID mismatch
+/// 2 = mobile - out of available time
+/// 3 = mobile - already logged in
+/// 4 = mobile - waiting state
+void clif_authrefuse(int fd, uint8 error_code)
+{
+ WFIFOHEAD(fd,packet_len(0x74));
+ WFIFOW(fd,0) = 0x74;
+ WFIFOB(fd,2) = error_code;
+ WFIFOSET(fd,packet_len(0x74));
+}
+
+
+/// Notifies the client of a ban or forced disconnect (SC_NOTIFY_BAN).
+/// 0081 <error code>.B
+/// error code:
+/// 0 = BAN_UNFAIR
+/// 1 = server closed -> MsgStringTable[4]
+/// 2 = ID already logged in -> MsgStringTable[5]
+/// 3 = timeout/too much lag -> MsgStringTable[241]
+/// 4 = server full -> MsgStringTable[264]
+/// 5 = underaged -> MsgStringTable[305]
+/// 8 = Server sill recognizes last connection -> MsgStringTable[441]
+/// 9 = too many connections from this ip -> MsgStringTable[529]
+/// 10 = out of available time paid for -> MsgStringTable[530]
+/// 11 = BAN_PAY_SUSPEND
+/// 12 = BAN_PAY_CHANGE
+/// 13 = BAN_PAY_WRONGIP
+/// 14 = BAN_PAY_PNGAMEROOM
+/// 15 = disconnected by a GM -> if( servicetype == taiwan ) MsgStringTable[579]
+/// 16 = BAN_JAPAN_REFUSE1
+/// 17 = BAN_JAPAN_REFUSE2
+/// 18 = BAN_INFORMATION_REMAINED_ANOTHER_ACCOUNT
+/// 100 = BAN_PC_IP_UNFAIR
+/// 101 = BAN_PC_IP_COUNT_ALL
+/// 102 = BAN_PC_IP_COUNT
+/// 103 = BAN_GRAVITY_MEM_AGREE
+/// 104 = BAN_GAME_MEM_AGREE
+/// 105 = BAN_HAN_VALID
+/// 106 = BAN_PC_IP_LIMIT_ACCESS
+/// 107 = BAN_OVER_CHARACTER_LIST
+/// 108 = BAN_IP_BLOCK
+/// 109 = BAN_INVALID_PWD_CNT
+/// 110 = BAN_NOT_ALLOWED_JOBCLASS
+/// ? = disconnected -> MsgStringTable[3]
+void clif_authfail_fd(int fd, int type)
+{
+ if (!fd || !session[fd] || session[fd]->func_parse != clif_parse) //clif_authfail should only be invoked on players!
+ return;
+
+ WFIFOHEAD(fd, packet_len(0x81));
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = type;
+ WFIFOSET(fd,packet_len(0x81));
+ set_eof(fd);
+}
+
+
+/// Notifies the client, whether it can disconnect and change servers (ZC_RESTART_ACK).
+/// 00b3 <type>.B
+/// type:
+/// 1 = disconnect, char-select
+/// ? = nothing
+void clif_charselectok(int id, uint8 ok)
+{
+ struct map_session_data* sd;
+ int fd;
+
+ if ((sd = map_id2sd(id)) == NULL || !sd->fd)
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xb3));
+ WFIFOW(fd,0) = 0xb3;
+ WFIFOB(fd,2) = ok;
+ WFIFOSET(fd,packet_len(0xb3));
+}
+
+/// Makes an item appear on the ground.
+/// 009e <id>.L <name id>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY)
+/// 084b (ZC_ITEM_FALL_ENTRY4)
+void clif_dropflooritem(struct flooritem_data* fitem)
+{
+ uint8 buf[17];
+ int view;
+
+ nullpo_retv(fitem);
+
+ if (fitem->item_data.nameid <= 0)
+ return;
+
+ WBUFW(buf, 0) = 0x9e;
+ WBUFL(buf, 2) = fitem->bl.id;
+ WBUFW(buf, 6) = ((view = itemdb_viewid(fitem->item_data.nameid)) > 0) ? view : fitem->item_data.nameid;
+ WBUFB(buf, 8) = fitem->item_data.identify;
+ WBUFW(buf, 9) = fitem->bl.x;
+ WBUFW(buf,11) = fitem->bl.y;
+ WBUFB(buf,13) = fitem->subx;
+ WBUFB(buf,14) = fitem->suby;
+ WBUFW(buf,15) = fitem->item_data.amount;
+
+ clif_send(buf, packet_len(0x9e), &fitem->bl, AREA);
+}
+
+
+
+/// Makes an item disappear from the ground.
+/// 00a1 <id>.L (ZC_ITEM_DISAPPEAR)
+void clif_clearflooritem(struct flooritem_data *fitem, int fd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(fitem);
+
+ WBUFW(buf,0) = 0xa1;
+ WBUFL(buf,2) = fitem->bl.id;
+
+ if (fd == 0) {
+ clif_send(buf, packet_len(0xa1), &fitem->bl, AREA);
+ } else {
+ WFIFOHEAD(fd,packet_len(0xa1));
+ memcpy(WFIFOP(fd,0), buf, packet_len(0xa1));
+ WFIFOSET(fd,packet_len(0xa1));
+ }
+}
+
+
+/// Makes a unit (char, npc, mob, homun) disappear to one client (ZC_NOTIFY_VANISH).
+/// 0080 <id>.L <type>.B
+/// type:
+/// 0 = out of sight
+/// 1 = died
+/// 2 = logged out
+/// 3 = teleport
+/// 4 = trickdead
+void clif_clearunit_single(int id, clr_type type, int fd)
+{
+ WFIFOHEAD(fd, packet_len(0x80));
+ WFIFOW(fd,0) = 0x80;
+ WFIFOL(fd,2) = id;
+ WFIFOB(fd,6) = type;
+ WFIFOSET(fd, packet_len(0x80));
+}
+
+/// Makes a unit (char, npc, mob, homun) disappear to all clients in area (ZC_NOTIFY_VANISH).
+/// 0080 <id>.L <type>.B
+/// type:
+/// 0 = out of sight
+/// 1 = died
+/// 2 = logged out
+/// 3 = teleport
+/// 4 = trickdead
+void clif_clearunit_area(struct block_list* bl, clr_type type)
+{
+ unsigned char buf[8];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x80;
+ WBUFL(buf,2) = bl->id;
+ WBUFB(buf,6) = type;
+
+ clif_send(buf, packet_len(0x80), bl, type == CLR_DEAD ? AREA : AREA_WOS);
+
+ if(disguised(bl)) {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x80), bl, SELF);
+ }
+}
+
+
+/// Used to make monsters with player-sprites disappear after dying
+/// like normal monsters, because the client does not remove those
+/// automatically.
+static int clif_clearunit_delayed_sub(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *bl = (struct block_list *)data;
+ clif_clearunit_area(bl, (clr_type) id);
+ ers_free(delay_clearunit_ers,bl);
+ return 0;
+}
+void clif_clearunit_delayed(struct block_list* bl, clr_type type, unsigned int tick)
+{
+ struct block_list *tbl = ers_alloc(delay_clearunit_ers, struct block_list);
+ memcpy (tbl, bl, sizeof (struct block_list));
+ add_timer(tick, clif_clearunit_delayed_sub, (int)type, (intptr_t)tbl);
+}
+
+void clif_get_weapon_view(struct map_session_data* sd, unsigned short *rhand, unsigned short *lhand)
+{
+ if(sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER))
+ {
+ *rhand = *lhand = 0;
+ return;
+ }
+
+#if PACKETVER < 4
+ *rhand = sd->status.weapon;
+ *lhand = sd->status.shield;
+#else
+ if (sd->equip_index[EQI_HAND_R] >= 0 &&
+ sd->inventory_data[sd->equip_index[EQI_HAND_R]])
+ {
+ struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_R]];
+ if (id->view_id > 0)
+ *rhand = id->view_id;
+ else
+ *rhand = id->nameid;
+ } else
+ *rhand = 0;
+
+ if (sd->equip_index[EQI_HAND_L] >= 0 &&
+ sd->equip_index[EQI_HAND_L] != sd->equip_index[EQI_HAND_R] &&
+ sd->inventory_data[sd->equip_index[EQI_HAND_L]])
+ {
+ struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_L]];
+ if (id->view_id > 0)
+ *lhand = id->view_id;
+ else
+ *lhand = id->nameid;
+ } else
+ *lhand = 0;
+#endif
+}
+
+//To make the assignation of the level based on limits clearer/easier. [Skotlex]
+static int clif_setlevel_sub(int lv)
+{
+ if( lv < battle_config.max_lv )
+ {
+ ;
+ }
+ else if( lv < battle_config.aura_lv )
+ {
+ lv = battle_config.max_lv - 1;
+ }
+ else
+ {
+ lv = battle_config.max_lv;
+ }
+
+ return lv;
+}
+
+static int clif_setlevel(struct block_list* bl)
+{
+ int lv = status_get_lv(bl);
+ if( battle_config.client_limit_unit_lv&bl->type )
+ return clif_setlevel_sub(lv);
+ switch( bl->type )
+ {
+ case BL_NPC:
+ case BL_PET:
+ // npcs and pets do not have level
+ return 0;
+ }
+ return lv;
+}
+
+/*==========================================
+ * Prepares 'unit standing/spawning' packet
+ *------------------------------------------*/
+static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn)
+{
+ struct map_session_data* sd;
+ struct status_change* sc = status_get_sc(bl);
+ struct view_data* vd = status_get_viewdata(bl);
+ unsigned char *buf = WBUFP(buffer,0);
+#if PACKETVER < 20091103
+ bool type = !pcdb_checkid(vd->class_);
+#endif
+ unsigned short offset = 0;
+#if PACKETVER >= 20091103
+ const char *name;
+#endif
+ sd = BL_CAST(BL_PC, bl);
+
+#if PACKETVER < 20091103
+ if(type)
+ WBUFW(buf,0) = spawn?0x7c:0x78;
+ else
+#endif
+#if PACKETVER < 4
+ WBUFW(buf,0) = spawn?0x79:0x78;
+#elif PACKETVER < 7
+ WBUFW(buf,0) = spawn?0x1d9:0x1d8;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0) = spawn?0x22b:0x22a;
+#elif PACKETVER < 20091103
+ WBUFW(buf,0) = spawn?0x2ed:0x2ee;
+#elif PACKETVER < 20101124
+ WBUFW(buf,0) = spawn?0x7f8:0x7f9;
+#else
+ WBUFW(buf,0) = spawn?0x858:0x857;
+#endif
+
+#if PACKETVER >= 20091103
+ name = status_get_name(bl);
+#if PACKETVER < 20110111
+ WBUFW(buf,2) = (spawn?62:63)+strlen(name);
+#else
+ WBUFW(buf,2) = (spawn?64:65)+strlen(name);
+#endif
+ WBUFB(buf,4) = clif_bl_type(bl);
+ offset+=3;
+ buf = WBUFP(buffer,offset);
+#elif PACKETVER >= 20071106
+ if (type) { //Non-player packets
+ WBUFB(buf,2) = clif_bl_type(bl);
+ offset++;
+ buf = WBUFP(buffer,offset);
+ }
+#endif
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = status_get_speed(bl);
+ WBUFW(buf, 8) = (sc)? sc->opt1 : 0;
+ WBUFW(buf,10) = (sc)? sc->opt2 : 0;
+#if PACKETVER < 20091103
+ if (type&&spawn) { //uses an older and different packet structure
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+ WBUFW(buf,14) = vd->hair_style;
+ WBUFW(buf,16) = vd->weapon;
+ WBUFW(buf,18) = vd->head_bottom;
+ WBUFW(buf,20) = vd->class_; //Pet armor (ignored by client)
+ WBUFW(buf,22) = vd->shield;
+ } else {
+#endif
+#if PACKETVER >= 20091103
+ WBUFL(buf,12) = (sc)? sc->option : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+#elif PACKETVER >= 7
+ if (!type) {
+ WBUFL(buf,12) = (sc)? sc->option : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+ } else
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+#else
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+#endif
+ WBUFW(buf,14) = vd->class_;
+ WBUFW(buf,16) = vd->hair_style;
+ WBUFW(buf,18) = vd->weapon;
+#if PACKETVER < 4
+ WBUFW(buf,20) = vd->head_bottom;
+ WBUFW(buf,22) = vd->shield;
+#else
+ WBUFW(buf,20) = vd->shield;
+ WBUFW(buf,22) = vd->head_bottom;
+#endif
+#if PACKETVER < 20091103
+ }
+#endif
+ WBUFW(buf,24) = vd->head_top;
+ WBUFW(buf,26) = vd->head_mid;
+
+ if( bl->type == BL_NPC && vd->class_ == FLAG_CLASS )
+ { //The hell, why flags work like this?
+ WBUFW(buf,22) = status_get_emblem_id(bl);
+ WBUFW(buf,24) = GetWord(status_get_guild_id(bl), 1);
+ WBUFW(buf,26) = GetWord(status_get_guild_id(bl), 0);
+ }
+
+ WBUFW(buf,28) = vd->hair_color;
+ WBUFW(buf,30) = vd->cloth_color;
+ WBUFW(buf,32) = (sd)? sd->head_dir : 0;
+#if PACKETVER < 20091103
+ if (type&&spawn) { //End of packet 0x7c
+ WBUFB(buf,34) = (sd)?sd->status.karma:0; // karma
+ WBUFB(buf,35) = vd->sex;
+ WBUFPOS(buf,36,bl->x,bl->y,unit_getdir(bl));
+ WBUFB(buf,39) = 0;
+ WBUFB(buf,40) = 0;
+ return packet_len(0x7c);
+ }
+#endif
+#if PACKETVER >= 20110111
+ WBUFW(buf,34) = vd->robe;
+ offset+= 2;
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFL(buf,34) = status_get_guild_id(bl);
+ WBUFW(buf,38) = status_get_emblem_id(bl);
+ WBUFW(buf,40) = (sd)? sd->status.manner : 0;
+#if PACKETVER >= 20091103
+ WBUFL(buf,42) = (sc)? sc->opt3 : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+#elif PACKETVER >= 7
+ if (!type) {
+ WBUFL(buf,42) = (sc)? sc->opt3 : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+ } else
+ WBUFW(buf,42) = (sc)? sc->opt3 : 0;
+#else
+ WBUFW(buf,42) = (sc)? sc->opt3 : 0;
+#endif
+ WBUFB(buf,44) = (sd)? sd->status.karma : 0;
+ WBUFB(buf,45) = vd->sex;
+ WBUFPOS(buf,46,bl->x,bl->y,unit_getdir(bl));
+ WBUFB(buf,49) = (sd)? 5 : 0;
+ WBUFB(buf,50) = (sd)? 5 : 0;
+ if (!spawn) {
+ WBUFB(buf,51) = vd->dead_sit;
+ offset++;
+ buf = WBUFP(buffer,offset);
+ }
+ WBUFW(buf,51) = clif_setlevel(bl);
+#if PACKETVER < 20091103
+ if (type) //End for non-player packet
+ return packet_len(WBUFW(buffer,0));
+#endif
+#if PACKETVER >= 20080102
+ WBUFW(buf,53) = sd?sd->user_font:0;
+#endif
+#if PACKETVER >= 20091103
+ memcpy((char*)WBUFP(buf,55), name, NAME_LENGTH);
+ return WBUFW(buffer,2);
+#else
+ return packet_len(WBUFW(buffer,0));
+#endif
+}
+
+/*==========================================
+ * Prepares 'unit walking' packet
+ *------------------------------------------*/
+static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, unsigned char* buffer)
+{
+ struct map_session_data* sd;
+ struct status_change* sc = status_get_sc(bl);
+ struct view_data* vd = status_get_viewdata(bl);
+ unsigned char* buf = WBUFP(buffer,0);
+#if PACKETVER >= 7
+ unsigned short offset = 0;
+#endif
+#if PACKETVER >= 20091103
+ const char *name;
+#endif
+
+ sd = BL_CAST(BL_PC, bl);
+
+#if PACKETVER < 4
+ WBUFW(buf, 0) = 0x7b;
+#elif PACKETVER < 7
+ WBUFW(buf, 0) = 0x1da;
+#elif PACKETVER < 20080102
+ WBUFW(buf, 0) = 0x22c;
+#elif PACKETVER < 20091103
+ WBUFW(buf, 0) = 0x2ec;
+#elif PACKETVER < 20101124
+ WBUFW(buf, 0) = 0x7f7;
+#else
+ WBUFW(buf, 0) = 0x856;
+#endif
+
+#if PACKETVER >= 20091103
+ name = status_get_name(bl);
+#if PACKETVER < 20110111
+ WBUFW(buf, 2) = 69+strlen(name);
+#else
+ WBUFW(buf, 2) = 71+strlen(name);
+#endif
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+#endif
+#if PACKETVER >= 20071106
+ WBUFB(buf, 2) = clif_bl_type(bl);
+ offset++;
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = status_get_speed(bl);
+ WBUFW(buf, 8) = (sc)? sc->opt1 : 0;
+ WBUFW(buf,10) = (sc)? sc->opt2 : 0;
+#if PACKETVER < 7
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+#else
+ WBUFL(buf,12) = (sc)? sc->option : 0;
+ offset+=2; //Shift the rest of elements by 2 bytes.
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFW(buf,14) = vd->class_;
+ WBUFW(buf,16) = vd->hair_style;
+ WBUFW(buf,18) = vd->weapon;
+#if PACKETVER < 4
+ WBUFW(buf,20) = vd->head_bottom;
+ WBUFL(buf,22) = gettick();
+ WBUFW(buf,26) = vd->shield;
+#else
+ WBUFW(buf,20) = vd->shield;
+ WBUFW(buf,22) = vd->head_bottom;
+ WBUFL(buf,24) = gettick();
+#endif
+ WBUFW(buf,28) = vd->head_top;
+ WBUFW(buf,30) = vd->head_mid;
+ WBUFW(buf,32) = vd->hair_color;
+ WBUFW(buf,34) = vd->cloth_color;
+ WBUFW(buf,36) = (sd)? sd->head_dir : 0;
+#if PACKETVER >= 20110111
+ WBUFW(buf,38) = vd->robe;
+ offset+= 2;
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFL(buf,38) = status_get_guild_id(bl);
+ WBUFW(buf,42) = status_get_emblem_id(bl);
+ WBUFW(buf,44) = (sd)? sd->status.manner : 0;
+#if PACKETVER < 7
+ WBUFW(buf,46) = (sc)? sc->opt3 : 0;
+#else
+ WBUFL(buf,46) = (sc)? sc->opt3 : 0;
+ offset+=2; //Shift the rest of elements by 2 bytes.
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFB(buf,48) = (sd)? sd->status.karma : 0;
+ WBUFB(buf,49) = vd->sex;
+ WBUFPOS2(buf,50,bl->x,bl->y,ud->to_x,ud->to_y,8,8);
+ WBUFB(buf,56) = (sd)? 5 : 0;
+ WBUFB(buf,57) = (sd)? 5 : 0;
+ WBUFW(buf,58) = clif_setlevel(bl);
+#if PACKETVER >= 20080102
+ WBUFW(buf,60) = sd?sd->user_font:0;
+#endif
+#if PACKETVER >= 20091103
+ memcpy((char*)WBUFP(buf,62), name, NAME_LENGTH);
+ return WBUFW(buffer,2);
+#else
+ return packet_len(WBUFW(buffer,0));
+#endif
+}
+
+//Modifies the buffer for disguise characters and sends it to self.
+//Used for spawn/walk packets, where the ID offset changes for packetver >=9
+static void clif_setdisguise(struct block_list *bl, unsigned char *buf,int len)
+{
+#if PACKETVER >= 20091103
+ WBUFB(buf,4)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE
+ WBUFL(buf,5)=-bl->id;
+#elif PACKETVER >= 20071106
+ WBUFB(buf,2)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE
+ WBUFL(buf,3)=-bl->id;
+#else
+ WBUFL(buf,2)=-bl->id;
+#endif
+ clif_send(buf, len, bl, SELF);
+}
+
+
+/// Changes sprite of an NPC object (ZC_NPCSPRITE_CHANGE).
+/// 01b0 <id>.L <type>.B <value>.L
+/// type:
+/// unused
+void clif_class_change(struct block_list *bl,int class_,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ if(!pcdb_checkid(class_))
+ {// player classes yield missing sprites
+ WBUFW(buf,0)=0x1b0;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFL(buf,7)=class_;
+ clif_send(buf,packet_len(0x1b0),bl,AREA);
+ }
+}
+
+
+/// Notifies the client of an object's spirits.
+/// 01d0 <id>.L <amount>.W (ZC_SPIRITS)
+/// 01e1 <id>.L <amount>.W (ZC_SPIRITS2)
+static void clif_spiritball_single(int fd, struct map_session_data *sd)
+{
+ WFIFOHEAD(fd, packet_len(0x1e1));
+ WFIFOW(fd,0)=0x1e1;
+ WFIFOL(fd,2)=sd->bl.id;
+ WFIFOW(fd,6)=sd->spiritball;
+ WFIFOSET(fd, packet_len(0x1e1));
+}
+
+/*==========================================
+ * Kagerou/Oboro amulet spirit
+ *------------------------------------------*/
+static void clif_talisman_single(int fd, struct map_session_data *sd, short type)
+{
+ WFIFOHEAD(fd, packet_len(0x08cf));
+ WFIFOW(fd,0)=0x08cf;
+ WFIFOL(fd,2)=sd->bl.id;
+ WFIFOW(fd,6)=type;
+ WFIFOW(fd,8)=sd->talisman[type];
+ WFIFOSET(fd, packet_len(0x08cf));
+}
+
+/*==========================================
+ * 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)
+{
+ int16 m = sd->bl.m;
+ int fd = sd->fd;
+
+ if (map[m].flag.snow
+ || map[m].flag.clouds
+ || map[m].flag.fog
+ || map[m].flag.fireworks
+ || map[m].flag.sakura
+ || map[m].flag.leaves
+ /**
+ * 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)
+ clif_specialeffect_single(&sd->bl, 162, fd);
+ if (map[m].flag.clouds)
+ clif_specialeffect_single(&sd->bl, 233, fd);
+ if (map[m].flag.clouds2)
+ clif_specialeffect_single(&sd->bl, 516, fd);
+ if (map[m].flag.fog)
+ clif_specialeffect_single(&sd->bl, 515, fd);
+ if (map[m].flag.fireworks) {
+ clif_specialeffect_single(&sd->bl, 297, fd);
+ clif_specialeffect_single(&sd->bl, 299, fd);
+ clif_specialeffect_single(&sd->bl, 301, fd);
+ }
+ if (map[m].flag.sakura)
+ clif_specialeffect_single(&sd->bl, 163, fd);
+ if (map[m].flag.leaves)
+ clif_specialeffect_single(&sd->bl, 333, 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(int16 m)
+{
+ struct s_mapiterator* iter;
+ struct map_session_data *sd=NULL;
+
+ iter = mapit_getallusers();
+ for( sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) )
+ {
+ if( sd->bl.m == m )
+ clif_weather_check(sd);
+ }
+ 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];
+ struct view_data *vd;
+ int len;
+
+ vd = status_get_viewdata(bl);
+ if( !vd || vd->class_ == INVISIBLE_CLASS )
+ return 0;
+
+ /**
+ * Hide NPC from maya purple card.
+ **/
+ if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
+ return 0;
+
+ len = clif_set_unit_idle(bl, buf,true);
+ clif_send(buf, len, bl, AREA_WOS);
+ if (disguised(bl))
+ clif_setdisguise(bl, buf, len);
+
+ if (vd->cloth_color)
+ clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ {
+ TBL_PC *sd = ((TBL_PC*)bl);
+ int i;
+ if (sd->spiritball > 0)
+ clif_spiritball(&sd->bl);
+ if(sd->state.size==SZ_BIG) // tiny/big players [Valaris]
+ clif_specialeffect(bl,423,AREA);
+ else if(sd->state.size==SZ_MEDIUM)
+ clif_specialeffect(bl,421,AREA);
+ if( sd->bg_id && map[sd->bl.m].flag.battleground )
+ clif_sendbgemblem_area(sd);
+ 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);
+ }
+ for(i = 1; i < 5; i++){
+ if( sd->talisman[i] > 0 )
+ clif_talisman(sd, i);
+ }
+ #ifdef NEW_CARTS
+ if( sd->sc.data[SC_PUSH_CART] )
+ clif_status_load_notick(&sd->bl, SI_ON_PUSH_CART, 2, sd->sc.data[SC_PUSH_CART]->val1, 0, 0);
+ #endif
+ #if PACKETVER <= 20120207
+ if (sd->status.robe)
+ clif_refreshlook(bl,bl->id,LOOK_ROBE,sd->status.robe,AREA);
+ #endif
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB *md = ((TBL_MOB*)bl);
+ if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,AREA);
+ else if(md->special_state.size==SZ_MEDIUM)
+ clif_specialeffect(&md->bl,421,AREA);
+ }
+ break;
+ case BL_NPC:
+ {
+ TBL_NPC *nd = ((TBL_NPC*)bl);
+ if( nd->size == SZ_BIG )
+ clif_specialeffect(&nd->bl,423,AREA);
+ else if( nd->size == SZ_MEDIUM )
+ clif_specialeffect(&nd->bl,421,AREA);
+ }
+ break;
+ case BL_PET:
+ if (vd->head_bottom)
+ clif_pet_equip_area((TBL_PET*)bl); // needed to display pet equip properly
+ break;
+ }
+ return 0;
+}
+
+/// Sends information about owned homunculus to the client (ZC_PROPERTY_HOMUN). [orn]
+/// 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W
+void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag)
+{
+ struct status_data *status;
+ unsigned char buf[128];
+ int m_class;
+
+ nullpo_retv(hd);
+
+ status = &hd->battle_status;
+ m_class = hom_class2mapid(hd->homunculus.class_);
+
+ memset(buf,0,packet_len(0x22e));
+ WBUFW(buf,0)=0x22e;
+ memcpy(WBUFP(buf,2),hd->homunculus.name,NAME_LENGTH);
+ // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true)
+ WBUFB(buf,26)=(battle_config.hom_rename?0:hd->homunculus.rename_flag) | (hd->homunculus.vaporize << 1) | (hd->homunculus.hp?0:4);
+ WBUFW(buf,27)=hd->homunculus.level;
+ WBUFW(buf,29)=hd->homunculus.hunger;
+ WBUFW(buf,31)=(unsigned short) (hd->homunculus.intimacy / 100) ;
+ WBUFW(buf,33)=0; // equip id
+ WBUFW(buf,35)=cap_value(status->rhw.atk2+status->batk, 0, INT16_MAX);
+ WBUFW(buf,37)=cap_value(status->matk_max, 0, INT16_MAX);
+ WBUFW(buf,39)=status->hit;
+ if (battle_config.hom_setting&0x10)
+ WBUFW(buf,41)=status->luk/3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious]
+ else
+ WBUFW(buf,41)=status->cri/10;
+ WBUFW(buf,43)=status->def + status->vit ;
+ WBUFW(buf,45)=status->mdef;
+ WBUFW(buf,47)=status->flee;
+ WBUFW(buf,49)=(flag)?0:status->amotion;
+ if (status->max_hp > INT16_MAX) {
+ WBUFW(buf,51) = status->hp/(status->max_hp/100);
+ WBUFW(buf,53) = 100;
+ } else {
+ WBUFW(buf,51)=status->hp;
+ WBUFW(buf,53)=status->max_hp;
+ }
+ if (status->max_sp > INT16_MAX) {
+ WBUFW(buf,55) = status->sp/(status->max_sp/100);
+ WBUFW(buf,57) = 100;
+ } else {
+ WBUFW(buf,55)=status->sp;
+ WBUFW(buf,57)=status->max_sp;
+ }
+ WBUFL(buf,59)=hd->homunculus.exp;
+ if( ((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level) || ((m_class&HOM_S) && hd->homunculus.level >= battle_config.hom_S_max_level) )
+ WBUFL(buf,63)=0;
+ else
+ WBUFL(buf,63)=hd->exp_next;
+ WBUFW(buf,67)=hd->homunculus.skillpts;
+ WBUFW(buf,69)=status_get_range(&hd->bl);
+ clif_send(buf,packet_len(0x22e),&sd->bl,SELF);
+}
+
+
+/// Notification about a change in homunuculus' state (ZC_CHANGESTATE_MER).
+/// 0230 <type>.B <state>.B <id>.L <data>.L
+/// type:
+/// unused
+/// state:
+/// 0 = pre-init
+/// 1 = intimacy
+/// 2 = hunger
+/// 3 = accessory?
+/// ? = ignored
+void clif_send_homdata(struct map_session_data *sd, int state, int param)
+{ //[orn]
+ int fd = sd->fd;
+
+ if ( (state == SP_INTIMATE) && (param >= 910) && (sd->hd->homunculus.class_ == sd->hd->homunculusDB->evo_class) )
+ merc_hom_calc_skilltree(sd->hd, 0);
+
+ WFIFOHEAD(fd, packet_len(0x230));
+ WFIFOW(fd,0)=0x230;
+ WFIFOB(fd,2)=0;
+ WFIFOB(fd,3)=state;
+ WFIFOL(fd,4)=sd->hd->bl.id;
+ WFIFOL(fd,8)=param;
+ WFIFOSET(fd,packet_len(0x230));
+}
+
+
+int clif_homskillinfoblock(struct map_session_data *sd)
+{ //[orn]
+ struct homun_data *hd;
+ int fd = sd->fd;
+ int i,j,len=4,id;
+ WFIFOHEAD(fd, 4+37*MAX_HOMUNSKILL);
+
+ hd = sd->hd;
+ if ( !hd )
+ return 0 ;
+
+ WFIFOW(fd,0)=0x235;
+ for ( i = 0; i < MAX_HOMUNSKILL; i++){
+ if( (id = hd->homunculus.hskill[i].id) != 0 ){
+ j = id - HM_SKILLBASE;
+ WFIFOW(fd,len ) = id;
+ WFIFOW(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+4) = 0;
+ WFIFOW(fd,len+6) = hd->homunculus.hskill[j].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[j].lv);
+ WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[j].lv);
+ safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ WFIFOB(fd,len+36) = (hd->homunculus.hskill[j].lv < merc_skill_tree_get_max(id, hd->homunculus.class_))?1:0;
+ len+=37;
+ }
+ }
+ WFIFOW(fd,2)=len;
+ WFIFOSET(fd,len);
+
+ return 0;
+}
+
+void clif_homskillup(struct map_session_data *sd, uint16 skill_id)
+{ //[orn]
+ struct homun_data *hd;
+ int fd, idx;
+ nullpo_retv(sd);
+ idx = skill_id - HM_SKILLBASE;
+
+ fd=sd->fd;
+ hd=sd->hd;
+
+ WFIFOHEAD(fd, packet_len(0x239));
+ WFIFOW(fd,0) = 0x239;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = hd->homunculus.hskill[idx].lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_id,hd->homunculus.hskill[idx].lv);
+ WFIFOW(fd,8) = skill_get_range2(&hd->bl, skill_id,hd->homunculus.hskill[idx].lv);
+ WFIFOB(fd,10) = (hd->homunculus.hskill[idx].lv < skill_get_max(hd->homunculus.hskill[idx].id)) ? 1 : 0;
+ WFIFOSET(fd,packet_len(0x239));
+}
+
+int clif_hom_food(struct map_session_data *sd,int foodid,int fail) //[orn]
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x22f));
+ WFIFOW(fd,0)=0x22f;
+ WFIFOB(fd,2)=fail;
+ WFIFOW(fd,3)=foodid;
+ WFIFOSET(fd,packet_len(0x22f));
+
+ return 0;
+}
+
+
+/// Notifies the client, that it is walking (ZC_NOTIFY_PLAYERMOVE).
+/// 0087 <walk start time>.L <walk data>.6B
+void clif_walkok(struct map_session_data *sd)
+{
+ int fd=sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x87));
+ WFIFOW(fd,0)=0x87;
+ WFIFOL(fd,2)=gettick();
+ WFIFOPOS2(fd,6,sd->bl.x,sd->bl.y,sd->ud.to_x,sd->ud.to_y,8,8);
+ WFIFOSET(fd,packet_len(0x87));
+}
+
+
+static void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *ud)
+{
+ uint8 buf[128];
+ int len;
+
+ len = clif_set_unit_walking(bl,ud,buf);
+ clif_send(buf,len,bl,AREA_WOS);
+ if (disguised(bl))
+ clif_setdisguise(bl, buf, len);
+
+ if(vd->cloth_color)
+ clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
+
+ switch(bl->type)
+ {
+ case BL_PC:
+ {
+ TBL_PC *sd = ((TBL_PC*)bl);
+// clif_movepc(sd);
+ if(sd->state.size==SZ_BIG) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,AREA);
+ else if(sd->state.size==SZ_MEDIUM)
+ clif_specialeffect(&sd->bl,421,AREA);
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB *md = ((TBL_MOB*)bl);
+ if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,AREA);
+ else if(md->special_state.size==SZ_MEDIUM)
+ clif_specialeffect(&md->bl,421,AREA);
+ }
+ break;
+ case BL_PET:
+ if( vd->head_bottom )
+ {// needed to display pet equip properly
+ clif_pet_equip_area((TBL_PET*)bl);
+ }
+ break;
+ }
+}
+
+
+/// Notifies clients in an area, that an other visible object is walking (ZC_NOTIFY_PLAYERMOVE).
+/// 0086 <id>.L <walk data>.6B <walk start time>.L
+/// Note: unit must not be self
+void clif_move(struct unit_data *ud)
+{
+ unsigned char buf[16];
+ struct view_data* vd;
+ struct block_list* bl = ud->bl;
+
+ vd = status_get_viewdata(bl);
+ if (!vd || vd->class_ == INVISIBLE_CLASS)
+ return; //This performance check is needed to keep GM-hidden objects from being notified to bots.
+
+ /**
+ * Hide NPC from maya purple card.
+ **/
+ if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
+ return;
+
+ if (ud->state.speed_changed) {
+ // Since we don't know how to update the speed of other objects,
+ // use the old walk packet to update the data.
+ ud->state.speed_changed = 0;
+ clif_move2(bl, vd, ud);
+ return;
+ }
+
+ WBUFW(buf,0)=0x86;
+ WBUFL(buf,2)=bl->id;
+ WBUFPOS2(buf,6,bl->x,bl->y,ud->to_x,ud->to_y,8,8);
+ WBUFL(buf,12)=gettick();
+ clif_send(buf, packet_len(0x86), bl, AREA_WOS);
+ if (disguised(bl))
+ {
+ WBUFL(buf,2)=-bl->id;
+ clif_send(buf, packet_len(0x86), bl, SELF);
+ }
+}
+
+
+/*==========================================
+ * Delays the map_quit of a player after they are disconnected. [Skotlex]
+ *------------------------------------------*/
+static int clif_delayquit(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct map_session_data *sd = NULL;
+
+ //Remove player from map server
+ if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player.
+ map_quit(sd);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_quitsave(int fd,struct map_session_data *sd)
+{
+ if (!battle_config.prevent_logout ||
+ DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
+ map_quit(sd);
+ else if (sd->fd)
+ { //Disassociate session from player (session is deleted after this function was called)
+ //And set a timer to make him quit later.
+ session[sd->fd]->session_data = NULL;
+ sd->fd = 0;
+ add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 0);
+ }
+}
+
+/// Notifies the client of a position change to coordinates on given map (ZC_NPCACK_MAPMOVE).
+/// 0091 <map name>.16B <x>.W <y>.W
+void clif_changemap(struct map_session_data *sd, short map, int x, int y)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x91));
+ WFIFOW(fd,0) = 0x91;
+ mapindex_getmapname_ext(mapindex_id2name(map), (char*)WFIFOP(fd,2));
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOSET(fd,packet_len(0x91));
+}
+
+
+/// Notifies the client of a position change to coordinates on given map, which is on another map-server (ZC_NPCACK_SERVERMOVE).
+/// 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W
+void clif_changemapserver(struct map_session_data* sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x92));
+ WFIFOW(fd,0) = 0x92;
+ mapindex_getmapname_ext(mapindex_id2name(map_index), (char*)WFIFOP(fd,2));
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOL(fd,22) = htonl(ip);
+ WFIFOW(fd,26) = ntows(htons(port)); // [!] LE byte order here [!]
+ WFIFOSET(fd,packet_len(0x92));
+}
+
+
+void clif_blown(struct block_list *bl)
+{
+//Aegis packets says fixpos, but it's unsure whether slide works better or not.
+// clif_fixpos(bl);
+ clif_slide(bl, bl->x, bl->y);
+}
+
+
+/// Visually moves(slides) a character to x,y. If the target cell
+/// isn't walkable, the char doesn't move at all. If the char is
+/// sitting it will stand up (ZC_STOPMOVE).
+/// 0088 <id>.L <x>.W <y>.W
+void clif_fixpos(struct block_list *bl)
+{
+ unsigned char buf[10];
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x88;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = bl->x;
+ WBUFW(buf,8) = bl->y;
+ clif_send(buf, packet_len(0x88), bl, AREA);
+
+ if( disguised(bl) )
+ {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x88), bl, SELF);
+ }
+}
+
+
+/// Displays the buy/sell dialog of an NPC shop (ZC_SELECT_DEALTYPE).
+/// 00c4 <shop id>.L
+void clif_npcbuysell(struct map_session_data* sd, int id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xc4));
+ WFIFOW(fd,0)=0xc4;
+ WFIFOL(fd,2)=id;
+ WFIFOSET(fd,packet_len(0xc4));
+}
+
+
+/// Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST).
+/// 00c6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.W }*
+void clif_buylist(struct map_session_data *sd, struct npc_data *nd)
+{
+ int fd,i,c;
+
+ nullpo_retv(sd);
+ nullpo_retv(nd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, 4 + nd->u.shop.count * 11);
+ WFIFOW(fd,0) = 0xc6;
+
+ c = 0;
+ for( i = 0; i < nd->u.shop.count; i++ )
+ {
+ struct item_data* id = itemdb_exists(nd->u.shop.shop_item[i].nameid);
+ int val = nd->u.shop.shop_item[i].value;
+ if( id == NULL )
+ continue;
+ WFIFOL(fd, 4+c*11) = val;
+ WFIFOL(fd, 8+c*11) = pc_modifybuyvalue(sd,val);
+ WFIFOB(fd,12+c*11) = itemtype(id->type);
+ WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid;
+ c++;
+ }
+
+ WFIFOW(fd,2) = 4 + c*11;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Presents list of items, that can be sold to an NPC shop (ZC_PC_SELL_ITEMLIST).
+/// 00c7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }*
+void clif_selllist(struct map_session_data *sd)
+{
+ int fd,i,c=0,val;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);
+ WFIFOW(fd,0)=0xc7;
+ for( i = 0; i < MAX_INVENTORY; i++ )
+ {
+ if( sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] )
+ {
+ if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )
+ continue;
+
+ if( sd->status.inventory[i].expire_time )
+ continue; // Cannot Sell Rental Items
+
+ val=sd->inventory_data[i]->value_sell;
+ if( val < 0 )
+ continue;
+ WFIFOW(fd,4+c*10)=i+2;
+ WFIFOL(fd,6+c*10)=val;
+ WFIFOL(fd,10+c*10)=pc_modifysellvalue(sd,val);
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*10+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Displays an NPC dialog message (ZC_SAY_DIALOG).
+/// 00b4 <packet len>.W <npc id>.L <message>.?B
+/// Client behavior (dialog window):
+/// - disable mouse targeting
+/// - open the dialog window
+/// - set npcid of dialog window (0 by default)
+/// - if set to clear on next mes, clear contents
+/// - append this text
+void clif_scriptmes(struct map_session_data *sd, int npcid, const char *mes)
+{
+ int fd = sd->fd;
+ int slen = strlen(mes) + 9;
+
+ WFIFOHEAD(fd, slen);
+ WFIFOW(fd,0)=0xb4;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ memcpy((char*)WFIFOP(fd,8), mes, slen-8);
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Adds a 'next' button to an NPC dialog (ZC_WAIT_DIALOG).
+/// 00b5 <npc id>.L
+/// Client behavior (dialog window):
+/// - disable mouse targeting
+/// - open the dialog window
+/// - add 'next' button
+/// When 'next' is pressed:
+/// - 00B9 <npcid of dialog window>.L
+/// - set to clear on next mes
+/// - remove 'next' button
+void clif_scriptnext(struct map_session_data *sd,int npcid)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xb5));
+ WFIFOW(fd,0)=0xb5;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0xb5));
+}
+
+
+/// Adds a 'close' button to an NPC dialog (ZC_CLOSE_DIALOG).
+/// 00b6 <npc id>.L
+/// Client behavior:
+/// - if dialog window is open:
+/// - remove 'next' button
+/// - add 'close' button
+/// - else:
+/// - enable mouse targeting
+/// - close the dialog window
+/// - close the menu window
+/// When 'close' is pressed:
+/// - enable mouse targeting
+/// - close the dialog window
+/// - close the menu window
+/// - 0146 <npcid of dialog window>.L
+void clif_scriptclose(struct map_session_data *sd, int npcid)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xb6));
+ WFIFOW(fd,0)=0xb6;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0xb6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_sendfakenpc(struct map_session_data *sd, int npcid)
+{
+ unsigned char *buf;
+ int fd = sd->fd;
+ sd->state.using_fake_npc = 1;
+
+ WFIFOHEAD(fd, packet_len(0x78));
+ buf = WFIFOP(fd,0);
+ memset(WBUFP(buf,0), 0, packet_len(0x78));
+ WBUFW(buf,0)=0x78;
+#if PACKETVER >= 20071106
+ WBUFB(buf,2) = 0; // object type
+ buf = WFIFOP(fd,1);
+#endif
+ WBUFL(buf,2)=npcid;
+ WBUFW(buf,14)=111;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y,sd->ud.dir);
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WFIFOSET(fd, packet_len(0x78));
+}
+
+
+/// Displays an NPC dialog menu (ZC_MENU_LIST).
+/// 00b7 <packet len>.W <npc id>.L <menu items>.?B
+/// Client behavior:
+/// - disable mouse targeting
+/// - close the menu window
+/// - open the menu window
+/// - add options to the menu (separated in the text by ":")
+/// - set npcid of menu window
+/// - if dialog window is open:
+/// - remove 'next' button
+/// When 'ok' is pressed:
+/// - 00B8 <npcid of menu window>.L <selected option>.B
+/// - close the menu window
+/// When 'cancel' is pressed:
+/// - 00B8 <npcid of menu window>.L <-1>.B
+/// - enable mouse targeting
+/// - close a bunch of windows...
+/// WARNING: the 'cancel' button closes other windows besides the dialog window and the menu window.
+/// Which suggests their have intertwined behavior. (probably the mouse targeting)
+/// TODO investigate behavior of other windows [FlavioJS]
+void clif_scriptmenu(struct map_session_data* sd, int npcid, const char* mes)
+{
+ int fd = sd->fd;
+ int slen = strlen(mes) + 9;
+ struct block_list *bl = NULL;
+
+ if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
+ clif_sendfakenpc(sd, npcid);
+
+ WFIFOHEAD(fd, slen);
+ WFIFOW(fd,0)=0xb7;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ memcpy((char*)WFIFOP(fd,8), mes, slen-8);
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLG).
+/// 0142 <npc id>.L
+/// Client behavior (inputnum window):
+/// - if npcid exists in the client:
+/// - open the inputnum window
+/// - set npcid of inputnum window
+/// When 'ok' is pressed:
+/// - if inputnum window has text:
+/// - if npcid exists in the client:
+/// - 0143 <npcid of inputnum window>.L <atoi(text)>.L
+/// - close inputnum window
+void clif_scriptinput(struct map_session_data *sd, int npcid)
+{
+ int fd;
+ struct block_list *bl = NULL;
+
+ nullpo_retv(sd);
+
+ if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
+ clif_sendfakenpc(sd, npcid);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x142));
+ WFIFOW(fd,0)=0x142;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0x142));
+}
+
+
+/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLGSTR).
+/// 01d4 <npc id>.L
+/// Client behavior (inputstr window):
+/// - if npcid is 0 or npcid exists in the client:
+/// - open the inputstr window
+/// - set npcid of inputstr window
+/// When 'ok' is pressed:
+/// - if inputstr window has text and isn't an insult(manner.txt):
+/// - if npcid is 0 or npcid exists in the client:
+/// - 01d5 <packetlen>.W <npcid of inputstr window>.L <text>.?B
+/// - close inputstr window
+void clif_scriptinputstr(struct map_session_data *sd, int npcid)
+{
+ int fd;
+ struct block_list *bl = NULL;
+
+ nullpo_retv(sd);
+
+ if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
+ clif_sendfakenpc(sd, npcid);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x1d4));
+ WFIFOW(fd,0)=0x1d4;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0x1d4));
+}
+
+
+/// Marks a position on client's minimap (ZC_COMPASS).
+/// 0144 <npc id>.L <type>.L <x>.L <y>.L <id>.B <color>.L
+/// npc id:
+/// is ignored in the client
+/// type:
+/// 0 = display mark for 15 seconds
+/// 1 = display mark until dead or teleported
+/// 2 = remove mark
+/// color:
+/// 0x00RRGGBB
+void clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x144));
+ WFIFOW(fd,0)=0x144;
+ WFIFOL(fd,2)=npc_id;
+ WFIFOL(fd,6)=type;
+ WFIFOL(fd,10)=x;
+ WFIFOL(fd,14)=y;
+ WFIFOB(fd,18)=id;
+ WFIFOL(fd,19)=color;
+ WFIFOSET(fd,packet_len(0x144));
+}
+
+
+/// Displays an illustration image.
+/// 0145 <image name>.16B <type>.B (ZC_SHOW_IMAGE)
+/// 01b3 <image name>.64B <type>.B (ZC_SHOW_IMAGE2)
+/// type:
+/// 0 = bottom left corner
+/// 1 = bottom middle
+/// 2 = bottom right corner
+/// 3 = middle of screen, inside a movable window
+/// 4 = middle of screen, movable with a close button, chrome-less
+void clif_cutin(struct map_session_data* sd, const char* image, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x1b3));
+ WFIFOW(fd,0)=0x1b3;
+ strncpy((char*)WFIFOP(fd,2),image,64);
+ WFIFOB(fd,66)=type;
+ WFIFOSET(fd,packet_len(0x1b3));
+}
+
+
+/*==========================================
+ * Fills in card data from the given item and into the buffer. [Skotlex]
+ *------------------------------------------*/
+static void clif_addcards(unsigned char* buf, struct item* item)
+{
+ int i=0,j;
+ if( item == NULL ) { //Blank data
+ WBUFW(buf,0) = 0;
+ WBUFW(buf,2) = 0;
+ WBUFW(buf,4) = 0;
+ WBUFW(buf,6) = 0;
+ return;
+ }
+ if( item->card[0] == CARD0_PET ) { //pet eggs
+ WBUFW(buf,0) = 0;
+ WBUFW(buf,2) = 0;
+ WBUFW(buf,4) = 0;
+ WBUFW(buf,6) = item->card[3]; //Pet renamed flag.
+ return;
+ }
+ if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ) { //Forged/created items
+ WBUFW(buf,0) = item->card[0];
+ WBUFW(buf,2) = item->card[1];
+ WBUFW(buf,4) = item->card[2];
+ WBUFW(buf,6) = item->card[3];
+ return;
+ }
+ //Client only receives four cards.. so randomly send them a set of cards. [Skotlex]
+ if( MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4 )
+ i = rnd()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3;
+
+ //Normal items.
+ if( item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,0) = j;
+ else
+ WBUFW(buf,0) = item->card[i];
+
+ if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,2) = j;
+ else
+ WBUFW(buf,2) = item->card[i];
+
+ if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,4) = j;
+ else
+ WBUFW(buf,4) = item->card[i];
+
+ if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,6) = j;
+ else
+ WBUFW(buf,6) = item->card[i];
+}
+
+
+/// Notifies the client, about a received inventory item or the result of a pick-up request.
+/// 00a0 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B (ZC_ITEM_PICKUP_ACK)
+/// 029a <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L (ZC_ITEM_PICKUP_ACK2)
+/// 02d4 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK3)
+void clif_additem(struct map_session_data *sd, int n, int amount, int fail)
+{
+ int fd;
+#if PACKETVER < 20061218
+ const int cmd = 0xa0;
+#elif PACKETVER < 20071002
+ const int cmd = 0x29a;
+#else
+ const int cmd = 0x2d4;
+#endif
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ if( !session_isActive(fd) ) //Sasuke-
+ return;
+
+ WFIFOHEAD(fd,packet_len(cmd));
+ if( fail )
+ {
+ WFIFOW(fd,0)=cmd;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOW(fd,6)=0;
+ WFIFOB(fd,8)=0;
+ WFIFOB(fd,9)=0;
+ WFIFOB(fd,10)=0;
+ WFIFOW(fd,11)=0;
+ WFIFOW(fd,13)=0;
+ WFIFOW(fd,15)=0;
+ WFIFOW(fd,17)=0;
+ WFIFOW(fd,19)=0;
+ WFIFOB(fd,21)=0;
+ WFIFOB(fd,22)=fail;
+#if PACKETVER >= 20061218
+ WFIFOL(fd,23)=0;
+#endif
+#if PACKETVER >= 20071002
+ WFIFOW(fd,27)=0; // unknown
+#endif
+ }
+ else
+ {
+ if( n < 0 || n >= MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL )
+ return;
+
+ WFIFOW(fd,0)=cmd;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+ if (sd->inventory_data[n]->view_id > 0)
+ WFIFOW(fd,6)=sd->inventory_data[n]->view_id;
+ else
+ WFIFOW(fd,6)=sd->status.inventory[n].nameid;
+ WFIFOB(fd,8)=sd->status.inventory[n].identify;
+ WFIFOB(fd,9)=sd->status.inventory[n].attribute;
+ WFIFOB(fd,10)=sd->status.inventory[n].refine;
+ clif_addcards(WFIFOP(fd,11), &sd->status.inventory[n]);
+ WFIFOW(fd,19)=pc_equippoint(sd,n);
+ WFIFOB(fd,21)=itemtype(sd->inventory_data[n]->type);
+ WFIFOB(fd,22)=fail;
+#if PACKETVER >= 20061218
+ WFIFOL(fd,23)=sd->status.inventory[n].expire_time;
+#endif
+#if PACKETVER >= 20071002
+ WFIFOW(fd,27)=0; // unknown
+#endif
+ }
+
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Notifies the client, that an inventory item was deleted or dropped (ZC_ITEM_THROW_ACK).
+/// 00af <index>.W <amount>.W
+void clif_dropitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xaf));
+ WFIFOW(fd,0)=0xaf;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOSET(fd,packet_len(0xaf));
+}
+
+
+/// Notifies the client, that an inventory item was deleted (ZC_DELETE_ITEM_FROM_BODY).
+/// 07fa <delete type>.W <index>.W <amount>.W
+/// delete type:
+/// 0 = Normal
+/// 1 = Item used for a skill
+/// 2 = Refine failed
+/// 3 = Material changed
+/// 4 = Moved to storage
+/// 5 = Moved to cart
+/// 6 = Item sold
+/// 7 = Consumed by Four Spirit Analysis (SO_EL_ANALYSIS) skill
+void clif_delitem(struct map_session_data *sd,int n,int amount, short reason)
+{
+#if PACKETVER < 20091117
+ clif_dropitem(sd,n,amount);
+#else
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7fa));
+ WFIFOW(fd,0)=0x7fa;
+ WFIFOW(fd,2)=reason;
+ WFIFOW(fd,4)=n+2;
+ WFIFOW(fd,6)=amount;
+ WFIFOSET(fd,packet_len(0x7fa));
+#endif
+}
+
+
+// Simplifies inventory/cart/storage packets by handling the packet section relevant to items. [Skotlex]
+// Equip is >= 0 for equippable items (holds the equip-point, is 0 for pet
+// armor/egg) -1 for stackable items, -2 for stackable items where arrows must send in the equip-point.
+void clif_item_sub(unsigned char *buf, int n, struct item *i, struct item_data *id, int equip)
+{
+ if (id->view_id > 0)
+ WBUFW(buf,n)=id->view_id;
+ else
+ WBUFW(buf,n)=i->nameid;
+ WBUFB(buf,n+2)=itemtype(id->type);
+ WBUFB(buf,n+3)=i->identify;
+ if (equip >= 0) { //Equippable item
+ WBUFW(buf,n+4)=equip;
+ WBUFW(buf,n+6)=i->equip;
+ WBUFB(buf,n+8)=i->attribute;
+ WBUFB(buf,n+9)=i->refine;
+ } else { //Stackable item.
+ WBUFW(buf,n+4)=i->amount;
+ if (equip == -2 && id->equip == EQP_AMMO)
+ WBUFW(buf,n+6)=EQP_AMMO;
+ else
+ WBUFW(buf,n+6)=0;
+ }
+
+}
+void clif_favorite_item(struct map_session_data* sd, unsigned short index);
+//Unified inventory function which sends all of the inventory (requires two packets, one for equipable items and one for stackable ones. [Skotlex]
+void clif_inventorylist(struct map_session_data *sd)
+{
+ int i,n,ne,arrow=-1;
+ unsigned char *buf;
+ unsigned char *bufe;
+
+#if PACKETVER < 5
+ const int s = 10; //Entry size.
+#elif PACKETVER < 20080102
+ const int s = 18;
+#else
+ const int s = 22;
+#endif
+#if PACKETVER < 20071002
+ const int se = 20;
+#elif PACKETVER < 20100629
+ const int se = 26;
+#else
+ const int se = 28;
+#endif
+
+ buf = (unsigned char*)aMalloc(MAX_INVENTORY * s + 4);
+ bufe = (unsigned char*)aMalloc(MAX_INVENTORY * se + 4);
+
+ for( i = 0, n = 0, ne = 0; i < MAX_INVENTORY; i++ )
+ {
+ if( sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL )
+ continue;
+
+ if( !itemdb_isstackable2(sd->inventory_data[i]) )
+ { //Non-stackable (Equippable)
+ WBUFW(bufe,ne*se+4)=i+2;
+ clif_item_sub(bufe, ne*se+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i));
+ clif_addcards(WBUFP(bufe, ne*se+16), &sd->status.inventory[i]);
+#if PACKETVER >= 20071002
+ WBUFL(bufe,ne*se+24)=sd->status.inventory[i].expire_time;
+ WBUFW(bufe,ne*se+28)=0; //Unknown
+#endif
+#if PACKETVER >= 20100629
+ if (sd->inventory_data[i]->equip&EQP_VISIBLE)
+ WBUFW(bufe,ne*se+30)= sd->inventory_data[i]->look;
+ else
+ WBUFW(bufe,ne*se+30)=0;
+#endif
+ ne++;
+ }
+ else
+ { //Stackable.
+ WBUFW(buf,n*s+4)=i+2;
+ clif_item_sub(buf, n*s+6, &sd->status.inventory[i], sd->inventory_data[i], -2);
+ if( sd->inventory_data[i]->equip == EQP_AMMO && sd->status.inventory[i].equip )
+ arrow=i;
+#if PACKETVER >= 5
+ clif_addcards(WBUFP(buf, n*s+14), &sd->status.inventory[i]);
+#endif
+#if PACKETVER >= 20080102
+ WBUFL(buf,n*s+22)=sd->status.inventory[i].expire_time;
+#endif
+ n++;
+ }
+ }
+ if( n )
+ {
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa3;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0)=0x1ee;
+#else
+ WBUFW(buf,0)=0x2e8;
+#endif
+ WBUFW(buf,2)=4+n*s;
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ }
+ if( arrow >= 0 )
+ clif_arrowequip(sd,arrow);
+
+ if( ne )
+ {
+#if PACKETVER < 20071002
+ WBUFW(bufe,0)=0xa4;
+#else
+ WBUFW(bufe,0)=0x2d0;
+#endif
+ WBUFW(bufe,2)=4+ne*se;
+ clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
+ }
+#if PACKETVER >= 20111122
+ for( i = 0; i < MAX_INVENTORY; i++ ) {
+ if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL )
+ continue;
+
+ if ( sd->status.inventory[i].favorite )
+ clif_favorite_item(sd, i);
+ }
+#endif
+
+ if( buf ) aFree(buf);
+ if( bufe ) aFree(bufe);
+}
+
+//Required when items break/get-repaired. Only sends equippable item list.
+void clif_equiplist(struct map_session_data *sd)
+{
+ int i,n,fd = sd->fd;
+ unsigned char *buf;
+#if PACKETVER < 20071002
+ const int cmd = 20;
+#elif PACKETVER < 20100629
+ const int cmd = 26;
+#else
+ const int cmd = 28;
+#endif
+
+ WFIFOHEAD(fd, MAX_INVENTORY * cmd + 4);
+ buf = WFIFOP(fd,0);
+
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if (sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL)
+ continue;
+
+ if(itemdb_isstackable2(sd->inventory_data[i]))
+ continue;
+ //Equippable
+ WBUFW(buf,n*cmd+4)=i+2;
+ clif_item_sub(buf, n*cmd+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i));
+ clif_addcards(WBUFP(buf, n*cmd+16), &sd->status.inventory[i]);
+#if PACKETVER >= 20071002
+ WBUFL(buf,n*cmd+24)=sd->status.inventory[i].expire_time;
+ WBUFW(buf,n*cmd+28)=0; //Unknown
+#endif
+#if PACKETVER >= 20100629
+ if (sd->inventory_data[i]->equip&EQP_VISIBLE)
+ WBUFW(buf,n*cmd+30)= sd->inventory_data[i]->look;
+ else
+ WBUFW(buf,n*cmd+30)=0;
+#endif
+ n++;
+ }
+ if (n) {
+#if PACKETVER < 20071002
+ WBUFW(buf,0)=0xa4;
+#else
+ WBUFW(buf,0)=0x2d0;
+#endif
+ WBUFW(buf,2)=4+n*cmd;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+}
+
+void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length)
+{
+ struct item_data *id;
+ int i,n,ne;
+ unsigned char *buf;
+ unsigned char *bufe;
+#if PACKETVER < 5
+ const int s = 10; //Entry size.
+#elif PACKETVER < 20080102
+ const int s = 18;
+#else
+ const int s = 22;
+#endif
+#if PACKETVER < 20071002
+ const int cmd = 20;
+#elif PACKETVER < 20100629
+ const int cmd = 26;
+#else
+ const int cmd = 28;
+#endif
+
+ buf = (unsigned char*)aMalloc(items_length * s + 4);
+ bufe = (unsigned char*)aMalloc(items_length * cmd + 4);
+
+ for( i = 0, n = 0, ne = 0; i < items_length; i++ )
+ {
+ if( items[i].nameid <= 0 )
+ continue;
+ id = itemdb_search(items[i].nameid);
+ if( !itemdb_isstackable2(id) )
+ { //Equippable
+ WBUFW(bufe,ne*cmd+4)=i+1;
+ clif_item_sub(bufe, ne*cmd+6, &items[i], id, id->equip);
+ clif_addcards(WBUFP(bufe, ne*cmd+16), &items[i]);
+#if PACKETVER >= 20071002
+ WBUFL(bufe,ne*cmd+24)=items[i].expire_time;
+ WBUFW(bufe,ne*cmd+28)=0; //Unknown
+#endif
+ ne++;
+ }
+ else
+ { //Stackable
+ WBUFW(buf,n*s+4)=i+1;
+ clif_item_sub(buf, n*s+6, &items[i], id,-1);
+#if PACKETVER >= 5
+ clif_addcards(WBUFP(buf,n*s+14), &items[i]);
+#endif
+#if PACKETVER >= 20080102
+ WBUFL(buf,n*s+22)=items[i].expire_time;
+#endif
+ n++;
+ }
+ }
+ if( n )
+ {
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa5;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0)=0x1f0;
+#else
+ WBUFW(buf,0)=0x2ea;
+#endif
+ WBUFW(buf,2)=4+n*s;
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ }
+ if( ne )
+ {
+#if PACKETVER < 20071002
+ WBUFW(bufe,0)=0xa6;
+#else
+ WBUFW(bufe,0)=0x2d1;
+#endif
+ WBUFW(bufe,2)=4+ne*cmd;
+ clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
+ }
+
+ if( buf ) aFree(buf);
+ if( bufe ) aFree(bufe);
+}
+
+void clif_cartlist(struct map_session_data *sd)
+{
+ struct item_data *id;
+ int i,n,ne;
+ unsigned char *buf;
+ unsigned char *bufe;
+#if PACKETVER < 5
+ const int s = 10; //Entry size.
+#elif PACKETVER < 20080102
+ const int s = 18;
+#else
+ const int s = 22;
+#endif
+#if PACKETVER < 20071002
+ const int cmd = 20;
+#elif PACKETVER < 20100629
+ const int cmd = 26;
+#else
+ const int cmd = 28;
+#endif
+
+ buf = (unsigned char*)aMalloc(MAX_CART * s + 4);
+ bufe = (unsigned char*)aMalloc(MAX_CART * cmd + 4);
+
+ for( i = 0, n = 0, ne = 0; i < MAX_CART; i++ )
+ {
+ if( sd->status.cart[i].nameid <= 0 )
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if( !itemdb_isstackable2(id) )
+ { //Equippable
+ WBUFW(bufe,ne*cmd+4)=i+2;
+ clif_item_sub(bufe, ne*cmd+6, &sd->status.cart[i], id, id->equip);
+ clif_addcards(WBUFP(bufe, ne*cmd+16), &sd->status.cart[i]);
+#if PACKETVER >= 20071002
+ WBUFL(bufe,ne*cmd+24)=sd->status.cart[i].expire_time;
+ WBUFW(bufe,ne*cmd+28)=0; //Unknown
+#endif
+ ne++;
+ }
+ else
+ { //Stackable
+ WBUFW(buf,n*s+4)=i+2;
+ clif_item_sub(buf, n*s+6, &sd->status.cart[i], id,-1);
+#if PACKETVER >= 5
+ clif_addcards(WBUFP(buf,n*s+14), &sd->status.cart[i]);
+#endif
+#if PACKETVER >= 20080102
+ WBUFL(buf,n*s+22)=sd->status.cart[i].expire_time;
+#endif
+ n++;
+ }
+ }
+ if( n )
+ {
+#if PACKETVER < 5
+ WBUFW(buf,0)=0x123;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0)=0x1ef;
+#else
+ WBUFW(buf,0)=0x2e9;
+#endif
+ WBUFW(buf,2)=4+n*s;
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ }
+ if( ne )
+ {
+#if PACKETVER < 20071002
+ WBUFW(bufe,0)=0x122;
+#else
+ WBUFW(bufe,0)=0x2d2;
+#endif
+ WBUFW(bufe,2)=4+ne*cmd;
+ clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
+ }
+
+ if( buf ) aFree(buf);
+ if( bufe ) aFree(bufe);
+}
+
+
+/// Removes cart (ZC_CARTOFF).
+/// 012b
+/// Client behaviour:
+/// Closes the cart storage and removes all it's items from memory.
+/// The Num & Weight values of the cart are left untouched and the cart is NOT removed.
+void clif_clearcart(int fd)
+{
+ WFIFOHEAD(fd, packet_len(0x12b));
+ WFIFOW(fd,0) = 0x12b;
+ WFIFOSET(fd, packet_len(0x12b));
+
+}
+
+
+/// Guild XY locators (ZC_NOTIFY_POSITION_TO_GUILDM) [Valaris]
+/// 01eb <account id>.L <x>.W <y>.W
+void clif_guild_xy(struct map_session_data *sd)
+{
+ unsigned char buf[10];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x1eb;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=sd->bl.x;
+ WBUFW(buf,8)=sd->bl.y;
+ clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS);
+}
+
+/*==========================================
+ * Sends x/y dot to a single fd. [Skotlex]
+ *------------------------------------------*/
+void clif_guild_xy_single(int fd, struct map_session_data *sd)
+{
+ if( sd->bg_id )
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x1eb));
+ WFIFOW(fd,0)=0x1eb;
+ WFIFOL(fd,2)=sd->status.account_id;
+ WFIFOW(fd,6)=sd->bl.x;
+ WFIFOW(fd,8)=sd->bl.y;
+ WFIFOSET(fd,packet_len(0x1eb));
+}
+
+// Guild XY locators [Valaris]
+void clif_guild_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[10];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x1eb;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=-1;
+ WBUFW(buf,8)=-1;
+ clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS);
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int clif_hpmeter_sub(struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd, *tsd;
+#if PACKETVER < 20100126
+ const int cmd = 0x106;
+#else
+ const int cmd = 0x80e;
+#endif
+
+ sd = va_arg(ap, struct map_session_data *);
+ tsd = (TBL_PC *)bl;
+
+ nullpo_ret(sd);
+ nullpo_ret(tsd);
+
+ if( !tsd->fd || tsd == sd )
+ return 0;
+
+ if( !pc_has_permission(tsd, PC_PERM_VIEW_HPMETER) )
+ return 0;
+ WFIFOHEAD(tsd->fd,packet_len(cmd));
+ WFIFOW(tsd->fd,0) = cmd;
+ WFIFOL(tsd->fd,2) = sd->status.account_id;
+#if PACKETVER < 20100126
+ if( sd->battle_status.max_hp > INT16_MAX )
+ { //To correctly display the %hp bar. [Skotlex]
+ WFIFOW(tsd->fd,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
+ WFIFOW(tsd->fd,8) = 100;
+ } else {
+ WFIFOW(tsd->fd,6) = sd->battle_status.hp;
+ WFIFOW(tsd->fd,8) = sd->battle_status.max_hp;
+ }
+#else
+ WFIFOL(tsd->fd,6) = sd->battle_status.hp;
+ WFIFOL(tsd->fd,10) = sd->battle_status.max_hp;
+#endif
+ WFIFOSET(tsd->fd,packet_len(cmd));
+ return 0;
+}
+
+/*==========================================
+ * Server tells all players that are allowed to view HP bars
+ * and are nearby 'sd' that 'sd' hp bar was updated.
+ *------------------------------------------*/
+static int clif_hpmeter(struct map_session_data *sd)
+{
+ nullpo_ret(sd);
+ map_foreachinarea(clif_hpmeter_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd);
+ return 0;
+}
+
+/// Notifies client of a character parameter change.
+/// 00b0 <var id>.W <value>.L (ZC_PAR_CHANGE)
+/// 00b1 <var id>.W <value>.L (ZC_LONGPAR_CHANGE)
+/// 00be <status id>.W <value>.B (ZC_STATUS_CHANGE)
+/// 0121 <current count>.W <max count>.W <current weight>.L <max weight>.L (ZC_NOTIFY_CARTITEM_COUNTINFO)
+/// 013a <atk range>.W (ZC_ATTACK_RANGE)
+/// 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS)
+/// TODO: Extract individual packets.
+/// FIXME: Packet lengths from packet_len(cmd)
+void clif_updatestatus(struct map_session_data *sd,int type)
+{
+ int fd,len=8;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ if ( !session_isActive(fd) ) // Invalid pointer fix, by sasuke [Kevin]
+ return;
+
+ WFIFOHEAD(fd, 14);
+ WFIFOW(fd,0)=0xb0;
+ WFIFOW(fd,2)=type;
+ switch(type){
+ // 00b0
+ case SP_WEIGHT:
+ pc_updateweightstatus(sd);
+ WFIFOHEAD(fd,14);
+ WFIFOW(fd,0)=0xb0; //Need to re-set as pc_updateweightstatus can alter the buffer. [Skotlex]
+ WFIFOW(fd,2)=type;
+ WFIFOL(fd,4)=sd->weight;
+ break;
+ case SP_MAXWEIGHT:
+ WFIFOL(fd,4)=sd->max_weight;
+ break;
+ case SP_SPEED:
+ WFIFOL(fd,4)=sd->battle_status.speed;
+ break;
+ case SP_BASELEVEL:
+ WFIFOL(fd,4)=sd->status.base_level;
+ break;
+ case SP_JOBLEVEL:
+ WFIFOL(fd,4)=sd->status.job_level;
+ break;
+ case SP_KARMA: // Adding this back, I wonder if the client intercepts this - [Lance]
+ WFIFOL(fd,4)=sd->status.karma;
+ break;
+ case SP_MANNER:
+ WFIFOL(fd,4)=sd->status.manner;
+ break;
+ case SP_STATUSPOINT:
+ WFIFOL(fd,4)=sd->status.status_point;
+ break;
+ case SP_SKILLPOINT:
+ WFIFOL(fd,4)=sd->status.skill_point;
+ break;
+ case SP_HIT:
+ WFIFOL(fd,4)=sd->battle_status.hit;
+ break;
+ case SP_FLEE1:
+ WFIFOL(fd,4)=sd->battle_status.flee;
+ break;
+ case SP_FLEE2:
+ WFIFOL(fd,4)=sd->battle_status.flee2/10;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4)=sd->battle_status.max_hp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4)=sd->battle_status.max_sp;
+ break;
+ case SP_HP:
+ WFIFOL(fd,4)=sd->battle_status.hp;
+ // TODO: Won't these overwrite the current packet?
+ clif_hpmeter(sd);
+ if( !battle_config.party_hp_mode && sd->status.party_id )
+ clif_party_hp(sd);
+ if( sd->bg_id )
+ clif_bg_hp(sd);
+ break;
+ case SP_SP:
+ WFIFOL(fd,4)=sd->battle_status.sp;
+ break;
+ case SP_ASPD:
+ WFIFOL(fd,4)=sd->battle_status.amotion;
+ break;
+ case SP_ATK1:
+ WFIFOL(fd,4)=pc_leftside_atk(sd);
+ break;
+ case SP_DEF1:
+ WFIFOL(fd,4)=pc_leftside_def(sd);
+ break;
+ case SP_MDEF1:
+ WFIFOL(fd,4)=pc_leftside_mdef(sd);
+ break;
+ case SP_ATK2:
+ WFIFOL(fd,4)=pc_rightside_atk(sd);
+ break;
+ case SP_DEF2:
+ WFIFOL(fd,4)=pc_rightside_def(sd);
+ break;
+ case SP_MDEF2: {
+ //negative check (in case you have something like Berserk active)
+ int mdef2 = pc_rightside_mdef(sd);
+
+ WFIFOL(fd,4)=
+#ifndef RENEWAL
+ ( mdef2 < 0 ) ? 0 :
+#endif
+ mdef2;
+
+ }
+ break;
+ case SP_CRITICAL:
+ WFIFOL(fd,4)=sd->battle_status.cri/10;
+ break;
+ case SP_MATK1:
+ WFIFOL(fd,4)=pc_rightside_matk(sd);
+ break;
+ case SP_MATK2:
+ WFIFOL(fd,4)=pc_leftside_matk(sd);
+ break;
+
+
+ case SP_ZENY:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.zeny;
+ break;
+ case SP_BASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.base_exp;
+ break;
+ case SP_JOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.job_exp;
+ break;
+ case SP_NEXTBASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextbaseexp(sd);
+ break;
+ case SP_NEXTJOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextjobexp(sd);
+ break;
+
+ /**
+ * 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:
+ case SP_UINT:
+ case SP_UDEX:
+ case SP_ULUK:
+ WFIFOW(fd,0)=0xbe;
+ WFIFOB(fd,4)=pc_need_status_point(sd,type-SP_USTR+SP_STR,1);
+ len=5;
+ break;
+
+ /**
+ * 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;
+
+ case SP_STR:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.str;
+ WFIFOL(fd,10)=sd->battle_status.str - sd->status.str;
+ len=14;
+ break;
+ case SP_AGI:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.agi;
+ WFIFOL(fd,10)=sd->battle_status.agi - sd->status.agi;
+ len=14;
+ break;
+ case SP_VIT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.vit;
+ WFIFOL(fd,10)=sd->battle_status.vit - sd->status.vit;
+ len=14;
+ break;
+ case SP_INT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.int_;
+ WFIFOL(fd,10)=sd->battle_status.int_ - sd->status.int_;
+ len=14;
+ break;
+ case SP_DEX:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.dex;
+ WFIFOL(fd,10)=sd->battle_status.dex - sd->status.dex;
+ len=14;
+ break;
+ case SP_LUK:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.luk;
+ WFIFOL(fd,10)=sd->battle_status.luk - sd->status.luk;
+ len=14;
+ break;
+
+ case SP_CARTINFO:
+ WFIFOW(fd,0)=0x121;
+ WFIFOW(fd,2)=sd->cart_num;
+ WFIFOW(fd,4)=MAX_CART;
+ WFIFOL(fd,6)=sd->cart_weight;
+ WFIFOL(fd,10)=sd->cart_weight_max;
+ len=14;
+ break;
+
+ default:
+ ShowError("clif_updatestatus : unrecognized type %d\n",type);
+ return;
+ }
+ WFIFOSET(fd,len);
+}
+
+
+/// Notifies client of a parameter change of an another player (ZC_PAR_CHANGE_USER).
+/// 01ab <account id>.L <var id>.W <value>.L
+void clif_changestatus(struct map_session_data* sd,int type,int val)
+{
+ unsigned char buf[12];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x1ab;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=type;
+
+ switch(type)
+ {
+ case SP_MANNER:
+ WBUFL(buf,8)=val;
+ break;
+ default:
+ ShowError("clif_changestatus : unrecognized type %d.\n",type);
+ return;
+ }
+
+ clif_send(buf,packet_len(0x1ab),&sd->bl,AREA_WOS);
+}
+
+
+/// Updates sprite/style properties of an object.
+/// 00c3 <id>.L <type>.B <value>.B (ZC_SPRITE_CHANGE)
+/// 01d7 <id>.L <type>.B <value>.L (ZC_SPRITE_CHANGE2)
+void clif_changelook(struct block_list *bl,int type,int val)
+{
+ unsigned char buf[16];
+ struct map_session_data* sd = NULL;
+ struct status_change* sc;
+ struct view_data* vd;
+ enum send_target target = AREA;
+ nullpo_retv(bl);
+
+ sd = BL_CAST(BL_PC, bl);
+ sc = status_get_sc(bl);
+ vd = status_get_viewdata(bl);
+ //nullpo_ret(vd);
+ if( vd ) //temp hack to let Warp Portal change appearance
+ switch(type)
+ {
+ case LOOK_WEAPON:
+ if (sd)
+ {
+ clif_get_weapon_view(sd, &vd->weapon, &vd->shield);
+ val = vd->weapon;
+ }
+ else vd->weapon = val;
+ break;
+ case LOOK_SHIELD:
+ if (sd)
+ {
+ clif_get_weapon_view(sd, &vd->weapon, &vd->shield);
+ val = vd->shield;
+ }
+ else vd->shield = val;
+ break;
+ case LOOK_BASE:
+ vd->class_ = val;
+ if (vd->class_ == JOB_WEDDING || vd->class_ == JOB_XMAS || vd->class_ == JOB_SUMMER)
+ vd->weapon = vd->shield = 0;
+ if (vd->cloth_color && (
+ (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) ||
+ (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) ||
+ (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette)
+ ))
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,0);
+ break;
+ case LOOK_HAIR:
+ vd->hair_style = val;
+ break;
+ case LOOK_HEAD_BOTTOM:
+ vd->head_bottom = val;
+ break;
+ case LOOK_HEAD_TOP:
+ vd->head_top = val;
+ break;
+ case LOOK_HEAD_MID:
+ vd->head_mid = val;
+ break;
+ case LOOK_HAIR_COLOR:
+ vd->hair_color = val;
+ break;
+ case LOOK_CLOTHES_COLOR:
+ if (val && (
+ (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) ||
+ (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) ||
+ (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette)
+ ))
+ val = 0;
+ vd->cloth_color = val;
+ break;
+ case LOOK_SHOES:
+#if PACKETVER > 3
+ if (sd) {
+ int n;
+ if((n = sd->equip_index[2]) >= 0 && sd->inventory_data[n]) {
+ if(sd->inventory_data[n]->view_id > 0)
+ val = sd->inventory_data[n]->view_id;
+ else
+ val = sd->status.inventory[n].nameid;
+ } else
+ val = 0;
+ }
+#endif
+ //Shoes? No packet uses this....
+ break;
+ case LOOK_BODY:
+ case LOOK_FLOOR:
+ // unknown purpose
+ break;
+ case LOOK_ROBE:
+#if PACKETVER < 20110111
+ return;
+#else
+ vd->robe = val;
+#endif
+ break;
+ }
+
+ // prevent leaking the presence of GM-hidden objects
+ if( sc && sc->option&OPTION_INVISIBLE )
+ target = SELF;
+
+#if PACKETVER < 4
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len(0xc3),bl,target);
+#else
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ if(type == LOOK_WEAPON || type == LOOK_SHIELD) {
+ WBUFB(buf,6)=LOOK_WEAPON;
+ WBUFW(buf,7)=vd->weapon;
+ WBUFW(buf,9)=vd->shield;
+ } else {
+ WBUFB(buf,6)=type;
+ WBUFL(buf,7)=val;
+ }
+ clif_send(buf,packet_len(0x1d7),bl,target);
+#endif
+}
+
+//Sends a change-base-look packet required for traps as they are triggered.
+void clif_changetraplook(struct block_list *bl,int val)
+{
+ unsigned char buf[32];
+#if PACKETVER < 4
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=LOOK_BASE;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len(0xc3),bl,AREA);
+#else
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=LOOK_BASE;
+ WBUFW(buf,7)=val;
+ WBUFW(buf,9)=0;
+ clif_send(buf,packet_len(0x1d7),bl,AREA);
+#endif
+}
+
+//For the stupid cloth-dye bug. Resends the given view data to the area specified by bl.
+void clif_refreshlook(struct block_list *bl,int id,int type,int val,enum send_target target)
+{
+ unsigned char buf[32];
+#if PACKETVER < 4
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len(0xc3),bl,target);
+#else
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=id;
+ WBUFB(buf,6)=type;
+ WBUFW(buf,7)=val;
+ WBUFW(buf,9)=0;
+ clif_send(buf,packet_len(0x1d7),bl,target);
+#endif
+}
+
+
+/// Character status (ZC_STATUS).
+/// 00bd <stpoint>.W <str>.B <need str>.B <agi>.B <need agi>.B <vit>.B <need vit>.B
+/// <int>.B <need int>.B <dex>.B <need dex>.B <luk>.B <need luk>.B <atk>.W <atk2>.W
+/// <matk min>.W <matk max>.W <def>.W <def2>.W <mdef>.W <mdef2>.W <hit>.W
+/// <flee>.W <flee2>.W <crit>.W <aspd>.W <aspd2>.W
+void clif_initialstatus(struct map_session_data *sd)
+{
+ int fd, mdef2;
+ unsigned char *buf;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xbd));
+ buf=WFIFOP(fd,0);
+
+ WBUFW(buf,0)=0xbd;
+ WBUFW(buf,2)=min(sd->status.status_point, INT16_MAX);
+ WBUFB(buf,4)=min(sd->status.str, UINT8_MAX);
+ WBUFB(buf,5)=pc_need_status_point(sd,SP_STR,1);
+ WBUFB(buf,6)=min(sd->status.agi, UINT8_MAX);
+ WBUFB(buf,7)=pc_need_status_point(sd,SP_AGI,1);
+ WBUFB(buf,8)=min(sd->status.vit, UINT8_MAX);
+ WBUFB(buf,9)=pc_need_status_point(sd,SP_VIT,1);
+ WBUFB(buf,10)=min(sd->status.int_, UINT8_MAX);
+ WBUFB(buf,11)=pc_need_status_point(sd,SP_INT,1);
+ WBUFB(buf,12)=min(sd->status.dex, UINT8_MAX);
+ WBUFB(buf,13)=pc_need_status_point(sd,SP_DEX,1);
+ WBUFB(buf,14)=min(sd->status.luk, UINT8_MAX);
+ WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK,1);
+
+ WBUFW(buf,16) = pc_leftside_atk(sd);
+ WBUFW(buf,18) = pc_rightside_atk(sd);
+ WBUFW(buf,20) = pc_rightside_matk(sd);
+ WBUFW(buf,22) = pc_leftside_matk(sd);
+ WBUFW(buf,24) = pc_leftside_def(sd);
+ WBUFW(buf,26) = pc_rightside_def(sd);
+ WBUFW(buf,28) = pc_leftside_mdef(sd);
+ mdef2 = pc_rightside_mdef(sd);
+ WBUFW(buf,30) =
+#ifndef RENEWAL
+ ( mdef2 < 0 ) ? 0 : //Negative check for Frenzy'ed characters.
+#endif
+ mdef2;
+ WBUFW(buf,32) = sd->battle_status.hit;
+ WBUFW(buf,34) = sd->battle_status.flee;
+ WBUFW(buf,36) = sd->battle_status.flee2/10;
+ WBUFW(buf,38) = sd->battle_status.cri/10;
+ WBUFW(buf,40) = sd->battle_status.amotion; // aspd
+ WBUFW(buf,42) = 0; // always 0 (plusASPD)
+
+ WFIFOSET(fd,packet_len(0xbd));
+
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ clif_updatestatus(sd,SP_ASPD);
+}
+
+
+/// Marks an ammunition item in inventory as equipped (ZC_EQUIP_ARROW).
+/// 013c <index>.W
+void clif_arrowequip(struct map_session_data *sd,int val)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ pc_stop_attack(sd); // [Valaris]
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x013c));
+ WFIFOW(fd,0)=0x013c;
+ WFIFOW(fd,2)=val+2; //Item ID of the arrow
+ WFIFOSET(fd,packet_len(0x013c));
+}
+
+
+/// Ammunition action message (ZC_ACTION_FAILURE).
+/// 013b <type>.W
+/// type:
+/// 0 = MsgStringTable[242]="Please equip the proper ammunition first."
+/// 1 = MsgStringTable[243]="You can't Attack or use Skills because your Weight Limit has been exceeded."
+/// 2 = MsgStringTable[244]="You can't use Skills because Weight Limit has been exceeded."
+/// 3 = assassin, baby_assassin, assassin_cross => MsgStringTable[1040]="You have equipped throwing daggers."
+/// gunslinger => MsgStringTable[1175]="Bullets have been equipped."
+/// NOT ninja => MsgStringTable[245]="Ammunition has been equipped."
+void clif_arrow_fail(struct map_session_data *sd,int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x013b));
+ WFIFOW(fd,0)=0x013b;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x013b));
+}
+
+
+/// Presents a list of items, that can be processed by Arrow Crafting (ZC_MAKINGARROW_LIST).
+/// 01ad <packet len>.W { <name id>.W }*
+void clif_arrow_create_list(struct map_session_data *sd)
+{
+ int i, c, j;
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_ARROW_DB*2+4);
+ WFIFOW(fd,0) = 0x1ad;
+
+ for (i = 0, c = 0; i < MAX_SKILL_ARROW_DB; i++) {
+ if (skill_arrow_db[i].nameid > 0 &&
+ (j = pc_search_inventory(sd, skill_arrow_db[i].nameid)) >= 0 &&
+ !sd->status.inventory[j].equip && sd->status.inventory[j].identify)
+ {
+ if ((j = itemdb_viewid(skill_arrow_db[i].nameid)) > 0)
+ WFIFOW(fd,c*2+4) = j;
+ else
+ WFIFOW(fd,c*2+4) = skill_arrow_db[i].nameid;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = c*2+4;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if (c > 0) {
+ sd->menuskill_id = AC_MAKINGARROW;
+ sd->menuskill_val = c;
+ }
+}
+
+
+/// Notifies the client, about the result of an status change request (ZC_STATUS_CHANGE_ACK).
+/// 00bc <status id>.W <result>.B <value>.B
+/// status id:
+/// SP_STR ~ SP_LUK
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_statusupack(struct map_session_data *sd,int type,int ok,int val)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xbc));
+ WFIFOW(fd,0)=0xbc;
+ WFIFOW(fd,2)=type;
+ WFIFOB(fd,4)=ok;
+ WFIFOB(fd,5)=cap_value(val,0,UINT8_MAX);
+ WFIFOSET(fd,packet_len(0xbc));
+}
+
+
+/// Notifies the client about the result of a request to equip an item (ZC_REQ_WEAR_EQUIP_ACK).
+/// 00aa <index>.W <equip location>.W <result>.B
+/// 00aa <index>.W <equip location>.W <view id>.W <result>.B (PACKETVER >= 20100629)
+/// result:
+/// 0 = failure
+/// 1 = success
+/// 2 = failure due to low level
+void clif_equipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xaa));
+ WFIFOW(fd,0)=0xaa;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+#if PACKETVER < 20100629
+ WFIFOB(fd,6)=ok;
+#else
+ if (ok && sd->inventory_data[n]->equip&EQP_VISIBLE)
+ WFIFOW(fd,6)=sd->inventory_data[n]->look;
+ else
+ WFIFOW(fd,6)=0;
+ WFIFOB(fd,8)=ok;
+#endif
+ WFIFOSET(fd,packet_len(0xaa));
+}
+
+
+/// Notifies the client about the result of a request to take off an item (ZC_REQ_TAKEOFF_EQUIP_ACK).
+/// 00ac <index>.W <equip location>.W <result>.B
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xac));
+ WFIFOW(fd,0)=0xac;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len(0xac));
+}
+
+
+/// Notifies clients in the area about an special/visual effect (ZC_NOTIFY_EFFECT).
+/// 019b <id>.L <effect id>.L
+/// effect id:
+/// 0 = base level up
+/// 1 = job level up
+/// 2 = refine failure
+/// 3 = refine success
+/// 4 = game over
+/// 5 = pharmacy success
+/// 6 = pharmacy failure
+/// 7 = base level up (super novice)
+/// 8 = job level up (super novice)
+/// 9 = base level up (taekwon)
+void clif_misceffect(struct block_list* bl,int type)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x19b;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf,packet_len(0x19b),bl,AREA);
+}
+
+
+/// Notifies clients in the area of a state change.
+/// 0119 <id>.L <body state>.W <health state>.W <effect state>.W <pk mode>.B (ZC_STATE_CHANGE)
+/// 0229 <id>.L <body state>.W <health state>.W <effect state>.L <pk mode>.B (ZC_STATE_CHANGE3)
+void clif_changeoption(struct block_list* bl)
+{
+ unsigned char buf[32];
+ struct status_change *sc;
+ struct map_session_data* sd;
+
+ nullpo_retv(bl);
+ sc = status_get_sc(bl);
+ if (!sc) return; //How can an option change if there's no sc?
+ sd = BL_CAST(BL_PC, bl);
+
+#if PACKETVER >= 7
+ WBUFW(buf,0) = 0x229;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = sc->opt1;
+ WBUFW(buf,8) = sc->opt2;
+ WBUFL(buf,10) = sc->option;
+ WBUFB(buf,14) = (sd)? sd->status.karma : 0;
+ if(disguised(bl)) {
+ clif_send(buf,packet_len(0x229),bl,AREA_WOS);
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf,packet_len(0x229),bl,SELF);
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,10) = OPTION_INVISIBLE;
+ clif_send(buf,packet_len(0x229),bl,SELF);
+ } else
+ clif_send(buf,packet_len(0x229),bl,AREA);
+#else
+ WBUFW(buf,0) = 0x119;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = sc->opt1;
+ WBUFW(buf,8) = sc->opt2;
+ WBUFW(buf,10) = sc->option;
+ WBUFB(buf,12) = (sd)? sd->status.karma : 0;
+ if(disguised(bl)) {
+ clif_send(buf,packet_len(0x119),bl,AREA_WOS);
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf,packet_len(0x119),bl,SELF);
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,10) = OPTION_INVISIBLE;
+ clif_send(buf,packet_len(0x119),bl,SELF);
+ } else
+ clif_send(buf,packet_len(0x119),bl,AREA);
+#endif
+}
+
+
+/// Displays status change effects on NPCs/monsters (ZC_NPC_SHOWEFST_UPDATE).
+/// 028a <id>.L <effect state>.L <level>.L <showEFST>.L
+void clif_changeoption2(struct block_list* bl)
+{
+ unsigned char buf[20];
+ struct status_change *sc;
+
+ sc = status_get_sc(bl);
+ if (!sc) return; //How can an option change if there's no sc?
+
+ WBUFW(buf,0) = 0x28a;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = sc->option;
+ WBUFL(buf,10) = clif_setlevel(bl);
+ WBUFL(buf,14) = sc->opt3;
+ if(disguised(bl)) {
+ clif_send(buf,packet_len(0x28a),bl,AREA_WOS);
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf,packet_len(0x28a),bl,SELF);
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = OPTION_INVISIBLE;
+ clif_send(buf,packet_len(0x28a),bl,SELF);
+ } else
+ clif_send(buf,packet_len(0x28a),bl,AREA);
+}
+
+
+/// Notifies the client about the result of an item use request.
+/// 00a8 <index>.W <amount>.W <result>.B (ZC_USE_ITEM_ACK)
+/// 01c8 <index>.W <name id>.W <id>.L <amount>.W <result>.B (ZC_USE_ITEM_ACK2)
+void clif_useitemack(struct map_session_data *sd,int index,int amount,bool ok)
+{
+ nullpo_retv(sd);
+
+ if(!ok) {
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xa8));
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len(0xa8));
+ }
+ else {
+#if PACKETVER < 3
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xa8));
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len(0xa8));
+#else
+ unsigned char buf[32];
+
+ WBUFW(buf,0)=0x1c8;
+ WBUFW(buf,2)=index+2;
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,4)=sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,4)=sd->status.inventory[index].nameid;
+ WBUFL(buf,6)=sd->bl.id;
+ WBUFW(buf,10)=amount;
+ WBUFB(buf,12)=ok;
+ clif_send(buf,packet_len(0x1c8),&sd->bl,AREA);
+#endif
+ }
+}
+
+
+/// Inform client whether chatroom creation was successful or not (ZC_ACK_CREATE_CHATROOM).
+/// 00d6 <flag>.B
+/// flag:
+/// 0 = Room has been successfully created (opens chat room)
+/// 1 = Room limit exceeded
+/// 2 = Same room already exists
+void clif_createchat(struct map_session_data* sd, int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xd6));
+ WFIFOW(fd,0) = 0xd6;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0xd6));
+}
+
+
+/// Display a chat above the owner (ZC_ROOM_NEWENTRY).
+/// 00d7 <packet len>.W <owner id>.L <char id>.L <limit>.W <users>.W <type>.B <title>.?B
+/// type:
+/// 0 = private (password protected)
+/// 1 = public
+/// 2 = arena (npc waiting room)
+/// 3 = PK zone (non-clickable)
+void clif_dispchat(struct chat_data* cd, int fd)
+{
+ unsigned char buf[128];
+ uint8 type;
+
+ if( cd == NULL || cd->owner == NULL )
+ return;
+
+ type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0
+ : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3
+ : 1;
+
+ WBUFW(buf, 0) = 0xd7;
+ WBUFW(buf, 2) = 17 + strlen(cd->title);
+ WBUFL(buf, 4) = cd->owner->id;
+ WBUFL(buf, 8) = cd->bl.id;
+ WBUFW(buf,12) = cd->limit;
+ WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users;
+ WBUFB(buf,16) = type;
+ memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated
+
+ if( fd ) {
+ WFIFOHEAD(fd,WBUFW(buf,2));
+ memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2));
+ WFIFOSET(fd,WBUFW(buf,2));
+ } else {
+ clif_send(buf,WBUFW(buf,2),cd->owner,AREA_WOSC);
+ }
+}
+
+
+/// Chatroom properties adjustment (ZC_CHANGE_CHATROOM).
+/// 00df <packet len>.W <owner id>.L <chat id>.L <limit>.W <users>.W <type>.B <title>.?B
+/// type:
+/// 0 = private (password protected)
+/// 1 = public
+/// 2 = arena (npc waiting room)
+/// 3 = PK zone (non-clickable)
+void clif_changechatstatus(struct chat_data* cd)
+{
+ unsigned char buf[128];
+ uint8 type;
+
+ if( cd == NULL || cd->usersd[0] == NULL )
+ return;
+
+ type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0
+ : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3
+ : 1;
+
+ WBUFW(buf, 0) = 0xdf;
+ WBUFW(buf, 2) = 17 + strlen(cd->title);
+ WBUFL(buf, 4) = cd->owner->id;
+ WBUFL(buf, 8) = cd->bl.id;
+ WBUFW(buf,12) = cd->limit;
+ WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users;
+ WBUFB(buf,16) = type;
+ memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated
+
+ clif_send(buf,WBUFW(buf,2),cd->owner,CHAT);
+}
+
+
+/// Removes the chatroom (ZC_DESTROY_ROOM).
+/// 00d8 <chat id>.L
+void clif_clearchat(struct chat_data *cd,int fd)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(cd);
+
+ WBUFW(buf,0) = 0xd8;
+ WBUFL(buf,2) = cd->bl.id;
+ if( fd ) {
+ WFIFOHEAD(fd,packet_len(0xd8));
+ memcpy(WFIFOP(fd,0),buf,packet_len(0xd8));
+ WFIFOSET(fd,packet_len(0xd8));
+ } else {
+ clif_send(buf,packet_len(0xd8),cd->owner,AREA_WOSC);
+ }
+}
+
+
+/// Displays messages regarding join chat failures (ZC_REFUSE_ENTER_ROOM).
+/// 00da <result>.B
+/// result:
+/// 0 = room full
+/// 1 = wrong password
+/// 2 = kicked
+/// 3 = success (no message)
+/// 4 = no enough zeny
+/// 5 = too low level
+/// 6 = too high level
+/// 7 = unsuitable job class
+void clif_joinchatfail(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xda));
+ WFIFOW(fd,0) = 0xda;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0xda));
+}
+
+
+/// Notifies the client about entering a chatroom (ZC_ENTER_ROOM).
+/// 00db <packet len>.W <chat id>.L { <role>.L <name>.24B }*
+/// role:
+/// 0 = owner (menu)
+/// 1 = normal
+void clif_joinchatok(struct map_session_data *sd,struct chat_data* cd)
+{
+ int fd;
+ int i,t;
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ fd = sd->fd;
+ if (!session_isActive(fd))
+ return;
+ t = (int)(cd->owner->type == BL_NPC);
+ WFIFOHEAD(fd, 8 + (28*(cd->users+t)));
+ WFIFOW(fd, 0) = 0xdb;
+ WFIFOW(fd, 2) = 8 + (28*(cd->users+t));
+ WFIFOL(fd, 4) = cd->bl.id;
+
+ if(cd->owner->type == BL_NPC){
+ WFIFOL(fd, 30) = 1;
+ WFIFOL(fd, 8) = 0;
+ memcpy(WFIFOP(fd, 12), ((struct npc_data *)cd->owner)->name, NAME_LENGTH);
+ for (i = 0; i < cd->users; i++) {
+ WFIFOL(fd, 8+(i+1)*28) = 1;
+ memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
+ }
+ } else
+ for (i = 0; i < cd->users; i++) {
+ WFIFOL(fd, 8+i*28) = (i != 0 || cd->owner->type == BL_NPC);
+ memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
+ }
+ WFIFOSET(fd, WFIFOW(fd, 2));
+}
+
+
+/// Notifies clients in a chat about a new member (ZC_MEMBER_NEWENTRY).
+/// 00dc <users>.W <name>.24B
+void clif_addchat(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ WBUFW(buf, 0) = 0xdc;
+ WBUFW(buf, 2) = cd->users;
+ memcpy(WBUFP(buf, 4),sd->status.name,NAME_LENGTH);
+ clif_send(buf,packet_len(0xdc),&sd->bl,CHAT_WOS);
+}
+
+
+/// Announce the new owner (ZC_ROLE_CHANGE).
+/// 00e1 <role>.L <nick>.24B
+/// role:
+/// 0 = owner (menu)
+/// 1 = normal
+void clif_changechatowner(struct chat_data* cd, struct map_session_data* sd)
+{
+ unsigned char buf[64];
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ WBUFW(buf, 0) = 0xe1;
+ WBUFL(buf, 2) = 1;
+ memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,NAME_LENGTH);
+
+ WBUFW(buf,30) = 0xe1;
+ WBUFL(buf,32) = 0;
+ memcpy(WBUFP(buf,36),sd->status.name,NAME_LENGTH);
+
+ clif_send(buf,packet_len(0xe1)*2,&sd->bl,CHAT);
+}
+
+
+/// Notify about user leaving the chatroom (ZC_MEMBER_EXIT).
+/// 00dd <users>.W <nick>.24B <flag>.B
+/// flag:
+/// 0 = left
+/// 1 = kicked
+void clif_leavechat(struct chat_data* cd, struct map_session_data* sd, bool flag)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ WBUFW(buf, 0) = 0xdd;
+ WBUFW(buf, 2) = cd->users-1;
+ memcpy(WBUFP(buf,4),sd->status.name,NAME_LENGTH);
+ WBUFB(buf,28) = flag;
+
+ clif_send(buf,packet_len(0xdd),&sd->bl,CHAT);
+}
+
+
+/// Opens a trade request window from char 'name'.
+/// 00e5 <nick>.24B (ZC_REQ_EXCHANGE_ITEM)
+/// 01f4 <nick>.24B <charid>.L <baselvl>.W (ZC_REQ_EXCHANGE_ITEM2)
+void clif_traderequest(struct map_session_data* sd, const char* name)
+{
+ int fd = sd->fd;
+
+#if PACKETVER < 6
+ WFIFOHEAD(fd,packet_len(0xe5));
+ WFIFOW(fd,0) = 0xe5;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0xe5));
+#else
+ struct map_session_data* tsd = map_id2sd(sd->trade_partner);
+ if( !tsd ) return;
+
+ WFIFOHEAD(fd,packet_len(0x1f4));
+ WFIFOW(fd,0) = 0x1f4;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOL(fd,26) = tsd->status.char_id;
+ WFIFOW(fd,30) = tsd->status.base_level;
+ WFIFOSET(fd,packet_len(0x1f4));
+#endif
+}
+
+
+/// Reply to a trade-request.
+/// 00e7 <result>.B (ZC_ACK_EXCHANGE_ITEM)
+/// 01f5 <result>.B <charid>.L <baselvl>.W (ZC_ACK_EXCHANGE_ITEM2)
+/// result:
+/// 0 = Char is too far
+/// 1 = Character does not exist
+/// 2 = Trade failed
+/// 3 = Accept
+/// 4 = Cancel
+/// 5 = Busy
+void clif_tradestart(struct map_session_data* sd, uint8 type)
+{
+ int fd = sd->fd;
+ struct map_session_data* tsd = map_id2sd(sd->trade_partner);
+ if( PACKETVER < 6 || !tsd ) {
+ WFIFOHEAD(fd,packet_len(0xe7));
+ WFIFOW(fd,0) = 0xe7;
+ WFIFOB(fd,2) = type;
+ WFIFOSET(fd,packet_len(0xe7));
+ } else {
+ WFIFOHEAD(fd,packet_len(0x1f5));
+ WFIFOW(fd,0) = 0x1f5;
+ WFIFOB(fd,2) = type;
+ WFIFOL(fd,3) = tsd->status.char_id;
+ WFIFOW(fd,7) = tsd->status.base_level;
+ WFIFOSET(fd,packet_len(0x1f5));
+ }
+}
+
+
+/// Notifies the client about an item from other player in current trade.
+/// 00e9 <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM)
+/// 080f <nameid>.W <item type>.B <amount>.L <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM2)
+void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount)
+{
+ int fd;
+ unsigned char *buf;
+#if PACKETVER < 20100223
+ const int cmd = 0xe9;
+#else
+ const int cmd = 0x80f;
+#endif
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+
+ fd = tsd->fd;
+ buf = WFIFOP(fd,0);
+ WFIFOHEAD(fd,packet_len(cmd));
+ WBUFW(buf,0) = cmd;
+ if( index == 0 )
+ {
+#if PACKETVER < 20100223
+ WBUFL(buf,2) = amount; //amount
+ WBUFW(buf,6) = 0; // type id
+#else
+ WBUFW(buf,2) = 0; // type id
+ WBUFB(buf,4) = 0; // item type
+ WBUFL(buf,5) = amount; // amount
+ buf = WBUFP(buf,1); //Advance 1B
+#endif
+ WBUFB(buf,8) = 0; //identify flag
+ WBUFB(buf,9) = 0; // attribute
+ WBUFB(buf,10)= 0; //refine
+ WBUFW(buf,11)= 0; //card (4w)
+ WBUFW(buf,13)= 0; //card (4w)
+ WBUFW(buf,15)= 0; //card (4w)
+ WBUFW(buf,17)= 0; //card (4w)
+ }
+ else
+ {
+ index -= 2; //index fix
+#if PACKETVER < 20100223
+ WBUFL(buf,2) = amount; //amount
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,6) = sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,6) = sd->status.inventory[index].nameid; // type id
+#else
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,2) = sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,2) = sd->status.inventory[index].nameid; // type id
+ WBUFB(buf,4) = sd->inventory_data[index]->type; // item type
+ WBUFL(buf,5) = amount; // amount
+ buf = WBUFP(buf,1); //Advance 1B
+#endif
+ WBUFB(buf,8) = sd->status.inventory[index].identify; //identify flag
+ WBUFB(buf,9) = sd->status.inventory[index].attribute; // attribute
+ WBUFB(buf,10)= sd->status.inventory[index].refine; //refine
+ clif_addcards(WBUFP(buf, 11), &sd->status.inventory[index]);
+ }
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Notifies the client about the result of request to add an item to the current trade (ZC_ACK_ADD_EXCHANGE_ITEM).
+/// 00ea <index>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = overweight
+/// 2 = trade canceled
+void clif_tradeitemok(struct map_session_data* sd, int index, int fail)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xea));
+ WFIFOW(fd,0) = 0xea;
+ WFIFOW(fd,2) = index;
+ WFIFOB(fd,4) = fail;
+ WFIFOSET(fd,packet_len(0xea));
+}
+
+
+/// Notifies the client about finishing one side of the current trade (ZC_CONCLUDE_EXCHANGE_ITEM).
+/// 00ec <who>.B
+/// who:
+/// 0 = self
+/// 1 = other player
+void clif_tradedeal_lock(struct map_session_data* sd, int fail)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xec));
+ WFIFOW(fd,0) = 0xec;
+ WFIFOB(fd,2) = fail;
+ WFIFOSET(fd,packet_len(0xec));
+}
+
+
+/// Notifies the client about the trade being canceled (ZC_CANCEL_EXCHANGE_ITEM).
+/// 00ee
+void clif_tradecancelled(struct map_session_data* sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xee));
+ WFIFOW(fd,0) = 0xee;
+ WFIFOSET(fd,packet_len(0xee));
+}
+
+
+/// Result of a trade (ZC_EXEC_EXCHANGE_ITEM).
+/// 00f0 <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_tradecompleted(struct map_session_data* sd, int fail)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf0));
+ WFIFOW(fd,0) = 0xf0;
+ WFIFOB(fd,2) = fail;
+ WFIFOSET(fd,packet_len(0xf0));
+}
+
+
+/// Resets the trade window on the send side (ZC_EXCHANGEITEM_UNDO).
+/// 00f1
+/// NOTE: Unknown purpose. Items are not removed until the window is
+/// refreshed (ex. by putting another item in there).
+void clif_tradeundo(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xf1));
+ WFIFOW(fd,0) = 0xf1;
+ WFIFOSET(fd,packet_len(0xf1));
+}
+
+
+/// Updates storage total amount (ZC_NOTIFY_STOREITEM_COUNTINFO).
+/// 00f2 <current count>.W <max count>.W
+void clif_updatestorageamount(struct map_session_data* sd, int amount, int max_amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf2));
+ WFIFOW(fd,0) = 0xf2;
+ WFIFOW(fd,2) = amount;
+ WFIFOW(fd,4) = max_amount;
+ WFIFOSET(fd,packet_len(0xf2));
+}
+
+
+/// Notifies the client of an item being added to the storage.
+/// 00f4 <index>.W <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE)
+/// 01c4 <index>.W <amount>.L <nameid>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE2)
+void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(i);
+ fd=sd->fd;
+ view = itemdb_viewid(i->nameid);
+
+#if PACKETVER < 5
+ WFIFOHEAD(fd,packet_len(0xf4));
+ WFIFOW(fd, 0) = 0xf4; // Storage item added
+ WFIFOW(fd, 2) = index+1; // index
+ WFIFOL(fd, 4) = amount; // amount
+ WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id
+ WFIFOB(fd,10) = i->identify; //identify flag
+ WFIFOB(fd,11) = i->attribute; // attribute
+ WFIFOB(fd,12) = i->refine; //refine
+ clif_addcards(WFIFOP(fd,13), i);
+ WFIFOSET(fd,packet_len(0xf4));
+#else
+ WFIFOHEAD(fd,packet_len(0x1c4));
+ WFIFOW(fd, 0) = 0x1c4; // Storage item added
+ WFIFOW(fd, 2) = index+1; // index
+ WFIFOL(fd, 4) = amount; // amount
+ WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id
+ WFIFOB(fd,10) = itemdb_type(i->nameid); //type
+ WFIFOB(fd,11) = i->identify; //identify flag
+ WFIFOB(fd,12) = i->attribute; // attribute
+ WFIFOB(fd,13) = i->refine; //refine
+ clif_addcards(WFIFOP(fd,14), i);
+ WFIFOSET(fd,packet_len(0x1c4));
+#endif
+}
+
+
+/// Notifies the client of an item being deleted from the storage (ZC_DELETE_ITEM_FROM_STORE).
+/// 00f6 <index>.W <amount>.L
+void clif_storageitemremoved(struct map_session_data* sd, int index, int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf6));
+ WFIFOW(fd,0)=0xf6; // Storage item removed
+ WFIFOW(fd,2)=index+1;
+ WFIFOL(fd,4)=amount;
+ WFIFOSET(fd,packet_len(0xf6));
+}
+
+
+/// Closes storage (ZC_CLOSE_STORE).
+/// 00f8
+void clif_storageclose(struct map_session_data* sd)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf8));
+ WFIFOW(fd,0) = 0xf8; // Storage Closed
+ WFIFOSET(fd,packet_len(0xf8));
+}
+
+/*==========================================
+ * 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)
+{
+ struct block_list *d_bl;
+ int i;
+
+ if( dstsd->chatID ) {
+ struct chat_data *cd = NULL;
+ if( (cd = (struct chat_data*)map_id2bl(dstsd->chatID)) && cd->usersd[0]==dstsd)
+ clif_dispchat(cd,sd->fd);
+ } else if( dstsd->state.vending )
+ clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd);
+ else if( dstsd->state.buyingstore )
+ clif_buyingstore_entry_single(sd, dstsd);
+
+ if(dstsd->spiritball > 0)
+ clif_spiritball_single(sd->fd, dstsd);
+ for(i = 1; i < 5; i++){
+ if( dstsd->talisman[i] > 0 )
+ clif_talisman_single(sd->fd, dstsd, i);
+ }
+ 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);
+ }
+#ifdef NEW_CARTS
+ if( dstsd->sc.data[SC_PUSH_CART] )
+ clif_status_load_single(sd->fd, dstsd->bl.id, SI_ON_PUSH_CART, 2, dstsd->sc.data[SC_PUSH_CART]->val1, 0, 0);
+#endif
+ 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
+ pc_has_permission(sd, PC_PERM_VIEW_HPMETER)
+ )
+ clif_hpmeter_single(sd->fd, dstsd->bl.id, dstsd->battle_status.hp, dstsd->battle_status.max_hp);
+
+ // display link (sd - dstsd) to sd
+ ARR_FIND( 0, 5, i, sd->devotion[i] == dstsd->bl.id );
+ if( i < 5 ) clif_devotion(&sd->bl, sd);
+ // display links (dstsd - devotees) to sd
+ ARR_FIND( 0, 5, i, dstsd->devotion[i] > 0 );
+ if( i < 5 ) clif_devotion(&dstsd->bl, sd);
+ // display link (dstsd - crusader) to sd
+ if( dstsd->sc.data[SC_DEVOTION] && (d_bl = map_id2bl(dstsd->sc.data[SC_DEVOTION]->val1)) != NULL )
+ clif_devotion(d_bl, sd);
+}
+
+void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
+{
+ uint8 buf[128];
+ struct unit_data *ud;
+ struct view_data *vd;
+ int len;
+
+ vd = status_get_viewdata(bl);
+ if (!vd || vd->class_ == INVISIBLE_CLASS)
+ return;
+
+ /**
+ * Hide NPC from maya purple card.
+ **/
+ if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
+ return;
+
+ ud = unit_bl2ud(bl);
+ len = ( ud && ud->walktimer != INVALID_TIMER ) ? clif_set_unit_walking(bl,ud,buf) : clif_set_unit_idle(bl,buf,false);
+ clif_send(buf,len,&sd->bl,SELF);
+
+ if (vd->cloth_color)
+ clif_refreshlook(&sd->bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,SELF);
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ {
+ TBL_PC* tsd = (TBL_PC*)bl;
+ clif_getareachar_pc(sd, tsd);
+ if(tsd->state.size==SZ_BIG) // tiny/big players [Valaris]
+ clif_specialeffect_single(bl,423,sd->fd);
+ else if(tsd->state.size==SZ_MEDIUM)
+ clif_specialeffect_single(bl,421,sd->fd);
+ if( tsd->bg_id && map[tsd->bl.m].flag.battleground )
+ clif_sendbgemblem_single(sd->fd,tsd);
+ if( tsd->sc.data[SC_CAMOUFLAGE] )
+ clif_status_load(bl,SI_CAMOUFLAGE,1);
+ }
+ break;
+ case BL_MER: // Devotion Effects
+ if( ((TBL_MER*)bl)->devotion_flag )
+ clif_devotion(bl, sd);
+ break;
+ case BL_NPC:
+ {
+ TBL_NPC* nd = (TBL_NPC*)bl;
+ if( nd->chat_id )
+ clif_dispchat((struct chat_data*)map_id2bl(nd->chat_id),sd->fd);
+ if( nd->size == SZ_BIG )
+ clif_specialeffect_single(bl,423,sd->fd);
+ else if( nd->size == SZ_MEDIUM )
+ clif_specialeffect_single(bl,421,sd->fd);
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB* md = (TBL_MOB*)bl;
+ if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
+ clif_specialeffect_single(bl,423,sd->fd);
+ else if(md->special_state.size==SZ_MEDIUM)
+ clif_specialeffect_single(bl,421,sd->fd);
+#if PACKETVER >= 20120404
+ if( !(md->status.mode&MD_BOSS) ){
+ int i;
+ for(i = 0; i < DAMAGELOG_SIZE; i++)// must show hp bar to all char who already hit the mob.
+ if( md->dmglog[i].id == sd->status.char_id )
+ clif_monster_hp_bar(md, sd->fd);
+ }
+#endif
+ }
+ break;
+ case BL_PET:
+ if (vd->head_bottom)
+ clif_pet_equip(sd, (TBL_PET*)bl); // needed to display pet equip properly
+ break;
+ }
+}
+
+//Modifies the type of damage according to status changes [Skotlex]
+//Aegis data specifies that: 4 endure against single hit sources, 9 against multi-hit.
+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]
+ *------------------------------------------*/
+static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int damage, int div_)
+{
+ if (type == 4 || type == 9 || damage <=0)
+ return 0;
+
+ if (bl->type == BL_PC) {
+ if (battle_config.pc_walk_delay_rate != 100)
+ delay = delay*battle_config.pc_walk_delay_rate/100;
+ } else
+ if (battle_config.walk_delay_rate != 100)
+ delay = delay*battle_config.walk_delay_rate/100;
+
+ if (div_ > 1) //Multi-hit skills mean higher delays.
+ delay += battle_config.multihit_delay*(div_-1);
+
+ return delay>0?delay:1; //Return 1 to specify there should be no noticeable delay, but you should stop walking.
+}
+
+
+/// Sends a 'damage' packet (src performs action on dst)
+/// 008a <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.W <div>.W <type>.B <damage2>.W (ZC_NOTIFY_ACT)
+/// 02e1 <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.L <div>.W <type>.B <damage2>.L (ZC_NOTIFY_ACT2)
+/// type:
+/// 0 = damage [ damage: total damage, div: amount of hits, damage2: assassin dual-wield damage ]
+/// 1 = pick up item
+/// 2 = sit down
+/// 3 = stand up
+/// 4 = damage (endure)
+/// 5 = (splash?)
+/// 6 = (skill?)
+/// 7 = (repeat damage?)
+/// 8 = multi-hit damage
+/// 9 = multi-hit damage (endure)
+/// 10 = critical hit
+/// 11 = lucky dodge
+/// 12 = (touch skill?)
+int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int damage, int div, int type, int damage2)
+{
+ unsigned char buf[33];
+ struct status_change *sc;
+#if PACKETVER < 20071113
+ const int cmd = 0x8a;
+#else
+ const int cmd = 0x2e1;
+#endif
+
+ nullpo_ret(src);
+ nullpo_ret(dst);
+
+ type = clif_calc_delay(type,div,damage+damage2,ddelay);
+ sc = status_get_sc(dst);
+ if(sc && sc->count) {
+ if(sc->data[SC_HALLUCINATION]) {
+ if(damage) damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ if(damage2) damage2 = damage2*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ }
+ }
+
+ WBUFW(buf,0)=cmd;
+ WBUFL(buf,2)=src->id;
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=tick;
+ WBUFL(buf,14)=sdelay;
+ WBUFL(buf,18)=ddelay;
+#if PACKETVER < 20071113
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFW(buf,22)=damage?div:0;
+ WBUFW(buf,27)=damage2?div:0;
+ } else {
+ WBUFW(buf,22)=min(damage, INT16_MAX);
+ WBUFW(buf,27)=damage2;
+ }
+ WBUFW(buf,24)=div;
+ WBUFB(buf,26)=type;
+#else
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFL(buf,22)=damage?div:0;
+ WBUFL(buf,29)=damage2?div:0;
+ } else {
+ WBUFL(buf,22)=damage;
+ WBUFL(buf,29)=damage2;
+ }
+ WBUFW(buf,26)=div;
+ WBUFB(buf,28)=type;
+#endif
+ if(disguised(dst)) {
+ clif_send(buf,packet_len(cmd),dst,AREA_WOS);
+ WBUFL(buf,6) = -dst->id;
+ clif_send(buf,packet_len(cmd),dst,SELF);
+ } else
+ clif_send(buf,packet_len(cmd),dst,AREA);
+
+ if(disguised(src)) {
+ WBUFL(buf,2) = -src->id;
+ if (disguised(dst))
+ WBUFL(buf,6) = dst->id;
+#if PACKETVER < 20071113
+ if(damage > 0) WBUFW(buf,22) = -1;
+ if(damage2 > 0) WBUFW(buf,27) = -1;
+#else
+ if(damage > 0) WBUFL(buf,22) = -1;
+ if(damage2 > 0) WBUFL(buf,29) = -1;
+#endif
+ clif_send(buf,packet_len(cmd),src,SELF);
+ }
+
+ if(src == dst) {
+ unit_setdir(src,unit_getdir(src));
+ }
+ //Return adjusted can't walk delay for further processing.
+ return clif_calc_walkdelay(dst,ddelay,type,damage+damage2,div);
+}
+
+/*==========================================
+ * src picks up dst
+ *------------------------------------------*/
+void clif_takeitem(struct block_list* src, struct block_list* dst)
+{
+ //clif_damage(src,dst,0,0,0,0,0,1,0);
+ unsigned char buf[32];
+
+ nullpo_retv(src);
+ nullpo_retv(dst);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = src->id;
+ WBUFL(buf, 6) = dst->id;
+ WBUFB(buf,26) = 1;
+ clif_send(buf, packet_len(0x8a), src, AREA);
+
+}
+
+/*==========================================
+ * inform clients in area that `bl` is sitting
+ *------------------------------------------*/
+void clif_sitting(struct block_list* bl)
+{
+ unsigned char buf[32];
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = bl->id;
+ WBUFB(buf,26) = 2;
+ clif_send(buf, packet_len(0x8a), bl, AREA);
+
+ if(disguised(bl)) {
+ WBUFL(buf, 2) = - bl->id;
+ clif_send(buf, packet_len(0x8a), bl, SELF);
+ }
+}
+
+/*==========================================
+ * inform clients in area that `bl` is standing
+ *------------------------------------------*/
+void clif_standing(struct block_list* bl)
+{
+ unsigned char buf[32];
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = bl->id;
+ WBUFB(buf,26) = 3;
+ clif_send(buf, packet_len(0x8a), bl, AREA);
+
+ if(disguised(bl)) {
+ WBUFL(buf, 2) = - bl->id;
+ clif_send(buf, packet_len(0x8a), bl, SELF);
+ }
+}
+
+
+/// Inform client(s) about a map-cell change (ZC_UPDATE_MAPINFO).
+/// 0192 <x>.W <y>.W <type>.W <map name>.16B
+void clif_changemapcell(int fd, int16 m, int x, int y, int type, enum send_target target)
+{
+ unsigned char buf[32];
+
+ WBUFW(buf,0) = 0x192;
+ WBUFW(buf,2) = x;
+ WBUFW(buf,4) = y;
+ WBUFW(buf,6) = type;
+ mapindex_getmapname_ext(map[m].name,(char*)WBUFP(buf,8));
+
+ if( fd )
+ {
+ WFIFOHEAD(fd,packet_len(0x192));
+ memcpy(WFIFOP(fd,0), buf, packet_len(0x192));
+ WFIFOSET(fd,packet_len(0x192));
+ }
+ else
+ {
+ struct block_list dummy_bl;
+ dummy_bl.type = BL_NUL;
+ dummy_bl.x = x;
+ dummy_bl.y = y;
+ dummy_bl.m = m;
+ clif_send(buf,packet_len(0x192),&dummy_bl,target);
+ }
+}
+
+
+/// Notifies the client about an item on floor (ZC_ITEM_ENTRY).
+/// 009d <id>.L <name id>.W <identified>.B <x>.W <y>.W <amount>.W <subX>.B <subY>.B
+void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fitem)
+{
+ int view,fd;
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x9d));
+ WFIFOW(fd,0)=0x9d;
+ WFIFOL(fd,2)=fitem->bl.id;
+ if((view = itemdb_viewid(fitem->item_data.nameid)) > 0)
+ WFIFOW(fd,6)=view;
+ else
+ WFIFOW(fd,6)=fitem->item_data.nameid;
+ WFIFOB(fd,8)=fitem->item_data.identify;
+ WFIFOW(fd,9)=fitem->bl.x;
+ WFIFOW(fd,11)=fitem->bl.y;
+ WFIFOW(fd,13)=fitem->item_data.amount;
+ WFIFOB(fd,15)=fitem->subx;
+ WFIFOB(fd,16)=fitem->suby;
+ WFIFOSET(fd,packet_len(0x9d));
+}
+
+
+/// Notifies the client of a skill unit.
+/// 011f <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B (ZC_SKILL_ENTRY)
+/// 01c9 <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B <has msg>.B <msg>.80B (ZC_SKILL_ENTRY2)
+static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill_unit *unit)
+{
+ int fd = sd->fd;
+
+ if( unit->group->state.guildaura )
+ return;
+
+#if PACKETVER >= 3
+ if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris]
+ WFIFOHEAD(fd,packet_len(0x1c9));
+ WFIFOW(fd, 0)=0x1c9;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=1;
+ WFIFOB(fd,16)=1;
+ safestrncpy((char*)WFIFOP(fd,17),unit->group->valstr,MESSAGE_SIZE);
+ WFIFOSET(fd,packet_len(0x1c9));
+ return;
+ }
+#endif
+ WFIFOHEAD(fd,packet_len(0x11f));
+ WFIFOW(fd, 0)=0x11f;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ if (battle_config.traps_setting&1 && skill_get_inf2(unit->group->skill_id)&INF2_TRAP)
+ WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
+ else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
+ WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
+ else
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=1; // ignored by client (always gets set to 1)
+ WFIFOSET(fd,packet_len(0x11f));
+
+ if(unit->group->skill_id == WZ_ICEWALL)
+ clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,5,SELF);
+}
+
+
+/*==========================================
+ * Server tells client to remove unit of id 'unit->bl.id'
+ *------------------------------------------*/
+static void clif_clearchar_skillunit(struct skill_unit *unit, int fd)
+{
+ nullpo_retv(unit);
+
+ WFIFOHEAD(fd,packet_len(0x120));
+ WFIFOW(fd, 0)=0x120;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOSET(fd,packet_len(0x120));
+
+ if(unit->group && unit->group->skill_id == WZ_ICEWALL)
+ clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,SELF);
+}
+
+
+/// Removes a skill unit (ZC_SKILL_DISAPPEAR).
+/// 0120 <id>.L
+void clif_skill_delunit(struct skill_unit *unit)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(unit);
+
+ WBUFW(buf, 0)=0x120;
+ WBUFL(buf, 2)=unit->bl.id;
+ clif_send(buf,packet_len(0x120),&unit->bl,AREA);
+}
+
+
+/// Sent when an object gets ankle-snared (ZC_SKILL_UPDATE).
+/// 01ac <id>.L
+/// Only affects units with class [139,153] client-side.
+void clif_skillunit_update(struct block_list* bl)
+{
+ unsigned char buf[6];
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1ac;
+ WBUFL(buf,2) = bl->id;
+
+ clif_send(buf,packet_len(0x1ac),bl,AREA);
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int clif_getareachar(struct block_list* bl,va_list ap)
+{
+ struct map_session_data *sd;
+
+ nullpo_ret(bl);
+
+ sd=va_arg(ap,struct map_session_data*);
+
+ if (sd == NULL || !sd->fd)
+ return 0;
+
+ switch(bl->type){
+ case BL_ITEM:
+ clif_getareachar_item(sd,(struct flooritem_data*) bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(sd,(TBL_SKILL*)bl);
+ break;
+ default:
+ if(&sd->bl == bl)
+ break;
+ clif_getareachar_unit(sd,bl);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * tbl has gone out of view-size of bl
+ *------------------------------------------*/
+int clif_outsight(struct block_list *bl,va_list ap)
+{
+ struct block_list *tbl;
+ struct view_data *vd;
+ TBL_PC *sd, *tsd;
+ tbl=va_arg(ap,struct block_list*);
+ if(bl == tbl) return 0;
+ sd = BL_CAST(BL_PC, bl);
+ tsd = BL_CAST(BL_PC, tbl);
+
+ if (tsd && tsd->fd)
+ { //tsd has lost sight of the bl object.
+ switch(bl->type){
+ case BL_PC:
+ if (sd->vd.class_ != INVISIBLE_CLASS)
+ clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
+ if(sd->chatID){
+ struct chat_data *cd;
+ cd=(struct chat_data*)map_id2bl(sd->chatID);
+ if(cd->usersd[0]==sd)
+ clif_dispchat(cd,tsd->fd);
+ }
+ if( sd->state.vending )
+ clif_closevendingboard(bl,tsd->fd);
+ if( sd->state.buyingstore )
+ clif_buyingstore_disappear_entry_single(tsd, sd);
+ break;
+ case BL_ITEM:
+ clif_clearflooritem((struct flooritem_data*)bl,tsd->fd);
+ break;
+ case BL_SKILL:
+ clif_clearchar_skillunit((struct skill_unit *)bl,tsd->fd);
+ break;
+ case BL_NPC:
+ if( !(((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE) )
+ clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
+ break;
+ default:
+ if ((vd=status_get_viewdata(bl)) && vd->class_ != INVISIBLE_CLASS)
+ clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
+ break;
+ }
+ }
+ if (sd && sd->fd)
+ { //sd is watching tbl go out of view.
+ if (((vd=status_get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) &&
+ !(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->sc.option&OPTION_INVISIBLE)))
+ clif_clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd);
+ }
+ return 0;
+}
+
+/*==========================================
+ * tbl has come into view of bl
+ *------------------------------------------*/
+int clif_insight(struct block_list *bl,va_list ap)
+{
+ struct block_list *tbl;
+ TBL_PC *sd, *tsd;
+ tbl=va_arg(ap,struct block_list*);
+
+ if (bl == tbl) return 0;
+
+ sd = BL_CAST(BL_PC, bl);
+ tsd = BL_CAST(BL_PC, tbl);
+
+ if (tsd && tsd->fd)
+ { //Tell tsd that bl entered into his view
+ switch(bl->type){
+ case BL_ITEM:
+ clif_getareachar_item(tsd,(struct flooritem_data*)bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(tsd,(TBL_SKILL*)bl);
+ break;
+ default:
+ clif_getareachar_unit(tsd,bl);
+ break;
+ }
+ }
+ if (sd && sd->fd)
+ { //Tell sd that tbl walked into his view
+ clif_getareachar_unit(sd,tbl);
+ }
+ return 0;
+}
+
+
+/// Updates whole skill tree (ZC_SKILLINFO_LIST).
+/// 010f <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }*
+void clif_skillinfoblock(struct map_session_data *sd)
+{
+ int fd;
+ int i,len,id;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ if (!fd) return;
+
+ WFIFOHEAD(fd, MAX_SKILL * 37 + 4);
+ WFIFOW(fd,0) = 0x10f;
+ for ( i = 0, len = 4; i < MAX_SKILL; i++)
+ {
+ if( (id = sd->status.skill[i].id) != 0 )
+ {
+ // workaround for bugreport:5348
+ if (len + 37 > 8192)
+ break;
+
+ WFIFOW(fd,len) = id;
+ WFIFOL(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+6) = sd->status.skill[i].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id,sd->status.skill[i].lv);
+ WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->status.skill[i].lv);
+ safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ if(sd->status.skill[i].flag == SKILL_FLAG_PERMANENT)
+ WFIFOB(fd,len+36) = (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
+ else
+ WFIFOB(fd,len+36) = 0;
+ len += 37;
+ }
+ }
+ WFIFOW(fd,2)=len;
+ WFIFOSET(fd,len);
+
+ // workaround for bugreport:5348; send the remaining skills one by one to bypass packet size limit
+ for ( ; i < MAX_SKILL; i++)
+ {
+ if( (id = sd->status.skill[i].id) != 0 )
+ {
+ clif_addskill(sd, id);
+ clif_skillinfo(sd, id, 0);
+ }
+ }
+}
+/**
+ * Server tells client 'sd' to add skill of id 'id' to it's skill tree (e.g. with Ice Falcion item)
+ **/
+
+/// Adds new skill to the skill tree (ZC_ADD_SKILL).
+/// 0111 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B
+void clif_addskill(struct map_session_data *sd, int id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ if (!fd) return;
+
+ if( sd->status.skill[id].id <= 0 )
+ return;
+
+ WFIFOHEAD(fd, packet_len(0x111));
+ WFIFOW(fd,0) = 0x111;
+ WFIFOW(fd,2) = id;
+ WFIFOL(fd,4) = skill_get_inf(id);
+ WFIFOW(fd,8) = sd->status.skill[id].lv;
+ WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[id].lv);
+ WFIFOW(fd,12)= skill_get_range2(&sd->bl, id,sd->status.skill[id].lv);
+ safestrncpy((char*)WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH);
+ if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT )
+ WFIFOB(fd,38) = (sd->status.skill[id].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
+ else
+ WFIFOB(fd,38) = 0;
+ WFIFOSET(fd,packet_len(0x111));
+}
+
+
+/// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE).
+/// 0441 <skill id>.W
+void clif_deleteskill(struct map_session_data *sd, int id)
+{
+#if PACKETVER >= 20081217
+ int fd;
+
+ nullpo_retv(sd);
+ fd = sd->fd;
+ if( !fd ) return;
+
+ WFIFOHEAD(fd,packet_len(0x441));
+ WFIFOW(fd,0) = 0x441;
+ WFIFOW(fd,2) = id;
+ WFIFOSET(fd,packet_len(0x441));
+#endif
+ clif_skillinfoblock(sd);
+}
+
+
+/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE).
+/// 010e <skill id>.W <level>.W <sp cost>.W <attack range>.W <upgradable>.B
+void clif_skillup(struct map_session_data *sd,uint16 skill_id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x10e));
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = sd->status.skill[skill_id].lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_id,sd->status.skill[skill_id].lv);
+ WFIFOW(fd,8) = skill_get_range2(&sd->bl,skill_id,sd->status.skill[skill_id].lv);
+ WFIFOB(fd,10) = (sd->status.skill[skill_id].lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0;
+ WFIFOSET(fd,packet_len(0x10e));
+}
+
+
+/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE2).
+/// 07e1 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <upgradable>.B
+void clif_skillinfo(struct map_session_data *sd,int skill, int inf)
+{
+ const int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x7e1));
+ WFIFOW(fd,0) = 0x7e1;
+ WFIFOW(fd,2) = skill;
+ WFIFOL(fd,4) = inf?inf:skill_get_inf(skill);
+ WFIFOW(fd,8) = sd->status.skill[skill].lv;
+ WFIFOW(fd,10) = skill_get_sp(skill,sd->status.skill[skill].lv);
+ WFIFOW(fd,12) = skill_get_range2(&sd->bl,skill,sd->status.skill[skill].lv);
+ if( sd->status.skill[skill].flag == SKILL_FLAG_PERMANENT )
+ WFIFOB(fd,14) = (sd->status.skill[skill].lv < skill_tree_get_max(skill, sd->status.class_))? 1:0;
+ else
+ WFIFOB(fd,14) = 0;
+ WFIFOSET(fd,packet_len(0x7e1));
+}
+
+
+/// Notifies clients in area, that an object is about to use a skill.
+/// 013e <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L (ZC_USESKILL_ACK)
+/// 07fb <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L <is disposable>.B (ZC_USESKILL_ACK2)
+/// property:
+/// 0 = Yellow cast aura
+/// 1 = Water elemental cast aura
+/// 2 = Earth elemental cast aura
+/// 3 = Fire elemental cast aura
+/// 4 = Wind elemental cast aura
+/// 5 = Poison elemental cast aura
+/// 6 = Holy elemental cast aura
+/// ? = like 0
+/// is disposable:
+/// 0 = yellow chat text "[src name] will use skill [skill name]."
+/// 1 = no text
+void clif_skillcasting(struct block_list* bl, int src_id, int dst_id, int dst_x, int dst_y, uint16 skill_id, int property, int casttime)
+{
+#if PACKETVER < 20091124
+ const int cmd = 0x13e;
+#else
+ const int cmd = 0x7fb;
+#endif
+ unsigned char buf[32];
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = src_id;
+ WBUFL(buf,6) = dst_id;
+ WBUFW(buf,10) = dst_x;
+ WBUFW(buf,12) = dst_y;
+ WBUFW(buf,14) = skill_id;
+ WBUFL(buf,16) = property<0?0:property; //Avoid sending negatives as element [Skotlex]
+ WBUFL(buf,20) = casttime;
+#if PACKETVER >= 20091124
+ WBUFB(buf,24) = 0; // isDisposable
+#endif
+
+ if (disguised(bl)) {
+ clif_send(buf,packet_len(cmd), bl, AREA_WOS);
+ WBUFL(buf,2) = -src_id;
+ clif_send(buf,packet_len(cmd), bl, SELF);
+ } else
+ clif_send(buf,packet_len(cmd), bl, AREA);
+}
+
+
+/// Notifies clients in area, that an object canceled casting (ZC_DISPEL).
+/// 01b9 <id>.L
+void clif_skillcastcancel(struct block_list* bl)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1b9;
+ WBUFL(buf,2) = bl->id;
+ clif_send(buf,packet_len(0x1b9), bl, AREA);
+}
+
+
+/// Notifies the client about the result of a skill use request (ZC_ACK_TOUSESKILL).
+/// 0110 <skill id>.W <num>.L <result>.B <cause>.B
+/// num (only used when skill id = NV_BASIC and cause = 0):
+/// 0 = "skill failed" MsgStringTable[159]
+/// 1 = "no emotions" MsgStringTable[160]
+/// 2 = "no sit" MsgStringTable[161]
+/// 3 = "no chat" MsgStringTable[162]
+/// 4 = "no party" MsgStringTable[163]
+/// 5 = "no shout" MsgStringTable[164]
+/// 6 = "no PKing" MsgStringTable[165]
+/// 7 = "no alligning" MsgStringTable[383]
+/// ? = ignored
+/// cause:
+/// 0 = "not enough skill level" MsgStringTable[214] (AL_WARP)
+/// "steal failed" MsgStringTable[205] (TF_STEAL)
+/// "envenom failed" MsgStringTable[207] (TF_POISON)
+/// "skill failed" MsgStringTable[204] (otherwise)
+/// ... = @see enum useskill_fail_cause
+/// ? = ignored
+///
+/// if(result!=0) doesn't display any of the previous messages
+/// Note: when this packet is received an unknown flag is always set to 0,
+/// suggesting this is an ACK packet for the UseSkill packets and should be sent on success too [FlavioJS]
+void clif_skill_fail(struct map_session_data *sd,uint16 skill_id,enum useskill_fail_cause cause,int btype)
+{
+ int fd;
+
+ if (!sd) { //Since this is the most common nullpo....
+ ShowDebug("clif_skill_fail: Error, received NULL sd for skill %d\n", skill_id);
+ return;
+ }
+
+ fd=sd->fd;
+ if (!fd) return;
+
+ if(battle_config.display_skill_fail&1)
+ return; //Disable all skill failed messages
+
+ if(cause==USESKILL_FAIL_SKILLINTERVAL && !sd->state.showdelay)
+ return; //Disable delay failed messages
+
+ if(skill_id == RG_SNATCHER && battle_config.display_skill_fail&4)
+ return;
+
+ if(skill_id == TF_POISON && battle_config.display_skill_fail&8)
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x110));
+ WFIFOW(fd,0) = 0x110;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOL(fd,4) = btype;
+ WFIFOB(fd,8) = 0;// success
+ WFIFOB(fd,9) = cause;
+ WFIFOSET(fd,packet_len(0x110));
+}
+
+
+/// Skill cooldown display icon (ZC_SKILL_POSTDELAY).
+/// 043d <skill ID>.W <tick>.L
+void clif_skill_cooldown(struct map_session_data *sd, uint16 skill_id, unsigned int tick)
+{
+#if PACKETVER>=20081112
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x43d));
+ WFIFOW(fd,0) = 0x43d;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOL(fd,4) = tick;
+ WFIFOSET(fd,packet_len(0x43d));
+#endif
+}
+
+
+/// Skill attack effect and damage.
+/// 0114 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.W <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL)
+/// 01de <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.L <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL2)
+int clif_skill_damage(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc;
+
+ nullpo_ret(src);
+ nullpo_ret(dst);
+
+ type = clif_calc_delay(type,div,damage,ddelay);
+ sc = status_get_sc(dst);
+ if(sc && sc->count) {
+ if(sc->data[SC_HALLUCINATION] && damage)
+ damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ }
+
+#if PACKETVER < 3
+ WBUFW(buf,0)=0x114;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFW(buf,24)=damage?div:0;
+ } else {
+ WBUFW(buf,24)=damage;
+ }
+ WBUFW(buf,26)=skill_lv;
+ WBUFW(buf,28)=div;
+ WBUFB(buf,30)=type;
+ if (disguised(dst)) {
+ clif_send(buf,packet_len(0x114),dst,AREA_WOS);
+ WBUFL(buf,8)=-dst->id;
+ clif_send(buf,packet_len(0x114),dst,SELF);
+ } else
+ clif_send(buf,packet_len(0x114),dst,AREA);
+
+ if(disguised(src)) {
+ WBUFL(buf,4)=-src->id;
+ if (disguised(dst))
+ WBUFL(buf,8)=dst->id;
+ if(damage > 0)
+ WBUFW(buf,24)=-1;
+ clif_send(buf,packet_len(0x114),src,SELF);
+ }
+#else
+ WBUFW(buf,0)=0x1de;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFL(buf,24)=damage?div:0;
+ } else {
+ WBUFL(buf,24)=damage;
+ }
+ WBUFW(buf,28)=skill_lv;
+ WBUFW(buf,30)=div;
+ WBUFB(buf,32)=type;
+ if (disguised(dst)) {
+ clif_send(buf,packet_len(0x1de),dst,AREA_WOS);
+ WBUFL(buf,8)=-dst->id;
+ clif_send(buf,packet_len(0x1de),dst,SELF);
+ } else
+ clif_send(buf,packet_len(0x1de),dst,AREA);
+
+ if(disguised(src)) {
+ WBUFL(buf,4)=-src->id;
+ if (disguised(dst))
+ WBUFL(buf,8)=dst->id;
+ if(damage > 0)
+ WBUFL(buf,24)=-1;
+ clif_send(buf,packet_len(0x1de),src,SELF);
+ }
+#endif
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ return clif_calc_walkdelay(dst,ddelay,type,damage,div);
+}
+
+
+/// Ground skill attack effect and damage (ZC_NOTIFY_SKILL_POSITION).
+/// 0115 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <x>.W <y>.W <damage>.W <level>.W <div>.W <type>.B
+/*
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc;
+
+ nullpo_ret(src);
+ nullpo_ret(dst);
+
+ type = (type>0)?type:skill_get_hit(skill_id);
+ type = clif_calc_delay(type,div,damage,ddelay);
+ sc = status_get_sc(dst);
+
+ if(sc && sc->count) {
+ if(sc->data[SC_HALLUCINATION] && damage)
+ damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ }
+
+ WBUFW(buf,0)=0x115;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFW(buf,24)=dst->x;
+ WBUFW(buf,26)=dst->y;
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFW(buf,28)=damage?div:0;
+ } else {
+ WBUFW(buf,28)=damage;
+ }
+ WBUFW(buf,30)=skill_lv;
+ WBUFW(buf,32)=div;
+ WBUFB(buf,34)=type;
+ clif_send(buf,packet_len(0x115),src,AREA);
+ if(disguised(src)) {
+ WBUFL(buf,4)=-src->id;
+ if(damage > 0)
+ WBUFW(buf,28)=-1;
+ clif_send(buf,packet_len(0x115),src,SELF);
+ }
+ if (disguised(dst)) {
+ WBUFL(buf,8)=-dst->id;
+ if (disguised(src))
+ WBUFL(buf,4)=src->id;
+ else if(damage > 0)
+ WBUFW(buf,28)=-1;
+ clif_send(buf,packet_len(0x115),dst,SELF);
+ }
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ return clif_calc_walkdelay(dst,ddelay,type,damage,div);
+}
+*/
+
+
+/// Non-damaging skill effect (ZC_USE_SKILL).
+/// 011a <skill id>.W <skill lv>.W <dst id>.L <src id>.L <result>.B
+int clif_skill_nodamage(struct block_list *src,struct block_list *dst,uint16 skill_id,int heal,int fail)
+{
+ unsigned char buf[32];
+
+ nullpo_ret(dst);
+
+ WBUFW(buf,0)=0x11a;
+ WBUFW(buf,2)=skill_id;
+ WBUFW(buf,4)=min(heal, INT16_MAX);
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=src?src->id:0;
+ WBUFB(buf,14)=fail;
+
+ if (disguised(dst)) {
+ clif_send(buf,packet_len(0x11a),dst,AREA_WOS);
+ WBUFL(buf,6)=-dst->id;
+ clif_send(buf,packet_len(0x11a),dst,SELF);
+ } else
+ clif_send(buf,packet_len(0x11a),dst,AREA);
+
+ if(src && disguised(src)) {
+ WBUFL(buf,10)=-src->id;
+ if (disguised(dst))
+ WBUFL(buf,6)=dst->id;
+ clif_send(buf,packet_len(0x11a),src,SELF);
+ }
+
+ return fail;
+}
+
+
+/// Non-damaging ground skill effect (ZC_NOTIFY_GROUNDSKILL).
+/// 0117 <skill id>.W <src id>.L <level>.W <x>.W <y>.W <tick>.L
+void clif_skill_poseffect(struct block_list *src,uint16 skill_id,int val,int x,int y,int tick)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(src);
+
+ WBUFW(buf,0)=0x117;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFW(buf,8)=val;
+ WBUFW(buf,10)=x;
+ WBUFW(buf,12)=y;
+ WBUFL(buf,14)=tick;
+ if(disguised(src)) {
+ clif_send(buf,packet_len(0x117),src,AREA_WOS);
+ WBUFL(buf,4)=-src->id;
+ clif_send(buf,packet_len(0x117),src,SELF);
+ } else
+ clif_send(buf,packet_len(0x117),src,AREA);
+}
+
+
+/*==========================================
+ * 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)
+{
+ unsigned char buf[128];
+
+ nullpo_retv(unit);
+
+ if( unit->group->state.guildaura )
+ return;
+
+#if PACKETVER >= 3
+ if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris]
+ WBUFW(buf, 0)=0x1c9;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=1;
+ WBUFB(buf,16)=1;
+ safestrncpy((char*)WBUFP(buf,17),unit->group->valstr,MESSAGE_SIZE);
+ clif_send(buf,packet_len(0x1c9),&unit->bl,AREA);
+ return;
+ }
+#endif
+ WBUFW(buf, 0)=0x11f;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ if (unit->group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE)
+ WBUFB(buf,14)=unit->val2&UF_SONG?UNT_DISSONANCE:UNT_UGLYDANCE;
+ else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
+ WBUFB(buf, 14) = UNT_DUMMYSKILL; // Only display the unit at center.
+ else
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=1; // ignored by client (always gets set to 1)
+ clif_send(buf,packet_len(0x11f),&unit->bl,AREA);
+}
+
+
+/// Presents a list of available warp destinations (ZC_WARPLIST).
+/// 011c <skill id>.W { <map name>.16B }*4
+void clif_skill_warppoint(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x11c));
+ WFIFOW(fd,0) = 0x11c;
+ WFIFOW(fd,2) = skill_id;
+ memset(WFIFOP(fd,4), 0x00, 4*MAP_NAME_LENGTH_EXT);
+ if (map1 == (unsigned short)-1) strcpy((char*)WFIFOP(fd,4), "Random");
+ else // normal map name
+ if (map1 > 0) mapindex_getmapname_ext(mapindex_id2name(map1), (char*)WFIFOP(fd,4));
+ if (map2 > 0) mapindex_getmapname_ext(mapindex_id2name(map2), (char*)WFIFOP(fd,20));
+ if (map3 > 0) mapindex_getmapname_ext(mapindex_id2name(map3), (char*)WFIFOP(fd,36));
+ if (map4 > 0) mapindex_getmapname_ext(mapindex_id2name(map4), (char*)WFIFOP(fd,52));
+ WFIFOSET(fd,packet_len(0x11c));
+
+ sd->menuskill_id = skill_id;
+ if (skill_id == AL_WARP)
+ sd->menuskill_val = (sd->ud.skillx<<16)|sd->ud.skilly; //Store warp position here.
+ else
+ sd->menuskill_val = skill_lv;
+}
+
+
+/// Memo message (ZC_ACK_REMEMBER_WARPPOINT).
+/// 011e <type>.B
+/// type:
+/// 0 = "Saved location as a Memo Point for Warp skill." in color 0xFFFF00 (cyan)
+/// 1 = "Skill Level is not high enough." in color 0x0000FF (red)
+/// 2 = "You haven't learned Warp." in color 0x0000FF (red)
+///
+/// @param sd Who receives the message
+/// @param type What message
+void clif_skill_memomessage(struct map_session_data* sd, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x11e));
+ WFIFOW(fd,0)=0x11e;
+ WFIFOB(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x11e));
+}
+
+
+/// Teleport message (ZC_NOTIFY_MAPINFO).
+/// 0189 <type>.W
+/// type:
+/// 0 = "Unable to Teleport in this area" in color 0xFFFF00 (cyan)
+/// 1 = "Saved point cannot be memorized." in color 0x0000FF (red)
+///
+/// @param sd Who receives the message
+/// @param type What message
+void clif_skill_teleportmessage(struct map_session_data *sd, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x189));
+ WFIFOW(fd,0)=0x189;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x189));
+}
+
+
+/// Displays Sense (WZ_ESTIMATION) information window (ZC_MONSTER_INFO).
+/// 018c <class>.W <level>.W <size>.W <hp>.L <def>.W <race>.W <mdef>.W <element>.W
+/// <water%>.B <earth%>.B <fire%>.B <wind%>.B <poison%>.B <holy%>.B <shadow%>.B <ghost%>.B <undead%>.B
+void clif_skill_estimation(struct map_session_data *sd,struct block_list *dst)
+{
+ struct status_data *status;
+ unsigned char buf[64];
+ int i;//, fix;
+
+ nullpo_retv(sd);
+ nullpo_retv(dst);
+
+ if( dst->type != BL_MOB )
+ return;
+
+ status = status_get_status_data(dst);
+
+ WBUFW(buf, 0)=0x18c;
+ WBUFW(buf, 2)=status_get_class(dst);
+ WBUFW(buf, 4)=status_get_lv(dst);
+ WBUFW(buf, 6)=status->size;
+ WBUFL(buf, 8)=status->hp;
+ WBUFW(buf,12)= (battle_config.estimation_type&1?status->def:0)
+ +(battle_config.estimation_type&2?status->def2:0);
+ WBUFW(buf,14)=status->race;
+ WBUFW(buf,16)= (battle_config.estimation_type&1?status->mdef:0)
+ +(battle_config.estimation_type&2?status->mdef2:0);
+ WBUFW(buf,18)= status->def_ele;
+ for(i=0;i<9;i++)
+ WBUFB(buf,20+i)= (unsigned char)battle_attr_ratio(i+1,status->def_ele, status->ele_lv);
+// The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex]
+// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_ratio(i+1,status->def_ele, status->ele_lv))<0?0:fix);
+
+ clif_send(buf,packet_len(0x18c),&sd->bl,sd->status.party_id>0?PARTY_SAMEMAP:SELF);
+}
+
+
+/// Presents a textual list of producable items (ZC_MAKABLEITEMLIST).
+/// 018d <packet len>.W { <name id>.W { <material id>.W }*3 }*
+/// material id:
+/// unused by the client
+void clif_skill_produce_mix_list(struct map_session_data *sd, int skill_id , int trigger)
+{
+ int i,c,view,fd;
+ nullpo_retv(sd);
+
+ if(sd->menuskill_id == skill_id)
+ return; //Avoid resending the menu twice or more times...
+ if( skill_id == GC_CREATENEWPOISON )
+ skill_id = GC_RESEARCHNEWPOISON;
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8);
+ WFIFOW(fd, 0)=0x18d;
+
+ for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if( skill_can_produce_mix(sd,skill_produce_db[i].nameid, trigger, 1) &&
+ ( skill_id > 0 && skill_produce_db[i].req_skill == skill_id || skill_id < 0 )
+ ){
+ if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
+ WFIFOW(fd,c*8+ 4)= view;
+ else
+ WFIFOW(fd,c*8+ 4)= skill_produce_db[i].nameid;
+ WFIFOW(fd,c*8+ 6)= 0;
+ WFIFOW(fd,c*8+ 8)= 0;
+ WFIFOW(fd,c*8+10)= 0;
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*8+8;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ if(c > 0) {
+ sd->menuskill_id = skill_id;
+ sd->menuskill_val = trigger;
+ return;
+ }
+}
+
+
+/// Present a list of producable items (ZC_MAKINGITEM_LIST).
+/// 025a <packet len>.W <mk type>.W { <name id>.W }*
+/// mk type:
+/// 1 = cooking
+/// 2 = arrow
+/// 3 = elemental
+/// 4 = GN_MIX_COOKING
+/// 5 = GN_MAKEBOMB
+/// 6 = GN_S_PHARMACY
+void clif_cooking_list(struct map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type)
+{
+ int fd;
+ int i, c;
+ int view;
+
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, 6 + 2 * MAX_SKILL_PRODUCE_DB);
+ WFIFOW(fd,0) = 0x25a;
+ WFIFOW(fd,4) = list_type; // list type
+
+ c = 0;
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, qty) )
+ continue;
+
+ if( (view = itemdb_viewid(skill_produce_db[i].nameid)) > 0 )
+ WFIFOW(fd, 6 + 2 * c) = view;
+ else
+ WFIFOW(fd, 6 + 2 * c) = skill_produce_db[i].nameid;
+
+ c++;
+ }
+
+ if( skill_id == AM_PHARMACY ) { // Only send it while Cooking else check for c.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+
+ if( c > 0 ) {
+ sd->menuskill_id = skill_id;
+ sd->menuskill_val = trigger;
+ if( skill_id != AM_PHARMACY ) {
+ sd->menuskill_val2 = qty; // amount.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ } else {
+ clif_menuskill_clear(sd);
+ if( skill_id != AM_PHARMACY ) { // AM_PHARMACY is used to Cooking.
+ // It fails.
+#if PACKETVER >= 20090922
+ clif_msg_skill(sd,skill_id,0x625);
+#else
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+#endif
+ }
+ }
+}
+
+
+/// Notifies clients of a status change.
+/// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)]
+/// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs]
+void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val1, int val2, int val3)
+{
+ unsigned char buf[32];
+ struct map_session_data *sd;
+
+ if (type == SI_BLANK) //It shows nothing on the client...
+ return;
+
+ nullpo_retv(bl);
+
+ sd = BL_CAST(BL_PC, bl);
+
+ if (!(status_type2relevant_bl_types(type)&bl->type)) // only send status changes that actually matter to the client
+ return;
+
+#if PACKETVER >= 20090121
+ if(flag && battle_config.display_status_timers && sd)
+ WBUFW(buf,0)=0x43f;
+ else
+#endif
+ WBUFW(buf,0)=0x196;
+ WBUFW(buf,2)=type;
+ WBUFL(buf,4)=bl->id;
+ WBUFB(buf,8)=flag;
+#if PACKETVER >= 20090121
+ if(flag && battle_config.display_status_timers && sd)
+ {
+ if (tick <= 0)
+ tick = 9999; // this is indeed what official servers do
+
+ WBUFL(buf,9) = tick;
+ WBUFL(buf,13) = val1;
+ WBUFL(buf,17) = val2;
+ WBUFL(buf,21) = val3;
+ }
+#endif
+ clif_send(buf,packet_len(WBUFW(buf,0)),bl, (sd && sd->status.option&OPTION_INVISIBLE) ? SELF : AREA);
+}
+
+/// Send message (modified by [Yor]) (ZC_NOTIFY_PLAYERCHAT).
+/// 008e <packet len>.W <message>.?B
+void clif_displaymessage(const int fd, const char* mes)
+{
+ nullpo_retv(mes);
+
+ //Scrapped, as these are shared by disconnected players =X [Skotlex]
+ if (fd == 0)
+ ;
+ else {
+ char *message, *line;
+
+ message = aStrdup(mes);
+ line = strtok(message, "\n");
+ while(line != NULL) {
+ // Limit message to 255+1 characters (otherwise it causes a buffer overflow in the client)
+ int len = strnlen(line, 255);
+
+ if (len > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
+ WFIFOHEAD(fd, 5 + len);
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOW(fd,2) = 5 + len; // 4 + len + NULL teminate
+ safestrncpy((char *)WFIFOP(fd,4), line, len + 1);
+ WFIFOSET(fd, 5 + len);
+ }
+ line = strtok(NULL, "\n");
+ }
+ aFree(message);
+ }
+}
+
+/// Send broadcast message in yellow or blue without font formatting (ZC_BROADCAST).
+/// 009a <packet len>.W <message>.?B
+void clif_broadcast(struct block_list* bl, const char* mes, int len, int type, enum send_target target)
+{
+ int lp = type ? 4 : 0;
+ unsigned char *buf = (unsigned char*)aMalloc((4 + lp + len)*sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x9a;
+ WBUFW(buf,2) = 4 + lp + len;
+ if (type == 0x10) // bc_blue
+ WBUFL(buf,4) = 0x65756c62; //If there's "blue" at the beginning of the message, game client will display it in blue instead of yellow.
+ else if (type == 0x20) // bc_woe
+ WBUFL(buf,4) = 0x73737373; //If there's "ssss", game client will recognize message as 'WoE broadcast'.
+ memcpy(WBUFP(buf, 4 + lp), mes, len);
+ clif_send(buf, WBUFW(buf,2), bl, target);
+
+ if (buf)
+ aFree(buf);
+}
+
+/*==========================================
+ * 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) {
+ char buf[100];
+ int len;
+ nullpo_retv(bl);
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+
+ if( len > sizeof(buf)-8 ) {
+ ShowWarning("clif_GlobalMessage: Truncating too long message '%s' (len=%d).\n", message, len);
+ len = sizeof(buf)-8;
+ }
+
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=bl->id;
+ safestrncpy((char *) WBUFP(buf,8),message,len);
+ clif_send((unsigned char *) buf,WBUFW(buf,2),bl,ALL_CLIENT);
+
+}
+
+/*==========================================
+ * Send main chat message [LuzZza]
+ *------------------------------------------*/
+void clif_MainChatMessage(const char* message) {
+ uint8 buf[200];
+ int len;
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+ if (len+8 > sizeof(buf)) {
+ ShowDebug("clif_MainChatMessage: Received message too long (len %d): %s\n", len, message);
+ len = sizeof(buf)-8;
+ }
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=0;
+ safestrncpy((char *) WBUFP(buf,8),message,len);
+ clif_send(buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT);
+}
+
+/// Send broadcast message with font formatting (ZC_BROADCAST2).
+/// 01c3 <packet len>.W <fontColor>.L <fontType>.W <fontSize>.W <fontAlign>.W <fontY>.W <message>.?B
+void clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY, enum send_target target)
+{
+ unsigned char *buf = (unsigned char*)aMalloc((16 + len)*sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x1c3;
+ WBUFW(buf,2) = len + 16;
+ WBUFL(buf,4) = fontColor;
+ WBUFW(buf,8) = fontType;
+ WBUFW(buf,10) = fontSize;
+ WBUFW(buf,12) = fontAlign;
+ WBUFW(buf,14) = fontY;
+ memcpy(WBUFP(buf,16), mes, len);
+ clif_send(buf, WBUFW(buf,2), bl, target);
+
+ if (buf)
+ aFree(buf);
+}
+
+
+/// Displays heal effect (ZC_RECOVERY).
+/// 013d <var id>.W <amount>.W
+/// var id:
+/// 5 = HP (SP_HP)
+/// 7 = SP (SP_SP)
+/// ? = ignored
+void clif_heal(int fd,int type,int val)
+{
+ WFIFOHEAD(fd,packet_len(0x13d));
+ WFIFOW(fd,0)=0x13d;
+ WFIFOW(fd,2)=type;
+ WFIFOW(fd,4)=cap_value(val,0,INT16_MAX);
+ WFIFOSET(fd,packet_len(0x13d));
+}
+
+
+/// Displays resurrection effect (ZC_RESURRECTION).
+/// 0148 <id>.L <type>.W
+/// type:
+/// ignored
+void clif_resurrection(struct block_list *bl,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0x148;
+ WBUFL(buf,2)=bl->id;
+ WBUFW(buf,6)=0;
+
+ clif_send(buf,packet_len(0x148),bl,type==1 ? AREA : AREA_WOS);
+ if (disguised(bl))
+ clif_spawn(bl);
+}
+
+
+/// Sets the map property (ZC_NOTIFY_MAPPROPERTY).
+/// 0199 <type>.W
+void clif_map_property(struct map_session_data* sd, enum map_property property)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x199));
+ WFIFOW(fd,0)=0x199;
+ WFIFOW(fd,2)=property;
+ WFIFOSET(fd,packet_len(0x199));
+}
+
+
+/// Set the map type (ZC_NOTIFY_MAPPROPERTY2).
+/// 01d6 <type>.W
+void clif_map_type(struct map_session_data* sd, enum map_type type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1D6));
+ WFIFOW(fd,0)=0x1D6;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x1D6));
+}
+
+
+/// Updates PvP ranking (ZC_NOTIFY_RANKING).
+/// 019a <id>.L <ranking>.L <total>.L
+void clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type)
+{
+ if(type == 2) {
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x19a));
+ WFIFOW(fd,0) = 0x19a;
+ WFIFOL(fd,2) = sd->bl.id;
+ WFIFOL(fd,6) = pvprank;
+ WFIFOL(fd,10) = pvpnum;
+ WFIFOSET(fd,packet_len(0x19a));
+ } else {
+ unsigned char buf[32];
+ WBUFW(buf,0) = 0x19a;
+ WBUFL(buf,2) = sd->bl.id;
+ if(sd->sc.option&(OPTION_HIDE|OPTION_CLOAK))
+ WBUFL(buf,6) = UINT32_MAX; //On client displays as --
+ else
+ WBUFL(buf,6) = pvprank;
+ WBUFL(buf,10) = pvpnum;
+ if(sd->sc.option&OPTION_INVISIBLE || sd->disguise) //Causes crashes when a 'mob' with pvp info dies.
+ clif_send(buf,packet_len(0x19a),&sd->bl,SELF);
+ else if(!type)
+ clif_send(buf,packet_len(0x19a),&sd->bl,AREA);
+ else
+ clif_send(buf,packet_len(0x19a),&sd->bl,ALL_SAMEMAP);
+ }
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_map_property_mapall(int map, enum map_property property)
+{
+ struct block_list bl;
+ unsigned char buf[16];
+
+ bl.id = 0;
+ bl.type = BL_NUL;
+ bl.m = map;
+ WBUFW(buf,0)=0x199;
+ WBUFW(buf,2)=property;
+ clif_send(buf,packet_len(0x199),&bl,ALL_SAMEMAP);
+}
+
+
+/// Notifies the client about the result of a refine attempt (ZC_ACK_ITEMREFINING).
+/// 0188 <result>.W <index>.W <refine>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = downgrade
+void clif_refine(int fd, int fail, int index, int val)
+{
+ WFIFOHEAD(fd,packet_len(0x188));
+ WFIFOW(fd,0)=0x188;
+ WFIFOW(fd,2)=fail;
+ WFIFOW(fd,4)=index+2;
+ WFIFOW(fd,6)=val;
+ WFIFOSET(fd,packet_len(0x188));
+}
+
+
+/// Notifies the client about the result of a weapon refine attempt (ZC_ACK_WEAPONREFINE).
+/// 0223 <result>.L <nameid>.W
+/// result:
+/// 0 = "weapon upgraded: %s" MsgStringTable[911] in rgb(0,255,255)
+/// 1 = "weapon upgraded: %s" MsgStringTable[912] in rgb(0,205,205)
+/// 2 = "cannot upgrade %s until you level up the upgrade weapon skill" MsgStringTable[913] in rgb(255,200,200)
+/// 3 = "you lack the item %s to upgrade the weapon" MsgStringTable[914] in rgb(255,200,200)
+void clif_upgrademessage(int fd, int result, int item_id)
+{
+ WFIFOHEAD(fd,packet_len(0x223));
+ WFIFOW(fd,0)=0x223;
+ WFIFOL(fd,2)=result;
+ WFIFOW(fd,6)=item_id;
+ WFIFOSET(fd,packet_len(0x223));
+}
+
+
+/// Whisper is transmitted to the destination player (ZC_WHISPER).
+/// 0097 <packet len>.W <nick>.24B <message>.?B
+/// 0097 <packet len>.W <nick>.24B <isAdmin>.L <message>.?B (PACKETVER >= 20091104)
+void clif_wis_message(int fd, const char* nick, const char* mes, int mes_len)
+{
+#if PACKETVER < 20091104
+ WFIFOHEAD(fd, mes_len + NAME_LENGTH + 4);
+ WFIFOW(fd,0) = 0x97;
+ WFIFOW(fd,2) = mes_len + NAME_LENGTH + 4;
+ safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH);
+ safestrncpy((char*)WFIFOP(fd,28), mes, mes_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+#else
+ WFIFOHEAD(fd, mes_len + NAME_LENGTH + 8);
+ WFIFOW(fd,0) = 0x97;
+ WFIFOW(fd,2) = mes_len + NAME_LENGTH + 8;
+ safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH);
+ WFIFOL(fd,28) = 0; // isAdmin; if nonzero, also displays text above char
+ // TODO: WFIFOL(fd,28) = pc_get_group_level(ssd);
+ safestrncpy((char*)WFIFOP(fd,32), mes, mes_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+#endif
+}
+
+
+/// Inform the player about the result of his whisper action (ZC_ACK_WHISPER).
+/// 0098 <result>.B
+/// result:
+/// 0 = success to send wisper
+/// 1 = target character is not loged in
+/// 2 = ignored by target
+/// 3 = everyone ignored by target
+void clif_wis_end(int fd, int flag)
+{
+ WFIFOHEAD(fd,packet_len(0x98));
+ WFIFOW(fd,0) = 0x98;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x98));
+}
+
+
+/// Returns character name requested by char_id (ZC_ACK_REQNAME_BYGID).
+/// 0194 <char id>.L <name>.24B
+void clif_solved_charname(int fd, int charid, const char* name)
+{
+ WFIFOHEAD(fd,packet_len(0x194));
+ WFIFOW(fd,0)=0x194;
+ WFIFOL(fd,2)=charid;
+ safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x194));
+}
+
+
+/// Presents a list of items that can be carded/composed (ZC_ITEMCOMPOSITION_LIST).
+/// 017b <packet len>.W { <name id>.W }*
+void clif_use_card(struct map_session_data *sd,int idx)
+{
+ int i,c,ep;
+ int fd=sd->fd;
+
+ nullpo_retv(sd);
+ if (idx < 0 || idx >= MAX_INVENTORY) //Crash-fix from bad packets.
+ return;
+
+ if (!sd->inventory_data[idx] || sd->inventory_data[idx]->type != IT_CARD)
+ return; //Avoid parsing invalid item indexes (no card/no item)
+
+ ep=sd->inventory_data[idx]->equip;
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x17b;
+
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ int j;
+
+ if(sd->inventory_data[i] == NULL)
+ continue;
+ if(sd->inventory_data[i]->type!=IT_WEAPON && sd->inventory_data[i]->type!=IT_ARMOR)
+ continue;
+ if(itemdb_isspecial(sd->status.inventory[i].card[0])) //Can't slot it
+ continue;
+
+ if(sd->status.inventory[i].identify==0 ) //Not identified
+ continue;
+
+ if((sd->inventory_data[i]->equip&ep)==0) //Not equippable on this part.
+ continue;
+
+ if(sd->inventory_data[i]->type==IT_WEAPON && ep==EQP_SHIELD) //Shield card won't go on left weapon.
+ continue;
+
+ ARR_FIND( 0, sd->inventory_data[i]->slot, j, sd->status.inventory[i].card[j] == 0 );
+ if( j == sd->inventory_data[i]->slot ) // No room
+ continue;
+
+ WFIFOW(fd,4+c*2)=i+2;
+ c++;
+ }
+ WFIFOW(fd,2)=4+c*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notifies the client about the result of item carding/composition (ZC_ACK_ITEMCOMPOSITION).
+/// 017d <equip index>.W <card index>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x17d));
+ WFIFOW(fd,0)=0x17d;
+ WFIFOW(fd,2)=idx_equip+2;
+ WFIFOW(fd,4)=idx_card+2;
+ WFIFOB(fd,6)=flag;
+ WFIFOSET(fd,packet_len(0x17d));
+}
+
+
+/// Presents a list of items that can be identified (ZC_ITEMIDENTIFY_LIST).
+/// 0177 <packet len>.W { <name id>.W }*
+void clif_item_identify_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x177;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && !sd->status.inventory[i].identify){
+ WFIFOW(fd,c*2+4)=i+2;
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*2+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->menuskill_id = MC_IDENTIFY;
+ sd->menuskill_val = c;
+ }
+}
+
+
+/// Notifies the client about the result of a item identify request (ZC_ACK_ITEMIDENTIFY).
+/// 0179 <index>.W <result>.B
+void clif_item_identified(struct map_session_data *sd,int idx,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x179));
+ WFIFOW(fd, 0)=0x179;
+ WFIFOW(fd, 2)=idx+2;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len(0x179));
+}
+
+
+/// Presents a list of items that can be repaired (ZC_REPAIRITEMLIST).
+/// 01fc <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+void clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd, int lv)
+{
+ int i,c;
+ int fd;
+ int nameid;
+
+ nullpo_retv(sd);
+ nullpo_retv(dstsd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x1fc;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if((nameid=dstsd->status.inventory[i].nameid) > 0 && dstsd->status.inventory[i].attribute!=0){// && skill_can_repair(sd,nameid)){
+ WFIFOW(fd,c*13+4) = i;
+ WFIFOW(fd,c*13+6) = nameid;
+ WFIFOB(fd,c*13+8) = dstsd->status.inventory[i].refine;
+ clif_addcards(WFIFOP(fd,c*13+9), &dstsd->status.inventory[i]);
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->menuskill_id = BS_REPAIRWEAPON;
+ sd->menuskill_val = dstsd->bl.id;
+ sd->menuskill_val2 = lv;
+ }else
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+}
+
+
+/// Notifies the client about the result of a item repair request (ZC_ACK_ITEMREPAIR).
+/// 01fe <index>.W <result>.B
+/// index:
+/// ignored (inventory index)
+/// result:
+/// 0 = Item repair success.
+/// 1 = Item repair failure.
+void clif_item_repaireffect(struct map_session_data *sd,int idx,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x1fe));
+ WFIFOW(fd, 0)=0x1fe;
+ WFIFOW(fd, 2)=idx+2;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len(0x1fe));
+
+}
+
+
+/// Displays a message, that an equipment got damaged (ZC_EQUIPITEM_DAMAGED).
+/// 02bb <equip location>.W <account id>.L
+void clif_item_damaged(struct map_session_data* sd, unsigned short position)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2bb));
+ WFIFOW(fd,0) = 0x2bb;
+ WFIFOW(fd,2) = position;
+ WFIFOL(fd,4) = sd->bl.id; // TODO: the packet seems to be sent to other people as well, probably party and/or guild.
+ WFIFOSET(fd,packet_len(0x2bb));
+}
+
+
+/// Presents a list of weapon items that can be refined [Taken from jAthena] (ZC_NOTIFY_WEAPONITEMLIST).
+/// 0221 <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+void clif_item_refine_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+ uint16 skill_lv;
+ int wlv;
+ int refine_item[5];
+
+ nullpo_retv(sd);
+
+ skill_lv = pc_checkskill(sd,WS_WEAPONREFINE);
+
+ fd=sd->fd;
+
+ refine_item[0] = -1;
+ refine_item[1] = pc_search_inventory(sd,1010);
+ refine_item[2] = pc_search_inventory(sd,1011);
+ refine_item[3] = refine_item[4] = pc_search_inventory(sd,984);
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x221;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].refine < skill_lv &&
+ sd->status.inventory[i].identify && (wlv=itemdb_wlv(sd->status.inventory[i].nameid)) >=1 &&
+ refine_item[wlv]!=-1 && !(sd->status.inventory[i].equip&EQP_ARMS)){
+ WFIFOW(fd,c*13+ 4)=i+2;
+ WFIFOW(fd,c*13+ 6)=sd->status.inventory[i].nameid;
+ WFIFOB(fd,c*13+ 8)=sd->status.inventory[i].refine;
+ clif_addcards(WFIFOP(fd,c*13+9), &sd->status.inventory[i]);
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ if (c > 0) {
+ sd->menuskill_id = WS_WEAPONREFINE;
+ sd->menuskill_val = skill_lv;
+ }
+}
+
+
+/// Notification of an auto-casted skill (ZC_AUTORUN_SKILL).
+/// 0147 <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B
+void clif_item_skill(struct map_session_data *sd,uint16 skill_id,uint16 skill_lv)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x147));
+ WFIFOW(fd, 0)=0x147;
+ WFIFOW(fd, 2)=skill_id;
+ WFIFOW(fd, 4)=skill_get_inf(skill_id);
+ WFIFOW(fd, 6)=0;
+ WFIFOW(fd, 8)=skill_lv;
+ WFIFOW(fd,10)=skill_get_sp(skill_id,skill_lv);
+ WFIFOW(fd,12)=skill_get_range2(&sd->bl, skill_id,skill_lv);
+ safestrncpy((char*)WFIFOP(fd,14),skill_get_name(skill_id),NAME_LENGTH);
+ WFIFOB(fd,38)=0;
+ WFIFOSET(fd,packet_len(0x147));
+}
+
+
+/// Adds an item to character's cart.
+/// 0124 <index>.W <amount>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART)
+/// 01c5 <index>.W <amount>.L <name id>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART2)
+void clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail)
+{
+ int view,fd;
+ unsigned char *buf;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ if(n<0 || n>=MAX_CART || sd->status.cart[n].nameid<=0)
+ return;
+
+#if PACKETVER < 5
+ WFIFOHEAD(fd,packet_len(0x124));
+ buf=WFIFOP(fd,0);
+ WBUFW(buf,0)=0x124;
+ WBUFW(buf,2)=n+2;
+ WBUFL(buf,4)=amount;
+ if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
+ WBUFW(buf,8)=view;
+ else
+ WBUFW(buf,8)=sd->status.cart[n].nameid;
+ WBUFB(buf,10)=sd->status.cart[n].identify;
+ WBUFB(buf,11)=sd->status.cart[n].attribute;
+ WBUFB(buf,12)=sd->status.cart[n].refine;
+ clif_addcards(WBUFP(buf,13), &sd->status.cart[n]);
+ WFIFOSET(fd,packet_len(0x124));
+#else
+ WFIFOHEAD(fd,packet_len(0x1c5));
+ buf=WFIFOP(fd,0);
+ WBUFW(buf,0)=0x1c5;
+ WBUFW(buf,2)=n+2;
+ WBUFL(buf,4)=amount;
+ if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
+ WBUFW(buf,8)=view;
+ else
+ WBUFW(buf,8)=sd->status.cart[n].nameid;
+ WBUFB(buf,10)=itemdb_type(sd->status.cart[n].nameid);
+ WBUFB(buf,11)=sd->status.cart[n].identify;
+ WBUFB(buf,12)=sd->status.cart[n].attribute;
+ WBUFB(buf,13)=sd->status.cart[n].refine;
+ clif_addcards(WBUFP(buf,14), &sd->status.cart[n]);
+ WFIFOSET(fd,packet_len(0x1c5));
+#endif
+}
+
+
+/// Deletes an item from character's cart (ZC_DELETE_ITEM_FROM_CART).
+/// 0125 <index>.W <amount>.L
+void clif_cart_delitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x125));
+ WFIFOW(fd,0)=0x125;
+ WFIFOW(fd,2)=n+2;
+ WFIFOL(fd,4)=amount;
+ WFIFOSET(fd,packet_len(0x125));
+}
+
+
+/// Opens the shop creation menu (ZC_OPENSTORE).
+/// 012d <num>.W
+/// num:
+/// number of allowed item slots
+void clif_openvendingreq(struct map_session_data* sd, int num)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x12d));
+ WFIFOW(fd,0) = 0x12d;
+ WFIFOW(fd,2) = num;
+ WFIFOSET(fd,packet_len(0x12d));
+}
+
+
+/// Displays a vending board to target/area (ZC_STORE_ENTRY).
+/// 0131 <owner id>.L <message>.80B
+void clif_showvendingboard(struct block_list* bl, const char* message, int fd)
+{
+ unsigned char buf[128];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x131;
+ WBUFL(buf,2) = bl->id;
+ safestrncpy((char*)WBUFP(buf,6), message, 80);
+
+ if( fd ) {
+ WFIFOHEAD(fd,packet_len(0x131));
+ memcpy(WFIFOP(fd,0),buf,packet_len(0x131));
+ WFIFOSET(fd,packet_len(0x131));
+ } else {
+ clif_send(buf,packet_len(0x131),bl,AREA_WOS);
+ }
+}
+
+
+/// Removes a vending board from screen (ZC_DISAPPEAR_ENTRY).
+/// 0132 <owner id>.L
+void clif_closevendingboard(struct block_list* bl, int fd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x132;
+ WBUFL(buf,2) = bl->id;
+ if( fd ) {
+ WFIFOHEAD(fd,packet_len(0x132));
+ memcpy(WFIFOP(fd,0),buf,packet_len(0x132));
+ WFIFOSET(fd,packet_len(0x132));
+ } else {
+ clif_send(buf,packet_len(0x132),bl,AREA_WOS);
+ }
+}
+
+
+/// Sends a list of items in a shop.
+/// R 0133 <packet len>.W <owner id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC)
+/// R 0800 <packet len>.W <owner id>.L <unique id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC2)
+void clif_vendinglist(struct map_session_data* sd, int id, struct s_vending* vending)
+{
+ int i,fd;
+ int count;
+ struct map_session_data* vsd;
+#if PACKETVER < 20100105
+ const int cmd = 0x133;
+ const int offset = 8;
+#else
+ const int cmd = 0x800;
+ const int offset = 12;
+#endif
+
+ nullpo_retv(sd);
+ nullpo_retv(vending);
+ nullpo_retv(vsd=map_id2sd(id));
+
+ fd = sd->fd;
+ count = vsd->vend_num;
+
+ WFIFOHEAD(fd, offset+count*22);
+ WFIFOW(fd,0) = cmd;
+ WFIFOW(fd,2) = offset+count*22;
+ WFIFOL(fd,4) = id;
+#if PACKETVER >= 20100105
+ WFIFOL(fd,8) = vsd->vender_id;
+#endif
+
+ for( i = 0; i < count; i++ )
+ {
+ int index = vending[i].index;
+ struct item_data* data = itemdb_search(vsd->status.cart[index].nameid);
+ WFIFOL(fd,offset+ 0+i*22) = vending[i].value;
+ WFIFOW(fd,offset+ 4+i*22) = vending[i].amount;
+ WFIFOW(fd,offset+ 6+i*22) = vending[i].index + 2;
+ WFIFOB(fd,offset+ 8+i*22) = itemtype(data->type);
+ WFIFOW(fd,offset+ 9+i*22) = ( data->view_id > 0 ) ? data->view_id : vsd->status.cart[index].nameid;
+ WFIFOB(fd,offset+11+i*22) = vsd->status.cart[index].identify;
+ WFIFOB(fd,offset+12+i*22) = vsd->status.cart[index].attribute;
+ WFIFOB(fd,offset+13+i*22) = vsd->status.cart[index].refine;
+ clif_addcards(WFIFOP(fd,offset+14+i*22), &vsd->status.cart[index]);
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Shop purchase failure (ZC_PC_PURCHASE_RESULT_FROMMC).
+/// 0135 <index>.W <amount>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = not enough zeny
+/// 2 = overweight
+/// 4 = out of stock
+/// 5 = "cannot use an npc shop while in a trade"
+/// 6 = Because the store information was incorrect the item was not purchased.
+/// 7 = No sales information.
+void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x135));
+ WFIFOW(fd,0) = 0x135;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = amount;
+ WFIFOB(fd,6) = fail;
+ WFIFOSET(fd,packet_len(0x135));
+}
+
+
+/// Shop creation success (ZC_PC_PURCHASE_MYITEMLIST).
+/// 0136 <packet len>.W <owner id>.L { <price>.L <index>.W <amount>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+void clif_openvending(struct map_session_data* sd, int id, struct s_vending* vending)
+{
+ int i,fd;
+ int count;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ count = sd->vend_num;
+
+ WFIFOHEAD(fd, 8+count*22);
+ WFIFOW(fd,0) = 0x136;
+ WFIFOW(fd,2) = 8+count*22;
+ WFIFOL(fd,4) = id;
+ for( i = 0; i < count; i++ )
+ {
+ int index = vending[i].index;
+ struct item_data* data = itemdb_search(sd->status.cart[index].nameid);
+ WFIFOL(fd, 8+i*22) = vending[i].value;
+ WFIFOW(fd,12+i*22) = vending[i].index + 2;
+ WFIFOW(fd,14+i*22) = vending[i].amount;
+ WFIFOB(fd,16+i*22) = itemtype(data->type);
+ WFIFOW(fd,17+i*22) = ( data->view_id > 0 ) ? data->view_id : sd->status.cart[index].nameid;
+ WFIFOB(fd,19+i*22) = sd->status.cart[index].identify;
+ WFIFOB(fd,20+i*22) = sd->status.cart[index].attribute;
+ WFIFOB(fd,21+i*22) = sd->status.cart[index].refine;
+ clif_addcards(WFIFOP(fd,22+i*22), &sd->status.cart[index]);
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Inform merchant that someone has bought an item (ZC_DELETEITEM_FROM_MCSTORE).
+/// 0137 <index>.W <amount>.W
+void clif_vendingreport(struct map_session_data* sd, int index, int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x137));
+ WFIFOW(fd,0) = 0x137;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = amount;
+ WFIFOSET(fd,packet_len(0x137));
+}
+
+
+/// Result of organizing a party (ZC_ACK_MAKE_GROUP).
+/// 00fa <result>.B
+/// result:
+/// 0 = opens party window and shows MsgStringTable[77]="party successfully organized"
+/// 1 = MsgStringTable[78]="party name already exists"
+/// 2 = MsgStringTable[79]="already in a party"
+/// 3 = cannot organize parties on this map
+/// ? = nothing
+void clif_party_created(struct map_session_data *sd,int result)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xfa));
+ WFIFOW(fd,0)=0xfa;
+ WFIFOB(fd,2)=result;
+ WFIFOSET(fd,packet_len(0xfa));
+}
+
+
+/// Adds new member to a party.
+/// 0104 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B (ZC_ADD_MEMBER_TO_GROUP)
+/// 01e9 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B <item pickup rule>.B <item share rule>.B (ZC_ADD_MEMBER_TO_GROUP2)
+/// role:
+/// 0 = leader
+/// 1 = normal
+/// state:
+/// 0 = connected
+/// 1 = disconnected
+void clif_party_member_info(struct party_data *p, struct map_session_data *sd)
+{
+ unsigned char buf[81];
+ int i;
+
+ if (!sd) { //Pick any party member (this call is used when changing item share rules)
+ ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd != 0 );
+ } else {
+ ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd );
+ }
+ if (i >= MAX_PARTY) return; //Should never happen...
+ sd = p->data[i].sd;
+
+ WBUFW(buf, 0) = 0x1e9;
+ WBUFL(buf, 2) = sd->status.account_id;
+ WBUFL(buf, 6) = (p->party.member[i].leader)?0:1;
+ WBUFW(buf,10) = sd->bl.x;
+ WBUFW(buf,12) = sd->bl.y;
+ WBUFB(buf,14) = (p->party.member[i].online)?0:1;
+ memcpy(WBUFP(buf,15), p->party.name, NAME_LENGTH);
+ memcpy(WBUFP(buf,39), sd->status.name, NAME_LENGTH);
+ mapindex_getmapname_ext(map[sd->bl.m].name, (char*)WBUFP(buf,63));
+ WBUFB(buf,79) = (p->party.item&1)?1:0;
+ WBUFB(buf,80) = (p->party.item&2)?1:0;
+ clif_send(buf,packet_len(0x1e9),&sd->bl,PARTY);
+}
+
+
+/// Sends party information (ZC_GROUP_LIST).
+/// 00fb <packet len>.W <party name>.24B { <account id>.L <nick>.24B <map name>.16B <role>.B <state>.B }*
+/// role:
+/// 0 = leader
+/// 1 = normal
+/// state:
+/// 0 = connected
+/// 1 = disconnected
+void clif_party_info(struct party_data* p, struct map_session_data *sd)
+{
+ unsigned char buf[2+2+NAME_LENGTH+(4+NAME_LENGTH+MAP_NAME_LENGTH_EXT+1+1)*MAX_PARTY];
+ struct map_session_data* party_sd = NULL;
+ int i, c;
+
+ nullpo_retv(p);
+
+ WBUFW(buf,0) = 0xfb;
+ memcpy(WBUFP(buf,4), p->party.name, NAME_LENGTH);
+ for(i = 0, c = 0; i < MAX_PARTY; i++)
+ {
+ struct party_member* m = &p->party.member[i];
+ if(!m->account_id) continue;
+
+ if(party_sd == NULL) party_sd = p->data[i].sd;
+
+ WBUFL(buf,28+c*46) = m->account_id;
+ memcpy(WBUFP(buf,28+c*46+4), m->name, NAME_LENGTH);
+ mapindex_getmapname_ext(mapindex_id2name(m->map), (char*)WBUFP(buf,28+c*46+28));
+ WBUFB(buf,28+c*46+44) = (m->leader) ? 0 : 1;
+ WBUFB(buf,28+c*46+45) = (m->online) ? 0 : 1;
+ c++;
+ }
+ WBUFW(buf,2) = 28+c*46;
+
+ if(sd) { // send only to self
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ } else if (party_sd) { // send to whole party
+ clif_send(buf, WBUFW(buf,2), &party_sd->bl, PARTY);
+ }
+}
+
+
+/// The player's 'party invite' state, sent during login (ZC_PARTY_CONFIG).
+/// 02c9 <flag>.B
+/// flag:
+/// 0 = allow party invites
+/// 1 = auto-deny party invites
+void clif_partyinvitationstate(struct map_session_data* sd)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2c9));
+ WFIFOW(fd, 0) = 0x2c9;
+ WFIFOB(fd, 2) = 0; // not implemented
+ WFIFOSET(fd, packet_len(0x2c9));
+}
+
+
+/// Party invitation request.
+/// 00fe <party id>.L <party name>.24B (ZC_REQ_JOIN_GROUP)
+/// 02c6 <party id>.L <party name>.24B (ZC_PARTY_JOIN_REQ)
+void clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd)
+{
+#if PACKETVER < 20070821
+ const int cmd = 0xfe;
+#else
+ const int cmd = 0x2c6;
+#endif
+ int fd;
+ struct party_data *p;
+
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+
+ fd=tsd->fd;
+
+ if( (p=party_search(sd->status.party_id))==NULL )
+ return;
+
+ WFIFOHEAD(fd,packet_len(cmd));
+ WFIFOW(fd,0)=cmd;
+ WFIFOL(fd,2)=sd->status.party_id;
+ memcpy(WFIFOP(fd,6),p->party.name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Party invite result.
+/// 00fd <nick>.24S <result>.B (ZC_ACK_REQ_JOIN_GROUP)
+/// 02c5 <nick>.24S <result>.L (ZC_PARTY_JOIN_REQ_ACK)
+/// 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) = 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) = result;
+ WFIFOSET(fd,packet_len(0x2c5));
+#endif
+}
+
+
+/// Updates party settings.
+/// 0101 <exp option>.L (ZC_GROUPINFO_CHANGE)
+/// 07d8 <exp option>.L <item pick rule>.B <item share rule>.B (ZC_REQ_GROUPINFO_CHANGE_V2)
+/// exp option:
+/// 0 = exp sharing disabled
+/// 1 = exp sharing enabled
+/// 2 = cannot change exp sharing
+///
+/// flag:
+/// 0 = send to party
+/// 1 = send to sd
+void clif_party_option(struct party_data *p,struct map_session_data *sd,int flag)
+{
+ unsigned char buf[16];
+#if PACKETVER < 20090603
+ const int cmd = 0x101;
+#else
+ const int cmd = 0x7d8;
+#endif
+
+ nullpo_retv(p);
+
+ if(!sd && flag==0){
+ int i;
+ for(i=0;i<MAX_PARTY && !p->data[i].sd;i++);
+ if (i < MAX_PARTY)
+ sd = p->data[i].sd;
+ }
+ if(!sd) return;
+ WBUFW(buf,0)=cmd;
+ WBUFL(buf,2)=((flag&0x01)?2:p->party.exp);
+#if PACKETVER >= 20090603
+ WBUFB(buf,6)=(p->party.item&1)?1:0;
+ WBUFB(buf,7)=(p->party.item&2)?1:0;
+#endif
+ if(flag==0)
+ clif_send(buf,packet_len(cmd),&sd->bl,PARTY);
+ else
+ clif_send(buf,packet_len(cmd),&sd->bl,SELF);
+}
+
+
+/// 0105 <account id>.L <char name>.24B <result>.B (ZC_DELETE_MEMBER_FROM_GROUP).
+/// result:
+/// 0 = leave
+/// 1 = expel
+/// 2 = cannot leave party on this map
+/// 3 = cannot expel from party on this map
+void clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int account_id, const char* name, int flag)
+{
+ unsigned char buf[64];
+ int i;
+
+ nullpo_retv(p);
+
+ if(!sd && (flag&0xf0)==0)
+ {
+ for(i=0;i<MAX_PARTY && !p->data[i].sd;i++);
+ if (i < MAX_PARTY)
+ sd = p->data[i].sd;
+ }
+
+ if(!sd) return;
+
+ WBUFW(buf,0)=0x105;
+ WBUFL(buf,2)=account_id;
+ memcpy(WBUFP(buf,6),name,NAME_LENGTH);
+ WBUFB(buf,30)=flag&0x0f;
+ if((flag&0xf0)==0)
+ clif_send(buf,packet_len(0x105),&sd->bl,PARTY);
+ else
+ clif_send(buf,packet_len(0x105),&sd->bl,SELF);
+}
+
+
+/// Party chat message (ZC_NOTIFY_CHAT_PARTY).
+/// 0109 <packet len>.W <account id>.L <message>.?B
+void clif_party_message(struct party_data* p, int account_id, const char* mes, int len)
+{
+ struct map_session_data *sd;
+ int i;
+
+ nullpo_retv(p);
+
+ for(i=0; i < MAX_PARTY && !p->data[i].sd;i++);
+ if(i < MAX_PARTY){
+ unsigned char buf[1024];
+
+ if( len > sizeof(buf)-8 )
+ {
+ ShowWarning("clif_party_message: Truncated message '%s' (len=%d, max=%d, party_id=%d).\n", mes, len, sizeof(buf)-8, p->party.party_id);
+ len = sizeof(buf)-8;
+ }
+
+ sd = p->data[i].sd;
+ WBUFW(buf,0)=0x109;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=account_id;
+ safestrncpy((char *)WBUFP(buf,8), mes, len);
+ clif_send(buf,len+8,&sd->bl,PARTY);
+ }
+}
+
+
+/// Updates the position of a party member on the minimap (ZC_NOTIFY_POSITION_TO_GROUPM).
+/// 0107 <account id>.L <x>.W <y>.W
+void clif_party_xy(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=sd->bl.x;
+ WBUFW(buf,8)=sd->bl.y;
+ clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS);
+}
+
+
+/*==========================================
+ * Sends x/y dot to a single fd. [Skotlex]
+ *------------------------------------------*/
+void clif_party_xy_single(int fd, struct map_session_data *sd)
+{
+ WFIFOHEAD(fd,packet_len(0x107));
+ WFIFOW(fd,0)=0x107;
+ WFIFOL(fd,2)=sd->status.account_id;
+ WFIFOW(fd,6)=sd->bl.x;
+ WFIFOW(fd,8)=sd->bl.y;
+ WFIFOSET(fd,packet_len(0x107));
+}
+
+
+/// Updates HP bar of a party member.
+/// 0106 <account id>.L <hp>.W <max hp>.W (ZC_NOTIFY_HP_TO_GROUPM)
+/// 080e <account id>.L <hp>.L <max hp>.L (ZC_NOTIFY_HP_TO_GROUPM_R2)
+void clif_party_hp(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+#if PACKETVER < 20100126
+ const int cmd = 0x106;
+#else
+ const int cmd = 0x80e;
+#endif
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=cmd;
+ WBUFL(buf,2)=sd->status.account_id;
+#if PACKETVER < 20100126
+ if (sd->battle_status.max_hp > INT16_MAX) { //To correctly display the %hp bar. [Skotlex]
+ WBUFW(buf,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
+ WBUFW(buf,8) = 100;
+ } else {
+ WBUFW(buf,6) = sd->battle_status.hp;
+ WBUFW(buf,8) = sd->battle_status.max_hp;
+ }
+#else
+ WBUFL(buf,6) = sd->battle_status.hp;
+ WBUFL(buf,10) = sd->battle_status.max_hp;
+#endif
+ clif_send(buf,packet_len(cmd),&sd->bl,PARTY_AREA_WOS);
+}
+
+
+/*==========================================
+ * Sends HP bar to a single fd. [Skotlex]
+ *------------------------------------------*/
+void clif_hpmeter_single(int fd, int id, unsigned int hp, unsigned int maxhp)
+{
+#if PACKETVER < 20100126
+ const int cmd = 0x106;
+#else
+ const int cmd = 0x80e;
+#endif
+ WFIFOHEAD(fd,packet_len(cmd));
+ WFIFOW(fd,0) = cmd;
+ WFIFOL(fd,2) = id;
+#if PACKETVER < 20100126
+ if( maxhp > INT16_MAX )
+ {// To correctly display the %hp bar. [Skotlex]
+ WFIFOW(fd,6) = hp/(maxhp/100);
+ WFIFOW(fd,8) = 100;
+ } else {
+ WFIFOW(fd,6) = hp;
+ WFIFOW(fd,8) = maxhp;
+ }
+#else
+ WFIFOL(fd,6) = hp;
+ WFIFOL(fd,10) = maxhp;
+#endif
+ WFIFOSET(fd, packet_len(cmd));
+}
+
+/// Notifies the client, that it's attack target is too far (ZC_ATTACK_FAILURE_FOR_DISTANCE).
+/// 0139 <target id>.L <target x>.W <target y>.W <x>.W <y>.W <atk range>.W
+void clif_movetoattack(struct map_session_data *sd,struct block_list *bl)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(bl);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x139));
+ WFIFOW(fd, 0)=0x139;
+ WFIFOL(fd, 2)=bl->id;
+ WFIFOW(fd, 6)=bl->x;
+ WFIFOW(fd, 8)=bl->y;
+ WFIFOW(fd,10)=sd->bl.x;
+ WFIFOW(fd,12)=sd->bl.y;
+ WFIFOW(fd,14)=sd->battle_status.rhw.range;
+ WFIFOSET(fd,packet_len(0x139));
+}
+
+
+/// Notifies the client about the result of an item produce request (ZC_ACK_REQMAKINGITEM).
+/// 018f <result>.W <name id>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = success (alchemist)
+/// 3 = failure (alchemist)
+void clif_produceeffect(struct map_session_data* sd,int flag,int nameid)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ clif_solved_charname(fd, sd->status.char_id, sd->status.name);
+ WFIFOHEAD(fd,packet_len(0x18f));
+ WFIFOW(fd, 0)=0x18f;
+ WFIFOW(fd, 2)=flag;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd, 4)=view;
+ else
+ WFIFOW(fd, 4)=nameid;
+ WFIFOSET(fd,packet_len(0x18f));
+}
+
+
+/// Initiates the pet taming process (ZC_START_CAPTURE).
+/// 019e
+void clif_catch_process(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x19e));
+ WFIFOW(fd,0)=0x19e;
+ WFIFOSET(fd,packet_len(0x19e));
+}
+
+
+/// Displays the result of a pet taming attempt (ZC_TRYCAPTURE_MONSTER).
+/// 01a0 <result>.B
+/// 0 = failure
+/// 1 = success
+void clif_pet_roulette(struct map_session_data *sd,int data)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1a0));
+ WFIFOW(fd,0)=0x1a0;
+ WFIFOB(fd,2)=data;
+ WFIFOSET(fd,packet_len(0x1a0));
+}
+
+
+/// Presents a list of pet eggs that can be hatched (ZC_PETEGG_LIST).
+/// 01a6 <packet len>.W { <index>.W }*
+void clif_sendegg(struct map_session_data *sd)
+{
+ int i,n=0,fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ if (battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m))
+ { //Disable pet hatching in GvG grounds during Guild Wars [Skotlex]
+ clif_displaymessage(fd, msg_txt(666));
+ return;
+ }
+ WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x1a6;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->inventory_data[i]->type!=IT_PETEGG ||
+ sd->status.inventory[i].amount<=0)
+ continue;
+ WFIFOW(fd,n*2+4)=i+2;
+ n++;
+ }
+ WFIFOW(fd,2)=4+n*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ sd->menuskill_id = SA_TAMINGMONSTER;
+ sd->menuskill_val = -1;
+}
+
+
+/// Sends a specific pet data update (ZC_CHANGESTATE_PET).
+/// 01a4 <type>.B <id>.L <data>.L
+/// type:
+/// 0 = pre-init (data = 0)
+/// 1 = intimacy (data = 0~4)
+/// 2 = hunger (data = 0~4)
+/// 3 = accessory
+/// 4 = performance (data = 1~3: normal, 4: special)
+/// 5 = hairstyle
+///
+/// If sd is null, the update is sent to nearby objects, otherwise it is sent only to that player.
+void clif_send_petdata(struct map_session_data* sd, struct pet_data* pd, int type, int param)
+{
+ uint8 buf[16];
+ nullpo_retv(pd);
+
+ WBUFW(buf,0) = 0x1a4;
+ WBUFB(buf,2) = type;
+ WBUFL(buf,3) = pd->bl.id;
+ WBUFL(buf,7) = param;
+ if (sd)
+ clif_send(buf, packet_len(0x1a4), &sd->bl, SELF);
+ else
+ clif_send(buf, packet_len(0x1a4), &pd->bl, AREA);
+}
+
+
+/// Pet's base data (ZC_PROPERTY_PET).
+/// 01a2 <name>.24B <renamed>.B <level>.W <hunger>.W <intimacy>.W <accessory id>.W <class>.W
+void clif_send_petstatus(struct map_session_data *sd)
+{
+ int fd;
+ struct s_pet *pet;
+
+ nullpo_retv(sd);
+ nullpo_retv(sd->pd);
+
+ fd=sd->fd;
+ pet = &sd->pd->pet;
+ WFIFOHEAD(fd,packet_len(0x1a2));
+ WFIFOW(fd,0)=0x1a2;
+ memcpy(WFIFOP(fd,2),pet->name,NAME_LENGTH);
+ WFIFOB(fd,26)=battle_config.pet_rename?0:pet->rename_flag;
+ WFIFOW(fd,27)=pet->level;
+ WFIFOW(fd,29)=pet->hungry;
+ WFIFOW(fd,31)=pet->intimate;
+ WFIFOW(fd,33)=pet->equip;
+#if PACKETVER >= 20081126
+ WFIFOW(fd,35)=pet->class_;
+#endif
+ WFIFOSET(fd,packet_len(0x1a2));
+}
+
+
+/// Notification about a pet's emotion/talk (ZC_PET_ACT).
+/// 01aa <id>.L <data>.L
+/// data:
+/// @see CZ_PET_ACT.
+void clif_pet_emotion(struct pet_data *pd,int param)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(pd);
+
+ memset(buf,0,packet_len(0x1aa));
+
+ WBUFW(buf,0)=0x1aa;
+ WBUFL(buf,2)=pd->bl.id;
+ if(param >= 100 && pd->petDB->talk_convert_class) {
+ if(pd->petDB->talk_convert_class < 0)
+ return;
+ else if(pd->petDB->talk_convert_class > 0) {
+ // replace mob_id component of talk/act data
+ param -= (pd->pet.class_ - 100)*100;
+ param += (pd->petDB->talk_convert_class - 100)*100;
+ }
+ }
+ WBUFL(buf,6)=param;
+
+ clif_send(buf,packet_len(0x1aa),&pd->bl,AREA);
+}
+
+
+/// Result of request to feed a pet (ZC_FEED_PET).
+/// 01a3 <result>.B <name id>.W
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_pet_food(struct map_session_data *sd,int foodid,int fail)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1a3));
+ WFIFOW(fd,0)=0x1a3;
+ WFIFOB(fd,2)=fail;
+ WFIFOW(fd,3)=foodid;
+ WFIFOSET(fd,packet_len(0x1a3));
+}
+
+
+/// Presents a list of skills that can be auto-spelled (ZC_AUTOSPELLLIST).
+/// 01cd { <skill id>.L }*7
+void clif_autospell(struct map_session_data *sd,uint16 skill_lv)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1cd));
+ WFIFOW(fd, 0)=0x1cd;
+
+ if(skill_lv>0 && pc_checkskill(sd,MG_NAPALMBEAT)>0)
+ WFIFOL(fd,2)= MG_NAPALMBEAT;
+ else
+ WFIFOL(fd,2)= 0x00000000;
+ if(skill_lv>1 && pc_checkskill(sd,MG_COLDBOLT)>0)
+ WFIFOL(fd,6)= MG_COLDBOLT;
+ else
+ WFIFOL(fd,6)= 0x00000000;
+ if(skill_lv>1 && pc_checkskill(sd,MG_FIREBOLT)>0)
+ WFIFOL(fd,10)= MG_FIREBOLT;
+ else
+ WFIFOL(fd,10)= 0x00000000;
+ if(skill_lv>1 && pc_checkskill(sd,MG_LIGHTNINGBOLT)>0)
+ WFIFOL(fd,14)= MG_LIGHTNINGBOLT;
+ else
+ WFIFOL(fd,14)= 0x00000000;
+ if(skill_lv>4 && pc_checkskill(sd,MG_SOULSTRIKE)>0)
+ WFIFOL(fd,18)= MG_SOULSTRIKE;
+ else
+ WFIFOL(fd,18)= 0x00000000;
+ if(skill_lv>7 && pc_checkskill(sd,MG_FIREBALL)>0)
+ WFIFOL(fd,22)= MG_FIREBALL;
+ else
+ WFIFOL(fd,22)= 0x00000000;
+ if(skill_lv>9 && pc_checkskill(sd,MG_FROSTDIVER)>0)
+ WFIFOL(fd,26)= MG_FROSTDIVER;
+ else
+ WFIFOL(fd,26)= 0x00000000;
+
+ WFIFOSET(fd,packet_len(0x1cd));
+ sd->menuskill_id = SA_AUTOSPELL;
+ sd->menuskill_val = skill_lv;
+}
+
+
+/// Devotion's visual effect (ZC_DEVOTIONLIST).
+/// 01cf <devoter id>.L { <devotee id>.L }*5 <max distance>.W
+void clif_devotion(struct block_list *src, struct map_session_data *tsd)
+{
+ unsigned char buf[56];
+ int i;
+
+ nullpo_retv(src);
+ memset(buf,0,packet_len(0x1cf));
+
+ WBUFW(buf,0) = 0x1cf;
+ WBUFL(buf,2) = src->id;
+ if( src->type == BL_MER )
+ {
+ struct mercenary_data *md = BL_CAST(BL_MER,src);
+ if( md && md->master && md->devotion_flag )
+ WBUFL(buf,6) = md->master->bl.id;
+
+ WBUFW(buf,26) = skill_get_range2(src, ML_DEVOTION, mercenary_checkskill(md, ML_DEVOTION));
+ }
+ else
+ {
+ struct map_session_data *sd = BL_CAST(BL_PC,src);
+ if( sd == NULL )
+ return;
+
+ for( i = 0; i < 5; i++ )
+ WBUFL(buf,6+4*i) = sd->devotion[i];
+ WBUFW(buf,26) = skill_get_range2(src, CR_DEVOTION, pc_checkskill(sd, CR_DEVOTION));
+ }
+
+ if( tsd )
+ clif_send(buf, packet_len(0x1cf), &tsd->bl, SELF);
+ else
+ clif_send(buf, packet_len(0x1cf), src, AREA);
+}
+
+/*==========================================
+ * Server tells clients nearby 'sd' (and himself) to display 'sd->spiritball' number of spiritballs on 'sd'
+ * Notifies clients in an area of an object's spirits.
+ * 01d0 <id>.L <amount>.W (ZC_SPIRITS)
+ * 01e1 <id>.L <amount>.W (ZC_SPIRITS2)
+ *------------------------------------------*/
+void clif_spiritball(struct block_list *bl) {
+ unsigned char buf[16];
+ TBL_PC *sd = BL_CAST(BL_PC,bl);
+ TBL_HOM *hd = BL_CAST(BL_HOM,bl);
+
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x1d0;
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = 0; //init to 0
+ switch(bl->type){
+ case BL_PC: WBUFW(buf, 6) = sd->spiritball; break;
+ case BL_HOM: WBUFW(buf, 6) = hd->homunculus.spiritball; break;
+ }
+ clif_send(buf, packet_len(0x1d0), bl, AREA);
+}
+
+
+/// Notifies clients in area of a character's combo delay (ZC_COMBODELAY).
+/// 01d2 <account id>.L <delay>.L
+void clif_combo_delay(struct block_list *bl,int wait)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0x1d2;
+ WBUFL(buf,2)=bl->id;
+ WBUFL(buf,6)=wait;
+ clif_send(buf,packet_len(0x1d2),bl,AREA);
+}
+
+
+/// Notifies clients in area that a character has blade-stopped another (ZC_BLADESTOP).
+/// 01d1 <src id>.L <dst id>.L <flag>.L
+/// flag:
+/// 0 = inactive
+/// 1 = active
+void clif_bladestop(struct block_list *src, int dst_id, int active)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(src);
+
+ WBUFW(buf,0)=0x1d1;
+ WBUFL(buf,2)=src->id;
+ WBUFL(buf,6)=dst_id;
+ WBUFL(buf,10)=active;
+
+ clif_send(buf,packet_len(0x1d1),src,AREA);
+}
+
+
+/// MVP effect (ZC_MVP).
+/// 010c <account id>.L
+void clif_mvp_effect(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x10c;
+ WBUFL(buf,2)=sd->bl.id;
+ clif_send(buf,packet_len(0x10c),&sd->bl,AREA);
+}
+
+
+/// MVP item reward message (ZC_MVP_GETTING_ITEM).
+/// 010a <name id>.W
+void clif_mvp_item(struct map_session_data *sd,int nameid)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x10a));
+ WFIFOW(fd,0)=0x10a;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd,2)=view;
+ else
+ WFIFOW(fd,2)=nameid;
+ WFIFOSET(fd,packet_len(0x10a));
+}
+
+
+/// MVP EXP reward message (ZC_MVP_GETTING_SPECIAL_EXP).
+/// 010b <exp>.L
+void clif_mvp_exp(struct map_session_data *sd, unsigned int exp)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x10b));
+ WFIFOW(fd,0)=0x10b;
+ WFIFOL(fd,2)=cap_value(exp,0,INT32_MAX);
+ WFIFOSET(fd,packet_len(0x10b));
+}
+
+
+/// Dropped MVP item reward message (ZC_THROW_MVPITEM).
+/// 010d
+///
+/// "You are the MVP, but cannot obtain the reward because
+/// you are overweight."
+void clif_mvp_noitem(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x10d));
+ WFIFOW(fd,0) = 0x10d;
+ WFIFOSET(fd,packet_len(0x10d));
+}
+
+
+/// Guild creation result (ZC_RESULT_MAKE_GUILD).
+/// 0167 <result>.B
+/// result:
+/// 0 = "Guild has been created."
+/// 1 = "You are already in a Guild."
+/// 2 = "That Guild Name already exists."
+/// 3 = "You need the neccessary item to create a Guild."
+void clif_guild_created(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x167));
+ WFIFOW(fd,0)=0x167;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x167));
+}
+
+
+/// Notifies the client that it is belonging to a guild (ZC_UPDATE_GDID).
+/// 016c <guild id>.L <emblem id>.L <mode>.L <ismaster>.B <inter sid>.L <guild name>.24B
+/// mode:
+/// &0x01 = allow invite
+/// &0x10 = allow expel
+void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g)
+{
+ int ps,fd;
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd=sd->fd;
+ ps=guild_getposition(g,sd);
+ WFIFOHEAD(fd,packet_len(0x16c));
+ WFIFOW(fd,0)=0x16c;
+ WFIFOL(fd,2)=g->guild_id;
+ WFIFOL(fd,6)=g->emblem_id;
+ WFIFOL(fd,10)=g->position[ps].mode;
+ WFIFOB(fd,14)=(bool)(sd->state.gmaster_flag==g);
+ WFIFOL(fd,15)=0; // InterSID (unknown purpose)
+ memcpy(WFIFOP(fd,19),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x16c));
+}
+
+
+/// Guild member login notice.
+/// 016d <account id>.L <char id>.L <status>.L (ZC_UPDATE_CHARSTAT)
+/// 01f2 <account id>.L <char id>.L <status>.L <gender>.W <hair style>.W <hair color>.W (ZC_UPDATE_CHARSTAT2)
+/// status:
+/// 0 = offline
+/// 1 = online
+void clif_guild_memberlogin_notice(struct guild *g,int idx,int flag)
+{
+ unsigned char buf[64];
+ struct map_session_data* sd;
+
+ nullpo_retv(g);
+
+ WBUFW(buf, 0)=0x1f2;
+ WBUFL(buf, 2)=g->member[idx].account_id;
+ WBUFL(buf, 6)=g->member[idx].char_id;
+ WBUFL(buf,10)=flag;
+
+ if( ( sd = g->member[idx].sd ) != NULL )
+ {
+ WBUFW(buf,14) = sd->status.sex;
+ WBUFW(buf,16) = sd->status.hair;
+ WBUFW(buf,18) = sd->status.hair_color;
+ clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD_WOS);
+ }
+ else if( ( sd = guild_getavailablesd(g) ) != NULL )
+ {
+ WBUFW(buf,14) = 0;
+ WBUFW(buf,16) = 0;
+ WBUFW(buf,18) = 0;
+ clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD);
+ }
+}
+
+// Function `clif_guild_memberlogin_notice` sends info about
+// logins and logouts of a guild member to the rest members.
+// But at the 1st time (after a player login or map changing)
+// the client won't show the message.
+// So I suggest use this function for sending "first-time-info"
+// to some player on entering the game or changing location.
+// At next time the client would always show the message.
+// The function sends all the statuses in the single packet
+// to economize traffic. [LuzZza]
+void clif_guild_send_onlineinfo(struct map_session_data *sd)
+{
+ struct guild *g;
+ unsigned char buf[14*128];
+ int i, count=0, p_len;
+
+ nullpo_retv(sd);
+
+ p_len = packet_len(0x16d);
+
+ if(!(g = guild_search(sd->status.guild_id)))
+ return;
+
+ for(i=0; i<g->max_member; i++) {
+
+ if(g->member[i].account_id > 0 &&
+ g->member[i].account_id != sd->status.account_id) {
+
+ WBUFW(buf,count*p_len) = 0x16d;
+ WBUFL(buf,count*p_len+2) = g->member[i].account_id;
+ WBUFL(buf,count*p_len+6) = g->member[i].char_id;
+ WBUFL(buf,count*p_len+10) = g->member[i].online;
+ count++;
+ }
+ }
+
+ clif_send(buf, p_len*count, &sd->bl, SELF);
+}
+
+
+/// Bitmask of enabled guild window tabs (ZC_ACK_GUILD_MENUINTERFACE).
+/// 014e <menu flag>.L
+/// menu flag:
+/// 0x00 = Basic Info (always on)
+/// &0x01 = Member manager
+/// &0x02 = Positions
+/// &0x04 = Skills
+/// &0x10 = Expulsion list
+/// &0x40 = Unknown (GMENUFLAG_ALLGUILDLIST)
+/// &0x80 = Notice
+void clif_guild_masterormember(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x14e));
+ WFIFOW(fd,0) = 0x14e;
+ WFIFOL(fd,2) = (sd->state.gmaster_flag) ? 0xd7 : 0x57;
+ WFIFOSET(fd,packet_len(0x14e));
+}
+
+
+/// Guild basic information (Territories [Valaris])
+/// 0150 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B (ZC_GUILD_INFO)
+/// 01b6 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B <zeny>.L (ZC_GUILD_INFO2)
+void clif_guild_basicinfo(struct map_session_data *sd) {
+ int fd;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x1b6));
+ WFIFOW(fd, 0)=0x1b6;//0x150;
+ WFIFOL(fd, 2)=g->guild_id;
+ WFIFOL(fd, 6)=g->guild_lv;
+ WFIFOL(fd,10)=g->connect_member;
+ WFIFOL(fd,14)=g->max_member;
+ WFIFOL(fd,18)=g->average_lv;
+ WFIFOL(fd,22)=(uint32)cap_value(g->exp,0,INT32_MAX);
+ WFIFOL(fd,26)=g->next_exp;
+ WFIFOL(fd,30)=0; // Tax Points
+ WFIFOL(fd,34)=0; // Honor: (left) Vulgar [-100,100] Famed (right)
+ WFIFOL(fd,38)=0; // Virtue: (down) Wicked [-100,100] Righteous (up)
+ WFIFOL(fd,42)=g->emblem_id;
+ memcpy(WFIFOP(fd,46),g->name, NAME_LENGTH);
+ memcpy(WFIFOP(fd,70),g->master, NAME_LENGTH);
+
+ safestrncpy((char*)WFIFOP(fd,94),msg_txt(300+guild_checkcastles(g)),16); // "'N' castles"
+ WFIFOL(fd,110) = 0; // zeny
+
+ WFIFOSET(fd,packet_len(0x1b6));
+}
+
+
+/// Guild alliance and opposition list (ZC_MYGUILD_BASIC_INFO).
+/// 014c <packet len>.W { <relation>.L <guild id>.L <guild name>.24B }*
+void clif_guild_allianceinfo(struct map_session_data *sd)
+{
+ int fd,i,c;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_GUILDALLIANCE * 32 + 4);
+ WFIFOW(fd, 0)=0x14c;
+ for(i=c=0;i<MAX_GUILDALLIANCE;i++){
+ struct guild_alliance *a=&g->alliance[i];
+ if(a->guild_id>0){
+ WFIFOL(fd,c*32+4)=a->opposition;
+ WFIFOL(fd,c*32+8)=a->guild_id;
+ memcpy(WFIFOP(fd,c*32+12),a->name,NAME_LENGTH);
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*32+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Guild member manager information (ZC_MEMBERMGR_INFO).
+/// 0154 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B }*
+/// state:
+/// 0 = offline
+/// 1 = online
+/// memo:
+/// probably member's self-introduction (unused, no client UI/packets for editing it)
+void clif_guild_memberlist(struct map_session_data *sd)
+{
+ int fd;
+ int i,c;
+ struct guild *g;
+ nullpo_retv(sd);
+
+ if( (fd = sd->fd) == 0 )
+ return;
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ WFIFOHEAD(fd, g->max_member * 104 + 4);
+ WFIFOW(fd, 0)=0x154;
+ for(i=0,c=0;i<g->max_member;i++){
+ struct guild_member *m=&g->member[i];
+ if(m->account_id==0)
+ continue;
+ WFIFOL(fd,c*104+ 4)=m->account_id;
+ WFIFOL(fd,c*104+ 8)=m->char_id;
+ WFIFOW(fd,c*104+12)=m->hair;
+ WFIFOW(fd,c*104+14)=m->hair_color;
+ WFIFOW(fd,c*104+16)=m->gender;
+ WFIFOW(fd,c*104+18)=m->class_;
+ WFIFOW(fd,c*104+20)=m->lv;
+ WFIFOL(fd,c*104+22)=(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); //[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++;
+ }
+ WFIFOW(fd, 2)=c*104+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Guild position name information (ZC_POSITION_ID_NAME_INFO).
+/// 0166 <packet len>.W { <position id>.L <position name>.24B }*
+void clif_guild_positionnamelist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 28 + 4);
+ WFIFOW(fd, 0)=0x166;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ WFIFOL(fd,i*28+4)=i;
+ memcpy(WFIFOP(fd,i*28+8),g->position[i].name,NAME_LENGTH);
+ }
+ WFIFOW(fd,2)=i*28+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Guild position information (ZC_POSITION_INFO).
+/// 0160 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L }*
+/// mode:
+/// &0x01 = allow invite
+/// &0x10 = allow expel
+/// ranking:
+/// TODO
+void clif_guild_positioninfolist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 16 + 4);
+ WFIFOW(fd, 0)=0x160;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ struct guild_position *p=&g->position[i];
+ WFIFOL(fd,i*16+ 4)=i;
+ WFIFOL(fd,i*16+ 8)=p->mode;
+ WFIFOL(fd,i*16+12)=i;
+ WFIFOL(fd,i*16+16)=p->exp_mode;
+ }
+ WFIFOW(fd, 2)=i*16+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notifies clients in a guild about updated position information (ZC_ACK_CHANGE_GUILD_POSITIONINFO).
+/// 0174 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <position name>.24B }*
+/// mode:
+/// &0x01 = allow invite
+/// &0x10 = allow expel
+/// ranking:
+/// TODO
+void clif_guild_positionchanged(struct guild *g,int idx)
+{
+ // FIXME: This packet is intended to update the clients after a
+ // commit of position info changes, not sending one packet per
+ // position.
+ struct map_session_data *sd;
+ unsigned char buf[128];
+
+ nullpo_retv(g);
+
+ WBUFW(buf, 0)=0x174;
+ WBUFW(buf, 2)=44; // packet len
+ // GUILD_REG_POSITION_INFO{
+ WBUFL(buf, 4)=idx;
+ WBUFL(buf, 8)=g->position[idx].mode;
+ WBUFL(buf,12)=idx;
+ WBUFL(buf,16)=g->position[idx].exp_mode;
+ memcpy(WBUFP(buf,20),g->position[idx].name,NAME_LENGTH);
+ // }*
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+}
+
+
+/// Notifies clients in a guild about updated member position assignments (ZC_ACK_REQ_CHANGE_MEMBERS).
+/// 0156 <packet len>.W { <account id>.L <char id>.L <position id>.L }*
+void clif_guild_memberpositionchanged(struct guild *g,int idx)
+{
+ // FIXME: This packet is intended to update the clients after a
+ // commit of member position assignment changes, not sending one
+ // packet per position.
+ struct map_session_data *sd;
+ unsigned char buf[64];
+
+ nullpo_retv(g);
+
+ WBUFW(buf, 0)=0x156;
+ WBUFW(buf, 2)=16; // packet len
+ // MEMBER_POSITION_INFO{
+ WBUFL(buf, 4)=g->member[idx].account_id;
+ WBUFL(buf, 8)=g->member[idx].char_id;
+ WBUFL(buf,12)=g->member[idx].position;
+ // }*
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+}
+
+
+/// Sends emblems bitmap data to the client that requested it (ZC_GUILD_EMBLEM_IMG).
+/// 0152 <packet len>.W <guild id>.L <emblem id>.L <emblem data>.?B
+void clif_guild_emblem(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd = sd->fd;
+ if( g->emblem_len <= 0 )
+ return;
+
+ WFIFOHEAD(fd,g->emblem_len+12);
+ WFIFOW(fd,0)=0x152;
+ WFIFOW(fd,2)=g->emblem_len+12;
+ WFIFOL(fd,4)=g->guild_id;
+ WFIFOL(fd,8)=g->emblem_id;
+ memcpy(WFIFOP(fd,12),g->emblem_data,g->emblem_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Sends update of the guild id/emblem id to everyone in the area (ZC_CHANGE_GUILD).
+/// 01b4 <id>.L <guild id>.L <emblem id>.W
+void clif_guild_emblem_area(struct block_list* bl)
+{
+ uint8 buf[12];
+
+ nullpo_retv(bl);
+
+ // TODO this packet doesn't force the update of ui components that have the emblem visible
+ // (emblem in the flag npcs and emblem over the head in agit maps) [FlavioJS]
+ WBUFW(buf,0) = 0x1b4;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = status_get_guild_id(bl);
+ WBUFW(buf,10) = status_get_emblem_id(bl);
+ clif_send(buf, 12, bl, AREA_WOS);
+}
+
+
+/// Sends guild skills (ZC_GUILD_SKILLINFO).
+/// 0162 <packet len>.W <skill points>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B }*
+void clif_guild_skillinfo(struct map_session_data* sd)
+{
+ int fd;
+ struct guild* g;
+ int i,c;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, 6 + MAX_GUILDSKILL*37);
+ WFIFOW(fd,0) = 0x0162;
+ WFIFOW(fd,4) = g->skill_point;
+ for(i = 0, c = 0; i < MAX_GUILDSKILL; i++)
+ {
+ if(g->skill[i].id > 0 && guild_check_skill_require(g, g->skill[i].id))
+ {
+ int id = g->skill[i].id;
+ int p = 6 + c*37;
+ WFIFOW(fd,p+0) = id;
+ WFIFOL(fd,p+2) = skill_get_inf(id);
+ WFIFOW(fd,p+6) = g->skill[i].lv;
+ WFIFOW(fd,p+8) = skill_get_sp(id, g->skill[i].lv);
+ WFIFOW(fd,p+10) = skill_get_range(id, g->skill[i].lv);
+ safestrncpy((char*)WFIFOP(fd,p+12), skill_get_name(id), NAME_LENGTH);
+ WFIFOB(fd,p+36)= (g->skill[i].lv < guild_skill_get_max(id) && sd == g->member[0].sd) ? 1 : 0;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = 6 + c*37;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Sends guild notice to client (ZC_GUILD_NOTICE).
+/// 016f <subject>.60B <notice>.120B
+void clif_guild_notice(struct map_session_data* sd, struct guild* g)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd = sd->fd;
+
+ if ( !session_isActive(fd) )
+ return;
+
+ if(g->mes1[0] == '\0' && g->mes2[0] == '\0')
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x16f));
+ WFIFOW(fd,0) = 0x16f;
+ memcpy(WFIFOP(fd,2), g->mes1, MAX_GUILDMES1);
+ memcpy(WFIFOP(fd,62), g->mes2, MAX_GUILDMES2);
+ WFIFOSET(fd,packet_len(0x16f));
+}
+
+
+/// Guild invite (ZC_REQ_JOIN_GUILD).
+/// 016a <guild id>.L <guild name>.24B
+void clif_guild_invite(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x16a));
+ WFIFOW(fd,0)=0x16a;
+ WFIFOL(fd,2)=g->guild_id;
+ memcpy(WFIFOP(fd,6),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x16a));
+}
+
+
+/// Reply to invite request (ZC_ACK_REQ_JOIN_GUILD).
+/// 0169 <answer>.B
+/// answer:
+/// 0 = Already in guild.
+/// 1 = Offer rejected.
+/// 2 = Offer accepted.
+/// 3 = Guild full.
+void clif_guild_inviteack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x169));
+ WFIFOW(fd,0)=0x169;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x169));
+}
+
+
+/// Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD).
+/// 015a <char name>.24B <reason>.40B
+void clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes)
+{
+ unsigned char buf[128];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf, 0)=0x15a;
+ memcpy(WBUFP(buf, 2),name,NAME_LENGTH);
+ memcpy(WBUFP(buf,26),mes,40);
+ clif_send(buf,packet_len(0x15a),&sd->bl,GUILD_NOBG);
+}
+
+
+/// Notifies clients of a guild of an expelled member.
+/// 015c <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD)
+/// 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO)
+void clif_guild_expulsion(struct map_session_data* sd, const char* name, const char* mes, int account_id)
+{
+ unsigned char buf[128];
+#if PACKETVER < 20100803
+ const unsigned short cmd = 0x15c;
+#else
+ const unsigned short cmd = 0x839;
+#endif
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0) = cmd;
+ safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH);
+ safestrncpy((char*)WBUFP(buf,26), mes, 40);
+#if PACKETVER < 20100803
+ memset(WBUFP(buf,66), 0, NAME_LENGTH); // account name (not used for security reasons)
+#endif
+ clif_send(buf, packet_len(cmd), &sd->bl, GUILD_NOBG);
+}
+
+
+/// Guild expulsion list (ZC_BAN_LIST).
+/// 0163 <packet len>.W { <char name>.24B <account name>.24B <reason>.40B }*
+/// 0163 <packet len>.W { <char name>.24B <reason>.40B }* (PACKETVER >= 20100803)
+void clif_guild_expulsionlist(struct map_session_data* sd)
+{
+#if PACKETVER < 20100803
+ const int offset = NAME_LENGTH*2+40;
+#else
+ const int offset = NAME_LENGTH+40;
+#endif
+ int fd, i, c = 0;
+ struct guild* g;
+
+ nullpo_retv(sd);
+
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,4 + MAX_GUILDEXPULSION * offset);
+ WFIFOW(fd,0) = 0x163;
+
+ for( i = 0; i < MAX_GUILDEXPULSION; i++ )
+ {
+ struct guild_expulsion* e = &g->expulsion[i];
+
+ if( e->account_id > 0 )
+ {
+ memcpy(WFIFOP(fd,4 + c*offset), e->name, NAME_LENGTH);
+#if PACKETVER < 20100803
+ memset(WFIFOP(fd,4 + c*offset+24), 0, NAME_LENGTH); // account name (not used for security reasons)
+ memcpy(WFIFOP(fd,4 + c*offset+48), e->mes, 40);
+#else
+ memcpy(WFIFOP(fd,4 + c*offset+24), e->mes, 40);
+#endif
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = 4 + c*offset;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// 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;
+ uint8 buf[256];
+
+ 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 + 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);
+}
+
+
+/*==========================================
+ * Server tells client 'sd' that his guild skill 'skill_id' gone to level 'lv'
+ *------------------------------------------*/
+int clif_guild_skillup(struct map_session_data *sd,uint16 skill_id,int lv)
+{// TODO: Merge with clif_skillup (same packet).
+ int fd;
+
+ nullpo_ret(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,11);
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_id,lv);
+ WFIFOW(fd,8) = skill_get_range(skill_id,lv);
+ WFIFOB(fd,10) = 1;
+ WFIFOSET(fd,11);
+ return 0;
+}
+
+
+/// Request for guild alliance (ZC_REQ_ALLY_GUILD).
+/// 0171 <inviter account id>.L <guild name>.24B
+void clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x171));
+ WFIFOW(fd,0)=0x171;
+ WFIFOL(fd,2)=account_id;
+ memcpy(WFIFOP(fd,6),name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x171));
+}
+
+
+/// Notifies the client about the result of a alliance request (ZC_ACK_REQ_ALLY_GUILD).
+/// 0173 <answer>.B
+/// answer:
+/// 0 = Already allied.
+/// 1 = You rejected the offer.
+/// 2 = You accepted the offer.
+/// 3 = They have too any alliances.
+/// 4 = You have too many alliances.
+/// 5 = Alliances are disabled.
+void clif_guild_allianceack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x173));
+ WFIFOW(fd,0)=0x173;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x173));
+}
+
+
+/// Notifies the client that a alliance or opposition has been removed (ZC_DELETE_RELATED_GUILD).
+/// 0184 <other guild id>.L <relation>.L
+/// relation:
+/// 0 = Ally
+/// 1 = Enemy
+void clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ if (fd <= 0)
+ return;
+ WFIFOHEAD(fd,packet_len(0x184));
+ WFIFOW(fd,0)=0x184;
+ WFIFOL(fd,2)=guild_id;
+ WFIFOL(fd,6)=flag;
+ WFIFOSET(fd,packet_len(0x184));
+}
+
+
+/// Notifies the client about the result of a opposition request (ZC_ACK_REQ_HOSTILE_GUILD).
+/// 0181 <result>.B
+/// result:
+/// 0 = Antagonist has been set.
+/// 1 = Guild has too many Antagonists.
+/// 2 = Already set as an Antagonist.
+/// 3 = Antagonists are disabled.
+void clif_guild_oppositionack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x181));
+ WFIFOW(fd,0)=0x181;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x181));
+}
+
+
+/// Adds alliance or opposition (ZC_ADD_RELATED_GUILD).
+/// 0185 <relation>.L <guild id>.L <guild name>.24B
+/*
+void clif_guild_allianceadded(struct guild *g,int idx)
+{
+ unsigned char buf[64];
+ WBUFW(buf,0)=0x185;
+ WBUFL(buf,2)=g->alliance[idx].opposition;
+ WBUFL(buf,6)=g->alliance[idx].guild_id;
+ memcpy(WBUFP(buf,10),g->alliance[idx].name,NAME_LENGTH);
+ clif_send(buf,packet_len(0x185),guild_getavailablesd(g),GUILD);
+}
+*/
+
+
+/// Notifies the client about the result of a guild break (ZC_ACK_DISORGANIZE_GUILD_RESULT).
+/// 015e <reason>.L
+/// 0 = success
+/// 1 = invalid key (guild name, @see clif_parse_GuildBreak)
+/// 2 = there are still members in the guild
+void clif_guild_broken(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x15e));
+ WFIFOW(fd,0)=0x15e;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x15e));
+}
+
+
+/// Displays emotion on an object (ZC_EMOTION).
+/// 00c0 <id>.L <type>.B
+/// type:
+/// enum emotion_type
+void clif_emotion(struct block_list *bl,int type)
+{
+ unsigned char buf[8];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0xc0;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ clif_send(buf,packet_len(0xc0),bl,AREA);
+}
+
+
+/// Displays the contents of a talkiebox trap (ZC_TALKBOX_CHATCONTENTS).
+/// 0191 <id>.L <contents>.80B
+void clif_talkiebox(struct block_list* bl, const char* talkie)
+{
+ unsigned char buf[MESSAGE_SIZE+6];
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x191;
+ WBUFL(buf,2) = bl->id;
+ safestrncpy((char*)WBUFP(buf,6),talkie,MESSAGE_SIZE);
+ clif_send(buf,packet_len(0x191),bl,AREA);
+}
+
+
+/// Displays wedding effect centered on an object (ZC_CONGRATULATION).
+/// 01ea <id>.L
+void clif_wedding_effect(struct block_list *bl)
+{
+ unsigned char buf[6];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1ea;
+ WBUFL(buf,2) = bl->id;
+ clif_send(buf, packet_len(0x1ea), bl, AREA);
+}
+
+
+/// Notifies the client of the name of the partner character (ZC_COUPLENAME).
+/// 01e6 <partner name>.24B
+void clif_callpartner(struct map_session_data *sd)
+{
+ unsigned char buf[26];
+ const char *p;
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0) = 0x1e6;
+
+ if( sd->status.partner_id )
+ {
+ if( ( p = map_charid2nick(sd->status.partner_id) ) != NULL )
+ {
+ memcpy(WBUFP(buf,2), p, NAME_LENGTH);
+ }
+ else
+ {
+ WBUFB(buf,2) = 0;
+ }
+ }
+ else
+ {// Send zero-length name if no partner, to initialize the client buffer.
+ WBUFB(buf,2) = 0;
+ }
+
+ clif_send(buf, packet_len(0x1e6), &sd->bl, AREA);
+}
+
+
+/// Initiates the partner "taming" process [DracoRPG] (ZC_START_COUPLE).
+/// 01e4
+/// This packet while still implemented by the client is no longer being officially used.
+/*
+void clif_marriage_process(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1e4));
+ WFIFOW(fd,0)=0x1e4;
+ WFIFOSET(fd,packet_len(0x1e4));
+}
+*/
+
+
+/// Notice of divorce (ZC_DIVORCE).
+/// 0205 <partner name>.24B
+void clif_divorced(struct map_session_data* sd, const char* name)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x205));
+ WFIFOW(fd,0)=0x205;
+ memcpy(WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len(0x205));
+}
+
+
+/// Marriage proposal (ZC_REQ_COUPLE).
+/// 01e2 <account id>.L <char id>.L <char name>.24B
+/// This packet while still implemented by the client is no longer being officially used.
+/*
+void clif_marriage_proposal(int fd, struct map_session_data *sd, struct map_session_data* ssd)
+{
+ nullpo_retv(sd);
+
+ WFIFOHEAD(fd,packet_len(0x1e2));
+ WFIFOW(fd,0) = 0x1e2;
+ WFIFOL(fd,2) = ssd->status.account_id;
+ WFIFOL(fd,6) = ssd->status.char_id;
+ safestrncpy((char*)WFIFOP(fd,10), ssd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len(0x1e2));
+}
+*/
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len)
+{
+ clif_disp_message(&sd->bl, mes, len, SELF);
+}
+
+/*==========================================
+ * Displays a message using the guild-chat colors to the specified targets. [Skotlex]
+ *------------------------------------------*/
+void clif_disp_message(struct block_list* src, const char* mes, int len, enum send_target target)
+{
+ 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;
+ safestrncpy((char*)WBUFP(buf,4), mes, len+1);
+ clif_send(buf, WBUFW(buf,2), src, target);
+}
+
+
+/// Notifies the client about the result of a request to disconnect another player (ZC_ACK_DISCONNECT_CHARACTER).
+/// 00cd <result>.L (unknown packet version or invalid information at packet_len_table)
+/// 00cd <result>.B
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_GM_kickack(struct map_session_data *sd, int id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xcd));
+ WFIFOW(fd,0) = 0xcd;
+ WFIFOB(fd,2) = id; // FIXME: this is not account id
+ WFIFOSET(fd, packet_len(0xcd));
+}
+
+
+void clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ int fd = tsd->fd;
+
+ if( fd > 0 )
+ clif_authfail_fd(fd, 15);
+ else
+ map_quit(tsd);
+
+ if( sd )
+ clif_GM_kickack(sd,tsd->status.account_id);
+}
+
+
+/// Displays various manner-related status messages (ZC_ACK_GIVE_MANNER_POINT).
+/// 014a <result>.L
+/// result:
+/// 0 = "A manner point has been successfully aligned."
+/// 1 = MP_FAILURE_EXHAUST
+/// 2 = MP_FAILURE_ALREADY_GIVING
+/// 3 = "Chat Block has been applied by GM due to your ill-mannerous action."
+/// 4 = "Automated Chat Block has been applied due to Anti-Spam System."
+/// 5 = "You got a good point from %s."
+void clif_manner_message(struct map_session_data* sd, uint32 type)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x14a));
+ WFIFOW(fd,0) = 0x14a;
+ WFIFOL(fd,2) = type;
+ WFIFOSET(fd, packet_len(0x14a));
+}
+
+
+/// Followup to 0x14a type 3/5, informs who did the manner adjustment action (ZC_NOTIFY_MANNER_POINT_GIVEN).
+/// 014b <type>.B <GM name>.24B
+/// type:
+/// 0 = positive (unmute)
+/// 1 = negative (mute)
+void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, uint8 type)
+{
+ int fd;
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+
+ fd = tsd->fd;
+ WFIFOHEAD(fd,packet_len(0x14b));
+ WFIFOW(fd,0) = 0x14b;
+ WFIFOB(fd,2) = type;
+ safestrncpy((char*)WFIFOP(fd,3), sd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len(0x14b));
+}
+
+
+/// Notifies the client about the result of a request to allow/deny whispers from a player (ZC_SETTING_WHISPER_PC).
+/// 00d1 <type>.B <result>.B
+/// type:
+/// 0 = /ex (deny)
+/// 1 = /in (allow)
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = too many blocks
+void clif_wisexin(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retv(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));
+}
+
+/// Notifies the client about the result of a request to allow/deny whispers from anyone (ZC_SETTING_WHISPER_STATE).
+/// 00d2 <type>.B <result>.B
+/// type:
+/// 0 = /exall (deny)
+/// 1 = /inall (allow)
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_wisall(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retv(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));
+}
+
+
+/// Play a BGM! [Rikter/Yommy] (ZC_PLAY_NPC_BGM).
+/// 07fe <bgm>.24B
+void clif_playBGM(struct map_session_data* sd, const char* name)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x7fe));
+ WFIFOW(fd,0) = 0x7fe;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x7fe));
+}
+
+
+/// Plays/stops a wave sound (ZC_SOUND).
+/// 01d3 <file name>.24B <act>.B <term>.L <npc id>.L
+/// file name:
+/// relative to data\wav
+/// act:
+/// 0 = play (once)
+/// 1 = play (repeat, does not work)
+/// 2 = stops all sound instances of file name (does not work)
+/// term:
+/// unknown purpose, only relevant to act = 1
+/// npc id:
+/// The accustic direction of the sound is determined by the
+/// relative position of the NPC to the player (3D sound).
+void clif_soundeffect(struct map_session_data* sd, struct block_list* bl, const char* name, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(bl);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1d3));
+ WFIFOW(fd,0) = 0x1d3;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOB(fd,26) = type;
+ WFIFOL(fd,27) = 0;
+ WFIFOL(fd,31) = bl->id;
+ WFIFOSET(fd,packet_len(0x1d3));
+}
+
+void clif_soundeffectall(struct block_list* bl, const char* name, int type, enum send_target coverage)
+{
+ unsigned char buf[40];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1d3;
+ safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH);
+ WBUFB(buf,26) = type;
+ WBUFL(buf,27) = 0;
+ WBUFL(buf,31) = bl->id;
+ clif_send(buf, packet_len(0x1d3), bl, coverage);
+}
+
+
+/// Displays special effects (npcs, weather, etc) [Valaris] (ZC_NOTIFY_EFFECT2).
+/// 01f3 <id>.L <effect id>.L
+/// effect id:
+/// @see doc/effect_list.txt
+void clif_specialeffect(struct block_list* bl, int type, enum send_target target)
+{
+ unsigned char buf[24];
+
+ nullpo_retv(bl);
+
+ memset(buf, 0, packet_len(0x1f3));
+
+ WBUFW(buf,0) = 0x1f3;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf, packet_len(0x1f3), bl, target);
+
+ if (disguised(bl)) {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x1f3), bl, SELF);
+ }
+}
+
+void clif_specialeffect_single(struct block_list* bl, int type, int fd)
+{
+ WFIFOHEAD(fd,10);
+ WFIFOW(fd,0) = 0x1f3;
+ WFIFOL(fd,2) = bl->id;
+ WFIFOL(fd,6) = type;
+ WFIFOSET(fd,10);
+}
+
+
+/// Notifies clients of an special/visual effect that accepts an value (ZC_NOTIFY_EFFECT3).
+/// 0284 <id>.L <effect id>.L <num data>.L
+/// effect id:
+/// @see doc/effect_list.txt
+/// num data:
+/// effect-dependent value
+void clif_specialeffect_value(struct block_list* bl, int effect_id, int num, send_target target)
+{
+ uint8 buf[14];
+
+ WBUFW(buf,0) = 0x284;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = effect_id;
+ WBUFL(buf,10) = num;
+
+ clif_send(buf, packet_len(0x284), bl, target);
+
+ if( disguised(bl) )
+ {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x284), bl, SELF);
+ }
+}
+// Modification of clif_messagecolor to send colored messages to players to chat log only (doesn't display overhead)
+/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B
+int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg) {
+ unsigned short msg_len = strlen(msg) + 1;
+
+ WFIFOHEAD(sd->fd,msg_len + 12);
+ WFIFOW(sd->fd,0) = 0x2C1;
+ WFIFOW(sd->fd,2) = msg_len + 12;
+ WFIFOL(sd->fd,4) = 0;
+ WFIFOL(sd->fd,8) = color_table[color];
+ safestrncpy((char*)WFIFOP(sd->fd,12), msg, msg_len);
+ clif_send(WFIFOP(sd->fd,0), WFIFOW(sd->fd,2), &sd->bl, SELF);
+
+ return 0;
+}
+
+/// Monster/NPC color chat [SnakeDrak] (ZC_NPC_CHAT).
+/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B
+void clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg) {
+ unsigned short msg_len = strlen(msg) + 1;
+ uint8 buf[256];
+ color = (color & 0x0000FF) << 16 | (color & 0x00FF00) | (color & 0xFF0000) >> 16; // RGB to BGR
+
+ nullpo_retv(bl);
+
+ if( msg_len > sizeof(buf)-12 )
+ {
+ ShowWarning("clif_messagecolor: Truncating too long message '%s' (len=%u).\n", msg, msg_len);
+ msg_len = sizeof(buf)-12;
+ }
+
+ WBUFW(buf,0) = 0x2C1;
+ WBUFW(buf,2) = msg_len + 12;
+ WBUFL(buf,4) = bl->id;
+ WBUFL(buf,8) = color;
+ memcpy(WBUFP(buf,12), msg, msg_len);
+
+ clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC);
+}
+
+/// Public chat message [Valaris] (ZC_NOTIFY_CHAT).
+/// 008d <packet len>.W <id>.L <message>.?B
+void clif_message(struct block_list* bl, const char* msg) {
+ unsigned short msg_len = strlen(msg) + 1;
+ uint8 buf[256];
+ nullpo_retv(bl);
+
+ if( msg_len > sizeof(buf)-8 ) {
+ ShowWarning("clif_message: Truncating too long message '%s' (len=%u).\n", msg, msg_len);
+ msg_len = sizeof(buf)-8;
+ }
+
+ WBUFW(buf,0) = 0x8d;
+ WBUFW(buf,2) = msg_len + 8;
+ WBUFL(buf,4) = bl->id;
+ safestrncpy((char*)WBUFP(buf,8), msg, msg_len);
+
+ clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC);
+}
+
+// refresh the client's screen, getting rid of any effects
+void clif_refresh(struct map_session_data *sd)
+{
+ int i;
+ nullpo_retv(sd);
+
+ clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y);
+ clif_inventorylist(sd);
+ if(pc_iscarton(sd)) {
+ clif_cartlist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+ clif_updatestatus(sd,SP_WEIGHT);
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+ if (sd->spiritball)
+ clif_spiritball_single(sd->fd, sd);
+ for(i = 1; i < 5; i++){
+ if( sd->talisman[i] > 0 )
+ clif_talisman_single(sd->fd, sd, i);
+ }
+ if (sd->vd.cloth_color)
+ clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
+ if(merc_is_hom_active(sd->hd))
+ clif_send_homdata(sd,SP_ACK,0);
+ if( sd->md ) {
+ clif_mercenary_info(sd);
+ clif_mercenary_skillblock(sd);
+ }
+ if( sd->ed )
+ clif_elemental_info(sd);
+ map_foreachinrange(clif_getareachar,&sd->bl,AREA_SIZE,BL_ALL,sd);
+ clif_weather_check(sd);
+ if( sd->chatID )
+ chat_leavechat(sd,0);
+ if( sd->state.vending )
+ clif_openvending(sd, sd->bl.id, sd->vending);
+ if( pc_issit(sd) )
+ clif_sitting(&sd->bl); // FIXME: just send to self, not area
+ if( pc_isdead(sd) ) // When you refresh, resend the death packet.
+ clif_clearunit_single(sd->bl.id,CLR_DEAD,sd->fd);
+ else
+ clif_changed_dir(&sd->bl, SELF);
+
+ // unlike vending, resuming buyingstore crashes the client.
+ buyingstore_close(sd);
+
+ mail_clear(sd);
+}
+
+
+/// Updates the object's (bl) name on client.
+/// 0095 <id>.L <char name>.24B (ZC_ACK_REQNAME)
+/// 0195 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B (ZC_ACK_REQNAMEALL)
+void clif_charnameack (int fd, struct block_list *bl)
+{
+ unsigned char buf[103];
+ int cmd = 0x95, i, ps = -1;
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = bl->id;
+
+ switch( bl->type )
+ {
+ case BL_PC:
+ {
+ struct map_session_data *ssd = (struct map_session_data *)bl;
+ struct party_data *p = NULL;
+ struct guild *g = NULL;
+
+ //Requesting your own "shadow" name. [Skotlex]
+ if (ssd->fd == fd && ssd->disguise)
+ WBUFL(buf,2) = -bl->id;
+
+ if( ssd->fakename[0] )
+ {
+ WBUFW(buf, 0) = cmd = 0x195;
+ memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH);
+ WBUFB(buf,30) = WBUFB(buf,54) = WBUFB(buf,78) = 0;
+ break;
+ }
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if( ssd->status.party_id )
+ {
+ p = party_search(ssd->status.party_id);
+ }
+ if( ssd->status.guild_id )
+ {
+ if( ( g = guild_search(ssd->status.guild_id) ) != NULL )
+ {
+ ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id);
+ if( i < g->max_member ) ps = g->member[i].position;
+ }
+ }
+
+ if( !battle_config.display_party_name && g == NULL )
+ {// do not display party unless the player is also in a guild
+ p = NULL;
+ }
+
+ if (p == NULL && g == NULL)
+ break;
+
+ WBUFW(buf, 0) = cmd = 0x195;
+ if (p)
+ memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if (g && ps >= 0 && ps < MAX_GUILDPOSITION)
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ } else { //Assume no guild.
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ }
+ break;
+ //[blackhole89]
+ case BL_HOM:
+ memcpy(WBUFP(buf,6), ((TBL_HOM*)bl)->homunculus.name, NAME_LENGTH);
+ break;
+ case BL_MER:
+ memcpy(WBUFP(buf,6), ((TBL_MER*)bl)->db->name, NAME_LENGTH);
+ break;
+ case BL_PET:
+ memcpy(WBUFP(buf,6), ((TBL_PET*)bl)->pet.name, NAME_LENGTH);
+ break;
+ case BL_NPC:
+ memcpy(WBUFP(buf,6), ((TBL_NPC*)bl)->name, NAME_LENGTH);
+ break;
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retv(md);
+
+ memcpy(WBUFP(buf,6), md->name, NAME_LENGTH);
+ if( md->guardian_data && md->guardian_data->guild_id )
+ {
+ WBUFW(buf, 0) = cmd = 0x195;
+ WBUFB(buf,30) = 0;
+ memcpy(WBUFP(buf,54), md->guardian_data->guild_name, NAME_LENGTH);
+ memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH);
+ }
+ else if( battle_config.show_mob_info )
+ {
+ char mobhp[50], *str_p = mobhp;
+ WBUFW(buf, 0) = cmd = 0x195;
+ if( battle_config.show_mob_info&4 )
+ str_p += sprintf(str_p, "Lv. %d | ", md->level);
+ if( battle_config.show_mob_info&1 )
+ str_p += sprintf(str_p, "HP: %u/%u | ", md->status.hp, md->status.max_hp);
+ if( battle_config.show_mob_info&2 )
+ str_p += sprintf(str_p, "HP: %d%% | ", get_percentage(md->status.hp, md->status.max_hp));
+ //Even thought mobhp ain't a name, we send it as one so the client
+ //can parse it. [Skotlex]
+ if( str_p != mobhp )
+ {
+ *(str_p-3) = '\0'; //Remove trailing space + pipe.
+ memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH);
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ }
+ }
+ break;
+ case BL_CHAT: //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex]
+// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH);
+// break;
+ return;
+ case BL_ELEM:
+ memcpy(WBUFP(buf,6), ((TBL_ELEM*)bl)->db->name, NAME_LENGTH);
+ break;
+ default:
+ ShowError("clif_charnameack: bad type %d(%d)\n", bl->type, bl->id);
+ return;
+ }
+
+ // if no receipient specified just update nearby clients
+ if (fd == 0)
+ clif_send(buf, packet_len(cmd), bl, AREA);
+ else {
+ WFIFOHEAD(fd, packet_len(cmd));
+ memcpy(WFIFOP(fd, 0), buf, packet_len(cmd));
+ WFIFOSET(fd, packet_len(cmd));
+ }
+}
+
+
+//Used to update when a char leaves a party/guild. [Skotlex]
+//Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent.
+void clif_charnameupdate (struct map_session_data *ssd)
+{
+ unsigned char buf[103];
+ int cmd = 0x195, ps = -1, i;
+ struct party_data *p = NULL;
+ struct guild *g = NULL;
+
+ nullpo_retv(ssd);
+
+ if( ssd->fakename[0] )
+ return; //No need to update as the party/guild was not displayed anyway.
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = ssd->bl.id;
+
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if (!battle_config.display_party_name) {
+ if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL)
+ p = party_search(ssd->status.party_id);
+ }else{
+ if (ssd->status.party_id > 0)
+ p = party_search(ssd->status.party_id);
+ }
+
+ if( ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL )
+ {
+ ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id);
+ if( i < g->max_member ) ps = g->member[i].position;
+ }
+
+ if( p )
+ memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if( g && ps >= 0 && ps < MAX_GUILDPOSITION )
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ }
+ else
+ {
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+
+ // Update nearby clients
+ clif_send(buf, packet_len(cmd), &ssd->bl, AREA);
+}
+
+
+/// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP).
+/// 01ff <id>.L <x>.W <y>.W
+///
+/// Visually moves(instant) a character to x,y. The char moves even
+/// when the target cell isn't walkable. If the char is sitting it
+/// stays that way.
+void clif_slide(struct block_list *bl, int x, int y)
+{
+ unsigned char buf[10];
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x01ff;
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = x;
+ WBUFW(buf, 8) = y;
+ clif_send(buf, packet_len(0x1ff), bl, AREA);
+
+ if( disguised(bl) )
+ {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x1ff), bl, SELF);
+ }
+}
+
+
+/*------------------------------------------
+ * @me command by lordalfa, rewritten implementation by Skotlex
+ *------------------------------------------*/
+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 > sizeof(buf)-8) {
+ ShowError("clif_disp_overhead: Message too long (length %d)\n", len_mes);
+ 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;
+ 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;
+ safestrncpy((char*)WBUFP(buf,4), mes, len_mes);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+}
+
+/*==========================
+ * Minimap fix [Kevin]
+ * Remove dot from minimap
+ *--------------------------*/
+void clif_party_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+ nullpo_retv(sd);
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=-1;
+ WBUFW(buf,8)=-1;
+ clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS);
+}
+
+
+/// Displays a skill message (thanks to Rayce) (ZC_SKILLMSG).
+/// 0215 <msg id>.L
+/// msg id:
+/// 0x15 = End all negative status (PA_GOSPEL)
+/// 0x16 = Immunity to all status (PA_GOSPEL)
+/// 0x17 = MaxHP +100% (PA_GOSPEL)
+/// 0x18 = MaxSP +100% (PA_GOSPEL)
+/// 0x19 = All stats +20 (PA_GOSPEL)
+/// 0x1c = Enchant weapon with Holy element (PA_GOSPEL)
+/// 0x1d = Enchant armor with Holy element (PA_GOSPEL)
+/// 0x1e = DEF +25% (PA_GOSPEL)
+/// 0x1f = ATK +100% (PA_GOSPEL)
+/// 0x20 = HIT/Flee +50 (PA_GOSPEL)
+/// 0x28 = Full strip failed because of coating (ST_FULLSTRIP)
+/// ? = nothing
+void clif_gospel_info(struct map_session_data *sd, int type)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x215));
+ WFIFOW(fd,0)=0x215;
+ WFIFOL(fd,2)=type;
+ WFIFOSET(fd, packet_len(0x215));
+
+}
+
+
+/// Multi-purpose mission information packet (ZC_STARSKILL).
+/// 020e <mapname>.24B <monster_id>.L <star>.B <result>.B
+/// result:
+/// 0 = Star Gladiator %s has designed <mapname>'s as the %s.
+/// star:
+/// 0 = Place of the Sun
+/// 1 = Place of the Moon
+/// 2 = Place of the Stars
+/// 1 = Star Gladiator %s's %s: <mapname>
+/// star:
+/// 0 = Place of the Sun
+/// 1 = Place of the Moon
+/// 2 = Place of the Stars
+/// 10 = Star Gladiator %s has designed <mapname>'s as the %s.
+/// star:
+/// 0 = Target of the Sun
+/// 1 = Target of the Moon
+/// 2 = Target of the Stars
+/// 11 = Star Gladiator %s's %s: <mapname used as monster name>
+/// star:
+/// 0 = Monster of the Sun
+/// 1 = Monster of the Moon
+/// 2 = Monster of the Stars
+/// 20 = [TaeKwon Mission] Target Monster : <mapname used as monster name> (<star>%)
+/// 21 = [Taming Mission] Target Monster : <mapname used as monster name>
+/// 22 = [Collector Rank] Target Item : <monster_id used as item id>
+/// 30 = [Sun, Moon and Stars Angel] Designed places and monsters have been reset.
+/// 40 = Target HP : <monster_id used as HP>
+void clif_starskill(struct map_session_data* sd, const char* mapname, int monster_id, unsigned char star, unsigned char result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x20e));
+ WFIFOW(fd,0) = 0x20e;
+ safestrncpy((char*)WFIFOP(fd,2), mapname, NAME_LENGTH);
+ WFIFOL(fd,26) = monster_id;
+ WFIFOB(fd,30) = star;
+ WFIFOB(fd,31) = result;
+ WFIFOSET(fd,packet_len(0x20e));
+}
+
+/*==========================================
+ * Info about Star Glaldiator save map [Komurka]
+ * type: 1: Information, 0: Map registered
+ *------------------------------------------*/
+void clif_feel_info(struct map_session_data* sd, unsigned char feel_level, unsigned char type)
+{
+ char mapname[MAP_NAME_LENGTH_EXT];
+
+ mapindex_getmapname_ext(mapindex_id2name(sd->feel_map[feel_level].index), mapname);
+ clif_starskill(sd, mapname, 0, feel_level, type ? 1 : 0);
+}
+
+/*==========================================
+ * Info about Star Glaldiator hate mob [Komurka]
+ * type: 1: Register mob, 0: Information.
+ *------------------------------------------*/
+void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type)
+{
+ if( pcdb_checkid(class_) )
+ {
+ clif_starskill(sd, job_name(class_), class_, hate_level, type ? 10 : 11);
+ }
+ else if( mobdb_checkid(class_) )
+ {
+ clif_starskill(sd, mob_db(class_)->jname, class_, hate_level, type ? 10 : 11);
+ }
+ else
+ {
+ ShowWarning("clif_hate_info: Received invalid class %d for this packet (char_id=%d, hate_level=%u, type=%u).\n", class_, sd->status.char_id, (unsigned int)hate_level, (unsigned int)type);
+ }
+}
+
+/*==========================================
+ * Info about TaeKwon Do TK_MISSION mob [Skotlex]
+ *------------------------------------------*/
+void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress)
+{
+ clif_starskill(sd, mob_db(mob_id)->jname, mob_id, progress, 20);
+}
+
+/*==========================================
+ * Feel/Hate reset (thanks to Rayce) [Skotlex]
+ *------------------------------------------*/
+void clif_feel_hate_reset(struct map_session_data *sd)
+{
+ clif_starskill(sd, "", 0, 0, 30);
+}
+
+
+/// Equip window (un)tick ack (ZC_CONFIG).
+/// 02d9 <type>.L <value>.L
+/// type:
+/// 0 = open equip window
+/// value:
+/// 0 = disabled
+/// 1 = enabled
+void clif_equiptickack(struct map_session_data* sd, int flag)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2d9));
+ WFIFOW(fd, 0) = 0x2d9;
+ WFIFOL(fd, 2) = 0;
+ WFIFOL(fd, 6) = flag;
+ WFIFOSET(fd, packet_len(0x2d9));
+}
+
+
+/// The player's 'view equip' state, sent during login (ZC_CONFIG_NOTIFY).
+/// 02da <open equip window>.B
+/// open equip window:
+/// 0 = disabled
+/// 1 = enabled
+void clif_equipcheckbox(struct map_session_data* sd)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2da));
+ WFIFOW(fd, 0) = 0x2da;
+ WFIFOB(fd, 2) = (sd->status.show_equip ? 1 : 0);
+ WFIFOSET(fd, packet_len(0x2da));
+}
+
+
+/// Sends info about a player's equipped items.
+/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <up-viewid>.W <mid-viewid>.W <low-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.26B* (ZC_EQUIPWIN_MICROSCOPE)
+/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE, PACKETVER >= 20100629)
+/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20101124)
+/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20110111)
+void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* tsd)
+{
+ uint8* buf;
+ int i, n, fd, offset = 0;
+#if PACKETVER < 20100629
+ const int s = 26;
+#else
+ const int s = 28;
+#endif
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, MAX_INVENTORY * s + 43);
+ buf = WFIFOP(fd,0);
+
+#if PACKETVER < 20101124
+ WBUFW(buf, 0) = 0x2d7;
+#else
+ WBUFW(buf, 0) = 0x859;
+#endif
+ safestrncpy((char*)WBUFP(buf, 4), tsd->status.name, NAME_LENGTH);
+ WBUFW(buf,28) = tsd->status.class_;
+ WBUFW(buf,30) = tsd->vd.hair_style;
+ WBUFW(buf,32) = tsd->vd.head_bottom;
+ WBUFW(buf,34) = tsd->vd.head_mid;
+ WBUFW(buf,36) = tsd->vd.head_top;
+#if PACKETVER >= 20110111
+ WBUFW(buf,38) = tsd->vd.robe;
+ offset+= 2;
+ buf = WBUFP(buf,2);
+#endif
+ WBUFW(buf,38) = tsd->vd.hair_color;
+ WBUFW(buf,40) = tsd->vd.cloth_color;
+ WBUFB(buf,42) = tsd->vd.sex;
+
+ for(i=0,n=0; i < MAX_INVENTORY; i++)
+ {
+ if (tsd->status.inventory[i].nameid <= 0 || tsd->inventory_data[i] == NULL) // Item doesn't exist
+ continue;
+ if (!itemdb_isequip2(tsd->inventory_data[i])) // Is not equippable
+ continue;
+
+ // Inventory position
+ WBUFW(buf, n*s+43) = i + 2;
+ // Add refine, identify flag, element, etc.
+ clif_item_sub(WBUFP(buf,0), n*s+45, &tsd->status.inventory[i], tsd->inventory_data[i], pc_equippoint(tsd, i));
+ // Add cards
+ clif_addcards(WBUFP(buf, n*s+55), &tsd->status.inventory[i]);
+ // Expiration date stuff, if all of those are set to 0 then the client doesn't show anything related (6 bytes)
+ WBUFL(buf, n*s+63) = tsd->status.inventory[i].expire_time;
+ WBUFW(buf, n*s+67) = 0;
+#if PACKETVER >= 20100629
+ if (tsd->inventory_data[i]->equip&EQP_VISIBLE)
+ WBUFW(buf, n*s+69) = tsd->inventory_data[i]->look;
+ else
+ WBUFW(buf, n*s+69) = 0;
+#endif
+ n++;
+ }
+
+ WFIFOW(fd, 2) = 43+offset+n*s; // Set length
+ WFIFOSET(fd, WFIFOW(fd, 2));
+}
+
+
+/// Display msgstringtable.txt string (ZC_MSG).
+/// 0291 <message>.W
+void clif_msg(struct map_session_data* sd, unsigned short id)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x291));
+ WFIFOW(fd, 0) = 0x291;
+ WFIFOW(fd, 2) = id; // zero-based msgstringtable.txt index
+ WFIFOSET(fd, packet_len(0x291));
+}
+
+
+/// Display msgstringtable.txt string and fill in a valid for %d format (ZC_MSG_VALUE).
+/// 0x7e2 <message>.W <value>.L
+void clif_msg_value(struct map_session_data* sd, unsigned short id, int value)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7e2));
+ WFIFOW(fd,0) = 0x7e2;
+ WFIFOW(fd,2) = id;
+ WFIFOL(fd,4) = value;
+ WFIFOSET(fd, packet_len(0x7e2));
+}
+
+
+/// Displays msgstringtable.txt string, prefixed with a skill name. (ZC_MSG_SKILL).
+/// 07e6 <skill id>.W <msg id>.L
+///
+/// NOTE: Message has following format and is printed in color 0xCDCDFF (purple):
+/// "[SkillName] Message"
+void clif_msg_skill(struct map_session_data* sd, uint16 skill_id, int msg_id)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7e6));
+ WFIFOW(fd,0) = 0x7e6;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOL(fd,4) = msg_id;
+ WFIFOSET(fd, packet_len(0x7e6));
+}
+
+
+/// View player equip request denied
+void clif_viewequip_fail(struct map_session_data* sd)
+{
+ clif_msg(sd, 0x54d);
+}
+
+
+/// Validates one global/guild/party/whisper message packet and tries to recognize its components.
+/// Returns true if the packet was parsed successfully.
+/// Formats: 0 - <packet id>.w <packet len>.w (<name> : <message>).?B 00
+/// 1 - <packet id>.w <packet len>.w <name>.24B <message>.?B 00
+static bool clif_process_message(struct map_session_data* sd, int format, char** name_, int* namelen_, char** message_, int* messagelen_)
+{
+ char *text, *name, *message;
+ unsigned int packetlen, textlen, namelen, messagelen;
+ int fd = sd->fd;
+
+ *name_ = NULL;
+ *namelen_ = 0;
+ *message_ = NULL;
+ *messagelen_ = 0;
+
+ packetlen = RFIFOW(fd,2);
+ // basic structure checks
+ if( packetlen < 4 + 1 )
+ { // 4-byte header and at least an empty string is expected
+ ShowWarning("clif_process_message: Received malformed packet from player '%s' (no message data)!\n", sd->status.name);
+ return false;
+ }
+
+ text = (char*)RFIFOP(fd,4);
+ textlen = packetlen - 4;
+
+ // process <name> part of the packet
+ if( format == 0 )
+ {// name and message are separated by ' : '
+ // validate name
+ name = text;
+ namelen = strnlen(sd->status.name, NAME_LENGTH-1); // name length (w/o zero byte)
+
+ if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name
+ name[namelen] != ' ' || name[namelen+1] != ':' || name[namelen+2] != ' ' ) // followed by ' : '
+ {
+ //Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
+ ShowWarning("clif_process_message: Player '%s' sent a message using an incorrect name! Forcing a relog...\n", sd->status.name);
+ set_eof(fd); // Just kick them out to correct it.
+ return false;
+ }
+
+ message = name + namelen + 3;
+ messagelen = textlen - namelen - 3; // this should be the message length (w/ zero byte included)
+ }
+ else
+ {// name has fixed width
+ if( textlen < NAME_LENGTH + 1 )
+ {
+ ShowWarning("clif_process_message: Received malformed packet from player '%s' (packet length is incorrect)!\n", sd->status.name);
+ return false;
+ }
+
+ // validate name
+ name = text;
+ namelen = strnlen(name, NAME_LENGTH-1); // name length (w/o zero byte)
+
+ if( name[namelen] != '\0' )
+ { // only restriction is that the name must be zero-terminated
+ ShowWarning("clif_process_message: Player '%s' sent an unterminated name!\n", sd->status.name);
+ return false;
+ }
+
+ message = name + NAME_LENGTH;
+ messagelen = textlen - NAME_LENGTH; // this should be the message length (w/ zero byte included)
+ }
+
+ if( messagelen != strnlen(message, messagelen)+1 )
+ { // the declared length must match real length
+ ShowWarning("clif_process_message: Received malformed packet from player '%s' (length is incorrect)!\n", sd->status.name);
+ return false;
+ }
+ // verify <message> part of the packet
+ if( message[messagelen-1] != '\0' )
+ { // message must be zero-terminated
+ ShowWarning("clif_process_message: Player '%s' sent an unterminated message string!\n", sd->status.name);
+ return false;
+ }
+ if( messagelen > CHAT_SIZE_MAX-1 )
+ { // messages mustn't be too long
+ // Normally you can only enter CHATBOX_SIZE-1 letters into the chat box, but Frost Joke / Dazzler's text can be longer.
+ // Also, the physical size of strings that use multibyte encoding can go multiple times over the chatbox capacity.
+ // Neither the official client nor server place any restriction on the length of the data in the packet,
+ // but we'll only allow reasonably long strings here. This also makes sure that they fit into the `chatlog` table.
+ ShowWarning("clif_process_message: Player '%s' sent a message too long ('%.*s')!\n", sd->status.name, CHAT_SIZE_MAX-1, message);
+ return false;
+ }
+
+ *name_ = name;
+ *namelen_ = namelen;
+ *message_ = message;
+ *messagelen_ = messagelen;
+ return true;
+}
+
+// ---------------------
+// clif_guess_PacketVer
+// ---------------------
+// Parses a WantToConnection packet to try to identify which is the packet version used. [Skotlex]
+// error codes:
+// 0 - Success
+// 1 - Unknown packet_ver
+// 2 - Invalid account_id
+// 3 - Invalid char_id
+// 4 - Invalid login_id1 (reserved)
+// 5 - Invalid client_tick (reserved)
+// 6 - Invalid sex
+// Only the first 'invalid' error that appears is used.
+static int clif_guess_PacketVer(int fd, int get_previous, int *error)
+{
+ static int err = 1;
+ static int packet_ver = -1;
+ int cmd, packet_len, value; //Value is used to temporarily store account/char_id/sex
+
+ if (get_previous)
+ {//For quick reruns, since the normal code flow is to fetch this once to identify the packet version, then again in the wanttoconnect function. [Skotlex]
+ if( error )
+ *error = err;
+ return packet_ver;
+ }
+
+ //By default, start searching on the default one.
+ err = 1;
+ packet_ver = clif_config.packet_db_ver;
+ cmd = RFIFOW(fd,0);
+ packet_len = RFIFOREST(fd);
+
+#define SET_ERROR(n) \
+ if( err == 1 )\
+ err = n;\
+//define SET_ERROR
+
+ // FIXME: If the packet is not received at once, this will FAIL.
+ // Figure out, when it happens, that only part of the packet is
+ // received, or fix the function to be able to deal with that
+ // case.
+#define CHECK_PACKET_VER() \
+ if( cmd != clif_config.connect_cmd[packet_ver] || packet_len != packet_db[packet_ver][cmd].len )\
+ ;/* not wanttoconnection or wrong length */\
+ else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) < START_ACCOUNT_NUM || value > END_ACCOUNT_NUM )\
+ { SET_ERROR(2); }/* invalid account_id */\
+ else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) <= 0 )\
+ { SET_ERROR(3); }/* invalid char_id */\
+ /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) - don't care about login_id1 */\
+ /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]) - don't care about client_tick */\
+ else if( (value=(int)RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) != 0 && value != 1 )\
+ { SET_ERROR(6); }/* invalid sex */\
+ else\
+ {\
+ err = 0;\
+ if( error )\
+ *error = 0;\
+ return packet_ver;\
+ }\
+//define CHECK_PACKET_VER
+
+ CHECK_PACKET_VER();//Default packet version found.
+
+ for (packet_ver = MAX_PACKET_VER; packet_ver > 0; packet_ver--)
+ { //Start guessing the version, giving priority to the newer ones. [Skotlex]
+ CHECK_PACKET_VER();
+ }
+ if( error )
+ *error = err;
+ packet_ver = -1;
+ return -1;
+#undef SET_ERROR
+#undef CHECK_PACKET_VER
+}
+
+// ------------
+// clif_parse_*
+// ------------
+// Parses incoming (player) connection
+
+
+/// Request to connect to map-server.
+/// 0072 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER)
+/// 0436 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_WantToConnection(int fd, TBL_PC* sd)
+{
+ struct block_list* bl;
+ struct auth_node* node;
+ int cmd, account_id, char_id, login_id1, sex;
+ unsigned int client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex]
+ int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 (by [Yor])
+
+ if (sd) {
+ ShowError("clif_parse_WantToConnection : invalid request (character already logged in)\n");
+ return;
+ }
+
+ // Only valid packet version get here
+ packet_ver = clif_guess_PacketVer(fd, 1, NULL);
+
+ cmd = RFIFOW(fd,0);
+ account_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0]);
+ char_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1]);
+ login_id1 = RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]);
+ client_tick = RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]);
+ sex = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4]);
+
+ if( packet_ver < 5 || // reject really old client versions
+ (packet_ver <= 9 && (battle_config.packet_ver_flag & 1) == 0) || // older than 6sept04
+ (packet_ver > 9 && (battle_config.packet_ver_flag & 1<<(packet_ver-9)) == 0)) // version not allowed
+ {// packet version rejected
+ ShowInfo("Rejected connection attempt, forbidden packet version (AID/CID: '"CL_WHITE"%d/%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%s"CL_RESET"').\n", account_id, char_id, packet_ver, ip2str(session[fd]->client_addr, NULL));
+ WFIFOHEAD(fd,packet_len(0x6a));
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 5; // Your Game's EXE file is not the latest version
+ WFIFOSET(fd,packet_len(0x6a));
+ set_eof(fd);
+ return;
+ }
+
+ if( runflag != MAPSERVER_ST_RUNNING )
+ {// not allowed
+ clif_authfail_fd(fd,1);// server closed
+ return;
+ }
+
+ //Check for double login.
+ bl = map_id2bl(account_id);
+ if(bl && bl->type != BL_PC) {
+ ShowError("clif_parse_WantToConnection: a non-player object already has id %d, please increase the starting account number\n", account_id);
+ WFIFOHEAD(fd,packet_len(0x6a));
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 3; // Rejected by server
+ WFIFOSET(fd,packet_len(0x6a));
+ set_eof(fd);
+ return;
+ }
+
+ if (bl ||
+ ((node=chrif_search(account_id)) && //An already existing node is valid only if it is for this login.
+ !(node->account_id == account_id && node->char_id == char_id && node->state == ST_LOGIN)))
+ {
+ clif_authfail_fd(fd, 8); //Still recognizes last connection
+ return;
+ }
+
+ CREATE(sd, TBL_PC, 1);
+ sd->fd = fd;
+ sd->packet_ver = packet_ver;
+ session[fd]->session_data = sd;
+
+ pc_setnewpc(sd, account_id, char_id, login_id1, client_tick, sex, fd);
+
+#if PACKETVER < 20070521
+ WFIFOHEAD(fd,4);
+ WFIFOL(fd,0) = sd->bl.id;
+ WFIFOSET(fd,4);
+#else
+ WFIFOHEAD(fd,packet_len(0x283));
+ WFIFOW(fd,0) = 0x283;
+ WFIFOL(fd,2) = sd->bl.id;
+ WFIFOSET(fd,packet_len(0x283));
+#endif
+
+ chrif_authreq(sd);
+}
+
+
+/// Notification from the client, that it has finished map loading and is about to display player's character (CZ_NOTIFY_ACTORINIT).
+/// 007d
+void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
+{
+ if(sd->bl.prev != NULL)
+ return;
+
+ if (!sd->state.active)
+ { //Character loading is not complete yet!
+ //Let pc_reg_received reinvoke this when ready.
+ sd->state.connect_new = 0;
+ return;
+ }
+
+ if (sd->state.rewarp)
+ { //Rewarp player.
+ sd->state.rewarp = 0;
+ clif_changemap(sd, sd->mapindex, sd->bl.x, sd->bl.y);
+ return;
+ }
+
+ sd->state.warping = 0;
+
+ // look
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+
+ if(sd->vd.cloth_color)
+ clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
+
+ // item
+ clif_inventorylist(sd); // inventory list first, otherwise deleted items in pc_checkitem show up as 'unknown item'
+ pc_checkitem(sd);
+
+ // cart
+ if(pc_iscarton(sd)) {
+ clif_cartlist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+
+ // weight
+ clif_updatestatus(sd,SP_WEIGHT);
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+
+ // guild
+ // (needs to go before clif_spawn() to show guild emblems correctly)
+ if(sd->status.guild_id)
+ guild_send_memberinfoshort(sd,1);
+
+ if(battle_config.pc_invincible_time > 0) {
+ if(map_flag_gvg(sd->bl.m))
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time<<1);
+ else
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time);
+ }
+
+ if( map[sd->bl.m].users++ == 0 && battle_config.dynamic_mobs )
+ map_spawnmobs(sd->bl.m);
+ if( !(sd->sc.option&OPTION_INVISIBLE) )
+ {// increment the number of pvp players on the map
+ map[sd->bl.m].users_pvp++;
+ }
+ if( map[sd->bl.m].instance_id )
+ {
+ instance[map[sd->bl.m].instance_id].users++;
+ instance_check_idle(map[sd->bl.m].instance_id);
+ }
+ sd->state.debug_remove_map = 0; // temporary state to track double remove_map's [FlavioJS]
+
+ // reset the callshop flag if the player changes map
+ sd->state.callshop = 0;
+
+ map_addblock(&sd->bl);
+ clif_spawn(&sd->bl);
+
+ // Party
+ // (needs to go after clif_spawn() to show hp bars correctly)
+ if(sd->status.party_id) {
+ party_send_movemap(sd);
+ clif_party_hp(sd); // Show hp after displacement [LuzZza]
+ }
+
+ if( sd->bg_id ) clif_bg_hp(sd); // BattleGround System
+
+ if(map[sd->bl.m].flag.pvp && !(sd->sc.option&OPTION_INVISIBLE)) {
+ if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris]
+ if (!map[sd->bl.m].flag.pvp_nocalcrank)
+ sd->pvp_timer = add_timer(gettick()+200, pc_calc_pvprank_timer, sd->bl.id, 0);
+ sd->pvp_rank = 0;
+ sd->pvp_lastusers = 0;
+ sd->pvp_point = 5;
+ sd->pvp_won = 0;
+ sd->pvp_lost = 0;
+ }
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
+ } else
+ // set flag, if it's a duel [LuzZza]
+ if(sd->duel_group)
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
+
+ if (map[sd->bl.m].flag.gvg_dungeon)
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE); //TODO: Figure out the real packet to send here.
+
+ if( map_flag_gvg(sd->bl.m) )
+ clif_map_property(sd, MAPPROPERTY_AGITZONE);
+
+ // info about nearby objects
+ // must use foreachinarea (CIRCULAR_AREA interferes with foreachinrange)
+ map_foreachinarea(clif_getareachar, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_ALL, sd);
+
+ // pet
+ if( sd->pd )
+ {
+ if( battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m) )
+ { //Return the pet to egg. [Skotlex]
+ clif_displaymessage(sd->fd, msg_txt(666));
+ pet_menu(sd, 3); //Option 3 is return to egg.
+ }
+ else
+ {
+ map_addblock(&sd->pd->bl);
+ clif_spawn(&sd->pd->bl);
+ clif_send_petdata(sd,sd->pd,0,0);
+ clif_send_petstatus(sd);
+// skill_unit_move(&sd->pd->bl,gettick(),1);
+ }
+ }
+
+ //homunculus [blackhole89]
+ if( merc_is_hom_active(sd->hd) )
+ {
+ map_addblock(&sd->hd->bl);
+ clif_spawn(&sd->hd->bl);
+ clif_send_homdata(sd,SP_ACK,0);
+ clif_hominfo(sd,sd->hd,1);
+ clif_hominfo(sd,sd->hd,0); //for some reason, at least older clients want this sent twice
+ clif_homskillinfoblock(sd);
+ if( battle_config.hom_setting&0x8 )
+ status_calc_bl(&sd->hd->bl, SCB_SPEED); //Homunc mimic their master's speed on each map change
+ if( !(battle_config.hom_setting&0x2) )
+ skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
+ }
+
+ if( sd->md ) {
+ map_addblock(&sd->md->bl);
+ clif_spawn(&sd->md->bl);
+ clif_mercenary_info(sd);
+ clif_mercenary_skillblock(sd);
+ status_calc_bl(&sd->md->bl, SCB_SPEED); // Mercenary mimic their master's speed on each map change
+ }
+
+ if( sd->ed ) {
+ map_addblock(&sd->ed->bl);
+ clif_spawn(&sd->ed->bl);
+ clif_elemental_info(sd);
+ clif_elemental_updatestatus(sd,SP_HP);
+ clif_hpmeter_single(sd->fd,sd->ed->bl.id,sd->ed->battle_status.hp,sd->ed->battle_status.max_hp);
+ clif_elemental_updatestatus(sd,SP_SP);
+ status_calc_bl(&sd->ed->bl, SCB_SPEED); //Elemental mimic their master's speed on each map change
+ }
+
+ if(sd->state.connect_new) {
+ int lv;
+ sd->state.connect_new = 0;
+ clif_skillinfoblock(sd);
+ clif_hotkeys_send(sd);
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ clif_initialstatus(sd);
+
+ if (sd->sc.option&OPTION_FALCON)
+ clif_status_load(&sd->bl, SI_FALCON, 1);
+
+ if (sd->sc.option&OPTION_RIDING)
+ clif_status_load(&sd->bl, SI_RIDING, 1);
+ else if (sd->sc.option&OPTION_WUGRIDER)
+ clif_status_load(&sd->bl, SI_WUGRIDER, 1);
+
+ if(sd->status.manner < 0)
+ sc_start(&sd->bl,SC_NOCHAT,100,0,0);
+
+ //Auron reported that This skill only triggers when you logon on the map o.O [Skotlex]
+ if ((lv = pc_checkskill(sd,SG_KNOWLEDGE)) > 0) {
+ if(sd->bl.m == sd->feel_map[0].m
+ || sd->bl.m == sd->feel_map[1].m
+ || sd->bl.m == sd->feel_map[2].m)
+ sc_start(&sd->bl, SC_KNOWLEDGE, 100, lv, skill_get_time(SG_KNOWLEDGE, lv));
+ }
+
+ if(sd->pd && sd->pd->pet.intimate > 900)
+ clif_pet_emotion(sd->pd,(sd->pd->pet.class_ - 100)*100 + 50 + pet_hungry_val(sd->pd));
+
+ if(merc_is_hom_active(sd->hd))
+ merc_hom_init_timers(sd->hd);
+
+ if (night_flag && map[sd->bl.m].flag.nightenabled) {
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ }
+
+ // Notify everyone that this char logged in [Skotlex].
+ map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1);
+
+ //Login Event
+ npc_script_event(sd, NPCE_LOGIN);
+ } else {
+ //For some reason the client "loses" these on warp/map-change.
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ // abort currently running script
+ sd->state.using_fake_npc = 0;
+ sd->state.menu_or_input = 0;
+ sd->npc_menu = 0;
+
+ if(sd->npc_id)
+ npc_event_dequeue(sd);
+ }
+
+ if( sd->state.changemap )
+ {// restore information that gets lost on map-change
+#if PACKETVER >= 20070918
+ clif_partyinvitationstate(sd);
+ clif_equipcheckbox(sd);
+#endif
+ if( (battle_config.bg_flee_penalty != 100 || battle_config.gvg_flee_penalty != 100) &&
+ (map_flag_gvg(sd->state.pmap) || map_flag_gvg(sd->bl.m) || map[sd->state.pmap].flag.battleground || map[sd->bl.m].flag.battleground) )
+ status_calc_bl(&sd->bl, SCB_FLEE); //Refresh flee penalty
+
+ if( night_flag && map[sd->bl.m].flag.nightenabled )
+ { //Display night.
+ if( !sd->state.night )
+ {
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ }
+ }
+ else if( sd->state.night )
+ { //Clear night display.
+ sd->state.night = 0;
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ }
+
+ if( map[sd->bl.m].flag.battleground )
+ {
+ clif_map_type(sd, MAPTYPE_BATTLEFIELD); // Battleground Mode
+ if( map[sd->bl.m].flag.battleground == 2 )
+ clif_bg_updatescore_single(sd);
+ }
+
+ if( map[sd->bl.m].flag.allowks && !map_flag_ks(sd->bl.m) )
+ {
+ char output[128];
+ sprintf(output, "[ Kill Steal Protection Disable. KS is allowed in this map ]");
+ clif_broadcast(&sd->bl, output, strlen(output) + 1, 0x10, SELF);
+ }
+
+ map_iwall_get(sd); // Updates Walls Info on this Map to Client
+ sd->state.changemap = false;
+ }
+
+ mail_clear(sd);
+
+ /* Guild Aura Init */
+ if( sd->state.gmaster_flag ) {
+ guild_guildaura_refresh(sd,GD_LEADERSHIP,guild_checkskill(sd->state.gmaster_flag,GD_LEADERSHIP));
+ guild_guildaura_refresh(sd,GD_GLORYWOUNDS,guild_checkskill(sd->state.gmaster_flag,GD_GLORYWOUNDS));
+ guild_guildaura_refresh(sd,GD_SOULCOLD,guild_checkskill(sd->state.gmaster_flag,GD_SOULCOLD));
+ guild_guildaura_refresh(sd,GD_HAWKEYES,guild_checkskill(sd->state.gmaster_flag,GD_HAWKEYES));
+ }
+
+ if( sd->state.vending ) { /* show we have a vending */
+ clif_openvending(sd,sd->bl.id,sd->vending);
+ clif_showvendingboard(&sd->bl,sd->message,0);
+ }
+
+ if(map[sd->bl.m].flag.loadevent) // Lance
+ npc_script_event(sd, NPCE_LOADMAP);
+
+ if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd))
+ clif_status_load(&sd->bl, SI_DEVIL, 1); //blindness [Komurka]
+
+ if (sd->sc.opt2) //Client loses these on warp.
+ clif_changeoption(&sd->bl);
+
+ clif_weather_check(sd);
+
+ // For automatic triggering of NPCs after map loading (so you don't need to walk 1 step first)
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y);
+ else
+ sd->areanpc_id = 0;
+
+ /* it broke at some point (e.g. during a crash), so we make it visibly dead again. */
+ if( !sd->status.hp && !pc_isdead(sd) && status_isdead(&sd->bl) )
+ pc_setdead(sd);
+
+ // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris]
+ if(pc_isdead(sd))
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ else {
+ skill_usave_trigger(sd);
+ clif_changed_dir(&sd->bl, SELF);
+ }
+
+// Trigger skill effects if you appear standing on them
+ if(!battle_config.pc_invincible_time)
+ skill_unit_move(&sd->bl,gettick(),1);
+}
+
+
+/// Server's tick (ZC_NOTIFY_TIME).
+/// 007f <time>.L
+void clif_notify_time(struct map_session_data* sd, unsigned long time)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x7f));
+ WFIFOW(fd,0) = 0x7f;
+ WFIFOL(fd,2) = time;
+ WFIFOSET(fd,packet_len(0x7f));
+}
+
+
+/// Request for server's tick.
+/// 007e <client tick>.L (CZ_REQUEST_TIME)
+/// 0360 <client tick>.L (CZ_REQUEST_TIME2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_TickSend(int fd, struct map_session_data *sd)
+{
+ sd->client_tick = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ clif_notify_time(sd, gettick());
+}
+
+
+/// Sends hotkey bar.
+/// 02b9 { <is skill>.B <id>.L <count>.W }*27 (ZC_SHORTCUT_KEY_LIST)
+/// 07d9 { <is skill>.B <id>.L <count>.W }*36 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090603)
+/// 07d9 { <is skill>.B <id>.L <count>.W }*38 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090617)
+void clif_hotkeys_send(struct map_session_data *sd) {
+#ifdef HOTKEY_SAVING
+ const int fd = sd->fd;
+ int i;
+#if PACKETVER < 20090603
+ const int cmd = 0x2b9;
+#else
+ const int cmd = 0x7d9;
+#endif
+ if (!fd) return;
+ WFIFOHEAD(fd, 2+MAX_HOTKEYS*7);
+ WFIFOW(fd, 0) = cmd;
+ for(i = 0; i < MAX_HOTKEYS; i++) {
+ WFIFOB(fd, 2 + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill
+ WFIFOL(fd, 2 + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID
+ WFIFOW(fd, 2 + 5 + i * 7) = sd->status.hotkeys[i].lv; // skill level
+ }
+ WFIFOSET(fd, packet_len(cmd));
+#endif
+}
+
+
+/// Request to update a position on the hotkey bar (CZ_SHORTCUT_KEY_CHANGE).
+/// 02ba <index>.W <is skill>.B <id>.L <count>.W
+void clif_parse_Hotkey(int fd, struct map_session_data *sd) {
+#ifdef HOTKEY_SAVING
+ unsigned short idx;
+ int cmd;
+
+ cmd = RFIFOW(fd, 0);
+ idx = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[0]);
+ if (idx >= MAX_HOTKEYS) return;
+
+ sd->status.hotkeys[idx].type = RFIFOB(fd, packet_db[sd->packet_ver][cmd].pos[1]);
+ sd->status.hotkeys[idx].id = RFIFOL(fd, packet_db[sd->packet_ver][cmd].pos[2]);
+ sd->status.hotkeys[idx].lv = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[3]);
+#endif
+}
+
+
+/// Displays cast-like progress bar (ZC_PROGRESS).
+/// 02f0 <color>.L <time>.L
+void clif_progressbar(struct map_session_data * sd, unsigned long color, unsigned int second)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2f0));
+ WFIFOW(fd,0) = 0x2f0;
+ WFIFOL(fd,2) = color;
+ WFIFOL(fd,6) = second;
+ WFIFOSET(fd,packet_len(0x2f0));
+}
+
+
+/// Removes an ongoing progress bar (ZC_PROGRESS_CANCEL).
+/// 02f2
+void clif_progressbar_abort(struct map_session_data * sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2f2));
+ WFIFOW(fd,0) = 0x2f2;
+ WFIFOSET(fd,packet_len(0x2f2));
+}
+
+
+/// Notification from the client, that the progress bar has reached 100% (CZ_PROGRESS).
+/// 02f1
+void clif_parse_progressbar(int fd, struct map_session_data * sd)
+{
+ int npc_id = sd->progressbar.npc_id;
+
+ if( gettick() < sd->progressbar.timeout && sd->st )
+ sd->st->state = END;
+
+ sd->progressbar.npc_id = sd->progressbar.timeout = 0;
+ npc_scriptcont(sd, npc_id);
+}
+
+
+/// Request to walk to a certain position on the current map.
+/// 0085 <dest>.3B (CZ_REQUEST_MOVE)
+/// 035f <dest>.3B (CZ_REQUEST_MOVE2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_WalkToXY(int fd, struct map_session_data *sd)
+{
+ short x, y;
+
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ return;
+ }
+
+ if (sd->sc.opt1 && ( sd->sc.opt1 == OPT1_STONEWAIT || sd->sc.opt1 == OPT1_BURNING ))
+ ; //You CAN walk on this OPT1 value.
+ else if( sd->progressbar.npc_id )
+ clif_progressbar_abort(sd);
+ else if (pc_cant_act(sd))
+ return;
+
+ if(sd->sc.data[SC_RUN] || sd->sc.data[SC_WUGDASH])
+ return;
+
+ pc_delinvincibletimer(sd);
+
+ RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0], &x, &y, NULL);
+
+ //Set last idle time... [Skotlex]
+ sd->idletime = last_tick;
+
+ unit_walktoxy(&sd->bl, x, y, 4);
+}
+
+
+/// Notification about the result of a disconnect request (ZC_ACK_REQ_DISCONNECT).
+/// 018b <result>.W
+/// result:
+/// 0 = disconnect (quit)
+/// 1 = cannot disconnect (wait 10 seconds)
+/// ? = ignored
+void clif_disconnect_ack(struct map_session_data* sd, short result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x18b));
+ WFIFOW(fd,0) = 0x18b;
+ WFIFOW(fd,2) = result;
+ WFIFOSET(fd,packet_len(0x18b));
+}
+
+
+/// Request to disconnect from server (CZ_REQ_DISCONNECT).
+/// 018a <type>.W
+/// type:
+/// 0 = quit
+void clif_parse_QuitGame(int fd, struct map_session_data *sd)
+{
+ /* Rovert's prevent logout option fixed [Valaris] */
+ if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] &&
+ (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) )
+ {
+ set_eof(fd);
+ clif_disconnect_ack(sd, 0);
+ } else {
+ clif_disconnect_ack(sd, 1);
+ }
+}
+
+
+/// Requesting unit's name.
+/// 0094 <id>.L (CZ_REQNAME)
+/// 0368 <id>.L (CZ_REQNAME2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd)
+{
+ int id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ struct block_list* bl;
+ //struct status_change *sc;
+
+ if( id < 0 && -id == sd->bl.id ) // for disguises [Valaris]
+ id = sd->bl.id;
+
+ bl = map_id2bl(id);
+ if( bl == NULL )
+ return; // Lagged clients could request names of already gone mobs/players. [Skotlex]
+
+ if( sd->bl.m != bl->m || !check_distance_bl(&sd->bl, bl, AREA_SIZE) )
+ return; // Block namerequests past view range
+
+ // 'see people in GM hide' cheat detection
+ /* disabled due to false positives (network lag + request name of char that's about to hide = race condition)
+ sc = status_get_sc(bl);
+ if (sc && sc->option&OPTION_INVISIBLE && !disguised(bl) &&
+ bl->type != BL_NPC && //Skip hidden NPCs which can be seen using Maya Purple
+ pc_get_group_level(sd) < battle_config.hack_info_GM_level
+ ) {
+ char gm_msg[256];
+ sprintf(gm_msg, "Hack on NameRequest: character '%s' (account: %d) requested the name of an invisible target (id: %d).\n", sd->status.name, sd->status.account_id, id);
+ ShowWarning(gm_msg);
+ // information is sent to all online GMs
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, gm_msg);
+ return;
+ }
+ */
+
+ clif_charnameack(fd, bl);
+}
+
+
+/// Validates and processes global messages
+/// 008c <packet len>.W <text>.?B (<name> : <message>) 00 (CZ_REQUEST_CHAT)
+/// There are various variants of this packet.
+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, *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;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
+ 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*) aMalloc(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), 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
+ if( is_fake ) {
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOW(fd,2) = textlen + 4;
+ safestrncpy((char*)WFIFOP(fd,4), fakename, textlen);
+ aFree(fakename);
+ } 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);
+#endif
+
+ // Chat logging type 'O' / Global Chat
+ log_chat(LOG_CHAT_GLOBAL, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, message);
+}
+
+
+/// /mm /mapmove (as @rura GM command) (CZ_MOVETO_MAP).
+/// Request to warp to a map on given coordinates.
+/// 0140 <map name>.16B <x>.W <y>.W
+void clif_parse_MapMove(int fd, struct map_session_data *sd)
+{
+ char command[MAP_NAME_LENGTH_EXT+25];
+ char* map_name;
+
+ map_name = (char*)RFIFOP(fd,2);
+ map_name[MAP_NAME_LENGTH_EXT-1]='\0';
+ sprintf(command, "%cmapmove %s %d %d", atcommand_symbol, map_name, RFIFOW(fd,18), RFIFOW(fd,20));
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Updates body and head direction of an object (ZC_CHANGE_DIRECTION).
+/// 009c <id>.L <head dir>.W <dir>.B
+/// head dir:
+/// 0 = straight
+/// 1 = turned CW
+/// 2 = turned CCW
+/// dir:
+/// 0 = north
+/// 1 = northwest
+/// 2 = west
+/// 3 = southwest
+/// 4 = south
+/// 5 = southeast
+/// 6 = east
+/// 7 = northeast
+void clif_changed_dir(struct block_list *bl, enum send_target target)
+{
+ unsigned char buf[64];
+
+ WBUFW(buf,0) = 0x9c;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = bl->type==BL_PC?((TBL_PC*)bl)->head_dir:0;
+ WBUFB(buf,8) = unit_getdir(bl);
+
+ clif_send(buf, packet_len(0x9c), bl, target);
+
+ if (disguised(bl)) {
+ WBUFL(buf,2) = -bl->id;
+ WBUFW(buf,6) = 0;
+ clif_send(buf, packet_len(0x9c), bl, SELF);
+ }
+}
+
+
+/// Request to change own body and head direction.
+/// 009b <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION)
+/// 0361 <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_ChangeDir(int fd, struct map_session_data *sd)
+{
+ unsigned char headdir, dir;
+
+ headdir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ dir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ pc_setdir(sd, dir, headdir);
+
+ clif_changed_dir(&sd->bl, AREA_WOS);
+}
+
+
+/// Request to show an emotion (CZ_REQ_EMOTION).
+/// 00bf <type>.B
+/// type:
+/// @see enum emotion_type
+void clif_parse_Emotion(int fd, struct map_session_data *sd)
+{
+ int emoticon = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) {
+ if (emoticon == E_MUTE) {// prevent use of the mute emote [Valaris]
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
+ return;
+ }
+ // fix flood of emotion icon (ro-proxy): flood only the hacker player
+ if (sd->emotionlasttime + 1 >= time(NULL)) { // not more than 1 per second
+ sd->emotionlasttime = time(NULL);
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
+ return;
+ }
+ sd->emotionlasttime = time(NULL);
+
+ if(battle_config.client_reshuffle_dice && emoticon>=E_DICE1 && emoticon<=E_DICE6)
+ {// re-roll dice
+ emoticon = rnd()%6+E_DICE1;
+ }
+
+ clif_emotion(&sd->bl, emoticon);
+ } else
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
+}
+
+
+/// Amount of currently online players, reply to /w /who (ZC_USER_COUNT).
+/// 00c2 <count>.L
+void clif_user_count(struct map_session_data* sd, int count)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xc2));
+ WFIFOW(fd,0) = 0xc2;
+ WFIFOL(fd,2) = count;
+ WFIFOSET(fd,packet_len(0xc2));
+}
+
+
+/// /w /who (CZ_REQ_USER_COUNT).
+/// Request to display amount of currently connected players.
+/// 00c1
+void clif_parse_HowManyConnections(int fd, struct map_session_data *sd)
+{
+ clif_user_count(sd, map_getusers());
+}
+
+
+void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick)
+{
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ return;
+ }
+
+ if (sd->sc.count &&
+ (sd->sc.data[SC_TRICKDEAD] ||
+ sd->sc.data[SC_AUTOCOUNTER] ||
+ sd->sc.data[SC_BLADESTOP] ||
+ sd->sc.data[SC__MANHOLE] ||
+ sd->sc.data[SC_CURSEDCIRCLE_ATKER] ||
+ sd->sc.data[SC_CURSEDCIRCLE_TARGET] ))
+ return;
+
+ pc_stop_walking(sd, 1);
+ pc_stop_attack(sd);
+
+ if(target_id<0 && -target_id == sd->bl.id) // for disguises [Valaris]
+ target_id = sd->bl.id;
+
+ switch(action_type)
+ {
+ case 0x00: // once attack
+ case 0x07: // continuous attack
+
+ if( pc_cant_act(sd) || sd->sc.option&OPTION_HIDE )
+ return;
+
+ if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
+ return;
+
+ if( sd->sc.data[SC_BASILICA] || sd->sc.data[SC__SHADOWFORM] )
+ return;
+
+ if (!battle_config.sdelay_attack_enable && pc_checkskill(sd, SA_FREECAST) <= 0) {
+ if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) {
+ clif_skill_fail(sd, 1, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+ }
+
+ pc_delinvincibletimer(sd);
+ sd->idletime = last_tick;
+ unit_attack(&sd->bl, target_id, action_type != 0);
+ break;
+ case 0x02: // sitdown
+ if (battle_config.basic_skill_check && pc_checkskill(sd, NV_BASIC) < 3) {
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 2);
+ break;
+ }
+
+ if(pc_issit(sd)) {
+ //Bugged client? Just refresh them.
+ clif_sitting(&sd->bl);
+ return;
+ }
+
+ if (sd->ud.skilltimer != INVALID_TIMER || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING ))
+ break;
+
+ if (sd->sc.count && (
+ sd->sc.data[SC_DANCING] ||
+ (sd->sc.data[SC_GRAVITATION] && sd->sc.data[SC_GRAVITATION]->val3 == BCT_SELF)
+ )) //No sitting during these states either.
+ break;
+
+ pc_setsit(sd);
+ skill_sit(sd,1);
+ clif_sitting(&sd->bl);
+ break;
+ case 0x03: // standup
+ if (!pc_issit(sd)) {
+ //Bugged client? Just refresh them.
+ clif_standing(&sd->bl);
+ return;
+ }
+ pc_setstand(sd);
+ skill_sit(sd,0);
+ clif_standing(&sd->bl);
+ break;
+ }
+}
+
+
+/// Request for an action.
+/// 0089 <target id>.L <action>.B (CZ_REQUEST_ACT)
+/// 0437 <target id>.L <action>.B (CZ_REQUEST_ACT2)
+/// action:
+/// 0 = attack
+/// 1 = pick up item
+/// 2 = sit down
+/// 3 = stand up
+/// 7 = continous attack
+/// 12 = (touch skill?)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_ActionRequest(int fd, struct map_session_data *sd)
+{
+ clif_parse_ActionRequest_sub(sd,
+ RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]),
+ RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]),
+ gettick()
+ );
+}
+
+
+/// Response to the death/system menu (CZ_RESTART).
+/// 00b2 <type>.B
+/// type:
+/// 0 = restart (respawn)
+/// 1 = char-select (disconnect)
+void clif_parse_Restart(int fd, struct map_session_data *sd)
+{
+ switch(RFIFOB(fd,2)) {
+ case 0x00:
+ pc_respawn(sd,CLR_RESPAWN);
+ break;
+ case 0x01:
+ /* Rovert's Prevent logout option - Fixed [Valaris] */
+ if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] &&
+ (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) )
+ { //Send to char-server for character selection.
+ chrif_charselectreq(sd, session[fd]->client_addr);
+ } else {
+ clif_disconnect_ack(sd, 1);
+ }
+ break;
+ }
+}
+
+
+/// Validates and processes whispered messages (CZ_WHISPER).
+/// 0096 <packet len>.W <nick>.24B <message>.?B
+void clif_parse_WisMessage(int fd, struct map_session_data* sd)
+{
+ struct map_session_data* dstsd;
+ int i;
+
+ char *target, *message;
+ int namelen, messagelen;
+
+ // validate packet and retrieve name and message
+ if( !clif_process_message(sd, 1, &target, &namelen, &message, &messagelen) )
+ return;
+
+ if ( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT))
+ return;
+
+ if (battle_config.min_chat_delay) { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) {
+ return;
+ }
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ // Chat logging type 'W' / Whisper
+ log_chat(LOG_CHAT_WHISPER, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, target, message);
+
+ //-------------------------------------------------------//
+ // Lordalfa - Paperboy - To whisper NPC commands //
+ //-------------------------------------------------------//
+ if (target[0] && (strncasecmp(target,"NPC:",4) == 0) && (strlen(target) > 4))
+ {
+ char* str = target+4; //Skip the NPC: string part.
+ struct npc_data* npc;
+ if ((npc = npc_name2id(str))) {
+ char split_data[NUM_WHISPER_VAR][CHAT_SIZE_MAX];
+ char *split;
+ char output[256];
+
+ str = message;
+ // skip codepage indicator, if detected
+ if( str[0] == '|' && strlen(str) >= 4 )
+ str += 3;
+ for( i = 0; i < NUM_WHISPER_VAR; ++i ) {// Splits the message using '#' as separators
+ split = strchr(str,'#');
+ if( split == NULL ) { // use the remaining string
+ safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i]));
+ for( ++i; i < NUM_WHISPER_VAR; ++i )
+ split_data[i][0] = '\0';
+ break;
+ }
+ *split = '\0';
+ safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i]));
+ str = split+1;
+ }
+
+ for( i = 0; i < NUM_WHISPER_VAR; ++i ) {
+ sprintf(output, "@whispervar%d$", i);
+ set_var(sd,output,(char *) split_data[i]);
+ }
+
+ sprintf(output, "%s::OnWhisperGlobal", npc->exname);
+ npc_event(sd,output,0); // Calls the NPC label
+
+ return;
+ }
+ } else if(strcmpi(target, main_chat_nick) == 0) { // Main chat [LuzZza]
+ if(!sd->state.mainchat)
+ clif_displaymessage(fd, msg_txt(388)); // You should enable main chat with "@main on" command.
+ else {
+ // send the main message using inter-server system
+ intif_main_message( sd, message );
+ }
+
+ return;
+ }
+
+ // searching destination character
+ dstsd = map_nick2sd(target);
+
+ if (dstsd == NULL || strcmp(dstsd->status.name, target) != 0) {
+ // player is not on this map-server
+ // At this point, don't send wisp/page if it's not exactly the same name, because (example)
+ // if there are 'Test' player on an other map-server and 'test' player on this map-server,
+ // and if we ask for 'Test', we must not contact 'test' player
+ // so, we send information to inter-server, which is the only one which decide (and copy correct name).
+ intif_wis_message(sd, target, message, messagelen);
+ return;
+ }
+
+ // if player ignores everyone
+ if (dstsd->state.ignoreAll) {
+ if (dstsd->sc.option & OPTION_INVISIBLE && pc_get_group_level(sd) < pc_get_group_level(dstsd))
+ clif_wis_end(fd, 1); // 1: target character is not loged in
+ else
+ clif_wis_end(fd, 3); // 3: everyone ignored by target
+ return;
+ }
+
+ // if player is autotrading
+ if( dstsd->state.autotrade == 1 ) {
+ char output[256];
+ sprintf(output, "%s is in autotrade mode and cannot receive whispered messages.", dstsd->status.name);
+ clif_wis_message(fd, wisp_server_name, output, strlen(output) + 1);
+ return;
+ }
+
+ // if player ignores the source character
+ ARR_FIND(0, MAX_IGNORE_LIST, i, dstsd->ignore[i].name[0] == '\0' || strcmp(dstsd->ignore[i].name, sd->status.name) == 0);
+ if(i < MAX_IGNORE_LIST && dstsd->ignore[i].name[0] != '\0') { // source char present in ignore list
+ clif_wis_end(fd, 2); // 2: ignored by target
+ return;
+ }
+
+ // notify sender of success
+ clif_wis_end(fd, 0); // 0: success to send wisper
+
+ // Normal message
+ clif_wis_message(dstsd->fd, sd->status.name, message, messagelen);
+}
+
+
+/// /b /nb (CZ_BROADCAST).
+/// Request to broadcast a message on whole server.
+/// 0099 <packet len>.W <text>.?B 00
+void clif_parse_Broadcast(int fd, struct map_session_data* sd) {
+ char command[CHAT_SIZE_MAX+11];
+ char* msg = (char*)RFIFOP(fd,4);
+ unsigned int len = RFIFOW(fd,2)-4;
+
+ // as the length varies depending on the command used, just block unreasonably long strings
+ mes_len_check(msg, len, CHAT_SIZE_MAX);
+
+ sprintf(command, "%ckami %s", atcommand_symbol, msg);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Request to pick up an item.
+/// 009f <id>.L (CZ_ITEM_PICKUP)
+/// 0362 <id>.L (CZ_ITEM_PICKUP2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_TakeItem(int fd, struct map_session_data *sd)
+{
+ struct flooritem_data *fitem;
+ int map_object_id;
+
+ map_object_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ fitem = (struct flooritem_data*)map_id2bl(map_object_id);
+
+ do {
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ break;
+ }
+
+ if (fitem == NULL || fitem->bl.type != BL_ITEM || fitem->bl.m != sd->bl.m)
+ break;
+
+ if( sd->sc.cant.pickup )
+ break;
+
+ if (pc_cant_act(sd))
+ break;
+
+ if (!pc_takeitem(sd, fitem))
+ break;
+
+ return;
+ } while (0);
+ // Client REQUIRES a fail packet or you can no longer pick items.
+ clif_additem(sd,0,0,6);
+}
+
+
+/// Request to drop an item.
+/// 00a2 <index>.W <amount>.W (CZ_ITEM_THROW)
+/// 0363 <index>.W <amount>.W (CZ_ITEM_THROW2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_DropItem(int fd, struct map_session_data *sd)
+{
+ int item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ int item_amount = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+
+ for(;;) {
+ if (pc_isdead(sd))
+ break;
+
+ if (pc_cant_act(sd))
+ break;
+
+ if (sd->sc.count && (
+ sd->sc.data[SC_AUTOCOUNTER] ||
+ sd->sc.data[SC_BLADESTOP] ||
+ (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOITEM)
+ ))
+ break;
+
+ if (!pc_dropitem(sd, item_index, item_amount))
+ break;
+
+ return;
+ }
+
+ //Because the client does not like being ignored.
+ clif_dropitem(sd, item_index,0);
+}
+
+
+/// Request to use an item.
+/// 00a7 <index>.W <account id>.L (CZ_USE_ITEM)
+/// 0439 <index>.W <account id>.L (CZ_USE_ITEM2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseItem(int fd, struct map_session_data *sd)
+{
+ int n;
+
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ return;
+ }
+
+ //This flag enables you to use items while in an NPC. [Skotlex]
+ if (sd->npc_id) {
+ if (sd->npc_id != sd->npc_item_flag)
+ return;
+ }
+ else if (pc_istrading(sd))
+ return;
+
+ //Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+ n = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+
+ if(n <0 || n >= MAX_INVENTORY)
+ return;
+ if (!pc_useitem(sd,n))
+ clif_useitemack(sd,n,0,false); //Send an empty ack packet or the client gets stuck.
+}
+
+
+/// Request to equip an item (CZ_REQ_WEAR_EQUIP).
+/// 00a9 <index>.W <position>.W
+void clif_parse_EquipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+
+ if(pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl,CLR_DEAD);
+ return;
+ }
+ index = RFIFOW(fd,2)-2;
+ if (index < 0 || index >= MAX_INVENTORY)
+ return; //Out of bounds check.
+
+ if(sd->npc_id) {
+ if (sd->npc_id != sd->npc_item_flag)
+ return;
+ } 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;
+
+ if(!sd->status.inventory[index].identify) {
+ clif_equipitemack(sd,index,0,0); // fail
+ return;
+ }
+
+ if(!sd->inventory_data[index])
+ return;
+
+ if(sd->inventory_data[index]->type == IT_PETARMOR){
+ pet_equipitem(sd,index);
+ return;
+ }
+
+ //Client doesn't send the position for ammo.
+ if(sd->inventory_data[index]->type == IT_AMMO)
+ pc_equipitem(sd,index,EQP_AMMO);
+ else
+ pc_equipitem(sd,index,RFIFOW(fd,4));
+}
+
+
+/// Request to take off an equip (CZ_REQ_TAKEOFF_EQUIP).
+/// 00ab <index>.W
+void clif_parse_UnequipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+
+ if(pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl,CLR_DEAD);
+ return;
+ }
+
+ 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;
+
+ index = RFIFOW(fd,2)-2;
+
+ pc_unequipitem(sd,index,1);
+}
+
+
+/// Request to start a conversation with an NPC (CZ_CONTACTNPC).
+/// 0090 <id>.L <type>.B
+/// type:
+/// 1 = click
+void clif_parse_NpcClicked(int fd,struct map_session_data *sd)
+{
+ struct block_list *bl;
+
+ if(pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl,CLR_DEAD);
+ return;
+ }
+
+ if (pc_cant_act(sd))
+ return;
+
+ bl = map_id2bl(RFIFOL(fd,2));
+ if (!bl) return;
+ switch (bl->type) {
+ case BL_MOB:
+ case BL_PC:
+ clif_parse_ActionRequest_sub(sd, 0x07, bl->id, gettick());
+ break;
+ case BL_NPC:
+ if( bl->m != -1 )// the user can't click floating npcs directly (hack attempt)
+ npc_click(sd,(TBL_NPC*)bl);
+ break;
+ }
+}
+
+
+/// Selection between buy/sell was made (CZ_ACK_SELECT_DEALTYPE).
+/// 00c5 <id>.L <type>.B
+/// type:
+/// 0 = buy
+/// 1 = sell
+void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd)
+{
+ if (sd->state.trading)
+ return;
+ npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+
+/// Notification about the result of a purchase attempt from an NPC shop (ZC_PC_PURCHASE_RESULT).
+/// 00ca <result>.B
+/// result:
+/// 0 = "The deal has successfully completed."
+/// 1 = "You do not have enough zeny."
+/// 2 = "You are over your Weight Limit."
+/// 3 = "Out of the maximum capacity, you have too many items."
+void clif_npc_buy_result(struct map_session_data* sd, unsigned char result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xca));
+ WFIFOW(fd,0) = 0xca;
+ WFIFOB(fd,2) = result;
+ WFIFOSET(fd,packet_len(0xca));
+}
+
+
+/// Request to buy chosen items from npc shop (CZ_PC_PURCHASE_ITEMLIST).
+/// 00c8 <packet len>.W { <amount>.W <name id>.W }*
+void clif_parse_NpcBuyListSend(int fd, struct map_session_data* sd)
+{
+ int n = (RFIFOW(fd,2)-4) /4;
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,4);
+ int result;
+
+ if( sd->state.trading || !sd->npc_shopid )
+ result = 1;
+ else
+ result = npc_buylist(sd,n,item_list);
+
+ sd->npc_shopid = 0; //Clear shop data.
+
+ clif_npc_buy_result(sd, result);
+}
+
+
+/// Notification about the result of a sell attempt to an NPC shop (ZC_PC_SELL_RESULT).
+/// 00cb <result>.B
+/// result:
+/// 0 = "The deal has successfully completed."
+/// 1 = "The deal has failed."
+void clif_npc_sell_result(struct map_session_data* sd, unsigned char result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xcb));
+ WFIFOW(fd,0) = 0xcb;
+ WFIFOB(fd,2) = result;
+ WFIFOSET(fd,packet_len(0xcb));
+}
+
+
+/// Request to sell chosen items to npc shop (CZ_PC_SELL_ITEMLIST).
+/// 00c9 <packet len>.W { <index>.W <amount>.W }*
+void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+
+ n = (RFIFOW(fd,2)-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,4);
+
+ if (sd->state.trading || !sd->npc_shopid)
+ fail = 1;
+ else
+ fail = npc_selllist(sd,n,item_list);
+
+ sd->npc_shopid = 0; //Clear shop data.
+
+ clif_npc_sell_result(sd, fail);
+}
+
+
+/// Chatroom creation request (CZ_CREATE_CHATROOM).
+/// 00d5 <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B
+/// type:
+/// 0 = private
+/// 1 = public
+void clif_parse_CreateChatRoom(int fd, struct map_session_data* sd)
+{
+ int len = RFIFOW(fd,2)-15;
+ int limit = RFIFOW(fd,4);
+ bool pub = (RFIFOB(fd,6) != 0);
+ const char* password = (char*)RFIFOP(fd,7); //not zero-terminated
+ const char* title = (char*)RFIFOP(fd,15); // not zero-terminated
+ char s_password[CHATROOM_PASS_SIZE];
+ char s_title[CHATROOM_TITLE_SIZE];
+
+ if (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM)
+ return;
+ if(battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 4) {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,3);
+ return;
+ }
+
+ if( len <= 0 )
+ return; // invalid input
+
+ safestrncpy(s_password, password, CHATROOM_PASS_SIZE);
+ safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte
+
+ chat_createpcchat(sd, s_title, s_password, limit, pub);
+}
+
+
+/// Chatroom join request (CZ_REQ_ENTER_ROOM).
+/// 00d9 <chat ID>.L <passwd>.8B
+void clif_parse_ChatAddMember(int fd, struct map_session_data* sd)
+{
+ int chatid = RFIFOL(fd,2);
+ const char* password = (char*)RFIFOP(fd,6); // not zero-terminated
+
+ chat_joinchat(sd,chatid,password);
+}
+
+
+/// Chatroom properties adjustment request (CZ_CHANGE_CHATROOM).
+/// 00de <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B
+/// type:
+/// 0 = private
+/// 1 = public
+void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data* sd)
+{
+ int len = RFIFOW(fd,2)-15;
+ int limit = RFIFOW(fd,4);
+ bool pub = (RFIFOB(fd,6) != 0);
+ const char* password = (char*)RFIFOP(fd,7); // not zero-terminated
+ const char* title = (char*)RFIFOP(fd,15); // not zero-terminated
+ char s_password[CHATROOM_PASS_SIZE];
+ char s_title[CHATROOM_TITLE_SIZE];
+
+ if( len <= 0 )
+ return; // invalid input
+
+ safestrncpy(s_password, password, CHATROOM_PASS_SIZE);
+ safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte
+
+ chat_changechatstatus(sd, s_title, s_password, limit, pub);
+}
+
+
+/// Request to change the chat room ownership (CZ_REQ_ROLE_CHANGE).
+/// 00e0 <role>.L <nick>.24B
+/// role:
+/// 0 = owner
+/// 1 = normal
+void clif_parse_ChangeChatOwner(int fd, struct map_session_data* sd)
+{
+ chat_changechatowner(sd,(char*)RFIFOP(fd,6));
+}
+
+
+/// Request to expel a player from chat room (CZ_REQ_EXPEL_MEMBER).
+/// 00e2 <name>.24B
+void clif_parse_KickFromChat(int fd,struct map_session_data *sd)
+{
+ chat_kickchat(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// Request to leave the current chatroom (CZ_EXIT_ROOM).
+/// 00e3
+void clif_parse_ChatLeave(int fd, struct map_session_data* sd)
+{
+ chat_leavechat(sd,0);
+}
+
+
+//Handles notifying asker and rejecter of what has just ocurred.
+//Type is used to determine the correct msg_txt to use:
+//0:
+static void clif_noask_sub(struct map_session_data *src, struct map_session_data *target, int type)
+{
+ const char* msg;
+ char output[256];
+ // Your request has been rejected by autoreject option.
+ msg = msg_txt(392);
+ clif_disp_onlyself(src, msg, strlen(msg));
+ //Notice that a request was rejected.
+ snprintf(output, 256, msg_txt(393+type), src->status.name, 256);
+ clif_disp_onlyself(target, output, strlen(output));
+}
+
+
+/// Request to begin a trade (CZ_REQ_EXCHANGE_ITEM).
+/// 00e4 <account id>.L
+void clif_parse_TradeRequest(int fd,struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ if(!sd->chatID && pc_cant_act(sd))
+ return; //You can trade while in a chatroom.
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 0);
+ return;
+ }
+
+ if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 1)
+ {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,0);
+ return;
+ }
+
+ trade_traderequest(sd,t_sd);
+}
+
+
+/// Answer to a trade request (CZ_ACK_EXCHANGE_ITEM).
+/// 00e6 <result>.B
+/// result:
+/// 3 = accepted
+/// 4 = rejected
+void clif_parse_TradeAck(int fd,struct map_session_data *sd)
+{
+ trade_tradeack(sd,RFIFOB(fd,2));
+}
+
+
+/// Request to add an item to current trade (CZ_ADD_EXCHANGE_ITEM).
+/// 00e8 <index>.W <amount>.L
+void clif_parse_TradeAddItem(int fd,struct map_session_data *sd)
+{
+ short index = RFIFOW(fd,2);
+ int amount = RFIFOL(fd,4);
+
+ if( index == 0 )
+ trade_tradeaddzeny(sd, amount);
+ else
+ trade_tradeadditem(sd, index, (short)amount);
+}
+
+
+/// Request to lock items in current trade (CZ_CONCLUDE_EXCHANGE_ITEM).
+/// 00eb
+void clif_parse_TradeOk(int fd,struct map_session_data *sd)
+{
+ trade_tradeok(sd);
+}
+
+
+/// Request to cancel current trade (CZ_CANCEL_EXCHANGE_ITEM).
+/// 00ed
+void clif_parse_TradeCancel(int fd,struct map_session_data *sd)
+{
+ trade_tradecancel(sd);
+}
+
+
+/// Request to commit current trade (CZ_EXEC_EXCHANGE_ITEM).
+/// 00ef
+void clif_parse_TradeCommit(int fd,struct map_session_data *sd)
+{
+ trade_tradecommit(sd);
+}
+
+
+/// Request to stop chasing/attacking an unit (CZ_CANCEL_LOCKON).
+/// 0118
+void clif_parse_StopAttack(int fd,struct map_session_data *sd)
+{
+ pc_stop_attack(sd);
+}
+
+
+/// Request to move an item from inventory to cart (CZ_MOVE_ITEM_FROM_BODY_TO_CART).
+/// 0126 <index>.W <amount>.L
+void clif_parse_PutItemToCart(int fd,struct map_session_data *sd)
+{
+ if (pc_istrading(sd))
+ return;
+ if (!pc_iscarton(sd))
+ return;
+ pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+
+
+/// Request to move an item from cart to inventory (CZ_MOVE_ITEM_FROM_CART_TO_BODY).
+/// 0127 <index>.W <amount>.L
+void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd)
+{
+ if (!pc_iscarton(sd))
+ return;
+ pc_getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+
+
+/// Request to remove cart/falcon/peco/dragon (CZ_REQ_CARTOFF).
+/// 012a
+void clif_parse_RemoveOption(int fd,struct map_session_data *sd)
+{
+ /**
+ * Attempts to remove these options when this function is called (will remove all available)
+ **/
+#ifdef NEW_CARTS
+ pc_setoption(sd,sd->sc.option&~(OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
+ if( sd->sc.data[SC_PUSH_CART] )
+ pc_setcart(sd,0);
+#else
+ pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
+#endif
+}
+
+
+/// Request to change cart's visual look (CZ_REQ_CHANGECART).
+/// 01af <num>.W
+void clif_parse_ChangeCart(int fd,struct map_session_data *sd)
+{// TODO: State tracking?
+ int type;
+
+ if( sd && pc_checkskill(sd, MC_CHANGECART) < 1 )
+ return;
+
+ type = (int)RFIFOW(fd,2);
+#ifdef NEW_CARTS
+ if( (type == 9 && sd->status.base_level > 131) ||
+ (type == 8 && sd->status.base_level > 121) ||
+ (type == 7 && sd->status.base_level > 111) ||
+ (type == 6 && sd->status.base_level > 101) ||
+ (type == 5 && sd->status.base_level > 90) ||
+ (type == 4 && sd->status.base_level > 80) ||
+ (type == 3 && sd->status.base_level > 65) ||
+ (type == 2 && sd->status.base_level > 40) ||
+ (type == 1))
+#else
+ if( (type == 5 && sd->status.base_level > 90) ||
+ (type == 4 && sd->status.base_level > 80) ||
+ (type == 3 && sd->status.base_level > 65) ||
+ (type == 2 && sd->status.base_level > 40) ||
+ (type == 1))
+#endif
+ pc_setcart(sd,type);
+}
+
+
+/// Request to increase status (CZ_STATUS_CHANGE).
+/// 00bb <status id>.W <amount>.B
+/// status id:
+/// SP_STR ~ SP_LUK
+/// amount:
+/// client sends always 1 for this, even when using /str+ and
+/// the like
+void clif_parse_StatusUp(int fd,struct map_session_data *sd)
+{
+ pc_statusup(sd,RFIFOW(fd,2));
+}
+
+
+/// Request to increase level of a skill (CZ_UPGRADE_SKILLLEVEL).
+/// 0112 <skill id>.W
+void clif_parse_SkillUp(int fd,struct map_session_data *sd)
+{
+ pc_skillup(sd,RFIFOW(fd,2));
+}
+
+static void clif_parse_UseSkillToId_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id)
+{
+ int lv;
+
+ if( !hd )
+ return;
+ if( skillnotok_hom(skill_id, hd) )
+ return;
+ if( hd->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
+ target_id = hd->bl.id;
+ if( hd->ud.skilltimer != INVALID_TIMER )
+ {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
+ }
+ else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 )
+ return;
+
+ lv = merc_hom_checkskill(hd, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_id(&hd->bl, target_id, skill_id, skill_lv);
+}
+
+static void clif_parse_UseSkillToPos_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo)
+{
+ int lv;
+ if( !hd )
+ return;
+ if( skillnotok_hom(skill_id, hd) )
+ return;
+ if( hd->ud.skilltimer != INVALID_TIMER ) {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
+ } else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 )
+ return;
+
+ if( hd->sc.data[SC_BASILICA] )
+ return;
+ lv = merc_hom_checkskill(hd, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_pos(&hd->bl, x, y, skill_id, skill_lv);
+}
+
+static void clif_parse_UseSkillToId_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id)
+{
+ int lv;
+
+ if( !md )
+ return;
+ if( skillnotok_mercenary(skill_id, md) )
+ return;
+ if( md->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
+ target_id = md->bl.id;
+ if( md->ud.skilltimer != INVALID_TIMER )
+ {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
+ }
+ else if( DIFF_TICK(tick, md->ud.canact_tick) < 0 )
+ return;
+
+ lv = mercenary_checkskill(md, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_id(&md->bl, target_id, skill_id, skill_lv);
+}
+
+static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo)
+{
+ int lv;
+ if( !md )
+ return;
+ if( skillnotok_mercenary(skill_id, md) )
+ return;
+ if( md->ud.skilltimer != INVALID_TIMER )
+ return;
+ if( DIFF_TICK(tick, md->ud.canact_tick) < 0 )
+ {
+ clif_skill_fail(md->master, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+
+ if( md->sc.data[SC_BASILICA] )
+ return;
+ lv = mercenary_checkskill(md, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_pos(&md->bl, x, y, skill_id, skill_lv);
+}
+
+
+/// Request to use a targeted skill.
+/// 0113 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL)
+/// 0438 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
+{
+ uint16 skill_id, skill_lv;
+ int tmp, target_id;
+ unsigned int tick = gettick();
+
+ skill_lv = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ skill_id = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]);
+
+ if( skill_lv < 1 ) skill_lv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex]
+
+ tmp = skill_get_inf(skill_id);
+ if (tmp&INF_GROUND_SKILL || !tmp)
+ return; //Using a ground/passive skill on a target? WRONG.
+
+ if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL )
+ {
+ clif_parse_UseSkillToId_homun(sd->hd, sd, tick, skill_id, skill_lv, target_id);
+ return;
+ }
+
+ if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL )
+ {
+ clif_parse_UseSkillToId_mercenary(sd->md, sd, tick, skill_id, skill_lv, target_id);
+ return;
+ }
+
+ // Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if( pc_cant_act(sd) && skill_id != RK_REFRESH && !(skill_id == SR_GENTLETOUCH_CURE && (sd->sc.opt1 == OPT1_STONE || sd->sc.opt1 == OPT1_FREEZE || sd->sc.opt1 == OPT1_STUN)) )
+ return;
+ if( pc_issit(sd) )
+ return;
+
+ if( skillnotok(skill_id, sd) )
+ return;
+
+ if( sd->bl.id != target_id && tmp&INF_SELF_SKILL )
+ target_id = sd->bl.id; // never trust the client
+
+ if( target_id < 0 && -target_id == sd->bl.id ) // for disguises [Valaris]
+ target_id = sd->bl.id;
+
+ if( sd->ud.skilltimer != INVALID_TIMER )
+ {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST )
+ return;
+ }
+ else if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 )
+ {
+ if( sd->skillitem != skill_id )
+ {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+ }
+
+ if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
+ return;
+
+ if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
+ return; // On basilica only caster can use Basilica again to stop it.
+
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
+ return; //Can't use skills while a menu is open.
+ }
+ if( sd->skillitem == skill_id ) {
+ if( skill_lv != sd->skillitemlv )
+ skill_lv = sd->skillitemlv;
+ if( !(tmp&INF_SELF_SKILL) )
+ pc_delinvincibletimer(sd); // Target skills thru items cancel invincibility. [Inkfish]
+ unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv);
+ return;
+ }
+
+ sd->skillitem = sd->skillitemlv = 0;
+
+ if( skill_id >= GD_SKILLBASE ) {
+ if( sd->state.gmaster_flag )
+ skill_lv = guild_checkskill(sd->state.gmaster_flag, skill_id);
+ else
+ skill_lv = 0;
+ } else {
+ tmp = pc_checkskill(sd, skill_id);
+ if( skill_lv > tmp )
+ skill_lv = tmp;
+ }
+
+ pc_delinvincibletimer(sd);
+
+ if( skill_lv )
+ unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv);
+}
+
+/*==========================================
+ * Client tells server he'd like to use AoE skill id 'skill_id' of level 'skill_lv' on 'x','y' location
+ *------------------------------------------*/
+static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uint16 skill_lv, uint16 skill_id, short x, short y, int skillmoreinfo)
+{
+ unsigned int tick = gettick();
+
+ if( !(skill_get_inf(skill_id)&INF_GROUND_SKILL) )
+ return; //Using a target skill on the ground? WRONG.
+
+ if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) {
+ clif_parse_UseSkillToPos_homun(sd->hd, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
+ return;
+ }
+
+ if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL )
+ {
+ clif_parse_UseSkillToPos_mercenary(sd->md, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
+ return;
+ }
+
+ //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if( skillnotok(skill_id, sd) )
+ return;
+ if( skillmoreinfo != -1 )
+ {
+ if( pc_issit(sd) )
+ {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ return;
+ }
+ //You can't use Graffiti/TalkieBox AND have a vending open, so this is safe.
+ safestrncpy(sd->message, (char*)RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
+ }
+
+ if( sd->ud.skilltimer != INVALID_TIMER )
+ return;
+
+ if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) {
+ if( sd->skillitem != skill_id ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+ }
+
+ if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
+ return;
+
+ if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
+ return; // On basilica only caster can use Basilica again to stop it.
+
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
+ return; //Can't use skills while a menu is open.
+ }
+
+ pc_delinvincibletimer(sd);
+
+ if( sd->skillitem == skill_id ) {
+ if( skill_lv != sd->skillitemlv )
+ skill_lv = sd->skillitemlv;
+ unit_skilluse_pos(&sd->bl, x, y, skill_id, skill_lv);
+ } else {
+ int lv;
+ sd->skillitem = sd->skillitemlv = 0;
+ if( (lv = pc_checkskill(sd, skill_id)) > 0 ) {
+ if( skill_lv > lv )
+ skill_lv = lv;
+ unit_skilluse_pos(&sd->bl, x, y, skill_id,skill_lv);
+ }
+ }
+}
+
+
+/// Request to use a ground skill.
+/// 0116 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND)
+/// 0366 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd)
+{
+ if (pc_cant_act(sd))
+ return;
+ if (pc_issit(sd))
+ return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ -1 //Skill more info.
+ );
+}
+
+
+/// Request to use a ground skill with text.
+/// 0190 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX)
+/// 0367 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd)
+{
+ if (pc_cant_act(sd))
+ return;
+ if (pc_issit(sd))
+ return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //Skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //Skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[4] //skill more info
+ );
+}
+
+
+/// Answer to map selection dialog (CZ_SELECT_WARPPOINT).
+/// 011b <skill id>.W <map name>.16B
+void clif_parse_UseSkillMap(int fd, struct map_session_data* sd)
+{
+ uint16 skill_id = RFIFOW(fd,2);
+ char map_name[MAP_NAME_LENGTH];
+ mapindex_getmapname((char*)RFIFOP(fd,4), map_name);
+
+ if(skill_id != sd->menuskill_id)
+ return;
+
+ if( pc_cant_act(sd) ) {
+ clif_menuskill_clear(sd);
+ return;
+ }
+
+ pc_delinvincibletimer(sd);
+ skill_castend_map(sd,skill_id,map_name);
+}
+
+
+/// Request to set a memo on current map (CZ_REMEMBER_WARPPOINT).
+/// 011d
+void clif_parse_RequestMemo(int fd,struct map_session_data *sd)
+{
+ if (!pc_isdead(sd))
+ pc_memo(sd,-1);
+}
+
+
+/// Answer to pharmacy item selection dialog (CZ_REQMAKINGITEM).
+/// 018e <name id>.W { <material id>.W }*3
+void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
+{
+ switch( sd->menuskill_id ) {
+ case -1:
+ case AM_PHARMACY:
+ case RK_RUNEMASTERY:
+ case GC_RESEARCHNEWPOISON:
+ break;
+ default:
+ 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.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ if( skill_can_produce_mix(sd,RFIFOW(fd,2),sd->menuskill_val, 1) )
+ skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to mixing item selection dialog (CZ_REQ_MAKINGITEM).
+/// 025b <mk type>.W <name id>.W
+/// mk type:
+/// 1 = cooking
+/// 2 = arrow
+/// 3 = elemental
+/// 4 = GN_MIX_COOKING
+/// 5 = GN_MAKEBOMB
+/// 6 = GN_S_PHARMACY
+void clif_parse_Cooking(int fd,struct map_session_data *sd) {
+ int type = RFIFOW(fd,2);
+ int nameid = RFIFOW(fd,4);
+ int amount = sd->menuskill_val2?sd->menuskill_val2:1;
+ if( type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY )
+ return;
+
+ if (pc_istrading(sd)) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, amount) )
+ skill_produce_mix(sd,0,nameid,0,0,0,amount);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to repair weapon item selection dialog (CZ_REQ_ITEMREPAIR).
+/// 01fd <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W
+void clif_parse_RepairItem(int fd, struct map_session_data *sd)
+{
+ if (sd->menuskill_id != BS_REPAIRWEAPON)
+ 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.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ skill_repairweapon(sd,RFIFOW(fd,2));
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to refine weapon item selection dialog (CZ_REQ_WEAPONREFINE).
+/// 0222 <index>.L
+void clif_parse_WeaponRefine(int fd, struct map_session_data *sd)
+{
+ int idx;
+
+ if (sd->menuskill_id != WS_WEAPONREFINE) //Packet exploit?
+ 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.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ idx = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ skill_weaponrefine(sd, idx-2);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to script menu dialog (CZ_CHOOSE_MENU).
+/// 00b8 <npc id>.L <choice>.B
+/// choice:
+/// 1~254 = menu item
+/// 255 = cancel
+/// NOTE: If there were more than 254 items in the list, choice
+/// overflows to choice%256.
+void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
+{
+ int npc_id = RFIFOL(fd,2);
+ uint8 select = RFIFOB(fd,6);
+
+ if( (select > sd->npc_menu && select != 0xff) || select == 0 )
+ {
+ TBL_NPC* nd = map_id2nd(npc_id);
+ ShowWarning("Invalid menu selection on npc %d:'%s' - got %d, valid range is [%d..%d] (player AID:%d, CID:%d, name:'%s')!\n", npc_id, (nd)?nd->name:"invalid npc id", select, 1, sd->npc_menu, sd->bl.id, sd->status.char_id, sd->status.name);
+ clif_GM_kick(NULL,sd);
+ return;
+ }
+
+ sd->npc_menu = select;
+ npc_scriptcont(sd,npc_id);
+}
+
+
+/// NPC dialog 'next' click (CZ_REQ_NEXT_SCRIPT).
+/// 00b9 <npc id>.L
+void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd)
+{
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+
+/// NPC numeric input dialog value (CZ_INPUT_EDITDLG).
+/// 0143 <npc id>.L <value>.L
+void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
+{
+ int npcid = RFIFOL(fd,2);
+ int amount = (int)RFIFOL(fd,6);
+
+ sd->npc_amount = amount;
+ npc_scriptcont(sd, npcid);
+}
+
+
+/// NPC text input dialog value (CZ_INPUT_EDITDLGSTR).
+/// 01d5 <packet len>.W <npc id>.L <string>.?B
+void clif_parse_NpcStringInput(int fd, struct map_session_data* sd)
+{
+ int message_len = RFIFOW(fd,2)-8;
+ int npcid = RFIFOL(fd,4);
+ const char* message = (char*)RFIFOP(fd,8);
+
+ if( message_len <= 0 )
+ return; // invalid input
+
+ safestrncpy(sd->npc_str, message, min(message_len,CHATBOX_SIZE));
+ npc_scriptcont(sd, npcid);
+}
+
+
+/// NPC dialog 'close' click (CZ_CLOSE_DIALOG).
+/// 0146 <npc id>.L
+void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd)
+{
+ if (!sd->npc_id) //Avoid parsing anything when the script was done with. [Skotlex]
+ return;
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+
+/// Answer to identify item selection dialog (CZ_REQ_ITEMIDENTIFY).
+/// 0178 <index>.W
+/// index:
+/// -1 = cancel
+void clif_parse_ItemIdentify(int fd,struct map_session_data *sd)
+{
+ short idx = RFIFOW(fd,2);
+
+ if (sd->menuskill_id != MC_IDENTIFY)
+ return;
+ if( idx == -1 ) {// cancel pressed
+ clif_menuskill_clear(sd);
+ return;
+ }
+ skill_identify(sd,idx-2);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to arrow crafting item selection dialog (CZ_REQ_MAKINGARROW).
+/// 01ae <name id>.W
+void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
+{
+ 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.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ switch( sd->menuskill_id ) {
+ case AC_MAKINGARROW:
+ skill_arrow_create(sd,RFIFOW(fd,2));
+ break;
+ case SA_CREATECON:
+ skill_produce_mix(sd,SA_CREATECON,RFIFOW(fd,2),0,0,0, 1);
+ 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;
+ }
+
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to SA_AUTOSPELL skill selection dialog (CZ_SELECTAUTOSPELL).
+/// 01ce <skill id>.L
+void clif_parse_AutoSpell(int fd,struct map_session_data *sd)
+{
+ if (sd->menuskill_id != SA_AUTOSPELL)
+ return;
+ skill_autospell(sd,RFIFOL(fd,2));
+ clif_menuskill_clear(sd);
+}
+
+
+/// Request to display item carding/composition list (CZ_REQ_ITEMCOMPOSITION_LIST).
+/// 017a <card index>.W
+void clif_parse_UseCard(int fd,struct map_session_data *sd)
+{
+ if (sd->state.trading != 0)
+ return;
+ clif_use_card(sd,RFIFOW(fd,2)-2);
+}
+
+
+/// Answer to carding/composing item selection dialog (CZ_REQ_ITEMCOMPOSITION).
+/// 017c <card index>.W <equip index>.W
+void clif_parse_InsertCard(int fd,struct map_session_data *sd)
+{
+ if (sd->state.trading != 0)
+ return;
+ pc_insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2);
+}
+
+
+/// Request of character's name by char ID.
+/// 0193 <char id>.L (CZ_REQNAME_BYGID)
+/// 0369 <char id>.L (CZ_REQNAME_BYGID2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_SolveCharName(int fd, struct map_session_data *sd)
+{
+ int charid;
+
+ charid = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ map_reqnickdb(sd, charid);
+}
+
+
+/// /resetskill /resetstate (CZ_RESET).
+/// Request to reset stats or skills.
+/// 0197 <type>.W
+/// type:
+/// 0 = state
+/// 1 = skill
+void clif_parse_ResetChar(int fd, struct map_session_data *sd) {
+ char cmd[15];
+
+ if( RFIFOW(fd,2) )
+ sprintf(cmd,"%cresetskill",atcommand_symbol);
+ else
+ sprintf(cmd,"%cresetstat",atcommand_symbol);
+
+ is_atcommand(fd, sd, cmd, 1);
+}
+
+
+/// /lb /nlb (CZ_LOCALBROADCAST).
+/// Request to broadcast a message on current map.
+/// 019c <packet len>.W <text>.?B
+void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd)
+{
+ char command[CHAT_SIZE_MAX+16];
+ char* msg = (char*)RFIFOP(fd,4);
+ unsigned int len = RFIFOW(fd,2)-4;
+
+ // as the length varies depending on the command used, just block unreasonably long strings
+ mes_len_check(msg, len, CHAT_SIZE_MAX);
+
+ sprintf(command, "%clkami %s", atcommand_symbol, msg);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Request to move an item from inventory to storage.
+/// 00f3 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE)
+/// 0364 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_MoveToKafra(int fd, struct map_session_data *sd)
+{
+ int item_index, item_amount;
+
+ if (pc_istrading(sd))
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ if (item_index < 0 || item_index >= MAX_INVENTORY || item_amount < 1)
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageadd(sd, item_index, item_amount);
+ else
+ if (sd->state.storage_flag == 2)
+ storage_guild_storageadd(sd, item_index, item_amount);
+}
+
+
+/// Request to move an item from storage to inventory.
+/// 00f5 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY)
+/// 0365 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd)
+{
+ int item_index, item_amount;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-1;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+
+ if (sd->state.storage_flag == 1)
+ storage_storageget(sd, item_index, item_amount);
+ else
+ if(sd->state.storage_flag == 2)
+ storage_guild_storageget(sd, item_index, item_amount);
+}
+
+
+/// Request to move an item from cart to storage (CZ_MOVE_ITEM_FROM_CART_TO_STORE).
+/// 0129 <index>.W <amount>.L
+void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd)
+{
+ if( sd->state.vending )
+ return;
+ if (!pc_iscarton(sd))
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+ else
+ if (sd->state.storage_flag == 2)
+ storage_guild_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+}
+
+
+/// Request to move an item from storage to cart (CZ_MOVE_ITEM_FROM_STORE_TO_CART).
+/// 0128 <index>.W <amount>.L
+void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd)
+{
+ if( sd->state.vending )
+ return;
+ if (!pc_iscarton(sd))
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+ else
+ if (sd->state.storage_flag == 2)
+ storage_guild_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+}
+
+
+/// Request to close storage (CZ_CLOSE_STORE).
+/// 00f7
+void clif_parse_CloseKafra(int fd, struct map_session_data *sd)
+{
+ if( sd->state.storage_flag == 1 )
+ storage_storageclose(sd);
+ else
+ if( sd->state.storage_flag == 2 )
+ storage_guild_storageclose(sd);
+}
+
+
+/// Displays kafra storage password dialog (ZC_REQ_STORE_PASSWORD).
+/// 023a <info>.W
+/// info:
+/// 0 = password has not been set yet
+/// 1 = storage is password-protected
+/// 8 = too many wrong passwords
+/// ? = ignored
+/// NOTE: This packet is only available on certain non-kRO clients.
+void clif_storagepassword(struct map_session_data* sd, short info)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x23a));
+ WFIFOW(fd,0) = 0x23a;
+ WFIFOW(fd,2) = info;
+ WFIFOSET(fd,packet_len(0x23a));
+}
+
+
+/// Answer to the kafra storage password dialog (CZ_ACK_STORE_PASSWORD).
+/// 023b <type>.W <password>.16B <new password>.16B
+/// type:
+/// 2 = change password
+/// 3 = check password
+/// NOTE: This packet is only available on certain non-kRO clients.
+void clif_parse_StoragePassword(int fd, struct map_session_data *sd)
+{
+ //TODO
+}
+
+
+/// Result of kafra storage password validation (ZC_RESULT_STORE_PASSWORD).
+/// 023c <result>.W <error count>.W
+/// result:
+/// 4 = password change success
+/// 5 = password change failure
+/// 6 = password check success
+/// 7 = password check failure
+/// 8 = too many wrong passwords
+/// ? = ignored
+/// NOTE: This packet is only available on certain non-kRO clients.
+void clif_storagepassword_result(struct map_session_data* sd, short result, short error_count)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x23c));
+ WFIFOW(fd,0) = 0x23c;
+ WFIFOW(fd,2) = result;
+ WFIFOW(fd,4) = error_count;
+ WFIFOSET(fd,packet_len(0x23c));
+}
+
+
+/// Party creation request
+/// 00f9 <party name>.24B (CZ_MAKE_GROUP)
+/// 01e8 <party name>.24B <item pickup rule>.B <item share rule>.B (CZ_MAKE_GROUP2)
+void clif_parse_CreateParty(int fd, struct map_session_data *sd)
+{
+ char* name = (char*)RFIFOP(fd,2);
+ name[NAME_LENGTH-1] = '\0';
+
+ if( map[sd->bl.m].flag.partylock )
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 )
+ {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4);
+ return;
+ }
+
+ party_create(sd,name,0,0);
+}
+
+void clif_parse_CreateParty2(int fd, struct map_session_data *sd)
+{
+ char* name = (char*)RFIFOP(fd,2);
+ int item1 = RFIFOB(fd,26);
+ int item2 = RFIFOB(fd,27);
+ name[NAME_LENGTH-1] = '\0';
+
+ if( map[sd->bl.m].flag.partylock )
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 )
+ {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4);
+ return;
+ }
+
+ party_create(sd,name,item1,item2);
+}
+
+
+/// Party invitation request
+/// 00fc <account id>.L (CZ_REQ_JOIN_GROUP)
+/// 02c4 <char name>.24B (CZ_PARTY_JOIN_REQ)
+void clif_parse_PartyInvite(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(map[sd->bl.m].flag.partylock)
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ if(t_sd && t_sd->state.noask)
+ {// @noask [LuzZza]
+ clif_noask_sub(sd, t_sd, 1);
+ return;
+ }
+
+ party_invite(sd, t_sd);
+}
+
+void clif_parse_PartyInvite2(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+ char *name = (char*)RFIFOP(fd,2);
+ name[NAME_LENGTH-1] = '\0';
+
+ if(map[sd->bl.m].flag.partylock)
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+
+ t_sd = map_nick2sd(name);
+
+ if(t_sd && t_sd->state.noask)
+ {// @noask [LuzZza]
+ clif_noask_sub(sd, t_sd, 1);
+ return;
+ }
+
+ party_invite(sd, t_sd);
+}
+
+
+/// Party invitation reply
+/// 00ff <party id>.L <flag>.L (CZ_JOIN_GROUP)
+/// 02c7 <party id>.L <flag>.B (CZ_PARTY_JOIN_REQ_ACK)
+/// flag:
+/// 0 = reject
+/// 1 = accept
+void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd)
+{
+ party_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+void clif_parse_ReplyPartyInvite2(int fd,struct map_session_data *sd)
+{
+ party_reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+
+/// Request to leave party (CZ_REQ_LEAVE_GROUP).
+/// 0100
+void clif_parse_LeaveParty(int fd, struct map_session_data *sd)
+{
+ if(map[sd->bl.m].flag.partylock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ party_leave(sd);
+}
+
+
+/// Request to expel a party member (CZ_REQ_EXPEL_GROUP_MEMBER).
+/// 0103 <account id>.L <char name>.24B
+void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd)
+{
+ if(map[sd->bl.m].flag.partylock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ party_removemember(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6));
+}
+
+
+/// Request to change party options.
+/// 0102 <exp share rule>.L (CZ_CHANGE_GROUPEXPOPTION)
+/// 07d7 <exp share rule>.L <item pickup rule>.B <item share rule>.B (CZ_GROUPINFO_CHANGE_V2)
+void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd)
+{
+ struct party_data *p;
+ int i;
+
+ if( !sd->status.party_id )
+ return;
+
+ p = party_search(sd->status.party_id);
+ if( p == NULL )
+ return;
+
+ ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd );
+ if( i == MAX_PARTY )
+ return; //Shouldn't happen
+
+ if( !p->party.member[i].leader )
+ return;
+
+#if PACKETVER < 20090603
+ //Client can't change the item-field
+ party_changeoption(sd, RFIFOL(fd,2), p->party.item);
+#else
+ party_changeoption(sd, RFIFOL(fd,2), ((RFIFOB(fd,6)?1:0)|(RFIFOB(fd,7)?2:0)));
+#endif
+}
+
+
+/// Validates and processes party messages (CZ_REQUEST_CHAT_PARTY).
+/// 0108 <packet len>.W <text>.?B (<name> : <message>) 00
+void clif_parse_PartyMessage(int fd, struct map_session_data* sd)
+{
+ const char* text = (char*)RFIFOP(fd,4);
+ int textlen = RFIFOW(fd,2) - 4;
+
+ char *name, *message;
+ int namelen, messagelen;
+
+ // validate packet and retrieve name and message
+ if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
+ return;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
+ return;
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ party_send_message(sd, text, textlen);
+}
+
+
+/// Changes Party Leader (CZ_CHANGE_GROUP_MASTER).
+/// 07da <account id>.L
+void clif_parse_PartyChangeLeader(int fd, struct map_session_data* sd)
+{
+ party_changeleader(sd, map_id2sd(RFIFOL(fd,2)));
+}
+
+
+/// Party Booking in KRO [Spiria]
+///
+
+/// Request to register a party booking advertisment (CZ_PARTY_BOOKING_REQ_REGISTER).
+/// 0802 <level>.W <map id>.W { <job>.W }*6
+void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data* sd)
+{
+ short level = RFIFOW(fd,2);
+ short mapid = RFIFOW(fd,4);
+ short job[PARTY_BOOKING_JOBS];
+ int i;
+
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ job[i] = RFIFOB(fd,6+i*2);
+
+ party_booking_register(sd, level, mapid, job);
+}
+
+
+/// Result of request to register a party booking advertisment (ZC_PARTY_BOOKING_ACK_REGISTER).
+/// 0803 <result>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = already registered
+void clif_PartyBookingRegisterAck(struct map_session_data *sd, int flag)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x803));
+ WFIFOW(fd,0) = 0x803;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x803));
+}
+
+
+/// Request to search for party booking advertisments (CZ_PARTY_BOOKING_REQ_SEARCH).
+/// 0804 <level>.W <map id>.W <job>.W <last index>.L <result count>.W
+void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data* sd)
+{
+ short level = RFIFOW(fd,2);
+ short mapid = RFIFOW(fd,4);
+ short job = RFIFOW(fd,6);
+ unsigned long lastindex = RFIFOL(fd,8);
+ short resultcount = RFIFOW(fd,12);
+
+ party_booking_search(sd, level, mapid, job, lastindex, resultcount);
+}
+
+
+/// Party booking search results (ZC_PARTY_BOOKING_ACK_SEARCH).
+/// 0805 <packet len>.W <more results>.B { <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6 }*
+/// more results:
+/// 0 = no
+/// 1 = yes
+void clif_PartyBookingSearchAck(int fd, struct party_booking_ad_info** results, int count, bool more_result)
+{
+ int i, j;
+ int size = sizeof(struct party_booking_ad_info); // structure size (48)
+ struct party_booking_ad_info *pb_ad;
+ WFIFOHEAD(fd,size*count + 5);
+ WFIFOW(fd,0) = 0x805;
+ WFIFOW(fd,2) = size*count + 5;
+ WFIFOB(fd,4) = more_result;
+ for(i=0; i<count; i++)
+ {
+ pb_ad = results[i];
+ WFIFOL(fd,i*size+5) = pb_ad->index;
+ memcpy(WFIFOP(fd,i*size+9),pb_ad->charname,NAME_LENGTH);
+ WFIFOL(fd,i*size+33) = pb_ad->starttime; // FIXME: This is expire time
+ WFIFOW(fd,i*size+37) = pb_ad->p_detail.level;
+ WFIFOW(fd,i*size+39) = pb_ad->p_detail.mapid;
+ for(j=0; j<PARTY_BOOKING_JOBS; j++)
+ WFIFOW(fd,i*size+41+j*2) = pb_ad->p_detail.job[j];
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Request to delete own party booking advertisment (CZ_PARTY_BOOKING_REQ_DELETE).
+/// 0806
+void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data* sd)
+{
+ if(party_booking_delete(sd))
+ clif_PartyBookingDeleteAck(sd, 0);
+}
+
+
+/// Result of request to delete own party booking advertisment (ZC_PARTY_BOOKING_ACK_DELETE).
+/// 0807 <result>.W
+/// result:
+/// 0 = success
+/// 1 = success (auto-removed expired ad)
+/// 2 = failure
+/// 3 = nothing registered
+void clif_PartyBookingDeleteAck(struct map_session_data* sd, int flag)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x807));
+ WFIFOW(fd,0) = 0x807;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x807));
+}
+
+
+/// Request to update party booking advertisment (CZ_PARTY_BOOKING_REQ_UPDATE).
+/// 0808 { <job>.W }*6
+void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data* sd)
+{
+ short job[PARTY_BOOKING_JOBS];
+ int i;
+
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ job[i] = RFIFOW(fd,2+i*2);
+
+ party_booking_update(sd, job);
+}
+
+
+/// Notification about new party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_INSERT).
+/// 0809 <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6
+void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad)
+{
+ int i;
+ uint8 buf[38+PARTY_BOOKING_JOBS*2];
+
+ if(pb_ad == NULL) return;
+
+ WBUFW(buf,0) = 0x809;
+ WBUFL(buf,2) = pb_ad->index;
+ memcpy(WBUFP(buf,6),pb_ad->charname,NAME_LENGTH);
+ WBUFL(buf,30) = pb_ad->starttime; // FIXME: This is expire time
+ WBUFW(buf,34) = pb_ad->p_detail.level;
+ WBUFW(buf,36) = pb_ad->p_detail.mapid;
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ WBUFW(buf,38+i*2) = pb_ad->p_detail.job[i];
+
+ clif_send(buf, packet_len(0x809), &sd->bl, ALL_CLIENT);
+}
+
+
+/// Notification about updated party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_UPDATE).
+/// 080a <index>.L { <job>.W }*6
+void clif_PartyBookingUpdateNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad)
+{
+ int i;
+ uint8 buf[6+PARTY_BOOKING_JOBS*2];
+
+ if(pb_ad == NULL) return;
+
+ WBUFW(buf,0) = 0x80a;
+ WBUFL(buf,2) = pb_ad->index;
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ WBUFW(buf,6+i*2) = pb_ad->p_detail.job[i];
+ clif_send(buf,packet_len(0x80a),&sd->bl,ALL_CLIENT); // Now UPDATE all client.
+}
+
+
+/// Notification about deleted party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_DELETE).
+/// 080b <index>.L
+void clif_PartyBookingDeleteNotify(struct map_session_data* sd, int index)
+{
+ uint8 buf[6];
+
+ WBUFW(buf,0) = 0x80b;
+ WBUFL(buf,2) = index;
+
+ clif_send(buf, packet_len(0x80b), &sd->bl, ALL_CLIENT); // Now UPDATE all client.
+}
+
+
+/// Request to close own vending (CZ_REQ_CLOSESTORE).
+/// 012e
+void clif_parse_CloseVending(int fd, struct map_session_data* sd)
+{
+ vending_closevending(sd);
+}
+
+
+/// Request to open a vending shop (CZ_REQ_BUY_FROMMC).
+/// 0130 <account id>.L
+void clif_parse_VendingListReq(int fd, struct map_session_data* sd)
+{
+ if( sd->npc_id )
+ {// using an NPC
+ return;
+ }
+ vending_vendinglistreq(sd,RFIFOL(fd,2));
+}
+
+
+/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC).
+/// 0134 <packet len>.W <account id>.L { <amount>.W <index>.W }*
+void clif_parse_PurchaseReq(int fd, struct map_session_data* sd)
+{
+ int len = (int)RFIFOW(fd,2) - 8;
+ int id = (int)RFIFOL(fd,4);
+ const uint8* data = (uint8*)RFIFOP(fd,8);
+
+ vending_purchasereq(sd, id, sd->vended_id, data, len/4);
+
+ // whether it fails or not, the buy window is closed
+ sd->vended_id = 0;
+}
+
+
+/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC2).
+/// 0801 <packet len>.W <account id>.L <unique id>.L { <amount>.W <index>.W }*
+void clif_parse_PurchaseReq2(int fd, struct map_session_data* sd)
+{
+ int len = (int)RFIFOW(fd,2) - 12;
+ int aid = (int)RFIFOL(fd,4);
+ int uid = (int)RFIFOL(fd,8);
+ const uint8* data = (uint8*)RFIFOP(fd,12);
+
+ vending_purchasereq(sd, aid, uid, data, len/4);
+
+ // whether it fails or not, the buy window is closed
+ sd->vended_id = 0;
+}
+
+
+/// Confirm or cancel the shop preparation window.
+/// 012f <packet len>.W <shop name>.80B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE)
+/// 01b2 <packet len>.W <shop name>.80B <result>.B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE2)
+/// result:
+/// 0 = canceled
+/// 1 = open
+void clif_parse_OpenVending(int fd, struct map_session_data* sd)
+{
+ short len = (short)RFIFOW(fd,2) - 85;
+ const char* message = (char*)RFIFOP(fd,4);
+ bool flag = (bool)RFIFOB(fd,84);
+ const uint8* data = (uint8*)RFIFOP(fd,85);
+
+ if( sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM )
+ return;
+ 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( vending_checknearnpc(&sd->bl) ) {
+ char output[150];
+ sprintf(output, msg_txt(662), battle_config.min_npc_vending_distance);
+ clif_displaymessage(sd->fd, output);
+ clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
+ return;
+ }
+
+ if( message[0] == '\0' ) // invalid input
+ return;
+
+ vending_openvending(sd, message, flag, data, len/8);
+}
+
+
+/// Guild creation request (CZ_REQ_MAKE_GUILD).
+/// 0165 <char id>.L <guild name>.24B
+void clif_parse_CreateGuild(int fd,struct map_session_data *sd)
+{
+ char* name = (char*)RFIFOP(fd,6);
+ name[NAME_LENGTH-1] = '\0';
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ guild_create(sd, name);
+}
+
+
+/// Request for guild window interface permissions (CZ_REQ_GUILD_MENUINTERFACE).
+/// 014d
+void clif_parse_GuildCheckMaster(int fd, struct map_session_data *sd)
+{
+ clif_guild_masterormember(sd);
+}
+
+
+/// Request for guild window information (CZ_REQ_GUILD_MENU).
+/// 014f <type>.L
+/// type:
+/// 0 = basic info
+/// 1 = member manager
+/// 2 = positions
+/// 3 = skills
+/// 4 = expulsion list
+/// 5 = unknown (GM_ALLGUILDLIST)
+/// 6 = notice
+void clif_parse_GuildRequestInfo(int fd, struct map_session_data *sd)
+{
+ if( !sd->status.guild_id && !sd->bg_id )
+ return;
+
+ switch( RFIFOL(fd,2) )
+ {
+ case 0: // Basic Information Guild, hostile alliance information
+ clif_guild_basicinfo(sd);
+ clif_guild_allianceinfo(sd);
+ break;
+ case 1: // Members list, list job title
+ clif_guild_positionnamelist(sd);
+ clif_guild_memberlist(sd);
+ break;
+ case 2: // List job title, title information list
+ clif_guild_positionnamelist(sd);
+ clif_guild_positioninfolist(sd);
+ break;
+ case 3: // Skill list
+ clif_guild_skillinfo(sd);
+ break;
+ case 4: // Expulsion list
+ clif_guild_expulsionlist(sd);
+ break;
+ default:
+ ShowError("clif: guild request info: unknown type %d\n", RFIFOL(fd,2));
+ break;
+ }
+}
+
+
+/// Request to update guild positions (CZ_REG_CHANGE_GUILD_POSITIONINFO).
+/// 0161 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <name>.24B }*
+void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd)
+{
+ int i;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ for(i = 4; i < RFIFOW(fd,2); i += 40 ){
+ guild_change_position(sd->status.guild_id, RFIFOL(fd,i), RFIFOL(fd,i+4), RFIFOL(fd,i+12), (char*)RFIFOP(fd,i+16));
+ }
+}
+
+
+/// Request to update the position of guild members (CZ_REQ_CHANGE_MEMBERPOS).
+/// 0155 <packet len>.W { <account id>.L <char id>.L <position id>.L }*
+void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd)
+{
+ int i;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ for(i=4;i<RFIFOW(fd,2);i+=12){
+ guild_change_memberposition(sd->status.guild_id,
+ RFIFOL(fd,i),RFIFOL(fd,i+4),RFIFOL(fd,i+8));
+ }
+}
+
+
+/// Request for guild emblem data (CZ_REQ_GUILD_EMBLEM_IMG).
+/// 0151 <guild id>.L
+void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd)
+{
+ struct guild* g;
+ int guild_id = RFIFOL(fd,2);
+
+ if( (g = guild_search(guild_id)) != NULL )
+ clif_guild_emblem(sd,g);
+}
+
+
+/// Validates data of a guild emblem (compressed bitmap)
+static bool clif_validate_emblem(const uint8* emblem, unsigned long emblem_len)
+{
+ bool success;
+ uint8 buf[1800]; // no well-formed emblem bitmap is larger than 1782 (24 bit) / 1654 (8 bit) bytes
+ unsigned long buf_len = sizeof(buf);
+
+ success = ( decode_zip(buf, &buf_len, emblem, emblem_len) == 0 && buf_len >= 18 ) // sizeof(BITMAPFILEHEADER) + sizeof(biSize) of the following info header struct
+ && RBUFW(buf,0) == 0x4d42 // BITMAPFILEHEADER.bfType (signature)
+ && RBUFL(buf,2) == buf_len // BITMAPFILEHEADER.bfSize (file size)
+ && RBUFL(buf,10) < buf_len // BITMAPFILEHEADER.bfOffBits (offset to bitmap bits)
+ ;
+
+ return success;
+}
+
+
+/// Request to update the guild emblem (CZ_REGISTER_GUILD_EMBLEM_IMG).
+/// 0153 <packet len>.W <emblem data>.?B
+void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd)
+{
+ unsigned long emblem_len = RFIFOW(fd,2)-4;
+ const uint8* emblem = RFIFOP(fd,4);
+
+ if( !emblem_len || !sd->state.gmaster_flag )
+ return;
+
+ if( !clif_validate_emblem(emblem, emblem_len) )
+ {
+ ShowWarning("clif_parse_GuildChangeEmblem: Rejected malformed guild emblem (size=%lu, accound_id=%d, char_id=%d, guild_id=%d).\n", emblem_len, sd->status.account_id, sd->status.char_id, sd->status.guild_id);
+ return;
+ }
+
+ guild_change_emblem(sd, emblem_len, (const char*)emblem);
+}
+
+
+/// Guild notice update request (CZ_GUILD_NOTICE).
+/// 016e <guild id>.L <msg1>.60B <msg2>.120B
+void clif_parse_GuildChangeNotice(int fd, struct map_session_data* sd)
+{
+ int guild_id = RFIFOL(fd,2);
+ char* msg1 = (char*)RFIFOP(fd,6);
+ char* msg2 = (char*)RFIFOP(fd,66);
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ // compensate for some client defects when using multilanguage mode
+ if (msg1[0] == '|' && msg1[3] == '|') msg1+= 3; // skip duplicate marker
+ if (msg2[0] == '|' && msg2[3] == '|') msg2+= 3; // skip duplicate marker
+ if (msg2[0] == '|') msg2[strnlen(msg2, MAX_GUILDMES2)-1] = '\0'; // delete extra space at the end of string
+
+ guild_change_notice(sd, guild_id, msg1, msg2);
+}
+
+
+/// Guild invite request (CZ_REQ_JOIN_GUILD).
+/// 0168 <account id>.L <inviter account id>.L <inviter char id>.L
+void clif_parse_GuildInvite(int fd,struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 2);
+ return;
+ }
+
+ guild_invite(sd,t_sd);
+}
+
+
+/// Answer to guild invitation (CZ_JOIN_GUILD).
+/// 016b <guild id>.L <answer>.L
+/// answer:
+/// 0 = refuse
+/// 1 = accept
+void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd)
+{
+ guild_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+
+/// Request to leave guild (CZ_REQ_LEAVE_GUILD).
+/// 0159 <guild id>.L <account id>.L <char id>.L <reason>.40B
+void clif_parse_GuildLeave(int fd,struct map_session_data *sd)
+{
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ if( sd->bg_id )
+ {
+ clif_displaymessage(fd, msg_txt(670)); //"You can't leave battleground guilds."
+ return;
+ }
+
+ guild_leave(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+
+/// Request to expel a member of a guild (CZ_REQ_BAN_GUILD).
+/// 015b <guild id>.L <account id>.L <char id>.L <reason>.40B
+void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd)
+{
+ if( map[sd->bl.m].flag.guildlock || sd->bg_id )
+ { // Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ guild_expulsion(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+
+/// Validates and processes guild messages (CZ_GUILD_CHAT).
+/// 017e <packet len>.W <text>.?B (<name> : <message>) 00
+void clif_parse_GuildMessage(int fd, struct map_session_data* sd)
+{
+ const char* text = (char*)RFIFOP(fd,4);
+ int textlen = RFIFOW(fd,2) - 4;
+
+ char *name, *message;
+ int namelen, messagelen;
+
+ // validate packet and retrieve name and message
+ if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
+ return;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
+ return;
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ if( sd->bg_id )
+ bg_send_message(sd, text, textlen);
+ else
+ guild_send_message(sd, text, textlen);
+}
+
+
+/// Guild alliance request (CZ_REQ_ALLY_GUILD).
+/// 0170 <account id>.L <inviter account id>.L <inviter char id>.L
+void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 3);
+ return;
+ }
+
+ guild_reqalliance(sd,t_sd);
+}
+
+
+/// Answer to a guild alliance request (CZ_ALLY_GUILD).
+/// 0172 <inviter account id>.L <answer>.L
+/// answer:
+/// 0 = refuse
+/// 1 = accept
+void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd)
+{
+ guild_reply_reqalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+
+/// Request to delete a guild alliance or opposition (CZ_REQ_DELETE_RELATED_GUILD).
+/// 0183 <opponent guild id>.L <relation>.L
+/// relation:
+/// 0 = Ally
+/// 1 = Enemy
+void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd)
+{
+ if(!sd->state.gmaster_flag)
+ return;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ guild_delalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+
+/// Request to set a guild as opposition (CZ_REQ_HOSTILE_GUILD).
+/// 0180 <account id>.L
+void clif_parse_GuildOpposition(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 4);
+ return;
+ }
+
+ guild_opposition(sd,t_sd);
+}
+
+
+/// Request to delete own guild (CZ_REQ_DISORGANIZE_GUILD).
+/// 015d <key>.40B
+/// key:
+/// now guild name; might have been (intended) email, since the
+/// field name and size is same as the one in CH_DELETE_CHAR.
+void clif_parse_GuildBreak(int fd, struct map_session_data *sd)
+{
+ if( map[sd->bl.m].flag.guildlock )
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ guild_break(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// Pet
+///
+
+/// Request to invoke a pet menu action (CZ_COMMAND_PET).
+/// 01a1 <type>.B
+/// type:
+/// 0 = pet information
+/// 1 = feed
+/// 2 = performance
+/// 3 = return to egg
+/// 4 = unequip accessory
+void clif_parse_PetMenu(int fd, struct map_session_data *sd)
+{
+ pet_menu(sd,RFIFOB(fd,2));
+}
+
+
+/// Attempt to tame a monster (CZ_TRYCAPTURE_MONSTER).
+/// 019f <id>.L
+void clif_parse_CatchPet(int fd, struct map_session_data *sd)
+{
+ pet_catch_process2(sd,RFIFOL(fd,2));
+}
+
+
+/// Answer to pet incubator egg selection dialog (CZ_SELECT_PETEGG).
+/// 01a7 <index>.W
+void clif_parse_SelectEgg(int fd, struct map_session_data *sd)
+{
+ if (sd->menuskill_id != SA_TAMINGMONSTER || sd->menuskill_val != -1)
+ {
+ //Forged packet, disconnect them [Kevin]
+ clif_authfail_fd(fd, 0);
+ return;
+ }
+ pet_select_egg(sd,RFIFOW(fd,2)-2);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Request to display pet's emotion/talk (CZ_PET_ACT).
+/// 01a9 <data>.L
+/// data:
+/// is either emotion (@see enum emotion_type) or a compound value
+/// (((mob id)-100)*100+(act id)*10+(hunger)) that describes an
+/// entry (given in parentheses) in data\pettalktable.xml
+/// act id:
+/// 0 = feeding
+/// 1 = hunting
+/// 2 = danger
+/// 3 = dead
+/// 4 = normal (stand)
+/// 5 = special performance (perfor_s)
+/// 6 = level up (levelup)
+/// 7 = performance 1 (perfor_1)
+/// 8 = performance 2 (perfor_2)
+/// 9 = performance 3 (perfor_3)
+/// 10 = log-in greeting (connect)
+/// hungry value:
+/// 0 = very hungry (hungry)
+/// 1 = hungry (bit_hungry)
+/// 2 = satisfied (noting)
+/// 3 = stuffed (full)
+/// 4 = full (so_full)
+void clif_parse_SendEmotion(int fd, struct map_session_data *sd)
+{
+ if(sd->pd)
+ clif_pet_emotion(sd->pd,RFIFOL(fd,2));
+}
+
+
+/// Request to change pet's name (CZ_RENAME_PET).
+/// 01a5 <name>.24B
+void clif_parse_ChangePetName(int fd, struct map_session_data *sd)
+{
+ pet_change_name(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// /kill (CZ_DISCONNECT_CHARACTER).
+/// Request to disconnect a character.
+/// 00cc <account id>.L
+/// NOTE: Also sent when using GM right click menu "(name) force to quit"
+void clif_parse_GMKick(int fd, struct map_session_data *sd)
+{
+ struct block_list *target;
+ int tid;
+
+ tid = RFIFOL(fd,2);
+ target = map_id2bl(tid);
+ if (!target) {
+ clif_GM_kickack(sd, 0);
+ return;
+ }
+
+ switch (target->type) {
+ case BL_PC:
+ {
+ char command[NAME_LENGTH+6];
+ sprintf(command, "%ckick %s", atcommand_symbol, status_get_name(target));
+ is_atcommand(fd, sd, command, 1);
+ }
+ break;
+
+ /**
+ * This one does not invoke any atcommand, so we need to check for permissions.
+ */
+ case BL_MOB:
+ {
+ char command[100];
+ if( !pc_can_use_command(sd, "killmonster", COMMAND_ATCOMMAND)) {
+ clif_GM_kickack(sd, 0);
+ return;
+ }
+ sprintf(command, "/kick %s (%d)", status_get_name(target), status_get_class(target));
+ log_atcommand(sd, command);
+ status_percent_damage(&sd->bl, target, 100, 0, true); // can invalidate 'target'
+ }
+ break;
+
+ case BL_NPC:
+ {
+ char command[NAME_LENGTH+11];
+ sprintf(command, "%cunloadnpc %s", atcommand_symbol, status_get_name(target));
+ is_atcommand(fd, sd, command, 1);
+ }
+ break;
+
+ default:
+ clif_GM_kickack(sd, 0);
+ }
+}
+
+
+/// /killall (CZ_DISCONNECT_ALL_CHARACTER).
+/// Request to disconnect all characters.
+/// 00ce
+void clif_parse_GMKickAll(int fd, struct map_session_data* sd) {
+ char cmd[15];
+ sprintf(cmd,"%ckickall",atcommand_symbol);
+ is_atcommand(fd, sd, cmd, 1);
+}
+
+
+/// /remove (CZ_REMOVE_AID).
+/// Request to warp to a character with given login ID.
+/// 01ba <account name>.24B
+
+/// /shift (CZ_SHIFT).
+/// Request to warp to a character with given name.
+/// 01bb <char name>.24B
+void clif_parse_GMShift(int fd, struct map_session_data *sd)
+{// FIXME: remove is supposed to receive account name for clients prior 20100803RE
+ char *player_name;
+ char command[NAME_LENGTH+8];
+
+ player_name = (char*)RFIFOP(fd,2);
+ player_name[NAME_LENGTH-1] = '\0';
+
+ sprintf(command, "%cjumpto %s", atcommand_symbol, player_name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// /remove (CZ_REMOVE_AID_SSO).
+/// Request to warp to a character with given account ID.
+/// 0843 <account id>.L
+void clif_parse_GMRemove2(int fd, struct map_session_data* sd)
+{
+ int account_id;
+ struct map_session_data* pl_sd;
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if( (pl_sd = map_id2sd(account_id)) != NULL )
+ {
+ char command[NAME_LENGTH+8];
+ sprintf(command, "%cjumpto %s", atcommand_symbol, pl_sd->status.name);
+ is_atcommand(fd, sd, command, 1);
+ }
+}
+
+
+/// /recall (CZ_RECALL).
+/// Request to summon a player with given login ID to own position.
+/// 01bc <account name>.24B
+
+/// /summon (CZ_RECALL_GID).
+/// Request to summon a player with given name to own position.
+/// 01bd <char name>.24B
+void clif_parse_GMRecall(int fd, struct map_session_data *sd)
+{// FIXME: recall is supposed to receive account name for clients prior 20100803RE
+ char *player_name;
+ char command [NAME_LENGTH+8];
+
+ player_name = (char*)RFIFOP(fd,2);
+ player_name[NAME_LENGTH-1] = '\0';
+
+ sprintf(command, "%crecall %s", atcommand_symbol, player_name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// /recall (CZ_RECALL_SSO).
+/// Request to summon a player with given account ID to own position.
+/// 0842 <account id>.L
+void clif_parse_GMRecall2(int fd, struct map_session_data* sd)
+{
+ int account_id;
+ struct map_session_data* pl_sd;
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if( (pl_sd = map_id2sd(account_id)) != NULL )
+ {
+ char command[NAME_LENGTH+8];
+ sprintf(command, "%crecall %s", atcommand_symbol, pl_sd->status.name);
+ is_atcommand(fd, sd, command, 1);
+ }
+}
+
+
+/// /item /monster (CZ_ITEM_CREATE).
+/// Request to make items or spawn monsters.
+/// 013f <item/mob name>.24B
+void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd)
+{
+ char *monster_item_name;
+ char command[NAME_LENGTH+10];
+
+ monster_item_name = (char*)RFIFOP(fd,2);
+ monster_item_name[NAME_LENGTH-1] = '\0';
+
+ // FIXME: Should look for item first, then for monster.
+ // FIXME: /monster takes mob_db Sprite_Name as argument
+ if( mobdb_searchname(monster_item_name) ) {
+ snprintf(command, sizeof(command)-1, "%cmonster %s", atcommand_symbol, monster_item_name);
+ is_atcommand(fd, sd, command, 1);
+ return;
+ }
+ // FIXME: Stackables have a quantity of 20.
+ // FIXME: Equips are supposed to be unidentified.
+
+ if( itemdb_searchname(monster_item_name) ) {
+ snprintf(command, sizeof(command)-1, "%citem %s", atcommand_symbol, monster_item_name);
+ is_atcommand(fd, sd, command, 1);
+ return;
+ }
+}
+
+
+/// /hide (CZ_CHANGE_EFFECTSTATE).
+/// 019d <effect state>.L
+/// effect state:
+/// TODO: Any OPTION_* ?
+void clif_parse_GMHide(int fd, struct map_session_data *sd) {
+ char cmd[6];
+
+ sprintf(cmd,"%chide",atcommand_symbol);
+
+ is_atcommand(fd, sd, cmd, 1);
+}
+
+
+/// Request to adjust player's manner points (CZ_REQ_GIVE_MANNER_POINT).
+/// 0149 <account id>.L <type>.B <value>.W
+/// type:
+/// 0 = positive points
+/// 1 = negative points
+/// 2 = self mute (+10 minutes)
+void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd)
+{
+ int id, type, value;
+ struct map_session_data *dstsd;
+ char command[NAME_LENGTH+15];
+
+ id = RFIFOL(fd,2);
+ type = RFIFOB(fd,6);
+ value = RFIFOW(fd,7);
+
+ if( type == 0 )
+ value = -value;
+
+ //If type is 2 and the ids don't match, this is a crafted hacked packet!
+ //Disabled because clients keep self-muting when you give players public @ commands... [Skotlex]
+ if (type == 2 /* && (pc_get_group_level(sd) > 0 || sd->bl.id != id)*/)
+ return;
+
+ dstsd = map_id2sd(id);
+ if( dstsd == NULL )
+ return;
+
+ sprintf(command, "%cmute %d %s", atcommand_symbol, value, dstsd->status.name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// /rc (CZ_REQ_GIVE_MANNER_BYNAME).
+/// GM adjustment of a player's manner value by -60.
+/// 0212 <char name>.24B
+void clif_parse_GMRc(int fd, struct map_session_data* sd)
+{
+ char command[NAME_LENGTH+15];
+ char *name = (char*)RFIFOP(fd,2);
+
+ name[NAME_LENGTH-1] = '\0';
+ sprintf(command, "%cmute %d %s", atcommand_symbol, 60, name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Result of request to resolve account name (ZC_ACK_ACCOUNTNAME).
+/// 01e0 <account id>.L <account name>.24B
+void clif_account_name(struct map_session_data* sd, int account_id, const char* accname)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x1e0));
+ WFIFOW(fd,0) = 0x1e0;
+ WFIFOL(fd,2) = account_id;
+ safestrncpy((char*)WFIFOP(fd,6), accname, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x1e0));
+}
+
+
+/// GM requesting account name (for right-click gm menu) (CZ_REQ_ACCOUNTNAME).
+/// 01df <account id>.L
+void clif_parse_GMReqAccountName(int fd, struct map_session_data *sd)
+{
+ int account_id = RFIFOL(fd,2);
+
+ //TODO: find out if this works for any player or only for authorized GMs
+ clif_account_name(sd, account_id, ""); // insert account name here >_<
+}
+
+
+/// /changemaptype <x> <y> <type> (CZ_CHANGE_MAPTYPE).
+/// GM single cell type change request.
+/// 0198 <x>.W <y>.W <type>.W
+/// type:
+/// 0 = not walkable
+/// 1 = walkable
+void clif_parse_GMChangeMapType(int fd, struct map_session_data *sd)
+{
+ int x,y,type;
+
+ if( pc_has_permission(sd, PC_PERM_USE_CHANGEMAPTYPE) )
+ return;
+
+ x = RFIFOW(fd,2);
+ y = RFIFOW(fd,4);
+ type = RFIFOW(fd,6);
+
+ map_setgatcell(sd->bl.m,x,y,type);
+ clif_changemapcell(0,sd->bl.m,x,y,type,ALL_SAMEMAP);
+ //FIXME: once players leave the map, the client 'forgets' this information.
+}
+
+
+/// /in /ex (CZ_SETTING_WHISPER_PC).
+/// Request to allow/deny whispers from a nick.
+/// 00cf <nick>.24B <type>.B
+/// type:
+/// 0 = (/ex nick) deny speech from nick
+/// 1 = (/in nick) allow speech from nick
+void clif_parse_PMIgnore(int fd, struct map_session_data* sd)
+{
+ char* nick;
+ uint8 type;
+ int i;
+
+ nick = (char*)RFIFOP(fd,2); // speed up
+ nick[NAME_LENGTH-1] = '\0'; // to be sure that the player name has at most 23 characters
+ type = RFIFOB(fd,26);
+
+ if( type == 0 )
+ { // Add name to ignore list (block)
+ if (strcmp(wisp_server_name, nick) == 0) {
+ clif_wisexin(sd, type, 1); // fail
+ return;
+ }
+
+ // try to find a free spot, while checking for duplicates at the same time
+ ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 );
+ if( i == MAX_IGNORE_LIST )
+ {// no space for new entry
+ clif_wisexin(sd, type, 2); // too many blocks
+ return;
+ }
+ if( sd->ignore[i].name[0] != '\0' )
+ {// name already exists
+ clif_wisexin(sd, type, 0); // Aegis reports success.
+ return;
+ }
+
+ //Insert in position i
+ safestrncpy(sd->ignore[i].name, nick, NAME_LENGTH);
+ }
+ else
+ { // Remove name from ignore list (unblock)
+
+ // find entry
+ ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 );
+ if( i == MAX_IGNORE_LIST || sd->ignore[i].name[i] == '\0' )
+ { //Not found
+ clif_wisexin(sd, type, 1); // fail
+ return;
+ }
+ // move everything one place down to overwrite removed entry
+ memmove(sd->ignore[i].name, sd->ignore[i+1].name, (MAX_IGNORE_LIST-i-1)*sizeof(sd->ignore[0].name));
+ // wipe last entry
+ memset(sd->ignore[MAX_IGNORE_LIST-1].name, 0, sizeof(sd->ignore[0].name));
+ }
+
+ clif_wisexin(sd, type, 0); // success
+}
+
+
+/// /inall /exall (CZ_SETTING_WHISPER_STATE).
+/// Request to allow/deny all whispers.
+/// 00d0 <type>.B
+/// type:
+/// 0 = (/exall) deny all speech
+/// 1 = (/inall) allow all speech
+void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd)
+{
+ int type = RFIFOB(fd,2), flag;
+
+ if( type == 0 )
+ {// Deny all
+ if( sd->state.ignoreAll ) {
+ flag = 1; // fail
+ } else {
+ sd->state.ignoreAll = 1;
+ flag = 0; // success
+ }
+ }
+ else
+ {//Unblock everyone
+ if( sd->state.ignoreAll ) {
+ sd->state.ignoreAll = 0;
+ flag = 0; // success
+ } else {
+ if (sd->ignore[0].name[0] != '\0')
+ { //Wipe the ignore list.
+ memset(sd->ignore, 0, sizeof(sd->ignore));
+ flag = 0; // success
+ } else {
+ flag = 1; // fail
+ }
+ }
+ }
+
+ clif_wisall(sd, type, flag);
+}
+
+
+/// Whisper ignore list (ZC_WHISPER_LIST).
+/// 00d4 <packet len>.W { <char name>.24B }*
+void clif_PMIgnoreList(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+
+ WFIFOHEAD(fd,4+ARRAYLENGTH(sd->ignore)*NAME_LENGTH);
+ WFIFOW(fd,0) = 0xd4;
+
+ for( i = 0; i < ARRAYLENGTH(sd->ignore) && sd->ignore[i].name[0]; i++ )
+ {
+ memcpy(WFIFOP(fd,4+i*NAME_LENGTH), sd->ignore[i].name, NAME_LENGTH);
+ }
+
+ WFIFOW(fd,2) = 4+i*NAME_LENGTH;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Whisper ignore list request (CZ_REQ_WHISPER_LIST).
+/// 00d3
+void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd)
+{
+ clif_PMIgnoreList(sd);
+}
+
+
+/// Request to invoke the /doridori recovery bonus (CZ_DORIDORI).
+/// 01e7
+void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd)
+{
+ if (sd->state.doridori) return;
+
+ switch (sd->class_&MAPID_UPPERMASK)
+ {
+ case MAPID_SOUL_LINKER:
+ case MAPID_STAR_GLADIATOR:
+ case MAPID_TAEKWON:
+ if (!sd->state.rest)
+ break;
+ case MAPID_SUPER_NOVICE:
+ sd->state.doridori=1;
+ break;
+ }
+}
+
+
+/// Request to invoke the effect of super novice's guardian angel prayer (CZ_CHOPOKGI).
+/// 01ed
+/// Note: This packet is caused by 7 lines of any text, followed by
+/// the prayer and an another line of any text. The prayer is
+/// defined by lines 790~793 in data\msgstringtable.txt
+/// "Dear angel, can you hear my voice?"
+/// "I am" (space separated player name) "Super Novice~"
+/// "Help me out~ Please~ T_T"
+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. );
+
+ if( percent && ( percent%100 ) == 0 )
+ {// 10.0%, 20.0%, ..., 90.0%
+ sc_start(&sd->bl, status_skill2sc(MO_EXPLOSIONSPIRITS), 100, 17, skill_get_time(MO_EXPLOSIONSPIRITS, 5)); //Lv17-> +50 critical (noted by Poki) [Skotlex]
+ clif_skill_nodamage(&sd->bl, &sd->bl, MO_EXPLOSIONSPIRITS, 5, 1); // prayer always shows successful Lv5 cast and disregards noskill restrictions
+ }
+ }
+ }
+}
+
+
+/// Friends List
+///
+
+/// Toggles a single friend online/offline [Skotlex] (ZC_FRIENDS_STATE).
+/// 0206 <account id>.L <char id>.L <state>.B
+/// state:
+/// 0 = online
+/// 1 = offline
+void clif_friendslist_toggle(struct map_session_data *sd,int account_id, int char_id, int online)
+{
+ int i, fd = sd->fd;
+
+ //Seek friend.
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if(i == MAX_FRIENDS || sd->status.friends[i].char_id == 0)
+ return; //Not found
+
+ WFIFOHEAD(fd,packet_len(0x206));
+ WFIFOW(fd, 0) = 0x206;
+ WFIFOL(fd, 2) = sd->status.friends[i].account_id;
+ WFIFOL(fd, 6) = sd->status.friends[i].char_id;
+ WFIFOB(fd,10) = !online; //Yeah, a 1 here means "logged off", go figure...
+ WFIFOSET(fd, packet_len(0x206));
+}
+
+
+//Subfunction called from clif_foreachclient to toggle friends on/off [Skotlex]
+int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap)
+{
+ int account_id, char_id, online;
+ account_id = va_arg(ap, int);
+ char_id = va_arg(ap, int);
+ online = va_arg(ap, int);
+ clif_friendslist_toggle(sd, account_id, char_id, online);
+ return 0;
+}
+
+
+/// Sends the whole friends list (ZC_FRIENDS_LIST).
+/// 0201 <packet len>.W { <account id>.L <char id>.L <name>.24B }*
+void clif_friendslist_send(struct map_session_data *sd)
+{
+ int i = 0, n, fd = sd->fd;
+
+ // Send friends list
+ WFIFOHEAD(fd, MAX_FRIENDS * 32 + 4);
+ WFIFOW(fd, 0) = 0x201;
+ for(i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id; i++)
+ {
+ WFIFOL(fd, 4 + 32 * i + 0) = sd->status.friends[i].account_id;
+ WFIFOL(fd, 4 + 32 * i + 4) = sd->status.friends[i].char_id;
+ memcpy(WFIFOP(fd, 4 + 32 * i + 8), &sd->status.friends[i].name, NAME_LENGTH);
+ }
+
+ if (i) {
+ WFIFOW(fd,2) = 4 + 32 * i;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ }
+
+ for (n = 0; n < i; n++)
+ { //Sending the online players
+ if (map_charid2sd(sd->status.friends[n].char_id))
+ clif_friendslist_toggle(sd, sd->status.friends[n].account_id, sd->status.friends[n].char_id, 1);
+ }
+}
+
+
+/// Notification about the result of a friend add request (ZC_ADD_FRIENDS_LIST).
+/// 0209 <result>.W <account id>.L <char id>.L <name>.24B
+/// result:
+/// 0 = MsgStringTable[821]="You have become friends with (%s)."
+/// 1 = MsgStringTable[822]="(%s) does not want to be friends with you."
+/// 2 = MsgStringTable[819]="Your Friend List is full."
+/// 3 = MsgStringTable[820]="(%s)'s Friend List is full."
+void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x209));
+ WFIFOW(fd,0) = 0x209;
+ WFIFOW(fd,2) = type;
+ if (f_sd)
+ {
+ WFIFOL(fd,4) = f_sd->status.account_id;
+ WFIFOL(fd,8) = f_sd->status.char_id;
+ memcpy(WFIFOP(fd, 12), f_sd->status.name,NAME_LENGTH);
+ }
+ WFIFOSET(fd, packet_len(0x209));
+}
+
+
+/// Asks a player for permission to be added as friend (ZC_REQ_ADD_FRIENDS).
+/// 0207 <req account id>.L <req char id>.L <req char name>.24B
+void clif_friendlist_req(struct map_session_data* sd, int account_id, int char_id, const char* name)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x207));
+ WFIFOW(fd,0) = 0x207;
+ WFIFOL(fd,2) = account_id;
+ WFIFOL(fd,6) = char_id;
+ memcpy(WFIFOP(fd,10), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x207));
+}
+
+
+/// Request to add a player as friend (CZ_ADD_FRIENDS).
+/// 0202 <name>.24B
+void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *f_sd;
+ int i;
+
+ f_sd = map_nick2sd((char*)RFIFOP(fd,2));
+
+ // ensure that the request player's friend list is not full
+ ARR_FIND(0, MAX_FRIENDS, i, sd->status.friends[i].char_id == 0);
+
+ if( i == MAX_FRIENDS ) {
+ clif_friendslist_reqack(sd, f_sd, 2);
+ return;
+ }
+
+ // Friend doesn't exist (no player with this name)
+ if (f_sd == NULL) {
+ clif_displaymessage(fd, msg_txt(3));
+ return;
+ }
+
+ if( sd->bl.id == f_sd->bl.id )
+ {// adding oneself as friend
+ return;
+ }
+
+ // @noask [LuzZza]
+ if(f_sd->state.noask) {
+ clif_noask_sub(sd, f_sd, 5);
+ return;
+ }
+
+ // Friend already exists
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id != 0; i++) {
+ if (sd->status.friends[i].char_id == f_sd->status.char_id) {
+ clif_displaymessage(fd, msg_txt(671)); //"Friend already exists."
+ return;
+ }
+ }
+
+ f_sd->friend_req = sd->status.char_id;
+ sd->friend_req = f_sd->status.char_id;
+
+ clif_friendlist_req(f_sd, sd->status.account_id, sd->status.char_id, sd->status.name);
+}
+
+
+/// Answer to a friend add request (CZ_ACK_REQ_ADD_FRIENDS).
+/// 0208 <inviter account id>.L <inviter char id>.L <result>.B
+/// 0208 <inviter account id>.L <inviter char id>.L <result>.L (PACKETVER >= 6)
+/// result:
+/// 0 = rejected
+/// 1 = accepted
+void clif_parse_FriendsListReply(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *f_sd;
+ int account_id;
+ char reply;
+
+ account_id = RFIFOL(fd,2);
+ //char_id = RFIFOL(fd,6);
+#if PACKETVER < 6
+ reply = RFIFOB(fd,10);
+#else
+ reply = RFIFOL(fd,10);
+#endif
+
+ if( sd->bl.id == account_id )
+ {// adding oneself as friend
+ return;
+ }
+
+ f_sd = map_id2sd(account_id); //The account id is the same as the bl.id of players.
+ if (f_sd == NULL)
+ return;
+
+ if (reply == 0 || !( sd->friend_req == f_sd->status.char_id && f_sd->friend_req == sd->status.char_id ) )
+ clif_friendslist_reqack(f_sd, sd, 1);
+ else {
+ int i;
+ // Find an empty slot
+ for (i = 0; i < MAX_FRIENDS; i++)
+ if (f_sd->status.friends[i].char_id == 0)
+ break;
+ if (i == MAX_FRIENDS) {
+ clif_friendslist_reqack(f_sd, sd, 2);
+ return;
+ }
+
+ f_sd->status.friends[i].account_id = sd->status.account_id;
+ f_sd->status.friends[i].char_id = sd->status.char_id;
+ memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH);
+ clif_friendslist_reqack(f_sd, sd, 0);
+
+ if (battle_config.friend_auto_add) {
+ // Also add f_sd to sd's friendlist.
+ for (i = 0; i < MAX_FRIENDS; i++) {
+ if (sd->status.friends[i].char_id == f_sd->status.char_id)
+ return; //No need to add anything.
+ if (sd->status.friends[i].char_id == 0)
+ break;
+ }
+ if (i == MAX_FRIENDS) {
+ clif_friendslist_reqack(sd, f_sd, 2);
+ return;
+ }
+
+ sd->status.friends[i].account_id = f_sd->status.account_id;
+ sd->status.friends[i].char_id = f_sd->status.char_id;
+ memcpy(sd->status.friends[i].name, f_sd->status.name, NAME_LENGTH);
+ clif_friendslist_reqack(sd, f_sd, 0);
+ }
+ }
+}
+
+
+/// Request to delete a friend (CZ_DELETE_FRIENDS).
+/// 0203 <account id>.L <char id>.L
+void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *f_sd = NULL;
+ int account_id, char_id;
+ int i, j;
+
+ account_id = RFIFOL(fd,2);
+ char_id = RFIFOL(fd,6);
+
+ // Search friend
+ for (i = 0; i < MAX_FRIENDS &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if (i == MAX_FRIENDS) {
+ clif_displaymessage(fd, msg_txt(672)); //"Name not found in list."
+ return;
+ }
+
+ //remove from friend's list first
+ if( (f_sd = map_id2sd(account_id)) && f_sd->status.char_id == char_id) {
+ for (i = 0; i < MAX_FRIENDS &&
+ (f_sd->status.friends[i].char_id != sd->status.char_id || f_sd->status.friends[i].account_id != sd->status.account_id); i++);
+
+ if (i != MAX_FRIENDS) {
+ // move all chars up
+ for(j = i + 1; j < MAX_FRIENDS; j++)
+ memcpy(&f_sd->status.friends[j-1], &f_sd->status.friends[j], sizeof(f_sd->status.friends[0]));
+
+ memset(&f_sd->status.friends[MAX_FRIENDS-1], 0, sizeof(f_sd->status.friends[MAX_FRIENDS-1]));
+ //should the guy be notified of some message? we should add it here if so
+ WFIFOHEAD(f_sd->fd,packet_len(0x20a));
+ WFIFOW(f_sd->fd,0) = 0x20a;
+ WFIFOL(f_sd->fd,2) = sd->status.account_id;
+ WFIFOL(f_sd->fd,6) = sd->status.char_id;
+ WFIFOSET(f_sd->fd, packet_len(0x20a));
+ }
+
+ } else { //friend not online -- ask char server to delete from his friendlist
+ if(chrif_removefriend(char_id,sd->status.char_id)) { // char-server offline, abort
+ clif_displaymessage(fd, msg_txt(673)); //"This action can't be performed at the moment. Please try again later."
+ return;
+ }
+ }
+
+ // We can now delete from original requester
+ for (i = 0; i < MAX_FRIENDS &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+ // move all chars up
+ for(j = i + 1; j < MAX_FRIENDS; j++)
+ memcpy(&sd->status.friends[j-1], &sd->status.friends[j], sizeof(sd->status.friends[0]));
+
+ memset(&sd->status.friends[MAX_FRIENDS-1], 0, sizeof(sd->status.friends[MAX_FRIENDS-1]));
+ clif_displaymessage(fd, msg_txt(674)); //"Friend removed"
+
+ WFIFOHEAD(fd,packet_len(0x20a));
+ WFIFOW(fd,0) = 0x20a;
+ WFIFOL(fd,2) = account_id;
+ WFIFOL(fd,6) = char_id;
+ WFIFOSET(fd, packet_len(0x20a));
+}
+
+
+/// /pvpinfo list (ZC_ACK_PVPPOINT).
+/// 0210 <char id>.L <account id>.L <win point>.L <lose point>.L <point>.L
+void clif_PVPInfo(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x210));
+ WFIFOW(fd,0) = 0x210;
+ WFIFOL(fd,2) = sd->status.char_id;
+ WFIFOL(fd,6) = sd->status.account_id;
+ WFIFOL(fd,10) = sd->pvp_won; // times won
+ WFIFOL(fd,14) = sd->pvp_lost; // times lost
+ WFIFOL(fd,18) = sd->pvp_point;
+ WFIFOSET(fd, packet_len(0x210));
+}
+
+
+/// /pvpinfo (CZ_REQ_PVPPOINT).
+/// 020f <char id>.L <account id>.L
+void clif_parse_PVPInfo(int fd,struct map_session_data *sd)
+{
+ // TODO: Is there a way to use this on an another player (char/acc id)?
+ clif_PVPInfo(sd);
+}
+
+
+/// /blacksmith list (ZC_BLACKSMITH_RANK).
+/// 0219 { <name>.24B }*10 { <point>.L }*10
+void clif_blacksmith(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+ const char* name;
+
+ WFIFOHEAD(fd,packet_len(0x219));
+ WFIFOW(fd,0) = 0x219;
+ //Packet size limits this list to 10 elements. [Skotlex]
+ for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
+ if (smith_fame_list[i].id > 0) {
+ if (strcmp(smith_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(smith_fame_list[i].id)) != NULL)
+ {
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), smith_fame_list[i].name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "None", 5);
+ WFIFOL(fd, 242 + i * 4) = smith_fame_list[i].fame;
+ }
+ for(;i < 10; i++) { //In case the MAX is less than 10.
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "Unavailable", 12);
+ WFIFOL(fd, 242 + i * 4) = 0;
+ }
+
+ WFIFOSET(fd, packet_len(0x219));
+}
+
+
+/// /blacksmith (CZ_BLACKSMITH_RANK).
+/// 0217
+void clif_parse_Blacksmith(int fd,struct map_session_data *sd)
+{
+ clif_blacksmith(sd);
+}
+
+
+/// Notification about backsmith points (ZC_BLACKSMITH_POINT).
+/// 021b <points>.L <total points>.L
+void clif_fame_blacksmith(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x21b));
+ WFIFOW(fd,0) = 0x21b;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len(0x21b));
+}
+
+
+/// /alchemist list (ZC_ALCHEMIST_RANK).
+/// 021a { <name>.24B }*10 { <point>.L }*10
+void clif_alchemist(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+ const char* name;
+
+ WFIFOHEAD(fd,packet_len(0x21a));
+ WFIFOW(fd,0) = 0x21a;
+ //Packet size limits this list to 10 elements. [Skotlex]
+ for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
+ if (chemist_fame_list[i].id > 0) {
+ if (strcmp(chemist_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(chemist_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), chemist_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = chemist_fame_list[i].fame;
+ }
+ for(;i < 10; i++) { //In case the MAX is less than 10.
+ memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = 0;
+ }
+
+ WFIFOSET(fd, packet_len(0x21a));
+}
+
+
+/// /alchemist (CZ_ALCHEMIST_RANK).
+/// 0218
+void clif_parse_Alchemist(int fd,struct map_session_data *sd)
+{
+ clif_alchemist(sd);
+}
+
+
+/// Notification about alchemist points (ZC_ALCHEMIST_POINT).
+/// 021c <points>.L <total points>.L
+void clif_fame_alchemist(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x21c));
+ WFIFOW(fd,0) = 0x21c;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len(0x21c));
+}
+
+
+/// /taekwon list (ZC_TAEKWON_RANK).
+/// 0226 { <name>.24B }*10 { <point>.L }*10
+void clif_taekwon(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+ const char* name;
+
+ WFIFOHEAD(fd,packet_len(0x226));
+ WFIFOW(fd,0) = 0x226;
+ //Packet size limits this list to 10 elements. [Skotlex]
+ for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
+ if (taekwon_fame_list[i].id > 0) {
+ if (strcmp(taekwon_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(taekwon_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), taekwon_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = taekwon_fame_list[i].fame;
+ }
+ for(;i < 10; i++) { //In case the MAX is less than 10.
+ memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = 0;
+ }
+ WFIFOSET(fd, packet_len(0x226));
+}
+
+
+/// /taekwon (CZ_TAEKWON_RANK).
+/// 0225
+void clif_parse_Taekwon(int fd,struct map_session_data *sd)
+{
+ clif_taekwon(sd);
+}
+
+
+/// Notification about taekwon points (ZC_TAEKWON_POINT).
+/// 0224 <points>.L <total points>.L
+void clif_fame_taekwon(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x224));
+ WFIFOW(fd,0) = 0x224;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len(0x224));
+}
+
+
+/// /pk list (ZC_KILLER_RANK).
+/// 0238 { <name>.24B }*10 { <point>.L }*10
+void clif_ranking_pk(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x238));
+ WFIFOW(fd,0) = 0x238;
+ for(i=0;i<10;i++){
+ memcpy(WFIFOP(fd,i*24+2), "Unknown", NAME_LENGTH);
+ WFIFOL(fd,i*4+242) = 0;
+ }
+ WFIFOSET(fd, packet_len(0x238));
+}
+
+
+/// /pk (CZ_KILLER_RANK).
+/// 0237
+void clif_parse_RankingPk(int fd,struct map_session_data *sd)
+{
+ clif_ranking_pk(sd);
+}
+
+
+/// SG Feel save OK [Komurka] (CZ_AGREE_STARPLACE).
+/// 0254 <which>.B
+/// which:
+/// 0 = sun
+/// 1 = moon
+/// 2 = star
+void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd)
+{
+ int i;
+ if (sd->menuskill_id != SG_FEEL)
+ return;
+ i = sd->menuskill_val-1;
+ if (i<0 || i >= MAX_PC_FEELHATE) return; //Bug?
+
+ sd->feel_map[i].index = map_id2index(sd->bl.m);
+ sd->feel_map[i].m = sd->bl.m;
+ pc_setglobalreg(sd,sg_info[i].feel_var,sd->feel_map[i].index);
+
+//Are these really needed? Shouldn't they show up automatically from the feel save packet?
+// clif_misceffect2(&sd->bl, 0x1b0);
+// clif_misceffect2(&sd->bl, 0x21f);
+ clif_feel_info(sd, i, 0);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Star Gladiator's Feeling map confirmation prompt (ZC_STARPLACE).
+/// 0253 <which>.B
+/// which:
+/// 0 = sun
+/// 1 = moon
+/// 2 = star
+void clif_feel_req(int fd, struct map_session_data *sd, uint16 skill_lv)
+{
+ WFIFOHEAD(fd,packet_len(0x253));
+ WFIFOW(fd,0)=0x253;
+ WFIFOB(fd,2)=TOB(skill_lv-1);
+ WFIFOSET(fd, packet_len(0x253));
+ sd->menuskill_id = SG_FEEL;
+ sd->menuskill_val = skill_lv;
+}
+
+
+/// Request to change homunculus' name (CZ_RENAME_MER).
+/// 0231 <name>.24B
+void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd)
+{
+ merc_hom_change_name(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// Request to warp/move homunculus/mercenary to it's owner (CZ_REQUEST_MOVETOOWNER).
+/// 0234 <id>.L
+void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd)
+{
+ int id = RFIFOL(fd,2); // Mercenary or Homunculus
+ struct block_list *bl = NULL;
+ struct unit_data *ud = NULL;
+
+ if( sd->md && sd->md->bl.id == id )
+ bl = &sd->md->bl;
+ else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
+ bl = &sd->hd->bl; // Moving Homunculus
+ else
+ return;
+
+ unit_calc_pos(bl, sd->bl.x, sd->bl.y, sd->ud.dir);
+ ud = unit_bl2ud(bl);
+ unit_walktoxy(bl, ud->to_x, ud->to_y, 4);
+}
+
+
+/// Request to move homunculus/mercenary (CZ_REQUEST_MOVENPC).
+/// 0232 <id>.L <position data>.3B
+void clif_parse_HomMoveTo(int fd, struct map_session_data *sd)
+{
+ int id = RFIFOL(fd,2); // Mercenary or Homunculus
+ struct block_list *bl = NULL;
+ short x, y;
+
+ RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1], &x, &y, NULL);
+
+ if( sd->md && sd->md->bl.id == id )
+ bl = &sd->md->bl; // Moving Mercenary
+ else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
+ bl = &sd->hd->bl; // Moving Homunculus
+ else
+ return;
+
+ unit_walktoxy(bl, x, y, 4);
+}
+
+
+/// Request to do an action with homunculus/mercenary (CZ_REQUEST_ACTNPC).
+/// 0233 <id>.L <target id>.L <action>.B
+/// action:
+/// always 0
+void clif_parse_HomAttack(int fd,struct map_session_data *sd)
+{
+ struct block_list *bl = NULL;
+ int id = RFIFOL(fd,2),
+ target_id = RFIFOL(fd,6),
+ action_type = RFIFOB(fd,10);
+
+ if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
+ bl = &sd->hd->bl;
+ else if( sd->md && sd->md->bl.id == id )
+ bl = &sd->md->bl;
+ else return;
+
+ unit_stop_attack(bl);
+ unit_attack(bl, target_id, action_type != 0);
+}
+
+
+/// Request to invoke a homunculus menu action (CZ_COMMAND_MER).
+/// 022d <type>.W <command>.B
+/// type:
+/// always 0
+/// command:
+/// 0 = homunculus information
+/// 1 = feed
+/// 2 = delete
+void clif_parse_HomMenu(int fd, struct map_session_data *sd)
+{ //[orn]
+ int cmd;
+
+ cmd = RFIFOW(fd,0);
+
+ if(!merc_is_hom_active(sd->hd))
+ return;
+
+ merc_menu(sd,RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[1]));
+}
+
+
+/// Request to resurrect oneself using Token of Siegfried (CZ_STANDING_RESURRECTION).
+/// 0292
+void clif_parse_AutoRevive(int fd, struct map_session_data *sd)
+{
+ int item_position = pc_search_inventory(sd, ITEMID_TOKEN_OF_SIEGFRIED);
+
+ if (item_position < 0)
+ return;
+
+ if (sd->sc.data[SC_HELLPOWER]) //Cannot res while under the effect of SC_HELLPOWER.
+ return;
+
+ if (!status_revive(&sd->bl, 100, 100))
+ return;
+
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ pc_delitem(sd, item_position, 1, 0, 1, LOG_TYPE_CONSUME);
+}
+
+
+/// Information about character's status values (ZC_ACK_STATUS_GM).
+/// 0214 <str>.B <standardStr>.B <agi>.B <standardAgi>.B <vit>.B <standardVit>.B
+/// <int>.B <standardInt>.B <dex>.B <standardDex>.B <luk>.B <standardLuk>.B
+/// <attPower>.W <refiningPower>.W <max_mattPower>.W <min_mattPower>.W
+/// <itemdefPower>.W <plusdefPower>.W <mdefPower>.W <plusmdefPower>.W
+/// <hitSuccessValue>.W <avoidSuccessValue>.W <plusAvoidSuccessValue>.W
+/// <criticalSuccessValue>.W <ASPD>.W <plusASPD>.W
+void clif_check(int fd, struct map_session_data* pl_sd)
+{
+ WFIFOHEAD(fd,packet_len(0x214));
+ WFIFOW(fd, 0) = 0x214;
+ WFIFOB(fd, 2) = min(pl_sd->status.str, UINT8_MAX);
+ WFIFOB(fd, 3) = pc_need_status_point(pl_sd, SP_STR, 1);
+ WFIFOB(fd, 4) = min(pl_sd->status.agi, UINT8_MAX);
+ WFIFOB(fd, 5) = pc_need_status_point(pl_sd, SP_AGI, 1);
+ WFIFOB(fd, 6) = min(pl_sd->status.vit, UINT8_MAX);
+ WFIFOB(fd, 7) = pc_need_status_point(pl_sd, SP_VIT, 1);
+ WFIFOB(fd, 8) = min(pl_sd->status.int_, UINT8_MAX);
+ WFIFOB(fd, 9) = pc_need_status_point(pl_sd, SP_INT, 1);
+ WFIFOB(fd,10) = min(pl_sd->status.dex, UINT8_MAX);
+ WFIFOB(fd,11) = pc_need_status_point(pl_sd, SP_DEX, 1);
+ WFIFOB(fd,12) = min(pl_sd->status.luk, UINT8_MAX);
+ WFIFOB(fd,13) = pc_need_status_point(pl_sd, SP_LUK, 1);
+ WFIFOW(fd,14) = pl_sd->battle_status.batk+pl_sd->battle_status.rhw.atk+pl_sd->battle_status.lhw.atk;
+ WFIFOW(fd,16) = pl_sd->battle_status.rhw.atk2+pl_sd->battle_status.lhw.atk2;
+ WFIFOW(fd,18) = pl_sd->battle_status.matk_max;
+ WFIFOW(fd,20) = pl_sd->battle_status.matk_min;
+ WFIFOW(fd,22) = pl_sd->battle_status.def;
+ WFIFOW(fd,24) = pl_sd->battle_status.def2;
+ WFIFOW(fd,26) = pl_sd->battle_status.mdef;
+ WFIFOW(fd,28) = pl_sd->battle_status.mdef2;
+ WFIFOW(fd,30) = pl_sd->battle_status.hit;
+ WFIFOW(fd,32) = pl_sd->battle_status.flee;
+ WFIFOW(fd,34) = pl_sd->battle_status.flee2/10;
+ WFIFOW(fd,36) = pl_sd->battle_status.cri/10;
+ WFIFOW(fd,38) = (2000-pl_sd->battle_status.amotion)/10; // aspd
+ WFIFOW(fd,40) = 0; // FIXME: What is 'plusASPD' supposed to be? Maybe adelay?
+ WFIFOSET(fd,packet_len(0x214));
+}
+
+
+/// /check (CZ_REQ_STATUS_GM).
+/// Request character's status values.
+/// 0213 <char name>.24B
+void clif_parse_Check(int fd, struct map_session_data *sd)
+{
+ char charname[NAME_LENGTH];
+ struct map_session_data* pl_sd;
+
+ if(!pc_has_permission(sd, PC_PERM_USE_CHECK))
+ return;
+
+ safestrncpy(charname, (const char*)RFIFOP(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), sizeof(charname));
+
+ if( ( pl_sd = map_nick2sd(charname) ) == NULL || pc_get_group_level(sd) < pc_get_group_level(pl_sd) )
+ {
+ return;
+ }
+
+ clif_check(fd, pl_sd);
+}
+
+
+
+/// MAIL SYSTEM
+/// By Zephyrus
+///
+
+/// Notification about the result of adding an item to mail (ZC_ACK_MAIL_ADD_ITEM).
+/// 0255 <index>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_Mail_setattachment(int fd, int index, uint8 flag)
+{
+ WFIFOHEAD(fd,packet_len(0x255));
+ WFIFOW(fd,0) = 0x255;
+ WFIFOW(fd,2) = index;
+ WFIFOB(fd,4) = flag;
+ WFIFOSET(fd,packet_len(0x255));
+}
+
+
+/// Notification about the result of retrieving a mail attachment (ZC_MAIL_REQ_GET_ITEM).
+/// 0245 <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = too many items
+void clif_Mail_getattachment(int fd, uint8 flag)
+{
+ WFIFOHEAD(fd,packet_len(0x245));
+ WFIFOW(fd,0) = 0x245;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x245));
+}
+
+
+/// Notification about the result of sending a mail (ZC_MAIL_REQ_SEND).
+/// 0249 <result>.B
+/// result:
+/// 0 = success
+/// 1 = recipinent does not exist
+void clif_Mail_send(int fd, bool fail)
+{
+ WFIFOHEAD(fd,packet_len(0x249));
+ WFIFOW(fd,0) = 0x249;
+ WFIFOB(fd,2) = fail;
+ WFIFOSET(fd,packet_len(0x249));
+}
+
+
+/// Notification about the result of deleting a mail (ZC_ACK_MAIL_DELETE).
+/// 0257 <mail id>.L <result>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_Mail_delete(int fd, int mail_id, short fail)
+{
+ WFIFOHEAD(fd, packet_len(0x257));
+ WFIFOW(fd,0) = 0x257;
+ WFIFOL(fd,2) = mail_id;
+ WFIFOW(fd,6) = fail;
+ WFIFOSET(fd, packet_len(0x257));
+}
+
+
+/// Notification about the result of returning a mail (ZC_ACK_MAIL_RETURN).
+/// 0274 <mail id>.L <result>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_Mail_return(int fd, int mail_id, short fail)
+{
+ WFIFOHEAD(fd,packet_len(0x274));
+ WFIFOW(fd,0) = 0x274;
+ WFIFOL(fd,2) = mail_id;
+ WFIFOW(fd,6) = fail;
+ WFIFOSET(fd,packet_len(0x274));
+}
+
+
+/// Notification about new mail (ZC_MAIL_RECEIVE).
+/// 024a <mail id>.L <title>.40B <sender>.24B
+void clif_Mail_new(int fd, int mail_id, const char *sender, const char *title)
+{
+ WFIFOHEAD(fd,packet_len(0x24a));
+ WFIFOW(fd,0) = 0x24a;
+ WFIFOL(fd,2) = mail_id;
+ safestrncpy((char*)WFIFOP(fd,6), title, MAIL_TITLE_LENGTH);
+ safestrncpy((char*)WFIFOP(fd,46), sender, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x24a));
+}
+
+
+/// Opens/closes the mail window (ZC_MAIL_WINDOWS).
+/// 0260 <type>.L
+/// type:
+/// 0 = open
+/// 1 = close
+void clif_Mail_window(int fd, int flag)
+{
+ WFIFOHEAD(fd,packet_len(0x260));
+ WFIFOW(fd,0) = 0x260;
+ WFIFOL(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x260));
+}
+
+
+/// Lists mails stored in inbox (ZC_MAIL_REQ_GET_LIST).
+/// 0240 <packet len>.W <amount>.L { <mail id>.L <title>.40B <read>.B <sender>.24B <time>.L }*amount
+/// read:
+/// 0 = unread
+/// 1 = read
+void clif_Mail_refreshinbox(struct map_session_data *sd)
+{
+ int fd = sd->fd;
+ struct mail_data *md = &sd->mail.inbox;
+ struct mail_message *msg;
+ int len, i, j;
+
+ len = 8 + (73 * md->amount);
+
+ WFIFOHEAD(fd,len);
+ WFIFOW(fd,0) = 0x240;
+ WFIFOW(fd,2) = len;
+ WFIFOL(fd,4) = md->amount;
+ for( i = j = 0; i < MAIL_MAX_INBOX && j < md->amount; i++ )
+ {
+ msg = &md->msg[i];
+ if (msg->id < 1)
+ continue;
+
+ WFIFOL(fd,8+73*j) = msg->id;
+ memcpy(WFIFOP(fd,12+73*j), msg->title, MAIL_TITLE_LENGTH);
+ WFIFOB(fd,52+73*j) = (msg->status != MAIL_UNREAD);
+ memcpy(WFIFOP(fd,53+73*j), msg->send_name, NAME_LENGTH);
+ WFIFOL(fd,77+73*j) = (uint32)msg->timestamp;
+ j++;
+ }
+ WFIFOSET(fd,len);
+
+ if( md->full )
+ {// TODO: is this official?
+ char output[100];
+ sprintf(output, "Inbox is full (Max %d). Delete some mails.", MAIL_MAX_INBOX);
+ clif_disp_onlyself(sd, output, strlen(output));
+ }
+}
+
+
+/// Mail inbox list request (CZ_MAIL_GET_LIST).
+/// 023f
+void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd)
+{
+ struct mail_data* md = &sd->mail.inbox;
+
+ if( md->amount < MAIL_MAX_INBOX && (md->full || sd->mail.changed) )
+ intif_Mail_requestinbox(sd->status.char_id, 1);
+ else
+ clif_Mail_refreshinbox(sd);
+
+ mail_removeitem(sd, 0);
+ mail_removezeny(sd, 0);
+}
+
+
+/// Opens a mail (ZC_MAIL_REQ_OPEN).
+/// 0242 <packet len>.W <mail id>.L <title>.40B <sender>.24B <time>.L <zeny>.L
+/// <amount>.L <name id>.W <item type>.W <identified>.B <damaged>.B <refine>.B
+/// <card1>.W <card2>.W <card3>.W <card4>.W <message>.?B
+void clif_Mail_read(struct map_session_data *sd, int mail_id)
+{
+ int i, fd = sd->fd;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if( i == MAIL_MAX_INBOX )
+ {
+ clif_Mail_return(sd->fd, mail_id, 1); // Mail doesn't exist
+ ShowWarning("clif_parse_Mail_read: char '%s' trying to read a message not the inbox.\n", sd->status.name);
+ return;
+ }
+ else
+ {
+ struct mail_message *msg = &sd->mail.inbox.msg[i];
+ struct item *item = &msg->item;
+ struct item_data *data;
+ int msg_len = strlen(msg->body), len;
+
+ if( msg_len == 0 ) {
+ strcpy(msg->body, "(no message)");
+ msg_len = strlen(msg->body);
+ }
+
+ len = 101 + msg_len;
+
+ WFIFOHEAD(fd,len);
+ WFIFOW(fd,0) = 0x242;
+ WFIFOW(fd,2) = len;
+ WFIFOL(fd,4) = msg->id;
+ safestrncpy((char*)WFIFOP(fd,8), msg->title, MAIL_TITLE_LENGTH + 1);
+ safestrncpy((char*)WFIFOP(fd,48), msg->send_name, NAME_LENGTH + 1);
+ WFIFOL(fd,72) = 0;
+ WFIFOL(fd,76) = msg->zeny;
+
+ if( item->nameid && (data = itemdb_exists(item->nameid)) != NULL )
+ {
+ WFIFOL(fd,80) = item->amount;
+ WFIFOW(fd,84) = (data->view_id)?data->view_id:item->nameid;
+ WFIFOW(fd,86) = data->type;
+ WFIFOB(fd,88) = item->identify;
+ WFIFOB(fd,89) = item->attribute;
+ WFIFOB(fd,90) = item->refine;
+ WFIFOW(fd,91) = item->card[0];
+ WFIFOW(fd,93) = item->card[1];
+ WFIFOW(fd,95) = item->card[2];
+ WFIFOW(fd,97) = item->card[3];
+ }
+ else // no item, set all to zero
+ memset(WFIFOP(fd,80), 0x00, 19);
+
+ WFIFOB(fd,99) = (unsigned char)msg_len;
+ safestrncpy((char*)WFIFOP(fd,100), msg->body, msg_len + 1);
+ WFIFOSET(fd,len);
+
+ if (msg->status == MAIL_UNREAD) {
+ msg->status = MAIL_READ;
+ intif_Mail_read(mail_id);
+ clif_parse_Mail_refreshinbox(fd, sd);
+ }
+ }
+}
+
+
+/// Request to open a mail (CZ_MAIL_OPEN).
+/// 0241 <mail id>.L
+void clif_parse_Mail_read(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ clif_Mail_read(sd, RFIFOL(fd,2));
+}
+
+
+/// Request to receive mail's attachment (CZ_MAIL_GET_ITEM).
+/// 0244 <mail id>.L
+void clif_parse_Mail_getattach(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+ int i;
+ bool fail = false;
+
+ if( !chrif_isconnected() )
+ return;
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if( i == MAIL_MAX_INBOX )
+ return;
+
+ if( sd->mail.inbox.msg[i].zeny < 1 && (sd->mail.inbox.msg[i].item.nameid < 1 || sd->mail.inbox.msg[i].item.amount < 1) )
+ return;
+
+ if( sd->mail.inbox.msg[i].zeny + sd->status.zeny > MAX_ZENY )
+ {
+ clif_Mail_getattachment(fd, 1);
+ return;
+ }
+
+ if( sd->mail.inbox.msg[i].item.nameid > 0 )
+ {
+ struct item_data *data;
+ unsigned int weight;
+
+ if ((data = itemdb_exists(sd->mail.inbox.msg[i].item.nameid)) == NULL)
+ return;
+
+ switch( pc_checkadditem(sd, data->nameid, sd->mail.inbox.msg[i].item.amount) )
+ {
+ case ADDITEM_NEW:
+ fail = ( pc_inventoryblank(sd) == 0 );
+ break;
+ case ADDITEM_OVERAMOUNT:
+ fail = true;
+ }
+
+ if( fail )
+ {
+ clif_Mail_getattachment(fd, 1);
+ return;
+ }
+
+ weight = data->weight * sd->mail.inbox.msg[i].item.amount;
+ if( sd->weight + weight > sd->max_weight )
+ {
+ clif_Mail_getattachment(fd, 2);
+ return;
+ }
+ }
+
+ sd->mail.inbox.msg[i].zeny = 0;
+ memset(&sd->mail.inbox.msg[i].item, 0, sizeof(struct item));
+ clif_Mail_read(sd, mail_id);
+
+ intif_Mail_getattach(sd->status.char_id, mail_id);
+}
+
+
+/// Request to delete a mail (CZ_MAIL_DELETE).
+/// 0243 <mail id>.L
+void clif_parse_Mail_delete(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+ int i;
+
+ if( !chrif_isconnected() )
+ return;
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if (i < MAIL_MAX_INBOX)
+ {
+ struct mail_message *msg = &sd->mail.inbox.msg[i];
+
+ if( (msg->item.nameid > 0 && msg->item.amount > 0) || msg->zeny > 0 )
+ {// can't delete mail without removing attachment first
+ clif_Mail_delete(sd->fd, mail_id, 1);
+ return;
+ }
+
+ intif_Mail_delete(sd->status.char_id, mail_id);
+ }
+}
+
+
+/// Request to return a mail (CZ_REQ_MAIL_RETURN).
+/// 0273 <mail id>.L <receive name>.24B
+void clif_parse_Mail_return(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+ int i;
+
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if( i < MAIL_MAX_INBOX && sd->mail.inbox.msg[i].send_id != 0 )
+ intif_Mail_return(sd->status.char_id, mail_id);
+ else
+ clif_Mail_return(sd->fd, mail_id, 1);
+}
+
+
+/// Request to add an item or Zeny to mail (CZ_MAIL_ADD_ITEM).
+/// 0247 <index>.W <amount>.L
+void clif_parse_Mail_setattach(int fd, struct map_session_data *sd)
+{
+ int idx = RFIFOW(fd,2);
+ int amount = RFIFOL(fd,4);
+ unsigned char flag;
+
+ if( !chrif_isconnected() )
+ return;
+ if (idx < 0 || amount < 0)
+ return;
+
+ flag = mail_setitem(sd, idx, amount);
+ clif_Mail_setattachment(fd,idx,flag);
+}
+
+
+/// Request to reset mail item and/or Zeny (CZ_MAIL_RESET_ITEM).
+/// 0246 <type>.W
+/// type:
+/// 0 = reset all
+/// 1 = remove item
+/// 2 = remove zeny
+void clif_parse_Mail_winopen(int fd, struct map_session_data *sd)
+{
+ int flag = RFIFOW(fd,2);
+
+ if (flag == 0 || flag == 1)
+ mail_removeitem(sd, 0);
+ if (flag == 0 || flag == 2)
+ mail_removezeny(sd, 0);
+}
+
+
+/// Request to send mail (CZ_MAIL_SEND).
+/// 0248 <packet len>.W <recipient>.24B <title>.40B <body len>.B <body>.?B
+void clif_parse_Mail_send(int fd, struct map_session_data *sd)
+{
+ struct mail_message msg;
+ int body_len;
+
+ if( !chrif_isconnected() )
+ return;
+ if( sd->state.trading )
+ return;
+
+ if( RFIFOW(fd,2) < 69 ) {
+ ShowWarning("Invalid Msg Len from account %d.\n", sd->status.account_id);
+ return;
+ }
+
+ if( DIFF_TICK(sd->cansendmail_tick, gettick()) > 0 )
+ {
+ clif_displaymessage(sd->fd,msg_txt(675)); //"Cannot send mails too fast!!."
+ clif_Mail_send(fd, true); // fail
+ return;
+ }
+
+ body_len = RFIFOB(fd,68);
+
+ if (body_len > MAIL_BODY_LENGTH)
+ body_len = MAIL_BODY_LENGTH;
+
+ if( !mail_setattachment(sd, &msg) )
+ { // Invalid Append condition
+ clif_Mail_send(sd->fd, true); // fail
+ mail_removeitem(sd,0);
+ mail_removezeny(sd,0);
+ return;
+ }
+
+ msg.id = 0; // id will be assigned by charserver
+ msg.send_id = sd->status.char_id;
+ msg.dest_id = 0; // will attempt to resolve name
+ safestrncpy(msg.send_name, sd->status.name, NAME_LENGTH);
+ safestrncpy(msg.dest_name, (char*)RFIFOP(fd,4), NAME_LENGTH);
+ safestrncpy(msg.title, (char*)RFIFOP(fd,28), MAIL_TITLE_LENGTH);
+
+ if (msg.title[0] == '\0') {
+ return; // Message has no length and somehow client verification was skipped.
+ }
+
+ if (body_len)
+ safestrncpy(msg.body, (char*)RFIFOP(fd,69), body_len + 1);
+ else
+ memset(msg.body, 0x00, MAIL_BODY_LENGTH);
+
+ msg.timestamp = time(NULL);
+ if( !intif_Mail_send(sd->status.account_id, &msg) )
+ mail_deliveryfail(sd, &msg);
+
+ sd->cansendmail_tick = gettick() + 1000; // 1 Second flood Protection
+}
+
+
+/// AUCTION SYSTEM
+/// By Zephyrus
+///
+
+/// Opens/closes the auction window (ZC_AUCTION_WINDOWS).
+/// 025f <type>.L
+/// type:
+/// 0 = open
+/// 1 = close
+void clif_Auction_openwindow(struct map_session_data *sd)
+{
+ int fd = sd->fd;
+
+ if( sd->state.storage_flag || sd->state.vending || sd->state.buyingstore || sd->state.trading )
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x25f));
+ WFIFOW(fd,0) = 0x25f;
+ WFIFOL(fd,2) = 0;
+ WFIFOSET(fd,packet_len(0x25f));
+}
+
+
+/// Returns auction item search results (ZC_AUCTION_ITEM_REQ_SEARCH).
+/// 0252 <packet len>.W <pages>.L <count>.L { <auction id>.L <seller name>.24B <name id>.W <type>.L <amount>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <now price>.L <max price>.L <buyer name>.24B <delete time>.L }*
+void clif_Auction_results(struct map_session_data *sd, short count, short pages, uint8 *buf)
+{
+ int i, fd = sd->fd, len = sizeof(struct auction_data);
+ struct auction_data auction;
+ struct item_data *item;
+ int k;
+
+ WFIFOHEAD(fd,12 + (count * 83));
+ WFIFOW(fd,0) = 0x252;
+ WFIFOW(fd,2) = 12 + (count * 83);
+ WFIFOL(fd,4) = pages;
+ WFIFOL(fd,8) = count;
+
+ for( i = 0; i < count; i++ )
+ {
+ memcpy(&auction, RBUFP(buf,i * len), len);
+ k = 12 + (i * 83);
+
+ WFIFOL(fd,k) = auction.auction_id;
+ safestrncpy((char*)WFIFOP(fd,4+k), auction.seller_name, NAME_LENGTH);
+
+ if( (item = itemdb_exists(auction.item.nameid)) != NULL && item->view_id > 0 )
+ WFIFOW(fd,28+k) = item->view_id;
+ else
+ WFIFOW(fd,28+k) = auction.item.nameid;
+
+ WFIFOL(fd,30+k) = auction.type;
+ WFIFOW(fd,34+k) = auction.item.amount; // Always 1
+ WFIFOB(fd,36+k) = auction.item.identify;
+ WFIFOB(fd,37+k) = auction.item.attribute;
+ WFIFOB(fd,38+k) = auction.item.refine;
+ WFIFOW(fd,39+k) = auction.item.card[0];
+ WFIFOW(fd,41+k) = auction.item.card[1];
+ WFIFOW(fd,43+k) = auction.item.card[2];
+ WFIFOW(fd,45+k) = auction.item.card[3];
+ WFIFOL(fd,47+k) = auction.price;
+ WFIFOL(fd,51+k) = auction.buynow;
+ safestrncpy((char*)WFIFOP(fd,55+k), auction.buyer_name, NAME_LENGTH);
+ WFIFOL(fd,79+k) = (uint32)auction.timestamp;
+ }
+ 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));
+ WFIFOW(fd,0) = 0x256;
+ WFIFOW(fd,2) = index;
+ WFIFOB(fd,4) = 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 )
+ clif_additem(sd, sd->auction.index, sd->auction.amount, 0);
+
+ 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;
+ int amount = RFIFOL(fd,4); // Always 1
+ struct item_data *item;
+
+ if( sd->auction.amount > 0 )
+ sd->auction.amount = 0;
+
+ if( idx < 0 || idx >= MAX_INVENTORY )
+ {
+ ShowWarning("Character %s trying to set invalid item index in auctions.\n", sd->status.name);
+ return;
+ }
+
+ 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;
+ }
+
+ if( (item = itemdb_exists(sd->status.inventory[idx].nameid)) != NULL && !(item->type == IT_ARMOR || item->type == IT_PETARMOR || item->type == IT_WEAPON || item->type == IT_CARD || item->type == IT_ETC) )
+ { // Consumable or pets are not allowed
+ clif_Auction_setitem(sd->fd, idx, true);
+ return;
+ }
+
+ if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
+ !sd->status.inventory[idx].identify ||
+ !itemdb_canauction(&sd->status.inventory[idx],pc_get_group_level(sd)) ) { // Quest Item or something else
+ clif_Auction_setitem(sd->fd, idx, true);
+ return;
+ }
+
+ sd->auction.index = idx;
+ sd->auction.amount = amount;
+ clif_Auction_setitem(fd, idx + 2, false);
+}
+
+/// Result from an auction action (ZC_AUCTION_RESULT).
+/// 0250 <result>.B
+/// result:
+/// 0 = You have failed to bid into the auction
+/// 1 = You have successfully bid in the auction
+/// 2 = The auction has been canceled
+/// 3 = An auction with at least one bidder cannot be canceled
+/// 4 = You cannot register more than 5 items in an auction at a time
+/// 5 = You do not have enough Zeny to pay the Auction Fee
+/// 6 = You have won the auction
+/// 7 = You have failed to win the auction
+/// 8 = You do not have enough Zeny
+/// 9 = You cannot place more than 5 bids at a time
+void clif_Auction_message(int fd, unsigned char flag)
+{
+ WFIFOHEAD(fd,packet_len(0x250));
+ WFIFOW(fd,0) = 0x250;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x250));
+}
+
+
+/// Result of the auction close request (ZC_AUCTION_ACK_MY_SELL_STOP).
+/// 025e <result>.W
+/// result:
+/// 0 = You have ended the auction
+/// 1 = You cannot end the auction
+/// 2 = Auction ID is incorrect
+void clif_Auction_close(int fd, unsigned char flag)
+{
+ WFIFOHEAD(fd,packet_len(0x25e));
+ WFIFOW(fd,0) = 0x25d; // BUG: The client identifies this packet as 0x25d (CZ_AUCTION_REQ_MY_SELL_STOP)
+ WFIFOW(fd,2) = 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;
+ struct item_data *item;
+
+ auction.price = RFIFOL(fd,2);
+ auction.buynow = RFIFOL(fd,6);
+ auction.hours = RFIFOW(fd,10);
+
+ // Invalid Situations...
+ if( sd->auction.amount < 1 )
+ {
+ ShowWarning("Character %s trying to register auction without item.\n", sd->status.name);
+ return;
+ }
+
+ if( auction.price >= auction.buynow )
+ {
+ ShowWarning("Character %s trying to alter auction prices.\n", sd->status.name);
+ return;
+ }
+
+ if( auction.hours < 1 || auction.hours > 48 )
+ {
+ ShowWarning("Character %s trying to enter an invalid time for auction.\n", sd->status.name);
+ return;
+ }
+
+ // Auction checks...
+ if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) )
+ {
+ clif_Auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee.
+ return;
+ }
+
+ if( auction.buynow > battle_config.auction_maximumprice )
+ { // Zeny Limits
+ auction.buynow = battle_config.auction_maximumprice;
+ if( auction.price >= auction.buynow )
+ auction.price = auction.buynow - 1;
+ }
+
+ auction.auction_id = 0;
+ auction.seller_id = sd->status.char_id;
+ safestrncpy(auction.seller_name, sd->status.name, sizeof(auction.seller_name));
+ auction.buyer_id = 0;
+ 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 )
+ {
+ clif_Auction_message(fd, 2); // The auction has been canceled
+ return;
+ }
+
+ if( (item = itemdb_exists(sd->status.inventory[sd->auction.index].nameid)) == NULL )
+ { // Just in case
+ clif_Auction_message(fd, 2); // The auction has been canceled
+ return;
+ }
+
+ 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;
+ auction.timestamp = 0;
+
+ if( !intif_Auction_register(&auction) )
+ clif_Auction_message(fd, 4); // No Char Server? lets say something to the client
+ else
+ {
+ int zeny = auction.hours*battle_config.auction_feeperhour;
+
+ pc_delitem(sd, sd->auction.index, sd->auction.amount, 1, 6, LOG_TYPE_AUCTION);
+ sd->auction.amount = 0;
+
+ pc_payzeny(sd, zeny, LOG_TYPE_AUCTION, NULL);
+ }
+}
+
+
+/// 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);
+
+ 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);
+
+ 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);
+ int bid = RFIFOL(fd,6);
+
+ if( !pc_can_give_items(sd) ) { //They aren't supposed to give zeny [Inkfish]
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return;
+ }
+
+ if( bid <= 0 )
+ clif_Auction_message(fd, 0); // You have failed to bid into the auction
+ else if( bid > sd->status.zeny )
+ clif_Auction_message(fd, 8); // You do not have enough zeny
+ else if ( CheckForCharServer() ) // char server is down (bugreport:1138)
+ clif_Auction_message(fd, 0); // You have failed to bid into the auction
+ else {
+ pc_payzeny(sd, bid, LOG_TYPE_AUCTION, NULL);
+ intif_Auction_bid(sd->status.char_id, sd->status.name, auction_id, bid);
+ }
+}
+
+
+/// 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); // FIXME: bug #5071
+
+ clif_parse_Auction_cancelreg(fd, sd);
+
+ 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;
+ clif_parse_Auction_cancelreg(fd, sd);
+
+ intif_Auction_requestlist(sd->status.char_id, type, 0, "", 1);
+}
+
+
+/// CASH/POINT SHOP
+///
+
+/// List of items offered in a cash shop (ZC_PC_CASH_POINT_ITEMLIST).
+/// 0287 <packet len>.W <cash point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }*
+/// 0287 <packet len>.W <cash point>.L <kafra point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }* (PACKETVER >= 20070711)
+void clif_cashshop_show(struct map_session_data *sd, struct npc_data *nd)
+{
+ int fd,i;
+#if PACKETVER < 20070711
+ const int offset = 8;
+#else
+ const int offset = 12;
+#endif
+
+ nullpo_retv(sd);
+ nullpo_retv(nd);
+
+ fd = sd->fd;
+ sd->npc_shopid = nd->bl.id;
+ WFIFOHEAD(fd,offset+nd->u.shop.count*11);
+ WFIFOW(fd,0) = 0x287;
+ WFIFOW(fd,2) = offset+nd->u.shop.count*11;
+ WFIFOL(fd,4) = sd->cashPoints; // Cash Points
+#if PACKETVER >= 20070711
+ WFIFOL(fd,8) = sd->kafraPoints; // Kafra Points
+#endif
+
+ for( i = 0; i < nd->u.shop.count; i++ )
+ {
+ struct item_data* id = itemdb_search(nd->u.shop.shop_item[i].nameid);
+ WFIFOL(fd,offset+0+i*11) = nd->u.shop.shop_item[i].value;
+ WFIFOL(fd,offset+4+i*11) = nd->u.shop.shop_item[i].value; // Discount Price
+ WFIFOB(fd,offset+8+i*11) = itemtype(id->type);
+ WFIFOW(fd,offset+9+i*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid;
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Cashshop Buy Ack (ZC_PC_CASH_POINT_UPDATE).
+/// 0289 <cash point>.L <error>.W
+/// 0289 <cash point>.L <kafra point>.L <error>.W (PACKETVER >= 20070711)
+/// error:
+/// 0 = The deal has successfully completed. (ERROR_TYPE_NONE)
+/// 1 = The Purchase has failed because the NPC does not exist. (ERROR_TYPE_NPC)
+/// 2 = The Purchase has failed because the Kafra Shop System is not working correctly. (ERROR_TYPE_SYSTEM)
+/// 3 = You are over your Weight Limit. (ERROR_TYPE_INVENTORY_WEIGHT)
+/// 4 = You cannot purchase items while you are in a trade. (ERROR_TYPE_EXCHANGE)
+/// 5 = The Purchase has failed because the Item Information was incorrect. (ERROR_TYPE_ITEM_ID)
+/// 6 = You do not have enough Kafra Credit Points. (ERROR_TYPE_MONEY)
+/// 7 = You can purchase up to 10 items.
+/// 8 = Some items could not be purchased.
+void clif_cashshop_ack(struct map_session_data* sd, int error)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x289));
+ WFIFOW(fd,0) = 0x289;
+ WFIFOL(fd,2) = sd->cashPoints;
+#if PACKETVER < 20070711
+ WFIFOW(fd,6) = TOW(error);
+#else
+ WFIFOL(fd,6) = sd->kafraPoints;
+ WFIFOW(fd,10) = TOW(error);
+#endif
+ WFIFOSET(fd, packet_len(0x289));
+}
+
+
+/// Request to buy item(s) from cash shop (CZ_PC_BUY_CASH_POINT_ITEM).
+/// 0288 <name id>.W <amount>.W
+/// 0288 <name id>.W <amount>.W <kafra points>.L (PACKETVER >= 20070711)
+/// 0288 <packet len>.W <kafra points>.L <count>.W { <amount>.W <name id>.W }.4B*count (PACKETVER >= 20100803)
+void clif_parse_cashshop_buy(int fd, struct map_session_data *sd)
+{
+ int fail = 0;
+ nullpo_retv(sd);
+
+ if( sd->state.trading || !sd->npc_shopid )
+ fail = 1;
+ else
+ {
+#if PACKETVER < 20101116
+ short nameid = RFIFOW(fd,2);
+ short amount = RFIFOW(fd,4);
+ int points = RFIFOL(fd,6);
+
+ fail = npc_cashshop_buy(sd, nameid, amount, points);
+#else
+ int len = RFIFOW(fd,2);
+ int points = RFIFOL(fd,4);
+ int count = RFIFOW(fd,8);
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,10);
+
+ if( len < 10 || len != 10 + count * 4)
+ {
+ ShowWarning("Player %u sent incorrect cash shop buy packet (len %u:%u)!\n", sd->status.char_id, len, 10 + count * 4);
+ return;
+ }
+ fail = npc_cashshop_buylist(sd,points,count,item_list);
+#endif
+ }
+
+ clif_cashshop_ack(sd,fail);
+}
+
+
+/// Adoption System
+///
+
+/// Adoption message (ZC_BABYMSG).
+/// 0216 <msg>.L
+/// msg:
+/// 0 = "You cannot adopt more than 1 child."
+/// 1 = "You must be at least character level 70 in order to adopt someone."
+/// 2 = "You cannot adopt a married person."
+void clif_Adopt_reply(struct map_session_data *sd, int type)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,6);
+ WFIFOW(fd,0) = 0x216;
+ WFIFOL(fd,2) = type;
+ WFIFOSET(fd,6);
+}
+
+
+/// Adoption confirmation (ZC_REQ_BABY).
+/// 01f6 <account id>.L <char id>.L <name>.B
+void clif_Adopt_request(struct map_session_data *sd, struct map_session_data *src, int p_id)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,34);
+ WFIFOW(fd,0) = 0x1f6;
+ WFIFOL(fd,2) = src->status.account_id;
+ WFIFOL(fd,6) = p_id;
+ memcpy(WFIFOP(fd,10), src->status.name, NAME_LENGTH);
+ WFIFOSET(fd,34);
+}
+
+
+/// Request to adopt a player (CZ_REQ_JOIN_BABY).
+/// 01f9 <account id>.L
+void clif_parse_Adopt_request(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *tsd = map_id2sd(RFIFOL(fd,2)), *p_sd = map_charid2sd(sd->status.partner_id);
+
+ if( pc_can_Adopt(sd, p_sd, tsd) )
+ {
+ tsd->adopt_invite = sd->status.account_id;
+ clif_Adopt_request(tsd, sd, p_sd->status.account_id);
+ }
+}
+
+
+/// Answer to adopt confirmation (CZ_JOIN_BABY).
+/// 01f7 <account id>.L <char id>.L <answer>.L
+/// answer:
+/// 0 = rejected
+/// 1 = accepted
+void clif_parse_Adopt_reply(int fd, struct map_session_data *sd)
+{
+ int p1_id = RFIFOL(fd,2);
+ int p2_id = RFIFOL(fd,6);
+ int result = RFIFOL(fd,10);
+ struct map_session_data* p1_sd = map_id2sd(p1_id);
+ struct map_session_data* p2_sd = map_id2sd(p2_id);
+
+ int pid = sd->adopt_invite;
+ sd->adopt_invite = 0;
+
+ if( p1_sd == NULL || p2_sd == NULL )
+ return; // Both players need to be online
+
+ if( pid != p1_sd->status.account_id )
+ return; // Incorrect values
+
+ if( result == 0 )
+ return; // Rejected
+
+ pc_adoption(p1_sd, p2_sd, sd);
+}
+
+
+/// Convex Mirror (ZC_BOSS_INFO).
+/// 0293 <infoType>.B <x>.L <y>.L <minHours>.W <minMinutes>.W <maxHours>.W <maxMinutes>.W <monster name>.51B
+/// infoType:
+/// 0 = No boss on this map (BOSS_INFO_NOT).
+/// 1 = Boss is alive (position update) (BOSS_INFO_ALIVE).
+/// 2 = Boss is alive (initial announce) (BOSS_INFO_ALIVE_WITHMSG).
+/// 3 = Boss is dead (BOSS_INFO_DEAD).
+void clif_bossmapinfo(int fd, struct mob_data *md, short flag)
+{
+ WFIFOHEAD(fd,70);
+ memset(WFIFOP(fd,0),0,70);
+ WFIFOW(fd,0) = 0x293;
+
+ if( md != NULL )
+ {
+ if( md->bl.prev != NULL )
+ { // Boss on This Map
+ if( flag )
+ {
+ WFIFOB(fd,2) = 1;
+ WFIFOL(fd,3) = md->bl.x;
+ WFIFOL(fd,7) = md->bl.y;
+ }
+ else
+ WFIFOB(fd,2) = 2; // First Time
+ }
+ else if (md->spawn_timer != INVALID_TIMER)
+ { // Boss is Dead
+ const struct TimerData * timer_data = get_timer(md->spawn_timer);
+ unsigned int seconds;
+ int hours, minutes;
+
+ seconds = DIFF_TICK(timer_data->tick, gettick()) / 1000 + 60;
+ hours = seconds / (60 * 60);
+ seconds = seconds - (60 * 60 * hours);
+ minutes = seconds / 60;
+
+ WFIFOB(fd,2) = 3;
+ WFIFOW(fd,11) = hours; // Hours
+ WFIFOW(fd,13) = minutes; // Minutes
+ }
+ safestrncpy((char*)WFIFOP(fd,19), md->db->jname, NAME_LENGTH);
+ }
+
+ WFIFOSET(fd,70);
+}
+
+
+/// Requesting equip of a player (CZ_EQUIPWIN_MICROSCOPE).
+/// 02d6 <account id>.L
+void clif_parse_ViewPlayerEquip(int fd, struct map_session_data* sd)
+{
+ int charid = RFIFOL(fd, 2);
+ struct map_session_data* tsd = map_id2sd(charid);
+
+ if (!tsd)
+ return;
+
+ if( tsd->status.show_equip || pc_has_permission(sd, PC_PERM_VIEW_EQUIPMENT) )
+ clif_viewequip_ack(sd, tsd);
+ else
+ clif_viewequip_fail(sd);
+}
+
+
+/// Request to change equip window tick (CZ_CONFIG).
+/// 02d8 <type>.L <value>.L
+/// type:
+/// 0 = open equip window
+/// value:
+/// 0 = disabled
+/// 1 = enabled
+void clif_parse_EquipTick(int fd, struct map_session_data* sd)
+{
+ bool flag = (bool)RFIFOL(fd,6);
+ sd->status.show_equip = flag;
+ clif_equiptickack(sd, flag);
+}
+
+
+/// Questlog System [Kevin] [Inkfish]
+///
+
+/// Sends list of all quest states (ZC_ALL_QUEST_LIST).
+/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
+void clif_quest_send_list(struct map_session_data * sd)
+{
+ int fd = sd->fd;
+ int i;
+ int len = sd->avail_quests*5+8;
+
+ WFIFOHEAD(fd,len);
+ WFIFOW(fd, 0) = 0x2b1;
+ WFIFOW(fd, 2) = len;
+ WFIFOL(fd, 4) = sd->avail_quests;
+
+ for( i = 0; i < sd->avail_quests; i++ )
+ {
+ WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id;
+ WFIFOB(fd, i*5+12) = sd->quest_log[i].state;
+ }
+
+ WFIFOSET(fd, len);
+}
+
+
+/// Sends list of all quest missions (ZC_ALL_QUEST_MISSION).
+/// 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num
+void clif_quest_send_mission(struct map_session_data * sd)
+{
+ int fd = sd->fd;
+ int i, j;
+ int len = sd->avail_quests*104+8;
+ struct mob_db *mob;
+
+ WFIFOHEAD(fd, len);
+ WFIFOW(fd, 0) = 0x2b2;
+ WFIFOW(fd, 2) = len;
+ WFIFOL(fd, 4) = sd->avail_quests;
+
+ for( i = 0; i < sd->avail_quests; i++ )
+ {
+ WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
+ WFIFOL(fd, i*104+12) = sd->quest_log[i].time - quest_db[sd->quest_index[i]].time;
+ WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
+ WFIFOW(fd, i*104+20) = quest_db[sd->quest_index[i]].num_objectives;
+
+ for( j = 0 ; j < quest_db[sd->quest_index[i]].num_objectives; j++ )
+ {
+ WFIFOL(fd, i*104+22+j*30) = quest_db[sd->quest_index[i]].mob[j];
+ WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
+ mob = mob_db(quest_db[sd->quest_index[i]].mob[j]);
+ memcpy(WFIFOP(fd, i*104+28+j*30), mob?mob->jname:"NULL", NAME_LENGTH);
+ }
+ }
+
+ WFIFOSET(fd, len);
+}
+
+
+/// Notification about a new quest (ZC_ADD_QUEST).
+/// 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3
+void clif_quest_add(struct map_session_data * sd, struct quest * qd, int index)
+{
+ int fd = sd->fd;
+ int i;
+ struct mob_db *mob;
+
+ WFIFOHEAD(fd, packet_len(0x2b3));
+ WFIFOW(fd, 0) = 0x2b3;
+ WFIFOL(fd, 2) = qd->quest_id;
+ WFIFOB(fd, 6) = qd->state;
+ WFIFOB(fd, 7) = qd->time - quest_db[index].time;
+ WFIFOL(fd, 11) = qd->time;
+ WFIFOW(fd, 15) = quest_db[index].num_objectives;
+
+ for( i = 0; i < quest_db[index].num_objectives; i++ )
+ {
+ WFIFOL(fd, i*30+17) = quest_db[index].mob[i];
+ WFIFOW(fd, i*30+21) = qd->count[i];
+ mob = mob_db(quest_db[index].mob[i]);
+ memcpy(WFIFOP(fd, i*30+23), mob?mob->jname:"NULL", NAME_LENGTH);
+ }
+
+ WFIFOSET(fd, packet_len(0x2b3));
+}
+
+
+/// Notification about a quest being removed (ZC_DEL_QUEST).
+/// 02b4 <quest id>.L
+void clif_quest_delete(struct map_session_data * sd, int quest_id)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2b4));
+ WFIFOW(fd, 0) = 0x2b4;
+ WFIFOL(fd, 2) = quest_id;
+ WFIFOSET(fd, packet_len(0x2b4));
+}
+
+
+/// Notification of an update to the hunting mission counter (ZC_UPDATE_MISSION_HUNT).
+/// 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3
+void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd, int index)
+{
+ int fd = sd->fd;
+ int i;
+ int len = quest_db[index].num_objectives*12+6;
+
+ WFIFOHEAD(fd, len);
+ WFIFOW(fd, 0) = 0x2b5;
+ WFIFOW(fd, 2) = len;
+ WFIFOW(fd, 4) = quest_db[index].num_objectives;
+
+ for( i = 0; i < quest_db[index].num_objectives; i++ )
+ {
+ WFIFOL(fd, i*12+6) = qd->quest_id;
+ WFIFOL(fd, i*12+10) = quest_db[index].mob[i];
+ WFIFOW(fd, i*12+14) = quest_db[index].count[i];
+ WFIFOW(fd, i*12+16) = qd->count[i];
+ }
+
+ WFIFOSET(fd, len);
+}
+
+
+/// Request to change the state of a quest (CZ_ACTIVE_QUEST).
+/// 02b6 <quest id>.L <active>.B
+void clif_parse_questStateAck(int fd, struct map_session_data * sd)
+{
+ quest_update_status(sd, RFIFOL(fd,2), RFIFOB(fd,6)?Q_ACTIVE:Q_INACTIVE);
+}
+
+
+/// Notification about the change of a quest state (ZC_ACTIVE_QUEST).
+/// 02b7 <quest id>.L <active>.B
+void clif_quest_update_status(struct map_session_data * sd, int quest_id, bool active)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2b7));
+ WFIFOW(fd, 0) = 0x2b7;
+ WFIFOL(fd, 2) = quest_id;
+ WFIFOB(fd, 6) = active;
+ WFIFOSET(fd, packet_len(0x2b7));
+}
+
+
+/// Notification about an NPC's quest state (ZC_QUEST_NOTIFY_EFFECT).
+/// 0446 <npc id>.L <x>.W <y>.W <effect>.W <type>.W
+/// effect:
+/// 0 = none
+/// 1 = exclamation mark icon
+/// 2 = question mark icon
+/// type:
+/// 0 = yellow
+/// 1 = orange
+/// 2 = green
+/// 3 = purple
+void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, short state, short color)
+{
+#if PACKETVER >= 20090218
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x446));
+ WFIFOW(fd, 0) = 0x446;
+ WFIFOL(fd, 2) = bl->id;
+ WFIFOW(fd, 6) = bl->x;
+ WFIFOW(fd, 8) = bl->y;
+ WFIFOW(fd, 10) = state;
+ WFIFOW(fd, 12) = color;
+ WFIFOSET(fd, packet_len(0x446));
+#endif
+}
+
+
+/// Mercenary System
+///
+
+/// Notification about a mercenary status parameter change (ZC_MER_PAR_CHANGE).
+/// 02a2 <var id>.W <value>.L
+void clif_mercenary_updatestatus(struct map_session_data *sd, int type)
+{
+ struct mercenary_data *md;
+ struct status_data *status;
+ int fd;
+ if( sd == NULL || (md = sd->md) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &md->battle_status;
+ WFIFOHEAD(fd,packet_len(0x2a2));
+ WFIFOW(fd,0) = 0x2a2;
+ WFIFOW(fd,2) = type;
+ switch( type )
+ {
+ case SP_ATK1:
+ {
+ int atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk;
+ WFIFOL(fd,4) = cap_value(atk, 0, INT16_MAX);
+ }
+ break;
+ case SP_MATK1:
+ WFIFOL(fd,4) = cap_value(status->matk_max, 0, INT16_MAX);
+ break;
+ case SP_HIT:
+ WFIFOL(fd,4) = status->hit;
+ break;
+ case SP_CRITICAL:
+ WFIFOL(fd,4) = status->cri/10;
+ break;
+ case SP_DEF1:
+ WFIFOL(fd,4) = status->def;
+ break;
+ case SP_MDEF1:
+ WFIFOL(fd,4) = status->mdef;
+ break;
+ case SP_MERCFLEE:
+ WFIFOL(fd,4) = status->flee;
+ break;
+ case SP_ASPD:
+ WFIFOL(fd,4) = status->amotion;
+ break;
+ case SP_HP:
+ WFIFOL(fd,4) = status->hp;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4) = status->max_hp;
+ break;
+ case SP_SP:
+ WFIFOL(fd,4) = status->sp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4) = status->max_sp;
+ break;
+ case SP_MERCKILLS:
+ WFIFOL(fd,4) = md->mercenary.kill_count;
+ break;
+ case SP_MERCFAITH:
+ WFIFOL(fd,4) = mercenary_get_faith(md);
+ break;
+ }
+ WFIFOSET(fd,packet_len(0x2a2));
+}
+
+
+/// Mercenary base status data (ZC_MER_INIT).
+/// 029b <id>.L <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W
+/// <name>.24B <level>.W <hp>.L <maxhp>.L <sp>.L <maxsp>.L <expire time>.L <faith>.W
+/// <calls>.L <kills>.L <atk range>.W
+void clif_mercenary_info(struct map_session_data *sd)
+{
+ int fd;
+ struct mercenary_data *md;
+ struct status_data *status;
+ int atk;
+
+ if( sd == NULL || (md = sd->md) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &md->battle_status;
+
+ WFIFOHEAD(fd,packet_len(0x29b));
+ WFIFOW(fd,0) = 0x29b;
+ WFIFOL(fd,2) = md->bl.id;
+
+ // Mercenary shows ATK as a random value between ATK ~ ATK2
+ atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk;
+ WFIFOW(fd,6) = cap_value(atk, 0, INT16_MAX);
+ WFIFOW(fd,8) = cap_value(status->matk_max, 0, INT16_MAX);
+ WFIFOW(fd,10) = status->hit;
+ WFIFOW(fd,12) = status->cri/10;
+ WFIFOW(fd,14) = status->def;
+ WFIFOW(fd,16) = status->mdef;
+ WFIFOW(fd,18) = status->flee;
+ WFIFOW(fd,20) = status->amotion;
+ safestrncpy((char*)WFIFOP(fd,22), md->db->name, NAME_LENGTH);
+ WFIFOW(fd,46) = md->db->lv;
+ WFIFOL(fd,48) = status->hp;
+ WFIFOL(fd,52) = status->max_hp;
+ WFIFOL(fd,56) = status->sp;
+ WFIFOL(fd,60) = status->max_sp;
+ WFIFOL(fd,64) = (int)time(NULL) + (mercenary_get_lifetime(md) / 1000);
+ WFIFOW(fd,68) = mercenary_get_faith(md);
+ WFIFOL(fd,70) = mercenary_get_calls(md);
+ WFIFOL(fd,74) = md->mercenary.kill_count;
+ WFIFOW(fd,78) = md->battle_status.rhw.range;
+ WFIFOSET(fd,packet_len(0x29b));
+}
+
+
+/// Mercenary skill tree (ZC_MER_SKILLINFO_LIST).
+/// 029d <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }*
+void clif_mercenary_skillblock(struct map_session_data *sd)
+{
+ struct mercenary_data *md;
+ int fd, i, len = 4, id, j;
+
+ if( sd == NULL || (md = sd->md) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,4+37*MAX_MERCSKILL);
+ WFIFOW(fd,0) = 0x29d;
+ for( i = 0; i < MAX_MERCSKILL; i++ )
+ {
+ if( (id = md->db->skill[i].id) == 0 )
+ continue;
+ j = id - MC_SKILLBASE;
+ WFIFOW(fd,len) = id;
+ WFIFOL(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+6) = md->db->skill[j].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id, md->db->skill[j].lv);
+ WFIFOW(fd,len+10) = skill_get_range2(&md->bl, id, md->db->skill[j].lv);
+ safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ WFIFOB(fd,len+36) = 0; // Skillable for Mercenary?
+ len += 37;
+ }
+
+ WFIFOW(fd,2) = len;
+ WFIFOSET(fd,len);
+}
+
+
+/// Request to invoke a mercenary menu action (CZ_MER_COMMAND).
+/// 029f <command>.B
+/// 1 = mercenary information
+/// 2 = delete
+void clif_parse_mercenary_action(int fd, struct map_session_data* sd)
+{
+ int option = RFIFOB(fd,2);
+ if( sd->md == NULL )
+ return;
+
+ if( option == 2 ) merc_delete(sd->md, 2);
+}
+
+
+/// Mercenary Message
+/// message:
+/// 0 = Mercenary soldier's duty hour is over.
+/// 1 = Your mercenary soldier has been killed.
+/// 2 = Your mercenary soldier has been fired.
+/// 3 = Your mercenary soldier has ran away.
+void clif_mercenary_message(struct map_session_data* sd, int message)
+{
+ clif_msg(sd, 1266 + message);
+}
+
+
+/// Notification about the remaining time of a rental item (ZC_CASH_TIME_COUNTER).
+/// 0298 <name id>.W <seconds>.L
+void clif_rental_time(int fd, int nameid, int seconds)
+{ // '<ItemName>' item will disappear in <seconds/60> minutes.
+ WFIFOHEAD(fd,packet_len(0x298));
+ WFIFOW(fd,0) = 0x298;
+ WFIFOW(fd,2) = nameid;
+ WFIFOL(fd,4) = seconds;
+ WFIFOSET(fd,packet_len(0x298));
+}
+
+
+/// Deletes a rental item from client's inventory (ZC_CASH_ITEM_DELETE).
+/// 0299 <index>.W <name id>.W
+void clif_rental_expired(int fd, int index, int nameid)
+{ // '<ItemName>' item has been deleted from the Inventory
+ WFIFOHEAD(fd,packet_len(0x299));
+ WFIFOW(fd,0) = 0x299;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = nameid;
+ WFIFOSET(fd,packet_len(0x299));
+}
+
+
+/// Book Reading (ZC_READ_BOOK).
+/// 0294 <book id>.L <page>.L
+void clif_readbook(int fd, int book_id, int page)
+{
+ WFIFOHEAD(fd,packet_len(0x294));
+ WFIFOW(fd,0) = 0x294;
+ WFIFOL(fd,2) = book_id;
+ WFIFOL(fd,6) = page;
+ WFIFOSET(fd,packet_len(0x294));
+}
+
+
+/// Battlegrounds
+///
+
+/// Updates HP bar of a camp member (ZC_BATTLEFIELD_NOTIFY_HP).
+/// 02e0 <account id>.L <name>.24B <hp>.W <max hp>.W
+void clif_bg_hp(struct map_session_data *sd)
+{
+ unsigned char buf[34];
+ const int cmd = 0x2e0;
+ nullpo_retv(sd);
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = sd->status.account_id;
+ memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH);
+
+ if( sd->battle_status.max_hp > INT16_MAX )
+ { // To correctly display the %hp bar. [Skotlex]
+ WBUFW(buf,30) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
+ WBUFW(buf,32) = 100;
+ }
+ else
+ {
+ WBUFW(buf,30) = sd->battle_status.hp;
+ WBUFW(buf,32) = sd->battle_status.max_hp;
+ }
+
+ clif_send(buf, packet_len(cmd), &sd->bl, BG_AREA_WOS);
+}
+
+
+/// Updates the position of a camp member on the minimap (ZC_BATTLEFIELD_NOTIFY_POSITION).
+/// 02df <account id>.L <name>.24B <class>.W <x>.W <y>.W
+void clif_bg_xy(struct map_session_data *sd)
+{
+ unsigned char buf[36];
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x2df;
+ WBUFL(buf,2)=sd->status.account_id;
+ memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH);
+ WBUFW(buf,30)=sd->status.class_;
+ WBUFW(buf,32)=sd->bl.x;
+ WBUFW(buf,34)=sd->bl.y;
+
+ clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS);
+}
+
+void clif_bg_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[36];
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x2df;
+ WBUFL(buf,2)=sd->status.account_id;
+ memset(WBUFP(buf,6), 0, NAME_LENGTH);
+ WBUFW(buf,30)=0;
+ WBUFW(buf,32)=-1;
+ WBUFW(buf,34)=-1;
+
+ clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS);
+}
+
+
+/// Notifies clients of a battleground message (ZC_BATTLEFIELD_CHAT).
+/// 02dc <packet len>.W <account id>.L <name>.24B <message>.?B
+void clif_bg_message(struct battleground_data *bg, int src_id, const char *name, const char *mes, int len)
+{
+ struct map_session_data *sd;
+ unsigned char *buf;
+ if( (sd = bg_getavailablesd(bg)) == NULL )
+ return;
+
+ buf = (unsigned char*)aMalloc((len + NAME_LENGTH + 8)*sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x2dc;
+ WBUFW(buf,2) = len + NAME_LENGTH + 8;
+ WBUFL(buf,4) = src_id;
+ memcpy(WBUFP(buf,8), name, NAME_LENGTH);
+ memcpy(WBUFP(buf,32), mes, len);
+ clif_send(buf,WBUFW(buf,2), &sd->bl, BG);
+
+ if( buf )
+ aFree(buf);
+}
+
+
+/// Validates and processes battlechat messages [pakpil] (CZ_BATTLEFIELD_CHAT).
+/// 0x2db <packet len>.W <text>.?B (<name> : <message>) 00
+void clif_parse_BattleChat(int fd, struct map_session_data* sd)
+{
+ const char* text = (char*)RFIFOP(fd,4);
+ int textlen = RFIFOW(fd,2) - 4;
+
+ char *name, *message;
+ int namelen, messagelen;
+
+ if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
+ return;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ {
+ if( DIFF_TICK(sd->cantalk_tick, gettick()) > 0 )
+ return;
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ bg_send_message(sd, text, textlen);
+}
+
+
+/// Notifies client of a battleground score change (ZC_BATTLEFIELD_NOTIFY_POINT).
+/// 02de <camp A points>.W <camp B points>.W
+void clif_bg_updatescore(int16 m)
+{
+ struct block_list bl;
+ unsigned char buf[6];
+
+ bl.id = 0;
+ bl.type = BL_NUL;
+ bl.m = m;
+
+ WBUFW(buf,0) = 0x2de;
+ WBUFW(buf,2) = map[m].bgscore_lion;
+ WBUFW(buf,4) = map[m].bgscore_eagle;
+ clif_send(buf,packet_len(0x2de),&bl,ALL_SAMEMAP);
+}
+
+void clif_bg_updatescore_single(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2de));
+ WFIFOW(fd,0) = 0x2de;
+ WFIFOW(fd,2) = map[sd->bl.m].bgscore_lion;
+ WFIFOW(fd,4) = map[sd->bl.m].bgscore_eagle;
+ WFIFOSET(fd,packet_len(0x2de));
+}
+
+
+/// Battleground camp belong-information (ZC_BATTLEFIELD_NOTIFY_CAMPINFO).
+/// 02dd <account id>.L <name>.24B <camp>.W
+void clif_sendbgemblem_area(struct map_session_data *sd)
+{
+ unsigned char buf[33];
+ nullpo_retv(sd);
+
+ WBUFW(buf, 0) = 0x2dd;
+ WBUFL(buf,2) = sd->bl.id;
+ safestrncpy((char*)WBUFP(buf,6), sd->status.name, NAME_LENGTH); // name don't show in screen.
+ WBUFW(buf,30) = sd->bg_id;
+ clif_send(buf,packet_len(0x2dd), &sd->bl, AREA);
+}
+
+void clif_sendbgemblem_single(int fd, struct map_session_data *sd)
+{
+ nullpo_retv(sd);
+ WFIFOHEAD(fd,32);
+ WFIFOW(fd,0) = 0x2dd;
+ WFIFOL(fd,2) = sd->bl.id;
+ safestrncpy((char*)WFIFOP(fd,6), sd->status.name, NAME_LENGTH);
+ WFIFOW(fd,30) = sd->bg_id;
+ WFIFOSET(fd,packet_len(0x2dd));
+}
+
+
+/// Custom Fonts (ZC_NOTIFY_FONT).
+/// 02ef <account_id>.L <font id>.W
+void clif_font(struct map_session_data *sd)
+{
+#if PACKETVER >= 20080102
+ unsigned char buf[8];
+ nullpo_retv(sd);
+ WBUFW(buf,0) = 0x2ef;
+ WBUFL(buf,2) = sd->bl.id;
+ WBUFW(buf,6) = sd->user_font;
+ clif_send(buf, packet_len(0x2ef), &sd->bl, AREA);
+#endif
+}
+
+
+/*==========================================
+ * Instancing Window
+ *------------------------------------------*/
+int clif_instance(int instance_id, int type, int flag)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ unsigned char buf[255];
+
+ if( (p = party_search(instance[instance_id].party_id)) == NULL || (sd = party_getavailablesd(p)) == NULL )
+ return 0;
+
+ switch( type )
+ {
+ case 1:
+ // S 0x2cb <Instance name>.61B <Standby Position>.W
+ // Required to start the instancing information window on Client
+ // This window re-appear each "refresh" of client automatically until type 4 is send to client.
+ WBUFW(buf,0) = 0x02CB;
+ memcpy(WBUFP(buf,2),instance[instance_id].name,INSTANCE_NAME_LENGTH);
+ WBUFW(buf,63) = flag;
+ clif_send(buf,packet_len(0x02CB),&sd->bl,PARTY);
+ break;
+ case 2:
+ // S 0x2cc <Standby Position>.W
+ // To announce Instancing queue creation if no maps available
+ WBUFW(buf,0) = 0x02CC;
+ WBUFW(buf,2) = flag;
+ clif_send(buf,packet_len(0x02CC),&sd->bl,PARTY);
+ break;
+ case 3:
+ case 4:
+ // S 0x2cd <Instance Name>.61B <Instance Remaining Time>.L <Instance Noplayers close time>.L
+ WBUFW(buf,0) = 0x02CD;
+ memcpy(WBUFP(buf,2),instance[instance_id].name,61);
+ if( type == 3 )
+ {
+ WBUFL(buf,63) = (uint32)instance[instance_id].progress_timeout;
+ WBUFL(buf,67) = 0;
+ }
+ else
+ {
+ WBUFL(buf,63) = 0;
+ WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout;
+ }
+ clif_send(buf,packet_len(0x02CD),&sd->bl,PARTY);
+ break;
+ case 5:
+ // S 0x2ce <Message ID>.L
+ // 0 = Notification (EnterLimitDate update?)
+ // 1 = The Memorial Dungeon expired; it has been destroyed
+ // 2 = The Memorial Dungeon's entry time limit expired; it has been destroyed
+ // 3 = The Memorial Dungeon has been removed.
+ // 4 = Create failure (removes the instance window)
+ WBUFW(buf,0) = 0x02CE;
+ WBUFL(buf,2) = flag;
+ //WBUFL(buf,6) = EnterLimitDate;
+ clif_send(buf,packet_len(0x02CE),&sd->bl,PARTY);
+ break;
+ }
+ return 0;
+}
+
+void clif_instance_join(int fd, int instance_id)
+{
+ if( instance[instance_id].idle_timer != INVALID_TIMER )
+ {
+ WFIFOHEAD(fd,packet_len(0x02CD));
+ WFIFOW(fd,0) = 0x02CD;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOL(fd,63) = 0;
+ WFIFOL(fd,67) = (uint32)instance[instance_id].idle_timeout;
+ WFIFOSET(fd,packet_len(0x02CD));
+ }
+ else if( instance[instance_id].progress_timer != INVALID_TIMER )
+ {
+ WFIFOHEAD(fd,packet_len(0x02CD));
+ WFIFOW(fd,0) = 0x02CD;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOL(fd,63) = (uint32)instance[instance_id].progress_timeout;;
+ WFIFOL(fd,67) = 0;
+ WFIFOSET(fd,packet_len(0x02CD));
+ }
+ else
+ {
+ WFIFOHEAD(fd,packet_len(0x02CB));
+ WFIFOW(fd,0) = 0x02CB;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOW(fd,63) = 0;
+ WFIFOSET(fd,packet_len(0x02CB));
+ }
+}
+
+void clif_instance_leave(int fd)
+{
+ WFIFOHEAD(fd,packet_len(0x02CE));
+ WFIFOW(fd,0) = 0x02ce;
+ WFIFOL(fd,2) = 4;
+ WFIFOSET(fd,packet_len(0x02CE));
+}
+
+
+/// Notifies clients about item picked up by a party member (ZC_ITEM_PICKUP_PARTY).
+/// 02b8 <account id>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B
+void clif_party_show_picker(struct map_session_data * sd, struct item * item_data)
+{
+#if PACKETVER >= 20071002
+ unsigned char buf[22];
+ struct item_data* id = itemdb_search(item_data->nameid);
+
+ WBUFW(buf,0) = 0x2b8;
+ WBUFL(buf,2) = sd->status.account_id;
+ WBUFW(buf,6) = item_data->nameid;
+ WBUFB(buf,8) = item_data->identify;
+ WBUFB(buf,9) = item_data->attribute;
+ WBUFB(buf,10) = item_data->refine;
+ clif_addcards(WBUFP(buf,11), item_data);
+ WBUFW(buf,19) = id->equip; // equip location
+ WBUFB(buf,21) = itemtype(id->type); // item type
+ clif_send(buf, packet_len(0x2b8), &sd->bl, PARTY_SAMEMAP_WOS);
+#endif
+}
+
+
+/// Display gained exp (ZC_NOTIFY_EXP).
+/// 07f6 <account id>.L <amount>.L <var id>.W <exp type>.W
+/// var id:
+/// SP_BASEEXP, SP_JOBEXP
+/// exp type:
+/// 0 = normal exp gain/loss
+/// 1 = quest exp gain/loss
+void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, bool quest)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7f6));
+ WFIFOW(fd,0) = 0x7f6;
+ WFIFOL(fd,2) = sd->bl.id;
+ WFIFOL(fd,6) = exp;
+ WFIFOW(fd,10) = type;
+ WFIFOW(fd,12) = quest?1:0;// Normal exp is shown in yellow, quest exp is shown in purple.
+ WFIFOSET(fd,packet_len(0x7f6));
+}
+
+
+/// Displays digital clock digits on top of the screen (ZC_SHOWDIGIT).
+/// type:
+/// 0 = Displays 'value' for 5 seconds.
+/// 1 = Incremental counter (1 tick/second), negated 'value' specifies start value (e.g. using -10 lets the counter start at 10).
+/// 2 = Decremental counter (1 tick/second), negated 'value' specifies start value (does not stop when reaching 0, but overflows).
+/// 3 = Decremental counter (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)
+{
+ WFIFOHEAD(sd->fd, packet_len(0x1b1));
+ WFIFOW(sd->fd,0) = 0x1b1;
+ WFIFOB(sd->fd,2) = type;
+ WFIFOL(sd->fd,3) = value;
+ WFIFOSET(sd->fd, packet_len(0x1b1));
+}
+
+
+/// Notification of the state of client command /effect (CZ_LESSEFFECT).
+/// 021d <state>.L
+/// state:
+/// 0 = Full effects
+/// 1 = Reduced effects
+///
+/// NOTE: The state is used on Aegis for sending skill unit packet
+/// 0x11f (ZC_SKILL_ENTRY) instead of 0x1c9 (ZC_SKILL_ENTRY2)
+/// whenever possible. Due to the way the decision check is
+/// constructed, this state tracking was rendered useless,
+/// as the only skill unit, that is sent with 0x1c9 is
+/// Graffiti.
+void clif_parse_LessEffect(int fd, struct map_session_data* sd)
+{
+ int isLess = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ sd->state.lesseffect = ( isLess != 0 );
+}
+
+/// S 07e4 <length>.w <option>.l <val>.l {<index>.w <amount>.w).4b*
+void clif_parse_ItemListWindowSelected(int fd, struct map_session_data* sd) {
+ int n = (RFIFOW(fd,2)-12) / 4;
+ int type = RFIFOL(fd,4);
+ int flag = RFIFOL(fd,8); // Button clicked: 0 = Cancel, 1 = OK
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,12);
+
+ if( sd->state.trading || sd->npc_shopid )
+ return;
+
+ if( flag == 0 || n == 0) {
+ clif_menuskill_clear(sd);
+ return; // Canceled by player.
+ }
+
+ if( sd->menuskill_id != SO_EL_ANALYSIS && sd->menuskill_id != GN_CHANGEMATERIAL ) {
+ clif_menuskill_clear(sd);
+ return; // Prevent hacking.
+ }
+
+ switch( type ) {
+ case 0: // Change Material
+ skill_changematerial(sd,n,item_list);
+ break;
+ case 1: // Level 1: Pure to Rough
+ case 2: // Level 2: Rough to Pure
+ skill_elementalanalysis(sd,n,type,item_list);
+ break;
+ }
+ clif_menuskill_clear(sd);
+
+ return;
+}
+
+/*==========================================
+ * Elemental System
+ *==========================================*/
+void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
+ struct elemental_data *ed;
+ struct status_data *status;
+ int fd;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+ WFIFOHEAD(fd,8);
+ WFIFOW(fd,0) = 0x81e;
+ WFIFOW(fd,2) = type;
+ switch( type ) {
+ case SP_HP:
+ WFIFOL(fd,4) = status->hp;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4) = status->max_hp;
+ break;
+ case SP_SP:
+ WFIFOL(fd,4) = status->sp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4) = status->max_sp;
+ break;
+ }
+ WFIFOSET(fd,8);
+}
+
+void clif_elemental_info(struct map_session_data *sd) {
+ int fd;
+ struct elemental_data *ed;
+ struct status_data *status;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+
+ WFIFOHEAD(fd,22);
+ WFIFOW(fd, 0) = 0x81d;
+ WFIFOL(fd, 2) = ed->bl.id;
+ WFIFOL(fd, 6) = status->hp;
+ WFIFOL(fd,10) = status->max_hp;
+ WFIFOL(fd,14) = status->sp;
+ WFIFOL(fd,18) = status->max_sp;
+ WFIFOSET(fd,22);
+}
+
+
+/// Buying Store System
+///
+
+/// Opens preparation window for buying store (ZC_OPEN_BUYING_STORE).
+/// 0810 <slots>.B
+void clif_buyingstore_open(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x810));
+ WFIFOW(fd,0) = 0x810;
+ WFIFOB(fd,2) = sd->buyingstore.slots;
+ WFIFOSET(fd,packet_len(0x810));
+}
+
+
+/// Request to create a buying store (CZ_REQ_OPEN_BUYING_STORE).
+/// 0811 <packet len>.W <limit zeny>.L <result>.B <store name>.80B { <name id>.W <amount>.W <price>.L }*
+/// result:
+/// 0 = cancel
+/// 1 = open
+static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data* sd)
+{
+ const unsigned int blocksize = 8;
+ uint8* itemlist;
+ char storename[MESSAGE_SIZE];
+ unsigned char result;
+ int zenylimit;
+ unsigned int count, packet_len;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ packet_len = RFIFOW(fd,info->pos[0]);
+
+ // TODO: Make this check global for all variable length packets.
+ if( packet_len < 89 )
+ {// minimum packet length
+ ShowError("clif_parse_ReqOpenBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 89, packet_len, sd->bl.id);
+ return;
+ }
+
+ zenylimit = RFIFOL(fd,info->pos[1]);
+ result = RFIFOL(fd,info->pos[2]);
+ safestrncpy(storename, (const char*)RFIFOP(fd,info->pos[3]), sizeof(storename));
+ itemlist = RFIFOP(fd,info->pos[4]);
+
+ // so that buyingstore_create knows, how many elements it has access to
+ packet_len-= info->pos[4];
+
+ if( packet_len%blocksize )
+ {
+ ShowError("clif_parse_ReqOpenBuyingStore: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
+ return;
+ }
+ count = packet_len/blocksize;
+
+ buyingstore_create(sd, zenylimit, result, storename, itemlist, count);
+}
+
+
+/// Notification, that the requested buying store could not be created (ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER).
+/// 0812 <result>.W <total weight>.L
+/// result:
+/// 1 = "Failed to open buying store." (0x6cd, MSI_BUYINGSTORE_OPEN_FAILED)
+/// 2 = "Total amount of then possessed items exceeds the weight limit by <weight/10-maxweight*90%>. Please re-enter." (0x6ce, MSI_BUYINGSTORE_OVERWEIGHT)
+/// 8 = "No sale (purchase) information available." (0x705)
+/// ? = nothing
+void clif_buyingstore_open_failed(struct map_session_data* sd, unsigned short result, unsigned int weight)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x812));
+ WFIFOW(fd,0) = 0x812;
+ WFIFOW(fd,2) = result;
+ WFIFOL(fd,4) = weight;
+ WFIFOSET(fd,packet_len(0x812));
+}
+
+
+/// Notification, that the requested buying store was created (ZC_MYITEMLIST_BUYING_STORE).
+/// 0813 <packet len>.W <account id>.L <limit zeny>.L { <price>.L <count>.W <type>.B <name id>.W }*
+void clif_buyingstore_myitemlist(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+ unsigned int i;
+
+ WFIFOHEAD(fd,12+sd->buyingstore.slots*9);
+ WFIFOW(fd,0) = 0x813;
+ WFIFOW(fd,2) = 12+sd->buyingstore.slots*9;
+ WFIFOL(fd,4) = sd->bl.id;
+ WFIFOL(fd,8) = sd->buyingstore.zenylimit;
+
+ for( i = 0; i < sd->buyingstore.slots; i++ )
+ {
+ WFIFOL(fd,12+i*9) = sd->buyingstore.items[i].price;
+ WFIFOW(fd,16+i*9) = sd->buyingstore.items[i].amount;
+ WFIFOB(fd,18+i*9) = itemtype(itemdb_type(sd->buyingstore.items[i].nameid));
+ WFIFOW(fd,19+i*9) = sd->buyingstore.items[i].nameid;
+ }
+
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notifies clients in area of a buying store (ZC_BUYING_STORE_ENTRY).
+/// 0814 <account id>.L <store name>.80B
+void clif_buyingstore_entry(struct map_session_data* sd)
+{
+ uint8 buf[86];
+
+ WBUFW(buf,0) = 0x814;
+ WBUFL(buf,2) = sd->bl.id;
+ memcpy(WBUFP(buf,6), sd->message, MESSAGE_SIZE);
+
+ clif_send(buf, packet_len(0x814), &sd->bl, AREA_WOS);
+}
+void clif_buyingstore_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x814));
+ WFIFOW(fd,0) = 0x814;
+ WFIFOL(fd,2) = pl_sd->bl.id;
+ memcpy(WFIFOP(fd,6), pl_sd->message, MESSAGE_SIZE);
+ WFIFOSET(fd,packet_len(0x814));
+}
+
+
+/// Request to close own buying store (CZ_REQ_CLOSE_BUYING_STORE).
+/// 0815
+static void clif_parse_ReqCloseBuyingStore(int fd, struct map_session_data* sd)
+{
+ buyingstore_close(sd);
+}
+
+
+/// Notifies clients in area that a buying store was closed (ZC_DISAPPEAR_BUYING_STORE_ENTRY).
+/// 0816 <account id>.L
+void clif_buyingstore_disappear_entry(struct map_session_data* sd)
+{
+ uint8 buf[6];
+
+ WBUFW(buf,0) = 0x816;
+ WBUFL(buf,2) = sd->bl.id;
+
+ clif_send(buf, packet_len(0x816), &sd->bl, AREA_WOS);
+}
+void clif_buyingstore_disappear_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x816));
+ WFIFOW(fd,0) = 0x816;
+ WFIFOL(fd,2) = pl_sd->bl.id;
+ WFIFOSET(fd,packet_len(0x816));
+}
+
+
+/// Request to open someone else's buying store (CZ_REQ_CLICK_TO_BUYING_STORE).
+/// 0817 <account id>.L
+static void clif_parse_ReqClickBuyingStore(int fd, struct map_session_data* sd)
+{
+ int account_id;
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ buyingstore_open(sd, account_id);
+}
+
+
+/// Sends buying store item list (ZC_ACK_ITEMLIST_BUYING_STORE).
+/// 0818 <packet len>.W <account id>.L <store id>.L <limit zeny>.L { <price>.L <amount>.W <type>.B <name id>.W }*
+void clif_buyingstore_itemlist(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ int fd = sd->fd;
+ unsigned int i;
+
+ WFIFOHEAD(fd,16+pl_sd->buyingstore.slots*9);
+ WFIFOW(fd,0) = 0x818;
+ WFIFOW(fd,2) = 16+pl_sd->buyingstore.slots*9;
+ WFIFOL(fd,4) = pl_sd->bl.id;
+ WFIFOL(fd,8) = pl_sd->buyer_id;
+ WFIFOL(fd,12) = pl_sd->buyingstore.zenylimit;
+
+ for( i = 0; i < pl_sd->buyingstore.slots; i++ )
+ {
+ WFIFOL(fd,16+i*9) = pl_sd->buyingstore.items[i].price;
+ WFIFOW(fd,20+i*9) = pl_sd->buyingstore.items[i].amount; // TODO: Figure out, if no longer needed items (amount == 0) are listed on official.
+ WFIFOB(fd,22+i*9) = itemtype(itemdb_type(pl_sd->buyingstore.items[i].nameid));
+ WFIFOW(fd,23+i*9) = pl_sd->buyingstore.items[i].nameid;
+ }
+
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Request to sell items to a buying store (CZ_REQ_TRADE_BUYING_STORE).
+/// 0819 <packet len>.W <account id>.L <store id>.L { <index>.W <name id>.W <amount>.W }*
+static void clif_parse_ReqTradeBuyingStore(int fd, struct map_session_data* sd)
+{
+ const unsigned int blocksize = 6;
+ uint8* itemlist;
+ int account_id;
+ unsigned int count, packet_len, buyer_id;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ packet_len = RFIFOW(fd,info->pos[0]);
+
+ if( packet_len < 12 )
+ {// minimum packet length
+ ShowError("clif_parse_ReqTradeBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 12, packet_len, sd->bl.id);
+ return;
+ }
+
+ account_id = RFIFOL(fd,info->pos[1]);
+ buyer_id = RFIFOL(fd,info->pos[2]);
+ itemlist = RFIFOP(fd,info->pos[3]);
+
+ // so that buyingstore_trade knows, how many elements it has access to
+ packet_len-= info->pos[3];
+
+ if( packet_len%blocksize )
+ {
+ ShowError("clif_parse_ReqTradeBuyingStore: Unexpected item list size %u (account_id=%d, buyer_id=%d, block size=%u)\n", packet_len, sd->bl.id, account_id, blocksize);
+ return;
+ }
+ count = packet_len/blocksize;
+
+ buyingstore_trade(sd, account_id, buyer_id, itemlist, count);
+}
+
+
+/// Notifies the buyer, that the buying store has been closed due to a post-trade condition (ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER).
+/// 081a <result>.W
+/// result:
+/// 3 = "All items within the buy limit were purchased." (0x6cf, MSI_BUYINGSTORE_TRADE_OVERLIMITZENY)
+/// 4 = "All items were purchased." (0x6d0, MSI_BUYINGSTORE_TRADE_BUYCOMPLETE)
+/// ? = nothing
+void clif_buyingstore_trade_failed_buyer(struct map_session_data* sd, short result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x81a));
+ WFIFOW(fd,0) = 0x81a;
+ WFIFOW(fd,2) = result;
+ WFIFOSET(fd,packet_len(0x81a));
+}
+
+
+/// Updates the zeny limit and an item in the buying store item list (ZC_UPDATE_ITEM_FROM_BUYING_STORE).
+/// 081b <name id>.W <amount>.W <limit zeny>.L
+void clif_buyingstore_update_item(struct map_session_data* sd, unsigned short nameid, unsigned short amount)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x81b));
+ WFIFOW(fd,0) = 0x81b;
+ WFIFOW(fd,2) = nameid;
+ WFIFOW(fd,4) = amount; // amount of nameid received
+ WFIFOL(fd,6) = sd->buyingstore.zenylimit;
+ WFIFOSET(fd,packet_len(0x81b));
+}
+
+
+/// Deletes item from inventory, that was sold to a buying store (ZC_ITEM_DELETE_BUYING_STORE).
+/// 081c <index>.W <amount>.W <price>.L
+/// message:
+/// "%s (%d) were sold at %dz." (0x6d2, MSI_BUYINGSTORE_TRADE_SELLCOMPLETE)
+///
+/// NOTE: This function has to be called _instead_ of clif_delitem/clif_dropitem.
+void clif_buyingstore_delete_item(struct map_session_data* sd, short index, unsigned short amount, int price)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x81c));
+ WFIFOW(fd,0) = 0x81c;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = amount;
+ WFIFOL(fd,6) = price; // price per item, client calculates total Zeny by itself
+ WFIFOSET(fd,packet_len(0x81c));
+}
+
+
+/// Notifies the seller, that a buying store trade failed (ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER).
+/// 0824 <result>.W <name id>.W
+/// result:
+/// 5 = "The deal has failed." (0x39, MSI_DEAL_FAIL)
+/// 6 = "The trade failed, because the entered amount of item %s is higher, than the buyer is willing to buy." (0x6d3, MSI_BUYINGSTORE_TRADE_OVERCOUNT)
+/// 7 = "The trade failed, because the buyer is lacking required balance." (0x6d1, MSI_BUYINGSTORE_TRADE_LACKBUYERZENY)
+/// ? = nothing
+void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short result, unsigned short nameid)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x824));
+ WFIFOW(fd,0) = 0x824;
+ WFIFOW(fd,2) = result;
+ WFIFOW(fd,4) = nameid;
+ WFIFOSET(fd,packet_len(0x824));
+}
+
+
+/// Search Store Info System
+///
+
+/// Request to search for stores (CZ_SEARCH_STORE_INFO).
+/// 0835 <packet len>.W <type>.B <max price>.L <min price>.L <name id count>.B <card count>.B { <name id>.W }* { <card>.W }*
+/// type:
+/// 0 = Vending
+/// 1 = Buying Store
+///
+/// NOTE: The client determines the item ids by specifying a name and optionally,
+/// amount of card slots. If the client does not know about the item it
+/// cannot be searched.
+static void clif_parse_SearchStoreInfo(int fd, struct map_session_data* sd)
+{
+ const unsigned int blocksize = 2;
+ const uint8* itemlist;
+ const uint8* cardlist;
+ unsigned char type;
+ unsigned int min_price, max_price, packet_len, count, item_count, card_count;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ packet_len = RFIFOW(fd,info->pos[0]);
+
+ if( packet_len < 15 )
+ {// minimum packet length
+ ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 15, packet_len, sd->bl.id);
+ return;
+ }
+
+ type = RFIFOB(fd,info->pos[1]);
+ max_price = RFIFOL(fd,info->pos[2]);
+ min_price = RFIFOL(fd,info->pos[3]);
+ item_count = RFIFOB(fd,info->pos[4]);
+ card_count = RFIFOB(fd,info->pos[5]);
+ itemlist = RFIFOP(fd,info->pos[6]);
+ cardlist = RFIFOP(fd,info->pos[6]+blocksize*item_count);
+
+ // check, if there is enough data for the claimed count of items
+ packet_len-= info->pos[6];
+
+ if( packet_len%blocksize )
+ {
+ ShowError("clif_parse_SearchStoreInfo: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
+ return;
+ }
+ count = packet_len/blocksize;
+
+ if( count < item_count+card_count )
+ {
+ ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected count=%u, count=%u, account_id=%d).\n", item_count+card_count, count, sd->bl.id);
+ return;
+ }
+
+ searchstore_query(sd, type, min_price, max_price, (const unsigned short*)itemlist, item_count, (const unsigned short*)cardlist, card_count);
+}
+
+
+/// Results for a store search request (ZC_SEARCH_STORE_INFO_ACK).
+/// 0836 <packet len>.W <is first page>.B <is next page>.B <remaining uses>.B { <store id>.L <account id>.L <shop name>.80B <nameid>.W <item type>.B <price>.L <amount>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+/// is first page:
+/// 0 = appends to existing results
+/// 1 = clears previous results before displaying this result set
+/// is next page:
+/// 0 = no "next" label
+/// 1 = "next" label to retrieve more results
+void clif_search_store_info_ack(struct map_session_data* sd)
+{
+ const unsigned int blocksize = MESSAGE_SIZE+26;
+ int fd = sd->fd;
+ unsigned int i, start, end;
+
+ start = sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE;
+ end = min(sd->searchstore.count, start+SEARCHSTORE_RESULTS_PER_PAGE);
+
+ WFIFOHEAD(fd,7+(end-start)*blocksize);
+ WFIFOW(fd,0) = 0x836;
+ WFIFOW(fd,2) = 7+(end-start)*blocksize;
+ WFIFOB(fd,4) = !sd->searchstore.pages;
+ WFIFOB(fd,5) = searchstore_querynext(sd);
+ WFIFOB(fd,6) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX);
+
+ for( i = start; i < end; i++ )
+ {
+ struct s_search_store_info_item* ssitem = &sd->searchstore.items[i];
+ struct item it;
+
+ WFIFOL(fd,i*blocksize+ 7) = ssitem->store_id;
+ WFIFOL(fd,i*blocksize+11) = ssitem->account_id;
+ memcpy(WFIFOP(fd,i*blocksize+15), ssitem->store_name, MESSAGE_SIZE);
+ WFIFOW(fd,i*blocksize+15+MESSAGE_SIZE) = ssitem->nameid;
+ WFIFOB(fd,i*blocksize+17+MESSAGE_SIZE) = itemtype(itemdb_type(ssitem->nameid));
+ WFIFOL(fd,i*blocksize+18+MESSAGE_SIZE) = ssitem->price;
+ WFIFOW(fd,i*blocksize+22+MESSAGE_SIZE) = ssitem->amount;
+ WFIFOB(fd,i*blocksize+24+MESSAGE_SIZE) = ssitem->refine;
+
+ // make-up an item for clif_addcards
+ memset(&it, 0, sizeof(it));
+ memcpy(&it.card, &ssitem->card, sizeof(it.card));
+ it.nameid = ssitem->nameid;
+ it.amount = ssitem->amount;
+
+ clif_addcards(WFIFOP(fd,i*blocksize+25+MESSAGE_SIZE), &it);
+ }
+
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notification of failure when searching for stores (ZC_SEARCH_STORE_INFO_FAILED).
+/// 0837 <reason>.B
+/// reason:
+/// 0 = "No matching stores were found." (0x70b)
+/// 1 = "There are too many results. Please enter more detailed search term." (0x6f8)
+/// 2 = "You cannot search anymore." (0x706)
+/// 3 = "You cannot search yet." (0x708)
+/// 4 = "No sale (purchase) information available." (0x705)
+void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x837));
+ WFIFOW(fd,0) = 0x837;
+ WFIFOB(fd,2) = reason;
+ WFIFOSET(fd,packet_len(0x837));
+}
+
+
+/// Request to display next page of results (CZ_SEARCH_STORE_INFO_NEXT_PAGE).
+/// 0838
+static void clif_parse_SearchStoreInfoNextPage(int fd, struct map_session_data* sd)
+{
+ searchstore_next(sd);
+}
+
+
+/// Opens the search store window (ZC_OPEN_SEARCH_STORE_INFO).
+/// 083a <type>.W <remaining uses>.B
+/// type:
+/// 0 = Search Stores
+/// 1 = Search Stores (Cash), asks for confirmation, when clicking a store
+void clif_open_search_store_info(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x83a));
+ WFIFOW(fd,0) = 0x83a;
+ WFIFOW(fd,2) = sd->searchstore.effect;
+#if PACKETVER > 20100701
+ WFIFOB(fd,4) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX);
+#endif
+ WFIFOSET(fd,packet_len(0x83a));
+}
+
+
+/// Request to close the store search window (CZ_CLOSE_SEARCH_STORE_INFO).
+/// 083b
+static void clif_parse_CloseSearchStoreInfo(int fd, struct map_session_data* sd)
+{
+ searchstore_close(sd);
+}
+
+
+/// Request to invoke catalog effect on a store from search results (CZ_SSILIST_ITEM_CLICK).
+/// 083c <account id>.L <store id>.L <nameid>.W
+static void clif_parse_SearchStoreInfoListItemClick(int fd, struct map_session_data* sd)
+{
+ unsigned short nameid;
+ int account_id, store_id;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ account_id = RFIFOL(fd,info->pos[0]);
+ store_id = RFIFOL(fd,info->pos[1]);
+ nameid = RFIFOW(fd,info->pos[2]);
+
+ searchstore_click(sd, account_id, store_id, nameid);
+}
+
+
+/// Notification of the store position on current map (ZC_SSILIST_ITEM_CLICK_ACK).
+/// 083d <xPos>.W <yPos>.W
+void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x83d));
+ WFIFOW(fd,0) = 0x83d;
+ WFIFOW(fd,2) = x;
+ WFIFOW(fd,4) = y;
+ WFIFOSET(fd,packet_len(0x83d));
+}
+
+
+/// Parse function for packet debugging.
+void clif_parse_debug(int fd,struct map_session_data *sd)
+{
+ int cmd, packet_len;
+
+ // clif_parse ensures, that there is at least 2 bytes of data
+ cmd = RFIFOW(fd,0);
+
+ if( sd )
+ {
+ packet_len = packet_db[sd->packet_ver][cmd].len;
+
+ if( packet_len == 0 )
+ {// unknown
+ packet_len = RFIFOREST(fd);
+ }
+ else if( packet_len == -1 )
+ {// variable length
+ packet_len = RFIFOW(fd,2); // clif_parse ensures, that this amount of data is already received
+ }
+ ShowDebug("Packet debug of 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
+ }
+ else
+ {
+ packet_len = RFIFOREST(fd);
+ ShowDebug("Packet debug of 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
+ }
+
+ ShowDump(RFIFOP(fd,0), packet_len);
+}
+/*==========================================
+ * Server tells client to display a window similar to Magnifier (item) one
+ * Server populates the window with avilable elemental converter options according to player's inventory
+ *------------------------------------------*/
+int clif_elementalconverter_list(struct map_session_data *sd) {
+ int i,c,view,fd;
+
+ nullpo_ret(sd);
+
+
+/// Main client packet processing function
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB *2+4);
+ WFIFOW(fd, 0)=0x1ad;
+
+ for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if( skill_can_produce_mix(sd,skill_produce_db[i].nameid,23, 1) ){
+ if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
+ WFIFOW(fd,c*2+ 4)= view;
+ else
+ WFIFOW(fd,c*2+ 4)= skill_produce_db[i].nameid;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = c*2+4;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if (c > 0) {
+ sd->menuskill_id = SA_CREATECON;
+ sd->menuskill_val = c;
+ }
+
+ return 0;
+}
+/**
+ * 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,INVALID_TIMER);
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK, 0);
+ }
+
+ return 1;
+}
+/**
+ * Mechanic
+ **/
+/*==========================================
+ * Magic Decoy Material List
+ *------------------------------------------*/
+int clif_magicdecoy_list(struct map_session_data *sd, uint16 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,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+
+ return 1;
+}
+/**
+ * Guilotine Cross
+ **/
+/*==========================================
+ * Guillotine Cross Poisons List
+ *------------------------------------------*/
+int clif_poison_list(struct map_session_data *sd, uint16 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,USESKILL_FAIL_GUILLONTINE_POISON,0);
+ return 0;
+ }
+
+ return 1;
+}
+int clif_autoshadowspell_list(struct map_session_data *sd) {
+ int fd, i, c;
+ nullpo_ret(sd);
+ fd = sd->fd;
+ if( !fd ) return 0;
+
+ if( sd->menuskill_id == SC_AUTOSHADOWSPELL )
+ return 0;
+
+ WFIFOHEAD(fd, 2 * 6 + 4);
+ WFIFOW(fd,0) = 0x442;
+ for( i = 0, c = 0; i < MAX_SKILL; i++ )
+ if( sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].id > 0 &&
+ sd->status.skill[i].id < GS_GLITTERING && skill_get_type(sd->status.skill[i].id) == BF_MAGIC )
+ { // Can't auto cast both Extended class and 3rd class skills.
+ WFIFOW(fd,8+c*2) = sd->status.skill[i].id;
+ c++;
+ }
+
+ if( c > 0 ) {
+ WFIFOW(fd,2) = 8 + c * 2;
+ WFIFOL(fd,4) = c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->menuskill_id = SC_AUTOSHADOWSPELL;
+ sd->menuskill_val = c;
+ } else {
+ status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
+ clif_skill_fail(sd,SC_AUTOSHADOWSPELL,USESKILL_FAIL_IMITATION_SKILL_NONE,0);
+ }
+
+ return 1;
+}
+/*===========================================
+ * Skill list for Four Elemental Analysis
+ * and Change Material skills.
+ *------------------------------------------*/
+int clif_skill_itemlistwindow( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv )
+{
+#if PACKETVER >= 20090922
+ int fd;
+
+ nullpo_ret(sd);
+
+ sd->menuskill_id = skill_id; // To prevent hacking.
+ sd->menuskill_val = skill_lv;
+
+ if( skill_id == GN_CHANGEMATERIAL )
+ skill_lv = 0; // Changematerial
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x7e3));
+ WFIFOW(fd,0) = 0x7e3;
+ WFIFOL(fd,2) = skill_lv;
+ WFIFOL(fd,4) = 0;
+ WFIFOSET(fd,packet_len(0x7e3));
+
+#endif
+
+ 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(0x043f),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
+}
+/*==========================================
+ * used by SC_AUTOSHADOWSPELL
+ * RFIFOL(fd,2) - flag (currently not used)
+ *------------------------------------------*/
+void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) {
+
+ if( sd->menuskill_id != SC_AUTOSHADOWSPELL )
+ return;
+
+ if( pc_istrading(sd) ) {
+ clif_skill_fail(sd,sd->ud.skill_id,0,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+
+ skill_select_menu(sd,RFIFOW(fd,6));
+
+ clif_menuskill_clear(sd);
+}
+/*==========================================
+ * Kagerou/Oboro amulet spirit
+ *------------------------------------------*/
+void clif_talisman(struct map_session_data *sd,short type)
+{
+ unsigned char buf[10];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x08cf;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=type;
+ WBUFW(buf,8)=sd->talisman[type];
+ clif_send(buf,packet_len(0x08cf),&sd->bl,AREA);
+}
+/// Move Item from or to Personal Tab (CZ_WHATSOEVER) [FE]
+/// 0907 <index>.W
+///
+/// R 0908 <index>.w <type>.b
+/// type:
+/// 0 = move item to personal tab
+/// 1 = move item to normal tab
+void clif_parse_MoveItem(int fd, struct map_session_data *sd) {
+#if PACKETVER >= 20111122
+ int index;
+
+ /* can't move while dead. */
+ if(pc_isdead(sd)) {
+ return;
+ }
+
+ index = RFIFOW(fd,2)-2;
+
+ if (index < 0 || index >= MAX_INVENTORY)
+ return;
+
+ if ( sd->status.inventory[index].favorite && RFIFOB(fd, 4) == 1 )
+ sd->status.inventory[index].favorite = 0;
+ else if( RFIFOB(fd, 4) == 0 )
+ sd->status.inventory[index].favorite = 1;
+ else
+ return;/* nothing to do. */
+
+ clif_favorite_item(sd, index);
+#endif
+}
+
+
+/// Items that are in favorite tab of inventory (ZC_ITEM_FAVORITE).
+/// 0900 <index>.W <favorite>.B
+void clif_favorite_item(struct map_session_data* sd, unsigned short index) {
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x908));
+ WFIFOW(fd,0) = 0x908;
+ WFIFOW(fd,2) = index+2;
+ WFIFOL(fd,4) = (sd->status.inventory[index].favorite == 1) ? 0 : 1;
+ WFIFOSET(fd,packet_len(0x908));
+}
+
+void clif_snap( struct block_list *bl, short x, short y ) {
+ unsigned char buf[10];
+
+ WBUFW(buf,0) = 0x8d2;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = x;
+ WBUFW(buf,8) = y;
+
+ clif_send(buf,packet_len(0x8d2),bl,AREA);
+}
+
+void clif_monster_hp_bar( struct mob_data* md, int fd ) {
+#if PACKETVER >= 20120404
+ WFIFOHEAD(fd,packet_len(0x977));
+
+ WFIFOW(fd,0) = 0x977;
+ WFIFOL(fd,2) = md->bl.id;
+ WFIFOL(fd,6) = md->status.hp;
+ WFIFOL(fd,10) = md->status.max_hp;
+
+ WFIFOSET(fd,packet_len(0x977));
+#endif
+}
+
+/*==========================================
+ * Main client packet processing function
+ *------------------------------------------*/
+static int clif_parse(int fd)
+{
+ int cmd, packet_ver, packet_len, err;
+ TBL_PC* sd;
+ int pnum;
+
+ //TODO apply delays or disconnect based on packet throughput [FlavioJS]
+ // Note: "click masters" can do 80+ clicks in 10 seconds
+
+ for( pnum = 0; pnum < 3; ++pnum )// Limit max packets per cycle to 3 (delay packet spammers) [FlavioJS] -- This actually aids packet spammers, but stuff like /str+ gets slow without it [Ai4rei]
+ { // begin main client packet processing loop
+
+ sd = (TBL_PC *)session[fd]->session_data;
+ if (session[fd]->flag.eof) {
+ if (sd) {
+ if (sd->state.autotrade) {
+ //Disassociate character from the socket connection.
+ session[fd]->session_data = NULL;
+ sd->fd = 0;
+ ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", sd->status.name);
+ } else
+ if (sd->state.active) {
+ // Player logout display [Valaris]
+ ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off.\n", sd->status.name);
+ clif_quitsave(fd, sd);
+ } else {
+ //Unusual logout (during log on/off/map-changer procedure)
+ ShowInfo("Player AID:%d/CID:%d logged off.\n", sd->status.account_id, sd->status.char_id);
+ map_quit(sd);
+ }
+ } else {
+ ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip2str(session[fd]->client_addr, NULL));
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ if (RFIFOREST(fd) < 2)
+ return 0;
+
+ cmd = RFIFOW(fd,0);
+
+ // identify client's packet version
+ if (sd) {
+ packet_ver = sd->packet_ver;
+ } else {
+ // check authentification packet to know packet version
+ packet_ver = clif_guess_PacketVer(fd, 0, &err);
+ if( err ) {// failed to identify packet version
+ ShowInfo("clif_parse: Disconnecting session #%d with unknown packet version%s (p:0x%04x,l:%d).\n", fd, (
+ err == 1 ? "" :
+ err == 2 ? ", possibly for having an invalid account_id" :
+ err == 3 ? ", possibly for having an invalid char_id." :
+ /* Uncomment when checks are added in clif_guess_PacketVer. [FlavioJS]
+ err == 4 ? ", possibly for having an invalid login_id1." :
+ err == 5 ? ", possibly for having an invalid client_tick." :
+ */
+ err == 6 ? ", possibly for having an invalid sex." :
+ ". ERROR invalid error code"), cmd, RFIFOREST(fd));
+ WFIFOHEAD(fd,packet_len(0x6a));
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 3; // Rejected from Server
+ WFIFOSET(fd,packet_len(0x6a));
+
+#ifdef DUMP_INVALID_PACKET
+ ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
+#endif
+
+ RFIFOSKIP(fd, RFIFOREST(fd));
+ set_eof(fd);
+ return 0;
+ }
+ }
+
+ // filter out invalid / unsupported packets
+ if (cmd > MAX_PACKET_DB || packet_db[packet_ver][cmd].len == 0) {
+ ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", cmd, RFIFOREST(fd), fd);
+#ifdef DUMP_INVALID_PACKET
+ ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
+#endif
+ set_eof(fd);
+ return 0;
+ }
+
+ // determine real packet length
+ packet_len = packet_db[packet_ver][cmd].len;
+ if (packet_len == -1) { // variable-length packet
+ if (RFIFOREST(fd) < 4)
+ return 0;
+
+ packet_len = RFIFOW(fd,2);
+ if (packet_len < 4 || packet_len > 32768) {
+ ShowWarning("clif_parse: Received packet 0x%04x specifies invalid packet_len (%d), disconnecting session #%d.\n", cmd, packet_len, fd);
+#ifdef DUMP_INVALID_PACKET
+ ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
+#endif
+ set_eof(fd);
+ return 0;
+ }
+ }
+ if ((int)RFIFOREST(fd) < packet_len)
+ return 0; // not enough data received to form the packet
+
+ if( packet_db[packet_ver][cmd].func == clif_parse_debug )
+ packet_db[packet_ver][cmd].func(fd, sd);
+ else if( packet_db[packet_ver][cmd].func != NULL ) {
+ if( !sd && packet_db[packet_ver][cmd].func != clif_parse_WantToConnection )
+ ; //Only valid packet when there is no session
+ else
+ if( sd && sd->bl.prev == NULL && packet_db[packet_ver][cmd].func != clif_parse_LoadEndAck )
+ ; //Only valid packet when player is not on a map
+ else
+ if( sd && session[sd->fd]->flag.eof )
+ ; //No more packets accepted
+ else
+ packet_db[packet_ver][cmd].func(fd, sd);
+ }
+#ifdef DUMP_UNKNOWN_PACKET
+ else {
+ const char* packet_txt = "save/packet.txt";
+ FILE* fp;
+
+ if( ( fp = fopen( packet_txt , "a" ) ) != NULL ) {
+ if( sd ) {
+ fprintf(fp, "Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
+ } else {
+ fprintf(fp, "Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
+ }
+
+ WriteDump(fp, RFIFOP(fd,0), packet_len);
+ fprintf(fp, "\n");
+ fclose(fp);
+ } else {
+ ShowError("Failed to write '%s'.\n", packet_txt);
+
+ // Dump on console instead
+ if( sd ) {
+ ShowDebug("Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
+ } else {
+ ShowDebug("Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
+ }
+
+ ShowDump(RFIFOP(fd,0), packet_len);
+ }
+ }
+#endif
+
+ RFIFOSKIP(fd, packet_len);
+
+ }; // main loop end
+
+ return 0;
+}
+
+/*==========================================
+ * Reads packet_db.txt and setups its array reference
+ *------------------------------------------*/
+static int packetdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int cmd,i,j,packet_ver;
+ int max_cmd=-1;
+ int skip_ver = 0;
+ int warned = 0;
+ char *str[64],*p,*str2[64],*p2,w1[64],w2[64];
+ int packet_len_table[MAX_PACKET_DB] = {
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#if PACKETVER <= 20081217
+ 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,110, 3, 2,
+#else
+ 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,114, 3, 2,
+#endif
+#if PACKETVER < 2
+ 3, 28, 19, 11, 3, -1, 9, 5, 52, 51, 56, 58, 41, 2, 6, 6,
+#elif PACKETVER < 20071106 // 78-7b Lv99 effect for later Kameshima
+ 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6,
+#elif PACKETVER <= 20081217 // change in 0x78 and 0x7c
+ 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 42, 2, 6, 6,
+#else
+ 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 44, 2, 6, 6,
+#endif
+ //#0x0080
+ 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 2, -1, -1, -1, 0, // 0x8b changed to 2 (was 23)
+ 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
+#if PACKETVER <= 20100622
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
+#else
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 9, 4, 7, 0, -1, 6, // 0xaa changed to 9 (was 7)
+#endif
+ 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
+ //#0x00C0
+ 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 3, 2, 27, // 0xcd change to 3 (was 6)
+ 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
+ 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
+ 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
+ //#0x0100
+ 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
+ 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
+ 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
+ 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
+ //#0x0140
+ 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
+ 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
+ -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182,
+ 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
+ //#0x0180
+ 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
+#if PACKETVER < 1
+ 90, 86, 24, 6, 30,102, 8, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#else // 196 comodo icon status display for later
+ 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#endif
+#if PACKETVER < 20081126
+ 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+#else // 0x1a2 changed (35->37)
+ 3, 3, 37, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+#endif
+ 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
+ //#0x01C0, Set 0x1d5=-1
+ 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 3, 9, 9, 30, 6, 28,
+ 8, 14, 10, 35, 6, -1, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
+ 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
+ -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
+ //#0x0200
+ 26, -1, 26, 10, 18, 26, 11, 34, 14, 36, 10, 0, 0, -1, 32, 10, // 0x20c change to 0 (was 19)
+ 22, 0, 26, 26, 42, 6, 6, 2, 2,282,282, 10, 10, -1, -1, 66,
+#if PACKETVER < 20071106
+ 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 64, 5, 71, 5,
+#else // 0x22c changed
+ 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 65, 5, 71, 5,
+#endif
+ 12, 26, 9, 11, -1, -1, 10, 2,282, 11, 4, 36, 6, -1, 4, 2,
+ //#0x0240
+ -1, -1, -1, -1, -1, 3, 4, 8, -1, 3, 70, 4, 8, 12, 4, 10,
+ 3, 32, -1, 3, 3, 5, 5, 8, 2, 3, -1, 6, 4, 6, 4, 6,
+ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0280
+#if PACKETVER < 20070711
+ 0, 0, 0, 6, 14, 0, 0, -1, 6, 8, 18, 0, 0, 0, 0, 0,
+#else
+ 0, 0, 0, 6, 14, 0, 0, -1, 10, 12, 18, 0, 0, 0, 0, 0, // 0x288, 0x289 increase by 4 (kafra points)
+#endif
+ 0, 4, 0, 70, 10, 0, 0, 0, 8, 6, 27, 80, 0, -1, 0, 0,
+ 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, -1, -1,107, 6, -1, 7, 7, 22,191, 0, 8, 0, 0, 0, 0,
+ //#0x02C0
+ 0, -1, 0, 0, 0, 30, 30, 0, 0, 3, 0, 65, 4, 71, 10, 0,
+ -1, -1, -1, 0, 29, 0, 6, -1, 10, 10, 3, 0, -1, 32, 6, 36,
+ 34, 33, 0, 0, 0, 0, 0, 0, -1, -1, -1, 13, 67, 59, 60, 8,
+ 10, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0300
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0340
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0380
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x03C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0400
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 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, -1, 0, 0, 0, 14, 0, 0, 0, 6, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0480
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x04C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0500
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ //#0x0540
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0580
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x05C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0600
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ //#0x0640
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0680
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x06C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0700
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ //#0x0740
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0780
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x07C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#if PACKETVER < 20090617
+ 6, 2, -1, 4, 4, 4, 4, 8, 8,254, 6, 8, 6, 54, 30, 54,
+#else // 0x7d9 changed
+ 6, 2, -1, 4, 4, 4, 4, 8, 8,268, 6, 8, 6, 54, 30, 54,
+#endif
+ 0, 15, 8, 6, -1, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 14, -1, -1, -1, 8, 25, 0, 0, 26, 0,
+ //#0x0800
+#if PACKETVER < 20091229
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 20,
+#else // for Party booking ( PACKETVER >= 20091229 )
+ -1, -1, 18, 4, 8, 6, 2, 4, 14, 50, 18, 6, 2, 3, 14, 20,
+#endif
+ 3, -1, 8, -1, 86, 2, 6, 6, -1, -1, 4, 10, 10, 0, 0, 0,
+ 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -1, -1, 3, 2, 66, 5, 2, 12, 6, 0, 0,
+ //#0x0840
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0880
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x08C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
+ 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0900
+ 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0940
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ };
+ struct {
+ void (*func)(int, struct map_session_data *);
+ char *name;
+ } clif_parse_func[]={
+ {clif_parse_WantToConnection,"wanttoconnection"},
+ {clif_parse_LoadEndAck,"loadendack"},
+ {clif_parse_TickSend,"ticksend"},
+ {clif_parse_WalkToXY,"walktoxy"},
+ {clif_parse_QuitGame,"quitgame"},
+ {clif_parse_GetCharNameRequest,"getcharnamerequest"},
+ {clif_parse_GlobalMessage,"globalmessage"},
+ {clif_parse_MapMove,"mapmove"},
+ {clif_parse_ChangeDir,"changedir"},
+ {clif_parse_Emotion,"emotion"},
+ {clif_parse_HowManyConnections,"howmanyconnections"},
+ {clif_parse_ActionRequest,"actionrequest"},
+ {clif_parse_Restart,"restart"},
+ {clif_parse_WisMessage,"wis"},
+ {clif_parse_Broadcast,"broadcast"},
+ {clif_parse_TakeItem,"takeitem"},
+ {clif_parse_DropItem,"dropitem"},
+ {clif_parse_UseItem,"useitem"},
+ {clif_parse_EquipItem,"equipitem"},
+ {clif_parse_UnequipItem,"unequipitem"},
+ {clif_parse_NpcClicked,"npcclicked"},
+ {clif_parse_NpcBuySellSelected,"npcbuysellselected"},
+ {clif_parse_NpcBuyListSend,"npcbuylistsend"},
+ {clif_parse_NpcSellListSend,"npcselllistsend"},
+ {clif_parse_CreateChatRoom,"createchatroom"},
+ {clif_parse_ChatAddMember,"chataddmember"},
+ {clif_parse_ChatRoomStatusChange,"chatroomstatuschange"},
+ {clif_parse_ChangeChatOwner,"changechatowner"},
+ {clif_parse_KickFromChat,"kickfromchat"},
+ {clif_parse_ChatLeave,"chatleave"},
+ {clif_parse_TradeRequest,"traderequest"},
+ {clif_parse_TradeAck,"tradeack"},
+ {clif_parse_TradeAddItem,"tradeadditem"},
+ {clif_parse_TradeOk,"tradeok"},
+ {clif_parse_TradeCancel,"tradecancel"},
+ {clif_parse_TradeCommit,"tradecommit"},
+ {clif_parse_StopAttack,"stopattack"},
+ {clif_parse_PutItemToCart,"putitemtocart"},
+ {clif_parse_GetItemFromCart,"getitemfromcart"},
+ {clif_parse_RemoveOption,"removeoption"},
+ {clif_parse_ChangeCart,"changecart"},
+ {clif_parse_StatusUp,"statusup"},
+ {clif_parse_SkillUp,"skillup"},
+ {clif_parse_UseSkillToId,"useskilltoid"},
+ {clif_parse_UseSkillToPos,"useskilltopos"},
+ {clif_parse_UseSkillToPosMoreInfo,"useskilltoposinfo"},
+ {clif_parse_UseSkillMap,"useskillmap"},
+ {clif_parse_RequestMemo,"requestmemo"},
+ {clif_parse_ProduceMix,"producemix"},
+ {clif_parse_Cooking,"cooking"},
+ {clif_parse_NpcSelectMenu,"npcselectmenu"},
+ {clif_parse_NpcNextClicked,"npcnextclicked"},
+ {clif_parse_NpcAmountInput,"npcamountinput"},
+ {clif_parse_NpcStringInput,"npcstringinput"},
+ {clif_parse_NpcCloseClicked,"npccloseclicked"},
+ {clif_parse_ItemIdentify,"itemidentify"},
+ {clif_parse_SelectArrow,"selectarrow"},
+ {clif_parse_AutoSpell,"autospell"},
+ {clif_parse_UseCard,"usecard"},
+ {clif_parse_InsertCard,"insertcard"},
+ {clif_parse_RepairItem,"repairitem"},
+ {clif_parse_WeaponRefine,"weaponrefine"},
+ {clif_parse_SolveCharName,"solvecharname"},
+ {clif_parse_ResetChar,"resetchar"},
+ {clif_parse_LocalBroadcast,"localbroadcast"},
+ {clif_parse_MoveToKafra,"movetokafra"},
+ {clif_parse_MoveFromKafra,"movefromkafra"},
+ {clif_parse_MoveToKafraFromCart,"movetokafrafromcart"},
+ {clif_parse_MoveFromKafraToCart,"movefromkafratocart"},
+ {clif_parse_CloseKafra,"closekafra"},
+ {clif_parse_CreateParty,"createparty"},
+ {clif_parse_CreateParty2,"createparty2"},
+ {clif_parse_PartyInvite,"partyinvite"},
+ {clif_parse_PartyInvite2,"partyinvite2"},
+ {clif_parse_ReplyPartyInvite,"replypartyinvite"},
+ {clif_parse_ReplyPartyInvite2,"replypartyinvite2"},
+ {clif_parse_LeaveParty,"leaveparty"},
+ {clif_parse_RemovePartyMember,"removepartymember"},
+ {clif_parse_PartyChangeOption,"partychangeoption"},
+ {clif_parse_PartyMessage,"partymessage"},
+ {clif_parse_PartyChangeLeader,"partychangeleader"},
+ {clif_parse_CloseVending,"closevending"},
+ {clif_parse_VendingListReq,"vendinglistreq"},
+ {clif_parse_PurchaseReq,"purchasereq"},
+ {clif_parse_PurchaseReq2,"purchasereq2"},
+ {clif_parse_OpenVending,"openvending"},
+ {clif_parse_CreateGuild,"createguild"},
+ {clif_parse_GuildCheckMaster,"guildcheckmaster"},
+ {clif_parse_GuildRequestInfo,"guildrequestinfo"},
+ {clif_parse_GuildChangePositionInfo,"guildchangepositioninfo"},
+ {clif_parse_GuildChangeMemberPosition,"guildchangememberposition"},
+ {clif_parse_GuildRequestEmblem,"guildrequestemblem"},
+ {clif_parse_GuildChangeEmblem,"guildchangeemblem"},
+ {clif_parse_GuildChangeNotice,"guildchangenotice"},
+ {clif_parse_GuildInvite,"guildinvite"},
+ {clif_parse_GuildReplyInvite,"guildreplyinvite"},
+ {clif_parse_GuildLeave,"guildleave"},
+ {clif_parse_GuildExpulsion,"guildexpulsion"},
+ {clif_parse_GuildMessage,"guildmessage"},
+ {clif_parse_GuildRequestAlliance,"guildrequestalliance"},
+ {clif_parse_GuildReplyAlliance,"guildreplyalliance"},
+ {clif_parse_GuildDelAlliance,"guilddelalliance"},
+ {clif_parse_GuildOpposition,"guildopposition"},
+ {clif_parse_GuildBreak,"guildbreak"},
+ {clif_parse_PetMenu,"petmenu"},
+ {clif_parse_CatchPet,"catchpet"},
+ {clif_parse_SelectEgg,"selectegg"},
+ {clif_parse_SendEmotion,"sendemotion"},
+ {clif_parse_ChangePetName,"changepetname"},
+
+ {clif_parse_GMKick,"gmkick"},
+ {clif_parse_GMHide,"gmhide"},
+ {clif_parse_GMReqNoChat,"gmreqnochat"},
+ {clif_parse_GMReqAccountName,"gmreqaccname"},
+ {clif_parse_GMKickAll,"killall"},
+ {clif_parse_GMRecall,"recall"},
+ {clif_parse_GMRecall,"summon"},
+ {clif_parse_GM_Monster_Item,"itemmonster"},
+ {clif_parse_GMShift,"remove"},
+ {clif_parse_GMShift,"shift"},
+ {clif_parse_GMChangeMapType,"changemaptype"},
+ {clif_parse_GMRc,"rc"},
+ {clif_parse_GMRecall2,"recall2"},
+ {clif_parse_GMRemove2,"remove2"},
+
+ {clif_parse_NoviceDoriDori,"sndoridori"},
+ {clif_parse_NoviceExplosionSpirits,"snexplosionspirits"},
+ {clif_parse_PMIgnore,"wisexin"},
+ {clif_parse_PMIgnoreList,"wisexlist"},
+ {clif_parse_PMIgnoreAll,"wisall"},
+ {clif_parse_FriendsListAdd,"friendslistadd"},
+ {clif_parse_FriendsListRemove,"friendslistremove"},
+ {clif_parse_FriendsListReply,"friendslistreply"},
+ {clif_parse_Blacksmith,"blacksmith"},
+ {clif_parse_Alchemist,"alchemist"},
+ {clif_parse_Taekwon,"taekwon"},
+ {clif_parse_RankingPk,"rankingpk"},
+ {clif_parse_FeelSaveOk,"feelsaveok"},
+ {clif_parse_debug,"debug"},
+ {clif_parse_ChangeHomunculusName,"changehomunculusname"},
+ {clif_parse_HomMoveToMaster,"hommovetomaster"},
+ {clif_parse_HomMoveTo,"hommoveto"},
+ {clif_parse_HomAttack,"homattack"},
+ {clif_parse_HomMenu,"hommenu"},
+ {clif_parse_StoragePassword,"storagepassword"},
+ {clif_parse_Hotkey,"hotkey"},
+ {clif_parse_AutoRevive,"autorevive"},
+ {clif_parse_Check,"check"},
+ {clif_parse_Adopt_request,"adoptrequest"},
+ {clif_parse_Adopt_reply,"adoptreply"},
+ // MAIL SYSTEM
+ {clif_parse_Mail_refreshinbox,"mailrefresh"},
+ {clif_parse_Mail_read,"mailread"},
+ {clif_parse_Mail_getattach,"mailgetattach"},
+ {clif_parse_Mail_delete,"maildelete"},
+ {clif_parse_Mail_return,"mailreturn"},
+ {clif_parse_Mail_setattach,"mailsetattach"},
+ {clif_parse_Mail_winopen,"mailwinopen"},
+ {clif_parse_Mail_send,"mailsend"},
+ // AUCTION SYSTEM
+ {clif_parse_Auction_search,"auctionsearch"},
+ {clif_parse_Auction_buysell,"auctionbuysell"},
+ {clif_parse_Auction_setitem,"auctionsetitem"},
+ {clif_parse_Auction_cancelreg,"auctioncancelreg"},
+ {clif_parse_Auction_register,"auctionregister"},
+ {clif_parse_Auction_cancel,"auctioncancel"},
+ {clif_parse_Auction_close,"auctionclose"},
+ {clif_parse_Auction_bid,"auctionbid"},
+ // Quest Log System
+ {clif_parse_questStateAck,"queststate"},
+ {clif_parse_cashshop_buy,"cashshopbuy"},
+ {clif_parse_ViewPlayerEquip,"viewplayerequip"},
+ {clif_parse_EquipTick,"equiptickbox"},
+ {clif_parse_BattleChat,"battlechat"},
+ {clif_parse_mercenary_action,"mermenu"},
+ {clif_parse_progressbar,"progressbar"},
+ {clif_parse_SkillSelectMenu,"skillselectmenu"},
+ {clif_parse_ItemListWindowSelected,"itemlistwindowselected"},
+#if PACKETVER >= 20091229
+ {clif_parse_PartyBookingRegisterReq,"bookingregreq"},
+ {clif_parse_PartyBookingSearchReq,"bookingsearchreq"},
+ {clif_parse_PartyBookingUpdateReq,"bookingupdatereq"},
+ {clif_parse_PartyBookingDeleteReq,"bookingdelreq"},
+#endif
+ {clif_parse_PVPInfo,"pvpinfo"},
+ {clif_parse_LessEffect,"lesseffect"},
+ // Buying Store
+ {clif_parse_ReqOpenBuyingStore,"reqopenbuyingstore"},
+ {clif_parse_ReqCloseBuyingStore,"reqclosebuyingstore"},
+ {clif_parse_ReqClickBuyingStore,"reqclickbuyingstore"},
+ {clif_parse_ReqTradeBuyingStore,"reqtradebuyingstore"},
+ // Store Search
+ {clif_parse_SearchStoreInfo,"searchstoreinfo"},
+ {clif_parse_SearchStoreInfoNextPage,"searchstoreinfonextpage"},
+ {clif_parse_CloseSearchStoreInfo,"closesearchstoreinfo"},
+ {clif_parse_SearchStoreInfoListItemClick,"searchstoreinfolistitemclick"},
+ /* */
+ { clif_parse_MoveItem , "moveitem" },
+ {NULL,NULL}
+ };
+
+ // initialize packet_db[SERVER] from hardcoded packet_len_table[] values
+ memset(packet_db,0,sizeof(packet_db));
+ for( i = 0; i < ARRAYLENGTH(packet_len_table); ++i )
+ packet_len(i) = packet_len_table[i];
+
+ sprintf(line, "%s/packet_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowFatalError("can't read %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ packet_ver = MAX_PACKET_VER; // read into packet_db's version by default
+ while( fgets(line, sizeof(line), fp) )
+ {
+ ln++;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if (sscanf(line,"%256[^:]: %256[^\r\n]",w1,w2) == 2)
+ {
+ if(strcmpi(w1,"packet_ver")==0) {
+ int prev_ver = packet_ver;
+ skip_ver = 0;
+ packet_ver = atoi(w2);
+ if ( packet_ver > MAX_PACKET_VER )
+ { //Check to avoid overflowing. [Skotlex]
+ if( (warned&1) == 0 )
+ ShowWarning("The packet_db table only has support up to version %d.\n", MAX_PACKET_VER);
+ warned &= 1;
+ skip_ver = 1;
+ }
+ else if( packet_ver < 0 )
+ {
+ if( (warned&2) == 0 )
+ ShowWarning("Negative packet versions are not supported.\n");
+ warned &= 2;
+ skip_ver = 1;
+ }
+ else if( packet_ver == SERVER )
+ {
+ if( (warned&4) == 0 )
+ ShowWarning("Packet version %d is reserved for server use only.\n", SERVER);
+ warned &= 4;
+ skip_ver = 1;
+ }
+
+ if( skip_ver )
+ {
+ ShowWarning("Skipping packet version %d.\n", packet_ver);
+ packet_ver = prev_ver;
+ continue;
+ }
+ // copy from previous version into new version and continue
+ // - indicating all following packets should be read into the newer version
+ memcpy(&packet_db[packet_ver], &packet_db[prev_ver], sizeof(packet_db[0]));
+ continue;
+ } else if(strcmpi(w1,"packet_db_ver")==0) {
+ if(strcmpi(w2,"default")==0) //This is the preferred version.
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ else // to manually set the packet DB version
+ clif_config.packet_db_ver = cap_value(atoi(w2), 0, MAX_PACKET_VER);
+
+ continue;
+ }
+ }
+
+ if( skip_ver != 0 )
+ continue; // Skipping current packet version
+
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<4 && p; ++j)
+ {
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+ cmd=strtol(str[0],(char **)NULL,0);
+ if(max_cmd < cmd)
+ max_cmd = cmd;
+ if(cmd <= 0 || cmd > MAX_PACKET_DB)
+ continue;
+ if(str[1]==NULL){
+ ShowError("packet_db: packet len error\n");
+ continue;
+ }
+
+ packet_db[packet_ver][cmd].len = (short)atoi(str[1]);
+
+ if(str[2]==NULL){
+ packet_db[packet_ver][cmd].func = NULL;
+ ln++;
+ continue;
+ }
+
+ // look up processing function by name
+ ARR_FIND( 0, ARRAYLENGTH(clif_parse_func), j, clif_parse_func[j].name != NULL && strcmp(str[2],clif_parse_func[j].name)==0 );
+ if( j < ARRAYLENGTH(clif_parse_func) )
+ packet_db[packet_ver][cmd].func = clif_parse_func[j].func;
+
+ // set the identifying cmd for the packet_db version
+ if (strcmp(str[2],"wanttoconnection")==0)
+ clif_config.connect_cmd[packet_ver] = cmd;
+
+ if(str[3]==NULL){
+ ShowError("packet_db: packet error\n");
+ exit(EXIT_FAILURE);
+ }
+ for(j=0,p2=str[3];p2;j++){
+ short k;
+ str2[j]=p2;
+ p2=strchr(p2,':');
+ if(p2) *p2++=0;
+ k = atoi(str2[j]);
+ // if (packet_db[packet_ver][cmd].pos[j] != k && clif_config.prefer_packet_db) // not used for now
+
+ 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;
+ }
+ }
+ fclose(fp);
+ if(max_cmd > MAX_PACKET_DB)
+ {
+ ShowWarning("Found packets up to 0x%X, ignored 0x%X and above.\n", max_cmd, MAX_PACKET_DB);
+ ShowWarning("Please increase MAX_PACKET_DB and recompile.\n");
+ }
+ if (!clif_config.connect_cmd[clif_config.packet_db_ver])
+ { //Locate the nearest version that we still support. [Skotlex]
+ for(j = clif_config.packet_db_ver; j >= 0 && !clif_config.connect_cmd[j]; j--);
+
+ clif_config.packet_db_ver = j?j:MAX_PACKET_VER;
+ }
+ ShowStatus("Done reading packet database from '"CL_WHITE"%s"CL_RESET"'. Using default packet version: "CL_WHITE"%d"CL_RESET".\n", "packet_db.txt", clif_config.packet_db_ver);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int do_init_clif(void) {
+ const char* colors[COLOR_MAX] = { "0xFF0000" };
+ int i;
+ /**
+ * Setup Color Table (saves unnecessary load of strtoul on every call)
+ **/
+ for(i = 0; i < COLOR_MAX; i++) {
+ color_table[i] = strtoul(colors[i],NULL,0);
+ color_table[i] = (color_table[i] & 0x0000FF) << 16 | (color_table[i] & 0x00FF00) | (color_table[i] & 0xFF0000) >> 16;//RGB to BGR
+ }
+
+ clif_config.packet_db_ver = -1; // the main packet version of the DB
+ memset(clif_config.connect_cmd, 0, sizeof(clif_config.connect_cmd)); //The default connect command will be determined after reading the packet_db [Skotlex]
+
+ memset(packet_db,0,sizeof(packet_db));
+ //Using the packet_db file is the only way to set up packets now [Skotlex]
+ packetdb_readdb();
+
+ set_defaultparse(clif_parse);
+ if( make_listen_bind(bind_ip,map_port) == -1 ) {
+ ShowFatalError("can't bind game port\n");
+ exit(EXIT_FAILURE);
+ }
+
+ add_timer_func_list(clif_clearunit_delayed_sub, "clif_clearunit_delayed_sub");
+ add_timer_func_list(clif_delayquit, "clif_delayquit");
+
+ delay_clearunit_ers = ers_new(sizeof(struct block_list),"clif.c::delay_clearunit_ers",ERS_OPT_CLEAR);
+
+ return 0;
+}
+
+void do_final_clif(void) {
+ ers_destroy(delay_clearunit_ers);
+}
diff --git a/src/map/script.c b/src/map/script.c index a918bc853..27e0bb549 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -1,17779 +1,17784 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -//#define DEBUG_DISP -//#define DEBUG_DISASM -//#define DEBUG_RUN -//#define DEBUG_HASH -//#define DEBUG_DUMP_STACK - -#include "../common/cbasetypes.h" -#include "../common/malloc.h" -#include "../common/md5calc.h" -#include "../common/nullpo.h" -#include "../common/random.h" -#include "../common/showmsg.h" -#include "../common/socket.h" // usage: getcharip -#include "../common/strlib.h" -#include "../common/timer.h" -#include "../common/utils.h" - -#include "map.h" -#include "path.h" -#include "clif.h" -#include "chrif.h" -#include "itemdb.h" -#include "pc.h" -#include "status.h" -#include "storage.h" -#include "mob.h" -#include "npc.h" -#include "pet.h" -#include "mapreg.h" -#include "homunculus.h" -#include "instance.h" -#include "mercenary.h" -#include "intif.h" -#include "skill.h" -#include "status.h" -#include "chat.h" -#include "battle.h" -#include "battleground.h" -#include "party.h" -#include "guild.h" -#include "atcommand.h" -#include "log.h" -#include "unit.h" -#include "pet.h" -#include "mail.h" -#include "script.h" -#include "quest.h" -#include "elemental.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#ifndef WIN32 - #include <sys/time.h> -#endif -#include <time.h> -#include <setjmp.h> -#include <errno.h> - -#ifdef BETA_THREAD_TEST - #include "../common/atomic.h" - #include "../common/spinlock.h" - #include "../common/thread.h" - #include "../common/mutex.h" -#endif - - -/////////////////////////////////////////////////////////////////////////////// -//## TODO possible enhancements: [FlavioJS] -// - 'callfunc' supporting labels in the current npc "::LabelName" -// - 'callfunc' supporting labels in other npcs "NpcName::LabelName" -// - 'function FuncName;' function declarations reverting to global functions -// if local label isn't found -// - join callfunc and callsub's functionality -// - remove dynamic allocation in add_word() -// - remove GETVALUE / SETVALUE -// - clean up the set_reg / set_val / setd_sub mess -// - detect invalid label references at parse-time - -// -// struct script_state* st; -// - -/// Returns the script_data at the target index -#define script_getdata(st,i) ( &((st)->stack->stack_data[(st)->start + (i)]) ) -/// Returns if the stack contains data at the target index -#define script_hasdata(st,i) ( (st)->end > (st)->start + (i) ) -/// Returns the index of the last data in the stack -#define script_lastdata(st) ( (st)->end - (st)->start - 1 ) -/// Pushes an int into the stack -#define script_pushint(st,val) push_val((st)->stack, C_INT, (val)) -/// Pushes a string into the stack (script engine frees it automatically) -#define script_pushstr(st,val) push_str((st)->stack, C_STR, (val)) -/// Pushes a copy of a string into the stack -#define script_pushstrcopy(st,val) push_str((st)->stack, C_STR, aStrdup(val)) -/// Pushes a constant string into the stack (must never change or be freed) -#define script_pushconststr(st,val) push_str((st)->stack, C_CONSTSTR, (val)) -/// Pushes a nil into the stack -#define script_pushnil(st) push_val((st)->stack, C_NOP, 0) -/// Pushes a copy of the data in the target index -#define script_pushcopy(st,i) push_copy((st)->stack, (st)->start + (i)) - -#define script_isstring(st,i) data_isstring(script_getdata(st,i)) -#define script_isint(st,i) data_isint(script_getdata(st,i)) - -#define script_getnum(st,val) conv_num(st, script_getdata(st,val)) -#define script_getstr(st,val) conv_str(st, script_getdata(st,val)) -#define script_getref(st,val) ( script_getdata(st,val)->ref ) - -// Note: "top" functions/defines use indexes relative to the top of the stack -// -1 is the index of the data at the top - -/// Returns the script_data at the target index relative to the top of the stack -#define script_getdatatop(st,i) ( &((st)->stack->stack_data[(st)->stack->sp + (i)]) ) -/// Pushes a copy of the data in the target index relative to the top of the stack -#define script_pushcopytop(st,i) push_copy((st)->stack, (st)->stack->sp + (i)) -/// Removes the range of values [start,end[ relative to the top of the stack -#define script_removetop(st,start,end) ( pop_stack((st), ((st)->stack->sp + (start)), (st)->stack->sp + (end)) ) - -// -// struct script_data* data; -// - -/// Returns if the script data is a string -#define data_isstring(data) ( (data)->type == C_STR || (data)->type == C_CONSTSTR ) -/// Returns if the script data is an int -#define data_isint(data) ( (data)->type == C_INT ) -/// Returns if the script data is a reference -#define data_isreference(data) ( (data)->type == C_NAME ) -/// Returns if the script data is a label -#define data_islabel(data) ( (data)->type == C_POS ) -/// Returns if the script data is an internal script function label -#define data_isfunclabel(data) ( (data)->type == C_USERFUNC_POS ) - -/// Returns if this is a reference to a constant -#define reference_toconstant(data) ( str_data[reference_getid(data)].type == C_INT ) -/// Returns if this a reference to a param -#define reference_toparam(data) ( str_data[reference_getid(data)].type == C_PARAM ) -/// Returns if this a reference to a variable -//##TODO confirm it's C_NAME [FlavioJS] -#define reference_tovariable(data) ( str_data[reference_getid(data)].type == C_NAME ) -/// Returns the unique id of the reference (id and index) -#define reference_getuid(data) ( (data)->u.num ) -/// Returns the id of the reference -#define reference_getid(data) ( (int32)(reference_getuid(data) & 0x00ffffff) ) -/// Returns the array index of the reference -#define reference_getindex(data) ( (int32)(((uint32)(reference_getuid(data) & 0xff000000)) >> 24) ) -/// Returns the name of the reference -#define reference_getname(data) ( str_buf + str_data[reference_getid(data)].str ) -/// Returns the linked list of uid-value pairs of the reference (can be NULL) -#define reference_getref(data) ( (data)->ref ) -/// Returns the value of the constant -#define reference_getconstant(data) ( str_data[reference_getid(data)].val ) -/// Returns the type of param -#define reference_getparamtype(data) ( str_data[reference_getid(data)].val ) - -/// Composes the uid of a reference from the id and the index -#define reference_uid(id,idx) ( (int32)((((uint32)(id)) & 0x00ffffff) | (((uint32)(idx)) << 24)) ) - -#define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.' && (prefix) != '\'') -#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' && (prefix) != '\'' ) -#define is_string_variable(name) ( (name)[strlen(name) - 1] == '$' ) - -#define FETCH(n, t) \ - if( script_hasdata(st,n) ) \ - (t)=script_getnum(st,n); - -/// Maximum amount of elements in script arrays -#define SCRIPT_MAX_ARRAYSIZE 128 - -#define SCRIPT_BLOCK_SIZE 512 -enum { LABEL_NEXTLINE=1,LABEL_START }; - -/// temporary buffer for passing around compiled bytecode -/// @see add_scriptb, set_label, parse_script -static unsigned char* script_buf = NULL; -static int script_pos = 0, script_size = 0; - -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 -static struct str_data_struct { - enum c_op type; - int str; - int backpatch; - int label; - int (*func)(struct script_state *st); - int val; - int next; -} *str_data = NULL; -static int str_data_size = 0; // size of the data -static int str_num = LABEL_START; // next id to be assigned - -// str_buf holds the strings themselves -static char *str_buf; -static int str_size = 0; // size of the buffer -static int str_pos = 0; // next position to be assigned - - -// Using a prime number for SCRIPT_HASH_SIZE should give better distributions -#define SCRIPT_HASH_SIZE 1021 -int str_hash[SCRIPT_HASH_SIZE]; -// Specifies which string hashing method to use -//#define SCRIPT_HASH_DJB2 -//#define SCRIPT_HASH_SDBM -#define SCRIPT_HASH_ELF - -static DBMap* scriptlabel_db=NULL; // const char* label_name -> int script_pos -static DBMap* userfunc_db=NULL; // const char* func_name -> struct script_code* -static int parse_options=0; -DBMap* script_get_label_db(void){ return scriptlabel_db; } -DBMap* script_get_userfunc_db(void){ return userfunc_db; } - -// important buildin function references for usage in scripts -static int buildin_set_ref = 0; -static int buildin_callsub_ref = 0; -static int buildin_callfunc_ref = 0; -static int buildin_getelementofarray_ref = 0; - -// Caches compiled autoscript item code. -// Note: This is not cleared when reloading itemdb. -static DBMap* autobonus_db=NULL; // char* script -> char* bytecode - -struct Script_Config script_config = { - 1, // warn_func_mismatch_argtypes - 1, 65535, 2048, //warn_func_mismatch_paramnum/check_cmdcount/check_gotocount - 0, INT_MAX, // input_min_value/input_max_value - "OnPCDieEvent", //die_event_name - "OnPCKillEvent", //kill_pc_event_name - "OnNPCKillEvent", //kill_mob_event_name - "OnPCLoginEvent", //login_event_name - "OnPCLogoutEvent", //logout_event_name - "OnPCLoadMapEvent", //loadmap_event_name - "OnPCBaseLvUpEvent", //baselvup_event_name - "OnPCJobLvUpEvent", //joblvup_event_name - "OnTouch_", //ontouch_name (runs on first visible char to enter area, picks another char if the first char leaves) - "OnTouch", //ontouch2_name (run whenever a char walks into the OnTouch area) -}; - -static jmp_buf error_jump; -static char* error_msg; -static const char* error_pos; -static int error_report; // if the error should produce output - -// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc ) -// [Eoe / jA 1080, 1081, 1094, 1164] -enum curly_type { - TYPE_NULL = 0, - TYPE_IF, - TYPE_SWITCH, - TYPE_WHILE, - TYPE_FOR, - TYPE_DO, - TYPE_USERFUNC, - TYPE_ARGLIST // function argument list -}; - -enum e_arglist -{ - ARGLIST_UNDEFINED = 0, - ARGLIST_NO_PAREN = 1, - ARGLIST_PAREN = 2, -}; - -static struct { - struct { - enum curly_type type; - int index; - int count; - int flag; - struct linkdb_node *case_label; - } curly[256]; // Information right parenthesis - int curly_count; // The number of right brackets - int index; // Number of the syntax used in the script -} syntax; - -const char* parse_curly_close(const char* p); -const char* parse_syntax_close(const char* p); -const char* parse_syntax_close_sub(const char* p,int* flag); -const char* parse_syntax(const char* p); -static int parse_syntax_for_flag = 0; - -extern int current_equip_item_index; //for New CARDS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus] -int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex] -int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0; -int potion_target=0; - - -c_op get_com(unsigned char *script,int *pos); -int get_num(unsigned char *script,int *pos); - -typedef struct script_function { - int (*func)(struct script_state *st); - const char *name; - const char *arg; -} script_function; - -extern script_function buildin_func[]; - -static struct linkdb_node* sleep_db;// int oid -> struct script_state* - -#ifdef BETA_THREAD_TEST -/** - * MySQL Query Slave - **/ -static SPIN_LOCK queryThreadLock; -static rAthread queryThread = NULL; -static ramutex queryThreadMutex = NULL; -static racond queryThreadCond = NULL; -static volatile int32 queryThreadTerminate = 0; - -struct queryThreadEntry { - bool ok; - bool type; /* main db or log db? */ - struct script_state *st; -}; - -/* Ladies and Gentleman the Manager! */ -struct { - struct queryThreadEntry **entry;/* array of structs */ - int count; - int timer;/* used to receive processed entries */ -} queryThreadData; -#endif - -/*========================================== - * (Only those needed) local declaration prototype - *------------------------------------------*/ -const char* parse_subexpr(const char* p,int limit); -int run_func(struct script_state *st); - -enum { - MF_NOMEMO, //0 - MF_NOTELEPORT, - MF_NOSAVE, - MF_NOBRANCH, - MF_NOPENALTY, - MF_NOZENYPENALTY, - MF_PVP, - MF_PVP_NOPARTY, - MF_PVP_NOGUILD, - MF_GVG, - MF_GVG_NOPARTY, //10 - MF_NOTRADE, - MF_NOSKILL, - MF_NOWARP, - MF_PARTYLOCK, - MF_NOICEWALL, - MF_SNOW, - MF_FOG, - MF_SAKURA, - MF_LEAVES, - /** - * No longer available, keeping here just in case it's back someday. [Ind] - **/ - //MF_RAIN, //20 - // 21 free - MF_NOGO = 22, - MF_CLOUDS, - MF_CLOUDS2, - MF_FIREWORKS, - MF_GVG_CASTLE, - MF_GVG_DUNGEON, - MF_NIGHTENABLED, - MF_NOBASEEXP, - MF_NOJOBEXP, //30 - MF_NOMOBLOOT, - MF_NOMVPLOOT, - MF_NORETURN, - MF_NOWARPTO, - MF_NIGHTMAREDROP, - MF_RESTRICTED, - MF_NOCOMMAND, - MF_NODROP, - MF_JEXP, - MF_BEXP, //40 - MF_NOVENDING, - MF_LOADEVENT, - MF_NOCHAT, - MF_NOEXPPENALTY, - MF_GUILDLOCK, - MF_TOWN, - MF_AUTOTRADE, - MF_ALLOWKS, - MF_MONSTER_NOTELEPORT, - MF_PVP_NOCALCRANK, //50 - MF_BATTLEGROUND, - MF_RESET -}; - -const char* script_op2name(int op) -{ -#define RETURN_OP_NAME(type) case type: return #type - switch( op ) - { - RETURN_OP_NAME(C_NOP); - RETURN_OP_NAME(C_POS); - RETURN_OP_NAME(C_INT); - RETURN_OP_NAME(C_PARAM); - RETURN_OP_NAME(C_FUNC); - RETURN_OP_NAME(C_STR); - RETURN_OP_NAME(C_CONSTSTR); - RETURN_OP_NAME(C_ARG); - RETURN_OP_NAME(C_NAME); - RETURN_OP_NAME(C_EOL); - RETURN_OP_NAME(C_RETINFO); - RETURN_OP_NAME(C_USERFUNC); - RETURN_OP_NAME(C_USERFUNC_POS); - - // operators - RETURN_OP_NAME(C_OP3); - RETURN_OP_NAME(C_LOR); - RETURN_OP_NAME(C_LAND); - RETURN_OP_NAME(C_LE); - RETURN_OP_NAME(C_LT); - RETURN_OP_NAME(C_GE); - RETURN_OP_NAME(C_GT); - RETURN_OP_NAME(C_EQ); - RETURN_OP_NAME(C_NE); - RETURN_OP_NAME(C_XOR); - RETURN_OP_NAME(C_OR); - RETURN_OP_NAME(C_AND); - RETURN_OP_NAME(C_ADD); - RETURN_OP_NAME(C_SUB); - RETURN_OP_NAME(C_MUL); - RETURN_OP_NAME(C_DIV); - RETURN_OP_NAME(C_MOD); - RETURN_OP_NAME(C_NEG); - RETURN_OP_NAME(C_LNOT); - RETURN_OP_NAME(C_NOT); - RETURN_OP_NAME(C_R_SHIFT); - RETURN_OP_NAME(C_L_SHIFT); - - default: - ShowDebug("script_op2name: unexpected op=%d\n", op); - return "???"; - } -#undef RETURN_OP_NAME -} - -#ifdef DEBUG_DUMP_STACK -static void script_dump_stack(struct script_state* st) -{ - int i; - ShowMessage("\tstart = %d\n", st->start); - ShowMessage("\tend = %d\n", st->end); - ShowMessage("\tdefsp = %d\n", st->stack->defsp); - ShowMessage("\tsp = %d\n", st->stack->sp); - for( i = 0; i < st->stack->sp; ++i ) - { - struct script_data* data = &st->stack->stack_data[i]; - ShowMessage("\t[%d] %s", i, script_op2name(data->type)); - switch( data->type ) - { - case C_INT: - case C_POS: - ShowMessage(" %d\n", data->u.num); - break; - - case C_STR: - case C_CONSTSTR: - ShowMessage(" \"%s\"\n", data->u.str); - break; - - case C_NAME: - ShowMessage(" \"%s\" (id=%d ref=%p subtype=%s)\n", reference_getname(data), data->u.num, data->ref, script_op2name(str_data[data->u.num].type)); - break; - - case C_RETINFO: - { - struct script_retinfo* ri = data->u.ri; - ShowMessage(" %p {var_function=%p, script=%p, pos=%d, nargs=%d, defsp=%d}\n", ri, ri->var_function, ri->script, ri->pos, ri->nargs, ri->defsp); - } - break; - default: - ShowMessage("\n"); - break; - } - } -} -#endif - -/// Reports on the console the src of a script error. -static void script_reportsrc(struct script_state *st) -{ - struct block_list* bl; - - if( st->oid == 0 ) - return; //Can't report source. - - bl = map_id2bl(st->oid); - if( bl == NULL ) - return; - - switch( bl->type ) - { - case BL_NPC: - if( bl->m >= 0 ) - ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y); - else - ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name); - break; - default: - if( bl->m >= 0 ) - ShowDebug("Source (Non-NPC type %d): name %s at %s (%d,%d)\n", bl->type, status_get_name(bl), map[bl->m].name, bl->x, bl->y); - else - ShowDebug("Source (Non-NPC type %d): name %s (invisible/not on a map)\n", bl->type, status_get_name(bl)); - break; - } -} - -/// Reports on the console information about the script data. -static void script_reportdata(struct script_data* data) -{ - if( data == NULL ) - return; - switch( data->type ) - { - case C_NOP:// no value - ShowDebug("Data: nothing (nil)\n"); - break; - case C_INT:// number - ShowDebug("Data: number value=%d\n", data->u.num); - break; - case C_STR: - case C_CONSTSTR:// string - if( data->u.str ) - { - ShowDebug("Data: string value=\"%s\"\n", data->u.str); - } - else - { - ShowDebug("Data: string value=NULL\n"); - } - break; - case C_NAME:// reference - if( reference_tovariable(data) ) - {// variable - const char* name = reference_getname(data); - if( not_array_variable(*name) ) - ShowDebug("Data: variable name='%s'\n", name); - else - ShowDebug("Data: variable name='%s' index=%d\n", name, reference_getindex(data)); - } - else if( reference_toconstant(data) ) - {// constant - ShowDebug("Data: constant name='%s' value=%d\n", reference_getname(data), reference_getconstant(data)); - } - else if( reference_toparam(data) ) - {// param - ShowDebug("Data: param name='%s' type=%d\n", reference_getname(data), reference_getparamtype(data)); - } - else - {// ??? - ShowDebug("Data: reference name='%s' type=%s\n", reference_getname(data), script_op2name(data->type)); - ShowDebug("Please report this!!! - str_data.type=%s\n", script_op2name(str_data[reference_getid(data)].type)); - } - break; - case C_POS:// label - ShowDebug("Data: label pos=%d\n", data->u.num); - break; - default: - ShowDebug("Data: %s\n", script_op2name(data->type)); - break; - } -} - - -/// Reports on the console information about the current built-in function. -static void script_reportfunc(struct script_state* st) -{ - int i, params, id; - struct script_data* data; - - if( !script_hasdata(st,0) ) - {// no stack - return; - } - - data = script_getdata(st,0); - - if( !data_isreference(data) || str_data[reference_getid(data)].type != C_FUNC ) - {// script currently not executing a built-in function or corrupt stack - return; - } - - id = reference_getid(data); - params = script_lastdata(st)-1; - - if( params > 0 ) - { - ShowDebug("Function: %s (%d parameter%s):\n", get_str(id), params, ( params == 1 ) ? "" : "s"); - - for( i = 2; i <= script_lastdata(st); i++ ) - { - script_reportdata(script_getdata(st,i)); - } - } - else - { - ShowDebug("Function: %s (no parameters)\n", get_str(id)); - } -} - - -/*========================================== - * Output error message - *------------------------------------------*/ -static void disp_error_message2(const char *mes,const char *pos,int report) -{ - error_msg = aStrdup(mes); - error_pos = pos; - error_report = report; - longjmp( error_jump, 1 ); -} -#define disp_error_message(mes,pos) disp_error_message2(mes,pos,1) - -/// Checks event parameter validity -static void check_event(struct script_state *st, const char *evt) -{ - if( evt && evt[0] && !stristr(evt, "::On") ) - { - ShowWarning("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n", evt); - script_reportsrc(st); - } -} - -/*========================================== - * Hashes the input string - *------------------------------------------*/ -static unsigned int calc_hash(const char* p) -{ - unsigned int h; - -#if defined(SCRIPT_HASH_DJB2) - h = 5381; - while( *p ) // hash*33 + c - h = ( h << 5 ) + h + ((unsigned char)TOLOWER(*p++)); -#elif defined(SCRIPT_HASH_SDBM) - h = 0; - while( *p ) // hash*65599 + c - h = ( h << 6 ) + ( h << 16 ) - h + ((unsigned char)TOLOWER(*p++)); -#elif defined(SCRIPT_HASH_ELF) // UNIX ELF hash - h = 0; - while( *p ){ - unsigned int g; - h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++)); - g = h & 0xF0000000; - if( g ) - { - h ^= g >> 24; - h &= ~g; - } - } -#else // athena hash - h = 0; - while( *p ) - h = ( h << 1 ) + ( h >> 3 ) + ( h >> 5 ) + ( h >> 8 ) + (unsigned char)TOLOWER(*p++); -#endif - - return h % SCRIPT_HASH_SIZE; -} - - -/*========================================== - * str_data manipulation functions - *------------------------------------------*/ - -/// Looks up string using the provided id. -const char* get_str(int id) -{ - Assert( id >= LABEL_START && id < str_size ); - return str_buf+str_data[id].str; -} - -/// Returns the uid of the string, or -1. -static int search_str(const char* p) -{ - int i; - - for( i = str_hash[calc_hash(p)]; i != 0; i = str_data[i].next ) - if( strcasecmp(get_str(i),p) == 0 ) - return i; - - return -1; -} - -/// Stores a copy of the string and returns its id. -/// If an identical string is already present, returns its id instead. -int add_str(const char* p) -{ - int i, h; - int len; - - h = calc_hash(p); - - if( str_hash[h] == 0 ) - {// empty bucket, add new node here - str_hash[h] = str_num; - } - else - {// scan for end of list, or occurence of identical string - for( i = str_hash[h]; ; i = str_data[i].next ) - { - if( strcasecmp(get_str(i),p) == 0 ) - return i; // string already in list - if( str_data[i].next == 0 ) - break; // reached the end - } - - // append node to end of list - str_data[i].next = str_num; - } - - // grow list if neccessary - if( str_num >= str_data_size ) - { - str_data_size += 128; - RECREATE(str_data,struct str_data_struct,str_data_size); - memset(str_data + (str_data_size - 128), '\0', 128); - } - - len=(int)strlen(p); - - // grow string buffer if neccessary - while( str_pos+len+1 >= str_size ) - { - str_size += 256; - RECREATE(str_buf,char,str_size); - memset(str_buf + (str_size - 256), '\0', 256); - } - - safestrncpy(str_buf+str_pos, p, len+1); - str_data[str_num].type = C_NOP; - str_data[str_num].str = str_pos; - str_data[str_num].next = 0; - str_data[str_num].func = NULL; - str_data[str_num].backpatch = -1; - str_data[str_num].label = -1; - str_pos += len+1; - - return str_num++; -} - - -/// Appends 1 byte to the script buffer. -static void add_scriptb(int a) -{ - if( script_pos+1 >= script_size ) - { - script_size += SCRIPT_BLOCK_SIZE; - RECREATE(script_buf,unsigned char,script_size); - } - script_buf[script_pos++] = (uint8)(a); -} - -/// Appends a c_op value to the script buffer. -/// The value is variable-length encoded into 8-bit blocks. -/// The encoding scheme is ( 01?????? )* 00??????, LSB first. -/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries). -static void add_scriptc(int a) -{ - while( a >= 0x40 ) - { - add_scriptb((a&0x3f)|0x40); - a = (a - 0x40) >> 6; - } - - add_scriptb(a); -} - -/// Appends an integer value to the script buffer. -/// The value is variable-length encoded into 8-bit blocks. -/// The encoding scheme is ( 11?????? )* 10??????, LSB first. -/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries). -static void add_scripti(int a) -{ - while( a >= 0x40 ) - { - add_scriptb((a&0x3f)|0xc0); - a = (a - 0x40) >> 6; - } - add_scriptb(a|0x80); -} - -/// Appends a str_data object (label/function/variable/integer) to the script buffer. - -/// -/// @param l The id of the str_data entry -// Maximum up to 16M -static void add_scriptl(int l) -{ - int backpatch = str_data[l].backpatch; - - switch(str_data[l].type){ - case C_POS: - case C_USERFUNC_POS: - add_scriptc(C_POS); - add_scriptb(str_data[l].label); - add_scriptb(str_data[l].label>>8); - add_scriptb(str_data[l].label>>16); - break; - case C_NOP: - case C_USERFUNC: - // Embedded data backpatch there is a possibility of label - add_scriptc(C_NAME); - str_data[l].backpatch = script_pos; - add_scriptb(backpatch); - add_scriptb(backpatch>>8); - add_scriptb(backpatch>>16); - break; - case C_INT: - add_scripti(abs(str_data[l].val)); - if( str_data[l].val < 0 ) //Notice that this is negative, from jA (Rayce) - add_scriptc(C_NEG); - break; - default: // assume C_NAME - add_scriptc(C_NAME); - add_scriptb(l); - add_scriptb(l>>8); - add_scriptb(l>>16); - break; - } -} - -/*========================================== - * Resolve the label - *------------------------------------------*/ -void set_label(int l,int pos, const char* script_pos) -{ - int i,next; - - if(str_data[l].type==C_INT || str_data[l].type==C_PARAM || str_data[l].type==C_FUNC) - { //Prevent overwriting constants values, parameters and built-in functions [Skotlex] - disp_error_message("set_label: invalid label name",script_pos); - return; - } - if(str_data[l].label!=-1){ - disp_error_message("set_label: dup label ",script_pos); - return; - } - str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); - str_data[l].label=pos; - for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){ - next=GETVALUE(script_buf,i); - script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); - SETVALUE(script_buf,i,pos); - i=next; - } -} - -/// Skips spaces and/or comments. -const char* skip_space(const char* p) -{ - if( p == NULL ) - return NULL; - for(;;) - { - while( ISSPACE(*p) ) - ++p; - if( *p == '/' && p[1] == '/' ) - {// line comment - while(*p && *p!='\n') - ++p; - } - else if( *p == '/' && p[1] == '*' ) - {// block comment - p += 2; - for(;;) - { - if( *p == '\0' ) - return p;//disp_error_message("script:skip_space: end of file while parsing block comment. expected "CL_BOLD"*/"CL_NORM, p); - if( *p == '*' && p[1] == '/' ) - {// end of block comment - p += 2; - break; - } - ++p; - } - } - else - break; - } - return p; -} - -/// Skips a word. -/// A word consists of undercores and/or alfanumeric characters, -/// and valid variable prefixes/postfixes. -static -const char* skip_word(const char* p) -{ - // prefix - switch( *p ) - { - case '@':// temporary char variable - ++p; break; - case '#':// account variable - p += ( p[1] == '#' ? 2 : 1 ); break; - case '\'':// instance variable - ++p; break; - case '.':// npc variable - p += ( p[1] == '@' ? 2 : 1 ); break; - case '$':// global variable - p += ( p[1] == '@' ? 2 : 1 ); break; - } - - while( ISALNUM(*p) || *p == '_' ) - ++p; - - // postfix - if( *p == '$' )// string - p++; - - return p; -} - -/// Adds a word to str_data. -/// @see skip_word -/// @see add_str -static -int add_word(const char* p) -{ - char* word; - int len; - int i; - - // Check for a word - len = skip_word(p) - p; - if( len == 0 ) - disp_error_message("script:add_word: invalid word. A word consists of undercores and/or alfanumeric characters, and valid variable prefixes/postfixes.", p); - - // Duplicate the word - word = (char*)aMalloc(len+1); - memcpy(word, p, len); - word[len] = 0; - - // add the word - i = add_str(word); - aFree(word); - return i; -} - -/// Parses a function call. -/// The argument list can have parenthesis or not. -/// The number of arguments is checked. -static -const char* parse_callfunc(const char* p, int require_paren, int is_custom) -{ - const char* p2; - const char* arg=NULL; - int func; - - func = add_word(p); - if( str_data[func].type == C_FUNC ){ - // buildin function - add_scriptl(func); - add_scriptc(C_ARG); - arg = buildin_func[str_data[func].val].arg; - } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){ - // script defined function - add_scriptl(buildin_callsub_ref); - add_scriptc(C_ARG); - add_scriptl(func); - arg = buildin_func[str_data[buildin_callsub_ref].val].arg; - if( *arg == 0 ) - disp_error_message("parse_callfunc: callsub has no arguments, please review it's definition",p); - if( *arg != '*' ) - ++arg; // count func as argument - } else { -#ifdef SCRIPT_CALLFUNC_CHECK - const char* name = get_str(func); - if( !is_custom && strdb_get(userfunc_db, name) == NULL ) { -#endif - disp_error_message("parse_line: expect command, missing function name or calling undeclared function",p); -#ifdef SCRIPT_CALLFUNC_CHECK - } else {; - add_scriptl(buildin_callfunc_ref); - add_scriptc(C_ARG); - add_scriptc(C_STR); - while( *name ) add_scriptb(*name ++); - add_scriptb(0); - arg = buildin_func[str_data[buildin_callfunc_ref].val].arg; - if( *arg != '*' ) ++ arg; - } -#endif - } - - p = skip_word(p); - p = skip_space(p); - syntax.curly[syntax.curly_count].type = TYPE_ARGLIST; - syntax.curly[syntax.curly_count].count = 0; - if( *p == ';' ) - {// <func name> ';' - syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN; - } else if( *p == '(' && *(p2=skip_space(p+1)) == ')' ) - {// <func name> '(' ')' - syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN; - p = p2; - /* - } else if( 0 && require_paren && *p != '(' ) - {// <func name> - syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN; - */ - } else - {// <func name> <arg list> - if( require_paren ){ - if( *p != '(' ) - disp_error_message("need '('",p); - ++p; // skip '(' - syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN; - } else if( *p == '(' ){ - syntax.curly[syntax.curly_count].flag = ARGLIST_UNDEFINED; - } else { - syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN; - } - ++syntax.curly_count; - while( *arg ) { - p2=parse_subexpr(p,-1); - if( p == p2 ) - break; // not an argument - if( *arg != '*' ) - ++arg; // next argument - - p=skip_space(p2); - if( *arg == 0 || *p != ',' ) - break; // no more arguments - ++p; // skip comma - } - --syntax.curly_count; - } - if( *arg && *arg != '?' && *arg != '*' ) - disp_error_message2("parse_callfunc: not enough arguments, expected ','", p, script_config.warn_func_mismatch_paramnum); - if( syntax.curly[syntax.curly_count].type != TYPE_ARGLIST ) - disp_error_message("parse_callfunc: DEBUG last curly is not an argument list",p); - if( syntax.curly[syntax.curly_count].flag == ARGLIST_PAREN ){ - if( *p != ')' ) - disp_error_message("parse_callfunc: expected ')' to close argument list",p); - ++p; - } - add_scriptc(C_FUNC); - return p; -} - -/// Processes end of logical script line. -/// @param first When true, only fix up scheduling data is initialized -/// @param p Script position for error reporting in set_label -static void parse_nextline(bool first, const char* p) -{ - if( !first ) - { - add_scriptc(C_EOL); // mark end of line for stack cleanup - set_label(LABEL_NEXTLINE, script_pos, p); // fix up '-' labels - } - - // initialize data for new '-' label fix up scheduling - str_data[LABEL_NEXTLINE].type = C_NOP; - str_data[LABEL_NEXTLINE].backpatch = -1; - str_data[LABEL_NEXTLINE].label = -1; -} - -/// Parse a variable assignment using the direct equals operator -/// @param p script position where the function should run from -/// @return NULL if not a variable assignment, the new position otherwise -const char* parse_variable(const char* p) { - int i, j, word; - c_op type = C_NOP; - const char *p2 = NULL; - const char *var = p; - - // skip the variable where applicable - p = skip_word(p); - p = skip_space(p); - - if( p == NULL ) {// end of the line or invalid buffer - return NULL; - } - - if( *p == '[' ) {// array variable so process the array as appropriate - for( p2 = p, i = 0, j = 1; p; ++ i ) { - if( *p ++ == ']' && --(j) == 0 ) break; - if( *p == '[' ) ++ j; - } - - if( !(p = skip_space(p)) ) {// end of line or invalid characters remaining - disp_error_message("Missing right expression or closing bracket for variable.", p); - } - } - - if( type == C_NOP && - !( ( p[0] == '=' && p[1] != '=' && (type = C_EQ) ) // = - || ( p[0] == '+' && p[1] == '=' && (type = C_ADD) ) // += - || ( p[0] == '-' && p[1] == '=' && (type = C_SUB) ) // -= - || ( p[0] == '^' && p[1] == '=' && (type = C_XOR) ) // ^= - || ( p[0] == '|' && p[1] == '=' && (type = C_OR ) ) // |= - || ( p[0] == '&' && p[1] == '=' && (type = C_AND) ) // &= - || ( p[0] == '*' && p[1] == '=' && (type = C_MUL) ) // *= - || ( p[0] == '/' && p[1] == '=' && (type = C_DIV) ) // /= - || ( p[0] == '%' && p[1] == '=' && (type = C_MOD) ) // %= - || ( p[0] == '~' && p[1] == '=' && (type = C_NOT) ) // ~= - || ( p[0] == '+' && p[1] == '+' && (type = C_ADD_PP) ) // ++ - || ( p[0] == '-' && p[1] == '-' && (type = C_SUB_PP) ) // -- - || ( p[0] == '<' && p[1] == '<' && p[2] == '=' && (type = C_L_SHIFT) ) // <<= - || ( p[0] == '>' && p[1] == '>' && p[2] == '=' && (type = C_R_SHIFT) ) // >>= - ) ) - {// failed to find a matching operator combination so invalid - return NULL; - } - - switch( type ) { - case C_EQ: {// incremental modifier - p = skip_space( &p[1] ); - } - break; - - case C_L_SHIFT: - case C_R_SHIFT: {// left or right shift modifier - p = skip_space( &p[3] ); - } - break; - - default: {// normal incremental command - p = skip_space( &p[2] ); - } - } - - if( p == NULL ) {// end of line or invalid buffer - return NULL; - } - - // push the set function onto the stack - add_scriptl(buildin_set_ref); - add_scriptc(C_ARG); - - // always append parenthesis to avoid errors - syntax.curly[syntax.curly_count].type = TYPE_ARGLIST; - syntax.curly[syntax.curly_count].count = 0; - syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN; - - // increment the total curly count for the position in the script - ++ syntax.curly_count; - - // parse the variable currently being modified - word = add_word(var); - - if( str_data[word].type == C_FUNC || str_data[word].type == C_USERFUNC || str_data[word].type == C_USERFUNC_POS ) - {// cannot assign a variable which exists as a function or label - disp_error_message("Cannot modify a variable which has the same name as a function or label.", p); - } - - if( p2 ) {// process the variable index - const char* p3 = NULL; - - // push the getelementofarray method into the stack - add_scriptl(buildin_getelementofarray_ref); - add_scriptc(C_ARG); - add_scriptl(word); - - // process the sub-expression for this assignment - p3 = parse_subexpr(p2 + 1, 1); - p3 = skip_space(p3); - - if( *p3 != ']' ) {// closing parenthesis is required for this script - disp_error_message("Missing closing ']' parenthesis for the variable assignment.", p3); - } - - // push the closing function stack operator onto the stack - add_scriptc(C_FUNC); - p3 ++; - } else {// simply push the variable or value onto the stack - add_scriptl(word); - } - - if( type != C_EQ ) - add_scriptc(C_REF); - - if( type == C_ADD_PP || type == C_SUB_PP ) {// incremental operator for the method - add_scripti(1); - add_scriptc(type == C_ADD_PP ? C_ADD : C_SUB); - } else {// process the value as an expression - p = parse_subexpr(p, -1); - - if( type != C_EQ ) - {// push the type of modifier onto the stack - add_scriptc(type); - } - } - - // decrement the curly count for the position within the script - -- syntax.curly_count; - - // close the script by appending the function operator - add_scriptc(C_FUNC); - - // push the buffer from the method - return p; -} - -/*========================================== - * Analysis section - *------------------------------------------*/ -const char* parse_simpleexpr(const char *p) -{ - int i; - p=skip_space(p); - - if(*p==';' || *p==',') - disp_error_message("parse_simpleexpr: unexpected expr end",p); - if(*p=='('){ - if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST ) - ++syntax.curly[i].count; - p=parse_subexpr(p+1,-1); - p=skip_space(p); - if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST && - syntax.curly[i].flag == ARGLIST_UNDEFINED && --syntax.curly[i].count == 0 - ){ - if( *p == ',' ){ - syntax.curly[i].flag = ARGLIST_PAREN; - return p; - } else - syntax.curly[i].flag = ARGLIST_NO_PAREN; - } - if( *p != ')' ) - disp_error_message("parse_simpleexpr: unmatch ')'",p); - ++p; - } else if(ISDIGIT(*p) || ((*p=='-' || *p=='+') && ISDIGIT(p[1]))){ - char *np; - while(*p == '0' && ISDIGIT(p[1])) p++; - i=strtoul(p,&np,0); - add_scripti(i); - p=np; - } else if(*p=='"'){ - add_scriptc(C_STR); - p++; - while( *p && *p != '"' ){ - if( (unsigned char)p[-1] <= 0x7e && *p == '\\' ) - { - char buf[8]; - size_t len = skip_escaped_c(p) - p; - size_t n = sv_unescape_c(buf, p, len); - if( n != 1 ) - ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf); - p += len; - add_scriptb(*buf); - continue; - } - else if( *p == '\n' ) - disp_error_message("parse_simpleexpr: unexpected newline @ string",p); - add_scriptb(*p++); - } - if(!*p) - disp_error_message("parse_simpleexpr: unexpected eof @ string",p); - add_scriptb(0); - p++; //'"' - } else { - int l; - const char* pv; - - // label , register , function etc - if(skip_word(p)==p) - disp_error_message("parse_simpleexpr: unexpected character",p); - - l=add_word(p); - if( str_data[l].type == C_FUNC || str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS) - return parse_callfunc(p,1,0); -#ifdef SCRIPT_CALLFUNC_CHECK - else { - const char* name = get_str(l); - if( strdb_get(userfunc_db,name) != NULL ) { - return parse_callfunc(p,1,1); - } - } -#endif - - if( (pv = parse_variable(p)) ) - {// successfully processed a variable assignment - return pv; - } - - p=skip_word(p); - if( *p == '[' ){ - // array(name[i] => getelementofarray(name,i) ) - add_scriptl(buildin_getelementofarray_ref); - add_scriptc(C_ARG); - add_scriptl(l); - - p=parse_subexpr(p+1,-1); - p=skip_space(p); - if( *p != ']' ) - disp_error_message("parse_simpleexpr: unmatch ']'",p); - ++p; - add_scriptc(C_FUNC); - }else - add_scriptl(l); - - } - - return p; -} - -/*========================================== - * Analysis of the expression - *------------------------------------------*/ -const char* parse_subexpr(const char* p,int limit) -{ - int op,opl,len; - const char* tmpp; - - p=skip_space(p); - - if( *p == '-' ){ - tmpp = skip_space(p+1); - if( *tmpp == ';' || *tmpp == ',' ){ - add_scriptl(LABEL_NEXTLINE); - p++; - return p; - } - } - - if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){ - p=parse_subexpr(p+1,10); - add_scriptc(op); - } else - p=parse_simpleexpr(p); - p=skip_space(p); - while(( - (op=C_OP3,opl=0,len=1,*p=='?') || - (op=C_ADD,opl=8,len=1,*p=='+') || - (op=C_SUB,opl=8,len=1,*p=='-') || - (op=C_MUL,opl=9,len=1,*p=='*') || - (op=C_DIV,opl=9,len=1,*p=='/') || - (op=C_MOD,opl=9,len=1,*p=='%') || - (op=C_LAND,opl=2,len=2,*p=='&' && p[1]=='&') || - (op=C_AND,opl=6,len=1,*p=='&') || - (op=C_LOR,opl=1,len=2,*p=='|' && p[1]=='|') || - (op=C_OR,opl=5,len=1,*p=='|') || - (op=C_XOR,opl=4,len=1,*p=='^') || - (op=C_EQ,opl=3,len=2,*p=='=' && p[1]=='=') || - (op=C_NE,opl=3,len=2,*p=='!' && p[1]=='=') || - (op=C_R_SHIFT,opl=7,len=2,*p=='>' && p[1]=='>') || - (op=C_GE,opl=3,len=2,*p=='>' && p[1]=='=') || - (op=C_GT,opl=3,len=1,*p=='>') || - (op=C_L_SHIFT,opl=7,len=2,*p=='<' && p[1]=='<') || - (op=C_LE,opl=3,len=2,*p=='<' && p[1]=='=') || - (op=C_LT,opl=3,len=1,*p=='<')) && opl>limit){ - p+=len; - if(op == C_OP3) { - p=parse_subexpr(p,-1); - p=skip_space(p); - if( *(p++) != ':') - disp_error_message("parse_subexpr: need ':'", p-1); - p=parse_subexpr(p,-1); - } else { - p=parse_subexpr(p,opl); - } - add_scriptc(op); - p=skip_space(p); - } - - return p; /* return first untreated operator */ -} - -/*========================================== - * Evaluation of the expression - *------------------------------------------*/ -const char* parse_expr(const char *p) -{ - switch(*p){ - case ')': case ';': case ':': case '[': case ']': - case '}': - disp_error_message("parse_expr: unexpected char",p); - } - p=parse_subexpr(p,-1); - return p; -} - -/*========================================== - * Analysis of the line - *------------------------------------------*/ -const char* parse_line(const char* p) -{ - const char* p2; - - p=skip_space(p); - if(*p==';') { - //Close decision for if(); for(); while(); - p = parse_syntax_close(p + 1); - return p; - } - if(*p==')' && parse_syntax_for_flag) - return p+1; - - p = skip_space(p); - if(p[0] == '{') { - syntax.curly[syntax.curly_count].type = TYPE_NULL; - syntax.curly[syntax.curly_count].count = -1; - syntax.curly[syntax.curly_count].index = -1; - syntax.curly_count++; - return p + 1; - } else if(p[0] == '}') { - return parse_curly_close(p); - } - - // Syntax-related processing - p2 = parse_syntax(p); - if(p2 != NULL) - return p2; - - // attempt to process a variable assignment - p2 = parse_variable(p); - - if( p2 != NULL ) - {// variable assignment processed so leave the method - return parse_syntax_close(p2 + 1); - } - - p = parse_callfunc(p,0,0); - p = skip_space(p); - - if(parse_syntax_for_flag) { - if( *p != ')' ) - disp_error_message("parse_line: need ')'",p); - } else { - if( *p != ';' ) - disp_error_message("parse_line: need ';'",p); - } - - //Binding decision for if(), for(), while() - p = parse_syntax_close(p+1); - - return p; -} - -// { ... } Closing process -const char* parse_curly_close(const char* p) -{ - if(syntax.curly_count <= 0) { - disp_error_message("parse_curly_close: unexpected string",p); - return p + 1; - } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) { - syntax.curly_count--; - //Close decision if, for , while - p = parse_syntax_close(p + 1); - return p; - } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) { - //Closing switch() - int pos = syntax.curly_count-1; - char label[256]; - int l; - // Remove temporary variables - sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // Go to the end pointer unconditionally - sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // You are here labeled - sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - set_label(l,script_pos, p); - - if(syntax.curly[pos].flag) { - //Exists default - sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - } - - // Label end - sprintf(label,"__SW%x_FIN",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos, p); - linkdb_final(&syntax.curly[pos].case_label); // free the list of case label - syntax.curly_count--; - //Closing decision if, for , while - p = parse_syntax_close(p + 1); - return p; - } else { - disp_error_message("parse_curly_close: unexpected string",p); - return p + 1; - } -} - -// Syntax-related processing -// break, case, continue, default, do, for, function, -// if, switch, while ? will handle this internally. -const char* parse_syntax(const char* p) -{ - const char *p2 = skip_word(p); - - switch(*p) { - case 'B': - case 'b': - if(p2 - p == 5 && !strncasecmp(p,"break",5)) { - // break Processing - char label[256]; - int pos = syntax.curly_count - 1; - while(pos >= 0) { - if(syntax.curly[pos].type == TYPE_DO) { - sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_FOR) { - sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_WHILE) { - sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_SWITCH) { - sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index); - break; - } - pos--; - } - if(pos < 0) { - disp_error_message("parse_syntax: unexpected 'break'",p); - } else { - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - } - p = skip_space(p2); - if(*p != ';') - disp_error_message("parse_syntax: need ';'",p); - // Closing decision if, for , while - p = parse_syntax_close(p + 1); - return p; - } - break; - case 'c': - case 'C': - if(p2 - p == 4 && !strncasecmp(p,"case",4)) { - //Processing case - int pos = syntax.curly_count-1; - if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) { - disp_error_message("parse_syntax: unexpected 'case' ",p); - return p+1; - } else { - char label[256]; - int l,v; - char *np; - if(syntax.curly[pos].count != 1) { - //Jump for FALLTHRU - sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // You are here labeled - sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - set_label(l,script_pos, p); - } - //Decision statement switch - p = skip_space(p2); - if(p == p2) { - disp_error_message("parse_syntax: expect space ' '",p); - } - // check whether case label is integer or not - v = strtol(p,&np,0); - if(np == p) { //Check for constants - p2 = skip_word(p); - v = p2-p; // length of word at p2 - memcpy(label,p,v); - label[v]='\0'; - if( !script_get_constant(label, &v) ) - disp_error_message("parse_syntax: 'case' label not integer",p); - p = skip_word(p); - } else { //Numeric value - if((*p == '-' || *p == '+') && ISDIGIT(p[1])) // pre-skip because '-' can not skip_word - p++; - p = skip_word(p); - if(np != p) - disp_error_message("parse_syntax: 'case' label not integer",np); - } - p = skip_space(p); - if(*p != ':') - disp_error_message("parse_syntax: expect ':'",p); - sprintf(label,"if(%d != $@__SW%x_VAL) goto __SW%x_%x;", - v,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - // Bad I do not parse twice - p2 = parse_line(label); - parse_line(p2); - syntax.curly_count--; - if(syntax.curly[pos].count != 1) { - // Label after the completion of FALLTHRU - sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - set_label(l,script_pos,p); - } - // check duplication of case label [Rayce] - if(linkdb_search(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v)) != NULL) - disp_error_message("parse_syntax: dup 'case'",p); - linkdb_insert(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v), (void*)1); - - sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - - parse_line(label); - syntax.curly_count--; - syntax.curly[pos].count++; - } - return p + 1; - } else if(p2 - p == 8 && !strncasecmp(p,"continue",8)) { - // Processing continue - char label[256]; - int pos = syntax.curly_count - 1; - while(pos >= 0) { - if(syntax.curly[pos].type == TYPE_DO) { - sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index); - syntax.curly[pos].flag = 1; //Flag put the link for continue - break; - } else if(syntax.curly[pos].type == TYPE_FOR) { - sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_WHILE) { - sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); - break; - } - pos--; - } - if(pos < 0) { - disp_error_message("parse_syntax: unexpected 'continue'",p); - } else { - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - } - p = skip_space(p2); - if(*p != ';') - disp_error_message("parse_syntax: need ';'",p); - //Closing decision if, for , while - p = parse_syntax_close(p + 1); - return p; - } - break; - case 'd': - case 'D': - if(p2 - p == 7 && !strncasecmp(p,"default",7)) { - // Switch - default processing - int pos = syntax.curly_count-1; - if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) { - disp_error_message("parse_syntax: unexpected 'default'",p); - } else if(syntax.curly[pos].flag) { - disp_error_message("parse_syntax: dup 'default'",p); - } else { - char label[256]; - int l; - // Put the label location - p = skip_space(p2); - if(*p != ':') { - disp_error_message("parse_syntax: need ':'",p); - } - sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - set_label(l,script_pos,p); - - // Skip to the next link w/o condition - sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // The default label - sprintf(label,"__SW%x_DEF",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - - syntax.curly[syntax.curly_count - 1].flag = 1; - syntax.curly[pos].count++; - } - return p + 1; - } else if(p2 - p == 2 && !strncasecmp(p,"do",2)) { - int l; - char label[256]; - p=skip_space(p2); - - syntax.curly[syntax.curly_count].type = TYPE_DO; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - // Label of the (do) form here - sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index); - l=add_str(label); - set_label(l,script_pos,p); - syntax.curly_count++; - return p; - } - break; - case 'f': - case 'F': - if(p2 - p == 3 && !strncasecmp(p,"for",3)) { - int l; - char label[256]; - int pos = syntax.curly_count; - syntax.curly[syntax.curly_count].type = TYPE_FOR; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - syntax.curly_count++; - - p=skip_space(p2); - - if(*p != '(') - disp_error_message("parse_syntax: need '('",p); - p++; - - // Execute the initialization statement - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - p=parse_line(p); - syntax.curly_count--; - - // Form the start of label decision - sprintf(label,"__FR%x_J",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - - p=skip_space(p); - if(*p == ';') { - // For (; Because the pattern of always true ;) - ; - } else { - // Skip to the end point if the condition is false - sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - } - if(*p != ';') - disp_error_message("parse_syntax: need ';'",p); - p++; - - // Skip to the beginning of the loop - sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // Labels to form the next loop - sprintf(label,"__FR%x_NXT",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - - // Process the next time you enter the loop - // A ')' last for; flag to be treated as' - parse_syntax_for_flag = 1; - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - p=parse_line(p); - syntax.curly_count--; - parse_syntax_for_flag = 0; - - // Skip to the determination process conditions - sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // Loop start labeling - sprintf(label,"__FR%x_BGN",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - return p; - } - else if( p2 - p == 8 && strncasecmp(p,"function",8) == 0 ) - {// internal script function - const char *func_name; - - func_name = skip_space(p2); - p = skip_word(func_name); - if( p == func_name ) - disp_error_message("parse_syntax:function: function name is missing or invalid", p); - p2 = skip_space(p); - if( *p2 == ';' ) - {// function <name> ; - // function declaration - just register the name - int l; - l = add_word(func_name); - if( str_data[l].type == C_NOP )// register only, if the name was not used by something else - str_data[l].type = C_USERFUNC; - else if( str_data[l].type == C_USERFUNC ) - ; // already registered - else - disp_error_message("parse_syntax:function: function name is invalid", func_name); - - // Close condition of if, for, while - p = parse_syntax_close(p2 + 1); - return p; - } - else if(*p2 == '{') - {// function <name> <line/block of code> - char label[256]; - int l; - - syntax.curly[syntax.curly_count].type = TYPE_USERFUNC; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - ++syntax.curly_count; - - // Jump over the function code - sprintf(label, "goto __FN%x_FIN;", syntax.curly[syntax.curly_count-1].index); - syntax.curly[syntax.curly_count].type = TYPE_NULL; - ++syntax.curly_count; - parse_line(label); - --syntax.curly_count; - - // Set the position of the function (label) - l=add_word(func_name); - if( str_data[l].type == C_NOP || str_data[l].type == C_USERFUNC )// register only, if the name was not used by something else - { - str_data[l].type = C_USERFUNC; - set_label(l, script_pos, p); - if( parse_options&SCRIPT_USE_LABEL_DB ) - strdb_iput(scriptlabel_db, get_str(l), script_pos); - } - else - disp_error_message("parse_syntax:function: function name is invalid", func_name); - - return skip_space(p); - } - else - { - disp_error_message("expect ';' or '{' at function syntax",p); - } - } - break; - case 'i': - case 'I': - if(p2 - p == 2 && !strncasecmp(p,"if",2)) { - // If process - char label[256]; - p=skip_space(p2); - if(*p != '(') { //Prevent if this {} non-c syntax. from Rayce (jA) - disp_error_message("need '('",p); - } - syntax.curly[syntax.curly_count].type = TYPE_IF; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count); - syntax.curly_count++; - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - return p; - } - break; - case 's': - case 'S': - if(p2 - p == 6 && !strncasecmp(p,"switch",6)) { - // Processing of switch () - char label[256]; - p=skip_space(p2); - if(*p != '(') { - disp_error_message("need '('",p); - } - syntax.curly[syntax.curly_count].type = TYPE_SWITCH; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index); - syntax.curly_count++; - add_scriptl(add_str("set")); - add_scriptc(C_ARG); - add_scriptl(add_str(label)); - p=parse_expr(p); - p=skip_space(p); - if(*p != '{') { - disp_error_message("parse_syntax: need '{'",p); - } - add_scriptc(C_FUNC); - return p + 1; - } - break; - case 'w': - case 'W': - if(p2 - p == 5 && !strncasecmp(p,"while",5)) { - int l; - char label[256]; - p=skip_space(p2); - if(*p != '(') { - disp_error_message("need '('",p); - } - syntax.curly[syntax.curly_count].type = TYPE_WHILE; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - // Form the start of label decision - sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index); - l=add_str(label); - set_label(l,script_pos,p); - - // Skip to the end point if the condition is false - sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index); - syntax.curly_count++; - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - return p; - } - break; - } - return NULL; -} - -const char* parse_syntax_close(const char *p) { - // If (...) for (...) hoge (); as to make sure closed closed once again - int flag; - - do { - p = parse_syntax_close_sub(p,&flag); - } while(flag); - return p; -} - -// Close judgment if, for, while, of do -// flag == 1 : closed -// flag == 0 : not closed -const char* parse_syntax_close_sub(const char* p,int* flag) -{ - char label[256]; - int pos = syntax.curly_count - 1; - int l; - *flag = 1; - - if(syntax.curly_count <= 0) { - *flag = 0; - return p; - } else if(syntax.curly[pos].type == TYPE_IF) { - const char *bp = p; - const char *p2; - - // if-block and else-block end is a new line - parse_nextline(false, p); - - // Skip to the last location if - sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // Put the label of the location - sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - set_label(l,script_pos,p); - - syntax.curly[pos].count++; - p = skip_space(p); - p2 = skip_word(p); - if(!syntax.curly[pos].flag && p2 - p == 4 && !strncasecmp(p,"else",4)) { - // else or else - if - p = skip_space(p2); - p2 = skip_word(p); - if(p2 - p == 2 && !strncasecmp(p,"if",2)) { - // else - if - p=skip_space(p2); - if(*p != '(') { - disp_error_message("need '('",p); - } - sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - *flag = 0; - return p; - } else { - // else - if(!syntax.curly[pos].flag) { - syntax.curly[pos].flag = 1; - *flag = 0; - return p; - } - } - } - // Close if - syntax.curly_count--; - // Put the label of the final location - sprintf(label,"__IF%x_FIN",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - if(syntax.curly[pos].flag == 1) { - // Because the position of the pointer is the same if not else for this - return bp; - } - return p; - } else if(syntax.curly[pos].type == TYPE_DO) { - int l; - char label[256]; - const char *p2; - - if(syntax.curly[pos].flag) { - // (Come here continue) to form the label here - sprintf(label,"__DO%x_NXT",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - } - - // Skip to the end point if the condition is false - p = skip_space(p); - p2 = skip_word(p); - if(p2 - p != 5 || strncasecmp(p,"while",5)) - disp_error_message("parse_syntax: need 'while'",p); - - p = skip_space(p2); - if(*p != '(') { - disp_error_message("need '('",p); - } - - // do-block end is a new line - parse_nextline(false, p); - - sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - - // Skip to the starting point - sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // Form label of the end point conditions - sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - p = skip_space(p); - if(*p != ';') { - disp_error_message("parse_syntax: need ';'",p); - return p+1; - } - p++; - syntax.curly_count--; - return p; - } else if(syntax.curly[pos].type == TYPE_FOR) { - // for-block end is a new line - parse_nextline(false, p); - - // Skip to the next loop - sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // End for labeling - sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - syntax.curly_count--; - return p; - } else if(syntax.curly[pos].type == TYPE_WHILE) { - // while-block end is a new line - parse_nextline(false, p); - - // Skip to the decision while - sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // End while labeling - sprintf(label,"__WL%x_FIN",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - syntax.curly_count--; - return p; - } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) { - int pos = syntax.curly_count-1; - char label[256]; - int l; - // Back - sprintf(label,"return;"); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // Put the label of the location - sprintf(label,"__FN%x_FIN",syntax.curly[pos].index); - l=add_str(label); - set_label(l,script_pos,p); - syntax.curly_count--; - return p; - } else { - *flag = 0; - return p; - } -} - -/*========================================== - * Added built-in functions - *------------------------------------------*/ -static void add_buildin_func(void) -{ - int i,n; - const char* p; - for( i = 0; buildin_func[i].func; i++ ) - { - // arg must follow the pattern: (v|s|i|r|l)*\?*\*? - // 'v' - value (either string or int or reference) - // 's' - string - // 'i' - int - // 'r' - reference (of a variable) - // 'l' - label - // '?' - one optional parameter - // '*' - unknown number of optional parameters - p = buildin_func[i].arg; - while( *p == 'v' || *p == 's' || *p == 'i' || *p == 'r' || *p == 'l' ) ++p; - while( *p == '?' ) ++p; - if( *p == '*' ) ++p; - if( *p != 0){ - ShowWarning("add_buildin_func: ignoring function \"%s\" with invalid arg \"%s\".\n", buildin_func[i].name, buildin_func[i].arg); - } else if( *skip_word(buildin_func[i].name) != 0 ){ - ShowWarning("add_buildin_func: ignoring function with invalid name \"%s\" (must be a word).\n", buildin_func[i].name); - } else { - n = add_str(buildin_func[i].name); - str_data[n].type = C_FUNC; - str_data[n].val = i; - str_data[n].func = buildin_func[i].func; - - if (!strcmp(buildin_func[i].name, "set")) buildin_set_ref = n; - else if (!strcmp(buildin_func[i].name, "callsub")) buildin_callsub_ref = n; - else if (!strcmp(buildin_func[i].name, "callfunc")) buildin_callfunc_ref = n; - else if( !strcmp(buildin_func[i].name, "getelementofarray") ) buildin_getelementofarray_ref = n; - } - } -} - -/// Retrieves the value of a constant. -bool script_get_constant(const char* name, int* value) -{ - int n = search_str(name); - - if( n == -1 || str_data[n].type != C_INT ) - {// not found or not a constant - return false; - } - value[0] = str_data[n].val; - - return true; -} - -/// Creates new constant or parameter with given value. -void script_set_constant(const char* name, int value, bool isparameter) -{ - int n = add_str(name); - - if( str_data[n].type == C_NOP ) - {// new - str_data[n].type = isparameter ? C_PARAM : C_INT; - str_data[n].val = value; - } - else if( str_data[n].type == C_PARAM || str_data[n].type == C_INT ) - {// existing parameter or constant - ShowError("script_set_constant: Attempted to overwrite existing %s '%s' (old value=%d, new value=%d).\n", ( str_data[n].type == C_PARAM ) ? "parameter" : "constant", name, str_data[n].val, value); - } - else - {// existing name - ShowError("script_set_constant: Invalid name for %s '%s' (already defined as %s).\n", isparameter ? "parameter" : "constant", name, script_op2name(str_data[n].type)); - } -} - -/*========================================== - * Reading constant databases - * const.txt - *------------------------------------------*/ -static void read_constdb(void) -{ - FILE *fp; - char line[1024],name[1024],val[1024]; - int type; - - sprintf(line, "%s/const.txt", db_path); - fp=fopen(line, "r"); - if(fp==NULL){ - ShowError("can't read %s\n", line); - return ; - } - while(fgets(line, sizeof(line), fp)) - { - if(line[0]=='/' && line[1]=='/') - continue; - type=0; - if(sscanf(line,"%[A-Za-z0-9_],%[-0-9xXA-Fa-f],%d",name,val,&type)>=2 || - sscanf(line,"%[A-Za-z0-9_] %[-0-9xXA-Fa-f] %d",name,val,&type)>=2){ - script_set_constant(name, (int)strtol(val, NULL, 0), (bool)type); - } - } - fclose(fp); -} - -/*========================================== - * Display emplacement line of script - *------------------------------------------*/ -static const char* script_print_line(StringBuf* buf, const char* p, const char* mark, int line) -{ - int i; - if( p == NULL || !p[0] ) return NULL; - if( line < 0 ) - StringBuf_Printf(buf, "*% 5d : ", -line); - else - StringBuf_Printf(buf, " % 5d : ", line); - for(i=0;p[i] && p[i] != '\n';i++){ - if(p + i != mark) - StringBuf_Printf(buf, "%c", p[i]); - else - StringBuf_Printf(buf, "\'%c\'", p[i]); - } - StringBuf_AppendStr(buf, "\n"); - return p+i+(p[i] == '\n' ? 1 : 0); -} - -void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos) -{ - // Find the line where the error occurred - int j; - int line = start_line; - const char *p; - const char *linestart[5] = { NULL, NULL, NULL, NULL, NULL }; - StringBuf buf; - - for(p=src;p && *p;line++){ - const char *lineend=strchr(p,'\n'); - if(lineend==NULL || error_pos<lineend){ - break; - } - for( j = 0; j < 4; j++ ) { - linestart[j] = linestart[j+1]; - } - linestart[4] = p; - p=lineend+1; - } - - StringBuf_Init(&buf); - StringBuf_AppendStr(&buf, "\a\n"); - StringBuf_Printf(&buf, "script error on %s line %d\n", file, line); - StringBuf_Printf(&buf, " %s\n", error_msg); - for(j = 0; j < 5; j++ ) { - script_print_line(&buf, linestart[j], NULL, line + j - 5); - } - p = script_print_line(&buf, p, error_pos, -line); - for(j = 0; j < 5; j++) { - p = script_print_line(&buf, p, NULL, line + j + 1 ); - } - ShowError("%s", StringBuf_Value(&buf)); - StringBuf_Destroy(&buf); -} - -/*========================================== - * Analysis of the script - *------------------------------------------*/ -struct script_code* parse_script(const char *src,const char *file,int line,int options) -{ - const char *p,*tmpp; - int i; - struct script_code* code = NULL; - static int first=1; - char end; - bool unresolved_names = false; - - if( src == NULL ) - return NULL;// empty script - - memset(&syntax,0,sizeof(syntax)); - if(first){ - add_buildin_func(); - read_constdb(); - first=0; - } - - script_buf=(unsigned char *)aMalloc(SCRIPT_BLOCK_SIZE*sizeof(unsigned char)); - script_pos=0; - script_size=SCRIPT_BLOCK_SIZE; - parse_nextline(true, NULL); - - // who called parse_script is responsible for clearing the database after using it, but just in case... lets clear it here - if( options&SCRIPT_USE_LABEL_DB ) - db_clear(scriptlabel_db); - parse_options = options; - - if( setjmp( error_jump ) != 0 ) { - //Restore program state when script has problems. [from jA] - int i; - const int size = ARRAYLENGTH(syntax.curly); - if( error_report ) - script_error(src,file,line,error_msg,error_pos); - aFree( error_msg ); - aFree( script_buf ); - script_pos = 0; - script_size = 0; - script_buf = NULL; - for(i=LABEL_START;i<str_num;i++) - if(str_data[i].type == C_NOP) str_data[i].type = C_NAME; - for(i=0; i<size; i++) - linkdb_final(&syntax.curly[i].case_label); - return NULL; - } - - parse_syntax_for_flag=0; - p=src; - p=skip_space(p); - if( options&SCRIPT_IGNORE_EXTERNAL_BRACKETS ) - {// does not require brackets around the script - if( *p == '\0' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) ) - {// empty script and can return NULL - aFree( script_buf ); - script_pos = 0; - script_size = 0; - script_buf = NULL; - return NULL; - } - end = '\0'; - } - else - {// requires brackets around the script - if( *p != '{' ) - disp_error_message("not found '{'",p); - p = skip_space(p+1); - if( *p == '}' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) ) - {// empty script and can return NULL - aFree( script_buf ); - script_pos = 0; - script_size = 0; - script_buf = NULL; - return NULL; - } - end = '}'; - } - - // clear references of labels, variables and internal functions - for(i=LABEL_START;i<str_num;i++){ - if( - str_data[i].type==C_POS || str_data[i].type==C_NAME || - str_data[i].type==C_USERFUNC || str_data[i].type == C_USERFUNC_POS - ){ - str_data[i].type=C_NOP; - str_data[i].backpatch=-1; - str_data[i].label=-1; - } - } - - while( syntax.curly_count != 0 || *p != end ) - { - if( *p == '\0' ) - disp_error_message("unexpected end of script",p); - // Special handling only label - tmpp=skip_space(skip_word(p)); - if(*tmpp==':' && !(!strncasecmp(p,"default:",8) && p + 7 == tmpp)){ - i=add_word(p); - set_label(i,script_pos,p); - if( parse_options&SCRIPT_USE_LABEL_DB ) - strdb_iput(scriptlabel_db, get_str(i), script_pos); - p=tmpp+1; - p=skip_space(p); - continue; - } - - // All other lumped - p=parse_line(p); - p=skip_space(p); - - parse_nextline(false, p); - } - - add_scriptc(C_NOP); - - // trim code to size - script_size = script_pos; - RECREATE(script_buf,unsigned char,script_pos); - - // default unknown references to variables - for(i=LABEL_START;i<str_num;i++){ - if(str_data[i].type==C_NOP){ - int j,next; - str_data[i].type=C_NAME; - str_data[i].label=i; - for(j=str_data[i].backpatch;j>=0 && j!=0x00ffffff;){ - next=GETVALUE(script_buf,j); - SETVALUE(script_buf,j,i); - j=next; - } - } - else if( str_data[i].type == C_USERFUNC ) - {// 'function name;' without follow-up code - ShowError("parse_script: function '%s' declared but not defined.\n", str_buf+str_data[i].str); - unresolved_names = true; - } - } - - if( unresolved_names ) - { - disp_error_message("parse_script: unresolved function references", p); - } - -#ifdef DEBUG_DISP - for(i=0;i<script_pos;i++){ - if((i&15)==0) ShowMessage("%04x : ",i); - ShowMessage("%02x ",script_buf[i]); - if((i&15)==15) ShowMessage("\n"); - } - ShowMessage("\n"); -#endif -#ifdef DEBUG_DISASM - { - int i = 0,j; - while(i < script_pos) { - c_op op = get_com(script_buf,&i); - - ShowMessage("%06x %s", i, script_op2name(op)); - j = i; - switch(op) { - case C_INT: - ShowMessage(" %d", get_num(script_buf,&i)); - break; - case C_POS: - ShowMessage(" 0x%06x", *(int*)(script_buf+i)&0xffffff); - i += 3; - break; - case C_NAME: - j = (*(int*)(script_buf+i)&0xffffff); - ShowMessage(" %s", ( j == 0xffffff ) ? "?? unknown ??" : get_str(j)); - i += 3; - break; - case C_STR: - j = strlen(script_buf + i); - ShowMessage(" %s", script_buf + i); - i += j+1; - break; - } - ShowMessage(CL_CLL"\n"); - } - } -#endif - - CREATE(code,struct script_code,1); - code->script_buf = script_buf; - code->script_size = script_size; - code->script_vars = idb_alloc(DB_OPT_RELEASE_DATA); - return code; -} - -/// Returns the player attached to this script, identified by the rid. -/// If there is no player attached, the script is terminated. -TBL_PC *script_rid2sd(struct script_state *st) -{ - TBL_PC *sd=map_id2sd(st->rid); - if(!sd){ - ShowError("script_rid2sd: fatal error ! player not attached!\n"); - script_reportfunc(st); - script_reportsrc(st); - st->state = END; - } - return sd; -} - -/// Dereferences a variable/constant, replacing it with a copy of the value. -/// -/// @param st Script state -/// @param data Variable/constant -void get_val(struct script_state* st, struct script_data* data) -{ - const char* name; - char prefix; - char postfix; - TBL_PC* sd = NULL; - - if( !data_isreference(data) ) - return;// not a variable/constant - - name = reference_getname(data); - prefix = name[0]; - postfix = name[strlen(name) - 1]; - - //##TODO use reference_tovariable(data) when it's confirmed that it works [FlavioJS] - if( !reference_toconstant(data) && not_server_variable(prefix) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - {// needs player attached - if( postfix == '$' ) - {// string variable - ShowWarning("script:get_val: cannot access player variable '%s', defaulting to \"\"\n", name); - data->type = C_CONSTSTR; - data->u.str = ""; - } - else - {// integer variable - ShowWarning("script:get_val: cannot access player variable '%s', defaulting to 0\n", name); - data->type = C_INT; - data->u.num = 0; - } - return; - } - } - - if( postfix == '$' ) - {// string variable - - switch( prefix ) - { - case '@': - data->u.str = pc_readregstr(sd, data->u.num); - break; - case '$': - data->u.str = mapreg_readregstr(data->u.num); - break; - case '#': - if( name[1] == '#' ) - data->u.str = pc_readaccountreg2str(sd, name);// global - else - data->u.str = pc_readaccountregstr(sd, name);// local - break; - case '.': - { - struct DBMap* n = - data->ref ? *data->ref: - name[1] == '@' ? st->stack->var_function:// instance/scope variable - st->script->script_vars;// npc variable - if( n ) - data->u.str = (char*)idb_get(n,reference_getuid(data)); - else - data->u.str = NULL; - } - break; - case '\'': - if (st->instance_id) { - data->u.str = (char*)idb_get(instance[st->instance_id].vars,reference_getuid(data)); - } else { - ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to \"\"\n", name); - data->u.str = NULL; - } - break; - default: - data->u.str = pc_readglobalreg_str(sd, name); - break; - } - - if( data->u.str == NULL || data->u.str[0] == '\0' ) - {// empty string - data->type = C_CONSTSTR; - data->u.str = ""; - } - else - {// duplicate string - data->type = C_STR; - data->u.str = aStrdup(data->u.str); - } - - } - else - {// integer variable - - data->type = C_INT; - - if( reference_toconstant(data) ) - { - data->u.num = reference_getconstant(data); - } - else if( reference_toparam(data) ) - { - data->u.num = pc_readparam(sd, reference_getparamtype(data)); - } - else - switch( prefix ) - { - case '@': - data->u.num = pc_readreg(sd, data->u.num); - break; - case '$': - data->u.num = mapreg_readreg(data->u.num); - break; - case '#': - if( name[1] == '#' ) - data->u.num = pc_readaccountreg2(sd, name);// global - else - data->u.num = pc_readaccountreg(sd, name);// local - break; - case '.': - { - struct DBMap* n = - data->ref ? *data->ref: - name[1] == '@' ? st->stack->var_function:// instance/scope variable - st->script->script_vars;// npc variable - if( n ) - data->u.num = (int)idb_iget(n,reference_getuid(data)); - else - data->u.num = 0; - } - break; - case '\'': - if( st->instance_id ) - data->u.num = (int)idb_iget(instance[st->instance_id].vars,reference_getuid(data)); - else { - ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to 0\n", name); - data->u.num = 0; - } - break; - default: - data->u.num = pc_readglobalreg(sd, name); - break; - } - - } - - return; -} - -struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref); - -/// Retrieves the value of a reference identified by uid (variable, constant, param) -/// The value is left in the top of the stack and needs to be removed manually. -void* get_val2(struct script_state* st, int uid, struct DBMap** ref) -{ - struct script_data* data; - push_val2(st->stack, C_NAME, uid, ref); - data = script_getdatatop(st, -1); - get_val(st, data); - return (data->type == C_INT ? (void*)__64BPRTSIZE(data->u.num) : (void*)__64BPRTSIZE(data->u.str)); -} - -/*========================================== - * Stores the value of a script variable - * Return value is 0 on fail, 1 on success. - *------------------------------------------*/ -static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* name, const void* value, struct DBMap** ref) -{ - char prefix = name[0]; - - if( is_string_variable(name) ) - {// string variable - const char* str = (const char*)value; - switch (prefix) { - case '@': - return pc_setregstr(sd, num, str); - case '$': - return mapreg_setregstr(num, str); - case '#': - return (name[1] == '#') ? - pc_setaccountreg2str(sd, name, str) : - pc_setaccountregstr(sd, name, str); - case '.': - { - struct DBMap* n; - n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars; - if( n ) { - idb_remove(n, num); - if (str[0]) idb_put(n, num, aStrdup(str)); - } - } - return 1; - case '\'': - if( st->instance_id ) { - idb_remove(instance[st->instance_id].vars, num); - if( str[0] ) idb_put(instance[st->instance_id].vars, num, aStrdup(str)); - } - return 1; - default: - return pc_setglobalreg_str(sd, name, str); - } - } - else - {// integer variable - int val = (int)__64BPRTSIZE(value); - if(str_data[num&0x00ffffff].type == C_PARAM) - { - if( pc_setparam(sd, str_data[num&0x00ffffff].val, val) == 0 ) - { - if( st != NULL ) - { - ShowError("script:set_reg: failed to set param '%s' to %d.\n", name, val); - script_reportsrc(st); - st->state = END; - } - return 0; - } - return 1; - } - - switch (prefix) { - case '@': - return pc_setreg(sd, num, val); - case '$': - return mapreg_setreg(num, val); - case '#': - return (name[1] == '#') ? - pc_setaccountreg2(sd, name, val) : - pc_setaccountreg(sd, name, val); - case '.': - { - struct DBMap* n; - n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars; - if( n ) { - idb_remove(n, num); - if( val != 0 ) - idb_iput(n, num, val); - } - } - return 1; - case '\'': - if( st->instance_id ) { - idb_remove(instance[st->instance_id].vars, num); - if( val != 0 ) - idb_iput(instance[st->instance_id].vars, num, val); - } - return 1; - default: - return pc_setglobalreg(sd, name, val); - } - } -} - -int set_var(TBL_PC* sd, char* name, void* val) -{ - return set_reg(NULL, sd, reference_uid(add_str(name),0), name, val, NULL); -} - -void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct DBMap **ref) -{ - set_reg(st, sd, reference_uid(add_str(varname),elem), varname, value, ref); -} - -/// Converts the data to a string -const char* conv_str(struct script_state* st, struct script_data* data) -{ - char* p; - - get_val(st, data); - if( data_isstring(data) ) - {// nothing to convert - } - else if( data_isint(data) ) - {// int -> string - CREATE(p, char, ITEM_NAME_LENGTH); - snprintf(p, ITEM_NAME_LENGTH, "%d", data->u.num); - p[ITEM_NAME_LENGTH-1] = '\0'; - data->type = C_STR; - data->u.str = p; - } - else if( data_isreference(data) ) - {// reference -> string - //##TODO when does this happen (check get_val) [FlavioJS] - data->type = C_CONSTSTR; - data->u.str = reference_getname(data); - } - else - {// unsupported data type - ShowError("script:conv_str: cannot convert to string, defaulting to \"\"\n"); - script_reportdata(data); - script_reportsrc(st); - data->type = C_CONSTSTR; - data->u.str = ""; - } - return data->u.str; -} - -/// Converts the data to an int -int conv_num(struct script_state* st, struct script_data* data) -{ - char* p; - long num; - - get_val(st, data); - if( data_isint(data) ) - {// nothing to convert - } - else if( data_isstring(data) ) - {// string -> int - // the result does not overflow or underflow, it is capped instead - // ex: 999999999999 is capped to INT_MAX (2147483647) - p = data->u.str; - errno = 0; - num = strtol(data->u.str, NULL, 10);// change radix to 0 to support octal numbers "o377" and hex numbers "0xFF" - if( errno == ERANGE -#if LONG_MAX > INT_MAX - || num < INT_MIN || num > INT_MAX -#endif - ) - { - if( num <= INT_MIN ) - { - num = INT_MIN; - ShowError("script:conv_num: underflow detected, capping to %ld\n", num); - } - else//if( num >= INT_MAX ) - { - num = INT_MAX; - ShowError("script:conv_num: overflow detected, capping to %ld\n", num); - } - script_reportdata(data); - script_reportsrc(st); - } - if( data->type == C_STR ) - aFree(p); - data->type = C_INT; - data->u.num = (int)num; - } -#if 0 - // FIXME this function is being used to retrieve the position of labels and - // probably other stuff [FlavioJS] - else - {// unsupported data type - ShowError("script:conv_num: cannot convert to number, defaulting to 0\n"); - script_reportdata(data); - script_reportsrc(st); - data->type = C_INT; - data->u.num = 0; - } -#endif - return data->u.num; -} - -// -// Stack operations -// - -/// Increases the size of the stack -void stack_expand(struct script_stack* stack) -{ - stack->sp_max += 64; - stack->stack_data = (struct script_data*)aRealloc(stack->stack_data, - stack->sp_max * sizeof(stack->stack_data[0]) ); - memset(stack->stack_data + (stack->sp_max - 64), 0, - 64 * sizeof(stack->stack_data[0]) ); -} - -/// Pushes a value into the stack -#define push_val(stack,type,val) push_val2(stack, type, val, NULL) - -/// Pushes a value into the stack (with reference) -struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref) -{ - if( stack->sp >= stack->sp_max ) - stack_expand(stack); - stack->stack_data[stack->sp].type = type; - stack->stack_data[stack->sp].u.num = val; - stack->stack_data[stack->sp].ref = ref; - stack->sp++; - return &stack->stack_data[stack->sp-1]; -} - -/// Pushes a string into the stack -struct script_data* push_str(struct script_stack* stack, enum c_op type, char* str) -{ - if( stack->sp >= stack->sp_max ) - stack_expand(stack); - stack->stack_data[stack->sp].type = type; - stack->stack_data[stack->sp].u.str = str; - stack->stack_data[stack->sp].ref = NULL; - stack->sp++; - return &stack->stack_data[stack->sp-1]; -} - -/// Pushes a retinfo into the stack -struct script_data* push_retinfo(struct script_stack* stack, struct script_retinfo* ri, DBMap **ref) -{ - if( stack->sp >= stack->sp_max ) - stack_expand(stack); - stack->stack_data[stack->sp].type = C_RETINFO; - stack->stack_data[stack->sp].u.ri = ri; - stack->stack_data[stack->sp].ref = ref; - stack->sp++; - return &stack->stack_data[stack->sp-1]; -} - -/// Pushes a copy of the target position into the stack -struct script_data* push_copy(struct script_stack* stack, int pos) -{ - switch( stack->stack_data[pos].type ) - { - case C_CONSTSTR: - return push_str(stack, C_CONSTSTR, stack->stack_data[pos].u.str); - break; - case C_STR: - return push_str(stack, C_STR, aStrdup(stack->stack_data[pos].u.str)); - break; - case C_RETINFO: - ShowFatalError("script:push_copy: can't create copies of C_RETINFO. Exiting...\n"); - exit(1); - break; - default: - return push_val2( - stack,stack->stack_data[pos].type, - stack->stack_data[pos].u.num, - stack->stack_data[pos].ref - ); - break; - } -} - -/// Removes the values in indexes [start,end[ from the stack. -/// Adjusts all stack pointers. -void pop_stack(struct script_state* st, int start, int end) -{ - struct script_stack* stack = st->stack; - struct script_data* data; - int i; - - if( start < 0 ) - start = 0; - if( end > stack->sp ) - end = stack->sp; - if( start >= end ) - return;// nothing to pop - - // free stack elements - for( i = start; i < end; i++ ) - { - data = &stack->stack_data[i]; - if( data->type == C_STR ) - aFree(data->u.str); - if( data->type == C_RETINFO ) - { - struct script_retinfo* ri = data->u.ri; - if( ri->var_function ) - script_free_vars(ri->var_function); - if( data->ref ) - aFree(data->ref); - aFree(ri); - } - data->type = C_NOP; - } - // move the rest of the elements - if( stack->sp > end ) - { - memmove(&stack->stack_data[start], &stack->stack_data[end], sizeof(stack->stack_data[0])*(stack->sp - end)); - for( i = start + stack->sp - end; i < stack->sp; ++i ) - stack->stack_data[i].type = C_NOP; - } - // adjust stack pointers - if( st->start > end ) st->start -= end - start; - else if( st->start > start ) st->start = start; - if( st->end > end ) st->end -= end - start; - else if( st->end > start ) st->end = start; - if( stack->defsp > end ) stack->defsp -= end - start; - else if( stack->defsp > start ) stack->defsp = start; - stack->sp -= end - start; -} - -/// -/// -/// - -/*========================================== - * Release script dependent variable, dependent variable of function - *------------------------------------------*/ -void script_free_vars(struct DBMap* storage) -{ - if( storage ) - {// destroy the storage construct containing the variables - db_destroy(storage); - } -} - -void script_free_code(struct script_code* code) -{ - script_free_vars( code->script_vars ); - aFree( code->script_buf ); - aFree( code ); -} - -/// Creates a new script state. -/// -/// @param script Script code -/// @param pos Position in the code -/// @param rid Who is running the script (attached player) -/// @param oid Where the code is being run (npc 'object') -/// @return Script state -struct script_state* script_alloc_state(struct script_code* script, int pos, int rid, int oid) -{ - struct script_state* st; - CREATE(st, struct script_state, 1); - st->stack = (struct script_stack*)aMalloc(sizeof(struct script_stack)); - st->stack->sp = 0; - st->stack->sp_max = 64; - CREATE(st->stack->stack_data, struct script_data, st->stack->sp_max); - st->stack->defsp = st->stack->sp; - st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA); - st->state = RUN; - st->script = script; - //st->scriptroot = script; - st->pos = pos; - st->rid = rid; - st->oid = oid; - st->sleep.timer = INVALID_TIMER; - return st; -} - -/// Frees a script state. -/// -/// @param st Script state -void script_free_state(struct script_state* st) -{ - if(st->bk_st) - {// backup was not restored - ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid); - } - if( st->sleep.timer != INVALID_TIMER ) - delete_timer(st->sleep.timer, run_script_timer); - script_free_vars(st->stack->var_function); - pop_stack(st, 0, st->stack->sp); - aFree(st->stack->stack_data); - aFree(st->stack); - st->stack = NULL; - st->pos = -1; - aFree(st); -} - -// -// Main execution unit -// -/*========================================== - * Read command - *------------------------------------------*/ -c_op get_com(unsigned char *script,int *pos) -{ - int i = 0, j = 0; - - if(script[*pos]>=0x80){ - return C_INT; - } - while(script[*pos]>=0x40){ - i=script[(*pos)++]<<j; - j+=6; - } - return (c_op)(i+(script[(*pos)++]<<j)); -} - -/*========================================== - * Income figures - *------------------------------------------*/ -int get_num(unsigned char *script,int *pos) -{ - int i,j; - i=0; j=0; - while(script[*pos]>=0xc0){ - i+=(script[(*pos)++]&0x7f)<<j; - j+=6; - } - return i+((script[(*pos)++]&0x7f)<<j); -} - -/*========================================== - * Remove the value from the stack - *------------------------------------------*/ -int pop_val(struct script_state* st) -{ - if(st->stack->sp<=0) - return 0; - st->stack->sp--; - get_val(st,&(st->stack->stack_data[st->stack->sp])); - if(st->stack->stack_data[st->stack->sp].type==C_INT) - return st->stack->stack_data[st->stack->sp].u.num; - return 0; -} - -/// Ternary operators -/// test ? if_true : if_false -void op_3(struct script_state* st, int op) -{ - struct script_data* data; - int flag = 0; - - data = script_getdatatop(st, -3); - get_val(st, data); - - if( data_isstring(data) ) - flag = data->u.str[0];// "" -> false - else if( data_isint(data) ) - flag = data->u.num;// 0 -> false - else - { - ShowError("script:op_3: invalid data for the ternary operator test\n"); - script_reportdata(data); - script_reportsrc(st); - script_removetop(st, -3, 0); - script_pushnil(st); - return; - } - if( flag ) - script_pushcopytop(st, -2); - else - script_pushcopytop(st, -1); - script_removetop(st, -4, -1); -} - -/// Binary string operators -/// s1 EQ s2 -> i -/// s1 NE s2 -> i -/// s1 GT s2 -> i -/// s1 GE s2 -> i -/// s1 LT s2 -> i -/// s1 LE s2 -> i -/// s1 ADD s2 -> s -void op_2str(struct script_state* st, int op, const char* s1, const char* s2) -{ - int a = 0; - - switch(op){ - case C_EQ: a = (strcmp(s1,s2) == 0); break; - case C_NE: a = (strcmp(s1,s2) != 0); break; - case C_GT: a = (strcmp(s1,s2) > 0); break; - case C_GE: a = (strcmp(s1,s2) >= 0); break; - case C_LT: a = (strcmp(s1,s2) < 0); break; - case C_LE: a = (strcmp(s1,s2) <= 0); break; - case C_ADD: - { - char* buf = (char *)aMalloc((strlen(s1)+strlen(s2)+1)*sizeof(char)); - strcpy(buf, s1); - strcat(buf, s2); - script_pushstr(st, buf); - return; - } - default: - ShowError("script:op2_str: unexpected string operator %s\n", script_op2name(op)); - script_reportsrc(st); - script_pushnil(st); - st->state = END; - return; - } - - script_pushint(st,a); -} - -/// Binary number operators -/// i OP i -> i -void op_2num(struct script_state* st, int op, int i1, int i2) -{ - int ret; - double ret_double; - - switch( op ) - { - case C_AND: ret = i1 & i2; break; - case C_OR: ret = i1 | i2; break; - case C_XOR: ret = i1 ^ i2; break; - case C_LAND: ret = (i1 && i2); break; - case C_LOR: ret = (i1 || i2); break; - case C_EQ: ret = (i1 == i2); break; - case C_NE: ret = (i1 != i2); break; - case C_GT: ret = (i1 > i2); break; - case C_GE: ret = (i1 >= i2); break; - case C_LT: ret = (i1 < i2); break; - case C_LE: ret = (i1 <= i2); break; - case C_R_SHIFT: ret = i1>>i2; break; - case C_L_SHIFT: ret = i1<<i2; break; - case C_DIV: - case C_MOD: - if( i2 == 0 ) - { - ShowError("script:op_2num: division by zero detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2); - script_reportsrc(st); - script_pushnil(st); - st->state = END; - return; - } - else if( op == C_DIV ) - ret = i1 / i2; - else//if( op == C_MOD ) - ret = i1 % i2; - break; - default: - switch( op ) - {// operators that can overflow/underflow - case C_ADD: ret = i1 + i2; ret_double = (double)i1 + (double)i2; break; - case C_SUB: ret = i1 - i2; ret_double = (double)i1 - (double)i2; break; - case C_MUL: ret = i1 * i2; ret_double = (double)i1 * (double)i2; break; - default: - ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script_op2name(op), i1, i2); - script_reportsrc(st); - script_pushnil(st); - return; - } - if( ret_double < (double)INT_MIN ) - { - ShowWarning("script:op_2num: underflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2); - script_reportsrc(st); - ret = INT_MIN; - } - else if( ret_double > (double)INT_MAX ) - { - ShowWarning("script:op_2num: overflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2); - script_reportsrc(st); - ret = INT_MAX; - } - } - script_pushint(st, ret); -} - -/// Binary operators -void op_2(struct script_state *st, int op) -{ - struct script_data* left, leftref; - struct script_data* right; - - leftref.type = C_NOP; - - left = script_getdatatop(st, -2); - right = script_getdatatop(st, -1); - - if (st->op2ref) { - if (data_isreference(left)) { - leftref = *left; - } - - st->op2ref = 0; - } - - get_val(st, left); - get_val(st, right); - - // automatic conversions - switch( op ) - { - case C_ADD: - if( data_isint(left) && data_isstring(right) ) - {// convert int-string to string-string - conv_str(st, left); - } - else if( data_isstring(left) && data_isint(right) ) - {// convert string-int to string-string - conv_str(st, right); - } - break; - } - - if( data_isstring(left) && data_isstring(right) ) - {// ss => op_2str - op_2str(st, op, left->u.str, right->u.str); - script_removetop(st, leftref.type == C_NOP ? -3 : -2, -1);// pop the two values before the top one - - if (leftref.type != C_NOP) - { - aFree(left->u.str); - *left = leftref; - } - } - else if( data_isint(left) && data_isint(right) ) - {// ii => op_2num - int i1 = left->u.num; - int i2 = right->u.num; - - script_removetop(st, leftref.type == C_NOP ? -2 : -1, 0); - op_2num(st, op, i1, i2); - - if (leftref.type != C_NOP) - *left = leftref; - } - else - {// invalid argument - ShowError("script:op_2: invalid data for operator %s\n", script_op2name(op)); - script_reportdata(left); - script_reportdata(right); - script_reportsrc(st); - script_removetop(st, -2, 0); - script_pushnil(st); - st->state = END; - } -} - -/// Unary operators -/// NEG i -> i -/// NOT i -> i -/// LNOT i -> i -void op_1(struct script_state* st, int op) -{ - struct script_data* data; - int i1; - - data = script_getdatatop(st, -1); - get_val(st, data); - - if( !data_isint(data) ) - {// not a number - ShowError("script:op_1: argument is not a number (op=%s)\n", script_op2name(op)); - script_reportdata(data); - script_reportsrc(st); - script_pushnil(st); - st->state = END; - return; - } - - i1 = data->u.num; - script_removetop(st, -1, 0); - switch( op ) - { - case C_NEG: i1 = -i1; break; - case C_NOT: i1 = ~i1; break; - case C_LNOT: i1 = !i1; break; - default: - ShowError("script:op_1: unexpected operator %s i1=%d\n", script_op2name(op), i1); - script_reportsrc(st); - script_pushnil(st); - st->state = END; - return; - } - script_pushint(st, i1); -} - - -/// Checks the type of all arguments passed to a built-in function. -/// -/// @param st Script state whose stack arguments should be inspected. -/// @param func Built-in function for which the arguments are intended. -static void script_check_buildin_argtype(struct script_state* st, int func) -{ - char type; - int idx, invalid = 0; - script_function* sf = &buildin_func[str_data[func].val]; - - for( idx = 2; script_hasdata(st, idx); idx++ ) - { - struct script_data* data = script_getdata(st, idx); - - type = sf->arg[idx-2]; - - if( type == '?' || type == '*' ) - {// optional argument or unknown number of optional parameters ( no types are after this ) - break; - } - else if( type == 0 ) - {// more arguments than necessary ( should not happen, as it is checked before ) - ShowWarning("Found more arguments than necessary. unexpected arg type %s\n",script_op2name(data->type)); - invalid++; - break; - } - else - { - const char* name = NULL; - - if( data_isreference(data) ) - {// get name for variables to determine the type they refer to - name = reference_getname(data); - } - - switch( type ) - { - case 'v': - if( !data_isstring(data) && !data_isint(data) && !data_isreference(data) ) - {// variant - ShowWarning("Unexpected type for argument %d. Expected string, number or variable.\n", idx-1); - script_reportdata(data); - invalid++; - } - break; - case 's': - if( !data_isstring(data) && !( data_isreference(data) && is_string_variable(name) ) ) - {// string - ShowWarning("Unexpected type for argument %d. Expected string.\n", idx-1); - script_reportdata(data); - invalid++; - } - break; - case 'i': - if( !data_isint(data) && !( data_isreference(data) && ( reference_toparam(data) || reference_toconstant(data) || !is_string_variable(name) ) ) ) - {// int ( params and constants are always int ) - ShowWarning("Unexpected type for argument %d. Expected number.\n", idx-1); - script_reportdata(data); - invalid++; - } - break; - case 'r': - if( !data_isreference(data) ) - {// variables - ShowWarning("Unexpected type for argument %d. Expected variable, got %s.\n", idx-1,script_op2name(data->type)); - script_reportdata(data); - invalid++; - } - break; - case 'l': - if( !data_islabel(data) && !data_isfunclabel(data) ) - {// label - ShowWarning("Unexpected type for argument %d. Expected label, got %s\n", idx-1,script_op2name(data->type)); - script_reportdata(data); - invalid++; - } - break; - } - } - } - - if(invalid) - { - ShowDebug("Function: %s\n", get_str(func)); - script_reportsrc(st); - } -} - - -/// Executes a buildin command. -/// Stack: C_NAME(<command>) C_ARG <arg0> <arg1> ... <argN> -int run_func(struct script_state *st) -{ - struct script_data* data; - int i,start_sp,end_sp,func; - - end_sp = st->stack->sp;// position after the last argument - for( i = end_sp-1; i > 0 ; --i ) - if( st->stack->stack_data[i].type == C_ARG ) - break; - if( i == 0 ) - { - ShowError("script:run_func: C_ARG not found. please report this!!!\n"); - st->state = END; - script_reportsrc(st); - return 1; - } - start_sp = i-1;// C_NAME of the command - st->start = start_sp; - st->end = end_sp; - - data = &st->stack->stack_data[st->start]; - if( data->type == C_NAME && str_data[data->u.num].type == C_FUNC ) - func = data->u.num; - else - { - ShowError("script:run_func: not a buildin command.\n"); - script_reportdata(data); - script_reportsrc(st); - st->state = END; - return 1; - } - - if( script_config.warn_func_mismatch_argtypes ) - { - script_check_buildin_argtype(st, func); - } - - if(str_data[func].func){ - if (str_data[func].func(st)) //Report error - script_reportsrc(st); - } else { - ShowError("script:run_func: '%s' (id=%d type=%s) has no C function. please report this!!!\n", get_str(func), func, script_op2name(str_data[func].type)); - script_reportsrc(st); - st->state = END; - } - - // Stack's datum are used when re-running functions [Eoe] - if( st->state == RERUNLINE ) - return 0; - - pop_stack(st, st->start, st->end); - if( st->state == RETFUNC ) - {// return from a user-defined function - struct script_retinfo* ri; - int olddefsp = st->stack->defsp; - int nargs; - - pop_stack(st, st->stack->defsp, st->start);// pop distractions from the stack - if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp-1].type != C_RETINFO ) - { - ShowWarning("script:run_func: return without callfunc or callsub!\n"); - script_reportsrc(st); - st->state = END; - return 1; - } - script_free_vars( st->stack->var_function ); - - ri = st->stack->stack_data[st->stack->defsp-1].u.ri; - nargs = ri->nargs; - st->pos = ri->pos; - st->script = ri->script; - st->stack->var_function = ri->var_function; - st->stack->defsp = ri->defsp; - memset(ri, 0, sizeof(struct script_retinfo)); - - pop_stack(st, olddefsp-nargs-1, olddefsp);// pop arguments and retinfo - - st->state = GOTO; - } - - return 0; -} - -/*========================================== - * script execution - *------------------------------------------*/ -void run_script(struct script_code *rootscript,int pos,int rid,int oid) -{ - struct script_state *st; - - if( rootscript == NULL || pos < 0 ) - return; - - // TODO In jAthena, this function can take over the pending script in the player. [FlavioJS] - // It is unclear how that can be triggered, so it needs the be traced/checked in more detail. - // NOTE At the time of this change, this function wasn't capable of taking over the script state because st->scriptroot was never set. - st = script_alloc_state(rootscript, pos, rid, oid); - run_script_main(st); -} - -void script_stop_sleeptimers(int id) -{ - struct script_state* st; - for(;;) - { - st = (struct script_state*)linkdb_erase(&sleep_db,(void*)__64BPRTSIZE(id)); - if( st == NULL ) - break; // no more sleep timers - script_free_state(st); - } -} - -/*========================================== - * Delete the specified node from sleep_db - *------------------------------------------*/ -struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n) -{ - struct linkdb_node *retnode; - - if( n == NULL) - return NULL; - if( n->prev == NULL ) - sleep_db = n->next; - else - n->prev->next = n->next; - if( n->next ) - n->next->prev = n->prev; - retnode = n->next; - aFree( n ); - return retnode; // The following; return retnode -} - -/*========================================== - * Timer function for sleep - *------------------------------------------*/ -int run_script_timer(int tid, unsigned int tick, int id, intptr_t data) -{ - struct script_state *st = (struct script_state *)data; - struct linkdb_node *node = (struct linkdb_node *)sleep_db; - TBL_PC *sd = map_id2sd(st->rid); - - if((sd && sd->status.char_id != id) || (st->rid && !sd)) - { //Character mismatch. Cancel execution. - st->rid = 0; - st->state = END; - } - while( node && st->sleep.timer != INVALID_TIMER ) { - if( (int)__64BPRTSIZE(node->key) == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) { - script_erase_sleepdb(node); - st->sleep.timer = INVALID_TIMER; - break; - } - node = node->next; - } - if(st->state != RERUNLINE) - st->sleep.tick = 0; - run_script_main(st); - return 0; -} - -/// Detaches script state from possibly attached character and restores it's previous script if any. -/// -/// @param st Script state to detach. -/// @param dequeue_event Whether to schedule any queued events, when there was no previous script. -static void script_detach_state(struct script_state* st, bool dequeue_event) -{ - struct map_session_data* sd; - - if(st->rid && (sd = map_id2sd(st->rid))!=NULL) - { - sd->st = st->bk_st; - sd->npc_id = st->bk_npcid; - /** - * 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. - st->bk_st = NULL; - st->bk_npcid = 0; - } - else if(dequeue_event) - { - npc_event_dequeue(sd); - } - } - else if(st->bk_st) - {// rid was set to 0, before detaching the script state - ShowError("script_detach_state: Found previous script state without attached player (rid=%d, oid=%d, state=%d, bk_npcid=%d)\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid); - script_reportsrc(st->bk_st); - - script_free_state(st->bk_st); - st->bk_st = NULL; - } -} - -/// Attaches script state to possibly attached character and backups it's previous script, if any. -/// -/// @param st Script state to attach. -static void script_attach_state(struct script_state* st) -{ - struct map_session_data* sd; - - if(st->rid && (sd = map_id2sd(st->rid))!=NULL) - { - if(st!=sd->st) - { - if(st->bk_st) - {// there is already a backup - ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid); - } - st->bk_st = sd->st; - st->bk_npcid = sd->npc_id; - } - sd->st = st; - sd->npc_id = st->oid; -/** - * 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 - } -} - -/*========================================== - * The main part of the script execution - *------------------------------------------*/ -void run_script_main(struct script_state *st) -{ - int cmdcount = script_config.check_cmdcount; - int gotocount = script_config.check_gotocount; - TBL_PC *sd; - struct script_stack *stack=st->stack; - struct npc_data *nd; - - script_attach_state(st); - - nd = map_id2nd(st->oid); - if( nd && map[nd->bl.m].instance_id > 0 ) - st->instance_id = map[nd->bl.m].instance_id; - - if(st->state == RERUNLINE) { - run_func(st); - if(st->state == GOTO) - st->state = RUN; - } else if(st->state != END) - st->state = RUN; - - while(st->state == RUN) - { - enum c_op c = get_com(st->script->script_buf,&st->pos); - switch(c){ - case C_EOL: - if( stack->defsp > stack->sp ) - ShowError("script:run_script_main: unexpected stack position (defsp=%d sp=%d). please report this!!!\n", stack->defsp, stack->sp); - else - pop_stack(st, stack->defsp, stack->sp);// pop unused stack data. (unused return value) - break; - case C_INT: - push_val(stack,C_INT,get_num(st->script->script_buf,&st->pos)); - break; - case C_POS: - case C_NAME: - push_val(stack,c,GETVALUE(st->script->script_buf,st->pos)); - st->pos+=3; - break; - case C_ARG: - push_val(stack,c,0); - break; - case C_STR: - push_str(stack,C_CONSTSTR,(char*)(st->script->script_buf+st->pos)); - while(st->script->script_buf[st->pos++]); - break; - case C_FUNC: - run_func(st); - if(st->state==GOTO){ - st->state = RUN; - if( !st->freeloop && gotocount>0 && (--gotocount)<=0 ){ - ShowError("run_script: infinity loop !\n"); - script_reportsrc(st); - st->state=END; - } - } - break; - - case C_REF: - st->op2ref = 1; - break; - - case C_NEG: - case C_NOT: - case C_LNOT: - op_1(st ,c); - break; - - case C_ADD: - case C_SUB: - case C_MUL: - case C_DIV: - case C_MOD: - case C_EQ: - case C_NE: - case C_GT: - case C_GE: - case C_LT: - case C_LE: - case C_AND: - case C_OR: - case C_XOR: - case C_LAND: - case C_LOR: - case C_R_SHIFT: - case C_L_SHIFT: - op_2(st, c); - break; - - case C_OP3: - op_3(st, c); - break; - - case C_NOP: - st->state=END; - break; - - default: - ShowError("unknown command : %d @ %d\n",c,st->pos); - st->state=END; - break; - } - if( !st->freeloop && cmdcount>0 && (--cmdcount)<=0 ){ - ShowError("run_script: infinity loop !\n"); - script_reportsrc(st); - st->state=END; - } - } - - if(st->sleep.tick > 0) { - //Restore previous script - script_detach_state(st, false); - //Delay execution - sd = map_id2sd(st->rid); // Get sd since script might have attached someone while running. [Inkfish] - st->sleep.charid = sd?sd->status.char_id:0; - st->sleep.timer = add_timer(gettick()+st->sleep.tick, - run_script_timer, st->sleep.charid, (intptr_t)st); - linkdb_insert(&sleep_db, (void*)__64BPRTSIZE(st->oid), st); - } - else if(st->state != END && st->rid){ - //Resume later (st is already attached to player). - if(st->bk_st) { - ShowWarning("Unable to restore stack! Double continuation!\n"); - //Report BOTH scripts to see if that can help somehow. - ShowDebug("Previous script (lost):\n"); - script_reportsrc(st->bk_st); - ShowDebug("Current script:\n"); - script_reportsrc(st); - - script_free_state(st->bk_st); - st->bk_st = NULL; - } - } else { - //Dispose of script. - if ((sd = map_id2sd(st->rid))!=NULL) - { //Restore previous stack and save char. - if(sd->state.using_fake_npc){ - clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); - sd->state.using_fake_npc = 0; - } - //Restore previous script if any. - script_detach_state(st, true); - if (sd->state.reg_dirty&2) - intif_saveregistry(sd,2); - if (sd->state.reg_dirty&1) - intif_saveregistry(sd,1); - } - script_free_state(st); - st = NULL; - } -} - -int script_config_read(char *cfgName) -{ - int i; - char line[1024],w1[1024],w2[1024]; - FILE *fp; - - - fp=fopen(cfgName,"r"); - if(fp==NULL){ - ShowError("File not found: %s\n", cfgName); - return 1; - } - while(fgets(line, sizeof(line), fp)) - { - if(line[0] == '/' && line[1] == '/') - continue; - i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); - if(i!=2) - continue; - - if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) { - script_config.warn_func_mismatch_paramnum = config_switch(w2); - } - else if(strcmpi(w1,"check_cmdcount")==0) { - script_config.check_cmdcount = config_switch(w2); - } - else if(strcmpi(w1,"check_gotocount")==0) { - script_config.check_gotocount = config_switch(w2); - } - else if(strcmpi(w1,"input_min_value")==0) { - script_config.input_min_value = config_switch(w2); - } - else if(strcmpi(w1,"input_max_value")==0) { - script_config.input_max_value = config_switch(w2); - } - else if(strcmpi(w1,"warn_func_mismatch_argtypes")==0) { - script_config.warn_func_mismatch_argtypes = config_switch(w2); - } - else if(strcmpi(w1,"import")==0){ - script_config_read(w2); - } - else { - ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName); - } - } - fclose(fp); - - return 0; -} - -/** - * @see DBApply - */ -static int db_script_free_code_sub(DBKey key, DBData *data, va_list ap) -{ - struct script_code *code = db_data2ptr(data); - if (code) - script_free_code(code); - return 0; -} - -void script_run_autobonus(const char *autobonus, int id, int pos) -{ - struct script_code *script = (struct script_code *)strdb_get(autobonus_db, autobonus); - - if( script ) - { - current_equip_item_index = pos; - run_script(script,0,id,0); - } -} - -void script_add_autobonus(const char *autobonus) -{ - if( strdb_get(autobonus_db, autobonus) == NULL ) - { - struct script_code *script = parse_script(autobonus, "autobonus", 0, 0); - - if( script ) - strdb_put(autobonus_db, autobonus, script); - } -} - - -/// resets a temporary character array variable to given value -void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value) -{ - int key; - uint8 idx; - - if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) ) - { - ShowError("script_cleararray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id); - return; - } - - key = add_str(varname); - - if( is_string_variable(varname) ) - { - for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ ) - { - pc_setregstr(sd, reference_uid(key, idx), (const char*)value); - } - } - else - { - for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ ) - { - pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value)); - } - } -} - - -/// sets a temporary character array variable element idx to given value -/// @param refcache Pointer to an int variable, which keeps a copy of the reference to varname and must be initialized to 0. Can be NULL if only one element is set. -void script_setarray_pc(struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache) -{ - int key; - - if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) ) - { - ShowError("script_setarray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id); - return; - } - - if( idx >= SCRIPT_MAX_ARRAYSIZE ) - { - ShowError("script_setarray_pc: Variable '%s' has invalid index '%d' (char_id=%d).\n", varname, (int)idx, sd->status.char_id); - return; - } - - key = ( refcache && refcache[0] ) ? refcache[0] : add_str(varname); - - if( is_string_variable(varname) ) - { - pc_setregstr(sd, reference_uid(key, idx), (const char*)value); - } - else - { - pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value)); - } - - if( refcache ) - {// save to avoid repeated add_str calls - refcache[0] = key; - } -} -#ifdef BETA_THREAD_TEST -int buildin_query_sql_sub(struct script_state* st, Sql* handle); - -/* used to receive items the queryThread has already processed */ -int queryThread_timer(int tid, unsigned int tick, int id, intptr_t data) { - int i, cursor = 0; - bool allOk = true; - - EnterSpinLock(&queryThreadLock); - - for( i = 0; i < queryThreadData.count; i++ ) { - struct queryThreadEntry *entry = queryThreadData.entry[i]; - - if( !entry->ok ) { - allOk = false; - continue; - } - - run_script_main(entry->st); - - entry->st = NULL;/* empty entries */ - aFree(entry); - queryThreadData.entry[i] = NULL; - } - - - if( allOk ) { - /* cancel the repeating timer -- it'll re-create itself when necessary, dont need to remain looping */ - delete_timer(queryThreadData.timer, queryThread_timer); - queryThreadData.timer = INVALID_TIMER; - } - - /* now lets clear the mess. */ - for( i = 0; i < queryThreadData.count; i++ ) { - struct queryThreadEntry *entry = queryThreadData.entry[i]; - if( entry == NULL ) - continue;/* entry on hold */ - - /* move */ - memmove(&queryThreadData.entry[cursor], &queryThreadData.entry[i], sizeof(struct queryThreadEntry*)); - - cursor++; - } - - queryThreadData.count = cursor; - - LeaveSpinLock(&queryThreadLock); - - return 0; -} - -void queryThread_add(struct script_state *st, bool type) { - int idx = 0; - struct queryThreadEntry* entry = NULL; - - EnterSpinLock(&queryThreadLock); - - if( queryThreadData.count++ != 0 ) - RECREATE(queryThreadData.entry, struct queryThreadEntry* , queryThreadData.count); - - idx = queryThreadData.count-1; - - CREATE(queryThreadData.entry[idx],struct queryThreadEntry,1); - - entry = queryThreadData.entry[idx]; - - entry->st = st; - entry->ok = false; - entry->type = type; - if( queryThreadData.timer == INVALID_TIMER ) { /* start the receiver timer */ - queryThreadData.timer = add_timer_interval(gettick() + 100, queryThread_timer, 0, 0, 100); - } - - LeaveSpinLock(&queryThreadLock); - - /* unlock the queryThread */ - racond_signal(queryThreadCond); -} -/* adds a new log to the queue */ -void queryThread_log(char * entry, int length) { - int idx = logThreadData.count; - - EnterSpinLock(&queryThreadLock); - - if( logThreadData.count++ != 0 ) - RECREATE(logThreadData.entry, char* , logThreadData.count); - - CREATE(logThreadData.entry[idx], char, length + 1 ); - safestrncpy(logThreadData.entry[idx], entry, length + 1 ); - - LeaveSpinLock(&queryThreadLock); - - /* unlock the queryThread */ - racond_signal(queryThreadCond); -} - -/* queryThread_main */ -static void *queryThread_main(void *x) { - Sql *queryThread_handle = Sql_Malloc(); - int i; - - if ( SQL_ERROR == Sql_Connect(queryThread_handle, map_server_id, map_server_pw, map_server_ip, map_server_port, map_server_db) ) - exit(EXIT_FAILURE); - - if( strlen(default_codepage) > 0 ) - if ( SQL_ERROR == Sql_SetEncoding(queryThread_handle, default_codepage) ) - Sql_ShowDebug(queryThread_handle); - - if( log_config.sql_logs ) { - logmysql_handle = Sql_Malloc(); - - if ( SQL_ERROR == Sql_Connect(logmysql_handle, log_db_id, log_db_pw, log_db_ip, log_db_port, log_db_db) ) - exit(EXIT_FAILURE); - - if( strlen(default_codepage) > 0 ) - if ( SQL_ERROR == Sql_SetEncoding(logmysql_handle, default_codepage) ) - Sql_ShowDebug(logmysql_handle); - } - - while( 1 ) { - - if(queryThreadTerminate > 0) - break; - - EnterSpinLock(&queryThreadLock); - - /* mess with queryThreadData within the lock */ - for( i = 0; i < queryThreadData.count; i++ ) { - struct queryThreadEntry *entry = queryThreadData.entry[i]; - - if( entry->ok ) - continue; - else if ( !entry->st || !entry->st->stack ) { - entry->ok = true;/* dispose */ - continue; - } - - buildin_query_sql_sub(entry->st, entry->type ? logmysql_handle : queryThread_handle); - - entry->ok = true;/* we're done with this */ - } - - /* also check for any logs in need to be sent */ - if( log_config.sql_logs ) { - for( i = 0; i < logThreadData.count; i++ ) { - if( SQL_ERROR == Sql_Query(logmysql_handle, logThreadData.entry[i]) ) - Sql_ShowDebug(logmysql_handle); - aFree(logThreadData.entry[i]); - } - logThreadData.count = 0; - } - - LeaveSpinLock(&queryThreadLock); - - ramutex_lock( queryThreadMutex ); - racond_wait( queryThreadCond, queryThreadMutex, -1 ); - ramutex_unlock( queryThreadMutex ); - - } - - Sql_Free(queryThread_handle); - - if( log_config.sql_logs ) { - Sql_Free(logmysql_handle); - } - - return NULL; -} -#endif -/*========================================== - * Destructor - *------------------------------------------*/ -int do_final_script() { - int i; -#ifdef DEBUG_HASH - if (battle_config.etc_log) - { - FILE *fp = fopen("hash_dump.txt","wt"); - if(fp) { - int count[SCRIPT_HASH_SIZE]; - int count2[SCRIPT_HASH_SIZE]; // number of buckets with a certain number of items - int n=0; - int min=INT_MAX,max=0,zero=0; - double mean=0.0f; - double median=0.0f; - - ShowNotice("Dumping script str hash information to hash_dump.txt\n"); - memset(count, 0, sizeof(count)); - fprintf(fp,"num : hash : data_name\n"); - fprintf(fp,"---------------------------------------------------------------\n"); - for(i=LABEL_START; i<str_num; i++) { - unsigned int h = calc_hash(get_str(i)); - fprintf(fp,"%04d : %4u : %s\n",i,h, get_str(i)); - ++count[h]; - } - fprintf(fp,"--------------------\n\n"); - memset(count2, 0, sizeof(count2)); - for(i=0; i<SCRIPT_HASH_SIZE; i++) { - fprintf(fp," hash %3d = %d\n",i,count[i]); - if(min > count[i]) - min = count[i]; // minimun count of collision - if(max < count[i]) - max = count[i]; // maximun count of collision - if(count[i] == 0) - zero++; - ++count2[count[i]]; - } - fprintf(fp,"\n--------------------\n items : buckets\n--------------------\n"); - for( i=min; i <= max; ++i ){ - fprintf(fp," %5d : %7d\n",i,count2[i]); - mean += 1.0f*i*count2[i]/SCRIPT_HASH_SIZE; // Note: this will always result in <nr labels>/<nr buckets> - } - for( i=min; i <= max; ++i ){ - n += count2[i]; - if( n*2 >= SCRIPT_HASH_SIZE ) - { - if( SCRIPT_HASH_SIZE%2 == 0 && SCRIPT_HASH_SIZE/2 == n ) - median = (i+i+1)/2.0f; - else - median = i; - break; - } - } - fprintf(fp,"--------------------\n min = %d, max = %d, zero = %d\n mean = %lf, median = %lf\n",min,max,zero,mean,median); - fclose(fp); - } - } -#endif - - mapreg_final(); - - db_destroy(scriptlabel_db); - userfunc_db->destroy(userfunc_db, db_script_free_code_sub); - autobonus_db->destroy(autobonus_db, db_script_free_code_sub); - if(sleep_db) { - struct linkdb_node *n = (struct linkdb_node *)sleep_db; - while(n) { - struct script_state *st = (struct script_state *)n->data; - script_free_state(st); - n = n->next; - } - linkdb_final(&sleep_db); - } - - if (str_data) - aFree(str_data); - if (str_buf) - aFree(str_buf); - - for( i = 0; i < atcmd_binding_count; i++ ) { - aFree(atcmd_binding[i]); - } - - if( atcmd_binding_count != 0 ) - aFree(atcmd_binding); -#ifdef BETA_THREAD_TEST - /* QueryThread */ - InterlockedIncrement(&queryThreadTerminate); - racond_signal(queryThreadCond); - rathread_wait(queryThread, NULL); - - // Destroy cond var and mutex. - racond_destroy( queryThreadCond ); - ramutex_destroy( queryThreadMutex ); - - /* Clear missing vars */ - for( i = 0; i < queryThreadData.count; i++ ) { - aFree(queryThreadData.entry[i]); - } - - aFree(queryThreadData.entry); - - for( i = 0; i < logThreadData.count; i++ ) { - aFree(logThreadData.entry[i]); - } - - aFree(logThreadData.entry); -#endif - - return 0; -} -/*========================================== - * Initialization - *------------------------------------------*/ -int do_init_script() { - userfunc_db=strdb_alloc(DB_OPT_DUP_KEY,0); - scriptlabel_db=strdb_alloc(DB_OPT_DUP_KEY,50); - autobonus_db = strdb_alloc(DB_OPT_DUP_KEY,0); - - mapreg_init(); -#ifdef BETA_THREAD_TEST - CREATE(queryThreadData.entry, struct queryThreadEntry*, 1); - queryThreadData.count = 0; - CREATE(logThreadData.entry, char *, 1); - logThreadData.count = 0; - /* QueryThread Start */ - - InitializeSpinLock(&queryThreadLock); - - queryThreadData.timer = INVALID_TIMER; - queryThreadTerminate = 0; - queryThreadMutex = ramutex_create(); - queryThreadCond = racond_create(); - - queryThread = rathread_create(queryThread_main, NULL); - - if(queryThread == NULL){ - ShowFatalError("do_init_script: cannot spawn Query Thread.\n"); - exit(EXIT_FAILURE); - } - - add_timer_func_list(queryThread_timer, "queryThread_timer"); -#endif - return 0; -} - -int script_reload() { - int i; - -#ifdef BETA_THREAD_TEST - /* we're reloading so any queries undergoing should be...exterminated. */ - EnterSpinLock(&queryThreadLock); - - for( i = 0; i < queryThreadData.count; i++ ) { - aFree(queryThreadData.entry[i]); - } - queryThreadData.count = 0; - - if( queryThreadData.timer != INVALID_TIMER ) { - delete_timer(queryThreadData.timer, queryThread_timer); - queryThreadData.timer = INVALID_TIMER; - } - - LeaveSpinLock(&queryThreadLock); -#endif - - - userfunc_db->clear(userfunc_db, db_script_free_code_sub); - db_clear(scriptlabel_db); - - // @commands (script based) - // Clear bindings - for( i = 0; i < atcmd_binding_count; i++ ) { - aFree(atcmd_binding[i]); - } - - if( atcmd_binding_count != 0 ) - aFree(atcmd_binding); - - atcmd_binding_count = 0; - - if(sleep_db) { - struct linkdb_node *n = (struct linkdb_node *)sleep_db; - while(n) { - struct script_state *st = (struct script_state *)n->data; - script_free_state(st); - n = n->next; - } - linkdb_final(&sleep_db); - } - mapreg_reload(); - return 0; -} - -//----------------------------------------------------------------------------- -// buildin functions -// - -#define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args } -#define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args } -#define BUILDIN_FUNC(x) int buildin_ ## x (struct script_state* st) - -///////////////////////////////////////////////////////////////////// -// NPC interaction -// - -/// Appends a message to the npc dialog. -/// If a dialog doesn't exist yet, one is created. -/// -/// mes "<message>"; -BUILDIN_FUNC(mes) -{ - TBL_PC* sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if( !script_hasdata(st, 3) ) - {// only a single line detected in the script - clif_scriptmes(sd, st->oid, script_getstr(st, 2)); - } - else - {// parse multiple lines as they exist - int i; - - for( i = 2; script_hasdata(st, i); i++ ) - { - // send the message to the client - clif_scriptmes(sd, st->oid, script_getstr(st, i)); - } - } - - return 0; -} - -/// Displays the button 'next' in the npc dialog. -/// The dialog text is cleared and the script continues when the button is pressed. -/// -/// next; -BUILDIN_FUNC(next) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - st->state = STOP; - clif_scriptnext(sd, st->oid); - return 0; -} - -/// Ends the script and displays the button 'close' on the npc dialog. -/// The dialog is closed when the button is pressed. -/// -/// close; -BUILDIN_FUNC(close) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - st->state = END; - clif_scriptclose(sd, st->oid); - return 0; -} - -/// Displays the button 'close' on the npc dialog. -/// The dialog is closed and the script continues when the button is pressed. -/// -/// close2; -BUILDIN_FUNC(close2) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - st->state = STOP; - clif_scriptclose(sd, st->oid); - return 0; -} - -/// Counts the number of valid and total number of options in 'str' -/// If max_count > 0 the counting stops when that valid option is reached -/// total is incremented for each option (NULL is supported) -static int menu_countoptions(const char* str, int max_count, int* total) -{ - int count = 0; - int bogus_total; - - if( total == NULL ) - total = &bogus_total; - ++(*total); - - // initial empty options - while( *str == ':' ) - { - ++str; - ++(*total); - } - // count menu options - while( *str != '\0' ) - { - ++count; - --max_count; - if( max_count == 0 ) - break; - while( *str != ':' && *str != '\0' ) - ++str; - while( *str == ':' ) - { - ++str; - ++(*total); - } - } - return count; -} - -/// Displays a menu with options and goes to the target label. -/// The script is stopped if cancel is pressed. -/// Options with no text are not displayed in the client. -/// -/// Options can be grouped together, separated by the character ':' in the text: -/// ex: menu "A:B:C",L_target; -/// All these options go to the specified target label. -/// -/// The index of the selected option is put in the variable @menu. -/// Indexes start with 1 and are consistent with grouped and empty options. -/// ex: menu "A::B",-,"",L_Impossible,"C",-; -/// // displays "A", "B" and "C", corresponding to indexes 1, 3 and 5 -/// -/// NOTE: the client closes the npc dialog when cancel is pressed -/// -/// menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...}; -BUILDIN_FUNC(menu) -{ - int i; - const char* text; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - // TODO detect multiple scripts waiting for input at the same time, and what to do when that happens - if( sd->state.menu_or_input == 0 ) - { - struct StringBuf buf; - struct script_data* data; - - if( script_lastdata(st) % 2 == 0 ) - {// argument count is not even (1st argument is at index 2) - ShowError("script:menu: illegal number of arguments (%d).\n", (script_lastdata(st) - 1)); - st->state = END; - return 1; - } - - StringBuf_Init(&buf); - sd->npc_menu = 0; - for( i = 2; i < script_lastdata(st); i += 2 ) - { - // menu options - text = script_getstr(st, i); - - // target label - data = script_getdata(st, i+1); - if( !data_islabel(data) ) - {// not a label - StringBuf_Destroy(&buf); - ShowError("script:menu: argument #%d (from 1) is not a label or label not found.\n", i); - script_reportdata(data); - st->state = END; - return 1; - } - - // append option(s) - if( text[0] == '\0' ) - continue;// empty string, ignore - if( sd->npc_menu > 0 ) - StringBuf_AppendStr(&buf, ":"); - StringBuf_AppendStr(&buf, text); - sd->npc_menu += menu_countoptions(text, 0, NULL); - } - st->state = RERUNLINE; - sd->state.menu_or_input = 1; - - /** - * menus beyond this length crash the client (see bugreport:6402) - **/ - if( StringBuf_Length(&buf) >= 2047 ) { - struct npc_data * nd = map_id2nd(st->oid); - char* menu; - CREATE(menu, char, 2048); - safestrncpy(menu, StringBuf_Value(&buf), 2047); - ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf)); - clif_scriptmenu(sd, st->oid, menu); - aFree(menu); - } else - clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); - - StringBuf_Destroy(&buf); - - if( sd->npc_menu >= 0xff ) - {// client supports only up to 254 entries; 0 is not used and 255 is reserved for cancel; excess entries are displayed but cause 'uint8' overflow - ShowWarning("buildin_menu: Too many options specified (current=%d, max=254).\n", sd->npc_menu); - script_reportsrc(st); - } - } - else if( sd->npc_menu == 0xff ) - {// Cancel was pressed - sd->state.menu_or_input = 0; - st->state = END; - } - else - {// goto target label - int menu = 0; - - sd->state.menu_or_input = 0; - if( sd->npc_menu <= 0 ) - { - ShowDebug("script:menu: unexpected selection (%d)\n", sd->npc_menu); - st->state = END; - return 1; - } - - // get target label - for( i = 2; i < script_lastdata(st); i += 2 ) - { - text = script_getstr(st, i); - sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu); - if( sd->npc_menu <= 0 ) - break;// entry found - } - if( sd->npc_menu > 0 ) - {// Invalid selection - ShowDebug("script:menu: selection is out of range (%d pairs are missing?) - please report this\n", sd->npc_menu); - st->state = END; - return 1; - } - if( !data_islabel(script_getdata(st, i + 1)) ) - {// TODO remove this temporary crash-prevention code (fallback for multiple scripts requesting user input) - ShowError("script:menu: unexpected data in label argument\n"); - script_reportdata(script_getdata(st, i + 1)); - st->state = END; - return 1; - } - pc_setreg(sd, add_str("@menu"), menu); - st->pos = script_getnum(st, i + 1); - st->state = GOTO; - } - return 0; -} - -/// Displays a menu with options and returns the selected option. -/// Behaves like 'menu' without the target labels. -/// -/// select(<option_text>{,<option_text>,...}) -> <selected_option> -/// -/// @see menu -BUILDIN_FUNC(select) -{ - int i; - const char* text; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if( sd->state.menu_or_input == 0 ) { - struct StringBuf buf; - - StringBuf_Init(&buf); - sd->npc_menu = 0; - for( i = 2; i <= script_lastdata(st); ++i ) { - text = script_getstr(st, i); - - if( sd->npc_menu > 0 ) - StringBuf_AppendStr(&buf, ":"); - - StringBuf_AppendStr(&buf, text); - sd->npc_menu += menu_countoptions(text, 0, NULL); - } - - st->state = RERUNLINE; - sd->state.menu_or_input = 1; - - /** - * menus beyond this length crash the client (see bugreport:6402) - **/ - if( StringBuf_Length(&buf) >= 2047 ) { - struct npc_data * nd = map_id2nd(st->oid); - char* menu; - CREATE(menu, char, 2048); - safestrncpy(menu, StringBuf_Value(&buf), 2047); - ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf)); - clif_scriptmenu(sd, st->oid, menu); - aFree(menu); - } else - clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); - StringBuf_Destroy(&buf); - - if( sd->npc_menu >= 0xff ) { - ShowWarning("buildin_select: Too many options specified (current=%d, max=254).\n", sd->npc_menu); - script_reportsrc(st); - } - } else if( sd->npc_menu == 0xff ) {// Cancel was pressed - sd->state.menu_or_input = 0; - st->state = END; - } else {// return selected option - int menu = 0; - - sd->state.menu_or_input = 0; - for( i = 2; i <= script_lastdata(st); ++i ) { - text = script_getstr(st, i); - sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu); - if( sd->npc_menu <= 0 ) - break;// entry found - } - pc_setreg(sd, add_str("@menu"), menu); - script_pushint(st, menu); - st->state = RUN; - } - return 0; -} - -/// Displays a menu with options and returns the selected option. -/// Behaves like 'menu' without the target labels, except when cancel is -/// pressed. -/// When cancel is pressed, the script continues and 255 is returned. -/// -/// prompt(<option_text>{,<option_text>,...}) -> <selected_option> -/// -/// @see menu -BUILDIN_FUNC(prompt) -{ - int i; - const char *text; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if( sd->state.menu_or_input == 0 ) - { - struct StringBuf buf; - - StringBuf_Init(&buf); - sd->npc_menu = 0; - for( i = 2; i <= script_lastdata(st); ++i ) - { - text = script_getstr(st, i); - if( sd->npc_menu > 0 ) - StringBuf_AppendStr(&buf, ":"); - StringBuf_AppendStr(&buf, text); - sd->npc_menu += menu_countoptions(text, 0, NULL); - } - - st->state = RERUNLINE; - sd->state.menu_or_input = 1; - - /** - * menus beyond this length crash the client (see bugreport:6402) - **/ - if( StringBuf_Length(&buf) >= 2047 ) { - struct npc_data * nd = map_id2nd(st->oid); - char* menu; - CREATE(menu, char, 2048); - safestrncpy(menu, StringBuf_Value(&buf), 2047); - ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf)); - clif_scriptmenu(sd, st->oid, menu); - aFree(menu); - } else - clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); - StringBuf_Destroy(&buf); - - if( sd->npc_menu >= 0xff ) - { - ShowWarning("buildin_prompt: Too many options specified (current=%d, max=254).\n", sd->npc_menu); - script_reportsrc(st); - } - } - else if( sd->npc_menu == 0xff ) - {// Cancel was pressed - sd->state.menu_or_input = 0; - pc_setreg(sd, add_str("@menu"), 0xff); - script_pushint(st, 0xff); - st->state = RUN; - } - else - {// return selected option - int menu = 0; - - sd->state.menu_or_input = 0; - for( i = 2; i <= script_lastdata(st); ++i ) - { - text = script_getstr(st, i); - sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu); - if( sd->npc_menu <= 0 ) - break;// entry found - } - pc_setreg(sd, add_str("@menu"), menu); - script_pushint(st, menu); - st->state = RUN; - } - return 0; -} - -///////////////////////////////////////////////////////////////////// -// ... -// - -/// Jumps to the target script label. -/// -/// goto <label>; -BUILDIN_FUNC(goto) -{ - if( !data_islabel(script_getdata(st,2)) ) - { - ShowError("script:goto: not a label\n"); - script_reportdata(script_getdata(st,2)); - st->state = END; - return 1; - } - - st->pos = script_getnum(st,2); - st->state = GOTO; - return 0; -} - -/*========================================== - * user-defined function call - *------------------------------------------*/ -BUILDIN_FUNC(callfunc) -{ - int i, j; - struct script_retinfo* ri; - struct script_code* scr; - const char* str = script_getstr(st,2); - DBMap **ref = NULL; - - scr = (struct script_code*)strdb_get(userfunc_db, str); - if( !scr ) - { - ShowError("script:callfunc: function not found! [%s]\n", str); - st->state = END; - return 1; - } - - for( i = st->start+3, j = 0; i < st->end; i++, j++ ) - { - struct script_data* data = push_copy(st->stack,i); - if( data_isreference(data) && !data->ref ) - { - const char* name = reference_getname(data); - if( name[0] == '.' ) { - if ( !ref ) { - ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1); - ref[0] = (name[1] == '@' ? st->stack->var_function : st->script->script_vars); - } - data->ref = ref; - } - } - } - - CREATE(ri, struct script_retinfo, 1); - ri->script = st->script;// script code - ri->var_function = st->stack->var_function;// scope variables - ri->pos = st->pos;// script location - ri->nargs = j;// argument count - ri->defsp = st->stack->defsp;// default stack pointer - push_retinfo(st->stack, ri, ref); - - st->pos = 0; - st->script = scr; - st->stack->defsp = st->stack->sp; - st->state = GOTO; - st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA); - - return 0; -} -/*========================================== - * subroutine call - *------------------------------------------*/ -BUILDIN_FUNC(callsub) -{ - int i,j; - struct script_retinfo* ri; - int pos = script_getnum(st,2); - DBMap **ref = NULL; - - if( !data_islabel(script_getdata(st,2)) && !data_isfunclabel(script_getdata(st,2)) ) - { - ShowError("script:callsub: argument is not a label\n"); - script_reportdata(script_getdata(st,2)); - st->state = END; - return 1; - } - - for( i = st->start+3, j = 0; i < st->end; i++, j++ ) - { - struct script_data* data = push_copy(st->stack,i); - if( data_isreference(data) && !data->ref ) - { - const char* name = reference_getname(data); - if( name[0] == '.' && name[1] == '@' ) { - if ( !ref ) { - ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1); - ref[0] = st->stack->var_function; - } - data->ref = ref; - } - } - } - - CREATE(ri, struct script_retinfo, 1); - ri->script = st->script;// script code - ri->var_function = st->stack->var_function;// scope variables - ri->pos = st->pos;// script location - ri->nargs = j;// argument count - ri->defsp = st->stack->defsp;// default stack pointer - push_retinfo(st->stack, ri, ref); - - st->pos = pos; - st->stack->defsp = st->stack->sp; - st->state = GOTO; - st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA); - - return 0; -} - -/// Retrieves an argument provided to callfunc/callsub. -/// If the argument doesn't exist -/// -/// getarg(<index>{,<default_value>}) -> <value> -BUILDIN_FUNC(getarg) -{ - struct script_retinfo* ri; - int idx; - - if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO ) - { - ShowError("script:getarg: no callfunc or callsub!\n"); - st->state = END; - return 1; - } - ri = st->stack->stack_data[st->stack->defsp - 1].u.ri; - - idx = script_getnum(st,2); - - if( idx >= 0 && idx < ri->nargs ) - push_copy(st->stack, st->stack->defsp - 1 - ri->nargs + idx); - else if( script_hasdata(st,3) ) - script_pushcopy(st, 3); - else - { - ShowError("script:getarg: index (idx=%d) out of range (nargs=%d) and no default value found\n", idx, ri->nargs); - st->state = END; - return 1; - } - - return 0; -} - -/// Returns from the current function, optionaly returning a value from the functions. -/// Don't use outside script functions. -/// -/// return; -/// return <value>; -BUILDIN_FUNC(return) -{ - if( script_hasdata(st,2) ) - {// return value - struct script_data* data; - script_pushcopy(st, 2); - data = script_getdatatop(st, -1); - if( data_isreference(data) ) - { - const char* name = reference_getname(data); - if( name[0] == '.' && name[1] == '@' ) - {// scope variable - if( !data->ref || data->ref == (DBMap**)&st->stack->var_function ) - get_val(st, data);// current scope, convert to value - } - else if( name[0] == '.' && !data->ref ) - {// script variable, link to current script - data->ref = &st->script->script_vars; - } - } - } - else - {// no return value - script_pushnil(st); - } - st->state = RETFUNC; - return 0; -} - -/// Returns a random number from 0 to <range>-1. -/// Or returns a random number from <min> to <max>. -/// If <min> is greater than <max>, their numbers are switched. -/// rand(<range>) -> <int> -/// rand(<min>,<max>) -> <int> -BUILDIN_FUNC(rand) -{ - int range; - int min; - int max; - - if( script_hasdata(st,3) ) - {// min,max - min = script_getnum(st,2); - max = script_getnum(st,3); - if( max < min ) - swap(min, max); - range = max - min + 1; - } - else - {// range - min = 0; - range = script_getnum(st,2); - } - if( range <= 1 ) - script_pushint(st, min); - else - script_pushint(st, rnd()%range + min); - - return 0; -} - -/*========================================== - * Warp sd to str,x,y or Random or SavePoint/Save - *------------------------------------------*/ -BUILDIN_FUNC(warp) -{ - int ret; - int x,y; - const char* str; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - str = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - - if(strcmp(str,"Random")==0) - ret = pc_randomwarp(sd,CLR_TELEPORT); - else if(strcmp(str,"SavePoint")==0 || strcmp(str,"Save")==0) - ret = pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - else - ret = pc_setpos(sd,mapindex_name2id(str),x,y,CLR_OUTSIGHT); - - if( ret ) { - ShowError("buildin_warp: moving player '%s' to \"%s\",%d,%d failed.\n", sd->status.name, str, x, y); - script_reportsrc(st); - } - - return 0; -} -/*========================================== - * Warp a specified area - *------------------------------------------*/ -static int buildin_areawarp_sub(struct block_list *bl,va_list ap) -{ - int x2,y2,x3,y3; - unsigned int index; - - index = va_arg(ap,unsigned int); - x2 = va_arg(ap,int); - y2 = va_arg(ap,int); - x3 = va_arg(ap,int); - y3 = va_arg(ap,int); - - if(index == 0) - pc_randomwarp((TBL_PC *)bl,CLR_TELEPORT); - else if(x3 && y3) { - int max, tx, ty, j = 0; - - // choose a suitable max number of attempts - if( (max = (y3-y2+1)*(x3-x2+1)*3) > 1000 ) - max = 1000; - - // find a suitable map cell - do { - tx = rnd()%(x3-x2+1)+x2; - ty = rnd()%(y3-y2+1)+y2; - j++; - } while( map_getcell(index,tx,ty,CELL_CHKNOPASS) && j < max ); - - pc_setpos((TBL_PC *)bl,index,tx,ty,CLR_OUTSIGHT); - } - else - pc_setpos((TBL_PC *)bl,index,x2,y2,CLR_OUTSIGHT); - return 0; -} -BUILDIN_FUNC(areawarp) -{ - int16 m, x0,y0,x1,y1, x2,y2,x3=0,y3=0; - unsigned int index; - const char *str; - const char *mapname; - - mapname = script_getstr(st,2); - x0 = script_getnum(st,3); - y0 = script_getnum(st,4); - x1 = script_getnum(st,5); - y1 = script_getnum(st,6); - str = script_getstr(st,7); - x2 = script_getnum(st,8); - y2 = script_getnum(st,9); - - if( script_hasdata(st,10) && script_hasdata(st,11) ) { // Warp area to area - if( (x3 = script_getnum(st,10)) < 0 || (y3 = script_getnum(st,11)) < 0 ){ - x3 = 0; - y3 = 0; - } else if( x3 && y3 ) { - // normalize x3/y3 coordinates - if( x3 < x2 ) swap(x3,x2); - if( y3 < y2 ) swap(y3,y2); - } - } - - if( (m = map_mapname2mapid(mapname)) < 0 ) - return 0; - - if( strcmp(str,"Random") == 0 ) - index = 0; - else if( !(index=mapindex_name2id(str)) ) - return 0; - - map_foreachinarea(buildin_areawarp_sub, m,x0,y0,x1,y1, BL_PC, index,x2,y2,x3,y3); - return 0; -} - -/*========================================== - * areapercentheal <map>,<x1>,<y1>,<x2>,<y2>,<hp>,<sp> - *------------------------------------------*/ -static int buildin_areapercentheal_sub(struct block_list *bl,va_list ap) -{ - int hp, sp; - hp = va_arg(ap, int); - sp = va_arg(ap, int); - pc_percentheal((TBL_PC *)bl,hp,sp); - return 0; -} -BUILDIN_FUNC(areapercentheal) -{ - int hp,sp,m; - const char *mapname; - int x0,y0,x1,y1; - - mapname=script_getstr(st,2); - x0=script_getnum(st,3); - y0=script_getnum(st,4); - x1=script_getnum(st,5); - y1=script_getnum(st,6); - hp=script_getnum(st,7); - sp=script_getnum(st,8); - - if( (m=map_mapname2mapid(mapname))< 0) - return 0; - - map_foreachinarea(buildin_areapercentheal_sub,m,x0,y0,x1,y1,BL_PC,hp,sp); - return 0; -} - -/*========================================== - * warpchar [LuzZza] - * Useful for warp one player from - * another player npc-session. - * Using: warpchar "mapname",x,y,Char_ID; - *------------------------------------------*/ -BUILDIN_FUNC(warpchar) -{ - int x,y,a; - const char *str; - TBL_PC *sd; - - str=script_getstr(st,2); - x=script_getnum(st,3); - y=script_getnum(st,4); - a=script_getnum(st,5); - - sd = map_charid2sd(a); - if( sd == NULL ) - return 0; - - if(strcmp(str, "Random") == 0) - pc_randomwarp(sd, CLR_TELEPORT); - else - if(strcmp(str, "SavePoint") == 0) - pc_setpos(sd, sd->status.save_point.map,sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); - else - pc_setpos(sd, mapindex_name2id(str), x, y, CLR_TELEPORT); - - return 0; -} -/*========================================== - * Warpparty - [Fredzilla] [Paradox924X] - * Syntax: warpparty "to_mapname",x,y,Party_ID,{"from_mapname"}; - * If 'from_mapname' is specified, only the party members on that map will be warped - *------------------------------------------*/ -BUILDIN_FUNC(warpparty) -{ - TBL_PC *sd = NULL; - TBL_PC *pl_sd; - struct party_data* p; - int type; - int mapindex; - int i; - - const char* str = script_getstr(st,2); - int x = script_getnum(st,3); - int y = script_getnum(st,4); - int p_id = script_getnum(st,5); - const char* str2 = NULL; - if ( script_hasdata(st,6) ) - str2 = script_getstr(st,6); - - p = party_search(p_id); - if(!p) - return 0; - - type = ( strcmp(str,"Random")==0 ) ? 0 - : ( strcmp(str,"SavePointAll")==0 ) ? 1 - : ( strcmp(str,"SavePoint")==0 ) ? 2 - : ( strcmp(str,"Leader")==0 ) ? 3 - : 4; - - switch (type) - { - case 3: - for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++); - if (i == MAX_PARTY || !p->data[i].sd) //Leader not found / not online - return 0; - pl_sd = p->data[i].sd; - mapindex = pl_sd->mapindex; - x = pl_sd->bl.x; - y = pl_sd->bl.y; - break; - case 4: - mapindex = mapindex_name2id(str); - break; - case 2: - //"SavePoint" uses save point of the currently attached player - if (( sd = script_rid2sd(st) ) == NULL ) - return 0; - default: - mapindex = 0; - break; - } - - for (i = 0; i < MAX_PARTY; i++) - { - if( !(pl_sd = p->data[i].sd) || pl_sd->status.party_id != p_id ) - continue; - - if( str2 && strcmp(str2, map[pl_sd->bl.m].name) != 0 ) - continue; - - if( pc_isdead(pl_sd) ) - continue; - - switch( type ) - { - case 0: // Random - if(!map[pl_sd->bl.m].flag.nowarp) - pc_randomwarp(pl_sd,CLR_TELEPORT); - break; - case 1: // SavePointAll - if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT); - break; - case 2: // SavePoint - if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - break; - case 3: // Leader - case 4: // m,x,y - if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp) - pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT); - break; - } - } - - return 0; -} -/*========================================== - * Warpguild - [Fredzilla] - * Syntax: warpguild "mapname",x,y,Guild_ID; - *------------------------------------------*/ -BUILDIN_FUNC(warpguild) -{ - TBL_PC *sd = NULL; - TBL_PC *pl_sd; - struct guild* g; - struct s_mapiterator* iter; - int type; - - const char* str = script_getstr(st,2); - int x = script_getnum(st,3); - int y = script_getnum(st,4); - int gid = script_getnum(st,5); - - g = guild_search(gid); - if( g == NULL ) - return 0; - - type = ( strcmp(str,"Random")==0 ) ? 0 - : ( strcmp(str,"SavePointAll")==0 ) ? 1 - : ( strcmp(str,"SavePoint")==0 ) ? 2 - : 3; - - if( type == 2 && ( sd = script_rid2sd(st) ) == NULL ) - {// "SavePoint" uses save point of the currently attached player - return 0; - } - - iter = mapit_getallusers(); - for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) - { - if( pl_sd->status.guild_id != gid ) - continue; - - switch( type ) - { - case 0: // Random - if(!map[pl_sd->bl.m].flag.nowarp) - pc_randomwarp(pl_sd,CLR_TELEPORT); - break; - case 1: // SavePointAll - if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT); - break; - case 2: // SavePoint - if(!map[pl_sd->bl.m].flag.noreturn) - pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - break; - case 3: // m,x,y - if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp) - pc_setpos(pl_sd,mapindex_name2id(str),x,y,CLR_TELEPORT); - break; - } - } - mapit_free(iter); - - return 0; -} -/*========================================== - * Force Heal a player (hp and sp) - *------------------------------------------*/ -BUILDIN_FUNC(heal) -{ - TBL_PC *sd; - int hp,sp; - - sd = script_rid2sd(st); - if (!sd) return 0; - - hp=script_getnum(st,2); - sp=script_getnum(st,3); - status_heal(&sd->bl, hp, sp, 1); - return 0; -} -/*========================================== - * Heal a player by item (get vit bonus etc) - *------------------------------------------*/ -BUILDIN_FUNC(itemheal) -{ - TBL_PC *sd; - int hp,sp; - - hp=script_getnum(st,2); - sp=script_getnum(st,3); - - if(potion_flag==1) { - potion_hp = hp; - potion_sp = sp; - return 0; - } - - sd = script_rid2sd(st); - if (!sd) return 0; - pc_itemheal(sd,sd->itemid,hp,sp); - return 0; -} -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(percentheal) -{ - int hp,sp; - TBL_PC* sd; - - hp=script_getnum(st,2); - sp=script_getnum(st,3); - - if(potion_flag==1) { - potion_per_hp = hp; - potion_per_sp = sp; - return 0; - } - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; -#ifdef RENEWAL - if( sd->sc.data[SC_EXTREMITYFIST2] ) - sp = 0; -#endif - pc_percentheal(sd,hp,sp); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(jobchange) -{ - int job, upper=-1; - - job=script_getnum(st,2); - if( script_hasdata(st,3) ) - upper=script_getnum(st,3); - - if (pcdb_checkid(job)) - { - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - pc_jobchange(sd, job, upper); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(jobname) -{ - int class_=script_getnum(st,2); - script_pushconststr(st, (char*)job_name(class_)); - return 0; -} - -/// Get input from the player. -/// For numeric inputs the value is capped to the range [min,max]. Returns 1 if -/// the value was higher than 'max', -1 if lower than 'min' and 0 otherwise. -/// For string inputs it returns 1 if the string was longer than 'max', -1 is -/// shorter than 'min' and 0 otherwise. -/// -/// input(<var>{,<min>{,<max>}}) -> <int> -BUILDIN_FUNC(input) -{ - TBL_PC* sd; - struct script_data* data; - int uid; - const char* name; - int min; - int max; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - data = script_getdata(st,2); - if( !data_isreference(data) ){ - ShowError("script:input: not a variable\n"); - script_reportdata(data); - st->state = END; - return 1; - } - uid = reference_getuid(data); - name = reference_getname(data); - min = (script_hasdata(st,3) ? script_getnum(st,3) : script_config.input_min_value); - max = (script_hasdata(st,4) ? script_getnum(st,4) : script_config.input_max_value); - - if( !sd->state.menu_or_input ) - { // first invocation, display npc input box - sd->state.menu_or_input = 1; - st->state = RERUNLINE; - if( is_string_variable(name) ) - clif_scriptinputstr(sd,st->oid); - else - clif_scriptinput(sd,st->oid); - } - else - { // take received text/value and store it in the designated variable - sd->state.menu_or_input = 0; - if( is_string_variable(name) ) - { - int len = (int)strlen(sd->npc_str); - set_reg(st, sd, uid, name, (void*)sd->npc_str, script_getref(st,2)); - script_pushint(st, (len > max ? 1 : len < min ? -1 : 0)); - } - else - { - int amount = sd->npc_amount; - set_reg(st, sd, uid, name, (void*)__64BPRTSIZE(cap_value(amount,min,max)), script_getref(st,2)); - script_pushint(st, (amount > max ? 1 : amount < min ? -1 : 0)); - } - st->state = RUN; - } - return 0; -} - -// declare the copyarray method here for future reference -BUILDIN_FUNC(copyarray); - -/// Sets the value of a variable. -/// The value is converted to the type of the variable. -/// -/// set(<variable>,<value>) -> <variable> -BUILDIN_FUNC(set) -{ - TBL_PC* sd = NULL; - struct script_data* data; - //struct script_data* datavalue; - int num; - const char* name; - char prefix; - - data = script_getdata(st,2); - //datavalue = script_getdata(st,3); - if( !data_isreference(data) ) - { - ShowError("script:set: not a variable\n"); - script_reportdata(script_getdata(st,2)); - st->state = END; - return 1; - } - - num = reference_getuid(data); - name = reference_getname(data); - prefix = *name; - - if( not_server_variable(prefix) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - { - ShowError("script:set: no player attached for player variable '%s'\n", name); - return 0; - } - } - -#if 0 - if( data_isreference(datavalue) ) - {// the value being referenced is a variable - const char* namevalue = reference_getname(datavalue); - - if( !not_array_variable(*namevalue) ) - {// array variable being copied into another array variable - if( sd == NULL && not_server_variable(*namevalue) && !(sd = script_rid2sd(st)) ) - {// player must be attached in order to copy a player variable - ShowError("script:set: no player attached for player variable '%s'\n", namevalue); - return 0; - } - - if( is_string_variable(namevalue) != is_string_variable(name) ) - {// non-matching array value types - ShowWarning("script:set: two array variables do not match in type.\n"); - return 0; - } - - // push the maximum number of array values to the stack - push_val(st->stack, C_INT, SCRIPT_MAX_ARRAYSIZE); - - // call the copy array method directly - return buildin_copyarray(st); - } - } -#endif - - if( is_string_variable(name) ) - set_reg(st,sd,num,name,(void*)script_getstr(st,3),script_getref(st,2)); - else - set_reg(st,sd,num,name,(void*)__64BPRTSIZE(script_getnum(st,3)),script_getref(st,2)); - - // return a copy of the variable reference - script_pushcopy(st,2); - - return 0; -} - -///////////////////////////////////////////////////////////////////// -/// Array variables -/// - -/// Returns the size of the specified array -static int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isstring, struct DBMap** ref) -{ - int32 ret = idx; - - if( isstring ) - { - for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx ) - { - char* str = (char*)get_val2(st, reference_uid(id, idx), ref); - if( str && *str ) - ret = idx + 1; - script_removetop(st, -1, 0); - } - } - else - { - for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx ) - { - int32 num = (int32)__64BPRTSIZE(get_val2(st, reference_uid(id, idx), ref)); - if( num ) - ret = idx + 1; - script_removetop(st, -1, 0); - } - } - return ret; -} - -/// Sets values of an array, from the starting index. -/// ex: setarray arr[1],1,2,3; -/// -/// setarray <array variable>,<value1>{,<value2>...}; -BUILDIN_FUNC(setarray) -{ - struct script_data* data; - const char* name; - int32 start; - int32 end; - int32 id; - int32 i; - TBL_PC* sd = NULL; - - data = script_getdata(st, 2); - if( !data_isreference(data) ) - { - ShowError("script:setarray: 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:setarray: illegal scope\n"); - script_reportdata(data); - st->state = END; - return 1;// not supported - } - - if( not_server_variable(*name) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached - } - - end = start + script_lastdata(st) - 2; - if( end > SCRIPT_MAX_ARRAYSIZE ) - end = SCRIPT_MAX_ARRAYSIZE; - - if( is_string_variable(name) ) - {// string array - for( i = 3; start < end; ++start, ++i ) - set_reg(st, sd, reference_uid(id, start), name, (void*)script_getstr(st,i), reference_getref(data)); - } - else - {// int array - for( i = 3; start < end; ++start, ++i ) - set_reg(st, sd, reference_uid(id, start), name, (void*)__64BPRTSIZE(script_getnum(st,i)), reference_getref(data)); - } - return 0; -} - -/// Sets count values of an array, from the starting index. -/// ex: cleararray arr[0],0,1; -/// -/// cleararray <array variable>,<value>,<count>; -BUILDIN_FUNC(cleararray) -{ - struct script_data* data; - const char* name; - int32 start; - int32 end; - int32 id; - void* v; - TBL_PC* sd = NULL; - - data = script_getdata(st, 2); - if( !data_isreference(data) ) - { - ShowError("script:cleararray: 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:cleararray: illegal scope\n"); - script_reportdata(data); - st->state = END; - return 1;// not supported - } - - if( not_server_variable(*name) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached - } - - if( is_string_variable(name) ) - v = (void*)script_getstr(st, 3); - else - v = (void*)__64BPRTSIZE(script_getnum(st, 3)); - - end = start + script_getnum(st, 4); - if( end > SCRIPT_MAX_ARRAYSIZE ) - end = SCRIPT_MAX_ARRAYSIZE; - - for( ; start < end; ++start ) - set_reg(st, sd, reference_uid(id, start), name, v, script_getref(st,2)); - return 0; -} - -/// Copies data from one array to another. -/// ex: copyarray arr[0],arr[2],2; -/// -/// copyarray <destination array variable>,<source array variable>,<count>; -BUILDIN_FUNC(copyarray) -{ - struct script_data* data1; - struct script_data* data2; - const char* name1; - const char* name2; - int32 idx1; - int32 idx2; - int32 id1; - int32 id2; - void* v; - int32 i; - int32 count; - TBL_PC* sd = NULL; - - data1 = script_getdata(st, 2); - data2 = script_getdata(st, 3); - if( !data_isreference(data1) || !data_isreference(data2) ) - { - ShowError("script:copyarray: not a variable\n"); - script_reportdata(data1); - script_reportdata(data2); - st->state = END; - return 1;// not a variable - } - - id1 = reference_getid(data1); - id2 = reference_getid(data2); - idx1 = reference_getindex(data1); - idx2 = reference_getindex(data2); - name1 = reference_getname(data1); - name2 = reference_getname(data2); - if( not_array_variable(*name1) || not_array_variable(*name2) ) - { - ShowError("script:copyarray: illegal scope\n"); - script_reportdata(data1); - script_reportdata(data2); - st->state = END; - return 1;// not supported - } - - if( is_string_variable(name1) != is_string_variable(name2) ) - { - ShowError("script:copyarray: type mismatch\n"); - script_reportdata(data1); - script_reportdata(data2); - st->state = END; - return 1;// data type mismatch - } - - if( not_server_variable(*name1) || not_server_variable(*name2) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached - } - - count = script_getnum(st, 4); - if( count > SCRIPT_MAX_ARRAYSIZE - idx1 ) - count = SCRIPT_MAX_ARRAYSIZE - idx1; - if( count <= 0 || (id1 == id2 && idx1 == idx2) ) - return 0;// nothing to copy - - if( id1 == id2 && idx1 > idx2 ) - {// destination might be overlapping the source - copy in reverse order - for( i = count - 1; i >= 0; --i ) - { - v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2)); - set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1)); - script_removetop(st, -1, 0); - } - } - else - {// normal copy - for( i = 0; i < count; ++i ) - { - if( idx2 + i < SCRIPT_MAX_ARRAYSIZE ) - { - v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2)); - set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1)); - script_removetop(st, -1, 0); - } - else// out of range - assume ""/0 - set_reg(st, sd, reference_uid(id1, idx1 + i), name1, (is_string_variable(name1)?(void*)"":(void*)0), reference_getref(data1)); - } - } - return 0; -} - -/// Returns the size of the array. -/// Assumes that everything before the starting index exists. -/// ex: getarraysize(arr[3]) -/// -/// getarraysize(<array variable>) -> <int> -BUILDIN_FUNC(getarraysize) -{ - struct script_data* data; - const char* name; - - data = script_getdata(st, 2); - if( !data_isreference(data) ) - { - ShowError("script:getarraysize: not a variable\n"); - script_reportdata(data); - script_pushnil(st); - st->state = END; - return 1;// not a variable - } - - name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:getarraysize: illegal scope\n"); - script_reportdata(data); - script_pushnil(st); - st->state = END; - return 1;// not supported - } - - script_pushint(st, getarraysize(st, reference_getid(data), reference_getindex(data), is_string_variable(name), reference_getref(data))); - return 0; -} - -/// Deletes count or all the elements in an array, from the starting index. -/// ex: deletearray arr[4],2; -/// -/// deletearray <array variable>; -/// deletearray <array variable>,<count>; -BUILDIN_FUNC(deletearray) -{ - struct script_data* data; - const char* name; - int start; - int end; - int id; - TBL_PC *sd = NULL; - - data = script_getdata(st, 2); - if( !data_isreference(data) ) - { - ShowError("script:deletearray: 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:deletearray: illegal scope\n"); - script_reportdata(data); - st->state = END; - return 1;// not supported - } - - if( not_server_variable(*name) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached - } - - end = SCRIPT_MAX_ARRAYSIZE; - - if( start >= end ) - return 0;// nothing to free - - if( script_hasdata(st,3) ) - { - int count = script_getnum(st, 3); - if( count > end - start ) - count = end - start; - if( count <= 0 ) - return 0;// nothing to free - - // move rest of the elements backward - for( ; start + count < end; ++start ) - { - void* v = get_val2(st, reference_uid(id, start + count), reference_getref(data)); - set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data)); - script_removetop(st, -1, 0); - } - } - - // clear the rest of the array - if( is_string_variable(name) ) - { - for( ; start < end; ++start ) - set_reg(st, sd, reference_uid(id, start), name, (void *)"", reference_getref(data)); - } - else - { - for( ; start < end; ++start ) - set_reg(st, sd, reference_uid(id, start), name, (void*)0, reference_getref(data)); - } - return 0; -} - -/// Returns a reference to the target index of the array variable. -/// Equivalent to var[index]. -/// -/// getelementofarray(<array variable>,<index>) -> <variable reference> -BUILDIN_FUNC(getelementofarray) -{ - struct script_data* data; - const char* name; - int32 id; - int i; - - data = script_getdata(st, 2); - if( !data_isreference(data) ) - { - ShowError("script:getelementofarray: not a variable\n"); - script_reportdata(data); - script_pushnil(st); - st->state = END; - return 1;// not a variable - } - - id = reference_getid(data); - name = reference_getname(data); - if( not_array_variable(*name) ) - { - ShowError("script:getelementofarray: illegal scope\n"); - script_reportdata(data); - script_pushnil(st); - st->state = END; - return 1;// not supported - } - - i = script_getnum(st, 3); - if( i < 0 || i >= SCRIPT_MAX_ARRAYSIZE ) - { - ShowWarning("script:getelementofarray: index out of range (%d)\n", i); - script_reportdata(data); - script_pushnil(st); - st->state = END; - return 1;// out of range - } - - push_val2(st->stack, C_NAME, reference_uid(id, i), reference_getref(data)); - return 0; -} - -///////////////////////////////////////////////////////////////////// -/// ... -/// - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(setlook) -{ - int type,val; - TBL_PC* sd; - - type=script_getnum(st,2); - val=script_getnum(st,3); - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - pc_changelook(sd,type,val); - - return 0; -} - -BUILDIN_FUNC(changelook) -{ // As setlook but only client side - int type,val; - TBL_PC* sd; - - type=script_getnum(st,2); - val=script_getnum(st,3); - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - clif_changelook(&sd->bl,type,val); - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(cutin) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - clif_cutin(sd,script_getstr(st,2),script_getnum(st,3)); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(viewpoint) -{ - int type,x,y,id,color; - TBL_PC* sd; - - type=script_getnum(st,2); - x=script_getnum(st,3); - y=script_getnum(st,4); - id=script_getnum(st,5); - color=script_getnum(st,6); - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - clif_viewpoint(sd,st->oid,type,x,y,id,color); - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(countitem) -{ - int nameid, i; - int count = 0; - struct item_data* id = NULL; - struct script_data* data; - - TBL_PC* sd = script_rid2sd(st); - if (!sd) { - script_pushint(st,0); - return 0; - } - - data = script_getdata(st,2); - get_val(st, data); // convert into value in case of a variable - - if( data_isstring(data) ) - {// item name - id = itemdb_searchname(conv_str(st, data)); - } - else - {// item id - id = itemdb_exists(conv_num(st, data)); - } - - if( id == NULL ) - { - ShowError("buildin_countitem: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was - script_pushint(st,0); - return 1; - } - - nameid = id->nameid; - - for(i = 0; i < MAX_INVENTORY; i++) - if(sd->status.inventory[i].nameid == nameid) - count += sd->status.inventory[i].amount; - - script_pushint(st,count); - return 0; -} - -/*========================================== - * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus] - * returns number of items that meet the conditions - *------------------------------------------*/ -BUILDIN_FUNC(countitem2) -{ - int nameid, iden, ref, attr, c1, c2, c3, c4; - int count = 0; - int i; - struct item_data* id = NULL; - struct script_data* data; - - TBL_PC* sd = script_rid2sd(st); - if (!sd) { - script_pushint(st,0); - return 0; - } - - data = script_getdata(st,2); - get_val(st, data); // convert into value in case of a variable - - if( data_isstring(data) ) - {// item name - id = itemdb_searchname(conv_str(st, data)); - } - else - {// item id - id = itemdb_exists(conv_num(st, data)); - } - - if( id == NULL ) - { - ShowError("buildin_countitem2: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was - script_pushint(st,0); - return 1; - } - - nameid = id->nameid; - iden = script_getnum(st,3); - ref = script_getnum(st,4); - attr = script_getnum(st,5); - c1 = (short)script_getnum(st,6); - c2 = (short)script_getnum(st,7); - c3 = (short)script_getnum(st,8); - c4 = (short)script_getnum(st,9); - - for(i = 0; i < MAX_INVENTORY; i++) - if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] != NULL && - sd->status.inventory[i].amount > 0 && sd->status.inventory[i].nameid == nameid && - sd->status.inventory[i].identify == iden && sd->status.inventory[i].refine == ref && - sd->status.inventory[i].attribute == attr && sd->status.inventory[i].card[0] == c1 && - sd->status.inventory[i].card[1] == c2 && sd->status.inventory[i].card[2] == c3 && - sd->status.inventory[i].card[3] == c4 - ) - count += sd->status.inventory[i].amount; - - script_pushint(st,count); - return 0; -} - -/*========================================== - * Check if item with this amount can fit in inventory - * Checking : weight, stack amount >32k, slots amount >(MAX_INVENTORY) - * Return - * 0 : fail - * 1 : success (npc side only) - *------------------------------------------*/ -BUILDIN_FUNC(checkweight) -{ - int nameid, amount, slots, amount2=0; - unsigned int weight=0, i, nbargs; - struct item_data* id = NULL; - struct map_session_data* sd; - struct script_data* data; - - if( ( sd = script_rid2sd(st) ) == NULL ){ - return 0; - } - nbargs = script_lastdata(st)+1; - if(nbargs%2){ - ShowError("buildin_checkweight: Invalid nb of args should be a multiple of 2.\n"); - script_pushint(st,0); - return 1; - } - slots = pc_inventoryblank(sd); //nb of empty slot - - for(i=2; i<nbargs; i=i+2){ - data = script_getdata(st,i); - get_val(st, data); // convert into value in case of a variable - if( data_isstring(data) ){// item name - id = itemdb_searchname(conv_str(st, data)); - } else {// item id - id = itemdb_exists(conv_num(st, data)); - } - if( id == NULL ) { - ShowError("buildin_checkweight: Invalid item '%s'.\n", script_getstr(st,i)); // returns string, regardless of what it was - script_pushint(st,0); - return 1; - } - nameid = id->nameid; - - amount = script_getnum(st,i+1); - if( amount < 1 ) { - ShowError("buildin_checkweight: Invalid amount '%d'.\n", amount); - script_pushint(st,0); - return 1; - } - - weight += itemdb_weight(nameid)*amount; //total weight for all chk - if( weight + sd->weight > sd->max_weight ) - {// too heavy - script_pushint(st,0); - return 0; - } - - switch( pc_checkadditem(sd, nameid, amount) ) - { - case ADDITEM_EXIST: - // item is already in inventory, but there is still space for the requested amount - break; - case ADDITEM_NEW: - if( itemdb_isstackable(nameid) ) {// stackable - amount2++; - if( slots < amount2 ) { - script_pushint(st,0); - return 0; - } - } - else {// non-stackable - amount2 += amount; - if( slots < amount2){ - script_pushint(st,0); - return 0; - } - } - break; - case ADDITEM_OVERAMOUNT: - script_pushint(st,0); - return 0; - } - } - script_pushint(st,1); - return 0; -} - -BUILDIN_FUNC(checkweight2) -{ - //variable sub checkweight - int32 nameid=-1, amount=-1; - int i=0, amount2=0, slots=0, weight=0; - short fail=0; - - //variable for array parsing - struct script_data* data_it; - struct script_data* data_nb; - const char* name_it; - const char* name_nb; - int32 id_it, id_nb; - int32 idx_it, idx_nb; - int nb_it, nb_nb; //array size - - TBL_PC *sd = script_rid2sd(st); - nullpo_retr(1,sd); - - data_it = script_getdata(st, 2); - data_nb = script_getdata(st, 3); - - if( !data_isreference(data_it) || !data_isreference(data_nb)) - { - ShowError("script:checkweight2: parameter not a variable\n"); - script_pushint(st,0); - return 1;// not a variable - } - id_it = reference_getid(data_it); - id_nb = reference_getid(data_nb); - idx_it = reference_getindex(data_it); - idx_nb = reference_getindex(data_nb); - name_it = reference_getname(data_it); - name_nb = reference_getname(data_nb); - - if( not_array_variable(*name_it) || not_array_variable(*name_nb)) - { - ShowError("script:checkweight2: illegal scope\n"); - script_pushint(st,0); - return 1;// not supported - } - if(is_string_variable(name_it) || is_string_variable(name_nb)){ - ShowError("script:checkweight2: illegal type, need int\n"); - script_pushint(st,0); - return 1;// not supported - } - nb_it = getarraysize(st, id_it, idx_it, 0, reference_getref(data_it)); - nb_nb = getarraysize(st, id_nb, idx_nb, 0, reference_getref(data_nb)); - if(nb_it != nb_nb){ - ShowError("Size mistmatch: nb_it=%d, nb_nb=%d\n",nb_it,nb_nb); - fail = 1; - } - - slots = pc_inventoryblank(sd); - for(i=0; i<nb_it; i++){ - nameid = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_it,idx_it+i),reference_getref(data_it))); - script_removetop(st, -1, 0); - amount = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_nb,idx_nb+i),reference_getref(data_nb))); - script_removetop(st, -1, 0); - if(fail) continue; //cpntonie to depop rest - - if(itemdb_exists(nameid) == NULL ){ - ShowError("buildin_checkweight2: Invalid item '%d'.\n", nameid); - fail=1; - continue; - } - if(amount < 0 ){ - ShowError("buildin_checkweight2: Invalid amount '%d'.\n", amount); - fail = 1; - continue; - } - weight += itemdb_weight(nameid)*amount; - if( weight + sd->weight > sd->max_weight ){ - fail = 1; - continue; - } - switch( pc_checkadditem(sd, nameid, amount) ) { - case ADDITEM_EXIST: - // item is already in inventory, but there is still space for the requested amount - break; - case ADDITEM_NEW: - if( itemdb_isstackable(nameid) ){// stackable - amount2++; - if( slots < amount2 ) - fail = 1; - } - else {// non-stackable - amount2 += amount; - if( slots < amount2 ){ - fail = 1; - } - } - break; - case ADDITEM_OVERAMOUNT: - fail = 1; - } //end switch - } //end loop DO NOT break it prematurly we need to depop all stack - - fail?script_pushint(st,0):script_pushint(st,1); - return 0; -} - -/*========================================== - * getitem <item id>,<amount>{,<account ID>}; - * getitem "<item name>",<amount>{,<account ID>}; - *------------------------------------------*/ -BUILDIN_FUNC(getitem) -{ - int nameid,amount,get_count,i,flag = 0; - struct item it; - TBL_PC *sd; - struct script_data *data; - - data=script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ) - {// "<item name>" - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data == NULL ){ - ShowError("buildin_getitem: Nonexistant item %s requested.\n", name); - return 1; //No item created. - } - nameid=item_data->nameid; - } else if( data_isint(data) ) - {// <item id> - nameid=conv_num(st,data); - //Violet Box, Blue Box, etc - random item pick - if( nameid < 0 ) { - nameid = -nameid; - flag = 1; - } - if( nameid <= 0 || !itemdb_exists(nameid) ){ - ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid); - return 1; //No item created. - } - } else { - ShowError("buildin_getitem: invalid data type for argument #1 (%d).", data->type); - return 1; - } - - // <amount> - if( (amount=script_getnum(st,3)) <= 0) - return 0; //return if amount <=0, skip the useles iteration - - memset(&it,0,sizeof(it)); - it.nameid=nameid; - if(!flag) - it.identify=1; - else - it.identify=itemdb_isidentified(nameid); - - if( script_hasdata(st,4) ) - sd=map_id2sd(script_getnum(st,4)); // <Account ID> - else - sd=script_rid2sd(st); // Attached player - - if( sd == NULL ) // no target - return 0; - - //Check if it's stackable. - if (!itemdb_isstackable(nameid)) - get_count = 1; - else - get_count = amount; - - for (i = 0; i < amount; i += get_count) - { - // if not pet egg - if (!pet_create_egg(sd, nameid)) - { - if ((flag = pc_additem(sd, &it, get_count, LOG_TYPE_SCRIPT))) - { - clif_additem(sd, 0, 0, flag); - if( pc_candrop(sd,&it) ) - map_addflooritem(&it,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(getitem2) -{ - int nameid,amount,get_count,i,flag = 0; - int iden,ref,attr,c1,c2,c3,c4; - struct item_data *item_data; - struct item item_tmp; - TBL_PC *sd; - struct script_data *data; - - if( script_hasdata(st,11) ) - sd=map_id2sd(script_getnum(st,11)); // <Account ID> - else - sd=script_rid2sd(st); // Attached player - - if( sd == NULL ) // no target - return 0; - - data=script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data ) - nameid=item_data->nameid; - else - nameid=UNKNOWN_ITEM_ID; - }else - nameid=conv_num(st,data); - - amount=script_getnum(st,3); - iden=script_getnum(st,4); - ref=script_getnum(st,5); - attr=script_getnum(st,6); - c1=(short)script_getnum(st,7); - c2=(short)script_getnum(st,8); - c3=(short)script_getnum(st,9); - c4=(short)script_getnum(st,10); - - if(nameid<0) { // Invalide nameid - nameid = -nameid; - flag = 1; - } - - if(nameid > 0) { - memset(&item_tmp,0,sizeof(item_tmp)); - item_data=itemdb_exists(nameid); - if (item_data == NULL) - return -1; - if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR){ - if(ref > MAX_REFINE) ref = MAX_REFINE; - } - else if(item_data->type==IT_PETEGG) { - iden = 1; - ref = 0; - } - else { - iden = 1; - ref = attr = 0; - } - - item_tmp.nameid=nameid; - if(!flag) - item_tmp.identify=iden; - else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR) - item_tmp.identify=0; - item_tmp.refine=ref; - item_tmp.attribute=attr; - item_tmp.card[0]=(short)c1; - item_tmp.card[1]=(short)c2; - item_tmp.card[2]=(short)c3; - item_tmp.card[3]=(short)c4; - - //Check if it's stackable. - if (!itemdb_isstackable(nameid)) - get_count = 1; - else - get_count = amount; - - for (i = 0; i < amount; i += get_count) - { - // if not pet egg - if (!pet_create_egg(sd, nameid)) - { - if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) - { - clif_additem(sd, 0, 0, flag); - if( pc_candrop(sd,&item_tmp) ) - map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - } - - return 0; -} - -/*========================================== - * rentitem <item id>,<seconds> - * rentitem "<item name>",<seconds> - *------------------------------------------*/ -BUILDIN_FUNC(rentitem) -{ - struct map_session_data *sd; - struct script_data *data; - struct item it; - int seconds; - int nameid = 0, flag; - - data = script_getdata(st,2); - get_val(st,data); - - if( (sd = script_rid2sd(st)) == NULL ) - return 0; - - if( data_isstring(data) ) - { - const char *name = conv_str(st,data); - struct item_data *itd = itemdb_searchname(name); - if( itd == NULL ) - { - ShowError("buildin_rentitem: Nonexistant item %s requested.\n", name); - return 1; - } - nameid = itd->nameid; - } - else if( data_isint(data) ) - { - nameid = conv_num(st,data); - if( nameid <= 0 || !itemdb_exists(nameid) ) - { - ShowError("buildin_rentitem: Nonexistant item %d requested.\n", nameid); - return 1; - } - } - else - { - ShowError("buildin_rentitem: invalid data type for argument #1 (%d).\n", data->type); - return 1; - } - - seconds = script_getnum(st,3); - memset(&it, 0, sizeof(it)); - it.nameid = nameid; - it.identify = 1; - it.expire_time = (unsigned int)(time(NULL) + seconds); - - if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) ) - { - clif_additem(sd, 0, 0, flag); - return 1; - } - - return 0; -} - -/*========================================== - * gets an item with someone's name inscribed [Skotlex] - * getinscribeditem item_num, character_name - * Returned Qty is always 1, only works on equip-able - * equipment - *------------------------------------------*/ -BUILDIN_FUNC(getnameditem) -{ - int nameid; - struct item item_tmp; - TBL_PC *sd, *tsd; - struct script_data *data; - - sd = script_rid2sd(st); - if (sd == NULL) - { //Player not attached! - script_pushint(st,0); - return 0; - } - - data=script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data == NULL) - { //Failed - script_pushint(st,0); - return 0; - } - nameid = item_data->nameid; - }else - nameid = conv_num(st,data); - - if(!itemdb_exists(nameid)/* || itemdb_isstackable(nameid)*/) - { //Even though named stackable items "could" be risky, they are required for certain quests. - script_pushint(st,0); - return 0; - } - - data=script_getdata(st,3); - get_val(st,data); - if( data_isstring(data) ) //Char Name - tsd=map_nick2sd(conv_str(st,data)); - else //Char Id was given - tsd=map_charid2sd(conv_num(st,data)); - - if( tsd == NULL ) - { //Failed - script_pushint(st,0); - return 0; - } - - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - item_tmp.amount=1; - item_tmp.identify=1; - item_tmp.card[0]=CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] - item_tmp.card[2]=tsd->status.char_id; - item_tmp.card[3]=tsd->status.char_id >> 16; - if(pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT)) { - script_pushint(st,0); - return 0; //Failed to add item, we will not drop if they don't fit - } - - script_pushint(st,1); - return 0; -} - -/*========================================== - * gets a random item ID from an item group [Skotlex] - * groupranditem group_num - *------------------------------------------*/ -BUILDIN_FUNC(grouprandomitem) -{ - int group; - - group = script_getnum(st,2); - script_pushint(st,itemdb_searchrandomid(group)); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(makeitem) -{ - int nameid,amount,flag = 0; - int x,y,m; - const char *mapname; - struct item item_tmp; - struct script_data *data; - - data=script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data ) - nameid=item_data->nameid; - else - nameid=UNKNOWN_ITEM_ID; - }else - nameid=conv_num(st,data); - - amount=script_getnum(st,3); - mapname =script_getstr(st,4); - x =script_getnum(st,5); - y =script_getnum(st,6); - - if(strcmp(mapname,"this")==0) - { - TBL_PC *sd; - sd = script_rid2sd(st); - if (!sd) return 0; //Failed... - m=sd->bl.m; - } else - m=map_mapname2mapid(mapname); - - if(nameid<0) { - nameid = -nameid; - flag = 1; - } - - if(nameid > 0) { - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - if(!flag) - item_tmp.identify=1; - else - item_tmp.identify=itemdb_isidentified(nameid); - - map_addflooritem(&item_tmp,amount,m,x,y,0,0,0,0); - } - - return 0; -} - - -/// Counts / deletes the current item given by idx. -/// Used by buildin_delitem_search -/// Relies on all input data being already fully valid. -static void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, bool delete_items) -{ - int delamount; - struct item* inv = &sd->status.inventory[idx]; - - delamount = ( amount[0] < inv->amount ) ? amount[0] : inv->amount; - - if( delete_items ) - { - if( sd->inventory_data[idx]->type == IT_PETEGG && inv->card[0] == CARD0_PET ) - {// delete associated pet - intif_delete_petdata(MakeDWord(inv->card[1], inv->card[2])); - } - pc_delitem(sd, idx, delamount, 0, 0, LOG_TYPE_SCRIPT); - } - - amount[0]-= delamount; -} - - -/// Searches for item(s) and checks, if there is enough of them. -/// Used by delitem and delitem2 -/// Relies on all input data being already fully valid. -/// @param exact_match will also match item attributes and cards, not just name id -/// @return true when all items could be deleted, false when there were not enough items to delete -static bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool exact_match) -{ - bool delete_items = false; - int i, amount, important; - struct item* inv; - - // prefer always non-equipped items - it->equip = 0; - - // when searching for nameid only, prefer additionally - if( !exact_match ) - { - // non-refined items - it->refine = 0; - // card-less items - memset(it->card, 0, sizeof(it->card)); - } - - for(;;) - { - amount = it->amount; - important = 0; - - // 1st pass -- less important items / exact match - for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ ) - { - inv = &sd->status.inventory[i]; - - if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid ) - {// wrong/invalid item - continue; - } - - if( inv->equip != it->equip || inv->refine != it->refine ) - {// not matching attributes - important++; - continue; - } - - if( exact_match ) - { - if( inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) ) - {// not matching exact attributes - continue; - } - } - else - { - if( sd->inventory_data[i]->type == IT_PETEGG ) - { - if( inv->card[0] == CARD0_PET && CheckForCharServer() ) - {// pet which cannot be deleted - continue; - } - } - else if( memcmp(inv->card, it->card, sizeof(inv->card)) ) - {// named/carded item - important++; - continue; - } - } - - // count / delete item - buildin_delitem_delete(sd, i, &amount, delete_items); - } - - // 2nd pass -- any matching item - if( amount == 0 || important == 0 ) - {// either everything was already consumed or no items were skipped - ; - } - else for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ ) - { - inv = &sd->status.inventory[i]; - - if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid ) - {// wrong/invalid item - continue; - } - - if( sd->inventory_data[i]->type == IT_PETEGG && inv->card[0] == CARD0_PET && CheckForCharServer() ) - {// pet which cannot be deleted - continue; - } - - if( exact_match ) - { - if( inv->refine != it->refine || inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) ) - {// not matching attributes - continue; - } - } - - // count / delete item - buildin_delitem_delete(sd, i, &amount, delete_items); - } - - if( amount ) - {// not enough items - return false; - } - else if( delete_items ) - {// we are done with the work - return true; - } - else - {// get rid of the items now - delete_items = true; - } - } -} - - -/// Deletes items from the target/attached player. -/// Prioritizes ordinary items. -/// -/// delitem <item id>,<amount>{,<account id>} -/// delitem "<item name>",<amount>{,<account id>} -BUILDIN_FUNC(delitem) -{ - TBL_PC *sd; - struct item it; - struct script_data *data; - - if( script_hasdata(st,4) ) - { - int account_id = script_getnum(st,4); - sd = map_id2sd(account_id); // <account id> - if( sd == NULL ) - { - ShowError("script:delitem: player not found (AID=%d).\n", account_id); - st->state = END; - return 1; - } - } - else - { - sd = script_rid2sd(st);// attached player - if( sd == NULL ) - return 0; - } - - data = script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ) - { - const char* item_name = conv_str(st,data); - struct item_data* id = itemdb_searchname(item_name); - if( id == NULL ) - { - ShowError("script:delitem: unknown item \"%s\".\n", item_name); - st->state = END; - return 1; - } - it.nameid = id->nameid;// "<item name>" - } - else - { - it.nameid = conv_num(st,data);// <item id> - if( !itemdb_exists( it.nameid ) ) - { - ShowError("script:delitem: unknown item \"%d\".\n", it.nameid); - st->state = END; - return 1; - } - } - - it.amount=script_getnum(st,3); - - if( it.amount <= 0 ) - return 0;// nothing to do - - if( buildin_delitem_search(sd, &it, false) ) - {// success - return 0; - } - - ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid); - st->state = END; - clif_scriptclose(sd, st->oid); - return 1; -} - -/// Deletes items from the target/attached player. -/// -/// delitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>} -/// delitem2 "<Item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>} -BUILDIN_FUNC(delitem2) -{ - TBL_PC *sd; - struct item it; - struct script_data *data; - - if( script_hasdata(st,11) ) - { - int account_id = script_getnum(st,11); - sd = map_id2sd(account_id); // <account id> - if( sd == NULL ) - { - ShowError("script:delitem2: player not found (AID=%d).\n", account_id); - st->state = END; - return 1; - } - } - else - { - sd = script_rid2sd(st);// attached player - if( sd == NULL ) - return 0; - } - - data = script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ) - { - const char* item_name = conv_str(st,data); - struct item_data* id = itemdb_searchname(item_name); - if( id == NULL ) - { - ShowError("script:delitem2: unknown item \"%s\".\n", item_name); - st->state = END; - return 1; - } - it.nameid = id->nameid;// "<item name>" - } - else - { - it.nameid = conv_num(st,data);// <item id> - if( !itemdb_exists( it.nameid ) ) - { - ShowError("script:delitem: unknown item \"%d\".\n", it.nameid); - st->state = END; - return 1; - } - } - - it.amount=script_getnum(st,3); - it.identify=script_getnum(st,4); - it.refine=script_getnum(st,5); - it.attribute=script_getnum(st,6); - it.card[0]=(short)script_getnum(st,7); - it.card[1]=(short)script_getnum(st,8); - it.card[2]=(short)script_getnum(st,9); - it.card[3]=(short)script_getnum(st,10); - - if( it.amount <= 0 ) - return 0;// nothing to do - - if( buildin_delitem_search(sd, &it, true) ) - {// success - return 0; - } - - ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid); - st->state = END; - clif_scriptclose(sd, st->oid); - return 1; -} - -/*========================================== - * Enables/Disables use of items while in an NPC [Skotlex] - *------------------------------------------*/ -BUILDIN_FUNC(enableitemuse) -{ - TBL_PC *sd; - sd=script_rid2sd(st); - if (sd) - sd->npc_item_flag = st->oid; - return 0; -} - -BUILDIN_FUNC(disableitemuse) -{ - TBL_PC *sd; - sd=script_rid2sd(st); - if (sd) - sd->npc_item_flag = 0; - return 0; -} - -/*========================================== - * return the basic stats of sd - * chk pc_readparam for available type - *------------------------------------------*/ -BUILDIN_FUNC(readparam) -{ - int type; - TBL_PC *sd; - - type=script_getnum(st,2); - if( script_hasdata(st,3) ) - sd=map_nick2sd(script_getstr(st,3)); - else - sd=script_rid2sd(st); - - if(sd==NULL){ - script_pushint(st,-1); - return 0; - } - - script_pushint(st,pc_readparam(sd,type)); - - return 0; -} - -/*========================================== - * Return charid identification - * return by @num : - * 0 : char_id - * 1 : party_id - * 2 : guild_id - * 3 : account_id - * 4 : bg_id - *------------------------------------------*/ -BUILDIN_FUNC(getcharid) -{ - int num; - TBL_PC *sd; - - num = script_getnum(st,2); - if( script_hasdata(st,3) ) - sd=map_nick2sd(script_getstr(st,3)); - else - sd=script_rid2sd(st); - - if(sd==NULL){ - script_pushint(st,0); //return 0, according docs - return 0; - } - - switch( num ) { - case 0: script_pushint(st,sd->status.char_id); break; - case 1: script_pushint(st,sd->status.party_id); break; - case 2: script_pushint(st,sd->status.guild_id); break; - case 3: script_pushint(st,sd->status.account_id); break; - case 4: script_pushint(st,sd->bg_id); break; - default: - ShowError("buildin_getcharid: invalid parameter (%d).\n", num); - script_pushint(st,0); - break; - } - - return 0; -} -/*========================================== - * returns the GID of an NPC - *------------------------------------------*/ -BUILDIN_FUNC(getnpcid) -{ - int num = script_getnum(st,2); - struct npc_data* nd = NULL; - - if( script_hasdata(st,3) ) - {// unique npc name - if( ( nd = npc_name2id(script_getstr(st,3)) ) == NULL ) - { - ShowError("buildin_getnpcid: No such NPC '%s'.\n", script_getstr(st,3)); - script_pushint(st,0); - return 1; - } - } - - switch (num) { - case 0: - script_pushint(st,nd ? nd->bl.id : st->oid); - break; - default: - ShowError("buildin_getnpcid: invalid parameter (%d).\n", num); - script_pushint(st,0); - return 1; - } - - return 0; -} - -/*========================================== - * Return the name of the party_id - * null if not found - *------------------------------------------*/ -BUILDIN_FUNC(getpartyname) -{ - int party_id; - struct party_data* p; - - party_id = script_getnum(st,2); - - if( ( p = party_search(party_id) ) != NULL ) - { - script_pushstrcopy(st,p->party.name); - } - else - { - script_pushconststr(st,"null"); - } - return 0; -} - -/*========================================== - * Get the information of the members of a party by type - * @party_id, @type - * return by @type : - * - : nom des membres - * 1 : char_id des membres - * 2 : account_id des membres - *------------------------------------------*/ -BUILDIN_FUNC(getpartymember) -{ - struct party_data *p; - int i,j=0,type=0; - - p=party_search(script_getnum(st,2)); - - if( script_hasdata(st,3) ) - type=script_getnum(st,3); - - if(p!=NULL){ - for(i=0;i<MAX_PARTY;i++){ - if(p->party.member[i].account_id){ - switch (type) { - case 2: - mapreg_setreg(reference_uid(add_str("$@partymemberaid"), j),p->party.member[i].account_id); - break; - case 1: - mapreg_setreg(reference_uid(add_str("$@partymembercid"), j),p->party.member[i].char_id); - break; - default: - mapreg_setregstr(reference_uid(add_str("$@partymembername$"), j),p->party.member[i].name); - } - j++; - } - } - } - mapreg_setreg(add_str("$@partymembercount"),j); - - return 0; -} - -/*========================================== - * Retrieves party leader. if flag is specified, - * return some of the leader data. Otherwise, return name. - *------------------------------------------*/ -BUILDIN_FUNC(getpartyleader) -{ - int party_id, type = 0, i=0; - struct party_data *p; - - party_id=script_getnum(st,2); - if( script_hasdata(st,3) ) - type=script_getnum(st,3); - - p=party_search(party_id); - - if (p) //Search leader - for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++); - - if (!p || i == MAX_PARTY) { //leader not found - if (type) - script_pushint(st,-1); - else - script_pushconststr(st,"null"); - return 0; - } - - switch (type) { - case 1: script_pushint(st,p->party.member[i].account_id); break; - case 2: script_pushint(st,p->party.member[i].char_id); break; - case 3: script_pushint(st,p->party.member[i].class_); break; - case 4: script_pushstrcopy(st,mapindex_id2name(p->party.member[i].map)); break; - case 5: script_pushint(st,p->party.member[i].lv); break; - default: script_pushstrcopy(st,p->party.member[i].name); break; - } - return 0; -} - -/*========================================== - * Return the name of the @guild_id - * null if not found - *------------------------------------------*/ -BUILDIN_FUNC(getguildname) -{ - int guild_id; - struct guild* g; - - guild_id = script_getnum(st,2); - - if( ( g = guild_search(guild_id) ) != NULL ) - { - script_pushstrcopy(st,g->name); - } - else - { - script_pushconststr(st,"null"); - } - return 0; -} - -/*========================================== - * Return the name of the guild master of @guild_id - * null if not found - *------------------------------------------*/ -BUILDIN_FUNC(getguildmaster) -{ - int guild_id; - struct guild* g; - - guild_id = script_getnum(st,2); - - if( ( g = guild_search(guild_id) ) != NULL ) - { - script_pushstrcopy(st,g->member[0].name); - } - else - { - script_pushconststr(st,"null"); - } - return 0; -} - -BUILDIN_FUNC(getguildmasterid) -{ - int guild_id; - struct guild* g; - - guild_id = script_getnum(st,2); - - if( ( g = guild_search(guild_id) ) != NULL ) - { - script_pushint(st,g->member[0].char_id); - } - else - { - script_pushint(st,0); - } - return 0; -} - -/*========================================== - * Get char string information by type : - * Return by @type : - * 0 : char_name - * 1 : party_name or "" - * 2 : guild_name or "" - * 3 : map_name - * - : "" - *------------------------------------------*/ -BUILDIN_FUNC(strcharinfo) -{ - TBL_PC *sd; - int num; - struct guild* g; - struct party_data* p; - - sd=script_rid2sd(st); - if (!sd) { //Avoid crashing.... - script_pushconststr(st,""); - return 0; - } - num=script_getnum(st,2); - switch(num){ - case 0: - script_pushstrcopy(st,sd->status.name); - break; - case 1: - if( ( p = party_search(sd->status.party_id) ) != NULL ) - { - script_pushstrcopy(st,p->party.name); - } - else - { - script_pushconststr(st,""); - } - break; - case 2: - if( ( g = guild_search(sd->status.guild_id) ) != NULL ) - { - script_pushstrcopy(st,g->name); - } - else - { - script_pushconststr(st,""); - } - break; - case 3: - script_pushconststr(st,map[sd->bl.m].name); - break; - default: - ShowWarning("buildin_strcharinfo: unknown parameter.\n"); - script_pushconststr(st,""); - break; - } - - return 0; -} - -/*========================================== - * Get npc string information by type - * return by @type: - * 0 : name - * 1 : str# - * 2 : #str - * 3 : ::str - * 4 : map name - *------------------------------------------*/ -BUILDIN_FUNC(strnpcinfo) -{ - TBL_NPC* nd; - int num; - char *buf,*name=NULL; - - nd = map_id2nd(st->oid); - if (!nd) { - script_pushconststr(st, ""); - return 0; - } - - num = script_getnum(st,2); - switch(num){ - case 0: // display name - name = aStrdup(nd->name); - break; - case 1: // visible part of display name - if((buf = strchr(nd->name,'#')) != NULL) - { - name = aStrdup(nd->name); - name[buf - nd->name] = 0; - } else // Return the name, there is no '#' present - name = aStrdup(nd->name); - break; - case 2: // # fragment - if((buf = strchr(nd->name,'#')) != NULL) - name = aStrdup(buf+1); - break; - case 3: // unique name - name = aStrdup(nd->exname); - break; - case 4: // map name - name = aStrdup(map[nd->bl.m].name); - break; - } - - if(name) - script_pushstr(st, name); - else - script_pushconststr(st, ""); - - return 0; -} - - -// aegis->athena slot position conversion table -static unsigned int equip[] = {EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_GARMENT,EQP_SHOES,EQP_ACC_L,EQP_ACC_R,EQP_HEAD_MID,EQP_HEAD_LOW}; - -/*========================================== - * GetEquipID(Pos); Pos: 1-10 - *------------------------------------------*/ -BUILDIN_FUNC(getequipid) -{ - int i, num; - TBL_PC* sd; - struct item_data* item; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - num = script_getnum(st,2) - 1; - if( num < 0 || num >= ARRAYLENGTH(equip) ) - { - script_pushint(st,-1); - return 0; - } - - // get inventory position of item - i = pc_checkequip(sd,equip[num]); - if( i < 0 ) - { - script_pushint(st,-1); - return 0; - } - - item = sd->inventory_data[i]; - if( item != 0 ) - script_pushint(st,item->nameid); - else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * Get the equipement name at pos - * return item jname or "" - *------------------------------------------*/ -BUILDIN_FUNC(getequipname) -{ - int i, num; - TBL_PC* sd; - struct item_data* item; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - num = script_getnum(st,2) - 1; - if( num < 0 || num >= ARRAYLENGTH(equip) ) - { - script_pushconststr(st,""); - return 0; - } - - // get inventory position of item - i = pc_checkequip(sd,equip[num]); - if( i < 0 ) - { - script_pushint(st,-1); - return 0; - } - - item = sd->inventory_data[i]; - if( item != 0 ) - script_pushstrcopy(st,item->jname); - else - script_pushconststr(st,""); - - return 0; -} - -/*========================================== - * getbrokenid [Valaris] - *------------------------------------------*/ -BUILDIN_FUNC(getbrokenid) -{ - int i,num,id=0,brokencounter=0; - TBL_PC *sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - num=script_getnum(st,2); - for(i=0; i<MAX_INVENTORY; i++) { - if(sd->status.inventory[i].attribute){ - brokencounter++; - if(num==brokencounter){ - id=sd->status.inventory[i].nameid; - break; - } - } - } - - script_pushint(st,id); - - return 0; -} - -/*========================================== - * repair [Valaris] - *------------------------------------------*/ -BUILDIN_FUNC(repair) -{ - int i,num; - int repaircounter=0; - TBL_PC *sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - num=script_getnum(st,2); - for(i=0; i<MAX_INVENTORY; i++) { - if(sd->status.inventory[i].attribute){ - repaircounter++; - if(num==repaircounter){ - sd->status.inventory[i].attribute=0; - clif_equiplist(sd); - clif_produceeffect(sd, 0, sd->status.inventory[i].nameid); - clif_misceffect(&sd->bl, 3); - break; - } - } - } - - return 0; -} - -/*========================================== - * repairall - *------------------------------------------*/ -BUILDIN_FUNC(repairall) -{ - int i, repaircounter = 0; - TBL_PC *sd; - - sd = script_rid2sd(st); - if(sd == NULL) - return 0; - - for(i = 0; i < MAX_INVENTORY; i++) - { - if(sd->status.inventory[i].nameid && sd->status.inventory[i].attribute) - { - sd->status.inventory[i].attribute = 0; - clif_produceeffect(sd,0,sd->status.inventory[i].nameid); - repaircounter++; - } - } - - if(repaircounter) - { - clif_misceffect(&sd->bl, 3); - clif_equiplist(sd); - } - - return 0; -} - -/*========================================== - * Chk if player have something equiped at pos - *------------------------------------------*/ -BUILDIN_FUNC(getequipisequiped) -{ - int i = -1,num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - - if(i >= 0) - script_pushint(st,1); - else - script_pushint(st,0); - return 0; -} - -/*========================================== - * Chk if the player have something equiped at pos - * if so chk if this item ain't marked not refinable or rental - * return (npc) - * 1 : true - * 0 : false - *------------------------------------------*/ -BUILDIN_FUNC(getequipisenableref) -{ - int i = -1,num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if( num > 0 && num <= ARRAYLENGTH(equip) ) - i = pc_checkequip(sd,equip[num-1]); - if( i >= 0 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine && !sd->status.inventory[i].expire_time ) - script_pushint(st,1); - else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * Chk if the item equiped at pos is identify (huh ?) - * return (npc) - * 1 : true - * 0 : false - *------------------------------------------*/ -BUILDIN_FUNC(getequipisidentify) -{ - int i = -1,num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) - script_pushint(st,sd->status.inventory[i].identify); - else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * Get the item refined value at pos - * return (npc) - * x : refine amount - * 0 : false (not refined) - *------------------------------------------*/ -BUILDIN_FUNC(getequiprefinerycnt) -{ - int i = -1,num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) - script_pushint(st,sd->status.inventory[i].refine); - else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * Get the weapon level value at pos - * (pos should normally only be EQI_HAND_L or EQI_HAND_R) - * return (npc) - * x : weapon level - * 0 : false - *------------------------------------------*/ -BUILDIN_FUNC(getequipweaponlv) -{ - int i = -1,num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0 && sd->inventory_data[i]) - script_pushint(st,sd->inventory_data[i]->wlv); - else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * Get the item refine chance (from refine.txt) for item at pos - * return (npc) - * x : refine chance - * 0 : false (max refine level or unequip..) - *------------------------------------------*/ -BUILDIN_FUNC(getequippercentrefinery) -{ - int i = -1,num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE) - script_pushint(st,status_get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int)sd->status.inventory[i].refine)); - else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * Refine +1 item at pos and log and display refine - *------------------------------------------*/ -BUILDIN_FUNC(successrefitem) -{ - int i=-1,num,ep; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) { - ep=sd->status.inventory[i].equip; - - //Logs items, got from (N)PC scripts [Lupus] - log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]); - - sd->status.inventory[i].refine++; - pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below - - clif_refine(sd->fd,0,i,sd->status.inventory[i].refine); - clif_delitem(sd,i,1,3); - - //Logs items, got from (N)PC scripts [Lupus] - log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]); - - clif_additem(sd,i,1,0); - pc_equipitem(sd,i,ep); - clif_misceffect(&sd->bl,3); - if(sd->status.inventory[i].refine == MAX_REFINE && - sd->status.inventory[i].card[0] == CARD0_FORGE && - sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3]) - ){ // Fame point system [DracoRPG] - switch (sd->inventory_data[i]->wlv){ - case 1: - pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point - break; - case 2: - pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point - break; - case 3: - pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point - break; - } - } - } - - return 0; -} - -/*========================================== - * Show a failed Refine +1 attempt - *------------------------------------------*/ -BUILDIN_FUNC(failedrefitem) -{ - int i=-1,num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) { - sd->status.inventory[i].refine = 0; - pc_unequipitem(sd,i,3); //recalculate bonus - clif_refine(sd->fd,1,i,sd->status.inventory[i].refine); //notify client of failure - - pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT); - - clif_misceffect(&sd->bl,2); // display failure effect - } - - return 0; -} - -/*========================================== - * Downgrades an Equipment Part by -1 . [Masao] - *------------------------------------------*/ -BUILDIN_FUNC(downrefitem) -{ - int i = -1,num,ep; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i = pc_checkequip(sd,equip[num-1]); - if(i >= 0) { - ep = sd->status.inventory[i].equip; - - //Logs items, got from (N)PC scripts [Lupus] - log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]); - - sd->status.inventory[i].refine++; - pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below - - clif_refine(sd->fd,2,i,sd->status.inventory[i].refine = sd->status.inventory[i].refine - 2); - clif_delitem(sd,i,1,3); - - //Logs items, got from (N)PC scripts [Lupus] - log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]); - - clif_additem(sd,i,1,0); - pc_equipitem(sd,i,ep); - clif_misceffect(&sd->bl,2); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(statusup) -{ - int type; - TBL_PC *sd; - - type=script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - pc_statusup(sd,type); - - return 0; -} -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(statusup2) -{ - int type,val; - TBL_PC *sd; - - type=script_getnum(st,2); - val=script_getnum(st,3); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - pc_statusup2(sd,type,val); - - return 0; -} - -/// See 'doc/item_bonus.txt' -/// -/// bonus <bonus type>,<val1>; -/// bonus2 <bonus type>,<val1>,<val2>; -/// bonus3 <bonus type>,<val1>,<val2>,<val3>; -/// bonus4 <bonus type>,<val1>,<val2>,<val3>,<val4>; -/// bonus5 <bonus type>,<val1>,<val2>,<val3>,<val4>,<val5>; -BUILDIN_FUNC(bonus) -{ - int type; - int val1; - int val2 = 0; - int val3 = 0; - int val4 = 0; - int val5 = 0; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; // no player attached - - type = script_getnum(st,2); - switch( type ) { - case SP_AUTOSPELL: - case SP_AUTOSPELL_WHENHIT: - case SP_AUTOSPELL_ONSKILL: - case SP_SKILL_ATK: - case SP_SKILL_HEAL: - case SP_SKILL_HEAL2: - case SP_ADD_SKILL_BLOW: - case SP_CASTRATE: - case SP_ADDEFF_ONSKILL: - case SP_SKILL_USE_SP_RATE: - case SP_SKILL_COOLDOWN: - case SP_SKILL_FIXEDCAST: - case SP_SKILL_VARIABLECAST: - case SP_VARCASTRATE: - case SP_SKILL_USE_SP: - // these bonuses support skill names - val1 = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) ); - break; - default: - val1 = script_getnum(st,3); - break; - } - - switch( script_lastdata(st)-2 ) { - case 1: - pc_bonus(sd, type, val1); - break; - case 2: - val2 = script_getnum(st,4); - pc_bonus2(sd, type, val1, val2); - break; - case 3: - val2 = script_getnum(st,4); - val3 = script_getnum(st,5); - pc_bonus3(sd, type, val1, val2, val3); - break; - case 4: - if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) ) - val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name - else - val2 = script_getnum(st,4); - - val3 = script_getnum(st,5); - val4 = script_getnum(st,6); - pc_bonus4(sd, type, val1, val2, val3, val4); - break; - case 5: - if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) ) - val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name - else - val2 = script_getnum(st,4); - - val3 = script_getnum(st,5); - val4 = script_getnum(st,6); - val5 = script_getnum(st,7); - pc_bonus5(sd, type, val1, val2, val3, val4, val5); - break; - default: - ShowDebug("buildin_bonus: unexpected number of arguments (%d)\n", (script_lastdata(st) - 1)); - break; - } - - return 0; -} - -BUILDIN_FUNC(autobonus) -{ - unsigned int dur; - short rate; - short atk_type = 0; - TBL_PC* sd; - const char *bonus_script, *other_script = NULL; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; // no player attached - - if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip ) - return 0; - - rate = script_getnum(st,3); - dur = script_getnum(st,4); - bonus_script = script_getstr(st,2); - if( !rate || !dur || !bonus_script ) - return 0; - - if( script_hasdata(st,5) ) - atk_type = script_getnum(st,5); - if( script_hasdata(st,6) ) - other_script = script_getstr(st,6); - - if( pc_addautobonus(sd->autobonus,ARRAYLENGTH(sd->autobonus), - bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) ) - { - script_add_autobonus(bonus_script); - if( other_script ) - script_add_autobonus(other_script); - } - - return 0; -} - -BUILDIN_FUNC(autobonus2) -{ - unsigned int dur; - short rate; - short atk_type = 0; - TBL_PC* sd; - const char *bonus_script, *other_script = NULL; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; // no player attached - - if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip ) - return 0; - - rate = script_getnum(st,3); - dur = script_getnum(st,4); - bonus_script = script_getstr(st,2); - if( !rate || !dur || !bonus_script ) - return 0; - - if( script_hasdata(st,5) ) - atk_type = script_getnum(st,5); - if( script_hasdata(st,6) ) - other_script = script_getstr(st,6); - - if( pc_addautobonus(sd->autobonus2,ARRAYLENGTH(sd->autobonus2), - bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) ) - { - script_add_autobonus(bonus_script); - if( other_script ) - script_add_autobonus(other_script); - } - - return 0; -} - -BUILDIN_FUNC(autobonus3) -{ - unsigned int dur; - short rate,atk_type; - TBL_PC* sd; - const char *bonus_script, *other_script = NULL; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; // no player attached - - if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip ) - return 0; - - rate = script_getnum(st,3); - dur = script_getnum(st,4); - atk_type = ( script_isstring(st,5) ? skill_name2id(script_getstr(st,5)) : script_getnum(st,5) ); - bonus_script = script_getstr(st,2); - if( !rate || !dur || !atk_type || !bonus_script ) - return 0; - - if( script_hasdata(st,6) ) - other_script = script_getstr(st,6); - - if( pc_addautobonus(sd->autobonus3,ARRAYLENGTH(sd->autobonus3), - bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,true) ) - { - script_add_autobonus(bonus_script); - if( other_script ) - script_add_autobonus(other_script); - } - - return 0; -} - -/// Changes the level of a player skill. -/// <flag> defaults to 1 -/// <flag>=0 : set the level of the skill -/// <flag>=1 : set the temporary level of the skill -/// <flag>=2 : add to the level of the skill -/// -/// skill <skill id>,<level>,<flag> -/// skill <skill id>,<level> -/// skill "<skill name>",<level>,<flag> -/// skill "<skill name>",<level> -BUILDIN_FUNC(skill) -{ - int id; - int level; - int flag = 1; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - level = script_getnum(st,3); - if( script_hasdata(st,4) ) - flag = script_getnum(st,4); - pc_skill(sd, id, level, flag); - - return 0; -} - -/// Changes the level of a player skill. -/// like skill, but <flag> defaults to 2 -/// -/// addtoskill <skill id>,<amount>,<flag> -/// addtoskill <skill id>,<amount> -/// addtoskill "<skill name>",<amount>,<flag> -/// addtoskill "<skill name>",<amount> -/// -/// @see skill -BUILDIN_FUNC(addtoskill) -{ - int id; - int level; - int flag = 2; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - level = script_getnum(st,3); - if( script_hasdata(st,4) ) - flag = script_getnum(st,4); - pc_skill(sd, id, level, flag); - - return 0; -} - -/// Increases the level of a guild skill. -/// -/// guildskill <skill id>,<amount>; -/// guildskill "<skill name>",<amount>; -BUILDIN_FUNC(guildskill) -{ - int id; - int level; - TBL_PC* sd; - int i; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - level = script_getnum(st,3); - for( i=0; i < level; i++ ) - guild_skillup(sd, id); - - return 0; -} - -/// Returns the level of the player skill. -/// -/// getskilllv(<skill id>) -> <level> -/// getskilllv("<skill name>") -> <level> -BUILDIN_FUNC(getskilllv) -{ - int id; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - script_pushint(st, pc_checkskill(sd,id)); - - return 0; -} - -/// Returns the level of the guild skill. -/// -/// getgdskilllv(<guild id>,<skill id>) -> <level> -/// getgdskilllv(<guild id>,"<skill name>") -> <level> -BUILDIN_FUNC(getgdskilllv) -{ - int guild_id; - uint16 skill_id; - struct guild* g; - - guild_id = script_getnum(st,2); - skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) ); - g = guild_search(guild_id); - if( g == NULL ) - script_pushint(st, -1); - else - script_pushint(st, guild_checkskill(g,skill_id)); - - return 0; -} - -/// Returns the 'basic_skill_check' setting. -/// This config determines if the server checks the skill level of NV_BASIC -/// before allowing the basic actions. -/// -/// basicskillcheck() -> <bool> -BUILDIN_FUNC(basicskillcheck) -{ - script_pushint(st, battle_config.basic_skill_check); - return 0; -} - -/// Returns the GM level of the player. -/// -/// getgmlevel() -> <level> -BUILDIN_FUNC(getgmlevel) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - script_pushint(st, pc_get_group_level(sd)); - - return 0; -} - -/// Returns the group ID of the player. -/// -/// getgroupid() -> <int> -BUILDIN_FUNC(getgroupid) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if (sd == NULL) - return 1; // no player attached, report source - script_pushint(st, pc_get_group_id(sd)); - - return 0; -} - -/// Terminates the execution of this script instance. -/// -/// end -BUILDIN_FUNC(end) -{ - st->state = END; - return 0; -} - -/// Checks if the player has that effect state (option). -/// -/// checkoption(<option>) -> <bool> -BUILDIN_FUNC(checkoption) -{ - int option; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - option = script_getnum(st,2); - if( sd->sc.option&option ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Checks if the player is in that body state (opt1). -/// -/// checkoption1(<opt1>) -> <bool> -BUILDIN_FUNC(checkoption1) -{ - int opt1; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - opt1 = script_getnum(st,2); - if( sd->sc.opt1 == opt1 ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Checks if the player has that health state (opt2). -/// -/// checkoption2(<opt2>) -> <bool> -BUILDIN_FUNC(checkoption2) -{ - int opt2; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - opt2 = script_getnum(st,2); - if( sd->sc.opt2&opt2 ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Changes the effect state (option) of the player. -/// <flag> defaults to 1 -/// <flag>=0 : removes the option -/// <flag>=other : adds the option -/// -/// setoption <option>,<flag>; -/// setoption <option>; -BUILDIN_FUNC(setoption) -{ - int option; - int flag = 1; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - option = script_getnum(st,2); - if( script_hasdata(st,3) ) - flag = script_getnum(st,3); - else if( !option ){// Request to remove everything. - flag = 0; - option = OPTION_FALCON|OPTION_RIDING; -#ifndef NEW_CARTS - option |= OPTION_CART; -#endif - } - if( flag ){// Add option - if( option&OPTION_WEDDING && !battle_config.wedding_modifydisplay ) - option &= ~OPTION_WEDDING;// Do not show the wedding sprites - pc_setoption(sd, sd->sc.option|option); - } else// Remove option - pc_setoption(sd, sd->sc.option&~option); - - return 0; -} - -/// Returns if the player has a cart. -/// -/// checkcart() -> <bool> -/// -/// @author Valaris -BUILDIN_FUNC(checkcart) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - if( pc_iscarton(sd) ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Sets the cart of the player. -/// <type> defaults to 1 -/// <type>=0 : removes the cart -/// <type>=1 : Normal cart -/// <type>=2 : Wooden cart -/// <type>=3 : Covered cart with flowers and ferns -/// <type>=4 : Wooden cart with a Panda doll on the back -/// <type>=5 : Normal cart with bigger wheels, a roof and a banner on the back -/// -/// setcart <type>; -/// setcart; -BUILDIN_FUNC(setcart) -{ - int type = 1; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - if( script_hasdata(st,2) ) - type = script_getnum(st,2); - pc_setcart(sd, type); - - return 0; -} - -/// Returns if the player has a falcon. -/// -/// checkfalcon() -> <bool> -/// -/// @author Valaris -BUILDIN_FUNC(checkfalcon) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - if( pc_isfalcon(sd) ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Sets if the player has a falcon or not. -/// <flag> defaults to 1 -/// -/// setfalcon <flag>; -/// setfalcon; -BUILDIN_FUNC(setfalcon) -{ - int flag = 1; - 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); - - pc_setfalcon(sd, flag); - - return 0; -} - -/// Returns if the player is riding. -/// -/// checkriding() -> <bool> -/// -/// @author Valaris -BUILDIN_FUNC(checkriding) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - if( pc_isriding(sd) || pc_isridingwug(sd) || pc_isridingdragon(sd) ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Sets if the player is riding. -/// <flag> defaults to 1 -/// -/// setriding <flag>; -/// setriding; -BUILDIN_FUNC(setriding) -{ - int flag = 1; - 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); - pc_setriding(sd, flag); - - return 0; -} - -/// Returns if the player has a warg. -/// -/// checkwug() -> <bool> -/// -BUILDIN_FUNC(checkwug) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - if( pc_iswug(sd) || pc_isridingwug(sd) ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Returns if the player is wearing MADO Gear. -/// -/// checkmadogear() -> <bool> -/// -BUILDIN_FUNC(checkmadogear) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - if( pc_ismadogear(sd) ) - script_pushint(st, 1); - else - script_pushint(st, 0); - - return 0; -} - -/// Sets if the player is riding MADO Gear. -/// <flag> defaults to 1 -/// -/// setmadogear <flag>; -/// setmadogear; -BUILDIN_FUNC(setmadogear) -{ - int flag = 1; - 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); - pc_setmadogear(sd, flag); - - return 0; -} - -/// Sets the save point of the player. -/// -/// save "<map name>",<x>,<y> -/// savepoint "<map name>",<x>,<y> -BUILDIN_FUNC(savepoint) -{ - int x; - int y; - short map; - const char* str; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached, report source - - str = script_getstr(st, 2); - x = script_getnum(st,3); - y = script_getnum(st,4); - map = mapindex_name2id(str); - if( map ) - pc_setsavepoint(sd, map, x, y); - - return 0; -} - -/*========================================== - * GetTimeTick(0: System Tick, 1: Time Second Tick) - *------------------------------------------*/ -BUILDIN_FUNC(gettimetick) /* Asgard Version */ -{ - int type; - time_t timer; - struct tm *t; - - type=script_getnum(st,2); - - switch(type){ - case 2: - //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC - // from the system clock.) - script_pushint(st,(int)time(NULL)); - break; - case 1: - //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59) - time(&timer); - t=localtime(&timer); - script_pushint(st,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec)); - break; - case 0: - default: - //type 0:(System Ticks) - script_pushint(st,gettick()); - break; - } - return 0; -} - -/*========================================== - * GetTime(Type); - * 1: Sec 2: Min 3: Hour - * 4: WeekDay 5: MonthDay 6: Month - * 7: Year - *------------------------------------------*/ -BUILDIN_FUNC(gettime) /* Asgard Version */ -{ - int type; - time_t timer; - struct tm *t; - - type=script_getnum(st,2); - - time(&timer); - t=localtime(&timer); - - switch(type){ - case 1://Sec(0~59) - script_pushint(st,t->tm_sec); - break; - case 2://Min(0~59) - script_pushint(st,t->tm_min); - break; - case 3://Hour(0~23) - script_pushint(st,t->tm_hour); - break; - case 4://WeekDay(0~6) - script_pushint(st,t->tm_wday); - break; - case 5://MonthDay(01~31) - script_pushint(st,t->tm_mday); - break; - case 6://Month(01~12) - script_pushint(st,t->tm_mon+1); - break; - case 7://Year(20xx) - script_pushint(st,t->tm_year+1900); - break; - case 8://Year Day(01~366) - script_pushint(st,t->tm_yday+1); - break; - default://(format error) - script_pushint(st,-1); - break; - } - return 0; -} - -/*========================================== - * GetTimeStr("TimeFMT", Length); - *------------------------------------------*/ -BUILDIN_FUNC(gettimestr) -{ - char *tmpstr; - const char *fmtstr; - int maxlen; - time_t now = time(NULL); - - fmtstr=script_getstr(st,2); - maxlen=script_getnum(st,3); - - tmpstr=(char *)aMalloc((maxlen+1)*sizeof(char)); - strftime(tmpstr,maxlen,fmtstr,localtime(&now)); - tmpstr[maxlen]='\0'; - - script_pushstr(st,tmpstr); - return 0; -} - -/*========================================== - * Open player storage - *------------------------------------------*/ -BUILDIN_FUNC(openstorage) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - storage_storageopen(sd); - return 0; -} - -BUILDIN_FUNC(guildopenstorage) -{ - TBL_PC* sd; - int ret; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - ret = storage_guild_storageopen(sd); - script_pushint(st,ret); - return 0; -} - -/*========================================== - * Make player use a skill trought item usage - *------------------------------------------*/ -/// itemskill <skill id>,<level> -/// itemskill "<skill name>",<level> -BUILDIN_FUNC(itemskill) -{ - int id; - int lv; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL || sd->ud.skilltimer != INVALID_TIMER ) - return 0; - - id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - lv = script_getnum(st,3); - - sd->skillitem=id; - sd->skillitemlv=lv; - clif_item_skill(sd,id,lv); - return 0; -} -/*========================================== - * Attempt to create an item - *------------------------------------------*/ -BUILDIN_FUNC(produce) -{ - int trigger; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - trigger=script_getnum(st,2); - clif_skill_produce_mix_list(sd, -1, trigger); - return 0; -} -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(cooking) -{ - int trigger; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - trigger=script_getnum(st,2); - clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1); - return 0; -} -/*========================================== - * Create a pet - *------------------------------------------*/ -BUILDIN_FUNC(makepet) -{ - TBL_PC* sd; - int id,pet_id; - - id=script_getnum(st,2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - pet_id = search_petDB_index(id, PET_CLASS); - - if (pet_id < 0) - pet_id = search_petDB_index(id, PET_EGG); - if (pet_id >= 0 && sd) { - sd->catch_target_class = pet_db[pet_id].class_; - intif_create_pet( - sd->status.account_id, sd->status.char_id, - (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv, - (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate, - 100, 0, 1, pet_db[pet_id].jname); - } - - return 0; -} -/*========================================== - * Give player exp base,job * quest_exp_rate/100 - *------------------------------------------*/ -BUILDIN_FUNC(getexp) -{ - TBL_PC* sd; - int base=0,job=0; - double bonus; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - base=script_getnum(st,2); - job =script_getnum(st,3); - if(base<0 || job<0) - return 0; - - // bonus for npc-given exp - bonus = battle_config.quest_exp_rate / 100.; - base = (int) cap_value(base * bonus, 0, INT_MAX); - job = (int) cap_value(job * bonus, 0, INT_MAX); - - pc_gainexp(sd, NULL, base, job, true); - - return 0; -} - -/*========================================== - * Gain guild exp [Celest] - *------------------------------------------*/ -BUILDIN_FUNC(guildgetexp) -{ - TBL_PC* sd; - int exp; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - exp = script_getnum(st,2); - if(exp < 0) - return 0; - if(sd && sd->status.guild_id > 0) - guild_getexp (sd, exp); - - return 0; -} - -/*========================================== - * Changes the guild master of a guild [Skotlex] - *------------------------------------------*/ -BUILDIN_FUNC(guildchangegm) -{ - TBL_PC *sd; - int guild_id; - const char *name; - - guild_id = script_getnum(st,2); - name = script_getstr(st,3); - sd=map_nick2sd(name); - - if (!sd) - script_pushint(st,0); - else - script_pushint(st,guild_gm_change(guild_id, sd)); - - return 0; -} - -/*========================================== - * Spawn a monster : - @mapn,x,y : location - @str : monster name - @class_ : mob_id - @amount : nb to spawn - @event : event to attach to mob - *------------------------------------------*/ -BUILDIN_FUNC(monster) -{ - const char* mapn = script_getstr(st,2); - int x = script_getnum(st,3); - int y = script_getnum(st,4); - const char* str = script_getstr(st,5); - int class_ = script_getnum(st,6); - int amount = script_getnum(st,7); - const char* event = ""; - unsigned int size = SZ_SMALL; - unsigned int ai = AI_NONE; - - struct map_session_data* sd; - int16 m; - - if (script_hasdata(st, 8)) - { - event = script_getstr(st, 8); - check_event(st, event); - } - - if (script_hasdata(st, 9)) - { - size = script_getnum(st, 9); - if (size > 3) - { - ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_); - return 1; - } - } - - if (script_hasdata(st, 10)) - { - ai = script_getnum(st, 10); - if (ai > 4) - { - ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_); - return 1; - } - } - - if (class_ >= 0 && !mobdb_checkid(class_)) - { - ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_); - return 1; - } - - sd = map_id2sd(st->rid); - - if (sd && strcmp(mapn, "this") == 0) - m = sd->bl.m; - else - { - m = map_mapname2mapid(mapn); - if (map[m].flag.src4instance && st->instance_id) - { // Try to redirect to the instance map, not the src map - if ((m = instance_mapid2imapid(m, st->instance_id)) < 0) - { - ShowError("buildin_monster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn); - return 1; - } - } - } - - mob_once_spawn(sd, m, x, y, str, class_, amount, event, size, ai); - return 0; -} -/*========================================== - * Request List of Monster Drops - *------------------------------------------*/ -BUILDIN_FUNC(getmobdrops) -{ - int class_ = script_getnum(st,2); - int i, j = 0; - struct mob_db *mob; - - if( !mobdb_checkid(class_) ) - { - script_pushint(st, 0); - return 0; - } - - mob = mob_db(class_); - - for( i = 0; i < MAX_MOB_DROP; i++ ) - { - if( mob->dropitem[i].nameid < 1 ) - continue; - if( itemdb_exists(mob->dropitem[i].nameid) == NULL ) - continue; - - mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), mob->dropitem[i].nameid); - mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].p); - - j++; - } - - mapreg_setreg(add_str("$@MobDrop_count"), j); - script_pushint(st, 1); - - return 0; -} -/*========================================== - * Same as monster but randomize location in x0,x1,y0,y1 area - *------------------------------------------*/ -BUILDIN_FUNC(areamonster) -{ - const char* mapn = script_getstr(st,2); - int x0 = script_getnum(st,3); - int y0 = script_getnum(st,4); - int x1 = script_getnum(st,5); - int y1 = script_getnum(st,6); - const char* str = script_getstr(st,7); - int class_ = script_getnum(st,8); - int amount = script_getnum(st,9); - const char* event = ""; - unsigned int size = SZ_SMALL; - unsigned int ai = AI_NONE; - - struct map_session_data* sd; - int16 m; - - if (script_hasdata(st,10)) - { - event = script_getstr(st, 10); - check_event(st, event); - } - - if (script_hasdata(st, 11)) - { - size = script_getnum(st, 11); - if (size > 3) - { - ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_); - return 1; - } - } - - if (script_hasdata(st, 12)) - { - ai = script_getnum(st, 12); - if (ai > 4) - { - ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_); - return 1; - } - } - - sd = map_id2sd(st->rid); - - if (sd && strcmp(mapn, "this") == 0) - m = sd->bl.m; - else - { - m = map_mapname2mapid(mapn); - if (map[m].flag.src4instance && st->instance_id) - { // Try to redirect to the instance map, not the src map - if ((m = instance_mapid2imapid(m, st->instance_id)) < 0) - { - ShowError("buildin_areamonster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn); - return 1; - } - } - } - - mob_once_spawn_area(sd, m, x0, y0, x1, y1, str, class_, amount, event, size, ai); - return 0; -} -/*========================================== - * KillMonster subcheck, verify if mob to kill ain't got an even to handle, could be force kill by allflag - *------------------------------------------*/ - static int buildin_killmonster_sub_strip(struct block_list *bl,va_list ap) -{ //same fix but with killmonster instead - stripping events from mobs. - TBL_MOB* md = (TBL_MOB*)bl; - char *event=va_arg(ap,char *); - int allflag=va_arg(ap,int); - - md->state.npc_killmonster = 1; - - if(!allflag){ - if(strcmp(event,md->npc_event)==0) - status_kill(bl); - }else{ - if(!md->spawn) - status_kill(bl); - } - md->state.npc_killmonster = 0; - return 0; -} -static int buildin_killmonster_sub(struct block_list *bl,va_list ap) -{ - TBL_MOB* md = (TBL_MOB*)bl; - char *event=va_arg(ap,char *); - int allflag=va_arg(ap,int); - - if(!allflag){ - if(strcmp(event,md->npc_event)==0) - status_kill(bl); - }else{ - if(!md->spawn) - status_kill(bl); - } - return 0; -} -BUILDIN_FUNC(killmonster) -{ - const char *mapname,*event; - int16 m,allflag=0; - mapname=script_getstr(st,2); - event=script_getstr(st,3); - if(strcmp(event,"All")==0) - allflag = 1; - else - check_event(st, event); - - if( (m=map_mapname2mapid(mapname))<0 ) - return 0; - - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - return 0; - - if( script_hasdata(st,4) ) { - if ( script_getnum(st,4) == 1 ) { - map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag); - return 0; - } - } - - map_freeblock_lock(); - map_foreachinmap(buildin_killmonster_sub_strip, m, BL_MOB, event ,allflag); - map_freeblock_unlock(); - return 0; -} - -static int buildin_killmonsterall_sub_strip(struct block_list *bl,va_list ap) -{ //Strips the event from the mob if it's killed the old method. - struct mob_data *md; - - md = BL_CAST(BL_MOB, bl); - if (md->npc_event[0]) - md->npc_event[0] = 0; - - status_kill(bl); - return 0; -} -static int buildin_killmonsterall_sub(struct block_list *bl,va_list ap) -{ - status_kill(bl); - return 0; -} -BUILDIN_FUNC(killmonsterall) -{ - const char *mapname; - int16 m; - mapname=script_getstr(st,2); - - if( (m = map_mapname2mapid(mapname))<0 ) - return 0; - - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - return 0; - - if( script_hasdata(st,3) ) { - if ( script_getnum(st,3) == 1 ) { - map_foreachinmap(buildin_killmonsterall_sub,m,BL_MOB); - return 0; - } - } - - map_foreachinmap(buildin_killmonsterall_sub_strip,m,BL_MOB); - return 0; -} - -/*========================================== - * Creates a clone of a player. - * clone map, x, y, event, char_id, master_id, mode, flag, duration - *------------------------------------------*/ -BUILDIN_FUNC(clone) -{ - TBL_PC *sd, *msd=NULL; - int char_id,master_id=0,x,y, mode = 0, flag = 0, m; - unsigned int duration = 0; - const char *map,*event=""; - - map=script_getstr(st,2); - x=script_getnum(st,3); - y=script_getnum(st,4); - event=script_getstr(st,5); - char_id=script_getnum(st,6); - - if( script_hasdata(st,7) ) - master_id=script_getnum(st,7); - - if( script_hasdata(st,8) ) - mode=script_getnum(st,8); - - if( script_hasdata(st,9) ) - flag=script_getnum(st,9); - - if( script_hasdata(st,10) ) - duration=script_getnum(st,10); - - check_event(st, event); - - m = map_mapname2mapid(map); - if (m < 0) return 0; - - sd = map_charid2sd(char_id); - - if (master_id) { - msd = map_charid2sd(master_id); - if (msd) - master_id = msd->bl.id; - else - master_id = 0; - } - if (sd) //Return ID of newly crafted clone. - script_pushint(st,mob_clone_spawn(sd, m, x, y, event, master_id, mode, flag, 1000*duration)); - else //Failed to create clone. - script_pushint(st,0); - - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(doevent) -{ - const char* event = script_getstr(st,2); - struct map_session_data* sd; - - if( ( sd = script_rid2sd(st) ) == NULL ) - { - return 0; - } - - check_event(st, event); - npc_event(sd, event, 0); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(donpcevent) -{ - const char* event = script_getstr(st,2); - check_event(st, event); - if( !npc_event_do(event) ) { - struct npc_data * nd = map_id2nd(st->oid); - ShowDebug("NPCEvent '%s' not found! (source: %s)\n",event,nd?nd->name:"Unknown"); - script_pushint(st, 0); - } else - script_pushint(st, 1); - return 0; -} - -/// for Aegis compatibility -/// basically a specialized 'donpcevent', with the event specified as two arguments instead of one -BUILDIN_FUNC(cmdothernpc) // Added by RoVeRT -{ - const char* npc = script_getstr(st,2); - const char* command = script_getstr(st,3); - char event[EVENT_NAME_LENGTH]; - snprintf(event, sizeof(event), "%s::OnCommand%s", npc, command); - check_event(st, event); - npc_event_do(event); - return 0; -} - -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(addtimer) -{ - int tick = script_getnum(st,2); - const char* event = script_getstr(st, 3); - TBL_PC* sd; - - check_event(st, event); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - pc_addeventtimer(sd,tick,event); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(deltimer) -{ - const char *event; - TBL_PC* sd; - - event=script_getstr(st, 2); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - check_event(st, event); - pc_deleventtimer(sd,event); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(addtimercount) -{ - const char *event; - int tick; - TBL_PC* sd; - - event=script_getstr(st, 2); - tick=script_getnum(st,3); - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - check_event(st, event); - pc_addeventtimercount(sd,event,tick); - return 0; -} - -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(initnpctimer) -{ - struct npc_data *nd; - int flag = 0; - - if( script_hasdata(st,3) ) - { //Two arguments: NPC name and attach flag. - nd = npc_name2id(script_getstr(st, 2)); - flag = script_getnum(st,3); - } - else if( script_hasdata(st,2) ) - { //Check if argument is numeric (flag) or string (npc name) - struct script_data *data; - data = script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ) //NPC name - nd = npc_name2id(conv_str(st, data)); - else if( data_isint(data) ) //Flag - { - nd = (struct npc_data *)map_id2bl(st->oid); - flag = conv_num(st,data); - } - else - { - ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n"); - return 1; - } - } - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( !nd ) - return 0; - if( flag ) //Attach - { - TBL_PC* sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - nd->u.scr.rid = sd->bl.id; - } - - nd->u.scr.timertick = 0; - npc_settimerevent_tick(nd,0); - npc_timerevent_start(nd, st->rid); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(startnpctimer) -{ - struct npc_data *nd; - int flag = 0; - - if( script_hasdata(st,3) ) - { //Two arguments: NPC name and attach flag. - nd = npc_name2id(script_getstr(st, 2)); - flag = script_getnum(st,3); - } - else if( script_hasdata(st,2) ) - { //Check if argument is numeric (flag) or string (npc name) - struct script_data *data; - data = script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ) //NPC name - nd = npc_name2id(conv_str(st, data)); - else if( data_isint(data) ) //Flag - { - nd = (struct npc_data *)map_id2bl(st->oid); - flag = conv_num(st,data); - } - else - { - ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n"); - return 1; - } - } - else - nd=(struct npc_data *)map_id2bl(st->oid); - - if( !nd ) - return 0; - if( flag ) //Attach - { - TBL_PC* sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - nd->u.scr.rid = sd->bl.id; - } - - npc_timerevent_start(nd, st->rid); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(stopnpctimer) -{ - struct npc_data *nd; - int flag = 0; - - if( script_hasdata(st,3) ) - { //Two arguments: NPC name and attach flag. - nd = npc_name2id(script_getstr(st, 2)); - flag = script_getnum(st,3); - } - else if( script_hasdata(st,2) ) - { //Check if argument is numeric (flag) or string (npc name) - struct script_data *data; - data = script_getdata(st,2); - get_val(st,data); - if( data_isstring(data) ) //NPC name - nd = npc_name2id(conv_str(st, data)); - else if( data_isint(data) ) //Flag - { - nd = (struct npc_data *)map_id2bl(st->oid); - flag = conv_num(st,data); - } - else - { - ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n"); - return 1; - } - } - else - nd=(struct npc_data *)map_id2bl(st->oid); - - if( !nd ) - return 0; - if( flag ) //Detach - nd->u.scr.rid = 0; - - npc_timerevent_stop(nd); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(getnpctimer) -{ - struct npc_data *nd; - TBL_PC *sd; - int type = script_getnum(st,2); - int val = 0; - - if( script_hasdata(st,3) ) - nd = npc_name2id(script_getstr(st,3)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( !nd || nd->bl.type != BL_NPC ) - { - script_pushint(st,0); - ShowError("getnpctimer: Invalid NPC.\n"); - return 1; - } - - switch( type ) - { - case 0: val = npc_gettimerevent_tick(nd); break; - case 1: - if( nd->u.scr.rid ) - { - sd = map_id2sd(nd->u.scr.rid); - if( !sd ) - { - ShowError("buildin_getnpctimer: Attached player not found!\n"); - break; - } - val = (sd->npc_timer_id != INVALID_TIMER); - } - else - val = (nd->u.scr.timerid != INVALID_TIMER); - break; - case 2: val = nd->u.scr.timeramount; break; - } - - script_pushint(st,val); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(setnpctimer) -{ - int tick; - struct npc_data *nd; - - tick = script_getnum(st,2); - if( script_hasdata(st,3) ) - nd = npc_name2id(script_getstr(st,3)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( !nd || nd->bl.type != BL_NPC ) - { - script_pushint(st,1); - ShowError("setnpctimer: Invalid NPC.\n"); - return 1; - } - - npc_settimerevent_tick(nd,tick); - script_pushint(st,0); - return 0; -} - -/*========================================== - * attaches the player rid to the timer [Celest] - *------------------------------------------*/ -BUILDIN_FUNC(attachnpctimer) -{ - TBL_PC *sd; - struct npc_data *nd = (struct npc_data *)map_id2bl(st->oid); - - if( !nd || nd->bl.type != BL_NPC ) - { - script_pushint(st,1); - ShowError("setnpctimer: Invalid NPC.\n"); - return 1; - } - - if( script_hasdata(st,2) ) - sd = map_nick2sd(script_getstr(st,2)); - else - sd = script_rid2sd(st); - - if( !sd ) - { - script_pushint(st,1); - ShowWarning("attachnpctimer: Invalid player.\n"); - return 1; - } - - nd->u.scr.rid = sd->bl.id; - script_pushint(st,0); - return 0; -} - -/*========================================== - * detaches a player rid from the timer [Celest] - *------------------------------------------*/ -BUILDIN_FUNC(detachnpctimer) -{ - struct npc_data *nd; - - if( script_hasdata(st,2) ) - nd = npc_name2id(script_getstr(st,2)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( !nd || nd->bl.type != BL_NPC ) - { - script_pushint(st,1); - ShowError("detachnpctimer: Invalid NPC.\n"); - return 1; - } - - nd->u.scr.rid = 0; - script_pushint(st,0); - return 0; -} - -/*========================================== - * To avoid "player not attached" script errors, this function is provided, - * it checks if there is a player attached to the current script. [Skotlex] - * If no, returns 0, if yes, returns the account_id of the attached player. - *------------------------------------------*/ -BUILDIN_FUNC(playerattached) -{ - if(st->rid == 0 || map_id2sd(st->rid) == NULL) - script_pushint(st,0); - else - script_pushint(st,st->rid); - return 0; -} - -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(announce) -{ - const char *mes = script_getstr(st,2); - int flag = script_getnum(st,3); - const char *fontColor = script_hasdata(st,4) ? script_getstr(st,4) : NULL; - int fontType = script_hasdata(st,5) ? script_getnum(st,5) : 0x190; // default fontType (FW_NORMAL) - int fontSize = script_hasdata(st,6) ? script_getnum(st,6) : 12; // default fontSize - int fontAlign = script_hasdata(st,7) ? script_getnum(st,7) : 0; // default fontAlign - int fontY = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontY - - if (flag&0x0f) // Broadcast source or broadcast region defined - { - send_target target; - struct block_list *bl = (flag&0x08) ? map_id2bl(st->oid) : (struct block_list *)script_rid2sd(st); // If bc_npc flag is set, use NPC as broadcast source - if (bl == NULL) - return 0; - - flag &= 0x07; - target = (flag == 1) ? ALL_SAMEMAP : - (flag == 2) ? AREA : - (flag == 3) ? SELF : - ALL_CLIENT; - if (fontColor) - clif_broadcast2(bl, mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target); - else - clif_broadcast(bl, mes, (int)strlen(mes)+1, flag&0xf0, target); - } - else - { - if (fontColor) - intif_broadcast2(mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY); - else - intif_broadcast(mes, (int)strlen(mes)+1, flag&0xf0); - } - return 0; -} -/*========================================== - *------------------------------------------*/ -static int buildin_announce_sub(struct block_list *bl, va_list ap) -{ - char *mes = va_arg(ap, char *); - int len = va_arg(ap, int); - int type = va_arg(ap, int); - char *fontColor = va_arg(ap, char *); - short fontType = (short)va_arg(ap, int); - short fontSize = (short)va_arg(ap, int); - short fontAlign = (short)va_arg(ap, int); - short fontY = (short)va_arg(ap, int); - if (fontColor) - clif_broadcast2(bl, mes, len, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, SELF); - else - clif_broadcast(bl, mes, len, type, SELF); - return 0; -} - -BUILDIN_FUNC(mapannounce) -{ - const char *mapname = script_getstr(st,2); - const char *mes = script_getstr(st,3); - int flag = script_getnum(st,4); - const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL; - int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL) - int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize - int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign - int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY - int16 m; - - if ((m = map_mapname2mapid(mapname)) < 0) - return 0; - - map_foreachinmap(buildin_announce_sub, m, BL_PC, - mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(areaannounce) -{ - const char *mapname = script_getstr(st,2); - int x0 = script_getnum(st,3); - int y0 = script_getnum(st,4); - int x1 = script_getnum(st,5); - int y1 = script_getnum(st,6); - const char *mes = script_getstr(st,7); - int flag = script_getnum(st,8); - const char *fontColor = script_hasdata(st,9) ? script_getstr(st,9) : NULL; - int fontType = script_hasdata(st,10) ? script_getnum(st,10) : 0x190; // default fontType (FW_NORMAL) - int fontSize = script_hasdata(st,11) ? script_getnum(st,11) : 12; // default fontSize - int fontAlign = script_hasdata(st,12) ? script_getnum(st,12) : 0; // default fontAlign - int fontY = script_hasdata(st,13) ? script_getnum(st,13) : 0; // default fontY - int16 m; - - if ((m = map_mapname2mapid(mapname)) < 0) - return 0; - - map_foreachinarea(buildin_announce_sub, m, x0, y0, x1, y1, BL_PC, - mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY); - return 0; -} - -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(getusers) -{ - int flag, val = 0; - struct map_session_data* sd; - struct block_list* bl = NULL; - - flag = script_getnum(st,2); - - switch(flag&0x07) - { - case 0: - if(flag&0x8) - {// npc - bl = map_id2bl(st->oid); - } - else if((sd = script_rid2sd(st))!=NULL) - {// pc - bl = &sd->bl; - } - - if(bl) - { - val = map[bl->m].users; - } - break; - case 1: - val = map_getusers(); - break; - default: - ShowWarning("buildin_getusers: Unknown type %d.\n", flag); - script_pushint(st,0); - return 1; - } - - script_pushint(st,val); - return 0; -} -/*========================================== - * Works like @WHO - displays all online users names in window - *------------------------------------------*/ -BUILDIN_FUNC(getusersname) -{ - TBL_PC *sd, *pl_sd; - int /*disp_num=1,*/ group_level = 0; - struct s_mapiterator* iter; - - sd = script_rid2sd(st); - if (!sd) return 0; - - group_level = pc_get_group_level(sd); - iter = mapit_getallusers(); - for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ) - { - if (pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) && pc_get_group_level(pl_sd) > group_level) - continue; // skip hidden sessions - - /* Temporary fix for bugreport:1023. - * Do not uncomment unless you want thousands of 'next' buttons. - if((disp_num++)%10==0) - clif_scriptnext(sd,st->oid);*/ - clif_scriptmes(sd,st->oid,pl_sd->status.name); - } - mapit_free(iter); - - return 0; -} -/*========================================== - * getmapguildusers("mapname",guild ID) Returns the number guild members present on a map [Reddozen] - *------------------------------------------*/ -BUILDIN_FUNC(getmapguildusers) -{ - const char *str; - int16 m; - int gid; - int i=0,c=0; - struct guild *g = NULL; - str=script_getstr(st,2); - gid=script_getnum(st,3); - if ((m = map_mapname2mapid(str)) < 0) { // map id on this server (m == -1 if not in actual map-server) - script_pushint(st,-1); - return 0; - } - g = guild_search(gid); - - if (g){ - for(i = 0; i < g->max_member; i++) - { - if (g->member[i].sd && g->member[i].sd->bl.m == m) - c++; - } - } - - script_pushint(st,c); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(getmapusers) -{ - const char *str; - int16 m; - str=script_getstr(st,2); - if( (m=map_mapname2mapid(str))< 0){ - script_pushint(st,-1); - return 0; - } - script_pushint(st,map[m].users); - return 0; -} -/*========================================== - *------------------------------------------*/ -static int buildin_getareausers_sub(struct block_list *bl,va_list ap) -{ - int *users=va_arg(ap,int *); - (*users)++; - return 0; -} -BUILDIN_FUNC(getareausers) -{ - const char *str; - int16 m,x0,y0,x1,y1,users=0; //doubt we can have more then 32k users on - str=script_getstr(st,2); - x0=script_getnum(st,3); - y0=script_getnum(st,4); - x1=script_getnum(st,5); - y1=script_getnum(st,6); - if( (m=map_mapname2mapid(str))< 0){ - script_pushint(st,-1); - return 0; - } - map_foreachinarea(buildin_getareausers_sub, - m,x0,y0,x1,y1,BL_PC,&users); - script_pushint(st,users); - return 0; -} - -/*========================================== - *------------------------------------------*/ -static int buildin_getareadropitem_sub(struct block_list *bl,va_list ap) -{ - int item=va_arg(ap,int); - int *amount=va_arg(ap,int *); - struct flooritem_data *drop=(struct flooritem_data *)bl; - - if(drop->item_data.nameid==item) - (*amount)+=drop->item_data.amount; - - return 0; -} -BUILDIN_FUNC(getareadropitem) -{ - const char *str; - int16 m,x0,y0,x1,y1; - int item,amount=0; - struct script_data *data; - - str=script_getstr(st,2); - x0=script_getnum(st,3); - y0=script_getnum(st,4); - x1=script_getnum(st,5); - y1=script_getnum(st,6); - - data=script_getdata(st,7); - get_val(st,data); - if( data_isstring(data) ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - item=UNKNOWN_ITEM_ID; - if( item_data ) - item=item_data->nameid; - }else - item=conv_num(st,data); - - if( (m=map_mapname2mapid(str))< 0){ - script_pushint(st,-1); - return 0; - } - map_foreachinarea(buildin_getareadropitem_sub, - m,x0,y0,x1,y1,BL_ITEM,item,&amount); - script_pushint(st,amount); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(enablenpc) -{ - const char *str; - str=script_getstr(st,2); - npc_enable(str,1); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(disablenpc) -{ - const char *str; - str=script_getstr(st,2); - npc_enable(str,0); - return 0; -} - -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(hideoffnpc) -{ - const char *str; - str=script_getstr(st,2); - npc_enable(str,2); - return 0; -} -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(hideonnpc) -{ - const char *str; - str=script_getstr(st,2); - npc_enable(str,4); - return 0; -} - -/// Starts a status effect on the target unit or on the attached player. -/// -/// sc_start <effect_id>,<duration>,<val1>{,<unit_id>}; -BUILDIN_FUNC(sc_start) -{ - struct block_list* bl; - enum sc_type type; - int tick; - int val1; - int val4 = 0; - - type = (sc_type)script_getnum(st,2); - tick = script_getnum(st,3); - val1 = script_getnum(st,4); - if( script_hasdata(st,5) ) - bl = map_id2bl(script_getnum(st,5)); - else - bl = map_id2bl(st->rid); - - if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 ) - {// When there isn't a duration specified, try to get it from the skill_db - tick = skill_get_time(status_sc2skill(type), val1); - } - - if( potion_flag == 1 && potion_target ) - { //skill.c set the flags before running the script, this must be a potion-pitched effect. - bl = map_id2bl(potion_target); - tick /= 2;// Thrown potions only last half. - val4 = 1;// Mark that this was a thrown sc_effect - } - - if( bl ) - status_change_start(bl, type, 10000, val1, 0, 0, val4, tick, 2); - - return 0; -} - -/// Starts a status effect on the target unit or on the attached player. -/// -/// sc_start2 <effect_id>,<duration>,<val1>,<percent chance>{,<unit_id>}; -BUILDIN_FUNC(sc_start2) -{ - struct block_list* bl; - enum sc_type type; - int tick; - int val1; - int val4 = 0; - int rate; - - type = (sc_type)script_getnum(st,2); - tick = script_getnum(st,3); - val1 = script_getnum(st,4); - rate = script_getnum(st,5); - if( script_hasdata(st,6) ) - bl = map_id2bl(script_getnum(st,6)); - else - bl = map_id2bl(st->rid); - - if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 ) - {// When there isn't a duration specified, try to get it from the skill_db - tick = skill_get_time(status_sc2skill(type), val1); - } - - if( potion_flag == 1 && potion_target ) - { //skill.c set the flags before running the script, this must be a potion-pitched effect. - bl = map_id2bl(potion_target); - tick /= 2;// Thrown potions only last half. - val4 = 1;// Mark that this was a thrown sc_effect - } - - if( bl ) - status_change_start(bl, type, rate, val1, 0, 0, val4, tick, 2); - - return 0; -} - -/// Starts a status effect on the target unit or on the attached player. -/// -/// sc_start4 <effect_id>,<duration>,<val1>,<val2>,<val3>,<val4>{,<unit_id>}; -BUILDIN_FUNC(sc_start4) -{ - struct block_list* bl; - enum sc_type type; - int tick; - int val1; - int val2; - int val3; - int val4; - - type = (sc_type)script_getnum(st,2); - tick = script_getnum(st,3); - val1 = script_getnum(st,4); - val2 = script_getnum(st,5); - val3 = script_getnum(st,6); - val4 = script_getnum(st,7); - if( script_hasdata(st,8) ) - bl = map_id2bl(script_getnum(st,8)); - else - bl = map_id2bl(st->rid); - - if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 ) - {// When there isn't a duration specified, try to get it from the skill_db - tick = skill_get_time(status_sc2skill(type), val1); - } - - if( potion_flag == 1 && potion_target ) - { //skill.c set the flags before running the script, this must be a potion-pitched effect. - bl = map_id2bl(potion_target); - tick /= 2;// Thrown potions only last half. - } - - if( bl ) - status_change_start(bl, type, 10000, val1, val2, val3, val4, tick, 2); - - return 0; -} - -/// Ends one or all status effects on the target unit or on the attached player. -/// -/// sc_end <effect_id>{,<unit_id>}; -BUILDIN_FUNC(sc_end) -{ - struct block_list* bl; - int type; - - type = script_getnum(st, 2); - if (script_hasdata(st, 3)) - bl = map_id2bl(script_getnum(st, 3)); - else - bl = map_id2bl(st->rid); - - if (potion_flag == 1 && potion_target) //##TODO how does this work [FlavioJS] - bl = map_id2bl(potion_target); - - if (!bl) - return 0; - - if (type >= 0 && type < SC_MAX) - { - struct status_change *sc = status_get_sc(bl); - struct status_change_entry *sce = sc ? sc->data[type] : NULL; - - if (!sce) - return 0; - - - switch (type) - { - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_NOCHAT: - case SC_PUSH_CART: - return 0; - - default: - break; - } - - //This should help status_change_end force disabling the SC in case it has no limit. - sce->val1 = sce->val2 = sce->val3 = sce->val4 = 0; - status_change_end(bl, (sc_type)type, INVALID_TIMER); - } - else - status_change_clear(bl, 3); // remove all effects - - return 0; -} - -/*========================================== - * @FIXME atm will return reduced tick, 0 immune, 1 no tick - *------------------------------------------*/ -BUILDIN_FUNC(getscrate) -{ - struct block_list *bl; - int type,rate; - - type=script_getnum(st,2); - rate=script_getnum(st,3); - if( script_hasdata(st,4) ) //get for the bl assigned - bl = map_id2bl(script_getnum(st,4)); - else - bl = map_id2bl(st->rid); - - if (bl) - rate = status_get_sc_def(bl, (sc_type)type, 10000, 10000, 0); - - script_pushint(st,rate); - return 0; -} - -/*========================================== - * getstatus <type>{, <info>}; - *------------------------------------------*/ -BUILDIN_FUNC(getstatus) -{ - int id, type; - struct map_session_data* sd = script_rid2sd(st); - - if( sd == NULL ) - {// no player attached - return 0; - } - - id = script_getnum(st, 2); - type = script_hasdata(st, 3) ? script_getnum(st, 3) : 0; - - if( id <= SC_NONE || id >= SC_MAX ) - {// invalid status type given - ShowWarning("script.c:getstatus: Invalid status type given (%d).\n", id); - return 0; - } - - if( sd->sc.count == 0 || !sd->sc.data[id] ) - {// no status is active - script_pushint(st, 0); - return 0; - } - - switch( type ) - { - case 1: script_pushint(st, sd->sc.data[id]->val1); break; - case 2: script_pushint(st, sd->sc.data[id]->val2); break; - case 3: script_pushint(st, sd->sc.data[id]->val3); break; - case 4: script_pushint(st, sd->sc.data[id]->val4); break; - case 5: - { - struct TimerData* timer = (struct TimerData*)get_timer(sd->sc.data[id]->timer); - - if( timer ) - {// return the amount of time remaining - script_pushint(st, timer->tick - gettick()); - } - } - break; - default: script_pushint(st, 1); break; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(debugmes) -{ - const char *str; - str=script_getstr(st,2); - ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,str); - return 0; -} - -/*========================================== - *------------------------------------------*/ -BUILDIN_FUNC(catchpet) -{ - int pet_id; - TBL_PC *sd; - - pet_id= script_getnum(st,2); - sd=script_rid2sd(st); - if( sd == NULL ) - return 0; - - pet_catch_process1(sd,pet_id); - return 0; -} - -/*========================================== - * [orn] - *------------------------------------------*/ -BUILDIN_FUNC(homunculus_evolution) -{ - TBL_PC *sd; - - sd=script_rid2sd(st); - if( sd == NULL ) - return 0; - - if(merc_is_hom_active(sd->hd)) - { - if (sd->hd->homunculus.intimacy > 91000) - merc_hom_evolution(sd->hd); - else - clif_emotion(&sd->hd->bl, E_SWT); - } - return 0; -} - -/*========================================== - * [Xantara] - *------------------------------------------*/ -BUILDIN_FUNC(homunculus_mutate) -{ - int homun_id, m_class, m_id; - TBL_PC *sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - if(script_hasdata(st,2)) - homun_id = script_getnum(st,2); - else - homun_id = 6048 + (rnd() % 4); - - if(merc_is_hom_active(sd->hd)) { - m_class = hom_class2mapid(sd->hd->homunculus.class_); - m_id = hom_class2mapid(homun_id); - - if ( m_class != -1 && m_id != -1 && m_class&HOM_EVO && m_id&HOM_S && sd->hd->homunculus.level >= 99 ) - hom_mutate(sd->hd, homun_id); - else - clif_emotion(&sd->hd->bl, E_SWT); - } - return 0; -} - -// [Zephyrus] -BUILDIN_FUNC(homunculus_shuffle) -{ - TBL_PC *sd; - - sd=script_rid2sd(st); - if( sd == NULL ) - return 0; - - if(merc_is_hom_active(sd->hd)) - merc_hom_shuffle(sd->hd); - - return 0; -} - -//These two functions bring the eA MAPID_* class functionality to scripts. -BUILDIN_FUNC(eaclass) -{ - int class_; - if( script_hasdata(st,2) ) - class_ = script_getnum(st,2); - else { - TBL_PC *sd; - sd=script_rid2sd(st); - if (!sd) { - script_pushint(st,-1); - return 0; - } - class_ = sd->status.class_; - } - script_pushint(st,pc_jobid2mapid(class_)); - return 0; -} - -BUILDIN_FUNC(roclass) -{ - int class_ =script_getnum(st,2); - int sex; - if( script_hasdata(st,3) ) - sex = script_getnum(st,3); - else { - TBL_PC *sd; - if (st->rid && (sd=script_rid2sd(st))) - sex = sd->status.sex; - else - sex = 1; //Just use male when not found. - } - script_pushint(st,pc_mapid2jobid(class_, sex)); - return 0; -} - -/*========================================== - * Tells client to open a hatching window, used for pet incubator - *------------------------------------------*/ -BUILDIN_FUNC(birthpet) -{ - TBL_PC *sd; - sd=script_rid2sd(st); - if( sd == NULL ) - return 0; - - if( sd->status.pet_id ) - {// do not send egg list, when you already have a pet - return 0; - } - - clif_sendegg(sd); - return 0; -} - -/*========================================== - * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes) - * @type - * 1 : make like after rebirth - * 2 : blvl,jlvl=1, skillpoint=0 - * 3 : don't reset skill, blvl=1 - * 4 : jlvl=0 - *------------------------------------------*/ -BUILDIN_FUNC(resetlvl) -{ - TBL_PC *sd; - - int type=script_getnum(st,2); - - sd=script_rid2sd(st); - if( sd == NULL ) - return 0; - - pc_resetlvl(sd,type); - return 0; -} -/*========================================== - * Reset a player status point - *------------------------------------------*/ -BUILDIN_FUNC(resetstatus) -{ - TBL_PC *sd; - sd=script_rid2sd(st); - pc_resetstate(sd); - return 0; -} - -/*========================================== - * script command resetskill - *------------------------------------------*/ -BUILDIN_FUNC(resetskill) -{ - TBL_PC *sd; - sd=script_rid2sd(st); - pc_resetskill(sd,1); - return 0; -} - -/*========================================== - * Counts total amount of skill points. - *------------------------------------------*/ -BUILDIN_FUNC(skillpointcount) -{ - TBL_PC *sd; - sd=script_rid2sd(st); - script_pushint(st,sd->status.skill_point + pc_resetskill(sd,2)); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(changebase) -{ - TBL_PC *sd=NULL; - int vclass; - - if( script_hasdata(st,3) ) - sd=map_id2sd(script_getnum(st,3)); - else - sd=script_rid2sd(st); - - if(sd == NULL) - return 0; - - vclass = script_getnum(st,2); - if(vclass == JOB_WEDDING) - { - if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites - sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore. - ) - return 0; - } - - if(!sd->disguise && vclass != sd->vd.class_) { - status_set_viewdata(&sd->bl, vclass); - //Updated client view. Base, Weapon and Cloth Colors. - clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_); - clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); - if (sd->vd.cloth_color) - clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); - clif_skillinfoblock(sd); - } - - return 0; -} - -/*========================================== - * Unequip all item and request for a changesex to char-serv - *------------------------------------------*/ -BUILDIN_FUNC(changesex) -{ - int i; - TBL_PC *sd = NULL; - sd = script_rid2sd(st); - - pc_resetskill(sd,4); - // to avoid any problem with equipment and invalid sex, equipment is unequiped. - for( i=0; i<EQI_MAX; i++ ) - if( sd->equip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3); - chrif_changesex(sd); - return 0; -} - -/*========================================== - * Works like 'announce' but outputs in the common chat window - *------------------------------------------*/ -BUILDIN_FUNC(globalmes) -{ - struct block_list *bl = map_id2bl(st->oid); - struct npc_data *nd = (struct npc_data *)bl; - const char *name=NULL,*mes; - - mes=script_getstr(st,2); - if(mes==NULL) return 0; - - if(script_hasdata(st,3)){ // npc name to display - name=script_getstr(st,3); - } else { - name=nd->name; //use current npc name - } - - npc_globalmessage(name,mes); // broadcast to all players connected - - return 0; -} - -///////////////////////////////////////////////////////////////////// -// NPC waiting room (chat room) -// - -/// Creates a waiting room (chat room) for this npc. -/// -/// waitingroom "<title>",<limit>{,"<event>"{,<trigger>{,<zeny>{,<minlvl>{,<maxlvl>}}}}}; -BUILDIN_FUNC(waitingroom) -{ - struct npc_data* nd; - int pub = 1; - const char* title = script_getstr(st, 2); - int limit = script_getnum(st, 3); - const char* ev = script_hasdata(st,4) ? script_getstr(st,4) : ""; - int trigger = script_hasdata(st,5) ? script_getnum(st,5) : limit; - int zeny = script_hasdata(st,6) ? script_getnum(st,6) : 0; - int minLvl = script_hasdata(st,7) ? script_getnum(st,7) : 1; - int maxLvl = script_hasdata(st,8) ? script_getnum(st,8) : MAX_LEVEL; - - nd = (struct npc_data *)map_id2bl(st->oid); - if( nd != NULL ) - chat_createnpcchat(nd, title, limit, pub, trigger, ev, zeny, minLvl, maxLvl); - - return 0; -} - -/// Removes the waiting room of the current or target npc. -/// -/// delwaitingroom "<npc_name>"; -/// delwaitingroom; -BUILDIN_FUNC(delwaitingroom) -{ - struct npc_data* nd; - if( script_hasdata(st,2) ) - nd = npc_name2id(script_getstr(st, 2)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - if( nd != NULL ) - chat_deletenpcchat(nd); - return 0; -} - -/// Kicks all the players from the waiting room of the current or target npc. -/// -/// kickwaitingroomall "<npc_name>"; -/// kickwaitingroomall; -BUILDIN_FUNC(waitingroomkickall) -{ - struct npc_data* nd; - struct chat_data* cd; - - if( script_hasdata(st,2) ) - nd = npc_name2id(script_getstr(st,2)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL ) - chat_npckickall(cd); - return 0; -} - -/// Enables the waiting room event of the current or target npc. -/// -/// enablewaitingroomevent "<npc_name>"; -/// enablewaitingroomevent; -BUILDIN_FUNC(enablewaitingroomevent) -{ - struct npc_data* nd; - struct chat_data* cd; - - if( script_hasdata(st,2) ) - nd = npc_name2id(script_getstr(st, 2)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL ) - chat_enableevent(cd); - return 0; -} - -/// Disables the waiting room event of the current or target npc. -/// -/// disablewaitingroomevent "<npc_name>"; -/// disablewaitingroomevent; -BUILDIN_FUNC(disablewaitingroomevent) -{ - struct npc_data *nd; - struct chat_data *cd; - - if( script_hasdata(st,2) ) - nd = npc_name2id(script_getstr(st, 2)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL ) - chat_disableevent(cd); - return 0; -} - -/// Returns info on the waiting room of the current or target npc. -/// Returns -1 if the type unknown -/// <type>=0 : current number of users -/// <type>=1 : maximum number of users allowed -/// <type>=2 : the number of users that trigger the event -/// <type>=3 : if the trigger is disabled -/// <type>=4 : the title of the waiting room -/// <type>=5 : the password of the waiting room -/// <type>=16 : the name of the waiting room event -/// <type>=32 : if the waiting room is full -/// <type>=33 : if there are enough users to trigger the event -/// -/// getwaitingroomstate(<type>,"<npc_name>") -> <info> -/// getwaitingroomstate(<type>) -> <info> -BUILDIN_FUNC(getwaitingroomstate) -{ - struct npc_data *nd; - struct chat_data *cd; - int type; - - type = script_getnum(st,2); - if( script_hasdata(st,3) ) - nd = npc_name2id(script_getstr(st, 3)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL ) - { - script_pushint(st, -1); - return 0; - } - - switch(type) - { - case 0: script_pushint(st, cd->users); break; - case 1: script_pushint(st, cd->limit); break; - case 2: script_pushint(st, cd->trigger&0x7f); break; - case 3: script_pushint(st, ((cd->trigger&0x80)!=0)); break; - case 4: script_pushstrcopy(st, cd->title); break; - case 5: script_pushstrcopy(st, cd->pass); break; - case 16: script_pushstrcopy(st, cd->npc_event);break; - case 32: script_pushint(st, (cd->users >= cd->limit)); break; - case 33: script_pushint(st, (cd->users >= cd->trigger)); break; - default: script_pushint(st, -1); break; - } - return 0; -} - -/// Warps the trigger or target amount of players to the target map and position. -/// Players are automatically removed from the waiting room. -/// Those waiting the longest will get warped first. -/// The target map can be "Random" for a random position in the current map, -/// and "SavePoint" for the savepoint map+position. -/// The map flag noteleport of the current map is only considered when teleporting to the savepoint. -/// -/// The id's of the teleported players are put into the array $@warpwaitingpc[] -/// The total number of teleported players is put into $@warpwaitingpcnum -/// -/// warpwaitingpc "<map name>",<x>,<y>,<number of players>; -/// warpwaitingpc "<map name>",<x>,<y>; -BUILDIN_FUNC(warpwaitingpc) -{ - int x; - int y; - int i; - int n; - const char* map_name; - struct npc_data* nd; - struct chat_data* cd; - TBL_PC* sd; - - nd = (struct npc_data *)map_id2bl(st->oid); - if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL ) - return 0; - - map_name = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - n = cd->trigger&0x7f; - - if( script_hasdata(st,5) ) - n = script_getnum(st,5); - - for( i = 0; i < n && cd->users > 0; i++ ) - { - sd = cd->usersd[0]; - - if( strcmp(map_name,"SavePoint") == 0 && map[sd->bl.m].flag.noteleport ) - {// can't teleport on this map - break; - } - - if( cd->zeny ) - {// fee set - if( (uint32)sd->status.zeny < cd->zeny ) - {// no zeny to cover set fee - break; - } - pc_payzeny(sd, cd->zeny, LOG_TYPE_NPC, NULL); - } - - mapreg_setreg(reference_uid(add_str("$@warpwaitingpc"), i), sd->bl.id); - - if( strcmp(map_name,"Random") == 0 ) - pc_randomwarp(sd,CLR_TELEPORT); - else if( strcmp(map_name,"SavePoint") == 0 ) - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); - else - pc_setpos(sd, mapindex_name2id(map_name), x, y, CLR_OUTSIGHT); - } - mapreg_setreg(add_str("$@warpwaitingpcnum"), i); - return 0; -} - -///////////////////////////////////////////////////////////////////// -// ... -// - -/// Detaches a character from a script. -/// -/// @param st Script state to detach the character from. -static void script_detach_rid(struct script_state* st) -{ - if(st->rid) - { - script_detach_state(st, false); - st->rid = 0; - } -} - -/*========================================== - * Attach sd char id to script and detach current one if any - *------------------------------------------*/ -BUILDIN_FUNC(attachrid) -{ - int rid = script_getnum(st,2); - struct map_session_data* sd; - - if ((sd = map_id2sd(rid))!=NULL) { - script_detach_rid(st); - - st->rid = rid; - script_attach_state(st); - script_pushint(st,1); - } else - script_pushint(st,0); - return 0; -} -/*========================================== - * Detach script to rid - *------------------------------------------*/ -BUILDIN_FUNC(detachrid) -{ - script_detach_rid(st); - return 0; -} -/*========================================== - * Chk if account connected, (and charid from account if specified) - *------------------------------------------*/ -BUILDIN_FUNC(isloggedin) -{ - TBL_PC* sd = map_id2sd(script_getnum(st,2)); - if (script_hasdata(st,3) && sd && - sd->status.char_id != script_getnum(st,3)) - sd = NULL; - push_val(st->stack,C_INT,sd!=NULL); - return 0; -} - - -/*========================================== - * - *------------------------------------------*/ -BUILDIN_FUNC(setmapflagnosave) -{ - int16 m,x,y; - unsigned short mapindex; - const char *str,*str2; - - str=script_getstr(st,2); - str2=script_getstr(st,3); - x=script_getnum(st,4); - y=script_getnum(st,5); - m = map_mapname2mapid(str); - mapindex = mapindex_name2id(str2); - - if(m >= 0 && mapindex) { - map[m].flag.nosave=1; - map[m].save.map=mapindex; - map[m].save.x=x; - map[m].save.y=y; - } - - return 0; -} - -BUILDIN_FUNC(getmapflag) -{ - int16 m,i; - const char *str; - - str=script_getstr(st,2); - i=script_getnum(st,3); - - m = map_mapname2mapid(str); - if(m >= 0) { - 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; - case MF_PVP: script_pushint(st,map[m].flag.pvp); break; - case MF_PVP_NOPARTY: script_pushint(st,map[m].flag.pvp_noparty); break; - 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_NOTRADE: script_pushint(st,map[m].flag.notrade); 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_FOG: script_pushint(st,map[m].flag.fog); break; - case MF_SAKURA: script_pushint(st,map[m].flag.sakura); break; - case MF_LEAVES: script_pushint(st,map[m].flag.leaves); 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; - case MF_NOMVPLOOT: script_pushint(st,map[m].flag.nomvploot); break; - case MF_NORETURN: script_pushint(st,map[m].flag.noreturn); break; - case MF_NOWARPTO: script_pushint(st,map[m].flag.nowarpto); break; - 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_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; - case MF_ALLOWKS: script_pushint(st,map[m].flag.allowks); break; - 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; - } - } - - return 0; -} -/* pvp timer handling */ -static int script_mapflag_pvp_sub(struct block_list *bl,va_list ap) { - TBL_PC* sd = (TBL_PC*)bl; - if (sd->pvp_timer == INVALID_TIMER) { - sd->pvp_timer = add_timer(gettick() + 200, pc_calc_pvprank_timer, sd->bl.id, 0); - sd->pvp_rank = 0; - sd->pvp_lastusers = 0; - sd->pvp_point = 5; - sd->pvp_won = 0; - sd->pvp_lost = 0; - } - clif_map_property(sd, MAPPROPERTY_FREEPVPZONE); - return 0; -} -BUILDIN_FUNC(setmapflag) -{ - int16 m,i; - const char *str; - int val=0; - - str=script_getstr(st,2); - i=script_getnum(st,3); - if(script_hasdata(st,4)){ - val=script_getnum(st,4); - } - 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_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; - if( !battle_config.pk_mode ) { - map_foreachinmap(script_mapflag_pvp_sub,m,BL_PC); - } - 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; - clif_map_property_mapall(m, MAPPROPERTY_AGITZONE); - 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].zone |= 1<<(val+1); - map[m].flag.restricted=1; - break; - case MF_NOCOMMAND: map[m].nocommand = (val <= 0) ? 100 : val; break; - case MF_NODROP: map[m].flag.nodrop = 1; break; - case MF_JEXP: map[m].jexp = (val <= 0) ? 100 : val; break; - case MF_BEXP: map[m].bexp = (val <= 0) ? 100 : 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_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: map[m].flag.battleground = (val <= 0 || val > 2) ? 1 : val; break; - case MF_RESET: map[m].flag.reset = 1; break; - } - } - - return 0; -} - -BUILDIN_FUNC(removemapflag) -{ - int16 m,i; - const char *str; - int val=0; - - str=script_getstr(st,2); - i=script_getnum(st,3); - if(script_hasdata(st,4)){ - val=script_getnum(st,4); - } - 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_NOZENYPENALTY: map[m].flag.nozenypenalty = 0; break; - case MF_PVP: - map[m].flag.pvp = 0; - clif_map_property_mapall(m, MAPPROPERTY_NOTHING); - 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; - clif_map_property_mapall(m, MAPPROPERTY_NOTHING); - 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].zone ^= 1<<(val+1); - if (map[m].zone == 0){ - 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; - } - } - - return 0; -} - -BUILDIN_FUNC(pvpon) -{ - int16 m; - const char *str; - TBL_PC* sd = NULL; - struct s_mapiterator* iter; - - str = script_getstr(st,2); - m = map_mapname2mapid(str); - if( m < 0 || map[m].flag.pvp ) - return 0; // nothing to do - - map[m].flag.pvp = 1; - clif_map_property_mapall(m, MAPPROPERTY_FREEPVPZONE); - - if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris] - return 0; - - iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) - { - if( sd->bl.m != m || sd->pvp_timer != INVALID_TIMER ) - continue; // not applicable - - sd->pvp_timer = add_timer(gettick()+200,pc_calc_pvprank_timer,sd->bl.id,0); - sd->pvp_rank = 0; - sd->pvp_lastusers = 0; - sd->pvp_point = 5; - sd->pvp_won = 0; - sd->pvp_lost = 0; - } - mapit_free(iter); - - return 0; -} - -static int buildin_pvpoff_sub(struct block_list *bl,va_list ap) -{ - TBL_PC* sd = (TBL_PC*)bl; - clif_pvpset(sd, 0, 0, 2); - if (sd->pvp_timer != INVALID_TIMER) { - delete_timer(sd->pvp_timer, pc_calc_pvprank_timer); - sd->pvp_timer = INVALID_TIMER; - } - return 0; -} - -BUILDIN_FUNC(pvpoff) -{ - int16 m; - const char *str; - - str=script_getstr(st,2); - m = map_mapname2mapid(str); - if(m < 0 || !map[m].flag.pvp) - return 0; //fixed Lupus - - map[m].flag.pvp = 0; - clif_map_property_mapall(m, MAPPROPERTY_NOTHING); - - if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] - return 0; - - map_foreachinmap(buildin_pvpoff_sub, m, BL_PC); - return 0; -} - -BUILDIN_FUNC(gvgon) -{ - int16 m; - const char *str; - - str=script_getstr(st,2); - m = map_mapname2mapid(str); - if(m >= 0 && !map[m].flag.gvg) { - map[m].flag.gvg = 1; - clif_map_property_mapall(m, MAPPROPERTY_AGITZONE); - } - - return 0; -} -BUILDIN_FUNC(gvgoff) -{ - int16 m; - const char *str; - - str=script_getstr(st,2); - m = map_mapname2mapid(str); - if(m >= 0 && map[m].flag.gvg) { - map[m].flag.gvg = 0; - clif_map_property_mapall(m, MAPPROPERTY_NOTHING); - } - - return 0; -} -/*========================================== - * Shows an emoticon on top of the player/npc - * emotion emotion#, <target: 0 - NPC, 1 - PC>, <NPC/PC name> - *------------------------------------------*/ -//Optional second parameter added by [Skotlex] -BUILDIN_FUNC(emotion) -{ - int type; - int player=0; - - type=script_getnum(st,2); - if(type < 0 || type > 100) - return 0; - - if( script_hasdata(st,3) ) - player=script_getnum(st,3); - - if (player) { - TBL_PC *sd = NULL; - if( script_hasdata(st,4) ) - sd = map_nick2sd(script_getstr(st,4)); - else - sd = script_rid2sd(st); - if (sd) - clif_emotion(&sd->bl,type); - } else - if( script_hasdata(st,4) ) - { - TBL_NPC *nd = npc_name2id(script_getstr(st,4)); - if(nd) - clif_emotion(&nd->bl,type); - } - else - clif_emotion(map_id2bl(st->oid),type); - return 0; -} - -static int buildin_maprespawnguildid_sub_pc(struct map_session_data* sd, va_list ap) -{ - int16 m=va_arg(ap,int); - int g_id=va_arg(ap,int); - int flag=va_arg(ap,int); - - if(!sd || sd->bl.m != m) - return 0; - if( - (sd->status.guild_id == g_id && flag&1) || //Warp out owners - (sd->status.guild_id != g_id && flag&2) || //Warp out outsiders - (sd->status.guild_id == 0) // Warp out players not in guild [Valaris] - ) - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - return 1; -} - -static int buildin_maprespawnguildid_sub_mob(struct block_list *bl,va_list ap) -{ - struct mob_data *md=(struct mob_data *)bl; - - if(!md->guardian_data && md->class_ != MOBID_EMPERIUM) - status_kill(bl); - - return 0; -} - -BUILDIN_FUNC(maprespawnguildid) -{ - const char *mapname=script_getstr(st,2); - int g_id=script_getnum(st,3); - int flag=script_getnum(st,4); - - int16 m=map_mapname2mapid(mapname); - - if(m == -1) - return 0; - - //Catch ALL players (in case some are 'between maps' on execution time) - map_foreachpc(buildin_maprespawnguildid_sub_pc,m,g_id,flag); - if (flag&4) //Remove script mobs. - map_foreachinmap(buildin_maprespawnguildid_sub_mob,m,BL_MOB); - return 0; -} - -BUILDIN_FUNC(agitstart) -{ - if(agit_flag==1) return 0; // Agit already Start. - agit_flag=1; - guild_agit_start(); - return 0; -} - -BUILDIN_FUNC(agitend) -{ - if(agit_flag==0) return 0; // Agit already End. - agit_flag=0; - guild_agit_end(); - return 0; -} - -BUILDIN_FUNC(agitstart2) -{ - if(agit2_flag==1) return 0; // Agit2 already Start. - agit2_flag=1; - guild_agit2_start(); - return 0; -} - -BUILDIN_FUNC(agitend2) -{ - if(agit2_flag==0) return 0; // Agit2 already End. - agit2_flag=0; - guild_agit2_end(); - return 0; -} - -/*========================================== - * Returns whether woe is on or off. // choice script - *------------------------------------------*/ -BUILDIN_FUNC(agitcheck) -{ - script_pushint(st,agit_flag); - return 0; -} - -/*========================================== - * Returns whether woese is on or off. // choice script - *------------------------------------------*/ -BUILDIN_FUNC(agitcheck2) -{ - script_pushint(st,agit2_flag); - return 0; -} - -/// Sets the guild_id of this npc. -/// -/// flagemblem <guild_id>; -BUILDIN_FUNC(flagemblem) -{ - TBL_NPC* nd; - int g_id = script_getnum(st,2); - - if(g_id < 0) return 0; - - nd = (TBL_NPC*)map_id2nd(st->oid); - if( nd == NULL ) { - ShowError("script:flagemblem: npc %d not found\n", st->oid); - } else if( nd->subtype != SCRIPT ) { - ShowError("script:flagemblem: unexpected subtype %d for npc %d '%s'\n", nd->subtype, st->oid, nd->exname); - } else { - bool changed = ( nd->u.scr.guild_id != g_id )?true:false; - nd->u.scr.guild_id = g_id; - clif_guild_emblem_area(&nd->bl); - /* guild flag caching */ - if( g_id ) /* adding a id */ - guild_flag_add(nd); - else if( changed ) /* removing a flag */ - guild_flag_remove(nd); - } - return 0; -} - -BUILDIN_FUNC(getcastlename) -{ - const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL); - struct guild_castle* gc = guild_mapname2gc(mapname); - const char* name = (gc) ? gc->castle_name : ""; - script_pushstrcopy(st,name); - return 0; -} - -BUILDIN_FUNC(getcastledata) -{ - const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL); - int index = script_getnum(st,3); - struct guild_castle *gc = guild_mapname2gc(mapname); - - if (gc == NULL) { - script_pushint(st,0); - ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname); - return 1; - } - - switch (index) { - case 1: - script_pushint(st,gc->guild_id); break; - case 2: - script_pushint(st,gc->economy); break; - case 3: - script_pushint(st,gc->defense); break; - case 4: - script_pushint(st,gc->triggerE); break; - case 5: - script_pushint(st,gc->triggerD); break; - case 6: - script_pushint(st,gc->nextTime); break; - case 7: - script_pushint(st,gc->payTime); break; - case 8: - script_pushint(st,gc->createTime); break; - case 9: - script_pushint(st,gc->visibleC); break; - default: - if (index > 9 && index <= 9+MAX_GUARDIANS) { - script_pushint(st,gc->guardian[index-10].visible); - break; - } - script_pushint(st,0); - ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index); - return 1; - } - return 0; -} - -BUILDIN_FUNC(setcastledata) -{ - const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL); - int index = script_getnum(st,3); - int value = script_getnum(st,4); - struct guild_castle *gc = guild_mapname2gc(mapname); - - if (gc == NULL) { - ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname); - return 1; - } - - if (index <= 0 || index > 9+MAX_GUARDIANS) { - ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index); - return 1; - } - - guild_castledatasave(gc->castle_id, index, value); - return 0; -} - -/* ===================================================================== - * ---------------------------------------------------------------------*/ -BUILDIN_FUNC(requestguildinfo) -{ - int guild_id=script_getnum(st,2); - const char *event=NULL; - - if( script_hasdata(st,3) ){ - event=script_getstr(st,3); - check_event(st, event); - } - - if(guild_id>0) - guild_npc_request_info(guild_id,event); - return 0; -} - -/// Returns the number of cards that have been compounded onto the specified equipped item. -/// getequipcardcnt(<equipment slot>); -BUILDIN_FUNC(getequipcardcnt) -{ - int i=-1,j,num; - TBL_PC *sd; - int count; - - num=script_getnum(st,2); - sd=script_rid2sd(st); - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - - if (i < 0 || !sd->inventory_data[i]) { - script_pushint(st,0); - return 0; - } - - if(itemdb_isspecial(sd->status.inventory[i].card[0])) - { - script_pushint(st,0); - return 0; - } - - count = 0; - for( j = 0; j < sd->inventory_data[i]->slot; j++ ) - if( sd->status.inventory[i].card[j] && itemdb_type(sd->status.inventory[i].card[j]) == IT_CARD ) - count++; - - script_pushint(st,count); - return 0; -} - -/// Removes all cards from the item found in the specified equipment slot of the invoking character, -/// and give them to the character. If any cards were removed in this manner, it will also show a success effect. -/// successremovecards <slot>; -BUILDIN_FUNC(successremovecards) { - int i=-1,j,c,cardflag=0; - - TBL_PC* sd = script_rid2sd(st); - int num = script_getnum(st,2); - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - - if (i < 0 || !sd->inventory_data[i]) { - return 0; - } - - if(itemdb_isspecial(sd->status.inventory[i].card[0])) - return 0; - - for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) { - if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {// extract this card from the item - int flag; - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - cardflag = 1; - item_tmp.nameid = sd->status.inventory[i].card[c]; - item_tmp.identify = 1; - - if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ // get back the cart in inventory - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - - if(cardflag == 1) {//if card was remove remplace item with no card - int flag; - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - - item_tmp.nameid = sd->status.inventory[i].nameid; - item_tmp.identify = 1; - item_tmp.refine = sd->status.inventory[i].refine; - item_tmp.attribute = sd->status.inventory[i].attribute; - item_tmp.expire_time = sd->status.inventory[i].expire_time; - - for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++) - item_tmp.card[j]=sd->status.inventory[i].card[j]; - - pc_delitem(sd,i,1,0,3,LOG_TYPE_SCRIPT); - if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ //chk if can be spawn in inventory otherwise put on floor - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - - clif_misceffect(&sd->bl,3); - } - return 0; -} - -/// Removes all cards from the item found in the specified equipment slot of the invoking character. -/// failedremovecards <slot>, <type>; -/// <type>=0 : will destroy both the item and the cards. -/// <type>=1 : will keep the item, but destroy the cards. -/// <type>=2 : will keep the cards, but destroy the item. -/// <type>=? : will just display the failure effect. -BUILDIN_FUNC(failedremovecards) { - int i=-1,j,c,cardflag=0; - - TBL_PC* sd = script_rid2sd(st); - int num = script_getnum(st,2); - int typefail = script_getnum(st,3); - - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - - if (i < 0 || !sd->inventory_data[i]) - return 0; - - if(itemdb_isspecial(sd->status.inventory[i].card[0])) - return 0; - - for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) { - if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) { - cardflag = 1; - - if(typefail == 2) {// add cards to inventory, clear - int flag; - struct item item_tmp; - - memset(&item_tmp,0,sizeof(item_tmp)); - - item_tmp.nameid = sd->status.inventory[i].card[c]; - item_tmp.identify = 1; - - if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - } - - if(cardflag == 1) { - if(typefail == 0 || typefail == 2){ // destroy the item - pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT); - } - if(typefail == 1){ // destroy the card - int flag; - struct item item_tmp; - - memset(&item_tmp,0,sizeof(item_tmp)); - - item_tmp.nameid = sd->status.inventory[i].nameid; - item_tmp.identify = 1; - item_tmp.refine = sd->status.inventory[i].refine; - item_tmp.attribute = sd->status.inventory[i].attribute; - item_tmp.expire_time = sd->status.inventory[i].expire_time; - - for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++) - item_tmp.card[j]=sd->status.inventory[i].card[j]; - - pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT); - - if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - clif_misceffect(&sd->bl,2); - } - - return 0; -} - -/* ================================================================ - * mapwarp "<from map>","<to map>",<x>,<y>,<type>,<ID for Type>; - * type: 0=everyone, 1=guild, 2=party; [Reddozen] - * improved by [Lance] - * ================================================================*/ -BUILDIN_FUNC(mapwarp) // Added by RoVeRT -{ - int x,y,m,check_val=0,check_ID=0,i=0; - struct guild *g = NULL; - struct party_data *p = NULL; - const char *str; - const char *mapname; - unsigned int index; - mapname=script_getstr(st,2); - str=script_getstr(st,3); - x=script_getnum(st,4); - y=script_getnum(st,5); - if(script_hasdata(st,7)){ - check_val=script_getnum(st,6); - check_ID=script_getnum(st,7); - } - - if((m=map_mapname2mapid(mapname))< 0) - return 0; - - if(!(index=mapindex_name2id(str))) - return 0; - - switch(check_val){ - case 1: - g = guild_search(check_ID); - if (g){ - for( i=0; i < g->max_member; i++) - { - if(g->member[i].sd && g->member[i].sd->bl.m==m){ - pc_setpos(g->member[i].sd,index,x,y,CLR_TELEPORT); - } - } - } - break; - case 2: - p = party_search(check_ID); - if(p){ - for(i=0;i<MAX_PARTY; i++){ - if(p->data[i].sd && p->data[i].sd->bl.m == m){ - pc_setpos(p->data[i].sd,index,x,y,CLR_TELEPORT); - } - } - } - break; - default: - map_foreachinmap(buildin_areawarp_sub,m,BL_PC,index,x,y,0,0); - break; - } - - return 0; -} - -static int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT -{ - char *event=va_arg(ap,char *); - struct mob_data *md = ((struct mob_data *)bl); - if( md->status.hp > 0 && (!event || strcmp(event,md->npc_event) == 0) ) - return 1; - return 0; -} - -BUILDIN_FUNC(mobcount) // Added by RoVeRT -{ - const char *mapname,*event; - int16 m; - mapname=script_getstr(st,2); - event=script_getstr(st,3); - - if( strcmp(event, "all") == 0 ) - event = NULL; - else - check_event(st, event); - - if( strcmp(mapname, "this") == 0 ) { - struct map_session_data *sd = script_rid2sd(st); - if( sd ) - m = sd->bl.m; - else { - script_pushint(st,-1); - return 0; - } - } - else if( (m = map_mapname2mapid(mapname)) < 0 ) { - script_pushint(st,-1); - return 0; - } - - if( map[m].flag.src4instance && map[m].instance_id == 0 && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - { - script_pushint(st,-1); - return 0; - } - - script_pushint(st,map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event)); - - return 0; -} - -BUILDIN_FUNC(marriage) -{ - const char *partner=script_getstr(st,2); - TBL_PC *sd=script_rid2sd(st); - TBL_PC *p_sd=map_nick2sd(partner); - - if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){ - script_pushint(st,0); - return 0; - } - script_pushint(st,1); - return 0; -} -BUILDIN_FUNC(wedding_effect) -{ - TBL_PC *sd=script_rid2sd(st); - struct block_list *bl; - - if(sd==NULL) { - bl=map_id2bl(st->oid); - } else - bl=&sd->bl; - clif_wedding_effect(bl); - return 0; -} -BUILDIN_FUNC(divorce) -{ - TBL_PC *sd=script_rid2sd(st); - if(sd==NULL || pc_divorce(sd) < 0){ - script_pushint(st,0); - return 0; - } - script_pushint(st,1); - return 0; -} - -BUILDIN_FUNC(ispartneron) -{ - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || !pc_ismarried(sd) || - map_charid2sd(sd->status.partner_id) == NULL) { - script_pushint(st,0); - return 0; - } - - script_pushint(st,1); - return 0; -} - -BUILDIN_FUNC(getpartnerid) -{ - TBL_PC *sd=script_rid2sd(st); - if (sd == NULL) { - script_pushint(st,0); - return 0; - } - - script_pushint(st,sd->status.partner_id); - return 0; -} - -BUILDIN_FUNC(getchildid) -{ - TBL_PC *sd=script_rid2sd(st); - if (sd == NULL) { - script_pushint(st,0); - return 0; - } - - script_pushint(st,sd->status.child); - return 0; -} - -BUILDIN_FUNC(getmotherid) -{ - TBL_PC *sd=script_rid2sd(st); - if (sd == NULL) { - script_pushint(st,0); - return 0; - } - - script_pushint(st,sd->status.mother); - return 0; -} - -BUILDIN_FUNC(getfatherid) -{ - TBL_PC *sd=script_rid2sd(st); - if (sd == NULL) { - script_pushint(st,0); - return 0; - } - - script_pushint(st,sd->status.father); - return 0; -} - -BUILDIN_FUNC(warppartner) -{ - int x,y; - unsigned short mapindex; - const char *str; - TBL_PC *sd=script_rid2sd(st); - TBL_PC *p_sd=NULL; - - if(sd==NULL || !pc_ismarried(sd) || - (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) { - script_pushint(st,0); - return 0; - } - - str=script_getstr(st,2); - x=script_getnum(st,3); - y=script_getnum(st,4); - - mapindex = mapindex_name2id(str); - if (mapindex) { - pc_setpos(p_sd,mapindex,x,y,CLR_OUTSIGHT); - script_pushint(st,1); - } else - script_pushint(st,0); - return 0; -} - -/*================================================ - * Script for Displaying MOB Information [Valaris] - *------------------------------------------------*/ -BUILDIN_FUNC(strmobinfo) -{ - - int num=script_getnum(st,2); - int class_=script_getnum(st,3); - - if(!mobdb_checkid(class_)) - { - if (num < 3) //requested a string - script_pushconststr(st,""); - else - script_pushint(st,0); - return 0; - } - - switch (num) { - case 1: script_pushstrcopy(st,mob_db(class_)->name); break; - case 2: script_pushstrcopy(st,mob_db(class_)->jname); break; - case 3: script_pushint(st,mob_db(class_)->lv); break; - case 4: script_pushint(st,mob_db(class_)->status.max_hp); break; - case 5: script_pushint(st,mob_db(class_)->status.max_sp); break; - case 6: script_pushint(st,mob_db(class_)->base_exp); break; - case 7: script_pushint(st,mob_db(class_)->job_exp); break; - default: - script_pushint(st,0); - break; - } - return 0; -} - -/*========================================== - * Summon guardians [Valaris] - * guardian("<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"}{,<guardian index>}) -> <id> - *------------------------------------------*/ -BUILDIN_FUNC(guardian) -{ - int class_=0,x=0,y=0,guardian=0; - const char *str,*map,*evt=""; - struct script_data *data; - bool has_index = false; - - map =script_getstr(st,2); - x =script_getnum(st,3); - y =script_getnum(st,4); - str =script_getstr(st,5); - class_=script_getnum(st,6); - - if( script_hasdata(st,8) ) - {// "<event label>",<guardian index> - evt=script_getstr(st,7); - guardian=script_getnum(st,8); - has_index = true; - } else if( script_hasdata(st,7) ){ - data=script_getdata(st,7); - get_val(st,data); - if( data_isstring(data) ) - {// "<event label>" - evt=script_getstr(st,7); - } else if( data_isint(data) ) - {// <guardian index> - guardian=script_getnum(st,7); - has_index = true; - } else { - ShowError("script:guardian: invalid data type for argument #6 (from 1)\n"); - script_reportdata(data); - return 1; - } - } - - check_event(st, evt); - script_pushint(st, mob_spawn_guardian(map,x,y,str,class_,evt,guardian,has_index)); - - return 0; -} -/*========================================== - * Invisible Walls [Zephyrus] - *------------------------------------------*/ -BUILDIN_FUNC(setwall) -{ - const char *map, *name; - int x, y, m, size, dir; - bool shootable; - - map = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - size = script_getnum(st,5); - dir = script_getnum(st,6); - shootable = script_getnum(st,7); - name = script_getstr(st,8); - - if( (m = map_mapname2mapid(map)) < 0 ) - return 0; // Invalid Map - - map_iwall_set(m, x, y, size, dir, shootable, name); - return 0; -} -BUILDIN_FUNC(delwall) -{ - const char *name = script_getstr(st,2); - map_iwall_remove(name); - - return 0; -} - -/// Retrieves various information about the specified guardian. -/// -/// guardianinfo("<map_name>", <index>, <type>) -> <value> -/// type: 0 - whether it is deployed or not -/// 1 - maximum hp -/// 2 - current hp -/// -BUILDIN_FUNC(guardianinfo) -{ - const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL); - int id = script_getnum(st,3); - int type = script_getnum(st,4); - - struct guild_castle* gc = guild_mapname2gc(mapname); - struct mob_data* gd; - - if( gc == NULL || id < 0 || id >= MAX_GUARDIANS ) - { - script_pushint(st,-1); - return 0; - } - - if( type == 0 ) - script_pushint(st, gc->guardian[id].visible); - else - if( !gc->guardian[id].visible ) - script_pushint(st,-1); - else - if( (gd = map_id2md(gc->guardian[id].id)) == NULL ) - script_pushint(st,-1); - else - { - if ( type == 1 ) script_pushint(st,gd->status.max_hp); - else if( type == 2 ) script_pushint(st,gd->status.hp); - else - script_pushint(st,-1); - } - - return 0; -} - -/*========================================== - * Get the item name by item_id or null - *------------------------------------------*/ -BUILDIN_FUNC(getitemname) -{ - int item_id=0; - struct item_data *i_data; - char *item_name; - struct script_data *data; - - data=script_getdata(st,2); - get_val(st,data); - - if( data_isstring(data) ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data ) - item_id=item_data->nameid; - }else - item_id=conv_num(st,data); - - i_data = itemdb_exists(item_id); - if (i_data == NULL) - { - script_pushconststr(st,"null"); - return 0; - } - item_name=(char *)aMalloc(ITEM_NAME_LENGTH*sizeof(char)); - - memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH); - script_pushstr(st,item_name); - return 0; -} -/*========================================== - * Returns number of slots an item has. [Skotlex] - *------------------------------------------*/ -BUILDIN_FUNC(getitemslots) -{ - int item_id; - struct item_data *i_data; - - item_id=script_getnum(st,2); - - i_data = itemdb_exists(item_id); - - if (i_data) - script_pushint(st,i_data->slot); - else - script_pushint(st,-1); - return 0; -} - -// TODO: add matk here if needed/once we get rid of RENEWAL - -/*========================================== - * Returns some values of an item [Lupus] - * Price, Weight, etc... - getiteminfo(itemID,n), where n - 0 value_buy; - 1 value_sell; - 2 type; - 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. - if = 0, then monsters don't drop it at all (rare or a quest item) - if = -1, then this item is sold in NPC shops only - 4 sex; - 5 equip; - 6 weight; - 7 atk; - 8 def; - 9 range; - 10 slot; - 11 look; - 12 elv; - 13 wlv; - 14 view id - *------------------------------------------*/ -BUILDIN_FUNC(getiteminfo) -{ - int item_id,n; - int *item_arr; - struct item_data *i_data; - - item_id = script_getnum(st,2); - n = script_getnum(st,3); - i_data = itemdb_exists(item_id); - - if (i_data && n>=0 && n<=14) { - item_arr = (int*)&i_data->value_buy; - script_pushint(st,item_arr[n]); - } else - script_pushint(st,-1); - return 0; -} - -/*========================================== - * Set some values of an item [Lupus] - * Price, Weight, etc... - setiteminfo(itemID,n,Value), where n - 0 value_buy; - 1 value_sell; - 2 type; - 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. - if = 0, then monsters don't drop it at all (rare or a quest item) - if = -1, then this item is sold in NPC shops only - 4 sex; - 5 equip; - 6 weight; - 7 atk; - 8 def; - 9 range; - 10 slot; - 11 look; - 12 elv; - 13 wlv; - 14 view id - * Returns Value or -1 if the wrong field's been set - *------------------------------------------*/ -BUILDIN_FUNC(setiteminfo) -{ - int item_id,n,value; - int *item_arr; - struct item_data *i_data; - - item_id = script_getnum(st,2); - n = script_getnum(st,3); - value = script_getnum(st,4); - i_data = itemdb_exists(item_id); - - if (i_data && n>=0 && n<=14) { - item_arr = (int*)&i_data->value_buy; - item_arr[n] = value; - script_pushint(st,value); - } else - script_pushint(st,-1); - return 0; -} - -/*========================================== - * Returns value from equipped item slot n [Lupus] - getequipcardid(num,slot) - where - num = eqip position slot - slot = 0,1,2,3 (Card Slot N) - - This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced) - it's useful when you want to check item cards or if it's signed - Useful for such quests as "Sign this refined item with players name" etc - Hat[0] +4 -> Player's Hat[0] +4 - *------------------------------------------*/ -BUILDIN_FUNC(getequipcardid) -{ - int i=-1,num,slot; - TBL_PC *sd; - - num=script_getnum(st,2); - slot=script_getnum(st,3); - sd=script_rid2sd(st); - if (num > 0 && num <= ARRAYLENGTH(equip)) - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0 && slot>=0 && slot<4) - script_pushint(st,sd->status.inventory[i].card[slot]); - else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * petskillbonus [Valaris] //Rewritten by [Skotlex] - *------------------------------------------*/ -BUILDIN_FUNC(petskillbonus) -{ - struct pet_data *pd; - - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->bonus) - { //Clear previous bonus - if (pd->bonus->timer != INVALID_TIMER) - delete_timer(pd->bonus->timer, pet_skill_bonus_timer); - } else //init - pd->bonus = (struct pet_bonus *) aMalloc(sizeof(struct pet_bonus)); - - pd->bonus->type=script_getnum(st,2); - pd->bonus->val=script_getnum(st,3); - pd->bonus->duration=script_getnum(st,4); - pd->bonus->delay=script_getnum(st,5); - - if (pd->state.skillbonus == 1) - pd->state.skillbonus=0; // waiting state - - // wait for timer to start - if (battle_config.pet_equip_required && pd->pet.equip == 0) - pd->bonus->timer = INVALID_TIMER; - else - pd->bonus->timer = add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); - - return 0; -} - -/*========================================== - * pet looting [Valaris] //Rewritten by [Skotlex] - *------------------------------------------*/ -BUILDIN_FUNC(petloot) -{ - int max; - struct pet_data *pd; - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - max=script_getnum(st,2); - - if(max < 1) - max = 1; //Let'em loot at least 1 item. - else if (max > MAX_PETLOOT_SIZE) - max = MAX_PETLOOT_SIZE; - - pd = sd->pd; - if (pd->loot != NULL) - { //Release whatever was there already and reallocate memory - pet_lootitem_drop(pd, pd->msd); - aFree(pd->loot->item); - } - else - pd->loot = (struct pet_loot *)aMalloc(sizeof(struct pet_loot)); - - pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item)); - - pd->loot->max=max; - pd->loot->count = 0; - pd->loot->weight = 0; - - return 0; -} -/*========================================== - * Set arrays with info of all sd inventory : - * @inventorylist_id, @inventorylist_amount, @inventorylist_equip, - * @inventorylist_refine, @inventorylist_identify, @inventorylist_attribute, - * @inventorylist_card(0..3), @inventorylist_expire - * @inventorylist_count = scalar - *------------------------------------------*/ -BUILDIN_FUNC(getinventorylist) -{ - TBL_PC *sd=script_rid2sd(st); - char card_var[NAME_LENGTH]; - - int i,j=0,k; - if(!sd) return 0; - for(i=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){ - pc_setreg(sd,reference_uid(add_str("@inventorylist_id"), j),sd->status.inventory[i].nameid); - pc_setreg(sd,reference_uid(add_str("@inventorylist_amount"), j),sd->status.inventory[i].amount); - pc_setreg(sd,reference_uid(add_str("@inventorylist_equip"), j),sd->status.inventory[i].equip); - pc_setreg(sd,reference_uid(add_str("@inventorylist_refine"), j),sd->status.inventory[i].refine); - pc_setreg(sd,reference_uid(add_str("@inventorylist_identify"), j),sd->status.inventory[i].identify); - pc_setreg(sd,reference_uid(add_str("@inventorylist_attribute"), j),sd->status.inventory[i].attribute); - for (k = 0; k < MAX_SLOTS; k++) - { - sprintf(card_var, "@inventorylist_card%d",k+1); - pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]); - } - pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); - j++; - } - } - pc_setreg(sd,add_str("@inventorylist_count"),j); - return 0; -} - -BUILDIN_FUNC(getskilllist) -{ - TBL_PC *sd=script_rid2sd(st); - int i,j=0; - if(!sd) return 0; - for(i=0;i<MAX_SKILL;i++){ - if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0){ - pc_setreg(sd,reference_uid(add_str("@skilllist_id"), j),sd->status.skill[i].id); - pc_setreg(sd,reference_uid(add_str("@skilllist_lv"), j),sd->status.skill[i].lv); - pc_setreg(sd,reference_uid(add_str("@skilllist_flag"), j),sd->status.skill[i].flag); - j++; - } - } - pc_setreg(sd,add_str("@skilllist_count"),j); - return 0; -} - -BUILDIN_FUNC(clearitem) -{ - TBL_PC *sd=script_rid2sd(st); - int i; - if(sd==NULL) return 0; - for (i=0; i<MAX_INVENTORY; i++) { - if (sd->status.inventory[i].amount) { - pc_delitem(sd, i, sd->status.inventory[i].amount, 0, 0, LOG_TYPE_SCRIPT); - } - } - return 0; -} - -/*========================================== - * Disguise Player (returns Mob/NPC ID if success, 0 on fail) - *------------------------------------------*/ -BUILDIN_FUNC(disguise) -{ - int id; - TBL_PC* sd = script_rid2sd(st); - if (sd == NULL) return 0; - - id = script_getnum(st,2); - - if (mobdb_checkid(id) || npcdb_checkid(id)) { - pc_disguise(sd, id); - script_pushint(st,id); - } else - script_pushint(st,0); - - return 0; -} - -/*========================================== - * Undisguise Player (returns 1 if success, 0 on fail) - *------------------------------------------*/ -BUILDIN_FUNC(undisguise) -{ - TBL_PC* sd = script_rid2sd(st); - if (sd == NULL) return 0; - - if (sd->disguise) { - pc_disguise(sd, 0); - script_pushint(st,0); - } else { - script_pushint(st,1); - } - return 0; -} - -/*========================================== - * Transform a bl to another _class, - * @type unused - *------------------------------------------*/ -BUILDIN_FUNC(classchange) -{ - int _class,type; - struct block_list *bl=map_id2bl(st->oid); - - if(bl==NULL) return 0; - - _class=script_getnum(st,2); - type=script_getnum(st,3); - clif_class_change(bl,_class,type); - return 0; -} - -/*========================================== - * Display an effect - *------------------------------------------*/ -BUILDIN_FUNC(misceffect) -{ - int type; - - type=script_getnum(st,2); - if(st->oid && st->oid != fake_nd->bl.id) { - struct block_list *bl = map_id2bl(st->oid); - if (bl) - clif_specialeffect(bl,type,AREA); - } else{ - TBL_PC *sd=script_rid2sd(st); - if(sd) - clif_specialeffect(&sd->bl,type,AREA); - } - return 0; -} -/*========================================== - * Play a BGM on a single client [Rikter/Yommy] - *------------------------------------------*/ -BUILDIN_FUNC(playBGM) -{ - const char* name; - struct map_session_data* sd; - - if( ( sd = script_rid2sd(st) ) != NULL ) - { - name = script_getstr(st,2); - - clif_playBGM(sd, name); - } - - return 0; -} - -static int playBGM_sub(struct block_list* bl,va_list ap) -{ - const char* name = va_arg(ap,const char*); - - clif_playBGM(BL_CAST(BL_PC, bl), name); - - return 0; -} - -static int playBGM_foreachpc_sub(struct map_session_data* sd, va_list args) -{ - const char* name = va_arg(args, const char*); - - clif_playBGM(sd, name); - return 0; -} - -/*========================================== - * Play a BGM on multiple client [Rikter/Yommy] - *------------------------------------------*/ -BUILDIN_FUNC(playBGMall) -{ - const char* name; - - name = script_getstr(st,2); - - if( script_hasdata(st,7) ) - {// specified part of map - const char* map = script_getstr(st,3); - int x0 = script_getnum(st,4); - int y0 = script_getnum(st,5); - int x1 = script_getnum(st,6); - int y1 = script_getnum(st,7); - - map_foreachinarea(playBGM_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name); - } - else if( script_hasdata(st,3) ) - {// entire map - const char* map = script_getstr(st,3); - - map_foreachinmap(playBGM_sub, map_mapname2mapid(map), BL_PC, name); - } - else - {// entire server - map_foreachpc(&playBGM_foreachpc_sub, name); - } - - return 0; -} - -/*========================================== - * Play a .wav sound for sd - *------------------------------------------*/ -BUILDIN_FUNC(soundeffect) -{ - TBL_PC* sd = script_rid2sd(st); - const char* name = script_getstr(st,2); - int type = script_getnum(st,3); - - if(sd) - { - clif_soundeffect(sd,&sd->bl,name,type); - } - return 0; -} - -int soundeffect_sub(struct block_list* bl,va_list ap) -{ - char* name = va_arg(ap,char*); - int type = va_arg(ap,int); - - clif_soundeffect((TBL_PC *)bl, bl, name, type); - - return 0; -} - -/*========================================== - * Play a sound effect (.wav) on multiple clients - * soundeffectall "<filepath>",<type>{,"<map name>"}{,<x0>,<y0>,<x1>,<y1>}; - *------------------------------------------*/ -BUILDIN_FUNC(soundeffectall) -{ - struct block_list* bl; - const char* name; - int type; - - bl = (st->rid) ? &(script_rid2sd(st)->bl) : map_id2bl(st->oid); - if (!bl) - return 0; - - name = script_getstr(st,2); - type = script_getnum(st,3); - - //FIXME: enumerating map squares (map_foreach) is slower than enumerating the list of online players (map_foreachpc?) [ultramage] - - if(!script_hasdata(st,4)) - { // area around - clif_soundeffectall(bl, name, type, AREA); - } - else - if(!script_hasdata(st,5)) - { // entire map - const char* map = script_getstr(st,4); - map_foreachinmap(soundeffect_sub, map_mapname2mapid(map), BL_PC, name, type); - } - else - if(script_hasdata(st,8)) - { // specified part of map - const char* map = script_getstr(st,4); - int x0 = script_getnum(st,5); - int y0 = script_getnum(st,6); - int x1 = script_getnum(st,7); - int y1 = script_getnum(st,8); - map_foreachinarea(soundeffect_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name, type); - } - else - { - ShowError("buildin_soundeffectall: insufficient arguments for specific area broadcast.\n"); - } - - return 0; -} -/*========================================== - * pet status recovery [Valaris] / Rewritten by [Skotlex] - *------------------------------------------*/ -BUILDIN_FUNC(petrecovery) -{ - struct pet_data *pd; - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - - if (pd->recovery) - { //Halt previous bonus - if (pd->recovery->timer != INVALID_TIMER) - delete_timer(pd->recovery->timer, pet_recovery_timer); - } else //Init - pd->recovery = (struct pet_recovery *)aMalloc(sizeof(struct pet_recovery)); - - pd->recovery->type = (sc_type)script_getnum(st,2); - pd->recovery->delay = script_getnum(st,3); - pd->recovery->timer = INVALID_TIMER; - - return 0; -} - -/*========================================== - * pet healing [Valaris] //Rewritten by [Skotlex] - *------------------------------------------*/ -BUILDIN_FUNC(petheal) -{ - struct pet_data *pd; - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->s_skill) - { //Clear previous skill - if (pd->s_skill->timer != INVALID_TIMER) - { - if (pd->s_skill->id) - delete_timer(pd->s_skill->timer, pet_skill_support_timer); - else - delete_timer(pd->s_skill->timer, pet_heal_timer); - } - } else //init memory - pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support)); - - pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport - //Use the lv as the amount to heal - pd->s_skill->lv=script_getnum(st,2); - pd->s_skill->delay=script_getnum(st,3); - pd->s_skill->hp=script_getnum(st,4); - pd->s_skill->sp=script_getnum(st,5); - - //Use delay as initial offset to avoid skill/heal exploits - if (battle_config.pet_equip_required && pd->pet.equip == 0) - pd->s_skill->timer = INVALID_TIMER; - else - pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0); - - return 0; -} - -/*========================================== - * pet attack skills [Valaris] //Rewritten by [Skotlex] - *------------------------------------------*/ -/// petskillattack <skill id>,<level>,<rate>,<bonusrate> -/// petskillattack "<skill name>",<level>,<rate>,<bonusrate> -BUILDIN_FUNC(petskillattack) -{ - struct pet_data *pd; - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->a_skill == NULL) - pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack)); - - pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - pd->a_skill->lv=script_getnum(st,3); - pd->a_skill->div_ = 0; - pd->a_skill->rate=script_getnum(st,4); - pd->a_skill->bonusrate=script_getnum(st,5); - - return 0; -} - -/*========================================== - * pet attack skills [Valaris] - *------------------------------------------*/ -/// petskillattack2 <skill id>,<level>,<div>,<rate>,<bonusrate> -/// petskillattack2 "<skill name>",<level>,<div>,<rate>,<bonusrate> -BUILDIN_FUNC(petskillattack2) -{ - struct pet_data *pd; - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->a_skill == NULL) - pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack)); - - pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - pd->a_skill->lv=script_getnum(st,3); - pd->a_skill->div_ = script_getnum(st,4); - pd->a_skill->rate=script_getnum(st,5); - pd->a_skill->bonusrate=script_getnum(st,6); - - return 0; -} - -/*========================================== - * pet support skills [Skotlex] - *------------------------------------------*/ -/// petskillsupport <skill id>,<level>,<delay>,<hp>,<sp> -/// petskillsupport "<skill name>",<level>,<delay>,<hp>,<sp> -BUILDIN_FUNC(petskillsupport) -{ - struct pet_data *pd; - TBL_PC *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->s_skill) - { //Clear previous skill - if (pd->s_skill->timer != INVALID_TIMER) - { - if (pd->s_skill->id) - delete_timer(pd->s_skill->timer, pet_skill_support_timer); - else - delete_timer(pd->s_skill->timer, pet_heal_timer); - } - } else //init memory - pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support)); - - pd->s_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - pd->s_skill->lv=script_getnum(st,3); - pd->s_skill->delay=script_getnum(st,4); - pd->s_skill->hp=script_getnum(st,5); - pd->s_skill->sp=script_getnum(st,6); - - //Use delay as initial offset to avoid skill/heal exploits - if (battle_config.pet_equip_required && pd->pet.equip == 0) - pd->s_skill->timer = INVALID_TIMER; - else - pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0); - - return 0; -} - -/*========================================== - * Scripted skill effects [Celest] - *------------------------------------------*/ -/// skilleffect <skill id>,<level> -/// skilleffect "<skill name>",<level> -BUILDIN_FUNC(skilleffect) -{ - TBL_PC *sd; - - uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - uint16 skill_lv=script_getnum(st,3); - sd=script_rid2sd(st); - - clif_skill_nodamage(&sd->bl,&sd->bl,skill_id,skill_lv,1); - - return 0; -} - -/*========================================== - * NPC skill effects [Valaris] - *------------------------------------------*/ -/// npcskilleffect <skill id>,<level>,<x>,<y> -/// npcskilleffect "<skill name>",<level>,<x>,<y> -BUILDIN_FUNC(npcskilleffect) -{ - struct block_list *bl= map_id2bl(st->oid); - - uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) ); - uint16 skill_lv=script_getnum(st,3); - int x=script_getnum(st,4); - int y=script_getnum(st,5); - - if (bl) - clif_skill_poseffect(bl,skill_id,skill_lv,x,y,gettick()); - - return 0; -} - -/*========================================== - * Special effects [Valaris] - *------------------------------------------*/ -BUILDIN_FUNC(specialeffect) -{ - struct block_list *bl=map_id2bl(st->oid); - int type = script_getnum(st,2); - enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA; - - if(bl==NULL) - return 0; - - if( script_hasdata(st,4) ) - { - TBL_NPC *nd = npc_name2id(script_getstr(st,4)); - if(nd) - clif_specialeffect(&nd->bl, type, target); - } - else - { - if (target == SELF) { - TBL_PC *sd=script_rid2sd(st); - if (sd) - clif_specialeffect_single(bl,type,sd->fd); - } else { - clif_specialeffect(bl, type, target); - } - } - - return 0; -} - -BUILDIN_FUNC(specialeffect2) -{ - TBL_PC *sd=script_rid2sd(st); - int type = script_getnum(st,2); - enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA; - - if( script_hasdata(st,4) ) - sd = map_nick2sd(script_getstr(st,4)); - - if (sd) - clif_specialeffect(&sd->bl, type, target); - - return 0; -} - -/*========================================== - * Nude [Valaris] - *------------------------------------------*/ -BUILDIN_FUNC(nude) -{ - TBL_PC *sd = script_rid2sd(st); - int i, calcflag = 0; - - if( sd == NULL ) - return 0; - - for( i = 0 ; i < EQI_MAX; i++ ) { - if( sd->equip_index[ i ] >= 0 ) { - if( !calcflag ) - calcflag = 1; - pc_unequipitem( sd , sd->equip_index[ i ] , 2); - } - } - - if( calcflag ) - status_calc_pc(sd,0); - - return 0; -} - -/*========================================== - * gmcommand [MouseJstr] - *------------------------------------------*/ -BUILDIN_FUNC(atcommand) -{ - TBL_PC dummy_sd; - TBL_PC* sd; - int fd; - const char* cmd; - - cmd = script_getstr(st,2); - - if (st->rid) { - sd = script_rid2sd(st); - fd = sd->fd; - } else { //Use a dummy character. - sd = &dummy_sd; - fd = 0; - - memset(&dummy_sd, 0, sizeof(TBL_PC)); - if (st->oid) - { - struct block_list* bl = map_id2bl(st->oid); - memcpy(&dummy_sd.bl, bl, sizeof(struct block_list)); - if (bl->type == BL_NPC) - safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH); - } - } - - if (!is_atcommand(fd, sd, cmd, 0)) { - ShowWarning("script: buildin_atcommand: failed to execute command '%s'\n", cmd); - script_reportsrc(st); - return 1; - } - - return 0; -} - -/*========================================== - * Displays a message for the player only (like system messages like "you got an apple" ) - *------------------------------------------*/ -BUILDIN_FUNC(dispbottom) -{ - TBL_PC *sd=script_rid2sd(st); - const char *message; - message=script_getstr(st,2); - if(sd) - clif_disp_onlyself(sd,message,(int)strlen(message)); - return 0; -} - -/*========================================== - * All The Players Full Recovery - * (HP/SP full restore and resurrect if need) - *------------------------------------------*/ -BUILDIN_FUNC(recovery) -{ - TBL_PC* 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(pc_isdead(sd)) - status_revive(&sd->bl, 100, 100); - else - status_percent_heal(&sd->bl, 100, 100); - clif_displaymessage(sd->fd,msg_txt(680)); - } - mapit_free(iter); - return 0; -} -/*========================================== - * Get your pet info: getpetinfo(n) - * n -> 0:pet_id 1:pet_class 2:pet_name - * 3:friendly 4:hungry, 5: rename flag. - *------------------------------------------*/ -BUILDIN_FUNC(getpetinfo) -{ - TBL_PC *sd=script_rid2sd(st); - TBL_PET *pd; - int type=script_getnum(st,2); - - if(!sd || !sd->pd) { - if (type == 2) - script_pushconststr(st,"null"); - else - script_pushint(st,0); - return 0; - } - pd = sd->pd; - switch(type){ - case 0: script_pushint(st,pd->pet.pet_id); break; - case 1: script_pushint(st,pd->pet.class_); break; - case 2: script_pushstrcopy(st,pd->pet.name); break; - case 3: script_pushint(st,pd->pet.intimate); break; - case 4: script_pushint(st,pd->pet.hungry); break; - case 5: script_pushint(st,pd->pet.rename_flag); break; - default: - script_pushint(st,0); - break; - } - return 0; -} - -/*========================================== - * Get your homunculus info: gethominfo(n) - * n -> 0:hom_id 1:class 2:name - * 3:friendly 4:hungry, 5: rename flag. - * 6: level - *------------------------------------------*/ -BUILDIN_FUNC(gethominfo) -{ - TBL_PC *sd=script_rid2sd(st); - TBL_HOM *hd; - int type=script_getnum(st,2); - - hd = sd?sd->hd:NULL; - if(!merc_is_hom_active(hd)) - { - if (type == 2) - script_pushconststr(st,"null"); - else - script_pushint(st,0); - return 0; - } - - switch(type){ - case 0: script_pushint(st,hd->homunculus.hom_id); break; - case 1: script_pushint(st,hd->homunculus.class_); break; - case 2: script_pushstrcopy(st,hd->homunculus.name); break; - case 3: script_pushint(st,hd->homunculus.intimacy); break; - case 4: script_pushint(st,hd->homunculus.hunger); break; - case 5: script_pushint(st,hd->homunculus.rename_flag); break; - case 6: script_pushint(st,hd->homunculus.level); break; - default: - script_pushint(st,0); - break; - } - return 0; -} - -/// Retrieves information about character's mercenary -/// getmercinfo <type>[,<char id>]; -BUILDIN_FUNC(getmercinfo) -{ - int type, char_id; - struct map_session_data* sd; - struct mercenary_data* md; - - type = script_getnum(st,2); - - if( script_hasdata(st,3) ) - { - char_id = script_getnum(st,3); - - if( ( sd = map_charid2sd(char_id) ) == NULL ) - { - ShowError("buildin_getmercinfo: No such character (char_id=%d).\n", char_id); - script_pushnil(st); - return 1; - } - } - else - { - if( ( sd = script_rid2sd(st) ) == NULL ) - { - script_pushnil(st); - return 0; - } - } - - md = ( sd->status.mer_id && sd->md ) ? sd->md : NULL; - - switch( type ) - { - case 0: script_pushint(st,md ? md->mercenary.mercenary_id : 0); break; - case 1: script_pushint(st,md ? md->mercenary.class_ : 0); break; - case 2: - if( md ) - script_pushstrcopy(st,md->db->name); - else - script_pushconststr(st,""); - break; - case 3: script_pushint(st,md ? mercenary_get_faith(md) : 0); break; - case 4: script_pushint(st,md ? mercenary_get_calls(md) : 0); break; - case 5: script_pushint(st,md ? md->mercenary.kill_count : 0); break; - case 6: script_pushint(st,md ? mercenary_get_lifetime(md) : 0); break; - case 7: script_pushint(st,md ? md->db->lv : 0); break; - default: - ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id); - script_pushnil(st); - return 1; - } - - return 0; -} - -/*========================================== - * Shows wether your inventory(and equips) contain - selected card or not. - checkequipedcard(4001); - *------------------------------------------*/ -BUILDIN_FUNC(checkequipedcard) -{ - TBL_PC *sd=script_rid2sd(st); - - if(sd){ - int n,i,c=0; - c=script_getnum(st,2); - - for(i=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount && sd->inventory_data[i]){ - if (itemdb_isspecial(sd->status.inventory[i].card[0])) - continue; - for(n=0;n<sd->inventory_data[i]->slot;n++){ - if(sd->status.inventory[i].card[n]==c){ - script_pushint(st,1); - return 0; - } - } - } - } - } - script_pushint(st,0); - return 0; -} - -BUILDIN_FUNC(jump_zero) -{ - int sel; - sel=script_getnum(st,2); - if(!sel) { - int pos; - if( !data_islabel(script_getdata(st,3)) ){ - ShowError("script: jump_zero: not label !\n"); - st->state=END; - return 1; - } - - pos=script_getnum(st,3); - st->pos=pos; - st->state=GOTO; - } - return 0; -} - -/*========================================== - * movenpc [MouseJstr] - *------------------------------------------*/ -BUILDIN_FUNC(movenpc) -{ - TBL_NPC *nd = NULL; - const char *npc; - int x,y; - - npc = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - - if ((nd = npc_name2id(npc)) == NULL) - return -1; - - if (script_hasdata(st,5)) - nd->ud.dir = script_getnum(st,5) % 8; - npc_movenpc(nd, x, y); - return 0; -} - -/*========================================== - * message [MouseJstr] - *------------------------------------------*/ -BUILDIN_FUNC(message) -{ - const char *msg,*player; - TBL_PC *pl_sd = NULL; - - player = script_getstr(st,2); - msg = script_getstr(st,3); - - if((pl_sd=map_nick2sd((char *) player)) == NULL) - return 0; - clif_displaymessage(pl_sd->fd, msg); - - return 0; -} - -/*========================================== - * npctalk (sends message to surrounding area) - *------------------------------------------*/ -BUILDIN_FUNC(npctalk) -{ - const char* str; - char name[NAME_LENGTH], message[256]; - - struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid); - str = script_getstr(st,2); - - if(nd) - { - safestrncpy(name, nd->name, sizeof(name)); - strtok(name, "#"); // discard extra name identifier if present - safesnprintf(message, sizeof(message), "%s : %s", name, str); - clif_message(&nd->bl, message); - } - - return 0; -} - -// change npc walkspeed [Valaris] -BUILDIN_FUNC(npcspeed) -{ - struct npc_data* nd; - int speed; - - speed = script_getnum(st,2); - nd =(struct npc_data *)map_id2bl(st->oid); - - if( nd ) - { - nd->speed = speed; - nd->ud.state.speed_changed = 1; - } - - return 0; -} -// make an npc walk to a position [Valaris] -BUILDIN_FUNC(npcwalkto) -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - int x=0,y=0; - - x=script_getnum(st,2); - y=script_getnum(st,3); - - if(nd) { - unit_walktoxy(&nd->bl,x,y,0); - } - - return 0; -} -// stop an npc's movement [Valaris] -BUILDIN_FUNC(npcstop) -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - - if(nd) { - unit_stop_walking(&nd->bl,1|4); - } - - return 0; -} - - -/*========================================== - * getlook char info. getlook(arg) - *------------------------------------------*/ -BUILDIN_FUNC(getlook) -{ - int type,val; - TBL_PC *sd; - sd=script_rid2sd(st); - - type=script_getnum(st,2); - val=-1; - switch(type) { - case LOOK_HAIR: val=sd->status.hair; break; //1 - case LOOK_WEAPON: val=sd->status.weapon; break; //2 - case LOOK_HEAD_BOTTOM: val=sd->status.head_bottom; break; //3 - case LOOK_HEAD_TOP: val=sd->status.head_top; break; //4 - case LOOK_HEAD_MID: val=sd->status.head_mid; break; //5 - case LOOK_HAIR_COLOR: val=sd->status.hair_color; break; //6 - case LOOK_CLOTHES_COLOR: val=sd->status.clothes_color; break; //7 - case LOOK_SHIELD: val=sd->status.shield; break; //8 - case LOOK_SHOES: break; //9 - } - - script_pushint(st,val); - return 0; -} - -/*========================================== - * get char save point. argument: 0- map name, 1- x, 2- y - *------------------------------------------*/ -BUILDIN_FUNC(getsavepoint) -{ - TBL_PC* sd; - int type; - - sd = script_rid2sd(st); - if (sd == NULL) { - script_pushint(st,0); - return 0; - } - - type = script_getnum(st,2); - - switch(type) { - case 0: script_pushstrcopy(st,mapindex_id2name(sd->status.save_point.map)); break; - case 1: script_pushint(st,sd->status.save_point.x); break; - case 2: script_pushint(st,sd->status.save_point.y); break; - default: - script_pushint(st,0); - break; - } - return 0; -} - -/*========================================== - * Get position for char/npc/pet/mob objects. Added by Lorky - * - * int getMapXY(MapName$,MapX,MapY,type,[CharName$]); - * where type: - * MapName$ - String variable for output map name - * MapX - Integer variable for output coord X - * MapY - Integer variable for output coord Y - * type - type of object - * 0 - Character coord - * 1 - NPC coord - * 2 - Pet coord - * 3 - Mob coord (not released) - * 4 - Homun coord - * 5 - Mercenary coord - * 6 - Elemental coord - * CharName$ - Name object. If miss or "this" the current object - * - * Return: - * 0 - success - * -1 - some error, MapName$,MapX,MapY contains unknown value. - *------------------------------------------*/ -BUILDIN_FUNC(getmapxy) -{ - struct block_list *bl = NULL; - TBL_PC *sd=NULL; - - int num; - const char *name; - char prefix; - - int x,y,type; - char mapname[MAP_NAME_LENGTH]; - - if( !data_isreference(script_getdata(st,2)) ){ - ShowWarning("script: buildin_getmapxy: not mapname variable\n"); - script_pushint(st,-1); - return 1; - } - if( !data_isreference(script_getdata(st,3)) ){ - ShowWarning("script: buildin_getmapxy: not mapx variable\n"); - script_pushint(st,-1); - return 1; - } - if( !data_isreference(script_getdata(st,4)) ){ - ShowWarning("script: buildin_getmapxy: not mapy variable\n"); - script_pushint(st,-1); - return 1; - } - - // Possible needly check function parameters on C_STR,C_INT,C_INT - type=script_getnum(st,5); - - switch (type){ - case 0: //Get Character Position - if( script_hasdata(st,6) ) - sd=map_nick2sd(script_getstr(st,6)); - else - sd=script_rid2sd(st); - - if (sd) - bl = &sd->bl; - break; - case 1: //Get NPC Position - if( script_hasdata(st,6) ) - { - struct npc_data *nd; - nd=npc_name2id(script_getstr(st,6)); - if (nd) - bl = &nd->bl; - } else //In case the origin is not an npc? - bl=map_id2bl(st->oid); - break; - case 2: //Get Pet Position - if(script_hasdata(st,6)) - sd=map_nick2sd(script_getstr(st,6)); - else - sd=script_rid2sd(st); - - if (sd && sd->pd) - bl = &sd->pd->bl; - break; - case 3: //Get Mob Position - break; //Not supported? - case 4: //Get Homun Position - if(script_hasdata(st,6)) - sd=map_nick2sd(script_getstr(st,6)); - else - sd=script_rid2sd(st); - - if (sd && sd->hd) - bl = &sd->hd->bl; - break; - case 5: //Get Mercenary Position - if(script_hasdata(st,6)) - sd=map_nick2sd(script_getstr(st,6)); - else - sd=script_rid2sd(st); - - if (sd && sd->md) - bl = &sd->md->bl; - break; - case 6: //Get Elemental Position - if(script_hasdata(st,6)) - sd=map_nick2sd(script_getstr(st,6)); - else - sd=script_rid2sd(st); - - if (sd && sd->ed) - bl = &sd->ed->bl; - break; - default: - ShowWarning("script: buildin_getmapxy: Invalid type %d\n", type); - script_pushint(st,-1); - return 1; - } - if (!bl) { //No object found. - script_pushint(st,-1); - return 0; - } - - x= bl->x; - y= bl->y; - safestrncpy(mapname, map[bl->m].name, MAP_NAME_LENGTH); - - //Set MapName$ - num=st->stack->stack_data[st->start+2].u.num; - name=get_str(num&0x00ffffff); - prefix=*name; - - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - else - sd=NULL; - set_reg(st,sd,num,name,(void*)mapname,script_getref(st,2)); - - //Set MapX - num=st->stack->stack_data[st->start+3].u.num; - name=get_str(num&0x00ffffff); - prefix=*name; - - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - else - sd=NULL; - set_reg(st,sd,num,name,(void*)__64BPRTSIZE(x),script_getref(st,3)); - - //Set MapY - num=st->stack->stack_data[st->start+4].u.num; - name=get_str(num&0x00ffffff); - prefix=*name; - - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - else - sd=NULL; - set_reg(st,sd,num,name,(void*)__64BPRTSIZE(y),script_getref(st,4)); - - //Return Success value - script_pushint(st,0); - return 0; -} - -/*========================================== - * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus] - *------------------------------------------*/ -BUILDIN_FUNC(logmes) -{ - const char *str; - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 1; - - str = script_getstr(st,2); - log_npc(sd,str); - return 0; -} - -BUILDIN_FUNC(summon) -{ - int _class, timeout=0; - const char *str,*event=""; - TBL_PC *sd; - struct mob_data *md; - int tick = gettick(); - - sd=script_rid2sd(st); - if (!sd) return 0; - - str =script_getstr(st,2); - _class=script_getnum(st,3); - if( script_hasdata(st,4) ) - timeout=script_getnum(st,4); - if( script_hasdata(st,5) ){ - event=script_getstr(st,5); - check_event(st, event); - } - - clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick); - - md = mob_once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, str, _class, event, SZ_SMALL, AI_NONE); - if (md) { - md->master_id=sd->bl.id; - md->special_state.ai = AI_ATTACK; - if( md->deletetimer != INVALID_TIMER ) - delete_timer(md->deletetimer, mob_timer_delete); - md->deletetimer = add_timer(tick+(timeout>0?timeout*1000:60000),mob_timer_delete,md->bl.id,0); - mob_spawn (md); //Now it is ready for spawning. - clif_specialeffect(&md->bl,344,AREA); - sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); - } - return 0; -} - -/*========================================== - * Checks whether it is daytime/nighttime - *------------------------------------------*/ -BUILDIN_FUNC(isnight) -{ - script_pushint(st,(night_flag == 1)); - return 0; -} - -BUILDIN_FUNC(isday) -{ - script_pushint(st,(night_flag == 0)); - return 0; -} - -/*================================================ - * Check how many items/cards in the list are - * equipped - used for 2/15's cards patch [celest] - *------------------------------------------------*/ -BUILDIN_FUNC(isequippedcnt) -{ - TBL_PC *sd; - int i, j, k, id = 1; - int ret = 0; - - sd = script_rid2sd(st); - if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing... - script_pushint(st,0); - return 0; - } - - for (i=0; id!=0; i++) { - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - - for (j=0; j<EQI_MAX; j++) { - int index; - index = sd->equip_index[j]; - if(index < 0) continue; - if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue; - if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue; - if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue; - - if(!sd->inventory_data[index]) - continue; - - if (itemdb_type(id) != IT_CARD) { //No card. Count amount in inventory. - if (sd->inventory_data[index]->nameid == id) - ret+= sd->status.inventory[index].amount; - } else { //Count cards. - if (itemdb_isspecial(sd->status.inventory[index].card[0])) - continue; //No cards - for(k=0; k<sd->inventory_data[index]->slot; k++) { - if (sd->status.inventory[index].card[k] == id) - ret++; //[Lupus] - } - } - } - } - - script_pushint(st,ret); - return 0; -} - -/*================================================ - * Check whether another card has been - * equipped - used for 2/15's cards patch [celest] - * -- Items checked cannot be reused in another - * card set to prevent exploits - *------------------------------------------------*/ -BUILDIN_FUNC(isequipped) -{ - TBL_PC *sd; - int i, j, k, id = 1; - int index, flag; - int ret = -1; - //Original hash to reverse it when full check fails. - unsigned int setitem_hash = 0, setitem_hash2 = 0; - - sd = script_rid2sd(st); - - if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing... - script_pushint(st,0); - return 0; - } - - setitem_hash = sd->bonus.setitem_hash; - setitem_hash2 = sd->bonus.setitem_hash2; - for (i=0; id!=0; i++) { - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - flag = 0; - for (j=0; j<EQI_MAX; j++) { - index = sd->equip_index[j]; - if(index < 0) continue; - if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue; - if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue; - if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue; - - if(!sd->inventory_data[index]) - continue; - - if (itemdb_type(id) != IT_CARD) { - if (sd->inventory_data[index]->nameid != id) - continue; - flag = 1; - break; - } else { //Cards - if (sd->inventory_data[index]->slot == 0 || - itemdb_isspecial(sd->status.inventory[index].card[0])) - continue; - - for (k = 0; k < sd->inventory_data[index]->slot; k++) - { //New hash system which should support up to 4 slots on any equipment. [Skotlex] - unsigned int hash = 0; - if (sd->status.inventory[index].card[k] != id) - continue; - - hash = 1<<((j<5?j:j-5)*4 + k); - // check if card is already used by another set - if ( ( j < 5 ? sd->bonus.setitem_hash : sd->bonus.setitem_hash2 ) & hash) - continue; - - // We have found a match - flag = 1; - // Set hash so this card cannot be used by another - if (j<5) - sd->bonus.setitem_hash |= hash; - else - sd->bonus.setitem_hash2 |= hash; - break; - } - } - if (flag) break; //Card found - } - if (ret == -1) - ret = flag; - else - ret &= flag; - if (!ret) break; - } - if (!ret) {//When check fails, restore original hash values. [Skotlex] - sd->bonus.setitem_hash = setitem_hash; - sd->bonus.setitem_hash2 = setitem_hash2; - } - script_pushint(st,ret); - return 0; -} - -/*================================================ - * Check how many given inserted cards in the CURRENT - * weapon - used for 2/15's cards patch [Lupus] - *------------------------------------------------*/ -BUILDIN_FUNC(cardscnt) -{ - TBL_PC *sd; - int i, k, id = 1; - int ret = 0; - int index; - - sd = script_rid2sd(st); - - for (i=0; id!=0; i++) { - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - - index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus] - if(index < 0) continue; - - if(!sd->inventory_data[index]) - continue; - - if(itemdb_type(id) != IT_CARD) { - if (sd->inventory_data[index]->nameid == id) - ret+= sd->status.inventory[index].amount; - } else { - if (itemdb_isspecial(sd->status.inventory[index].card[0])) - continue; - for(k=0; k<sd->inventory_data[index]->slot; k++) { - if (sd->status.inventory[index].card[k] == id) - ret++; - } - } - } - script_pushint(st,ret); -// script_pushint(st,current_equip_item_index); - return 0; -} - -/*======================================================= - * Returns the refined number of the current item, or an - * item with inventory index specified - *-------------------------------------------------------*/ -BUILDIN_FUNC(getrefine) -{ - TBL_PC *sd; - if ((sd = script_rid2sd(st))!= NULL) - script_pushint(st,sd->status.inventory[current_equip_item_index].refine); - else - script_pushint(st,0); - return 0; -} - -/*======================================================= - * Day/Night controls - *-------------------------------------------------------*/ -BUILDIN_FUNC(night) -{ - if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1); - return 0; -} -BUILDIN_FUNC(day) -{ - if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1); - return 0; -} - -//======================================================= -// Unequip [Spectre] -//------------------------------------------------------- -BUILDIN_FUNC(unequip) -{ - int i; - size_t num; - TBL_PC *sd; - - num = script_getnum(st,2); - sd = script_rid2sd(st); - if( sd != NULL && num >= 1 && num <= ARRAYLENGTH(equip) ) - { - i = pc_checkequip(sd,equip[num-1]); - if (i >= 0) - pc_unequipitem(sd,i,1|2); - } - return 0; -} - -BUILDIN_FUNC(equip) -{ - int nameid=0,i; - TBL_PC *sd; - struct item_data *item_data; - - sd = script_rid2sd(st); - - nameid=script_getnum(st,2); - if((item_data = itemdb_exists(nameid)) == NULL) - { - ShowError("wrong item ID : equipitem(%i)\n",nameid); - return 1; - } - ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid ); - if( i < MAX_INVENTORY ) - pc_equipitem(sd,i,item_data->equip); - - return 0; -} - -BUILDIN_FUNC(autoequip) -{ - int nameid, flag; - struct item_data *item_data; - nameid=script_getnum(st,2); - flag=script_getnum(st,3); - - if( ( item_data = itemdb_exists(nameid) ) == NULL ) - { - ShowError("buildin_autoequip: Invalid item '%d'.\n", nameid); - return 1; - } - - if( !itemdb_isequip2(item_data) ) - { - ShowError("buildin_autoequip: Item '%d' cannot be equipped.\n", nameid); - return 1; - } - - item_data->flag.autoequip = flag>0?1:0; - return 0; -} - -BUILDIN_FUNC(setbattleflag) -{ - const char *flag, *value; - - flag = script_getstr(st,2); - value = script_getstr(st,3); // HACK: Retrieve number as string (auto-converted) for battle_set_value - - if (battle_set_value(flag, value) == 0) - ShowWarning("buildin_setbattleflag: unknown battle_config flag '%s'\n",flag); - else - ShowInfo("buildin_setbattleflag: battle_config flag '%s' is now set to '%s'.\n",flag,value); - - return 0; -} - -BUILDIN_FUNC(getbattleflag) -{ - const char *flag; - flag = script_getstr(st,2); - script_pushint(st,battle_get_value(flag)); - return 0; -} - -//======================================================= -// strlen [Valaris] -//------------------------------------------------------- -BUILDIN_FUNC(getstrlen) -{ - - const char *str = script_getstr(st,2); - int len = (str) ? (int)strlen(str) : 0; - - script_pushint(st,len); - return 0; -} - -//======================================================= -// isalpha [Valaris] -//------------------------------------------------------- -BUILDIN_FUNC(charisalpha) -{ - const char *str=script_getstr(st,2); - int pos=script_getnum(st,3); - - int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISALPHA( str[pos] ) != 0 : 0; - - script_pushint(st,val); - 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); - - if( pos >= 0 && (unsigned int)pos < strlen(str) ) { - char output[2]; - output[0] = str[pos]; - output[1] = '\0'; - script_pushstrcopy(st, output); - } else - script_pushconststr(st, ""); - 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 = aStrdup(str); - - if(index >= 0 && index < strlen(output)) - output[index] = *c; - - 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*)aMalloc(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 - output = aStrdup(str); - script_pushstr(st, output); - return 0; - } - - output = (char*)aMalloc(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 = aStrdup(str); - char *cursor = output; - - while (*cursor != '\0') { - *cursor = TOUPPER(*cursor); - cursor++; - } - - script_pushstr(st, output); - return 0; -} - -//======================================================= -// strtolower <str> -//------------------------------------------------------- -BUILDIN_FUNC(strtolower) -{ - const char *str = script_getstr(st,2); - char *output = aStrdup(str); - char *cursor = output; - - while (*cursor != '\0') { - *cursor = TOLOWER(*cursor); - cursor++; - } - - 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*)aMalloc(len + 1); - memcpy(output, &str[start], len); - } else - output = (char*)aMalloc(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*)aMalloc(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 < SCRIPT_MAX_ARRAYSIZE-1) { //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*)aMalloc(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*)aMalloc(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){ - memcpy(buf,format,len+1); - 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 *)__64BPRTSIZE(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) = '*'; - } - - 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 (needle[0] == '\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_isstring(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)) { - count = script_getnum(st, 6); - if(count == 0) { - 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_isstring(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. -/// -/// setnpcdisplay("<npc name>", "<new display name>", <new class id>, <new size>) -> <int> -/// setnpcdisplay("<npc name>", "<new display name>", <new class id>) -> <int> -/// setnpcdisplay("<npc name>", "<new display name>") -> <int> -/// setnpcdisplay("<npc name>", <new class id>) -> <int> -BUILDIN_FUNC(setnpcdisplay) -{ - const char* name; - const char* newname = NULL; - int class_ = -1, size = -1; - struct script_data* data; - struct npc_data* nd; - - name = script_getstr(st,2); - data = script_getdata(st,3); - - if( script_hasdata(st,4) ) - class_ = script_getnum(st,4); - if( script_hasdata(st,5) ) - size = script_getnum(st,5); - - get_val(st, data); - if( data_isstring(data) ) - newname = conv_str(st,data); - else if( data_isint(data) ) - class_ = conv_num(st,data); - else - { - ShowError("script:setnpcdisplay: expected a string or number\n"); - script_reportdata(data); - return 1; - } - - nd = npc_name2id(name); - if( nd == NULL ) - {// not found - script_pushint(st,1); - return 0; - } - - // update npc - if( newname ) - npc_setdisplayname(nd, newname); - - if( size != -1 && size != (int)nd->size ) - nd->size = size; - else - size = -1; - - if( class_ != -1 && nd->class_ != class_ ) - npc_setclass(nd, class_); - else if( size != -1 ) - { // Required to update the visual size - clif_clearunit_area(&nd->bl, CLR_OUTSIGHT); - clif_spawn(&nd->bl); - } - - script_pushint(st,0); - return 0; -} - -BUILDIN_FUNC(atoi) -{ - const char *value; - value = script_getstr(st,2); - script_pushint(st,atoi(value)); - return 0; -} - -// case-insensitive substring search [lordalfa] -BUILDIN_FUNC(compare) -{ - const char *message; - const char *cmpstring; - message = script_getstr(st,2); - cmpstring = script_getstr(st,3); - script_pushint(st,(stristr(message,cmpstring) != NULL)); - return 0; -} - -// [zBuffer] List of mathematics commands ---> -BUILDIN_FUNC(sqrt) -{ - double i, a; - i = script_getnum(st,2); - a = sqrt(i); - script_pushint(st,(int)a); - return 0; -} - -BUILDIN_FUNC(pow) -{ - double i, a, b; - a = script_getnum(st,2); - b = script_getnum(st,3); - i = pow(a,b); - script_pushint(st,(int)i); - return 0; -} - -BUILDIN_FUNC(distance) -{ - int x0, y0, x1, y1; - - x0 = script_getnum(st,2); - y0 = script_getnum(st,3); - x1 = script_getnum(st,4); - y1 = script_getnum(st,5); - - script_pushint(st,distance_xy(x0,y0,x1,y1)); - return 0; -} - -// <--- [zBuffer] List of mathematics commands - -BUILDIN_FUNC(md5) -{ - const char *tmpstr; - char *md5str; - - tmpstr = script_getstr(st,2); - md5str = (char *)aMalloc((32+1)*sizeof(char)); - MD5_String(tmpstr, md5str); - script_pushstr(st, md5str); - return 0; -} - -// [zBuffer] List of dynamic var commands ---> - -BUILDIN_FUNC(setd) -{ - TBL_PC *sd=NULL; - char varname[100]; - const char *buffer; - int elem; - buffer = script_getstr(st, 2); - - if(sscanf(buffer, "%99[^[][%d]", varname, &elem) < 2) - elem = 0; - - if( not_server_variable(*varname) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - { - ShowError("script:setd: no player attached for player variable '%s'\n", buffer); - return 0; - } - } - - if( is_string_variable(varname) ) { - setd_sub(st, sd, varname, elem, (void *)script_getstr(st, 3), NULL); - } else { - setd_sub(st, sd, varname, elem, (void *)__64BPRTSIZE(script_getnum(st, 3)), NULL); - } - - return 0; -} - -int buildin_query_sql_sub(struct script_state* st, Sql* handle) -{ - int i, j; - TBL_PC* sd = NULL; - const char* query; - struct script_data* data; - const char* name; - int max_rows = SCRIPT_MAX_ARRAYSIZE; // maximum number of rows - int num_vars; - int num_cols; - - // check target variables - for( i = 3; script_hasdata(st,i); ++i ) { - data = script_getdata(st, i); - if( data_isreference(data) ) { // it's a variable - name = reference_getname(data); - if( not_server_variable(*name) && sd == NULL ) { // requires a player - sd = script_rid2sd(st); - if( sd == NULL ) { // no player attached - script_reportdata(data); - st->state = END; - return 1; - } - } - if( not_array_variable(*name) ) - max_rows = 1;// not an array, limit to one row - } else { - ShowError("script:query_sql: not a variable\n"); - script_reportdata(data); - st->state = END; - return 1; - } - } - num_vars = i - 3; - - // Execute the query - query = script_getstr(st,2); - - if( SQL_ERROR == Sql_QueryStr(handle, query) ) { - Sql_ShowDebug(handle); - script_pushint(st, 0); - return 1; - } - - if( Sql_NumRows(handle) == 0 ) { // No data received - Sql_FreeResult(handle); - script_pushint(st, 0); - return 0; - } - - // Count the number of columns to store - num_cols = Sql_NumColumns(handle); - if( num_vars < num_cols ) { - ShowWarning("script:query_sql: Too many columns, discarding last %u columns.\n", (unsigned int)(num_cols-num_vars)); - script_reportsrc(st); - } else if( num_vars > num_cols ) { - ShowWarning("script:query_sql: Too many variables (%u extra).\n", (unsigned int)(num_vars-num_cols)); - script_reportsrc(st); - } - - // Store data - for( i = 0; i < max_rows && SQL_SUCCESS == Sql_NextRow(handle); ++i ) { - for( j = 0; j < num_vars; ++j ) { - char* str = NULL; - - if( j < num_cols ) - Sql_GetData(handle, j, &str, NULL); - - data = script_getdata(st, j+3); - name = reference_getname(data); - if( is_string_variable(name) ) - setd_sub(st, sd, name, i, (void *)(str?str:""), reference_getref(data)); - else - setd_sub(st, sd, name, i, (void *)__64BPRTSIZE((str?atoi(str):0)), reference_getref(data)); - } - } - if( i == max_rows && max_rows < Sql_NumRows(handle) ) { - ShowWarning("script:query_sql: Only %d/%u rows have been stored.\n", max_rows, (unsigned int)Sql_NumRows(handle)); - script_reportsrc(st); - } - - // Free data - Sql_FreeResult(handle); - script_pushint(st, i); - - return 0; -} - -BUILDIN_FUNC(query_sql) { -#ifdef BETA_THREAD_TEST - if( st->state != RERUNLINE ) { - queryThread_add(st,false); - - st->state = RERUNLINE;/* will continue when the query is finished running. */ - } else - st->state = RUN; - - return 0; -#else - return buildin_query_sql_sub(st, mmysql_handle); -#endif -} - -BUILDIN_FUNC(query_logsql) { - if( !log_config.sql_logs ) {// logmysql_handle == NULL - ShowWarning("buildin_query_logsql: SQL logs are disabled, query '%s' will not be executed.\n", script_getstr(st,2)); - script_pushint(st,-1); - return 1; - } -#ifdef BETA_THREAD_TEST - if( st->state != RERUNLINE ) { - queryThread_add(st,true); - - st->state = RERUNLINE;/* will continue when the query is finished running. */ - } else - st->state = RUN; - - return 0; -#else - return buildin_query_sql_sub(st, logmysql_handle); -#endif -} - -//Allows escaping of a given string. -BUILDIN_FUNC(escape_sql) -{ - const char *str; - char *esc_str; - size_t len; - - str = script_getstr(st,2); - len = strlen(str); - esc_str = (char*)aMalloc(len*2+1); - Sql_EscapeStringLen(mmysql_handle, esc_str, str, len); - script_pushstr(st, esc_str); - return 0; -} - -BUILDIN_FUNC(getd) -{ - char varname[100]; - const char *buffer; - int elem; - - buffer = script_getstr(st, 2); - - if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2) - elem = 0; - - // Push the 'pointer' so it's more flexible [Lance] - push_val(st->stack, C_NAME, reference_uid(add_str(varname), elem)); - - return 0; -} - -// <--- [zBuffer] List of dynamic var commands -// Pet stat [Lance] -BUILDIN_FUNC(petstat) -{ - TBL_PC *sd = NULL; - struct pet_data *pd; - int flag = script_getnum(st,2); - sd = script_rid2sd(st); - if(!sd || !sd->status.pet_id || !sd->pd){ - if(flag == 2) - script_pushconststr(st, ""); - else - script_pushint(st,0); - return 0; - } - pd = sd->pd; - switch(flag){ - case 1: script_pushint(st,(int)pd->pet.class_); break; - case 2: script_pushstrcopy(st, pd->pet.name); break; - case 3: script_pushint(st,(int)pd->pet.level); break; - case 4: script_pushint(st,(int)pd->pet.hungry); break; - case 5: script_pushint(st,(int)pd->pet.intimate); break; - default: - script_pushint(st,0); - break; - } - return 0; -} - -BUILDIN_FUNC(callshop) -{ - TBL_PC *sd = NULL; - struct npc_data *nd; - const char *shopname; - int flag = 0; - sd = script_rid2sd(st); - if (!sd) { - script_pushint(st,0); - return 0; - } - shopname = script_getstr(st, 2); - if( script_hasdata(st,3) ) - flag = script_getnum(st,3); - nd = npc_name2id(shopname); - if( !nd || nd->bl.type != BL_NPC || (nd->subtype != SHOP && nd->subtype != CASHSHOP) ) - { - ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)\n", shopname); - script_pushint(st,0); - return 1; - } - - if( nd->subtype == SHOP ) - { - // flag the user as using a valid script call for opening the shop (for floating NPCs) - sd->state.callshop = 1; - - switch( flag ) - { - case 1: npc_buysellsel(sd,nd->bl.id,0); break; //Buy window - case 2: npc_buysellsel(sd,nd->bl.id,1); break; //Sell window - default: clif_npcbuysell(sd,nd->bl.id); break; //Show menu - } - } - else - clif_cashshop_show(sd, nd); - - sd->npc_shopid = nd->bl.id; - script_pushint(st,1); - return 0; -} - -BUILDIN_FUNC(npcshopitem) -{ - const char* npcname = script_getstr(st, 2); - struct npc_data* nd = npc_name2id(npcname); - int n, i; - int amount; - - if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) ) - { //Not found. - script_pushint(st,0); - return 0; - } - - // get the count of new entries - amount = (script_lastdata(st)-2)/2; - - // generate new shop item list - RECREATE(nd->u.shop.shop_item, struct npc_item_list, amount); - for( n = 0, i = 3; n < amount; n++, i+=2 ) - { - nd->u.shop.shop_item[n].nameid = script_getnum(st,i); - nd->u.shop.shop_item[n].value = script_getnum(st,i+1); - } - nd->u.shop.count = n; - - script_pushint(st,1); - return 0; -} - -BUILDIN_FUNC(npcshopadditem) -{ - const char* npcname = script_getstr(st,2); - struct npc_data* nd = npc_name2id(npcname); - int n, i; - int amount; - - if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) ) - { //Not found. - script_pushint(st,0); - return 0; - } - - // get the count of new entries - amount = (script_lastdata(st)-2)/2; - - // append new items to existing shop item list - RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+amount); - for( n = nd->u.shop.count, i = 3; n < nd->u.shop.count+amount; n++, i+=2 ) - { - nd->u.shop.shop_item[n].nameid = script_getnum(st,i); - nd->u.shop.shop_item[n].value = script_getnum(st,i+1); - } - nd->u.shop.count = n; - - script_pushint(st,1); - return 0; -} - -BUILDIN_FUNC(npcshopdelitem) -{ - const char* npcname = script_getstr(st,2); - struct npc_data* nd = npc_name2id(npcname); - unsigned int nameid; - int n, i; - int amount; - int size; - - if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) ) - { //Not found. - script_pushint(st,0); - return 0; - } - - amount = script_lastdata(st)-2; - size = nd->u.shop.count; - - // remove specified items from the shop item list - for( i = 3; i < 3 + amount; i++ ) - { - nameid = script_getnum(st,i); - - ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == nameid ); - if( n < size ) - { - memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n)); - size--; - } - } - - RECREATE(nd->u.shop.shop_item, struct npc_item_list, size); - nd->u.shop.count = size; - - script_pushint(st,1); - return 0; -} - -//Sets a script to attach to a shop npc. -BUILDIN_FUNC(npcshopattach) -{ - const char* npcname = script_getstr(st,2); - struct npc_data* nd = npc_name2id(npcname); - int flag = 1; - - if( script_hasdata(st,3) ) - flag = script_getnum(st,3); - - if( !nd || nd->subtype != SHOP ) - { //Not found. - script_pushint(st,0); - return 0; - } - - if (flag) - nd->master_nd = ((struct npc_data *)map_id2bl(st->oid)); - else - nd->master_nd = NULL; - - script_pushint(st,1); - return 0; -} - -/*========================================== - * Returns some values of an item [Lupus] - * Price, Weight, etc... - setitemscript(itemID,"{new item bonus script}",[n]); - Where n: - 0 - script - 1 - Equip script - 2 - Unequip script - *------------------------------------------*/ -BUILDIN_FUNC(setitemscript) -{ - int item_id,n=0; - const char *script; - struct item_data *i_data; - struct script_code **dstscript; - - item_id = script_getnum(st,2); - script = script_getstr(st,3); - if( script_hasdata(st,4) ) - n=script_getnum(st,4); - i_data = itemdb_exists(item_id); - - if (!i_data || script==NULL || ( script[0] && script[0]!='{' )) { - script_pushint(st,0); - return 0; - } - switch (n) { - case 2: - dstscript = &i_data->unequip_script; - break; - case 1: - dstscript = &i_data->equip_script; - break; - default: - dstscript = &i_data->script; - break; - } - if(*dstscript) - script_free_code(*dstscript); - - *dstscript = script[0] ? parse_script(script, "script_setitemscript", 0, 0) : NULL; - script_pushint(st,1); - return 0; -} - -/* Work In Progress [Lupus] -BUILDIN_FUNC(addmonsterdrop) -{ - int class_,item_id,chance; - class_=script_getnum(st,2); - item_id=script_getnum(st,3); - chance=script_getnum(st,4); - if(class_>1000 && item_id>500 && chance>0) { - script_pushint(st,1); - } else { - script_pushint(st,0); - } -} - -BUILDIN_FUNC(delmonsterdrop) -{ - int class_,item_id; - class_=script_getnum(st,2); - item_id=script_getnum(st,3); - if(class_>1000 && item_id>500) { - script_pushint(st,1); - } else { - script_pushint(st,0); - } -} -*/ - -/*========================================== - * Returns some values of a monster [Lupus] - * Name, Level, race, size, etc... - getmonsterinfo(monsterID,queryIndex); - *------------------------------------------*/ -BUILDIN_FUNC(getmonsterinfo) -{ - struct mob_db *mob; - int mob_id; - - mob_id = script_getnum(st,2); - if (!mobdb_checkid(mob_id)) { - ShowError("buildin_getmonsterinfo: Wrong Monster ID: %i\n", mob_id); - if ( !script_getnum(st,3) ) //requested a string - script_pushconststr(st,"null"); - else - script_pushint(st,-1); - return -1; - } - mob = mob_db(mob_id); - switch ( script_getnum(st,3) ) { - case 0: script_pushstrcopy(st,mob->jname); break; - case 1: script_pushint(st,mob->lv); break; - case 2: script_pushint(st,mob->status.max_hp); break; - case 3: script_pushint(st,mob->base_exp); break; - case 4: script_pushint(st,mob->job_exp); break; - case 5: script_pushint(st,mob->status.rhw.atk); break; - case 6: script_pushint(st,mob->status.rhw.atk2); break; - case 7: script_pushint(st,mob->status.def); break; - case 8: script_pushint(st,mob->status.mdef); break; - case 9: script_pushint(st,mob->status.str); break; - case 10: script_pushint(st,mob->status.agi); break; - case 11: script_pushint(st,mob->status.vit); break; - case 12: script_pushint(st,mob->status.int_); break; - case 13: script_pushint(st,mob->status.dex); break; - case 14: script_pushint(st,mob->status.luk); break; - case 15: script_pushint(st,mob->status.rhw.range); break; - case 16: script_pushint(st,mob->range2); break; - case 17: script_pushint(st,mob->range3); break; - case 18: script_pushint(st,mob->status.size); break; - case 19: script_pushint(st,mob->status.race); break; - case 20: script_pushint(st,mob->status.def_ele); break; - case 21: script_pushint(st,mob->status.mode); break; - case 22: script_pushint(st,mob->mexp); break; - default: script_pushint(st,-1); //wrong Index - } - return 0; -} - -BUILDIN_FUNC(checkvending) // check vending [Nab4] -{ - TBL_PC *sd = NULL; - - if(script_hasdata(st,2)) - sd = map_nick2sd(script_getstr(st,2)); - else - sd = script_rid2sd(st); - - if(sd) - script_pushint(st, sd->state.autotrade ? 2 : sd->state.vending); - else - script_pushint(st,0); - - return 0; -} - - -BUILDIN_FUNC(checkchatting) // check chatting [Marka] -{ - TBL_PC *sd = NULL; - - if(script_hasdata(st,2)) - sd = map_nick2sd(script_getstr(st,2)); - else - sd = script_rid2sd(st); - - if(sd) - script_pushint(st,(sd->chatID != 0)); - else - script_pushint(st,0); - - return 0; -} - -BUILDIN_FUNC(searchitem) -{ - struct script_data* data = script_getdata(st, 2); - const char *itemname = script_getstr(st,3); - struct item_data *items[MAX_SEARCH]; - int count; - - char* name; - int32 start; - int32 id; - int32 i; - TBL_PC* sd = NULL; - - if ((items[0] = itemdb_exists(atoi(itemname)))) - count = 1; - else { - count = itemdb_searchname_array(items, ARRAYLENGTH(items), itemname); - if (count > MAX_SEARCH) count = MAX_SEARCH; - } - - if (!count) { - script_pushint(st, 0); - return 0; - } - - if( !data_isreference(data) ) - { - ShowError("script:searchitem: 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:searchitem: illegal scope\n"); - script_reportdata(data); - st->state = END; - return 1;// not supported - } - - if( not_server_variable(*name) ) - { - sd = script_rid2sd(st); - if( sd == NULL ) - return 0;// no player attached - } - - if( is_string_variable(name) ) - {// string array - ShowError("script:searchitem: not an integer array reference\n"); - script_reportdata(data); - st->state = END; - return 1;// not supported - } - - for( i = 0; i < count; ++start, ++i ) - {// Set array - void* v = (void*)__64BPRTSIZE((int)items[i]->nameid); - set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data)); - } - - script_pushint(st, count); - return 0; -} - -int axtoi(const char *hexStg) -{ - int n = 0; // position in string - int16 m = 0; // position in digit[] to shift - int count; // loop index - int intValue = 0; // integer value of hex string - int digit[11]; // hold values to convert - while (n < 10) { - if (hexStg[n]=='\0') - break; - if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9 - digit[n] = hexStg[n] & 0x0f; //convert to int - else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f - digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int - else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F - digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int - else break; - n++; - } - count = n; - m = n - 1; - n = 0; - while(n < count) { - // digit[n] is value of hex digit at position n - // (m << 2) is the number of positions to shift - // OR the bits into return value - intValue = intValue | (digit[n] << (m << 2)); - m--; // adjust the position to set - n++; // next digit to process - } - return (intValue); -} - -// [Lance] Hex string to integer converter -BUILDIN_FUNC(axtoi) -{ - const char *hex = script_getstr(st,2); - script_pushint(st,axtoi(hex)); - return 0; -} - -// [zBuffer] List of player cont commands ---> -BUILDIN_FUNC(rid2name) -{ - struct block_list *bl = NULL; - int rid = script_getnum(st,2); - if((bl = map_id2bl(rid))) - { - switch(bl->type) { - case BL_MOB: script_pushstrcopy(st,((TBL_MOB*)bl)->name); break; - case BL_PC: script_pushstrcopy(st,((TBL_PC*)bl)->status.name); break; - case BL_NPC: script_pushstrcopy(st,((TBL_NPC*)bl)->exname); break; - case BL_PET: script_pushstrcopy(st,((TBL_PET*)bl)->pet.name); break; - case BL_HOM: script_pushstrcopy(st,((TBL_HOM*)bl)->homunculus.name); break; - case BL_MER: script_pushstrcopy(st,((TBL_MER*)bl)->db->name); break; - default: - ShowError("buildin_rid2name: BL type unknown.\n"); - script_pushconststr(st,""); - break; - } - } else { - ShowError("buildin_rid2name: invalid RID\n"); - script_pushconststr(st,"(null)"); - } - return 0; -} - -BUILDIN_FUNC(pcblockmove) -{ - int id, flag; - TBL_PC *sd = NULL; - - id = script_getnum(st,2); - flag = script_getnum(st,3); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd) - sd->state.blockedmove = flag > 0; - - return 0; -} - -BUILDIN_FUNC(pcfollow) -{ - int id, targetid; - TBL_PC *sd = NULL; - - - id = script_getnum(st,2); - targetid = script_getnum(st,3); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd) - pc_follow(sd, targetid); - - return 0; -} - -BUILDIN_FUNC(pcstopfollow) -{ - int id; - TBL_PC *sd = NULL; - - - id = script_getnum(st,2); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd) - pc_stop_following(sd); - - return 0; -} -// <--- [zBuffer] List of player cont commands -// [zBuffer] List of mob control commands ---> -//## TODO always return if the request/whatever was successfull [FlavioJS] - -/// Makes the unit walk to target position or map -/// Returns if it was successfull -/// -/// unitwalk(<unit_id>,<x>,<y>) -> <bool> -/// unitwalk(<unit_id>,<map_id>) -> <bool> -BUILDIN_FUNC(unitwalk) -{ - struct block_list* bl; - - bl = map_id2bl(script_getnum(st,2)); - if( bl == NULL ) - { - script_pushint(st, 0); - } - else if( script_hasdata(st,4) ) - { - int x = script_getnum(st,3); - int y = script_getnum(st,4); - script_pushint(st, unit_walktoxy(bl,x,y,0));// We'll use harder calculations. - } - else - { - int map_id = script_getnum(st,3); - script_pushint(st, unit_walktobl(bl,map_id2bl(map_id),65025,1)); - } - - return 0; -} - -/// Kills the unit -/// -/// unitkill <unit_id>; -BUILDIN_FUNC(unitkill) -{ - struct block_list* bl = map_id2bl(script_getnum(st,2)); - if( bl != NULL ) - status_kill(bl); - - return 0; -} - -/// Warps the unit to the target position in the target map -/// Returns if it was successfull -/// -/// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool> -BUILDIN_FUNC(unitwarp) -{ - int unit_id; - int map; - short x; - short y; - struct block_list* bl; - const char *mapname; - - unit_id = script_getnum(st,2); - mapname = script_getstr(st, 3); - x = (short)script_getnum(st,4); - y = (short)script_getnum(st,5); - - if (!unit_id) //Warp the script's runner - bl = map_id2bl(st->rid); - else - bl = map_id2bl(unit_id); - - if( strcmp(mapname,"this") == 0 ) - map = bl?bl->m:-1; - else - map = map_mapname2mapid(mapname); - - if( map >= 0 && bl != NULL ) - script_pushint(st, unit_warp(bl,map,x,y,CLR_OUTSIGHT)); - else - script_pushint(st, 0); - - return 0; -} - -/// Makes the unit attack the target. -/// If the unit is a player and <action type> is not 0, it does a continuous -/// attack instead of a single attack. -/// Returns if the request was successfull. -/// -/// unitattack(<unit_id>,"<target name>"{,<action type>}) -> <bool> -/// unitattack(<unit_id>,<target_id>{,<action type>}) -> <bool> -BUILDIN_FUNC(unitattack) -{ - struct block_list* unit_bl; - struct block_list* target_bl = NULL; - struct script_data* data; - int actiontype = 0; - - // get unit - unit_bl = map_id2bl(script_getnum(st,2)); - if( unit_bl == NULL ) { - script_pushint(st, 0); - return 0; - } - - data = script_getdata(st, 3); - get_val(st, data); - if( data_isstring(data) ) - { - TBL_PC* sd = map_nick2sd(conv_str(st, data)); - if( sd != NULL ) - target_bl = &sd->bl; - } else - target_bl = map_id2bl(conv_num(st, data)); - // request the attack - if( target_bl == NULL ) - { - script_pushint(st, 0); - return 0; - } - - // get actiontype - if( script_hasdata(st,4) ) - actiontype = script_getnum(st,4); - - switch( unit_bl->type ) - { - case BL_PC: - clif_parse_ActionRequest_sub(((TBL_PC *)unit_bl), actiontype > 0 ? 0x07 : 0x00, target_bl->id, gettick()); - script_pushint(st, 1); - return 0; - case BL_MOB: - ((TBL_MOB *)unit_bl)->target_id = target_bl->id; - break; - case BL_PET: - ((TBL_PET *)unit_bl)->target_id = target_bl->id; - break; - default: - ShowError("script:unitattack: unsupported source unit type %d\n", unit_bl->type); - script_pushint(st, 0); - return 1; - } - script_pushint(st, unit_walktobl(unit_bl, target_bl, 65025, 2)); - return 0; -} - -/// Makes the unit stop attacking and moving -/// -/// unitstop <unit_id>; -BUILDIN_FUNC(unitstop) -{ - int unit_id; - struct block_list* bl; - - unit_id = script_getnum(st,2); - - bl = map_id2bl(unit_id); - if( bl != NULL ) - { - unit_stop_attack(bl); - unit_stop_walking(bl,4); - if( bl->type == BL_MOB ) - ((TBL_MOB*)bl)->target_id = 0; - } - - return 0; -} - -/// Makes the unit say the message -/// -/// unittalk <unit_id>,"<message>"; -BUILDIN_FUNC(unittalk) -{ - int unit_id; - const char* message; - struct block_list* bl; - - unit_id = script_getnum(st,2); - message = script_getstr(st, 3); - - bl = map_id2bl(unit_id); - if( bl != NULL ) - { - struct StringBuf sbuf; - StringBuf_Init(&sbuf); - StringBuf_Printf(&sbuf, "%s : %s", status_get_name(bl), message); - clif_message(bl, StringBuf_Value(&sbuf)); - if( bl->type == BL_PC ) - clif_displaymessage(((TBL_PC*)bl)->fd, StringBuf_Value(&sbuf)); - StringBuf_Destroy(&sbuf); - } - - return 0; -} - -/// Makes the unit do an emotion -/// -/// unitemote <unit_id>,<emotion>; -/// -/// @see e_* in const.txt -BUILDIN_FUNC(unitemote) -{ - int unit_id; - int emotion; - struct block_list* bl; - - unit_id = script_getnum(st,2); - emotion = script_getnum(st,3); - bl = map_id2bl(unit_id); - if( bl != NULL ) - clif_emotion(bl, emotion); - - return 0; -} - -/// Makes the unit cast the skill on the target or self if no target is specified -/// -/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>}; -/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>}; -BUILDIN_FUNC(unitskilluseid) -{ - int unit_id; - uint16 skill_id; - uint16 skill_lv; - int target_id; - struct block_list* bl; - - unit_id = script_getnum(st,2); - skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) ); - skill_lv = script_getnum(st,4); - target_id = ( script_hasdata(st,5) ? script_getnum(st,5) : unit_id ); - - bl = map_id2bl(unit_id); - if( bl != NULL ) - unit_skilluse_id(bl, target_id, skill_id, skill_lv); - - return 0; -} - -/// Makes the unit cast the skill on the target position. -/// -/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>; -/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>; -BUILDIN_FUNC(unitskillusepos) -{ - int unit_id; - uint16 skill_id; - uint16 skill_lv; - int skill_x; - int skill_y; - struct block_list* bl; - - unit_id = script_getnum(st,2); - skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) ); - skill_lv = script_getnum(st,4); - skill_x = script_getnum(st,5); - skill_y = script_getnum(st,6); - - bl = map_id2bl(unit_id); - if( bl != NULL ) - unit_skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv); - - return 0; -} - -// <--- [zBuffer] List of mob control commands - -/// Pauses the execution of the script, detaching the player -/// -/// sleep <mili seconds>; -BUILDIN_FUNC(sleep) -{ - int ticks; - - ticks = script_getnum(st,2); - - // detach the player - script_detach_rid(st); - - if( ticks <= 0 ) - {// do nothing - } - else if( st->sleep.tick == 0 ) - {// sleep for the target amount of time - st->state = RERUNLINE; - st->sleep.tick = ticks; - } - else - {// sleep time is over - st->state = RUN; - st->sleep.tick = 0; - } - return 0; -} - -/// Pauses the execution of the script, keeping the player attached -/// Returns if a player is still attached -/// -/// sleep2(<mili secconds>) -> <bool> -BUILDIN_FUNC(sleep2) -{ - int ticks; - - ticks = script_getnum(st,2); - - if( ticks <= 0 ) - {// do nothing - script_pushint(st, (map_id2sd(st->rid)!=NULL)); - } - else if( !st->sleep.tick ) - {// sleep for the target amount of time - st->state = RERUNLINE; - st->sleep.tick = ticks; - } - else - {// sleep time is over - st->state = RUN; - st->sleep.tick = 0; - script_pushint(st, (map_id2sd(st->rid)!=NULL)); - } - return 0; -} - -/// Awakes all the sleep timers of the target npc -/// -/// awake "<npc name>"; -BUILDIN_FUNC(awake) -{ - struct npc_data* nd; - struct linkdb_node *node = (struct linkdb_node *)sleep_db; - - nd = npc_name2id(script_getstr(st, 2)); - if( nd == NULL ) { - ShowError("awake: NPC \"%s\" not found\n", script_getstr(st, 2)); - return 1; - } - - while( node ) - { - if( (int)__64BPRTSIZE(node->key) == nd->bl.id ) - {// sleep timer for the npc - struct script_state* tst = (struct script_state*)node->data; - TBL_PC* sd = map_id2sd(tst->rid); - - if( tst->sleep.timer == INVALID_TIMER ) - {// already awake ??? - node = node->next; - continue; - } - if( (sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd)) - {// char not online anymore / another char of the same account is online - Cancel execution - tst->state = END; - tst->rid = 0; - } - - delete_timer(tst->sleep.timer, run_script_timer); - node = script_erase_sleepdb(node); - tst->sleep.timer = INVALID_TIMER; - if(tst->state != RERUNLINE) - tst->sleep.tick = 0; - run_script_main(tst); - } - else - { - node = node->next; - } - } - return 0; -} - -/// Returns a reference to a variable of the target NPC. -/// Returns 0 if an error occurs. -/// -/// getvariableofnpc(<variable>, "<npc name>") -> <reference> -BUILDIN_FUNC(getvariableofnpc) -{ - struct script_data* data; - const char* name; - struct npc_data* nd; - - data = script_getdata(st,2); - if( !data_isreference(data) ) - {// Not a reference (aka varaible name) - ShowError("script:getvariableofnpc: not a variable\n"); - script_reportdata(data); - script_pushnil(st); - st->state = END; - return 1; - } - - name = reference_getname(data); - if( *name != '.' || name[1] == '@' ) - {// not a npc variable - ShowError("script:getvariableofnpc: invalid scope (not npc variable)\n"); - script_reportdata(data); - script_pushnil(st); - st->state = END; - return 1; - } - - nd = npc_name2id(script_getstr(st,3)); - if( nd == NULL || nd->subtype != SCRIPT || nd->u.scr.script == NULL ) - {// NPC not found or has no script - ShowError("script:getvariableofnpc: can't find npc %s\n", script_getstr(st,3)); - script_pushnil(st); - st->state = END; - return 1; - } - - push_val2(st->stack, C_NAME, reference_getuid(data), &nd->u.scr.script->script_vars ); - return 0; -} - -/// Opens a warp portal. -/// Has no "portal opening" effect/sound, it opens the portal immediately. -/// -/// warpportal <source x>,<source y>,"<target map>",<target x>,<target y>; -/// -/// @author blackhole89 -BUILDIN_FUNC(warpportal) -{ - int spx; - int spy; - unsigned short mapindex; - int tpx; - int tpy; - struct skill_unit_group* group; - struct block_list* bl; - - bl = map_id2bl(st->oid); - if( bl == NULL ) - { - ShowError("script:warpportal: npc is needed\n"); - return 1; - } - - spx = script_getnum(st,2); - spy = script_getnum(st,3); - mapindex = mapindex_name2id(script_getstr(st, 4)); - tpx = script_getnum(st,5); - tpy = script_getnum(st,6); - - if( mapindex == 0 ) - return 0;// map not found - - group = skill_unitsetting(bl, AL_WARP, 4, spx, spy, 0); - if( group == NULL ) - return 0;// failed - group->val2 = (tpx<<16) | tpy; - group->val3 = mapindex; - - return 0; -} - -BUILDIN_FUNC(openmail) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - mail_openmail(sd); - - return 0; -} - -BUILDIN_FUNC(openauction) -{ - TBL_PC* sd; - - sd = script_rid2sd(st); - if( sd == NULL ) - return 0; - - clif_Auction_openwindow(sd); - - return 0; -} - -/// Retrieves the value of the specified flag of the specified cell. -/// -/// checkcell("<map name>",<x>,<y>,<type>) -> <bool> -/// -/// @see cell_chk* constants in const.txt for the types -BUILDIN_FUNC(checkcell) -{ - int16 m = map_mapname2mapid(script_getstr(st,2)); - int16 x = script_getnum(st,3); - int16 y = script_getnum(st,4); - cell_chk type = (cell_chk)script_getnum(st,5); - - script_pushint(st, map_getcell(m, x, y, type)); - - return 0; -} - -/// Modifies flags of cells in the specified area. -/// -/// setcell "<map name>",<x1>,<y1>,<x2>,<y2>,<type>,<flag>; -/// -/// @see cell_* constants in const.txt for the types -BUILDIN_FUNC(setcell) -{ - int16 m = map_mapname2mapid(script_getstr(st,2)); - int16 x1 = script_getnum(st,3); - int16 y1 = script_getnum(st,4); - int16 x2 = script_getnum(st,5); - int16 y2 = script_getnum(st,6); - cell_t type = (cell_t)script_getnum(st,7); - bool flag = (bool)script_getnum(st,8); - - int x,y; - - if( x1 > x2 ) swap(x1,x2); - if( y1 > y2 ) swap(y1,y2); - - for( y = y1; y <= y2; ++y ) - for( x = x1; x <= x2; ++x ) - map_setcell(m, x, y, type, flag); - - return 0; -} - -/*========================================== - * Mercenary Commands - *------------------------------------------*/ -BUILDIN_FUNC(mercenary_create) -{ - struct map_session_data *sd; - int class_, contract_time; - - if( (sd = script_rid2sd(st)) == NULL || sd->md || sd->status.mer_id != 0 ) - return 0; - - class_ = script_getnum(st,2); - - if( !merc_class(class_) ) - return 0; - - contract_time = script_getnum(st,3); - merc_create(sd, class_, contract_time); - return 0; -} - -BUILDIN_FUNC(mercenary_heal) -{ - struct map_session_data *sd = script_rid2sd(st); - int hp, sp; - - if( sd == NULL || sd->md == NULL ) - return 0; - hp = script_getnum(st,2); - sp = script_getnum(st,3); - - status_heal(&sd->md->bl, hp, sp, 0); - return 0; -} - -BUILDIN_FUNC(mercenary_sc_start) -{ - struct map_session_data *sd = script_rid2sd(st); - enum sc_type type; - int tick, val1; - - if( sd == NULL || sd->md == NULL ) - return 0; - - type = (sc_type)script_getnum(st,2); - tick = script_getnum(st,3); - val1 = script_getnum(st,4); - - status_change_start(&sd->md->bl, type, 10000, val1, 0, 0, 0, tick, 2); - return 0; -} - -BUILDIN_FUNC(mercenary_get_calls) -{ - struct map_session_data *sd = script_rid2sd(st); - int guild; - - if( sd == NULL ) - return 0; - - guild = script_getnum(st,2); - switch( guild ) - { - case ARCH_MERC_GUILD: - script_pushint(st,sd->status.arch_calls); - break; - case SPEAR_MERC_GUILD: - script_pushint(st,sd->status.spear_calls); - break; - case SWORD_MERC_GUILD: - script_pushint(st,sd->status.sword_calls); - break; - default: - script_pushint(st,0); - break; - } - - return 0; -} - -BUILDIN_FUNC(mercenary_set_calls) -{ - struct map_session_data *sd = script_rid2sd(st); - int guild, value, *calls; - - if( sd == NULL ) - return 0; - - guild = script_getnum(st,2); - value = script_getnum(st,3); - - switch( guild ) - { - case ARCH_MERC_GUILD: - calls = &sd->status.arch_calls; - break; - case SPEAR_MERC_GUILD: - calls = &sd->status.spear_calls; - break; - case SWORD_MERC_GUILD: - calls = &sd->status.sword_calls; - break; - default: - return 0; // Invalid Guild - } - - *calls += value; - *calls = cap_value(*calls, 0, INT_MAX); - - return 0; -} - -BUILDIN_FUNC(mercenary_get_faith) -{ - struct map_session_data *sd = script_rid2sd(st); - int guild; - - if( sd == NULL ) - return 0; - - guild = script_getnum(st,2); - switch( guild ) - { - case ARCH_MERC_GUILD: - script_pushint(st,sd->status.arch_faith); - break; - case SPEAR_MERC_GUILD: - script_pushint(st,sd->status.spear_faith); - break; - case SWORD_MERC_GUILD: - script_pushint(st,sd->status.sword_faith); - break; - default: - script_pushint(st,0); - break; - } - - return 0; -} - -BUILDIN_FUNC(mercenary_set_faith) -{ - struct map_session_data *sd = script_rid2sd(st); - int guild, value, *calls; - - if( sd == NULL ) - return 0; - - guild = script_getnum(st,2); - value = script_getnum(st,3); - - switch( guild ) - { - case ARCH_MERC_GUILD: - calls = &sd->status.arch_faith; - break; - case SPEAR_MERC_GUILD: - calls = &sd->status.spear_faith; - break; - case SWORD_MERC_GUILD: - calls = &sd->status.sword_faith; - break; - default: - return 0; // Invalid Guild - } - - *calls += value; - *calls = cap_value(*calls, 0, INT_MAX); - if( mercenary_get_guild(sd->md) == guild ) - clif_mercenary_updatestatus(sd,SP_MERCFAITH); - - return 0; -} - -/*------------------------------------------ - * Book Reading - *------------------------------------------*/ -BUILDIN_FUNC(readbook) -{ - struct map_session_data *sd; - int book_id, page; - - if( (sd = script_rid2sd(st)) == NULL ) - return 0; - - book_id = script_getnum(st,2); - page = script_getnum(st,3); - - clif_readbook(sd->fd, book_id, page); - return 0; -} - -/****************** -Questlog script commands -*******************/ - -BUILDIN_FUNC(setquest) -{ - struct map_session_data *sd = script_rid2sd(st); - nullpo_ret(sd); - - quest_add(sd, script_getnum(st, 2)); - return 0; -} - -BUILDIN_FUNC(erasequest) -{ - struct map_session_data *sd = script_rid2sd(st); - nullpo_ret(sd); - - quest_delete(sd, script_getnum(st, 2)); - return 0; -} - -BUILDIN_FUNC(completequest) -{ - struct map_session_data *sd = script_rid2sd(st); - nullpo_ret(sd); - - quest_update_status(sd, script_getnum(st, 2), Q_COMPLETE); - return 0; -} - -BUILDIN_FUNC(changequest) -{ - struct map_session_data *sd = script_rid2sd(st); - nullpo_ret(sd); - - quest_change(sd, script_getnum(st, 2),script_getnum(st, 3)); - return 0; -} - -BUILDIN_FUNC(checkquest) -{ - struct map_session_data *sd = script_rid2sd(st); - quest_check_type type = HAVEQUEST; - - nullpo_ret(sd); - - if( script_hasdata(st, 3) ) - type = (quest_check_type)script_getnum(st, 3); - - script_pushint(st, quest_check(sd, script_getnum(st, 2), type)); - - return 0; -} - -BUILDIN_FUNC(showevent) -{ - TBL_PC *sd = script_rid2sd(st); - struct npc_data *nd = map_id2nd(st->oid); - int state, color; - - if( sd == NULL || nd == NULL ) - return 0; - state = script_getnum(st, 2); - color = script_getnum(st, 3); - - if( color < 0 || color > 3 ) - color = 0; // set default color - - clif_quest_show_event(sd, &nd->bl, state, color); - return 0; -} - -/*========================================== - * BattleGround System - *------------------------------------------*/ -BUILDIN_FUNC(waitingroom2bg) -{ - struct npc_data *nd; - struct chat_data *cd; - const char *map_name, *ev = "", *dev = ""; - int x, y, i, mapindex = 0, bg_id, n; - struct map_session_data *sd; - - if( script_hasdata(st,7) ) - nd = npc_name2id(script_getstr(st,7)); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL ) - { - script_pushint(st,0); - return 0; - } - - map_name = script_getstr(st,2); - if( strcmp(map_name,"-") != 0 ) - { - mapindex = mapindex_name2id(map_name); - if( mapindex == 0 ) - { // Invalid Map - script_pushint(st,0); - return 0; - } - } - - x = script_getnum(st,3); - y = script_getnum(st,4); - ev = script_getstr(st,5); // Logout Event - dev = script_getstr(st,6); // Die Event - - if( (bg_id = bg_create(mapindex, x, y, ev, dev)) == 0 ) - { // Creation failed - script_pushint(st,0); - return 0; - } - - n = cd->users; - for( i = 0; i < n && i < MAX_BG_MEMBERS; i++ ) - { - if( (sd = cd->usersd[i]) != NULL && bg_team_join(bg_id, sd) ) - mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), sd->bl.id); - else - mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), 0); - } - - mapreg_setreg(add_str("$@arenamembersnum"), i); - script_pushint(st,bg_id); - return 0; -} - -BUILDIN_FUNC(waitingroom2bg_single) -{ - const char* map_name; - struct npc_data *nd; - struct chat_data *cd; - struct map_session_data *sd; - int x, y, mapindex, bg_id; - - bg_id = script_getnum(st,2); - map_name = script_getstr(st,3); - if( (mapindex = mapindex_name2id(map_name)) == 0 ) - return 0; // Invalid Map - - x = script_getnum(st,4); - y = script_getnum(st,5); - nd = npc_name2id(script_getstr(st,6)); - - if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL || cd->users <= 0 ) - return 0; - - if( (sd = cd->usersd[0]) == NULL ) - return 0; - - if( bg_team_join(bg_id, sd) ) - { - pc_setpos(sd, mapindex, x, y, CLR_TELEPORT); - script_pushint(st,1); - } - else - script_pushint(st,0); - - return 0; -} - -BUILDIN_FUNC(bg_team_setxy) -{ - struct battleground_data *bg; - int bg_id; - - bg_id = script_getnum(st,2); - if( (bg = bg_team_search(bg_id)) == NULL ) - return 0; - - bg->x = script_getnum(st,3); - bg->y = script_getnum(st,4); - return 0; -} - -BUILDIN_FUNC(bg_warp) -{ - int x, y, mapindex, bg_id; - const char* map_name; - - bg_id = script_getnum(st,2); - map_name = script_getstr(st,3); - if( (mapindex = mapindex_name2id(map_name)) == 0 ) - return 0; // Invalid Map - x = script_getnum(st,4); - y = script_getnum(st,5); - bg_team_warp(bg_id, mapindex, x, y); - return 0; -} - -BUILDIN_FUNC(bg_monster) -{ - int class_ = 0, x = 0, y = 0, bg_id = 0; - const char *str,*map, *evt=""; - - bg_id = script_getnum(st,2); - map = script_getstr(st,3); - x = script_getnum(st,4); - y = script_getnum(st,5); - str = script_getstr(st,6); - class_ = script_getnum(st,7); - if( script_hasdata(st,8) ) evt = script_getstr(st,8); - check_event(st, evt); - script_pushint(st, mob_spawn_bg(map,x,y,str,class_,evt,bg_id)); - return 0; -} - -BUILDIN_FUNC(bg_monster_set_team) -{ - struct mob_data *md; - struct block_list *mbl; - int id = script_getnum(st,2), - bg_id = script_getnum(st,3); - - if( (mbl = map_id2bl(id)) == NULL || mbl->type != BL_MOB ) - return 0; - md = (TBL_MOB *)mbl; - md->bg_id = bg_id; - - mob_stop_attack(md); - mob_stop_walking(md, 0); - md->target_id = md->attacked_id = 0; - clif_charnameack(0, &md->bl); - - return 0; -} - -BUILDIN_FUNC(bg_leave) -{ - struct map_session_data *sd = script_rid2sd(st); - if( sd == NULL || !sd->bg_id ) - return 0; - - bg_team_leave(sd,0); - return 0; -} - -BUILDIN_FUNC(bg_destroy) -{ - int bg_id = script_getnum(st,2); - bg_team_delete(bg_id); - return 0; -} - -BUILDIN_FUNC(bg_getareausers) -{ - const char *str; - int16 m, x0, y0, x1, y1; - int bg_id; - int i = 0, c = 0; - struct battleground_data *bg = NULL; - struct map_session_data *sd; - - bg_id = script_getnum(st,2); - str = script_getstr(st,3); - - if( (bg = bg_team_search(bg_id)) == NULL || (m = map_mapname2mapid(str)) < 0 ) - { - script_pushint(st,0); - return 0; - } - - x0 = script_getnum(st,4); - y0 = script_getnum(st,5); - x1 = script_getnum(st,6); - y1 = script_getnum(st,7); - - for( i = 0; i < MAX_BG_MEMBERS; i++ ) - { - if( (sd = bg->members[i].sd) == NULL ) - continue; - if( sd->bl.m != m || sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1 ) - continue; - c++; - } - - script_pushint(st,c); - return 0; -} - -BUILDIN_FUNC(bg_updatescore) -{ - const char *str; - int16 m; - - str = script_getstr(st,2); - if( (m = map_mapname2mapid(str)) < 0 ) - return 0; - - map[m].bgscore_lion = script_getnum(st,3); - map[m].bgscore_eagle = script_getnum(st,4); - - clif_bg_updatescore(m); - return 0; -} - -BUILDIN_FUNC(bg_get_data) -{ - struct battleground_data *bg; - int bg_id = script_getnum(st,2), - type = script_getnum(st,3); - - if( (bg = bg_team_search(bg_id)) == NULL ) - { - script_pushint(st,0); - return 0; - } - - switch( type ) - { - case 0: script_pushint(st, bg->count); break; - default: - ShowError("script:bg_get_data: unknown data identifier %d\n", type); - break; - } - - return 0; -} - -/*========================================== - * Instancing Script Commands - *------------------------------------------*/ - -BUILDIN_FUNC(instance_create) -{ - const char *name; - int party_id, res; - - name = script_getstr(st, 2); - party_id = script_getnum(st, 3); - - res = instance_create(party_id, name); - if( res == -4 ) // Already exists - { - script_pushint(st, -1); - return 0; - } - else if( res < 0 ) - { - const char *err; - switch(res) - { - case -3: err = "No free instances"; break; - case -2: err = "Invalid party ID"; break; - case -1: err = "Invalid type"; break; - default: err = "Unknown"; break; - } - ShowError("buildin_instance_create: %s [%d].\n", err, res); - script_pushint(st, -2); - return 0; - } - - script_pushint(st, res); - return 0; -} - -BUILDIN_FUNC(instance_destroy) -{ - int instance_id; - struct map_session_data *sd; - struct party_data *p; - - if( script_hasdata(st, 2) ) - instance_id = script_getnum(st, 2); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - { - ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id); - return 0; - } - - instance_destroy(instance_id); - return 0; -} - -BUILDIN_FUNC(instance_attachmap) -{ - const char *name; - int16 m; - int instance_id; - bool usebasename = false; - - name = script_getstr(st,2); - instance_id = script_getnum(st,3); - if( script_hasdata(st,4) && script_getnum(st,4) > 0) - usebasename = true; - - if( (m = instance_add_map(name, instance_id, usebasename)) < 0 ) // [Saithis] - { - ShowError("buildin_instance_attachmap: instance creation failed (%s): %d\n", name, m); - script_pushconststr(st, ""); - return 0; - } - script_pushconststr(st, map[m].name); - - return 0; -} - -BUILDIN_FUNC(instance_detachmap) -{ - struct map_session_data *sd; - struct party_data *p; - const char *str; - int16 m; - int instance_id; - - str = script_getstr(st, 2); - if( script_hasdata(st, 3) ) - instance_id = script_getnum(st, 3); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - - if( (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m,instance_id)) < 0 ) - { - ShowError("buildin_instance_detachmap: Trying to detach invalid map %s\n", str); - return 0; - } - - instance_del_map(m); - return 0; -} - -BUILDIN_FUNC(instance_attach) -{ - int instance_id; - - instance_id = script_getnum(st, 2); - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - return 0; - - st->instance_id = instance_id; - return 0; -} - -BUILDIN_FUNC(instance_id) -{ - int type, instance_id; - struct map_session_data *sd; - struct party_data *p; - - if( script_hasdata(st, 2) ) - { - type = script_getnum(st, 2); - if( type == 0 ) - instance_id = st->instance_id; - else if( type == 1 && (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL ) - instance_id = p->instance_id; - else - instance_id = 0; - } - else - instance_id = st->instance_id; - - script_pushint(st, instance_id); - return 0; -} - -BUILDIN_FUNC(instance_set_timeout) -{ - int progress_timeout, idle_timeout; - int instance_id; - struct map_session_data *sd; - struct party_data *p; - - progress_timeout = script_getnum(st, 2); - idle_timeout = script_getnum(st, 3); - - if( script_hasdata(st, 4) ) - instance_id = script_getnum(st, 4); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - - if( instance_id > 0 ) - instance_set_timeout(instance_id, progress_timeout, idle_timeout); - - return 0; -} - -BUILDIN_FUNC(instance_init) -{ - int instance_id = script_getnum(st, 2); - - if( instance[instance_id].state != INSTANCE_IDLE ) - { - ShowError("instance_init: instance already initialized.\n"); - return 0; - } - - instance_init(instance_id); - return 0; -} - -BUILDIN_FUNC(instance_announce) -{ - int instance_id = script_getnum(st,2); - const char *mes = script_getstr(st,3); - int flag = script_getnum(st,4); - const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL; - int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL) - int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize - int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign - int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY - - int i; - struct map_session_data *sd; - struct party_data *p; - - if( instance_id == 0 ) - { - if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - } - - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - return 0; - - for( i = 0; i < instance[instance_id].num_map; i++ ) - map_foreachinmap(buildin_announce_sub, instance[instance_id].map[i], BL_PC, - mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY); - - return 0; -} - -BUILDIN_FUNC(instance_npcname) -{ - const char *str; - int instance_id = 0; - - struct map_session_data *sd; - struct party_data *p; - struct npc_data *nd; - - str = script_getstr(st, 2); - if( script_hasdata(st, 3) ) - instance_id = script_getnum(st, 3); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - - if( instance_id && (nd = npc_name2id(str)) != NULL ) - { - static char npcname[NAME_LENGTH]; - snprintf(npcname, sizeof(npcname), "dup_%d_%d", instance_id, nd->bl.id); - script_pushconststr(st,npcname); - } - else - { - ShowError("script:instance_npcname: invalid instance NPC (instance_id: %d, NPC name: \"%s\".)\n", instance_id, str); - st->state = END; - return 1; - } - - return 0; -} - -BUILDIN_FUNC(has_instance) -{ - struct map_session_data *sd; - struct party_data *p; - const char *str; - int16 m; - int instance_id = 0; - - str = script_getstr(st, 2); - if( script_hasdata(st, 3) ) - instance_id = script_getnum(st, 3); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - - if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m, instance_id)) < 0 ) - { - script_pushconststr(st, ""); - return 0; - } - - script_pushconststr(st, map[m].name); - return 0; -} - -BUILDIN_FUNC(instance_warpall) -{ - struct map_session_data *pl_sd; - int16 m, i; - int instance_id; - const char *mapn; - int x, y; - unsigned short mapindex; - struct party_data *p = NULL; - - mapn = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - if( script_hasdata(st,5) ) - instance_id = script_getnum(st,5); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (pl_sd = script_rid2sd(st)) != NULL && pl_sd->status.party_id && (p = party_search(pl_sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - - if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapid2imapid(m, instance_id)) < 0) ) - return 0; - - if( !(p = party_search(instance[instance_id].party_id)) ) - return 0; - - mapindex = map_id2index(m); - for( i = 0; i < MAX_PARTY; i++ ) - if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == st->instance_id ) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT); - - return 0; -} - -/*========================================== - * instance_check_party [malufett] - * Values: - * party_id : Party ID of the invoking character. [Required Parameter] - * amount : Amount of needed Partymembers for the Instance. [Optional Parameter] - * min : Minimum Level needed to join the Instance. [Optional Parameter] - * max : Maxium Level allowed to join the Instance. [Optional Parameter] - * Example: instance_check_party (getcharid(1){,amount}{,min}{,max}); - * Example 2: instance_check_party (getcharid(1),1,1,99); - *------------------------------------------*/ -BUILDIN_FUNC(instance_check_party) -{ - struct map_session_data *pl_sd; - int amount, min, max, i, party_id, c = 0; - struct party_data *p = NULL; - - amount = script_hasdata(st,3) ? script_getnum(st,3) : 1; // Amount of needed Partymembers for the Instance. - min = script_hasdata(st,4) ? script_getnum(st,4) : 1; // Minimum Level needed to join the Instance. - max = script_hasdata(st,5) ? script_getnum(st,5) : MAX_LEVEL; // Maxium Level allowed to join the Instance. - - if( min < 1 || min > MAX_LEVEL){ - ShowError("instance_check_party: Invalid min level, %d\n", min); - return 0; - }else if( max < 1 || max > MAX_LEVEL){ - ShowError("instance_check_party: Invalid max level, %d\n", max); - return 0; - } - - if( script_hasdata(st,2) ) - party_id = script_getnum(st,2); - else return 0; - - if( !(p = party_search(party_id)) ){ - script_pushint(st, 0); // Returns false if party does not exist. - return 0; - } - - for( i = 0; i < MAX_PARTY; i++ ) - if( (pl_sd = p->data[i].sd) ) - if(map_id2bl(pl_sd->bl.id)){ - if(pl_sd->status.base_level < min){ - script_pushint(st, 0); - return 0; - }else if(pl_sd->status.base_level > max){ - script_pushint(st, 0); - return 0; - } - c++; - } - - if(c < amount){ - script_pushint(st, 0); // Not enough Members in the Party to join Instance. - }else - script_pushint(st, 1); - - return 0; -} - -/*========================================== - * Custom Fonts - *------------------------------------------*/ -BUILDIN_FUNC(setfont) -{ - struct map_session_data *sd = script_rid2sd(st); - int font = script_getnum(st,2); - if( sd == NULL ) - return 0; - - if( sd->user_font != font ) - sd->user_font = font; - else - sd->user_font = 0; - - clif_font(sd); - return 0; -} - -static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap) -{ - TBL_MOB* md = (TBL_MOB*)bl; - struct block_list *tbl; - int mobid = va_arg(ap,int); - uint16 skill_id = va_arg(ap,int); - uint16 skill_lv = va_arg(ap,int); - int casttime = va_arg(ap,int); - int cancel = va_arg(ap,int); - int emotion = va_arg(ap,int); - int target = va_arg(ap,int); - - if( md->class_ != mobid ) - return 0; - - // 0:self, 1:target, 2:master, default:random - switch( target ) - { - case 0: tbl = map_id2bl(md->bl.id); break; - case 1: tbl = map_id2bl(md->target_id); break; - case 2: tbl = map_id2bl(md->master_id); break; - default:tbl = battle_getenemy(&md->bl, DEFAULT_ENEMY_TYPE(md),skill_get_range2(&md->bl, skill_id, skill_lv)); break; - } - - if( !tbl ) - return 0; - - if( md->ud.skilltimer != INVALID_TIMER ) // Cancel the casting skill. - unit_skillcastcancel(bl,0); - - if( skill_get_casttype(skill_id) == CAST_GROUND ) - unit_skilluse_pos2(&md->bl, tbl->x, tbl->y, skill_id, skill_lv, casttime, cancel); - else - unit_skilluse_id2(&md->bl, tbl->id, skill_id, skill_lv, casttime, cancel); - - clif_emotion(&md->bl, emotion); - - return 0; -} -/*========================================== - * areamobuseskill "Map Name",<x>,<y>,<range>,<Mob ID>,"Skill Name"/<Skill ID>,<Skill Lv>,<Cast Time>,<Cancelable>,<Emotion>,<Target Type>; - *------------------------------------------*/ -BUILDIN_FUNC(areamobuseskill) -{ - struct block_list center; - int16 m; - int range,mobid,skill_id,skill_lv,casttime,emotion,target,cancel; - - if( (m = map_mapname2mapid(script_getstr(st,2))) < 0 ) - { - ShowError("areamobuseskill: invalid map name.\n"); - return 0; - } - - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - return 0; - - center.m = m; - center.x = script_getnum(st,3); - center.y = script_getnum(st,4); - range = script_getnum(st,5); - mobid = script_getnum(st,6); - skill_id = ( script_isstring(st,7) ? skill_name2id(script_getstr(st,7)) : script_getnum(st,7) ); - if( (skill_lv = script_getnum(st,8)) > battle_config.mob_max_skilllvl ) - skill_lv = battle_config.mob_max_skilllvl; - - casttime = script_getnum(st,9); - cancel = script_getnum(st,10); - emotion = script_getnum(st,11); - target = script_getnum(st,12); - - map_foreachinrange(buildin_mobuseskill_sub, ¢er, range, BL_MOB, mobid, skill_id, skill_lv, casttime, cancel, emotion, target); - return 0; -} - - -BUILDIN_FUNC(progressbar) -{ - struct map_session_data * sd = script_rid2sd(st); - const char * color; - unsigned int second; - - if( !st || !sd ) - return 0; - - st->state = STOP; - - color = script_getstr(st,2); - second = script_getnum(st,3); - - sd->progressbar.npc_id = st->oid; - sd->progressbar.timeout = gettick() + second*1000; - - clif_progressbar(sd, strtol(color, (char **)NULL, 0), second); - return 0; -} - -BUILDIN_FUNC(pushpc) -{ - uint8 dir; - int cells, dx, dy; - struct map_session_data* sd; - - if((sd = script_rid2sd(st))==NULL) - { - return 0; - } - - dir = script_getnum(st,2); - cells = script_getnum(st,3); - - if(dir>7) - { - ShowWarning("buildin_pushpc: Invalid direction %d specified.\n", dir); - script_reportsrc(st); - - dir%= 8; // trim spin-over - } - - if(!cells) - {// zero distance - return 0; - } - else if(cells<0) - {// pushing backwards - dir = (dir+4)%8; // turn around - cells = -cells; - } - - dx = dirx[dir]; - dy = diry[dir]; - - unit_blown(&sd->bl, dx, dy, cells, 0); - return 0; -} - - -/// Invokes buying store preparation window -/// buyingstore <slots>; -BUILDIN_FUNC(buyingstore) -{ - struct map_session_data* sd; - - if( ( sd = script_rid2sd(st) ) == NULL ) - { - return 0; - } - - buyingstore_setup(sd, script_getnum(st,2)); - return 0; -} - - -/// Invokes search store info window -/// searchstores <uses>,<effect>; -BUILDIN_FUNC(searchstores) -{ - unsigned short effect; - unsigned int uses; - struct map_session_data* sd; - - if( ( sd = script_rid2sd(st) ) == NULL ) - { - return 0; - } - - uses = script_getnum(st,2); - effect = script_getnum(st,3); - - if( !uses ) - { - ShowError("buildin_searchstores: Amount of uses cannot be zero.\n"); - return 1; - } - - if( effect > 1 ) - { - ShowError("buildin_searchstores: Invalid effect id %hu, specified.\n", effect); - return 1; - } - - searchstore_open(sd, uses, effect); - return 0; -} -/// Displays a number as large digital clock. -/// showdigit <value>[,<type>]; -BUILDIN_FUNC(showdigit) -{ - unsigned int type = 0; - int value; - struct map_session_data* sd; - - if( ( sd = script_rid2sd(st) ) == NULL ) - { - return 0; - } - - value = script_getnum(st,2); - - if( script_hasdata(st,3) ) - { - type = script_getnum(st,3); - - if( type > 3 ) - { - ShowError("buildin_showdigit: Invalid type %u.\n", type); - return 1; - } - } - - 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( pc_isridingdragon(sd) ) - 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 ( pc_isridingdragon(sd) ) {//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; - } - } - pc_setoption(sd, sd->sc.option|option); - script_pushint(st,1); - } - 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; -} -/** - * Retrieves quantity of arguments provided to callfunc/callsub. - * getargcount() -> amount of arguments received in a function - **/ -BUILDIN_FUNC(getargcount) { - struct script_retinfo* ri; - - if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO ) { - ShowError("script:getargcount: used out of function or callsub label!\n"); - st->state = END; - return 1; - } - ri = st->stack->stack_data[st->stack->defsp - 1].u.ri; - - script_pushint(st, ri->nargs); - - return 0; -} -/** - * getcharip(<account ID>/<character ID>/<character name>) - **/ -BUILDIN_FUNC(getcharip) -{ - struct map_session_data* sd = NULL; - int id = 0; - - /* check if a character name is specified */ - if( script_hasdata(st, 2) ) - { - if (script_isstring(st, 2)) - sd = map_nick2sd(script_getstr(st, 2)); - else if (script_isint(st, 2) || script_getnum(st, 2)) - { - id = script_getnum(st, 2); - sd = (map_id2sd(id) ? map_id2sd(id) : map_charid2sd(id)); - } - } - else - sd = script_rid2sd(st); - - /* check for sd and IP */ - if (!sd || !session[sd->fd]->client_addr) - { - script_pushconststr(st, ""); - return 0; - } - - /* return the client ip_addr converted for output */ - if (sd && sd->fd && session[sd->fd]) - { - /* initiliaze */ - const char *ip_addr = NULL; - uint32 ip; - - /* set ip, ip_addr and convert to ip and push str */ - ip = session[sd->fd]->client_addr; - ip_addr = ip2str(ip, NULL); - script_pushstrcopy(st, ip_addr); - } - - return 0; -} -/** - * is_function(<function name>) -> 1 if function exists, 0 otherwise - **/ -BUILDIN_FUNC(is_function) { - const char* str = script_getstr(st,2); - - if( strdb_exists(userfunc_db, str) ) - script_pushint(st,1); - else - script_pushint(st,0); - - return 0; -} -/** - * get_revision() -> retrieves the current svn revision (if available) - **/ -BUILDIN_FUNC(get_revision) { - const char * revision; - - if ( (revision = get_svn_revision()) != 0 ) - script_pushint(st,atoi(revision)); - else - script_pushint(st,-1);//unknown - - return 0; -} -/** - * freeloop(<toggle>) -> toggles this script instance's looping-check ability - **/ -BUILDIN_FUNC(freeloop) { - - if( script_getnum(st,2) ) - st->freeloop = 1; - else - st->freeloop = 0; - - script_pushint(st, st->freeloop); - - return 0; -} - -/** - * @commands (script based) - **/ -BUILDIN_FUNC(bindatcmd) { - const char* atcmd; - const char* eventName; - int i, level = 0, level2 = 0; - bool create = false; - - atcmd = script_getstr(st,2); - eventName = script_getstr(st,3); - - if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol ) - atcmd++; - - if( script_hasdata(st,4) ) level = script_getnum(st,4); - if( script_hasdata(st,5) ) level2 = script_getnum(st,5); - - if( atcmd_binding_count == 0 ) { - CREATE(atcmd_binding,struct atcmd_binding_data*,1); - - create = true; - } else { - ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command,atcmd) == 0); - if( i < atcmd_binding_count ) {/* update existent entry */ - safestrncpy(atcmd_binding[i]->npc_event, eventName, 50); - atcmd_binding[i]->level = level; - atcmd_binding[i]->level2 = level2; - } else - create = true; - } - - if( create ) { - i = atcmd_binding_count; - - if( atcmd_binding_count++ != 0 ) - RECREATE(atcmd_binding,struct atcmd_binding_data*,atcmd_binding_count); - - CREATE(atcmd_binding[i],struct atcmd_binding_data,1); - - safestrncpy(atcmd_binding[i]->command, atcmd, 50); - safestrncpy(atcmd_binding[i]->npc_event, eventName, 50); - atcmd_binding[i]->level = level; - atcmd_binding[i]->level2 = level2; - } - - return 0; -} - -BUILDIN_FUNC(unbindatcmd) { - const char* atcmd; - int i = 0; - - atcmd = script_getstr(st, 2); - - if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol ) - atcmd++; - - if( atcmd_binding_count == 0 ) { - script_pushint(st, 0); - return 0; - } - - ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command, atcmd) == 0); - if( i < atcmd_binding_count ) { - int cursor = 0; - aFree(atcmd_binding[i]); - atcmd_binding[i] = NULL; - /* compact the list now that we freed a slot somewhere */ - for( i = 0, cursor = 0; i < atcmd_binding_count; i++ ) { - if( atcmd_binding[i] == NULL ) - continue; - - if( cursor != i ) { - memmove(&atcmd_binding[cursor], &atcmd_binding[i], sizeof(struct atcmd_binding_data*)); - } - - cursor++; - } - - if( (atcmd_binding_count = cursor) == 0 ) - aFree(atcmd_binding); - - script_pushint(st, 1); - } else - script_pushint(st, 0);/* not found */ - - return 0; -} - -BUILDIN_FUNC(useatcmd) -{ - TBL_PC dummy_sd; - TBL_PC* sd; - int fd; - const char* cmd; - - cmd = script_getstr(st,2); - - if( st->rid ) - { - sd = script_rid2sd(st); - fd = sd->fd; - } - else - { // Use a dummy character. - sd = &dummy_sd; - fd = 0; - - memset(&dummy_sd, 0, sizeof(TBL_PC)); - if( st->oid ) - { - struct block_list* bl = map_id2bl(st->oid); - memcpy(&dummy_sd.bl, bl, sizeof(struct block_list)); - if( bl->type == BL_NPC ) - safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH); - } - } - - // compatibility with previous implementation (deprecated!) - if( cmd[0] != atcommand_symbol ) - { - cmd += strlen(sd->status.name); - while( *cmd != atcommand_symbol && *cmd != 0 ) - cmd++; - } - - is_atcommand(fd, sd, cmd, 1); - return 0; -} - -BUILDIN_FUNC(checkre) -{ - int num; - - num=script_getnum(st,2); - switch(num){ - case 0: - #ifdef RENEWAL - script_pushint(st, 1); - #else - script_pushint(st, 0); - #endif - break; - case 1: - #ifdef RENEWAL_CAST - script_pushint(st, 1); - #else - script_pushint(st, 0); - #endif - break; - case 2: - #ifdef RENEWAL_DROP - script_pushint(st, 1); - #else - script_pushint(st, 0); - #endif - break; - case 3: - #ifdef RENEWAL_EXP - script_pushint(st, 1); - #else - script_pushint(st, 0); - #endif - break; - case 4: - #ifdef RENEWAL_LVDMG - script_pushint(st, 1); - #else - script_pushint(st, 0); - #endif - break; - case 5: - #ifdef RENEWAL_EDP - script_pushint(st, 1); - #else - script_pushint(st, 0); - #endif - break; - case 6: - #ifdef RENEWAL_ASPD - script_pushint(st, 1); - #else - script_pushint(st, 0); - #endif - break; - default: - ShowWarning("buildin_checkre: unknown parameter.\n"); - break; - } - return 0; -} - -/* getrandgroupitem <group_id>,<quantity> */ -BUILDIN_FUNC(getrandgroupitem) { - TBL_PC* sd; - int i, get_count = 0, flag, nameid, group = script_getnum(st, 2), qty = script_getnum(st,3); - struct item item_tmp; - - if( !( sd = script_rid2sd(st) ) ) - return 0; - - if( qty <= 0 ) { - ShowError("getrandgroupitem: qty is <= 0!\n"); - return 1; - } - if( (nameid = itemdb_searchrandomid(group)) == UNKNOWN_ITEM_ID ) { - return 1;/* itemdb_searchrandomid will already scream a error */ - } - - memset(&item_tmp,0,sizeof(item_tmp)); - - item_tmp.nameid = nameid; - item_tmp.identify = itemdb_isidentified(nameid); - - //Check if it's stackable. - if (!itemdb_isstackable(nameid)) - get_count = 1; - else - get_count = qty; - - for (i = 0; i < qty; i += get_count) { - // if not pet egg - if (!pet_create_egg(sd, nameid)) { - if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) { - clif_additem(sd, 0, 0, flag); - if( pc_candrop(sd,&item_tmp) ) - map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - - return 0; -} - -/* cleanmap <map_name>; - * cleanarea <map_name>, <x0>, <y0>, <x1>, <y1>; */ -static int atcommand_cleanfloor_sub(struct block_list *bl, va_list ap) -{ - nullpo_ret(bl); - map_clearflooritem(bl); - - return 0; -} - -BUILDIN_FUNC(cleanmap) -{ - const char *map; - int16 m = -1; - int16 x0 = 0, y0 = 0, x1 = 0, y1 = 0; - - map = script_getstr(st, 2); - m = map_mapname2mapid(map); - if (!m) - return 1; - - if ((script_lastdata(st) - 2) < 4) { - map_foreachinmap(atcommand_cleanfloor_sub, m, BL_ITEM); - } else { - x0 = script_getnum(st, 3); - y0 = script_getnum(st, 4); - x1 = script_getnum(st, 5); - y1 = script_getnum(st, 6); - if (x0 > 0 && y0 > 0 && x1 > 0 && y1 > 0) { - map_foreachinarea(atcommand_cleanfloor_sub, m, x0, y0, x1, y1, BL_ITEM); - } else { - ShowError("cleanarea: invalid coordinate defined!\n"); - return 1; - } - } - - return 0; -} -/* Cast a skill on the attached player. - * npcskill <skill id>, <skill lvl>, <stat point>, <NPC level>; - * npcskill "<skill name>", <skill lvl>, <stat point>, <NPC level>; */ -BUILDIN_FUNC(npcskill) -{ - uint16 skill_id; - unsigned short skill_level; - unsigned int stat_point; - unsigned int npc_level; - struct npc_data *nd; - struct map_session_data *sd; - - skill_id = script_isstring(st, 2) ? skill_name2id(script_getstr(st, 2)) : script_getnum(st, 2); - skill_level = script_getnum(st, 3); - stat_point = script_getnum(st, 4); - npc_level = script_getnum(st, 5); - sd = script_rid2sd(st); - nd = (struct npc_data *)map_id2bl(sd->npc_id); - - if (stat_point > battle_config.max_third_parameter) { - ShowError("npcskill: stat point exceeded maximum of %d.\n",battle_config.max_third_parameter ); - return 1; - } - if (npc_level > MAX_LEVEL) { - ShowError("npcskill: level exceeded maximum of %d.\n", MAX_LEVEL); - return 1; - } - if (sd == NULL || nd == NULL) { //ain't possible, but I don't trust people. - return 1; - } - - nd->level = npc_level; - nd->stat_point = stat_point; - - if (!nd->status.hp) { - status_calc_npc(nd, true); - } else { - status_calc_npc(nd, false); - } - - if (skill_get_inf(skill_id)&INF_GROUND_SKILL) { - unit_skilluse_pos(&nd->bl, sd->bl.x, sd->bl.y, skill_id, skill_level); - } else { - unit_skilluse_id(&nd->bl, sd->bl.id, skill_id, skill_level); - } - - return 0; -} - -// declarations that were supposed to be exported from npc_chat.c -#ifdef PCRE_SUPPORT -BUILDIN_FUNC(defpattern); -BUILDIN_FUNC(activatepset); -BUILDIN_FUNC(deactivatepset); -BUILDIN_FUNC(deletepset); -#endif - -/// script command definitions -/// for an explanation on args, see add_buildin_func -struct script_function buildin_func[] = { - // NPC interaction - BUILDIN_DEF(mes,"s*"), - BUILDIN_DEF(next,""), - BUILDIN_DEF(close,""), - BUILDIN_DEF(close2,""), - BUILDIN_DEF(menu,"sl*"), - BUILDIN_DEF(select,"s*"), //for future jA script compatibility - BUILDIN_DEF(prompt,"s*"), - // - BUILDIN_DEF(goto,"l"), - BUILDIN_DEF(callsub,"l*"), - BUILDIN_DEF(callfunc,"s*"), - BUILDIN_DEF(return,"?"), - BUILDIN_DEF(getarg,"i?"), - BUILDIN_DEF(jobchange,"i?"), - BUILDIN_DEF(jobname,"i"), - BUILDIN_DEF(input,"r??"), - BUILDIN_DEF(warp,"sii"), - BUILDIN_DEF(areawarp,"siiiisii??"), - BUILDIN_DEF(warpchar,"siii"), // [LuzZza] - BUILDIN_DEF(warpparty,"siii?"), // [Fredzilla] [Paradox924X] - BUILDIN_DEF(warpguild,"siii"), // [Fredzilla] - BUILDIN_DEF(setlook,"ii"), - BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it - BUILDIN_DEF(set,"rv"), - BUILDIN_DEF(setarray,"rv*"), - BUILDIN_DEF(cleararray,"rvi"), - BUILDIN_DEF(copyarray,"rri"), - BUILDIN_DEF(getarraysize,"r"), - BUILDIN_DEF(deletearray,"r?"), - BUILDIN_DEF(getelementofarray,"ri"), - BUILDIN_DEF(getitem,"vi?"), - BUILDIN_DEF(rentitem,"vi"), - BUILDIN_DEF(getitem2,"viiiiiiii?"), - BUILDIN_DEF(getnameditem,"vv"), - BUILDIN_DEF2(grouprandomitem,"groupranditem","i"), - BUILDIN_DEF(makeitem,"visii"), - BUILDIN_DEF(delitem,"vi?"), - BUILDIN_DEF(delitem2,"viiiiiiii?"), - BUILDIN_DEF2(enableitemuse,"enable_items",""), - BUILDIN_DEF2(disableitemuse,"disable_items",""), - BUILDIN_DEF(cutin,"si"), - BUILDIN_DEF(viewpoint,"iiiii"), - BUILDIN_DEF(heal,"ii"), - BUILDIN_DEF(itemheal,"ii"), - BUILDIN_DEF(percentheal,"ii"), - BUILDIN_DEF(rand,"i?"), - BUILDIN_DEF(countitem,"v"), - BUILDIN_DEF(countitem2,"viiiiiii"), - BUILDIN_DEF(checkweight,"vi*"), - BUILDIN_DEF(checkweight2,"rr"), - BUILDIN_DEF(readparam,"i?"), - BUILDIN_DEF(getcharid,"i?"), - BUILDIN_DEF(getnpcid,"i?"), - BUILDIN_DEF(getpartyname,"i"), - BUILDIN_DEF(getpartymember,"i?"), - BUILDIN_DEF(getpartyleader,"i?"), - BUILDIN_DEF(getguildname,"i"), - BUILDIN_DEF(getguildmaster,"i"), - BUILDIN_DEF(getguildmasterid,"i"), - BUILDIN_DEF(strcharinfo,"i"), - BUILDIN_DEF(strnpcinfo,"i"), - BUILDIN_DEF(getequipid,"i"), - BUILDIN_DEF(getequipname,"i"), - BUILDIN_DEF(getbrokenid,"i"), // [Valaris] - BUILDIN_DEF(repair,"i"), // [Valaris] - BUILDIN_DEF(repairall,""), - BUILDIN_DEF(getequipisequiped,"i"), - BUILDIN_DEF(getequipisenableref,"i"), - BUILDIN_DEF(getequipisidentify,"i"), - BUILDIN_DEF(getequiprefinerycnt,"i"), - BUILDIN_DEF(getequipweaponlv,"i"), - BUILDIN_DEF(getequippercentrefinery,"i"), - BUILDIN_DEF(successrefitem,"i"), - BUILDIN_DEF(failedrefitem,"i"), - BUILDIN_DEF(downrefitem,"i"), - BUILDIN_DEF(statusup,"i"), - BUILDIN_DEF(statusup2,"ii"), - BUILDIN_DEF(bonus,"iv"), - BUILDIN_DEF2(bonus,"bonus2","ivi"), - BUILDIN_DEF2(bonus,"bonus3","ivii"), - BUILDIN_DEF2(bonus,"bonus4","ivvii"), - BUILDIN_DEF2(bonus,"bonus5","ivviii"), - BUILDIN_DEF(autobonus,"sii??"), - BUILDIN_DEF(autobonus2,"sii??"), - BUILDIN_DEF(autobonus3,"siiv?"), - BUILDIN_DEF(skill,"vi?"), - BUILDIN_DEF(addtoskill,"vi?"), // [Valaris] - BUILDIN_DEF(guildskill,"vi"), - BUILDIN_DEF(getskilllv,"v"), - BUILDIN_DEF(getgdskilllv,"iv"), - BUILDIN_DEF(basicskillcheck,""), - BUILDIN_DEF(getgmlevel,""), - BUILDIN_DEF(getgroupid,""), - BUILDIN_DEF(end,""), - BUILDIN_DEF(checkoption,"i"), - BUILDIN_DEF(setoption,"i?"), - BUILDIN_DEF(setcart,"?"), - BUILDIN_DEF(checkcart,""), - BUILDIN_DEF(setfalcon,"?"), - BUILDIN_DEF(checkfalcon,""), - BUILDIN_DEF(setriding,"?"), - BUILDIN_DEF(checkriding,""), - BUILDIN_DEF(checkwug,""), - BUILDIN_DEF(checkmadogear,""), - BUILDIN_DEF(setmadogear,"?"), - BUILDIN_DEF2(savepoint,"save","sii"), - BUILDIN_DEF(savepoint,"sii"), - BUILDIN_DEF(gettimetick,"i"), - BUILDIN_DEF(gettime,"i"), - BUILDIN_DEF(gettimestr,"si"), - BUILDIN_DEF(openstorage,""), - BUILDIN_DEF(guildopenstorage,""), - BUILDIN_DEF(itemskill,"vi"), - BUILDIN_DEF(produce,"i"), - BUILDIN_DEF(cooking,"i"), - BUILDIN_DEF(monster,"siisii???"), - BUILDIN_DEF(getmobdrops,"i"), - BUILDIN_DEF(areamonster,"siiiisii???"), - BUILDIN_DEF(killmonster,"ss?"), - BUILDIN_DEF(killmonsterall,"s?"), - BUILDIN_DEF(clone,"siisi????"), - BUILDIN_DEF(doevent,"s"), - BUILDIN_DEF(donpcevent,"s"), - BUILDIN_DEF(cmdothernpc,"ss"), - BUILDIN_DEF(addtimer,"is"), - BUILDIN_DEF(deltimer,"s"), - BUILDIN_DEF(addtimercount,"si"), - BUILDIN_DEF(initnpctimer,"??"), - BUILDIN_DEF(stopnpctimer,"??"), - BUILDIN_DEF(startnpctimer,"??"), - BUILDIN_DEF(setnpctimer,"i?"), - BUILDIN_DEF(getnpctimer,"i?"), - BUILDIN_DEF(attachnpctimer,"?"), // attached the player id to the npc timer [Celest] - BUILDIN_DEF(detachnpctimer,"?"), // detached the player id from the npc timer [Celest] - BUILDIN_DEF(playerattached,""), // returns id of the current attached player. [Skotlex] - BUILDIN_DEF(announce,"si?????"), - BUILDIN_DEF(mapannounce,"ssi?????"), - BUILDIN_DEF(areaannounce,"siiiisi?????"), - BUILDIN_DEF(getusers,"i"), - BUILDIN_DEF(getmapguildusers,"si"), - BUILDIN_DEF(getmapusers,"s"), - BUILDIN_DEF(getareausers,"siiii"), - BUILDIN_DEF(getareadropitem,"siiiiv"), - BUILDIN_DEF(enablenpc,"s"), - BUILDIN_DEF(disablenpc,"s"), - BUILDIN_DEF(hideoffnpc,"s"), - BUILDIN_DEF(hideonnpc,"s"), - BUILDIN_DEF(sc_start,"iii?"), - BUILDIN_DEF(sc_start2,"iiii?"), - BUILDIN_DEF(sc_start4,"iiiiii?"), - BUILDIN_DEF(sc_end,"i?"), - BUILDIN_DEF(getstatus, "i?"), - BUILDIN_DEF(getscrate,"ii?"), - BUILDIN_DEF(debugmes,"s"), - BUILDIN_DEF2(catchpet,"pet","i"), - BUILDIN_DEF2(birthpet,"bpet",""), - BUILDIN_DEF(resetlvl,"i"), - BUILDIN_DEF(resetstatus,""), - BUILDIN_DEF(resetskill,""), - BUILDIN_DEF(skillpointcount,""), - BUILDIN_DEF(changebase,"i?"), - BUILDIN_DEF(changesex,""), - BUILDIN_DEF(waitingroom,"si?????"), - BUILDIN_DEF(delwaitingroom,"?"), - BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"), - BUILDIN_DEF(enablewaitingroomevent,"?"), - BUILDIN_DEF(disablewaitingroomevent,"?"), - BUILDIN_DEF2(enablewaitingroomevent,"enablearena",""), // Added by RoVeRT - BUILDIN_DEF2(disablewaitingroomevent,"disablearena",""), // Added by RoVeRT - BUILDIN_DEF(getwaitingroomstate,"i?"), - BUILDIN_DEF(warpwaitingpc,"sii?"), - BUILDIN_DEF(attachrid,"i"), - BUILDIN_DEF(detachrid,""), - BUILDIN_DEF(isloggedin,"i?"), - BUILDIN_DEF(setmapflagnosave,"ssii"), - BUILDIN_DEF(getmapflag,"si"), - BUILDIN_DEF(setmapflag,"si?"), - BUILDIN_DEF(removemapflag,"si?"), - BUILDIN_DEF(pvpon,"s"), - BUILDIN_DEF(pvpoff,"s"), - BUILDIN_DEF(gvgon,"s"), - BUILDIN_DEF(gvgoff,"s"), - BUILDIN_DEF(emotion,"i??"), - BUILDIN_DEF(maprespawnguildid,"sii"), - BUILDIN_DEF(agitstart,""), // <Agit> - BUILDIN_DEF(agitend,""), - BUILDIN_DEF(agitcheck,""), // <Agitcheck> - BUILDIN_DEF(flagemblem,"i"), // Flag Emblem - BUILDIN_DEF(getcastlename,"s"), - BUILDIN_DEF(getcastledata,"si"), - BUILDIN_DEF(setcastledata,"sii"), - BUILDIN_DEF(requestguildinfo,"i?"), - BUILDIN_DEF(getequipcardcnt,"i"), - BUILDIN_DEF(successremovecards,"i"), - BUILDIN_DEF(failedremovecards,"ii"), - BUILDIN_DEF(marriage,"s"), - BUILDIN_DEF2(wedding_effect,"wedding",""), - BUILDIN_DEF(divorce,""), - BUILDIN_DEF(ispartneron,""), - BUILDIN_DEF(getpartnerid,""), - BUILDIN_DEF(getchildid,""), - BUILDIN_DEF(getmotherid,""), - BUILDIN_DEF(getfatherid,""), - BUILDIN_DEF(warppartner,"sii"), - BUILDIN_DEF(getitemname,"v"), - BUILDIN_DEF(getitemslots,"i"), - BUILDIN_DEF(makepet,"i"), - BUILDIN_DEF(getexp,"ii"), - BUILDIN_DEF(getinventorylist,""), - BUILDIN_DEF(getskilllist,""), - BUILDIN_DEF(clearitem,""), - BUILDIN_DEF(classchange,"ii"), - BUILDIN_DEF(misceffect,"i"), - BUILDIN_DEF(playBGM,"s"), - BUILDIN_DEF(playBGMall,"s?????"), - BUILDIN_DEF(soundeffect,"si"), - BUILDIN_DEF(soundeffectall,"si?????"), // SoundEffectAll [Codemaster] - BUILDIN_DEF(strmobinfo,"ii"), // display mob data [Valaris] - BUILDIN_DEF(guardian,"siisi??"), // summon guardians - BUILDIN_DEF(guardianinfo,"sii"), // display guardian data [Valaris] - BUILDIN_DEF(petskillbonus,"iiii"), // [Valaris] - BUILDIN_DEF(petrecovery,"ii"), // [Valaris] - BUILDIN_DEF(petloot,"i"), // [Valaris] - BUILDIN_DEF(petheal,"iiii"), // [Valaris] - BUILDIN_DEF(petskillattack,"viii"), // [Skotlex] - BUILDIN_DEF(petskillattack2,"viiii"), // [Valaris] - BUILDIN_DEF(petskillsupport,"viiii"), // [Skotlex] - BUILDIN_DEF(skilleffect,"vi"), // skill effect [Celest] - BUILDIN_DEF(npcskilleffect,"viii"), // npc skill effect [Valaris] - BUILDIN_DEF(specialeffect,"i??"), // npc skill effect [Valaris] - BUILDIN_DEF(specialeffect2,"i??"), // skill effect on players[Valaris] - BUILDIN_DEF(nude,""), // nude command [Valaris] - BUILDIN_DEF(mapwarp,"ssii??"), // Added by RoVeRT - BUILDIN_DEF(atcommand,"s"), // [MouseJstr] - BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr] - BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr] - BUILDIN_DEF(message,"ss"), // [MouseJstr] - BUILDIN_DEF(npctalk,"s"), // [Valaris] - BUILDIN_DEF(mobcount,"ss"), - BUILDIN_DEF(getlook,"i"), - BUILDIN_DEF(getsavepoint,"i"), - BUILDIN_DEF(npcspeed,"i"), // [Valaris] - BUILDIN_DEF(npcwalkto,"ii"), // [Valaris] - BUILDIN_DEF(npcstop,""), // [Valaris] - BUILDIN_DEF(getmapxy,"rrri?"), //by Lorky [Lupus] - BUILDIN_DEF(checkoption1,"i"), - BUILDIN_DEF(checkoption2,"i"), - BUILDIN_DEF(guildgetexp,"i"), - BUILDIN_DEF(guildchangegm,"is"), - BUILDIN_DEF(logmes,"s"), //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus] - BUILDIN_DEF(summon,"si??"), // summons a slave monster [Celest] - BUILDIN_DEF(isnight,""), // check whether it is night time [Celest] - BUILDIN_DEF(isday,""), // check whether it is day time [Celest] - BUILDIN_DEF(isequipped,"i*"), // check whether another item/card has been equipped [Celest] - BUILDIN_DEF(isequippedcnt,"i*"), // check how many items/cards are being equipped [Celest] - BUILDIN_DEF(cardscnt,"i*"), // check how many items/cards are being equipped in the same arm [Lupus] - BUILDIN_DEF(getrefine,""), // returns the refined number of the current item, or an item with index specified [celest] - BUILDIN_DEF(night,""), // sets the server to night time - BUILDIN_DEF(day,""), // sets the server to day time -#ifdef PCRE_SUPPORT - BUILDIN_DEF(defpattern,"iss"), // Define pattern to listen for [MouseJstr] - BUILDIN_DEF(activatepset,"i"), // Activate a pattern set [MouseJstr] - BUILDIN_DEF(deactivatepset,"i"), // Deactive a pattern set [MouseJstr] - BUILDIN_DEF(deletepset,"i"), // Delete a pattern set [MouseJstr] -#endif - BUILDIN_DEF(dispbottom,"s"), //added from jA [Lupus] - BUILDIN_DEF(getusersname,""), - BUILDIN_DEF(recovery,""), - BUILDIN_DEF(getpetinfo,"i"), - BUILDIN_DEF(gethominfo,"i"), - BUILDIN_DEF(getmercinfo,"i?"), - BUILDIN_DEF(checkequipedcard,"i"), - BUILDIN_DEF(jump_zero,"il"), //for future jA script compatibility - BUILDIN_DEF(globalmes,"s?"), //end jA addition - 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 - BUILDIN_DEF(setiteminfo,"iii"), //[Lupus] set Items Buy / sell Price, etc info - BUILDIN_DEF(getequipcardid,"ii"), //[Lupus] returns CARD ID or other info from CARD slot N of equipped item - // [zBuffer] List of mathematics commands ---> - BUILDIN_DEF(sqrt,"i"), - BUILDIN_DEF(pow,"ii"), - BUILDIN_DEF(distance,"iiii"), - // <--- [zBuffer] List of mathematics commands - BUILDIN_DEF(md5,"s"), - // [zBuffer] List of dynamic var commands ---> - BUILDIN_DEF(getd,"s"), - BUILDIN_DEF(setd,"sv"), - // <--- [zBuffer] List of dynamic var commands - BUILDIN_DEF(petstat,"i"), - BUILDIN_DEF(callshop,"s?"), // [Skotlex] - BUILDIN_DEF(npcshopitem,"sii*"), // [Lance] - BUILDIN_DEF(npcshopadditem,"sii*"), - BUILDIN_DEF(npcshopdelitem,"si*"), - BUILDIN_DEF(npcshopattach,"s?"), - BUILDIN_DEF(equip,"i"), - BUILDIN_DEF(autoequip,"ii"), - BUILDIN_DEF(setbattleflag,"si"), - BUILDIN_DEF(getbattleflag,"s"), - BUILDIN_DEF(setitemscript,"is?"), //Set NEW item bonus script. Lupus - BUILDIN_DEF(disguise,"i"), //disguise player. Lupus - BUILDIN_DEF(undisguise,""), //undisguise player. Lupus - BUILDIN_DEF(getmonsterinfo,"ii"), //Lupus - BUILDIN_DEF(axtoi,"s"), - BUILDIN_DEF(query_sql,"s*"), - BUILDIN_DEF(query_logsql,"s*"), - BUILDIN_DEF(escape_sql,"v"), - BUILDIN_DEF(atoi,"s"), - // [zBuffer] List of player cont commands ---> - BUILDIN_DEF(rid2name,"i"), - BUILDIN_DEF(pcfollow,"ii"), - BUILDIN_DEF(pcstopfollow,"i"), - BUILDIN_DEF(pcblockmove,"ii"), - // <--- [zBuffer] List of player cont commands - // [zBuffer] List of mob control commands ---> - BUILDIN_DEF(unitwalk,"ii?"), - BUILDIN_DEF(unitkill,"i"), - BUILDIN_DEF(unitwarp,"isii"), - BUILDIN_DEF(unitattack,"iv?"), - BUILDIN_DEF(unitstop,"i"), - BUILDIN_DEF(unittalk,"is"), - BUILDIN_DEF(unitemote,"ii"), - BUILDIN_DEF(unitskilluseid,"ivi?"), // originally by Qamera [Celest] - BUILDIN_DEF(unitskillusepos,"iviii"), // [Celest] -// <--- [zBuffer] List of mob control commands - BUILDIN_DEF(sleep,"i"), - BUILDIN_DEF(sleep2,"i"), - BUILDIN_DEF(awake,"s"), - BUILDIN_DEF(getvariableofnpc,"rs"), - BUILDIN_DEF(warpportal,"iisii"), - BUILDIN_DEF2(homunculus_evolution,"homevolution",""), //[orn] - BUILDIN_DEF2(homunculus_mutate,"hommutate","?"), - BUILDIN_DEF2(homunculus_shuffle,"homshuffle",""), //[Zephyrus] - BUILDIN_DEF(eaclass,"?"), //[Skotlex] - BUILDIN_DEF(roclass,"i?"), //[Skotlex] - BUILDIN_DEF(checkvending,"?"), - BUILDIN_DEF(checkchatting,"?"), - BUILDIN_DEF(openmail,""), - BUILDIN_DEF(openauction,""), - BUILDIN_DEF(checkcell,"siii"), - BUILDIN_DEF(setcell,"siiiiii"), - BUILDIN_DEF(setwall,"siiiiis"), - BUILDIN_DEF(delwall,"s"), - BUILDIN_DEF(searchitem,"rs"), - BUILDIN_DEF(mercenary_create,"ii"), - BUILDIN_DEF(mercenary_heal,"ii"), - BUILDIN_DEF(mercenary_sc_start,"iii"), - BUILDIN_DEF(mercenary_get_calls,"i"), - BUILDIN_DEF(mercenary_get_faith,"i"), - BUILDIN_DEF(mercenary_set_calls,"ii"), - BUILDIN_DEF(mercenary_set_faith,"ii"), - BUILDIN_DEF(readbook,"ii"), - BUILDIN_DEF(setfont,"i"), - BUILDIN_DEF(areamobuseskill,"siiiiviiiii"), - BUILDIN_DEF(progressbar,"si"), - BUILDIN_DEF(pushpc,"ii"), - BUILDIN_DEF(buyingstore,"i"), - BUILDIN_DEF(searchstores,"ii"), - BUILDIN_DEF(showdigit,"i?"), - // WoE SE - BUILDIN_DEF(agitstart2,""), - BUILDIN_DEF(agitend2,""), - BUILDIN_DEF(agitcheck2,""), - // BattleGround - BUILDIN_DEF(waitingroom2bg,"siiss?"), - BUILDIN_DEF(waitingroom2bg_single,"isiis"), - BUILDIN_DEF(bg_team_setxy,"iii"), - BUILDIN_DEF(bg_warp,"isii"), - BUILDIN_DEF(bg_monster,"isiisi?"), - BUILDIN_DEF(bg_monster_set_team,"ii"), - BUILDIN_DEF(bg_leave,""), - BUILDIN_DEF(bg_destroy,"i"), - BUILDIN_DEF(areapercentheal,"siiiiii"), - BUILDIN_DEF(bg_get_data,"ii"), - BUILDIN_DEF(bg_getareausers,"isiiii"), - BUILDIN_DEF(bg_updatescore,"sii"), - - // Instancing - BUILDIN_DEF(instance_create,"si"), - BUILDIN_DEF(instance_destroy,"?"), - BUILDIN_DEF(instance_attachmap,"si?"), - BUILDIN_DEF(instance_detachmap,"s?"), - BUILDIN_DEF(instance_attach,"i"), - BUILDIN_DEF(instance_id,"?"), - BUILDIN_DEF(instance_set_timeout,"ii?"), - BUILDIN_DEF(instance_init,"i"), - BUILDIN_DEF(instance_announce,"isi?????"), - BUILDIN_DEF(instance_npcname,"s?"), - BUILDIN_DEF(has_instance,"s?"), - BUILDIN_DEF(instance_warpall,"sii?"), - BUILDIN_DEF(instance_check_party,"i???"), - /** - * 3rd-related - **/ - BUILDIN_DEF(makerune,"i"), - BUILDIN_DEF(checkdragon,""),//[Ind] - BUILDIN_DEF(setdragon,"?"),//[Ind] - BUILDIN_DEF(ismounting,""),//[Ind] - BUILDIN_DEF(setmounting,""),//[Ind] - BUILDIN_DEF(checkre,"i"), - /** - * rAthena and beyond! - **/ - BUILDIN_DEF(getargcount,""), - BUILDIN_DEF(getcharip,"?"), - BUILDIN_DEF(is_function,"s"), - BUILDIN_DEF(get_revision,""), - BUILDIN_DEF(freeloop,"i"), - BUILDIN_DEF(getrandgroupitem,"ii"), - BUILDIN_DEF(cleanmap,"s"), - BUILDIN_DEF2(cleanmap,"cleanarea","siiii"), - BUILDIN_DEF(npcskill,"viii"), - /** - * @commands (script based) - **/ - BUILDIN_DEF(bindatcmd, "ss??"), - BUILDIN_DEF(unbindatcmd, "s"), - BUILDIN_DEF(useatcmd, "s"), - - //Quest Log System [Inkfish] - BUILDIN_DEF(setquest, "i"), - BUILDIN_DEF(erasequest, "i"), - BUILDIN_DEF(completequest, "i"), - BUILDIN_DEF(checkquest, "i?"), - BUILDIN_DEF(changequest, "ii"), - BUILDIN_DEF(showevent, "ii"), - {NULL,NULL,NULL}, -}; +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+//#define DEBUG_DISP
+//#define DEBUG_DISASM
+//#define DEBUG_RUN
+//#define DEBUG_HASH
+//#define DEBUG_DUMP_STACK
+
+#include "../common/cbasetypes.h"
+#include "../common/malloc.h"
+#include "../common/md5calc.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/socket.h" // usage: getcharip
+#include "../common/strlib.h"
+#include "../common/timer.h"
+#include "../common/utils.h"
+
+#include "map.h"
+#include "path.h"
+#include "clif.h"
+#include "chrif.h"
+#include "itemdb.h"
+#include "pc.h"
+#include "status.h"
+#include "storage.h"
+#include "mob.h"
+#include "npc.h"
+#include "pet.h"
+#include "mapreg.h"
+#include "homunculus.h"
+#include "instance.h"
+#include "mercenary.h"
+#include "intif.h"
+#include "skill.h"
+#include "status.h"
+#include "chat.h"
+#include "battle.h"
+#include "battleground.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "log.h"
+#include "unit.h"
+#include "pet.h"
+#include "mail.h"
+#include "script.h"
+#include "quest.h"
+#include "elemental.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#ifndef WIN32
+ #include <sys/time.h>
+#endif
+#include <time.h>
+#include <setjmp.h>
+#include <errno.h>
+
+#ifdef BETA_THREAD_TEST
+ #include "../common/atomic.h"
+ #include "../common/spinlock.h"
+ #include "../common/thread.h"
+ #include "../common/mutex.h"
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+//## TODO possible enhancements: [FlavioJS]
+// - 'callfunc' supporting labels in the current npc "::LabelName"
+// - 'callfunc' supporting labels in other npcs "NpcName::LabelName"
+// - 'function FuncName;' function declarations reverting to global functions
+// if local label isn't found
+// - join callfunc and callsub's functionality
+// - remove dynamic allocation in add_word()
+// - remove GETVALUE / SETVALUE
+// - clean up the set_reg / set_val / setd_sub mess
+// - detect invalid label references at parse-time
+
+//
+// struct script_state* st;
+//
+
+/// Returns the script_data at the target index
+#define script_getdata(st,i) ( &((st)->stack->stack_data[(st)->start + (i)]) )
+/// Returns if the stack contains data at the target index
+#define script_hasdata(st,i) ( (st)->end > (st)->start + (i) )
+/// Returns the index of the last data in the stack
+#define script_lastdata(st) ( (st)->end - (st)->start - 1 )
+/// Pushes an int into the stack
+#define script_pushint(st,val) push_val((st)->stack, C_INT, (val))
+/// Pushes a string into the stack (script engine frees it automatically)
+#define script_pushstr(st,val) push_str((st)->stack, C_STR, (val))
+/// Pushes a copy of a string into the stack
+#define script_pushstrcopy(st,val) push_str((st)->stack, C_STR, aStrdup(val))
+/// Pushes a constant string into the stack (must never change or be freed)
+#define script_pushconststr(st,val) push_str((st)->stack, C_CONSTSTR, (val))
+/// Pushes a nil into the stack
+#define script_pushnil(st) push_val((st)->stack, C_NOP, 0)
+/// Pushes a copy of the data in the target index
+#define script_pushcopy(st,i) push_copy((st)->stack, (st)->start + (i))
+
+#define script_isstring(st,i) data_isstring(script_getdata(st,i))
+#define script_isint(st,i) data_isint(script_getdata(st,i))
+
+#define script_getnum(st,val) conv_num(st, script_getdata(st,val))
+#define script_getstr(st,val) conv_str(st, script_getdata(st,val))
+#define script_getref(st,val) ( script_getdata(st,val)->ref )
+
+// Note: "top" functions/defines use indexes relative to the top of the stack
+// -1 is the index of the data at the top
+
+/// Returns the script_data at the target index relative to the top of the stack
+#define script_getdatatop(st,i) ( &((st)->stack->stack_data[(st)->stack->sp + (i)]) )
+/// Pushes a copy of the data in the target index relative to the top of the stack
+#define script_pushcopytop(st,i) push_copy((st)->stack, (st)->stack->sp + (i))
+/// Removes the range of values [start,end[ relative to the top of the stack
+#define script_removetop(st,start,end) ( pop_stack((st), ((st)->stack->sp + (start)), (st)->stack->sp + (end)) )
+
+//
+// struct script_data* data;
+//
+
+/// Returns if the script data is a string
+#define data_isstring(data) ( (data)->type == C_STR || (data)->type == C_CONSTSTR )
+/// Returns if the script data is an int
+#define data_isint(data) ( (data)->type == C_INT )
+/// Returns if the script data is a reference
+#define data_isreference(data) ( (data)->type == C_NAME )
+/// Returns if the script data is a label
+#define data_islabel(data) ( (data)->type == C_POS )
+/// Returns if the script data is an internal script function label
+#define data_isfunclabel(data) ( (data)->type == C_USERFUNC_POS )
+
+/// Returns if this is a reference to a constant
+#define reference_toconstant(data) ( str_data[reference_getid(data)].type == C_INT )
+/// Returns if this a reference to a param
+#define reference_toparam(data) ( str_data[reference_getid(data)].type == C_PARAM )
+/// Returns if this a reference to a variable
+//##TODO confirm it's C_NAME [FlavioJS]
+#define reference_tovariable(data) ( str_data[reference_getid(data)].type == C_NAME )
+/// Returns the unique id of the reference (id and index)
+#define reference_getuid(data) ( (data)->u.num )
+/// Returns the id of the reference
+#define reference_getid(data) ( (int32)(reference_getuid(data) & 0x00ffffff) )
+/// Returns the array index of the reference
+#define reference_getindex(data) ( (int32)(((uint32)(reference_getuid(data) & 0xff000000)) >> 24) )
+/// Returns the name of the reference
+#define reference_getname(data) ( str_buf + str_data[reference_getid(data)].str )
+/// Returns the linked list of uid-value pairs of the reference (can be NULL)
+#define reference_getref(data) ( (data)->ref )
+/// Returns the value of the constant
+#define reference_getconstant(data) ( str_data[reference_getid(data)].val )
+/// Returns the type of param
+#define reference_getparamtype(data) ( str_data[reference_getid(data)].val )
+
+/// Composes the uid of a reference from the id and the index
+#define reference_uid(id,idx) ( (int32)((((uint32)(id)) & 0x00ffffff) | (((uint32)(idx)) << 24)) )
+
+#define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.' && (prefix) != '\'')
+#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' && (prefix) != '\'' )
+#define is_string_variable(name) ( (name)[strlen(name) - 1] == '$' )
+
+#define FETCH(n, t) \
+ if( script_hasdata(st,n) ) \
+ (t)=script_getnum(st,n);
+
+/// Maximum amount of elements in script arrays
+#define SCRIPT_MAX_ARRAYSIZE 128
+
+#define SCRIPT_BLOCK_SIZE 512
+enum { LABEL_NEXTLINE=1,LABEL_START };
+
+/// temporary buffer for passing around compiled bytecode
+/// @see add_scriptb, set_label, parse_script
+static unsigned char* script_buf = NULL;
+static int script_pos = 0, script_size = 0;
+
+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
+static struct str_data_struct {
+ enum c_op type;
+ int str;
+ int backpatch;
+ int label;
+ int (*func)(struct script_state *st);
+ int val;
+ int next;
+} *str_data = NULL;
+static int str_data_size = 0; // size of the data
+static int str_num = LABEL_START; // next id to be assigned
+
+// str_buf holds the strings themselves
+static char *str_buf;
+static int str_size = 0; // size of the buffer
+static int str_pos = 0; // next position to be assigned
+
+
+// Using a prime number for SCRIPT_HASH_SIZE should give better distributions
+#define SCRIPT_HASH_SIZE 1021
+int str_hash[SCRIPT_HASH_SIZE];
+// Specifies which string hashing method to use
+//#define SCRIPT_HASH_DJB2
+//#define SCRIPT_HASH_SDBM
+#define SCRIPT_HASH_ELF
+
+static DBMap* scriptlabel_db=NULL; // const char* label_name -> int script_pos
+static DBMap* userfunc_db=NULL; // const char* func_name -> struct script_code*
+static int parse_options=0;
+DBMap* script_get_label_db(void){ return scriptlabel_db; }
+DBMap* script_get_userfunc_db(void){ return userfunc_db; }
+
+// important buildin function references for usage in scripts
+static int buildin_set_ref = 0;
+static int buildin_callsub_ref = 0;
+static int buildin_callfunc_ref = 0;
+static int buildin_getelementofarray_ref = 0;
+
+// Caches compiled autoscript item code.
+// Note: This is not cleared when reloading itemdb.
+static DBMap* autobonus_db=NULL; // char* script -> char* bytecode
+
+struct Script_Config script_config = {
+ 1, // warn_func_mismatch_argtypes
+ 1, 65535, 2048, //warn_func_mismatch_paramnum/check_cmdcount/check_gotocount
+ 0, INT_MAX, // input_min_value/input_max_value
+ "OnPCDieEvent", //die_event_name
+ "OnPCKillEvent", //kill_pc_event_name
+ "OnNPCKillEvent", //kill_mob_event_name
+ "OnPCLoginEvent", //login_event_name
+ "OnPCLogoutEvent", //logout_event_name
+ "OnPCLoadMapEvent", //loadmap_event_name
+ "OnPCBaseLvUpEvent", //baselvup_event_name
+ "OnPCJobLvUpEvent", //joblvup_event_name
+ "OnTouch_", //ontouch_name (runs on first visible char to enter area, picks another char if the first char leaves)
+ "OnTouch", //ontouch2_name (run whenever a char walks into the OnTouch area)
+};
+
+static jmp_buf error_jump;
+static char* error_msg;
+static const char* error_pos;
+static int error_report; // if the error should produce output
+
+// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc )
+// [Eoe / jA 1080, 1081, 1094, 1164]
+enum curly_type {
+ TYPE_NULL = 0,
+ TYPE_IF,
+ TYPE_SWITCH,
+ TYPE_WHILE,
+ TYPE_FOR,
+ TYPE_DO,
+ TYPE_USERFUNC,
+ TYPE_ARGLIST // function argument list
+};
+
+enum e_arglist
+{
+ ARGLIST_UNDEFINED = 0,
+ ARGLIST_NO_PAREN = 1,
+ ARGLIST_PAREN = 2,
+};
+
+static struct {
+ struct {
+ enum curly_type type;
+ int index;
+ int count;
+ int flag;
+ struct linkdb_node *case_label;
+ } curly[256]; // Information right parenthesis
+ int curly_count; // The number of right brackets
+ int index; // Number of the syntax used in the script
+} syntax;
+
+const char* parse_curly_close(const char* p);
+const char* parse_syntax_close(const char* p);
+const char* parse_syntax_close_sub(const char* p,int* flag);
+const char* parse_syntax(const char* p);
+static int parse_syntax_for_flag = 0;
+
+extern int current_equip_item_index; //for New CARDS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus]
+int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
+int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0;
+int potion_target=0;
+
+
+c_op get_com(unsigned char *script,int *pos);
+int get_num(unsigned char *script,int *pos);
+
+typedef struct script_function {
+ int (*func)(struct script_state *st);
+ const char *name;
+ const char *arg;
+} script_function;
+
+extern script_function buildin_func[];
+
+static struct linkdb_node* sleep_db;// int oid -> struct script_state*
+
+#ifdef BETA_THREAD_TEST
+/**
+ * MySQL Query Slave
+ **/
+static SPIN_LOCK queryThreadLock;
+static rAthread queryThread = NULL;
+static ramutex queryThreadMutex = NULL;
+static racond queryThreadCond = NULL;
+static volatile int32 queryThreadTerminate = 0;
+
+struct queryThreadEntry {
+ bool ok;
+ bool type; /* main db or log db? */
+ struct script_state *st;
+};
+
+/* Ladies and Gentleman the Manager! */
+struct {
+ struct queryThreadEntry **entry;/* array of structs */
+ int count;
+ int timer;/* used to receive processed entries */
+} queryThreadData;
+#endif
+
+/*==========================================
+ * (Only those needed) local declaration prototype
+ *------------------------------------------*/
+const char* parse_subexpr(const char* p,int limit);
+int run_func(struct script_state *st);
+
+enum {
+ MF_NOMEMO, //0
+ MF_NOTELEPORT,
+ MF_NOSAVE,
+ MF_NOBRANCH,
+ MF_NOPENALTY,
+ MF_NOZENYPENALTY,
+ MF_PVP,
+ MF_PVP_NOPARTY,
+ MF_PVP_NOGUILD,
+ MF_GVG,
+ MF_GVG_NOPARTY, //10
+ MF_NOTRADE,
+ MF_NOSKILL,
+ MF_NOWARP,
+ MF_PARTYLOCK,
+ MF_NOICEWALL,
+ MF_SNOW,
+ MF_FOG,
+ MF_SAKURA,
+ MF_LEAVES,
+ /**
+ * No longer available, keeping here just in case it's back someday. [Ind]
+ **/
+ //MF_RAIN, //20
+ // 21 free
+ MF_NOGO = 22,
+ MF_CLOUDS,
+ MF_CLOUDS2,
+ MF_FIREWORKS,
+ MF_GVG_CASTLE,
+ MF_GVG_DUNGEON,
+ MF_NIGHTENABLED,
+ MF_NOBASEEXP,
+ MF_NOJOBEXP, //30
+ MF_NOMOBLOOT,
+ MF_NOMVPLOOT,
+ MF_NORETURN,
+ MF_NOWARPTO,
+ MF_NIGHTMAREDROP,
+ MF_RESTRICTED,
+ MF_NOCOMMAND,
+ MF_NODROP,
+ MF_JEXP,
+ MF_BEXP, //40
+ MF_NOVENDING,
+ MF_LOADEVENT,
+ MF_NOCHAT,
+ MF_NOEXPPENALTY,
+ MF_GUILDLOCK,
+ MF_TOWN,
+ MF_AUTOTRADE,
+ MF_ALLOWKS,
+ MF_MONSTER_NOTELEPORT,
+ MF_PVP_NOCALCRANK, //50
+ MF_BATTLEGROUND,
+ MF_RESET
+};
+
+const char* script_op2name(int op)
+{
+#define RETURN_OP_NAME(type) case type: return #type
+ switch( op )
+ {
+ RETURN_OP_NAME(C_NOP);
+ RETURN_OP_NAME(C_POS);
+ RETURN_OP_NAME(C_INT);
+ RETURN_OP_NAME(C_PARAM);
+ RETURN_OP_NAME(C_FUNC);
+ RETURN_OP_NAME(C_STR);
+ RETURN_OP_NAME(C_CONSTSTR);
+ RETURN_OP_NAME(C_ARG);
+ RETURN_OP_NAME(C_NAME);
+ RETURN_OP_NAME(C_EOL);
+ RETURN_OP_NAME(C_RETINFO);
+ RETURN_OP_NAME(C_USERFUNC);
+ RETURN_OP_NAME(C_USERFUNC_POS);
+
+ // operators
+ RETURN_OP_NAME(C_OP3);
+ RETURN_OP_NAME(C_LOR);
+ RETURN_OP_NAME(C_LAND);
+ RETURN_OP_NAME(C_LE);
+ RETURN_OP_NAME(C_LT);
+ RETURN_OP_NAME(C_GE);
+ RETURN_OP_NAME(C_GT);
+ RETURN_OP_NAME(C_EQ);
+ RETURN_OP_NAME(C_NE);
+ RETURN_OP_NAME(C_XOR);
+ RETURN_OP_NAME(C_OR);
+ RETURN_OP_NAME(C_AND);
+ RETURN_OP_NAME(C_ADD);
+ RETURN_OP_NAME(C_SUB);
+ RETURN_OP_NAME(C_MUL);
+ RETURN_OP_NAME(C_DIV);
+ RETURN_OP_NAME(C_MOD);
+ RETURN_OP_NAME(C_NEG);
+ RETURN_OP_NAME(C_LNOT);
+ RETURN_OP_NAME(C_NOT);
+ RETURN_OP_NAME(C_R_SHIFT);
+ RETURN_OP_NAME(C_L_SHIFT);
+
+ default:
+ ShowDebug("script_op2name: unexpected op=%d\n", op);
+ return "???";
+ }
+#undef RETURN_OP_NAME
+}
+
+#ifdef DEBUG_DUMP_STACK
+static void script_dump_stack(struct script_state* st)
+{
+ int i;
+ ShowMessage("\tstart = %d\n", st->start);
+ ShowMessage("\tend = %d\n", st->end);
+ ShowMessage("\tdefsp = %d\n", st->stack->defsp);
+ ShowMessage("\tsp = %d\n", st->stack->sp);
+ for( i = 0; i < st->stack->sp; ++i )
+ {
+ struct script_data* data = &st->stack->stack_data[i];
+ ShowMessage("\t[%d] %s", i, script_op2name(data->type));
+ switch( data->type )
+ {
+ case C_INT:
+ case C_POS:
+ ShowMessage(" %d\n", data->u.num);
+ break;
+
+ case C_STR:
+ case C_CONSTSTR:
+ ShowMessage(" \"%s\"\n", data->u.str);
+ break;
+
+ case C_NAME:
+ ShowMessage(" \"%s\" (id=%d ref=%p subtype=%s)\n", reference_getname(data), data->u.num, data->ref, script_op2name(str_data[data->u.num].type));
+ break;
+
+ case C_RETINFO:
+ {
+ struct script_retinfo* ri = data->u.ri;
+ ShowMessage(" %p {var_function=%p, script=%p, pos=%d, nargs=%d, defsp=%d}\n", ri, ri->var_function, ri->script, ri->pos, ri->nargs, ri->defsp);
+ }
+ break;
+ default:
+ ShowMessage("\n");
+ break;
+ }
+ }
+}
+#endif
+
+/// Reports on the console the src of a script error.
+static void script_reportsrc(struct script_state *st)
+{
+ struct block_list* bl;
+
+ if( st->oid == 0 )
+ return; //Can't report source.
+
+ bl = map_id2bl(st->oid);
+ if( bl == NULL )
+ return;
+
+ switch( bl->type )
+ {
+ case BL_NPC:
+ if( bl->m >= 0 )
+ ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y);
+ else
+ ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name);
+ break;
+ default:
+ if( bl->m >= 0 )
+ ShowDebug("Source (Non-NPC type %d): name %s at %s (%d,%d)\n", bl->type, status_get_name(bl), map[bl->m].name, bl->x, bl->y);
+ else
+ ShowDebug("Source (Non-NPC type %d): name %s (invisible/not on a map)\n", bl->type, status_get_name(bl));
+ break;
+ }
+}
+
+/// Reports on the console information about the script data.
+static void script_reportdata(struct script_data* data)
+{
+ if( data == NULL )
+ return;
+ switch( data->type )
+ {
+ case C_NOP:// no value
+ ShowDebug("Data: nothing (nil)\n");
+ break;
+ case C_INT:// number
+ ShowDebug("Data: number value=%d\n", data->u.num);
+ break;
+ case C_STR:
+ case C_CONSTSTR:// string
+ if( data->u.str )
+ {
+ ShowDebug("Data: string value=\"%s\"\n", data->u.str);
+ }
+ else
+ {
+ ShowDebug("Data: string value=NULL\n");
+ }
+ break;
+ case C_NAME:// reference
+ if( reference_tovariable(data) )
+ {// variable
+ const char* name = reference_getname(data);
+ if( not_array_variable(*name) )
+ ShowDebug("Data: variable name='%s'\n", name);
+ else
+ ShowDebug("Data: variable name='%s' index=%d\n", name, reference_getindex(data));
+ }
+ else if( reference_toconstant(data) )
+ {// constant
+ ShowDebug("Data: constant name='%s' value=%d\n", reference_getname(data), reference_getconstant(data));
+ }
+ else if( reference_toparam(data) )
+ {// param
+ ShowDebug("Data: param name='%s' type=%d\n", reference_getname(data), reference_getparamtype(data));
+ }
+ else
+ {// ???
+ ShowDebug("Data: reference name='%s' type=%s\n", reference_getname(data), script_op2name(data->type));
+ ShowDebug("Please report this!!! - str_data.type=%s\n", script_op2name(str_data[reference_getid(data)].type));
+ }
+ break;
+ case C_POS:// label
+ ShowDebug("Data: label pos=%d\n", data->u.num);
+ break;
+ default:
+ ShowDebug("Data: %s\n", script_op2name(data->type));
+ break;
+ }
+}
+
+
+/// Reports on the console information about the current built-in function.
+static void script_reportfunc(struct script_state* st)
+{
+ int i, params, id;
+ struct script_data* data;
+
+ if( !script_hasdata(st,0) )
+ {// no stack
+ return;
+ }
+
+ data = script_getdata(st,0);
+
+ if( !data_isreference(data) || str_data[reference_getid(data)].type != C_FUNC )
+ {// script currently not executing a built-in function or corrupt stack
+ return;
+ }
+
+ id = reference_getid(data);
+ params = script_lastdata(st)-1;
+
+ if( params > 0 )
+ {
+ ShowDebug("Function: %s (%d parameter%s):\n", get_str(id), params, ( params == 1 ) ? "" : "s");
+
+ for( i = 2; i <= script_lastdata(st); i++ )
+ {
+ script_reportdata(script_getdata(st,i));
+ }
+ }
+ else
+ {
+ ShowDebug("Function: %s (no parameters)\n", get_str(id));
+ }
+}
+
+
+/*==========================================
+ * Output error message
+ *------------------------------------------*/
+static void disp_error_message2(const char *mes,const char *pos,int report)
+{
+ error_msg = aStrdup(mes);
+ error_pos = pos;
+ error_report = report;
+ longjmp( error_jump, 1 );
+}
+#define disp_error_message(mes,pos) disp_error_message2(mes,pos,1)
+
+/// Checks event parameter validity
+static void check_event(struct script_state *st, const char *evt)
+{
+ if( evt && evt[0] && !stristr(evt, "::On") )
+ {
+ ShowWarning("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n", evt);
+ script_reportsrc(st);
+ }
+}
+
+/*==========================================
+ * Hashes the input string
+ *------------------------------------------*/
+static unsigned int calc_hash(const char* p)
+{
+ unsigned int h;
+
+#if defined(SCRIPT_HASH_DJB2)
+ h = 5381;
+ while( *p ) // hash*33 + c
+ h = ( h << 5 ) + h + ((unsigned char)TOLOWER(*p++));
+#elif defined(SCRIPT_HASH_SDBM)
+ h = 0;
+ while( *p ) // hash*65599 + c
+ h = ( h << 6 ) + ( h << 16 ) - h + ((unsigned char)TOLOWER(*p++));
+#elif defined(SCRIPT_HASH_ELF) // UNIX ELF hash
+ h = 0;
+ while( *p ){
+ unsigned int g;
+ h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++));
+ g = h & 0xF0000000;
+ if( g )
+ {
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ }
+#else // athena hash
+ h = 0;
+ while( *p )
+ h = ( h << 1 ) + ( h >> 3 ) + ( h >> 5 ) + ( h >> 8 ) + (unsigned char)TOLOWER(*p++);
+#endif
+
+ return h % SCRIPT_HASH_SIZE;
+}
+
+
+/*==========================================
+ * str_data manipulation functions
+ *------------------------------------------*/
+
+/// Looks up string using the provided id.
+const char* get_str(int id)
+{
+ Assert( id >= LABEL_START && id < str_size );
+ return str_buf+str_data[id].str;
+}
+
+/// Returns the uid of the string, or -1.
+static int search_str(const char* p)
+{
+ int i;
+
+ for( i = str_hash[calc_hash(p)]; i != 0; i = str_data[i].next )
+ if( strcasecmp(get_str(i),p) == 0 )
+ return i;
+
+ return -1;
+}
+
+/// Stores a copy of the string and returns its id.
+/// If an identical string is already present, returns its id instead.
+int add_str(const char* p)
+{
+ int i, h;
+ int len;
+
+ h = calc_hash(p);
+
+ if( str_hash[h] == 0 )
+ {// empty bucket, add new node here
+ str_hash[h] = str_num;
+ }
+ else
+ {// scan for end of list, or occurence of identical string
+ for( i = str_hash[h]; ; i = str_data[i].next )
+ {
+ if( strcasecmp(get_str(i),p) == 0 )
+ return i; // string already in list
+ if( str_data[i].next == 0 )
+ break; // reached the end
+ }
+
+ // append node to end of list
+ str_data[i].next = str_num;
+ }
+
+ // grow list if neccessary
+ if( str_num >= str_data_size )
+ {
+ str_data_size += 128;
+ RECREATE(str_data,struct str_data_struct,str_data_size);
+ memset(str_data + (str_data_size - 128), '\0', 128);
+ }
+
+ len=(int)strlen(p);
+
+ // grow string buffer if neccessary
+ while( str_pos+len+1 >= str_size )
+ {
+ str_size += 256;
+ RECREATE(str_buf,char,str_size);
+ memset(str_buf + (str_size - 256), '\0', 256);
+ }
+
+ safestrncpy(str_buf+str_pos, p, len+1);
+ str_data[str_num].type = C_NOP;
+ str_data[str_num].str = str_pos;
+ str_data[str_num].next = 0;
+ str_data[str_num].func = NULL;
+ str_data[str_num].backpatch = -1;
+ str_data[str_num].label = -1;
+ str_pos += len+1;
+
+ return str_num++;
+}
+
+
+/// Appends 1 byte to the script buffer.
+static void add_scriptb(int a)
+{
+ if( script_pos+1 >= script_size )
+ {
+ script_size += SCRIPT_BLOCK_SIZE;
+ RECREATE(script_buf,unsigned char,script_size);
+ }
+ script_buf[script_pos++] = (uint8)(a);
+}
+
+/// Appends a c_op value to the script buffer.
+/// The value is variable-length encoded into 8-bit blocks.
+/// The encoding scheme is ( 01?????? )* 00??????, LSB first.
+/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries).
+static void add_scriptc(int a)
+{
+ while( a >= 0x40 )
+ {
+ add_scriptb((a&0x3f)|0x40);
+ a = (a - 0x40) >> 6;
+ }
+
+ add_scriptb(a);
+}
+
+/// Appends an integer value to the script buffer.
+/// The value is variable-length encoded into 8-bit blocks.
+/// The encoding scheme is ( 11?????? )* 10??????, LSB first.
+/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries).
+static void add_scripti(int a)
+{
+ while( a >= 0x40 )
+ {
+ add_scriptb((a&0x3f)|0xc0);
+ a = (a - 0x40) >> 6;
+ }
+ add_scriptb(a|0x80);
+}
+
+/// Appends a str_data object (label/function/variable/integer) to the script buffer.
+
+///
+/// @param l The id of the str_data entry
+// Maximum up to 16M
+static void add_scriptl(int l)
+{
+ int backpatch = str_data[l].backpatch;
+
+ switch(str_data[l].type){
+ case C_POS:
+ case C_USERFUNC_POS:
+ add_scriptc(C_POS);
+ add_scriptb(str_data[l].label);
+ add_scriptb(str_data[l].label>>8);
+ add_scriptb(str_data[l].label>>16);
+ break;
+ case C_NOP:
+ case C_USERFUNC:
+ // Embedded data backpatch there is a possibility of label
+ add_scriptc(C_NAME);
+ str_data[l].backpatch = script_pos;
+ add_scriptb(backpatch);
+ add_scriptb(backpatch>>8);
+ add_scriptb(backpatch>>16);
+ break;
+ case C_INT:
+ add_scripti(abs(str_data[l].val));
+ if( str_data[l].val < 0 ) //Notice that this is negative, from jA (Rayce)
+ add_scriptc(C_NEG);
+ break;
+ default: // assume C_NAME
+ add_scriptc(C_NAME);
+ add_scriptb(l);
+ add_scriptb(l>>8);
+ add_scriptb(l>>16);
+ break;
+ }
+}
+
+/*==========================================
+ * Resolve the label
+ *------------------------------------------*/
+void set_label(int l,int pos, const char* script_pos)
+{
+ int i,next;
+
+ if(str_data[l].type==C_INT || str_data[l].type==C_PARAM || str_data[l].type==C_FUNC)
+ { //Prevent overwriting constants values, parameters and built-in functions [Skotlex]
+ disp_error_message("set_label: invalid label name",script_pos);
+ return;
+ }
+ if(str_data[l].label!=-1){
+ disp_error_message("set_label: dup label ",script_pos);
+ return;
+ }
+ str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ str_data[l].label=pos;
+ for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){
+ next=GETVALUE(script_buf,i);
+ script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ SETVALUE(script_buf,i,pos);
+ i=next;
+ }
+}
+
+/// Skips spaces and/or comments.
+const char* skip_space(const char* p)
+{
+ if( p == NULL )
+ return NULL;
+ for(;;)
+ {
+ while( ISSPACE(*p) )
+ ++p;
+ if( *p == '/' && p[1] == '/' )
+ {// line comment
+ while(*p && *p!='\n')
+ ++p;
+ }
+ else if( *p == '/' && p[1] == '*' )
+ {// block comment
+ p += 2;
+ for(;;)
+ {
+ if( *p == '\0' )
+ return p;//disp_error_message("script:skip_space: end of file while parsing block comment. expected "CL_BOLD"*/"CL_NORM, p);
+ if( *p == '*' && p[1] == '/' )
+ {// end of block comment
+ p += 2;
+ break;
+ }
+ ++p;
+ }
+ }
+ else
+ break;
+ }
+ return p;
+}
+
+/// Skips a word.
+/// A word consists of undercores and/or alfanumeric characters,
+/// and valid variable prefixes/postfixes.
+static
+const char* skip_word(const char* p)
+{
+ // prefix
+ switch( *p )
+ {
+ case '@':// temporary char variable
+ ++p; break;
+ case '#':// account variable
+ p += ( p[1] == '#' ? 2 : 1 ); break;
+ case '\'':// instance variable
+ ++p; break;
+ case '.':// npc variable
+ p += ( p[1] == '@' ? 2 : 1 ); break;
+ case '$':// global variable
+ p += ( p[1] == '@' ? 2 : 1 ); break;
+ }
+
+ while( ISALNUM(*p) || *p == '_' )
+ ++p;
+
+ // postfix
+ if( *p == '$' )// string
+ p++;
+
+ return p;
+}
+
+/// Adds a word to str_data.
+/// @see skip_word
+/// @see add_str
+static
+int add_word(const char* p)
+{
+ char* word;
+ int len;
+ int i;
+
+ // Check for a word
+ len = skip_word(p) - p;
+ if( len == 0 )
+ disp_error_message("script:add_word: invalid word. A word consists of undercores and/or alfanumeric characters, and valid variable prefixes/postfixes.", p);
+
+ // Duplicate the word
+ word = (char*)aMalloc(len+1);
+ memcpy(word, p, len);
+ word[len] = 0;
+
+ // add the word
+ i = add_str(word);
+ aFree(word);
+ return i;
+}
+
+/// Parses a function call.
+/// The argument list can have parenthesis or not.
+/// The number of arguments is checked.
+static
+const char* parse_callfunc(const char* p, int require_paren, int is_custom)
+{
+ const char* p2;
+ const char* arg=NULL;
+ int func;
+
+ func = add_word(p);
+ if( str_data[func].type == C_FUNC ){
+ // buildin function
+ add_scriptl(func);
+ add_scriptc(C_ARG);
+ arg = buildin_func[str_data[func].val].arg;
+ } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){
+ // script defined function
+ add_scriptl(buildin_callsub_ref);
+ add_scriptc(C_ARG);
+ add_scriptl(func);
+ arg = buildin_func[str_data[buildin_callsub_ref].val].arg;
+ if( *arg == 0 )
+ disp_error_message("parse_callfunc: callsub has no arguments, please review it's definition",p);
+ if( *arg != '*' )
+ ++arg; // count func as argument
+ } else {
+#ifdef SCRIPT_CALLFUNC_CHECK
+ const char* name = get_str(func);
+ if( !is_custom && strdb_get(userfunc_db, name) == NULL ) {
+#endif
+ disp_error_message("parse_line: expect command, missing function name or calling undeclared function",p);
+#ifdef SCRIPT_CALLFUNC_CHECK
+ } else {;
+ add_scriptl(buildin_callfunc_ref);
+ add_scriptc(C_ARG);
+ add_scriptc(C_STR);
+ while( *name ) add_scriptb(*name ++);
+ add_scriptb(0);
+ arg = buildin_func[str_data[buildin_callfunc_ref].val].arg;
+ if( *arg != '*' ) ++ arg;
+ }
+#endif
+ }
+
+ p = skip_word(p);
+ p = skip_space(p);
+ syntax.curly[syntax.curly_count].type = TYPE_ARGLIST;
+ syntax.curly[syntax.curly_count].count = 0;
+ if( *p == ';' )
+ {// <func name> ';'
+ syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
+ } else if( *p == '(' && *(p2=skip_space(p+1)) == ')' )
+ {// <func name> '(' ')'
+ syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
+ p = p2;
+ /*
+ } else if( 0 && require_paren && *p != '(' )
+ {// <func name>
+ syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
+ */
+ } else
+ {// <func name> <arg list>
+ if( require_paren ){
+ if( *p != '(' )
+ disp_error_message("need '('",p);
+ ++p; // skip '('
+ syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
+ } else if( *p == '(' ){
+ syntax.curly[syntax.curly_count].flag = ARGLIST_UNDEFINED;
+ } else {
+ syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
+ }
+ ++syntax.curly_count;
+ while( *arg ) {
+ p2=parse_subexpr(p,-1);
+ if( p == p2 )
+ break; // not an argument
+ if( *arg != '*' )
+ ++arg; // next argument
+
+ p=skip_space(p2);
+ if( *arg == 0 || *p != ',' )
+ break; // no more arguments
+ ++p; // skip comma
+ }
+ --syntax.curly_count;
+ }
+ if( *arg && *arg != '?' && *arg != '*' )
+ disp_error_message2("parse_callfunc: not enough arguments, expected ','", p, script_config.warn_func_mismatch_paramnum);
+ if( syntax.curly[syntax.curly_count].type != TYPE_ARGLIST )
+ disp_error_message("parse_callfunc: DEBUG last curly is not an argument list",p);
+ if( syntax.curly[syntax.curly_count].flag == ARGLIST_PAREN ){
+ if( *p != ')' )
+ disp_error_message("parse_callfunc: expected ')' to close argument list",p);
+ ++p;
+ }
+ add_scriptc(C_FUNC);
+ return p;
+}
+
+/// Processes end of logical script line.
+/// @param first When true, only fix up scheduling data is initialized
+/// @param p Script position for error reporting in set_label
+static void parse_nextline(bool first, const char* p)
+{
+ if( !first )
+ {
+ add_scriptc(C_EOL); // mark end of line for stack cleanup
+ set_label(LABEL_NEXTLINE, script_pos, p); // fix up '-' labels
+ }
+
+ // initialize data for new '-' label fix up scheduling
+ str_data[LABEL_NEXTLINE].type = C_NOP;
+ str_data[LABEL_NEXTLINE].backpatch = -1;
+ str_data[LABEL_NEXTLINE].label = -1;
+}
+
+/// Parse a variable assignment using the direct equals operator
+/// @param p script position where the function should run from
+/// @return NULL if not a variable assignment, the new position otherwise
+const char* parse_variable(const char* p) {
+ int i, j, word;
+ c_op type = C_NOP;
+ const char *p2 = NULL;
+ const char *var = p;
+
+ // skip the variable where applicable
+ p = skip_word(p);
+ p = skip_space(p);
+
+ if( p == NULL ) {// end of the line or invalid buffer
+ return NULL;
+ }
+
+ if( *p == '[' ) {// array variable so process the array as appropriate
+ for( p2 = p, i = 0, j = 1; p; ++ i ) {
+ if( *p ++ == ']' && --(j) == 0 ) break;
+ if( *p == '[' ) ++ j;
+ }
+
+ if( !(p = skip_space(p)) ) {// end of line or invalid characters remaining
+ disp_error_message("Missing right expression or closing bracket for variable.", p);
+ }
+ }
+
+ if( type == C_NOP &&
+ !( ( p[0] == '=' && p[1] != '=' && (type = C_EQ) ) // =
+ || ( p[0] == '+' && p[1] == '=' && (type = C_ADD) ) // +=
+ || ( p[0] == '-' && p[1] == '=' && (type = C_SUB) ) // -=
+ || ( p[0] == '^' && p[1] == '=' && (type = C_XOR) ) // ^=
+ || ( p[0] == '|' && p[1] == '=' && (type = C_OR ) ) // |=
+ || ( p[0] == '&' && p[1] == '=' && (type = C_AND) ) // &=
+ || ( p[0] == '*' && p[1] == '=' && (type = C_MUL) ) // *=
+ || ( p[0] == '/' && p[1] == '=' && (type = C_DIV) ) // /=
+ || ( p[0] == '%' && p[1] == '=' && (type = C_MOD) ) // %=
+ || ( p[0] == '~' && p[1] == '=' && (type = C_NOT) ) // ~=
+ || ( p[0] == '+' && p[1] == '+' && (type = C_ADD_PP) ) // ++
+ || ( p[0] == '-' && p[1] == '-' && (type = C_SUB_PP) ) // --
+ || ( p[0] == '<' && p[1] == '<' && p[2] == '=' && (type = C_L_SHIFT) ) // <<=
+ || ( p[0] == '>' && p[1] == '>' && p[2] == '=' && (type = C_R_SHIFT) ) // >>=
+ ) )
+ {// failed to find a matching operator combination so invalid
+ return NULL;
+ }
+
+ switch( type ) {
+ case C_EQ: {// incremental modifier
+ p = skip_space( &p[1] );
+ }
+ break;
+
+ case C_L_SHIFT:
+ case C_R_SHIFT: {// left or right shift modifier
+ p = skip_space( &p[3] );
+ }
+ break;
+
+ default: {// normal incremental command
+ p = skip_space( &p[2] );
+ }
+ }
+
+ if( p == NULL ) {// end of line or invalid buffer
+ return NULL;
+ }
+
+ // push the set function onto the stack
+ add_scriptl(buildin_set_ref);
+ add_scriptc(C_ARG);
+
+ // always append parenthesis to avoid errors
+ syntax.curly[syntax.curly_count].type = TYPE_ARGLIST;
+ syntax.curly[syntax.curly_count].count = 0;
+ syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
+
+ // increment the total curly count for the position in the script
+ ++ syntax.curly_count;
+
+ // parse the variable currently being modified
+ word = add_word(var);
+
+ if( str_data[word].type == C_FUNC || str_data[word].type == C_USERFUNC || str_data[word].type == C_USERFUNC_POS )
+ {// cannot assign a variable which exists as a function or label
+ disp_error_message("Cannot modify a variable which has the same name as a function or label.", p);
+ }
+
+ if( p2 ) {// process the variable index
+ const char* p3 = NULL;
+
+ // push the getelementofarray method into the stack
+ add_scriptl(buildin_getelementofarray_ref);
+ add_scriptc(C_ARG);
+ add_scriptl(word);
+
+ // process the sub-expression for this assignment
+ p3 = parse_subexpr(p2 + 1, 1);
+ p3 = skip_space(p3);
+
+ if( *p3 != ']' ) {// closing parenthesis is required for this script
+ disp_error_message("Missing closing ']' parenthesis for the variable assignment.", p3);
+ }
+
+ // push the closing function stack operator onto the stack
+ add_scriptc(C_FUNC);
+ p3 ++;
+ } else {// simply push the variable or value onto the stack
+ add_scriptl(word);
+ }
+
+ if( type != C_EQ )
+ add_scriptc(C_REF);
+
+ if( type == C_ADD_PP || type == C_SUB_PP ) {// incremental operator for the method
+ add_scripti(1);
+ add_scriptc(type == C_ADD_PP ? C_ADD : C_SUB);
+ } else {// process the value as an expression
+ p = parse_subexpr(p, -1);
+
+ if( type != C_EQ )
+ {// push the type of modifier onto the stack
+ add_scriptc(type);
+ }
+ }
+
+ // decrement the curly count for the position within the script
+ -- syntax.curly_count;
+
+ // close the script by appending the function operator
+ add_scriptc(C_FUNC);
+
+ // push the buffer from the method
+ return p;
+}
+
+/*==========================================
+ * Analysis section
+ *------------------------------------------*/
+const char* parse_simpleexpr(const char *p)
+{
+ int i;
+ p=skip_space(p);
+
+ if(*p==';' || *p==',')
+ disp_error_message("parse_simpleexpr: unexpected expr end",p);
+ if(*p=='('){
+ if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST )
+ ++syntax.curly[i].count;
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST &&
+ syntax.curly[i].flag == ARGLIST_UNDEFINED && --syntax.curly[i].count == 0
+ ){
+ if( *p == ',' ){
+ syntax.curly[i].flag = ARGLIST_PAREN;
+ return p;
+ } else
+ syntax.curly[i].flag = ARGLIST_NO_PAREN;
+ }
+ if( *p != ')' )
+ disp_error_message("parse_simpleexpr: unmatch ')'",p);
+ ++p;
+ } else if(ISDIGIT(*p) || ((*p=='-' || *p=='+') && ISDIGIT(p[1]))){
+ char *np;
+ while(*p == '0' && ISDIGIT(p[1])) p++;
+ i=strtoul(p,&np,0);
+ add_scripti(i);
+ p=np;
+ } else if(*p=='"'){
+ add_scriptc(C_STR);
+ p++;
+ while( *p && *p != '"' ){
+ if( (unsigned char)p[-1] <= 0x7e && *p == '\\' )
+ {
+ char buf[8];
+ size_t len = skip_escaped_c(p) - p;
+ size_t n = sv_unescape_c(buf, p, len);
+ if( n != 1 )
+ ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf);
+ p += len;
+ add_scriptb(*buf);
+ continue;
+ }
+ else if( *p == '\n' )
+ disp_error_message("parse_simpleexpr: unexpected newline @ string",p);
+ add_scriptb(*p++);
+ }
+ if(!*p)
+ disp_error_message("parse_simpleexpr: unexpected eof @ string",p);
+ add_scriptb(0);
+ p++; //'"'
+ } else {
+ int l;
+ const char* pv;
+
+ // label , register , function etc
+ if(skip_word(p)==p)
+ disp_error_message("parse_simpleexpr: unexpected character",p);
+
+ l=add_word(p);
+ if( str_data[l].type == C_FUNC || str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS)
+ return parse_callfunc(p,1,0);
+#ifdef SCRIPT_CALLFUNC_CHECK
+ else {
+ const char* name = get_str(l);
+ if( strdb_get(userfunc_db,name) != NULL ) {
+ return parse_callfunc(p,1,1);
+ }
+ }
+#endif
+
+ if( (pv = parse_variable(p)) )
+ {// successfully processed a variable assignment
+ return pv;
+ }
+
+ p=skip_word(p);
+ if( *p == '[' ){
+ // array(name[i] => getelementofarray(name,i) )
+ add_scriptl(buildin_getelementofarray_ref);
+ add_scriptc(C_ARG);
+ add_scriptl(l);
+
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if( *p != ']' )
+ disp_error_message("parse_simpleexpr: unmatch ']'",p);
+ ++p;
+ add_scriptc(C_FUNC);
+ }else
+ add_scriptl(l);
+
+ }
+
+ return p;
+}
+
+/*==========================================
+ * Analysis of the expression
+ *------------------------------------------*/
+const char* parse_subexpr(const char* p,int limit)
+{
+ int op,opl,len;
+ const char* tmpp;
+
+ p=skip_space(p);
+
+ if( *p == '-' ){
+ tmpp = skip_space(p+1);
+ if( *tmpp == ';' || *tmpp == ',' ){
+ add_scriptl(LABEL_NEXTLINE);
+ p++;
+ return p;
+ }
+ }
+
+ if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){
+ p=parse_subexpr(p+1,10);
+ add_scriptc(op);
+ } else
+ p=parse_simpleexpr(p);
+ p=skip_space(p);
+ while((
+ (op=C_OP3,opl=0,len=1,*p=='?') ||
+ (op=C_ADD,opl=8,len=1,*p=='+') ||
+ (op=C_SUB,opl=8,len=1,*p=='-') ||
+ (op=C_MUL,opl=9,len=1,*p=='*') ||
+ (op=C_DIV,opl=9,len=1,*p=='/') ||
+ (op=C_MOD,opl=9,len=1,*p=='%') ||
+ (op=C_LAND,opl=2,len=2,*p=='&' && p[1]=='&') ||
+ (op=C_AND,opl=6,len=1,*p=='&') ||
+ (op=C_LOR,opl=1,len=2,*p=='|' && p[1]=='|') ||
+ (op=C_OR,opl=5,len=1,*p=='|') ||
+ (op=C_XOR,opl=4,len=1,*p=='^') ||
+ (op=C_EQ,opl=3,len=2,*p=='=' && p[1]=='=') ||
+ (op=C_NE,opl=3,len=2,*p=='!' && p[1]=='=') ||
+ (op=C_R_SHIFT,opl=7,len=2,*p=='>' && p[1]=='>') ||
+ (op=C_GE,opl=3,len=2,*p=='>' && p[1]=='=') ||
+ (op=C_GT,opl=3,len=1,*p=='>') ||
+ (op=C_L_SHIFT,opl=7,len=2,*p=='<' && p[1]=='<') ||
+ (op=C_LE,opl=3,len=2,*p=='<' && p[1]=='=') ||
+ (op=C_LT,opl=3,len=1,*p=='<')) && opl>limit){
+ p+=len;
+ if(op == C_OP3) {
+ p=parse_subexpr(p,-1);
+ p=skip_space(p);
+ if( *(p++) != ':')
+ disp_error_message("parse_subexpr: need ':'", p-1);
+ p=parse_subexpr(p,-1);
+ } else {
+ p=parse_subexpr(p,opl);
+ }
+ add_scriptc(op);
+ p=skip_space(p);
+ }
+
+ return p; /* return first untreated operator */
+}
+
+/*==========================================
+ * Evaluation of the expression
+ *------------------------------------------*/
+const char* parse_expr(const char *p)
+{
+ switch(*p){
+ case ')': case ';': case ':': case '[': case ']':
+ case '}':
+ disp_error_message("parse_expr: unexpected char",p);
+ }
+ p=parse_subexpr(p,-1);
+ return p;
+}
+
+/*==========================================
+ * Analysis of the line
+ *------------------------------------------*/
+const char* parse_line(const char* p)
+{
+ const char* p2;
+
+ p=skip_space(p);
+ if(*p==';') {
+ //Close decision for if(); for(); while();
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ if(*p==')' && parse_syntax_for_flag)
+ return p+1;
+
+ p = skip_space(p);
+ if(p[0] == '{') {
+ syntax.curly[syntax.curly_count].type = TYPE_NULL;
+ syntax.curly[syntax.curly_count].count = -1;
+ syntax.curly[syntax.curly_count].index = -1;
+ syntax.curly_count++;
+ return p + 1;
+ } else if(p[0] == '}') {
+ return parse_curly_close(p);
+ }
+
+ // Syntax-related processing
+ p2 = parse_syntax(p);
+ if(p2 != NULL)
+ return p2;
+
+ // attempt to process a variable assignment
+ p2 = parse_variable(p);
+
+ if( p2 != NULL )
+ {// variable assignment processed so leave the method
+ return parse_syntax_close(p2 + 1);
+ }
+
+ p = parse_callfunc(p,0,0);
+ p = skip_space(p);
+
+ if(parse_syntax_for_flag) {
+ if( *p != ')' )
+ disp_error_message("parse_line: need ')'",p);
+ } else {
+ if( *p != ';' )
+ disp_error_message("parse_line: need ';'",p);
+ }
+
+ //Binding decision for if(), for(), while()
+ p = parse_syntax_close(p+1);
+
+ return p;
+}
+
+// { ... } Closing process
+const char* parse_curly_close(const char* p)
+{
+ if(syntax.curly_count <= 0) {
+ disp_error_message("parse_curly_close: unexpected string",p);
+ return p + 1;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) {
+ syntax.curly_count--;
+ //Close decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) {
+ //Closing switch()
+ int pos = syntax.curly_count-1;
+ char label[256];
+ int l;
+ // Remove temporary variables
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Go to the end pointer unconditionally
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // You are here labeled
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos, p);
+
+ if(syntax.curly[pos].flag) {
+ //Exists default
+ sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+
+ // Label end
+ sprintf(label,"__SW%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos, p);
+ linkdb_final(&syntax.curly[pos].case_label); // free the list of case label
+ syntax.curly_count--;
+ //Closing decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ } else {
+ disp_error_message("parse_curly_close: unexpected string",p);
+ return p + 1;
+ }
+}
+
+// Syntax-related processing
+// break, case, continue, default, do, for, function,
+// if, switch, while ? will handle this internally.
+const char* parse_syntax(const char* p)
+{
+ const char *p2 = skip_word(p);
+
+ switch(*p) {
+ case 'B':
+ case 'b':
+ if(p2 - p == 5 && !strncasecmp(p,"break",5)) {
+ // break Processing
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_SWITCH) {
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("parse_syntax: unexpected 'break'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_space(p2);
+ if(*p != ';')
+ disp_error_message("parse_syntax: need ';'",p);
+ // Closing decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'c':
+ case 'C':
+ if(p2 - p == 4 && !strncasecmp(p,"case",4)) {
+ //Processing case
+ int pos = syntax.curly_count-1;
+ if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) {
+ disp_error_message("parse_syntax: unexpected 'case' ",p);
+ return p+1;
+ } else {
+ char label[256];
+ int l,v;
+ char *np;
+ if(syntax.curly[pos].count != 1) {
+ //Jump for FALLTHRU
+ sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // You are here labeled
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos, p);
+ }
+ //Decision statement switch
+ p = skip_space(p2);
+ if(p == p2) {
+ disp_error_message("parse_syntax: expect space ' '",p);
+ }
+ // check whether case label is integer or not
+ v = strtol(p,&np,0);
+ if(np == p) { //Check for constants
+ p2 = skip_word(p);
+ v = p2-p; // length of word at p2
+ memcpy(label,p,v);
+ label[v]='\0';
+ if( !script_get_constant(label, &v) )
+ disp_error_message("parse_syntax: 'case' label not integer",p);
+ p = skip_word(p);
+ } else { //Numeric value
+ if((*p == '-' || *p == '+') && ISDIGIT(p[1])) // pre-skip because '-' can not skip_word
+ p++;
+ p = skip_word(p);
+ if(np != p)
+ disp_error_message("parse_syntax: 'case' label not integer",np);
+ }
+ p = skip_space(p);
+ if(*p != ':')
+ disp_error_message("parse_syntax: expect ':'",p);
+ sprintf(label,"if(%d != $@__SW%x_VAL) goto __SW%x_%x;",
+ v,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ // Bad I do not parse twice
+ p2 = parse_line(label);
+ parse_line(p2);
+ syntax.curly_count--;
+ if(syntax.curly[pos].count != 1) {
+ // Label after the completion of FALLTHRU
+ sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ }
+ // check duplication of case label [Rayce]
+ if(linkdb_search(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v)) != NULL)
+ disp_error_message("parse_syntax: dup 'case'",p);
+ linkdb_insert(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v), (void*)1);
+
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+
+ parse_line(label);
+ syntax.curly_count--;
+ syntax.curly[pos].count++;
+ }
+ return p + 1;
+ } else if(p2 - p == 8 && !strncasecmp(p,"continue",8)) {
+ // Processing continue
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[pos].flag = 1; //Flag put the link for continue
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("parse_syntax: unexpected 'continue'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_space(p2);
+ if(*p != ';')
+ disp_error_message("parse_syntax: need ';'",p);
+ //Closing decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'd':
+ case 'D':
+ if(p2 - p == 7 && !strncasecmp(p,"default",7)) {
+ // Switch - default processing
+ int pos = syntax.curly_count-1;
+ if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) {
+ disp_error_message("parse_syntax: unexpected 'default'",p);
+ } else if(syntax.curly[pos].flag) {
+ disp_error_message("parse_syntax: dup 'default'",p);
+ } else {
+ char label[256];
+ int l;
+ // Put the label location
+ p = skip_space(p2);
+ if(*p != ':') {
+ disp_error_message("parse_syntax: need ':'",p);
+ }
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ // Skip to the next link w/o condition
+ sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // The default label
+ sprintf(label,"__SW%x_DEF",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ syntax.curly[syntax.curly_count - 1].flag = 1;
+ syntax.curly[pos].count++;
+ }
+ return p + 1;
+ } else if(p2 - p == 2 && !strncasecmp(p,"do",2)) {
+ int l;
+ char label[256];
+ p=skip_space(p2);
+
+ syntax.curly[syntax.curly_count].type = TYPE_DO;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // Label of the (do) form here
+ sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count++;
+ return p;
+ }
+ break;
+ case 'f':
+ case 'F':
+ if(p2 - p == 3 && !strncasecmp(p,"for",3)) {
+ int l;
+ char label[256];
+ int pos = syntax.curly_count;
+ syntax.curly[syntax.curly_count].type = TYPE_FOR;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ syntax.curly_count++;
+
+ p=skip_space(p2);
+
+ if(*p != '(')
+ disp_error_message("parse_syntax: need '('",p);
+ p++;
+
+ // Execute the initialization statement
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+
+ // Form the start of label decision
+ sprintf(label,"__FR%x_J",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ p=skip_space(p);
+ if(*p == ';') {
+ // For (; Because the pattern of always true ;)
+ ;
+ } else {
+ // Skip to the end point if the condition is false
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ }
+ if(*p != ';')
+ disp_error_message("parse_syntax: need ';'",p);
+ p++;
+
+ // Skip to the beginning of the loop
+ sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Labels to form the next loop
+ sprintf(label,"__FR%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ // Process the next time you enter the loop
+ // A ')' last for; flag to be treated as'
+ parse_syntax_for_flag = 1;
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+ parse_syntax_for_flag = 0;
+
+ // Skip to the determination process conditions
+ sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Loop start labeling
+ sprintf(label,"__FR%x_BGN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ return p;
+ }
+ else if( p2 - p == 8 && strncasecmp(p,"function",8) == 0 )
+ {// internal script function
+ const char *func_name;
+
+ func_name = skip_space(p2);
+ p = skip_word(func_name);
+ if( p == func_name )
+ disp_error_message("parse_syntax:function: function name is missing or invalid", p);
+ p2 = skip_space(p);
+ if( *p2 == ';' )
+ {// function <name> ;
+ // function declaration - just register the name
+ int l;
+ l = add_word(func_name);
+ if( str_data[l].type == C_NOP )// register only, if the name was not used by something else
+ str_data[l].type = C_USERFUNC;
+ else if( str_data[l].type == C_USERFUNC )
+ ; // already registered
+ else
+ disp_error_message("parse_syntax:function: function name is invalid", func_name);
+
+ // Close condition of if, for, while
+ p = parse_syntax_close(p2 + 1);
+ return p;
+ }
+ else if(*p2 == '{')
+ {// function <name> <line/block of code>
+ char label[256];
+ int l;
+
+ syntax.curly[syntax.curly_count].type = TYPE_USERFUNC;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ ++syntax.curly_count;
+
+ // Jump over the function code
+ sprintf(label, "goto __FN%x_FIN;", syntax.curly[syntax.curly_count-1].index);
+ syntax.curly[syntax.curly_count].type = TYPE_NULL;
+ ++syntax.curly_count;
+ parse_line(label);
+ --syntax.curly_count;
+
+ // Set the position of the function (label)
+ l=add_word(func_name);
+ if( str_data[l].type == C_NOP || str_data[l].type == C_USERFUNC )// register only, if the name was not used by something else
+ {
+ str_data[l].type = C_USERFUNC;
+ set_label(l, script_pos, p);
+ if( parse_options&SCRIPT_USE_LABEL_DB )
+ strdb_iput(scriptlabel_db, get_str(l), script_pos);
+ }
+ else
+ disp_error_message("parse_syntax:function: function name is invalid", func_name);
+
+ return skip_space(p);
+ }
+ else
+ {
+ disp_error_message("expect ';' or '{' at function syntax",p);
+ }
+ }
+ break;
+ case 'i':
+ case 'I':
+ if(p2 - p == 2 && !strncasecmp(p,"if",2)) {
+ // If process
+ char label[256];
+ p=skip_space(p2);
+ if(*p != '(') { //Prevent if this {} non-c syntax. from Rayce (jA)
+ disp_error_message("need '('",p);
+ }
+ syntax.curly[syntax.curly_count].type = TYPE_IF;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count);
+ syntax.curly_count++;
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ return p;
+ }
+ break;
+ case 's':
+ case 'S':
+ if(p2 - p == 6 && !strncasecmp(p,"switch",6)) {
+ // Processing of switch ()
+ char label[256];
+ p=skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+ syntax.curly[syntax.curly_count].type = TYPE_SWITCH;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index);
+ syntax.curly_count++;
+ add_scriptl(add_str("set"));
+ add_scriptc(C_ARG);
+ add_scriptl(add_str(label));
+ p=parse_expr(p);
+ p=skip_space(p);
+ if(*p != '{') {
+ disp_error_message("parse_syntax: need '{'",p);
+ }
+ add_scriptc(C_FUNC);
+ return p + 1;
+ }
+ break;
+ case 'w':
+ case 'W':
+ if(p2 - p == 5 && !strncasecmp(p,"while",5)) {
+ int l;
+ char label[256];
+ p=skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+ syntax.curly[syntax.curly_count].type = TYPE_WHILE;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // Form the start of label decision
+ sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ // Skip to the end point if the condition is false
+ sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index);
+ syntax.curly_count++;
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ return p;
+ }
+ break;
+ }
+ return NULL;
+}
+
+const char* parse_syntax_close(const char *p) {
+ // If (...) for (...) hoge (); as to make sure closed closed once again
+ int flag;
+
+ do {
+ p = parse_syntax_close_sub(p,&flag);
+ } while(flag);
+ return p;
+}
+
+// Close judgment if, for, while, of do
+// flag == 1 : closed
+// flag == 0 : not closed
+const char* parse_syntax_close_sub(const char* p,int* flag)
+{
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ int l;
+ *flag = 1;
+
+ if(syntax.curly_count <= 0) {
+ *flag = 0;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_IF) {
+ const char *bp = p;
+ const char *p2;
+
+ // if-block and else-block end is a new line
+ parse_nextline(false, p);
+
+ // Skip to the last location if
+ sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Put the label of the location
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ syntax.curly[pos].count++;
+ p = skip_space(p);
+ p2 = skip_word(p);
+ if(!syntax.curly[pos].flag && p2 - p == 4 && !strncasecmp(p,"else",4)) {
+ // else or else - if
+ p = skip_space(p2);
+ p2 = skip_word(p);
+ if(p2 - p == 2 && !strncasecmp(p,"if",2)) {
+ // else - if
+ p=skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ *flag = 0;
+ return p;
+ } else {
+ // else
+ if(!syntax.curly[pos].flag) {
+ syntax.curly[pos].flag = 1;
+ *flag = 0;
+ return p;
+ }
+ }
+ }
+ // Close if
+ syntax.curly_count--;
+ // Put the label of the final location
+ sprintf(label,"__IF%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ if(syntax.curly[pos].flag == 1) {
+ // Because the position of the pointer is the same if not else for this
+ return bp;
+ }
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_DO) {
+ int l;
+ char label[256];
+ const char *p2;
+
+ if(syntax.curly[pos].flag) {
+ // (Come here continue) to form the label here
+ sprintf(label,"__DO%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ }
+
+ // Skip to the end point if the condition is false
+ p = skip_space(p);
+ p2 = skip_word(p);
+ if(p2 - p != 5 || strncasecmp(p,"while",5))
+ disp_error_message("parse_syntax: need 'while'",p);
+
+ p = skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+
+ // do-block end is a new line
+ parse_nextline(false, p);
+
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+
+ // Skip to the starting point
+ sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Form label of the end point conditions
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ p = skip_space(p);
+ if(*p != ';') {
+ disp_error_message("parse_syntax: need ';'",p);
+ return p+1;
+ }
+ p++;
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ // for-block end is a new line
+ parse_nextline(false, p);
+
+ // Skip to the next loop
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // End for labeling
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ // while-block end is a new line
+ parse_nextline(false, p);
+
+ // Skip to the decision while
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // End while labeling
+ sprintf(label,"__WL%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) {
+ int pos = syntax.curly_count-1;
+ char label[256];
+ int l;
+ // Back
+ sprintf(label,"return;");
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Put the label of the location
+ sprintf(label,"__FN%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count--;
+ return p;
+ } else {
+ *flag = 0;
+ return p;
+ }
+}
+
+/*==========================================
+ * Added built-in functions
+ *------------------------------------------*/
+static void add_buildin_func(void)
+{
+ int i,n;
+ const char* p;
+ for( i = 0; buildin_func[i].func; i++ )
+ {
+ // arg must follow the pattern: (v|s|i|r|l)*\?*\*?
+ // 'v' - value (either string or int or reference)
+ // 's' - string
+ // 'i' - int
+ // 'r' - reference (of a variable)
+ // 'l' - label
+ // '?' - one optional parameter
+ // '*' - unknown number of optional parameters
+ p = buildin_func[i].arg;
+ while( *p == 'v' || *p == 's' || *p == 'i' || *p == 'r' || *p == 'l' ) ++p;
+ while( *p == '?' ) ++p;
+ if( *p == '*' ) ++p;
+ if( *p != 0){
+ ShowWarning("add_buildin_func: ignoring function \"%s\" with invalid arg \"%s\".\n", buildin_func[i].name, buildin_func[i].arg);
+ } else if( *skip_word(buildin_func[i].name) != 0 ){
+ ShowWarning("add_buildin_func: ignoring function with invalid name \"%s\" (must be a word).\n", buildin_func[i].name);
+ } else {
+ n = add_str(buildin_func[i].name);
+ str_data[n].type = C_FUNC;
+ str_data[n].val = i;
+ str_data[n].func = buildin_func[i].func;
+
+ if (!strcmp(buildin_func[i].name, "set")) buildin_set_ref = n;
+ else if (!strcmp(buildin_func[i].name, "callsub")) buildin_callsub_ref = n;
+ else if (!strcmp(buildin_func[i].name, "callfunc")) buildin_callfunc_ref = n;
+ else if( !strcmp(buildin_func[i].name, "getelementofarray") ) buildin_getelementofarray_ref = n;
+ }
+ }
+}
+
+/// Retrieves the value of a constant.
+bool script_get_constant(const char* name, int* value)
+{
+ int n = search_str(name);
+
+ if( n == -1 || str_data[n].type != C_INT )
+ {// not found or not a constant
+ return false;
+ }
+ value[0] = str_data[n].val;
+
+ return true;
+}
+
+/// Creates new constant or parameter with given value.
+void script_set_constant(const char* name, int value, bool isparameter)
+{
+ int n = add_str(name);
+
+ if( str_data[n].type == C_NOP )
+ {// new
+ str_data[n].type = isparameter ? C_PARAM : C_INT;
+ str_data[n].val = value;
+ }
+ else if( str_data[n].type == C_PARAM || str_data[n].type == C_INT )
+ {// existing parameter or constant
+ ShowError("script_set_constant: Attempted to overwrite existing %s '%s' (old value=%d, new value=%d).\n", ( str_data[n].type == C_PARAM ) ? "parameter" : "constant", name, str_data[n].val, value);
+ }
+ else
+ {// existing name
+ ShowError("script_set_constant: Invalid name for %s '%s' (already defined as %s).\n", isparameter ? "parameter" : "constant", name, script_op2name(str_data[n].type));
+ }
+}
+
+/*==========================================
+ * Reading constant databases
+ * const.txt
+ *------------------------------------------*/
+static void read_constdb(void)
+{
+ FILE *fp;
+ char line[1024],name[1024],val[1024];
+ int type;
+
+ sprintf(line, "%s/const.txt", db_path);
+ fp=fopen(line, "r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return ;
+ }
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ type=0;
+ if(sscanf(line,"%[A-Za-z0-9_],%[-0-9xXA-Fa-f],%d",name,val,&type)>=2 ||
+ sscanf(line,"%[A-Za-z0-9_] %[-0-9xXA-Fa-f] %d",name,val,&type)>=2){
+ script_set_constant(name, (int)strtol(val, NULL, 0), (bool)type);
+ }
+ }
+ fclose(fp);
+}
+
+/*==========================================
+ * Display emplacement line of script
+ *------------------------------------------*/
+static const char* script_print_line(StringBuf* buf, const char* p, const char* mark, int line)
+{
+ int i;
+ if( p == NULL || !p[0] ) return NULL;
+ if( line < 0 )
+ StringBuf_Printf(buf, "*% 5d : ", -line);
+ else
+ StringBuf_Printf(buf, " % 5d : ", line);
+ for(i=0;p[i] && p[i] != '\n';i++){
+ if(p + i != mark)
+ StringBuf_Printf(buf, "%c", p[i]);
+ else
+ StringBuf_Printf(buf, "\'%c\'", p[i]);
+ }
+ StringBuf_AppendStr(buf, "\n");
+ return p+i+(p[i] == '\n' ? 1 : 0);
+}
+
+void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos)
+{
+ // Find the line where the error occurred
+ int j;
+ int line = start_line;
+ const char *p;
+ const char *linestart[5] = { NULL, NULL, NULL, NULL, NULL };
+ StringBuf buf;
+
+ for(p=src;p && *p;line++){
+ const char *lineend=strchr(p,'\n');
+ if(lineend==NULL || error_pos<lineend){
+ break;
+ }
+ for( j = 0; j < 4; j++ ) {
+ linestart[j] = linestart[j+1];
+ }
+ linestart[4] = p;
+ p=lineend+1;
+ }
+
+ StringBuf_Init(&buf);
+ StringBuf_AppendStr(&buf, "\a\n");
+ StringBuf_Printf(&buf, "script error on %s line %d\n", file, line);
+ StringBuf_Printf(&buf, " %s\n", error_msg);
+ for(j = 0; j < 5; j++ ) {
+ script_print_line(&buf, linestart[j], NULL, line + j - 5);
+ }
+ p = script_print_line(&buf, p, error_pos, -line);
+ for(j = 0; j < 5; j++) {
+ p = script_print_line(&buf, p, NULL, line + j + 1 );
+ }
+ ShowError("%s", StringBuf_Value(&buf));
+ StringBuf_Destroy(&buf);
+}
+
+/*==========================================
+ * Analysis of the script
+ *------------------------------------------*/
+struct script_code* parse_script(const char *src,const char *file,int line,int options)
+{
+ const char *p,*tmpp;
+ int i;
+ struct script_code* code = NULL;
+ static int first=1;
+ char end;
+ bool unresolved_names = false;
+
+ if( src == NULL )
+ return NULL;// empty script
+
+ memset(&syntax,0,sizeof(syntax));
+ if(first){
+ add_buildin_func();
+ read_constdb();
+ first=0;
+ }
+
+ script_buf=(unsigned char *)aMalloc(SCRIPT_BLOCK_SIZE*sizeof(unsigned char));
+ script_pos=0;
+ script_size=SCRIPT_BLOCK_SIZE;
+ parse_nextline(true, NULL);
+
+ // who called parse_script is responsible for clearing the database after using it, but just in case... lets clear it here
+ if( options&SCRIPT_USE_LABEL_DB )
+ db_clear(scriptlabel_db);
+ parse_options = options;
+
+ if( setjmp( error_jump ) != 0 ) {
+ //Restore program state when script has problems. [from jA]
+ int i;
+ const int size = ARRAYLENGTH(syntax.curly);
+ if( error_report )
+ script_error(src,file,line,error_msg,error_pos);
+ aFree( error_msg );
+ aFree( script_buf );
+ script_pos = 0;
+ script_size = 0;
+ script_buf = NULL;
+ for(i=LABEL_START;i<str_num;i++)
+ if(str_data[i].type == C_NOP) str_data[i].type = C_NAME;
+ for(i=0; i<size; i++)
+ linkdb_final(&syntax.curly[i].case_label);
+ return NULL;
+ }
+
+ parse_syntax_for_flag=0;
+ p=src;
+ p=skip_space(p);
+ if( options&SCRIPT_IGNORE_EXTERNAL_BRACKETS )
+ {// does not require brackets around the script
+ if( *p == '\0' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
+ {// empty script and can return NULL
+ aFree( script_buf );
+ script_pos = 0;
+ script_size = 0;
+ script_buf = NULL;
+ return NULL;
+ }
+ end = '\0';
+ }
+ else
+ {// requires brackets around the script
+ if( *p != '{' )
+ disp_error_message("not found '{'",p);
+ p = skip_space(p+1);
+ if( *p == '}' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
+ {// empty script and can return NULL
+ aFree( script_buf );
+ script_pos = 0;
+ script_size = 0;
+ script_buf = NULL;
+ return NULL;
+ }
+ end = '}';
+ }
+
+ // clear references of labels, variables and internal functions
+ for(i=LABEL_START;i<str_num;i++){
+ if(
+ str_data[i].type==C_POS || str_data[i].type==C_NAME ||
+ str_data[i].type==C_USERFUNC || str_data[i].type == C_USERFUNC_POS
+ ){
+ str_data[i].type=C_NOP;
+ str_data[i].backpatch=-1;
+ str_data[i].label=-1;
+ }
+ }
+
+ while( syntax.curly_count != 0 || *p != end )
+ {
+ if( *p == '\0' )
+ disp_error_message("unexpected end of script",p);
+ // Special handling only label
+ tmpp=skip_space(skip_word(p));
+ if(*tmpp==':' && !(!strncasecmp(p,"default:",8) && p + 7 == tmpp)){
+ i=add_word(p);
+ set_label(i,script_pos,p);
+ if( parse_options&SCRIPT_USE_LABEL_DB )
+ strdb_iput(scriptlabel_db, get_str(i), script_pos);
+ p=tmpp+1;
+ p=skip_space(p);
+ continue;
+ }
+
+ // All other lumped
+ p=parse_line(p);
+ p=skip_space(p);
+
+ parse_nextline(false, p);
+ }
+
+ add_scriptc(C_NOP);
+
+ // trim code to size
+ script_size = script_pos;
+ RECREATE(script_buf,unsigned char,script_pos);
+
+ // default unknown references to variables
+ for(i=LABEL_START;i<str_num;i++){
+ if(str_data[i].type==C_NOP){
+ int j,next;
+ str_data[i].type=C_NAME;
+ str_data[i].label=i;
+ for(j=str_data[i].backpatch;j>=0 && j!=0x00ffffff;){
+ next=GETVALUE(script_buf,j);
+ SETVALUE(script_buf,j,i);
+ j=next;
+ }
+ }
+ else if( str_data[i].type == C_USERFUNC )
+ {// 'function name;' without follow-up code
+ ShowError("parse_script: function '%s' declared but not defined.\n", str_buf+str_data[i].str);
+ unresolved_names = true;
+ }
+ }
+
+ if( unresolved_names )
+ {
+ disp_error_message("parse_script: unresolved function references", p);
+ }
+
+#ifdef DEBUG_DISP
+ for(i=0;i<script_pos;i++){
+ if((i&15)==0) ShowMessage("%04x : ",i);
+ ShowMessage("%02x ",script_buf[i]);
+ if((i&15)==15) ShowMessage("\n");
+ }
+ ShowMessage("\n");
+#endif
+#ifdef DEBUG_DISASM
+ {
+ int i = 0,j;
+ while(i < script_pos) {
+ c_op op = get_com(script_buf,&i);
+
+ ShowMessage("%06x %s", i, script_op2name(op));
+ j = i;
+ switch(op) {
+ case C_INT:
+ ShowMessage(" %d", get_num(script_buf,&i));
+ break;
+ case C_POS:
+ ShowMessage(" 0x%06x", *(int*)(script_buf+i)&0xffffff);
+ i += 3;
+ break;
+ case C_NAME:
+ j = (*(int*)(script_buf+i)&0xffffff);
+ ShowMessage(" %s", ( j == 0xffffff ) ? "?? unknown ??" : get_str(j));
+ i += 3;
+ break;
+ case C_STR:
+ j = strlen(script_buf + i);
+ ShowMessage(" %s", script_buf + i);
+ i += j+1;
+ break;
+ }
+ ShowMessage(CL_CLL"\n");
+ }
+ }
+#endif
+
+ CREATE(code,struct script_code,1);
+ code->script_buf = script_buf;
+ code->script_size = script_size;
+ code->script_vars = idb_alloc(DB_OPT_RELEASE_DATA);
+ return code;
+}
+
+/// Returns the player attached to this script, identified by the rid.
+/// If there is no player attached, the script is terminated.
+TBL_PC *script_rid2sd(struct script_state *st)
+{
+ TBL_PC *sd=map_id2sd(st->rid);
+ if(!sd){
+ ShowError("script_rid2sd: fatal error ! player not attached!\n");
+ script_reportfunc(st);
+ script_reportsrc(st);
+ st->state = END;
+ }
+ return sd;
+}
+
+/// Dereferences a variable/constant, replacing it with a copy of the value.
+///
+/// @param st Script state
+/// @param data Variable/constant
+void get_val(struct script_state* st, struct script_data* data)
+{
+ const char* name;
+ char prefix;
+ char postfix;
+ TBL_PC* sd = NULL;
+
+ if( !data_isreference(data) )
+ return;// not a variable/constant
+
+ name = reference_getname(data);
+ prefix = name[0];
+ postfix = name[strlen(name) - 1];
+
+ //##TODO use reference_tovariable(data) when it's confirmed that it works [FlavioJS]
+ if( !reference_toconstant(data) && not_server_variable(prefix) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ {// needs player attached
+ if( postfix == '$' )
+ {// string variable
+ ShowWarning("script:get_val: cannot access player variable '%s', defaulting to \"\"\n", name);
+ data->type = C_CONSTSTR;
+ data->u.str = "";
+ }
+ else
+ {// integer variable
+ ShowWarning("script:get_val: cannot access player variable '%s', defaulting to 0\n", name);
+ data->type = C_INT;
+ data->u.num = 0;
+ }
+ return;
+ }
+ }
+
+ if( postfix == '$' )
+ {// string variable
+
+ switch( prefix )
+ {
+ case '@':
+ data->u.str = pc_readregstr(sd, data->u.num);
+ break;
+ case '$':
+ data->u.str = mapreg_readregstr(data->u.num);
+ break;
+ case '#':
+ if( name[1] == '#' )
+ data->u.str = pc_readaccountreg2str(sd, name);// global
+ else
+ data->u.str = pc_readaccountregstr(sd, name);// local
+ break;
+ case '.':
+ {
+ struct DBMap* n =
+ data->ref ? *data->ref:
+ name[1] == '@' ? st->stack->var_function:// instance/scope variable
+ st->script->script_vars;// npc variable
+ if( n )
+ data->u.str = (char*)idb_get(n,reference_getuid(data));
+ else
+ data->u.str = NULL;
+ }
+ break;
+ case '\'':
+ if (st->instance_id) {
+ data->u.str = (char*)idb_get(instance[st->instance_id].vars,reference_getuid(data));
+ } else {
+ ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to \"\"\n", name);
+ data->u.str = NULL;
+ }
+ break;
+ default:
+ data->u.str = pc_readglobalreg_str(sd, name);
+ break;
+ }
+
+ if( data->u.str == NULL || data->u.str[0] == '\0' )
+ {// empty string
+ data->type = C_CONSTSTR;
+ data->u.str = "";
+ }
+ else
+ {// duplicate string
+ data->type = C_STR;
+ data->u.str = aStrdup(data->u.str);
+ }
+
+ }
+ else
+ {// integer variable
+
+ data->type = C_INT;
+
+ if( reference_toconstant(data) )
+ {
+ data->u.num = reference_getconstant(data);
+ }
+ else if( reference_toparam(data) )
+ {
+ data->u.num = pc_readparam(sd, reference_getparamtype(data));
+ }
+ else
+ switch( prefix )
+ {
+ case '@':
+ data->u.num = pc_readreg(sd, data->u.num);
+ break;
+ case '$':
+ data->u.num = mapreg_readreg(data->u.num);
+ break;
+ case '#':
+ if( name[1] == '#' )
+ data->u.num = pc_readaccountreg2(sd, name);// global
+ else
+ data->u.num = pc_readaccountreg(sd, name);// local
+ break;
+ case '.':
+ {
+ struct DBMap* n =
+ data->ref ? *data->ref:
+ name[1] == '@' ? st->stack->var_function:// instance/scope variable
+ st->script->script_vars;// npc variable
+ if( n )
+ data->u.num = (int)idb_iget(n,reference_getuid(data));
+ else
+ data->u.num = 0;
+ }
+ break;
+ case '\'':
+ if( st->instance_id )
+ data->u.num = (int)idb_iget(instance[st->instance_id].vars,reference_getuid(data));
+ else {
+ ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to 0\n", name);
+ data->u.num = 0;
+ }
+ break;
+ default:
+ data->u.num = pc_readglobalreg(sd, name);
+ break;
+ }
+
+ }
+
+ return;
+}
+
+struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref);
+
+/// Retrieves the value of a reference identified by uid (variable, constant, param)
+/// The value is left in the top of the stack and needs to be removed manually.
+void* get_val2(struct script_state* st, int uid, struct DBMap** ref)
+{
+ struct script_data* data;
+ push_val2(st->stack, C_NAME, uid, ref);
+ data = script_getdatatop(st, -1);
+ get_val(st, data);
+ return (data->type == C_INT ? (void*)__64BPRTSIZE(data->u.num) : (void*)__64BPRTSIZE(data->u.str));
+}
+
+/*==========================================
+ * Stores the value of a script variable
+ * Return value is 0 on fail, 1 on success.
+ *------------------------------------------*/
+static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* name, const void* value, struct DBMap** ref)
+{
+ char prefix = name[0];
+
+ if( is_string_variable(name) )
+ {// string variable
+ const char* str = (const char*)value;
+ switch (prefix) {
+ case '@':
+ return pc_setregstr(sd, num, str);
+ case '$':
+ return mapreg_setregstr(num, str);
+ case '#':
+ return (name[1] == '#') ?
+ pc_setaccountreg2str(sd, name, str) :
+ pc_setaccountregstr(sd, name, str);
+ case '.':
+ {
+ struct DBMap* n;
+ n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars;
+ if( n ) {
+ idb_remove(n, num);
+ if (str[0]) idb_put(n, num, aStrdup(str));
+ }
+ }
+ return 1;
+ case '\'':
+ if( st->instance_id ) {
+ idb_remove(instance[st->instance_id].vars, num);
+ if( str[0] ) idb_put(instance[st->instance_id].vars, num, aStrdup(str));
+ }
+ return 1;
+ default:
+ return pc_setglobalreg_str(sd, name, str);
+ }
+ }
+ else
+ {// integer variable
+ int val = (int)__64BPRTSIZE(value);
+ if(str_data[num&0x00ffffff].type == C_PARAM)
+ {
+ if( pc_setparam(sd, str_data[num&0x00ffffff].val, val) == 0 )
+ {
+ if( st != NULL )
+ {
+ ShowError("script:set_reg: failed to set param '%s' to %d.\n", name, val);
+ script_reportsrc(st);
+ st->state = END;
+ }
+ return 0;
+ }
+ return 1;
+ }
+
+ switch (prefix) {
+ case '@':
+ return pc_setreg(sd, num, val);
+ case '$':
+ return mapreg_setreg(num, val);
+ case '#':
+ return (name[1] == '#') ?
+ pc_setaccountreg2(sd, name, val) :
+ pc_setaccountreg(sd, name, val);
+ case '.':
+ {
+ struct DBMap* n;
+ n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars;
+ if( n ) {
+ idb_remove(n, num);
+ if( val != 0 )
+ idb_iput(n, num, val);
+ }
+ }
+ return 1;
+ case '\'':
+ if( st->instance_id ) {
+ idb_remove(instance[st->instance_id].vars, num);
+ if( val != 0 )
+ idb_iput(instance[st->instance_id].vars, num, val);
+ }
+ return 1;
+ default:
+ return pc_setglobalreg(sd, name, val);
+ }
+ }
+}
+
+int set_var(TBL_PC* sd, char* name, void* val)
+{
+ return set_reg(NULL, sd, reference_uid(add_str(name),0), name, val, NULL);
+}
+
+void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct DBMap **ref)
+{
+ set_reg(st, sd, reference_uid(add_str(varname),elem), varname, value, ref);
+}
+
+/// Converts the data to a string
+const char* conv_str(struct script_state* st, struct script_data* data)
+{
+ char* p;
+
+ get_val(st, data);
+ if( data_isstring(data) )
+ {// nothing to convert
+ }
+ else if( data_isint(data) )
+ {// int -> string
+ CREATE(p, char, ITEM_NAME_LENGTH);
+ snprintf(p, ITEM_NAME_LENGTH, "%d", data->u.num);
+ p[ITEM_NAME_LENGTH-1] = '\0';
+ data->type = C_STR;
+ data->u.str = p;
+ }
+ else if( data_isreference(data) )
+ {// reference -> string
+ //##TODO when does this happen (check get_val) [FlavioJS]
+ data->type = C_CONSTSTR;
+ data->u.str = reference_getname(data);
+ }
+ else
+ {// unsupported data type
+ ShowError("script:conv_str: cannot convert to string, defaulting to \"\"\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ data->type = C_CONSTSTR;
+ data->u.str = "";
+ }
+ return data->u.str;
+}
+
+/// Converts the data to an int
+int conv_num(struct script_state* st, struct script_data* data)
+{
+ char* p;
+ long num;
+
+ get_val(st, data);
+ if( data_isint(data) )
+ {// nothing to convert
+ }
+ else if( data_isstring(data) )
+ {// string -> int
+ // the result does not overflow or underflow, it is capped instead
+ // ex: 999999999999 is capped to INT_MAX (2147483647)
+ p = data->u.str;
+ errno = 0;
+ num = strtol(data->u.str, NULL, 10);// change radix to 0 to support octal numbers "o377" and hex numbers "0xFF"
+ if( errno == ERANGE
+#if LONG_MAX > INT_MAX
+ || num < INT_MIN || num > INT_MAX
+#endif
+ )
+ {
+ if( num <= INT_MIN )
+ {
+ num = INT_MIN;
+ ShowError("script:conv_num: underflow detected, capping to %ld\n", num);
+ }
+ else//if( num >= INT_MAX )
+ {
+ num = INT_MAX;
+ ShowError("script:conv_num: overflow detected, capping to %ld\n", num);
+ }
+ script_reportdata(data);
+ script_reportsrc(st);
+ }
+ if( data->type == C_STR )
+ aFree(p);
+ data->type = C_INT;
+ data->u.num = (int)num;
+ }
+#if 0
+ // FIXME this function is being used to retrieve the position of labels and
+ // probably other stuff [FlavioJS]
+ else
+ {// unsupported data type
+ ShowError("script:conv_num: cannot convert to number, defaulting to 0\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ data->type = C_INT;
+ data->u.num = 0;
+ }
+#endif
+ return data->u.num;
+}
+
+//
+// Stack operations
+//
+
+/// Increases the size of the stack
+void stack_expand(struct script_stack* stack)
+{
+ stack->sp_max += 64;
+ stack->stack_data = (struct script_data*)aRealloc(stack->stack_data,
+ stack->sp_max * sizeof(stack->stack_data[0]) );
+ memset(stack->stack_data + (stack->sp_max - 64), 0,
+ 64 * sizeof(stack->stack_data[0]) );
+}
+
+/// Pushes a value into the stack
+#define push_val(stack,type,val) push_val2(stack, type, val, NULL)
+
+/// Pushes a value into the stack (with reference)
+struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref)
+{
+ if( stack->sp >= stack->sp_max )
+ stack_expand(stack);
+ stack->stack_data[stack->sp].type = type;
+ stack->stack_data[stack->sp].u.num = val;
+ stack->stack_data[stack->sp].ref = ref;
+ stack->sp++;
+ return &stack->stack_data[stack->sp-1];
+}
+
+/// Pushes a string into the stack
+struct script_data* push_str(struct script_stack* stack, enum c_op type, char* str)
+{
+ if( stack->sp >= stack->sp_max )
+ stack_expand(stack);
+ stack->stack_data[stack->sp].type = type;
+ stack->stack_data[stack->sp].u.str = str;
+ stack->stack_data[stack->sp].ref = NULL;
+ stack->sp++;
+ return &stack->stack_data[stack->sp-1];
+}
+
+/// Pushes a retinfo into the stack
+struct script_data* push_retinfo(struct script_stack* stack, struct script_retinfo* ri, DBMap **ref)
+{
+ if( stack->sp >= stack->sp_max )
+ stack_expand(stack);
+ stack->stack_data[stack->sp].type = C_RETINFO;
+ stack->stack_data[stack->sp].u.ri = ri;
+ stack->stack_data[stack->sp].ref = ref;
+ stack->sp++;
+ return &stack->stack_data[stack->sp-1];
+}
+
+/// Pushes a copy of the target position into the stack
+struct script_data* push_copy(struct script_stack* stack, int pos)
+{
+ switch( stack->stack_data[pos].type )
+ {
+ case C_CONSTSTR:
+ return push_str(stack, C_CONSTSTR, stack->stack_data[pos].u.str);
+ break;
+ case C_STR:
+ return push_str(stack, C_STR, aStrdup(stack->stack_data[pos].u.str));
+ break;
+ case C_RETINFO:
+ ShowFatalError("script:push_copy: can't create copies of C_RETINFO. Exiting...\n");
+ exit(1);
+ break;
+ default:
+ return push_val2(
+ stack,stack->stack_data[pos].type,
+ stack->stack_data[pos].u.num,
+ stack->stack_data[pos].ref
+ );
+ break;
+ }
+}
+
+/// Removes the values in indexes [start,end[ from the stack.
+/// Adjusts all stack pointers.
+void pop_stack(struct script_state* st, int start, int end)
+{
+ struct script_stack* stack = st->stack;
+ struct script_data* data;
+ int i;
+
+ if( start < 0 )
+ start = 0;
+ if( end > stack->sp )
+ end = stack->sp;
+ if( start >= end )
+ return;// nothing to pop
+
+ // free stack elements
+ for( i = start; i < end; i++ )
+ {
+ data = &stack->stack_data[i];
+ if( data->type == C_STR )
+ aFree(data->u.str);
+ if( data->type == C_RETINFO )
+ {
+ struct script_retinfo* ri = data->u.ri;
+ if( ri->var_function )
+ script_free_vars(ri->var_function);
+ if( data->ref )
+ aFree(data->ref);
+ aFree(ri);
+ }
+ data->type = C_NOP;
+ }
+ // move the rest of the elements
+ if( stack->sp > end )
+ {
+ memmove(&stack->stack_data[start], &stack->stack_data[end], sizeof(stack->stack_data[0])*(stack->sp - end));
+ for( i = start + stack->sp - end; i < stack->sp; ++i )
+ stack->stack_data[i].type = C_NOP;
+ }
+ // adjust stack pointers
+ if( st->start > end ) st->start -= end - start;
+ else if( st->start > start ) st->start = start;
+ if( st->end > end ) st->end -= end - start;
+ else if( st->end > start ) st->end = start;
+ if( stack->defsp > end ) stack->defsp -= end - start;
+ else if( stack->defsp > start ) stack->defsp = start;
+ stack->sp -= end - start;
+}
+
+///
+///
+///
+
+/*==========================================
+ * Release script dependent variable, dependent variable of function
+ *------------------------------------------*/
+void script_free_vars(struct DBMap* storage)
+{
+ if( storage )
+ {// destroy the storage construct containing the variables
+ db_destroy(storage);
+ }
+}
+
+void script_free_code(struct script_code* code)
+{
+ script_free_vars( code->script_vars );
+ aFree( code->script_buf );
+ aFree( code );
+}
+
+/// Creates a new script state.
+///
+/// @param script Script code
+/// @param pos Position in the code
+/// @param rid Who is running the script (attached player)
+/// @param oid Where the code is being run (npc 'object')
+/// @return Script state
+struct script_state* script_alloc_state(struct script_code* script, int pos, int rid, int oid)
+{
+ struct script_state* st;
+ CREATE(st, struct script_state, 1);
+ st->stack = (struct script_stack*)aMalloc(sizeof(struct script_stack));
+ st->stack->sp = 0;
+ st->stack->sp_max = 64;
+ CREATE(st->stack->stack_data, struct script_data, st->stack->sp_max);
+ st->stack->defsp = st->stack->sp;
+ st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
+ st->state = RUN;
+ st->script = script;
+ //st->scriptroot = script;
+ st->pos = pos;
+ st->rid = rid;
+ st->oid = oid;
+ st->sleep.timer = INVALID_TIMER;
+ return st;
+}
+
+/// Frees a script state.
+///
+/// @param st Script state
+void script_free_state(struct script_state* st)
+{
+ if(st->bk_st)
+ {// backup was not restored
+ ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
+ }
+ if( st->sleep.timer != INVALID_TIMER )
+ delete_timer(st->sleep.timer, run_script_timer);
+ script_free_vars(st->stack->var_function);
+ pop_stack(st, 0, st->stack->sp);
+ aFree(st->stack->stack_data);
+ aFree(st->stack);
+ st->stack = NULL;
+ st->pos = -1;
+ aFree(st);
+}
+
+//
+// Main execution unit
+//
+/*==========================================
+ * Read command
+ *------------------------------------------*/
+c_op get_com(unsigned char *script,int *pos)
+{
+ int i = 0, j = 0;
+
+ if(script[*pos]>=0x80){
+ return C_INT;
+ }
+ while(script[*pos]>=0x40){
+ i=script[(*pos)++]<<j;
+ j+=6;
+ }
+ return (c_op)(i+(script[(*pos)++]<<j));
+}
+
+/*==========================================
+ * Income figures
+ *------------------------------------------*/
+int get_num(unsigned char *script,int *pos)
+{
+ int i,j;
+ i=0; j=0;
+ while(script[*pos]>=0xc0){
+ i+=(script[(*pos)++]&0x7f)<<j;
+ j+=6;
+ }
+ return i+((script[(*pos)++]&0x7f)<<j);
+}
+
+/*==========================================
+ * Remove the value from the stack
+ *------------------------------------------*/
+int pop_val(struct script_state* st)
+{
+ if(st->stack->sp<=0)
+ return 0;
+ st->stack->sp--;
+ get_val(st,&(st->stack->stack_data[st->stack->sp]));
+ if(st->stack->stack_data[st->stack->sp].type==C_INT)
+ return st->stack->stack_data[st->stack->sp].u.num;
+ return 0;
+}
+
+/// Ternary operators
+/// test ? if_true : if_false
+void op_3(struct script_state* st, int op)
+{
+ struct script_data* data;
+ int flag = 0;
+
+ data = script_getdatatop(st, -3);
+ get_val(st, data);
+
+ if( data_isstring(data) )
+ flag = data->u.str[0];// "" -> false
+ else if( data_isint(data) )
+ flag = data->u.num;// 0 -> false
+ else
+ {
+ ShowError("script:op_3: invalid data for the ternary operator test\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ script_removetop(st, -3, 0);
+ script_pushnil(st);
+ return;
+ }
+ if( flag )
+ script_pushcopytop(st, -2);
+ else
+ script_pushcopytop(st, -1);
+ script_removetop(st, -4, -1);
+}
+
+/// Binary string operators
+/// s1 EQ s2 -> i
+/// s1 NE s2 -> i
+/// s1 GT s2 -> i
+/// s1 GE s2 -> i
+/// s1 LT s2 -> i
+/// s1 LE s2 -> i
+/// s1 ADD s2 -> s
+void op_2str(struct script_state* st, int op, const char* s1, const char* s2)
+{
+ int a = 0;
+
+ switch(op){
+ case C_EQ: a = (strcmp(s1,s2) == 0); break;
+ case C_NE: a = (strcmp(s1,s2) != 0); break;
+ case C_GT: a = (strcmp(s1,s2) > 0); break;
+ case C_GE: a = (strcmp(s1,s2) >= 0); break;
+ case C_LT: a = (strcmp(s1,s2) < 0); break;
+ case C_LE: a = (strcmp(s1,s2) <= 0); break;
+ case C_ADD:
+ {
+ char* buf = (char *)aMalloc((strlen(s1)+strlen(s2)+1)*sizeof(char));
+ strcpy(buf, s1);
+ strcat(buf, s2);
+ script_pushstr(st, buf);
+ return;
+ }
+ default:
+ ShowError("script:op2_str: unexpected string operator %s\n", script_op2name(op));
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+
+ script_pushint(st,a);
+}
+
+/// Binary number operators
+/// i OP i -> i
+void op_2num(struct script_state* st, int op, int i1, int i2)
+{
+ int ret;
+ double ret_double;
+
+ switch( op )
+ {
+ case C_AND: ret = i1 & i2; break;
+ case C_OR: ret = i1 | i2; break;
+ case C_XOR: ret = i1 ^ i2; break;
+ case C_LAND: ret = (i1 && i2); break;
+ case C_LOR: ret = (i1 || i2); break;
+ case C_EQ: ret = (i1 == i2); break;
+ case C_NE: ret = (i1 != i2); break;
+ case C_GT: ret = (i1 > i2); break;
+ case C_GE: ret = (i1 >= i2); break;
+ case C_LT: ret = (i1 < i2); break;
+ case C_LE: ret = (i1 <= i2); break;
+ case C_R_SHIFT: ret = i1>>i2; break;
+ case C_L_SHIFT: ret = i1<<i2; break;
+ case C_DIV:
+ case C_MOD:
+ if( i2 == 0 )
+ {
+ ShowError("script:op_2num: division by zero detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+ else if( op == C_DIV )
+ ret = i1 / i2;
+ else//if( op == C_MOD )
+ ret = i1 % i2;
+ break;
+ default:
+ switch( op )
+ {// operators that can overflow/underflow
+ case C_ADD: ret = i1 + i2; ret_double = (double)i1 + (double)i2; break;
+ case C_SUB: ret = i1 - i2; ret_double = (double)i1 - (double)i2; break;
+ case C_MUL: ret = i1 * i2; ret_double = (double)i1 * (double)i2; break;
+ default:
+ ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ script_pushnil(st);
+ return;
+ }
+ if( ret_double < (double)INT_MIN )
+ {
+ ShowWarning("script:op_2num: underflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ ret = INT_MIN;
+ }
+ else if( ret_double > (double)INT_MAX )
+ {
+ ShowWarning("script:op_2num: overflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ ret = INT_MAX;
+ }
+ }
+ script_pushint(st, ret);
+}
+
+/// Binary operators
+void op_2(struct script_state *st, int op)
+{
+ struct script_data* left, leftref;
+ struct script_data* right;
+
+ leftref.type = C_NOP;
+
+ left = script_getdatatop(st, -2);
+ right = script_getdatatop(st, -1);
+
+ if (st->op2ref) {
+ if (data_isreference(left)) {
+ leftref = *left;
+ }
+
+ st->op2ref = 0;
+ }
+
+ get_val(st, left);
+ get_val(st, right);
+
+ // automatic conversions
+ switch( op )
+ {
+ case C_ADD:
+ if( data_isint(left) && data_isstring(right) )
+ {// convert int-string to string-string
+ conv_str(st, left);
+ }
+ else if( data_isstring(left) && data_isint(right) )
+ {// convert string-int to string-string
+ conv_str(st, right);
+ }
+ break;
+ }
+
+ if( data_isstring(left) && data_isstring(right) )
+ {// ss => op_2str
+ op_2str(st, op, left->u.str, right->u.str);
+ script_removetop(st, leftref.type == C_NOP ? -3 : -2, -1);// pop the two values before the top one
+
+ if (leftref.type != C_NOP)
+ {
+ aFree(left->u.str);
+ *left = leftref;
+ }
+ }
+ else if( data_isint(left) && data_isint(right) )
+ {// ii => op_2num
+ int i1 = left->u.num;
+ int i2 = right->u.num;
+
+ script_removetop(st, leftref.type == C_NOP ? -2 : -1, 0);
+ op_2num(st, op, i1, i2);
+
+ if (leftref.type != C_NOP)
+ *left = leftref;
+ }
+ else
+ {// invalid argument
+ ShowError("script:op_2: invalid data for operator %s\n", script_op2name(op));
+ script_reportdata(left);
+ script_reportdata(right);
+ script_reportsrc(st);
+ script_removetop(st, -2, 0);
+ script_pushnil(st);
+ st->state = END;
+ }
+}
+
+/// Unary operators
+/// NEG i -> i
+/// NOT i -> i
+/// LNOT i -> i
+void op_1(struct script_state* st, int op)
+{
+ struct script_data* data;
+ int i1;
+
+ data = script_getdatatop(st, -1);
+ get_val(st, data);
+
+ if( !data_isint(data) )
+ {// not a number
+ ShowError("script:op_1: argument is not a number (op=%s)\n", script_op2name(op));
+ script_reportdata(data);
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+
+ i1 = data->u.num;
+ script_removetop(st, -1, 0);
+ switch( op )
+ {
+ case C_NEG: i1 = -i1; break;
+ case C_NOT: i1 = ~i1; break;
+ case C_LNOT: i1 = !i1; break;
+ default:
+ ShowError("script:op_1: unexpected operator %s i1=%d\n", script_op2name(op), i1);
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+ script_pushint(st, i1);
+}
+
+
+/// Checks the type of all arguments passed to a built-in function.
+///
+/// @param st Script state whose stack arguments should be inspected.
+/// @param func Built-in function for which the arguments are intended.
+static void script_check_buildin_argtype(struct script_state* st, int func)
+{
+ char type;
+ int idx, invalid = 0;
+ script_function* sf = &buildin_func[str_data[func].val];
+
+ for( idx = 2; script_hasdata(st, idx); idx++ )
+ {
+ struct script_data* data = script_getdata(st, idx);
+
+ type = sf->arg[idx-2];
+
+ if( type == '?' || type == '*' )
+ {// optional argument or unknown number of optional parameters ( no types are after this )
+ break;
+ }
+ else if( type == 0 )
+ {// more arguments than necessary ( should not happen, as it is checked before )
+ ShowWarning("Found more arguments than necessary. unexpected arg type %s\n",script_op2name(data->type));
+ invalid++;
+ break;
+ }
+ else
+ {
+ const char* name = NULL;
+
+ if( data_isreference(data) )
+ {// get name for variables to determine the type they refer to
+ name = reference_getname(data);
+ }
+
+ switch( type )
+ {
+ case 'v':
+ if( !data_isstring(data) && !data_isint(data) && !data_isreference(data) )
+ {// variant
+ ShowWarning("Unexpected type for argument %d. Expected string, number or variable.\n", idx-1);
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 's':
+ if( !data_isstring(data) && !( data_isreference(data) && is_string_variable(name) ) )
+ {// string
+ ShowWarning("Unexpected type for argument %d. Expected string.\n", idx-1);
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 'i':
+ if( !data_isint(data) && !( data_isreference(data) && ( reference_toparam(data) || reference_toconstant(data) || !is_string_variable(name) ) ) )
+ {// int ( params and constants are always int )
+ ShowWarning("Unexpected type for argument %d. Expected number.\n", idx-1);
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 'r':
+ if( !data_isreference(data) )
+ {// variables
+ ShowWarning("Unexpected type for argument %d. Expected variable, got %s.\n", idx-1,script_op2name(data->type));
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 'l':
+ if( !data_islabel(data) && !data_isfunclabel(data) )
+ {// label
+ ShowWarning("Unexpected type for argument %d. Expected label, got %s\n", idx-1,script_op2name(data->type));
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ }
+ }
+ }
+
+ if(invalid)
+ {
+ ShowDebug("Function: %s\n", get_str(func));
+ script_reportsrc(st);
+ }
+}
+
+
+/// Executes a buildin command.
+/// Stack: C_NAME(<command>) C_ARG <arg0> <arg1> ... <argN>
+int run_func(struct script_state *st)
+{
+ struct script_data* data;
+ int i,start_sp,end_sp,func;
+
+ end_sp = st->stack->sp;// position after the last argument
+ for( i = end_sp-1; i > 0 ; --i )
+ if( st->stack->stack_data[i].type == C_ARG )
+ break;
+ if( i == 0 )
+ {
+ ShowError("script:run_func: C_ARG not found. please report this!!!\n");
+ st->state = END;
+ script_reportsrc(st);
+ return 1;
+ }
+ start_sp = i-1;// C_NAME of the command
+ st->start = start_sp;
+ st->end = end_sp;
+
+ data = &st->stack->stack_data[st->start];
+ if( data->type == C_NAME && str_data[data->u.num].type == C_FUNC )
+ func = data->u.num;
+ else
+ {
+ ShowError("script:run_func: not a buildin command.\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ st->state = END;
+ return 1;
+ }
+
+ if( script_config.warn_func_mismatch_argtypes )
+ {
+ script_check_buildin_argtype(st, func);
+ }
+
+ if(str_data[func].func){
+ if (str_data[func].func(st)) //Report error
+ script_reportsrc(st);
+ } else {
+ ShowError("script:run_func: '%s' (id=%d type=%s) has no C function. please report this!!!\n", get_str(func), func, script_op2name(str_data[func].type));
+ script_reportsrc(st);
+ st->state = END;
+ }
+
+ // Stack's datum are used when re-running functions [Eoe]
+ if( st->state == RERUNLINE )
+ return 0;
+
+ pop_stack(st, st->start, st->end);
+ if( st->state == RETFUNC )
+ {// return from a user-defined function
+ struct script_retinfo* ri;
+ int olddefsp = st->stack->defsp;
+ int nargs;
+
+ pop_stack(st, st->stack->defsp, st->start);// pop distractions from the stack
+ if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp-1].type != C_RETINFO )
+ {
+ ShowWarning("script:run_func: return without callfunc or callsub!\n");
+ script_reportsrc(st);
+ st->state = END;
+ return 1;
+ }
+ script_free_vars( st->stack->var_function );
+
+ ri = st->stack->stack_data[st->stack->defsp-1].u.ri;
+ nargs = ri->nargs;
+ st->pos = ri->pos;
+ st->script = ri->script;
+ st->stack->var_function = ri->var_function;
+ st->stack->defsp = ri->defsp;
+ memset(ri, 0, sizeof(struct script_retinfo));
+
+ pop_stack(st, olddefsp-nargs-1, olddefsp);// pop arguments and retinfo
+
+ st->state = GOTO;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * script execution
+ *------------------------------------------*/
+void run_script(struct script_code *rootscript,int pos,int rid,int oid)
+{
+ struct script_state *st;
+
+ if( rootscript == NULL || pos < 0 )
+ return;
+
+ // TODO In jAthena, this function can take over the pending script in the player. [FlavioJS]
+ // It is unclear how that can be triggered, so it needs the be traced/checked in more detail.
+ // NOTE At the time of this change, this function wasn't capable of taking over the script state because st->scriptroot was never set.
+ st = script_alloc_state(rootscript, pos, rid, oid);
+ run_script_main(st);
+}
+
+void script_stop_sleeptimers(int id)
+{
+ struct script_state* st;
+ for(;;)
+ {
+ st = (struct script_state*)linkdb_erase(&sleep_db,(void*)__64BPRTSIZE(id));
+ if( st == NULL )
+ break; // no more sleep timers
+ script_free_state(st);
+ }
+}
+
+/*==========================================
+ * Delete the specified node from sleep_db
+ *------------------------------------------*/
+struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n)
+{
+ struct linkdb_node *retnode;
+
+ if( n == NULL)
+ return NULL;
+ if( n->prev == NULL )
+ sleep_db = n->next;
+ else
+ n->prev->next = n->next;
+ if( n->next )
+ n->next->prev = n->prev;
+ retnode = n->next;
+ aFree( n );
+ return retnode; // The following; return retnode
+}
+
+/*==========================================
+ * Timer function for sleep
+ *------------------------------------------*/
+int run_script_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct script_state *st = (struct script_state *)data;
+ struct linkdb_node *node = (struct linkdb_node *)sleep_db;
+ TBL_PC *sd = map_id2sd(st->rid);
+
+ if((sd && sd->status.char_id != id) || (st->rid && !sd))
+ { //Character mismatch. Cancel execution.
+ st->rid = 0;
+ st->state = END;
+ }
+ while( node && st->sleep.timer != INVALID_TIMER ) {
+ if( (int)__64BPRTSIZE(node->key) == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) {
+ script_erase_sleepdb(node);
+ st->sleep.timer = INVALID_TIMER;
+ break;
+ }
+ node = node->next;
+ }
+ if(st->state != RERUNLINE)
+ st->sleep.tick = 0;
+ run_script_main(st);
+ return 0;
+}
+
+/// Detaches script state from possibly attached character and restores it's previous script if any.
+///
+/// @param st Script state to detach.
+/// @param dequeue_event Whether to schedule any queued events, when there was no previous script.
+static void script_detach_state(struct script_state* st, bool dequeue_event)
+{
+ struct map_session_data* sd;
+
+ if(st->rid && (sd = map_id2sd(st->rid))!=NULL)
+ {
+ sd->st = st->bk_st;
+ sd->npc_id = st->bk_npcid;
+ /**
+ * 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.
+ st->bk_st = NULL;
+ st->bk_npcid = 0;
+ }
+ else if(dequeue_event)
+ {
+ npc_event_dequeue(sd);
+ }
+ }
+ else if(st->bk_st)
+ {// rid was set to 0, before detaching the script state
+ ShowError("script_detach_state: Found previous script state without attached player (rid=%d, oid=%d, state=%d, bk_npcid=%d)\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
+ script_reportsrc(st->bk_st);
+
+ script_free_state(st->bk_st);
+ st->bk_st = NULL;
+ }
+}
+
+/// Attaches script state to possibly attached character and backups it's previous script, if any.
+///
+/// @param st Script state to attach.
+static void script_attach_state(struct script_state* st)
+{
+ struct map_session_data* sd;
+
+ if(st->rid && (sd = map_id2sd(st->rid))!=NULL)
+ {
+ if(st!=sd->st)
+ {
+ if(st->bk_st)
+ {// there is already a backup
+ ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
+ }
+ st->bk_st = sd->st;
+ st->bk_npcid = sd->npc_id;
+ }
+ sd->st = st;
+ sd->npc_id = st->oid;
+/**
+ * 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
+ }
+}
+
+/*==========================================
+ * The main part of the script execution
+ *------------------------------------------*/
+void run_script_main(struct script_state *st)
+{
+ int cmdcount = script_config.check_cmdcount;
+ int gotocount = script_config.check_gotocount;
+ TBL_PC *sd;
+ struct script_stack *stack=st->stack;
+ struct npc_data *nd;
+
+ script_attach_state(st);
+
+ nd = map_id2nd(st->oid);
+ if( nd && map[nd->bl.m].instance_id > 0 )
+ st->instance_id = map[nd->bl.m].instance_id;
+
+ if(st->state == RERUNLINE) {
+ run_func(st);
+ if(st->state == GOTO)
+ st->state = RUN;
+ } else if(st->state != END)
+ st->state = RUN;
+
+ while(st->state == RUN)
+ {
+ enum c_op c = get_com(st->script->script_buf,&st->pos);
+ switch(c){
+ case C_EOL:
+ if( stack->defsp > stack->sp )
+ ShowError("script:run_script_main: unexpected stack position (defsp=%d sp=%d). please report this!!!\n", stack->defsp, stack->sp);
+ else
+ pop_stack(st, stack->defsp, stack->sp);// pop unused stack data. (unused return value)
+ break;
+ case C_INT:
+ push_val(stack,C_INT,get_num(st->script->script_buf,&st->pos));
+ break;
+ case C_POS:
+ case C_NAME:
+ push_val(stack,c,GETVALUE(st->script->script_buf,st->pos));
+ st->pos+=3;
+ break;
+ case C_ARG:
+ push_val(stack,c,0);
+ break;
+ case C_STR:
+ push_str(stack,C_CONSTSTR,(char*)(st->script->script_buf+st->pos));
+ while(st->script->script_buf[st->pos++]);
+ break;
+ case C_FUNC:
+ run_func(st);
+ if(st->state==GOTO){
+ st->state = RUN;
+ if( !st->freeloop && gotocount>0 && (--gotocount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ script_reportsrc(st);
+ st->state=END;
+ }
+ }
+ break;
+
+ case C_REF:
+ st->op2ref = 1;
+ break;
+
+ case C_NEG:
+ case C_NOT:
+ case C_LNOT:
+ op_1(st ,c);
+ break;
+
+ case C_ADD:
+ case C_SUB:
+ case C_MUL:
+ case C_DIV:
+ case C_MOD:
+ case C_EQ:
+ case C_NE:
+ case C_GT:
+ case C_GE:
+ case C_LT:
+ case C_LE:
+ case C_AND:
+ case C_OR:
+ case C_XOR:
+ case C_LAND:
+ case C_LOR:
+ case C_R_SHIFT:
+ case C_L_SHIFT:
+ op_2(st, c);
+ break;
+
+ case C_OP3:
+ op_3(st, c);
+ break;
+
+ case C_NOP:
+ st->state=END;
+ break;
+
+ default:
+ ShowError("unknown command : %d @ %d\n",c,st->pos);
+ st->state=END;
+ break;
+ }
+ if( !st->freeloop && cmdcount>0 && (--cmdcount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ script_reportsrc(st);
+ st->state=END;
+ }
+ }
+
+ if(st->sleep.tick > 0) {
+ //Restore previous script
+ script_detach_state(st, false);
+ //Delay execution
+ sd = map_id2sd(st->rid); // Get sd since script might have attached someone while running. [Inkfish]
+ st->sleep.charid = sd?sd->status.char_id:0;
+ st->sleep.timer = add_timer(gettick()+st->sleep.tick,
+ run_script_timer, st->sleep.charid, (intptr_t)st);
+ linkdb_insert(&sleep_db, (void*)__64BPRTSIZE(st->oid), st);
+ }
+ else if(st->state != END && st->rid){
+ //Resume later (st is already attached to player).
+ if(st->bk_st) {
+ ShowWarning("Unable to restore stack! Double continuation!\n");
+ //Report BOTH scripts to see if that can help somehow.
+ ShowDebug("Previous script (lost):\n");
+ script_reportsrc(st->bk_st);
+ ShowDebug("Current script:\n");
+ script_reportsrc(st);
+
+ script_free_state(st->bk_st);
+ st->bk_st = NULL;
+ }
+ } else {
+ //Dispose of script.
+ if ((sd = map_id2sd(st->rid))!=NULL)
+ { //Restore previous stack and save char.
+ if(sd->state.using_fake_npc){
+ clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd);
+ sd->state.using_fake_npc = 0;
+ }
+ //Restore previous script if any.
+ script_detach_state(st, true);
+ if (sd->state.reg_dirty&2)
+ intif_saveregistry(sd,2);
+ if (sd->state.reg_dirty&1)
+ intif_saveregistry(sd,1);
+ }
+ script_free_state(st);
+ st = NULL;
+ }
+}
+
+int script_config_read(char *cfgName)
+{
+ int i;
+ char line[1024],w1[1024],w2[1024];
+ FILE *fp;
+
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+
+ if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) {
+ script_config.warn_func_mismatch_paramnum = config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_cmdcount")==0) {
+ script_config.check_cmdcount = config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_gotocount")==0) {
+ script_config.check_gotocount = config_switch(w2);
+ }
+ else if(strcmpi(w1,"input_min_value")==0) {
+ script_config.input_min_value = config_switch(w2);
+ }
+ else if(strcmpi(w1,"input_max_value")==0) {
+ script_config.input_max_value = config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_func_mismatch_argtypes")==0) {
+ script_config.warn_func_mismatch_argtypes = config_switch(w2);
+ }
+ else if(strcmpi(w1,"import")==0){
+ script_config_read(w2);
+ }
+ else {
+ ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName);
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/**
+ * @see DBApply
+ */
+static int db_script_free_code_sub(DBKey key, DBData *data, va_list ap)
+{
+ struct script_code *code = db_data2ptr(data);
+ if (code)
+ script_free_code(code);
+ return 0;
+}
+
+void script_run_autobonus(const char *autobonus, int id, int pos)
+{
+ struct script_code *script = (struct script_code *)strdb_get(autobonus_db, autobonus);
+
+ if( script )
+ {
+ current_equip_item_index = pos;
+ run_script(script,0,id,0);
+ }
+}
+
+void script_add_autobonus(const char *autobonus)
+{
+ if( strdb_get(autobonus_db, autobonus) == NULL )
+ {
+ struct script_code *script = parse_script(autobonus, "autobonus", 0, 0);
+
+ if( script )
+ strdb_put(autobonus_db, autobonus, script);
+ }
+}
+
+
+/// resets a temporary character array variable to given value
+void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value)
+{
+ int key;
+ uint8 idx;
+
+ if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) )
+ {
+ ShowError("script_cleararray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id);
+ return;
+ }
+
+ key = add_str(varname);
+
+ if( is_string_variable(varname) )
+ {
+ for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ )
+ {
+ pc_setregstr(sd, reference_uid(key, idx), (const char*)value);
+ }
+ }
+ else
+ {
+ for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ )
+ {
+ pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value));
+ }
+ }
+}
+
+
+/// sets a temporary character array variable element idx to given value
+/// @param refcache Pointer to an int variable, which keeps a copy of the reference to varname and must be initialized to 0. Can be NULL if only one element is set.
+void script_setarray_pc(struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache)
+{
+ int key;
+
+ if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) )
+ {
+ ShowError("script_setarray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id);
+ return;
+ }
+
+ if( idx >= SCRIPT_MAX_ARRAYSIZE )
+ {
+ ShowError("script_setarray_pc: Variable '%s' has invalid index '%d' (char_id=%d).\n", varname, (int)idx, sd->status.char_id);
+ return;
+ }
+
+ key = ( refcache && refcache[0] ) ? refcache[0] : add_str(varname);
+
+ if( is_string_variable(varname) )
+ {
+ pc_setregstr(sd, reference_uid(key, idx), (const char*)value);
+ }
+ else
+ {
+ pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value));
+ }
+
+ if( refcache )
+ {// save to avoid repeated add_str calls
+ refcache[0] = key;
+ }
+}
+#ifdef BETA_THREAD_TEST
+int buildin_query_sql_sub(struct script_state* st, Sql* handle);
+
+/* used to receive items the queryThread has already processed */
+int queryThread_timer(int tid, unsigned int tick, int id, intptr_t data) {
+ int i, cursor = 0;
+ bool allOk = true;
+
+ EnterSpinLock(&queryThreadLock);
+
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ struct queryThreadEntry *entry = queryThreadData.entry[i];
+
+ if( !entry->ok ) {
+ allOk = false;
+ continue;
+ }
+
+ run_script_main(entry->st);
+
+ entry->st = NULL;/* empty entries */
+ aFree(entry);
+ queryThreadData.entry[i] = NULL;
+ }
+
+
+ if( allOk ) {
+ /* cancel the repeating timer -- it'll re-create itself when necessary, dont need to remain looping */
+ delete_timer(queryThreadData.timer, queryThread_timer);
+ queryThreadData.timer = INVALID_TIMER;
+ }
+
+ /* now lets clear the mess. */
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ struct queryThreadEntry *entry = queryThreadData.entry[i];
+ if( entry == NULL )
+ continue;/* entry on hold */
+
+ /* move */
+ memmove(&queryThreadData.entry[cursor], &queryThreadData.entry[i], sizeof(struct queryThreadEntry*));
+
+ cursor++;
+ }
+
+ queryThreadData.count = cursor;
+
+ LeaveSpinLock(&queryThreadLock);
+
+ return 0;
+}
+
+void queryThread_add(struct script_state *st, bool type) {
+ int idx = 0;
+ struct queryThreadEntry* entry = NULL;
+
+ EnterSpinLock(&queryThreadLock);
+
+ if( queryThreadData.count++ != 0 )
+ RECREATE(queryThreadData.entry, struct queryThreadEntry* , queryThreadData.count);
+
+ idx = queryThreadData.count-1;
+
+ CREATE(queryThreadData.entry[idx],struct queryThreadEntry,1);
+
+ entry = queryThreadData.entry[idx];
+
+ entry->st = st;
+ entry->ok = false;
+ entry->type = type;
+ if( queryThreadData.timer == INVALID_TIMER ) { /* start the receiver timer */
+ queryThreadData.timer = add_timer_interval(gettick() + 100, queryThread_timer, 0, 0, 100);
+ }
+
+ LeaveSpinLock(&queryThreadLock);
+
+ /* unlock the queryThread */
+ racond_signal(queryThreadCond);
+}
+/* adds a new log to the queue */
+void queryThread_log(char * entry, int length) {
+ int idx = logThreadData.count;
+
+ EnterSpinLock(&queryThreadLock);
+
+ if( logThreadData.count++ != 0 )
+ RECREATE(logThreadData.entry, char* , logThreadData.count);
+
+ CREATE(logThreadData.entry[idx], char, length + 1 );
+ safestrncpy(logThreadData.entry[idx], entry, length + 1 );
+
+ LeaveSpinLock(&queryThreadLock);
+
+ /* unlock the queryThread */
+ racond_signal(queryThreadCond);
+}
+
+/* queryThread_main */
+static void *queryThread_main(void *x) {
+ Sql *queryThread_handle = Sql_Malloc();
+ int i;
+
+ if ( SQL_ERROR == Sql_Connect(queryThread_handle, map_server_id, map_server_pw, map_server_ip, map_server_port, map_server_db) )
+ exit(EXIT_FAILURE);
+
+ if( strlen(default_codepage) > 0 )
+ if ( SQL_ERROR == Sql_SetEncoding(queryThread_handle, default_codepage) )
+ Sql_ShowDebug(queryThread_handle);
+
+ if( log_config.sql_logs ) {
+ logmysql_handle = Sql_Malloc();
+
+ if ( SQL_ERROR == Sql_Connect(logmysql_handle, log_db_id, log_db_pw, log_db_ip, log_db_port, log_db_db) )
+ exit(EXIT_FAILURE);
+
+ if( strlen(default_codepage) > 0 )
+ if ( SQL_ERROR == Sql_SetEncoding(logmysql_handle, default_codepage) )
+ Sql_ShowDebug(logmysql_handle);
+ }
+
+ while( 1 ) {
+
+ if(queryThreadTerminate > 0)
+ break;
+
+ EnterSpinLock(&queryThreadLock);
+
+ /* mess with queryThreadData within the lock */
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ struct queryThreadEntry *entry = queryThreadData.entry[i];
+
+ if( entry->ok )
+ continue;
+ else if ( !entry->st || !entry->st->stack ) {
+ entry->ok = true;/* dispose */
+ continue;
+ }
+
+ buildin_query_sql_sub(entry->st, entry->type ? logmysql_handle : queryThread_handle);
+
+ entry->ok = true;/* we're done with this */
+ }
+
+ /* also check for any logs in need to be sent */
+ if( log_config.sql_logs ) {
+ for( i = 0; i < logThreadData.count; i++ ) {
+ if( SQL_ERROR == Sql_Query(logmysql_handle, logThreadData.entry[i]) )
+ Sql_ShowDebug(logmysql_handle);
+ aFree(logThreadData.entry[i]);
+ }
+ logThreadData.count = 0;
+ }
+
+ LeaveSpinLock(&queryThreadLock);
+
+ ramutex_lock( queryThreadMutex );
+ racond_wait( queryThreadCond, queryThreadMutex, -1 );
+ ramutex_unlock( queryThreadMutex );
+
+ }
+
+ Sql_Free(queryThread_handle);
+
+ if( log_config.sql_logs ) {
+ Sql_Free(logmysql_handle);
+ }
+
+ return NULL;
+}
+#endif
+/*==========================================
+ * Destructor
+ *------------------------------------------*/
+int do_final_script() {
+ int i;
+#ifdef DEBUG_HASH
+ if (battle_config.etc_log)
+ {
+ FILE *fp = fopen("hash_dump.txt","wt");
+ if(fp) {
+ int count[SCRIPT_HASH_SIZE];
+ int count2[SCRIPT_HASH_SIZE]; // number of buckets with a certain number of items
+ int n=0;
+ int min=INT_MAX,max=0,zero=0;
+ double mean=0.0f;
+ double median=0.0f;
+
+ ShowNotice("Dumping script str hash information to hash_dump.txt\n");
+ memset(count, 0, sizeof(count));
+ fprintf(fp,"num : hash : data_name\n");
+ fprintf(fp,"---------------------------------------------------------------\n");
+ for(i=LABEL_START; i<str_num; i++) {
+ unsigned int h = calc_hash(get_str(i));
+ fprintf(fp,"%04d : %4u : %s\n",i,h, get_str(i));
+ ++count[h];
+ }
+ fprintf(fp,"--------------------\n\n");
+ memset(count2, 0, sizeof(count2));
+ for(i=0; i<SCRIPT_HASH_SIZE; i++) {
+ fprintf(fp," hash %3d = %d\n",i,count[i]);
+ if(min > count[i])
+ min = count[i]; // minimun count of collision
+ if(max < count[i])
+ max = count[i]; // maximun count of collision
+ if(count[i] == 0)
+ zero++;
+ ++count2[count[i]];
+ }
+ fprintf(fp,"\n--------------------\n items : buckets\n--------------------\n");
+ for( i=min; i <= max; ++i ){
+ fprintf(fp," %5d : %7d\n",i,count2[i]);
+ mean += 1.0f*i*count2[i]/SCRIPT_HASH_SIZE; // Note: this will always result in <nr labels>/<nr buckets>
+ }
+ for( i=min; i <= max; ++i ){
+ n += count2[i];
+ if( n*2 >= SCRIPT_HASH_SIZE )
+ {
+ if( SCRIPT_HASH_SIZE%2 == 0 && SCRIPT_HASH_SIZE/2 == n )
+ median = (i+i+1)/2.0f;
+ else
+ median = i;
+ break;
+ }
+ }
+ fprintf(fp,"--------------------\n min = %d, max = %d, zero = %d\n mean = %lf, median = %lf\n",min,max,zero,mean,median);
+ fclose(fp);
+ }
+ }
+#endif
+
+ mapreg_final();
+
+ db_destroy(scriptlabel_db);
+ userfunc_db->destroy(userfunc_db, db_script_free_code_sub);
+ autobonus_db->destroy(autobonus_db, db_script_free_code_sub);
+ if(sleep_db) {
+ struct linkdb_node *n = (struct linkdb_node *)sleep_db;
+ while(n) {
+ struct script_state *st = (struct script_state *)n->data;
+ script_free_state(st);
+ n = n->next;
+ }
+ linkdb_final(&sleep_db);
+ }
+
+ if (str_data)
+ aFree(str_data);
+ if (str_buf)
+ aFree(str_buf);
+
+ for( i = 0; i < atcmd_binding_count; i++ ) {
+ aFree(atcmd_binding[i]);
+ }
+
+ if( atcmd_binding_count != 0 )
+ aFree(atcmd_binding);
+#ifdef BETA_THREAD_TEST
+ /* QueryThread */
+ InterlockedIncrement(&queryThreadTerminate);
+ racond_signal(queryThreadCond);
+ rathread_wait(queryThread, NULL);
+
+ // Destroy cond var and mutex.
+ racond_destroy( queryThreadCond );
+ ramutex_destroy( queryThreadMutex );
+
+ /* Clear missing vars */
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ aFree(queryThreadData.entry[i]);
+ }
+
+ aFree(queryThreadData.entry);
+
+ for( i = 0; i < logThreadData.count; i++ ) {
+ aFree(logThreadData.entry[i]);
+ }
+
+ aFree(logThreadData.entry);
+#endif
+
+ return 0;
+}
+/*==========================================
+ * Initialization
+ *------------------------------------------*/
+int do_init_script() {
+ userfunc_db=strdb_alloc(DB_OPT_DUP_KEY,0);
+ scriptlabel_db=strdb_alloc(DB_OPT_DUP_KEY,50);
+ autobonus_db = strdb_alloc(DB_OPT_DUP_KEY,0);
+
+ mapreg_init();
+#ifdef BETA_THREAD_TEST
+ CREATE(queryThreadData.entry, struct queryThreadEntry*, 1);
+ queryThreadData.count = 0;
+ CREATE(logThreadData.entry, char *, 1);
+ logThreadData.count = 0;
+ /* QueryThread Start */
+
+ InitializeSpinLock(&queryThreadLock);
+
+ queryThreadData.timer = INVALID_TIMER;
+ queryThreadTerminate = 0;
+ queryThreadMutex = ramutex_create();
+ queryThreadCond = racond_create();
+
+ queryThread = rathread_create(queryThread_main, NULL);
+
+ if(queryThread == NULL){
+ ShowFatalError("do_init_script: cannot spawn Query Thread.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ add_timer_func_list(queryThread_timer, "queryThread_timer");
+#endif
+ return 0;
+}
+
+int script_reload() {
+ int i;
+
+#ifdef BETA_THREAD_TEST
+ /* we're reloading so any queries undergoing should be...exterminated. */
+ EnterSpinLock(&queryThreadLock);
+
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ aFree(queryThreadData.entry[i]);
+ }
+ queryThreadData.count = 0;
+
+ if( queryThreadData.timer != INVALID_TIMER ) {
+ delete_timer(queryThreadData.timer, queryThread_timer);
+ queryThreadData.timer = INVALID_TIMER;
+ }
+
+ LeaveSpinLock(&queryThreadLock);
+#endif
+
+
+ userfunc_db->clear(userfunc_db, db_script_free_code_sub);
+ db_clear(scriptlabel_db);
+
+ // @commands (script based)
+ // Clear bindings
+ for( i = 0; i < atcmd_binding_count; i++ ) {
+ aFree(atcmd_binding[i]);
+ }
+
+ if( atcmd_binding_count != 0 )
+ aFree(atcmd_binding);
+
+ atcmd_binding_count = 0;
+
+ if(sleep_db) {
+ struct linkdb_node *n = (struct linkdb_node *)sleep_db;
+ while(n) {
+ struct script_state *st = (struct script_state *)n->data;
+ script_free_state(st);
+ n = n->next;
+ }
+ linkdb_final(&sleep_db);
+ }
+ mapreg_reload();
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// buildin functions
+//
+
+#define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args }
+#define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args }
+#define BUILDIN_FUNC(x) int buildin_ ## x (struct script_state* st)
+
+/////////////////////////////////////////////////////////////////////
+// NPC interaction
+//
+
+/// Appends a message to the npc dialog.
+/// If a dialog doesn't exist yet, one is created.
+///
+/// mes "<message>";
+BUILDIN_FUNC(mes)
+{
+ TBL_PC* sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( !script_hasdata(st, 3) )
+ {// only a single line detected in the script
+ clif_scriptmes(sd, st->oid, script_getstr(st, 2));
+ }
+ else
+ {// parse multiple lines as they exist
+ int i;
+
+ for( i = 2; script_hasdata(st, i); i++ )
+ {
+ // send the message to the client
+ clif_scriptmes(sd, st->oid, script_getstr(st, i));
+ }
+ }
+
+ return 0;
+}
+
+/// Displays the button 'next' in the npc dialog.
+/// The dialog text is cleared and the script continues when the button is pressed.
+///
+/// next;
+BUILDIN_FUNC(next)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ st->state = STOP;
+ clif_scriptnext(sd, st->oid);
+ return 0;
+}
+
+/// Ends the script and displays the button 'close' on the npc dialog.
+/// The dialog is closed when the button is pressed.
+///
+/// close;
+BUILDIN_FUNC(close)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ st->state = END;
+ clif_scriptclose(sd, st->oid);
+ return 0;
+}
+
+/// Displays the button 'close' on the npc dialog.
+/// The dialog is closed and the script continues when the button is pressed.
+///
+/// close2;
+BUILDIN_FUNC(close2)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ st->state = STOP;
+ clif_scriptclose(sd, st->oid);
+ return 0;
+}
+
+/// Counts the number of valid and total number of options in 'str'
+/// If max_count > 0 the counting stops when that valid option is reached
+/// total is incremented for each option (NULL is supported)
+static int menu_countoptions(const char* str, int max_count, int* total)
+{
+ int count = 0;
+ int bogus_total;
+
+ if( total == NULL )
+ total = &bogus_total;
+ ++(*total);
+
+ // initial empty options
+ while( *str == ':' )
+ {
+ ++str;
+ ++(*total);
+ }
+ // count menu options
+ while( *str != '\0' )
+ {
+ ++count;
+ --max_count;
+ if( max_count == 0 )
+ break;
+ while( *str != ':' && *str != '\0' )
+ ++str;
+ while( *str == ':' )
+ {
+ ++str;
+ ++(*total);
+ }
+ }
+ return count;
+}
+
+/// Displays a menu with options and goes to the target label.
+/// The script is stopped if cancel is pressed.
+/// Options with no text are not displayed in the client.
+///
+/// Options can be grouped together, separated by the character ':' in the text:
+/// ex: menu "A:B:C",L_target;
+/// All these options go to the specified target label.
+///
+/// The index of the selected option is put in the variable @menu.
+/// Indexes start with 1 and are consistent with grouped and empty options.
+/// ex: menu "A::B",-,"",L_Impossible,"C",-;
+/// // displays "A", "B" and "C", corresponding to indexes 1, 3 and 5
+///
+/// NOTE: the client closes the npc dialog when cancel is pressed
+///
+/// menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...};
+BUILDIN_FUNC(menu)
+{
+ int i;
+ const char* text;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ // TODO detect multiple scripts waiting for input at the same time, and what to do when that happens
+ if( sd->state.menu_or_input == 0 )
+ {
+ struct StringBuf buf;
+ struct script_data* data;
+
+ if( script_lastdata(st) % 2 == 0 )
+ {// argument count is not even (1st argument is at index 2)
+ ShowError("script:menu: illegal number of arguments (%d).\n", (script_lastdata(st) - 1));
+ st->state = END;
+ return 1;
+ }
+
+ StringBuf_Init(&buf);
+ sd->npc_menu = 0;
+ for( i = 2; i < script_lastdata(st); i += 2 )
+ {
+ // menu options
+ text = script_getstr(st, i);
+
+ // target label
+ data = script_getdata(st, i+1);
+ if( !data_islabel(data) )
+ {// not a label
+ StringBuf_Destroy(&buf);
+ ShowError("script:menu: argument #%d (from 1) is not a label or label not found.\n", i);
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+
+ // append option(s)
+ if( text[0] == '\0' )
+ continue;// empty string, ignore
+ if( sd->npc_menu > 0 )
+ StringBuf_AppendStr(&buf, ":");
+ StringBuf_AppendStr(&buf, text);
+ sd->npc_menu += menu_countoptions(text, 0, NULL);
+ }
+ st->state = RERUNLINE;
+ sd->state.menu_or_input = 1;
+
+ /**
+ * menus beyond this length crash the client (see bugreport:6402)
+ **/
+ if( StringBuf_Length(&buf) >= 2047 ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ char* menu;
+ CREATE(menu, char, 2048);
+ safestrncpy(menu, StringBuf_Value(&buf), 2047);
+ ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
+ clif_scriptmenu(sd, st->oid, menu);
+ aFree(menu);
+ } else
+ clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
+
+ StringBuf_Destroy(&buf);
+
+ if( sd->npc_menu >= 0xff )
+ {// client supports only up to 254 entries; 0 is not used and 255 is reserved for cancel; excess entries are displayed but cause 'uint8' overflow
+ ShowWarning("buildin_menu: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
+ script_reportsrc(st);
+ }
+ }
+ else if( sd->npc_menu == 0xff )
+ {// Cancel was pressed
+ sd->state.menu_or_input = 0;
+ st->state = END;
+ }
+ else
+ {// goto target label
+ int menu = 0;
+
+ sd->state.menu_or_input = 0;
+ if( sd->npc_menu <= 0 )
+ {
+ ShowDebug("script:menu: unexpected selection (%d)\n", sd->npc_menu);
+ st->state = END;
+ return 1;
+ }
+
+ // get target label
+ for( i = 2; i < script_lastdata(st); i += 2 )
+ {
+ text = script_getstr(st, i);
+ sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
+ if( sd->npc_menu <= 0 )
+ break;// entry found
+ }
+ if( sd->npc_menu > 0 )
+ {// Invalid selection
+ ShowDebug("script:menu: selection is out of range (%d pairs are missing?) - please report this\n", sd->npc_menu);
+ st->state = END;
+ return 1;
+ }
+ if( !data_islabel(script_getdata(st, i + 1)) )
+ {// TODO remove this temporary crash-prevention code (fallback for multiple scripts requesting user input)
+ ShowError("script:menu: unexpected data in label argument\n");
+ script_reportdata(script_getdata(st, i + 1));
+ st->state = END;
+ return 1;
+ }
+ pc_setreg(sd, add_str("@menu"), menu);
+ st->pos = script_getnum(st, i + 1);
+ st->state = GOTO;
+ }
+ return 0;
+}
+
+/// Displays a menu with options and returns the selected option.
+/// Behaves like 'menu' without the target labels.
+///
+/// select(<option_text>{,<option_text>,...}) -> <selected_option>
+///
+/// @see menu
+BUILDIN_FUNC(select)
+{
+ int i;
+ const char* text;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->state.menu_or_input == 0 ) {
+ struct StringBuf buf;
+
+ StringBuf_Init(&buf);
+ sd->npc_menu = 0;
+ for( i = 2; i <= script_lastdata(st); ++i ) {
+ text = script_getstr(st, i);
+
+ if( sd->npc_menu > 0 )
+ StringBuf_AppendStr(&buf, ":");
+
+ StringBuf_AppendStr(&buf, text);
+ sd->npc_menu += menu_countoptions(text, 0, NULL);
+ }
+
+ st->state = RERUNLINE;
+ sd->state.menu_or_input = 1;
+
+ /**
+ * menus beyond this length crash the client (see bugreport:6402)
+ **/
+ if( StringBuf_Length(&buf) >= 2047 ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ char* menu;
+ CREATE(menu, char, 2048);
+ safestrncpy(menu, StringBuf_Value(&buf), 2047);
+ ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
+ clif_scriptmenu(sd, st->oid, menu);
+ aFree(menu);
+ } else
+ clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
+ StringBuf_Destroy(&buf);
+
+ if( sd->npc_menu >= 0xff ) {
+ ShowWarning("buildin_select: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
+ script_reportsrc(st);
+ }
+ } else if( sd->npc_menu == 0xff ) {// Cancel was pressed
+ sd->state.menu_or_input = 0;
+ st->state = END;
+ } else {// return selected option
+ int menu = 0;
+
+ sd->state.menu_or_input = 0;
+ for( i = 2; i <= script_lastdata(st); ++i ) {
+ text = script_getstr(st, i);
+ sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
+ if( sd->npc_menu <= 0 )
+ break;// entry found
+ }
+ pc_setreg(sd, add_str("@menu"), menu);
+ script_pushint(st, menu);
+ st->state = RUN;
+ }
+ return 0;
+}
+
+/// Displays a menu with options and returns the selected option.
+/// Behaves like 'menu' without the target labels, except when cancel is
+/// pressed.
+/// When cancel is pressed, the script continues and 255 is returned.
+///
+/// prompt(<option_text>{,<option_text>,...}) -> <selected_option>
+///
+/// @see menu
+BUILDIN_FUNC(prompt)
+{
+ int i;
+ const char *text;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->state.menu_or_input == 0 )
+ {
+ struct StringBuf buf;
+
+ StringBuf_Init(&buf);
+ sd->npc_menu = 0;
+ for( i = 2; i <= script_lastdata(st); ++i )
+ {
+ text = script_getstr(st, i);
+ if( sd->npc_menu > 0 )
+ StringBuf_AppendStr(&buf, ":");
+ StringBuf_AppendStr(&buf, text);
+ sd->npc_menu += menu_countoptions(text, 0, NULL);
+ }
+
+ st->state = RERUNLINE;
+ sd->state.menu_or_input = 1;
+
+ /**
+ * menus beyond this length crash the client (see bugreport:6402)
+ **/
+ if( StringBuf_Length(&buf) >= 2047 ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ char* menu;
+ CREATE(menu, char, 2048);
+ safestrncpy(menu, StringBuf_Value(&buf), 2047);
+ ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
+ clif_scriptmenu(sd, st->oid, menu);
+ aFree(menu);
+ } else
+ clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
+ StringBuf_Destroy(&buf);
+
+ if( sd->npc_menu >= 0xff )
+ {
+ ShowWarning("buildin_prompt: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
+ script_reportsrc(st);
+ }
+ }
+ else if( sd->npc_menu == 0xff )
+ {// Cancel was pressed
+ sd->state.menu_or_input = 0;
+ pc_setreg(sd, add_str("@menu"), 0xff);
+ script_pushint(st, 0xff);
+ st->state = RUN;
+ }
+ else
+ {// return selected option
+ int menu = 0;
+
+ sd->state.menu_or_input = 0;
+ for( i = 2; i <= script_lastdata(st); ++i )
+ {
+ text = script_getstr(st, i);
+ sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
+ if( sd->npc_menu <= 0 )
+ break;// entry found
+ }
+ pc_setreg(sd, add_str("@menu"), menu);
+ script_pushint(st, menu);
+ st->state = RUN;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// ...
+//
+
+/// Jumps to the target script label.
+///
+/// goto <label>;
+BUILDIN_FUNC(goto)
+{
+ if( !data_islabel(script_getdata(st,2)) )
+ {
+ ShowError("script:goto: not a label\n");
+ script_reportdata(script_getdata(st,2));
+ st->state = END;
+ return 1;
+ }
+
+ st->pos = script_getnum(st,2);
+ st->state = GOTO;
+ return 0;
+}
+
+/*==========================================
+ * user-defined function call
+ *------------------------------------------*/
+BUILDIN_FUNC(callfunc)
+{
+ int i, j;
+ struct script_retinfo* ri;
+ struct script_code* scr;
+ const char* str = script_getstr(st,2);
+ DBMap **ref = NULL;
+
+ scr = (struct script_code*)strdb_get(userfunc_db, str);
+ if( !scr )
+ {
+ ShowError("script:callfunc: function not found! [%s]\n", str);
+ st->state = END;
+ return 1;
+ }
+
+ for( i = st->start+3, j = 0; i < st->end; i++, j++ )
+ {
+ struct script_data* data = push_copy(st->stack,i);
+ if( data_isreference(data) && !data->ref )
+ {
+ const char* name = reference_getname(data);
+ if( name[0] == '.' ) {
+ if ( !ref ) {
+ ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1);
+ ref[0] = (name[1] == '@' ? st->stack->var_function : st->script->script_vars);
+ }
+ data->ref = ref;
+ }
+ }
+ }
+
+ CREATE(ri, struct script_retinfo, 1);
+ ri->script = st->script;// script code
+ ri->var_function = st->stack->var_function;// scope variables
+ ri->pos = st->pos;// script location
+ ri->nargs = j;// argument count
+ ri->defsp = st->stack->defsp;// default stack pointer
+ push_retinfo(st->stack, ri, ref);
+
+ st->pos = 0;
+ st->script = scr;
+ st->stack->defsp = st->stack->sp;
+ st->state = GOTO;
+ st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
+
+ return 0;
+}
+/*==========================================
+ * subroutine call
+ *------------------------------------------*/
+BUILDIN_FUNC(callsub)
+{
+ int i,j;
+ struct script_retinfo* ri;
+ int pos = script_getnum(st,2);
+ DBMap **ref = NULL;
+
+ if( !data_islabel(script_getdata(st,2)) && !data_isfunclabel(script_getdata(st,2)) )
+ {
+ ShowError("script:callsub: argument is not a label\n");
+ script_reportdata(script_getdata(st,2));
+ st->state = END;
+ return 1;
+ }
+
+ for( i = st->start+3, j = 0; i < st->end; i++, j++ )
+ {
+ struct script_data* data = push_copy(st->stack,i);
+ if( data_isreference(data) && !data->ref )
+ {
+ const char* name = reference_getname(data);
+ if( name[0] == '.' && name[1] == '@' ) {
+ if ( !ref ) {
+ ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1);
+ ref[0] = st->stack->var_function;
+ }
+ data->ref = ref;
+ }
+ }
+ }
+
+ CREATE(ri, struct script_retinfo, 1);
+ ri->script = st->script;// script code
+ ri->var_function = st->stack->var_function;// scope variables
+ ri->pos = st->pos;// script location
+ ri->nargs = j;// argument count
+ ri->defsp = st->stack->defsp;// default stack pointer
+ push_retinfo(st->stack, ri, ref);
+
+ st->pos = pos;
+ st->stack->defsp = st->stack->sp;
+ st->state = GOTO;
+ st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
+
+ return 0;
+}
+
+/// Retrieves an argument provided to callfunc/callsub.
+/// If the argument doesn't exist
+///
+/// getarg(<index>{,<default_value>}) -> <value>
+BUILDIN_FUNC(getarg)
+{
+ struct script_retinfo* ri;
+ int idx;
+
+ if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO )
+ {
+ ShowError("script:getarg: no callfunc or callsub!\n");
+ st->state = END;
+ return 1;
+ }
+ ri = st->stack->stack_data[st->stack->defsp - 1].u.ri;
+
+ idx = script_getnum(st,2);
+
+ if( idx >= 0 && idx < ri->nargs )
+ push_copy(st->stack, st->stack->defsp - 1 - ri->nargs + idx);
+ else if( script_hasdata(st,3) )
+ script_pushcopy(st, 3);
+ else
+ {
+ ShowError("script:getarg: index (idx=%d) out of range (nargs=%d) and no default value found\n", idx, ri->nargs);
+ st->state = END;
+ return 1;
+ }
+
+ return 0;
+}
+
+/// Returns from the current function, optionaly returning a value from the functions.
+/// Don't use outside script functions.
+///
+/// return;
+/// return <value>;
+BUILDIN_FUNC(return)
+{
+ if( script_hasdata(st,2) )
+ {// return value
+ struct script_data* data;
+ script_pushcopy(st, 2);
+ data = script_getdatatop(st, -1);
+ if( data_isreference(data) )
+ {
+ const char* name = reference_getname(data);
+ if( name[0] == '.' && name[1] == '@' )
+ {// scope variable
+ if( !data->ref || data->ref == (DBMap**)&st->stack->var_function )
+ get_val(st, data);// current scope, convert to value
+ }
+ else if( name[0] == '.' && !data->ref )
+ {// script variable, link to current script
+ data->ref = &st->script->script_vars;
+ }
+ }
+ }
+ else
+ {// no return value
+ script_pushnil(st);
+ }
+ st->state = RETFUNC;
+ return 0;
+}
+
+/// Returns a random number from 0 to <range>-1.
+/// Or returns a random number from <min> to <max>.
+/// If <min> is greater than <max>, their numbers are switched.
+/// rand(<range>) -> <int>
+/// rand(<min>,<max>) -> <int>
+BUILDIN_FUNC(rand)
+{
+ int range;
+ int min;
+ int max;
+
+ if( script_hasdata(st,3) )
+ {// min,max
+ min = script_getnum(st,2);
+ max = script_getnum(st,3);
+ if( max < min )
+ swap(min, max);
+ range = max - min + 1;
+ }
+ else
+ {// range
+ min = 0;
+ range = script_getnum(st,2);
+ }
+ if( range <= 1 )
+ script_pushint(st, min);
+ else
+ script_pushint(st, rnd()%range + min);
+
+ return 0;
+}
+
+/*==========================================
+ * Warp sd to str,x,y or Random or SavePoint/Save
+ *------------------------------------------*/
+BUILDIN_FUNC(warp)
+{
+ int ret;
+ int x,y;
+ const char* str;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ str = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+
+ if(strcmp(str,"Random")==0)
+ ret = pc_randomwarp(sd,CLR_TELEPORT);
+ else if(strcmp(str,"SavePoint")==0 || strcmp(str,"Save")==0)
+ ret = pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ else
+ ret = pc_setpos(sd,mapindex_name2id(str),x,y,CLR_OUTSIGHT);
+
+ if( ret ) {
+ ShowError("buildin_warp: moving player '%s' to \"%s\",%d,%d failed.\n", sd->status.name, str, x, y);
+ script_reportsrc(st);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Warp a specified area
+ *------------------------------------------*/
+static int buildin_areawarp_sub(struct block_list *bl,va_list ap)
+{
+ int x2,y2,x3,y3;
+ unsigned int index;
+
+ index = va_arg(ap,unsigned int);
+ x2 = va_arg(ap,int);
+ y2 = va_arg(ap,int);
+ x3 = va_arg(ap,int);
+ y3 = va_arg(ap,int);
+
+ if(index == 0)
+ pc_randomwarp((TBL_PC *)bl,CLR_TELEPORT);
+ else if(x3 && y3) {
+ int max, tx, ty, j = 0;
+
+ // choose a suitable max number of attempts
+ if( (max = (y3-y2+1)*(x3-x2+1)*3) > 1000 )
+ max = 1000;
+
+ // find a suitable map cell
+ do {
+ tx = rnd()%(x3-x2+1)+x2;
+ ty = rnd()%(y3-y2+1)+y2;
+ j++;
+ } while( map_getcell(index,tx,ty,CELL_CHKNOPASS) && j < max );
+
+ pc_setpos((TBL_PC *)bl,index,tx,ty,CLR_OUTSIGHT);
+ }
+ else
+ pc_setpos((TBL_PC *)bl,index,x2,y2,CLR_OUTSIGHT);
+ return 0;
+}
+BUILDIN_FUNC(areawarp)
+{
+ int16 m, x0,y0,x1,y1, x2,y2,x3=0,y3=0;
+ unsigned int index;
+ const char *str;
+ const char *mapname;
+
+ mapname = script_getstr(st,2);
+ x0 = script_getnum(st,3);
+ y0 = script_getnum(st,4);
+ x1 = script_getnum(st,5);
+ y1 = script_getnum(st,6);
+ str = script_getstr(st,7);
+ x2 = script_getnum(st,8);
+ y2 = script_getnum(st,9);
+
+ if( script_hasdata(st,10) && script_hasdata(st,11) ) { // Warp area to area
+ if( (x3 = script_getnum(st,10)) < 0 || (y3 = script_getnum(st,11)) < 0 ){
+ x3 = 0;
+ y3 = 0;
+ } else if( x3 && y3 ) {
+ // normalize x3/y3 coordinates
+ if( x3 < x2 ) swap(x3,x2);
+ if( y3 < y2 ) swap(y3,y2);
+ }
+ }
+
+ if( (m = map_mapname2mapid(mapname)) < 0 )
+ return 0;
+
+ if( strcmp(str,"Random") == 0 )
+ index = 0;
+ else if( !(index=mapindex_name2id(str)) )
+ return 0;
+
+ map_foreachinarea(buildin_areawarp_sub, m,x0,y0,x1,y1, BL_PC, index,x2,y2,x3,y3);
+ return 0;
+}
+
+/*==========================================
+ * areapercentheal <map>,<x1>,<y1>,<x2>,<y2>,<hp>,<sp>
+ *------------------------------------------*/
+static int buildin_areapercentheal_sub(struct block_list *bl,va_list ap)
+{
+ int hp, sp;
+ hp = va_arg(ap, int);
+ sp = va_arg(ap, int);
+ pc_percentheal((TBL_PC *)bl,hp,sp);
+ return 0;
+}
+BUILDIN_FUNC(areapercentheal)
+{
+ int hp,sp,m;
+ const char *mapname;
+ int x0,y0,x1,y1;
+
+ mapname=script_getstr(st,2);
+ x0=script_getnum(st,3);
+ y0=script_getnum(st,4);
+ x1=script_getnum(st,5);
+ y1=script_getnum(st,6);
+ hp=script_getnum(st,7);
+ sp=script_getnum(st,8);
+
+ if( (m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ map_foreachinarea(buildin_areapercentheal_sub,m,x0,y0,x1,y1,BL_PC,hp,sp);
+ return 0;
+}
+
+/*==========================================
+ * warpchar [LuzZza]
+ * Useful for warp one player from
+ * another player npc-session.
+ * Using: warpchar "mapname",x,y,Char_ID;
+ *------------------------------------------*/
+BUILDIN_FUNC(warpchar)
+{
+ int x,y,a;
+ const char *str;
+ TBL_PC *sd;
+
+ str=script_getstr(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+ a=script_getnum(st,5);
+
+ sd = map_charid2sd(a);
+ if( sd == NULL )
+ return 0;
+
+ if(strcmp(str, "Random") == 0)
+ pc_randomwarp(sd, CLR_TELEPORT);
+ else
+ if(strcmp(str, "SavePoint") == 0)
+ pc_setpos(sd, sd->status.save_point.map,sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
+ else
+ pc_setpos(sd, mapindex_name2id(str), x, y, CLR_TELEPORT);
+
+ return 0;
+}
+/*==========================================
+ * Warpparty - [Fredzilla] [Paradox924X]
+ * Syntax: warpparty "to_mapname",x,y,Party_ID,{"from_mapname"};
+ * If 'from_mapname' is specified, only the party members on that map will be warped
+ *------------------------------------------*/
+BUILDIN_FUNC(warpparty)
+{
+ TBL_PC *sd = NULL;
+ TBL_PC *pl_sd;
+ struct party_data* p;
+ int type;
+ int mapindex;
+ int i;
+
+ const char* str = script_getstr(st,2);
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ int p_id = script_getnum(st,5);
+ const char* str2 = NULL;
+ if ( script_hasdata(st,6) )
+ str2 = script_getstr(st,6);
+
+ p = party_search(p_id);
+ if(!p)
+ return 0;
+
+ type = ( strcmp(str,"Random")==0 ) ? 0
+ : ( strcmp(str,"SavePointAll")==0 ) ? 1
+ : ( strcmp(str,"SavePoint")==0 ) ? 2
+ : ( strcmp(str,"Leader")==0 ) ? 3
+ : 4;
+
+ switch (type)
+ {
+ case 3:
+ for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++);
+ if (i == MAX_PARTY || !p->data[i].sd) //Leader not found / not online
+ return 0;
+ pl_sd = p->data[i].sd;
+ mapindex = pl_sd->mapindex;
+ x = pl_sd->bl.x;
+ y = pl_sd->bl.y;
+ break;
+ case 4:
+ mapindex = mapindex_name2id(str);
+ break;
+ case 2:
+ //"SavePoint" uses save point of the currently attached player
+ if (( sd = script_rid2sd(st) ) == NULL )
+ return 0;
+ default:
+ mapindex = 0;
+ break;
+ }
+
+ for (i = 0; i < MAX_PARTY; i++)
+ {
+ if( !(pl_sd = p->data[i].sd) || pl_sd->status.party_id != p_id )
+ continue;
+
+ if( str2 && strcmp(str2, map[pl_sd->bl.m].name) != 0 )
+ continue;
+
+ if( pc_isdead(pl_sd) )
+ continue;
+
+ switch( type )
+ {
+ case 0: // Random
+ if(!map[pl_sd->bl.m].flag.nowarp)
+ pc_randomwarp(pl_sd,CLR_TELEPORT);
+ break;
+ case 1: // SavePointAll
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 2: // SavePoint
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 3: // Leader
+ case 4: // m,x,y
+ if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp)
+ pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
+ break;
+ }
+ }
+
+ return 0;
+}
+/*==========================================
+ * Warpguild - [Fredzilla]
+ * Syntax: warpguild "mapname",x,y,Guild_ID;
+ *------------------------------------------*/
+BUILDIN_FUNC(warpguild)
+{
+ TBL_PC *sd = NULL;
+ TBL_PC *pl_sd;
+ struct guild* g;
+ struct s_mapiterator* iter;
+ int type;
+
+ const char* str = script_getstr(st,2);
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ int gid = script_getnum(st,5);
+
+ g = guild_search(gid);
+ if( g == NULL )
+ return 0;
+
+ type = ( strcmp(str,"Random")==0 ) ? 0
+ : ( strcmp(str,"SavePointAll")==0 ) ? 1
+ : ( strcmp(str,"SavePoint")==0 ) ? 2
+ : 3;
+
+ if( type == 2 && ( sd = script_rid2sd(st) ) == NULL )
+ {// "SavePoint" uses save point of the currently attached player
+ return 0;
+ }
+
+ iter = mapit_getallusers();
+ for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
+ {
+ if( pl_sd->status.guild_id != gid )
+ continue;
+
+ switch( type )
+ {
+ case 0: // Random
+ if(!map[pl_sd->bl.m].flag.nowarp)
+ pc_randomwarp(pl_sd,CLR_TELEPORT);
+ break;
+ case 1: // SavePointAll
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 2: // SavePoint
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 3: // m,x,y
+ if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp)
+ pc_setpos(pl_sd,mapindex_name2id(str),x,y,CLR_TELEPORT);
+ break;
+ }
+ }
+ mapit_free(iter);
+
+ return 0;
+}
+/*==========================================
+ * Force Heal a player (hp and sp)
+ *------------------------------------------*/
+BUILDIN_FUNC(heal)
+{
+ TBL_PC *sd;
+ int hp,sp;
+
+ sd = script_rid2sd(st);
+ if (!sd) return 0;
+
+ hp=script_getnum(st,2);
+ sp=script_getnum(st,3);
+ status_heal(&sd->bl, hp, sp, 1);
+ return 0;
+}
+/*==========================================
+ * Heal a player by item (get vit bonus etc)
+ *------------------------------------------*/
+BUILDIN_FUNC(itemheal)
+{
+ TBL_PC *sd;
+ int hp,sp;
+
+ hp=script_getnum(st,2);
+ sp=script_getnum(st,3);
+
+ if(potion_flag==1) {
+ potion_hp = hp;
+ potion_sp = sp;
+ return 0;
+ }
+
+ sd = script_rid2sd(st);
+ if (!sd) return 0;
+ pc_itemheal(sd,sd->itemid,hp,sp);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(percentheal)
+{
+ int hp,sp;
+ TBL_PC* sd;
+
+ hp=script_getnum(st,2);
+ sp=script_getnum(st,3);
+
+ if(potion_flag==1) {
+ potion_per_hp = hp;
+ potion_per_sp = sp;
+ return 0;
+ }
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+#ifdef RENEWAL
+ if( sd->sc.data[SC_EXTREMITYFIST2] )
+ sp = 0;
+#endif
+ pc_percentheal(sd,hp,sp);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(jobchange)
+{
+ int job, upper=-1;
+
+ job=script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ upper=script_getnum(st,3);
+
+ if (pcdb_checkid(job))
+ {
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_jobchange(sd, job, upper);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(jobname)
+{
+ int class_=script_getnum(st,2);
+ script_pushconststr(st, (char*)job_name(class_));
+ return 0;
+}
+
+/// Get input from the player.
+/// For numeric inputs the value is capped to the range [min,max]. Returns 1 if
+/// the value was higher than 'max', -1 if lower than 'min' and 0 otherwise.
+/// For string inputs it returns 1 if the string was longer than 'max', -1 is
+/// shorter than 'min' and 0 otherwise.
+///
+/// input(<var>{,<min>{,<max>}}) -> <int>
+BUILDIN_FUNC(input)
+{
+ TBL_PC* sd;
+ struct script_data* data;
+ int uid;
+ const char* name;
+ int min;
+ int max;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ data = script_getdata(st,2);
+ if( !data_isreference(data) ){
+ ShowError("script:input: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+ uid = reference_getuid(data);
+ name = reference_getname(data);
+ min = (script_hasdata(st,3) ? script_getnum(st,3) : script_config.input_min_value);
+ max = (script_hasdata(st,4) ? script_getnum(st,4) : script_config.input_max_value);
+
+ if( !sd->state.menu_or_input )
+ { // first invocation, display npc input box
+ sd->state.menu_or_input = 1;
+ st->state = RERUNLINE;
+ if( is_string_variable(name) )
+ clif_scriptinputstr(sd,st->oid);
+ else
+ clif_scriptinput(sd,st->oid);
+ }
+ else
+ { // take received text/value and store it in the designated variable
+ sd->state.menu_or_input = 0;
+ if( is_string_variable(name) )
+ {
+ int len = (int)strlen(sd->npc_str);
+ set_reg(st, sd, uid, name, (void*)sd->npc_str, script_getref(st,2));
+ script_pushint(st, (len > max ? 1 : len < min ? -1 : 0));
+ }
+ else
+ {
+ int amount = sd->npc_amount;
+ set_reg(st, sd, uid, name, (void*)__64BPRTSIZE(cap_value(amount,min,max)), script_getref(st,2));
+ script_pushint(st, (amount > max ? 1 : amount < min ? -1 : 0));
+ }
+ st->state = RUN;
+ }
+ return 0;
+}
+
+// declare the copyarray method here for future reference
+BUILDIN_FUNC(copyarray);
+
+/// Sets the value of a variable.
+/// The value is converted to the type of the variable.
+///
+/// set(<variable>,<value>) -> <variable>
+BUILDIN_FUNC(set)
+{
+ TBL_PC* sd = NULL;
+ struct script_data* data;
+ //struct script_data* datavalue;
+ int num;
+ const char* name;
+ char prefix;
+
+ data = script_getdata(st,2);
+ //datavalue = script_getdata(st,3);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:set: not a variable\n");
+ script_reportdata(script_getdata(st,2));
+ st->state = END;
+ return 1;
+ }
+
+ num = reference_getuid(data);
+ name = reference_getname(data);
+ prefix = *name;
+
+ if( not_server_variable(prefix) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ {
+ ShowError("script:set: no player attached for player variable '%s'\n", name);
+ return 0;
+ }
+ }
+
+#if 0
+ if( data_isreference(datavalue) )
+ {// the value being referenced is a variable
+ const char* namevalue = reference_getname(datavalue);
+
+ if( !not_array_variable(*namevalue) )
+ {// array variable being copied into another array variable
+ if( sd == NULL && not_server_variable(*namevalue) && !(sd = script_rid2sd(st)) )
+ {// player must be attached in order to copy a player variable
+ ShowError("script:set: no player attached for player variable '%s'\n", namevalue);
+ return 0;
+ }
+
+ if( is_string_variable(namevalue) != is_string_variable(name) )
+ {// non-matching array value types
+ ShowWarning("script:set: two array variables do not match in type.\n");
+ return 0;
+ }
+
+ // push the maximum number of array values to the stack
+ push_val(st->stack, C_INT, SCRIPT_MAX_ARRAYSIZE);
+
+ // call the copy array method directly
+ return buildin_copyarray(st);
+ }
+ }
+#endif
+
+ if( is_string_variable(name) )
+ set_reg(st,sd,num,name,(void*)script_getstr(st,3),script_getref(st,2));
+ else
+ set_reg(st,sd,num,name,(void*)__64BPRTSIZE(script_getnum(st,3)),script_getref(st,2));
+
+ // return a copy of the variable reference
+ script_pushcopy(st,2);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+/// Array variables
+///
+
+/// Returns the size of the specified array
+static int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isstring, struct DBMap** ref)
+{
+ int32 ret = idx;
+
+ if( isstring )
+ {
+ for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx )
+ {
+ char* str = (char*)get_val2(st, reference_uid(id, idx), ref);
+ if( str && *str )
+ ret = idx + 1;
+ script_removetop(st, -1, 0);
+ }
+ }
+ else
+ {
+ for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx )
+ {
+ int32 num = (int32)__64BPRTSIZE(get_val2(st, reference_uid(id, idx), ref));
+ if( num )
+ ret = idx + 1;
+ script_removetop(st, -1, 0);
+ }
+ }
+ return ret;
+}
+
+/// Sets values of an array, from the starting index.
+/// ex: setarray arr[1],1,2,3;
+///
+/// setarray <array variable>,<value1>{,<value2>...};
+BUILDIN_FUNC(setarray)
+{
+ struct script_data* data;
+ const char* name;
+ int32 start;
+ int32 end;
+ int32 id;
+ int32 i;
+ TBL_PC* sd = NULL;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:setarray: 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:setarray: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ end = start + script_lastdata(st) - 2;
+ if( end > SCRIPT_MAX_ARRAYSIZE )
+ end = SCRIPT_MAX_ARRAYSIZE;
+
+ if( is_string_variable(name) )
+ {// string array
+ for( i = 3; start < end; ++start, ++i )
+ set_reg(st, sd, reference_uid(id, start), name, (void*)script_getstr(st,i), reference_getref(data));
+ }
+ else
+ {// int array
+ for( i = 3; start < end; ++start, ++i )
+ set_reg(st, sd, reference_uid(id, start), name, (void*)__64BPRTSIZE(script_getnum(st,i)), reference_getref(data));
+ }
+ return 0;
+}
+
+/// Sets count values of an array, from the starting index.
+/// ex: cleararray arr[0],0,1;
+///
+/// cleararray <array variable>,<value>,<count>;
+BUILDIN_FUNC(cleararray)
+{
+ struct script_data* data;
+ const char* name;
+ int32 start;
+ int32 end;
+ int32 id;
+ void* v;
+ TBL_PC* sd = NULL;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:cleararray: 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:cleararray: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ if( is_string_variable(name) )
+ v = (void*)script_getstr(st, 3);
+ else
+ v = (void*)__64BPRTSIZE(script_getnum(st, 3));
+
+ end = start + script_getnum(st, 4);
+ if( end > SCRIPT_MAX_ARRAYSIZE )
+ end = SCRIPT_MAX_ARRAYSIZE;
+
+ for( ; start < end; ++start )
+ set_reg(st, sd, reference_uid(id, start), name, v, script_getref(st,2));
+ return 0;
+}
+
+/// Copies data from one array to another.
+/// ex: copyarray arr[0],arr[2],2;
+///
+/// copyarray <destination array variable>,<source array variable>,<count>;
+BUILDIN_FUNC(copyarray)
+{
+ struct script_data* data1;
+ struct script_data* data2;
+ const char* name1;
+ const char* name2;
+ int32 idx1;
+ int32 idx2;
+ int32 id1;
+ int32 id2;
+ void* v;
+ int32 i;
+ int32 count;
+ TBL_PC* sd = NULL;
+
+ data1 = script_getdata(st, 2);
+ data2 = script_getdata(st, 3);
+ if( !data_isreference(data1) || !data_isreference(data2) )
+ {
+ ShowError("script:copyarray: not a variable\n");
+ script_reportdata(data1);
+ script_reportdata(data2);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id1 = reference_getid(data1);
+ id2 = reference_getid(data2);
+ idx1 = reference_getindex(data1);
+ idx2 = reference_getindex(data2);
+ name1 = reference_getname(data1);
+ name2 = reference_getname(data2);
+ if( not_array_variable(*name1) || not_array_variable(*name2) )
+ {
+ ShowError("script:copyarray: illegal scope\n");
+ script_reportdata(data1);
+ script_reportdata(data2);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( is_string_variable(name1) != is_string_variable(name2) )
+ {
+ ShowError("script:copyarray: type mismatch\n");
+ script_reportdata(data1);
+ script_reportdata(data2);
+ st->state = END;
+ return 1;// data type mismatch
+ }
+
+ if( not_server_variable(*name1) || not_server_variable(*name2) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ count = script_getnum(st, 4);
+ if( count > SCRIPT_MAX_ARRAYSIZE - idx1 )
+ count = SCRIPT_MAX_ARRAYSIZE - idx1;
+ if( count <= 0 || (id1 == id2 && idx1 == idx2) )
+ return 0;// nothing to copy
+
+ if( id1 == id2 && idx1 > idx2 )
+ {// destination might be overlapping the source - copy in reverse order
+ for( i = count - 1; i >= 0; --i )
+ {
+ v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2));
+ set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1));
+ script_removetop(st, -1, 0);
+ }
+ }
+ else
+ {// normal copy
+ for( i = 0; i < count; ++i )
+ {
+ if( idx2 + i < SCRIPT_MAX_ARRAYSIZE )
+ {
+ v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2));
+ set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1));
+ script_removetop(st, -1, 0);
+ }
+ else// out of range - assume ""/0
+ set_reg(st, sd, reference_uid(id1, idx1 + i), name1, (is_string_variable(name1)?(void*)"":(void*)0), reference_getref(data1));
+ }
+ }
+ return 0;
+}
+
+/// Returns the size of the array.
+/// Assumes that everything before the starting index exists.
+/// ex: getarraysize(arr[3])
+///
+/// getarraysize(<array variable>) -> <int>
+BUILDIN_FUNC(getarraysize)
+{
+ struct script_data* data;
+ const char* name;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:getarraysize: not a variable\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:getarraysize: illegal scope\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ script_pushint(st, getarraysize(st, reference_getid(data), reference_getindex(data), is_string_variable(name), reference_getref(data)));
+ return 0;
+}
+
+/// Deletes count or all the elements in an array, from the starting index.
+/// ex: deletearray arr[4],2;
+///
+/// deletearray <array variable>;
+/// deletearray <array variable>,<count>;
+BUILDIN_FUNC(deletearray)
+{
+ struct script_data* data;
+ const char* name;
+ int start;
+ int end;
+ int id;
+ TBL_PC *sd = NULL;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:deletearray: 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:deletearray: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ end = SCRIPT_MAX_ARRAYSIZE;
+
+ if( start >= end )
+ return 0;// nothing to free
+
+ if( script_hasdata(st,3) )
+ {
+ int count = script_getnum(st, 3);
+ if( count > end - start )
+ count = end - start;
+ if( count <= 0 )
+ return 0;// nothing to free
+
+ // move rest of the elements backward
+ for( ; start + count < end; ++start )
+ {
+ void* v = get_val2(st, reference_uid(id, start + count), reference_getref(data));
+ set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data));
+ script_removetop(st, -1, 0);
+ }
+ }
+
+ // clear the rest of the array
+ if( is_string_variable(name) )
+ {
+ for( ; start < end; ++start )
+ set_reg(st, sd, reference_uid(id, start), name, (void *)"", reference_getref(data));
+ }
+ else
+ {
+ for( ; start < end; ++start )
+ set_reg(st, sd, reference_uid(id, start), name, (void*)0, reference_getref(data));
+ }
+ return 0;
+}
+
+/// Returns a reference to the target index of the array variable.
+/// Equivalent to var[index].
+///
+/// getelementofarray(<array variable>,<index>) -> <variable reference>
+BUILDIN_FUNC(getelementofarray)
+{
+ struct script_data* data;
+ const char* name;
+ int32 id;
+ int i;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:getelementofarray: not a variable\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:getelementofarray: illegal scope\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ i = script_getnum(st, 3);
+ if( i < 0 || i >= SCRIPT_MAX_ARRAYSIZE )
+ {
+ ShowWarning("script:getelementofarray: index out of range (%d)\n", i);
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// out of range
+ }
+
+ push_val2(st->stack, C_NAME, reference_uid(id, i), reference_getref(data));
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+/// ...
+///
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(setlook)
+{
+ int type,val;
+ TBL_PC* sd;
+
+ type=script_getnum(st,2);
+ val=script_getnum(st,3);
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_changelook(sd,type,val);
+
+ return 0;
+}
+
+BUILDIN_FUNC(changelook)
+{ // As setlook but only client side
+ int type,val;
+ TBL_PC* sd;
+
+ type=script_getnum(st,2);
+ val=script_getnum(st,3);
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_changelook(&sd->bl,type,val);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(cutin)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_cutin(sd,script_getstr(st,2),script_getnum(st,3));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(viewpoint)
+{
+ int type,x,y,id,color;
+ TBL_PC* sd;
+
+ type=script_getnum(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+ id=script_getnum(st,5);
+ color=script_getnum(st,6);
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_viewpoint(sd,st->oid,type,x,y,id,color);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(countitem)
+{
+ int nameid, i;
+ int count = 0;
+ struct item_data* id = NULL;
+ struct script_data* data;
+
+ TBL_PC* sd = script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st, data); // convert into value in case of a variable
+
+ if( data_isstring(data) )
+ {// item name
+ id = itemdb_searchname(conv_str(st, data));
+ }
+ else
+ {// item id
+ id = itemdb_exists(conv_num(st, data));
+ }
+
+ if( id == NULL )
+ {
+ ShowError("buildin_countitem: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was
+ script_pushint(st,0);
+ return 1;
+ }
+
+ nameid = id->nameid;
+
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if(sd->status.inventory[i].nameid == nameid)
+ count += sd->status.inventory[i].amount;
+
+ script_pushint(st,count);
+ return 0;
+}
+
+/*==========================================
+ * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus]
+ * returns number of items that meet the conditions
+ *------------------------------------------*/
+BUILDIN_FUNC(countitem2)
+{
+ int nameid, iden, ref, attr, c1, c2, c3, c4;
+ int count = 0;
+ int i;
+ struct item_data* id = NULL;
+ struct script_data* data;
+
+ TBL_PC* sd = script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st, data); // convert into value in case of a variable
+
+ if( data_isstring(data) )
+ {// item name
+ id = itemdb_searchname(conv_str(st, data));
+ }
+ else
+ {// item id
+ id = itemdb_exists(conv_num(st, data));
+ }
+
+ if( id == NULL )
+ {
+ ShowError("buildin_countitem2: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was
+ script_pushint(st,0);
+ return 1;
+ }
+
+ nameid = id->nameid;
+ iden = script_getnum(st,3);
+ ref = script_getnum(st,4);
+ attr = script_getnum(st,5);
+ c1 = (short)script_getnum(st,6);
+ c2 = (short)script_getnum(st,7);
+ c3 = (short)script_getnum(st,8);
+ c4 = (short)script_getnum(st,9);
+
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] != NULL &&
+ sd->status.inventory[i].amount > 0 && sd->status.inventory[i].nameid == nameid &&
+ sd->status.inventory[i].identify == iden && sd->status.inventory[i].refine == ref &&
+ sd->status.inventory[i].attribute == attr && sd->status.inventory[i].card[0] == c1 &&
+ sd->status.inventory[i].card[1] == c2 && sd->status.inventory[i].card[2] == c3 &&
+ sd->status.inventory[i].card[3] == c4
+ )
+ count += sd->status.inventory[i].amount;
+
+ script_pushint(st,count);
+ return 0;
+}
+
+/*==========================================
+ * Check if item with this amount can fit in inventory
+ * Checking : weight, stack amount >32k, slots amount >(MAX_INVENTORY)
+ * Return
+ * 0 : fail
+ * 1 : success (npc side only)
+ *------------------------------------------*/
+BUILDIN_FUNC(checkweight)
+{
+ int nameid, amount, slots, amount2=0;
+ unsigned int weight=0, i, nbargs;
+ struct item_data* id = NULL;
+ struct map_session_data* sd;
+ struct script_data* data;
+
+ if( ( sd = script_rid2sd(st) ) == NULL ){
+ return 0;
+ }
+ nbargs = script_lastdata(st)+1;
+ if(nbargs%2){
+ ShowError("buildin_checkweight: Invalid nb of args should be a multiple of 2.\n");
+ script_pushint(st,0);
+ return 1;
+ }
+ slots = pc_inventoryblank(sd); //nb of empty slot
+
+ for(i=2; i<nbargs; i=i+2){
+ data = script_getdata(st,i);
+ get_val(st, data); // convert into value in case of a variable
+ if( data_isstring(data) ){// item name
+ id = itemdb_searchname(conv_str(st, data));
+ } else {// item id
+ id = itemdb_exists(conv_num(st, data));
+ }
+ if( id == NULL ) {
+ ShowError("buildin_checkweight: Invalid item '%s'.\n", script_getstr(st,i)); // returns string, regardless of what it was
+ script_pushint(st,0);
+ return 1;
+ }
+ nameid = id->nameid;
+
+ amount = script_getnum(st,i+1);
+ if( amount < 1 ) {
+ ShowError("buildin_checkweight: Invalid amount '%d'.\n", amount);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ weight += itemdb_weight(nameid)*amount; //total weight for all chk
+ if( weight + sd->weight > sd->max_weight )
+ {// too heavy
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch( pc_checkadditem(sd, nameid, amount) )
+ {
+ case ADDITEM_EXIST:
+ // item is already in inventory, but there is still space for the requested amount
+ break;
+ case ADDITEM_NEW:
+ if( itemdb_isstackable(nameid) ) {// stackable
+ amount2++;
+ if( slots < amount2 ) {
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+ else {// non-stackable
+ amount2 += amount;
+ if( slots < amount2){
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+ break;
+ case ADDITEM_OVERAMOUNT:
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(checkweight2)
+{
+ //variable sub checkweight
+ int32 nameid=-1, amount=-1;
+ int i=0, amount2=0, slots=0, weight=0;
+ short fail=0;
+
+ //variable for array parsing
+ struct script_data* data_it;
+ struct script_data* data_nb;
+ const char* name_it;
+ const char* name_nb;
+ int32 id_it, id_nb;
+ int32 idx_it, idx_nb;
+ int nb_it, nb_nb; //array size
+
+ TBL_PC *sd = script_rid2sd(st);
+ nullpo_retr(1,sd);
+
+ data_it = script_getdata(st, 2);
+ data_nb = script_getdata(st, 3);
+
+ if( !data_isreference(data_it) || !data_isreference(data_nb))
+ {
+ ShowError("script:checkweight2: parameter not a variable\n");
+ script_pushint(st,0);
+ return 1;// not a variable
+ }
+ id_it = reference_getid(data_it);
+ id_nb = reference_getid(data_nb);
+ idx_it = reference_getindex(data_it);
+ idx_nb = reference_getindex(data_nb);
+ name_it = reference_getname(data_it);
+ name_nb = reference_getname(data_nb);
+
+ if( not_array_variable(*name_it) || not_array_variable(*name_nb))
+ {
+ ShowError("script:checkweight2: illegal scope\n");
+ script_pushint(st,0);
+ return 1;// not supported
+ }
+ if(is_string_variable(name_it) || is_string_variable(name_nb)){
+ ShowError("script:checkweight2: illegal type, need int\n");
+ script_pushint(st,0);
+ return 1;// not supported
+ }
+ nb_it = getarraysize(st, id_it, idx_it, 0, reference_getref(data_it));
+ nb_nb = getarraysize(st, id_nb, idx_nb, 0, reference_getref(data_nb));
+ if(nb_it != nb_nb){
+ ShowError("Size mistmatch: nb_it=%d, nb_nb=%d\n",nb_it,nb_nb);
+ fail = 1;
+ }
+
+ slots = pc_inventoryblank(sd);
+ for(i=0; i<nb_it; i++){
+ nameid = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_it,idx_it+i),reference_getref(data_it)));
+ script_removetop(st, -1, 0);
+ amount = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_nb,idx_nb+i),reference_getref(data_nb)));
+ script_removetop(st, -1, 0);
+ if(fail) continue; //cpntonie to depop rest
+
+ if(itemdb_exists(nameid) == NULL ){
+ ShowError("buildin_checkweight2: Invalid item '%d'.\n", nameid);
+ fail=1;
+ continue;
+ }
+ if(amount < 0 ){
+ ShowError("buildin_checkweight2: Invalid amount '%d'.\n", amount);
+ fail = 1;
+ continue;
+ }
+ weight += itemdb_weight(nameid)*amount;
+ if( weight + sd->weight > sd->max_weight ){
+ fail = 1;
+ continue;
+ }
+ switch( pc_checkadditem(sd, nameid, amount) ) {
+ case ADDITEM_EXIST:
+ // item is already in inventory, but there is still space for the requested amount
+ break;
+ case ADDITEM_NEW:
+ if( itemdb_isstackable(nameid) ){// stackable
+ amount2++;
+ if( slots < amount2 )
+ fail = 1;
+ }
+ else {// non-stackable
+ amount2 += amount;
+ if( slots < amount2 ){
+ fail = 1;
+ }
+ }
+ break;
+ case ADDITEM_OVERAMOUNT:
+ fail = 1;
+ } //end switch
+ } //end loop DO NOT break it prematurly we need to depop all stack
+
+ fail?script_pushint(st,0):script_pushint(st,1);
+ return 0;
+}
+
+/*==========================================
+ * getitem <item id>,<amount>{,<account ID>};
+ * getitem "<item name>",<amount>{,<account ID>};
+ *------------------------------------------*/
+BUILDIN_FUNC(getitem)
+{
+ int nameid,amount,get_count,i,flag = 0;
+ struct item it;
+ TBL_PC *sd;
+ struct script_data *data;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {// "<item name>"
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data == NULL ){
+ ShowError("buildin_getitem: Nonexistant item %s requested.\n", name);
+ return 1; //No item created.
+ }
+ nameid=item_data->nameid;
+ } else if( data_isint(data) )
+ {// <item id>
+ nameid=conv_num(st,data);
+ //Violet Box, Blue Box, etc - random item pick
+ if( nameid < 0 ) {
+ nameid = -nameid;
+ flag = 1;
+ }
+ if( nameid <= 0 || !itemdb_exists(nameid) ){
+ ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid);
+ return 1; //No item created.
+ }
+ } else {
+ ShowError("buildin_getitem: invalid data type for argument #1 (%d).", data->type);
+ return 1;
+ }
+
+ // <amount>
+ if( (amount=script_getnum(st,3)) <= 0)
+ return 0; //return if amount <=0, skip the useles iteration
+
+ memset(&it,0,sizeof(it));
+ it.nameid=nameid;
+ if(!flag)
+ it.identify=1;
+ else
+ it.identify=itemdb_isidentified(nameid);
+
+ if( script_hasdata(st,4) )
+ sd=map_id2sd(script_getnum(st,4)); // <Account ID>
+ else
+ sd=script_rid2sd(st); // Attached player
+
+ if( sd == NULL ) // no target
+ return 0;
+
+ //Check if it's stackable.
+ if (!itemdb_isstackable(nameid))
+ get_count = 1;
+ else
+ get_count = amount;
+
+ for (i = 0; i < amount; i += get_count)
+ {
+ // if not pet egg
+ if (!pet_create_egg(sd, nameid))
+ {
+ if ((flag = pc_additem(sd, &it, get_count, LOG_TYPE_SCRIPT)))
+ {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&it) )
+ map_addflooritem(&it,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(getitem2)
+{
+ int nameid,amount,get_count,i,flag = 0;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct item_data *item_data;
+ struct item item_tmp;
+ TBL_PC *sd;
+ struct script_data *data;
+
+ if( script_hasdata(st,11) )
+ sd=map_id2sd(script_getnum(st,11)); // <Account ID>
+ else
+ sd=script_rid2sd(st); // Attached player
+
+ if( sd == NULL ) // no target
+ return 0;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ nameid=item_data->nameid;
+ else
+ nameid=UNKNOWN_ITEM_ID;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=script_getnum(st,3);
+ iden=script_getnum(st,4);
+ ref=script_getnum(st,5);
+ attr=script_getnum(st,6);
+ c1=(short)script_getnum(st,7);
+ c2=(short)script_getnum(st,8);
+ c3=(short)script_getnum(st,9);
+ c4=(short)script_getnum(st,10);
+
+ if(nameid<0) { // Invalide nameid
+ nameid = -nameid;
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_data=itemdb_exists(nameid);
+ if (item_data == NULL)
+ return -1;
+ if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR){
+ if(ref > MAX_REFINE) ref = MAX_REFINE;
+ }
+ else if(item_data->type==IT_PETEGG) {
+ iden = 1;
+ ref = 0;
+ }
+ else {
+ iden = 1;
+ ref = attr = 0;
+ }
+
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=iden;
+ else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR)
+ item_tmp.identify=0;
+ item_tmp.refine=ref;
+ item_tmp.attribute=attr;
+ item_tmp.card[0]=(short)c1;
+ item_tmp.card[1]=(short)c2;
+ item_tmp.card[2]=(short)c3;
+ item_tmp.card[3]=(short)c4;
+
+ //Check if it's stackable.
+ if (!itemdb_isstackable(nameid))
+ get_count = 1;
+ else
+ get_count = amount;
+
+ for (i = 0; i < amount; i += get_count)
+ {
+ // if not pet egg
+ if (!pet_create_egg(sd, nameid))
+ {
+ if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT)))
+ {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&item_tmp) )
+ map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * rentitem <item id>,<seconds>
+ * rentitem "<item name>",<seconds>
+ *------------------------------------------*/
+BUILDIN_FUNC(rentitem)
+{
+ struct map_session_data *sd;
+ struct script_data *data;
+ struct item it;
+ int seconds;
+ int nameid = 0, flag;
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+
+ if( data_isstring(data) )
+ {
+ const char *name = conv_str(st,data);
+ struct item_data *itd = itemdb_searchname(name);
+ if( itd == NULL )
+ {
+ ShowError("buildin_rentitem: Nonexistant item %s requested.\n", name);
+ return 1;
+ }
+ nameid = itd->nameid;
+ }
+ else if( data_isint(data) )
+ {
+ nameid = conv_num(st,data);
+ if( nameid <= 0 || !itemdb_exists(nameid) )
+ {
+ ShowError("buildin_rentitem: Nonexistant item %d requested.\n", nameid);
+ return 1;
+ }
+ }
+ else
+ {
+ ShowError("buildin_rentitem: invalid data type for argument #1 (%d).\n", data->type);
+ return 1;
+ }
+
+ seconds = script_getnum(st,3);
+ memset(&it, 0, sizeof(it));
+ it.nameid = nameid;
+ it.identify = 1;
+ it.expire_time = (unsigned int)(time(NULL) + seconds);
+
+ if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) )
+ {
+ clif_additem(sd, 0, 0, flag);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * gets an item with someone's name inscribed [Skotlex]
+ * getinscribeditem item_num, character_name
+ * Returned Qty is always 1, only works on equip-able
+ * equipment
+ *------------------------------------------*/
+BUILDIN_FUNC(getnameditem)
+{
+ int nameid;
+ struct item item_tmp;
+ TBL_PC *sd, *tsd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL)
+ { //Player not attached!
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data == NULL)
+ { //Failed
+ script_pushint(st,0);
+ return 0;
+ }
+ nameid = item_data->nameid;
+ }else
+ nameid = conv_num(st,data);
+
+ if(!itemdb_exists(nameid)/* || itemdb_isstackable(nameid)*/)
+ { //Even though named stackable items "could" be risky, they are required for certain quests.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data=script_getdata(st,3);
+ get_val(st,data);
+ if( data_isstring(data) ) //Char Name
+ tsd=map_nick2sd(conv_str(st,data));
+ else //Char Id was given
+ tsd=map_charid2sd(conv_num(st,data));
+
+ if( tsd == NULL )
+ { //Failed
+ script_pushint(st,0);
+ return 0;
+ }
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ item_tmp.amount=1;
+ item_tmp.identify=1;
+ item_tmp.card[0]=CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus]
+ item_tmp.card[2]=tsd->status.char_id;
+ item_tmp.card[3]=tsd->status.char_id >> 16;
+ if(pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT)) {
+ script_pushint(st,0);
+ return 0; //Failed to add item, we will not drop if they don't fit
+ }
+
+ script_pushint(st,1);
+ return 0;
+}
+
+/*==========================================
+ * gets a random item ID from an item group [Skotlex]
+ * groupranditem group_num
+ *------------------------------------------*/
+BUILDIN_FUNC(grouprandomitem)
+{
+ int group;
+
+ group = script_getnum(st,2);
+ script_pushint(st,itemdb_searchrandomid(group));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(makeitem)
+{
+ int nameid,amount,flag = 0;
+ int x,y,m;
+ const char *mapname;
+ struct item item_tmp;
+ struct script_data *data;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ nameid=item_data->nameid;
+ else
+ nameid=UNKNOWN_ITEM_ID;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=script_getnum(st,3);
+ mapname =script_getstr(st,4);
+ x =script_getnum(st,5);
+ y =script_getnum(st,6);
+
+ if(strcmp(mapname,"this")==0)
+ {
+ TBL_PC *sd;
+ sd = script_rid2sd(st);
+ if (!sd) return 0; //Failed...
+ m=sd->bl.m;
+ } else
+ m=map_mapname2mapid(mapname);
+
+ if(nameid<0) {
+ nameid = -nameid;
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=1;
+ else
+ item_tmp.identify=itemdb_isidentified(nameid);
+
+ map_addflooritem(&item_tmp,amount,m,x,y,0,0,0,0);
+ }
+
+ return 0;
+}
+
+
+/// Counts / deletes the current item given by idx.
+/// Used by buildin_delitem_search
+/// Relies on all input data being already fully valid.
+static void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, bool delete_items)
+{
+ int delamount;
+ struct item* inv = &sd->status.inventory[idx];
+
+ delamount = ( amount[0] < inv->amount ) ? amount[0] : inv->amount;
+
+ if( delete_items )
+ {
+ if( sd->inventory_data[idx]->type == IT_PETEGG && inv->card[0] == CARD0_PET )
+ {// delete associated pet
+ intif_delete_petdata(MakeDWord(inv->card[1], inv->card[2]));
+ }
+ pc_delitem(sd, idx, delamount, 0, 0, LOG_TYPE_SCRIPT);
+ }
+
+ amount[0]-= delamount;
+}
+
+
+/// Searches for item(s) and checks, if there is enough of them.
+/// Used by delitem and delitem2
+/// Relies on all input data being already fully valid.
+/// @param exact_match will also match item attributes and cards, not just name id
+/// @return true when all items could be deleted, false when there were not enough items to delete
+static bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool exact_match)
+{
+ bool delete_items = false;
+ int i, amount, important;
+ struct item* inv;
+
+ // prefer always non-equipped items
+ it->equip = 0;
+
+ // when searching for nameid only, prefer additionally
+ if( !exact_match )
+ {
+ // non-refined items
+ it->refine = 0;
+ // card-less items
+ memset(it->card, 0, sizeof(it->card));
+ }
+
+ for(;;)
+ {
+ amount = it->amount;
+ important = 0;
+
+ // 1st pass -- less important items / exact match
+ for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
+ {
+ inv = &sd->status.inventory[i];
+
+ if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
+ {// wrong/invalid item
+ continue;
+ }
+
+ if( inv->equip != it->equip || inv->refine != it->refine )
+ {// not matching attributes
+ important++;
+ continue;
+ }
+
+ if( exact_match )
+ {
+ if( inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// not matching exact attributes
+ continue;
+ }
+ }
+ else
+ {
+ if( sd->inventory_data[i]->type == IT_PETEGG )
+ {
+ if( inv->card[0] == CARD0_PET && CheckForCharServer() )
+ {// pet which cannot be deleted
+ continue;
+ }
+ }
+ else if( memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// named/carded item
+ important++;
+ continue;
+ }
+ }
+
+ // count / delete item
+ buildin_delitem_delete(sd, i, &amount, delete_items);
+ }
+
+ // 2nd pass -- any matching item
+ if( amount == 0 || important == 0 )
+ {// either everything was already consumed or no items were skipped
+ ;
+ }
+ else for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
+ {
+ inv = &sd->status.inventory[i];
+
+ if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
+ {// wrong/invalid item
+ continue;
+ }
+
+ if( sd->inventory_data[i]->type == IT_PETEGG && inv->card[0] == CARD0_PET && CheckForCharServer() )
+ {// pet which cannot be deleted
+ continue;
+ }
+
+ if( exact_match )
+ {
+ if( inv->refine != it->refine || inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// not matching attributes
+ continue;
+ }
+ }
+
+ // count / delete item
+ buildin_delitem_delete(sd, i, &amount, delete_items);
+ }
+
+ if( amount )
+ {// not enough items
+ return false;
+ }
+ else if( delete_items )
+ {// we are done with the work
+ return true;
+ }
+ else
+ {// get rid of the items now
+ delete_items = true;
+ }
+ }
+}
+
+
+/// Deletes items from the target/attached player.
+/// Prioritizes ordinary items.
+///
+/// delitem <item id>,<amount>{,<account id>}
+/// delitem "<item name>",<amount>{,<account id>}
+BUILDIN_FUNC(delitem)
+{
+ TBL_PC *sd;
+ struct item it;
+ struct script_data *data;
+
+ if( script_hasdata(st,4) )
+ {
+ int account_id = script_getnum(st,4);
+ sd = map_id2sd(account_id); // <account id>
+ if( sd == NULL )
+ {
+ ShowError("script:delitem: player not found (AID=%d).\n", account_id);
+ st->state = END;
+ return 1;
+ }
+ }
+ else
+ {
+ sd = script_rid2sd(st);// attached player
+ if( sd == NULL )
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {
+ const char* item_name = conv_str(st,data);
+ struct item_data* id = itemdb_searchname(item_name);
+ if( id == NULL )
+ {
+ ShowError("script:delitem: unknown item \"%s\".\n", item_name);
+ st->state = END;
+ return 1;
+ }
+ it.nameid = id->nameid;// "<item name>"
+ }
+ else
+ {
+ it.nameid = conv_num(st,data);// <item id>
+ if( !itemdb_exists( it.nameid ) )
+ {
+ ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
+ st->state = END;
+ return 1;
+ }
+ }
+
+ it.amount=script_getnum(st,3);
+
+ if( it.amount <= 0 )
+ return 0;// nothing to do
+
+ if( buildin_delitem_search(sd, &it, false) )
+ {// success
+ return 0;
+ }
+
+ ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
+ st->state = END;
+ clif_scriptclose(sd, st->oid);
+ return 1;
+}
+
+/// Deletes items from the target/attached player.
+///
+/// delitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>}
+/// delitem2 "<Item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>}
+BUILDIN_FUNC(delitem2)
+{
+ TBL_PC *sd;
+ struct item it;
+ struct script_data *data;
+
+ if( script_hasdata(st,11) )
+ {
+ int account_id = script_getnum(st,11);
+ sd = map_id2sd(account_id); // <account id>
+ if( sd == NULL )
+ {
+ ShowError("script:delitem2: player not found (AID=%d).\n", account_id);
+ st->state = END;
+ return 1;
+ }
+ }
+ else
+ {
+ sd = script_rid2sd(st);// attached player
+ if( sd == NULL )
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {
+ const char* item_name = conv_str(st,data);
+ struct item_data* id = itemdb_searchname(item_name);
+ if( id == NULL )
+ {
+ ShowError("script:delitem2: unknown item \"%s\".\n", item_name);
+ st->state = END;
+ return 1;
+ }
+ it.nameid = id->nameid;// "<item name>"
+ }
+ else
+ {
+ it.nameid = conv_num(st,data);// <item id>
+ if( !itemdb_exists( it.nameid ) )
+ {
+ ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
+ st->state = END;
+ return 1;
+ }
+ }
+
+ it.amount=script_getnum(st,3);
+ it.identify=script_getnum(st,4);
+ it.refine=script_getnum(st,5);
+ it.attribute=script_getnum(st,6);
+ it.card[0]=(short)script_getnum(st,7);
+ it.card[1]=(short)script_getnum(st,8);
+ it.card[2]=(short)script_getnum(st,9);
+ it.card[3]=(short)script_getnum(st,10);
+
+ if( it.amount <= 0 )
+ return 0;// nothing to do
+
+ if( buildin_delitem_search(sd, &it, true) )
+ {// success
+ return 0;
+ }
+
+ ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
+ st->state = END;
+ clif_scriptclose(sd, st->oid);
+ return 1;
+}
+
+/*==========================================
+ * Enables/Disables use of items while in an NPC [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(enableitemuse)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = st->oid;
+ return 0;
+}
+
+BUILDIN_FUNC(disableitemuse)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = 0;
+ return 0;
+}
+
+/*==========================================
+ * return the basic stats of sd
+ * chk pc_readparam for available type
+ *------------------------------------------*/
+BUILDIN_FUNC(readparam)
+{
+ int type;
+ TBL_PC *sd;
+
+ type=script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ sd=map_nick2sd(script_getstr(st,3));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd==NULL){
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ script_pushint(st,pc_readparam(sd,type));
+
+ return 0;
+}
+
+/*==========================================
+ * Return charid identification
+ * return by @num :
+ * 0 : char_id
+ * 1 : party_id
+ * 2 : guild_id
+ * 3 : account_id
+ * 4 : bg_id
+ *------------------------------------------*/
+BUILDIN_FUNC(getcharid)
+{
+ int num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ sd=map_nick2sd(script_getstr(st,3));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd==NULL){
+ script_pushint(st,0); //return 0, according docs
+ return 0;
+ }
+
+ switch( num ) {
+ case 0: script_pushint(st,sd->status.char_id); break;
+ case 1: script_pushint(st,sd->status.party_id); break;
+ case 2: script_pushint(st,sd->status.guild_id); break;
+ case 3: script_pushint(st,sd->status.account_id); break;
+ case 4: script_pushint(st,sd->bg_id); break;
+ default:
+ ShowError("buildin_getcharid: invalid parameter (%d).\n", num);
+ script_pushint(st,0);
+ break;
+ }
+
+ return 0;
+}
+/*==========================================
+ * returns the GID of an NPC
+ *------------------------------------------*/
+BUILDIN_FUNC(getnpcid)
+{
+ int num = script_getnum(st,2);
+ struct npc_data* nd = NULL;
+
+ if( script_hasdata(st,3) )
+ {// unique npc name
+ if( ( nd = npc_name2id(script_getstr(st,3)) ) == NULL )
+ {
+ ShowError("buildin_getnpcid: No such NPC '%s'.\n", script_getstr(st,3));
+ script_pushint(st,0);
+ return 1;
+ }
+ }
+
+ switch (num) {
+ case 0:
+ script_pushint(st,nd ? nd->bl.id : st->oid);
+ break;
+ default:
+ ShowError("buildin_getnpcid: invalid parameter (%d).\n", num);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Return the name of the party_id
+ * null if not found
+ *------------------------------------------*/
+BUILDIN_FUNC(getpartyname)
+{
+ int party_id;
+ struct party_data* p;
+
+ party_id = script_getnum(st,2);
+
+ if( ( p = party_search(party_id) ) != NULL )
+ {
+ script_pushstrcopy(st,p->party.name);
+ }
+ else
+ {
+ script_pushconststr(st,"null");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get the information of the members of a party by type
+ * @party_id, @type
+ * return by @type :
+ * - : nom des membres
+ * 1 : char_id des membres
+ * 2 : account_id des membres
+ *------------------------------------------*/
+BUILDIN_FUNC(getpartymember)
+{
+ struct party_data *p;
+ int i,j=0,type=0;
+
+ p=party_search(script_getnum(st,2));
+
+ if( script_hasdata(st,3) )
+ type=script_getnum(st,3);
+
+ if(p!=NULL){
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->party.member[i].account_id){
+ switch (type) {
+ case 2:
+ mapreg_setreg(reference_uid(add_str("$@partymemberaid"), j),p->party.member[i].account_id);
+ break;
+ case 1:
+ mapreg_setreg(reference_uid(add_str("$@partymembercid"), j),p->party.member[i].char_id);
+ break;
+ default:
+ mapreg_setregstr(reference_uid(add_str("$@partymembername$"), j),p->party.member[i].name);
+ }
+ j++;
+ }
+ }
+ }
+ mapreg_setreg(add_str("$@partymembercount"),j);
+
+ return 0;
+}
+
+/*==========================================
+ * Retrieves party leader. if flag is specified,
+ * return some of the leader data. Otherwise, return name.
+ *------------------------------------------*/
+BUILDIN_FUNC(getpartyleader)
+{
+ int party_id, type = 0, i=0;
+ struct party_data *p;
+
+ party_id=script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ type=script_getnum(st,3);
+
+ p=party_search(party_id);
+
+ if (p) //Search leader
+ for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++);
+
+ if (!p || i == MAX_PARTY) { //leader not found
+ if (type)
+ script_pushint(st,-1);
+ else
+ script_pushconststr(st,"null");
+ return 0;
+ }
+
+ switch (type) {
+ case 1: script_pushint(st,p->party.member[i].account_id); break;
+ case 2: script_pushint(st,p->party.member[i].char_id); break;
+ case 3: script_pushint(st,p->party.member[i].class_); break;
+ case 4: script_pushstrcopy(st,mapindex_id2name(p->party.member[i].map)); break;
+ case 5: script_pushint(st,p->party.member[i].lv); break;
+ default: script_pushstrcopy(st,p->party.member[i].name); break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Return the name of the @guild_id
+ * null if not found
+ *------------------------------------------*/
+BUILDIN_FUNC(getguildname)
+{
+ int guild_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+
+ if( ( g = guild_search(guild_id) ) != NULL )
+ {
+ script_pushstrcopy(st,g->name);
+ }
+ else
+ {
+ script_pushconststr(st,"null");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Return the name of the guild master of @guild_id
+ * null if not found
+ *------------------------------------------*/
+BUILDIN_FUNC(getguildmaster)
+{
+ int guild_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+
+ if( ( g = guild_search(guild_id) ) != NULL )
+ {
+ script_pushstrcopy(st,g->member[0].name);
+ }
+ else
+ {
+ script_pushconststr(st,"null");
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(getguildmasterid)
+{
+ int guild_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+
+ if( ( g = guild_search(guild_id) ) != NULL )
+ {
+ script_pushint(st,g->member[0].char_id);
+ }
+ else
+ {
+ script_pushint(st,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get char string information by type :
+ * Return by @type :
+ * 0 : char_name
+ * 1 : party_name or ""
+ * 2 : guild_name or ""
+ * 3 : map_name
+ * - : ""
+ *------------------------------------------*/
+BUILDIN_FUNC(strcharinfo)
+{
+ TBL_PC *sd;
+ int num;
+ struct guild* g;
+ struct party_data* p;
+
+ sd=script_rid2sd(st);
+ if (!sd) { //Avoid crashing....
+ script_pushconststr(st,"");
+ return 0;
+ }
+ num=script_getnum(st,2);
+ switch(num){
+ case 0:
+ script_pushstrcopy(st,sd->status.name);
+ break;
+ case 1:
+ if( ( p = party_search(sd->status.party_id) ) != NULL )
+ {
+ script_pushstrcopy(st,p->party.name);
+ }
+ else
+ {
+ script_pushconststr(st,"");
+ }
+ break;
+ case 2:
+ if( ( g = guild_search(sd->status.guild_id) ) != NULL )
+ {
+ script_pushstrcopy(st,g->name);
+ }
+ else
+ {
+ script_pushconststr(st,"");
+ }
+ break;
+ case 3:
+ script_pushconststr(st,map[sd->bl.m].name);
+ break;
+ default:
+ ShowWarning("buildin_strcharinfo: unknown parameter.\n");
+ script_pushconststr(st,"");
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Get npc string information by type
+ * return by @type:
+ * 0 : name
+ * 1 : str#
+ * 2 : #str
+ * 3 : ::str
+ * 4 : map name
+ *------------------------------------------*/
+BUILDIN_FUNC(strnpcinfo)
+{
+ TBL_NPC* nd;
+ int num;
+ char *buf,*name=NULL;
+
+ nd = map_id2nd(st->oid);
+ if (!nd) {
+ script_pushconststr(st, "");
+ return 0;
+ }
+
+ num = script_getnum(st,2);
+ switch(num){
+ case 0: // display name
+ name = aStrdup(nd->name);
+ break;
+ case 1: // visible part of display name
+ if((buf = strchr(nd->name,'#')) != NULL)
+ {
+ name = aStrdup(nd->name);
+ name[buf - nd->name] = 0;
+ } else // Return the name, there is no '#' present
+ name = aStrdup(nd->name);
+ break;
+ case 2: // # fragment
+ if((buf = strchr(nd->name,'#')) != NULL)
+ name = aStrdup(buf+1);
+ break;
+ case 3: // unique name
+ name = aStrdup(nd->exname);
+ break;
+ case 4: // map name
+ name = aStrdup(map[nd->bl.m].name);
+ break;
+ }
+
+ if(name)
+ script_pushstr(st, name);
+ else
+ script_pushconststr(st, "");
+
+ return 0;
+}
+
+
+// aegis->athena slot position conversion table
+static unsigned int equip[] = {EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_GARMENT,EQP_SHOES,EQP_ACC_L,EQP_ACC_R,EQP_HEAD_MID,EQP_HEAD_LOW};
+
+/*==========================================
+ * GetEquipID(Pos); Pos: 1-10
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipid)
+{
+ int i, num;
+ TBL_PC* sd;
+ struct item_data* item;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num = script_getnum(st,2) - 1;
+ if( num < 0 || num >= ARRAYLENGTH(equip) )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ // get inventory position of item
+ i = pc_checkequip(sd,equip[num]);
+ if( i < 0 )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ item = sd->inventory_data[i];
+ if( item != 0 )
+ script_pushint(st,item->nameid);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the equipement name at pos
+ * return item jname or ""
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipname)
+{
+ int i, num;
+ TBL_PC* sd;
+ struct item_data* item;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num = script_getnum(st,2) - 1;
+ if( num < 0 || num >= ARRAYLENGTH(equip) )
+ {
+ script_pushconststr(st,"");
+ return 0;
+ }
+
+ // get inventory position of item
+ i = pc_checkequip(sd,equip[num]);
+ if( i < 0 )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ item = sd->inventory_data[i];
+ if( item != 0 )
+ script_pushstrcopy(st,item->jname);
+ else
+ script_pushconststr(st,"");
+
+ return 0;
+}
+
+/*==========================================
+ * getbrokenid [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(getbrokenid)
+{
+ int i,num,id=0,brokencounter=0;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num=script_getnum(st,2);
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute){
+ brokencounter++;
+ if(num==brokencounter){
+ id=sd->status.inventory[i].nameid;
+ break;
+ }
+ }
+ }
+
+ script_pushint(st,id);
+
+ return 0;
+}
+
+/*==========================================
+ * repair [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(repair)
+{
+ int i,num;
+ int repaircounter=0;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num=script_getnum(st,2);
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute){
+ repaircounter++;
+ if(num==repaircounter){
+ sd->status.inventory[i].attribute=0;
+ clif_equiplist(sd);
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ clif_misceffect(&sd->bl, 3);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * repairall
+ *------------------------------------------*/
+BUILDIN_FUNC(repairall)
+{
+ int i, repaircounter = 0;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if(sd == NULL)
+ return 0;
+
+ for(i = 0; i < MAX_INVENTORY; i++)
+ {
+ if(sd->status.inventory[i].nameid && sd->status.inventory[i].attribute)
+ {
+ sd->status.inventory[i].attribute = 0;
+ clif_produceeffect(sd,0,sd->status.inventory[i].nameid);
+ repaircounter++;
+ }
+ }
+
+ if(repaircounter)
+ {
+ clif_misceffect(&sd->bl, 3);
+ clif_equiplist(sd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Chk if player have something equiped at pos
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipisequiped)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if(i >= 0)
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * Chk if the player have something equiped at pos
+ * if so chk if this item ain't marked not refinable or rental
+ * return (npc)
+ * 1 : true
+ * 0 : false
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipisenableref)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( num > 0 && num <= ARRAYLENGTH(equip) )
+ i = pc_checkequip(sd,equip[num-1]);
+ if( i >= 0 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine && !sd->status.inventory[i].expire_time )
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Chk if the item equiped at pos is identify (huh ?)
+ * return (npc)
+ * 1 : true
+ * 0 : false
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipisidentify)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0)
+ script_pushint(st,sd->status.inventory[i].identify);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the item refined value at pos
+ * return (npc)
+ * x : refine amount
+ * 0 : false (not refined)
+ *------------------------------------------*/
+BUILDIN_FUNC(getequiprefinerycnt)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0)
+ script_pushint(st,sd->status.inventory[i].refine);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the weapon level value at pos
+ * (pos should normally only be EQI_HAND_L or EQI_HAND_R)
+ * return (npc)
+ * x : weapon level
+ * 0 : false
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipweaponlv)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && sd->inventory_data[i])
+ script_pushint(st,sd->inventory_data[i]->wlv);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the item refine chance (from refine.txt) for item at pos
+ * return (npc)
+ * x : refine chance
+ * 0 : false (max refine level or unequip..)
+ *------------------------------------------*/
+BUILDIN_FUNC(getequippercentrefinery)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE)
+ script_pushint(st,status_get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int)sd->status.inventory[i].refine));
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Refine +1 item at pos and log and display refine
+ *------------------------------------------*/
+BUILDIN_FUNC(successrefitem)
+{
+ int i=-1,num,ep;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ ep=sd->status.inventory[i].equip;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]);
+
+ sd->status.inventory[i].refine++;
+ pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below
+
+ clif_refine(sd->fd,0,i,sd->status.inventory[i].refine);
+ clif_delitem(sd,i,1,3);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]);
+
+ clif_additem(sd,i,1,0);
+ pc_equipitem(sd,i,ep);
+ clif_misceffect(&sd->bl,3);
+ if(sd->status.inventory[i].refine == MAX_REFINE &&
+ sd->status.inventory[i].card[0] == CARD0_FORGE &&
+ sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])
+ ){ // Fame point system [DracoRPG]
+ switch (sd->inventory_data[i]->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Show a failed Refine +1 attempt
+ *------------------------------------------*/
+BUILDIN_FUNC(failedrefitem)
+{
+ int i=-1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ sd->status.inventory[i].refine = 0;
+ pc_unequipitem(sd,i,3); //recalculate bonus
+ clif_refine(sd->fd,1,i,sd->status.inventory[i].refine); //notify client of failure
+
+ pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
+
+ clif_misceffect(&sd->bl,2); // display failure effect
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Downgrades an Equipment Part by -1 . [Masao]
+ *------------------------------------------*/
+BUILDIN_FUNC(downrefitem)
+{
+ int i = -1,num,ep;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i = pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ ep = sd->status.inventory[i].equip;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]);
+
+ sd->status.inventory[i].refine++;
+ pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below
+
+ clif_refine(sd->fd,2,i,sd->status.inventory[i].refine = sd->status.inventory[i].refine - 2);
+ clif_delitem(sd,i,1,3);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]);
+
+ clif_additem(sd,i,1,0);
+ pc_equipitem(sd,i,ep);
+ clif_misceffect(&sd->bl,2);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(statusup)
+{
+ int type;
+ TBL_PC *sd;
+
+ type=script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_statusup(sd,type);
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(statusup2)
+{
+ int type,val;
+ TBL_PC *sd;
+
+ type=script_getnum(st,2);
+ val=script_getnum(st,3);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_statusup2(sd,type,val);
+
+ return 0;
+}
+
+/// See 'doc/item_bonus.txt'
+///
+/// bonus <bonus type>,<val1>;
+/// bonus2 <bonus type>,<val1>,<val2>;
+/// bonus3 <bonus type>,<val1>,<val2>,<val3>;
+/// bonus4 <bonus type>,<val1>,<val2>,<val3>,<val4>;
+/// bonus5 <bonus type>,<val1>,<val2>,<val3>,<val4>,<val5>;
+BUILDIN_FUNC(bonus)
+{
+ int type;
+ int val1;
+ int val2 = 0;
+ int val3 = 0;
+ int val4 = 0;
+ int val5 = 0;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ type = script_getnum(st,2);
+ switch( type ) {
+ case SP_AUTOSPELL:
+ case SP_AUTOSPELL_WHENHIT:
+ case SP_AUTOSPELL_ONSKILL:
+ case SP_SKILL_ATK:
+ case SP_SKILL_HEAL:
+ case SP_SKILL_HEAL2:
+ case SP_ADD_SKILL_BLOW:
+ case SP_CASTRATE:
+ case SP_ADDEFF_ONSKILL:
+ case SP_SKILL_USE_SP_RATE:
+ case SP_SKILL_COOLDOWN:
+ case SP_SKILL_FIXEDCAST:
+ case SP_SKILL_VARIABLECAST:
+ case SP_VARCASTRATE:
+ case SP_SKILL_USE_SP:
+ // these bonuses support skill names
+ val1 = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ break;
+ default:
+ val1 = script_getnum(st,3);
+ break;
+ }
+
+ switch( script_lastdata(st)-2 ) {
+ case 1:
+ pc_bonus(sd, type, val1);
+ break;
+ case 2:
+ val2 = script_getnum(st,4);
+ pc_bonus2(sd, type, val1, val2);
+ break;
+ case 3:
+ val2 = script_getnum(st,4);
+ val3 = script_getnum(st,5);
+ pc_bonus3(sd, type, val1, val2, val3);
+ break;
+ case 4:
+ if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) )
+ val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name
+ else
+ val2 = script_getnum(st,4);
+
+ val3 = script_getnum(st,5);
+ val4 = script_getnum(st,6);
+ pc_bonus4(sd, type, val1, val2, val3, val4);
+ break;
+ case 5:
+ if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) )
+ val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name
+ else
+ val2 = script_getnum(st,4);
+
+ val3 = script_getnum(st,5);
+ val4 = script_getnum(st,6);
+ val5 = script_getnum(st,7);
+ pc_bonus5(sd, type, val1, val2, val3, val4, val5);
+ break;
+ default:
+ ShowDebug("buildin_bonus: unexpected number of arguments (%d)\n", (script_lastdata(st) - 1));
+ break;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(autobonus)
+{
+ unsigned int dur;
+ short rate;
+ short atk_type = 0;
+ TBL_PC* sd;
+ const char *bonus_script, *other_script = NULL;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
+ return 0;
+
+ rate = script_getnum(st,3);
+ dur = script_getnum(st,4);
+ bonus_script = script_getstr(st,2);
+ if( !rate || !dur || !bonus_script )
+ return 0;
+
+ if( script_hasdata(st,5) )
+ atk_type = script_getnum(st,5);
+ if( script_hasdata(st,6) )
+ other_script = script_getstr(st,6);
+
+ if( pc_addautobonus(sd->autobonus,ARRAYLENGTH(sd->autobonus),
+ bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) )
+ {
+ script_add_autobonus(bonus_script);
+ if( other_script )
+ script_add_autobonus(other_script);
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(autobonus2)
+{
+ unsigned int dur;
+ short rate;
+ short atk_type = 0;
+ TBL_PC* sd;
+ const char *bonus_script, *other_script = NULL;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
+ return 0;
+
+ rate = script_getnum(st,3);
+ dur = script_getnum(st,4);
+ bonus_script = script_getstr(st,2);
+ if( !rate || !dur || !bonus_script )
+ return 0;
+
+ if( script_hasdata(st,5) )
+ atk_type = script_getnum(st,5);
+ if( script_hasdata(st,6) )
+ other_script = script_getstr(st,6);
+
+ if( pc_addautobonus(sd->autobonus2,ARRAYLENGTH(sd->autobonus2),
+ bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) )
+ {
+ script_add_autobonus(bonus_script);
+ if( other_script )
+ script_add_autobonus(other_script);
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(autobonus3)
+{
+ unsigned int dur;
+ short rate,atk_type;
+ TBL_PC* sd;
+ const char *bonus_script, *other_script = NULL;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
+ return 0;
+
+ rate = script_getnum(st,3);
+ dur = script_getnum(st,4);
+ atk_type = ( script_isstring(st,5) ? skill_name2id(script_getstr(st,5)) : script_getnum(st,5) );
+ bonus_script = script_getstr(st,2);
+ if( !rate || !dur || !atk_type || !bonus_script )
+ return 0;
+
+ if( script_hasdata(st,6) )
+ other_script = script_getstr(st,6);
+
+ if( pc_addautobonus(sd->autobonus3,ARRAYLENGTH(sd->autobonus3),
+ bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,true) )
+ {
+ script_add_autobonus(bonus_script);
+ if( other_script )
+ script_add_autobonus(other_script);
+ }
+
+ return 0;
+}
+
+/// Changes the level of a player skill.
+/// <flag> defaults to 1
+/// <flag>=0 : set the level of the skill
+/// <flag>=1 : set the temporary level of the skill
+/// <flag>=2 : add to the level of the skill
+///
+/// skill <skill id>,<level>,<flag>
+/// skill <skill id>,<level>
+/// skill "<skill name>",<level>,<flag>
+/// skill "<skill name>",<level>
+BUILDIN_FUNC(skill)
+{
+ int id;
+ int level;
+ int flag = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ level = script_getnum(st,3);
+ if( script_hasdata(st,4) )
+ flag = script_getnum(st,4);
+ pc_skill(sd, id, level, flag);
+
+ return 0;
+}
+
+/// Changes the level of a player skill.
+/// like skill, but <flag> defaults to 2
+///
+/// addtoskill <skill id>,<amount>,<flag>
+/// addtoskill <skill id>,<amount>
+/// addtoskill "<skill name>",<amount>,<flag>
+/// addtoskill "<skill name>",<amount>
+///
+/// @see skill
+BUILDIN_FUNC(addtoskill)
+{
+ int id;
+ int level;
+ int flag = 2;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ level = script_getnum(st,3);
+ if( script_hasdata(st,4) )
+ flag = script_getnum(st,4);
+ pc_skill(sd, id, level, flag);
+
+ return 0;
+}
+
+/// Increases the level of a guild skill.
+///
+/// guildskill <skill id>,<amount>;
+/// guildskill "<skill name>",<amount>;
+BUILDIN_FUNC(guildskill)
+{
+ int id;
+ int level;
+ TBL_PC* sd;
+ int i;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ level = script_getnum(st,3);
+ for( i=0; i < level; i++ )
+ guild_skillup(sd, id);
+
+ return 0;
+}
+
+/// Returns the level of the player skill.
+///
+/// getskilllv(<skill id>) -> <level>
+/// getskilllv("<skill name>") -> <level>
+BUILDIN_FUNC(getskilllv)
+{
+ int id;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ script_pushint(st, pc_checkskill(sd,id));
+
+ return 0;
+}
+
+/// Returns the level of the guild skill.
+///
+/// getgdskilllv(<guild id>,<skill id>) -> <level>
+/// getgdskilllv(<guild id>,"<skill name>") -> <level>
+BUILDIN_FUNC(getgdskilllv)
+{
+ int guild_id;
+ uint16 skill_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+ skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ g = guild_search(guild_id);
+ if( g == NULL )
+ script_pushint(st, -1);
+ else
+ script_pushint(st, guild_checkskill(g,skill_id));
+
+ return 0;
+}
+
+/// Returns the 'basic_skill_check' setting.
+/// This config determines if the server checks the skill level of NV_BASIC
+/// before allowing the basic actions.
+///
+/// basicskillcheck() -> <bool>
+BUILDIN_FUNC(basicskillcheck)
+{
+ script_pushint(st, battle_config.basic_skill_check);
+ return 0;
+}
+
+/// Returns the GM level of the player.
+///
+/// getgmlevel() -> <level>
+BUILDIN_FUNC(getgmlevel)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ script_pushint(st, pc_get_group_level(sd));
+
+ return 0;
+}
+
+/// Returns the group ID of the player.
+///
+/// getgroupid() -> <int>
+BUILDIN_FUNC(getgroupid)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL)
+ return 1; // no player attached, report source
+ script_pushint(st, pc_get_group_id(sd));
+
+ return 0;
+}
+
+/// Terminates the execution of this script instance.
+///
+/// end
+BUILDIN_FUNC(end)
+{
+ st->state = END;
+ return 0;
+}
+
+/// Checks if the player has that effect state (option).
+///
+/// checkoption(<option>) -> <bool>
+BUILDIN_FUNC(checkoption)
+{
+ int option;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ option = script_getnum(st,2);
+ if( sd->sc.option&option )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Checks if the player is in that body state (opt1).
+///
+/// checkoption1(<opt1>) -> <bool>
+BUILDIN_FUNC(checkoption1)
+{
+ int opt1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ opt1 = script_getnum(st,2);
+ if( sd->sc.opt1 == opt1 )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Checks if the player has that health state (opt2).
+///
+/// checkoption2(<opt2>) -> <bool>
+BUILDIN_FUNC(checkoption2)
+{
+ int opt2;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ opt2 = script_getnum(st,2);
+ if( sd->sc.opt2&opt2 )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Changes the effect state (option) of the player.
+/// <flag> defaults to 1
+/// <flag>=0 : removes the option
+/// <flag>=other : adds the option
+///
+/// setoption <option>,<flag>;
+/// setoption <option>;
+BUILDIN_FUNC(setoption)
+{
+ int option;
+ int flag = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ option = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ flag = script_getnum(st,3);
+ else if( !option ){// Request to remove everything.
+ flag = 0;
+ option = OPTION_FALCON|OPTION_RIDING;
+#ifndef NEW_CARTS
+ option |= OPTION_CART;
+#endif
+ }
+ if( flag ){// Add option
+ if( option&OPTION_WEDDING && !battle_config.wedding_modifydisplay )
+ option &= ~OPTION_WEDDING;// Do not show the wedding sprites
+ pc_setoption(sd, sd->sc.option|option);
+ } else// Remove option
+ pc_setoption(sd, sd->sc.option&~option);
+
+ return 0;
+}
+
+/// Returns if the player has a cart.
+///
+/// checkcart() -> <bool>
+///
+/// @author Valaris
+BUILDIN_FUNC(checkcart)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_iscarton(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets the cart of the player.
+/// <type> defaults to 1
+/// <type>=0 : removes the cart
+/// <type>=1 : Normal cart
+/// <type>=2 : Wooden cart
+/// <type>=3 : Covered cart with flowers and ferns
+/// <type>=4 : Wooden cart with a Panda doll on the back
+/// <type>=5 : Normal cart with bigger wheels, a roof and a banner on the back
+///
+/// setcart <type>;
+/// setcart;
+BUILDIN_FUNC(setcart)
+{
+ int type = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( script_hasdata(st,2) )
+ type = script_getnum(st,2);
+ pc_setcart(sd, type);
+
+ return 0;
+}
+
+/// Returns if the player has a falcon.
+///
+/// checkfalcon() -> <bool>
+///
+/// @author Valaris
+BUILDIN_FUNC(checkfalcon)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_isfalcon(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets if the player has a falcon or not.
+/// <flag> defaults to 1
+///
+/// setfalcon <flag>;
+/// setfalcon;
+BUILDIN_FUNC(setfalcon)
+{
+ int flag = 1;
+ 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);
+
+ pc_setfalcon(sd, flag);
+
+ return 0;
+}
+
+/// Returns if the player is riding.
+///
+/// checkriding() -> <bool>
+///
+/// @author Valaris
+BUILDIN_FUNC(checkriding)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_isriding(sd) || pc_isridingwug(sd) || pc_isridingdragon(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets if the player is riding.
+/// <flag> defaults to 1
+///
+/// setriding <flag>;
+/// setriding;
+BUILDIN_FUNC(setriding)
+{
+ int flag = 1;
+ 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);
+ pc_setriding(sd, flag);
+
+ return 0;
+}
+
+/// Returns if the player has a warg.
+///
+/// checkwug() -> <bool>
+///
+BUILDIN_FUNC(checkwug)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_iswug(sd) || pc_isridingwug(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Returns if the player is wearing MADO Gear.
+///
+/// checkmadogear() -> <bool>
+///
+BUILDIN_FUNC(checkmadogear)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_ismadogear(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets if the player is riding MADO Gear.
+/// <flag> defaults to 1
+///
+/// setmadogear <flag>;
+/// setmadogear;
+BUILDIN_FUNC(setmadogear)
+{
+ int flag = 1;
+ 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);
+ pc_setmadogear(sd, flag);
+
+ return 0;
+}
+
+/// Sets the save point of the player.
+///
+/// save "<map name>",<x>,<y>
+/// savepoint "<map name>",<x>,<y>
+BUILDIN_FUNC(savepoint)
+{
+ int x;
+ int y;
+ short map;
+ const char* str;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ str = script_getstr(st, 2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ map = mapindex_name2id(str);
+ if( map )
+ pc_setsavepoint(sd, map, x, y);
+
+ return 0;
+}
+
+/*==========================================
+ * GetTimeTick(0: System Tick, 1: Time Second Tick)
+ *------------------------------------------*/
+BUILDIN_FUNC(gettimetick) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=script_getnum(st,2);
+
+ switch(type){
+ case 2:
+ //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC
+ // from the system clock.)
+ script_pushint(st,(int)time(NULL));
+ break;
+ case 1:
+ //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59)
+ time(&timer);
+ t=localtime(&timer);
+ script_pushint(st,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec));
+ break;
+ case 0:
+ default:
+ //type 0:(System Ticks)
+ script_pushint(st,gettick());
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTime(Type);
+ * 1: Sec 2: Min 3: Hour
+ * 4: WeekDay 5: MonthDay 6: Month
+ * 7: Year
+ *------------------------------------------*/
+BUILDIN_FUNC(gettime) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=script_getnum(st,2);
+
+ time(&timer);
+ t=localtime(&timer);
+
+ switch(type){
+ case 1://Sec(0~59)
+ script_pushint(st,t->tm_sec);
+ break;
+ case 2://Min(0~59)
+ script_pushint(st,t->tm_min);
+ break;
+ case 3://Hour(0~23)
+ script_pushint(st,t->tm_hour);
+ break;
+ case 4://WeekDay(0~6)
+ script_pushint(st,t->tm_wday);
+ break;
+ case 5://MonthDay(01~31)
+ script_pushint(st,t->tm_mday);
+ break;
+ case 6://Month(01~12)
+ script_pushint(st,t->tm_mon+1);
+ break;
+ case 7://Year(20xx)
+ script_pushint(st,t->tm_year+1900);
+ break;
+ case 8://Year Day(01~366)
+ script_pushint(st,t->tm_yday+1);
+ break;
+ default://(format error)
+ script_pushint(st,-1);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTimeStr("TimeFMT", Length);
+ *------------------------------------------*/
+BUILDIN_FUNC(gettimestr)
+{
+ char *tmpstr;
+ const char *fmtstr;
+ int maxlen;
+ time_t now = time(NULL);
+
+ fmtstr=script_getstr(st,2);
+ maxlen=script_getnum(st,3);
+
+ tmpstr=(char *)aMalloc((maxlen+1)*sizeof(char));
+ strftime(tmpstr,maxlen,fmtstr,localtime(&now));
+ tmpstr[maxlen]='\0';
+
+ script_pushstr(st,tmpstr);
+ return 0;
+}
+
+/*==========================================
+ * Open player storage
+ *------------------------------------------*/
+BUILDIN_FUNC(openstorage)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ storage_storageopen(sd);
+ return 0;
+}
+
+BUILDIN_FUNC(guildopenstorage)
+{
+ TBL_PC* sd;
+ int ret;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ ret = storage_guild_storageopen(sd);
+ script_pushint(st,ret);
+ return 0;
+}
+
+/*==========================================
+ * Make player use a skill trought item usage
+ *------------------------------------------*/
+/// itemskill <skill id>,<level>
+/// itemskill "<skill name>",<level>
+BUILDIN_FUNC(itemskill)
+{
+ int id;
+ int lv;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL || sd->ud.skilltimer != INVALID_TIMER )
+ return 0;
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ lv = script_getnum(st,3);
+
+ sd->skillitem=id;
+ sd->skillitemlv=lv;
+ clif_item_skill(sd,id,lv);
+ return 0;
+}
+/*==========================================
+ * Attempt to create an item
+ *------------------------------------------*/
+BUILDIN_FUNC(produce)
+{
+ int trigger;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ trigger=script_getnum(st,2);
+ clif_skill_produce_mix_list(sd, -1, trigger);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(cooking)
+{
+ int trigger;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ trigger=script_getnum(st,2);
+ clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1);
+ return 0;
+}
+/*==========================================
+ * Create a pet
+ *------------------------------------------*/
+BUILDIN_FUNC(makepet)
+{
+ TBL_PC* sd;
+ int id,pet_id;
+
+ id=script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pet_id = search_petDB_index(id, PET_CLASS);
+
+ if (pet_id < 0)
+ pet_id = search_petDB_index(id, PET_EGG);
+ if (pet_id >= 0 && sd) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(
+ sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Give player exp base,job * quest_exp_rate/100
+ *------------------------------------------*/
+BUILDIN_FUNC(getexp)
+{
+ TBL_PC* sd;
+ int base=0,job=0;
+ double bonus;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ base=script_getnum(st,2);
+ job =script_getnum(st,3);
+ if(base<0 || job<0)
+ return 0;
+
+ // bonus for npc-given exp
+ bonus = battle_config.quest_exp_rate / 100.;
+ base = (int) cap_value(base * bonus, 0, INT_MAX);
+ job = (int) cap_value(job * bonus, 0, INT_MAX);
+
+ pc_gainexp(sd, NULL, base, job, true);
+
+ return 0;
+}
+
+/*==========================================
+ * Gain guild exp [Celest]
+ *------------------------------------------*/
+BUILDIN_FUNC(guildgetexp)
+{
+ TBL_PC* sd;
+ int exp;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ exp = script_getnum(st,2);
+ if(exp < 0)
+ return 0;
+ if(sd && sd->status.guild_id > 0)
+ guild_getexp (sd, exp);
+
+ return 0;
+}
+
+/*==========================================
+ * Changes the guild master of a guild [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(guildchangegm)
+{
+ TBL_PC *sd;
+ int guild_id;
+ const char *name;
+
+ guild_id = script_getnum(st,2);
+ name = script_getstr(st,3);
+ sd=map_nick2sd(name);
+
+ if (!sd)
+ script_pushint(st,0);
+ else
+ script_pushint(st,guild_gm_change(guild_id, sd));
+
+ return 0;
+}
+
+/*==========================================
+ * Spawn a monster :
+ @mapn,x,y : location
+ @str : monster name
+ @class_ : mob_id
+ @amount : nb to spawn
+ @event : event to attach to mob
+ *------------------------------------------*/
+BUILDIN_FUNC(monster)
+{
+ const char* mapn = script_getstr(st,2);
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ const char* str = script_getstr(st,5);
+ int class_ = script_getnum(st,6);
+ int amount = script_getnum(st,7);
+ const char* event = "";
+ unsigned int size = SZ_SMALL;
+ unsigned int ai = AI_NONE;
+
+ struct map_session_data* sd;
+ int16 m;
+
+ if (script_hasdata(st, 8))
+ {
+ event = script_getstr(st, 8);
+ check_event(st, event);
+ }
+
+ if (script_hasdata(st, 9))
+ {
+ size = script_getnum(st, 9);
+ if (size > 3)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_);
+ return 1;
+ }
+ }
+
+ if (script_hasdata(st, 10))
+ {
+ ai = script_getnum(st, 10);
+ if (ai > 4)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_);
+ return 1;
+ }
+ }
+
+ if (class_ >= 0 && !mobdb_checkid(class_))
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_);
+ return 1;
+ }
+
+ sd = map_id2sd(st->rid);
+
+ if (sd && strcmp(mapn, "this") == 0)
+ m = sd->bl.m;
+ else
+ {
+ m = map_mapname2mapid(mapn);
+ if (map[m].flag.src4instance && st->instance_id)
+ { // Try to redirect to the instance map, not the src map
+ if ((m = instance_mapid2imapid(m, st->instance_id)) < 0)
+ {
+ ShowError("buildin_monster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn);
+ return 1;
+ }
+ }
+ }
+
+ mob_once_spawn(sd, m, x, y, str, class_, amount, event, size, ai);
+ return 0;
+}
+/*==========================================
+ * Request List of Monster Drops
+ *------------------------------------------*/
+BUILDIN_FUNC(getmobdrops)
+{
+ int class_ = script_getnum(st,2);
+ int i, j = 0;
+ struct mob_db *mob;
+
+ if( !mobdb_checkid(class_) )
+ {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ mob = mob_db(class_);
+
+ for( i = 0; i < MAX_MOB_DROP; i++ )
+ {
+ if( mob->dropitem[i].nameid < 1 )
+ continue;
+ if( itemdb_exists(mob->dropitem[i].nameid) == NULL )
+ continue;
+
+ mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), mob->dropitem[i].nameid);
+ mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].p);
+
+ j++;
+ }
+
+ mapreg_setreg(add_str("$@MobDrop_count"), j);
+ script_pushint(st, 1);
+
+ return 0;
+}
+/*==========================================
+ * Same as monster but randomize location in x0,x1,y0,y1 area
+ *------------------------------------------*/
+BUILDIN_FUNC(areamonster)
+{
+ const char* mapn = script_getstr(st,2);
+ int x0 = script_getnum(st,3);
+ int y0 = script_getnum(st,4);
+ int x1 = script_getnum(st,5);
+ int y1 = script_getnum(st,6);
+ const char* str = script_getstr(st,7);
+ int class_ = script_getnum(st,8);
+ int amount = script_getnum(st,9);
+ const char* event = "";
+ unsigned int size = SZ_SMALL;
+ unsigned int ai = AI_NONE;
+
+ struct map_session_data* sd;
+ int16 m;
+
+ if (script_hasdata(st,10))
+ {
+ event = script_getstr(st, 10);
+ check_event(st, event);
+ }
+
+ if (script_hasdata(st, 11))
+ {
+ size = script_getnum(st, 11);
+ if (size > 3)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_);
+ return 1;
+ }
+ }
+
+ if (script_hasdata(st, 12))
+ {
+ ai = script_getnum(st, 12);
+ if (ai > 4)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_);
+ return 1;
+ }
+ }
+
+ sd = map_id2sd(st->rid);
+
+ if (sd && strcmp(mapn, "this") == 0)
+ m = sd->bl.m;
+ else
+ {
+ m = map_mapname2mapid(mapn);
+ if (map[m].flag.src4instance && st->instance_id)
+ { // Try to redirect to the instance map, not the src map
+ if ((m = instance_mapid2imapid(m, st->instance_id)) < 0)
+ {
+ ShowError("buildin_areamonster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn);
+ return 1;
+ }
+ }
+ }
+
+ mob_once_spawn_area(sd, m, x0, y0, x1, y1, str, class_, amount, event, size, ai);
+ return 0;
+}
+/*==========================================
+ * KillMonster subcheck, verify if mob to kill ain't got an even to handle, could be force kill by allflag
+ *------------------------------------------*/
+ static int buildin_killmonster_sub_strip(struct block_list *bl,va_list ap)
+{ //same fix but with killmonster instead - stripping events from mobs.
+ TBL_MOB* md = (TBL_MOB*)bl;
+ char *event=va_arg(ap,char *);
+ int allflag=va_arg(ap,int);
+
+ md->state.npc_killmonster = 1;
+
+ if(!allflag){
+ if(strcmp(event,md->npc_event)==0)
+ status_kill(bl);
+ }else{
+ if(!md->spawn)
+ status_kill(bl);
+ }
+ md->state.npc_killmonster = 0;
+ return 0;
+}
+static int buildin_killmonster_sub(struct block_list *bl,va_list ap)
+{
+ TBL_MOB* md = (TBL_MOB*)bl;
+ char *event=va_arg(ap,char *);
+ int allflag=va_arg(ap,int);
+
+ if(!allflag){
+ if(strcmp(event,md->npc_event)==0)
+ status_kill(bl);
+ }else{
+ if(!md->spawn)
+ status_kill(bl);
+ }
+ return 0;
+}
+BUILDIN_FUNC(killmonster)
+{
+ const char *mapname,*event;
+ int16 m,allflag=0;
+ mapname=script_getstr(st,2);
+ event=script_getstr(st,3);
+ if(strcmp(event,"All")==0)
+ allflag = 1;
+ else
+ check_event(st, event);
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+
+ if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ return 0;
+
+ if( script_hasdata(st,4) ) {
+ if ( script_getnum(st,4) == 1 ) {
+ map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag);
+ return 0;
+ }
+ }
+
+ map_freeblock_lock();
+ map_foreachinmap(buildin_killmonster_sub_strip, m, BL_MOB, event ,allflag);
+ map_freeblock_unlock();
+ return 0;
+}
+
+static int buildin_killmonsterall_sub_strip(struct block_list *bl,va_list ap)
+{ //Strips the event from the mob if it's killed the old method.
+ struct mob_data *md;
+
+ md = BL_CAST(BL_MOB, bl);
+ if (md->npc_event[0])
+ md->npc_event[0] = 0;
+
+ status_kill(bl);
+ return 0;
+}
+static int buildin_killmonsterall_sub(struct block_list *bl,va_list ap)
+{
+ status_kill(bl);
+ return 0;
+}
+BUILDIN_FUNC(killmonsterall)
+{
+ const char *mapname;
+ int16 m;
+ mapname=script_getstr(st,2);
+
+ if( (m = map_mapname2mapid(mapname))<0 )
+ return 0;
+
+ if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ return 0;
+
+ if( script_hasdata(st,3) ) {
+ if ( script_getnum(st,3) == 1 ) {
+ map_foreachinmap(buildin_killmonsterall_sub,m,BL_MOB);
+ return 0;
+ }
+ }
+
+ map_foreachinmap(buildin_killmonsterall_sub_strip,m,BL_MOB);
+ return 0;
+}
+
+/*==========================================
+ * Creates a clone of a player.
+ * clone map, x, y, event, char_id, master_id, mode, flag, duration
+ *------------------------------------------*/
+BUILDIN_FUNC(clone)
+{
+ TBL_PC *sd, *msd=NULL;
+ int char_id,master_id=0,x,y, mode = 0, flag = 0, m;
+ unsigned int duration = 0;
+ const char *map,*event="";
+
+ map=script_getstr(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+ event=script_getstr(st,5);
+ char_id=script_getnum(st,6);
+
+ if( script_hasdata(st,7) )
+ master_id=script_getnum(st,7);
+
+ if( script_hasdata(st,8) )
+ mode=script_getnum(st,8);
+
+ if( script_hasdata(st,9) )
+ flag=script_getnum(st,9);
+
+ if( script_hasdata(st,10) )
+ duration=script_getnum(st,10);
+
+ check_event(st, event);
+
+ m = map_mapname2mapid(map);
+ if (m < 0) return 0;
+
+ sd = map_charid2sd(char_id);
+
+ if (master_id) {
+ msd = map_charid2sd(master_id);
+ if (msd)
+ master_id = msd->bl.id;
+ else
+ master_id = 0;
+ }
+ if (sd) //Return ID of newly crafted clone.
+ script_pushint(st,mob_clone_spawn(sd, m, x, y, event, master_id, mode, flag, 1000*duration));
+ else //Failed to create clone.
+ script_pushint(st,0);
+
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(doevent)
+{
+ const char* event = script_getstr(st,2);
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ check_event(st, event);
+ npc_event(sd, event, 0);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(donpcevent)
+{
+ const char* event = script_getstr(st,2);
+ check_event(st, event);
+ if( !npc_event_do(event) ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ ShowDebug("NPCEvent '%s' not found! (source: %s)\n",event,nd?nd->name:"Unknown");
+ script_pushint(st, 0);
+ } else
+ script_pushint(st, 1);
+ return 0;
+}
+
+/// for Aegis compatibility
+/// basically a specialized 'donpcevent', with the event specified as two arguments instead of one
+BUILDIN_FUNC(cmdothernpc) // Added by RoVeRT
+{
+ const char* npc = script_getstr(st,2);
+ const char* command = script_getstr(st,3);
+ char event[EVENT_NAME_LENGTH];
+ snprintf(event, sizeof(event), "%s::OnCommand%s", npc, command);
+ check_event(st, event);
+ npc_event_do(event);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(addtimer)
+{
+ int tick = script_getnum(st,2);
+ const char* event = script_getstr(st, 3);
+ TBL_PC* sd;
+
+ check_event(st, event);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_addeventtimer(sd,tick,event);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(deltimer)
+{
+ const char *event;
+ TBL_PC* sd;
+
+ event=script_getstr(st, 2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ check_event(st, event);
+ pc_deleventtimer(sd,event);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(addtimercount)
+{
+ const char *event;
+ int tick;
+ TBL_PC* sd;
+
+ event=script_getstr(st, 2);
+ tick=script_getnum(st,3);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ check_event(st, event);
+ pc_addeventtimercount(sd,event,tick);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(initnpctimer)
+{
+ struct npc_data *nd;
+ int flag = 0;
+
+ if( script_hasdata(st,3) )
+ { //Two arguments: NPC name and attach flag.
+ nd = npc_name2id(script_getstr(st, 2));
+ flag = script_getnum(st,3);
+ }
+ else if( script_hasdata(st,2) )
+ { //Check if argument is numeric (flag) or string (npc name)
+ struct script_data *data;
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) //NPC name
+ nd = npc_name2id(conv_str(st, data));
+ else if( data_isint(data) ) //Flag
+ {
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ flag = conv_num(st,data);
+ }
+ else
+ {
+ ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
+ return 1;
+ }
+ }
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd )
+ return 0;
+ if( flag ) //Attach
+ {
+ TBL_PC* sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+ nd->u.scr.rid = sd->bl.id;
+ }
+
+ nd->u.scr.timertick = 0;
+ npc_settimerevent_tick(nd,0);
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(startnpctimer)
+{
+ struct npc_data *nd;
+ int flag = 0;
+
+ if( script_hasdata(st,3) )
+ { //Two arguments: NPC name and attach flag.
+ nd = npc_name2id(script_getstr(st, 2));
+ flag = script_getnum(st,3);
+ }
+ else if( script_hasdata(st,2) )
+ { //Check if argument is numeric (flag) or string (npc name)
+ struct script_data *data;
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) //NPC name
+ nd = npc_name2id(conv_str(st, data));
+ else if( data_isint(data) ) //Flag
+ {
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ flag = conv_num(st,data);
+ }
+ else
+ {
+ ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
+ return 1;
+ }
+ }
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd )
+ return 0;
+ if( flag ) //Attach
+ {
+ TBL_PC* sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+ nd->u.scr.rid = sd->bl.id;
+ }
+
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(stopnpctimer)
+{
+ struct npc_data *nd;
+ int flag = 0;
+
+ if( script_hasdata(st,3) )
+ { //Two arguments: NPC name and attach flag.
+ nd = npc_name2id(script_getstr(st, 2));
+ flag = script_getnum(st,3);
+ }
+ else if( script_hasdata(st,2) )
+ { //Check if argument is numeric (flag) or string (npc name)
+ struct script_data *data;
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) //NPC name
+ nd = npc_name2id(conv_str(st, data));
+ else if( data_isint(data) ) //Flag
+ {
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ flag = conv_num(st,data);
+ }
+ else
+ {
+ ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
+ return 1;
+ }
+ }
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd )
+ return 0;
+ if( flag ) //Detach
+ nd->u.scr.rid = 0;
+
+ npc_timerevent_stop(nd);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(getnpctimer)
+{
+ struct npc_data *nd;
+ TBL_PC *sd;
+ int type = script_getnum(st,2);
+ int val = 0;
+
+ if( script_hasdata(st,3) )
+ nd = npc_name2id(script_getstr(st,3));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,0);
+ ShowError("getnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ switch( type )
+ {
+ case 0: val = npc_gettimerevent_tick(nd); break;
+ case 1:
+ if( nd->u.scr.rid )
+ {
+ sd = map_id2sd(nd->u.scr.rid);
+ if( !sd )
+ {
+ ShowError("buildin_getnpctimer: Attached player not found!\n");
+ break;
+ }
+ val = (sd->npc_timer_id != INVALID_TIMER);
+ }
+ else
+ val = (nd->u.scr.timerid != INVALID_TIMER);
+ break;
+ case 2: val = nd->u.scr.timeramount; break;
+ }
+
+ script_pushint(st,val);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(setnpctimer)
+{
+ int tick;
+ struct npc_data *nd;
+
+ tick = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ nd = npc_name2id(script_getstr(st,3));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,1);
+ ShowError("setnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ npc_settimerevent_tick(nd,tick);
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * attaches the player rid to the timer [Celest]
+ *------------------------------------------*/
+BUILDIN_FUNC(attachnpctimer)
+{
+ TBL_PC *sd;
+ struct npc_data *nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,1);
+ ShowError("setnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ if( script_hasdata(st,2) )
+ sd = map_nick2sd(script_getstr(st,2));
+ else
+ sd = script_rid2sd(st);
+
+ if( !sd )
+ {
+ script_pushint(st,1);
+ ShowWarning("attachnpctimer: Invalid player.\n");
+ return 1;
+ }
+
+ nd->u.scr.rid = sd->bl.id;
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * detaches a player rid from the timer [Celest]
+ *------------------------------------------*/
+BUILDIN_FUNC(detachnpctimer)
+{
+ struct npc_data *nd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st,2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,1);
+ ShowError("detachnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ nd->u.scr.rid = 0;
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * To avoid "player not attached" script errors, this function is provided,
+ * it checks if there is a player attached to the current script. [Skotlex]
+ * If no, returns 0, if yes, returns the account_id of the attached player.
+ *------------------------------------------*/
+BUILDIN_FUNC(playerattached)
+{
+ if(st->rid == 0 || map_id2sd(st->rid) == NULL)
+ script_pushint(st,0);
+ else
+ script_pushint(st,st->rid);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(announce)
+{
+ const char *mes = script_getstr(st,2);
+ int flag = script_getnum(st,3);
+ const char *fontColor = script_hasdata(st,4) ? script_getstr(st,4) : NULL;
+ int fontType = script_hasdata(st,5) ? script_getnum(st,5) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,6) ? script_getnum(st,6) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,7) ? script_getnum(st,7) : 0; // default fontAlign
+ int fontY = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontY
+
+ if (flag&0x0f) // Broadcast source or broadcast region defined
+ {
+ send_target target;
+ struct block_list *bl = (flag&0x08) ? map_id2bl(st->oid) : (struct block_list *)script_rid2sd(st); // If bc_npc flag is set, use NPC as broadcast source
+ if (bl == NULL)
+ return 0;
+
+ flag &= 0x07;
+ target = (flag == 1) ? ALL_SAMEMAP :
+ (flag == 2) ? AREA :
+ (flag == 3) ? SELF :
+ ALL_CLIENT;
+ if (fontColor)
+ clif_broadcast2(bl, mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target);
+ else
+ clif_broadcast(bl, mes, (int)strlen(mes)+1, flag&0xf0, target);
+ }
+ else
+ {
+ if (fontColor)
+ intif_broadcast2(mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY);
+ else
+ intif_broadcast(mes, (int)strlen(mes)+1, flag&0xf0);
+ }
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+static int buildin_announce_sub(struct block_list *bl, va_list ap)
+{
+ char *mes = va_arg(ap, char *);
+ int len = va_arg(ap, int);
+ int type = va_arg(ap, int);
+ char *fontColor = va_arg(ap, char *);
+ short fontType = (short)va_arg(ap, int);
+ short fontSize = (short)va_arg(ap, int);
+ short fontAlign = (short)va_arg(ap, int);
+ short fontY = (short)va_arg(ap, int);
+ if (fontColor)
+ clif_broadcast2(bl, mes, len, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, SELF);
+ else
+ clif_broadcast(bl, mes, len, type, SELF);
+ return 0;
+}
+
+BUILDIN_FUNC(mapannounce)
+{
+ const char *mapname = script_getstr(st,2);
+ const char *mes = script_getstr(st,3);
+ int flag = script_getnum(st,4);
+ const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL;
+ int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign
+ int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY
+ int16 m;
+
+ if ((m = map_mapname2mapid(mapname)) < 0)
+ return 0;
+
+ map_foreachinmap(buildin_announce_sub, m, BL_PC,
+ mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(areaannounce)
+{
+ const char *mapname = script_getstr(st,2);
+ int x0 = script_getnum(st,3);
+ int y0 = script_getnum(st,4);
+ int x1 = script_getnum(st,5);
+ int y1 = script_getnum(st,6);
+ const char *mes = script_getstr(st,7);
+ int flag = script_getnum(st,8);
+ const char *fontColor = script_hasdata(st,9) ? script_getstr(st,9) : NULL;
+ int fontType = script_hasdata(st,10) ? script_getnum(st,10) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,11) ? script_getnum(st,11) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,12) ? script_getnum(st,12) : 0; // default fontAlign
+ int fontY = script_hasdata(st,13) ? script_getnum(st,13) : 0; // default fontY
+ int16 m;
+
+ if ((m = map_mapname2mapid(mapname)) < 0)
+ return 0;
+
+ map_foreachinarea(buildin_announce_sub, m, x0, y0, x1, y1, BL_PC,
+ mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(getusers)
+{
+ int flag, val = 0;
+ struct map_session_data* sd;
+ struct block_list* bl = NULL;
+
+ flag = script_getnum(st,2);
+
+ switch(flag&0x07)
+ {
+ case 0:
+ if(flag&0x8)
+ {// npc
+ bl = map_id2bl(st->oid);
+ }
+ else if((sd = script_rid2sd(st))!=NULL)
+ {// pc
+ bl = &sd->bl;
+ }
+
+ if(bl)
+ {
+ val = map[bl->m].users;
+ }
+ break;
+ case 1:
+ val = map_getusers();
+ break;
+ default:
+ ShowWarning("buildin_getusers: Unknown type %d.\n", flag);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ script_pushint(st,val);
+ return 0;
+}
+/*==========================================
+ * Works like @WHO - displays all online users names in window
+ *------------------------------------------*/
+BUILDIN_FUNC(getusersname)
+{
+ TBL_PC *sd, *pl_sd;
+ int /*disp_num=1,*/ group_level = 0;
+ struct s_mapiterator* iter;
+
+ sd = script_rid2sd(st);
+ if (!sd) return 0;
+
+ group_level = pc_get_group_level(sd);
+ iter = mapit_getallusers();
+ for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
+ {
+ if (pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) && pc_get_group_level(pl_sd) > group_level)
+ continue; // skip hidden sessions
+
+ /* Temporary fix for bugreport:1023.
+ * Do not uncomment unless you want thousands of 'next' buttons.
+ if((disp_num++)%10==0)
+ clif_scriptnext(sd,st->oid);*/
+ clif_scriptmes(sd,st->oid,pl_sd->status.name);
+ }
+ mapit_free(iter);
+
+ return 0;
+}
+/*==========================================
+ * getmapguildusers("mapname",guild ID) Returns the number guild members present on a map [Reddozen]
+ *------------------------------------------*/
+BUILDIN_FUNC(getmapguildusers)
+{
+ const char *str;
+ int16 m;
+ int gid;
+ int i=0,c=0;
+ struct guild *g = NULL;
+ str=script_getstr(st,2);
+ gid=script_getnum(st,3);
+ if ((m = map_mapname2mapid(str)) < 0) { // map id on this server (m == -1 if not in actual map-server)
+ script_pushint(st,-1);
+ return 0;
+ }
+ g = guild_search(gid);
+
+ if (g){
+ for(i = 0; i < g->max_member; i++)
+ {
+ if (g->member[i].sd && g->member[i].sd->bl.m == m)
+ c++;
+ }
+ }
+
+ script_pushint(st,c);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(getmapusers)
+{
+ const char *str;
+ int16 m;
+ str=script_getstr(st,2);
+ if( (m=map_mapname2mapid(str))< 0){
+ script_pushint(st,-1);
+ return 0;
+ }
+ script_pushint(st,map[m].users);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+static int buildin_getareausers_sub(struct block_list *bl,va_list ap)
+{
+ int *users=va_arg(ap,int *);
+ (*users)++;
+ return 0;
+}
+BUILDIN_FUNC(getareausers)
+{
+ const char *str;
+ int16 m,x0,y0,x1,y1,users=0; //doubt we can have more then 32k users on
+ str=script_getstr(st,2);
+ x0=script_getnum(st,3);
+ y0=script_getnum(st,4);
+ x1=script_getnum(st,5);
+ y1=script_getnum(st,6);
+ if( (m=map_mapname2mapid(str))< 0){
+ script_pushint(st,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareausers_sub,
+ m,x0,y0,x1,y1,BL_PC,&users);
+ script_pushint(st,users);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+static int buildin_getareadropitem_sub(struct block_list *bl,va_list ap)
+{
+ int item=va_arg(ap,int);
+ int *amount=va_arg(ap,int *);
+ struct flooritem_data *drop=(struct flooritem_data *)bl;
+
+ if(drop->item_data.nameid==item)
+ (*amount)+=drop->item_data.amount;
+
+ return 0;
+}
+BUILDIN_FUNC(getareadropitem)
+{
+ const char *str;
+ int16 m,x0,y0,x1,y1;
+ int item,amount=0;
+ struct script_data *data;
+
+ str=script_getstr(st,2);
+ x0=script_getnum(st,3);
+ y0=script_getnum(st,4);
+ x1=script_getnum(st,5);
+ y1=script_getnum(st,6);
+
+ data=script_getdata(st,7);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ item=UNKNOWN_ITEM_ID;
+ if( item_data )
+ item=item_data->nameid;
+ }else
+ item=conv_num(st,data);
+
+ if( (m=map_mapname2mapid(str))< 0){
+ script_pushint(st,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareadropitem_sub,
+ m,x0,y0,x1,y1,BL_ITEM,item,&amount);
+ script_pushint(st,amount);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(enablenpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,1);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(disablenpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,0);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(hideoffnpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,2);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(hideonnpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,4);
+ return 0;
+}
+
+/// Starts a status effect on the target unit or on the attached player.
+///
+/// sc_start <effect_id>,<duration>,<val1>{,<unit_id>};
+BUILDIN_FUNC(sc_start)
+{
+ struct block_list* bl;
+ enum sc_type type;
+ int tick;
+ int val1;
+ int val4 = 0;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+ if( script_hasdata(st,5) )
+ bl = map_id2bl(script_getnum(st,5));
+ else
+ bl = map_id2bl(st->rid);
+
+ if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
+ {// When there isn't a duration specified, try to get it from the skill_db
+ tick = skill_get_time(status_sc2skill(type), val1);
+ }
+
+ if( potion_flag == 1 && potion_target )
+ { //skill.c set the flags before running the script, this must be a potion-pitched effect.
+ bl = map_id2bl(potion_target);
+ tick /= 2;// Thrown potions only last half.
+ val4 = 1;// Mark that this was a thrown sc_effect
+ }
+
+ if( bl )
+ status_change_start(bl, type, 10000, val1, 0, 0, val4, tick, 2);
+
+ return 0;
+}
+
+/// Starts a status effect on the target unit or on the attached player.
+///
+/// sc_start2 <effect_id>,<duration>,<val1>,<percent chance>{,<unit_id>};
+BUILDIN_FUNC(sc_start2)
+{
+ struct block_list* bl;
+ enum sc_type type;
+ int tick;
+ int val1;
+ int val4 = 0;
+ int rate;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+ rate = script_getnum(st,5);
+ if( script_hasdata(st,6) )
+ bl = map_id2bl(script_getnum(st,6));
+ else
+ bl = map_id2bl(st->rid);
+
+ if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
+ {// When there isn't a duration specified, try to get it from the skill_db
+ tick = skill_get_time(status_sc2skill(type), val1);
+ }
+
+ if( potion_flag == 1 && potion_target )
+ { //skill.c set the flags before running the script, this must be a potion-pitched effect.
+ bl = map_id2bl(potion_target);
+ tick /= 2;// Thrown potions only last half.
+ val4 = 1;// Mark that this was a thrown sc_effect
+ }
+
+ if( bl )
+ status_change_start(bl, type, rate, val1, 0, 0, val4, tick, 2);
+
+ return 0;
+}
+
+/// Starts a status effect on the target unit or on the attached player.
+///
+/// sc_start4 <effect_id>,<duration>,<val1>,<val2>,<val3>,<val4>{,<unit_id>};
+BUILDIN_FUNC(sc_start4)
+{
+ struct block_list* bl;
+ enum sc_type type;
+ int tick;
+ int val1;
+ int val2;
+ int val3;
+ int val4;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+ val2 = script_getnum(st,5);
+ val3 = script_getnum(st,6);
+ val4 = script_getnum(st,7);
+ if( script_hasdata(st,8) )
+ bl = map_id2bl(script_getnum(st,8));
+ else
+ bl = map_id2bl(st->rid);
+
+ if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
+ {// When there isn't a duration specified, try to get it from the skill_db
+ tick = skill_get_time(status_sc2skill(type), val1);
+ }
+
+ if( potion_flag == 1 && potion_target )
+ { //skill.c set the flags before running the script, this must be a potion-pitched effect.
+ bl = map_id2bl(potion_target);
+ tick /= 2;// Thrown potions only last half.
+ }
+
+ if( bl )
+ status_change_start(bl, type, 10000, val1, val2, val3, val4, tick, 2);
+
+ return 0;
+}
+
+/// Ends one or all status effects on the target unit or on the attached player.
+///
+/// sc_end <effect_id>{,<unit_id>};
+BUILDIN_FUNC(sc_end)
+{
+ struct block_list* bl;
+ int type;
+
+ type = script_getnum(st, 2);
+ if (script_hasdata(st, 3))
+ bl = map_id2bl(script_getnum(st, 3));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag == 1 && potion_target) //##TODO how does this work [FlavioJS]
+ bl = map_id2bl(potion_target);
+
+ if (!bl)
+ return 0;
+
+ if (type >= 0 && type < SC_MAX)
+ {
+ struct status_change *sc = status_get_sc(bl);
+ struct status_change_entry *sce = sc ? sc->data[type] : NULL;
+
+ if (!sce)
+ return 0;
+
+
+ switch (type)
+ {
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_NOCHAT:
+ case SC_PUSH_CART:
+ return 0;
+
+ default:
+ break;
+ }
+
+ //This should help status_change_end force disabling the SC in case it has no limit.
+ sce->val1 = sce->val2 = sce->val3 = sce->val4 = 0;
+ status_change_end(bl, (sc_type)type, INVALID_TIMER);
+ }
+ else
+ status_change_clear(bl, 3); // remove all effects
+
+ return 0;
+}
+
+/*==========================================
+ * @FIXME atm will return reduced tick, 0 immune, 1 no tick
+ *------------------------------------------*/
+BUILDIN_FUNC(getscrate)
+{
+ struct block_list *bl;
+ int type,rate;
+
+ type=script_getnum(st,2);
+ rate=script_getnum(st,3);
+ if( script_hasdata(st,4) ) //get for the bl assigned
+ bl = map_id2bl(script_getnum(st,4));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (bl)
+ rate = status_get_sc_def(bl, (sc_type)type, 10000, 10000, 0);
+
+ script_pushint(st,rate);
+ return 0;
+}
+
+/*==========================================
+ * getstatus <type>{, <info>};
+ *------------------------------------------*/
+BUILDIN_FUNC(getstatus)
+{
+ int id, type;
+ struct map_session_data* sd = script_rid2sd(st);
+
+ if( sd == NULL )
+ {// no player attached
+ return 0;
+ }
+
+ id = script_getnum(st, 2);
+ type = script_hasdata(st, 3) ? script_getnum(st, 3) : 0;
+
+ if( id <= SC_NONE || id >= SC_MAX )
+ {// invalid status type given
+ ShowWarning("script.c:getstatus: Invalid status type given (%d).\n", id);
+ return 0;
+ }
+
+ if( sd->sc.count == 0 || !sd->sc.data[id] )
+ {// no status is active
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ switch( type )
+ {
+ case 1: script_pushint(st, sd->sc.data[id]->val1); break;
+ case 2: script_pushint(st, sd->sc.data[id]->val2); break;
+ case 3: script_pushint(st, sd->sc.data[id]->val3); break;
+ case 4: script_pushint(st, sd->sc.data[id]->val4); break;
+ case 5:
+ {
+ struct TimerData* timer = (struct TimerData*)get_timer(sd->sc.data[id]->timer);
+
+ if( timer )
+ {// return the amount of time remaining
+ script_pushint(st, timer->tick - gettick());
+ }
+ }
+ break;
+ default: script_pushint(st, 1); break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(debugmes)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,str);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(catchpet)
+{
+ int pet_id;
+ TBL_PC *sd;
+
+ pet_id= script_getnum(st,2);
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pet_catch_process1(sd,pet_id);
+ return 0;
+}
+
+/*==========================================
+ * [orn]
+ *------------------------------------------*/
+BUILDIN_FUNC(homunculus_evolution)
+{
+ TBL_PC *sd;
+
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if(merc_is_hom_active(sd->hd))
+ {
+ if (sd->hd->homunculus.intimacy > 91000)
+ merc_hom_evolution(sd->hd);
+ else
+ clif_emotion(&sd->hd->bl, E_SWT);
+ }
+ return 0;
+}
+
+/*==========================================
+ * [Xantara]
+ *------------------------------------------*/
+BUILDIN_FUNC(homunculus_mutate)
+{
+ int homun_id, m_class, m_id;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if(script_hasdata(st,2))
+ homun_id = script_getnum(st,2);
+ else
+ homun_id = 6048 + (rnd() % 4);
+
+ if(merc_is_hom_active(sd->hd)) {
+ m_class = hom_class2mapid(sd->hd->homunculus.class_);
+ m_id = hom_class2mapid(homun_id);
+
+ if ( m_class != -1 && m_id != -1 && m_class&HOM_EVO && m_id&HOM_S && sd->hd->homunculus.level >= 99 )
+ hom_mutate(sd->hd, homun_id);
+ else
+ clif_emotion(&sd->hd->bl, E_SWT);
+ }
+ return 0;
+}
+
+// [Zephyrus]
+BUILDIN_FUNC(homunculus_shuffle)
+{
+ TBL_PC *sd;
+
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if(merc_is_hom_active(sd->hd))
+ merc_hom_shuffle(sd->hd);
+
+ return 0;
+}
+
+//These two functions bring the eA MAPID_* class functionality to scripts.
+BUILDIN_FUNC(eaclass)
+{
+ int class_;
+ if( script_hasdata(st,2) )
+ class_ = script_getnum(st,2);
+ else {
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,-1);
+ return 0;
+ }
+ class_ = sd->status.class_;
+ }
+ script_pushint(st,pc_jobid2mapid(class_));
+ return 0;
+}
+
+BUILDIN_FUNC(roclass)
+{
+ int class_ =script_getnum(st,2);
+ int sex;
+ if( script_hasdata(st,3) )
+ sex = script_getnum(st,3);
+ else {
+ TBL_PC *sd;
+ if (st->rid && (sd=script_rid2sd(st)))
+ sex = sd->status.sex;
+ else
+ sex = 1; //Just use male when not found.
+ }
+ script_pushint(st,pc_mapid2jobid(class_, sex));
+ return 0;
+}
+
+/*==========================================
+ * Tells client to open a hatching window, used for pet incubator
+ *------------------------------------------*/
+BUILDIN_FUNC(birthpet)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->status.pet_id )
+ {// do not send egg list, when you already have a pet
+ return 0;
+ }
+
+ clif_sendegg(sd);
+ return 0;
+}
+
+/*==========================================
+ * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes)
+ * @type
+ * 1 : make like after rebirth
+ * 2 : blvl,jlvl=1, skillpoint=0
+ * 3 : don't reset skill, blvl=1
+ * 4 : jlvl=0
+ *------------------------------------------*/
+BUILDIN_FUNC(resetlvl)
+{
+ TBL_PC *sd;
+
+ int type=script_getnum(st,2);
+
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_resetlvl(sd,type);
+ return 0;
+}
+/*==========================================
+ * Reset a player status point
+ *------------------------------------------*/
+BUILDIN_FUNC(resetstatus)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ pc_resetstate(sd);
+ return 0;
+}
+
+/*==========================================
+ * script command resetskill
+ *------------------------------------------*/
+BUILDIN_FUNC(resetskill)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ pc_resetskill(sd,1);
+ return 0;
+}
+
+/*==========================================
+ * Counts total amount of skill points.
+ *------------------------------------------*/
+BUILDIN_FUNC(skillpointcount)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ script_pushint(st,sd->status.skill_point + pc_resetskill(sd,2));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(changebase)
+{
+ TBL_PC *sd=NULL;
+ int vclass;
+
+ if( script_hasdata(st,3) )
+ sd=map_id2sd(script_getnum(st,3));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd == NULL)
+ return 0;
+
+ vclass = script_getnum(st,2);
+ if(vclass == JOB_WEDDING)
+ {
+ if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites
+ sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore.
+ )
+ return 0;
+ }
+
+ if(!sd->disguise && vclass != sd->vd.class_) {
+ status_set_viewdata(&sd->bl, vclass);
+ //Updated client view. Base, Weapon and Cloth Colors.
+ clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_);
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ if (sd->vd.cloth_color)
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
+ clif_skillinfoblock(sd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Unequip all item and request for a changesex to char-serv
+ *------------------------------------------*/
+BUILDIN_FUNC(changesex)
+{
+ int i;
+ TBL_PC *sd = NULL;
+ sd = script_rid2sd(st);
+
+ pc_resetskill(sd,4);
+ // to avoid any problem with equipment and invalid sex, equipment is unequiped.
+ for( i=0; i<EQI_MAX; i++ )
+ if( sd->equip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3);
+ chrif_changesex(sd);
+ return 0;
+}
+
+/*==========================================
+ * Works like 'announce' but outputs in the common chat window
+ *------------------------------------------*/
+BUILDIN_FUNC(globalmes)
+{
+ struct block_list *bl = map_id2bl(st->oid);
+ struct npc_data *nd = (struct npc_data *)bl;
+ const char *name=NULL,*mes;
+
+ mes=script_getstr(st,2);
+ if(mes==NULL) return 0;
+
+ if(script_hasdata(st,3)){ // npc name to display
+ name=script_getstr(st,3);
+ } else {
+ name=nd->name; //use current npc name
+ }
+
+ npc_globalmessage(name,mes); // broadcast to all players connected
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// NPC waiting room (chat room)
+//
+
+/// Creates a waiting room (chat room) for this npc.
+///
+/// waitingroom "<title>",<limit>{,"<event>"{,<trigger>{,<zeny>{,<minlvl>{,<maxlvl>}}}}};
+BUILDIN_FUNC(waitingroom)
+{
+ struct npc_data* nd;
+ int pub = 1;
+ const char* title = script_getstr(st, 2);
+ int limit = script_getnum(st, 3);
+ const char* ev = script_hasdata(st,4) ? script_getstr(st,4) : "";
+ int trigger = script_hasdata(st,5) ? script_getnum(st,5) : limit;
+ int zeny = script_hasdata(st,6) ? script_getnum(st,6) : 0;
+ int minLvl = script_hasdata(st,7) ? script_getnum(st,7) : 1;
+ int maxLvl = script_hasdata(st,8) ? script_getnum(st,8) : MAX_LEVEL;
+
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ if( nd != NULL )
+ chat_createnpcchat(nd, title, limit, pub, trigger, ev, zeny, minLvl, maxLvl);
+
+ return 0;
+}
+
+/// Removes the waiting room of the current or target npc.
+///
+/// delwaitingroom "<npc_name>";
+/// delwaitingroom;
+BUILDIN_FUNC(delwaitingroom)
+{
+ struct npc_data* nd;
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st, 2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ if( nd != NULL )
+ chat_deletenpcchat(nd);
+ return 0;
+}
+
+/// Kicks all the players from the waiting room of the current or target npc.
+///
+/// kickwaitingroomall "<npc_name>";
+/// kickwaitingroomall;
+BUILDIN_FUNC(waitingroomkickall)
+{
+ struct npc_data* nd;
+ struct chat_data* cd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st,2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
+ chat_npckickall(cd);
+ return 0;
+}
+
+/// Enables the waiting room event of the current or target npc.
+///
+/// enablewaitingroomevent "<npc_name>";
+/// enablewaitingroomevent;
+BUILDIN_FUNC(enablewaitingroomevent)
+{
+ struct npc_data* nd;
+ struct chat_data* cd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st, 2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
+ chat_enableevent(cd);
+ return 0;
+}
+
+/// Disables the waiting room event of the current or target npc.
+///
+/// disablewaitingroomevent "<npc_name>";
+/// disablewaitingroomevent;
+BUILDIN_FUNC(disablewaitingroomevent)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st, 2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
+ chat_disableevent(cd);
+ return 0;
+}
+
+/// Returns info on the waiting room of the current or target npc.
+/// Returns -1 if the type unknown
+/// <type>=0 : current number of users
+/// <type>=1 : maximum number of users allowed
+/// <type>=2 : the number of users that trigger the event
+/// <type>=3 : if the trigger is disabled
+/// <type>=4 : the title of the waiting room
+/// <type>=5 : the password of the waiting room
+/// <type>=16 : the name of the waiting room event
+/// <type>=32 : if the waiting room is full
+/// <type>=33 : if there are enough users to trigger the event
+///
+/// getwaitingroomstate(<type>,"<npc_name>") -> <info>
+/// getwaitingroomstate(<type>) -> <info>
+BUILDIN_FUNC(getwaitingroomstate)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+ int type;
+
+ type = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ nd = npc_name2id(script_getstr(st, 3));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
+ {
+ script_pushint(st, -1);
+ return 0;
+ }
+
+ switch(type)
+ {
+ case 0: script_pushint(st, cd->users); break;
+ case 1: script_pushint(st, cd->limit); break;
+ case 2: script_pushint(st, cd->trigger&0x7f); break;
+ case 3: script_pushint(st, ((cd->trigger&0x80)!=0)); break;
+ case 4: script_pushstrcopy(st, cd->title); break;
+ case 5: script_pushstrcopy(st, cd->pass); break;
+ case 16: script_pushstrcopy(st, cd->npc_event);break;
+ case 32: script_pushint(st, (cd->users >= cd->limit)); break;
+ case 33: script_pushint(st, (cd->users >= cd->trigger)); break;
+ default: script_pushint(st, -1); break;
+ }
+ return 0;
+}
+
+/// Warps the trigger or target amount of players to the target map and position.
+/// Players are automatically removed from the waiting room.
+/// Those waiting the longest will get warped first.
+/// The target map can be "Random" for a random position in the current map,
+/// and "SavePoint" for the savepoint map+position.
+/// The map flag noteleport of the current map is only considered when teleporting to the savepoint.
+///
+/// The id's of the teleported players are put into the array $@warpwaitingpc[]
+/// The total number of teleported players is put into $@warpwaitingpcnum
+///
+/// warpwaitingpc "<map name>",<x>,<y>,<number of players>;
+/// warpwaitingpc "<map name>",<x>,<y>;
+BUILDIN_FUNC(warpwaitingpc)
+{
+ int x;
+ int y;
+ int i;
+ int n;
+ const char* map_name;
+ struct npc_data* nd;
+ struct chat_data* cd;
+ TBL_PC* sd;
+
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
+ return 0;
+
+ map_name = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ n = cd->trigger&0x7f;
+
+ if( script_hasdata(st,5) )
+ n = script_getnum(st,5);
+
+ for( i = 0; i < n && cd->users > 0; i++ )
+ {
+ sd = cd->usersd[0];
+
+ if( strcmp(map_name,"SavePoint") == 0 && map[sd->bl.m].flag.noteleport )
+ {// can't teleport on this map
+ break;
+ }
+
+ if( cd->zeny )
+ {// fee set
+ if( (uint32)sd->status.zeny < cd->zeny )
+ {// no zeny to cover set fee
+ break;
+ }
+ pc_payzeny(sd, cd->zeny, LOG_TYPE_NPC, NULL);
+ }
+
+ mapreg_setreg(reference_uid(add_str("$@warpwaitingpc"), i), sd->bl.id);
+
+ if( strcmp(map_name,"Random") == 0 )
+ pc_randomwarp(sd,CLR_TELEPORT);
+ else if( strcmp(map_name,"SavePoint") == 0 )
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
+ else
+ pc_setpos(sd, mapindex_name2id(map_name), x, y, CLR_OUTSIGHT);
+ }
+ mapreg_setreg(add_str("$@warpwaitingpcnum"), i);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// ...
+//
+
+/// Detaches a character from a script.
+///
+/// @param st Script state to detach the character from.
+static void script_detach_rid(struct script_state* st)
+{
+ if(st->rid)
+ {
+ script_detach_state(st, false);
+ st->rid = 0;
+ }
+}
+
+/*==========================================
+ * Attach sd char id to script and detach current one if any
+ *------------------------------------------*/
+BUILDIN_FUNC(attachrid)
+{
+ int rid = script_getnum(st,2);
+ struct map_session_data* sd;
+
+ if ((sd = map_id2sd(rid))!=NULL) {
+ script_detach_rid(st);
+
+ st->rid = rid;
+ script_attach_state(st);
+ script_pushint(st,1);
+ } else
+ script_pushint(st,0);
+ return 0;
+}
+/*==========================================
+ * Detach script to rid
+ *------------------------------------------*/
+BUILDIN_FUNC(detachrid)
+{
+ script_detach_rid(st);
+ return 0;
+}
+/*==========================================
+ * Chk if account connected, (and charid from account if specified)
+ *------------------------------------------*/
+BUILDIN_FUNC(isloggedin)
+{
+ TBL_PC* sd = map_id2sd(script_getnum(st,2));
+ if (script_hasdata(st,3) && sd &&
+ sd->status.char_id != script_getnum(st,3))
+ sd = NULL;
+ push_val(st->stack,C_INT,sd!=NULL);
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(setmapflagnosave)
+{
+ int16 m,x,y;
+ unsigned short mapindex;
+ const char *str,*str2;
+
+ str=script_getstr(st,2);
+ str2=script_getstr(st,3);
+ x=script_getnum(st,4);
+ y=script_getnum(st,5);
+ m = map_mapname2mapid(str);
+ mapindex = mapindex_name2id(str2);
+
+ if(m >= 0 && mapindex) {
+ map[m].flag.nosave=1;
+ map[m].save.map=mapindex;
+ map[m].save.x=x;
+ map[m].save.y=y;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(getmapflag)
+{
+ int16 m,i;
+ const char *str;
+
+ str=script_getstr(st,2);
+ i=script_getnum(st,3);
+
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ 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;
+ case MF_PVP: script_pushint(st,map[m].flag.pvp); break;
+ case MF_PVP_NOPARTY: script_pushint(st,map[m].flag.pvp_noparty); break;
+ 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_NOTRADE: script_pushint(st,map[m].flag.notrade); 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_FOG: script_pushint(st,map[m].flag.fog); break;
+ case MF_SAKURA: script_pushint(st,map[m].flag.sakura); break;
+ case MF_LEAVES: script_pushint(st,map[m].flag.leaves); 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;
+ case MF_NOMVPLOOT: script_pushint(st,map[m].flag.nomvploot); break;
+ case MF_NORETURN: script_pushint(st,map[m].flag.noreturn); break;
+ case MF_NOWARPTO: script_pushint(st,map[m].flag.nowarpto); break;
+ 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_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;
+ case MF_ALLOWKS: script_pushint(st,map[m].flag.allowks); break;
+ 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;
+ }
+ }
+
+ return 0;
+}
+/* pvp timer handling */
+static int script_mapflag_pvp_sub(struct block_list *bl,va_list ap) {
+ TBL_PC* sd = (TBL_PC*)bl;
+ if (sd->pvp_timer == INVALID_TIMER) {
+ sd->pvp_timer = add_timer(gettick() + 200, pc_calc_pvprank_timer, sd->bl.id, 0);
+ sd->pvp_rank = 0;
+ sd->pvp_lastusers = 0;
+ sd->pvp_point = 5;
+ sd->pvp_won = 0;
+ sd->pvp_lost = 0;
+ }
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
+ return 0;
+}
+BUILDIN_FUNC(setmapflag)
+{
+ int16 m,i;
+ const char *str;
+ int val=0;
+
+ str=script_getstr(st,2);
+ i=script_getnum(st,3);
+ if(script_hasdata(st,4)){
+ val=script_getnum(st,4);
+ }
+ 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_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;
+ if( !battle_config.pk_mode ) {
+ map_foreachinmap(script_mapflag_pvp_sub,m,BL_PC);
+ }
+ 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;
+ clif_map_property_mapall(m, MAPPROPERTY_AGITZONE);
+ 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].zone |= 1<<(val+1);
+ map[m].flag.restricted=1;
+ break;
+ case MF_NOCOMMAND: map[m].nocommand = (val <= 0) ? 100 : val; break;
+ case MF_NODROP: map[m].flag.nodrop = 1; break;
+ case MF_JEXP: map[m].jexp = (val <= 0) ? 100 : val; break;
+ case MF_BEXP: map[m].bexp = (val <= 0) ? 100 : 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_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: map[m].flag.battleground = (val <= 0 || val > 2) ? 1 : val; break;
+ case MF_RESET: map[m].flag.reset = 1; break;
+ }
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(removemapflag)
+{
+ int16 m,i;
+ const char *str;
+ int val=0;
+
+ str=script_getstr(st,2);
+ i=script_getnum(st,3);
+ if(script_hasdata(st,4)){
+ val=script_getnum(st,4);
+ }
+ 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_NOZENYPENALTY: map[m].flag.nozenypenalty = 0; break;
+ case MF_PVP:
+ map[m].flag.pvp = 0;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+ 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;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+ 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].zone ^= 1<<(val+1);
+ if (map[m].zone == 0){
+ 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;
+ }
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(pvpon)
+{
+ int16 m;
+ const char *str;
+ TBL_PC* sd = NULL;
+ struct s_mapiterator* iter;
+
+ str = script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if( m < 0 || map[m].flag.pvp )
+ return 0; // nothing to do
+
+ map[m].flag.pvp = 1;
+ clif_map_property_mapall(m, MAPPROPERTY_FREEPVPZONE);
+
+ if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris]
+ return 0;
+
+ iter = mapit_getallusers();
+ for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
+ {
+ if( sd->bl.m != m || sd->pvp_timer != INVALID_TIMER )
+ continue; // not applicable
+
+ sd->pvp_timer = add_timer(gettick()+200,pc_calc_pvprank_timer,sd->bl.id,0);
+ sd->pvp_rank = 0;
+ sd->pvp_lastusers = 0;
+ sd->pvp_point = 5;
+ sd->pvp_won = 0;
+ sd->pvp_lost = 0;
+ }
+ mapit_free(iter);
+
+ return 0;
+}
+
+static int buildin_pvpoff_sub(struct block_list *bl,va_list ap)
+{
+ TBL_PC* sd = (TBL_PC*)bl;
+ clif_pvpset(sd, 0, 0, 2);
+ if (sd->pvp_timer != INVALID_TIMER) {
+ delete_timer(sd->pvp_timer, pc_calc_pvprank_timer);
+ sd->pvp_timer = INVALID_TIMER;
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(pvpoff)
+{
+ int16 m;
+ const char *str;
+
+ str=script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if(m < 0 || !map[m].flag.pvp)
+ return 0; //fixed Lupus
+
+ map[m].flag.pvp = 0;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+
+ if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris]
+ return 0;
+
+ map_foreachinmap(buildin_pvpoff_sub, m, BL_PC);
+ return 0;
+}
+
+BUILDIN_FUNC(gvgon)
+{
+ int16 m;
+ const char *str;
+
+ str=script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if(m >= 0 && !map[m].flag.gvg) {
+ map[m].flag.gvg = 1;
+ clif_map_property_mapall(m, MAPPROPERTY_AGITZONE);
+ }
+
+ return 0;
+}
+BUILDIN_FUNC(gvgoff)
+{
+ int16 m;
+ const char *str;
+
+ str=script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if(m >= 0 && map[m].flag.gvg) {
+ map[m].flag.gvg = 0;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Shows an emoticon on top of the player/npc
+ * emotion emotion#, <target: 0 - NPC, 1 - PC>, <NPC/PC name>
+ *------------------------------------------*/
+//Optional second parameter added by [Skotlex]
+BUILDIN_FUNC(emotion)
+{
+ int type;
+ int player=0;
+
+ type=script_getnum(st,2);
+ if(type < 0 || type > 100)
+ return 0;
+
+ if( script_hasdata(st,3) )
+ player=script_getnum(st,3);
+
+ if (player) {
+ TBL_PC *sd = NULL;
+ if( script_hasdata(st,4) )
+ sd = map_nick2sd(script_getstr(st,4));
+ else
+ sd = script_rid2sd(st);
+ if (sd)
+ clif_emotion(&sd->bl,type);
+ } else
+ if( script_hasdata(st,4) )
+ {
+ TBL_NPC *nd = npc_name2id(script_getstr(st,4));
+ if(nd)
+ clif_emotion(&nd->bl,type);
+ }
+ else
+ clif_emotion(map_id2bl(st->oid),type);
+ return 0;
+}
+
+static int buildin_maprespawnguildid_sub_pc(struct map_session_data* sd, va_list ap)
+{
+ int16 m=va_arg(ap,int);
+ int g_id=va_arg(ap,int);
+ int flag=va_arg(ap,int);
+
+ if(!sd || sd->bl.m != m)
+ return 0;
+ if(
+ (sd->status.guild_id == g_id && flag&1) || //Warp out owners
+ (sd->status.guild_id != g_id && flag&2) || //Warp out outsiders
+ (sd->status.guild_id == 0) // Warp out players not in guild [Valaris]
+ )
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ return 1;
+}
+
+static int buildin_maprespawnguildid_sub_mob(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md=(struct mob_data *)bl;
+
+ if(!md->guardian_data && md->class_ != MOBID_EMPERIUM)
+ status_kill(bl);
+
+ return 0;
+}
+
+BUILDIN_FUNC(maprespawnguildid)
+{
+ const char *mapname=script_getstr(st,2);
+ int g_id=script_getnum(st,3);
+ int flag=script_getnum(st,4);
+
+ int16 m=map_mapname2mapid(mapname);
+
+ if(m == -1)
+ return 0;
+
+ //Catch ALL players (in case some are 'between maps' on execution time)
+ map_foreachpc(buildin_maprespawnguildid_sub_pc,m,g_id,flag);
+ if (flag&4) //Remove script mobs.
+ map_foreachinmap(buildin_maprespawnguildid_sub_mob,m,BL_MOB);
+ return 0;
+}
+
+BUILDIN_FUNC(agitstart)
+{
+ if(agit_flag==1) return 0; // Agit already Start.
+ agit_flag=1;
+ guild_agit_start();
+ return 0;
+}
+
+BUILDIN_FUNC(agitend)
+{
+ if(agit_flag==0) return 0; // Agit already End.
+ agit_flag=0;
+ guild_agit_end();
+ return 0;
+}
+
+BUILDIN_FUNC(agitstart2)
+{
+ if(agit2_flag==1) return 0; // Agit2 already Start.
+ agit2_flag=1;
+ guild_agit2_start();
+ return 0;
+}
+
+BUILDIN_FUNC(agitend2)
+{
+ if(agit2_flag==0) return 0; // Agit2 already End.
+ agit2_flag=0;
+ guild_agit2_end();
+ return 0;
+}
+
+/*==========================================
+ * Returns whether woe is on or off. // choice script
+ *------------------------------------------*/
+BUILDIN_FUNC(agitcheck)
+{
+ script_pushint(st,agit_flag);
+ return 0;
+}
+
+/*==========================================
+ * Returns whether woese is on or off. // choice script
+ *------------------------------------------*/
+BUILDIN_FUNC(agitcheck2)
+{
+ script_pushint(st,agit2_flag);
+ return 0;
+}
+
+/// Sets the guild_id of this npc.
+///
+/// flagemblem <guild_id>;
+BUILDIN_FUNC(flagemblem)
+{
+ TBL_NPC* nd;
+ int g_id = script_getnum(st,2);
+
+ if(g_id < 0) return 0;
+
+ nd = (TBL_NPC*)map_id2nd(st->oid);
+ if( nd == NULL ) {
+ ShowError("script:flagemblem: npc %d not found\n", st->oid);
+ } else if( nd->subtype != SCRIPT ) {
+ ShowError("script:flagemblem: unexpected subtype %d for npc %d '%s'\n", nd->subtype, st->oid, nd->exname);
+ } else {
+ bool changed = ( nd->u.scr.guild_id != g_id )?true:false;
+ nd->u.scr.guild_id = g_id;
+ clif_guild_emblem_area(&nd->bl);
+ /* guild flag caching */
+ if( g_id ) /* adding a id */
+ guild_flag_add(nd);
+ else if( changed ) /* removing a flag */
+ guild_flag_remove(nd);
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(getcastlename)
+{
+ const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ struct guild_castle* gc = guild_mapname2gc(mapname);
+ const char* name = (gc) ? gc->castle_name : "";
+ script_pushstrcopy(st,name);
+ return 0;
+}
+
+BUILDIN_FUNC(getcastledata)
+{
+ const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ int index = script_getnum(st,3);
+ struct guild_castle *gc = guild_mapname2gc(mapname);
+
+ if (gc == NULL) {
+ script_pushint(st,0);
+ ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname);
+ return 1;
+ }
+
+ switch (index) {
+ case 1:
+ script_pushint(st,gc->guild_id); break;
+ case 2:
+ script_pushint(st,gc->economy); break;
+ case 3:
+ script_pushint(st,gc->defense); break;
+ case 4:
+ script_pushint(st,gc->triggerE); break;
+ case 5:
+ script_pushint(st,gc->triggerD); break;
+ case 6:
+ script_pushint(st,gc->nextTime); break;
+ case 7:
+ script_pushint(st,gc->payTime); break;
+ case 8:
+ script_pushint(st,gc->createTime); break;
+ case 9:
+ script_pushint(st,gc->visibleC); break;
+ default:
+ if (index > 9 && index <= 9+MAX_GUARDIANS) {
+ script_pushint(st,gc->guardian[index-10].visible);
+ break;
+ }
+ script_pushint(st,0);
+ ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index);
+ return 1;
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(setcastledata)
+{
+ const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ int index = script_getnum(st,3);
+ int value = script_getnum(st,4);
+ struct guild_castle *gc = guild_mapname2gc(mapname);
+
+ if (gc == NULL) {
+ ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname);
+ return 1;
+ }
+
+ if (index <= 0 || index > 9+MAX_GUARDIANS) {
+ ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index);
+ return 1;
+ }
+
+ guild_castledatasave(gc->castle_id, index, value);
+ return 0;
+}
+
+/* =====================================================================
+ * ---------------------------------------------------------------------*/
+BUILDIN_FUNC(requestguildinfo)
+{
+ int guild_id=script_getnum(st,2);
+ const char *event=NULL;
+
+ if( script_hasdata(st,3) ){
+ event=script_getstr(st,3);
+ check_event(st, event);
+ }
+
+ if(guild_id>0)
+ guild_npc_request_info(guild_id,event);
+ return 0;
+}
+
+/// Returns the number of cards that have been compounded onto the specified equipped item.
+/// getequipcardcnt(<equipment slot>);
+BUILDIN_FUNC(getequipcardcnt)
+{
+ int i=-1,j,num;
+ TBL_PC *sd;
+ int count;
+
+ num=script_getnum(st,2);
+ sd=script_rid2sd(st);
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if (i < 0 || !sd->inventory_data[i]) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ if(itemdb_isspecial(sd->status.inventory[i].card[0]))
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ count = 0;
+ for( j = 0; j < sd->inventory_data[i]->slot; j++ )
+ if( sd->status.inventory[i].card[j] && itemdb_type(sd->status.inventory[i].card[j]) == IT_CARD )
+ count++;
+
+ script_pushint(st,count);
+ return 0;
+}
+
+/// Removes all cards from the item found in the specified equipment slot of the invoking character,
+/// and give them to the character. If any cards were removed in this manner, it will also show a success effect.
+/// successremovecards <slot>;
+BUILDIN_FUNC(successremovecards) {
+ int i=-1,j,c,cardflag=0;
+
+ TBL_PC* sd = script_rid2sd(st);
+ int num = script_getnum(st,2);
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if (i < 0 || !sd->inventory_data[i]) {
+ return 0;
+ }
+
+ if(itemdb_isspecial(sd->status.inventory[i].card[0]))
+ return 0;
+
+ for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) {
+ if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {// extract this card from the item
+ int flag;
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ cardflag = 1;
+ item_tmp.nameid = sd->status.inventory[i].card[c];
+ item_tmp.identify = 1;
+
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ // get back the cart in inventory
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+
+ if(cardflag == 1) {//if card was remove remplace item with no card
+ int flag;
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = sd->status.inventory[i].nameid;
+ item_tmp.identify = 1;
+ item_tmp.refine = sd->status.inventory[i].refine;
+ item_tmp.attribute = sd->status.inventory[i].attribute;
+ item_tmp.expire_time = sd->status.inventory[i].expire_time;
+
+ for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=sd->status.inventory[i].card[j];
+
+ pc_delitem(sd,i,1,0,3,LOG_TYPE_SCRIPT);
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ //chk if can be spawn in inventory otherwise put on floor
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+
+ clif_misceffect(&sd->bl,3);
+ }
+ return 0;
+}
+
+/// Removes all cards from the item found in the specified equipment slot of the invoking character.
+/// failedremovecards <slot>, <type>;
+/// <type>=0 : will destroy both the item and the cards.
+/// <type>=1 : will keep the item, but destroy the cards.
+/// <type>=2 : will keep the cards, but destroy the item.
+/// <type>=? : will just display the failure effect.
+BUILDIN_FUNC(failedremovecards) {
+ int i=-1,j,c,cardflag=0;
+
+ TBL_PC* sd = script_rid2sd(st);
+ int num = script_getnum(st,2);
+ int typefail = script_getnum(st,3);
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if (i < 0 || !sd->inventory_data[i])
+ return 0;
+
+ if(itemdb_isspecial(sd->status.inventory[i].card[0]))
+ return 0;
+
+ for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) {
+ if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {
+ cardflag = 1;
+
+ if(typefail == 2) {// add cards to inventory, clear
+ int flag;
+ struct item item_tmp;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = sd->status.inventory[i].card[c];
+ item_tmp.identify = 1;
+
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ }
+
+ if(cardflag == 1) {
+ if(typefail == 0 || typefail == 2){ // destroy the item
+ pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
+ }
+ if(typefail == 1){ // destroy the card
+ int flag;
+ struct item item_tmp;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = sd->status.inventory[i].nameid;
+ item_tmp.identify = 1;
+ item_tmp.refine = sd->status.inventory[i].refine;
+ item_tmp.attribute = sd->status.inventory[i].attribute;
+ item_tmp.expire_time = sd->status.inventory[i].expire_time;
+
+ for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=sd->status.inventory[i].card[j];
+
+ pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
+
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ clif_misceffect(&sd->bl,2);
+ }
+
+ return 0;
+}
+
+/* ================================================================
+ * mapwarp "<from map>","<to map>",<x>,<y>,<type>,<ID for Type>;
+ * type: 0=everyone, 1=guild, 2=party; [Reddozen]
+ * improved by [Lance]
+ * ================================================================*/
+BUILDIN_FUNC(mapwarp) // Added by RoVeRT
+{
+ int x,y,m,check_val=0,check_ID=0,i=0;
+ struct guild *g = NULL;
+ struct party_data *p = NULL;
+ const char *str;
+ const char *mapname;
+ unsigned int index;
+ mapname=script_getstr(st,2);
+ str=script_getstr(st,3);
+ x=script_getnum(st,4);
+ y=script_getnum(st,5);
+ if(script_hasdata(st,7)){
+ check_val=script_getnum(st,6);
+ check_ID=script_getnum(st,7);
+ }
+
+ if((m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ if(!(index=mapindex_name2id(str)))
+ return 0;
+
+ switch(check_val){
+ case 1:
+ g = guild_search(check_ID);
+ if (g){
+ for( i=0; i < g->max_member; i++)
+ {
+ if(g->member[i].sd && g->member[i].sd->bl.m==m){
+ pc_setpos(g->member[i].sd,index,x,y,CLR_TELEPORT);
+ }
+ }
+ }
+ break;
+ case 2:
+ p = party_search(check_ID);
+ if(p){
+ for(i=0;i<MAX_PARTY; i++){
+ if(p->data[i].sd && p->data[i].sd->bl.m == m){
+ pc_setpos(p->data[i].sd,index,x,y,CLR_TELEPORT);
+ }
+ }
+ }
+ break;
+ default:
+ map_foreachinmap(buildin_areawarp_sub,m,BL_PC,index,x,y,0,0);
+ break;
+ }
+
+ return 0;
+}
+
+static int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT
+{
+ char *event=va_arg(ap,char *);
+ struct mob_data *md = ((struct mob_data *)bl);
+ if( md->status.hp > 0 && (!event || strcmp(event,md->npc_event) == 0) )
+ return 1;
+ return 0;
+}
+
+BUILDIN_FUNC(mobcount) // Added by RoVeRT
+{
+ const char *mapname,*event;
+ int16 m;
+ mapname=script_getstr(st,2);
+ event=script_getstr(st,3);
+
+ if( strcmp(event, "all") == 0 )
+ event = NULL;
+ else
+ check_event(st, event);
+
+ if( strcmp(mapname, "this") == 0 ) {
+ struct map_session_data *sd = script_rid2sd(st);
+ if( sd )
+ m = sd->bl.m;
+ else {
+ script_pushint(st,-1);
+ return 0;
+ }
+ }
+ else if( (m = map_mapname2mapid(mapname)) < 0 ) {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ if( map[m].flag.src4instance && map[m].instance_id == 0 && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ script_pushint(st,map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event));
+
+ return 0;
+}
+
+BUILDIN_FUNC(marriage)
+{
+ const char *partner=script_getstr(st,2);
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_PC *p_sd=map_nick2sd(partner);
+
+ if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){
+ script_pushint(st,0);
+ return 0;
+ }
+ script_pushint(st,1);
+ return 0;
+}
+BUILDIN_FUNC(wedding_effect)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ struct block_list *bl;
+
+ if(sd==NULL) {
+ bl=map_id2bl(st->oid);
+ } else
+ bl=&sd->bl;
+ clif_wedding_effect(bl);
+ return 0;
+}
+BUILDIN_FUNC(divorce)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if(sd==NULL || pc_divorce(sd) < 0){
+ script_pushint(st,0);
+ return 0;
+ }
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(ispartneron)
+{
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ map_charid2sd(sd->status.partner_id) == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(getpartnerid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.partner_id);
+ return 0;
+}
+
+BUILDIN_FUNC(getchildid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.child);
+ return 0;
+}
+
+BUILDIN_FUNC(getmotherid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.mother);
+ return 0;
+}
+
+BUILDIN_FUNC(getfatherid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.father);
+ return 0;
+}
+
+BUILDIN_FUNC(warppartner)
+{
+ int x,y;
+ unsigned short mapindex;
+ const char *str;
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_PC *p_sd=NULL;
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ str=script_getstr(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+
+ mapindex = mapindex_name2id(str);
+ if (mapindex) {
+ pc_setpos(p_sd,mapindex,x,y,CLR_OUTSIGHT);
+ script_pushint(st,1);
+ } else
+ script_pushint(st,0);
+ return 0;
+}
+
+/*================================================
+ * Script for Displaying MOB Information [Valaris]
+ *------------------------------------------------*/
+BUILDIN_FUNC(strmobinfo)
+{
+
+ int num=script_getnum(st,2);
+ int class_=script_getnum(st,3);
+
+ if(!mobdb_checkid(class_))
+ {
+ if (num < 3) //requested a string
+ script_pushconststr(st,"");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch (num) {
+ case 1: script_pushstrcopy(st,mob_db(class_)->name); break;
+ case 2: script_pushstrcopy(st,mob_db(class_)->jname); break;
+ case 3: script_pushint(st,mob_db(class_)->lv); break;
+ case 4: script_pushint(st,mob_db(class_)->status.max_hp); break;
+ case 5: script_pushint(st,mob_db(class_)->status.max_sp); break;
+ case 6: script_pushint(st,mob_db(class_)->base_exp); break;
+ case 7: script_pushint(st,mob_db(class_)->job_exp); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Summon guardians [Valaris]
+ * guardian("<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"}{,<guardian index>}) -> <id>
+ *------------------------------------------*/
+BUILDIN_FUNC(guardian)
+{
+ int class_=0,x=0,y=0,guardian=0;
+ const char *str,*map,*evt="";
+ struct script_data *data;
+ bool has_index = false;
+
+ map =script_getstr(st,2);
+ x =script_getnum(st,3);
+ y =script_getnum(st,4);
+ str =script_getstr(st,5);
+ class_=script_getnum(st,6);
+
+ if( script_hasdata(st,8) )
+ {// "<event label>",<guardian index>
+ evt=script_getstr(st,7);
+ guardian=script_getnum(st,8);
+ has_index = true;
+ } else if( script_hasdata(st,7) ){
+ data=script_getdata(st,7);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {// "<event label>"
+ evt=script_getstr(st,7);
+ } else if( data_isint(data) )
+ {// <guardian index>
+ guardian=script_getnum(st,7);
+ has_index = true;
+ } else {
+ ShowError("script:guardian: invalid data type for argument #6 (from 1)\n");
+ script_reportdata(data);
+ return 1;
+ }
+ }
+
+ check_event(st, evt);
+ script_pushint(st, mob_spawn_guardian(map,x,y,str,class_,evt,guardian,has_index));
+
+ return 0;
+}
+/*==========================================
+ * Invisible Walls [Zephyrus]
+ *------------------------------------------*/
+BUILDIN_FUNC(setwall)
+{
+ const char *map, *name;
+ int x, y, m, size, dir;
+ bool shootable;
+
+ map = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ size = script_getnum(st,5);
+ dir = script_getnum(st,6);
+ shootable = script_getnum(st,7);
+ name = script_getstr(st,8);
+
+ if( (m = map_mapname2mapid(map)) < 0 )
+ return 0; // Invalid Map
+
+ map_iwall_set(m, x, y, size, dir, shootable, name);
+ return 0;
+}
+BUILDIN_FUNC(delwall)
+{
+ const char *name = script_getstr(st,2);
+ map_iwall_remove(name);
+
+ return 0;
+}
+
+/// Retrieves various information about the specified guardian.
+///
+/// guardianinfo("<map_name>", <index>, <type>) -> <value>
+/// type: 0 - whether it is deployed or not
+/// 1 - maximum hp
+/// 2 - current hp
+///
+BUILDIN_FUNC(guardianinfo)
+{
+ const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ int id = script_getnum(st,3);
+ int type = script_getnum(st,4);
+
+ struct guild_castle* gc = guild_mapname2gc(mapname);
+ struct mob_data* gd;
+
+ if( gc == NULL || id < 0 || id >= MAX_GUARDIANS )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ if( type == 0 )
+ script_pushint(st, gc->guardian[id].visible);
+ else
+ if( !gc->guardian[id].visible )
+ script_pushint(st,-1);
+ else
+ if( (gd = map_id2md(gc->guardian[id].id)) == NULL )
+ script_pushint(st,-1);
+ else
+ {
+ if ( type == 1 ) script_pushint(st,gd->status.max_hp);
+ else if( type == 2 ) script_pushint(st,gd->status.hp);
+ else
+ script_pushint(st,-1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Get the item name by item_id or null
+ *------------------------------------------*/
+BUILDIN_FUNC(getitemname)
+{
+ int item_id=0;
+ struct item_data *i_data;
+ char *item_name;
+ struct script_data *data;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ item_id=item_data->nameid;
+ }else
+ item_id=conv_num(st,data);
+
+ i_data = itemdb_exists(item_id);
+ if (i_data == NULL)
+ {
+ script_pushconststr(st,"null");
+ return 0;
+ }
+ item_name=(char *)aMalloc(ITEM_NAME_LENGTH*sizeof(char));
+
+ memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH);
+ script_pushstr(st,item_name);
+ return 0;
+}
+/*==========================================
+ * Returns number of slots an item has. [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(getitemslots)
+{
+ int item_id;
+ struct item_data *i_data;
+
+ item_id=script_getnum(st,2);
+
+ i_data = itemdb_exists(item_id);
+
+ if (i_data)
+ script_pushint(st,i_data->slot);
+ else
+ script_pushint(st,-1);
+ return 0;
+}
+
+// TODO: add matk here if needed/once we get rid of RENEWAL
+
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ getiteminfo(itemID,n), where n
+ 0 value_buy;
+ 1 value_sell;
+ 2 type;
+ 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
+ if = 0, then monsters don't drop it at all (rare or a quest item)
+ if = -1, then this item is sold in NPC shops only
+ 4 sex;
+ 5 equip;
+ 6 weight;
+ 7 atk;
+ 8 def;
+ 9 range;
+ 10 slot;
+ 11 look;
+ 12 elv;
+ 13 wlv;
+ 14 view id
+ *------------------------------------------*/
+BUILDIN_FUNC(getiteminfo)
+{
+ int item_id,n;
+ int *item_arr;
+ struct item_data *i_data;
+
+ item_id = script_getnum(st,2);
+ n = script_getnum(st,3);
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && n>=0 && n<=14) {
+ item_arr = (int*)&i_data->value_buy;
+ script_pushint(st,item_arr[n]);
+ } else
+ script_pushint(st,-1);
+ return 0;
+}
+
+/*==========================================
+ * Set some values of an item [Lupus]
+ * Price, Weight, etc...
+ setiteminfo(itemID,n,Value), where n
+ 0 value_buy;
+ 1 value_sell;
+ 2 type;
+ 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
+ if = 0, then monsters don't drop it at all (rare or a quest item)
+ if = -1, then this item is sold in NPC shops only
+ 4 sex;
+ 5 equip;
+ 6 weight;
+ 7 atk;
+ 8 def;
+ 9 range;
+ 10 slot;
+ 11 look;
+ 12 elv;
+ 13 wlv;
+ 14 view id
+ * Returns Value or -1 if the wrong field's been set
+ *------------------------------------------*/
+BUILDIN_FUNC(setiteminfo)
+{
+ int item_id,n,value;
+ int *item_arr;
+ struct item_data *i_data;
+
+ item_id = script_getnum(st,2);
+ n = script_getnum(st,3);
+ value = script_getnum(st,4);
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && n>=0 && n<=14) {
+ item_arr = (int*)&i_data->value_buy;
+ item_arr[n] = value;
+ script_pushint(st,value);
+ } else
+ script_pushint(st,-1);
+ return 0;
+}
+
+/*==========================================
+ * Returns value from equipped item slot n [Lupus]
+ getequipcardid(num,slot)
+ where
+ num = eqip position slot
+ slot = 0,1,2,3 (Card Slot N)
+
+ This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced)
+ it's useful when you want to check item cards or if it's signed
+ Useful for such quests as "Sign this refined item with players name" etc
+ Hat[0] +4 -> Player's Hat[0] +4
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipcardid)
+{
+ int i=-1,num,slot;
+ TBL_PC *sd;
+
+ num=script_getnum(st,2);
+ slot=script_getnum(st,3);
+ sd=script_rid2sd(st);
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && slot>=0 && slot<4)
+ script_pushint(st,sd->status.inventory[i].card[slot]);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * petskillbonus [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petskillbonus)
+{
+ struct pet_data *pd;
+
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->bonus)
+ { //Clear previous bonus
+ if (pd->bonus->timer != INVALID_TIMER)
+ delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
+ } else //init
+ pd->bonus = (struct pet_bonus *) aMalloc(sizeof(struct pet_bonus));
+
+ pd->bonus->type=script_getnum(st,2);
+ pd->bonus->val=script_getnum(st,3);
+ pd->bonus->duration=script_getnum(st,4);
+ pd->bonus->delay=script_getnum(st,5);
+
+ if (pd->state.skillbonus == 1)
+ pd->state.skillbonus=0; // waiting state
+
+ // wait for timer to start
+ if (battle_config.pet_equip_required && pd->pet.equip == 0)
+ pd->bonus->timer = INVALID_TIMER;
+ else
+ pd->bonus->timer = add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet looting [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petloot)
+{
+ int max;
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ max=script_getnum(st,2);
+
+ if(max < 1)
+ max = 1; //Let'em loot at least 1 item.
+ else if (max > MAX_PETLOOT_SIZE)
+ max = MAX_PETLOOT_SIZE;
+
+ pd = sd->pd;
+ if (pd->loot != NULL)
+ { //Release whatever was there already and reallocate memory
+ pet_lootitem_drop(pd, pd->msd);
+ aFree(pd->loot->item);
+ }
+ else
+ pd->loot = (struct pet_loot *)aMalloc(sizeof(struct pet_loot));
+
+ pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item));
+
+ pd->loot->max=max;
+ pd->loot->count = 0;
+ pd->loot->weight = 0;
+
+ return 0;
+}
+/*==========================================
+ * Set arrays with info of all sd inventory :
+ * @inventorylist_id, @inventorylist_amount, @inventorylist_equip,
+ * @inventorylist_refine, @inventorylist_identify, @inventorylist_attribute,
+ * @inventorylist_card(0..3), @inventorylist_expire
+ * @inventorylist_count = scalar
+ *------------------------------------------*/
+BUILDIN_FUNC(getinventorylist)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ char card_var[NAME_LENGTH];
+
+ int i,j=0,k;
+ if(!sd) return 0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_id"), j),sd->status.inventory[i].nameid);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_amount"), j),sd->status.inventory[i].amount);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_equip"), j),sd->status.inventory[i].equip);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_refine"), j),sd->status.inventory[i].refine);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_identify"), j),sd->status.inventory[i].identify);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_attribute"), j),sd->status.inventory[i].attribute);
+ for (k = 0; k < MAX_SLOTS; k++)
+ {
+ sprintf(card_var, "@inventorylist_card%d",k+1);
+ pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]);
+ }
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time);
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str("@inventorylist_count"),j);
+ return 0;
+}
+
+BUILDIN_FUNC(getskilllist)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ int i,j=0;
+ if(!sd) return 0;
+ for(i=0;i<MAX_SKILL;i++){
+ if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0){
+ pc_setreg(sd,reference_uid(add_str("@skilllist_id"), j),sd->status.skill[i].id);
+ pc_setreg(sd,reference_uid(add_str("@skilllist_lv"), j),sd->status.skill[i].lv);
+ pc_setreg(sd,reference_uid(add_str("@skilllist_flag"), j),sd->status.skill[i].flag);
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str("@skilllist_count"),j);
+ return 0;
+}
+
+BUILDIN_FUNC(clearitem)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ int i;
+ if(sd==NULL) return 0;
+ for (i=0; i<MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+ pc_delitem(sd, i, sd->status.inventory[i].amount, 0, 0, LOG_TYPE_SCRIPT);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Disguise Player (returns Mob/NPC ID if success, 0 on fail)
+ *------------------------------------------*/
+BUILDIN_FUNC(disguise)
+{
+ int id;
+ TBL_PC* sd = script_rid2sd(st);
+ if (sd == NULL) return 0;
+
+ id = script_getnum(st,2);
+
+ if (mobdb_checkid(id) || npcdb_checkid(id)) {
+ pc_disguise(sd, id);
+ script_pushint(st,id);
+ } else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Undisguise Player (returns 1 if success, 0 on fail)
+ *------------------------------------------*/
+BUILDIN_FUNC(undisguise)
+{
+ TBL_PC* sd = script_rid2sd(st);
+ if (sd == NULL) return 0;
+
+ if (sd->disguise) {
+ pc_disguise(sd, 0);
+ script_pushint(st,0);
+ } else {
+ script_pushint(st,1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Transform a bl to another _class,
+ * @type unused
+ *------------------------------------------*/
+BUILDIN_FUNC(classchange)
+{
+ int _class,type;
+ struct block_list *bl=map_id2bl(st->oid);
+
+ if(bl==NULL) return 0;
+
+ _class=script_getnum(st,2);
+ type=script_getnum(st,3);
+ clif_class_change(bl,_class,type);
+ return 0;
+}
+
+/*==========================================
+ * Display an effect
+ *------------------------------------------*/
+BUILDIN_FUNC(misceffect)
+{
+ int type;
+
+ type=script_getnum(st,2);
+ if(st->oid && st->oid != fake_nd->bl.id) {
+ struct block_list *bl = map_id2bl(st->oid);
+ if (bl)
+ clif_specialeffect(bl,type,AREA);
+ } else{
+ TBL_PC *sd=script_rid2sd(st);
+ if(sd)
+ clif_specialeffect(&sd->bl,type,AREA);
+ }
+ return 0;
+}
+/*==========================================
+ * Play a BGM on a single client [Rikter/Yommy]
+ *------------------------------------------*/
+BUILDIN_FUNC(playBGM)
+{
+ const char* name;
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) != NULL )
+ {
+ name = script_getstr(st,2);
+
+ clif_playBGM(sd, name);
+ }
+
+ return 0;
+}
+
+static int playBGM_sub(struct block_list* bl,va_list ap)
+{
+ const char* name = va_arg(ap,const char*);
+
+ clif_playBGM(BL_CAST(BL_PC, bl), name);
+
+ return 0;
+}
+
+static int playBGM_foreachpc_sub(struct map_session_data* sd, va_list args)
+{
+ const char* name = va_arg(args, const char*);
+
+ clif_playBGM(sd, name);
+ return 0;
+}
+
+/*==========================================
+ * Play a BGM on multiple client [Rikter/Yommy]
+ *------------------------------------------*/
+BUILDIN_FUNC(playBGMall)
+{
+ const char* name;
+
+ name = script_getstr(st,2);
+
+ if( script_hasdata(st,7) )
+ {// specified part of map
+ const char* map = script_getstr(st,3);
+ int x0 = script_getnum(st,4);
+ int y0 = script_getnum(st,5);
+ int x1 = script_getnum(st,6);
+ int y1 = script_getnum(st,7);
+
+ map_foreachinarea(playBGM_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name);
+ }
+ else if( script_hasdata(st,3) )
+ {// entire map
+ const char* map = script_getstr(st,3);
+
+ map_foreachinmap(playBGM_sub, map_mapname2mapid(map), BL_PC, name);
+ }
+ else
+ {// entire server
+ map_foreachpc(&playBGM_foreachpc_sub, name);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Play a .wav sound for sd
+ *------------------------------------------*/
+BUILDIN_FUNC(soundeffect)
+{
+ TBL_PC* sd = script_rid2sd(st);
+ const char* name = script_getstr(st,2);
+ int type = script_getnum(st,3);
+
+ if(sd)
+ {
+ clif_soundeffect(sd,&sd->bl,name,type);
+ }
+ return 0;
+}
+
+int soundeffect_sub(struct block_list* bl,va_list ap)
+{
+ char* name = va_arg(ap,char*);
+ int type = va_arg(ap,int);
+
+ clif_soundeffect((TBL_PC *)bl, bl, name, type);
+
+ return 0;
+}
+
+/*==========================================
+ * Play a sound effect (.wav) on multiple clients
+ * soundeffectall "<filepath>",<type>{,"<map name>"}{,<x0>,<y0>,<x1>,<y1>};
+ *------------------------------------------*/
+BUILDIN_FUNC(soundeffectall)
+{
+ struct block_list* bl;
+ const char* name;
+ int type;
+
+ bl = (st->rid) ? &(script_rid2sd(st)->bl) : map_id2bl(st->oid);
+ if (!bl)
+ return 0;
+
+ name = script_getstr(st,2);
+ type = script_getnum(st,3);
+
+ //FIXME: enumerating map squares (map_foreach) is slower than enumerating the list of online players (map_foreachpc?) [ultramage]
+
+ if(!script_hasdata(st,4))
+ { // area around
+ clif_soundeffectall(bl, name, type, AREA);
+ }
+ else
+ if(!script_hasdata(st,5))
+ { // entire map
+ const char* map = script_getstr(st,4);
+ map_foreachinmap(soundeffect_sub, map_mapname2mapid(map), BL_PC, name, type);
+ }
+ else
+ if(script_hasdata(st,8))
+ { // specified part of map
+ const char* map = script_getstr(st,4);
+ int x0 = script_getnum(st,5);
+ int y0 = script_getnum(st,6);
+ int x1 = script_getnum(st,7);
+ int y1 = script_getnum(st,8);
+ map_foreachinarea(soundeffect_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name, type);
+ }
+ else
+ {
+ ShowError("buildin_soundeffectall: insufficient arguments for specific area broadcast.\n");
+ }
+
+ return 0;
+}
+/*==========================================
+ * pet status recovery [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petrecovery)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+
+ if (pd->recovery)
+ { //Halt previous bonus
+ if (pd->recovery->timer != INVALID_TIMER)
+ delete_timer(pd->recovery->timer, pet_recovery_timer);
+ } else //Init
+ pd->recovery = (struct pet_recovery *)aMalloc(sizeof(struct pet_recovery));
+
+ pd->recovery->type = (sc_type)script_getnum(st,2);
+ pd->recovery->delay = script_getnum(st,3);
+ pd->recovery->timer = INVALID_TIMER;
+
+ return 0;
+}
+
+/*==========================================
+ * pet healing [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petheal)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != INVALID_TIMER)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport
+ //Use the lv as the amount to heal
+ pd->s_skill->lv=script_getnum(st,2);
+ pd->s_skill->delay=script_getnum(st,3);
+ pd->s_skill->hp=script_getnum(st,4);
+ pd->s_skill->sp=script_getnum(st,5);
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->pet.equip == 0)
+ pd->s_skill->timer = INVALID_TIMER;
+ else
+ pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+/// petskillattack <skill id>,<level>,<rate>,<bonusrate>
+/// petskillattack "<skill name>",<level>,<rate>,<bonusrate>
+BUILDIN_FUNC(petskillattack)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ pd->a_skill->lv=script_getnum(st,3);
+ pd->a_skill->div_ = 0;
+ pd->a_skill->rate=script_getnum(st,4);
+ pd->a_skill->bonusrate=script_getnum(st,5);
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris]
+ *------------------------------------------*/
+/// petskillattack2 <skill id>,<level>,<div>,<rate>,<bonusrate>
+/// petskillattack2 "<skill name>",<level>,<div>,<rate>,<bonusrate>
+BUILDIN_FUNC(petskillattack2)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ pd->a_skill->lv=script_getnum(st,3);
+ pd->a_skill->div_ = script_getnum(st,4);
+ pd->a_skill->rate=script_getnum(st,5);
+ pd->a_skill->bonusrate=script_getnum(st,6);
+
+ return 0;
+}
+
+/*==========================================
+ * pet support skills [Skotlex]
+ *------------------------------------------*/
+/// petskillsupport <skill id>,<level>,<delay>,<hp>,<sp>
+/// petskillsupport "<skill name>",<level>,<delay>,<hp>,<sp>
+BUILDIN_FUNC(petskillsupport)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != INVALID_TIMER)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ pd->s_skill->lv=script_getnum(st,3);
+ pd->s_skill->delay=script_getnum(st,4);
+ pd->s_skill->hp=script_getnum(st,5);
+ pd->s_skill->sp=script_getnum(st,6);
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->pet.equip == 0)
+ pd->s_skill->timer = INVALID_TIMER;
+ else
+ pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Scripted skill effects [Celest]
+ *------------------------------------------*/
+/// skilleffect <skill id>,<level>
+/// skilleffect "<skill name>",<level>
+BUILDIN_FUNC(skilleffect)
+{
+ TBL_PC *sd;
+
+ uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ uint16 skill_lv=script_getnum(st,3);
+ sd=script_rid2sd(st);
+
+ clif_skill_nodamage(&sd->bl,&sd->bl,skill_id,skill_lv,1);
+
+ return 0;
+}
+
+/*==========================================
+ * NPC skill effects [Valaris]
+ *------------------------------------------*/
+/// npcskilleffect <skill id>,<level>,<x>,<y>
+/// npcskilleffect "<skill name>",<level>,<x>,<y>
+BUILDIN_FUNC(npcskilleffect)
+{
+ struct block_list *bl= map_id2bl(st->oid);
+
+ uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ uint16 skill_lv=script_getnum(st,3);
+ int x=script_getnum(st,4);
+ int y=script_getnum(st,5);
+
+ if (bl)
+ clif_skill_poseffect(bl,skill_id,skill_lv,x,y,gettick());
+
+ return 0;
+}
+
+/*==========================================
+ * Special effects [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(specialeffect)
+{
+ struct block_list *bl=map_id2bl(st->oid);
+ int type = script_getnum(st,2);
+ enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA;
+
+ if(bl==NULL)
+ return 0;
+
+ if( script_hasdata(st,4) )
+ {
+ TBL_NPC *nd = npc_name2id(script_getstr(st,4));
+ if(nd)
+ clif_specialeffect(&nd->bl, type, target);
+ }
+ else
+ {
+ if (target == SELF) {
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd)
+ clif_specialeffect_single(bl,type,sd->fd);
+ } else {
+ clif_specialeffect(bl, type, target);
+ }
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(specialeffect2)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ int type = script_getnum(st,2);
+ enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA;
+
+ if( script_hasdata(st,4) )
+ sd = map_nick2sd(script_getstr(st,4));
+
+ if (sd)
+ clif_specialeffect(&sd->bl, type, target);
+
+ return 0;
+}
+
+/*==========================================
+ * Nude [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(nude)
+{
+ TBL_PC *sd = script_rid2sd(st);
+ int i, calcflag = 0;
+
+ if( sd == NULL )
+ return 0;
+
+ for( i = 0 ; i < EQI_MAX; i++ ) {
+ if( sd->equip_index[ i ] >= 0 ) {
+ if( !calcflag )
+ calcflag = 1;
+ pc_unequipitem( sd , sd->equip_index[ i ] , 2);
+ }
+ }
+
+ if( calcflag )
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * gmcommand [MouseJstr]
+ *------------------------------------------*/
+BUILDIN_FUNC(atcommand)
+{
+ TBL_PC dummy_sd;
+ TBL_PC* sd;
+ int fd;
+ const char* cmd;
+
+ cmd = script_getstr(st,2);
+
+ if (st->rid) {
+ sd = script_rid2sd(st);
+ fd = sd->fd;
+ } else { //Use a dummy character.
+ sd = &dummy_sd;
+ fd = 0;
+
+ memset(&dummy_sd, 0, sizeof(TBL_PC));
+ if (st->oid)
+ {
+ struct block_list* bl = map_id2bl(st->oid);
+ memcpy(&dummy_sd.bl, bl, sizeof(struct block_list));
+ if (bl->type == BL_NPC)
+ safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH);
+ }
+ }
+
+ if (!is_atcommand(fd, sd, cmd, 0)) {
+ ShowWarning("script: buildin_atcommand: failed to execute command '%s'\n", cmd);
+ script_reportsrc(st);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Displays a message for the player only (like system messages like "you got an apple" )
+ *------------------------------------------*/
+BUILDIN_FUNC(dispbottom)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ const char *message;
+ message=script_getstr(st,2);
+ if(sd)
+ clif_disp_onlyself(sd,message,(int)strlen(message));
+ return 0;
+}
+
+/*==========================================
+ * All The Players Full Recovery
+ * (HP/SP full restore and resurrect if need)
+ *------------------------------------------*/
+BUILDIN_FUNC(recovery)
+{
+ TBL_PC* 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(pc_isdead(sd))
+ status_revive(&sd->bl, 100, 100);
+ else
+ status_percent_heal(&sd->bl, 100, 100);
+ clif_displaymessage(sd->fd,msg_txt(680));
+ }
+ mapit_free(iter);
+ return 0;
+}
+/*==========================================
+ * Get your pet info: getpetinfo(n)
+ * n -> 0:pet_id 1:pet_class 2:pet_name
+ * 3:friendly 4:hungry, 5: rename flag.
+ *------------------------------------------*/
+BUILDIN_FUNC(getpetinfo)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_PET *pd;
+ int type=script_getnum(st,2);
+
+ if(!sd || !sd->pd) {
+ if (type == 2)
+ script_pushconststr(st,"null");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+ pd = sd->pd;
+ switch(type){
+ case 0: script_pushint(st,pd->pet.pet_id); break;
+ case 1: script_pushint(st,pd->pet.class_); break;
+ case 2: script_pushstrcopy(st,pd->pet.name); break;
+ case 3: script_pushint(st,pd->pet.intimate); break;
+ case 4: script_pushint(st,pd->pet.hungry); break;
+ case 5: script_pushint(st,pd->pet.rename_flag); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get your homunculus info: gethominfo(n)
+ * n -> 0:hom_id 1:class 2:name
+ * 3:friendly 4:hungry, 5: rename flag.
+ * 6: level
+ *------------------------------------------*/
+BUILDIN_FUNC(gethominfo)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_HOM *hd;
+ int type=script_getnum(st,2);
+
+ hd = sd?sd->hd:NULL;
+ if(!merc_is_hom_active(hd))
+ {
+ if (type == 2)
+ script_pushconststr(st,"null");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch(type){
+ case 0: script_pushint(st,hd->homunculus.hom_id); break;
+ case 1: script_pushint(st,hd->homunculus.class_); break;
+ case 2: script_pushstrcopy(st,hd->homunculus.name); break;
+ case 3: script_pushint(st,hd->homunculus.intimacy); break;
+ case 4: script_pushint(st,hd->homunculus.hunger); break;
+ case 5: script_pushint(st,hd->homunculus.rename_flag); break;
+ case 6: script_pushint(st,hd->homunculus.level); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/// Retrieves information about character's mercenary
+/// getmercinfo <type>[,<char id>];
+BUILDIN_FUNC(getmercinfo)
+{
+ int type, char_id;
+ struct map_session_data* sd;
+ struct mercenary_data* md;
+
+ type = script_getnum(st,2);
+
+ if( script_hasdata(st,3) )
+ {
+ char_id = script_getnum(st,3);
+
+ if( ( sd = map_charid2sd(char_id) ) == NULL )
+ {
+ ShowError("buildin_getmercinfo: No such character (char_id=%d).\n", char_id);
+ script_pushnil(st);
+ return 1;
+ }
+ }
+ else
+ {
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ script_pushnil(st);
+ return 0;
+ }
+ }
+
+ md = ( sd->status.mer_id && sd->md ) ? sd->md : NULL;
+
+ switch( type )
+ {
+ case 0: script_pushint(st,md ? md->mercenary.mercenary_id : 0); break;
+ case 1: script_pushint(st,md ? md->mercenary.class_ : 0); break;
+ case 2:
+ if( md )
+ script_pushstrcopy(st,md->db->name);
+ else
+ script_pushconststr(st,"");
+ break;
+ case 3: script_pushint(st,md ? mercenary_get_faith(md) : 0); break;
+ case 4: script_pushint(st,md ? mercenary_get_calls(md) : 0); break;
+ case 5: script_pushint(st,md ? md->mercenary.kill_count : 0); break;
+ case 6: script_pushint(st,md ? mercenary_get_lifetime(md) : 0); break;
+ case 7: script_pushint(st,md ? md->db->lv : 0); break;
+ default:
+ ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id);
+ script_pushnil(st);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Shows wether your inventory(and equips) contain
+ selected card or not.
+ checkequipedcard(4001);
+ *------------------------------------------*/
+BUILDIN_FUNC(checkequipedcard)
+{
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd){
+ int n,i,c=0;
+ c=script_getnum(st,2);
+
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount && sd->inventory_data[i]){
+ if (itemdb_isspecial(sd->status.inventory[i].card[0]))
+ continue;
+ for(n=0;n<sd->inventory_data[i]->slot;n++){
+ if(sd->status.inventory[i].card[n]==c){
+ script_pushint(st,1);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ script_pushint(st,0);
+ return 0;
+}
+
+BUILDIN_FUNC(jump_zero)
+{
+ int sel;
+ sel=script_getnum(st,2);
+ if(!sel) {
+ int pos;
+ if( !data_islabel(script_getdata(st,3)) ){
+ ShowError("script: jump_zero: not label !\n");
+ st->state=END;
+ return 1;
+ }
+
+ pos=script_getnum(st,3);
+ st->pos=pos;
+ st->state=GOTO;
+ }
+ return 0;
+}
+
+/*==========================================
+ * movenpc [MouseJstr]
+ *------------------------------------------*/
+BUILDIN_FUNC(movenpc)
+{
+ TBL_NPC *nd = NULL;
+ const char *npc;
+ int x,y;
+
+ npc = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+
+ if ((nd = npc_name2id(npc)) == NULL)
+ return -1;
+
+ if (script_hasdata(st,5))
+ nd->ud.dir = script_getnum(st,5) % 8;
+ npc_movenpc(nd, x, y);
+ return 0;
+}
+
+/*==========================================
+ * message [MouseJstr]
+ *------------------------------------------*/
+BUILDIN_FUNC(message)
+{
+ const char *msg,*player;
+ TBL_PC *pl_sd = NULL;
+
+ player = script_getstr(st,2);
+ msg = script_getstr(st,3);
+
+ if((pl_sd=map_nick2sd((char *) player)) == NULL)
+ return 0;
+ clif_displaymessage(pl_sd->fd, msg);
+
+ return 0;
+}
+
+/*==========================================
+ * npctalk (sends message to surrounding area)
+ *------------------------------------------*/
+BUILDIN_FUNC(npctalk)
+{
+ const char* str;
+ char name[NAME_LENGTH], message[256];
+
+ struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid);
+ str = script_getstr(st,2);
+
+ if(nd)
+ {
+ safestrncpy(name, nd->name, sizeof(name));
+ strtok(name, "#"); // discard extra name identifier if present
+ safesnprintf(message, sizeof(message), "%s : %s", name, str);
+ clif_message(&nd->bl, message);
+ }
+
+ return 0;
+}
+
+// change npc walkspeed [Valaris]
+BUILDIN_FUNC(npcspeed)
+{
+ struct npc_data* nd;
+ int speed;
+
+ speed = script_getnum(st,2);
+ nd =(struct npc_data *)map_id2bl(st->oid);
+
+ if( nd )
+ {
+ nd->speed = speed;
+ nd->ud.state.speed_changed = 1;
+ }
+
+ return 0;
+}
+// make an npc walk to a position [Valaris]
+BUILDIN_FUNC(npcwalkto)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0,y=0;
+
+ x=script_getnum(st,2);
+ y=script_getnum(st,3);
+
+ if(nd) {
+ if (!nd->status.hp) {
+ status_calc_npc(nd, true);
+ } else {
+ status_calc_npc(nd, false);
+ }
+ unit_walktoxy(&nd->bl,x,y,0);
+ }
+
+ return 0;
+}
+// stop an npc's movement [Valaris]
+BUILDIN_FUNC(npcstop)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd) {
+ unit_stop_walking(&nd->bl,1|4);
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * getlook char info. getlook(arg)
+ *------------------------------------------*/
+BUILDIN_FUNC(getlook)
+{
+ int type,val;
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+
+ type=script_getnum(st,2);
+ val=-1;
+ switch(type) {
+ case LOOK_HAIR: val=sd->status.hair; break; //1
+ case LOOK_WEAPON: val=sd->status.weapon; break; //2
+ case LOOK_HEAD_BOTTOM: val=sd->status.head_bottom; break; //3
+ case LOOK_HEAD_TOP: val=sd->status.head_top; break; //4
+ case LOOK_HEAD_MID: val=sd->status.head_mid; break; //5
+ case LOOK_HAIR_COLOR: val=sd->status.hair_color; break; //6
+ case LOOK_CLOTHES_COLOR: val=sd->status.clothes_color; break; //7
+ case LOOK_SHIELD: val=sd->status.shield; break; //8
+ case LOOK_SHOES: break; //9
+ }
+
+ script_pushint(st,val);
+ return 0;
+}
+
+/*==========================================
+ * get char save point. argument: 0- map name, 1- x, 2- y
+ *------------------------------------------*/
+BUILDIN_FUNC(getsavepoint)
+{
+ TBL_PC* sd;
+ int type;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ type = script_getnum(st,2);
+
+ switch(type) {
+ case 0: script_pushstrcopy(st,mapindex_id2name(sd->status.save_point.map)); break;
+ case 1: script_pushint(st,sd->status.save_point.x); break;
+ case 2: script_pushint(st,sd->status.save_point.y); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get position for char/npc/pet/mob objects. Added by Lorky
+ *
+ * int getMapXY(MapName$,MapX,MapY,type,[CharName$]);
+ * where type:
+ * MapName$ - String variable for output map name
+ * MapX - Integer variable for output coord X
+ * MapY - Integer variable for output coord Y
+ * type - type of object
+ * 0 - Character coord
+ * 1 - NPC coord
+ * 2 - Pet coord
+ * 3 - Mob coord (not released)
+ * 4 - Homun coord
+ * 5 - Mercenary coord
+ * 6 - Elemental coord
+ * CharName$ - Name object. If miss or "this" the current object
+ *
+ * Return:
+ * 0 - success
+ * -1 - some error, MapName$,MapX,MapY contains unknown value.
+ *------------------------------------------*/
+BUILDIN_FUNC(getmapxy)
+{
+ struct block_list *bl = NULL;
+ TBL_PC *sd=NULL;
+
+ int num;
+ const char *name;
+ char prefix;
+
+ int x,y,type;
+ char mapname[MAP_NAME_LENGTH];
+
+ if( !data_isreference(script_getdata(st,2)) ){
+ ShowWarning("script: buildin_getmapxy: not mapname variable\n");
+ script_pushint(st,-1);
+ return 1;
+ }
+ if( !data_isreference(script_getdata(st,3)) ){
+ ShowWarning("script: buildin_getmapxy: not mapx variable\n");
+ script_pushint(st,-1);
+ return 1;
+ }
+ if( !data_isreference(script_getdata(st,4)) ){
+ ShowWarning("script: buildin_getmapxy: not mapy variable\n");
+ script_pushint(st,-1);
+ return 1;
+ }
+
+ // Possible needly check function parameters on C_STR,C_INT,C_INT
+ type=script_getnum(st,5);
+
+ switch (type){
+ case 0: //Get Character Position
+ if( script_hasdata(st,6) )
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd)
+ bl = &sd->bl;
+ break;
+ case 1: //Get NPC Position
+ if( script_hasdata(st,6) )
+ {
+ struct npc_data *nd;
+ nd=npc_name2id(script_getstr(st,6));
+ if (nd)
+ bl = &nd->bl;
+ } else //In case the origin is not an npc?
+ bl=map_id2bl(st->oid);
+ break;
+ case 2: //Get Pet Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->pd)
+ bl = &sd->pd->bl;
+ break;
+ case 3: //Get Mob Position
+ break; //Not supported?
+ case 4: //Get Homun Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->hd)
+ bl = &sd->hd->bl;
+ break;
+ case 5: //Get Mercenary Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->md)
+ bl = &sd->md->bl;
+ break;
+ case 6: //Get Elemental Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->ed)
+ bl = &sd->ed->bl;
+ break;
+ default:
+ ShowWarning("script: buildin_getmapxy: Invalid type %d\n", type);
+ script_pushint(st,-1);
+ return 1;
+ }
+ if (!bl) { //No object found.
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ x= bl->x;
+ y= bl->y;
+ safestrncpy(mapname, map[bl->m].name, MAP_NAME_LENGTH);
+
+ //Set MapName$
+ num=st->stack->stack_data[st->start+2].u.num;
+ name=get_str(num&0x00ffffff);
+ prefix=*name;
+
+ if(not_server_variable(prefix))
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(st,sd,num,name,(void*)mapname,script_getref(st,2));
+
+ //Set MapX
+ num=st->stack->stack_data[st->start+3].u.num;
+ name=get_str(num&0x00ffffff);
+ prefix=*name;
+
+ if(not_server_variable(prefix))
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(st,sd,num,name,(void*)__64BPRTSIZE(x),script_getref(st,3));
+
+ //Set MapY
+ num=st->stack->stack_data[st->start+4].u.num;
+ name=get_str(num&0x00ffffff);
+ prefix=*name;
+
+ if(not_server_variable(prefix))
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(st,sd,num,name,(void*)__64BPRTSIZE(y),script_getref(st,4));
+
+ //Return Success value
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus]
+ *------------------------------------------*/
+BUILDIN_FUNC(logmes)
+{
+ const char *str;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 1;
+
+ str = script_getstr(st,2);
+ log_npc(sd,str);
+ return 0;
+}
+
+BUILDIN_FUNC(summon)
+{
+ int _class, timeout=0;
+ const char *str,*event="";
+ TBL_PC *sd;
+ struct mob_data *md;
+ int tick = gettick();
+
+ sd=script_rid2sd(st);
+ if (!sd) return 0;
+
+ str =script_getstr(st,2);
+ _class=script_getnum(st,3);
+ if( script_hasdata(st,4) )
+ timeout=script_getnum(st,4);
+ if( script_hasdata(st,5) ){
+ event=script_getstr(st,5);
+ check_event(st, event);
+ }
+
+ clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick);
+
+ md = mob_once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, str, _class, event, SZ_SMALL, AI_NONE);
+ if (md) {
+ md->master_id=sd->bl.id;
+ md->special_state.ai = AI_ATTACK;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer(tick+(timeout>0?timeout*1000:60000),mob_timer_delete,md->bl.id,0);
+ mob_spawn (md); //Now it is ready for spawning.
+ clif_specialeffect(&md->bl,344,AREA);
+ sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks whether it is daytime/nighttime
+ *------------------------------------------*/
+BUILDIN_FUNC(isnight)
+{
+ script_pushint(st,(night_flag == 1));
+ return 0;
+}
+
+BUILDIN_FUNC(isday)
+{
+ script_pushint(st,(night_flag == 0));
+ return 0;
+}
+
+/*================================================
+ * Check how many items/cards in the list are
+ * equipped - used for 2/15's cards patch [celest]
+ *------------------------------------------------*/
+BUILDIN_FUNC(isequippedcnt)
+{
+ TBL_PC *sd;
+ int i, j, k, id = 1;
+ int ret = 0;
+
+ sd = script_rid2sd(st);
+ if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
+ script_pushint(st,0);
+ return 0;
+ }
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ for (j=0; j<EQI_MAX; j++) {
+ int index;
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue;
+ if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue;
+ if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ if (itemdb_type(id) != IT_CARD) { //No card. Count amount in inventory.
+ if (sd->inventory_data[index]->nameid == id)
+ ret+= sd->status.inventory[index].amount;
+ } else { //Count cards.
+ if (itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue; //No cards
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[k] == id)
+ ret++; //[Lupus]
+ }
+ }
+ }
+ }
+
+ script_pushint(st,ret);
+ return 0;
+}
+
+/*================================================
+ * Check whether another card has been
+ * equipped - used for 2/15's cards patch [celest]
+ * -- Items checked cannot be reused in another
+ * card set to prevent exploits
+ *------------------------------------------------*/
+BUILDIN_FUNC(isequipped)
+{
+ TBL_PC *sd;
+ int i, j, k, id = 1;
+ int index, flag;
+ int ret = -1;
+ //Original hash to reverse it when full check fails.
+ unsigned int setitem_hash = 0, setitem_hash2 = 0;
+
+ sd = script_rid2sd(st);
+
+ if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
+ script_pushint(st,0);
+ return 0;
+ }
+
+ setitem_hash = sd->bonus.setitem_hash;
+ setitem_hash2 = sd->bonus.setitem_hash2;
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+ flag = 0;
+ for (j=0; j<EQI_MAX; j++) {
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue;
+ if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue;
+ if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ if (itemdb_type(id) != IT_CARD) {
+ if (sd->inventory_data[index]->nameid != id)
+ continue;
+ flag = 1;
+ break;
+ } else { //Cards
+ if (sd->inventory_data[index]->slot == 0 ||
+ itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue;
+
+ for (k = 0; k < sd->inventory_data[index]->slot; k++)
+ { //New hash system which should support up to 4 slots on any equipment. [Skotlex]
+ unsigned int hash = 0;
+ if (sd->status.inventory[index].card[k] != id)
+ continue;
+
+ hash = 1<<((j<5?j:j-5)*4 + k);
+ // check if card is already used by another set
+ if ( ( j < 5 ? sd->bonus.setitem_hash : sd->bonus.setitem_hash2 ) & hash)
+ continue;
+
+ // We have found a match
+ flag = 1;
+ // Set hash so this card cannot be used by another
+ if (j<5)
+ sd->bonus.setitem_hash |= hash;
+ else
+ sd->bonus.setitem_hash2 |= hash;
+ break;
+ }
+ }
+ if (flag) break; //Card found
+ }
+ if (ret == -1)
+ ret = flag;
+ else
+ ret &= flag;
+ if (!ret) break;
+ }
+ if (!ret) {//When check fails, restore original hash values. [Skotlex]
+ sd->bonus.setitem_hash = setitem_hash;
+ sd->bonus.setitem_hash2 = setitem_hash2;
+ }
+ script_pushint(st,ret);
+ return 0;
+}
+
+/*================================================
+ * Check how many given inserted cards in the CURRENT
+ * weapon - used for 2/15's cards patch [Lupus]
+ *------------------------------------------------*/
+BUILDIN_FUNC(cardscnt)
+{
+ TBL_PC *sd;
+ int i, k, id = 1;
+ int ret = 0;
+ int index;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus]
+ if(index < 0) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ if(itemdb_type(id) != IT_CARD) {
+ if (sd->inventory_data[index]->nameid == id)
+ ret+= sd->status.inventory[index].amount;
+ } else {
+ if (itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue;
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[k] == id)
+ ret++;
+ }
+ }
+ }
+ script_pushint(st,ret);
+// script_pushint(st,current_equip_item_index);
+ return 0;
+}
+
+/*=======================================================
+ * Returns the refined number of the current item, or an
+ * item with inventory index specified
+ *-------------------------------------------------------*/
+BUILDIN_FUNC(getrefine)
+{
+ TBL_PC *sd;
+ if ((sd = script_rid2sd(st))!= NULL)
+ script_pushint(st,sd->status.inventory[current_equip_item_index].refine);
+ else
+ script_pushint(st,0);
+ return 0;
+}
+
+/*=======================================================
+ * Day/Night controls
+ *-------------------------------------------------------*/
+BUILDIN_FUNC(night)
+{
+ if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1);
+ return 0;
+}
+BUILDIN_FUNC(day)
+{
+ if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1);
+ return 0;
+}
+
+//=======================================================
+// Unequip [Spectre]
+//-------------------------------------------------------
+BUILDIN_FUNC(unequip)
+{
+ int i;
+ size_t num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd != NULL && num >= 1 && num <= ARRAYLENGTH(equip) )
+ {
+ i = pc_checkequip(sd,equip[num-1]);
+ if (i >= 0)
+ pc_unequipitem(sd,i,1|2);
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(equip)
+{
+ int nameid=0,i;
+ TBL_PC *sd;
+ struct item_data *item_data;
+
+ sd = script_rid2sd(st);
+
+ nameid=script_getnum(st,2);
+ if((item_data = itemdb_exists(nameid)) == NULL)
+ {
+ ShowError("wrong item ID : equipitem(%i)\n",nameid);
+ return 1;
+ }
+ ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid );
+ if( i < MAX_INVENTORY )
+ pc_equipitem(sd,i,item_data->equip);
+
+ return 0;
+}
+
+BUILDIN_FUNC(autoequip)
+{
+ int nameid, flag;
+ struct item_data *item_data;
+ nameid=script_getnum(st,2);
+ flag=script_getnum(st,3);
+
+ if( ( item_data = itemdb_exists(nameid) ) == NULL )
+ {
+ ShowError("buildin_autoequip: Invalid item '%d'.\n", nameid);
+ return 1;
+ }
+
+ if( !itemdb_isequip2(item_data) )
+ {
+ ShowError("buildin_autoequip: Item '%d' cannot be equipped.\n", nameid);
+ return 1;
+ }
+
+ item_data->flag.autoequip = flag>0?1:0;
+ return 0;
+}
+
+BUILDIN_FUNC(setbattleflag)
+{
+ const char *flag, *value;
+
+ flag = script_getstr(st,2);
+ value = script_getstr(st,3); // HACK: Retrieve number as string (auto-converted) for battle_set_value
+
+ if (battle_set_value(flag, value) == 0)
+ ShowWarning("buildin_setbattleflag: unknown battle_config flag '%s'\n",flag);
+ else
+ ShowInfo("buildin_setbattleflag: battle_config flag '%s' is now set to '%s'.\n",flag,value);
+
+ return 0;
+}
+
+BUILDIN_FUNC(getbattleflag)
+{
+ const char *flag;
+ flag = script_getstr(st,2);
+ script_pushint(st,battle_get_value(flag));
+ return 0;
+}
+
+//=======================================================
+// strlen [Valaris]
+//-------------------------------------------------------
+BUILDIN_FUNC(getstrlen)
+{
+
+ const char *str = script_getstr(st,2);
+ int len = (str) ? (int)strlen(str) : 0;
+
+ script_pushint(st,len);
+ return 0;
+}
+
+//=======================================================
+// isalpha [Valaris]
+//-------------------------------------------------------
+BUILDIN_FUNC(charisalpha)
+{
+ const char *str=script_getstr(st,2);
+ int pos=script_getnum(st,3);
+
+ int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISALPHA( str[pos] ) != 0 : 0;
+
+ script_pushint(st,val);
+ 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);
+
+ if( pos >= 0 && (unsigned int)pos < strlen(str) ) {
+ char output[2];
+ output[0] = str[pos];
+ output[1] = '\0';
+ script_pushstrcopy(st, output);
+ } else
+ script_pushconststr(st, "");
+ 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 = aStrdup(str);
+
+ if(index >= 0 && index < strlen(output))
+ output[index] = *c;
+
+ 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*)aMalloc(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
+ output = aStrdup(str);
+ script_pushstr(st, output);
+ return 0;
+ }
+
+ output = (char*)aMalloc(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 = aStrdup(str);
+ char *cursor = output;
+
+ while (*cursor != '\0') {
+ *cursor = TOUPPER(*cursor);
+ cursor++;
+ }
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// strtolower <str>
+//-------------------------------------------------------
+BUILDIN_FUNC(strtolower)
+{
+ const char *str = script_getstr(st,2);
+ char *output = aStrdup(str);
+ char *cursor = output;
+
+ while (*cursor != '\0') {
+ *cursor = TOLOWER(*cursor);
+ cursor++;
+ }
+
+ 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*)aMalloc(len + 1);
+ memcpy(output, &str[start], len);
+ } else
+ output = (char*)aMalloc(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*)aMalloc(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 < SCRIPT_MAX_ARRAYSIZE-1) { //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*)aMalloc(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*)aMalloc(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){
+ memcpy(buf,format,len+1);
+ 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 *)__64BPRTSIZE(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) = '*';
+ }
+
+ 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 (needle[0] == '\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_isstring(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)) {
+ count = script_getnum(st, 6);
+ if(count == 0) {
+ 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_isstring(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.
+///
+/// setnpcdisplay("<npc name>", "<new display name>", <new class id>, <new size>) -> <int>
+/// setnpcdisplay("<npc name>", "<new display name>", <new class id>) -> <int>
+/// setnpcdisplay("<npc name>", "<new display name>") -> <int>
+/// setnpcdisplay("<npc name>", <new class id>) -> <int>
+BUILDIN_FUNC(setnpcdisplay)
+{
+ const char* name;
+ const char* newname = NULL;
+ int class_ = -1, size = -1;
+ struct script_data* data;
+ struct npc_data* nd;
+
+ name = script_getstr(st,2);
+ data = script_getdata(st,3);
+
+ if( script_hasdata(st,4) )
+ class_ = script_getnum(st,4);
+ if( script_hasdata(st,5) )
+ size = script_getnum(st,5);
+
+ get_val(st, data);
+ if( data_isstring(data) )
+ newname = conv_str(st,data);
+ else if( data_isint(data) )
+ class_ = conv_num(st,data);
+ else
+ {
+ ShowError("script:setnpcdisplay: expected a string or number\n");
+ script_reportdata(data);
+ return 1;
+ }
+
+ nd = npc_name2id(name);
+ if( nd == NULL )
+ {// not found
+ script_pushint(st,1);
+ return 0;
+ }
+
+ // update npc
+ if( newname )
+ npc_setdisplayname(nd, newname);
+
+ if( size != -1 && size != (int)nd->size )
+ nd->size = size;
+ else
+ size = -1;
+
+ if( class_ != -1 && nd->class_ != class_ )
+ npc_setclass(nd, class_);
+ else if( size != -1 )
+ { // Required to update the visual size
+ clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);
+ clif_spawn(&nd->bl);
+ }
+
+ script_pushint(st,0);
+ return 0;
+}
+
+BUILDIN_FUNC(atoi)
+{
+ const char *value;
+ value = script_getstr(st,2);
+ script_pushint(st,atoi(value));
+ return 0;
+}
+
+// case-insensitive substring search [lordalfa]
+BUILDIN_FUNC(compare)
+{
+ const char *message;
+ const char *cmpstring;
+ message = script_getstr(st,2);
+ cmpstring = script_getstr(st,3);
+ script_pushint(st,(stristr(message,cmpstring) != NULL));
+ return 0;
+}
+
+// [zBuffer] List of mathematics commands --->
+BUILDIN_FUNC(sqrt)
+{
+ double i, a;
+ i = script_getnum(st,2);
+ a = sqrt(i);
+ script_pushint(st,(int)a);
+ return 0;
+}
+
+BUILDIN_FUNC(pow)
+{
+ double i, a, b;
+ a = script_getnum(st,2);
+ b = script_getnum(st,3);
+ i = pow(a,b);
+ script_pushint(st,(int)i);
+ return 0;
+}
+
+BUILDIN_FUNC(distance)
+{
+ int x0, y0, x1, y1;
+
+ x0 = script_getnum(st,2);
+ y0 = script_getnum(st,3);
+ x1 = script_getnum(st,4);
+ y1 = script_getnum(st,5);
+
+ script_pushint(st,distance_xy(x0,y0,x1,y1));
+ return 0;
+}
+
+// <--- [zBuffer] List of mathematics commands
+
+BUILDIN_FUNC(md5)
+{
+ const char *tmpstr;
+ char *md5str;
+
+ tmpstr = script_getstr(st,2);
+ md5str = (char *)aMalloc((32+1)*sizeof(char));
+ MD5_String(tmpstr, md5str);
+ script_pushstr(st, md5str);
+ return 0;
+}
+
+// [zBuffer] List of dynamic var commands --->
+
+BUILDIN_FUNC(setd)
+{
+ TBL_PC *sd=NULL;
+ char varname[100];
+ const char *buffer;
+ int elem;
+ buffer = script_getstr(st, 2);
+
+ if(sscanf(buffer, "%99[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ if( not_server_variable(*varname) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ {
+ ShowError("script:setd: no player attached for player variable '%s'\n", buffer);
+ return 0;
+ }
+ }
+
+ if( is_string_variable(varname) ) {
+ setd_sub(st, sd, varname, elem, (void *)script_getstr(st, 3), NULL);
+ } else {
+ setd_sub(st, sd, varname, elem, (void *)__64BPRTSIZE(script_getnum(st, 3)), NULL);
+ }
+
+ return 0;
+}
+
+int buildin_query_sql_sub(struct script_state* st, Sql* handle)
+{
+ int i, j;
+ TBL_PC* sd = NULL;
+ const char* query;
+ struct script_data* data;
+ const char* name;
+ int max_rows = SCRIPT_MAX_ARRAYSIZE; // maximum number of rows
+ int num_vars;
+ int num_cols;
+
+ // check target variables
+ for( i = 3; script_hasdata(st,i); ++i ) {
+ data = script_getdata(st, i);
+ if( data_isreference(data) ) { // it's a variable
+ name = reference_getname(data);
+ if( not_server_variable(*name) && sd == NULL ) { // requires a player
+ sd = script_rid2sd(st);
+ if( sd == NULL ) { // no player attached
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+ }
+ if( not_array_variable(*name) )
+ max_rows = 1;// not an array, limit to one row
+ } else {
+ ShowError("script:query_sql: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+ }
+ num_vars = i - 3;
+
+ // Execute the query
+ query = script_getstr(st,2);
+
+ if( SQL_ERROR == Sql_QueryStr(handle, query) ) {
+ Sql_ShowDebug(handle);
+ script_pushint(st, 0);
+ return 1;
+ }
+
+ if( Sql_NumRows(handle) == 0 ) { // No data received
+ Sql_FreeResult(handle);
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ // Count the number of columns to store
+ num_cols = Sql_NumColumns(handle);
+ if( num_vars < num_cols ) {
+ ShowWarning("script:query_sql: Too many columns, discarding last %u columns.\n", (unsigned int)(num_cols-num_vars));
+ script_reportsrc(st);
+ } else if( num_vars > num_cols ) {
+ ShowWarning("script:query_sql: Too many variables (%u extra).\n", (unsigned int)(num_vars-num_cols));
+ script_reportsrc(st);
+ }
+
+ // Store data
+ for( i = 0; i < max_rows && SQL_SUCCESS == Sql_NextRow(handle); ++i ) {
+ for( j = 0; j < num_vars; ++j ) {
+ char* str = NULL;
+
+ if( j < num_cols )
+ Sql_GetData(handle, j, &str, NULL);
+
+ data = script_getdata(st, j+3);
+ name = reference_getname(data);
+ if( is_string_variable(name) )
+ setd_sub(st, sd, name, i, (void *)(str?str:""), reference_getref(data));
+ else
+ setd_sub(st, sd, name, i, (void *)__64BPRTSIZE((str?atoi(str):0)), reference_getref(data));
+ }
+ }
+ if( i == max_rows && max_rows < Sql_NumRows(handle) ) {
+ ShowWarning("script:query_sql: Only %d/%u rows have been stored.\n", max_rows, (unsigned int)Sql_NumRows(handle));
+ script_reportsrc(st);
+ }
+
+ // Free data
+ Sql_FreeResult(handle);
+ script_pushint(st, i);
+
+ return 0;
+}
+
+BUILDIN_FUNC(query_sql) {
+#ifdef BETA_THREAD_TEST
+ if( st->state != RERUNLINE ) {
+ queryThread_add(st,false);
+
+ st->state = RERUNLINE;/* will continue when the query is finished running. */
+ } else
+ st->state = RUN;
+
+ return 0;
+#else
+ return buildin_query_sql_sub(st, mmysql_handle);
+#endif
+}
+
+BUILDIN_FUNC(query_logsql) {
+ if( !log_config.sql_logs ) {// logmysql_handle == NULL
+ ShowWarning("buildin_query_logsql: SQL logs are disabled, query '%s' will not be executed.\n", script_getstr(st,2));
+ script_pushint(st,-1);
+ return 1;
+ }
+#ifdef BETA_THREAD_TEST
+ if( st->state != RERUNLINE ) {
+ queryThread_add(st,true);
+
+ st->state = RERUNLINE;/* will continue when the query is finished running. */
+ } else
+ st->state = RUN;
+
+ return 0;
+#else
+ return buildin_query_sql_sub(st, logmysql_handle);
+#endif
+}
+
+//Allows escaping of a given string.
+BUILDIN_FUNC(escape_sql)
+{
+ const char *str;
+ char *esc_str;
+ size_t len;
+
+ str = script_getstr(st,2);
+ len = strlen(str);
+ esc_str = (char*)aMalloc(len*2+1);
+ Sql_EscapeStringLen(mmysql_handle, esc_str, str, len);
+ script_pushstr(st, esc_str);
+ return 0;
+}
+
+BUILDIN_FUNC(getd)
+{
+ char varname[100];
+ const char *buffer;
+ int elem;
+
+ buffer = script_getstr(st, 2);
+
+ if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ // Push the 'pointer' so it's more flexible [Lance]
+ push_val(st->stack, C_NAME, reference_uid(add_str(varname), elem));
+
+ return 0;
+}
+
+// <--- [zBuffer] List of dynamic var commands
+// Pet stat [Lance]
+BUILDIN_FUNC(petstat)
+{
+ TBL_PC *sd = NULL;
+ struct pet_data *pd;
+ int flag = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if(!sd || !sd->status.pet_id || !sd->pd){
+ if(flag == 2)
+ script_pushconststr(st, "");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+ pd = sd->pd;
+ switch(flag){
+ case 1: script_pushint(st,(int)pd->pet.class_); break;
+ case 2: script_pushstrcopy(st, pd->pet.name); break;
+ case 3: script_pushint(st,(int)pd->pet.level); break;
+ case 4: script_pushint(st,(int)pd->pet.hungry); break;
+ case 5: script_pushint(st,(int)pd->pet.intimate); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(callshop)
+{
+ TBL_PC *sd = NULL;
+ struct npc_data *nd;
+ const char *shopname;
+ int flag = 0;
+ sd = script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,0);
+ return 0;
+ }
+ shopname = script_getstr(st, 2);
+ if( script_hasdata(st,3) )
+ flag = script_getnum(st,3);
+ nd = npc_name2id(shopname);
+ if( !nd || nd->bl.type != BL_NPC || (nd->subtype != SHOP && nd->subtype != CASHSHOP) )
+ {
+ ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)\n", shopname);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ if( nd->subtype == SHOP )
+ {
+ // flag the user as using a valid script call for opening the shop (for floating NPCs)
+ sd->state.callshop = 1;
+
+ switch( flag )
+ {
+ case 1: npc_buysellsel(sd,nd->bl.id,0); break; //Buy window
+ case 2: npc_buysellsel(sd,nd->bl.id,1); break; //Sell window
+ default: clif_npcbuysell(sd,nd->bl.id); break; //Show menu
+ }
+ }
+ else
+ clif_cashshop_show(sd, nd);
+
+ sd->npc_shopid = nd->bl.id;
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(npcshopitem)
+{
+ const char* npcname = script_getstr(st, 2);
+ struct npc_data* nd = npc_name2id(npcname);
+ int n, i;
+ int amount;
+
+ if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ // get the count of new entries
+ amount = (script_lastdata(st)-2)/2;
+
+ // generate new shop item list
+ RECREATE(nd->u.shop.shop_item, struct npc_item_list, amount);
+ for( n = 0, i = 3; n < amount; n++, i+=2 )
+ {
+ nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
+ nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
+ }
+ nd->u.shop.count = n;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(npcshopadditem)
+{
+ const char* npcname = script_getstr(st,2);
+ struct npc_data* nd = npc_name2id(npcname);
+ int n, i;
+ int amount;
+
+ if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ // get the count of new entries
+ amount = (script_lastdata(st)-2)/2;
+
+ // append new items to existing shop item list
+ RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+amount);
+ for( n = nd->u.shop.count, i = 3; n < nd->u.shop.count+amount; n++, i+=2 )
+ {
+ nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
+ nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
+ }
+ nd->u.shop.count = n;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(npcshopdelitem)
+{
+ const char* npcname = script_getstr(st,2);
+ struct npc_data* nd = npc_name2id(npcname);
+ unsigned int nameid;
+ int n, i;
+ int amount;
+ int size;
+
+ if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ amount = script_lastdata(st)-2;
+ size = nd->u.shop.count;
+
+ // remove specified items from the shop item list
+ for( i = 3; i < 3 + amount; i++ )
+ {
+ nameid = script_getnum(st,i);
+
+ ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == nameid );
+ if( n < size )
+ {
+ memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n));
+ size--;
+ }
+ }
+
+ RECREATE(nd->u.shop.shop_item, struct npc_item_list, size);
+ nd->u.shop.count = size;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+//Sets a script to attach to a shop npc.
+BUILDIN_FUNC(npcshopattach)
+{
+ const char* npcname = script_getstr(st,2);
+ struct npc_data* nd = npc_name2id(npcname);
+ int flag = 1;
+
+ if( script_hasdata(st,3) )
+ flag = script_getnum(st,3);
+
+ if( !nd || nd->subtype != SHOP )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ if (flag)
+ nd->master_nd = ((struct npc_data *)map_id2bl(st->oid));
+ else
+ nd->master_nd = NULL;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ setitemscript(itemID,"{new item bonus script}",[n]);
+ Where n:
+ 0 - script
+ 1 - Equip script
+ 2 - Unequip script
+ *------------------------------------------*/
+BUILDIN_FUNC(setitemscript)
+{
+ int item_id,n=0;
+ const char *script;
+ struct item_data *i_data;
+ struct script_code **dstscript;
+
+ item_id = script_getnum(st,2);
+ script = script_getstr(st,3);
+ if( script_hasdata(st,4) )
+ n=script_getnum(st,4);
+ i_data = itemdb_exists(item_id);
+
+ if (!i_data || script==NULL || ( script[0] && script[0]!='{' )) {
+ script_pushint(st,0);
+ return 0;
+ }
+ switch (n) {
+ case 2:
+ dstscript = &i_data->unequip_script;
+ break;
+ case 1:
+ dstscript = &i_data->equip_script;
+ break;
+ default:
+ dstscript = &i_data->script;
+ break;
+ }
+ if(*dstscript)
+ script_free_code(*dstscript);
+
+ *dstscript = script[0] ? parse_script(script, "script_setitemscript", 0, 0) : NULL;
+ script_pushint(st,1);
+ return 0;
+}
+
+/* Work In Progress [Lupus]
+BUILDIN_FUNC(addmonsterdrop)
+{
+ int class_,item_id,chance;
+ class_=script_getnum(st,2);
+ item_id=script_getnum(st,3);
+ chance=script_getnum(st,4);
+ if(class_>1000 && item_id>500 && chance>0) {
+ script_pushint(st,1);
+ } else {
+ script_pushint(st,0);
+ }
+}
+
+BUILDIN_FUNC(delmonsterdrop)
+{
+ int class_,item_id;
+ class_=script_getnum(st,2);
+ item_id=script_getnum(st,3);
+ if(class_>1000 && item_id>500) {
+ script_pushint(st,1);
+ } else {
+ script_pushint(st,0);
+ }
+}
+*/
+
+/*==========================================
+ * Returns some values of a monster [Lupus]
+ * Name, Level, race, size, etc...
+ getmonsterinfo(monsterID,queryIndex);
+ *------------------------------------------*/
+BUILDIN_FUNC(getmonsterinfo)
+{
+ struct mob_db *mob;
+ int mob_id;
+
+ mob_id = script_getnum(st,2);
+ if (!mobdb_checkid(mob_id)) {
+ ShowError("buildin_getmonsterinfo: Wrong Monster ID: %i\n", mob_id);
+ if ( !script_getnum(st,3) ) //requested a string
+ script_pushconststr(st,"null");
+ else
+ script_pushint(st,-1);
+ return -1;
+ }
+ mob = mob_db(mob_id);
+ switch ( script_getnum(st,3) ) {
+ case 0: script_pushstrcopy(st,mob->jname); break;
+ case 1: script_pushint(st,mob->lv); break;
+ case 2: script_pushint(st,mob->status.max_hp); break;
+ case 3: script_pushint(st,mob->base_exp); break;
+ case 4: script_pushint(st,mob->job_exp); break;
+ case 5: script_pushint(st,mob->status.rhw.atk); break;
+ case 6: script_pushint(st,mob->status.rhw.atk2); break;
+ case 7: script_pushint(st,mob->status.def); break;
+ case 8: script_pushint(st,mob->status.mdef); break;
+ case 9: script_pushint(st,mob->status.str); break;
+ case 10: script_pushint(st,mob->status.agi); break;
+ case 11: script_pushint(st,mob->status.vit); break;
+ case 12: script_pushint(st,mob->status.int_); break;
+ case 13: script_pushint(st,mob->status.dex); break;
+ case 14: script_pushint(st,mob->status.luk); break;
+ case 15: script_pushint(st,mob->status.rhw.range); break;
+ case 16: script_pushint(st,mob->range2); break;
+ case 17: script_pushint(st,mob->range3); break;
+ case 18: script_pushint(st,mob->status.size); break;
+ case 19: script_pushint(st,mob->status.race); break;
+ case 20: script_pushint(st,mob->status.def_ele); break;
+ case 21: script_pushint(st,mob->status.mode); break;
+ case 22: script_pushint(st,mob->mexp); break;
+ default: script_pushint(st,-1); //wrong Index
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(checkvending) // check vending [Nab4]
+{
+ TBL_PC *sd = NULL;
+
+ if(script_hasdata(st,2))
+ sd = map_nick2sd(script_getstr(st,2));
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ script_pushint(st, sd->state.autotrade ? 2 : sd->state.vending);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+
+BUILDIN_FUNC(checkchatting) // check chatting [Marka]
+{
+ TBL_PC *sd = NULL;
+
+ if(script_hasdata(st,2))
+ sd = map_nick2sd(script_getstr(st,2));
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ script_pushint(st,(sd->chatID != 0));
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+BUILDIN_FUNC(searchitem)
+{
+ struct script_data* data = script_getdata(st, 2);
+ const char *itemname = script_getstr(st,3);
+ struct item_data *items[MAX_SEARCH];
+ int count;
+
+ char* name;
+ int32 start;
+ int32 id;
+ int32 i;
+ TBL_PC* sd = NULL;
+
+ if ((items[0] = itemdb_exists(atoi(itemname))))
+ count = 1;
+ else {
+ count = itemdb_searchname_array(items, ARRAYLENGTH(items), itemname);
+ if (count > MAX_SEARCH) count = MAX_SEARCH;
+ }
+
+ if (!count) {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ if( !data_isreference(data) )
+ {
+ ShowError("script:searchitem: 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:searchitem: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ if( is_string_variable(name) )
+ {// string array
+ ShowError("script:searchitem: not an integer array reference\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ for( i = 0; i < count; ++start, ++i )
+ {// Set array
+ void* v = (void*)__64BPRTSIZE((int)items[i]->nameid);
+ set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data));
+ }
+
+ script_pushint(st, count);
+ return 0;
+}
+
+int axtoi(const char *hexStg)
+{
+ int n = 0; // position in string
+ int16 m = 0; // position in digit[] to shift
+ int count; // loop index
+ int intValue = 0; // integer value of hex string
+ int digit[11]; // hold values to convert
+ while (n < 10) {
+ if (hexStg[n]=='\0')
+ break;
+ if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
+ digit[n] = hexStg[n] & 0x0f; //convert to int
+ else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else break;
+ n++;
+ }
+ count = n;
+ m = n - 1;
+ n = 0;
+ while(n < count) {
+ // digit[n] is value of hex digit at position n
+ // (m << 2) is the number of positions to shift
+ // OR the bits into return value
+ intValue = intValue | (digit[n] << (m << 2));
+ m--; // adjust the position to set
+ n++; // next digit to process
+ }
+ return (intValue);
+}
+
+// [Lance] Hex string to integer converter
+BUILDIN_FUNC(axtoi)
+{
+ const char *hex = script_getstr(st,2);
+ script_pushint(st,axtoi(hex));
+ return 0;
+}
+
+// [zBuffer] List of player cont commands --->
+BUILDIN_FUNC(rid2name)
+{
+ struct block_list *bl = NULL;
+ int rid = script_getnum(st,2);
+ if((bl = map_id2bl(rid)))
+ {
+ switch(bl->type) {
+ case BL_MOB: script_pushstrcopy(st,((TBL_MOB*)bl)->name); break;
+ case BL_PC: script_pushstrcopy(st,((TBL_PC*)bl)->status.name); break;
+ case BL_NPC: script_pushstrcopy(st,((TBL_NPC*)bl)->exname); break;
+ case BL_PET: script_pushstrcopy(st,((TBL_PET*)bl)->pet.name); break;
+ case BL_HOM: script_pushstrcopy(st,((TBL_HOM*)bl)->homunculus.name); break;
+ case BL_MER: script_pushstrcopy(st,((TBL_MER*)bl)->db->name); break;
+ default:
+ ShowError("buildin_rid2name: BL type unknown.\n");
+ script_pushconststr(st,"");
+ break;
+ }
+ } else {
+ ShowError("buildin_rid2name: invalid RID\n");
+ script_pushconststr(st,"(null)");
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(pcblockmove)
+{
+ int id, flag;
+ TBL_PC *sd = NULL;
+
+ id = script_getnum(st,2);
+ flag = script_getnum(st,3);
+
+ if(id)
+ sd = map_id2sd(id);
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ sd->state.blockedmove = flag > 0;
+
+ return 0;
+}
+
+BUILDIN_FUNC(pcfollow)
+{
+ int id, targetid;
+ TBL_PC *sd = NULL;
+
+
+ id = script_getnum(st,2);
+ targetid = script_getnum(st,3);
+
+ if(id)
+ sd = map_id2sd(id);
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ pc_follow(sd, targetid);
+
+ return 0;
+}
+
+BUILDIN_FUNC(pcstopfollow)
+{
+ int id;
+ TBL_PC *sd = NULL;
+
+
+ id = script_getnum(st,2);
+
+ if(id)
+ sd = map_id2sd(id);
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ pc_stop_following(sd);
+
+ return 0;
+}
+// <--- [zBuffer] List of player cont commands
+// [zBuffer] List of mob control commands --->
+//## TODO always return if the request/whatever was successfull [FlavioJS]
+
+/// Makes the unit walk to target position or map
+/// Returns if it was successfull
+///
+/// unitwalk(<unit_id>,<x>,<y>) -> <bool>
+/// unitwalk(<unit_id>,<map_id>) -> <bool>
+BUILDIN_FUNC(unitwalk)
+{
+ struct block_list* bl;
+
+ bl = map_id2bl(script_getnum(st,2));
+ if( bl == NULL )
+ {
+ script_pushint(st, 0);
+ }
+ else if( script_hasdata(st,4) )
+ {
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ script_pushint(st, unit_walktoxy(bl,x,y,0));// We'll use harder calculations.
+ }
+ else
+ {
+ int map_id = script_getnum(st,3);
+ script_pushint(st, unit_walktobl(bl,map_id2bl(map_id),65025,1));
+ }
+
+ return 0;
+}
+
+/// Kills the unit
+///
+/// unitkill <unit_id>;
+BUILDIN_FUNC(unitkill)
+{
+ struct block_list* bl = map_id2bl(script_getnum(st,2));
+ if( bl != NULL )
+ status_kill(bl);
+
+ return 0;
+}
+
+/// Warps the unit to the target position in the target map
+/// Returns if it was successfull
+///
+/// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool>
+BUILDIN_FUNC(unitwarp)
+{
+ int unit_id;
+ int map;
+ short x;
+ short y;
+ struct block_list* bl;
+ const char *mapname;
+
+ unit_id = script_getnum(st,2);
+ mapname = script_getstr(st, 3);
+ x = (short)script_getnum(st,4);
+ y = (short)script_getnum(st,5);
+
+ if (!unit_id) //Warp the script's runner
+ bl = map_id2bl(st->rid);
+ else
+ bl = map_id2bl(unit_id);
+
+ if( strcmp(mapname,"this") == 0 )
+ map = bl?bl->m:-1;
+ else
+ map = map_mapname2mapid(mapname);
+
+ if( map >= 0 && bl != NULL )
+ script_pushint(st, unit_warp(bl,map,x,y,CLR_OUTSIGHT));
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Makes the unit attack the target.
+/// If the unit is a player and <action type> is not 0, it does a continuous
+/// attack instead of a single attack.
+/// Returns if the request was successfull.
+///
+/// unitattack(<unit_id>,"<target name>"{,<action type>}) -> <bool>
+/// unitattack(<unit_id>,<target_id>{,<action type>}) -> <bool>
+BUILDIN_FUNC(unitattack)
+{
+ struct block_list* unit_bl;
+ struct block_list* target_bl = NULL;
+ struct script_data* data;
+ int actiontype = 0;
+
+ // get unit
+ unit_bl = map_id2bl(script_getnum(st,2));
+ if( unit_bl == NULL ) {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ data = script_getdata(st, 3);
+ get_val(st, data);
+ if( data_isstring(data) )
+ {
+ TBL_PC* sd = map_nick2sd(conv_str(st, data));
+ if( sd != NULL )
+ target_bl = &sd->bl;
+ } else
+ target_bl = map_id2bl(conv_num(st, data));
+ // request the attack
+ if( target_bl == NULL )
+ {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ // get actiontype
+ if( script_hasdata(st,4) )
+ actiontype = script_getnum(st,4);
+
+ switch( unit_bl->type )
+ {
+ case BL_PC:
+ clif_parse_ActionRequest_sub(((TBL_PC *)unit_bl), actiontype > 0 ? 0x07 : 0x00, target_bl->id, gettick());
+ script_pushint(st, 1);
+ return 0;
+ case BL_MOB:
+ ((TBL_MOB *)unit_bl)->target_id = target_bl->id;
+ break;
+ case BL_PET:
+ ((TBL_PET *)unit_bl)->target_id = target_bl->id;
+ break;
+ default:
+ ShowError("script:unitattack: unsupported source unit type %d\n", unit_bl->type);
+ script_pushint(st, 0);
+ return 1;
+ }
+ script_pushint(st, unit_walktobl(unit_bl, target_bl, 65025, 2));
+ return 0;
+}
+
+/// Makes the unit stop attacking and moving
+///
+/// unitstop <unit_id>;
+BUILDIN_FUNC(unitstop)
+{
+ int unit_id;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ {
+ unit_stop_attack(bl);
+ unit_stop_walking(bl,4);
+ if( bl->type == BL_MOB )
+ ((TBL_MOB*)bl)->target_id = 0;
+ }
+
+ return 0;
+}
+
+/// Makes the unit say the message
+///
+/// unittalk <unit_id>,"<message>";
+BUILDIN_FUNC(unittalk)
+{
+ int unit_id;
+ const char* message;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ message = script_getstr(st, 3);
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ {
+ struct StringBuf sbuf;
+ StringBuf_Init(&sbuf);
+ StringBuf_Printf(&sbuf, "%s : %s", status_get_name(bl), message);
+ clif_message(bl, StringBuf_Value(&sbuf));
+ if( bl->type == BL_PC )
+ clif_displaymessage(((TBL_PC*)bl)->fd, StringBuf_Value(&sbuf));
+ StringBuf_Destroy(&sbuf);
+ }
+
+ return 0;
+}
+
+/// Makes the unit do an emotion
+///
+/// unitemote <unit_id>,<emotion>;
+///
+/// @see e_* in const.txt
+BUILDIN_FUNC(unitemote)
+{
+ int unit_id;
+ int emotion;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ emotion = script_getnum(st,3);
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ clif_emotion(bl, emotion);
+
+ return 0;
+}
+
+/// Makes the unit cast the skill on the target or self if no target is specified
+///
+/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>};
+/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>};
+BUILDIN_FUNC(unitskilluseid)
+{
+ int unit_id;
+ uint16 skill_id;
+ uint16 skill_lv;
+ int target_id;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ skill_lv = script_getnum(st,4);
+ target_id = ( script_hasdata(st,5) ? script_getnum(st,5) : unit_id );
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ unit_skilluse_id(bl, target_id, skill_id, skill_lv);
+
+ return 0;
+}
+
+/// Makes the unit cast the skill on the target position.
+///
+/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>;
+/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>;
+BUILDIN_FUNC(unitskillusepos)
+{
+ int unit_id;
+ uint16 skill_id;
+ uint16 skill_lv;
+ int skill_x;
+ int skill_y;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ skill_lv = script_getnum(st,4);
+ skill_x = script_getnum(st,5);
+ skill_y = script_getnum(st,6);
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ unit_skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv);
+
+ return 0;
+}
+
+// <--- [zBuffer] List of mob control commands
+
+/// Pauses the execution of the script, detaching the player
+///
+/// sleep <mili seconds>;
+BUILDIN_FUNC(sleep)
+{
+ int ticks;
+
+ ticks = script_getnum(st,2);
+
+ // detach the player
+ script_detach_rid(st);
+
+ if( ticks <= 0 )
+ {// do nothing
+ }
+ else if( st->sleep.tick == 0 )
+ {// sleep for the target amount of time
+ st->state = RERUNLINE;
+ st->sleep.tick = ticks;
+ }
+ else
+ {// sleep time is over
+ st->state = RUN;
+ st->sleep.tick = 0;
+ }
+ return 0;
+}
+
+/// Pauses the execution of the script, keeping the player attached
+/// Returns if a player is still attached
+///
+/// sleep2(<mili secconds>) -> <bool>
+BUILDIN_FUNC(sleep2)
+{
+ int ticks;
+
+ ticks = script_getnum(st,2);
+
+ if( ticks <= 0 )
+ {// do nothing
+ script_pushint(st, (map_id2sd(st->rid)!=NULL));
+ }
+ else if( !st->sleep.tick )
+ {// sleep for the target amount of time
+ st->state = RERUNLINE;
+ st->sleep.tick = ticks;
+ }
+ else
+ {// sleep time is over
+ st->state = RUN;
+ st->sleep.tick = 0;
+ script_pushint(st, (map_id2sd(st->rid)!=NULL));
+ }
+ return 0;
+}
+
+/// Awakes all the sleep timers of the target npc
+///
+/// awake "<npc name>";
+BUILDIN_FUNC(awake)
+{
+ struct npc_data* nd;
+ struct linkdb_node *node = (struct linkdb_node *)sleep_db;
+
+ nd = npc_name2id(script_getstr(st, 2));
+ if( nd == NULL ) {
+ ShowError("awake: NPC \"%s\" not found\n", script_getstr(st, 2));
+ return 1;
+ }
+
+ while( node )
+ {
+ if( (int)__64BPRTSIZE(node->key) == nd->bl.id )
+ {// sleep timer for the npc
+ struct script_state* tst = (struct script_state*)node->data;
+ TBL_PC* sd = map_id2sd(tst->rid);
+
+ if( tst->sleep.timer == INVALID_TIMER )
+ {// already awake ???
+ node = node->next;
+ continue;
+ }
+ if( (sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd))
+ {// char not online anymore / another char of the same account is online - Cancel execution
+ tst->state = END;
+ tst->rid = 0;
+ }
+
+ delete_timer(tst->sleep.timer, run_script_timer);
+ node = script_erase_sleepdb(node);
+ tst->sleep.timer = INVALID_TIMER;
+ if(tst->state != RERUNLINE)
+ tst->sleep.tick = 0;
+ run_script_main(tst);
+ }
+ else
+ {
+ node = node->next;
+ }
+ }
+ return 0;
+}
+
+/// Returns a reference to a variable of the target NPC.
+/// Returns 0 if an error occurs.
+///
+/// getvariableofnpc(<variable>, "<npc name>") -> <reference>
+BUILDIN_FUNC(getvariableofnpc)
+{
+ struct script_data* data;
+ const char* name;
+ struct npc_data* nd;
+
+ data = script_getdata(st,2);
+ if( !data_isreference(data) )
+ {// Not a reference (aka varaible name)
+ ShowError("script:getvariableofnpc: not a variable\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;
+ }
+
+ name = reference_getname(data);
+ if( *name != '.' || name[1] == '@' )
+ {// not a npc variable
+ ShowError("script:getvariableofnpc: invalid scope (not npc variable)\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;
+ }
+
+ nd = npc_name2id(script_getstr(st,3));
+ if( nd == NULL || nd->subtype != SCRIPT || nd->u.scr.script == NULL )
+ {// NPC not found or has no script
+ ShowError("script:getvariableofnpc: can't find npc %s\n", script_getstr(st,3));
+ script_pushnil(st);
+ st->state = END;
+ return 1;
+ }
+
+ push_val2(st->stack, C_NAME, reference_getuid(data), &nd->u.scr.script->script_vars );
+ return 0;
+}
+
+/// Opens a warp portal.
+/// Has no "portal opening" effect/sound, it opens the portal immediately.
+///
+/// warpportal <source x>,<source y>,"<target map>",<target x>,<target y>;
+///
+/// @author blackhole89
+BUILDIN_FUNC(warpportal)
+{
+ int spx;
+ int spy;
+ unsigned short mapindex;
+ int tpx;
+ int tpy;
+ struct skill_unit_group* group;
+ struct block_list* bl;
+
+ bl = map_id2bl(st->oid);
+ if( bl == NULL )
+ {
+ ShowError("script:warpportal: npc is needed\n");
+ return 1;
+ }
+
+ spx = script_getnum(st,2);
+ spy = script_getnum(st,3);
+ mapindex = mapindex_name2id(script_getstr(st, 4));
+ tpx = script_getnum(st,5);
+ tpy = script_getnum(st,6);
+
+ if( mapindex == 0 )
+ return 0;// map not found
+
+ group = skill_unitsetting(bl, AL_WARP, 4, spx, spy, 0);
+ if( group == NULL )
+ return 0;// failed
+ group->val2 = (tpx<<16) | tpy;
+ group->val3 = mapindex;
+
+ return 0;
+}
+
+BUILDIN_FUNC(openmail)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ mail_openmail(sd);
+
+ return 0;
+}
+
+BUILDIN_FUNC(openauction)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_Auction_openwindow(sd);
+
+ return 0;
+}
+
+/// Retrieves the value of the specified flag of the specified cell.
+///
+/// checkcell("<map name>",<x>,<y>,<type>) -> <bool>
+///
+/// @see cell_chk* constants in const.txt for the types
+BUILDIN_FUNC(checkcell)
+{
+ int16 m = map_mapname2mapid(script_getstr(st,2));
+ int16 x = script_getnum(st,3);
+ int16 y = script_getnum(st,4);
+ cell_chk type = (cell_chk)script_getnum(st,5);
+
+ script_pushint(st, map_getcell(m, x, y, type));
+
+ return 0;
+}
+
+/// Modifies flags of cells in the specified area.
+///
+/// setcell "<map name>",<x1>,<y1>,<x2>,<y2>,<type>,<flag>;
+///
+/// @see cell_* constants in const.txt for the types
+BUILDIN_FUNC(setcell)
+{
+ int16 m = map_mapname2mapid(script_getstr(st,2));
+ int16 x1 = script_getnum(st,3);
+ int16 y1 = script_getnum(st,4);
+ int16 x2 = script_getnum(st,5);
+ int16 y2 = script_getnum(st,6);
+ cell_t type = (cell_t)script_getnum(st,7);
+ bool flag = (bool)script_getnum(st,8);
+
+ int x,y;
+
+ if( x1 > x2 ) swap(x1,x2);
+ if( y1 > y2 ) swap(y1,y2);
+
+ for( y = y1; y <= y2; ++y )
+ for( x = x1; x <= x2; ++x )
+ map_setcell(m, x, y, type, flag);
+
+ return 0;
+}
+
+/*==========================================
+ * Mercenary Commands
+ *------------------------------------------*/
+BUILDIN_FUNC(mercenary_create)
+{
+ struct map_session_data *sd;
+ int class_, contract_time;
+
+ if( (sd = script_rid2sd(st)) == NULL || sd->md || sd->status.mer_id != 0 )
+ return 0;
+
+ class_ = script_getnum(st,2);
+
+ if( !merc_class(class_) )
+ return 0;
+
+ contract_time = script_getnum(st,3);
+ merc_create(sd, class_, contract_time);
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_heal)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int hp, sp;
+
+ if( sd == NULL || sd->md == NULL )
+ return 0;
+ hp = script_getnum(st,2);
+ sp = script_getnum(st,3);
+
+ status_heal(&sd->md->bl, hp, sp, 0);
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_sc_start)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ enum sc_type type;
+ int tick, val1;
+
+ if( sd == NULL || sd->md == NULL )
+ return 0;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+
+ status_change_start(&sd->md->bl, type, 10000, val1, 0, 0, 0, tick, 2);
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_get_calls)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ script_pushint(st,sd->status.arch_calls);
+ break;
+ case SPEAR_MERC_GUILD:
+ script_pushint(st,sd->status.spear_calls);
+ break;
+ case SWORD_MERC_GUILD:
+ script_pushint(st,sd->status.sword_calls);
+ break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_set_calls)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild, value, *calls;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ value = script_getnum(st,3);
+
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ calls = &sd->status.arch_calls;
+ break;
+ case SPEAR_MERC_GUILD:
+ calls = &sd->status.spear_calls;
+ break;
+ case SWORD_MERC_GUILD:
+ calls = &sd->status.sword_calls;
+ break;
+ default:
+ return 0; // Invalid Guild
+ }
+
+ *calls += value;
+ *calls = cap_value(*calls, 0, INT_MAX);
+
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_get_faith)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ script_pushint(st,sd->status.arch_faith);
+ break;
+ case SPEAR_MERC_GUILD:
+ script_pushint(st,sd->status.spear_faith);
+ break;
+ case SWORD_MERC_GUILD:
+ script_pushint(st,sd->status.sword_faith);
+ break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_set_faith)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild, value, *calls;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ value = script_getnum(st,3);
+
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ calls = &sd->status.arch_faith;
+ break;
+ case SPEAR_MERC_GUILD:
+ calls = &sd->status.spear_faith;
+ break;
+ case SWORD_MERC_GUILD:
+ calls = &sd->status.sword_faith;
+ break;
+ default:
+ return 0; // Invalid Guild
+ }
+
+ *calls += value;
+ *calls = cap_value(*calls, 0, INT_MAX);
+ if( mercenary_get_guild(sd->md) == guild )
+ clif_mercenary_updatestatus(sd,SP_MERCFAITH);
+
+ return 0;
+}
+
+/*------------------------------------------
+ * Book Reading
+ *------------------------------------------*/
+BUILDIN_FUNC(readbook)
+{
+ struct map_session_data *sd;
+ int book_id, page;
+
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+
+ book_id = script_getnum(st,2);
+ page = script_getnum(st,3);
+
+ clif_readbook(sd->fd, book_id, page);
+ return 0;
+}
+
+/******************
+Questlog script commands
+*******************/
+
+BUILDIN_FUNC(setquest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_add(sd, script_getnum(st, 2));
+ return 0;
+}
+
+BUILDIN_FUNC(erasequest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_delete(sd, script_getnum(st, 2));
+ return 0;
+}
+
+BUILDIN_FUNC(completequest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_update_status(sd, script_getnum(st, 2), Q_COMPLETE);
+ return 0;
+}
+
+BUILDIN_FUNC(changequest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_change(sd, script_getnum(st, 2),script_getnum(st, 3));
+ return 0;
+}
+
+BUILDIN_FUNC(checkquest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ quest_check_type type = HAVEQUEST;
+
+ nullpo_ret(sd);
+
+ if( script_hasdata(st, 3) )
+ type = (quest_check_type)script_getnum(st, 3);
+
+ script_pushint(st, quest_check(sd, script_getnum(st, 2), type));
+
+ return 0;
+}
+
+BUILDIN_FUNC(showevent)
+{
+ TBL_PC *sd = script_rid2sd(st);
+ struct npc_data *nd = map_id2nd(st->oid);
+ int state, color;
+
+ if( sd == NULL || nd == NULL )
+ return 0;
+ state = script_getnum(st, 2);
+ color = script_getnum(st, 3);
+
+ if( color < 0 || color > 3 )
+ color = 0; // set default color
+
+ clif_quest_show_event(sd, &nd->bl, state, color);
+ return 0;
+}
+
+/*==========================================
+ * BattleGround System
+ *------------------------------------------*/
+BUILDIN_FUNC(waitingroom2bg)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+ const char *map_name, *ev = "", *dev = "";
+ int x, y, i, mapindex = 0, bg_id, n;
+ struct map_session_data *sd;
+
+ if( script_hasdata(st,7) )
+ nd = npc_name2id(script_getstr(st,7));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ map_name = script_getstr(st,2);
+ if( strcmp(map_name,"-") != 0 )
+ {
+ mapindex = mapindex_name2id(map_name);
+ if( mapindex == 0 )
+ { // Invalid Map
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ ev = script_getstr(st,5); // Logout Event
+ dev = script_getstr(st,6); // Die Event
+
+ if( (bg_id = bg_create(mapindex, x, y, ev, dev)) == 0 )
+ { // Creation failed
+ script_pushint(st,0);
+ return 0;
+ }
+
+ n = cd->users;
+ for( i = 0; i < n && i < MAX_BG_MEMBERS; i++ )
+ {
+ if( (sd = cd->usersd[i]) != NULL && bg_team_join(bg_id, sd) )
+ mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), sd->bl.id);
+ else
+ mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), 0);
+ }
+
+ mapreg_setreg(add_str("$@arenamembersnum"), i);
+ script_pushint(st,bg_id);
+ return 0;
+}
+
+BUILDIN_FUNC(waitingroom2bg_single)
+{
+ const char* map_name;
+ struct npc_data *nd;
+ struct chat_data *cd;
+ struct map_session_data *sd;
+ int x, y, mapindex, bg_id;
+
+ bg_id = script_getnum(st,2);
+ map_name = script_getstr(st,3);
+ if( (mapindex = mapindex_name2id(map_name)) == 0 )
+ return 0; // Invalid Map
+
+ x = script_getnum(st,4);
+ y = script_getnum(st,5);
+ nd = npc_name2id(script_getstr(st,6));
+
+ if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL || cd->users <= 0 )
+ return 0;
+
+ if( (sd = cd->usersd[0]) == NULL )
+ return 0;
+
+ if( bg_team_join(bg_id, sd) )
+ {
+ pc_setpos(sd, mapindex, x, y, CLR_TELEPORT);
+ script_pushint(st,1);
+ }
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+BUILDIN_FUNC(bg_team_setxy)
+{
+ struct battleground_data *bg;
+ int bg_id;
+
+ bg_id = script_getnum(st,2);
+ if( (bg = bg_team_search(bg_id)) == NULL )
+ return 0;
+
+ bg->x = script_getnum(st,3);
+ bg->y = script_getnum(st,4);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_warp)
+{
+ int x, y, mapindex, bg_id;
+ const char* map_name;
+
+ bg_id = script_getnum(st,2);
+ map_name = script_getstr(st,3);
+ if( (mapindex = mapindex_name2id(map_name)) == 0 )
+ return 0; // Invalid Map
+ x = script_getnum(st,4);
+ y = script_getnum(st,5);
+ bg_team_warp(bg_id, mapindex, x, y);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_monster)
+{
+ int class_ = 0, x = 0, y = 0, bg_id = 0;
+ const char *str,*map, *evt="";
+
+ bg_id = script_getnum(st,2);
+ map = script_getstr(st,3);
+ x = script_getnum(st,4);
+ y = script_getnum(st,5);
+ str = script_getstr(st,6);
+ class_ = script_getnum(st,7);
+ if( script_hasdata(st,8) ) evt = script_getstr(st,8);
+ check_event(st, evt);
+ script_pushint(st, mob_spawn_bg(map,x,y,str,class_,evt,bg_id));
+ return 0;
+}
+
+BUILDIN_FUNC(bg_monster_set_team)
+{
+ struct mob_data *md;
+ struct block_list *mbl;
+ int id = script_getnum(st,2),
+ bg_id = script_getnum(st,3);
+
+ if( (mbl = map_id2bl(id)) == NULL || mbl->type != BL_MOB )
+ return 0;
+ md = (TBL_MOB *)mbl;
+ md->bg_id = bg_id;
+
+ mob_stop_attack(md);
+ mob_stop_walking(md, 0);
+ md->target_id = md->attacked_id = 0;
+ clif_charnameack(0, &md->bl);
+
+ return 0;
+}
+
+BUILDIN_FUNC(bg_leave)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ if( sd == NULL || !sd->bg_id )
+ return 0;
+
+ bg_team_leave(sd,0);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_destroy)
+{
+ int bg_id = script_getnum(st,2);
+ bg_team_delete(bg_id);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_getareausers)
+{
+ const char *str;
+ int16 m, x0, y0, x1, y1;
+ int bg_id;
+ int i = 0, c = 0;
+ struct battleground_data *bg = NULL;
+ struct map_session_data *sd;
+
+ bg_id = script_getnum(st,2);
+ str = script_getstr(st,3);
+
+ if( (bg = bg_team_search(bg_id)) == NULL || (m = map_mapname2mapid(str)) < 0 )
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ x0 = script_getnum(st,4);
+ y0 = script_getnum(st,5);
+ x1 = script_getnum(st,6);
+ y1 = script_getnum(st,7);
+
+ for( i = 0; i < MAX_BG_MEMBERS; i++ )
+ {
+ if( (sd = bg->members[i].sd) == NULL )
+ continue;
+ if( sd->bl.m != m || sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1 )
+ continue;
+ c++;
+ }
+
+ script_pushint(st,c);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_updatescore)
+{
+ const char *str;
+ int16 m;
+
+ str = script_getstr(st,2);
+ if( (m = map_mapname2mapid(str)) < 0 )
+ return 0;
+
+ map[m].bgscore_lion = script_getnum(st,3);
+ map[m].bgscore_eagle = script_getnum(st,4);
+
+ clif_bg_updatescore(m);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_get_data)
+{
+ struct battleground_data *bg;
+ int bg_id = script_getnum(st,2),
+ type = script_getnum(st,3);
+
+ if( (bg = bg_team_search(bg_id)) == NULL )
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch( type )
+ {
+ case 0: script_pushint(st, bg->count); break;
+ default:
+ ShowError("script:bg_get_data: unknown data identifier %d\n", type);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Instancing Script Commands
+ *------------------------------------------*/
+
+BUILDIN_FUNC(instance_create)
+{
+ const char *name;
+ int party_id, res;
+
+ name = script_getstr(st, 2);
+ party_id = script_getnum(st, 3);
+
+ res = instance_create(party_id, name);
+ if( res == -4 ) // Already exists
+ {
+ script_pushint(st, -1);
+ return 0;
+ }
+ else if( res < 0 )
+ {
+ const char *err;
+ switch(res)
+ {
+ case -3: err = "No free instances"; break;
+ case -2: err = "Invalid party ID"; break;
+ case -1: err = "Invalid type"; break;
+ default: err = "Unknown"; break;
+ }
+ ShowError("buildin_instance_create: %s [%d].\n", err, res);
+ script_pushint(st, -2);
+ return 0;
+ }
+
+ script_pushint(st, res);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_destroy)
+{
+ int instance_id;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ if( script_hasdata(st, 2) )
+ instance_id = script_getnum(st, 2);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ {
+ ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id);
+ return 0;
+ }
+
+ instance_destroy(instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_attachmap)
+{
+ const char *name;
+ int16 m;
+ int instance_id;
+ bool usebasename = false;
+
+ name = script_getstr(st,2);
+ instance_id = script_getnum(st,3);
+ if( script_hasdata(st,4) && script_getnum(st,4) > 0)
+ usebasename = true;
+
+ if( (m = instance_add_map(name, instance_id, usebasename)) < 0 ) // [Saithis]
+ {
+ ShowError("buildin_instance_attachmap: instance creation failed (%s): %d\n", name, m);
+ script_pushconststr(st, "");
+ return 0;
+ }
+ script_pushconststr(st, map[m].name);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_detachmap)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ const char *str;
+ int16 m;
+ int instance_id;
+
+ str = script_getstr(st, 2);
+ if( script_hasdata(st, 3) )
+ instance_id = script_getnum(st, 3);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m,instance_id)) < 0 )
+ {
+ ShowError("buildin_instance_detachmap: Trying to detach invalid map %s\n", str);
+ return 0;
+ }
+
+ instance_del_map(m);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_attach)
+{
+ int instance_id;
+
+ instance_id = script_getnum(st, 2);
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ return 0;
+
+ st->instance_id = instance_id;
+ return 0;
+}
+
+BUILDIN_FUNC(instance_id)
+{
+ int type, instance_id;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ if( script_hasdata(st, 2) )
+ {
+ type = script_getnum(st, 2);
+ if( type == 0 )
+ instance_id = st->instance_id;
+ else if( type == 1 && (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL )
+ instance_id = p->instance_id;
+ else
+ instance_id = 0;
+ }
+ else
+ instance_id = st->instance_id;
+
+ script_pushint(st, instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_set_timeout)
+{
+ int progress_timeout, idle_timeout;
+ int instance_id;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ progress_timeout = script_getnum(st, 2);
+ idle_timeout = script_getnum(st, 3);
+
+ if( script_hasdata(st, 4) )
+ instance_id = script_getnum(st, 4);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( instance_id > 0 )
+ instance_set_timeout(instance_id, progress_timeout, idle_timeout);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_init)
+{
+ int instance_id = script_getnum(st, 2);
+
+ if( instance[instance_id].state != INSTANCE_IDLE )
+ {
+ ShowError("instance_init: instance already initialized.\n");
+ return 0;
+ }
+
+ instance_init(instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_announce)
+{
+ int instance_id = script_getnum(st,2);
+ const char *mes = script_getstr(st,3);
+ int flag = script_getnum(st,4);
+ const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL;
+ int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign
+ int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY
+
+ int i;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ if( instance_id == 0 )
+ {
+ if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+ }
+
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ return 0;
+
+ for( i = 0; i < instance[instance_id].num_map; i++ )
+ map_foreachinmap(buildin_announce_sub, instance[instance_id].map[i], BL_PC,
+ mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_npcname)
+{
+ const char *str;
+ int instance_id = 0;
+
+ struct map_session_data *sd;
+ struct party_data *p;
+ struct npc_data *nd;
+
+ str = script_getstr(st, 2);
+ if( script_hasdata(st, 3) )
+ instance_id = script_getnum(st, 3);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+
+ if( instance_id && (nd = npc_name2id(str)) != NULL )
+ {
+ static char npcname[NAME_LENGTH];
+ snprintf(npcname, sizeof(npcname), "dup_%d_%d", instance_id, nd->bl.id);
+ script_pushconststr(st,npcname);
+ }
+ else
+ {
+ ShowError("script:instance_npcname: invalid instance NPC (instance_id: %d, NPC name: \"%s\".)\n", instance_id, str);
+ st->state = END;
+ return 1;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(has_instance)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ const char *str;
+ int16 m;
+ int instance_id = 0;
+
+ str = script_getstr(st, 2);
+ if( script_hasdata(st, 3) )
+ instance_id = script_getnum(st, 3);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+
+ if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m, instance_id)) < 0 )
+ {
+ script_pushconststr(st, "");
+ return 0;
+ }
+
+ script_pushconststr(st, map[m].name);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_warpall)
+{
+ struct map_session_data *pl_sd;
+ int16 m, i;
+ int instance_id;
+ const char *mapn;
+ int x, y;
+ unsigned short mapindex;
+ struct party_data *p = NULL;
+
+ mapn = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ if( script_hasdata(st,5) )
+ instance_id = script_getnum(st,5);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (pl_sd = script_rid2sd(st)) != NULL && pl_sd->status.party_id && (p = party_search(pl_sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapid2imapid(m, instance_id)) < 0) )
+ return 0;
+
+ if( !(p = party_search(instance[instance_id].party_id)) )
+ return 0;
+
+ mapindex = map_id2index(m);
+ for( i = 0; i < MAX_PARTY; i++ )
+ if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == st->instance_id ) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
+
+ return 0;
+}
+
+/*==========================================
+ * instance_check_party [malufett]
+ * Values:
+ * party_id : Party ID of the invoking character. [Required Parameter]
+ * amount : Amount of needed Partymembers for the Instance. [Optional Parameter]
+ * min : Minimum Level needed to join the Instance. [Optional Parameter]
+ * max : Maxium Level allowed to join the Instance. [Optional Parameter]
+ * Example: instance_check_party (getcharid(1){,amount}{,min}{,max});
+ * Example 2: instance_check_party (getcharid(1),1,1,99);
+ *------------------------------------------*/
+BUILDIN_FUNC(instance_check_party)
+{
+ struct map_session_data *pl_sd;
+ int amount, min, max, i, party_id, c = 0;
+ struct party_data *p = NULL;
+
+ amount = script_hasdata(st,3) ? script_getnum(st,3) : 1; // Amount of needed Partymembers for the Instance.
+ min = script_hasdata(st,4) ? script_getnum(st,4) : 1; // Minimum Level needed to join the Instance.
+ max = script_hasdata(st,5) ? script_getnum(st,5) : MAX_LEVEL; // Maxium Level allowed to join the Instance.
+
+ if( min < 1 || min > MAX_LEVEL){
+ ShowError("instance_check_party: Invalid min level, %d\n", min);
+ return 0;
+ }else if( max < 1 || max > MAX_LEVEL){
+ ShowError("instance_check_party: Invalid max level, %d\n", max);
+ return 0;
+ }
+
+ if( script_hasdata(st,2) )
+ party_id = script_getnum(st,2);
+ else return 0;
+
+ if( !(p = party_search(party_id)) ){
+ script_pushint(st, 0); // Returns false if party does not exist.
+ return 0;
+ }
+
+ for( i = 0; i < MAX_PARTY; i++ )
+ if( (pl_sd = p->data[i].sd) )
+ if(map_id2bl(pl_sd->bl.id)){
+ if(pl_sd->status.base_level < min){
+ script_pushint(st, 0);
+ return 0;
+ }else if(pl_sd->status.base_level > max){
+ script_pushint(st, 0);
+ return 0;
+ }
+ c++;
+ }
+
+ if(c < amount){
+ script_pushint(st, 0); // Not enough Members in the Party to join Instance.
+ }else
+ script_pushint(st, 1);
+
+ return 0;
+}
+
+/*==========================================
+ * Custom Fonts
+ *------------------------------------------*/
+BUILDIN_FUNC(setfont)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int font = script_getnum(st,2);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->user_font != font )
+ sd->user_font = font;
+ else
+ sd->user_font = 0;
+
+ clif_font(sd);
+ return 0;
+}
+
+static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap)
+{
+ TBL_MOB* md = (TBL_MOB*)bl;
+ struct block_list *tbl;
+ int mobid = va_arg(ap,int);
+ uint16 skill_id = va_arg(ap,int);
+ uint16 skill_lv = va_arg(ap,int);
+ int casttime = va_arg(ap,int);
+ int cancel = va_arg(ap,int);
+ int emotion = va_arg(ap,int);
+ int target = va_arg(ap,int);
+
+ if( md->class_ != mobid )
+ return 0;
+
+ // 0:self, 1:target, 2:master, default:random
+ switch( target )
+ {
+ case 0: tbl = map_id2bl(md->bl.id); break;
+ case 1: tbl = map_id2bl(md->target_id); break;
+ case 2: tbl = map_id2bl(md->master_id); break;
+ default:tbl = battle_getenemy(&md->bl, DEFAULT_ENEMY_TYPE(md),skill_get_range2(&md->bl, skill_id, skill_lv)); break;
+ }
+
+ if( !tbl )
+ return 0;
+
+ if( md->ud.skilltimer != INVALID_TIMER ) // Cancel the casting skill.
+ unit_skillcastcancel(bl,0);
+
+ if( skill_get_casttype(skill_id) == CAST_GROUND )
+ unit_skilluse_pos2(&md->bl, tbl->x, tbl->y, skill_id, skill_lv, casttime, cancel);
+ else
+ unit_skilluse_id2(&md->bl, tbl->id, skill_id, skill_lv, casttime, cancel);
+
+ clif_emotion(&md->bl, emotion);
+
+ return 0;
+}
+/*==========================================
+ * areamobuseskill "Map Name",<x>,<y>,<range>,<Mob ID>,"Skill Name"/<Skill ID>,<Skill Lv>,<Cast Time>,<Cancelable>,<Emotion>,<Target Type>;
+ *------------------------------------------*/
+BUILDIN_FUNC(areamobuseskill)
+{
+ struct block_list center;
+ int16 m;
+ int range,mobid,skill_id,skill_lv,casttime,emotion,target,cancel;
+
+ if( (m = map_mapname2mapid(script_getstr(st,2))) < 0 )
+ {
+ ShowError("areamobuseskill: invalid map name.\n");
+ return 0;
+ }
+
+ if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ return 0;
+
+ center.m = m;
+ center.x = script_getnum(st,3);
+ center.y = script_getnum(st,4);
+ range = script_getnum(st,5);
+ mobid = script_getnum(st,6);
+ skill_id = ( script_isstring(st,7) ? skill_name2id(script_getstr(st,7)) : script_getnum(st,7) );
+ if( (skill_lv = script_getnum(st,8)) > battle_config.mob_max_skilllvl )
+ skill_lv = battle_config.mob_max_skilllvl;
+
+ casttime = script_getnum(st,9);
+ cancel = script_getnum(st,10);
+ emotion = script_getnum(st,11);
+ target = script_getnum(st,12);
+
+ map_foreachinrange(buildin_mobuseskill_sub, ¢er, range, BL_MOB, mobid, skill_id, skill_lv, casttime, cancel, emotion, target);
+ return 0;
+}
+
+
+BUILDIN_FUNC(progressbar)
+{
+ struct map_session_data * sd = script_rid2sd(st);
+ const char * color;
+ unsigned int second;
+
+ if( !st || !sd )
+ return 0;
+
+ st->state = STOP;
+
+ color = script_getstr(st,2);
+ second = script_getnum(st,3);
+
+ sd->progressbar.npc_id = st->oid;
+ sd->progressbar.timeout = gettick() + second*1000;
+
+ clif_progressbar(sd, strtol(color, (char **)NULL, 0), second);
+ return 0;
+}
+
+BUILDIN_FUNC(pushpc)
+{
+ uint8 dir;
+ int cells, dx, dy;
+ struct map_session_data* sd;
+
+ if((sd = script_rid2sd(st))==NULL)
+ {
+ return 0;
+ }
+
+ dir = script_getnum(st,2);
+ cells = script_getnum(st,3);
+
+ if(dir>7)
+ {
+ ShowWarning("buildin_pushpc: Invalid direction %d specified.\n", dir);
+ script_reportsrc(st);
+
+ dir%= 8; // trim spin-over
+ }
+
+ if(!cells)
+ {// zero distance
+ return 0;
+ }
+ else if(cells<0)
+ {// pushing backwards
+ dir = (dir+4)%8; // turn around
+ cells = -cells;
+ }
+
+ dx = dirx[dir];
+ dy = diry[dir];
+
+ unit_blown(&sd->bl, dx, dy, cells, 0);
+ return 0;
+}
+
+
+/// Invokes buying store preparation window
+/// buyingstore <slots>;
+BUILDIN_FUNC(buyingstore)
+{
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ buyingstore_setup(sd, script_getnum(st,2));
+ return 0;
+}
+
+
+/// Invokes search store info window
+/// searchstores <uses>,<effect>;
+BUILDIN_FUNC(searchstores)
+{
+ unsigned short effect;
+ unsigned int uses;
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ uses = script_getnum(st,2);
+ effect = script_getnum(st,3);
+
+ if( !uses )
+ {
+ ShowError("buildin_searchstores: Amount of uses cannot be zero.\n");
+ return 1;
+ }
+
+ if( effect > 1 )
+ {
+ ShowError("buildin_searchstores: Invalid effect id %hu, specified.\n", effect);
+ return 1;
+ }
+
+ searchstore_open(sd, uses, effect);
+ return 0;
+}
+/// Displays a number as large digital clock.
+/// showdigit <value>[,<type>];
+BUILDIN_FUNC(showdigit)
+{
+ unsigned int type = 0;
+ int value;
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ value = script_getnum(st,2);
+
+ if( script_hasdata(st,3) )
+ {
+ type = script_getnum(st,3);
+
+ if( type > 3 )
+ {
+ ShowError("buildin_showdigit: Invalid type %u.\n", type);
+ return 1;
+ }
+ }
+
+ 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( pc_isridingdragon(sd) )
+ 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 ( pc_isridingdragon(sd) ) {//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;
+ }
+ }
+ pc_setoption(sd, sd->sc.option|option);
+ script_pushint(st,1);
+ }
+ 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;
+}
+/**
+ * Retrieves quantity of arguments provided to callfunc/callsub.
+ * getargcount() -> amount of arguments received in a function
+ **/
+BUILDIN_FUNC(getargcount) {
+ struct script_retinfo* ri;
+
+ if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO ) {
+ ShowError("script:getargcount: used out of function or callsub label!\n");
+ st->state = END;
+ return 1;
+ }
+ ri = st->stack->stack_data[st->stack->defsp - 1].u.ri;
+
+ script_pushint(st, ri->nargs);
+
+ return 0;
+}
+/**
+ * getcharip(<account ID>/<character ID>/<character name>)
+ **/
+BUILDIN_FUNC(getcharip)
+{
+ struct map_session_data* sd = NULL;
+ int id = 0;
+
+ /* check if a character name is specified */
+ if( script_hasdata(st, 2) )
+ {
+ if (script_isstring(st, 2))
+ sd = map_nick2sd(script_getstr(st, 2));
+ else if (script_isint(st, 2) || script_getnum(st, 2))
+ {
+ id = script_getnum(st, 2);
+ sd = (map_id2sd(id) ? map_id2sd(id) : map_charid2sd(id));
+ }
+ }
+ else
+ sd = script_rid2sd(st);
+
+ /* check for sd and IP */
+ if (!sd || !session[sd->fd]->client_addr)
+ {
+ script_pushconststr(st, "");
+ return 0;
+ }
+
+ /* return the client ip_addr converted for output */
+ if (sd && sd->fd && session[sd->fd])
+ {
+ /* initiliaze */
+ const char *ip_addr = NULL;
+ uint32 ip;
+
+ /* set ip, ip_addr and convert to ip and push str */
+ ip = session[sd->fd]->client_addr;
+ ip_addr = ip2str(ip, NULL);
+ script_pushstrcopy(st, ip_addr);
+ }
+
+ return 0;
+}
+/**
+ * is_function(<function name>) -> 1 if function exists, 0 otherwise
+ **/
+BUILDIN_FUNC(is_function) {
+ const char* str = script_getstr(st,2);
+
+ if( strdb_exists(userfunc_db, str) )
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+/**
+ * get_revision() -> retrieves the current svn revision (if available)
+ **/
+BUILDIN_FUNC(get_revision) {
+ const char * revision;
+
+ if ( (revision = get_svn_revision()) != 0 )
+ script_pushint(st,atoi(revision));
+ else
+ script_pushint(st,-1);//unknown
+
+ return 0;
+}
+/**
+ * freeloop(<toggle>) -> toggles this script instance's looping-check ability
+ **/
+BUILDIN_FUNC(freeloop) {
+
+ if( script_getnum(st,2) )
+ st->freeloop = 1;
+ else
+ st->freeloop = 0;
+
+ script_pushint(st, st->freeloop);
+
+ return 0;
+}
+
+/**
+ * @commands (script based)
+ **/
+BUILDIN_FUNC(bindatcmd) {
+ const char* atcmd;
+ const char* eventName;
+ int i, level = 0, level2 = 0;
+ bool create = false;
+
+ atcmd = script_getstr(st,2);
+ eventName = script_getstr(st,3);
+
+ if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol )
+ atcmd++;
+
+ if( script_hasdata(st,4) ) level = script_getnum(st,4);
+ if( script_hasdata(st,5) ) level2 = script_getnum(st,5);
+
+ if( atcmd_binding_count == 0 ) {
+ CREATE(atcmd_binding,struct atcmd_binding_data*,1);
+
+ create = true;
+ } else {
+ ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command,atcmd) == 0);
+ if( i < atcmd_binding_count ) {/* update existent entry */
+ safestrncpy(atcmd_binding[i]->npc_event, eventName, 50);
+ atcmd_binding[i]->level = level;
+ atcmd_binding[i]->level2 = level2;
+ } else
+ create = true;
+ }
+
+ if( create ) {
+ i = atcmd_binding_count;
+
+ if( atcmd_binding_count++ != 0 )
+ RECREATE(atcmd_binding,struct atcmd_binding_data*,atcmd_binding_count);
+
+ CREATE(atcmd_binding[i],struct atcmd_binding_data,1);
+
+ safestrncpy(atcmd_binding[i]->command, atcmd, 50);
+ safestrncpy(atcmd_binding[i]->npc_event, eventName, 50);
+ atcmd_binding[i]->level = level;
+ atcmd_binding[i]->level2 = level2;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(unbindatcmd) {
+ const char* atcmd;
+ int i = 0;
+
+ atcmd = script_getstr(st, 2);
+
+ if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol )
+ atcmd++;
+
+ if( atcmd_binding_count == 0 ) {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command, atcmd) == 0);
+ if( i < atcmd_binding_count ) {
+ int cursor = 0;
+ aFree(atcmd_binding[i]);
+ atcmd_binding[i] = NULL;
+ /* compact the list now that we freed a slot somewhere */
+ for( i = 0, cursor = 0; i < atcmd_binding_count; i++ ) {
+ if( atcmd_binding[i] == NULL )
+ continue;
+
+ if( cursor != i ) {
+ memmove(&atcmd_binding[cursor], &atcmd_binding[i], sizeof(struct atcmd_binding_data*));
+ }
+
+ cursor++;
+ }
+
+ if( (atcmd_binding_count = cursor) == 0 )
+ aFree(atcmd_binding);
+
+ script_pushint(st, 1);
+ } else
+ script_pushint(st, 0);/* not found */
+
+ return 0;
+}
+
+BUILDIN_FUNC(useatcmd)
+{
+ TBL_PC dummy_sd;
+ TBL_PC* sd;
+ int fd;
+ const char* cmd;
+
+ cmd = script_getstr(st,2);
+
+ if( st->rid )
+ {
+ sd = script_rid2sd(st);
+ fd = sd->fd;
+ }
+ else
+ { // Use a dummy character.
+ sd = &dummy_sd;
+ fd = 0;
+
+ memset(&dummy_sd, 0, sizeof(TBL_PC));
+ if( st->oid )
+ {
+ struct block_list* bl = map_id2bl(st->oid);
+ memcpy(&dummy_sd.bl, bl, sizeof(struct block_list));
+ if( bl->type == BL_NPC )
+ safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH);
+ }
+ }
+
+ // compatibility with previous implementation (deprecated!)
+ if( cmd[0] != atcommand_symbol )
+ {
+ cmd += strlen(sd->status.name);
+ while( *cmd != atcommand_symbol && *cmd != 0 )
+ cmd++;
+ }
+
+ is_atcommand(fd, sd, cmd, 1);
+ return 0;
+}
+
+BUILDIN_FUNC(checkre)
+{
+ int num;
+
+ num=script_getnum(st,2);
+ switch(num){
+ case 0:
+ #ifdef RENEWAL
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 1:
+ #ifdef RENEWAL_CAST
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 2:
+ #ifdef RENEWAL_DROP
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 3:
+ #ifdef RENEWAL_EXP
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 4:
+ #ifdef RENEWAL_LVDMG
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 5:
+ #ifdef RENEWAL_EDP
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 6:
+ #ifdef RENEWAL_ASPD
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ default:
+ ShowWarning("buildin_checkre: unknown parameter.\n");
+ break;
+ }
+ return 0;
+}
+
+/* getrandgroupitem <group_id>,<quantity> */
+BUILDIN_FUNC(getrandgroupitem) {
+ TBL_PC* sd;
+ int i, get_count = 0, flag, nameid, group = script_getnum(st, 2), qty = script_getnum(st,3);
+ struct item item_tmp;
+
+ if( !( sd = script_rid2sd(st) ) )
+ return 0;
+
+ if( qty <= 0 ) {
+ ShowError("getrandgroupitem: qty is <= 0!\n");
+ return 1;
+ }
+ if( (nameid = itemdb_searchrandomid(group)) == UNKNOWN_ITEM_ID ) {
+ return 1;/* itemdb_searchrandomid will already scream a error */
+ }
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = nameid;
+ item_tmp.identify = itemdb_isidentified(nameid);
+
+ //Check if it's stackable.
+ if (!itemdb_isstackable(nameid))
+ get_count = 1;
+ else
+ get_count = qty;
+
+ for (i = 0; i < qty; i += get_count) {
+ // if not pet egg
+ if (!pet_create_egg(sd, nameid)) {
+ if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&item_tmp) )
+ map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* cleanmap <map_name>;
+ * cleanarea <map_name>, <x0>, <y0>, <x1>, <y1>; */
+static int atcommand_cleanfloor_sub(struct block_list *bl, va_list ap)
+{
+ nullpo_ret(bl);
+ map_clearflooritem(bl);
+
+ return 0;
+}
+
+BUILDIN_FUNC(cleanmap)
+{
+ const char *map;
+ int16 m = -1;
+ int16 x0 = 0, y0 = 0, x1 = 0, y1 = 0;
+
+ map = script_getstr(st, 2);
+ m = map_mapname2mapid(map);
+ if (!m)
+ return 1;
+
+ if ((script_lastdata(st) - 2) < 4) {
+ map_foreachinmap(atcommand_cleanfloor_sub, m, BL_ITEM);
+ } else {
+ x0 = script_getnum(st, 3);
+ y0 = script_getnum(st, 4);
+ x1 = script_getnum(st, 5);
+ y1 = script_getnum(st, 6);
+ if (x0 > 0 && y0 > 0 && x1 > 0 && y1 > 0) {
+ map_foreachinarea(atcommand_cleanfloor_sub, m, x0, y0, x1, y1, BL_ITEM);
+ } else {
+ ShowError("cleanarea: invalid coordinate defined!\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+/* Cast a skill on the attached player.
+ * npcskill <skill id>, <skill lvl>, <stat point>, <NPC level>;
+ * npcskill "<skill name>", <skill lvl>, <stat point>, <NPC level>; */
+BUILDIN_FUNC(npcskill)
+{
+ uint16 skill_id;
+ unsigned short skill_level;
+ unsigned int stat_point;
+ unsigned int npc_level;
+ struct npc_data *nd;
+ struct map_session_data *sd;
+
+ skill_id = script_isstring(st, 2) ? skill_name2id(script_getstr(st, 2)) : script_getnum(st, 2);
+ skill_level = script_getnum(st, 3);
+ stat_point = script_getnum(st, 4);
+ npc_level = script_getnum(st, 5);
+ sd = script_rid2sd(st);
+ nd = (struct npc_data *)map_id2bl(sd->npc_id);
+
+ if (stat_point > battle_config.max_third_parameter) {
+ ShowError("npcskill: stat point exceeded maximum of %d.\n",battle_config.max_third_parameter );
+ return 1;
+ }
+ if (npc_level > MAX_LEVEL) {
+ ShowError("npcskill: level exceeded maximum of %d.\n", MAX_LEVEL);
+ return 1;
+ }
+ if (sd == NULL || nd == NULL) { //ain't possible, but I don't trust people.
+ return 1;
+ }
+
+ nd->level = npc_level;
+ nd->stat_point = stat_point;
+
+ if (!nd->status.hp) {
+ status_calc_npc(nd, true);
+ } else {
+ status_calc_npc(nd, false);
+ }
+
+ if (skill_get_inf(skill_id)&INF_GROUND_SKILL) {
+ unit_skilluse_pos(&nd->bl, sd->bl.x, sd->bl.y, skill_id, skill_level);
+ } else {
+ unit_skilluse_id(&nd->bl, sd->bl.id, skill_id, skill_level);
+ }
+
+ return 0;
+}
+
+// declarations that were supposed to be exported from npc_chat.c
+#ifdef PCRE_SUPPORT
+BUILDIN_FUNC(defpattern);
+BUILDIN_FUNC(activatepset);
+BUILDIN_FUNC(deactivatepset);
+BUILDIN_FUNC(deletepset);
+#endif
+
+/// script command definitions
+/// for an explanation on args, see add_buildin_func
+struct script_function buildin_func[] = {
+ // NPC interaction
+ BUILDIN_DEF(mes,"s*"),
+ BUILDIN_DEF(next,""),
+ BUILDIN_DEF(close,""),
+ BUILDIN_DEF(close2,""),
+ BUILDIN_DEF(menu,"sl*"),
+ BUILDIN_DEF(select,"s*"), //for future jA script compatibility
+ BUILDIN_DEF(prompt,"s*"),
+ //
+ BUILDIN_DEF(goto,"l"),
+ BUILDIN_DEF(callsub,"l*"),
+ BUILDIN_DEF(callfunc,"s*"),
+ BUILDIN_DEF(return,"?"),
+ BUILDIN_DEF(getarg,"i?"),
+ BUILDIN_DEF(jobchange,"i?"),
+ BUILDIN_DEF(jobname,"i"),
+ BUILDIN_DEF(input,"r??"),
+ BUILDIN_DEF(warp,"sii"),
+ BUILDIN_DEF(areawarp,"siiiisii??"),
+ BUILDIN_DEF(warpchar,"siii"), // [LuzZza]
+ BUILDIN_DEF(warpparty,"siii?"), // [Fredzilla] [Paradox924X]
+ BUILDIN_DEF(warpguild,"siii"), // [Fredzilla]
+ BUILDIN_DEF(setlook,"ii"),
+ BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it
+ BUILDIN_DEF(set,"rv"),
+ BUILDIN_DEF(setarray,"rv*"),
+ BUILDIN_DEF(cleararray,"rvi"),
+ BUILDIN_DEF(copyarray,"rri"),
+ BUILDIN_DEF(getarraysize,"r"),
+ BUILDIN_DEF(deletearray,"r?"),
+ BUILDIN_DEF(getelementofarray,"ri"),
+ BUILDIN_DEF(getitem,"vi?"),
+ BUILDIN_DEF(rentitem,"vi"),
+ BUILDIN_DEF(getitem2,"viiiiiiii?"),
+ BUILDIN_DEF(getnameditem,"vv"),
+ BUILDIN_DEF2(grouprandomitem,"groupranditem","i"),
+ BUILDIN_DEF(makeitem,"visii"),
+ BUILDIN_DEF(delitem,"vi?"),
+ BUILDIN_DEF(delitem2,"viiiiiiii?"),
+ BUILDIN_DEF2(enableitemuse,"enable_items",""),
+ BUILDIN_DEF2(disableitemuse,"disable_items",""),
+ BUILDIN_DEF(cutin,"si"),
+ BUILDIN_DEF(viewpoint,"iiiii"),
+ BUILDIN_DEF(heal,"ii"),
+ BUILDIN_DEF(itemheal,"ii"),
+ BUILDIN_DEF(percentheal,"ii"),
+ BUILDIN_DEF(rand,"i?"),
+ BUILDIN_DEF(countitem,"v"),
+ BUILDIN_DEF(countitem2,"viiiiiii"),
+ BUILDIN_DEF(checkweight,"vi*"),
+ BUILDIN_DEF(checkweight2,"rr"),
+ BUILDIN_DEF(readparam,"i?"),
+ BUILDIN_DEF(getcharid,"i?"),
+ BUILDIN_DEF(getnpcid,"i?"),
+ BUILDIN_DEF(getpartyname,"i"),
+ BUILDIN_DEF(getpartymember,"i?"),
+ BUILDIN_DEF(getpartyleader,"i?"),
+ BUILDIN_DEF(getguildname,"i"),
+ BUILDIN_DEF(getguildmaster,"i"),
+ BUILDIN_DEF(getguildmasterid,"i"),
+ BUILDIN_DEF(strcharinfo,"i"),
+ BUILDIN_DEF(strnpcinfo,"i"),
+ BUILDIN_DEF(getequipid,"i"),
+ BUILDIN_DEF(getequipname,"i"),
+ BUILDIN_DEF(getbrokenid,"i"), // [Valaris]
+ BUILDIN_DEF(repair,"i"), // [Valaris]
+ BUILDIN_DEF(repairall,""),
+ BUILDIN_DEF(getequipisequiped,"i"),
+ BUILDIN_DEF(getequipisenableref,"i"),
+ BUILDIN_DEF(getequipisidentify,"i"),
+ BUILDIN_DEF(getequiprefinerycnt,"i"),
+ BUILDIN_DEF(getequipweaponlv,"i"),
+ BUILDIN_DEF(getequippercentrefinery,"i"),
+ BUILDIN_DEF(successrefitem,"i"),
+ BUILDIN_DEF(failedrefitem,"i"),
+ BUILDIN_DEF(downrefitem,"i"),
+ BUILDIN_DEF(statusup,"i"),
+ BUILDIN_DEF(statusup2,"ii"),
+ BUILDIN_DEF(bonus,"iv"),
+ BUILDIN_DEF2(bonus,"bonus2","ivi"),
+ BUILDIN_DEF2(bonus,"bonus3","ivii"),
+ BUILDIN_DEF2(bonus,"bonus4","ivvii"),
+ BUILDIN_DEF2(bonus,"bonus5","ivviii"),
+ BUILDIN_DEF(autobonus,"sii??"),
+ BUILDIN_DEF(autobonus2,"sii??"),
+ BUILDIN_DEF(autobonus3,"siiv?"),
+ BUILDIN_DEF(skill,"vi?"),
+ BUILDIN_DEF(addtoskill,"vi?"), // [Valaris]
+ BUILDIN_DEF(guildskill,"vi"),
+ BUILDIN_DEF(getskilllv,"v"),
+ BUILDIN_DEF(getgdskilllv,"iv"),
+ BUILDIN_DEF(basicskillcheck,""),
+ BUILDIN_DEF(getgmlevel,""),
+ BUILDIN_DEF(getgroupid,""),
+ BUILDIN_DEF(end,""),
+ BUILDIN_DEF(checkoption,"i"),
+ BUILDIN_DEF(setoption,"i?"),
+ BUILDIN_DEF(setcart,"?"),
+ BUILDIN_DEF(checkcart,""),
+ BUILDIN_DEF(setfalcon,"?"),
+ BUILDIN_DEF(checkfalcon,""),
+ BUILDIN_DEF(setriding,"?"),
+ BUILDIN_DEF(checkriding,""),
+ BUILDIN_DEF(checkwug,""),
+ BUILDIN_DEF(checkmadogear,""),
+ BUILDIN_DEF(setmadogear,"?"),
+ BUILDIN_DEF2(savepoint,"save","sii"),
+ BUILDIN_DEF(savepoint,"sii"),
+ BUILDIN_DEF(gettimetick,"i"),
+ BUILDIN_DEF(gettime,"i"),
+ BUILDIN_DEF(gettimestr,"si"),
+ BUILDIN_DEF(openstorage,""),
+ BUILDIN_DEF(guildopenstorage,""),
+ BUILDIN_DEF(itemskill,"vi"),
+ BUILDIN_DEF(produce,"i"),
+ BUILDIN_DEF(cooking,"i"),
+ BUILDIN_DEF(monster,"siisii???"),
+ BUILDIN_DEF(getmobdrops,"i"),
+ BUILDIN_DEF(areamonster,"siiiisii???"),
+ BUILDIN_DEF(killmonster,"ss?"),
+ BUILDIN_DEF(killmonsterall,"s?"),
+ BUILDIN_DEF(clone,"siisi????"),
+ BUILDIN_DEF(doevent,"s"),
+ BUILDIN_DEF(donpcevent,"s"),
+ BUILDIN_DEF(cmdothernpc,"ss"),
+ BUILDIN_DEF(addtimer,"is"),
+ BUILDIN_DEF(deltimer,"s"),
+ BUILDIN_DEF(addtimercount,"si"),
+ BUILDIN_DEF(initnpctimer,"??"),
+ BUILDIN_DEF(stopnpctimer,"??"),
+ BUILDIN_DEF(startnpctimer,"??"),
+ BUILDIN_DEF(setnpctimer,"i?"),
+ BUILDIN_DEF(getnpctimer,"i?"),
+ BUILDIN_DEF(attachnpctimer,"?"), // attached the player id to the npc timer [Celest]
+ BUILDIN_DEF(detachnpctimer,"?"), // detached the player id from the npc timer [Celest]
+ BUILDIN_DEF(playerattached,""), // returns id of the current attached player. [Skotlex]
+ BUILDIN_DEF(announce,"si?????"),
+ BUILDIN_DEF(mapannounce,"ssi?????"),
+ BUILDIN_DEF(areaannounce,"siiiisi?????"),
+ BUILDIN_DEF(getusers,"i"),
+ BUILDIN_DEF(getmapguildusers,"si"),
+ BUILDIN_DEF(getmapusers,"s"),
+ BUILDIN_DEF(getareausers,"siiii"),
+ BUILDIN_DEF(getareadropitem,"siiiiv"),
+ BUILDIN_DEF(enablenpc,"s"),
+ BUILDIN_DEF(disablenpc,"s"),
+ BUILDIN_DEF(hideoffnpc,"s"),
+ BUILDIN_DEF(hideonnpc,"s"),
+ BUILDIN_DEF(sc_start,"iii?"),
+ BUILDIN_DEF(sc_start2,"iiii?"),
+ BUILDIN_DEF(sc_start4,"iiiiii?"),
+ BUILDIN_DEF(sc_end,"i?"),
+ BUILDIN_DEF(getstatus, "i?"),
+ BUILDIN_DEF(getscrate,"ii?"),
+ BUILDIN_DEF(debugmes,"s"),
+ BUILDIN_DEF2(catchpet,"pet","i"),
+ BUILDIN_DEF2(birthpet,"bpet",""),
+ BUILDIN_DEF(resetlvl,"i"),
+ BUILDIN_DEF(resetstatus,""),
+ BUILDIN_DEF(resetskill,""),
+ BUILDIN_DEF(skillpointcount,""),
+ BUILDIN_DEF(changebase,"i?"),
+ BUILDIN_DEF(changesex,""),
+ BUILDIN_DEF(waitingroom,"si?????"),
+ BUILDIN_DEF(delwaitingroom,"?"),
+ BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"),
+ BUILDIN_DEF(enablewaitingroomevent,"?"),
+ BUILDIN_DEF(disablewaitingroomevent,"?"),
+ BUILDIN_DEF2(enablewaitingroomevent,"enablearena",""), // Added by RoVeRT
+ BUILDIN_DEF2(disablewaitingroomevent,"disablearena",""), // Added by RoVeRT
+ BUILDIN_DEF(getwaitingroomstate,"i?"),
+ BUILDIN_DEF(warpwaitingpc,"sii?"),
+ BUILDIN_DEF(attachrid,"i"),
+ BUILDIN_DEF(detachrid,""),
+ BUILDIN_DEF(isloggedin,"i?"),
+ BUILDIN_DEF(setmapflagnosave,"ssii"),
+ BUILDIN_DEF(getmapflag,"si"),
+ BUILDIN_DEF(setmapflag,"si?"),
+ BUILDIN_DEF(removemapflag,"si?"),
+ BUILDIN_DEF(pvpon,"s"),
+ BUILDIN_DEF(pvpoff,"s"),
+ BUILDIN_DEF(gvgon,"s"),
+ BUILDIN_DEF(gvgoff,"s"),
+ BUILDIN_DEF(emotion,"i??"),
+ BUILDIN_DEF(maprespawnguildid,"sii"),
+ BUILDIN_DEF(agitstart,""), // <Agit>
+ BUILDIN_DEF(agitend,""),
+ BUILDIN_DEF(agitcheck,""), // <Agitcheck>
+ BUILDIN_DEF(flagemblem,"i"), // Flag Emblem
+ BUILDIN_DEF(getcastlename,"s"),
+ BUILDIN_DEF(getcastledata,"si"),
+ BUILDIN_DEF(setcastledata,"sii"),
+ BUILDIN_DEF(requestguildinfo,"i?"),
+ BUILDIN_DEF(getequipcardcnt,"i"),
+ BUILDIN_DEF(successremovecards,"i"),
+ BUILDIN_DEF(failedremovecards,"ii"),
+ BUILDIN_DEF(marriage,"s"),
+ BUILDIN_DEF2(wedding_effect,"wedding",""),
+ BUILDIN_DEF(divorce,""),
+ BUILDIN_DEF(ispartneron,""),
+ BUILDIN_DEF(getpartnerid,""),
+ BUILDIN_DEF(getchildid,""),
+ BUILDIN_DEF(getmotherid,""),
+ BUILDIN_DEF(getfatherid,""),
+ BUILDIN_DEF(warppartner,"sii"),
+ BUILDIN_DEF(getitemname,"v"),
+ BUILDIN_DEF(getitemslots,"i"),
+ BUILDIN_DEF(makepet,"i"),
+ BUILDIN_DEF(getexp,"ii"),
+ BUILDIN_DEF(getinventorylist,""),
+ BUILDIN_DEF(getskilllist,""),
+ BUILDIN_DEF(clearitem,""),
+ BUILDIN_DEF(classchange,"ii"),
+ BUILDIN_DEF(misceffect,"i"),
+ BUILDIN_DEF(playBGM,"s"),
+ BUILDIN_DEF(playBGMall,"s?????"),
+ BUILDIN_DEF(soundeffect,"si"),
+ BUILDIN_DEF(soundeffectall,"si?????"), // SoundEffectAll [Codemaster]
+ BUILDIN_DEF(strmobinfo,"ii"), // display mob data [Valaris]
+ BUILDIN_DEF(guardian,"siisi??"), // summon guardians
+ BUILDIN_DEF(guardianinfo,"sii"), // display guardian data [Valaris]
+ BUILDIN_DEF(petskillbonus,"iiii"), // [Valaris]
+ BUILDIN_DEF(petrecovery,"ii"), // [Valaris]
+ BUILDIN_DEF(petloot,"i"), // [Valaris]
+ BUILDIN_DEF(petheal,"iiii"), // [Valaris]
+ BUILDIN_DEF(petskillattack,"viii"), // [Skotlex]
+ BUILDIN_DEF(petskillattack2,"viiii"), // [Valaris]
+ BUILDIN_DEF(petskillsupport,"viiii"), // [Skotlex]
+ BUILDIN_DEF(skilleffect,"vi"), // skill effect [Celest]
+ BUILDIN_DEF(npcskilleffect,"viii"), // npc skill effect [Valaris]
+ BUILDIN_DEF(specialeffect,"i??"), // npc skill effect [Valaris]
+ BUILDIN_DEF(specialeffect2,"i??"), // skill effect on players[Valaris]
+ BUILDIN_DEF(nude,""), // nude command [Valaris]
+ BUILDIN_DEF(mapwarp,"ssii??"), // Added by RoVeRT
+ BUILDIN_DEF(atcommand,"s"), // [MouseJstr]
+ BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr]
+ BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr]
+ BUILDIN_DEF(message,"ss"), // [MouseJstr]
+ BUILDIN_DEF(npctalk,"s"), // [Valaris]
+ BUILDIN_DEF(mobcount,"ss"),
+ BUILDIN_DEF(getlook,"i"),
+ BUILDIN_DEF(getsavepoint,"i"),
+ BUILDIN_DEF(npcspeed,"i"), // [Valaris]
+ BUILDIN_DEF(npcwalkto,"ii"), // [Valaris]
+ BUILDIN_DEF(npcstop,""), // [Valaris]
+ BUILDIN_DEF(getmapxy,"rrri?"), //by Lorky [Lupus]
+ BUILDIN_DEF(checkoption1,"i"),
+ BUILDIN_DEF(checkoption2,"i"),
+ BUILDIN_DEF(guildgetexp,"i"),
+ BUILDIN_DEF(guildchangegm,"is"),
+ BUILDIN_DEF(logmes,"s"), //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus]
+ BUILDIN_DEF(summon,"si??"), // summons a slave monster [Celest]
+ BUILDIN_DEF(isnight,""), // check whether it is night time [Celest]
+ BUILDIN_DEF(isday,""), // check whether it is day time [Celest]
+ BUILDIN_DEF(isequipped,"i*"), // check whether another item/card has been equipped [Celest]
+ BUILDIN_DEF(isequippedcnt,"i*"), // check how many items/cards are being equipped [Celest]
+ BUILDIN_DEF(cardscnt,"i*"), // check how many items/cards are being equipped in the same arm [Lupus]
+ BUILDIN_DEF(getrefine,""), // returns the refined number of the current item, or an item with index specified [celest]
+ BUILDIN_DEF(night,""), // sets the server to night time
+ BUILDIN_DEF(day,""), // sets the server to day time
+#ifdef PCRE_SUPPORT
+ BUILDIN_DEF(defpattern,"iss"), // Define pattern to listen for [MouseJstr]
+ BUILDIN_DEF(activatepset,"i"), // Activate a pattern set [MouseJstr]
+ BUILDIN_DEF(deactivatepset,"i"), // Deactive a pattern set [MouseJstr]
+ BUILDIN_DEF(deletepset,"i"), // Delete a pattern set [MouseJstr]
+#endif
+ BUILDIN_DEF(dispbottom,"s"), //added from jA [Lupus]
+ BUILDIN_DEF(getusersname,""),
+ BUILDIN_DEF(recovery,""),
+ BUILDIN_DEF(getpetinfo,"i"),
+ BUILDIN_DEF(gethominfo,"i"),
+ BUILDIN_DEF(getmercinfo,"i?"),
+ BUILDIN_DEF(checkequipedcard,"i"),
+ BUILDIN_DEF(jump_zero,"il"), //for future jA script compatibility
+ BUILDIN_DEF(globalmes,"s?"), //end jA addition
+ 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
+ BUILDIN_DEF(setiteminfo,"iii"), //[Lupus] set Items Buy / sell Price, etc info
+ BUILDIN_DEF(getequipcardid,"ii"), //[Lupus] returns CARD ID or other info from CARD slot N of equipped item
+ // [zBuffer] List of mathematics commands --->
+ BUILDIN_DEF(sqrt,"i"),
+ BUILDIN_DEF(pow,"ii"),
+ BUILDIN_DEF(distance,"iiii"),
+ // <--- [zBuffer] List of mathematics commands
+ BUILDIN_DEF(md5,"s"),
+ // [zBuffer] List of dynamic var commands --->
+ BUILDIN_DEF(getd,"s"),
+ BUILDIN_DEF(setd,"sv"),
+ // <--- [zBuffer] List of dynamic var commands
+ BUILDIN_DEF(petstat,"i"),
+ BUILDIN_DEF(callshop,"s?"), // [Skotlex]
+ BUILDIN_DEF(npcshopitem,"sii*"), // [Lance]
+ BUILDIN_DEF(npcshopadditem,"sii*"),
+ BUILDIN_DEF(npcshopdelitem,"si*"),
+ BUILDIN_DEF(npcshopattach,"s?"),
+ BUILDIN_DEF(equip,"i"),
+ BUILDIN_DEF(autoequip,"ii"),
+ BUILDIN_DEF(setbattleflag,"si"),
+ BUILDIN_DEF(getbattleflag,"s"),
+ BUILDIN_DEF(setitemscript,"is?"), //Set NEW item bonus script. Lupus
+ BUILDIN_DEF(disguise,"i"), //disguise player. Lupus
+ BUILDIN_DEF(undisguise,""), //undisguise player. Lupus
+ BUILDIN_DEF(getmonsterinfo,"ii"), //Lupus
+ BUILDIN_DEF(axtoi,"s"),
+ BUILDIN_DEF(query_sql,"s*"),
+ BUILDIN_DEF(query_logsql,"s*"),
+ BUILDIN_DEF(escape_sql,"v"),
+ BUILDIN_DEF(atoi,"s"),
+ // [zBuffer] List of player cont commands --->
+ BUILDIN_DEF(rid2name,"i"),
+ BUILDIN_DEF(pcfollow,"ii"),
+ BUILDIN_DEF(pcstopfollow,"i"),
+ BUILDIN_DEF(pcblockmove,"ii"),
+ // <--- [zBuffer] List of player cont commands
+ // [zBuffer] List of mob control commands --->
+ BUILDIN_DEF(unitwalk,"ii?"),
+ BUILDIN_DEF(unitkill,"i"),
+ BUILDIN_DEF(unitwarp,"isii"),
+ BUILDIN_DEF(unitattack,"iv?"),
+ BUILDIN_DEF(unitstop,"i"),
+ BUILDIN_DEF(unittalk,"is"),
+ BUILDIN_DEF(unitemote,"ii"),
+ BUILDIN_DEF(unitskilluseid,"ivi?"), // originally by Qamera [Celest]
+ BUILDIN_DEF(unitskillusepos,"iviii"), // [Celest]
+// <--- [zBuffer] List of mob control commands
+ BUILDIN_DEF(sleep,"i"),
+ BUILDIN_DEF(sleep2,"i"),
+ BUILDIN_DEF(awake,"s"),
+ BUILDIN_DEF(getvariableofnpc,"rs"),
+ BUILDIN_DEF(warpportal,"iisii"),
+ BUILDIN_DEF2(homunculus_evolution,"homevolution",""), //[orn]
+ BUILDIN_DEF2(homunculus_mutate,"hommutate","?"),
+ BUILDIN_DEF2(homunculus_shuffle,"homshuffle",""), //[Zephyrus]
+ BUILDIN_DEF(eaclass,"?"), //[Skotlex]
+ BUILDIN_DEF(roclass,"i?"), //[Skotlex]
+ BUILDIN_DEF(checkvending,"?"),
+ BUILDIN_DEF(checkchatting,"?"),
+ BUILDIN_DEF(openmail,""),
+ BUILDIN_DEF(openauction,""),
+ BUILDIN_DEF(checkcell,"siii"),
+ BUILDIN_DEF(setcell,"siiiiii"),
+ BUILDIN_DEF(setwall,"siiiiis"),
+ BUILDIN_DEF(delwall,"s"),
+ BUILDIN_DEF(searchitem,"rs"),
+ BUILDIN_DEF(mercenary_create,"ii"),
+ BUILDIN_DEF(mercenary_heal,"ii"),
+ BUILDIN_DEF(mercenary_sc_start,"iii"),
+ BUILDIN_DEF(mercenary_get_calls,"i"),
+ BUILDIN_DEF(mercenary_get_faith,"i"),
+ BUILDIN_DEF(mercenary_set_calls,"ii"),
+ BUILDIN_DEF(mercenary_set_faith,"ii"),
+ BUILDIN_DEF(readbook,"ii"),
+ BUILDIN_DEF(setfont,"i"),
+ BUILDIN_DEF(areamobuseskill,"siiiiviiiii"),
+ BUILDIN_DEF(progressbar,"si"),
+ BUILDIN_DEF(pushpc,"ii"),
+ BUILDIN_DEF(buyingstore,"i"),
+ BUILDIN_DEF(searchstores,"ii"),
+ BUILDIN_DEF(showdigit,"i?"),
+ // WoE SE
+ BUILDIN_DEF(agitstart2,""),
+ BUILDIN_DEF(agitend2,""),
+ BUILDIN_DEF(agitcheck2,""),
+ // BattleGround
+ BUILDIN_DEF(waitingroom2bg,"siiss?"),
+ BUILDIN_DEF(waitingroom2bg_single,"isiis"),
+ BUILDIN_DEF(bg_team_setxy,"iii"),
+ BUILDIN_DEF(bg_warp,"isii"),
+ BUILDIN_DEF(bg_monster,"isiisi?"),
+ BUILDIN_DEF(bg_monster_set_team,"ii"),
+ BUILDIN_DEF(bg_leave,""),
+ BUILDIN_DEF(bg_destroy,"i"),
+ BUILDIN_DEF(areapercentheal,"siiiiii"),
+ BUILDIN_DEF(bg_get_data,"ii"),
+ BUILDIN_DEF(bg_getareausers,"isiiii"),
+ BUILDIN_DEF(bg_updatescore,"sii"),
+
+ // Instancing
+ BUILDIN_DEF(instance_create,"si"),
+ BUILDIN_DEF(instance_destroy,"?"),
+ BUILDIN_DEF(instance_attachmap,"si?"),
+ BUILDIN_DEF(instance_detachmap,"s?"),
+ BUILDIN_DEF(instance_attach,"i"),
+ BUILDIN_DEF(instance_id,"?"),
+ BUILDIN_DEF(instance_set_timeout,"ii?"),
+ BUILDIN_DEF(instance_init,"i"),
+ BUILDIN_DEF(instance_announce,"isi?????"),
+ BUILDIN_DEF(instance_npcname,"s?"),
+ BUILDIN_DEF(has_instance,"s?"),
+ BUILDIN_DEF(instance_warpall,"sii?"),
+ BUILDIN_DEF(instance_check_party,"i???"),
+ /**
+ * 3rd-related
+ **/
+ BUILDIN_DEF(makerune,"i"),
+ BUILDIN_DEF(checkdragon,""),//[Ind]
+ BUILDIN_DEF(setdragon,"?"),//[Ind]
+ BUILDIN_DEF(ismounting,""),//[Ind]
+ BUILDIN_DEF(setmounting,""),//[Ind]
+ BUILDIN_DEF(checkre,"i"),
+ /**
+ * rAthena and beyond!
+ **/
+ BUILDIN_DEF(getargcount,""),
+ BUILDIN_DEF(getcharip,"?"),
+ BUILDIN_DEF(is_function,"s"),
+ BUILDIN_DEF(get_revision,""),
+ BUILDIN_DEF(freeloop,"i"),
+ BUILDIN_DEF(getrandgroupitem,"ii"),
+ BUILDIN_DEF(cleanmap,"s"),
+ BUILDIN_DEF2(cleanmap,"cleanarea","siiii"),
+ BUILDIN_DEF(npcskill,"viii"),
+ /**
+ * @commands (script based)
+ **/
+ BUILDIN_DEF(bindatcmd, "ss??"),
+ BUILDIN_DEF(unbindatcmd, "s"),
+ BUILDIN_DEF(useatcmd, "s"),
+
+ //Quest Log System [Inkfish]
+ BUILDIN_DEF(setquest, "i"),
+ BUILDIN_DEF(erasequest, "i"),
+ BUILDIN_DEF(completequest, "i"),
+ BUILDIN_DEF(checkquest, "i?"),
+ BUILDIN_DEF(changequest, "ii"),
+ BUILDIN_DEF(showevent, "ii"),
+ {NULL,NULL,NULL},
+};
diff --git a/src/map/skill.c b/src/map/skill.c index 757165107..3b3ac547d 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -1,17983 +1,17998 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include "../common/cbasetypes.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/random.h" -#include "../common/showmsg.h" -#include "../common/strlib.h" -#include "../common/utils.h" -#include "../common/ers.h" - -#include "map.h" -#include "path.h" -#include "clif.h" -#include "pc.h" -#include "status.h" -#include "skill.h" -#include "pet.h" -#include "homunculus.h" -#include "mercenary.h" -#include "elemental.h" -#include "mob.h" -#include "npc.h" -#include "battle.h" -#include "battleground.h" -#include "party.h" -#include "itemdb.h" -#include "script.h" -#include "intif.h" -#include "log.h" -#include "chrif.h" -#include "guild.h" -#include "date.h" -#include "unit.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <math.h> - - -#define SKILLUNITTIMER_INTERVAL 100 - -// ranges reserved for mapping skill ids to skilldb offsets -#define HM_SKILLRANGEMIN 700 -#define HM_SKILLRANGEMAX HM_SKILLRANGEMIN + MAX_HOMUNSKILL -#define MC_SKILLRANGEMIN HM_SKILLRANGEMAX + 1 -#define MC_SKILLRANGEMAX MC_SKILLRANGEMIN + MAX_MERCSKILL -#define EL_SKILLRANGEMIN MC_SKILLRANGEMAX + 1 -#define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL -#define GD_SKILLRANGEMIN EL_SKILLRANGEMAX + 1 -#define GD_SKILLRANGEMAX GD_SKILLRANGEMIN + MAX_GUILDSKILL - -#if GD_SKILLRANGEMAX > 999 - #error GD_SKILLRANGEMAX is greater than 999 -#endif -static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex] -static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex] - -DBMap* skillunit_db = NULL; // int id -> struct skill_unit* - -DBMap* skilldb_name2id = NULL; - -/** - * Skill Cool Down Delay Saving - * Struct skill_cd is not a member of struct map_session_data - * to keep cooldowns in memory between player log-ins. - * All cooldowns are reset when server is restarted. - **/ -DBMap* skillcd_db = NULL; // char_id -> struct skill_cd -struct skill_cd { - int duration[MAX_SKILL_TREE];//milliseconds - short skidx[MAX_SKILL_TREE];//the skill index entries belong to - short nameid[MAX_SKILL_TREE];//skill id - unsigned char cursor; -}; - -/** - * Skill Unit Persistency during endack routes (mostly for songs see bugreport:4574) - **/ -DBMap* skillusave_db = NULL; // char_id -> struct skill_usave -struct skill_usave { - uint16 skill_id, skill_lv; -}; - -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]; -struct s_skill_improvise_db { - uint16 skill_id; - short per;//1-10000 -}; -struct s_skill_improvise_db skill_improvise_db[MAX_SKILL_IMPROVISE_DB]; -bool skill_reproduce_db[MAX_SKILL_DB]; -struct s_skill_changematerial_db { - int itemid; - short rate; - int qty[5]; - short qty_rate[5]; -}; -struct s_skill_changematerial_db skill_changematerial_db[MAX_SKILL_PRODUCE_DB]; - -//Warlock -struct s_skill_spellbook_db { - int nameid; - uint16 skill_id; - int point; -}; - -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_block_check(struct block_list *bl, enum sc_type type, uint16 skill_id); -static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv); -static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv); -static int skill_destroy_trap( struct block_list *bl, va_list ap ); -//Since only mob-casted splash skills can hit ice-walls -static inline int splash_target(struct block_list* bl) -{ -#ifndef RENEWAL - return ( bl->type == BL_MOB ) ? BL_SKILL|BL_CHAR : BL_CHAR; -#else // Some skills can now hit ground skills(traps, ice wall & etc.) - return BL_SKILL|BL_CHAR; -#endif -} - -/// Returns the id of the skill, or 0 if not found. -int skill_name2id(const char* name) -{ - if( name == NULL ) - return 0; - - return strdb_iget(skilldb_name2id, name); -} - -/// Maps skill ids to skill db offsets. -/// Returns the skill's array index, or 0 (Unknown Skill). -int skill_get_index( uint16 skill_id ) -{ - // avoid ranges reserved for mapping guild/homun/mercenary skills - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) - return 0; - - // map skill id to skill db index - if( skill_id >= GD_SKILLBASE ) - skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE; - else if( skill_id >= EL_SKILLBASE ) - skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE; - else if( skill_id >= MC_SKILLBASE ) - skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE; - else if( skill_id >= HM_SKILLBASE ) - skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE; - - // validate result - if( !skill_id || skill_id >= MAX_SKILL_DB ) - return 0; - - return skill_id; -} - -const char* skill_get_name( uint16 skill_id ) -{ - return skill_db[skill_get_index(skill_id)].name; -} - -const char* skill_get_desc( uint16 skill_id ) -{ - return skill_db[skill_get_index(skill_id)].desc; -} - -// out of bounds error checking [celest] -static void skill_chk(int16* skill_id, uint16 skill_lv) -{ - *skill_id = skill_get_index(*skill_id); // checks/adjusts id - if( skill_lv > MAX_SKILL_LEVEL ) *skill_id = 0; -} - -#define skill_get(var,id,lv) { skill_chk(&id,lv); if(!id) return 0; return var; } - -// Skill DB -int skill_get_hit( uint16 skill_id ) { skill_get (skill_db[skill_id].hit, skill_id, 1); } -int skill_get_inf( uint16 skill_id ) { skill_get (skill_db[skill_id].inf, skill_id, 1); } -int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].element[skill_lv-1], skill_id, skill_lv); } -int skill_get_nk( uint16 skill_id ) { skill_get (skill_db[skill_id].nk, skill_id, 1); } -int skill_get_max( uint16 skill_id ) { skill_get (skill_db[skill_id].max, skill_id, 1); } -int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].range[skill_lv-1], skill_id, skill_lv); } -int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { skill_get ( (skill_db[skill_id].splash[skill_lv-1]>=0?skill_db[skill_id].splash[skill_lv-1]:AREA_SIZE), skill_id, skill_lv); } -int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].hp[skill_lv-1], skill_id, skill_lv); } -int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].sp[skill_lv-1], skill_id, skill_lv); } -int skill_get_hp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].hp_rate[skill_lv-1], skill_id, skill_lv); } -int skill_get_sp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].sp_rate[skill_lv-1], skill_id, skill_lv); } -int skill_get_state(uint16 skill_id) { skill_get (skill_db[skill_id].state, skill_id, 1); } -int skill_get_spiritball(uint16 skill_id, uint16 skill_lv) { skill_get (skill_db[skill_id].spiritball[skill_lv-1], skill_id, skill_lv); } -int skill_get_itemid(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].itemid[idx], skill_id, 1); } -int skill_get_itemqty(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].amount[idx], skill_id, 1); } -int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].zeny[skill_lv-1], skill_id, skill_lv); } -int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].num[skill_lv-1], skill_id, skill_lv); } -int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].cast[skill_lv-1], skill_id, skill_lv); } -int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].delay[skill_lv-1], skill_id, skill_lv); } -int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].walkdelay[skill_lv-1], skill_id, skill_lv); } -int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time[skill_lv-1], skill_id, skill_lv); } -int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time2[skill_lv-1], skill_id, skill_lv); } -int skill_get_castdef( uint16 skill_id ) { skill_get (skill_db[skill_id].cast_def_rate, skill_id, 1); } -int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_db[skill_id].weapon, skill_id, 1); } -int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_db[skill_id].ammo, skill_id, 1); } -int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].ammo_qty[skill_lv-1], skill_id, skill_lv); } -int skill_get_inf2( uint16 skill_id ) { skill_get (skill_db[skill_id].inf2, skill_id, 1); } -int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_db[skill_id].castcancel, skill_id, 1); } -int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].maxcount[skill_lv-1], skill_id, skill_lv); } -int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].blewcount[skill_lv-1], skill_id, skill_lv); } -int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].mhp[skill_lv-1], skill_id, skill_lv); } -int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].castnodex[skill_lv-1], skill_id, skill_lv); } -int skill_get_delaynodex( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].delaynodex[skill_lv-1], skill_id, skill_lv); } -int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_db[skill_id].nocast, skill_id, 1); } -int skill_get_type( uint16 skill_id ) { skill_get (skill_db[skill_id].skill_type, skill_id, 1); } -int skill_get_unit_id ( uint16 skill_id, int flag ){ skill_get (skill_db[skill_id].unit_id[flag], skill_id, 1); } -int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_interval, skill_id, 1); } -int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_range[skill_lv-1], skill_id, skill_lv); } -int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BCT_ALL, skill_id, 1); } -int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BL_ALL, skill_id, 1); } -int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_flag, skill_id, 1); } -int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_layout_type[skill_lv-1], skill_id, skill_lv); } -int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].cooldown[skill_lv-1], skill_id, skill_lv); } -#ifdef RENEWAL_CAST -int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].fixed_cast[skill_lv-1], skill_id, skill_lv); } -#endif -int skill_tree_get_max(uint16 skill_id, int b_class) -{ - int i; - b_class = pc_class2idx(b_class); - - ARR_FIND( 0, MAX_SKILL_TREE, i, skill_tree[b_class][i].id == 0 || skill_tree[b_class][i].id == skill_id ); - if( i < MAX_SKILL_TREE && skill_tree[b_class][i].id == skill_id ) - return skill_tree[b_class][i].max; - else - return skill_get_max(skill_id); -} - -int skill_frostjoke_scream(struct block_list *bl,va_list ap); -int skill_attack_area(struct block_list *bl,va_list ap); -struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex] -int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris] -int skill_greed(struct block_list *bl, va_list ap); -static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id); -static int skill_cell_overlap(struct block_list *bl, va_list ap); -static int skill_trap_splash(struct block_list *bl, va_list ap); -struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick); -static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick); -static int skill_unit_onleft(uint16 skill_id, struct block_list *bl,unsigned int tick); -static int skill_unit_effect(struct block_list *bl,va_list ap); - -int enchant_eff[5] = { 10, 14, 17, 19, 20 }; -int deluge_eff[5] = { 5, 9, 12, 14, 15 }; - -int skill_get_casttype (uint16 skill_id) -{ - int inf = skill_get_inf(skill_id); - if (inf&(INF_GROUND_SKILL)) - return CAST_GROUND; - if (inf&INF_SUPPORT_SKILL) - return CAST_NODAMAGE; - if (inf&INF_SELF_SKILL) { - if(skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF) - return CAST_DAMAGE; //Combo skill. - return CAST_NODAMAGE; - } - if (skill_get_nk(skill_id)&NK_NO_DAMAGE) - return CAST_NODAMAGE; - return CAST_DAMAGE; -} - -//Returns actual skill range taking into account attack range and AC_OWL [Skotlex] -int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv) -{ - int range; - if( bl->type == BL_MOB && battle_config.mob_ai&0x400 ) - return 9; //Mobs have a range of 9 regardless of skill used. - - range = skill_get_range(skill_id, skill_lv); - - if( range < 0 ) - { - if( battle_config.use_weapon_skill_range&bl->type ) - return status_get_range(bl); - range *=-1; - } - - //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE - switch( skill_id ) - { - case AC_SHOWER: case MA_SHOWER: - case AC_DOUBLE: case MA_DOUBLE: - case HT_BLITZBEAT: - case AC_CHARGEARROW: - 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 - range += 10; //Assume level 10? - break; - // added to allow GS skills to be effected by the range of Snake Eyes [Reddozen] - case GS_RAPIDSHOWER: - case GS_PIERCINGSHOT: - case GS_FULLBUSTER: - case GS_SPREADATTACK: - case GS_GROUNDDRIFT: - if (bl->type == BL_PC) - range += pc_checkskill((TBL_PC*)bl, GS_SNAKEEYE); - else - range += 10; //Assume level 10? - break; - case NJ_KIRIKAGE: - 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 ) - return 9; // Enable non players to use self skills on others. [Skotlex] - return range; -} - -int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal) { - 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; - - switch( skill_id ) { - case BA_APPLEIDUN: - #ifdef RENEWAL - hp = 100+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery - #else - hp = 30+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery - #endif - if( sd ) - hp += 5*pc_checkskill(sd,BA_MUSICALLESSON); - break; - case PR_SANCTUARY: - hp = (skill_lv>6)?777:skill_lv*100; - break; - case NPC_EVILLAND: - hp = (skill_lv>6)?666:skill_lv*100; - break; - default: - if (skill_lv >= battle_config.max_heal_lv) - return battle_config.max_heal; - #ifdef RENEWAL - /** - * Renewal Heal Formula - * Formula: ( [(Base Level + INT) / 5] × 30 ) × (Heal Level / 10) × (Modifiers) + MATK - **/ - hp = (status_get_lv(src) + status_get_int(src)) / 5 * 30 * skill_lv / 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) ) - hp += hp * skill * 2 / 100; - else if( src->type == BL_HOM && (skill = merc_hom_checkskill(((TBL_HOM*)src), HLIF_BRAIN)) > 0 ) - hp += hp * skill * 2 / 100; - break; - } - - if( ( (target && target->type == BL_MER) || !heal ) && skill_id != NPC_EVILLAND ) - hp >>= 1; - - if( sd && (skill = pc_skillheal_bonus(sd, skill_id)) ) - hp += hp*skill/100; - - if( tsd && (skill = pc_skillheal2_bonus(tsd, skill_id)) ) - 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] - hp -= hp * sc->data[SC_CRITICALWOUND]->val2/100; - if( sc->data[SC_DEATHHURT] && heal ) - hp -= hp * 20/100; - if( sc->data[SC_INCHEALRATE] && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN ) - hp += hp * sc->data[SC_INCHEALRATE]->val1/100; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish] - if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) - hp += hp / 10; - } - -#ifdef RENEWAL - // MATK part of the RE heal formula [malufett] - // Note: in this part matk bonuses from items or skills are not applied - switch( skill_id ) { - case BA_APPLEIDUN: case PR_SANCTUARY: - case NPC_EVILLAND: break; - default: - { - struct status_data *status = status_get_status_data(src); - int min, max, wMatk, variance; - - min = max = status_base_matk(status, status_get_lv(src)); - if( status->rhw.matk > 0 ){ - wMatk = status->rhw.matk; - variance = wMatk * status->rhw.wlv / 10; - min += wMatk - variance; - max += wMatk + variance; - } - - if( sc && sc->data[SC_RECOGNIZEDSPELL] ) - min = max; - - if( sd && sd->right_weapon.overrefine > 0 ){ - min++; - max += sd->right_weapon.overrefine - 1; - } - - if(max > min) - hp += min+rnd()%(max-min); - else - hp += min; - } - } -#endif - return hp; -} - -// Making plagiarize check its own function [Aru] -int can_copy (struct map_session_data *sd, uint16 skill_id, struct block_list* bl) -{ - // Never copy NPC/Wedding Skills - if (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL)) - return 0; - - // High-class skills - if((skill_id >= LK_AURABLADE && skill_id <= ASC_CDP) || (skill_id >= ST_PRESERVE && skill_id <= CR_CULTIVATION)) - { - if(battle_config.copyskill_restrict == 2) - return 0; - else if(battle_config.copyskill_restrict) - return (sd->status.class_ == JOB_STALKER); - } - - //Added so plagarize can't copy agi/bless if you're undead since it damages you - if ((skill_id == AL_INCAGI || skill_id == AL_BLESSING || - skill_id == CASH_BLESSING || skill_id == CASH_INCAGI || - skill_id == MER_INCAGI || skill_id == MER_BLESSING)) - return 0; - - // Couldn't preserve 3rd Class skills except only when using Reproduce skill. [Jobbie] - if( !(sd->sc.data[SC__REPRODUCE]) && (skill_id >= RK_ENCHANTBLADE && skill_id <= SR_RIDEINLIGHTNING) ) - return 0; - // Reproduce will only copy skills according on the list. [Jobbie] - else if( sd->sc.data[SC__REPRODUCE] && !skill_reproduce_db[skill_id] ) - return 0; - - return 1; -} - -// [MouseJstr] - skill ok to cast? and when? -int skillnotok (uint16 skill_id, struct map_session_data *sd) -{ - int16 idx,m; - nullpo_retr (1, sd); - m = sd->bl.m; - idx = skill_get_index(skill_id); - - if (idx == 0) - return 1; // invalid skill id - - if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL)) - return 0; // can do any damn thing they want - - if( skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2 ) - return 0; // Teleport lv 3 bypasses this check.[Inkfish] - - // Epoque: - // This code will compare the player's attack motion value which is influenced by ASPD before - // allowing a skill to be cast. This is to prevent no-delay ACT files from spamming skills such as - // AC_DOUBLE which do not have a skill delay and are not regarded in terms of attack motion. - if( !sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick && - DIFF_TICK(gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100) ) - {// attempted to cast a skill before the attack motion has finished - return 1; - } - - if (sd->blockskill[idx] > 0){ - clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0); - return 1; - } - /** - * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions above - * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map - **/ - if( sd->skillitem == skill_id ) - return 0; - // Check skill restrictions [Celest] - if( (!map_flag_vs(m) && skill_get_nocast (skill_id) & 1) || - (map[m].flag.pvp && skill_get_nocast (skill_id) & 2) || - (map_flag_gvg(m) && skill_get_nocast (skill_id) & 4) || - (map[m].flag.battleground && skill_get_nocast (skill_id) & 8) || - (map[m].flag.restricted && map[m].zone && skill_get_nocast (skill_id) & (8*map[m].zone)) ){ - clif_msg(sd, 0x536); // This skill cannot be used within this area - 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 (skill_id) { - case AL_WARP: - case RETURN_TO_ELDICASTES: - case ALL_GUARDIAN_RECALL: - if(map[m].flag.nowarp) { - clif_skill_teleportmessage(sd,0); - return 1; - } - return 0; - case AL_TELEPORT: - case SC_FATALMENACE: - case SC_DIMENSIONDOOR: - if(map[m].flag.noteleport) { - clif_skill_teleportmessage(sd,0); - return 1; - } - return 0; // gonna be checked in 'skill_castend_nodamage_id' - case WE_CALLPARTNER: - case WE_CALLPARENT: - case WE_CALLBABY: - if (map[m].flag.nomemo) { - clif_skill_teleportmessage(sd,1); - return 1; - } - break; - case MC_VENDING: - case MC_IDENTIFY: - case ALL_BUYING_STORE: - return 0; // always allowed - case WZ_ICEWALL: - // noicewall flag [Valaris] - if (map[m].flag.noicewall) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 1; - } - break; - case GC_DARKILLUSION: - if( map_flag_gvg(m) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 1; - } - break; - case GD_EMERGENCYCALL: - if ( - !(battle_config.emergency_call&((agit_flag || agit2_flag)?2:1)) || - !(battle_config.emergency_call&(map[m].flag.gvg || map[m].flag.gvg_castle?8:4)) || - (battle_config.emergency_call&16 && map[m].flag.nowarpto && !map[m].flag.gvg_castle) - ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - 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( pc_ismadogear(sd) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 1; - } - break; - - case WM_SIRCLEOFNATURE: - case WM_SOUND_OF_DESTRUCTION: - case SC_MANHOLE: - case WM_LULLABY_DEEPSLEEP: - case WM_SATURDAY_NIGHT_FEVER: - if( !map_flag_vs(m) ) { - clif_skill_teleportmessage(sd,2); // This skill uses this msg instead of skill fails. - return 1; - } - break; - - } - return (map[m].flag.noskill); -} - -int skillnotok_hom(uint16 skill_id, struct homun_data *hd) -{ - uint16 idx = skill_get_index(skill_id); - nullpo_retr(1,hd); - - if (idx == 0) - return 1; // invalid skill id - - if (hd->blockskill[idx] > 0) - return 1; - switch(skill_id){ - case MH_LIGHT_OF_REGENE: - if(hd->homunculus.intimacy <= 750) //if not cordial - return 1; - break; - case MH_OVERED_BOOST: - if(hd->homunculus.hunger <= 1) //if we starving - return 1; - case MH_GOLDENE_FERSE: //can be used with angriff - if(hd->sc.data[SC_ANGRIFFS_MODUS]) - return 1; - case MH_ANGRIFFS_MODUS: - if(hd->sc.data[SC_GOLDENE_FERSE]) - return 1; - break; - } - - //Use master's criteria. - return skillnotok(skill_id, hd->master); -} - -int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md) -{ - uint16 idx = skill_get_index(skill_id); - nullpo_retr(1,md); - - if( idx == 0 ) - return 1; // Invalid Skill ID - if( md->blockskill[idx] > 0 ) - return 1; - - return skillnotok(skill_id, md->master); -} - -struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill_lv, struct block_list* src, int x, int y) -{ - int pos = skill_get_unit_layout_type(skill_id,skill_lv); - uint8 dir; - - if (pos < -1 || pos >= MAX_SKILL_UNIT_LAYOUT) { - ShowError("skill_get_unit_layout: unsupported layout type %d for skill %d (level %d)\n", pos, skill_id, skill_lv); - pos = cap_value(pos, 0, MAX_SQUARE_LAYOUT); // cap to nearest square layout - } - - if (pos != -1) // simple single-definition layout - return &skill_unit_layout[pos]; - - dir = (src->x == x && src->y == y) ? 6 : map_calc_dir(src,x,y); // 6 - default aegis direction - - if (skill_id == MG_FIREWALL) - return &skill_unit_layout [firewall_unit_pos + dir]; - else if (skill_id == WZ_ICEWALL) - return &skill_unit_layout [icewall_unit_pos + dir]; - else if( skill_id == 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", skill_id, skill_lv); - return &skill_unit_layout[0]; // default 1x1 layout -} - -/*========================================== - * - *------------------------------------------*/ -int skill_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int dmg_lv, unsigned int tick) -{ - struct map_session_data *sd, *dstsd; - struct mob_data *md, *dstmd; - struct status_data *sstatus, *tstatus; - struct status_change *sc, *tsc; - - enum sc_type status; - int skill; - int rate; - - nullpo_ret(src); - nullpo_ret(bl); - - if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest - - if( dmg_lv < ATK_BLOCK ) // Don't apply effect if miss. - return 0; - - sd = BL_CAST(BL_PC, src); - md = BL_CAST(BL_MOB, src); - dstsd = BL_CAST(BL_PC, bl); - dstmd = BL_CAST(BL_MOB, bl); - - sc = status_get_sc(src); - tsc = status_get_sc(bl); - sstatus = status_get_status_data(src); - tstatus = status_get_status_data(bl); - if (!tsc) //skill additional effect is about adding effects to the target... - //So if the target can't be inflicted with statuses, this is pointless. - return 0; - - if( sd ) - { // These statuses would be applied anyway even if the damage was blocked by some skills. [Inkfish] - if( skill_id != WS_CARTTERMINATION && skill_id != AM_DEMONSTRATION && skill_id != CR_REFLECTSHIELD && skill_id != MS_REFLECTSHIELD && skill_id != ASC_BREAKER ) - { // Trigger status effects - enum sc_type type; - int i; - for( i = 0; i < ARRAYLENGTH(sd->addeff) && sd->addeff[i].flag; i++ ) - { - rate = sd->addeff[i].rate; - if( attack_type&BF_LONG ) // Any ranged physical attack takes status arrows into account (Grimtooth...) [DracoRPG] - rate += sd->addeff[i].arrow_rate; - if( !rate ) continue; - - if( (sd->addeff[i].flag&(ATF_WEAPON|ATF_MAGIC|ATF_MISC)) != (ATF_WEAPON|ATF_MAGIC|ATF_MISC) ) - { // Trigger has attack type consideration. - if( (sd->addeff[i].flag&ATF_WEAPON && attack_type&BF_WEAPON) || - (sd->addeff[i].flag&ATF_MAGIC && attack_type&BF_MAGIC) || - (sd->addeff[i].flag&ATF_MISC && attack_type&BF_MISC) ) ; - else - continue; - } - - if( (sd->addeff[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT) ) - { // Trigger has range consideration. - if((sd->addeff[i].flag&ATF_LONG && !(attack_type&BF_LONG)) || - (sd->addeff[i].flag&ATF_SHORT && !(attack_type&BF_SHORT))) - continue; //Range Failed. - } - - type = sd->addeff[i].id; - skill = skill_get_time2(status_sc2skill(type),7); - - if (sd->addeff[i].flag&ATF_TARGET) - status_change_start(bl,type,rate,7,0,0,0,skill,0); - - if (sd->addeff[i].flag&ATF_SELF) - status_change_start(src,type,rate,7,0,0,0,skill,0); - } - } - - if( skill_id ) - { // Trigger status effects on skills - enum sc_type type; - int i; - for( i = 0; i < ARRAYLENGTH(sd->addeff3) && sd->addeff3[i].skill; i++ ) - { - if( skill_id != sd->addeff3[i].skill || !sd->addeff3[i].rate ) - continue; - type = sd->addeff3[i].id; - skill = skill_get_time2(status_sc2skill(type),7); - - if( sd->addeff3[i].target&ATF_TARGET ) - status_change_start(bl,type,sd->addeff3[i].rate,7,0,0,0,skill,0); - if( sd->addeff3[i].target&ATF_SELF ) - status_change_start(src,type,sd->addeff3[i].rate,7,0,0,0,skill,0); - } - } - } - - if( dmg_lv < ATK_DEF ) // no damage, return; - return 0; - - switch(skill_id) - { - case 0: // Normal attacks (no skill used) - { - if( attack_type&BF_SKILL ) - break; // If a normal attack is a skill, it's splash damage. [Inkfish] - if(sd) { - // Automatic trigger of Blitz Beat - if (pc_isfalcon(sd) && sd->status.weapon == W_BOW && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 && - rnd()%1000 <= sstatus->luk*10/3+1 ) { - 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 && rnd()%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 && - (skill*15 + 55) + pc_checkskill(sd,TF_STEAL)*10 > rnd()%1000) { - if(pc_steal_item(sd,bl,pc_checkskill(sd,TF_STEAL))) - clif_skill_nodamage(src,bl,TF_STEAL,skill,1); - else - clif_skill_fail(sd,RG_SNATCHER,USESKILL_FAIL_LEVEL,0); - } - // Chance to trigger Taekwon kicks [Dralnu] - if(sc && !sc->data[SC_COMBO]) { - if(sc->data[SC_READYSTORM] && - sc_start(src,SC_COMBO, 15, TK_STORMKICK, - (2000 - 4*sstatus->agi - 2*sstatus->dex))) - ; //Stance triggered - else if(sc->data[SC_READYDOWN] && - sc_start(src,SC_COMBO, 15, TK_DOWNKICK, - (2000 - 4*sstatus->agi - 2*sstatus->dex))) - ; //Stance triggered - else if(sc->data[SC_READYTURN] && - sc_start(src,SC_COMBO, 15, TK_TURNKICK, - (2000 - 4*sstatus->agi - 2*sstatus->dex))) - ; //Stance triggered - else if (sc->data[SC_READYCOUNTER]) { //additional chance from SG_FRIEND [Komurka] - rate = 20; - if (sc->data[SC_SKILLRATE_UP] && sc->data[SC_SKILLRATE_UP]->val1 == TK_COUNTER) { - rate += rate*sc->data[SC_SKILLRATE_UP]->val2/100; - status_change_end(src, SC_SKILLRATE_UP, INVALID_TIMER); - } - sc_start2(src, SC_COMBO, rate, TK_COUNTER, bl->id, - (2000 - 4*sstatus->agi - 2*sstatus->dex)); - } - } - if(sc && sc->data[SC_PYROCLASTIC] && (rnd() % 1000 <= sstatus->luk * 10 / 3 + 1) ) - skill_castend_pos2(src, bl->x, bl->y, BS_HAMMERFALL,sc->data[SC_PYROCLASTIC]->val1, tick, 0); - } - - if (sc) { - struct status_change_entry *sce; - // Enchant Poison gives a chance to poison attacked enemies - if((sce=sc->data[SC_ENCPOISON])) //Don't use sc_start since chance comes in 1/10000 rate. - status_change_start(bl,SC_POISON,sce->val2, sce->val1,src->id,0,0, - skill_get_time2(AS_ENCHANTPOISON,sce->val1),0); - // Enchant Deadly Poison gives a chance to deadly poison attacked enemies - if((sce=sc->data[SC_EDP])) - sc_start4(bl,SC_DPOISON,sce->val2, sce->val1,src->id,0,0, - skill_get_time2(ASC_EDP,sce->val1)); - } - } - break; - - case SM_BASH: - if( sd && skill_lv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){ - //TODO: How much % per base level it actually is? - sc_start(bl,SC_STUN,(5*(skill_lv-5)+(int)sd->status.base_level/10), - skill_lv,skill_get_time2(SM_FATALBLOW,skill_lv)); - } - break; - - case MER_CRASH: - sc_start(bl,SC_STUN,(6*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case AS_VENOMKNIFE: - if (sd) //Poison chance must be that of Envenom. [Skotlex] - skill_lv = pc_checkskill(sd, TF_POISON); - case TF_POISON: - case AS_SPLASHER: - if(!sc_start2(bl,SC_POISON,(4*skill_lv+10),skill_lv,src->id,skill_get_time2(skill_id,skill_lv)) - && sd && skill_id==TF_POISON - ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - - case AS_SONICBLOW: - sc_start(bl,SC_STUN,(2*skill_lv+10),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case WZ_FIREPILLAR: - unit_set_walkdelay(bl, tick, skill_get_time2(skill_id, skill_lv), 1); - break; - - case MG_FROSTDIVER: -#ifndef RENEWAL - case WZ_FROSTNOVA: -#endif - sc_start(bl,SC_FREEZE,skill_lv*3+35,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - -#ifdef RENEWAL - case WZ_FROSTNOVA: - sc_start(bl,SC_FREEZE,skill_lv*5+33,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; -#endif - - case WZ_STORMGUST: - /** - * Storm Gust counter was dropped in renewal - **/ - #ifdef RENEWAL - sc_start(bl,SC_FREEZE,65-(5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); - #else - //Tharis pointed out that this is normal freeze chance with a base of 300% - if(tsc->sg_counter >= 3 && - sc_start(bl,SC_FREEZE,300,skill_lv,skill_get_time2(skill_id,skill_lv))) - tsc->sg_counter = 0; - /** - * being it only resets on success it'd keep stacking and eventually overflowing on mvps, so we reset at a high value - **/ - else if( tsc->sg_counter > 250 ) - tsc->sg_counter = 0; - #endif - break; - - case WZ_METEOR: - sc_start(bl,SC_STUN,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case WZ_VERMILION: - sc_start(bl,SC_BLIND,4*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case HT_FREEZINGTRAP: - case MA_FREEZINGTRAP: - sc_start(bl,SC_FREEZE,(3*skill_lv+35),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case HT_FLASHER: - sc_start(bl,SC_BLIND,(10*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case HT_LANDMINE: - case MA_LANDMINE: - sc_start(bl,SC_STUN,(5*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case HT_SHOCKWAVE: - status_percent_damage(src, bl, 0, 15*skill_lv+5, false); - break; - - case HT_SANDMAN: - case MA_SANDMAN: - sc_start(bl,SC_SLEEP,(10*skill_lv+40),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case TF_SPRINKLESAND: - sc_start(bl,SC_BLIND,20,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case TF_THROWSTONE: - sc_start(bl,SC_STUN,3,skill_lv,skill_get_time(skill_id,skill_lv)); - sc_start(bl,SC_BLIND,3,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case NPC_DARKCROSS: - case CR_HOLYCROSS: - sc_start(bl,SC_BLIND,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - //Chance to cause blind status vs demon and undead element, but not against players - if(!dstsd && (battle_check_undead(tstatus->race,tstatus->def_ele) || tstatus->race == RC_DEMON)) - sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - attack_type |= BF_WEAPON; - break; - - case AM_ACIDTERROR: - sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv)); - if (skill_break_equip(bl, EQP_ARMOR, 100*skill_get_time(skill_id,skill_lv), BCT_ENEMY)) - clif_emotion(bl,E_OMG); - break; - - case AM_DEMONSTRATION: - skill_break_equip(bl, EQP_WEAPON, 100*skill_lv, BCT_ENEMY); - break; - - case CR_SHIELDCHARGE: - sc_start(bl,SC_STUN,(15+skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case PA_PRESSURE: - status_percent_damage(src, bl, 0, 15+5*skill_lv, false); - break; - - case RG_RAID: - sc_start(bl,SC_STUN,(10+3*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv)); - sc_start(bl,SC_BLIND,(10+3*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); - -#ifdef RENEWAL - sc_start(bl,SC_RAID,100,7,5000); - break; - - case RG_BACKSTAP: - sc_start(bl,SC_STUN,(5+2*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv)); -#endif - break; - - case BA_FROSTJOKER: - sc_start(bl,SC_FREEZE,(15+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case DC_SCREAM: - sc_start(bl,SC_STUN,(25+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case BD_LULLABY: - sc_start(bl,SC_SLEEP,15,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case DC_UGLYDANCE: - rate = 5+5*skill_lv; - if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON))) - rate += 5+skill; - status_zap(bl, 0, rate); - break; - case SL_STUN: - if (tstatus->size==SZ_MEDIUM) //Only stuns mid-sized mobs. - sc_start(bl,SC_STUN,(30+10*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv)); - break; - - case NPC_PETRIFYATTACK: - sc_start4(bl,status_skill2sc(skill_id),50+10*skill_lv, - skill_lv,0,0,skill_get_time(skill_id,skill_lv), - skill_get_time2(skill_id,skill_lv)); - break; - case NPC_CURSEATTACK: - case NPC_SLEEPATTACK: - case NPC_BLINDATTACK: - case NPC_POISON: - case NPC_SILENCEATTACK: - case NPC_STUNATTACK: - case NPC_HELLPOWER: - sc_start(bl,status_skill2sc(skill_id),50+10*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case NPC_ACIDBREATH: - case NPC_ICEBREATH: - sc_start(bl,status_skill2sc(skill_id),70,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case NPC_BLEEDING: - sc_start(bl,SC_BLEEDING,(20*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case NPC_MENTALBREAKER: - { //Based on observations by Tharis, Mental Breaker should do SP damage - //equal to Matk*skLevel. - rate = sstatus->matk_min; - if (rate < sstatus->matk_max) - rate += rnd()%(sstatus->matk_max - sstatus->matk_min); - rate*=skill_lv; - status_zap(bl, 0, rate); - break; - } - // Equipment breaking monster skills [Celest] - case NPC_WEAPONBRAKER: - skill_break_equip(bl, EQP_WEAPON, 150*skill_lv, BCT_ENEMY); - break; - case NPC_ARMORBRAKE: - skill_break_equip(bl, EQP_ARMOR, 150*skill_lv, BCT_ENEMY); - break; - case NPC_HELMBRAKE: - skill_break_equip(bl, EQP_HELM, 150*skill_lv, BCT_ENEMY); - break; - case NPC_SHIELDBRAKE: - skill_break_equip(bl, EQP_SHIELD, 150*skill_lv, BCT_ENEMY); - break; - - case CH_TIGERFIST: - sc_start(bl,SC_STOP,(10+skill_lv*10),0,skill_get_time2(skill_id,skill_lv)); - break; - - case LK_SPIRALPIERCE: - case ML_SPIRALPIERCE: - sc_start(bl,SC_STOP,(15+skill_lv*5),0,skill_get_time2(skill_id,skill_lv)); - break; - - case ST_REJECTSWORD: - sc_start(bl,SC_AUTOCOUNTER,(skill_lv*15),skill_lv,skill_get_time(skill_id,skill_lv)); - break; - - case PF_FOGWALL: - if (src != bl && !tsc->data[SC_DELUGE]) - sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case LK_HEADCRUSH: //Headcrush has chance of causing Bleeding status, except on demon and undead element - if (!(battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON)) - sc_start(bl, SC_BLEEDING,50, skill_lv, skill_get_time2(skill_id,skill_lv)); - break; - - case LK_JOINTBEAT: - status = status_skill2sc(skill_id); - if (tsc->jb_flag) { - sc_start2(bl,status,(5*skill_lv+5),skill_lv,tsc->jb_flag&BREAK_FLAGS,skill_get_time2(skill_id,skill_lv)); - tsc->jb_flag = 0; - } - break; - case ASC_METEORASSAULT: - //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*skill_lv% chance. - switch(rnd()%3) { - case 0: - sc_start(bl,SC_BLIND,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,1)); - break; - case 1: - sc_start(bl,SC_STUN,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,2)); - break; - default: - sc_start(bl,SC_BLEEDING,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,3)); - } - break; - - case HW_NAPALMVULCAN: - sc_start(bl,SC_CURSE,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case WS_CARTTERMINATION: // Cart termination - sc_start(bl,SC_STUN,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case CR_ACIDDEMONSTRATION: - skill_break_equip(bl, EQP_WEAPON|EQP_ARMOR, 100*skill_lv, BCT_ENEMY); - break; - - case TK_DOWNKICK: - sc_start(bl,SC_STUN,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case TK_JUMPKICK: - if( dstsd && dstsd->class_ != MAPID_SOUL_LINKER && !tsc->data[SC_PRESERVE] ) - {// debuff the following statuses - status_change_end(bl, SC_SPIRIT, INVALID_TIMER); - status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER); - status_change_end(bl, SC_KAITE, INVALID_TIMER); - status_change_end(bl, SC_KAAHI, INVALID_TIMER); - status_change_end(bl, SC_ONEHAND, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER); - } - break; - case TK_TURNKICK: - case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs. - if(attack_type&BF_MISC) //70% base stun chance... - sc_start(bl,SC_STUN,70,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case GS_BULLSEYE: //0.1% coma rate. - if(tstatus->race == RC_BRUTE || tstatus->race == RC_DEMIHUMAN) - status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0); - break; - case GS_PIERCINGSHOT: - sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case NJ_HYOUSYOURAKU: - sc_start(bl,SC_FREEZE,(10+10*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case GS_FLING: - sc_start(bl,SC_FLING,100, sd?sd->spiritball_old:5,skill_get_time(skill_id,skill_lv)); - break; - case GS_DISARM: - rate = 3*skill_lv; - if (sstatus->dex > tstatus->dex) - rate += (sstatus->dex - tstatus->dex)/5; //TODO: Made up formula - skill_strip_equip(bl, EQP_WEAPON, rate, skill_lv, skill_get_time(skill_id,skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case NPC_EVILLAND: - sc_start(bl,SC_BLIND,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case NPC_HELLJUDGEMENT: - sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case NPC_CRITICALWOUND: - sc_start(bl,SC_CRITICALWOUND,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - 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 * skill_lv; - if( rnd()%100 < rate ) - skill_castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0); - break; - case RK_WINDCUTTER: - sc_start(bl,SC_FEAR,3+2*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case RK_DRAGONBREATH: - sc_start4(bl,SC_BURNING,5+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv)); - break; - case AB_ADORAMUS: - if( tsc && !tsc->data[SC_DECREASEAGI] ) //Prevent duplicate agi-down effect. - sc_start(bl, SC_ADORAMUS, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case WL_CRIMSONROCK: - sc_start(bl, SC_STUN, 40, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case WL_COMET: - sc_start4(bl,SC_BURNING,100,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv)); - break; - case WL_EARTHSTRAIN: - { - int rate = 0, i; - const int pos[5] = { EQP_WEAPON, EQP_HELM, EQP_SHIELD, EQP_ARMOR, EQP_ACC }; - rate = 6 * skill_lv + 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 < skill_lv; i++ ) - skill_strip_equip(bl,pos[i],rate,skill_lv,skill_get_time2(skill_id,skill_lv)); - } - break; - case WL_JACKFROST: - sc_start(bl,SC_FREEZE,100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case RA_WUGBITE: - sc_start(bl, SC_BITE, (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*2 : 0), skill_lv, (skill_get_time(skill_id,skill_lv) + (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*500 : 0)) ); - break; - case RA_SENSITIVEKEEN: - if( rnd()%100 < 8 * skill_lv ) - skill_castend_damage_id(src, bl, RA_WUGBITE, sd ? pc_checkskill(sd, RA_WUGBITE):skill_lv, tick, SD_ANIMATION); - break; - case RA_FIRINGTRAP: - case RA_ICEBOUNDTRAP: - sc_start(bl, (skill_id == RA_FIRINGTRAP) ? SC_BURNING:SC_FREEZING, 40 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); - break; - case NC_PILEBUNKER: - if( rnd()%100 < 5 + 15*skill_lv ) - { //Deactivatable Statuses: Kyrie Eleison, Auto Guard, Steel Body, Assumptio, and Millennium Shield - status_change_end(bl, SC_KYRIE, INVALID_TIMER); - status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER); - status_change_end(bl, SC_STEELBODY, INVALID_TIMER); - status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER); - status_change_end(bl, SC_MILLENNIUMSHIELD, INVALID_TIMER); - } - break; - case NC_FLAMELAUNCHER: - sc_start4(bl, SC_BURNING, 50 + 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time2(skill_id, skill_lv)); - break; - case NC_COLDSLOWER: - sc_start(bl, SC_FREEZE, 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv)); - sc_start(bl, SC_FREEZING, 20 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case NC_POWERSWING: - sc_start(bl, SC_STUN, 5*skill_lv, skill_lv, skill_get_time(skill_id, skill_lv)); - if( rnd()%100 < 5*skill_lv ) - skill_castend_damage_id(src, bl, NC_AXEBOOMERANG, pc_checkskill(sd, NC_AXEBOOMERANG), tick, 1); - break; - case GC_WEAPONCRUSH: - skill_castend_nodamage_id(src,bl,skill_id,skill_lv,tick,BCT_ENEMY); - break; - case LG_SHIELDPRESS: - sc_start(bl, SC_STUN, 30 + 8 * skill_lv, skill_lv, skill_get_time(skill_id,skill_lv)); - break; - case LG_PINPOINTATTACK: - rate = 30 + (((5 * (sd?pc_checkskill(sd,LG_PINPOINTATTACK):skill_lv)) + (sstatus->agi + status_get_lv(src))) / 10); - switch( skill_lv ) { - case 1: - sc_start(bl,SC_BLEEDING,rate,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case 2: - if( dstsd && dstsd->spiritball && rnd()%100 < rate ) - pc_delspiritball(dstsd, dstsd->spiritball, 0); - break; - default: - skill_break_equip(bl,(skill_lv == 3) ? EQP_SHIELD : (skill_lv == 4) ? EQP_ARMOR : EQP_WEAPON,rate * 100,BCT_ENEMY); - break; - } - break; - case LG_MOONSLASHER: - rate = 32 + 8 * skill_lv; - if( rnd()%100 < rate && dstsd ) // Uses skill_addtimerskill to avoid damage and setsit packet overlaping. Officially clif_setsit is received about 500 ms after damage packet. - skill_addtimerskill(src,tick+500,bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0); - else if( dstmd && !is_boss(bl) ) - sc_start(bl,SC_STOP,100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case LG_RAYOFGENESIS: // 50% chance to cause Blind on Undead and Demon monsters. - if ( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON ) - sc_start(bl, SC_BLIND,50, skill_lv, skill_get_time(skill_id,skill_lv)); - break; - case LG_EARTHDRIVE: - skill_break_equip(src, EQP_SHIELD, 500, BCT_SELF); - sc_start(bl, SC_EARTHDRIVE, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case SR_DRAGONCOMBO: - sc_start(bl, SC_STUN, 1 + skill_lv, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case SR_FALLENEMPIRE: - sc_start(bl, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case SR_WINDMILL: - if( dstsd ) - skill_addtimerskill(src,tick+status_get_amotion(src),bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0); - else if( dstmd && !is_boss(bl) ) - sc_start(bl, SC_STUN, 100, skill_lv, 1000 + 1000 * (rnd() %3)); - break; - case SR_GENTLETOUCH_QUIET: // [(Skill Level x 5) + (Caster?s DEX + Caster?s Base Level) / 10] - sc_start(bl, SC_SILENCE, 5 * skill_lv + (sstatus->dex + status_get_lv(src)) / 10, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case SR_EARTHSHAKER: - sc_start(bl,SC_STUN, 25 + 5 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case SR_HOWLINGOFLION: - sc_start(bl, SC_FEAR, 5 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case WM_SOUND_OF_DESTRUCTION: - if( rnd()%100 < 5 + 5 * skill_lv ) { // Temporarly Check Until We Get the Official Formula - status_change_end(bl, SC_DANCING, INVALID_TIMER); - status_change_end(bl, SC_RICHMANKIM, INVALID_TIMER); - status_change_end(bl, SC_ETERNALCHAOS, INVALID_TIMER); - status_change_end(bl, SC_DRUMBATTLE, INVALID_TIMER); - status_change_end(bl, SC_NIBELUNGEN, INVALID_TIMER); - status_change_end(bl, SC_INTOABYSS, INVALID_TIMER); - status_change_end(bl, SC_SIEGFRIED, INVALID_TIMER); - status_change_end(bl, SC_WHISTLE, INVALID_TIMER); - status_change_end(bl, SC_ASSNCROS, INVALID_TIMER); - status_change_end(bl, SC_POEMBRAGI, INVALID_TIMER); - status_change_end(bl, SC_APPLEIDUN, INVALID_TIMER); - status_change_end(bl, SC_HUMMING, INVALID_TIMER); - status_change_end(bl, SC_FORTUNE, INVALID_TIMER); - status_change_end(bl, SC_SERVICE4U, INVALID_TIMER); - status_change_end(bl, SC_LONGING, INVALID_TIMER); - status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER); - status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER); - status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER); - status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER); - status_change_end(bl, SC_ECHOSONG, INVALID_TIMER); - status_change_end(bl, SC_HARMONIZE, INVALID_TIMER); - status_change_end(bl, SC_WINKCHARM, INVALID_TIMER); - status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER); - status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER); - status_change_end(bl, SC_LERADSDEW, INVALID_TIMER); - status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER); - status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER); - status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER); - } - break; - case SO_EARTHGRAVE: - sc_start(bl, SC_BLEEDING, 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); // Need official rate. [LimitLine] - break; - case SO_DIAMONDDUST: - rate = 5 + 5 * skill_lv; - if( sc && sc->data[SC_COOLER_OPTION] ) - rate += rate * sc->data[SC_COOLER_OPTION]->val2 / 100; - sc_start(bl, SC_CRYSTALIZE, rate, skill_lv, skill_get_time2(skill_id, skill_lv)); - break; - case SO_VARETYR_SPEAR: - sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); - break; - case GN_SLINGITEM_RANGEMELEEATK: - if( sd ) { - switch( sd->itemid ) { // Starting SCs here instead of do it in skill_additional_effect to simplify the code. - case 13261: - sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv)); - sc_start(bl, SC_BLEEDING, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv)); - break; - case 13262: - sc_start(bl, SC_MELON_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces ASPD and moviment speed - break; - case 13264: - sc_start(bl, SC_BANANA_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces LUK ??Needed confirm it, may be it's bugged in kRORE? - sc_start(bl, SC_BANANA_BOMB_SITDOWN, 75, skill_lv, skill_get_time(GN_SLINGITEM_RANGEMELEEATK,skill_lv)); // Sitdown for 3 seconds. - break; - } - sd->itemid = -1; - } - break; - case GN_HELLS_PLANT_ATK: - sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); - sc_start(bl, SC_BLEEDING, 20 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); - break; - case EL_WIND_SLASH: // Non confirmed rate. - sc_start(bl, SC_BLEEDING, 25, skill_lv, skill_get_time(skill_id,skill_lv)); - break; - case EL_STONE_HAMMER: - rate = 10 * skill_lv; - sc_start(bl, SC_STUN, rate, skill_lv, skill_get_time(skill_id,skill_lv)); - break; - case EL_ROCK_CRUSHER: - case EL_ROCK_CRUSHER_ATK: - sc_start(bl,status_skill2sc(skill_id),50,skill_lv,skill_get_time(EL_ROCK_CRUSHER,skill_lv)); - break; - case EL_TYPOON_MIS: - sc_start(bl,SC_SILENCE,10*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case KO_JYUMONJIKIRI: // needs more info - sc_start(bl,SC_JYUMONJIKIRI,25,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case KO_MAKIBISHI: - sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(skill_id,skill_lv)); - break; - case MH_LAVA_SLIDE: - if (tsc && !tsc->data[SC_BURNING]) sc_start4(bl, SC_BURNING, 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time(skill_id, skill_lv)); - break; - case MH_STAHL_HORN: - sc_start(bl, SC_STUN, (20 + 4 * (skill_lv-1)), skill_lv, skill_get_time(skill_id, skill_lv)); - break; - case MH_NEEDLE_OF_PARALYZE: - sc_start(bl, SC_PARALYSIS, 40 + (5*skill_lv), skill_lv, skill_get_time(skill_id, skill_lv)); - break; - } - - if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai) - { //Pass heritage to Master for status causing effects. [Skotlex] - sd = map_id2sd(md->master_id); - src = sd?&sd->bl:src; - } - - if( attack_type&BF_WEAPON ) - { // Coma, Breaking Equipment - if( sd && sd->special_state.bonus_coma ) - { - rate = sd->weapon_coma_ele[tstatus->def_ele]; - rate += sd->weapon_coma_race[tstatus->race]; - rate += sd->weapon_coma_race[tstatus->mode&MD_BOSS?RC_BOSS:RC_NONBOSS]; - if (rate) - status_change_start(bl, SC_COMA, rate, 0, 0, src->id, 0, 0, 0); - } - if( sd && battle_config.equip_self_break_rate ) - { // Self weapon breaking - rate = battle_config.equip_natural_break_rate; - if( sc ) - { - if(sc->data[SC_OVERTHRUST]) - rate += 10; - if(sc->data[SC_MAXOVERTHRUST]) - rate += 10; - } - if( rate ) - skill_break_equip(src, EQP_WEAPON, rate, BCT_SELF); - } - if( battle_config.equip_skill_break_rate && skill_id != WS_CARTTERMINATION && skill_id != ITM_TOMAHAWK ) - { // Cart Termination/Tomahawk won't trigger breaking data. Why? No idea, go ask Gravity. - // Target weapon breaking - rate = 0; - if( sd ) - rate += sd->bonus.break_weapon_rate; - if( sc && sc->data[SC_MELTDOWN] ) - rate += sc->data[SC_MELTDOWN]->val2; - if( rate ) - skill_break_equip(bl, EQP_WEAPON, rate, BCT_ENEMY); - - // Target armor breaking - rate = 0; - if( sd ) - rate += sd->bonus.break_armor_rate; - if( sc && sc->data[SC_MELTDOWN] ) - rate += sc->data[SC_MELTDOWN]->val3; - if( rate ) - skill_break_equip(bl, EQP_ARMOR, rate, BCT_ENEMY); - } - } - - if( sd && sd->ed && sc && !status_isdead(bl) && !skill_id ){ - struct unit_data *ud = unit_bl2ud(src); - - if( sc->data[SC_WILD_STORM_OPTION] ) - skill = sc->data[SC_WILD_STORM_OPTION]->val2; - else if( sc->data[SC_UPHEAVAL_OPTION] ) - skill = sc->data[SC_UPHEAVAL_OPTION]->val2; - else if( sc->data[SC_TROPIC_OPTION] ) - skill = sc->data[SC_TROPIC_OPTION]->val3; - else if( sc->data[SC_CHILLY_AIR_OPTION] ) - skill = sc->data[SC_CHILLY_AIR_OPTION]->val3; - else - skill = 0; - - if ( rnd()%100 < 25 && skill ){ - skill_castend_damage_id(src, bl, skill, 5, tick, 0); - - if (ud) { - rate = skill_delayfix(src, skill, skill_lv); - if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ - ud->canact_tick = tick+rate; - if ( battle_config.display_status_timers ) - clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0); - } - } - } - } - - // Autospell when attacking - if( sd && !status_isdead(bl) && sd->autospell[0].id ) - { - struct block_list *tbl; - struct unit_data *ud; - int i, skill_lv, type, notok; - - for (i = 0; i < ARRAYLENGTH(sd->autospell) && sd->autospell[i].id; i++) { - - if(!(sd->autospell[i].flag&attack_type&BF_WEAPONMASK && - sd->autospell[i].flag&attack_type&BF_RANGEMASK && - sd->autospell[i].flag&attack_type&BF_SKILLMASK)) - continue; // one or more trigger conditions were not fulfilled - - skill = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id; - - sd->state.autocast = 1; - notok = skillnotok(skill, sd); - sd->state.autocast = 0; - - if ( notok ) - continue; - - skill_lv = sd->autospell[i].lv?sd->autospell[i].lv:1; - if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv); - - rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2; - - if (rnd()%1000 >= rate) - continue; - - tbl = (sd->autospell[i].id < 0) ? src : bl; - - if( (type = skill_get_casttype(skill)) == CAST_GROUND ) { - int maxcount = 0; - if( !(BL_PC&battle_config.skill_reiteration) && - skill_get_unit_flag(skill)&UF_NOREITERATION && - skill_check_unit_range(src,tbl->x,tbl->y,skill,skill_lv) - ) { - continue; - } - if( BL_PC&battle_config.skill_nofootset && - skill_get_unit_flag(skill)&UF_NOFOOTSET && - skill_check_unit_range2(src,tbl->x,tbl->y,skill,skill_lv) - ) { - continue; - } - if( BL_PC&battle_config.land_skill_limit && - (maxcount = skill_get_maxcount(skill, skill_lv)) > 0 - ) { - int v; - for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) { - if(sd->ud.skillunit[v]->skill_id == skill) - maxcount--; - } - if( maxcount == 0 ) { - continue; - } - } - } - if( battle_config.autospell_check_range && - !battle_check_range(src, tbl, skill_get_range2(src, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) ) - continue; - - if (skill == AS_SONICBLOW) - pc_stop_attack(sd); //Special case, Sonic Blow autospell should stop the player attacking. - if (skill == PF_SPIDERWEB) //Special case, due to its nature of coding. - type = CAST_GROUND; - - sd->state.autocast = 1; - skill_consume_requirement(sd,skill,skill_lv,1); - skill_toggle_magicpower(src, skill); - switch (type) { - case CAST_GROUND: - skill_castend_pos2(src, tbl->x, tbl->y, skill, skill_lv, tick, 0); - break; - case CAST_NODAMAGE: - skill_castend_nodamage_id(src, tbl, skill, skill_lv, tick, 0); - break; - case CAST_DAMAGE: - skill_castend_damage_id(src, tbl, skill, skill_lv, tick, 0); - break; - } - sd->state.autocast = 0; - //Set canact delay. [Skotlex] - ud = unit_bl2ud(src); - if (ud) { - rate = skill_delayfix(src, skill, skill_lv); - 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, 0, 0, 0); - } - } - } - } - - //Autobonus when attacking - if( sd && sd->autobonus[0].rate ) - { - int i; - for( i = 0; i < ARRAYLENGTH(sd->autobonus); i++ ) - { - if( rnd()%1000 >= sd->autobonus[i].rate ) - continue; - if( sd->autobonus[i].active != INVALID_TIMER ) - continue; - if(!(sd->autobonus[i].atk_type&attack_type&BF_WEAPONMASK && - sd->autobonus[i].atk_type&attack_type&BF_RANGEMASK && - sd->autobonus[i].atk_type&attack_type&BF_SKILLMASK)) - continue; // one or more trigger conditions were not fulfilled - pc_exeautobonus(sd,&sd->autobonus[i]); - } - } - - //Polymorph - if(sd && sd->bonus.classchange && attack_type&BF_WEAPON && - dstmd && !(tstatus->mode&MD_BOSS) && - (rnd()%10000 < sd->bonus.classchange)) - { - struct mob_db *mob; - int class_; - skill = 0; - do { - do { - class_ = rnd() % MAX_MOB_DB; - } while (!mobdb_checkid(class_)); - - rate = rnd() % 1000000; - mob = mob_db(class_); - } while ( - (mob->status.mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= rate) && - (skill++) < 2000); - if (skill < 2000) - mob_class_change(dstmd,class_); - } - - return 0; -} - -int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint16 skill_id, unsigned int tick) { - int skill, skill_lv, i, type, notok; - struct block_list *tbl; - - if( sd == NULL || !skill_id ) - return 0; - - for( i = 0; i < ARRAYLENGTH(sd->autospell3) && sd->autospell3[i].flag; i++ ) { - if( sd->autospell3[i].flag != skill_id ) - continue; - - if( sd->autospell3[i].lock ) - continue; // autospell already being executed - - skill = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id; - - sd->state.autocast = 1; - notok = skillnotok(skill, sd); - sd->state.autocast = 0; - - if ( notok ) - continue; - - skill_lv = sd->autospell3[i].lv ? sd->autospell3[i].lv : 1; - if( skill_lv < 0 ) skill_lv = 1 + rnd()%(-skill_lv); - - if( sd->autospell3[i].id >= 0 && bl == NULL ) - continue; // No target - if( rnd()%1000 >= sd->autospell3[i].rate ) - continue; - - tbl = (sd->autospell3[i].id < 0) ? &sd->bl : bl; - - if( (type = skill_get_casttype(skill)) == CAST_GROUND ) { - int maxcount = 0; - if( !(BL_PC&battle_config.skill_reiteration) && - skill_get_unit_flag(skill)&UF_NOREITERATION && - skill_check_unit_range(&sd->bl,tbl->x,tbl->y,skill,skill_lv) - ) { - continue; - } - if( BL_PC&battle_config.skill_nofootset && - skill_get_unit_flag(skill)&UF_NOFOOTSET && - skill_check_unit_range2(&sd->bl,tbl->x,tbl->y,skill,skill_lv) - ) { - continue; - } - if( BL_PC&battle_config.land_skill_limit && - (maxcount = skill_get_maxcount(skill, skill_lv)) > 0 - ) { - int v; - for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) { - if(sd->ud.skillunit[v]->skill_id == skill) - maxcount--; - } - if( maxcount == 0 ) { - continue; - } - } - } - if( battle_config.autospell_check_range && - !battle_check_range(&sd->bl, tbl, skill_get_range2(&sd->bl, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) ) - continue; - - sd->state.autocast = 1; - sd->autospell3[i].lock = true; - skill_consume_requirement(sd,skill,skill_lv,1); - switch( type ) - { - case CAST_GROUND: skill_castend_pos2(&sd->bl, tbl->x, tbl->y, skill, skill_lv, tick, 0); break; - case CAST_NODAMAGE: skill_castend_nodamage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break; - case CAST_DAMAGE: skill_castend_damage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break; - } - sd->autospell3[i].lock = false; - sd->state.autocast = 0; - } - - if( sd && sd->autobonus3[0].rate ) - { - for( i = 0; i < ARRAYLENGTH(sd->autobonus3); i++ ) - { - if( rnd()%1000 >= sd->autobonus3[i].rate ) - continue; - if( sd->autobonus3[i].active != INVALID_TIMER ) - continue; - if( sd->autobonus3[i].atk_type != skill_id ) - continue; - pc_exeautobonus(sd,&sd->autobonus3[i]); - } - } - - return 1; -} - -/* Splitted off from skill_additional_effect, which is never called when the - * attack skill kills the enemy. Place in this function counter status effects - * when using skills (eg: Asura's sp regen penalty, or counter-status effects - * from cards) that will take effect on the source, not the target. [Skotlex] - * Note: Currently this function only applies to Extremity Fist and BF_WEAPON - * type of skills, so not every instance of skill_additional_effect needs a call - * to this one. - */ -int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, unsigned int tick) -{ - int rate; - struct map_session_data *sd=NULL; - struct map_session_data *dstsd=NULL; - - nullpo_ret(src); - nullpo_ret(bl); - - if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest - - sd = BL_CAST(BL_PC, src); - dstsd = BL_CAST(BL_PC, bl); - - if(dstsd && attack_type&BF_WEAPON) - { //Counter effects. - enum sc_type type; - int i, time; - for(i=0; i < ARRAYLENGTH(dstsd->addeff2) && dstsd->addeff2[i].flag; i++) - { - rate = dstsd->addeff2[i].rate; - if (attack_type&BF_LONG) - rate+=dstsd->addeff2[i].arrow_rate; - if (!rate) continue; - - if ((dstsd->addeff2[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT)) - { //Trigger has range consideration. - if((dstsd->addeff2[i].flag&ATF_LONG && !(attack_type&BF_LONG)) || - (dstsd->addeff2[i].flag&ATF_SHORT && !(attack_type&BF_SHORT))) - continue; //Range Failed. - } - type = dstsd->addeff2[i].id; - time = skill_get_time2(status_sc2skill(type),7); - - if (dstsd->addeff2[i].flag&ATF_TARGET) - status_change_start(src,type,rate,7,0,0,0,time,0); - - if (dstsd->addeff2[i].flag&ATF_SELF && !status_isdead(bl)) - status_change_start(bl,type,rate,7,0,0,0,time,0); - } - } - - switch(skill_id){ - case MO_EXTREMITYFIST: - sc_start(src,SC_EXTREMITYFIST,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case GS_FULLBUSTER: - sc_start(src,SC_BLIND,2*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case HFLI_SBR44: //[orn] - case HVAN_EXPLOSION: - if(src->type == BL_HOM){ - TBL_HOM *hd = (TBL_HOM*)src; - hd->homunculus.intimacy = 200; - if (hd->master) - clif_send_homdata(hd->master,SP_INTIMATE,hd->homunculus.intimacy/100); - } - break; - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - attack_type |= BF_WEAPON; - break; - } - - if(sd && (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR && - rnd()%10000 < battle_config.sg_miracle_skill_ratio) //SG_MIRACLE [Komurka] - sc_start(src,SC_MIRACLE,100,1,battle_config.sg_miracle_skill_duration); - - if(sd && skill_id && attack_type&BF_MAGIC && status_isdead(bl) && - !(skill_get_inf(skill_id)&(INF_GROUND_SKILL|INF_SELF_SKILL)) && - (rate=pc_checkskill(sd,HW_SOULDRAIN))>0 - ){ //Soul Drain should only work on targetted spells [Skotlex] - if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex] - clif_skill_nodamage(src,bl,HW_SOULDRAIN,rate,1); - status_heal(src, 0, status_get_lv(bl)*(95+15*rate)/100, 2); - } - - if( sd && status_isdead(bl) ) { - int sp = 0, hp = 0; - if( attack_type&BF_WEAPON ) { - sp += sd->bonus.sp_gain_value; - sp += sd->sp_gain_race[status_get_race(bl)]; - sp += sd->sp_gain_race[is_boss(bl)?RC_BOSS:RC_NONBOSS]; - hp += sd->bonus.hp_gain_value; - } - if( attack_type&BF_MAGIC ) { - sp += sd->bonus.magic_sp_gain_value; - hp += sd->bonus.magic_hp_gain_value; - if( skill_id == WZ_WATERBALL ) {//(bugreport:5303) - struct status_change *sc = NULL; - if( ( sc = status_get_sc(src) ) ) { - if(sc->data[SC_SPIRIT] && - sc->data[SC_SPIRIT]->val2 == SL_WIZARD && - sc->data[SC_SPIRIT]->val3 == WZ_WATERBALL) - sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check. - } - } - } - if( hp || sp ) { // updated to force healing to allow healing through berserk - status_heal(src, hp, sp, battle_config.show_hp_sp_gain ? 3 : 1); - } - } - - // Trigger counter-spells to retaliate against damage causing skills. - if(dstsd && !status_isdead(bl) && dstsd->autospell2[0].id && - !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE)) - { - struct block_list *tbl; - struct unit_data *ud; - int i, skill_id, skill_lv, rate, type, notok; - - for (i = 0; i < ARRAYLENGTH(dstsd->autospell2) && dstsd->autospell2[i].id; i++) { - - if(!(dstsd->autospell2[i].flag&attack_type&BF_WEAPONMASK && - dstsd->autospell2[i].flag&attack_type&BF_RANGEMASK && - dstsd->autospell2[i].flag&attack_type&BF_SKILLMASK)) - continue; // one or more trigger conditions were not fulfilled - - skill_id = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id; - skill_lv = dstsd->autospell2[i].lv?dstsd->autospell2[i].lv:1; - if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv); - - rate = dstsd->autospell2[i].rate; - if (attack_type&BF_LONG) - rate>>=1; - - dstsd->state.autocast = 1; - notok = skillnotok(skill_id, dstsd); - dstsd->state.autocast = 0; - - if ( notok ) - continue; - - if (rnd()%1000 >= rate) - continue; - - tbl = (dstsd->autospell2[i].id < 0) ? bl : src; - - if( (type = skill_get_casttype(skill_id)) == CAST_GROUND ) { - int maxcount = 0; - if( !(BL_PC&battle_config.skill_reiteration) && - skill_get_unit_flag(skill_id)&UF_NOREITERATION && - skill_check_unit_range(bl,tbl->x,tbl->y,skill_id,skill_lv) - ) { - continue; - } - if( BL_PC&battle_config.skill_nofootset && - skill_get_unit_flag(skill_id)&UF_NOFOOTSET && - skill_check_unit_range2(bl,tbl->x,tbl->y,skill_id,skill_lv) - ) { - continue; - } - if( BL_PC&battle_config.land_skill_limit && - (maxcount = skill_get_maxcount(skill_id, skill_lv)) > 0 - ) { - int v; - for(v=0;v<MAX_SKILLUNITGROUP && dstsd->ud.skillunit[v] && maxcount;v++) { - if(dstsd->ud.skillunit[v]->skill_id == skill_id) - maxcount--; - } - if( maxcount == 0 ) { - continue; - } - } - } - - if( !battle_check_range(src, tbl, skill_get_range2(src, skill_id,skill_lv) + (skill_id == RG_CLOSECONFINE?0:1)) && battle_config.autospell_check_range ) - continue; - - dstsd->state.autocast = 1; - skill_consume_requirement(dstsd,skill_id,skill_lv,1); - switch (type) { - case CAST_GROUND: - skill_castend_pos2(bl, tbl->x, tbl->y, skill_id, skill_lv, tick, 0); - break; - case CAST_NODAMAGE: - skill_castend_nodamage_id(bl, tbl, skill_id, skill_lv, tick, 0); - break; - case CAST_DAMAGE: - skill_castend_damage_id(bl, tbl, skill_id, skill_lv, tick, 0); - break; - } - dstsd->state.autocast = 0; - //Set canact delay. [Skotlex] - ud = unit_bl2ud(bl); - if (ud) { - rate = skill_delayfix(bl, skill_id, skill_lv); - 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, 0, 0, 0); - } - } - } - } - - //Autobonus when attacked - if( dstsd && !status_isdead(bl) && dstsd->autobonus2[0].rate && !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE) ) - { - int i; - for( i = 0; i < ARRAYLENGTH(dstsd->autobonus2); i++ ) - { - if( rnd()%1000 >= dstsd->autobonus2[i].rate ) - continue; - if( dstsd->autobonus2[i].active != INVALID_TIMER ) - continue; - if(!(dstsd->autobonus2[i].atk_type&attack_type&BF_WEAPONMASK && - dstsd->autobonus2[i].atk_type&attack_type&BF_RANGEMASK && - dstsd->autobonus2[i].atk_type&attack_type&BF_SKILLMASK)) - continue; // one or more trigger conditions were not fulfilled - pc_exeautobonus(dstsd,&dstsd->autobonus2[i]); - } - } - - return 0; -} -/*========================================================================= - Breaks equipment. On-non players causes the corresponding strip effect. - - rate goes from 0 to 10000 (100.00%) - - flag is a BCT_ flag to indicate which type of adjustment should be used - (BCT_ENEMY/BCT_PARTY/BCT_SELF) are the valid values. ---------------------------------------------------------------------------*/ -int skill_break_equip (struct block_list *bl, unsigned short where, int rate, int flag) -{ - const int where_list[4] = {EQP_WEAPON, EQP_ARMOR, EQP_SHIELD, EQP_HELM}; - const enum sc_type scatk[4] = {SC_STRIPWEAPON, SC_STRIPARMOR, SC_STRIPSHIELD, SC_STRIPHELM}; - const enum sc_type scdef[4] = {SC_CP_WEAPON, SC_CP_ARMOR, SC_CP_SHIELD, SC_CP_HELM}; - struct status_change *sc = status_get_sc(bl); - int i,j; - TBL_PC *sd; - sd = BL_CAST(BL_PC, bl); - if (sc && !sc->count) - sc = NULL; - - if (sd) { - if (sd->bonus.unbreakable_equip) - where &= ~sd->bonus.unbreakable_equip; - if (sd->bonus.unbreakable) - rate -= rate*sd->bonus.unbreakable/100; - if (where&EQP_WEAPON) { - switch (sd->status.weapon) { - case W_FIST: //Bare fists should not break :P - case W_1HAXE: - case W_2HAXE: - case W_MACE: // Axes and Maces can't be broken [DracoRPG] - case W_2HMACE: - case W_STAFF: - case W_2HSTAFF: - case W_BOOK: //Rods and Books can't be broken [Skotlex] - case W_HUUMA: - where &= ~EQP_WEAPON; - } - } - } - if (flag&BCT_ENEMY) { - if (battle_config.equip_skill_break_rate != 100) - rate = rate*battle_config.equip_skill_break_rate/100; - } else if (flag&(BCT_PARTY|BCT_SELF)) { - if (battle_config.equip_self_break_rate != 100) - rate = rate*battle_config.equip_self_break_rate/100; - } - - for (i = 0; i < 4; i++) { - if (where&where_list[i]) { - if (sc && sc->count && sc->data[scdef[i]]) - where&=~where_list[i]; - else if (rnd()%10000 >= rate) - where&=~where_list[i]; - else if (!sd && !(status_get_mode(bl)&MD_BOSS)) //Cause Strip effect. - sc_start(bl,scatk[i],100,0,skill_get_time(status_sc2skill(scatk[i]),1)); - } - } - if (!where) //Nothing to break. - return 0; - if (sd) { - for (i = 0; i < EQI_MAX; i++) { - j = sd->equip_index[i]; - if (j < 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j]) - continue; - - switch(i) { - case EQI_HEAD_TOP: //Upper Head - flag = (where&EQP_HELM); - break; - case EQI_ARMOR: //Body - flag = (where&EQP_ARMOR); - break; - case EQI_HAND_R: //Left/Right hands - case EQI_HAND_L: - flag = ( - (where&EQP_WEAPON && sd->inventory_data[j]->type == IT_WEAPON) || - (where&EQP_SHIELD && sd->inventory_data[j]->type == IT_ARMOR)); - break; - case EQI_SHOES: - flag = (where&EQP_SHOES); - break; - case EQI_GARMENT: - flag = (where&EQP_GARMENT); - break; - default: - continue; - } - if (flag) { - sd->status.inventory[j].attribute = 1; - pc_unequipitem(sd, j, 3); - } - } - clif_equiplist(sd); - } - - return where; //Return list of pieces broken. -} - -int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int lv, int time) -{ - struct status_change *sc; - const int pos[5] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HELM, EQP_ACC}; - const enum sc_type sc_atk[5] = {SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC__STRIPACCESSORY}; - const enum sc_type sc_def[5] = {SC_CP_WEAPON, SC_CP_SHIELD, SC_CP_ARMOR, SC_CP_HELM, 0}; - int i; - - if (rnd()%100 >= rate) - return 0; - - sc = status_get_sc(bl); - if (!sc || sc->option&OPTION_MADOGEAR ) //Mado Gear cannot be divested [Ind] - return 0; - - for (i = 0; i < ARRAYLENGTH(pos); i++) { - if (where&pos[i] && sc->data[sc_def[i]]) - where&=~pos[i]; - } - if (!where) return 0; - - for (i = 0; i < ARRAYLENGTH(pos); i++) { - if (where&pos[i] && !sc_start(bl, sc_atk[i], 100, lv, time)) - where&=~pos[i]; - } - 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 - - 'direction' indicates the way OPPOSITE to the knockback direction (or -1 for default behavior) - - if 'flag&0x1', position update packets must not be sent. - - if 'flag&0x2', skill blown ignores players' special_state.no_knockback - -------------------------------------------------------------------------*/ -int skill_blown(struct block_list* src, struct block_list* target, int count, int8 dir, int flag) -{ - int dx = 0, dy = 0; - struct skill_unit* su = NULL; - - nullpo_ret(src); - - if (src != target && (map_flag_gvg(target->m) || map[target->m].flag.battleground)) - return 0; //No knocking back in WoE - if (count == 0) - return 0; //Actual knockback distance is 0. - - switch (target->type) { - case BL_MOB: { - struct mob_data* md = BL_CAST(BL_MOB, target); - if( md->class_ == MOBID_EMPERIUM ) - return 0; - if(src != target && is_boss(target)) //Bosses can't be knocked-back - return 0; - } - break; - case BL_PC: { - struct map_session_data *sd = BL_CAST(BL_PC, target); - if( sd->sc.data[SC_BASILICA] && sd->sc.data[SC_BASILICA]->val4 == sd->bl.id && !is_boss(src)) - return 0; // Basilica caster can't be knocked-back by normal monsters. - if( !(flag&0x2) && src != target && sd->special_state.no_knockback ) - return 0; - } - break; - case BL_SKILL: - su = (struct skill_unit *)target; - if( su && su->group && su->group->unit_id == UNT_ANKLESNARE ) - return 0; // ankle snare cannot be knocked back - break; - } - - if (dir == -1) // <optimized>: do the computation here instead of outside - dir = map_calc_dir(target, src->x, src->y); // direction from src to target, reversed - - if (dir >= 0 && dir < 8) - { // take the reversed 'direction' and reverse it - dx = -dirx[dir]; - dy = -diry[dir]; - } - - return unit_blown(target, dx, dy, count, flag); // send over the proper flag -} - - -//Checks if 'bl' should reflect back a spell cast by 'src'. -//type is the type of magic attack: 0: indirect (aoe), 1: direct (targetted) -static int skill_magic_reflect(struct block_list* src, struct block_list* bl, int type) -{ - struct status_change *sc = status_get_sc(bl); - struct map_session_data* sd = BL_CAST(BL_PC, bl); - - if( sc && sc->data[SC_KYOMU] ) // Nullify reflecting ability - return 0; - - // item-based reflection - if( sd && sd->bonus.magic_damage_return && type && rnd()%100 < sd->bonus.magic_damage_return ) - return 1; - - if( is_boss(src) ) - return 0; - - // status-based reflection - if( !sc || sc->count == 0 ) - return 0; - - if( sc->data[SC_MAGICMIRROR] && rnd()%100 < sc->data[SC_MAGICMIRROR]->val2 ) - return 1; - - if( sc->data[SC_KAITE] && (src->type == BL_PC || status_get_lv(src) <= 80) ) - {// Kaite only works against non-players if they are low-level. - clif_specialeffect(bl, 438, AREA); - if( --sc->data[SC_KAITE]->val2 <= 0 ) - status_change_end(bl, SC_KAITE, INVALID_TIMER); - return 2; - } - - return 0; -} - -/* - * ========================================================================= - * Does a skill attack with the given properties. - * src is the master behind the attack (player/mob/pet) - * dsrc is the actual originator of the damage, can be the same as src, or a BL_SKILL - * bl is the target to be attacked. - * flag can hold a bunch of information: - * flag&0xFFF is passed to the underlying battle_calc_attack for processing - * (usually holds number of targets, or just 1 for simple splash attacks) - * flag&0x1000 is used to tag that this is a splash-attack (so the damage - * packet shouldn't display a skill animation) - * flag&0x2000 is used to signal that the skill_lv should be passed as -1 to the - * client (causes player characters to not scream skill name) - *-------------------------------------------------------------------------*/ -int skill_attack (int attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag) -{ - struct Damage dmg; - struct status_data *sstatus, *tstatus; - struct status_change *sc; - struct map_session_data *sd, *tsd; - int type,damage,rdamage=0; - int8 rmdamage=0;//magic reflected - - if(skill_id > 0 && !skill_lv) return 0; - - nullpo_ret(src); //Source is the master behind the attack (player/mob/pet) - nullpo_ret(dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src. - nullpo_ret(bl); //Target to be attacked. - - if (src != dsrc) { - //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex] - if (!status_check_skilluse(battle_config.skill_caster_check?src:NULL, bl, skill_id, 2)) - return 0; - } else if ((flag&SD_ANIMATION) && skill_get_nk(skill_id)&NK_SPLASH) { - //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex] - if (!status_check_skilluse(src, bl, skill_id, 2)) - return 0; - } - - sd = BL_CAST(BL_PC, src); - tsd = BL_CAST(BL_PC, bl); - - sstatus = status_get_status_data(src); - tstatus = status_get_status_data(bl); - sc= status_get_sc(bl); - if (sc && !sc->count) sc = NULL; //Don't need it. - - // Is this check really needed? FrostNova won't hurt you if you step right where the caster is? - if(skill_id == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) - return 0; - //Trick Dead protects you from damage, but not from buffs and the like, hence it's placed here. - if (sc && sc->data[SC_TRICKDEAD] && !(sstatus->mode&MD_BOSS)) - return 0; - - dmg = battle_calc_attack(attack_type,src,bl,skill_id,skill_lv,flag&0xFFF); - - //Skotlex: Adjusted to the new system - if(src->type==BL_PET) - { // [Valaris] - struct pet_data *pd = (TBL_PET*)src; - if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skill_id) - { - int element = skill_get_ele(skill_id, skill_lv); - /*if (skill_id == -1) Does it ever worked? - element = sstatus->rhw.ele;*/ - if (element != ELE_NEUTRAL || !(battle_config.attack_attr_none&BL_PET)) - dmg.damage=battle_attr_fix(src, bl, skill_lv, element, tstatus->def_ele, tstatus->ele_lv); - else - dmg.damage= skill_lv; - dmg.damage2=0; - dmg.div_= pd->a_skill->div_; - } - } - - if( dmg.flag&BF_MAGIC && ( skill_id != NPC_EARTHQUAKE || (battle_config.eq_single_target_reflectable && (flag&0xFFF) == 1) ) ) - { // Earthquake on multiple targets is not counted as a target skill. [Inkfish] - if( (dmg.damage || dmg.damage2) && (type = skill_magic_reflect(src, bl, src==dsrc)) ) - { //Magic reflection, switch caster/target - struct block_list *tbl = bl; - rmdamage = 1; - bl = src; - src = tbl; - sd = BL_CAST(BL_PC, src); - tsd = BL_CAST(BL_PC, bl); - sc = status_get_sc(bl); - if (sc && !sc->count) - sc = NULL; //Don't need it. - /* bugreport:2564 flag&2 disables double casting trigger */ - flag |= 2; - - //Spirit of Wizard blocks Kaite's reflection - if( type == 2 && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD ) - { //Consume one Fragment per hit of the casted skill? [Skotlex] - type = tsd?pc_search_inventory (tsd, 7321):0; - if (type >= 0) { - if ( tsd ) pc_delitem(tsd, type, 1, 0, 1, LOG_TYPE_CONSUME); - dmg.damage = dmg.damage2 = 0; - dmg.dmg_lv = ATK_MISS; - sc->data[SC_SPIRIT]->val3 = skill_id; - sc->data[SC_SPIRIT]->val4 = dsrc->id; - } - } - - /** - * Official Magic Reflection Behavior : damage reflected depends on gears caster wears, not target - **/ - #if MAGIC_REFLECTION_TYPE - if( dmg.dmg_lv != ATK_MISS )//Wiz SL cancelled and consumed fragment - dmg = battle_calc_attack(BF_MAGIC,bl,bl,skill_id,skill_lv,flag&0xFFF); - #endif - } - if(sc && sc->data[SC_MAGICROD] && src == dsrc) { - int sp = skill_get_sp(skill_id,skill_lv); - dmg.damage = dmg.damage2 = 0; - dmg.dmg_lv = ATK_MISS; //This will prevent skill additional effect from taking effect. [Skotlex] - sp = sp * sc->data[SC_MAGICROD]->val2 / 100; - if(skill_id == WZ_WATERBALL && skill_lv > 1) - sp = sp/((skill_lv|1)*(skill_lv|1)); //Estimate SP cost of a single water-ball - status_heal(bl, 0, sp, 2); - } - } - - damage = dmg.damage + dmg.damage2; - - if( (skill_id == AL_INCAGI || skill_id == AL_BLESSING || - skill_id == CASH_BLESSING || skill_id == CASH_INCAGI || - skill_id == MER_INCAGI || skill_id == MER_BLESSING) && tsd->sc.data[SC_CHANGEUNDEAD] ) - damage = 1; - - if( damage > 0 && (( dmg.flag&BF_WEAPON && src != bl && ( src == dsrc || ( dsrc->type == BL_SKILL && ( skill_id == SG_SUN_WARM || skill_id == SG_MOON_WARM || skill_id == SG_STAR_WARM ) ) )) - || (sc && sc->data[SC_REFLECTDAMAGE])) ) - rdamage = battle_calc_return_damage(bl,src, &damage, dmg.flag, skill_id); - - if( damage && sc && sc->data[SC_GENSOU] && dmg.flag&BF_MAGIC ){ - struct block_list *nbl = NULL; - nbl = battle_getenemyarea(bl,bl->x,bl->y,2,BL_CHAR,bl->id); - if( nbl ){ // Only one target is chosen. - damage = damage / 2; // Deflect half of the damage to a target nearby - clif_skill_damage(bl, nbl, tick, status_get_amotion(src), 0, status_fix_damage(bl,nbl,damage,0), dmg.div_, OB_OBOROGENSOU_TRANSITION_ATK, -1, 6); - } - } - - //Skill hit type - type=(skill_id==0)?5:skill_get_hit(skill_id); - - if(damage < dmg.div_ - //Only skills that knockback even when they miss. [Skotlex] - && skill_id != CH_PALMSTRIKE) - dmg.blewcount = 0; - - if(skill_id == CR_GRANDCROSS||skill_id == NPC_GRANDDARKNESS) { - if(battle_config.gx_disptype) dsrc = src; - if(src == bl) type = 4; - else flag|=SD_ANIMATION; - } - if(skill_id == NJ_TATAMIGAESHI) { - dsrc = src; //For correct knockback. - flag|=SD_ANIMATION; - } - - if(sd) { - int flag = 0; //Used to signal if this skill can be combo'ed later on. - struct status_change_entry *sce; - if ((sce = sd->sc.data[SC_COMBO])) {//End combo state after skill is invoked. [Skotlex] - switch (skill_id) { - case TK_TURNKICK: - case TK_STORMKICK: - case TK_DOWNKICK: - case TK_COUNTER: - if (pc_famerank(sd->status.char_id,MAPID_TAEKWON)) {//Extend combo time. - sce->val1 = skill_id; //Update combo-skill - sce->val3 = skill_id; - if( sce->timer != INVALID_TIMER ) - delete_timer(sce->timer, status_change_timer); - sce->timer = add_timer(tick+sce->val4, status_change_timer, src->id, SC_COMBO); - break; - } - unit_cancel_combo(src); // Cancel combo wait - break; - default: - if( src == dsrc ) // Ground skills are exceptions. [Inkfish] - status_change_end(src, SC_COMBO, INVALID_TIMER); - } - } - switch(skill_id) { - case MO_TRIPLEATTACK: - if (pc_checkskill(sd, MO_CHAINCOMBO) > 0 || pc_checkskill(sd, SR_DRAGONCOMBO) > 0) - flag=1; - break; - case MO_CHAINCOMBO: - if(pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0) - flag=1; - break; - case MO_COMBOFINISH: - if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka] - party_skill_check(sd, sd->status.party_id, MO_COMBOFINISH, skill_lv); - if (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0) - flag=1; - case CH_TIGERFIST: - if (!flag && pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1) - flag=1; - case CH_CHAINCRUSH: - if (!flag && pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball > 0 && sd->sc.data[SC_EXPLOSIONSPIRITS]) - flag=1; - break; - case AC_DOUBLE: - if( (tstatus->race == RC_BRUTE || tstatus->race == RC_INSECT) && pc_checkskill(sd, HT_POWER)) - { - //TODO: This code was taken from Triple Blows, is this even how it should be? [Skotlex] - sc_start2(src,SC_COMBO,100,HT_POWER,bl->id,2000); - clif_combo_delay(src,2000); - } - break; - case TK_COUNTER: - { //bonus from SG_FRIEND [Komurka] - int level; - if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND))) - party_skill_check(sd, sd->status.party_id, TK_COUNTER,level); - } - break; - case SL_STIN: - case SL_STUN: - if (skill_lv >= 7 && !sd->sc.data[SC_SMA]) - sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv)); - break; - case GS_FULLBUSTER: - //Can't attack nor use items until skill's delay expires. [Skotlex] - sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick; - break; - case SR_DRAGONCOMBO: - if( pc_checkskill(sd, SR_FALLENEMPIRE) > 0 ) - flag = 1; - break; - case SR_FALLENEMPIRE: - if( pc_checkskill(sd, SR_TIGERCANNON) > 0 || pc_checkskill(sd, SR_GATEOFHELL) > 0 ) - flag = 1; - break; - } //Switch End - if (flag) { //Possible to chain - flag = DIFF_TICK(sd->ud.canact_tick, tick); - if (flag < 1) flag = 1; - sc_start2(src,SC_COMBO,100,skill_id,bl->id,flag); - clif_combo_delay(src, flag); - } - } - - //Display damage. - switch( skill_id ) - { - case PA_GOSPEL: //Should look like Holy Cross [Skotlex] - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5); - break; - //Skills that need be passed as a normal attack for the client to display correctly. - case HVAN_EXPLOSION: - case NPC_SELFDESTRUCTION: - if(src->type==BL_PC) - dmg.blewcount = 10; - dmg.amotion = 0; //Disable delay or attack will do no damage since source is dead by the time it takes effect. [Skotlex] - // fall through - case KN_AUTOCOUNTER: - case NPC_CRITICALSLASH: - case TF_DOUBLE: - case GS_CHAINACTION: - dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2); - break; - - case AS_SPLASHER: - if( flag&SD_ANIMATION ) // the surrounding targets - dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, 5); // needs -1 as skill level - else // the central target doesn't display an animation - dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, 5); // needs -2(!) as skill level - break; - case WL_HELLINFERNO: - case SR_EARTHSHAKER: - dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skill_id,-2,6); - break; - case WL_SOULEXPANSION: - case WL_COMET: - case KO_MUCHANAGE: - case NJ_HUUMA: - dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,skill_lv,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 LG_OVERBRAND_BRANDISH: - case LG_OVERBRAND_PLUSATK: - case EL_FIRE_BOMB: - case EL_FIRE_BOMB_ATK: - case EL_FIRE_WAVE: - case EL_FIRE_WAVE_ATK: - case EL_FIRE_MANTLE: - case EL_CIRCLE_OF_FIRE: - case EL_FIRE_ARROW: - case EL_ICE_NEEDLE: - case EL_WATER_SCREW: - case EL_WATER_SCREW_ATK: - case EL_WIND_SLASH: - case EL_TIDAL_WEAPON: - case EL_ROCK_CRUSHER: - case EL_ROCK_CRUSHER_ATK: - case EL_HURRICANE: - case EL_HURRICANE_ATK: - case KO_BAKURETSU: - case GN_CRAZYWEED_ATK: - dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,5); - break; - case GN_SLINGITEM_RANGEMELEEATK: - dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6); - break; - case EL_STONE_RAIN: - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,(flag&1)?8:5); - break; - case WM_SEVERE_RAINSTORM_MELEE: - dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,skill_lv,5); - break; - case WM_REVERBERATION_MELEE: - case WM_REVERBERATION_MAGIC: - dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_REVERBERATION,-2,6); - break; - case HT_CLAYMORETRAP: - case HT_BLASTMINE: - case HT_FLASHER: - case HT_FREEZINGTRAP: - case RA_CLUSTERBOMB: - case RA_FIRINGTRAP: - case RA_ICEBOUNDTRAP: - dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id,flag&SD_LEVEL?-1:skill_lv, 5); - if( dsrc != src ) // avoid damage display redundancy - break; - case HT_LANDMINE: - dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, type); - break; - case WZ_SIGHTBLASTER: - dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5); - break; - case AB_DUPLELIGHT_MELEE: - case AB_DUPLELIGHT_MAGIC: - dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */ - default: - if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit. - type = 5; - if( bl->type == BL_SKILL ){ - TBL_SKILL *su = (TBL_SKILL*)bl; - if( su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP )// show damage on trap targets - clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5); - } - dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, type); - break; - } - - map_freeblock_lock(); - - if(damage > 0 && dmg.flag&BF_SKILL && tsd - && pc_checkskill(tsd,RG_PLAGIARISM) - && (!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] - int copy_skill = skill_id; - /** - * Copy Referal: dummy skills should point to their source upon copying - **/ - switch( skill_id ) { - case AB_DUPLELIGHT_MELEE: - case AB_DUPLELIGHT_MAGIC: - copy_skill = AB_DUPLELIGHT; - break; - case WL_CHAINLIGHTNING_ATK: - copy_skill = WL_CHAINLIGHTNING; - break; - case WM_REVERBERATION_MELEE: - case WM_REVERBERATION_MAGIC: - copy_skill = WM_REVERBERATION; - break; - case WM_SEVERE_RAINSTORM_MELEE: - copy_skill = WM_SEVERE_RAINSTORM; - break; - case GN_CRAZYWEED_ATK: - copy_skill = GN_CRAZYWEED; - break; - case GN_HELLS_PLANT_ATK: - copy_skill = GN_HELLS_PLANT; - break; - case LG_OVERBRAND_BRANDISH: - case LG_OVERBRAND_PLUSATK: - copy_skill = LG_OVERBRAND; - 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; - if( sc && sc->data[SC__REPRODUCE] && (lv = sc->data[SC__REPRODUCE]->val1) ) { - //Level dependent and limitation. - lv = min(lv,skill_get_max(copy_skill)); - if( tsd->reproduceskill_id && tsd->status.skill[tsd->reproduceskill_id].flag == SKILL_FLAG_PLAGIARIZED ) { - tsd->status.skill[tsd->reproduceskill_id].id = 0; - tsd->status.skill[tsd->reproduceskill_id].lv = 0; - tsd->status.skill[tsd->reproduceskill_id].flag = 0; - clif_deleteskill(tsd,tsd->reproduceskill_id); - } - - tsd->reproduceskill_id = copy_skill; - pc_setglobalreg(tsd, "REPRODUCE_SKILL", copy_skill); - pc_setglobalreg(tsd, "REPRODUCE_SKILL_LV", lv); - - tsd->status.skill[copy_skill].id = copy_skill; - tsd->status.skill[copy_skill].lv = lv; - tsd->status.skill[copy_skill].flag = SKILL_FLAG_PLAGIARIZED; - clif_addskill(tsd,copy_skill); - } else { - lv = skill_lv; - if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED){ - tsd->status.skill[tsd->cloneskill_id].id = 0; - tsd->status.skill[tsd->cloneskill_id].lv = 0; - tsd->status.skill[tsd->cloneskill_id].flag = 0; - clif_deleteskill(tsd,tsd->cloneskill_id); - } - - if ((type = pc_checkskill(tsd,RG_PLAGIARISM)) < lv) - lv = type; - - tsd->cloneskill_id = copy_skill; - pc_setglobalreg(tsd, "CLONE_SKILL", copy_skill); - pc_setglobalreg(tsd, "CLONE_SKILL_LV", lv); - - tsd->status.skill[skill_id].id = copy_skill; - tsd->status.skill[skill_id].lv = lv; - tsd->status.skill[skill_id].flag = SKILL_FLAG_PLAGIARIZED; - clif_addskill(tsd,skill_id); - } - } - } - - if (dmg.dmg_lv >= ATK_MISS && (type = skill_get_walkdelay(skill_id, skill_lv)) > 0) - { //Skills with can't walk delay also stop normal attacking for that - //duration when the attack connects. [Skotlex] - struct unit_data *ud = unit_bl2ud(src); - if (ud && DIFF_TICK(ud->attackabletime, tick + type) < 0) - ud->attackabletime = tick + type; - } - - if( !dmg.amotion ) - { //Instant damage - if( !sc || (!sc->data[SC_DEVOTION] && skill_id != CR_REFLECTSHIELD) ) - status_fix_damage(src,bl,damage,dmg.dmotion); //Deal damage before knockback to allow stuff like firewall+storm gust combo. - if( !status_isdead(bl) ) - skill_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,dmg.dmg_lv,tick); - if( damage > 0 ) //Counter status effects [Skotlex] - skill_counter_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,tick); - } - // Hell Inferno burning status only starts if Fire part hits. - if( skill_id == WL_HELLINFERNO && dmg.damage > 0 ) - sc_start4(bl,SC_BURNING,55+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv)); - // Apply knock back chance in SC_TRIANGLESHOT skill. - else if( skill_id == SC_TRIANGLESHOT && rnd()%100 > (1 + skill_lv) ) - 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) - if (dmg.blewcount > 0 && bl!=dsrc && !status_isdead(bl)) { - int8 dir = -1; // default - switch(skill_id) {//direction - case MG_FIREWALL: - case PR_SANCTUARY: - case SC_TRIANGLESHOT: - case LG_OVERBRAND: - case SR_KNUCKLEARROW: - case GN_WALLOFTHORN: - case EL_FIRE_MANTLE: - dir = unit_getdir(bl);// backwards - break; - // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics. - case WZ_STORMGUST: - dir = rand()%8; - break; - case WL_CRIMSONROCK: - dir = map_calc_dir(bl,skill_area_temp[4],skill_area_temp[5]); - break; - - } - //blown-specific handling - switch( skill_id ) { - case LG_OVERBRAND: - if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) ) { - short dir_x, dir_y; - dir_x = dirx[(dir+4)%8]; - dir_y = diry[(dir+4)%8]; - if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 ) - skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag ); - } else - skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag ); - break; - case SR_KNUCKLEARROW: - if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) && !(flag&4) ) { - short dir_x, dir_y; - dir_x = dirx[(dir+4)%8]; - dir_y = diry[(dir+4)%8]; - if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 ) - skill_addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4); - } - break; - case GN_WALLOFTHORN: - unit_stop_walking(bl,1); - skill_blown(dsrc,bl,dmg.blewcount,dir, 0x2 ); - clif_fixpos(bl); - break; - default: - skill_blown(dsrc,bl,dmg.blewcount,dir, 0x0 ); - if ( !dmg.blewcount && bl->type == BL_SKILL && damage > 0 ){ - TBL_SKILL *su = (TBL_SKILL*)bl; - if( su->group && su->group->skill_id == HT_BLASTMINE) - skill_blown(src, bl, 3, -1, 0); - } - break; - } - } - - //Delayed damage must be dealt after the knockback (it needs to know actual position of target) - if (dmg.amotion) - battle_delay_damage(tick, dmg.amotion,src,bl,dmg.flag,skill_id,skill_lv,damage,dmg.dmg_lv,dmg.dmotion); - - if( sc && sc->data[SC_DEVOTION] && skill_id != PA_PRESSURE ) - { - struct status_change_entry *sce = sc->data[SC_DEVOTION]; - struct block_list *d_bl = map_id2bl(sce->val1); - - if( d_bl && ( - (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == bl->id) || - (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == bl->id) - ) && check_distance_bl(bl, d_bl, sce->val3) ) - { - if(!rmdamage){ - clif_damage(d_bl,d_bl, gettick(), 0, 0, damage, 0, 0, 0); - status_fix_damage(NULL,d_bl, damage, 0); - } - else{//Reflected magics are done directly on the target not on paladin - //This check is only for magical skill. - //For BF_WEAPON skills types track var rdamage and function battle_calc_return_damage - clif_damage(bl,bl, gettick(), 0, 0, damage, 0, 0, 0); - status_fix_damage(bl,bl, damage, 0); - } - } - else { - status_change_end(bl, SC_DEVOTION, INVALID_TIMER); - if( !dmg.amotion ) - status_fix_damage(src,bl,damage,dmg.dmotion); - } - } - - if(damage > 0 && !(tstatus->mode&MD_BOSS)) { - if( skill_id == RG_INTIMIDATE ) { - int rate = 50 + skill_lv * 5; - rate = rate + (status_get_lv(src) - status_get_lv(bl)); - if(rnd()%100 < rate) - skill_addtimerskill(src,tick + 800,bl->id,0,0,skill_id,skill_lv,0,flag); - } else if( skill_id == SC_FATALMENACE ) - skill_addtimerskill(src,tick + 800,bl->id,skill_area_temp[4],skill_area_temp[5],skill_id,skill_lv,0,flag); - } - - if(skill_id == CR_GRANDCROSS || skill_id == NPC_GRANDDARKNESS) - dmg.flag |= BF_WEAPON; - - if( sd && src != bl && damage > 0 && ( dmg.flag&BF_WEAPON || - (dmg.flag&BF_MISC && (skill_id == RA_CLUSTERBOMB || skill_id == RA_FIRINGTRAP || skill_id == RA_ICEBOUNDTRAP || skill_id == RK_DRAGONBREATH)) ) ) - { - if (battle_config.left_cardfix_to_right) - battle_drain(sd, bl, dmg.damage, dmg.damage, tstatus->race, tstatus->mode&MD_BOSS); - else - battle_drain(sd, bl, dmg.damage, dmg.damage2, tstatus->race, tstatus->mode&MD_BOSS); - } - - if( rdamage > 0 ) { - if( sc && sc->data[SC_REFLECTDAMAGE] ) { - if( src != bl )// Don't reflect your own damage (Grand Cross) - map_foreachinshootrange(battle_damage_area,bl,skill_get_splash(LG_REFLECTDAMAGE,1),BL_CHAR,tick,bl,dmg.amotion,sstatus->dmotion,rdamage,tstatus->race); - } else { - if( dmg.amotion ) - battle_delay_damage(tick, dmg.amotion,bl,src,0,CR_REFLECTSHIELD,0,rdamage,ATK_DEF,0); - else - status_fix_damage(bl,src,rdamage,0); - clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0); // in aegis damage reflected is shown in single hit. - //Use Reflect Shield to signal this kind of skill trigger. [Skotlex] - if( tsd && src != bl ) - 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 ) { - /** - * Post-damage effects - **/ - switch( skill_id ) { - case RK_CRUSHSTRIKE: - skill_break_equip(src,EQP_WEAPON,2000,BCT_SELF); // 20% chance to destroy the weapon. - break; - case GC_VENOMPRESSURE: { - struct status_change *ssc = status_get_sc(src); - if( ssc && ssc->data[SC_POISONINGWEAPON] && rnd()%100 < 70 + 5*skill_lv ) { - sc_start(bl,ssc->data[SC_POISONINGWEAPON]->val2,100,ssc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON, 1)); - status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - } - break; - case WM_METALICSOUND: - status_zap(bl, 0, damage*100/(100*(110-pc_checkskill(sd,WM_LESSON)*10))); - break; - case SR_TIGERCANNON: - status_zap(bl, 0, damage/10); // 10% of damage dealt - break; - } - if( sd ) - skill_onskillusage(sd, bl, skill_id, tick); - } - - if (!(flag&2) && - ( - skill_id == MG_COLDBOLT || skill_id == MG_FIREBOLT || skill_id == MG_LIGHTNINGBOLT - ) && - (sc = status_get_sc(src)) && - sc->data[SC_DOUBLECAST] && - rnd() % 100 < sc->data[SC_DOUBLECAST]->val2) - { -// skill_addtimerskill(src, tick + dmg.div_*dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2); - skill_addtimerskill(src, tick + dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2); - } - - map_freeblock_unlock(); - - return damage; -} - -/*========================================== - * sub fonction for recursive skill call. - * Checking bl battle flag and display dammage - * then call func with source,target,skill_id,skill_lv,tick,flag - *------------------------------------------*/ -typedef int (*SkillFunc)(struct block_list *, struct block_list *, int, int, unsigned int, int); -int skill_area_sub (struct block_list *bl, va_list ap) -{ - struct block_list *src; - uint16 skill_id,skill_lv; - int flag; - unsigned int tick; - SkillFunc func; - - nullpo_ret(bl); - - src=va_arg(ap,struct block_list *); - skill_id=va_arg(ap,int); - skill_lv=va_arg(ap,int); - tick=va_arg(ap,unsigned int); - flag=va_arg(ap,int); - func=va_arg(ap,SkillFunc); - - if(battle_check_target(src,bl,flag) > 0) - { - // several splash skills need this initial dummy packet to display correctly - if (flag&SD_PREAMBLE && skill_area_temp[2] == 0) - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - - if (flag&(SD_SPLASH|SD_PREAMBLE)) - skill_area_temp[2]++; - - return func(src,bl,skill_id,skill_lv,tick,flag); - } - return 0; -} - -static int skill_check_unit_range_sub (struct block_list *bl, va_list ap) -{ - struct skill_unit *unit; - uint16 skill_id,g_skill_id; - - unit = (struct skill_unit *)bl; - - if(bl->prev == NULL || bl->type != BL_SKILL) - return 0; - - if(!unit->alive) - return 0; - - skill_id = va_arg(ap,int); - g_skill_id = unit->group->skill_id; - - switch (skill_id) { - case MH_STEINWAND: - case MG_SAFETYWALL: - case AL_PNEUMA: - case SC_MAELSTROM: - if(g_skill_id != MH_STEINWAND && g_skill_id != MG_SAFETYWALL && g_skill_id != AL_PNEUMA && g_skill_id != SC_MAELSTROM) - return 0; - break; - case AL_WARP: - case HT_SKIDTRAP: - case MA_SKIDTRAP: - case HT_LANDMINE: - case MA_LANDMINE: - case HT_ANKLESNARE: - case HT_SHOCKWAVE: - case HT_SANDMAN: - case MA_SANDMAN: - case HT_FLASHER: - case HT_FREEZINGTRAP: - case MA_FREEZINGTRAP: - case HT_BLASTMINE: - case HT_CLAYMORETRAP: - case HT_TALKIEBOX: - case HP_BASILICA: - case RA_ELECTRICSHOCKER: - case RA_CLUSTERBOMB: - case RA_MAGENTATRAP: - case RA_COBALTTRAP: - case RA_MAIZETRAP: - case RA_VERDURETRAP: - case RA_FIRINGTRAP: - case RA_ICEBOUNDTRAP: - case SC_DIMENSIONDOOR: - case SC_BLOODYLUST: - //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set) - if (skill_id != g_skill_id && !(skill_get_inf2(g_skill_id)&INF2_TRAP) && g_skill_id != AS_VENOMDUST && g_skill_id != MH_POISON_MIST) - return 0; - break; - default: //Avoid stacking with same kind of trap. [Skotlex] - if (g_skill_id != skill_id) - return 0; - break; - } - - return 1; -} - -static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv) -{ - //Non players do not check for the skill's splash-trigger area. - int range = bl->type==BL_PC?skill_get_unit_range(skill_id, skill_lv):0; - int layout_type = skill_get_unit_layout_type(skill_id,skill_lv); - if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) { - ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skill_id); - return 0; - } - - range += layout_type; - return map_foreachinarea(skill_check_unit_range_sub,bl->m,x-range,y-range,x+range,y+range,BL_SKILL,skill_id); -} - -static int skill_check_unit_range2_sub (struct block_list *bl, va_list ap) -{ - uint16 skill_id; - - if(bl->prev == NULL) - return 0; - - skill_id = va_arg(ap,int); - - if( status_isdead(bl) && skill_id != AL_WARP ) - return 0; - - if( skill_id == HP_BASILICA && bl->type == BL_PC ) - return 0; - - if( skill_id == AM_DEMONSTRATION && bl->type == BL_MOB && ((TBL_MOB*)bl)->class_ == MOBID_EMPERIUM ) - return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex] - return 1; -} - -static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv) -{ - int range, type; - - switch (skill_id) { // to be expanded later - case WZ_ICEWALL: - range = 2; - break; - default: - { - int layout_type = skill_get_unit_layout_type(skill_id,skill_lv); - if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) { - ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skill_id); - return 0; - } - range = skill_get_unit_range(skill_id,skill_lv) + layout_type; - } - break; - } - - // if the caster is a monster/NPC, only check for players - // otherwise just check characters - if (bl->type == BL_PC) - type = BL_CHAR; - else - type = BL_PC; - - return map_foreachinarea(skill_check_unit_range2_sub, bl->m, - x - range, y - range, x + range, y + range, - type, skill_id); -} - -int skill_guildaura_sub (struct map_session_data* sd, int id, int strvit, int agidex) -{ - if(id == sd->bl.id && battle_config.guild_aura&16) - return 0; // Do not affect guild leader - - if (sd->sc.data[SC_GUILDAURA]) { - struct status_change_entry *sce = sd->sc.data[SC_GUILDAURA]; - if( sce->val3 != strvit || sce->val4 != agidex ) { - sce->val3 = strvit; - sce->val4 = agidex; - status_calc_bl(&sd->bl, status_sc2scb_flag(SC_GUILDAURA)); - } - return 0; - } - sc_start4(&sd->bl, SC_GUILDAURA,100, 1, id, strvit, agidex, 1000); - return 1; -} - -/*========================================== - * Checks that you have the requirements for casting a skill for homunculus/mercenary. - * Flag: - * &1: finished casting the skill (invoke hp/sp/item consumption) - * &2: picked menu entry (Warp Portal, Teleport and other menu based skills) - *------------------------------------------*/ -static int skill_check_condition_mercenary(struct block_list *bl, int skill, int lv, int type) -{ - struct status_data *status; - struct map_session_data *sd = NULL; - int i, hp, sp, hp_rate, sp_rate, state, mhp; - uint16 idx; - int itemid[MAX_SKILL_ITEM_REQUIRE],amount[ARRAYLENGTH(itemid)],index[ARRAYLENGTH(itemid)]; - - if( lv < 1 || lv > MAX_SKILL_LEVEL ) - return 0; - nullpo_ret(bl); - - switch( bl->type ) - { - case BL_HOM: sd = ((TBL_HOM*)bl)->master; break; - case BL_MER: sd = ((TBL_MER*)bl)->master; break; - } - - status = status_get_status_data(bl); - if( (idx = skill_get_index(skill)) == 0 ) - return 0; - - // Requeriments - for( i = 0; i < ARRAYLENGTH(itemid); i++ ) - { - itemid[i] = skill_db[idx].itemid[i]; - amount[i] = skill_db[idx].amount[i]; - } - hp = skill_db[idx].hp[lv-1]; - sp = skill_db[idx].sp[lv-1]; - hp_rate = skill_db[idx].hp_rate[lv-1]; - sp_rate = skill_db[idx].sp_rate[lv-1]; - state = skill_db[idx].state; - if( (mhp = skill_db[idx].mhp[lv-1]) > 0 ) - hp += (status->max_hp * mhp) / 100; - if( hp_rate > 0 ) - hp += (status->hp * hp_rate) / 100; - else - hp += (status->max_hp * (-hp_rate)) / 100; - if( sp_rate > 0 ) - sp += (status->sp * sp_rate) / 100; - else - sp += (status->max_sp * (-sp_rate)) / 100; - - if( bl->type == BL_HOM ) - { // Intimacy Requeriments - struct homun_data *hd = BL_CAST(BL_HOM, bl); - switch( skill ) - { - case HFLI_SBR44: - if( hd->homunculus.intimacy <= 200 ) - return 0; - break; - case HVAN_EXPLOSION: - if( hd->homunculus.intimacy < (unsigned int)battle_config.hvan_explosion_intimate ) - return 0; - break; - } - } - - if( !(type&2) ) - { - if( hp > 0 && status->hp <= (unsigned int)hp ) - { - clif_skill_fail(sd, skill, USESKILL_FAIL_HP_INSUFFICIENT, 0); - return 0; - } - if( sp > 0 && status->sp <= (unsigned int)sp ) - { - clif_skill_fail(sd, skill, USESKILL_FAIL_SP_INSUFFICIENT, 0); - return 0; - } - } - - if( !type ) - switch( state ) - { - case ST_MOVE_ENABLE: - if( !unit_can_move(bl) ) - { - clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0); - return 0; - } - break; - } - if( !(type&1) ) - return 1; - - // Check item existences - for( i = 0; i < ARRAYLENGTH(itemid); i++ ) - { - index[i] = -1; - if( itemid[i] < 1 ) continue; // No item - index[i] = pc_search_inventory(sd, itemid[i]); - if( index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i] ) - { - clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0); - return 0; - } - } - - // Consume items - for( i = 0; i < ARRAYLENGTH(itemid); i++ ) - { - if( index[i] >= 0 ) pc_delitem(sd, index[i], amount[i], 0, 1, LOG_TYPE_CONSUME); - } - - if( type&2 ) - return 1; - - if( sp || hp ) - status_zap(bl, hp, sp); - - return 1; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_area_sub_count (struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag) -{ - return 1; -} - -/*========================================== - * - *------------------------------------------*/ -static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data) -{ - struct block_list *src = map_id2bl(id),*target; - struct unit_data *ud = unit_bl2ud(src); - struct skill_timerskill *skl = NULL; - int range; - - nullpo_ret(src); - nullpo_ret(ud); - skl = ud->skilltimerskill[data]; - nullpo_ret(skl); - ud->skilltimerskill[data] = NULL; - - do { - if(src->prev == NULL) - break; // Source not on Map - if(skl->target_id) { - target = map_id2bl(skl->target_id); - if( ( skl->skill_id == RG_INTIMIDATE || skl->skill_id == SC_FATALMENACE ) && (!target || target->prev == NULL || !check_distance_bl(src,target,AREA_SIZE)) ) - target = src; //Required since it has to warp. - if(target == NULL) - break; // Target offline? - if(target->prev == NULL) - break; // Target not on Map - if(src->m != target->m) - break; // Different Maps - if(status_isdead(src)) - break; // Caster is Dead - if(status_isdead(target) && skl->skill_id != RG_INTIMIDATE && skl->skill_id != WZ_WATERBALL) - break; - - switch(skl->skill_id) { - case RG_INTIMIDATE: - if (unit_warp(src,-1,-1,-1,CLR_TELEPORT) == 0) { - short x,y; - map_search_freecell(src, 0, &x, &y, 1, 1, 0); - if (target != src && !status_isdead(target)) - unit_warp(target, -1, x, y, CLR_TELEPORT); - } - break; - case BA_FROSTJOKER: - case DC_SCREAM: - range= skill_get_splash(skl->skill_id, skl->skill_lv); - map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range, - skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick); - break; - case NPC_EARTHQUAKE: - if( skl->type > 1 ) - skill_addtimerskill(src,tick+250,src->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag); - skill_area_temp[0] = map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR, src, skl->skill_id, skl->skill_lv, tick, BCT_ENEMY, skill_area_sub_count); - skill_area_temp[1] = src->id; - skill_area_temp[2] = 0; - map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), splash_target(src), src, skl->skill_id, skl->skill_lv, tick, skl->flag, skill_castend_damage_id); - break; - case WZ_WATERBALL: - skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify - if (!status_isdead(target)) - skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); - if (skl->type>1 && !status_isdead(target) && !status_isdead(src)) { - skill_addtimerskill(src,tick+125,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag); - } else { - struct status_change *sc = status_get_sc(src); - if(sc) { - if(sc->data[SC_SPIRIT] && - sc->data[SC_SPIRIT]->val2 == SL_WIZARD && - sc->data[SC_SPIRIT]->val3 == skl->skill_id) - sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check. - } - } - break; - /** - * Warlock - **/ - case WL_CHAINLIGHTNING_ATK: - { - 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 - skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify - 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|SD_ANIMATION); - skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify - if( skl->type >= 3 ) - { // Final Hit - 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[rnd()%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; - case WM_REVERBERATION_MELEE: - case WM_REVERBERATION_MAGIC: - skill_castend_damage_id(src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL); // damage should split among targets - break; - case SC_FATALMENACE: - if( src == target ) // Casters Part - unit_warp(src, -1, skl->x, skl->y, 3); - else { // Target's Part - short x = skl->x, y = skl->y; - map_search_freecell(NULL, target->m, &x, &y, 2, 2, 1); - unit_warp(target,-1,x,y,3); - } - break; - case LG_MOONSLASHER: - case SR_WINDMILL: - if( target->type == BL_PC ) { - struct map_session_data *tsd = NULL; - if( (tsd = ((TBL_PC*)target)) && !pc_issit(tsd) ) { - pc_setsit(tsd); - skill_sit(tsd,1); - clif_sitting(&tsd->bl); - } - } - break; - case LG_OVERBRAND_BRANDISH: - case LG_OVERBRAND_PLUSATK: - case SR_KNUCKLEARROW: - skill_attack(BF_WEAPON, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL); - break; - case GN_SPORE_EXPLOSION: - map_foreachinrange(skill_area_sub, target, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR, - src, skl->skill_id, skl->skill_lv, 0, skl->flag|1|BCT_ENEMY, skill_castend_damage_id); - break; - case CH_PALMSTRIKE: - { - struct status_change* tsc = status_get_sc(target); - struct status_change* sc = status_get_sc(src); - if( tsc && tsc->option&OPTION_HIDE || - sc && sc->option&OPTION_HIDE ){ - skill_blown(src,target,skill_get_blewcount(skl->skill_id, skl->skill_lv), -1, 0x0 ); - break; - } - } - default: - skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); - break; - } - } - else { - if(src->m != skl->map) - break; - switch( skl->skill_id ) - { - case WZ_METEOR: - if( skl->type >= 0 ) - { - int x = skl->type>>16, y = skl->type&0xFFFF; - if( path_search_long(NULL, src->m, src->x, src->y, x, y, CELL_CHKWALL) ) - skill_unitsetting(src,skl->skill_id,skl->skill_lv,x,y,skl->flag); - if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) ) - clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick); - } - 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 GN_CRAZYWEED_ATK: - { - int dummy = 1, i = skill_get_unit_range(skl->skill_id,skl->skill_lv); - map_foreachinarea(skill_cell_overlap, src->m, skl->x-i, skl->y-i, skl->x+i, skl->y+i, BL_SKILL, skl->skill_id, &dummy, src); - } - case WL_EARTHSTRAIN: - skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag); - break; - - } - } - } while (0); - //Free skl now that it is no longer needed. - ers_free(skill_timer_ers, skl); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_addtimerskill (struct block_list *src, unsigned int tick, int target, int x,int y, uint16 skill_id, uint16 skill_lv, int type, int flag) -{ - int i; - struct unit_data *ud; - nullpo_retr(1, src); - if (src->prev == NULL) - return 0; - ud = unit_bl2ud(src); - nullpo_retr(1, ud); - - ARR_FIND( 0, MAX_SKILLTIMERSKILL, i, ud->skilltimerskill[i] == 0 ); - if( i == MAX_SKILLTIMERSKILL ) return 1; - - ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill); - ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i); - ud->skilltimerskill[i]->src_id = src->id; - ud->skilltimerskill[i]->target_id = target; - ud->skilltimerskill[i]->skill_id = skill_id; - ud->skilltimerskill[i]->skill_lv = skill_lv; - ud->skilltimerskill[i]->map = src->m; - ud->skilltimerskill[i]->x = x; - ud->skilltimerskill[i]->y = y; - ud->skilltimerskill[i]->type = type; - ud->skilltimerskill[i]->flag = flag; - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_cleartimerskill (struct block_list *src) -{ - int i; - struct unit_data *ud; - nullpo_ret(src); - ud = unit_bl2ud(src); - nullpo_ret(ud); - - for(i=0;i<MAX_SKILLTIMERSKILL;i++) { - if(ud->skilltimerskill[i]) { - delete_timer(ud->skilltimerskill[i]->timer, skill_timerskill); - ers_free(skill_timer_ers, ud->skilltimerskill[i]); - ud->skilltimerskill[i]=NULL; - } - } - return 1; -} -static int skill_ative_reverberation( struct block_list *bl, va_list ap) { - struct skill_unit *su = (TBL_SKILL*)bl; - struct skill_unit_group *sg; - if( bl->type != BL_SKILL ) - return 0; - if( su->alive && (sg = su->group) && sg->skill_id == WM_REVERBERATION ) { - map_foreachinrange(skill_trap_splash, bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, bl, gettick()); - su->limit=DIFF_TICK(gettick(),sg->tick); - sg->unit_id = UNT_USED_TRAPS; - } - return 0; -} - -static int skill_reveal_trap (struct block_list *bl, va_list ap) -{ - TBL_SKILL *su = (TBL_SKILL*)bl; - if (su->alive && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP) - { //Reveal trap. - //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex] - //clif_changetraplook(bl, su->group->unit_id); - clif_skill_setunit(su); - return 1; - } - return 0; -} - -/*========================================== - * - * - *------------------------------------------*/ -int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag) -{ - struct map_session_data *sd = NULL; - struct status_data *tstatus; - struct status_change *sc; - - if (skill_id > 0 && !skill_lv) return 0; - - nullpo_retr(1, src); - nullpo_retr(1, bl); - - if (src->m != bl->m) - return 1; - - if (bl->prev == NULL) - return 1; - - sd = BL_CAST(BL_PC, src); - - if (status_isdead(bl)) - return 1; - - if (skill_id && skill_get_type(skill_id) == BF_MAGIC && status_isimmune(bl) == 100) - { //GTB makes all targetted magic display miss with a single bolt. - sc_type sct = status_skill2sc(skill_id); - if(sct != SC_NONE) - status_change_end(bl, sct, INVALID_TIMER); - clif_skill_damage(src, bl, tick, status_get_amotion(src), status_get_dmotion(bl), 0, 1, skill_id, skill_lv, skill_get_hit(skill_id)); - return 1; - } - - sc = status_get_sc(src); - if (sc && !sc->count) - sc = NULL; //Unneeded - - tstatus = status_get_status_data(bl); - - map_freeblock_lock(); - - switch(skill_id) - { - case MER_CRASH: - case SM_BASH: - case MS_BASH: - case MC_MAMMONITE: - case TF_DOUBLE: - case AC_DOUBLE: - case MA_DOUBLE: - case AS_SONICBLOW: - case KN_PIERCE: - case ML_PIERCE: - case KN_SPEARBOOMERANG: - case TF_POISON: - case TF_SPRINKLESAND: - case AC_CHARGEARROW: - case MA_CHARGEARROW: - case RG_INTIMIDATE: - case AM_ACIDTERROR: - case BA_MUSICALSTRIKE: - case DC_THROWARROW: - case BA_DISSONANCE: - case CR_HOLYCROSS: - case NPC_DARKCROSS: - case CR_SHIELDCHARGE: - case CR_SHIELDBOOMERANG: - case NPC_PIERCINGATT: - case NPC_MENTALBREAKER: - case NPC_RANGEATTACK: - case NPC_CRITICALSLASH: - case NPC_COMBOATTACK: - case NPC_GUIDEDATTACK: - case NPC_POISON: - case NPC_RANDOMATTACK: - case NPC_WATERATTACK: - case NPC_GROUNDATTACK: - case NPC_FIREATTACK: - case NPC_WINDATTACK: - case NPC_POISONATTACK: - case NPC_HOLYATTACK: - case NPC_DARKNESSATTACK: - case NPC_TELEKINESISATTACK: - case NPC_UNDEADATTACK: - case NPC_ARMORBRAKE: - case NPC_WEAPONBRAKER: - case NPC_HELMBRAKE: - case NPC_SHIELDBRAKE: - case NPC_BLINDATTACK: - case NPC_SILENCEATTACK: - case NPC_STUNATTACK: - case NPC_PETRIFYATTACK: - case NPC_CURSEATTACK: - case NPC_SLEEPATTACK: - case LK_AURABLADE: - case LK_SPIRALPIERCE: - case ML_SPIRALPIERCE: - case LK_HEADCRUSH: - case CG_ARROWVULCAN: - case HW_MAGICCRASHER: - case ITM_TOMAHAWK: - case MO_TRIPLEATTACK: - case CH_CHAINCRUSH: - case CH_TIGERFIST: - case PA_SHIELDCHAIN: // Shield Chain - case PA_SACRIFICE: - case WS_CARTTERMINATION: // Cart Termination - case AS_VENOMKNIFE: - case HT_PHANTASMIC: - case HT_POWER: - case TK_DOWNKICK: - case TK_COUNTER: - case GS_CHAINACTION: - case GS_TRIPLEACTION: - case GS_MAGICALBULLET: - case GS_TRACKING: - case GS_PIERCINGSHOT: - case GS_RAPIDSHOWER: - case GS_DUST: - case GS_DISARM: // Added disarm. [Reddozen] - case GS_FULLBUSTER: - case NJ_SYURIKEN: - case NJ_KUNAI: - case ASC_BREAKER: - case HFLI_MOON: //[orn] - case HFLI_SBR44: //[orn] - case NPC_BLEEDING: - case NPC_CRITICALWOUND: - case NPC_HELLPOWER: - case RK_SONICWAVE: - case RK_HUNDREDSPEAR: - case AB_DUPLELIGHT_MELEE: - case RA_AIMEDBOLT: - case NC_AXEBOOMERANG: - case NC_POWERSWING: - case GC_CROSSIMPACT: - case GC_VENOMPRESSURE: - case SC_TRIANGLESHOT: - case SC_FEINTBOMB: - case LG_BANISHINGPOINT: - case LG_SHIELDPRESS: - case LG_RAGEBURST: - case LG_RAYOFGENESIS: - case LG_HESPERUSLIT: - case SR_FALLENEMPIRE: - case SR_CRESCENTELBOW_AUTOSPELL: - case SR_GATEOFHELL: - case SR_GENTLETOUCH_QUIET: - case WM_SEVERE_RAINSTORM_MELEE: - case WM_GREAT_ECHO: - case GN_SLINGITEM_RANGEMELEEATK: - case KO_JYUMONJIKIRI: - case KO_SETSUDAN: - case KO_KAIHOU: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,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); - case RK_WINDCUTTER: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION); - break; - - case LK_JOINTBEAT: // decide the ailment first (affects attack damage and effect) - switch( rnd()%6 ){ - case 0: flag |= BREAK_ANKLE; break; - case 1: flag |= BREAK_WRIST; break; - case 2: flag |= BREAK_KNEE; break; - case 3: flag |= BREAK_SHOULDER; break; - case 4: flag |= BREAK_WAIST; break; - case 5: flag |= BREAK_NECK; break; - } - //TODO: is there really no cleaner way to do this? - sc = status_get_sc(bl); - if (sc) sc->jb_flag = flag; - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case MO_COMBOFINISH: - if (!(flag&1) && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK) - { //Becomes a splash attack when Soul Linked. - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skill_id, skill_lv),splash_target(src), - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - } else - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case TK_STORMKICK: // Taekwon kicks [Dralnu] - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_area_temp[1] = 0; - map_foreachinrange(skill_attack_area, src, - skill_get_splash(skill_id, skill_lv), splash_target(src), - BF_WEAPON, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY); - break; - - case KN_CHARGEATK: - { - bool path = path_search_long(NULL, src->m, src->x, src->y, bl->x, bl->y,CELL_CHKWALL); - unsigned int dist = distance_bl(src, bl); - uint8 dir = map_calc_dir(bl, src->x, src->y); - - // teleport to target (if not on WoE grounds) - if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 0, 1) ) - clif_slide(src, bl->x, bl->y); - - // cause damage and knockback if the path to target was a straight one - if( path ) - { - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, dist); - skill_blown(src, bl, dist, dir, 0); - //HACK: since knockback officially defaults to the left, the client also turns to the left... therefore, - // make the caster look in the direction of the target - unit_setdir(src, (dir+4)%8); - } - - } - break; - - case NC_FLAMELAUNCHER: - if (sd) pc_overheat(sd,1); - case SN_SHARPSHOOTING: - case MA_SHARPSHOOTING: - case NJ_KAMAITACHI: - case LG_CANNONSPEAR: - //It won't shoot through walls since on castend there has to be a direct - //line of sight between caster and target. - skill_area_temp[1] = bl->id; - map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y, - skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src), - skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY); - break; - - case NPC_ACIDBREATH: - case NPC_DARKNESSBREATH: - case NPC_FIREBREATH: - case NPC_ICEBREATH: - case NPC_THUNDERBREATH: - skill_area_temp[1] = bl->id; - map_foreachinpath(skill_attack_area,src->m,src->x,src->y,bl->x,bl->y, - skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src), - skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY); - break; - - case MO_INVESTIGATE: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - status_change_end(src, SC_BLADESTOP, INVALID_TIMER); - break; - - case RG_BACKSTAP: - { - uint8 dir = map_calc_dir(src, bl->x, bl->y), t_dir = unit_getdir(bl); - if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) { - status_change_end(src, SC_HIDING, INVALID_TIMER); - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest] - unit_setdir(bl,dir); - } - else if (sd) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - - case MO_FINGEROFFENSIVE: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - if (battle_config.finger_offensive_type && sd) { - int i; - for (i = 1; i < sd->spiritball_old; i++) - skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag); - } - status_change_end(src, SC_BLADESTOP, INVALID_TIMER); - break; - - case MO_CHAINCOMBO: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - status_change_end(src, SC_BLADESTOP, INVALID_TIMER); - break; - - case NJ_ISSEN: - status_change_end(src, SC_NEN, INVALID_TIMER); - status_change_end(src, SC_HIDING, INVALID_TIMER); - // fall through - case MO_EXTREMITYFIST: - { - short x, y, i = 2; // Move 2 cells for Issen(from target) - struct block_list *mbl = bl; - short dir = 0; - - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - - if( skill_id == MO_EXTREMITYFIST ) - { - mbl = src; - i = 3; // for Asura(from caster) - status_set_sp(src, 0, 0); - status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER); - status_change_end(src, SC_BLADESTOP, INVALID_TIMER); -#ifdef RENEWAL - sc_start(src,SC_EXTREMITYFIST2,100,skill_lv,skill_get_time(skill_id,skill_lv)); -#endif - }else - status_set_hp(src, -#ifdef RENEWAL - max(status_get_max_hp(src)/100, 1) -#else - 1 -#endif - , 0); - - dir = map_calc_dir(src,bl->x,bl->y); - if( dir > 0 && dir < 4) x = -i; - else if( dir > 4 ) x = i; - else x = 0; - if( dir > 2 && dir < 6 ) y = -i; - else if( dir == 7 || dir < 2 ) y = i; - else y = 0; - if( (mbl == src || !map_flag_gvg(src->m) && !map[src->m].flag.battleground) && // only NJ_ISSEN don't have slide effect in GVG - unit_movepos(src, mbl->x+x, mbl->y+y, 1, 1) ) { - clif_slide(src, src->x, src->y); - //uncomment this if you want to remove MO_EXTREMITYFIST glitchy walking effect. [malufett] - //clif_fixpos(src); - } - } - break; - - //Splash attack skills. - case AS_GRIMTOOTH: - case MC_CARTREVOLUTION: - case NPC_SPLASHATTACK: - flag |= SD_PREAMBLE; // a fake packet will be sent for the first target to be hit - case AS_SPLASHER: - case SM_MAGNUM: - case MS_MAGNUM: - case HT_BLITZBEAT: - case AC_SHOWER: - case MA_SHOWER: - case MG_NAPALMBEAT: - case MG_FIREBALL: - case RG_RAID: - case HW_NAPALMVULCAN: - case NJ_HUUMA: - case NJ_BAKUENRYU: - case ASC_METEORASSAULT: - case GS_DESPERADO: - case GS_SPREADATTACK: - case NPC_EARTHQUAKE: - case NPC_PULSESTRIKE: - case NPC_HELLJUDGEMENT: - case NPC_VAMPIRE_GIFT: - case RK_IGNITIONBREAK: - case AB_JUDEX: - case WL_SOULEXPANSION: - case WL_CRIMSONROCK: - case WL_COMET: - case WL_JACKFROST: - case RA_ARROWSTORM: - case RA_WUGDASH: - case NC_SELFDESTRUCTION: - case NC_AXETORNADO: - case GC_ROLLINGCUTTER: - case GC_COUNTERSLASH: - case LG_MOONSLASHER: - case LG_EARTHDRIVE: - case SR_TIGERCANNON: - case SR_RAMPAGEBLASTER: - case SR_SKYNETBLOW: - case SR_WINDMILL: - case SR_RIDEINLIGHTNING: - case WM_SOUND_OF_DESTRUCTION: - case WM_REVERBERATION_MELEE: - case WM_REVERBERATION_MAGIC: - case SO_VARETYR_SPEAR: - case GN_CART_TORNADO: - case GN_CARTCANNON: - case KO_HAPPOKUNAI: - case KO_HUUMARANKA: - case KO_MUCHANAGE: - case KO_BAKURETSU: - if( flag&1 ) {//Recursive invocation - // skill_area_temp[0] holds number of targets in area - // skill_area_temp[1] holds the id of the original target - // skill_area_temp[2] counts how many targets have already been processed - int sflag = skill_area_temp[0] & 0xFFF, heal; - if( flag&SD_LEVEL ) - sflag |= SD_LEVEL; // -1 will be used in packets instead of the skill level - if( skill_area_temp[1] != bl->id && !(skill_get_inf2(skill_id)&INF2_NPC_SKILL) ) - sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills) - - heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, sflag); - if( skill_id == NPC_VAMPIRE_GIFT && heal > 0 ) { - clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1); - status_heal(src,heal,0,0); - } - } else { - switch ( skill_id ) { - case NJ_BAKUENRYU: - case LG_EARTHDRIVE: - case GN_CARTCANNON: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case LG_MOONSLASHER: - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - break; - case NPC_EARTHQUAKE://FIXME: Isn't EarthQuake a ground skill after all? - skill_addtimerskill(src,tick+250,src->id,0,0,skill_id,skill_lv,2,flag|BCT_ENEMY|SD_SPLASH|1); - default: - break; - } - - skill_area_temp[0] = 0; - skill_area_temp[1] = bl->id; - skill_area_temp[2] = 0; - if( skill_id == WL_CRIMSONROCK ) { - skill_area_temp[4] = bl->x; - skill_area_temp[5] = bl->y; - } - if( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC ) - skill_area_temp[1] = 0; - // 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 - if( flag&SD_LEVEL || skill_get_nk(skill_id)&NK_SPLASHSPLIT ) - skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, (skill_id == AS_SPLASHER)?1:skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, 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(skill_id, skill_lv), ( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC )?BL_CHAR:splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); - } - break; - - case KN_BRANDISHSPEAR: - case ML_BRANDISH: - //Coded apart for it needs the flag passed to the damage calculation. - if (skill_area_temp[1] != bl->id) - skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION); - else - skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); - break; - - case KN_BOWLINGBASH: - case MS_BOWLINGBASH: - if(flag&1){ - if(bl->id==skill_area_temp[1]) - break; - //two hits for 500% - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION); - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION); - } else { - int i,c; - c = skill_get_blewcount(skill_id,skill_lv); - // keep moving target in the direction that src is looking, square by square - for(i=0;i<c;i++){ - if (!skill_blown(src,bl,1,(unit_getdir(src)+4)%8,0x1)) - break; //Can't knockback - skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_area_sub_count); - if( skill_area_temp[0] > 1 ) break; // collision - } - clif_blown(bl); //Update target pos. - if (i!=c) { //Splash - skill_area_temp[1] = bl->id; - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id); - } - //Weirdo dual-hit property, two attacks for 500% - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0); - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0); - } - break; - - case KN_SPEARSTAB: - if(flag&1) { - if (bl->id==skill_area_temp[1]) - break; - if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION)) - skill_blown(src,bl,skill_area_temp[2],-1,0); - } else { - int x=bl->x,y=bl->y,i,dir; - dir = map_calc_dir(bl,src->x,src->y); - skill_area_temp[1] = bl->id; - skill_area_temp[2] = skill_get_blewcount(skill_id,skill_lv); - // all the enemies between the caster and the target are hit, as well as the target - if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0)) - skill_blown(src,bl,skill_area_temp[2],-1,0); - for (i=0;i<4;i++) { - map_foreachincell(skill_area_sub,bl->m,x,y,BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - x += dirx[dir]; - y += diry[dir]; - } - } - break; - - case TK_TURNKICK: - case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex] - { - skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target. - if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag)) - map_foreachinrange(skill_area_sub,bl, - skill_get_splash(skill_id, skill_lv),BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1, - skill_castend_nodamage_id); - } - break; - case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex] - // clif_skill_nodamage(src,bl,skill_id,skill_lv,0); //Can't make this one display the correct attack animation delay :/ - clif_damage(src,bl,tick,status_get_amotion(src),0,-1,1,4,0); //Display an absorbed damage attack. - skill_addtimerskill(src, tick + (1000+status_get_amotion(src)), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag); - break; - - case PR_TURNUNDEAD: - case ALL_RESURRECTION: - if (!battle_check_undead(tstatus->race, tstatus->def_ele)) - break; - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case MG_SOULSTRIKE: - case NPC_DARKSTRIKE: - case MG_COLDBOLT: - case MG_FIREBOLT: - case MG_LIGHTNINGBOLT: - case WZ_EARTHSPIKE: - case AL_HEAL: - case AL_HOLYLIGHT: - case WZ_JUPITEL: - case NPC_DARKTHUNDER: - case PR_ASPERSIO: - case MG_FROSTDIVER: - case WZ_SIGHTBLASTER: - case WZ_SIGHTRASHER: - case NJ_KOUENKA: - case NJ_HYOUSENSOU: - case NJ_HUUJIN: - case AB_ADORAMUS: - case AB_RENOVATIO: - case AB_HIGHNESSHEAL: - case AB_DUPLELIGHT_MAGIC: - case WM_METALICSOUND: - case MH_ERASER_CUTTER: - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case NPC_MAGICALATTACK: - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - sc_start(src,status_skill2sc(skill_id),100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - - case HVAN_CAPRICE: //[blackhole89] - { - int ran=rnd()%4; - int sid = 0; - switch(ran) - { - case 0: sid=MG_COLDBOLT; break; - case 1: sid=MG_FIREBOLT; break; - case 2: sid=MG_LIGHTNINGBOLT; break; - case 3: sid=WZ_EARTHSPIKE; break; - } - skill_attack(BF_MAGIC,src,src,bl,sid,skill_lv,tick,flag|SD_LEVEL); - } - break; - case WZ_WATERBALL: - { - int range = skill_lv / 2; - int maxlv = skill_get_max(skill_id); // learnable level - int count = 0; - int x, y; - struct skill_unit* unit; - - if( skill_lv > maxlv ) - { - if( src->type == BL_MOB && skill_lv == 10 ) - range = 4; - else - range = maxlv / 2; - } - - for( y = src->y - range; y <= src->y + range; ++y ) - for( x = src->x - range; x <= src->x + range; ++x ) - { - if( !map_find_skill_unit_oncell(src,x,y,SA_LANDPROTECTOR,NULL,1) ) - { - if( src->type != BL_PC || map_getcell(src->m,x,y,CELL_CHKWATER) ) // non-players bypass the water requirement - count++; // natural water cell - else if( (unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL,1)) != NULL || (unit = map_find_skill_unit_oncell(src,x,y,NJ_SUITON,NULL,1)) != NULL ) - { - count++; // skill-induced water cell - skill_delunit(unit); // consume cell - } - } - } - - if( count > 1 ) // queue the remaining count - 1 timerskill Waterballs - skill_addtimerskill(src,tick+150,bl->id,0,0,skill_id,skill_lv,count-1,flag); - } - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case PR_BENEDICTIO: - //Should attack undead and demons. [Skotlex] - if (battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON) - skill_attack(BF_MAGIC, src, src, bl, skill_id, skill_lv, tick, flag); - break; - - case SL_SMA: - status_change_end(src, SC_SMA, INVALID_TIMER); - case SL_STIN: - case SL_STUN: - if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { - status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10); - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case NPC_DARKBREATH: - clif_emotion(src,E_AG); - case SN_FALCONASSAULT: - case PA_PRESSURE: - case CR_ACIDDEMONSTRATION: - case TF_THROWSTONE: - case NPC_SMOKING: - case GS_FLING: - case NJ_ZENYNAGE: - case GN_THORNS_TRAP: - case GN_HELLS_PLANT_ATK: - skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - /** - * Rune Knight - **/ - case RK_DRAGONBREATH: { - struct status_change *tsc = NULL; - if( (tsc = status_get_sc(bl)) && (tsc->data[SC_HIDING] )) { - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - } else - skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag); - } - break; - - case NPC_SELFDESTRUCTION: { - struct status_change *tsc = NULL; - if( (tsc = status_get_sc(bl)) && tsc->data[SC_HIDING] ) - break; - } - case HVAN_EXPLOSION: - if (src != bl) - skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - // Celest - case PF_SOULBURN: - if (rnd()%100 < (skill_lv < 5 ? 30 + skill_lv * 10 : 70)) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (skill_lv == 5) - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - status_percent_damage(src, bl, 0, 100, false); - } else { - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - if (skill_lv == 5) - skill_attack(BF_MAGIC,src,src,src,skill_id,skill_lv,tick,flag); - status_percent_damage(src, src, 0, 100, false); - } - break; - - case NPC_BLOODDRAIN: - case NPC_ENERGYDRAIN: - { - int heal = skill_attack( (skill_id == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC, - src, src, bl, skill_id, skill_lv, tick, flag); - if (heal > 0){ - clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1); - status_heal(src, heal, 0, 0); - } - } - break; - - case GS_BULLSEYE: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case NJ_KASUMIKIRI: - if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag) > 0) - sc_start(src,SC_HIDING,100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case NJ_KIRIKAGE: - if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground ) - { //You don't move on GVG grounds. - short x, y; - map_search_freecell(bl, 0, &x, &y, 1, 1, 0); - if (unit_movepos(src, x, y, 0, 0)) - clif_slide(src,src->x,src->y); - } - status_change_end(src, SC_HIDING, INVALID_TIMER); - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - break; - case RK_PHANTOMTHRUST: - unit_setdir(src,map_calc_dir(src, bl->x, bl->y)); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - - 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,skill_id,skill_lv,tick,flag); - break; - - case RK_STORMBLAST: - case RK_CRUSHSTRIKE: - if( sd ) { - if( pc_checkskill(sd,RK_RUNEMASTERY) >= ( skill_id == RK_CRUSHSTRIKE ? 7 : 3 ) ) - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } else //non-sd support - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - break; - case GC_DARKILLUSION: - { - short x, y; - short dir = map_calc_dir(src,bl->x,bl->y); - - if( dir > 0 && dir < 4) x = 2; - else if( dir > 4 ) x = -2; - else x = 0; - if( dir > 2 && dir < 6 ) y = 2; - else if( dir == 7 || dir < 2 ) y = -2; - 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,skill_id,skill_lv,tick,flag); - if( rnd()%100 < 4 * skill_lv ) - skill_castend_damage_id(src,bl,GC_CROSSIMPACT,skill_lv,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,skill_id,skill_lv,tick,flag); - else if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0); - break; - - case GC_CROSSRIPPERSLASHER: - if( sd && !(sc && sc->data[SC_ROLLINGCUTTER]) ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0); - else - { - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - status_change_end(src,SC_ROLLINGCUTTER,INVALID_TIMER); - } - 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,skill_id,skill_lv,tick,flag); - } - break; - case WL_CHAINLIGHTNING: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_addtimerskill(src,tick + 150,bl->id,3,0,WL_CHAINLIGHTNING_ATK,skill_lv,4+skill_lv,flag); - break; - case WL_DRAINLIFE: - { - int heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); - int rate = 70 + 5 * skill_lv; - - heal = heal * (5 + 5 * skill_lv) / 100; - - if( bl->type == BL_SKILL ) - heal = 0; // Don't absorb heal from Ice Walls or other skill units. - - if( heal && rnd()%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,skill_id,USESKILL_FAIL_LEVEL,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 + i * 200, bl->id, k, 0, subskill, skill_lv, i, flag); - clif_skill_nodamage(src, bl, subskill, skill_lv, 1); - status_change_end(src, spheres[i], INVALID_TIMER); - } - } - break; - - case WL_RELEASE: - if( sd ) - { - int i; - // Priority is to release SpellBook - if( sc && sc->data[SC_READING_SB] ) - { // SpellBook - uint16 skill_id, skill_lv, point, s = 0; - int spell[SC_MAXSPELLBOOK-SC_SPELLBOOK1 + 1]; - - for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--) // List all available spell to be released - if( sc->data[i] ) spell[s++] = i; - - if ( s == 0 ) - break; - - i = spell[s==1?0:rand()%s];// Random select of spell to be released. - if( s && sc->data[i] ){// Now extract the data from the preserved spell - skill_id = sc->data[i]->val1; - skill_lv = sc->data[i]->val2; - point = sc->data[i]->val3; - status_change_end(src, (sc_type)i, INVALID_TIMER); - }else //something went wrong :( - break; - - if( sc->data[SC_READING_SB]->val2 > point ) - sc->data[SC_READING_SB]->val2 -= point; - else // Last spell to be released - status_change_end(src, SC_READING_SB, INVALID_TIMER); - - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - if( !skill_check_condition_castbegin(sd, skill_id, skill_lv) ) - break; - - switch( skill_get_casttype(skill_id) ) - { - case CAST_GROUND: - skill_castend_pos2(src, bl->x, bl->y, skill_id, skill_lv, tick, 0); - break; - case CAST_NODAMAGE: - skill_castend_nodamage_id(src, bl, skill_id, skill_lv, tick, 0); - break; - case CAST_DAMAGE: - skill_castend_damage_id(src, bl, skill_id, skill_lv, tick, 0); - break; - } - - sd->ud.canact_tick = tick + skill_delayfix(src, skill_id, skill_lv); - clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, skill_id, skill_lv), 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,skill_id,USESKILL_FAIL_SUMMON_NONE,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]); - } - - if( skill_lv == 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,skill_id,0,1); - } - } - break; - case WL_FROSTMISTY: - // Causes Freezing status through walls. - sc_start(bl,status_skill2sc(skill_id),20+12*skill_lv+(sd ? sd->status.job_level : 50)/5,skill_lv,skill_get_time(skill_id,skill_lv)); - // 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,skill_id,skill_lv,tick,flag|SD_ANIMATION); - break; - case WL_HELLINFERNO: - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|ELE_DARK); - break; - case RA_WUGSTRIKE: - if( sd && pc_isridingwug(sd) ){ - short x[8]={0,-1,-1,-1,0,1,1,1}; - short y[8]={1,1,0,-1,-1,-1,0,1}; - uint8 dir = map_calc_dir(bl, src->x, src->y); - - if( unit_movepos(src, bl->x+x[dir], bl->y+y[dir], 1, 1) ) - { - clif_slide(src, bl->x+x[dir], bl->y+y[dir]); - clif_fixpos(src); - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - } - break; - } - case RA_WUGBITE: - if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKNOREACH) ) { - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - }else if( sd && skill_id == RA_WUGBITE ) // Only RA_WUGBITE has the skill fail message. - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - - 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) ){ - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); - } - } - 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 ) - { - if( !(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->item_id?sg->item_id:ITEMID_TRAP; - item_tmp.identify = 1; - if( item_tmp.nameid ) - map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0); - } - skill_delunit(su); - } - } - break; - case NC_INFRAREDSCAN: - if( flag&1 ) - { //TODO: Need a confirmation if the other type of hidden status is included to be scanned. [Jobbie] - if( rnd()%100 < 50 ) - sc_start(bl, SC_INFRAREDSCAN, 10000, skill_lv, skill_get_time(skill_id, skill_lv)); - status_change_end(bl, SC_HIDING, INVALID_TIMER); - status_change_end(bl, SC_CLOAKING, INVALID_TIMER); - status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); // Need confirm it. - } - else - { - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); - clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( sd ) pc_overheat(sd,1); - } - break; - - case NC_MAGNETICFIELD: - sc_start2(bl,SC_MAGNETICFIELD,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)); - break; - case SC_FATALMENACE: - if( flag&1 ) - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - else - { - short x, y; - map_search_freecell(src, 0, &x, &y, -1, -1, 0); - // Destination area - skill_area_temp[4] = x; - skill_area_temp[5] = y; - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id); - skill_addtimerskill(src,tick + 800,src->id,x,y,skill_id,skill_lv,0,flag); // To teleport Self - clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6); - } - break; - case LG_PINPOINTATTACK: - if( !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,skill_id,skill_lv,tick,flag); - break; - - case LG_SHIELDSPELL: - // flag&1: Phisycal Attack, flag&2: Magic Attack. - skill_attack((flag&1)?BF_WEAPON:BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case LG_OVERBRAND: - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL); - break; - - case LG_OVERBRAND_BRANDISH: - skill_addtimerskill(src, tick + status_get_amotion(src)*8/10, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL); - break; - case SR_DRAGONCOMBO: - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case SR_KNUCKLEARROW: - if( !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); - clif_fixpos(src); // Aegis send this packet too. - } - - if( flag&1 ) - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL); - else - skill_addtimerskill(src, tick + 300, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL|2); - break; - - case SR_HOWLINGOFLION: - status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER); - status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER); - status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER); - status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER); - status_change_end(bl, SC_ECHOSONG, INVALID_TIMER); - status_change_end(bl, SC_HARMONIZE, INVALID_TIMER); - status_change_end(bl, SC_SIRCLEOFNATURE, INVALID_TIMER); - status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER); - status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER); - status_change_end(bl, SC_LERADSDEW, INVALID_TIMER); - status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER); - status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER); - status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER); - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION); - break; - - case SR_EARTHSHAKER: - if( flag&1 ) { //by default cloaking skills are remove by aoe skills so no more checking/removing except hiding and cloaking exceed. - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - status_change_end(bl, SC_HIDING, INVALID_TIMER); - status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); - } else{ - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - } - break; - - case WM_LULLABY_DEEPSLEEP: - if( bl != src && rnd()%100 < 88 + 2 * skill_lv ) - sc_start(bl,status_skill2sc(skill_id),100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - - case SO_POISON_BUSTER: { - struct status_change *tsc = status_get_sc(bl); - if( tsc && tsc->data[SC_POISON] ) { - skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); - status_change_end(bl, SC_POISON, INVALID_TIMER); - } - else if( sd ) - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - } - break; - - case GN_SPORE_EXPLOSION: - if( flag&1 ) - skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); - else { - clif_skill_nodamage(src, bl, skill_id, 0, 1); - skill_addtimerskill(src, gettick() + skill_get_time(skill_id, skill_lv) - 1000, bl->id, 0, 0, skill_id, skill_lv, 0, 0); - } - break; - - case EL_FIRE_BOMB: - case EL_FIRE_WAVE: - case EL_WATER_SCREW: - case EL_HURRICANE: - case EL_TYPOON_MIS: - if( flag&1 ) - skill_attack(skill_get_type(skill_id+1),src,src,bl,skill_id+1,skill_lv,tick,flag); - else { - int i = skill_get_splash(skill_id,skill_lv); - clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1); - clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( rnd()%100 < 30 ) - map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - else - skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); - } - break; - - case EL_ROCK_CRUSHER: - clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1); - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( rnd()%100 < 50 ) - skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag); - else - skill_attack(BF_WEAPON,src,src,bl,EL_ROCK_CRUSHER_ATK,skill_lv,tick,flag); - break; - - case EL_STONE_RAIN: - if( flag&1 ) - skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); - else { - int i = skill_get_splash(skill_id,skill_lv); - clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1); - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( rnd()%100 < 30 ) - map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - else - skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); - } - break; - - case EL_FIRE_ARROW: - case EL_ICE_NEEDLE: - case EL_WIND_SLASH: - case EL_STONE_HAMMER: - clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1); - clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); - break; - - case EL_TIDAL_WEAPON: - if( src->type == BL_ELEM ) { - struct elemental_data *ele = BL_CAST(BL_ELEM,src); - struct status_change *sc = status_get_sc(&ele->bl); - struct status_change *tsc = status_get_sc(bl); - sc_type type = status_skill2sc(skill_id), type2; - type2 = type-1; - - clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1); - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) { - elemental_clean_single_effect(ele, skill_id); - } - if( rnd()%100 < 50 ) - skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); - else { - sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv)); - sc_start(battle_get_master(src),type,100,ele->bl.id,skill_get_time(skill_id,skill_lv)); - } - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - } - break; - - - //recursive homon skill - case MH_MAGMA_FLOW: - case MH_XENO_SLASHER: - case MH_HEILIGE_STANGE: - if(flag & 1) - skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); - else { - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id); - } - break; - - case MH_STAHL_HORN: - case MH_NEEDLE_OF_PARALYZE: - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - break; - case MH_TINDER_BREAKER: - if (unit_movepos(src, bl->x, bl->y, 1, 1)) { -#if PACKETVER >= 20111005 - clif_snap(src, bl->x, bl->y); -#else - clif_skill_poseffect(src,skill_id,skill_lv,bl->x,bl->y,tick); -#endif - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start4(bl,SC_CLOSECONFINE2,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv))); - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - break; - - case 0:/* no skill - basic/normal attack */ - if(sd) { - if (flag & 3){ - if (bl->id != skill_area_temp[1]) - skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, SD_LEVEL|flag); - } else { - skill_area_temp[1] = bl->id; - map_foreachinrange(skill_area_sub, bl, - sd->bonus.splash_range, BL_CHAR, - src, skill_id, skill_lv, tick, flag | BCT_ENEMY | 1, - skill_castend_damage_id); - flag|=1; //Set flag to 1 so ammo is not double-consumed. [Skotlex] - } - } - break; - - default: - ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n",skill_id); - clif_skill_damage(src, bl, tick, status_get_amotion(src), tstatus->dmotion, - 0, abs(skill_get_num(skill_id, skill_lv)), - skill_id, skill_lv, skill_get_hit(skill_id)); - map_freeblock_unlock(); - return 1; - } - - if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted. - status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER); - - map_freeblock_unlock(); - - if( sd && !(flag&1) ) - {// ensure that the skill last-cast tick is recorded - sd->canskill_tick = gettick(); - - if( sd->state.arrow_atk ) - {// consume arrow on last invocation to this skill. - battle_consume_ammo(sd, skill_id, skill_lv); - } - - // perform skill requirement consumption - skill_consume_requirement(sd,skill_id,skill_lv,2); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag) -{ - struct map_session_data *sd, *dstsd; - struct mob_data *md, *dstmd; - struct homun_data *hd; - struct mercenary_data *mer; - struct status_data *sstatus, *tstatus; - struct status_change *tsc; - struct status_change_entry *tsce; - - int i = 0; - enum sc_type type; - - if(skill_id > 0 && !skill_lv) return 0; // celest - - nullpo_retr(1, src); - nullpo_retr(1, bl); - - if (src->m != bl->m) - return 1; - - sd = BL_CAST(BL_PC, src); - hd = BL_CAST(BL_HOM, src); - md = BL_CAST(BL_MOB, src); - mer = BL_CAST(BL_MER, src); - - dstsd = BL_CAST(BL_PC, bl); - dstmd = BL_CAST(BL_MOB, bl); - - if(bl->prev == NULL) - return 1; - if(status_isdead(src)) - return 1; - - if( src != bl && status_isdead(bl) ) { - /** - * Skills that may be cast on dead targets - **/ - switch( skill_id ) { - case NPC_WIDESOULDRAIN: - case PR_REDEMPTIO: - case ALL_RESURRECTION: - case WM_DEADHILLHERE: - break; - default: - return 1; - } - } - - tstatus = status_get_status_data(bl); - sstatus = status_get_status_data(src); - - //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex] - switch (skill_id) { - case HLIF_HEAL: //[orn] - if (bl->type != BL_HOM) { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0) ; - break ; - } - 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. - if (sd && battle_check_undead(tstatus->race,tstatus->def_ele)) { - if (battle_check_target(src, bl, BCT_ENEMY) < 1) { - //Offensive heal does not works on non-enemies. [Skotlex] - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag); - } - break; - case NPC_SMOKING: //Since it is a self skill, this one ends here rather than in damage_id. [Skotlex] - return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag); - case MH_STEINWAND: { - struct block_list *s_src = battle_get_master(src); - short ret = 0; - if(!skill_check_unit_range(src, src->x, src->y, skill_id, skill_lv)) //prevent reiteration - ret = skill_castend_pos2(src,src->x,src->y,skill_id,skill_lv,tick,flag); //cast on homon - if(s_src && !skill_check_unit_range(s_src, s_src->x, s_src->y, skill_id, skill_lv)) - ret |= skill_castend_pos2(s_src,s_src->x,s_src->y,skill_id,skill_lv,tick,flag); //cast on master - if (hd) - skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv)); - return ret; - } - break; - default: - //Skill is actually ground placed. - if (src == bl && skill_get_unit_id(skill_id,0)) - return skill_castend_pos2(src,bl->x,bl->y,skill_id,skill_lv,tick,0); - } - - type = status_skill2sc(skill_id); - tsc = status_get_sc(bl); - tsce = (tsc && type != -1)?tsc->data[type]:NULL; - - if (src!=bl && type > -1 && - (i = skill_get_ele(skill_id, skill_lv)) > ELE_NEUTRAL && - skill_get_inf(skill_id) != INF_SUPPORT_SKILL && - battle_attr_fix(NULL, NULL, 100, i, tstatus->def_ele, tstatus->ele_lv) <= 0) - return 1; //Skills that cause an status should be blocked if the target element blocks its element. - - map_freeblock_lock(); - switch(skill_id) - { - case HLIF_HEAL: //[orn] - case AL_HEAL: - /** - * Arch Bishop - **/ - case AB_HIGHNESSHEAL: - { - int heal = skill_calc_heal(src, bl, (skill_id == AB_HIGHNESSHEAL)?AL_HEAL:skill_id, (skill_id == AB_HIGHNESSHEAL)?10:skill_lv, true); - int heal_get_jobexp; - //Highness Heal: starts at 1.5 boost + 0.5 for each level - if( skill_id == AB_HIGHNESSHEAL ) { - heal = heal * ( 15 + 5 * skill_lv ) / 10; - } - if( status_isimmune(bl) || - (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) || - (dstsd && pc_ismadogear(dstsd)) )//Mado is immune to 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 ) - heal = heal*2; - - if( tsc && tsc->count ) - { - if( tsc->data[SC_KAITE] && !(sstatus->mode&MD_BOSS) ) - { //Bounce back heal - if (--tsc->data[SC_KAITE]->val2 <= 0) - status_change_end(bl, SC_KAITE, INVALID_TIMER); - if (src == bl) - heal=0; //When you try to heal yourself under Kaite, the heal is voided. - else { - bl = src; - dstsd = sd; - } - } - else if (tsc->data[SC_BERSERK] || tsc->data[SC_SATURDAYNIGHTFEVER] || tsc->data[SC__BLOODYLUST]) - heal = 0; //Needed so that it actually displays 0 when healing. - } - clif_skill_nodamage (src, bl, skill_id, heal, 1); - if( tsc && tsc->data[SC_AKAITSUKI] && heal && skill_id != HLIF_HEAL ) - heal = ~heal + 1; - heal_get_jobexp = status_heal(bl,heal,0,0); - - if(sd && dstsd && heal > 0 && sd != dstsd && battle_config.heal_exp > 0){ - heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100; - if (heal_get_jobexp <= 0) - heal_get_jobexp = 1; - pc_gainexp (sd, bl, 0, heal_get_jobexp, false); - } - } - break; - - case PR_REDEMPTIO: - if (sd && !(flag&1)) { - if (sd->status.party_id == 0) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - skill_area_temp[0] = 0; - party_foreachsamemap(skill_area_sub, - sd,skill_get_splash(skill_id, skill_lv), - src,skill_id,skill_lv,tick, flag|BCT_PARTY|1, - skill_castend_nodamage_id); - if (skill_area_temp[0] == 0) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty... - if (skill_area_temp[0] > 0 && !map[src->m].flag.noexppenalty) { //Apply penalty - sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000); //0.2% penalty per each. - sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000); - clif_updatestatus(sd,SP_BASEEXP); - clif_updatestatus(sd,SP_JOBEXP); - } - status_set_hp(src, 1, 0); - status_set_sp(src, 0, 0); - break; - } else if (status_isdead(bl) && flag&1) { //Revive - skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code. - skill_lv = 3; //Resurrection level 3 is used - } else //Invalid target, skip resurrection. - break; - - case ALL_RESURRECTION: - if(sd && (map_flag_gvg(bl->m) || map[bl->m].flag.battleground)) - { //No reviving in WoE grounds! - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - if (!status_isdead(bl)) - break; - { - int per = 0, sper = 0; - if (tsc && tsc->data[SC_HELLPOWER]) - break; - - if (map[bl->m].flag.pvp && dstsd && dstsd->pvp_point < 0) - break; - - switch(skill_lv){ - case 1: per=10; break; - case 2: per=30; break; - case 3: per=50; break; - case 4: per=80; break; - } - if(dstsd && dstsd->special_state.restart_full_recover) - per = sper = 100; - if (status_revive(bl, per, sper)) - { - clif_skill_nodamage(src,bl,ALL_RESURRECTION,skill_lv,1); //Both Redemptio and Res show this skill-animation. - if(sd && dstsd && battle_config.resurrection_exp > 0) - { - int exp = 0,jexp = 0; - int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level; - if(lv > 0 && pc_nextbaseexp(dstsd)) { - exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); - if (exp < 1) exp = 1; - } - if(jlv > 0 && pc_nextjobexp(dstsd)) { - jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); - if (jexp < 1) jexp = 1; - } - if(exp > 0 || jexp > 0) - pc_gainexp (sd, bl, exp, jexp, false); - } - } - } - break; - - case AL_DECAGI: - case MER_DECAGI: - clif_skill_nodamage (src, bl, skill_id, skill_lv, - sc_start(bl, type, (40 + skill_lv * 2 + (status_get_lv(src) + sstatus->int_)/5), skill_lv, skill_get_time(skill_id,skill_lv))); - break; - - case AL_CRUCIS: - if (flag&1) - sc_start(bl,type, 23+skill_lv*4 +status_get_lv(src) -status_get_lv(bl), skill_lv,skill_get_time(skill_id,skill_lv)); - else { - map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - } - break; - - case PR_LEXDIVINA: - case MER_LEXDIVINA: - if( tsce ) - status_change_end(bl,type, INVALID_TIMER); - else - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); - break; - - case SA_ABRACADABRA: - { - int abra_skill_id = 0, abra_skill_lv; - do { - i = rnd() % MAX_SKILL_ABRA_DB; - abra_skill_id = skill_abra_db[i].skill_id; - } while (abra_skill_id == 0 || - skill_abra_db[i].req_lv > skill_lv || //Required lv for it to appear - rnd()%10000 >= skill_abra_db[i].per - ); - abra_skill_lv = min(skill_lv, skill_get_max(abra_skill_id)); - clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); - - if( sd ) - {// player-casted - sd->state.abra_flag = 1; - sd->skillitem = abra_skill_id; - sd->skillitemlv = abra_skill_lv; - clif_item_skill(sd, abra_skill_id, abra_skill_lv); - } - else - {// mob-casted - struct unit_data *ud = unit_bl2ud(src); - int inf = skill_get_inf(abra_skill_id); - int target_id = 0; - if (!ud) break; - if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) { - if (src->type == BL_PET) - bl = (struct block_list*)((TBL_PET*)src)->msd; - if (!bl) bl = src; - unit_skilluse_id(src, bl->id, abra_skill_id, abra_skill_lv); - } else { //Assume offensive skills - if (ud->target) - target_id = ud->target; - else switch (src->type) { - case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break; - case BL_PET: target_id = ((TBL_PET*)src)->target_id; break; - } - if (!target_id) - break; - if (skill_get_casttype(abra_skill_id) == CAST_GROUND) { - bl = map_id2bl(target_id); - if (!bl) bl = src; - unit_skilluse_pos(src, bl->x, bl->y, abra_skill_id, abra_skill_lv); - } else - unit_skilluse_id(src, target_id, abra_skill_id, abra_skill_lv); - } - } - } - break; - - case SA_COMA: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv))); - break; - case SA_FULLRECOVERY: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (status_isimmune(bl)) - break; - status_percent_heal(bl, 100, 100); - break; - case NPC_ALLHEAL: - { - int heal; - if( status_isimmune(bl) ) - break; - heal = status_percent_heal(bl, 100, 0); - clif_skill_nodamage(NULL, bl, AL_HEAL, heal, 1); - if( dstmd ) - { // Reset Damage Logs - memset(dstmd->dmglog, 0, sizeof(dstmd->dmglog)); - dstmd->tdmg = 0; - } - } - break; - case SA_SUMMONMONSTER: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (sd) mob_once_spawn(sd, src->m, src->x, src->y," --ja--", -1, 1, "", SZ_SMALL, AI_NONE); - break; - case SA_LEVELUP: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, NULL, pc_nextbaseexp(sd) * 10 / 100, 0, false); - break; - case SA_INSTANTDEATH: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - status_set_hp(bl,1,0); - break; - case SA_QUESTION: - case SA_GRAVITY: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case SA_CLASSCHANGE: - case SA_MONOCELL: - if (dstmd) - { - int class_; - if ( sd && dstmd->status.mode&MD_BOSS ) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - class_ = skill_id==SA_MONOCELL?1002:mob_get_random_id(4, 1, 0); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - mob_class_change(dstmd,class_); - if( tsc && dstmd->status.mode&MD_BOSS ) - { - const enum sc_type scs[] = { SC_QUAGMIRE, SC_PROVOKE, SC_ROKISWEIL, SC_GRAVITATION, SC_SUITON, SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC_BLADESTOP }; - for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++) - if (tsc->data[i]) status_change_end(bl, (sc_type)i, INVALID_TIMER); - for (i = 0; i < ARRAYLENGTH(scs); i++) - if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER); - } - } - break; - case SA_DEATH: - if ( sd && dstmd && dstmd->status.mode&MD_BOSS ) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - status_kill(bl); - break; - case SA_REVERSEORCISH: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv))); - break; - case SA_FORTUNE: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if(sd) pc_getzeny(sd,status_get_lv(bl)*100,LOG_TYPE_STEAL,NULL); - break; - case SA_TAMINGMONSTER: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (sd && dstmd) { - ARR_FIND( 0, MAX_PET_DB, i, dstmd->class_ == pet_db[i].class_ ); - if( i < MAX_PET_DB ) - pet_catch_process1(sd, dstmd->class_); - } - break; - - case CR_PROVIDENCE: - if(sd && dstsd){ //Check they are not another crusader [Skotlex] - if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 1; - } - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - - case CG_MARIONETTE: - { - struct status_change* sc = status_get_sc(src); - - if( sd && dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && dstsd->status.sex == sd->status.sex ) - {// Cannot cast on another bard/dancer-type class of the same gender as caster - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 1; - } - - if( sc && tsc ) - { - if( !sc->data[SC_MARIONETTE] && !tsc->data[SC_MARIONETTE2] ) - { - sc_start(src,SC_MARIONETTE,100,bl->id,skill_get_time(skill_id,skill_lv)); - sc_start(bl,SC_MARIONETTE2,100,src->id,skill_get_time(skill_id,skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - else - if( sc->data[SC_MARIONETTE ] && sc->data[SC_MARIONETTE ]->val1 == bl->id && - tsc->data[SC_MARIONETTE2] && tsc->data[SC_MARIONETTE2]->val1 == src->id ) - { - status_change_end(src, SC_MARIONETTE, INVALID_TIMER); - status_change_end(bl, SC_MARIONETTE2, INVALID_TIMER); - } - else - { - if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - - map_freeblock_unlock(); - return 1; - } - } - } - break; - - case RG_CLOSECONFINE: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start4(bl,type,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv))); - break; - case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris] - case SA_FROSTWEAPON: - case SA_LIGHTNINGLOADER: - case SA_SEISMICWEAPON: - if (dstsd) { - if(dstsd->status.weapon == W_FIST || - (dstsd->sc.count && !dstsd->sc.data[type] && - ( //Allow re-enchanting to lenghten time. [Skotlex] - dstsd->sc.data[SC_FIREWEAPON] || - dstsd->sc.data[SC_WATERWEAPON] || - dstsd->sc.data[SC_WINDWEAPON] || - dstsd->sc.data[SC_EARTHWEAPON] || - dstsd->sc.data[SC_SHADOWWEAPON] || - dstsd->sc.data[SC_GHOSTWEAPON] || - dstsd->sc.data[SC_ENCPOISON] - )) - ) { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - clif_skill_nodamage(src,bl,skill_id,skill_lv,0); - break; - } - } - // 100% success rate at lv4 & 5, but lasts longer at lv5 - if(!clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,type,(60+skill_lv*10),skill_lv, skill_get_time(skill_id,skill_lv)))) { - if (sd) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - if (skill_break_equip(bl, EQP_WEAPON, 10000, BCT_PARTY) && sd && sd != dstsd) - clif_displaymessage(sd->fd, msg_txt(669)); - } - break; - - case PR_ASPERSIO: - if (sd && dstmd) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,0); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - - case ITEM_ENCHANTARMS: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(bl,type,100,skill_lv, - skill_get_ele(skill_id,skill_lv), skill_get_time(skill_id,skill_lv))); - break; - - case TK_SEVENWIND: - switch(skill_get_ele(skill_id,skill_lv)) { - case ELE_EARTH : type = SC_EARTHWEAPON; break; - case ELE_WIND : type = SC_WINDWEAPON; break; - case ELE_WATER : type = SC_WATERWEAPON; break; - case ELE_FIRE : type = SC_FIREWEAPON; break; - case ELE_GHOST : type = SC_GHOSTWEAPON; break; - case ELE_DARK : type = SC_SHADOWWEAPON; break; - case ELE_HOLY : type = SC_ASPERSIO; break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - - sc_start(bl,SC_SEVENWIND,100,skill_lv,skill_get_time(skill_id,skill_lv)); - - break; - - case PR_KYRIE: - case MER_KYRIE: - clif_skill_nodamage(bl,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - //Passive Magnum, should had been casted on yourself. - case SM_MAGNUM: - case MS_MAGNUM: - skill_area_temp[1] = 0; - map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_SKILL|BL_CHAR, - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, skill_castend_damage_id); - clif_skill_nodamage (src,src,skill_id,skill_lv,1); - // Initiate 10% of your damage becomes fire element. - sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skill_id, skill_lv)); - if( sd ) - skill_blockpc_start(sd, skill_id, skill_get_time(skill_id, skill_lv)); - else if( bl->type == BL_MER ) - skill_blockmerc_start((TBL_MER*)bl, skill_id, skill_get_time(skill_id, skill_lv)); - break; - - case TK_JUMPKICK: - /* Check if the target is an enemy; if not, skill should fail so the character doesn't unit_movepos (exploitable) */ - if( battle_check_target(src, bl, BCT_ENEMY) > 0 ) - { - if( unit_movepos(src, bl->x, bl->y, 1, 1) ) - { - skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); - clif_slide(src,bl->x,bl->y); - } - } - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL,0); - break; - - case AL_INCAGI: - case AL_BLESSING: - case MER_INCAGI: - case MER_BLESSING: - if (dstsd != NULL && tsc->data[SC_CHANGEUNDEAD]) { - skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag); - break; - } - case PR_SLOWPOISON: - case PR_IMPOSITIO: - case PR_LEXAETERNA: - case PR_SUFFRAGIUM: - case PR_BENEDICTIO: - case LK_BERSERK: - case MS_BERSERK: - case KN_AUTOCOUNTER: - case KN_TWOHANDQUICKEN: - case KN_ONEHAND: - case MER_QUICKEN: - case CR_SPEARQUICKEN: - case CR_REFLECTSHIELD: - case MS_REFLECTSHIELD: - case AS_POISONREACT: - case MC_LOUD: - case MG_ENERGYCOAT: - case MO_EXPLOSIONSPIRITS: - case MO_STEELBODY: - case MO_BLADESTOP: - case LK_AURABLADE: - case LK_PARRYING: - case MS_PARRYING: - case LK_CONCENTRATION: - case WS_CARTBOOST: - case SN_SIGHT: - case WS_MELTDOWN: - case WS_OVERTHRUSTMAX: - case ST_REJECTSWORD: - case HW_MAGICPOWER: - case PF_MEMORIZE: - case PA_SACRIFICE: - case ASC_EDP: - case PF_DOUBLECASTING: - case SG_SUN_COMFORT: - case SG_MOON_COMFORT: - case SG_STAR_COMFORT: - case NPC_HALLUCINATION: - case GS_MADNESSCANCEL: - case GS_ADJUSTMENT: - case GS_INCREASING: - case NJ_KASUMIKIRI: - case NJ_UTSUSEMI: - case NJ_NEN: - case NPC_DEFENDER: - case NPC_MAGICMIRROR: - case ST_PRESERVE: - case NPC_INVINCIBLE: - case NPC_INVINCIBLEOFF: - case RK_DEATHBOUND: - case AB_RENOVATIO: - case AB_EXPIATIO: - case AB_DUPLELIGHT: - case AB_SECRAMENT: - case NC_ACCELERATION: - case NC_HOVERING: - case NC_SHAPESHIFT: - case WL_RECOGNIZEDSPELL: - case GC_VENOMIMPRESS: - case SC_DEADLYINFECT: - case LG_EXEEDBREAK: - case LG_PRESTIGE: - case SR_CRESCENTELBOW: - case SR_LIGHTNINGWALK: - case SR_GENTLETOUCH_ENERGYGAIN: - case GN_CARTBOOST: - case KO_MEIKYOUSISUI: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - - case SO_STRIKING: - if (sd) { - int bonus = 25 + 10 * skill_lv; - bonus += (pc_checkskill(sd, SA_FLAMELAUNCHER)+pc_checkskill(sd, SA_FROSTWEAPON)+pc_checkskill(sd, SA_LIGHTNINGLOADER)+pc_checkskill(sd, SA_SEISMICWEAPON))*5; - clif_skill_nodamage( src, bl, skill_id, skill_lv, - battle_check_target(src,bl,BCT_PARTY) ? - sc_start2(bl, type, 100, skill_lv, bonus, skill_get_time(skill_id,skill_lv)) : - 0 - ); - } - break; - - case NPC_STOP: - if( clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)) ) ) - sc_start2(src,type,100,skill_lv,bl->id,skill_get_time(skill_id,skill_lv)); - break; - case HP_ASSUMPTIO: - if( sd && dstmd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - else - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - case MG_SIGHT: - case MER_SIGHT: - case AL_RUWACH: - case WZ_SIGHTBLASTER: - case NPC_WIDESIGHT: - case NPC_STONESKIN: - case NPC_ANTIMAGIC: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(bl,type,100,skill_lv,skill_id,skill_get_time(skill_id,skill_lv))); - break; - case HLIF_AVOID: - case HAMI_DEFENCE: - i = skill_get_time(skill_id,skill_lv); - clif_skill_nodamage(bl,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,i)); // Master - clif_skill_nodamage(src,src,skill_id,skill_lv,sc_start(src,type,100,skill_lv,i)); // Homunc - break; - case NJ_BUNSINJYUTSU: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - status_change_end(bl, SC_NEN, INVALID_TIMER); - break; -/* Was modified to only affect targetted char. [Skotlex] - case HP_ASSUMPTIO: - if (flag&1) - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - else - { - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skill_id, skill_lv), BL_PC, - src, skill_id, skill_lv, tick, flag|BCT_ALL|1, - skill_castend_nodamage_id); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; -*/ - case SM_ENDURE: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - if (sd) - skill_blockpc_start (sd, skill_id, skill_get_time2(skill_id,skill_lv)); - break; - - case AS_ENCHANTPOISON: // Prevent spamming [Valaris] - if (sd && dstsd && dstsd->sc.count) { - if (dstsd->sc.data[SC_FIREWEAPON] || - dstsd->sc.data[SC_WATERWEAPON] || - dstsd->sc.data[SC_WINDWEAPON] || - dstsd->sc.data[SC_EARTHWEAPON] || - dstsd->sc.data[SC_SHADOWWEAPON] || - dstsd->sc.data[SC_GHOSTWEAPON] - // dstsd->sc.data[SC_ENCPOISON] //People say you should be able to recast to lengthen the timer. [Skotlex] - ) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,0); - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - - case LK_TENSIONRELAX: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start4(bl,type,100,skill_lv,0,0,skill_get_time2(skill_id,skill_lv), - skill_get_time(skill_id,skill_lv))); - break; - - case MC_CHANGECART: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - - case TK_MISSION: - if (sd) { - int id; - if (sd->mission_mobid && (sd->mission_count || rnd()%100)) { //Cannot change target when already have one - clif_mission_info(sd, sd->mission_mobid, sd->mission_count); - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - id = mob_get_random_id(0,0xF, sd->status.base_level); - if (!id) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - sd->mission_mobid = id; - sd->mission_count = 0; - pc_setglobalreg(sd,"TK_MISSION_ID", id); - clif_mission_info(sd, id, 0); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case AC_CONCENTRATION: - { - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - map_foreachinrange( status_change_timer_sub, src, - skill_get_splash(skill_id, skill_lv), BL_CHAR, - src,NULL,type,tick); - } - break; - - case SM_PROVOKE: - case SM_SELFPROVOKE: - case MER_PROVOKE: - if( (tstatus->mode&MD_BOSS) || battle_check_undead(tstatus->race,tstatus->def_ele) ) - { - map_freeblock_unlock(); - return 1; - } - //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex] - clif_skill_nodamage(src,bl,skill_id == SM_SELFPROVOKE ? SM_PROVOKE : skill_id,skill_lv, - (i = sc_start(bl,type, skill_id == SM_SELFPROVOKE ? 100:( 50 + 3*skill_lv + status_get_lv(src) - status_get_lv(bl)), skill_lv, skill_get_time(skill_id,skill_lv)))); - if( !i ) - { - if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 0; - } - unit_skillcastcancel(bl, 2); - - if( tsc && tsc->count ) - { - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - if( tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE ) - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_TRICKDEAD, INVALID_TIMER); - } - - if( dstmd ) - { - dstmd->state.provoke_flag = src->id; - mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv)); - } - break; - - case ML_DEVOTION: - case CR_DEVOTION: - { - int count, lv; - if( !dstsd || (!sd && !mer) ) - { // Only players can be devoted - if( sd ) - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - break; - } - - if( (lv = status_get_lv(src) - dstsd->status.base_level) < 0 ) - lv = -lv; - if( lv > battle_config.devotion_level_difference || // Level difference requeriments - (dstsd->sc.data[type] && dstsd->sc.data[type]->val1 != src->id) || // Cannot Devote a player devoted from another source - (skill_id == ML_DEVOTION && (!mer || mer != dstsd->md)) || // Mercenary only can devote owner - (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER || // Crusader Cannot be devoted - (dstsd->sc.data[SC_HELLPOWER])) // Players affected by SC_HELLPOWERR cannot be devoted. - { - if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 1; - } - - i = 0; - count = (sd)? min(skill_lv,5) : 1; // Mercenary only can Devote owner - if( sd ) - { // Player Devoting Player - ARR_FIND(0, count, i, sd->devotion[i] == bl->id ); - if( i == count ) - { - ARR_FIND(0, count, i, sd->devotion[i] == 0 ); - if( i == count ) - { // No free slots, skill Fail - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - map_freeblock_unlock(); - return 1; - } - } - - sd->devotion[i] = bl->id; - } - else - mer->devotion_flag = 1; // Mercenary Devoting Owner - - clif_skill_nodamage(src, bl, skill_id, skill_lv, - sc_start4(bl, type, 100, src->id, i, skill_get_range2(src,skill_id,skill_lv),0, skill_get_time2(skill_id, skill_lv))); - clif_devotion(src, NULL); - } - break; - - case MO_CALLSPIRITS: - if(sd) { - int limit = skill_lv; - if( sd->sc.data[SC_RAISINGDRAGON] ) - limit += sd->sc.data[SC_RAISINGDRAGON]->val1; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit); - } - break; - - case CH_SOULCOLLECT: - if(sd) { - int limit = 5; - if( sd->sc.data[SC_RAISINGDRAGON] ) - limit += sd->sc.data[SC_RAISINGDRAGON]->val1; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - for (i = 0; i < limit; i++) - pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit); - } - break; - - case MO_KITRANSLATION: - if(dstsd && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) { - pc_addspiritball(dstsd,skill_get_time(skill_id,skill_lv),5); - } - break; - - case TK_TURNKICK: - case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex] - if (skill_area_temp[1] != bl->id) { - skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),-1,0); - skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick); //Use Misc rather than weapon to signal passive pushback - } - break; - - case MO_ABSORBSPIRITS: - i = 0; - if (dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) - { // split the if for readability, and included gunslingers in the check so that their coins cannot be removed [Reddozen] - i = dstsd->spiritball * 7; - pc_delspiritball(dstsd,dstsd->spiritball,0); - } else if (dstmd && !(tstatus->mode&MD_BOSS) && rnd() % 100 < 20) - { // check if target is a monster and not a Boss, for the 20% chance to absorb 2 SP per monster's level [Reddozen] - i = 2 * dstmd->level; - mob_target(dstmd,src,0); - } - if (i) status_heal(src, 0, i, 3); - clif_skill_nodamage(src,bl,skill_id,skill_lv,i?1:0); - break; - - case AC_MAKINGARROW: - if(sd) { - clif_arrow_create_list(sd); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case AM_PHARMACY: - if(sd) { - clif_skill_produce_mix_list(sd,skill_id,22); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case SA_CREATECON: - if(sd) { - clif_elementalconverter_list(sd); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case BS_HAMMERFALL: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,SC_STUN,(20 + 10 * skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv))); - break; - case RG_RAID: - skill_area_temp[1] = 0; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skill_id, skill_lv), splash_target(src), - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - status_change_end(src, SC_HIDING, INVALID_TIMER); - break; - - case ASC_METEORASSAULT: - case GS_SPREADATTACK: - case RK_STORMBLAST: - case NC_AXETORNADO: - case GC_COUNTERSLASH: - case SR_SKYNETBLOW: - case SR_RAMPAGEBLASTER: - case SR_HOWLINGOFLION: - case KO_HAPPOKUNAI: - skill_area_temp[1] = 0; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - i = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id); - if( !i && ( skill_id == NC_AXETORNADO || skill_id == SR_SKYNETBLOW || skill_id == KO_HAPPOKUNAI ) ) - clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - break; - - case NC_EMERGENCYCOOL: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - status_change_end(src,SC_OVERHEAT_LIMITPOINT,INVALID_TIMER); - status_change_end(src,SC_OVERHEAT,INVALID_TIMER); - break; - case SR_WINDMILL: - case GN_CART_TORNADO: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - case SR_EARTHSHAKER: - case NC_INFRAREDSCAN: - case NPC_EARTHQUAKE: - case NPC_VAMPIRE_GIFT: - case NPC_HELLJUDGEMENT: - case NPC_PULSESTRIKE: - case LG_MOONSLASHER: - skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag); - break; - - case KN_BRANDISHSPEAR: - case ML_BRANDISH: - skill_brandishspear(src, bl, skill_id, skill_lv, tick, flag); - break; - - case WZ_SIGHTRASHER: - //Passive side of the attack. - status_change_end(src, SC_SIGHT, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub,src, - skill_get_splash(skill_id, skill_lv),BL_CHAR|BL_SKILL, - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - break; - - case NJ_HYOUSYOURAKU: - case NJ_RAIGEKISAI: - case WZ_FROSTNOVA: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_area_temp[1] = 0; - map_foreachinrange(skill_attack_area, src, - skill_get_splash(skill_id, skill_lv), splash_target(src), - BF_MAGIC, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY); - break; - - case HVAN_EXPLOSION: //[orn] - case NPC_SELFDESTRUCTION: - //Self Destruction hits everyone in range (allies+enemies) - //Except for Summoned Marine spheres on non-versus maps, where it's just enemy. - i = ((!md || md->special_state.ai == 2) && !map_flag_vs(src->m))? - BCT_ENEMY:BCT_ALL; - clif_skill_nodamage(src, src, skill_id, -1, 1); - map_delblock(src); //Required to prevent chain-self-destructions hitting back. - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skill_id, skill_lv), splash_target(src), - src, skill_id, skill_lv, tick, flag|i, - skill_castend_damage_id); - map_addblock(src); - status_damage(src, src, sstatus->max_hp,0,0,1); - break; - - case AL_ANGELUS: - case PR_MAGNIFICAT: - case PR_GLORIA: - case SN_WINDWALK: - case CASH_BLESSING: - case CASH_INCAGI: - case CASH_ASSUMPTIO: - if( sd == NULL || sd->status.party_id == 0 || (flag & 1) ) - clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - else if( sd ) - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - break; - case MER_MAGNIFICAT: - if( mer != NULL ) - { - clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - if( mer->master && mer->master->status.party_id != 0 && !(flag&1) ) - party_foreachsamemap(skill_area_sub, mer->master, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - else if( mer->master && !(flag&1) ) - clif_skill_nodamage(src, &mer->master->bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - } - break; - - case BS_ADRENALINE: - case BS_ADRENALINE2: - case BS_WEAPONPERFECT: - case BS_OVERTHRUST: - if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { - clif_skill_nodamage(bl,bl,skill_id,skill_lv, - sc_start2(bl,type,100,skill_lv,(src == bl)? 1:0,skill_get_time(skill_id,skill_lv))); - } else if (sd) { - party_foreachsamemap(skill_area_sub, - sd,skill_get_splash(skill_id, skill_lv), - src,skill_id,skill_lv,tick, flag|BCT_PARTY|1, - skill_castend_nodamage_id); - } - break; - - case BS_MAXIMIZE: - case NV_TRICKDEAD: - case CR_DEFENDER: - case ML_DEFENDER: - case CR_AUTOGUARD: - case ML_AUTOGUARD: - case TK_READYSTORM: - case TK_READYDOWN: - case TK_READYTURN: - case TK_READYCOUNTER: - case TK_DODGE: - case CR_SHRINK: - case SG_FUSION: - case GS_GATLINGFEVER: - if( tsce ) - { - clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER)); - map_freeblock_unlock(); - return 0; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - case SL_KAITE: - case SL_KAAHI: - case SL_KAIZEL: - case SL_KAUPE: - if (sd) { - if (!dstsd || !( - (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SOULLINKER) || - (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER || - dstsd->status.char_id == sd->status.char_id || - dstsd->status.char_id == sd->status.partner_id || - dstsd->status.char_id == sd->status.child - )) { - status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,8); - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv))); - break; - case SM_AUTOBERSERK: - case MER_AUTOBERSERK: - if( tsce ) - i = status_change_end(bl, type, INVALID_TIMER); - else - i = sc_start(bl,type,100,skill_lv,60000); - clif_skill_nodamage(src,bl,skill_id,skill_lv,i); - break; - case TF_HIDING: - case ST_CHASEWALK: - case KO_YAMIKUMO: - if (tsce) - { - clif_skill_nodamage(src,bl,skill_id,-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,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 0; - } - clif_skill_nodamage(src,bl,skill_id,-1,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - case TK_RUN: - if (tsce) - { - clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER)); - map_freeblock_unlock(); - return 0; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,0)); - if (sd) // If the client receives a skill-use packet inmediately before a walkok packet, it will discard the walk packet! [Skotlex] - clif_walkok(sd); // So aegis has to resend the walk ok. - break; - case AS_CLOAKING: - case GC_CLOAKINGEXCEED: - case LG_FORCEOFVANGUARD: - case SC_REPRODUCE: - case SC_INVISIBILITY: - if (tsce) { - i = status_change_end(bl, type, INVALID_TIMER); - if( i ) - clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i); - else if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 0; - } - case RA_CAMOUFLAGE: - i = sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - if( i ) - clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i); - else if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - - case BD_ADAPTATION: - if(tsc && tsc->data[SC_DANCING]){ - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - status_change_end(bl, SC_DANCING, INVALID_TIMER); - } - break; - - case BA_FROSTJOKER: - case DC_SCREAM: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skill_id,skill_lv,0,flag); - - if (md) { - // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves - //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches) - char temp[70]; - snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_id].desc); - clif_message(&md->bl,temp); - } - break; - - case BA_PANGVOICE: - clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,50,7,skill_get_time(skill_id,skill_lv))); - break; - - case DC_WINKCHARM: - if( dstsd ) - clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,30,7,skill_get_time2(skill_id,skill_lv))); - else - if( dstmd ) - { - if( status_get_lv(src) > status_get_lv(bl) - && (tstatus->race == RC_DEMON || tstatus->race == RC_DEMIHUMAN || tstatus->race == RC_ANGEL) - && !(tstatus->mode&MD_BOSS) ) - clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start2(bl,type,70,skill_lv,src->id,skill_get_time(skill_id,skill_lv))); - else - { - clif_skill_nodamage(src,bl,skill_id,skill_lv,0); - if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - } - break; - - case TF_STEAL: - if(sd) { - if(pc_steal_item(sd,bl,skill_lv)) - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL,0); - } - break; - - case RG_STEALCOIN: - if(sd) { - if(pc_steal_coin(sd,bl)) - { - dstmd->state.provoke_flag = src->id; - mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - - } - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - - case MG_STONECURSE: - { - int brate = 0; - if (tstatus->mode&MD_BOSS) { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - if(status_isimmune(bl) || !tsc) - break; - - if (sd && sd->sc.data[SC_PETROLOGY_OPTION]) - brate = sd->sc.data[SC_PETROLOGY_OPTION]->val3; - - if (tsc->data[SC_STONE]) { - status_change_end(bl, SC_STONE, INVALID_TIMER); - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - if (sc_start4(bl,SC_STONE,(skill_lv*4+20)+brate, - skill_lv, 0, 0, skill_get_time(skill_id, skill_lv), - skill_get_time2(skill_id,skill_lv))) - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - else if(sd) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - // Level 6-10 doesn't consume a red gem if it fails [celest] - if (skill_lv > 5) - { // not to consume items - map_freeblock_unlock(); - return 0; - } - } - } - break; - - case NV_FIRSTAID: - clif_skill_nodamage(src,bl,skill_id,5,1); - status_heal(bl,5,0,0); - break; - - case AL_CURE: - if(status_isimmune(bl)) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,0); - break; - } - status_change_end(bl, SC_SILENCE, INVALID_TIMER); - status_change_end(bl, SC_BLIND, INVALID_TIMER); - status_change_end(bl, SC_CONFUSION, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - - case TF_DETOXIFY: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - status_change_end(bl, SC_POISON, INVALID_TIMER); - status_change_end(bl, SC_DPOISON, INVALID_TIMER); - break; - - case PR_STRECOVERY: - if(status_isimmune(bl)) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,0); - break; - } - if (tsc && tsc->opt1) { - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_STUN, INVALID_TIMER); - status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER); - } - //Is this equation really right? It looks so... special. - if(battle_check_undead(tstatus->race,tstatus->def_ele)) - { - status_change_start(bl, SC_BLIND, - 100*(100-(tstatus->int_/2+tstatus->vit/3+tstatus->luk/10)), - 1,0,0,0, - skill_get_time2(skill_id, skill_lv) * (100-(tstatus->int_+tstatus->vit)/2)/100,0); - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if(dstmd) - mob_unlocktarget(dstmd,tick); - break; - - // Mercenary Supportive Skills - case MER_BENEDICTION: - status_change_end(bl, SC_CURSE, INVALID_TIMER); - status_change_end(bl, SC_BLIND, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case MER_COMPRESS: - status_change_end(bl, SC_BLEEDING, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case MER_MENTALCURE: - status_change_end(bl, SC_CONFUSION, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case MER_RECUPERATE: - status_change_end(bl, SC_POISON, INVALID_TIMER); - status_change_end(bl, SC_SILENCE, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case MER_REGAIN: - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_STUN, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case MER_TENDER: - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_STONE, INVALID_TIMER); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - - case MER_SCAPEGOAT: - if( mer && mer->master ) - { - status_heal(&mer->master->bl, mer->battle_status.hp, 0, 2); - status_damage(src, src, mer->battle_status.max_hp, 0, 0, 1); - } - break; - - case MER_ESTIMATION: - if( !mer ) - break; - sd = mer->master; - case WZ_ESTIMATION: - if( sd == NULL ) - break; - if( dstsd ) - { // Fail on Players - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - if( dstmd && dstmd->class_ == MOBID_EMPERIUM ) - break; // Cannot be Used on Emperium - - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - clif_skill_estimation(sd, bl); - if( skill_id == MER_ESTIMATION ) - sd = NULL; - break; - - case BS_REPAIRWEAPON: - if(sd && dstsd) - clif_item_repair_list(sd,dstsd,skill_lv); - break; - - case MC_IDENTIFY: - if(sd) - clif_item_identify_list(sd); - break; - - // Weapon Refining [Celest] - case WS_WEAPONREFINE: - if(sd) - clif_item_refine_list(sd); - break; - - case MC_VENDING: - if(sd) - { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex] - if ( !pc_can_give_items(sd) ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - else { - sd->state.prevend = 1; - clif_openvendingreq(sd,2+skill_lv); - } - } - break; - - case AL_TELEPORT: - if(sd) - { - if (map[bl->m].flag.noteleport && skill_lv <= 2) { - clif_skill_teleportmessage(sd,0); - break; - } - if(!battle_config.duel_allow_teleport && sd->duel_group && skill_lv <= 2) { // duel restriction [LuzZza] - char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_TELEPORT)); - clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel." - break; - } - - if( sd->state.autocast || ( (sd->skillitem == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1 ) || skill_lv == 3 ) - { - if( skill_lv == 1 ) - pc_randomwarp(sd,CLR_TELEPORT); - else - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - break; - } - - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if( skill_lv == 1 ) - clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,0,0,0); - else - clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,sd->status.save_point.map,0,0); - } else - unit_warp(bl,-1,-1,-1,CLR_TELEPORT); - break; - - case NPC_EXPULSION: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - unit_warp(bl,-1,-1,-1,CLR_TELEPORT); - break; - - case AL_HOLYWATER: - if(sd) { - if (skill_produce_mix(sd, skill_id, 523, 0, 0, 0, 1)) - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - - case TF_PICKSTONE: - if(sd) { - int eflag; - struct item item_tmp; - struct block_list tbl; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - memset(&item_tmp,0,sizeof(item_tmp)); - memset(&tbl,0,sizeof(tbl)); // [MouseJstr] - item_tmp.nameid = ITEMID_STONE; - item_tmp.identify = 1; - tbl.id = 0; - clif_takeitem(&sd->bl,&tbl); - eflag = pc_additem(sd,&item_tmp,1,LOG_TYPE_PRODUCE); - if(eflag) { - clif_additem(sd,0,0,eflag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - break; - case ASC_CDP: - if(sd) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_produce_mix(sd, skill_id, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle. - } - break; - - case RG_STRIPWEAPON: - case RG_STRIPSHIELD: - case RG_STRIPARMOR: - case RG_STRIPHELM: - case ST_FULLSTRIP: - case GC_WEAPONCRUSH: - case SC_STRIPACCESSARY: { - unsigned short location = 0; - int d = 0; - - //Rate in percent - if ( skill_id == ST_FULLSTRIP ) { - i = 5 + 2*skill_lv + (sstatus->dex - tstatus->dex)/5; - } else if( skill_id == SC_STRIPACCESSARY ) { - i = 12 + 2 * skill_lv + (sstatus->dex - tstatus->dex)/5; - } else { - i = 5 + 5*skill_lv + (sstatus->dex - tstatus->dex)/5; - } - - if (i < 5) i = 5; //Minimum rate 5% - - //Duration in ms - if( skill_id == GC_WEAPONCRUSH){ - d = skill_get_time(skill_id,skill_lv); - if(bl->type == BL_PC) - d += skill_lv * 15 + (sstatus->dex - tstatus->dex); - else - d += skill_lv * 30 + (sstatus->dex - tstatus->dex) / 2; - }else - d = skill_get_time(skill_id,skill_lv) + (sstatus->dex - tstatus->dex)*500; - - if (d < 0) d = 0; //Minimum duration 0ms - - switch (skill_id) { - case RG_STRIPWEAPON: - case GC_WEAPONCRUSH: - location = EQP_WEAPON; - break; - case RG_STRIPSHIELD: - location = EQP_SHIELD; - break; - case RG_STRIPARMOR: - location = EQP_ARMOR; - break; - case RG_STRIPHELM: - location = EQP_HELM; - break; - case ST_FULLSTRIP: - location = EQP_WEAPON|EQP_SHIELD|EQP_ARMOR|EQP_HELM; - break; - case SC_STRIPACCESSARY: - location = EQP_ACC; - break; - } - - //Special message when trying to use strip on FCP [Jobbie] - if( sd && skill_id == 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, skill_lv, d)) || (skill_id != ST_FULLSTRIP && skill_id != GC_WEAPONCRUSH ) ) - clif_skill_nodamage(src,bl,skill_id,skill_lv,i); - - //Nothing stripped. - if( sd && !i ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - - case AM_BERSERKPITCHER: - case AM_POTIONPITCHER: { - int i,x,hp = 0,sp = 0,bonus=100; - if( dstmd && dstmd->class_ == MOBID_EMPERIUM ) { - map_freeblock_unlock(); - return 1; - } - if( sd ) { - x = skill_lv%11 - 1; - i = pc_search_inventory(sd,skill_db[skill_id].itemid[x]); - if( i < 0 || skill_db[skill_id].itemid[x] <= 0 ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 1; - } - if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skill_id].amount[x]) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 1; - } - if( skill_id == AM_BERSERKPITCHER ) { - if( dstsd && dstsd->status.base_level < (unsigned int)sd->inventory_data[i]->elv ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 1; - } - } - potion_flag = 1; - potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0; - potion_target = bl->id; - run_script(sd->inventory_data[i]->script,0,sd->bl.id,0); - potion_flag = potion_target = 0; - if( sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_ALCHEMIST ) - bonus += sd->status.base_level; - if( potion_per_hp > 0 || potion_per_sp > 0 ) { - hp = tstatus->max_hp * potion_per_hp / 100; - hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - if( dstsd ) { - sp = dstsd->status.max_sp * potion_per_sp / 100; - sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - } - } else { - if( potion_hp > 0 ) { - hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - hp = hp * (100 + (tstatus->vit<<1)) / 100; - if( dstsd ) - hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; - } - if( potion_sp > 0 ) { - sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - sp = sp * (100 + (tstatus->int_<<1)) / 100; - if( dstsd ) - sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100; - } - } - - if (sd->itemgrouphealrate[IG_POTION]>0) { - hp += hp * sd->itemgrouphealrate[IG_POTION] / 100; - sp += sp * sd->itemgrouphealrate[IG_POTION] / 100; - } - - if( (i = pc_skillheal_bonus(sd, skill_id)) ) { - hp += hp * i / 100; - sp += sp * i / 100; - } - } else { - hp = (1 + rnd()%400) * (100 + skill_lv*10) / 100; - hp = hp * (100 + (tstatus->vit<<1)) / 100; - if( dstsd ) - hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; - } - if( dstsd && (i = pc_skillheal2_bonus(dstsd, skill_id)) ) { - hp += hp * i / 100; - sp += sp * i / 100; - } - if( tsc && tsc->count ) { - if( tsc->data[SC_CRITICALWOUND] ) { - hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100; - sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100; - } - if( tsc->data[SC_DEATHHURT] ) { - hp -= hp * 20 / 100; - sp -= sp * 20 / 100; - } - if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2 ) { - hp += hp / 10; - sp += sp / 10; - } - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if( hp > 0 || (skill_id == AM_POTIONPITCHER && sp <= 0) ) - clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); - if( sp > 0 ) - clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1); -#ifdef RENEWAL - if( tsc && tsc->data[SC_EXTREMITYFIST2] ) - sp = 0; -#endif - status_heal(bl,hp,sp,0); - } - break; - case AM_CP_WEAPON: - case AM_CP_SHIELD: - case AM_CP_ARMOR: - case AM_CP_HELM: - { - unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP}; - - if( sd && ( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[skill_id - AM_CP_WEAPON]) < 0 ) ) ){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); // Don't consume item requirements - return 0; - } - - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - } - break; - case AM_TWILIGHT1: - if (sd) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - //Prepare 200 White Potions. - if (!skill_produce_mix(sd, skill_id, 504, 0, 0, 0, 200)) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - case AM_TWILIGHT2: - if (sd) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - //Prepare 200 Slim White Potions. - if (!skill_produce_mix(sd, skill_id, 547, 0, 0, 0, 200)) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - case AM_TWILIGHT3: - if (sd) { - int ebottle = pc_search_inventory(sd,713); - if( ebottle >= 0 ) - ebottle = sd->status.inventory[ebottle].amount; - //check if you can produce all three, if not, then fail: - if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol - || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle - || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle - || ebottle < 200 //200 empty bottle are required at total. - ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_produce_mix(sd, skill_id, 970, 0, 0, 0, 100); - skill_produce_mix(sd, skill_id, 7136, 0, 0, 0, 50); - skill_produce_mix(sd, skill_id, 7135, 0, 0, 0, 50); - } - break; - case SA_DISPELL: - if (flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1) - { - clif_skill_nodamage(src,bl,skill_id,skill_lv,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. - || rnd()%100 >= 50+10*skill_lv - || ( tsc && tsc->option&OPTION_MADOGEAR ) )//Mado Gear is immune to dispell according to bug report 49 [Ind] - { - if (sd) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,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_EDP: 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_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_SEVENWIND: - case SC_MIRACLE: case SC_S_LIFEPOTION: case SC_L_LIFEPOTION: - case SC_INCHEALRATE: case SC_ELECTRICSHOCKER: case SC__STRIPACCESSORY: - //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_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER: case SC_STEALTHFIELD_MASTER: - case SC_STEALTHFIELD: case SC_GIANTGROWTH: case SC_MILLENNIUMSHIELD: - case SC_REFRESH: case SC_STONEHARDSKIN: case SC_VITALITYACTIVATION: - case SC_FIGHTINGSPIRIT: case SC_ABUNDANCE: case SC__SHADOWFORM: - case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD: - case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART: - case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE: - case SC_GT_REVITALIZE: case SC_REFLECTDAMAGE: case SC_INSPIRATION: - case SC_EXEEDBREAK: case SC_FORCEOFVANGUARD: case SC_BANDING: - case SC_DUPLELIGHT: case SC_EXPIATIO: case SC_LAUDAAGNUS: - case SC_LAUDARAMUS: case SC_GATLINGFEVER: case SC_INCREASING: - case SC_ADJUSTMENT: case SC_MADNESSCANCEL: -#ifdef RENEWAL - case SC_EXTREMITYFIST2: -#endif - continue; - /** - * bugreport:4888 these songs may only be dispelled if you're not in their song area anymore - **/ - case SC_WHISTLE: - case SC_ASSNCROS: - case SC_POEMBRAGI: - case SC_APPLEIDUN: - case SC_HUMMING: - case SC_DONTFORGETME: - case SC_FORTUNE: - case SC_SERVICE4U: - if( tsc->data[i]->val4 ) //val4 = out-of-song-area - continue; - break; - 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, INVALID_TIMER); - } - break; - } - //Affect all targets on splash area. - map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, - src, skill_id, skill_lv, tick, flag|1, - skill_castend_damage_id); - break; - - case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex] - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0); - break; - - case TK_HIGHJUMP: - { - int x,y, dir = unit_getdir(src); - - //Fails on noteleport maps, except for GvG and BG maps [Skotlex] - if( map[src->m].flag.noteleport && - !(map[src->m].flag.battleground || map_flag_gvg2(src->m) ) - ) { - x = src->x; - y = src->y; - } else { - x = src->x + dirx[dir]*skill_lv*2; - y = src->y + diry[dir]*skill_lv*2; - } - - clif_skill_nodamage(src,bl,TK_HIGHJUMP,skill_lv,1); - if(!map_count_oncell(src->m,x,y,BL_PC|BL_NPC|BL_MOB) && map_getcell(src->m,x,y,CELL_CHKREACH)) { - clif_slide(src,x,y); - unit_movepos(src, x, y, 1, 0); - } - } - break; - - case SA_CASTCANCEL: - case SO_SPELLFIST: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - unit_skillcastcancel(src,1); - if(sd) { - int sp = skill_get_sp(sd->skill_id_old,sd->skill_lv_old); - if( skill_id == SO_SPELLFIST ){ - sc_start4(src,type,100,skill_lv+1,skill_lv,sd->skill_id_old,sd->skill_lv_old,skill_get_time(skill_id,skill_lv)); - sd->skill_id_old = sd->skill_lv_old = 0; - break; - } - sp = sp * (90 - (skill_lv-1)*20) / 100; - if(sp < 0) sp = 0; - status_zap(src, 0, sp); - } - break; - case SA_SPELLBREAKER: - { - int sp; - if(tsc && tsc->data[SC_MAGICROD]) { - sp = skill_get_sp(skill_id,skill_lv); - sp = sp * tsc->data[SC_MAGICROD]->val2 / 100; - if(sp < 1) sp = 1; - status_heal(bl,0,sp,2); - status_percent_damage(bl, src, 0, -20, false); //20% max SP damage. - } else { - struct unit_data *ud = unit_bl2ud(bl); - int bl_skill_id=0,bl_skill_lv=0,hp = 0; - if (!ud || ud->skilltimer == INVALID_TIMER) - break; //Nothing to cancel. - bl_skill_id = ud->skill_id; - bl_skill_lv = ud->skill_lv; - if (tstatus->mode & MD_BOSS) - { //Only 10% success chance against bosses. [Skotlex] - if (rnd()%100 < 90) - { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - } else if (!dstsd || map_flag_vs(bl->m)) //HP damage only on pvp-maps when against players. - hp = tstatus->max_hp/50; //Recover 2% HP [Skotlex] - - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - unit_skillcastcancel(bl,0); - sp = skill_get_sp(bl_skill_id,bl_skill_lv); - status_zap(bl, hp, sp); - - if (hp && skill_lv >= 5) - hp>>=1; //Recover half damaged HP at level 5 [Skotlex] - else - hp = 0; - - if (sp) //Recover some of the SP used - sp = sp*(25*(skill_lv-1))/100; - - if(hp || sp) - status_heal(src, hp, sp, 2); - } - } - break; - case SA_MAGICROD: - clif_skill_nodamage(src,src,SA_MAGICROD,skill_lv,1); - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - case SA_AUTOSPELL: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if(sd) - clif_autospell(sd,skill_lv); - else { - int maxlv=1,spellid=0; - static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT }; - if(skill_lv >= 10) { - spellid = MG_FROSTDIVER; -// if (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SA_SAGE) -// maxlv = 10; -// else - maxlv = skill_lv - 9; - } - else if(skill_lv >=8) { - spellid = MG_FIREBALL; - maxlv = skill_lv - 7; - } - else if(skill_lv >=5) { - spellid = MG_SOULSTRIKE; - maxlv = skill_lv - 4; - } - else if(skill_lv >=2) { - int i = rnd()%3; - spellid = spellarray[i]; - maxlv = skill_lv - 1; - } - else if(skill_lv > 0) { - spellid = MG_NAPALMBEAT; - maxlv = 3; - } - if(spellid > 0) - sc_start4(src,SC_AUTOSPELL,100,skill_lv,spellid,maxlv,0, - skill_get_time(SA_AUTOSPELL,skill_lv)); - } - break; - - case BS_GREED: - if(sd){ - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_greed,bl, - skill_get_splash(skill_id, skill_lv),BL_ITEM,bl); - } - break; - - case SA_ELEMENTWATER: - case SA_ELEMENTFIRE: - case SA_ELEMENTGROUND: - case SA_ELEMENTWIND: - if(sd && !dstmd) //Only works on monsters. - break; - if(tstatus->mode&MD_BOSS) - break; - case NPC_ATTRICHANGE: - case NPC_CHANGEWATER: - case NPC_CHANGEGROUND: - case NPC_CHANGEFIRE: - case NPC_CHANGEWIND: - case NPC_CHANGEPOISON: - case NPC_CHANGEHOLY: - case NPC_CHANGEDARKNESS: - case NPC_CHANGETELEKINESIS: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv), - skill_get_time(skill_id, skill_lv))); - break; - case NPC_CHANGEUNDEAD: - //This skill should fail if target is wearing bathory/evil druid card [Brainstorm] - //TO-DO This is ugly, fix it - if(tstatus->def_ele==ELE_UNDEAD || tstatus->def_ele==ELE_DARK) break; - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv), - skill_get_time(skill_id, skill_lv))); - break; - - case NPC_PROVOCATION: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (md) mob_unlocktarget(md, tick); - break; - - case NPC_KEEPING: - case NPC_BARRIER: - { - int skill_time = skill_get_time(skill_id,skill_lv); - struct unit_data *ud = unit_bl2ud(bl); - if (clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_time)) - && ud) { //Disable attacking/acting/moving for skill's duration. - ud->attackabletime = - ud->canact_tick = - ud->canmove_tick = tick + skill_time; - } - } - break; - - case NPC_REBIRTH: - if( md && md->state.rebirth ) - break; // only works once - sc_start(bl,type,100,skill_lv,-1); - break; - - case NPC_DARKBLESSING: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(bl,type,(50+skill_lv*5),skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv))); - break; - - case NPC_LICK: - status_zap(bl, 0, 100); - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,(skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv))); - break; - - case NPC_SUICIDE: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - status_kill(src); //When suiciding, neither exp nor drops is given. - break; - - case NPC_SUMMONSLAVE: - case NPC_SUMMONMONSTER: - if(md && md->skill_idx >= 0) - mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv,skill_id); - break; - - case NPC_CALLSLAVE: - mob_warpslave(src,MOB_SLAVEDISTANCE); - break; - - case NPC_RANDOMMOVE: - if (md) { - md->next_walktime = tick - 1; - mob_randomwalk(md,tick); - } - break; - - case NPC_SPEEDUP: - { - // or does it increase casting rate? just a guess xD - int i = SC_ASPDPOTION0 + skill_lv - 1; - if (i > SC_ASPDPOTION3) - i = SC_ASPDPOTION3; - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,(sc_type)i,100,skill_lv,skill_lv * 60000)); - } - break; - - case NPC_REVENGE: - // not really needed... but adding here anyway ^^ - if (md && md->master_id > 0) { - struct block_list *mbl, *tbl; - if ((mbl = map_id2bl(md->master_id)) == NULL || - (tbl = battle_gettargeted(mbl)) == NULL) - break; - md->state.provoke_flag = tbl->id; - mob_target(md, tbl, sstatus->rhw.range); - } - break; - - case NPC_RUN: - { - const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}}; - uint8 dir = (bl == src)?unit_getdir(src):map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away. - unit_stop_attack(src); - //Run skillv tiles overriding the can-move check. - if (unit_walktoxy(src, src->x + skill_lv * mask[dir][0], src->y + skill_lv * mask[dir][1], 2) && md) - md->state.skillstate = MSS_WALK; //Otherwise it isn't updated in the ai. - } - break; - - case NPC_TRANSFORMATION: - case NPC_METAMORPHOSIS: - if(md && md->skill_idx >= 0) { - int class_ = mob_random_class (md->db->skill[md->skill_idx].val,0); - if (skill_lv > 1) //Multiply the rest of mobs. [Skotlex] - mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv-1,skill_id); - if (class_) mob_class_change(md, class_); - } - break; - - case NPC_EMOTION_ON: - case NPC_EMOTION: - //va[0] is the emotion to use. - //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex] - //val[1] 'sets' the mode - //val[2] adds to the current mode - //val[3] removes from the current mode - //val[4] if set, asks to delete the previous mode change. - if(md && md->skill_idx >= 0 && tsc) - { - clif_emotion(bl, md->db->skill[md->skill_idx].val[0]); - if(md->db->skill[md->skill_idx].val[4] && tsce) - status_change_end(bl, type, INVALID_TIMER); - - if(md->db->skill[md->skill_idx].val[1] || md->db->skill[md->skill_idx].val[2]) - sc_start4(src, type, 100, skill_lv, - md->db->skill[md->skill_idx].val[1], - md->db->skill[md->skill_idx].val[2], - md->db->skill[md->skill_idx].val[3], - skill_get_time(skill_id, skill_lv)); - } - break; - - case NPC_POWERUP: - sc_start(bl,SC_INCATKRATE,100,200,skill_get_time(skill_id, skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv))); - break; - - case NPC_AGIUP: - sc_start(bl,SC_SPEEDUP1,100,skill_lv,skill_get_time(skill_id, skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv))); - break; - - case NPC_INVISIBLE: - //Have val4 passed as 6 is for "infinite cloak" (do not end on attack/skill use). - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start4(bl,type,100,skill_lv,0,0,6,skill_get_time(skill_id,skill_lv))); - break; - - case NPC_SIEGEMODE: - // not sure what it does - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - - case WE_MALE: - { - int hp_rate=(!skill_lv)? 0:skill_db[skill_id].hp_rate[skill_lv-1]; - int gain_hp= tstatus->max_hp*abs(hp_rate)/100; // The earned is the same % of the target HP than it costed the caster. [Skotlex] - clif_skill_nodamage(src,bl,skill_id,status_heal(bl, gain_hp, 0, 0),1); - } - break; - case WE_FEMALE: - { - int sp_rate=(!skill_lv)? 0:skill_db[skill_id].sp_rate[skill_lv-1]; - int gain_sp=tstatus->max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex] - clif_skill_nodamage(src,bl,skill_id,status_heal(bl, 0, gain_sp, 0),1); - } - break; - - // parent-baby skills - case WE_BABY: - if(sd){ - struct map_session_data *f_sd = pc_get_father(sd); - struct map_session_data *m_sd = pc_get_mother(sd); - // if neither was found - if(!f_sd && !m_sd){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 0; - } - status_change_start(bl,SC_STUN,10000,skill_lv,0,0,0,skill_get_time2(skill_id,skill_lv),8); - if (f_sd) sc_start(&f_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - if (m_sd) sc_start(&m_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - } - break; - - case PF_HPCONVERSION: - { - int hp, sp; - hp = sstatus->max_hp/10; - sp = hp * 10 * skill_lv / 100; - if (!status_charge(src,hp,0)) { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - status_heal(bl,0,sp,2); - } - break; - - case MA_REMOVETRAP: - case HT_REMOVETRAP: - { - struct skill_unit* su; - struct skill_unit_group* sg; - su = BL_CAST(BL_SKILL, bl); - - // Mercenaries can remove any trap - // Players can only remove their own traps or traps on Vs maps. - if( su && (sg = su->group) && (src->type == BL_MER || sg->src_id == src->id || map_flag_vs(bl->m)) && (skill_get_inf2(sg->skill_id)&INF2_TRAP) ) - { - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - if( sd && !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) ) - { // prevent picking up expired traps - if( battle_config.skill_removetrap_type ) - { // get back all items used to deploy the trap - for( i = 0; i < 10; i++ ) - { - if( skill_db[su->group->skill_id].itemid[i] > 0 ) - { - int flag; - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = skill_db[su->group->skill_id].itemid[i]; - item_tmp.identify = 1; - if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i],LOG_TYPE_OTHER)) ) - { - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - } - else - { // get back 1 trap - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = su->group->item_id?su->group->item_id:ITEMID_TRAP; - item_tmp.identify = 1; - if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_OTHER)) ) - { - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - skill_delunit(su); - }else if(sd) - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - - } - break; - case HT_SPRINGTRAP: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - { - struct skill_unit *su=NULL; - if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){ - switch(su->group->unit_id){ - case UNT_ANKLESNARE: // ankle snare - if (su->group->val2 != 0) - // if it is already trapping something don't spring it, - // remove trap should be used instead - break; - // otherwise fallthrough to below - case UNT_BLASTMINE: - case UNT_SKIDTRAP: - case UNT_LANDMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_FREEZINGTRAP: - case UNT_CLAYMORETRAP: - case UNT_TALKIEBOX: - su->group->unit_id = UNT_USED_TRAPS; - clif_changetraplook(bl, UNT_USED_TRAPS); - su->group->limit=DIFF_TICK(tick+1500,su->group->tick); - su->limit=DIFF_TICK(tick+1500,su->group->tick); - } - } - } - break; - case BD_ENCORE: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if(sd) - unit_skilluse_id(src,src->id,sd->skill_id_dance,sd->skill_lv_dance); - break; - - case AS_SPLASHER: - if(tstatus->mode&MD_BOSS - /** - * Renewal dropped the 3/4 hp requirement - **/ - #ifndef RENEWAL - || tstatus-> hp > tstatus->max_hp*3/4 - #endif - ) { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 1; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start4(bl,type,100,skill_lv,skill_id,src->id,skill_get_time(skill_id,skill_lv),1000)); -#ifndef RENEWAL - if (sd) skill_blockpc_start (sd, skill_id, skill_get_time(skill_id, skill_lv)+3000); -#endif - break; - - case PF_MINDBREAKER: - { - if(tstatus->mode&MD_BOSS || battle_check_undead(tstatus->race,tstatus->def_ele)) - { - map_freeblock_unlock(); - return 1; - } - - if (tsce) - { //HelloKitty2 (?) explained that this silently fails when target is - //already inflicted. [Skotlex] - map_freeblock_unlock(); - return 1; - } - - //Has a 55% + skill_lv*5% success chance. - if (!clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,55+5*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)))) - { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 0; - } - - unit_skillcastcancel(bl,0); - - if(tsc && tsc->count){ - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - if(tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE) - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - } - - if(dstmd) - mob_target(dstmd,src,skill_get_range2(src,skill_id,skill_lv)); - } - break; - - case PF_SOULCHANGE: - { - unsigned int sp1 = 0, sp2 = 0; - if (dstmd) { - if (dstmd->state.soul_change_flag) { - if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - dstmd->state.soul_change_flag = 1; - sp2 = sstatus->max_sp * 3 /100; - status_heal(src, 0, sp2, 2); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - } - sp1 = sstatus->sp; - sp2 = tstatus->sp; - #ifdef RENEWAL - sp1 = sp1 / 2; - sp2 = sp2 / 2; - if( tsc && tsc->data[SC_EXTREMITYFIST2] ) - sp1 = tstatus->sp; - #endif - status_set_sp(src, sp2, 3); - status_set_sp(bl, sp1, 3); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - // Slim Pitcher - case CR_SLIMPITCHER: - // Updated to block Slim Pitcher from working on barricades and guardian stones. - if( dstmd && (dstmd->class_ == MOBID_EMPERIUM || (dstmd->class_ >= MOBID_BARRICADE1 && dstmd->class_ <= MOBID_GUARIDAN_STONE2)) ) - break; - if (potion_hp || potion_sp) { - int hp = potion_hp, sp = potion_sp; - hp = hp * (100 + (tstatus->vit<<1))/100; - sp = sp * (100 + (tstatus->int_<<1))/100; - if (dstsd) { - if (hp) - hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100; - if (sp) - sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100; - } - if( tsc && tsc->count ) { - if (tsc->data[SC_CRITICALWOUND]) { - hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100; - sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100; - } - if (tsc->data[SC_DEATHHURT]) { - hp -= hp * 20 / 100; - sp -= sp * 20 / 100; - } - if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2) { - hp += hp / 10; - sp += sp / 10; - } - } - if(hp > 0) - clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); - if(sp > 0) - clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1); - status_heal(bl,hp,sp,0); - } - break; - // Full Chemical Protection - case CR_FULLPROTECTION: - { - unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP}; - int i, s = 0, skilltime = skill_get_time(skill_id,skill_lv); - - for (i=0 ; i<4; i++) { - if( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[i]) < 0 ) ) - continue; - sc_start(bl,(sc_type)(SC_CP_WEAPON + i),100,skill_lv,skilltime); - s++; - } - if( sd && !s ){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); // Don't consume item requirements - return 0; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case RG_CLEANER: //AppleGirl - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - - case CG_LONGINGFREEDOM: - { - if (tsc && !tsce && (tsce=tsc->data[SC_DANCING]) && tsce->val4 - && (tsce->val1&0xFFFF) != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex] - { - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - } - } - break; - - case CG_TAROTCARD: - { - int eff, count = -1; - if( rnd() % 100 > skill_lv * 8 || (dstmd && ((dstmd->guardian_data && dstmd->class_ == MOBID_EMPERIUM) || mob_is_battleground(dstmd))) ) - { - if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - - map_freeblock_unlock(); - return 0; - } - status_zap(src,0,skill_db[skill_get_index(skill_id)].sp[skill_lv]); // consume sp only if succeeded [Inkfish] - do { - eff = rnd() % 14; - clif_specialeffect(bl, 523 + eff, AREA); - switch (eff) - { - case 0: // heals SP to 0 - status_percent_damage(src, bl, 0, 100, false); - break; - case 1: // matk halved - sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv)); - break; - case 2: // all buffs removed - status_change_clear_buffs(bl,1); - break; - case 3: // 1000 damage, random armor destroyed - { - int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM, EQP_SHOES, EQP_GARMENT }; - status_fix_damage(src, bl, 1000, 0); - clif_damage(src,bl,tick,0,0,1000,0,0,0); - if( !status_isdead(bl) ) - skill_break_equip(bl, where[rnd()%5], 10000, BCT_ENEMY); - } - break; - case 4: // atk halved - sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv)); - break; - case 5: // 2000HP heal, random teleported - status_heal(src, 2000, 0, 0); - if( !map_flag_vs(bl->m) ) - unit_warp(bl, -1,-1,-1, CLR_TELEPORT); - break; - case 6: // random 2 other effects - if (count == -1) - count = 3; - else - count++; //Should not retrigger this one. - break; - case 7: // stop freeze or stoned - { - enum sc_type sc[] = { SC_STOP, SC_FREEZE, SC_STONE }; - sc_start(bl,sc[rnd()%3],100,skill_lv,skill_get_time2(skill_id,skill_lv)); - } - break; - case 8: // curse coma and poison - sc_start(bl,SC_COMA,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_POISON,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case 9: // confusion - sc_start(bl,SC_CONFUSION,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - case 10: // 6666 damage, atk matk halved, cursed - status_fix_damage(src, bl, 6666, 0); - clif_damage(src,bl,tick,0,0,6666,0,0,0); - sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_CURSE,skill_lv,100,skill_get_time2(skill_id,skill_lv)); - break; - case 11: // 4444 damage - status_fix_damage(src, bl, 4444, 0); - clif_damage(src,bl,tick,0,0,4444,0,0,0); - break; - case 12: // stun - sc_start(bl,SC_STUN,100,skill_lv,5000); - break; - case 13: // atk,matk,hit,flee,def reduced - sc_start(bl,SC_INCATKRATE,100,-20,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_INCMATKRATE,100,-20,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_INCHITRATE,100,-20,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_INCFLEERATE,100,-20,skill_get_time2(skill_id,skill_lv)); - sc_start(bl,SC_INCDEFRATE,100,-20,skill_get_time2(skill_id,skill_lv)); - break; - default: - break; - } - } while ((--count) > 0); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case SL_ALCHEMIST: - case SL_ASSASIN: - case SL_BARDDANCER: - case SL_BLACKSMITH: - case SL_CRUSADER: - case SL_HUNTER: - case SL_KNIGHT: - case SL_MONK: - case SL_PRIEST: - case SL_ROGUE: - case SL_SAGE: - case SL_SOULLINKER: - case SL_STAR: - case SL_SUPERNOVICE: - case SL_WIZARD: - //NOTE: here, 'type' has the value of the associated MAPID, not of the SC_SPIRIT constant. - if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == type)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100)) - { //Erase death count 1% of the casts - dstsd->die_counter = 0; - pc_setglobalreg(dstsd,"PC_DIE_COUNTER", 0); - clif_specialeffect(bl, 0x152, AREA); - //SC_SPIRIT invokes status_calc_pc for us. - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start4(bl,SC_SPIRIT,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv))); - sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv)); - break; - case SL_HIGH: - if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start4(bl,type,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv))); - sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv)); - break; - - case SL_SWOO: - if (tsce) { - if(sd) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,10000,8); - status_change_end(bl, SC_SWOO, INVALID_TIMER); - break; - } - case SL_SKA: // [marquis007] - case SL_SKE: - if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - if (skill_id == SL_SKE) - sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv)); - break; - - // New guild skills [Celest] - case GD_BATTLEORDER: - if(flag&1) { - if (status_get_guild_id(src) == status_get_guild_id(bl)) - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)); - } else if (status_get_guild_id(src)) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skill_id, skill_lv), BL_PC, - src,skill_id,skill_lv,tick, flag|BCT_GUILD|1, - skill_castend_nodamage_id); - if (sd) - guild_block_skill(sd,skill_get_time2(skill_id,skill_lv)); - } - break; - case GD_REGENERATION: - if(flag&1) { - if (status_get_guild_id(src) == status_get_guild_id(bl)) - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)); - } else if (status_get_guild_id(src)) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skill_id, skill_lv), BL_PC, - src,skill_id,skill_lv,tick, flag|BCT_GUILD|1, - skill_castend_nodamage_id); - if (sd) - guild_block_skill(sd,skill_get_time2(skill_id,skill_lv)); - } - break; - case GD_RESTORE: - if(flag&1) { - if (status_get_guild_id(src) == status_get_guild_id(bl)) - clif_skill_nodamage(src,bl,AL_HEAL,status_percent_heal(bl,90,90),1); - } else if (status_get_guild_id(src)) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skill_id, skill_lv), BL_PC, - src,skill_id,skill_lv,tick, flag|BCT_GUILD|1, - skill_castend_nodamage_id); - if (sd) - guild_block_skill(sd,skill_get_time2(skill_id,skill_lv)); - } - break; - case GD_EMERGENCYCALL: - { - int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0}; - int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0}; - int j = 0; - struct guild *g = NULL; - // i don't know if it actually summons in a circle, but oh well. ;P - g = sd?sd->state.gmaster_flag:guild_search(status_get_guild_id(src)); - if (!g) - break; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - for(i = 0; i < g->max_member; i++, j++) { - if (j>8) j=0; - if ((dstsd = g->member[i].sd) != NULL && sd != dstsd && !dstsd->state.autotrade && !pc_isdead(dstsd)) { - if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg2(dstsd->bl.m)) - continue; - if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH)) - dx[j] = dy[j] = 0; - pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN); - } - } - if (sd) - guild_block_skill(sd,skill_get_time2(skill_id,skill_lv)); - } - break; - - case SG_FEEL: - //AuronX reported you CAN memorize the same map as all three. [Skotlex] - if (sd) { - if(!sd->feel_map[skill_lv-1].index) - clif_feel_req(sd->fd,sd, skill_lv); - else - clif_feel_info(sd, skill_lv-1, 1); - } - break; - - case SG_HATE: - if (sd) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (!pc_set_hate_mob(sd, skill_lv-1, bl)) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - - case GS_GLITTERING: - if(sd) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if(rnd()%100 < (20+10*skill_lv)) - pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),10); - else if(sd->spiritball > 0) - pc_delspiritball(sd,1,0); - } - break; - - case GS_CRACKER: - /* per official standards, this skill works on players and mobs. */ - if (sd && (dstsd || dstmd)) - { - i =65 -5*distance_bl(src,bl); //Base rate - if (i < 30) i = 30; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - sc_start(bl,SC_STUN, i,skill_lv,skill_get_time2(skill_id,skill_lv)); - } - break; - - case AM_CALLHOMUN: //[orn] - if (sd && !merc_call_homunculus(sd)) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - - case AM_REST: - if (sd) { - if (merc_hom_vaporize(sd,1)) - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - - case HAMI_CASTLE: //[orn] - if(rnd()%100 < 20*skill_lv && src != bl) - { - int x,y; - x = src->x; - y = src->y; - if (hd) - skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv)); - - if (unit_movepos(src,bl->x,bl->y,0,0)) { - clif_skill_nodamage(src,src,skill_id,skill_lv,1); // Homunc - clif_slide(src,bl->x,bl->y) ; - if (unit_movepos(bl,x,y,0,0)) - { - clif_skill_nodamage(bl,bl,skill_id,skill_lv,1); // Master - clif_slide(bl,x,y) ; - } - - //TODO: Shouldn't also players and the like switch targets? - map_foreachinrange(skill_chastle_mob_changetarget,src, - AREA_SIZE, BL_MOB, bl, src); - } - } - // Failed - else if (hd && hd->master) - clif_skill_fail(hd->master, skill_id, USESKILL_FAIL_LEVEL, 0); - else if (sd) - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - break; - case HVAN_CHAOTIC: //[orn] - { - static const int per[5][2]={{20,50},{50,60},{25,75},{60,64},{34,67}}; - int r = rnd()%100; - i = (skill_lv-1)%5; - if(r<per[i][0]) //Self - bl = src; - else if(r<per[i][1]) //Master - bl = battle_get_master(src); - else //Enemy - bl = map_id2bl(battle_gettarget(src)); - - if (!bl) bl = src; - i = skill_calc_heal(src, bl, skill_id, 1+rnd()%skill_lv, true); - //Eh? why double skill packet? - clif_skill_nodamage(src,bl,AL_HEAL,i,1); - clif_skill_nodamage(src,bl,skill_id,i,1); - status_heal(bl, i, 0, 0); - } - break; - //Homun single-target support skills [orn] - case HAMI_BLOODLUST: - case HFLI_FLEET: - case HFLI_SPEED: - case HLIF_CHANGE: - case MH_ANGRIFFS_MODUS: - case MH_GOLDENE_FERSE: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - if (hd) - skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv)); - break; - - case NPC_DRAGONFEAR: - if (flag&1) { - const enum sc_type sc[] = { SC_STUN, SC_SILENCE, SC_CONFUSION, SC_BLEEDING }; - int j; - j = i = rnd()%ARRAYLENGTH(sc); - while ( !sc_start(bl,sc[i],100,skill_lv,skill_get_time2(skill_id,i+1)) ) { - i++; - if ( i == ARRAYLENGTH(sc) ) - i = 0; - if (i == j) - break; - } - break; - } - case NPC_WIDEBLEEDING: - case NPC_WIDECONFUSE: - case NPC_WIDECURSE: - case NPC_WIDEFREEZE: - case NPC_WIDESLEEP: - case NPC_WIDESILENCE: - case NPC_WIDESTONE: - case NPC_WIDESTUN: - case NPC_SLOWCAST: - case NPC_WIDEHELLDIGNITY: - if (flag&1) - sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - else { - skill_area_temp[2] = 0; //For SD_PREAMBLE - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skill_id, skill_lv),BL_CHAR, - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1, - skill_castend_nodamage_id); - } - break; - case NPC_WIDESOULDRAIN: - if (flag&1) - status_percent_damage(src,bl,0,((skill_lv-1)%5+1)*20,false); - else { - skill_area_temp[2] = 0; //For SD_PREAMBLE - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skill_id, skill_lv),BL_CHAR, - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1, - skill_castend_nodamage_id); - } - break; - case ALL_PARTYFLEE: - if( sd && !(flag&1) ) - { - if( !sd->status.party_id ) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - } - else - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - break; - case NPC_TALK: - case ALL_WEWISH: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - case ALL_BUYING_STORE: - if( sd ) - {// players only, skill allows 5 buying slots - clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS)); - } - break; - case RK_ENCHANTBLADE: - clif_skill_nodamage(src,bl,skill_id,skill_lv,// formula not confirmed - sc_start2(bl,type,100,skill_lv,100+20*skill_lv/*+sstatus->int_/2+status_get_lv(bl)/10*/,skill_get_time(skill_id,skill_lv))); - break; - case RK_DRAGONHOWLING: - if( flag&1) - sc_start(bl,type,50 + 6 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)); - else - { - skill_area_temp[2] = 0; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skill_id,skill_lv),BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_PREAMBLE|1, - skill_castend_nodamage_id); - } - break; - case RK_IGNITIONBREAK: - case LG_EARTHDRIVE: - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - i = skill_get_splash(skill_id,skill_lv); - if( skill_id == LG_EARTHDRIVE ) { - int dummy = 1; - 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,i,BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - break; - case RK_STONEHARDSKIN: - if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 4 ) - { - int heal = sstatus->hp / 4; // 25% HP - if( status_charge(bl,heal,0) ) - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start2(bl,type,100,skill_lv,heal,skill_get_time(skill_id,skill_lv))); - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - case RK_REFRESH: - if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 8 ) - { - int heal = status_get_max_hp(bl) * 25 / 100; - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - status_heal(bl,heal,0,1); - status_change_clear_buffs(bl,4); - } - break; - - case RK_MILLENNIUMSHIELD: - if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 9 ) - { - short shields = (rnd()%100<50) ? 4 : ((rnd()%100<80) ? 3 : 2); - sc_start4(bl,type,100,skill_lv,shields,1000,0,skill_get_time(skill_id,skill_lv)); - clif_millenniumshield(sd,shields); - clif_skill_nodamage(src,bl,skill_id,1,1); - } - break; - - case RK_GIANTGROWTH: - case RK_VITALITYACTIVATION: - case RK_ABUNDANCE: - case RK_CRUSHSTRIKE: - if( sd ) - { - int lv = 1; // RK_GIANTGROWTH - if( skill_id == RK_VITALITYACTIVATION ) - lv = 2; - else if( skill_id == RK_ABUNDANCE ) - lv = 6; - else if( skill_id == RK_CRUSHSTRIKE ) - lv = 7; - if( pc_checkskill(sd,RK_RUNEMASTERY) >= lv ) - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - } - 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(skill_id,skill_lv)); - else - sc_start(bl,type,100,skill_area_temp[5]/4,skill_get_time(skill_id,skill_lv)); - } else if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 5 ) { - if( sd->status.party_id ) { - i = party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,BCT_PARTY,skill_area_sub_count); - skill_area_temp[5] = 7 * i; // ATK - party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id); - } else - sc_start2(bl,type,100,7,5,skill_get_time(skill_id,skill_lv)); - } - clif_skill_nodamage(src,bl,skill_id,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(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,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(skill_id,skill_lv)); - clif_skill_nodamage(src,src,skill_id,skill_lv,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,skill_lv,skill_get_time(skill_id,skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - - case GC_CREATENEWPOISON: - if( sd ) - { - clif_skill_produce_mix_list(sd,skill_id,25); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - } - break; - - case GC_POISONINGWEAPON: - if( sd ) { - clif_poison_list(sd,skill_lv); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case GC_ANTIDOTE: - clif_skill_nodamage(src,bl,skill_id,skill_lv,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, skill_id, skill_lv, 6); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - break; - - case GC_HALLUCINATIONWALK: - { - 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,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); - break; - } - if( !status_charge(bl,heal,0) ) - { - if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - } - break; - /** - * Arch Bishop - **/ - case AB_ANCILLA: - if( sd ) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_produce_mix(sd, skill_id, ITEMID_ANCILLA, 0, 0, 0, 1); - } - break; - - case AB_CLEMENTIA: - case AB_CANTO: - { - int bless_lv = pc_checkskill(sd,AL_BLESSING) + (sd->status.job_level / 10); - int agi_lv = pc_checkskill(sd,AL_INCAGI) + (sd->status.job_level / 10); - if( sd == NULL || sd->status.party_id == 0 || flag&1 ) - clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100, - (skill_id == AB_CLEMENTIA)? bless_lv : (skill_id == AB_CANTO)? agi_lv : skill_lv, skill_get_time(skill_id,skill_lv))); - else if( sd ) - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, 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, skill_id, skill_lv, sc_start4(bl, type, 100, skill_lv, 0, 0, 1, skill_get_time(skill_id, skill_lv))); - else if( sd ) - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - break; - - case AB_CHEAL: - if( sd == NULL || sd->status.party_id == 0 || flag&1 ) - { - if( sd && tstatus && !battle_check_undead(tstatus->race, tstatus->def_ele) ) - { - i = skill_calc_heal(src, bl, AL_HEAL, pc_checkskill(sd, AL_HEAL), true); - - if( (dstsd && pc_ismadogear(dstsd)) || status_isimmune(bl)) - i = 0; // Should heal by 0 or won't do anything?? in iRO it breaks the healing to members.. [malufett] - - clif_skill_nodamage(bl, bl, skill_id, i, 1); - if( tsc && tsc->data[SC_AKAITSUKI] && i ) - i = ~i + 1; - status_heal(bl, i, 0, 0); - } - } - else if( sd ) - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - break; - - case AB_ORATIO: - if( flag&1 ) - sc_start(bl, type, 40 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv)); - else - { - map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - } - break; - - case AB_LAUDAAGNUS: - if( flag&1 || sd == NULL ) { - if( tsc && (tsc->data[SC_FREEZE] || tsc->data[SC_STONE] || tsc->data[SC_BLIND] || - tsc->data[SC_BURNING] || tsc->data[SC_FREEZING] || tsc->data[SC_CRYSTALIZE])) { - // Success Chance: (40 + 10 * Skill Level) % - if( rnd()%100 > 40+10*skill_lv ) break; - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_BLIND, INVALID_TIMER); - status_change_end(bl, SC_BURNING, INVALID_TIMER); - status_change_end(bl, SC_FREEZING, INVALID_TIMER); - status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER); - }else //Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets - clif_skill_nodamage(bl, bl, skill_id, skill_lv, - sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv))); - } else if( sd ) - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), - src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - break; - - case AB_LAUDARAMUS: - if( flag&1 || sd == NULL ) { - if( tsc && (tsc->data[SC_SLEEP] || tsc->data[SC_STUN] || tsc->data[SC_MANDRAGORA] || tsc->data[SC_SILENCE]) ){ - // Success Chance: (40 + 10 * Skill Level) % - if( rnd()%100 > 40+10*skill_lv ) break; - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_STUN, INVALID_TIMER); - status_change_end(bl, SC_MANDRAGORA, INVALID_TIMER); - status_change_end(bl, SC_SILENCE, INVALID_TIMER); - }else // Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets - clif_skill_nodamage(bl, bl, skill_id, skill_lv, - sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv))); - } else if( sd ) - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), - src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - break; - - case AB_CLEARANCE: - if( flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1 ) - { //As of the behavior in official server Clearance is just a super version of Dispell skill. [Jobbie] - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rnd()%100 >= 30 + 10 * skill_lv) - { - if (sd) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,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_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: - case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD: - case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART: - case SC_PARTYFLEE: case SC_GT_REVITALIZE: - case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE: -#ifdef RENEWAL - case SC_EXTREMITYFIST2: -#endif - 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,INVALID_TIMER); - } - break; - } - map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skill_id, skill_lv, 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(skill_id, skill_lv), BL_CHAR, - src, PR_LEXDIVINA, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - break; - /** - * Warlock - **/ - case WL_STASIS: - if( flag&1 ) - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - else - { - map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id, skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,(map_flag_vs(src->m)?BCT_ALL:BCT_ENEMY|BCT_SELF)|flag|1,skill_castend_nodamage_id); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - } - break; - - case WL_WHITEIMPRISON: - if( (src == bl || battle_check_target(src, bl, BCT_ENEMY)) && !is_boss(bl) )// Should not work with bosses. - { - int rate = ( sd? sd->status.job_level : 50 ) / 4; - - if( src == bl ) rate = 100; // Success Chance: On self, 100% - else if(bl->type == BL_PC) rate += 20 + 10 * skill_lv; // On Players, (20 + 10 * Skill Level) % - else rate += 40 + 10 * skill_lv; // On Monsters, (40 + 10 * Skill Level) % - - if( sd ) - skill_blockpc_start(sd,skill_id,4000); - - if( !(tsc && tsc->data[type]) ){ - i = sc_start2(bl,type,rate,skill_lv,src->id,(src == bl)?5000:(bl->type == BL_PC)?skill_get_time(skill_id,skill_lv):skill_get_time2(skill_id, skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv,i); - if( !i ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } - }else - if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0); - break; - - case WL_FROSTMISTY: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id); - break; - - case WL_JACKFROST: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_foreachinshootrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,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, skill_id, skill_lv, - sc_start4(bl, type, 100, skill_lv, status_get_int(src), sd ? sd->status.job_level : 50, 0, - skill_get_time(skill_id, skill_lv))); - break; - - case WL_SIENNAEXECRATE: - if( status_isimmune(bl) || !tsc ) - break; - - if( flag&1 ) { - 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,INVALID_TIMER); - else - status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2); - } else { - int rate = 40 + 8 * skill_lv + ( sd? sd->status.job_level : 50 ) / 4; - // IroWiki says Rate should be reduced by target stats, but currently unknown - if( rnd()%100 < rate ) { // Success on First Target - if( !tsc->data[SC_STONE] ) - rate = status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2); - else { - rate = 1; - status_change_end(bl,SC_STONE,INVALID_TIMER); - } - - if( rate ) { - skill_area_temp[1] = bl->id; - map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,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,skill_id,USESKILL_FAIL_LEVEL,0); - } - break; - - case WL_SUMMONFB: - case WL_SUMMONBL: - case WL_SUMMONWB: - case WL_SUMMONSTONE: - { - 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,skill_id,USESKILL_FAIL_SUMMON,0); - break; - } - - pos++; // Used in val2 for SC. Indicates the order of this ball - switch( skill_id ) - { // 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,skill_lv,0,skill_get_time(skill_id,skill_lv)); - clif_skill_nodamage(src,bl,skill_id,0,0); - } - break; - - case WL_READING_SB: - if( sd ) { - struct status_change *sc = status_get_sc(bl); - - for( i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) - if( sc && !sc->data[i] ) - break; - if( i == SC_MAXSPELLBOOK ) { - clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0); - break; - } - - sc_start(bl, SC_STOP, 100, skill_lv, INVALID_TIMER); //Can't move while selecting a spellbook. - clif_spellbook_list(sd); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - } - break; - /** - * Ranger - **/ - case RA_FEARBREEZE: - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv))); - break; - - case RA_WUGMASTERY: - if( sd ) { - 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,skill_id,skill_lv,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); - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case RA_WUGDASH: - if( tsce ) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER)); - map_freeblock_unlock(); - return 0; - } - if( sd && pc_isridingwug(sd) ) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,1)); - clif_walkok(sd); - } - break; - - case RA_SENSITIVEKEEN: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id); - break; - /** - * Mechanic - **/ - case NC_F_SIDESLIDE: - case NC_B_SIDESLIDE: - { - uint8 dir = (skill_id == NC_F_SIDESLIDE) ? (unit_getdir(src)+4)%8 : unit_getdir(src); - skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),dir,0x1); - clif_slide(src,src->x,src->y); - clif_fixpos(src); //Aegis sent this packet - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case NC_SELFDESTRUCTION: - if( sd ) { - if( pc_ismadogear(sd) ) - pc_setmadogear(sd, 0); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag); - status_set_sp(src, 0, 0); - } - break; - - case NC_ANALYZE: - clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - clif_skill_nodamage(src, bl, skill_id, skill_lv, - sc_start(bl,type, 30 + 12 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv))); - if( sd ) pc_overheat(sd,1); - break; - - case NC_MAGNETICFIELD: - if( (i = sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv))) ) - { - map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill_castend_damage_id);; - clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6); - if (sd) pc_overheat(sd,1); - } - clif_skill_nodamage(src,src,skill_id,skill_lv,i); - break; - - case NC_REPAIR: - if( sd ) - { - int heal; - if( dstsd && pc_ismadogear(dstsd) ) - { - heal = dstsd->status.max_hp * (3+3*skill_lv) / 100; - status_heal(bl,heal,0,2); - } else { - heal = sd->status.max_hp * (3+3*skill_lv) / 100; - status_heal(src,heal,0,2); - } - - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - clif_skill_nodamage(src, bl, skill_id, skill_lv, heal); - } - break; - - case NC_DISJOINT: - { - if( bl->type != BL_MOB ) break; - md = map_id2md(bl->id); - if( md && md->class_ >= MOBID_SILVERSNIPER && md->class_ <= MOBID_MAGICDECOY_WIND ) - status_kill(bl); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - } - break; - case SC_AUTOSHADOWSPELL: - if( sd ) { - if( sd->status.skill[sd->reproduceskill_id].id || sd->status.skill[sd->cloneskill_id].id ) { - sc_start(src,SC_STOP,100,skill_lv,-1);// The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax] - clif_autoshadowspell_list(sd); - clif_skill_nodamage(src,bl,skill_id,1,1); - } - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL_IMITATION_SKILL_NONE,0); - } - break; - - case SC_SHADOWFORM: - if( sd && dstsd && src != bl && !dstsd->shadowform_id ) { - if( clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(src,type,100,skill_lv,bl->id,4+skill_lv,0,skill_get_time(skill_id, skill_lv))) ) - dstsd->shadowform_id = src->id; - } - else if( sd ) - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - break; - - case SC_BODYPAINT: - if( flag&1 ) { - if( tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] || - tsc->data[SC_CHASEWALK] || tsc->data[SC_CLOAKINGEXCEED] || - tsc->data[SC__INVISIBILITY]) ) { - status_change_end(bl, SC_HIDING, INVALID_TIMER); - status_change_end(bl, SC_CLOAKING, INVALID_TIMER); - status_change_end(bl, SC_CHASEWALK, INVALID_TIMER); - status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); - status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER); - - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - sc_start(bl,SC_BLIND,53 + 2 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)); - } - } else { - clif_skill_nodamage(src, bl, skill_id, 0, 1); - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - } - break; - - case SC_ENERVATION: - case SC_GROOMY: - case SC_LAZINESS: - case SC_UNLUCKY: - case SC_WEAKNESS: - if( !(tsc && tsc->data[type]) ) { - //((rand(myDEX / 12, myDEX / 4) + myJobLevel + 10 * skLevel) + myLevel / 10) - (targetLevel / 10 + targetLUK / 10 + (targetMaxWeight - targetWeight) / 1000 + rand(targetAGI / 6, targetAGI / 3)) - int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10 - - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3); - rate = cap_value(rate, skill_lv+sstatus->dex/20, 100); - clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv))); - } else if( sd ) - clif_skill_fail(sd,skill_id,0,0); - break; - - case SC_IGNORANCE: - if( !(tsc && tsc->data[type]) ) { - int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10 - - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3); - rate = cap_value(rate, skill_lv+sstatus->dex/20, 100); - if (clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv)))) { - int sp = 200 * skill_lv; - if( dstmd ) sp = dstmd->level * 2; - if( status_zap(bl,0,sp) ) - status_heal(src,0,sp/2,3); - } - else if( sd ) clif_skill_fail(sd,skill_id,0,0); - } else if( sd ) - clif_skill_fail(sd,skill_id,0,0); - break; - - case LG_TRAMPLE: - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - map_foreachinrange(skill_destroy_trap,bl,skill_get_splash(skill_id,skill_lv),BL_SKILL,tick); - break; - - case LG_REFLECTDAMAGE: - if( tsc && tsc->data[type] ) - status_change_end(bl,type,INVALID_TIMER); - else - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - break; - - case LG_SHIELDSPELL: - if( flag&1 ) { - int duration = (sd) ? sd->bonus.shieldmdef * 2000 : 10000; - sc_start(bl,SC_SILENCE,100,skill_lv,duration); - } else if( sd ) { - int opt = skill_lv; - int rate = rnd()%100; - int val, brate; - switch( skill_lv ) { - case 1: - { - struct item_data *shield_data = sd->inventory_data[sd->equip_index[EQI_HAND_L]]; - if( !shield_data || shield_data->type != IT_ARMOR ) { // No shield? - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - break; - } - brate = shield_data->def * 10; - if( rate < 50 ) - opt = 1; - else if( rate < 75 ) - opt = 2; - else - opt = 3; - - switch( opt ) { - case 1: - sc_start(bl,SC_SHIELDSPELL_DEF,100,opt,-1); - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( rate < brate ) - map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - status_change_end(bl,SC_SHIELDSPELL_DEF,INVALID_TIMER); - break; - case 2: - val = shield_data->def / 10; // % Reflected damage. - sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 1000); - break; - case 3: - val = shield_data->def; // Attack increase. - sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 3000); - break; - } - } - break; - - case 2: - brate = sd->bonus.shieldmdef * 20; - if( rate < 30 ) - opt = 1; - else if( rate < 60 ) - opt = 2; - else - opt = 3; - switch( opt ) { - case 1: - sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1); - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( rate < brate ) - map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|2,skill_castend_damage_id); - status_change_end(bl,SC_SHIELDSPELL_MDEF,INVALID_TIMER); - break; - case 2: - sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1); - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( rate < brate ) - map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id); - break; - case 3: - if( sc_start(bl,SC_SHIELDSPELL_MDEF,brate,opt,sd->bonus.shieldmdef * 30000) ) - clif_skill_nodamage(src,bl,PR_MAGNIFICAT,skill_lv, - sc_start(bl,SC_MAGNIFICAT,100,1,sd->bonus.shieldmdef * 30000)); - break; - } - break; - - case 3: - { - struct item *it = &sd->status.inventory[sd->equip_index[EQI_HAND_L]]; - if( !it ) { // No shield? - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - brate = it->refine * 5; - if( rate < 25 ) - opt = 1; - else if( rate < 50 ) - opt = 2; - else - opt = 3; - switch( opt ) { - case 1: - val = 105 * it->refine / 10; - sc_start2(bl,SC_SHIELDSPELL_REF,brate,opt,val,skill_get_time(skill_id,skill_lv)); - break; - case 2: case 3: - if( rate < brate ) - { - val = sstatus->max_hp * (11 + it->refine) / 100; - status_heal(bl, val, 0, 3); - } - break; - /*case 3: - // Full protection. I need confirm what effect should be here. Moved to case 2 to until we got it. - break;*/ - } - } - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case LG_PIETY: - if( flag&1 ) - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - else { - skill_area_temp[2] = 0; - map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_PC,src,skill_id,skill_lv,tick,flag|SD_PREAMBLE|BCT_PARTY|BCT_SELF|1,skill_castend_nodamage_id); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case LG_INSPIRATION: - if( sd && !map[sd->bl.m].flag.noexppenalty && sd->status.base_level != MAX_LEVEL ) { - sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * 1 / 100); // 1% penalty. - sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * 1 / 100); - clif_updatestatus(sd,SP_BASEEXP); - clif_updatestatus(sd,SP_JOBEXP); - } - clif_skill_nodamage(bl,src,skill_id,skill_lv, - sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv))); - break; - case SR_CURSEDCIRCLE: - if( flag&1 ) { - if( is_boss(bl) ) break; - if( sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id, skill_lv))) { - if( bl->type == BL_MOB ) - mob_unlocktarget((TBL_MOB*)bl,gettick()); - unit_stop_attack(bl); - clif_bladestop(src, bl->id, 1); - map_freeblock_unlock(); - return 1; - } - } else { - int count = 0; - clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - count = map_forcountinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv), (sd)?sd->spiritball_old:15, // Assume 15 spiritballs in non-charactors - BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - if( sd ) pc_delspiritball(sd, count, 0); - clif_skill_nodamage(src, src, skill_id, skill_lv, - sc_start2(src, SC_CURSEDCIRCLE_ATKER, 100, skill_lv, count, skill_get_time(skill_id,skill_lv))); - } - break; - - case SR_RAISINGDRAGON: - if( sd ) { - short max = 5 + skill_lv; - sc_start(bl, SC_EXPLOSIONSPIRITS, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - for( i = 0; i < max; i++ ) // Don't call more than max available spheres. - pc_addspiritball(sd, skill_get_time(skill_id, skill_lv), max); - clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv))); - } - break; - - case SR_ASSIMILATEPOWER: - if( flag&1 ) { - i = 0; - if( dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER ) - { - i = dstsd->spiritball; //1%sp per spiritball. - pc_delspiritball(dstsd, dstsd->spiritball, 0); - } - if( i ) status_percent_heal(src, 0, i); - clif_skill_nodamage(src, bl, skill_id, skill_lv, i ? 1:0); - } else { - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF|SD_SPLASH|1, skill_castend_nodamage_id); - } - break; - - case SR_POWERVELOCITY: - if( !dstsd ) - break; - if( sd && dstsd->spiritball <= 5 ) { - for(i = 0; i <= 5; i++) { - pc_addspiritball(dstsd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), i); - pc_delspiritball(sd, sd->spiritball, 0); - } - } - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - break; - - case SR_GENTLETOUCH_CURE: - { - int heal; - - if( status_isimmune(bl) ) - { - clif_skill_nodamage(src,bl,skill_id,skill_lv,0); - break; - } - - heal = 120 * skill_lv + status_get_max_hp(bl) * (2 + skill_lv) / 100; - status_heal(bl, heal, 0, 0); - - if( (tsc && tsc->opt1) && (rnd()%100 < ((skill_lv * 5) + (status_get_dex(src) + status_get_lv(src)) / 4) - (1 + (rnd() % 10))) ) - { - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_STUN, INVALID_TIMER); - status_change_end(bl, SC_POISON, INVALID_TIMER); - status_change_end(bl, SC_SILENCE, INVALID_TIMER); - status_change_end(bl, SC_BLIND, INVALID_TIMER); - status_change_end(bl, SC_HALLUCINATION, INVALID_TIMER); - status_change_end(bl, SC_BURNING, INVALID_TIMER); - status_change_end(bl, SC_FREEZING, INVALID_TIMER); - } - - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - case SR_GENTLETOUCH_CHANGE: - case SR_GENTLETOUCH_REVITALIZE: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv))); - break; - case WA_SWING_DANCE: - case WA_MOONLIT_SERENADE: - if( sd == NULL || sd->status.party_id == 0 || (flag & 1) ) - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - else if( sd ) { // Only shows effects on caster. - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - } - break; - - case WA_SYMPHONY_OF_LOVER: - case MI_RUSH_WINDMILL: - case MI_ECHOSONG: - if( sd == NULL || sd->status.party_id == 0 || (flag & 1) ) - sc_start4(bl,type,100,skill_lv,6*skill_lv,(sd?pc_checkskill(sd,WM_LESSON):0),(sd?sd->status.job_level:0),skill_get_time(skill_id,skill_lv)); - else if( sd ) { // Only shows effects on caster. - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id); - } - break; - - case MI_HARMONIZE: - if( src != bl ) - clif_skill_nodamage(src, src, skill_id, skill_lv, sc_start(src, type, 100, skill_lv, skill_get_time(skill_id,skill_lv))); - clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id,skill_lv))); - break; - - case WM_DEADHILLHERE: - if( bl->type == BL_PC ) { - if( !status_isdead(bl) ) - break; - - if( rnd()%100 < 88 + 2 * skill_lv ) { - int heal = tstatus->sp; - if( heal <= 0 ) - heal = 1; - tstatus->hp = heal; - tstatus->sp -= tstatus->sp * ( 120 - 20 * skill_lv ) / 100; - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - pc_revive((TBL_PC*)bl,heal,0); - clif_resurrection(bl,1); - } - } - break; - - case WM_SIRCLEOFNATURE: - flag |= BCT_SELF|BCT_PARTY|BCT_GUILD; - case WM_VOICEOFSIREN: - if( skill_id != WM_SIRCLEOFNATURE ) - flag &= ~BCT_SELF; - if( flag&1 ) { - sc_start2(bl,type,(skill_id==WM_VOICEOFSIREN)?20+10*skill_lv:100,skill_lv,(skill_id==WM_VOICEOFSIREN)?src->id:0,skill_get_time(skill_id,skill_lv)); - } else { - map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),(skill_id==WM_VOICEOFSIREN)?BL_CHAR|BL_SKILL:BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case WM_GLOOMYDAY: - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if( dstsd && ( pc_checkskill(dstsd,KN_BRANDISHSPEAR) || pc_checkskill(dstsd,LK_SPIRALPIERCE) || - pc_checkskill(dstsd,CR_SHIELDCHARGE) || pc_checkskill(dstsd,CR_SHIELDBOOMERANG) || - pc_checkskill(dstsd,PA_SHIELDCHAIN) || pc_checkskill(dstsd,LG_SHIELDPRESS) ) ) - { - sc_start(bl,SC_GLOOMYDAY_SK,100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - } - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - break; - - case WM_SATURDAY_NIGHT_FEVER: - if( flag&1 ) { // Affect to all targets arround the caster and caster too. - if( !(tsc && tsc->data[type]) ) - sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv)); - } else if( flag&2 ) { - if( src->id != bl->id && battle_check_target(src,bl,BCT_ENEMY) > 0 ) - status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0)); - } else if( sd ) { - short chance = sstatus->int_/6 + sd->status.job_level/5 + skill_lv*4; - if( !sd->status.party_id || (rnd()%100 > chance)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0); - break; - } - if( map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id,skill_lv), - BL_PC, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count) > 7 ) - flag |= 2; - else - flag |= 1; - map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF, skill_castend_nodamage_id); - clif_skill_nodamage(src, bl, skill_id, skill_lv, - sc_start(src,SC_STOP,100,skill_lv,skill_get_time2(skill_id,skill_lv))); - if( flag&2 ) // Dealed here to prevent conflicts - status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0)); - } - break; - - case WM_SONG_OF_MANA: - case WM_DANCE_WITH_WUG: - case WM_LERADS_DEW: - if( flag&1 ) { // These affect to to all party members near the caster. - struct status_change *sc = status_get_sc(src); - if( sc && sc->data[type] ) { - sc_start2(bl,type,100,skill_lv,sc->data[type]->val2,skill_get_time(skill_id,skill_lv)); - } - } else if( sd ) { - short lv = (short)skill_lv; - int count = skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1); - if( sc_start2(bl,type,100,skill_lv,count,skill_get_time(skill_id,skill_lv)) ) - party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - - } - break; - - case WM_MELODYOFSINK: - case WM_BEYOND_OF_WARCRY: - case WM_UNLIMITED_HUMMING_VOICE: - if( flag&1 ) { - sc_start2(bl,type,100,skill_lv,skill_area_temp[0],skill_get_time(skill_id,skill_lv)); - } else { // These affect to all targets arround the caster. - short lv = (short)skill_lv; - skill_area_temp[0] = (sd) ? skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1) : 50; // 50% chance in non BL_PC (clones). - map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case WM_RANDOMIZESPELL: { - int improv_skill_id = 0, improv_skill_lv; - do { - i = rnd() % MAX_SKILL_IMPROVISE_DB; - improv_skill_id = skill_improvise_db[i].skill_id; - } while( improv_skill_id == 0 || rnd()%10000 >= skill_improvise_db[i].per ); - improv_skill_lv = 4 + skill_lv; - clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); - - if( sd ) { - sd->state.abra_flag = 2; - sd->skillitem = improv_skill_id; - sd->skillitemlv = improv_skill_lv; - clif_item_skill(sd, improv_skill_id, improv_skill_lv); - } else { - struct unit_data *ud = unit_bl2ud(src); - int inf = skill_get_inf(improv_skill_id); - int target_id = 0; - if (!ud) break; - if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) { - if (src->type == BL_PET) - bl = (struct block_list*)((TBL_PET*)src)->msd; - if (!bl) bl = src; - unit_skilluse_id(src, bl->id, improv_skill_id, improv_skill_lv); - } else { - if (ud->target) - target_id = ud->target; - else switch (src->type) { - case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break; - case BL_PET: target_id = ((TBL_PET*)src)->target_id; break; - } - if (!target_id) - break; - if (skill_get_casttype(improv_skill_id) == CAST_GROUND) { - bl = map_id2bl(target_id); - if (!bl) bl = src; - unit_skilluse_pos(src, bl->x, bl->y, improv_skill_id, improv_skill_lv); - } else - unit_skilluse_id(src, target_id, improv_skill_id, improv_skill_lv); - } - } - } - break; - - - case RETURN_TO_ELDICASTES: - case ALL_GUARDIAN_RECALL: - if( sd ) - { - short x, y; // Destiny position. - unsigned short mapindex; - - if( skill_id == RETURN_TO_ELDICASTES) - { - x = 198; - y = 187; - mapindex = mapindex_name2id(MAP_DICASTES); - } - else - { - x = 44; - y = 151; - mapindex = mapindex_name2id(MAP_MORA); - } - - if(!mapindex) - { //Given map not found? - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - map_freeblock_unlock(); - return 0; - } - pc_setpos(sd, mapindex, x, y, CLR_TELEPORT); - } - break; - - case GM_SANDMAN: - if( tsc ) { - if( tsc->opt1 == OPT1_SLEEP ) - tsc->opt1 = 0; - else - tsc->opt1 = OPT1_SLEEP; - clif_changeoption(bl); - clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); - } - break; - - case SO_ARRULLO: - if( flag&1 ) - sc_start2(bl, type, 88 + 2 * skill_lv, skill_lv, 1, skill_get_time(skill_id, skill_lv)); - else { - clif_skill_nodamage(src, bl, skill_id, 0, 1); - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - } - break; - - case SO_SUMMON_AGNI: - case SO_SUMMON_AQUA: - case SO_SUMMON_VENTUS: - case SO_SUMMON_TERA: - if( sd ) { - int elemental_class = skill_get_elemental_type(skill_id,skill_lv); - - // Remove previous elemental fisrt. - if( sd->ed ) - elemental_delete(sd->ed,0); - - // Summoning the new one. - if( !elemental_create(sd,elemental_class,skill_get_time(skill_id,skill_lv)) ) { - clif_skill_fail(sd,skill_id,0,0); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case SO_EL_CONTROL: - if( sd ) { - int mode = EL_MODE_PASSIVE; // Standard mode. - - if( !sd->ed ) break; - - if( skill_lv == 4 ) {// At level 4 delete elementals. - elemental_delete(sd->ed, 0); - break; - } - switch( skill_lv ) {// Select mode bassed on skill level used. - case 2: mode = EL_MODE_ASSIST; break; - case 3: mode = EL_MODE_AGGRESSIVE; break; - } - if( !elemental_change_mode(sd->ed,mode) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - - case SO_EL_ACTION: - if( sd ) { - int duration = 3000; - if( !sd->ed ) break; - sd->skill_id_old = skill_id; - elemental_action(sd->ed, bl, tick); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - switch(sd->ed->db->class_){ - case 2115:case 2124: - case 2118:case 2121: - duration = 6000; - break; - case 2116:case 2119: - case 2122:case 2125: - duration = 9000; - break; - } - skill_blockpc_start(sd, skill_id, duration); - } - break; - - case SO_EL_CURE: - if( sd ) { - struct elemental_data *ed = sd->ed; - int s_hp = sd->battle_status.hp * 10 / 100, s_sp = sd->battle_status.sp * 10 / 100; - int e_hp, e_sp; - - if( !ed ) break; - if( !status_charge(&sd->bl,s_hp,s_sp) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - e_hp = ed->battle_status.max_hp * 10 / 100; - e_sp = ed->battle_status.max_sp * 10 / 100; - status_heal(&ed->bl,e_hp,e_sp,3); - clif_skill_nodamage(src,&ed->bl,skill_id,skill_lv,1); - } - break; - - case GN_CHANGEMATERIAL: - case SO_EL_ANALYSIS: - if( sd ) { - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - clif_skill_itemlistwindow(sd,skill_id,skill_lv); - } - break; - - case GN_BLOOD_SUCKER: - { - struct status_change *sc = status_get_sc(src); - - if( sc && sc->bs_counter < skill_get_maxcount( skill_id , skill_lv) ) { - if( tsc && tsc->data[type] ){ - (sc->bs_counter)--; - status_change_end(src, type, INVALID_TIMER); // the first one cancels and the last one will take effect resetting the timer - } - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id,skill_lv)); - (sc->bs_counter)++; - } else if( sd ) { - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - break; - } - } - break; - - case GN_MANDRAGORA: - if( flag&1 ) { - if ( clif_skill_nodamage(bl, src, skill_id, skill_lv, - sc_start(bl, type, 25 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv))) ) - status_zap(bl, 0, status_get_max_sp(bl) * (25 + 5 * skill_lv) / 100); - } else - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id); - break; - - case GN_SLINGITEM: - if( sd ) { - short ammo_id; - i = sd->equip_index[EQI_AMMO]; - if( i <= 0 ) - break; // No ammo. - ammo_id = sd->inventory_data[i]->nameid; - if( ammo_id <= 0 ) - break; - sd->itemid = ammo_id; - if( itemdb_is_GNbomb(ammo_id) ) { - if(battle_check_target(src,bl,BCT_ENEMY) > 0) {// Only attack if the target is an enemy. - if( ammo_id == 13263 ) - map_foreachincell(skill_area_sub,bl->m,bl->x,bl->y,BL_CHAR,src,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - else - skill_attack(BF_WEAPON,src,src,bl,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag); - } else //Otherwise, it fails, shows animation and removes items. - clif_skill_fail(sd,GN_SLINGITEM_RANGEMELEEATK,0xa,0); - } else if( itemdb_is_GNthrowable(ammo_id) ){ - struct script_code *script = sd->inventory_data[i]->script; - if( !script ) - break; - if( dstsd ) - run_script(script,0,dstsd->bl.id,fake_nd->bl.id); - else - run_script(script,0,src->id,0); - } - } - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1);// This packet is received twice actually, I think it is to show the animation. - break; - - case GN_MIX_COOKING: - case GN_MAKEBOMB: - case GN_S_PHARMACY: - if( sd ) { - int qty = 1; - sd->skill_id_old = skill_id; - sd->skill_lv_old = skill_lv; - if( skill_id != GN_S_PHARMACY && skill_lv > 1 ) - qty = 10; - clif_cooking_list(sd,(skill_id - GN_MIX_COOKING) + 27,skill_id,qty,skill_id==GN_MAKEBOMB?5:6); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - } - break; - case EL_CIRCLE_OF_FIRE: - case EL_PYROTECHNIC: - case EL_HEATER: - case EL_TROPIC: - case EL_AQUAPLAY: - case EL_COOLER: - case EL_CHILLY_AIR: - case EL_GUST: - case EL_BLAST: - case EL_WILD_STORM: - case EL_PETROLOGY: - case EL_CURSED_SOIL: - case EL_UPHEAVAL: - case EL_FIRE_CLOAK: - case EL_WATER_DROP: - case EL_WIND_CURTAIN: - case EL_SOLID_SKIN: - case EL_STONE_SHIELD: - case EL_WIND_STEP: { - struct elemental_data *ele = BL_CAST(BL_ELEM, src); - if( ele ) { - sc_type type2 = type-1; - struct status_change *sc = status_get_sc(&ele->bl); - - if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) { - elemental_clean_single_effect(ele, skill_id); - } else { - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - clif_skill_damage(src, ( skill_id == EL_GUST || skill_id == EL_BLAST || skill_id == EL_WILD_STORM )?src:bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - if( skill_id == EL_WIND_STEP ) // There aren't teleport, just push the master away. - skill_blown(src,bl,(rnd()%skill_get_blewcount(skill_id,skill_lv))+1,rand()%8,0); - sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv)); - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)); - } - } - } - break; - - case EL_FIRE_MANTLE: - case EL_WATER_BARRIER: - case EL_ZEPHYR: - case EL_POWER_OF_GAIA: - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - skill_unitsetting(src,skill_id,skill_lv,bl->x,bl->y,0); - break; - - case EL_WATER_SCREEN: { - struct elemental_data *ele = BL_CAST(BL_ELEM, src); - if( ele ) { - struct status_change *sc = status_get_sc(&ele->bl); - sc_type type2 = type-1; - - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) { - elemental_clean_single_effect(ele, skill_id); - } else { - // This not heals at the end. - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv)); - sc_start(bl,type,100,src->id,skill_get_time(skill_id,skill_lv)); - } - } - } - break; - - case KO_KAHU_ENTEN: - case KO_HYOUHU_HUBUKI: - case KO_KAZEHU_SEIRAN: - case KO_DOHU_KOUKAI: - if(sd) { - int ttype = skill_get_ele(skill_id, skill_lv); - clif_skill_nodamage(src, bl, skill_id, skill_lv, 1); - pc_add_talisman(sd, skill_get_time(skill_id, skill_lv), 10, ttype); - } - break; - - case KO_ZANZOU: - if(sd){ - struct mob_data *md; - - md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), 2308, "", SZ_SMALL, AI_NONE); - if( md ) - { - md->master_id = src->id; - md->special_state.ai = AI_ZANZOU; - if( md->deletetimer != INVALID_TIMER ) - delete_timer(md->deletetimer, mob_timer_delete); - md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0); - mob_spawn( md ); - pc_setinvincibletimer(sd,500);// unlock target lock - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0); - } - } - break; - - case KO_KYOUGAKU: - if( dstsd && tsc && !tsc->data[type] && rand()%100 < tstatus->int_/2 ){ - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - }else if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - - case KO_JYUSATSU: - if( dstsd && tsc && !tsc->data[type] && - rand()%100 < ((45+5*skill_lv) + skill_lv*5 - status_get_int(bl)/2) ){//[(Base chance of success) + (Skill Level x 5) - (int / 2)]%. - clif_skill_nodamage(src,bl,skill_id,skill_lv, - status_change_start(bl,type,10000,skill_lv,0,0,0,skill_get_time(skill_id,skill_lv),1)); - status_zap(bl, tstatus->max_hp*skill_lv*5/100 , 0); - if( status_get_lv(bl) <= status_get_lv(src) ) - status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0); - }else if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - - case KO_GENWAKU: - if ( !map_flag_gvg(src->m) && ( dstsd || dstmd ) && battle_check_target(src,bl,BCT_ENEMY) > 0 ) { - int x = src->x, y = src->y; - - if( sd && rnd()%100 > ((45+5*skill_lv) - status_get_int(bl)/10) ){//[(Base chance of success) - (Intelligence Objectives / 10)]%. - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - - if (unit_movepos(src,bl->x,bl->y,0,0)) { - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - clif_slide(src,bl->x,bl->y) ; - sc_start(src,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv)); - if (unit_movepos(bl,x,y,0,0)) - { - clif_skill_damage(bl,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, -1, 6); - if( bl->type == BL_PC && pc_issit((TBL_PC*)bl)) - clif_sitting(bl); //Avoid sitting sync problem - clif_slide(bl,x,y) ; - sc_start(bl,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv)); - } - } - } - break; - - case OB_AKAITSUKI: - case OB_OBOROGENSOU: - if( sd && ( (skill_id == OB_OBOROGENSOU && bl->type == BL_MOB) // This skill does not work on monsters. - || is_boss(bl) ) ){ // Does not work on Boss monsters. - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - case KO_IZAYOI: - case OB_ZANGETSU: - case KG_KYOMU: - case KG_KAGEMUSYA: - clif_skill_nodamage(src,bl,skill_id,skill_lv, - sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv))); - clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - break; - - case KG_KAGEHUMI: - if( flag&1 ){ - if(tsc && ( tsc->option&(OPTION_CLOAK|OPTION_HIDE) || - tsc->data[SC_CAMOUFLAGE] || tsc->data[SC__SHADOWFORM] || - tsc->data[SC_MARIONETTE] || tsc->data[SC_HARMONIZE])){ - sc_start(src, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - 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); - status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); - status_change_end(bl, SC_MARIONETTE, INVALID_TIMER); - status_change_end(bl, SC_HARMONIZE, INVALID_TIMER); - } - if( skill_area_temp[2] == 1 ){ - clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - sc_start(src, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - } - }else{ - skill_area_temp[2] = 0; - map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_nodamage_id); - } - break; - - case MH_SILENT_BREEZE: { - struct status_change *ssc = status_get_sc(src); - struct block_list *m_bl = battle_get_master(src); - const enum sc_type scs[] = { - SC_MANDRAGORA, SC_HARMONIZE, SC_DEEPSLEEP, SC_VOICEOFSIREN, SC_SLEEP, SC_CONFUSION, SC_HALLUCINATION - }; - int heal; - if(tsc){ - for (i = 0; i < ARRAYLENGTH(scs); i++) { - if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER); - } - if (!tsc->data[SC_SILENCE]) //put inavoidable silence on target - status_change_start(bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8); - } - heal = status_get_matk_min(src)*4; - status_heal(bl, heal, 0, 7); - - //now inflict silence on everyone - if(ssc && !ssc->data[SC_SILENCE]) //put inavoidable silence on homun - status_change_start(src, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8); - if(m_bl){ - struct status_change *msc = status_get_sc(m_bl); - if(msc && !msc->data[SC_SILENCE]) //put inavoidable silence on master - status_change_start(m_bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8); - } - if (hd) - skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv)); - } - break; - case MH_OVERED_BOOST: - if (hd){ - struct block_list *s_bl = battle_get_master(src); - if(hd->homunculus.hunger>50) //reduce hunger - hd->homunculus.hunger = hd->homunculus.hunger/2; - else - hd->homunculus.hunger = min(1,hd->homunculus.hunger); - if(s_bl && s_bl->type==BL_PC){ - status_set_sp(s_bl,status_get_max_sp(s_bl)/2,0); //master drain 50% sp - clif_send_homdata(((TBL_PC *)s_bl), SP_HUNGRY, hd->homunculus.hunger); //refresh hunger info - sc_start(s_bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); //gene bonus - } - sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv)); - } - break; - case MH_GRANITIC_ARMOR: - case MH_PYROCLASTIC: { - struct block_list *s_bl = battle_get_master(src); - if(s_bl) sc_start2(s_bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv)); //start on master - sc_start2(bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv)); - if (hd) skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv)); - } - break; - - case MH_LIGHT_OF_REGENE: - if(hd){ - hd->homunculus.intimacy = 251; //change to neutral (can't be cast if < 750) - if(sd) clif_send_homdata(sd, SP_INTIMATE, hd->homunculus.intimacy); //refresh intimacy info - } - //don't break need to start status and start block timer - case MH_STYLE_CHANGE: - case MH_MAGMA_FLOW: - case MH_PAIN_KILLER: - sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); - if (hd) - skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv)); - break; - case MH_SUMMON_LEGION: - { - int summons[5] = {1004, 1303, 1303, 1994, 1994}; - int qty[5] = {3 , 3 , 4 , 4 , 5}; - struct mob_data *md; - int i; - - for(i=0; i<qty[skill_lv - 1]; i++){ //easy way - md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), summons[skill_lv - 1], "", SZ_SMALL, AI_ATTACK); - if (md) { - md->master_id = src->id; - if (md->deletetimer != INVALID_TIMER) - delete_timer(md->deletetimer, mob_timer_delete); - md->deletetimer = add_timer(gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0); - mob_spawn(md); //Now it is ready for spawning. - sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_ASSIST, 0, 60000); - } - } - if (hd) - skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv)); - } - break; - default: - ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skill_id); - clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - map_freeblock_unlock(); - return 1; - } - - if(skill_id != SR_CURSEDCIRCLE){ - struct status_change *sc = status_get_sc(src); - if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] )//Should only remove after the skill had been casted. - status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER); - } - - if (dstmd) { //Mob skill event for no damage skills (damage ones are handled in battle_calc_damage) [Skotlex] - mob_log_damage(dstmd, src, 0); //Log interaction (counts as 'attacker' for the exp bonus) - mobskill_event(dstmd, src, tick, MSC_SKILLUSED|(skill_id<<16)); - } - - if( sd && !(flag&1) ) - {// ensure that the skill last-cast tick is recorded - sd->canskill_tick = gettick(); - - if( sd->state.arrow_atk ) - {// consume arrow on last invocation to this skill. - battle_consume_ammo(sd, skill_id, skill_lv); - } - skill_onskillusage(sd, bl, skill_id, tick); - // perform skill requirement consumption - skill_consume_requirement(sd,skill_id,skill_lv,2); - } - - map_freeblock_unlock(); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data) -{ - struct block_list *target, *src; - struct map_session_data *sd; - struct mob_data *md; - struct unit_data *ud; - struct status_change *sc = NULL; - int inf,inf2,flag = 0; - - src = map_id2bl(id); - if( src == NULL ) - { - ShowDebug("skill_castend_id: src == NULL (tid=%d, id=%d)\n", tid, id); - return 0;// not found - } - - ud = unit_bl2ud(src); - if( ud == NULL ) - { - ShowDebug("skill_castend_id: ud == NULL (tid=%d, id=%d)\n", tid, id); - return 0;// ??? - } - - sd = BL_CAST(BL_PC, src); - md = BL_CAST(BL_MOB, src); - - if( src->prev == NULL ) { - ud->skilltimer = INVALID_TIMER; - return 0; - } - - if(ud->skill_id != SA_CASTCANCEL && ud->skill_id != SO_SPELLFIST) {// otherwise handled in unit_skillcastcancel() - if( ud->skilltimer != tid ) { - ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid); - ud->skilltimer = INVALID_TIMER; - return 0; - } - - if( sd && ud->skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK) ) - {// restore original walk speed - ud->skilltimer = INVALID_TIMER; - status_calc_bl(&sd->bl, SCB_SPEED); - } - - ud->skilltimer = INVALID_TIMER; - } - - if (ud->skilltarget == id) - target = src; - else - target = map_id2bl(ud->skilltarget); - - // Use a do so that you can break out of it when the skill fails. - do { - if(!target || target->prev==NULL) break; - - if(src->m != target->m || status_isdead(src)) break; - - switch (ud->skill_id) { - //These should become skill_castend_pos - case WE_CALLPARTNER: - if(sd) clif_callpartner(sd); - case WE_CALLPARENT: - case WE_CALLBABY: - case AM_RESURRECTHOMUN: - case PF_SPIDERWEB: - //Find a random spot to place the skill. [Skotlex] - inf2 = skill_get_splash(ud->skill_id, ud->skill_lv); - ud->skillx = target->x + inf2; - ud->skilly = target->y + inf2; - if (inf2 && !map_random_dir(target, &ud->skillx, &ud->skilly)) { - ud->skillx = target->x; - ud->skilly = target->y; - } - ud->skilltimer=tid; - return skill_castend_pos(tid,tick,id,data); - case GN_WALLOFTHORN: - ud->skillx = target->x; - ud->skilly = target->y; - ud->skilltimer = tid; - return skill_castend_pos(tid,tick,id,data); - } - - if(ud->skill_id == RG_BACKSTAP) { - uint8 dir = map_calc_dir(src,target->x,target->y),t_dir = unit_getdir(target); - if(check_distance_bl(src, target, 0) || map_check_dir(dir,t_dir)) { - break; - } - } - - if( ud->skill_id == PR_TURNUNDEAD ) - { - struct status_data *tstatus = status_get_status_data(target); - if( !battle_check_undead(tstatus->race, tstatus->def_ele) ) - break; - } - - if( ud->skill_id == RA_WUGSTRIKE ){ - if( !path_search(NULL,src->m,src->x,src->y,target->x,target->y,1,CELL_CHKNOREACH)) - break; - } - - if( ud->skill_id == PR_LEXDIVINA || ud->skill_id == MER_LEXDIVINA ) - { - sc = status_get_sc(target); - if( battle_check_target(src,target, BCT_ENEMY) <= 0 && (!sc || !sc->data[SC_SILENCE]) ) - { //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex] - clif_skill_nodamage (src, target, ud->skill_id, ud->skill_lv, 0); - break; - } - } - else - { // Check target validity. - inf = skill_get_inf(ud->skill_id); - inf2 = skill_get_inf2(ud->skill_id); - - if(inf&INF_ATTACK_SKILL || - (inf&INF_SELF_SKILL && inf2&INF2_NO_TARGET_SELF) //Combo skills - ) // Casted through combo. - inf = BCT_ENEMY; //Offensive skill. - else if(inf2&INF2_NO_ENEMY) - inf = BCT_NOENEMY; - else - inf = 0; - - if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && src != target) - { - inf |= - (inf2&INF2_PARTY_ONLY?BCT_PARTY:0)| - (inf2&INF2_GUILD_ONLY?BCT_GUILD:0); - //Remove neutral targets (but allow enemy if skill is designed to be so) - inf &= ~BCT_NEUTRAL; - } - - if( ud->skill_id >= SL_SKE && ud->skill_id <= SL_SKA && target->type == BL_MOB ) - { - if( ((TBL_MOB*)target)->class_ == MOBID_EMPERIUM ) - break; - } - else if (inf && battle_check_target(src, target, inf) <= 0){ - if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - - if(inf&BCT_ENEMY && (sc = status_get_sc(target)) && - sc->data[SC_FOGWALL] && - rnd() % 100 < 75) { //Fogwall makes all offensive-type targetted skills fail at 75% - if (sd) clif_skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0); - break; - } - } - - //Avoid doing double checks for instant-cast skills. - if (tid != INVALID_TIMER && !status_check_skilluse(src, target, ud->skill_id, 1)) - break; - - if(md) { - md->last_thinktime=tick +MIN_MOBTHINKTIME; - if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0) - clif_emotion(src, md->db->skill[md->skill_idx].emotion); - } - - if(src != target && battle_config.skill_add_range && - !check_distance_bl(src, target, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range)) - { - if (sd) { - clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); - if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex] - skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3); - } - break; - } - - if( sd ) - { - if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) ) - break; - else - skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1); - } -#ifdef OFFICIAL_WALKPATH - if( !path_search_long(NULL, src->m, src->x, src->y, target->x, target->y, CELL_CHKWALL) ) - break; -#endif - if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) ) - break; - - if (ud->state.running && ud->skill_id == TK_JUMPKICK) - { - ud->state.running = 0; - status_change_end(src, SC_RUN, INVALID_TIMER); - flag = 1; - } - - if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH) - unit_stop_walking(src,1); - - if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) ) - ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish] - if (sd) { //Cooldown application - int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv); - for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses. - if (sd->skillcooldown[i].id == ud->skill_id){ - cooldown += sd->skillcooldown[i].val; - break; - } - } - if(cooldown) - skill_blockpc_start(sd, ud->skill_id, cooldown); - } - if( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); - if( sd ) - { - switch( ud->skill_id ) - { - case GS_DESPERADO: - sd->canequip_tick = tick + skill_get_time(ud->skill_id, ud->skill_lv); - break; - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - if( (sc = status_get_sc(src)) && sc->data[SC_STRIPSHIELD] ) - { - const struct TimerData *timer = get_timer(sc->data[SC_STRIPSHIELD]->timer); - if( timer && timer->func == status_change_timer && DIFF_TICK(timer->tick,gettick()+skill_get_time(ud->skill_id, ud->skill_lv)) > 0 ) - break; - } - sc_start2(src, SC_STRIPSHIELD, 100, 0, 1, skill_get_time(ud->skill_id, ud->skill_lv)); - break; - } - } - if (skill_get_state(ud->skill_id) != ST_MOVE_ENABLE) - unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1); - - if(battle_config.skill_log && battle_config.skill_log&src->type) - ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n", - src->type, src->id, ud->skill_id, ud->skill_lv, target->id); - - map_freeblock_lock(); - - // SC_MAGICPOWER needs to switch states before any damage is actually dealt - skill_toggle_magicpower(src, ud->skill_id); - if( ud->skill_id != RA_CAMOUFLAGE ) // only normal attack and auto cast skills benefit from its bonuses - status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER); - - if (skill_get_casttype(ud->skill_id) == CAST_NODAMAGE) - skill_castend_nodamage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag); - else - skill_castend_damage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag); - - sc = status_get_sc(src); - if(sc && sc->count) { - if(sc->data[SC_SPIRIT] && - sc->data[SC_SPIRIT]->val2 == SL_WIZARD && - sc->data[SC_SPIRIT]->val3 == ud->skill_id && - ud->skill_id != WZ_WATERBALL) - sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check. - - if( sc->data[SC_DANCING] && skill_get_inf2(ud->skill_id)&INF2_SONG_DANCE && sd ) - skill_blockpc_start(sd,BD_ADAPTATION,3000); - } - - if( sd && ud->skill_id != SA_ABRACADABRA && ud->skill_id != WM_RANDOMIZESPELL ) // they just set the data so leave it as it is.[Inkfish] - sd->skillitem = sd->skillitemlv = 0; - - if (ud->skilltimer == INVALID_TIMER) { - if(md) md->skill_idx = -1; - else ud->skill_id = 0; //mobs can't clear this one as it is used for skill condition 'afterskill' - ud->skill_lv = ud->skilltarget = 0; - } - map_freeblock_unlock(); - return 1; - } while(0); - - //Skill failed. - if (ud->skill_id == MO_EXTREMITYFIST && sd && !(sc && sc->data[SC_FOGWALL])) - { //When Asura fails... (except when it fails from Fog of Wall) - //Consume SP/spheres - skill_consume_requirement(sd,ud->skill_id, ud->skill_lv,1); - status_set_sp(src, 0, 0); - sc = &sd->sc; - if (sc->count) - { //End states - status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER); - status_change_end(src, SC_BLADESTOP, INVALID_TIMER); -#ifdef RENEWAL - sc_start(src, SC_EXTREMITYFIST2, 100, ud->skill_lv, skill_get_time(ud->skill_id, ud->skill_lv)); -#endif - } - if (target && target->m == src->m) - { //Move character to target anyway. - if (unit_movepos(src, src->x+3, src->y+3, 1, 1)) - { //Display movement + animation. - clif_slide(src,src->x,src->y); - clif_skill_damage(src,target,tick,sd->battle_status.amotion,0,0,1,ud->skill_id, ud->skill_lv, 5); - } - clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); - } - } - - ud->skill_id = ud->skill_lv = ud->skilltarget = 0; - if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) ) - ud->canact_tick = tick; - //You can't place a skill failed packet here because it would be - //sent in ALL cases, even cases where skill_check_condition fails - //which would lead to double 'skill failed' messages u.u [Skotlex] - if(sd) - sd->skillitem = sd->skillitemlv = 0; - else if(md) - md->skill_idx = -1; - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data) -{ - struct block_list* src = map_id2bl(id); - int maxcount; - struct map_session_data *sd; - struct unit_data *ud = unit_bl2ud(src); - struct mob_data *md; - - nullpo_ret(ud); - - sd = BL_CAST(BL_PC , src); - md = BL_CAST(BL_MOB, src); - - if( src->prev == NULL ) { - ud->skilltimer = INVALID_TIMER; - return 0; - } - - if( ud->skilltimer != tid ) - { - ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", ud->skilltimer, tid); - ud->skilltimer = INVALID_TIMER; - return 0; - } - - if( sd && ud->skilltimer != INVALID_TIMER && ( pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK ) ) - {// restore original walk speed - ud->skilltimer = INVALID_TIMER; - status_calc_bl(&sd->bl, SCB_SPEED); - } - ud->skilltimer = INVALID_TIMER; - - do { - if( status_isdead(src) ) - break; - - if( !(src->type&battle_config.skill_reiteration) && - skill_get_unit_flag(ud->skill_id)&UF_NOREITERATION && - skill_check_unit_range(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv) - ) - { - if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - if( src->type&battle_config.skill_nofootset && - skill_get_unit_flag(ud->skill_id)&UF_NOFOOTSET && - skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv) - ) - { - if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - if( src->type&battle_config.land_skill_limit && - (maxcount = skill_get_maxcount(ud->skill_id, ud->skill_lv)) > 0 - ) { - int i; - for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i] && maxcount;i++) { - if(ud->skillunit[i]->skill_id == ud->skill_id) - maxcount--; - } - if( maxcount == 0 ) - { - if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - } - - if(tid != INVALID_TIMER) - { //Avoid double checks on instant cast skills. [Skotlex] - if (!status_check_skilluse(src, NULL, ud->skill_id, 1)) - break; - if(battle_config.skill_add_range && - !check_distance_blxy(src, ud->skillx, ud->skilly, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range)) { - if (sd && battle_config.skill_out_range_consume) //Consume items anyway. - skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3); - break; - } - } - - if( sd ) - { - if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) ) - break; - else - skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1); - } - - if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) ) - break; - - if(md) { - md->last_thinktime=tick +MIN_MOBTHINKTIME; - if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0) - clif_emotion(src, md->db->skill[md->skill_idx].emotion); - } - - if(battle_config.skill_log && battle_config.skill_log&src->type) - ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n", - src->type, src->id, ud->skill_id, ud->skill_lv, ud->skillx, ud->skilly); - - if (ud->walktimer != INVALID_TIMER) - unit_stop_walking(src,1); - - if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) ) - ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); - if (sd) { //Cooldown application - int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv); - for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses. - if (sd->skillcooldown[i].id == ud->skill_id){ - cooldown += sd->skillcooldown[i].val; - break; - } - } - if(cooldown) - skill_blockpc_start(sd, ud->skill_id, cooldown); - } - if( battle_config.display_status_timers && sd ) - clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); -// if( sd ) -// { -// switch( ud->skill_id ) -// { -// case ????: -// sd->canequip_tick = tick + ????; -// break; -// } -// } - unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1); - status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER);// only normal attack and auto cast skills benefit from its bonuses - map_freeblock_lock(); - skill_castend_pos2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv,tick,0); - - if( sd && sd->skillitem != AL_WARP ) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish] - sd->skillitem = sd->skillitemlv = 0; - - if (ud->skilltimer == INVALID_TIMER) { - if (md) md->skill_idx = -1; - else ud->skill_id = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill' - ud->skill_lv = ud->skillx = ud->skilly = 0; - } - - map_freeblock_unlock(); - return 1; - } while(0); - - if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) ) - ud->canact_tick = tick; - ud->skill_id = ud->skill_lv = 0; - if(sd) - sd->skillitem = sd->skillitemlv = 0; - else if(md) - md->skill_idx = -1; - return 0; - -} - -/*========================================== - * - *------------------------------------------*/ -int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag) -{ - struct map_session_data* sd; - struct status_change* sc; - struct status_change_entry *sce; - struct skill_unit_group* sg; - enum sc_type type; - int i; - - //if(skill_lv <= 0) return 0; - if(skill_id > 0 && !skill_lv) return 0; // celest - - nullpo_ret(src); - - if(status_isdead(src)) - return 0; - - sd = BL_CAST(BL_PC, src); - - sc = status_get_sc(src); - type = status_skill2sc(skill_id); - sce = (sc && type != -1)?sc->data[type]:NULL; - - switch (skill_id) { //Skill effect. - case WZ_METEOR: - case MO_BODYRELOCATION: - case CR_CULTIVATION: - case HW_GANBANTEIN: - case LG_EARTHDRIVE: - break; //Effect is displayed on respective switch case. - default: - if(skill_get_inf(skill_id)&INF_SELF_SKILL) - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - else - clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick); - } - - // SC_MAGICPOWER needs to switch states before any damage is actually dealt - skill_toggle_magicpower(src, skill_id); - - switch(skill_id) - { - case PR_BENEDICTIO: - skill_area_temp[1] = src->id; - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea(skill_area_sub, - src->m, x-i, y-i, x+i, y+i, BL_PC, - src, skill_id, skill_lv, tick, flag|BCT_ALL|1, - skill_castend_nodamage_id); - map_foreachinarea(skill_area_sub, - src->m, x-i, y-i, x+i, y+i, BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - break; - - case BS_HAMMERFALL: - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea (skill_area_sub, - src->m, x-i, y-i, x+i, y+i, BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|2, - skill_castend_nodamage_id); - break; - - case HT_DETECTING: - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea( status_change_timer_sub, - src->m, x-i, y-i, x+i,y+i,BL_CHAR, - src,NULL,SC_SIGHT,tick); - if(battle_config.traps_setting&1) - map_foreachinarea( skill_reveal_trap, - src->m, x-i, y-i, x+i,y+i,BL_SKILL); - break; - - case SR_RIDEINLIGHTNING: - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea(skill_area_sub, src->m, x-i, y-i, x+i, y+i, BL_CHAR, - src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id); - break; - - case SA_VOLCANO: - case SA_DELUGE: - case SA_VIOLENTGALE: - { //Does not consumes if the skill is already active. [Skotlex] - struct skill_unit_group *sg; - if ((sg= skill_locate_element_field(src)) != NULL && ( sg->skill_id == SA_VOLCANO || sg->skill_id == SA_DELUGE || sg->skill_id == SA_VIOLENTGALE )) - { - if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0) - { - skill_unitsetting(src,skill_id,skill_lv,x,y,0); - return 0; // not to consume items - } - else - sg->limit = 0; //Disable it. - } - skill_unitsetting(src,skill_id,skill_lv,x,y,0); - break; - } - case MG_SAFETYWALL: - case MG_FIREWALL: - case MG_THUNDERSTORM: - - case AL_PNEUMA: - case WZ_ICEWALL: - case WZ_FIREPILLAR: - case WZ_QUAGMIRE: - case WZ_VERMILION: - case WZ_STORMGUST: - case WZ_HEAVENDRIVE: - case PR_SANCTUARY: - case PR_MAGNUS: - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - case HT_SKIDTRAP: - case MA_SKIDTRAP: - case HT_LANDMINE: - case MA_LANDMINE: - case HT_ANKLESNARE: - case HT_SHOCKWAVE: - case HT_SANDMAN: - case MA_SANDMAN: - case HT_FLASHER: - case HT_FREEZINGTRAP: - case MA_FREEZINGTRAP: - case HT_BLASTMINE: - case HT_CLAYMORETRAP: - case AS_VENOMDUST: - case AM_DEMONSTRATION: - case PF_FOGWALL: - case PF_SPIDERWEB: - case HT_TALKIEBOX: - case WE_CALLPARTNER: - case WE_CALLPARENT: - case WE_CALLBABY: - case AC_SHOWER: //Ground-placed skill implementation. - case MA_SHOWER: - case SA_LANDPROTECTOR: - case BD_LULLABY: - case BD_RICHMANKIM: - case BD_ETERNALCHAOS: - case BD_DRUMBATTLEFIELD: - case BD_RINGNIBELUNGEN: - case BD_ROKISWEIL: - case BD_INTOABYSS: - case BD_SIEGFRIED: - case BA_DISSONANCE: - case BA_POEMBRAGI: - case BA_WHISTLE: - case BA_ASSASSINCROSS: - case BA_APPLEIDUN: - case DC_UGLYDANCE: - case DC_HUMMING: - case DC_DONTFORGETME: - case DC_FORTUNEKISS: - case DC_SERVICEFORYOU: - case CG_MOONLIT: - case GS_DESPERADO: - case NJ_KAENSIN: - case NJ_BAKUENRYU: - case NJ_SUITON: - case NJ_HYOUSYOURAKU: - case NJ_RAIGEKISAI: - case NJ_KAMAITACHI: -#ifdef RENEWAL - case NJ_HUUMA: -#endif - case NPC_EVILLAND: - case RA_ELECTRICSHOCKER: - case RA_CLUSTERBOMB: - case RA_MAGENTATRAP: - case RA_COBALTTRAP: - case RA_MAIZETRAP: - case RA_VERDURETRAP: - case RA_FIRINGTRAP: - case RA_ICEBOUNDTRAP: - case SC_MANHOLE: - case SC_DIMENSIONDOOR: - case SC_CHAOSPANIC: - case SC_MAELSTROM: - case WM_REVERBERATION: - case WM_SEVERE_RAINSTORM: - case WM_POEMOFNETHERWORLD: - case SO_PSYCHIC_WAVE: - case SO_VACUUM_EXTREME: - case GN_WALLOFTHORN: - case GN_THORNS_TRAP: - case GN_DEMONIC_FIRE: - case GN_HELLS_PLANT: - case SO_EARTHGRAVE: - case SO_DIAMONDDUST: - case SO_FIRE_INSIGNIA: - case SO_WATER_INSIGNIA: - case SO_WIND_INSIGNIA: - case SO_EARTH_INSIGNIA: - case KO_HUUMARANKA: - case KO_MUCHANAGE: - case KO_BAKURETSU: - case KO_ZENKAI: - case MH_LAVA_SLIDE: - case MH_VOLCANIC_ASH: - case MH_POISON_MIST: - case MH_STEINWAND: - case MH_XENO_SLASHER: - 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,skill_id,skill_lv,x,y,0); - break; - case RG_GRAFFITI: /* Graffiti [Valaris] */ - skill_clear_unitgroup(src); - skill_unitsetting(src,skill_id,skill_lv,x,y,0); - flag|=1; - break; - case HP_BASILICA: - if( sc->data[SC_BASILICA] ) - status_change_end(src, SC_BASILICA, INVALID_TIMER); // Cancel Basilica - else - { // Create Basilica. Start SC on caster. Unit timer start SC on others. - skill_clear_unitgroup(src); - if( skill_unitsetting(src,skill_id,skill_lv,x,y,0) ) - sc_start4(src,type,100,skill_lv,0,0,src->id,skill_get_time(skill_id,skill_lv)); - flag|=1; - } - break; - case CG_HERMODE: - skill_clear_unitgroup(src); - if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0))) - sc_start4(src,SC_DANCING,100, - skill_id,0,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv)); - flag|=1; - break; - case RG_CLEANER: // [Valaris] - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL); - break; - - case SO_WARMER: - flag|= 8; - case SO_CLOUD_KILL: - skill_unitsetting(src,skill_id,skill_lv,x,y,0); - break; - - case WZ_METEOR: { - int area = skill_get_splash(skill_id, skill_lv); - short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0; - - for( i = 0; i < 2 + (skill_lv>>1); i++ ) { - // Creates a random Cell in the Splash Area - tmpx = x - area + rnd()%(area * 2 + 1); - tmpy = y - area + rnd()%(area * 2 + 1); - - if( i == 0 && path_search_long(NULL, src->m, src->x, src->y, tmpx, tmpy, CELL_CHKWALL) ) - clif_skill_poseffect(src,skill_id,skill_lv,tmpx,tmpy,tick); - - if( i > 0 ) - skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,(x1<<16)|y1,0); - - x1 = tmpx; - y1 = tmpy; - } - - skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,-1,0); - } - break; - - case AL_WARP: - if(sd) - { - clif_skill_warppoint(sd, skill_id, skill_lv, sd->status.save_point.map, - (skill_lv >= 2) ? sd->status.memo_point[0].map : 0, - (skill_lv >= 3) ? sd->status.memo_point[1].map : 0, - (skill_lv >= 4) ? sd->status.memo_point[2].map : 0 - ); - } - return 0; // not to consume item. - - case MO_BODYRELOCATION: - if (unit_movepos(src, x, y, 1, 1)) { -#if PACKETVER >= 20111005 - clif_snap(src, src->x, src->y); -#else - clif_skill_poseffect(src,skill_id,skill_lv,src->x,src->y,tick); -#endif - if (sd) - skill_blockpc_start (sd, MO_EXTREMITYFIST, 2000); - } - break; - case NJ_SHADOWJUMP: - if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground ) { //You don't move on GVG grounds. - unit_movepos(src, x, y, 1, 0); - clif_slide(src,x,y); - } - status_change_end(src, SC_HIDING, INVALID_TIMER); - break; - case AM_SPHEREMINE: - case AM_CANNIBALIZE: - { - int summons[5] = { 1589, 1579, 1575, 1555, 1590 }; - //int summons[5] = { 1020, 1068, 1118, 1500, 1368 }; - int class_ = skill_id==AM_SPHEREMINE?1142:summons[skill_lv-1]; - struct mob_data *md; - - // Correct info, don't change any of this! [celest] - md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE); - if (md) { - md->master_id = src->id; - md->special_state.ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA; - if( md->deletetimer != INVALID_TIMER ) - delete_timer(md->deletetimer, mob_timer_delete); - md->deletetimer = add_timer (gettick() + skill_get_time(skill_id,skill_lv), mob_timer_delete, md->bl.id, 0); - mob_spawn (md); //Now it is ready for spawning. - } - } - break; - - // Slim Pitcher [Celest] - case CR_SLIMPITCHER: - if (sd) { - int i = skill_lv%11 - 1; - int j = pc_search_inventory(sd,skill_db[skill_id].itemid[i]); - if( j < 0 || skill_db[skill_id].itemid[i] <= 0 || sd->inventory_data[j] == NULL || sd->status.inventory[j].amount < skill_db[skill_id].amount[i] ) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 1; - } - potion_flag = 1; - potion_hp = 0; - potion_sp = 0; - run_script(sd->inventory_data[j]->script,0,sd->bl.id,0); - potion_flag = 0; - //Apply skill bonuses - i = pc_checkskill(sd,CR_SLIMPITCHER)*10 - + pc_checkskill(sd,AM_POTIONPITCHER)*10 - + pc_checkskill(sd,AM_LEARNINGPOTION)*5 - + pc_skillheal_bonus(sd, skill_id); - - potion_hp = potion_hp * (100+i)/100; - potion_sp = potion_sp * (100+i)/100; - - if(potion_hp > 0 || potion_sp > 0) { - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea(skill_area_sub, - src->m,x-i,y-i,x+i,y+i,BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1, - skill_castend_nodamage_id); - } - } else { - int i = skill_lv%11 - 1; - struct item_data *item; - i = skill_db[skill_id].itemid[i]; - item = itemdb_search(i); - potion_flag = 1; - potion_hp = 0; - potion_sp = 0; - run_script(item->script,0,src->id,0); - potion_flag = 0; - i = skill_get_max(CR_SLIMPITCHER)*10; - - potion_hp = potion_hp * (100+i)/100; - potion_sp = potion_sp * (100+i)/100; - - if(potion_hp > 0 || potion_sp > 0) { - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea(skill_area_sub, - src->m,x-i,y-i,x+i,y+i,BL_CHAR, - src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1, - skill_castend_nodamage_id); - } - } - break; - - case HW_GANBANTEIN: - if (rnd()%100 < 80) { - int dummy = 1; - clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick); - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea(skill_cell_overlap, src->m, x-i, y-i, x+i, y+i, BL_SKILL, HW_GANBANTEIN, &dummy, src); - } else { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 1; - } - break; - - case HW_GRAVITATION: - if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0))) - sc_start4(src,type,100,skill_lv,0,BCT_SELF,sg->group_id,skill_get_time(skill_id,skill_lv)); - flag|=1; - break; - - // Plant Cultivation [Celest] - case CR_CULTIVATION: - if (sd) { - if( map_count_oncell(src->m,x,y,BL_CHAR) > 0 ) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 1; - } - clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick); - if (rnd()%100 < 50) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - } else { - TBL_MOB* md = mob_once_spawn_sub(src, src->m, x, y, "--ja--",(skill_lv < 2 ? 1084+rnd()%2 : 1078+rnd()%6),"", SZ_SMALL, AI_NONE); - int i; - if (!md) break; - if ((i = skill_get_time(skill_id, skill_lv)) > 0) - { - if( md->deletetimer != INVALID_TIMER ) - delete_timer(md->deletetimer, mob_timer_delete); - md->deletetimer = add_timer (tick + i, mob_timer_delete, md->bl.id, 0); - } - mob_spawn (md); - } - } - break; - - case SG_SUN_WARM: - case SG_MOON_WARM: - case SG_STAR_WARM: - skill_clear_unitgroup(src); - if ((sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0))) - sc_start4(src,type,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv)); - flag|=1; - break; - - case PA_GOSPEL: - if (sce && sce->val4 == BCT_SELF) - { - status_change_end(src, SC_GOSPEL, INVALID_TIMER); - return 0; - } - else - { - sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0); - if (!sg) break; - if (sce) - status_change_end(src, type, INVALID_TIMER); //Was under someone else's Gospel. [Skotlex] - sc_start4(src,type,100,skill_lv,0,sg->group_id,BCT_SELF,skill_get_time(skill_id,skill_lv)); - clif_skill_poseffect(src, skill_id, skill_lv, 0, 0, tick); // PA_GOSPEL music packet - } - break; - case NJ_TATAMIGAESHI: - if (skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)) - sc_start(src,type,100,skill_lv,skill_get_time2(skill_id,skill_lv)); - break; - - case AM_RESURRECTHOMUN: //[orn] - if (sd) - { - if (!merc_resurrect_homunculus(sd, 20*skill_lv, x, y)) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } - } - break; - - case RK_WINDCUTTER: - clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6); - case NC_COLDSLOWER: - case NC_ARMSCANNON: - case RK_DRAGONBREATH: - case WM_LULLABY_DEEPSLEEP: - i = skill_get_splash(skill_id,skill_lv); - map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src), - src,skill_id,skill_lv,tick,flag|(skill_id==WM_LULLABY_DEEPSLEEP?BCT_ALL: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,skill_id,USESKILL_FAIL_GC_POISONINGWEAPON,0); - return 0; - } - clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6); - skill_unitsetting(src, skill_id, skill_lv, x, y, flag); - //status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER); // 08/31/2011 - When using poison smoke, you no longer lose the poisoning weapon effect. - break; - /** - * Arch Bishop - **/ - case AB_EPICLESIS: - if( (sg = skill_unitsetting(src, skill_id, skill_lv, 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(skill_id,skill_lv); - map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - break; - - case WL_EARTHSTRAIN: - { - int i, wave = skill_lv + 4, dir = map_calc_dir(src,x,y); - int sx = x = src->x, sy = y = src->y; // Store first caster's location to avoid glitch on unit setting - - for( i = 1; i <= wave; i++ ) - { - switch( dir ){ - case 0: case 1: case 7: sy = y + i; break; - case 3: case 4: case 5: sy = y - i; break; - case 2: sx = x - i; break; - case 6: sx = x + i; break; - } - skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skill_id,skill_lv,dir,flag&2); - } - } - break; - /** - * Ranger - **/ - case RA_DETONATOR: - i = skill_get_splash(skill_id, skill_lv); - 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, skill_id, skill_lv, 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,skill_id,skill_lv,src->x,src->y,0)) != NULL ) { - sc_start2(src,skill_id == NC_NEUTRALBARRIER ? SC_NEUTRALBARRIER_MASTER : SC_STEALTHFIELD_MASTER,100,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv)); - if( sd ) pc_overheat(sd,1); - } - break; - - case NC_SILVERSNIPER: - { - int class_ = 2042; - struct mob_data *md; - - md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE); - if( md ) - { - md->master_id = src->id; - md->special_state.ai = AI_FLORA; - if( md->deletetimer != INVALID_TIMER ) - delete_timer(md->deletetimer, mob_timer_delete); - md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0); - mob_spawn( md ); - } - } - break; - - case NC_MAGICDECOY: - if( sd ) clif_magicdecoy_list(sd,skill_lv,x,y); - break; - - case SC_FEINTBOMB: - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - skill_unitsetting(src,skill_id,skill_lv,x,y,0); // Set bomb on current Position - if( skill_blown(src,src,6,unit_getdir(src),0) ) - skill_castend_nodamage_id(src,src,TF_HIDING,1,tick,0); - break; - - case LG_OVERBRAND: - { - int width;//according to data from irowiki it actually is a square - for( width = 0; width < 7; width++ ) - for( i = 0; i < 7; i++ ) - map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, LG_OVERBRAND_BRANDISH, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id); - for( width = 0; width < 7; width++ ) - for( i = 0; i < 7; i++ ) - map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id); - } - break; - - case LG_BANDING: - if( sc && sc->data[SC_BANDING] ) - status_change_end(src,SC_BANDING,INVALID_TIMER); - else if( (sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)) != NULL ) { - sc_start4(src,SC_BANDING,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv)); - if( sd ) pc_banding(sd,skill_lv); - } - clif_skill_nodamage(src,src,skill_id,skill_lv,1); - break; - - case LG_RAYOFGENESIS: - if( status_charge(src,status_get_max_hp(src)*3*skill_lv / 100,0) ) { - i = skill_get_splash(skill_id,skill_lv); - map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src), - src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id); - } else if( sd ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL,0); - break; - - case WM_DOMINION_IMPULSE: - i = skill_get_splash(skill_id, skill_lv); - map_foreachinarea( skill_ative_reverberation, - src->m, x-i, y-i, x+i,y+i,BL_SKILL); - break; - - case WM_GREAT_ECHO: - flag|=1; // Should counsume 1 item per skill usage. - map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_castend_damage_id); - break; - case GN_CRAZYWEED: { - int area = skill_get_splash(GN_CRAZYWEED_ATK, skill_lv); - short x1 = 0, y1 = 0; - - for( i = 0; i < 3 + (skill_lv/2); i++ ) { - x1 = x - area + rnd()%(area * 2 + 1); - y1 = y - area + rnd()%(area * 2 + 1); - skill_addtimerskill(src,tick+i*150,0,x1,y1,GN_CRAZYWEED_ATK,skill_lv,-1,0); - } - } - break; - case GN_FIRE_EXPANSION: { - int i; - struct unit_data *ud = unit_bl2ud(src); - - if( !ud ) break; - - for( i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i ++ ) { - if( ud->skillunit[i]->skill_id == GN_DEMONIC_FIRE && - distance_xy(x, y, ud->skillunit[i]->unit->bl.x, ud->skillunit[i]->unit->bl.y) < 4 ) { - switch( skill_lv ) { - case 3: - ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_SMOKE_POWDER; - clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_SMOKE_POWDER); - break; - case 4: - ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_TEAR_GAS; - clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_TEAR_GAS); - break; - case 5: - map_foreachinarea(skill_area_sub, src->m, - ud->skillunit[i]->unit->bl.x - 3, ud->skillunit[i]->unit->bl.y - 3, - ud->skillunit[i]->unit->bl.x + 3, ud->skillunit[i]->unit->bl.y + 3, BL_CHAR, - src, CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : skill_lv, tick, flag|BCT_ENEMY|1|SD_LEVEL, skill_castend_damage_id); - skill_delunit(ud->skillunit[i]->unit); - break; - default: - ud->skillunit[i]->unit->val2 = skill_lv; - ud->skillunit[i]->unit->group->val2 = skill_lv; - break; - } - } - } - } - break; - - case SO_FIREWALK: - case SO_ELECTRICWALK: - if( sc && sc->data[type] ) - status_change_end(src,type,INVALID_TIMER); - clif_skill_nodamage(src, src ,skill_id, skill_lv, - sc_start2(src, type, 100, skill_id, skill_lv, skill_get_time(skill_id, skill_lv))); - break; - - case SC_BLOODYLUST: //set in another group so instance will move if recasted - flag |= 33; - skill_unitsetting(src, skill_id, skill_lv, x, y, 0); - break; - - case KO_MAKIBISHI: - for( i = 0; i < (skill_lv+2); i++ ) { - x = src->x - 1 + rnd()%3; - y = src->y - 1 + rnd()%3; - skill_unitsetting(src,skill_id,skill_lv,x,y,0); - } - break; - - default: - ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skill_id); - return 1; - } - - if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted. - status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER); - - if( sd ) - {// ensure that the skill last-cast tick is recorded - sd->canskill_tick = gettick(); - - if( sd->state.arrow_atk && !(flag&1) ) - {// consume arrow if this is a ground skill - battle_consume_ammo(sd, skill_id, skill_lv); - } - - // perform skill requirement consumption - skill_consume_requirement(sd,skill_id,skill_lv,2); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char *map) -{ - nullpo_ret(sd); - -//Simplify skill_failed code. -#define skill_failed(sd) { sd->menuskill_id = sd->menuskill_val = 0; } - if(skill_id != sd->menuskill_id) - return 0; - - if( sd->bl.prev == NULL || pc_isdead(sd) ) { - skill_failed(sd); - return 0; - } - - if( ( sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING ) || sd->sc.option&OPTION_HIDE ) { - skill_failed(sd); - return 0; - } - if(sd->sc.count && ( - sd->sc.data[SC_SILENCE] || - sd->sc.data[SC_ROKISWEIL] || - sd->sc.data[SC_AUTOCOUNTER] || - sd->sc.data[SC_STEELBODY] || - (sd->sc.data[SC_DANCING] && skill_id < RK_ENCHANTBLADE && !pc_checkskill(sd, WM_LESSON)) || - sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || - sd->sc.data[SC_BASILICA] || - sd->sc.data[SC_MARIONETTE] || - sd->sc.data[SC_WHITEIMPRISON] || - (sd->sc.data[SC_STASIS] && skill_block_check(&sd->bl, SC_STASIS, skill_id)) || - (sd->sc.data[SC_KAGEHUMI] && skill_block_check(&sd->bl, SC_KAGEHUMI, skill_id)) || - sd->sc.data[SC_OBLIVIONCURSE] || - sd->sc.data[SC__MANHOLE] || - (sd->sc.data[SC_ASH] && rnd()%2) //50% fail chance under ASH - )) { - skill_failed(sd); - return 0; - } - - pc_stop_attack(sd); - pc_stop_walking(sd,0); - - if(battle_config.skill_log && battle_config.skill_log&BL_PC) - ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_id,map); - - if(strcmp(map,"cancel")==0) { - skill_failed(sd); - return 0; - } - - switch(skill_id) - { - case AL_TELEPORT: - if(strcmp(map,"Random")==0) - pc_randomwarp(sd,CLR_TELEPORT); - else if (sd->menuskill_val > 1) //Need lv2 to be able to warp here. - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - break; - - case AL_WARP: - { - const struct point *p[4]; - struct skill_unit_group *group; - int i, lv, wx, wy; - int maxcount=0; - int x,y; - unsigned short mapindex; - - mapindex = mapindex_name2id((char*)map); - if(!mapindex) { //Given map not found? - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - skill_failed(sd); - return 0; - } - p[0] = &sd->status.save_point; - p[1] = &sd->status.memo_point[0]; - p[2] = &sd->status.memo_point[1]; - p[3] = &sd->status.memo_point[2]; - - if((maxcount = skill_get_maxcount(skill_id, sd->menuskill_val)) > 0) { - for(i=0;i<MAX_SKILLUNITGROUP && sd->ud.skillunit[i] && maxcount;i++) { - if(sd->ud.skillunit[i]->skill_id == skill_id) - maxcount--; - } - if(!maxcount) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - skill_failed(sd); - return 0; - } - } - - lv = sd->skillitem==skill_id?sd->skillitemlv:pc_checkskill(sd,skill_id); - wx = sd->menuskill_val>>16; - wy = sd->menuskill_val&0xffff; - - if( lv <= 0 ) return 0; - if( lv > 4 ) lv = 4; // crash prevention - - // check if the chosen map exists in the memo list - ARR_FIND( 0, lv, i, mapindex == p[i]->map ); - if( i < lv ) { - x=p[i]->x; - y=p[i]->y; - } else { - skill_failed(sd); - return 0; - } - - if(!skill_check_condition_castend(sd, sd->menuskill_id, lv)) - { // This checks versus skill_id/skill_lv... - skill_failed(sd); - return 0; - } - - skill_consume_requirement(sd,sd->menuskill_id,lv,2); - sd->skillitem = sd->skillitemlv = 0; // Clear data that's skipped in 'skill_castend_pos' [Inkfish] - - if((group=skill_unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==NULL) { - skill_failed(sd); - return 0; - } - - group->val1 = (group->val1<<16)|(short)0; - // record the destination coordinates - group->val2 = (x<<16)|y; - group->val3 = mapindex; - } - break; - } - - sd->menuskill_id = sd->menuskill_val = 0; - return 0; -#undef skill_failed -} - -/// transforms 'target' skill unit into dissonance (if conditions are met) -static int skill_dance_overlap_sub(struct block_list* bl, va_list ap) -{ - struct skill_unit* target = (struct skill_unit*)bl; - struct skill_unit* src = va_arg(ap, struct skill_unit*); - int flag = va_arg(ap, int); - - if (src == target) - return 0; - if (!target->group || !(target->group->state.song_dance&0x1)) - return 0; - if (!(target->val2 & src->val2 & ~UF_ENSEMBLE)) //They don't match (song + dance) is valid. - return 0; - - if (flag) //Set dissonance - target->val2 |= UF_ENSEMBLE; //Add ensemble to signal this unit is overlapping. - else //Remove dissonance - target->val2 &= ~UF_ENSEMBLE; - - clif_skill_setunit(target); //Update look of affected cell. - - return 1; -} - -//Does the song/dance overlapping -> dissonance check. [Skotlex] -//When flag is 0, this unit is about to be removed, cancel the dissonance effect -//When 1, this unit has been positioned, so start the cancel effect. -int skill_dance_overlap(struct skill_unit* unit, int flag) -{ - if (!unit || !unit->group || !(unit->group->state.song_dance&0x1)) - return 0; - if (!flag && !(unit->val2&UF_ENSEMBLE)) - return 0; //Nothing to remove, this unit is not overlapped. - - if (unit->val1 != unit->group->skill_id) - { //Reset state - unit->val1 = unit->group->skill_id; - unit->val2 &= ~UF_ENSEMBLE; - } - - return map_foreachincell(skill_dance_overlap_sub, unit->bl.m,unit->bl.x,unit->bl.y,BL_SKILL, unit,flag); -} - -/*========================================== - * Converts this group information so that it is handled as a Dissonance or Ugly Dance cell. - * Flag: 0 - Convert, 1 - Revert. - *------------------------------------------*/ -static bool skill_dance_switch(struct skill_unit* unit, int flag) -{ - static int prevflag = 1; // by default the backup is empty - static struct skill_unit_group backup; - struct skill_unit_group* group = unit->group; - - // val2&UF_ENSEMBLE is a hack to indicate dissonance - if ( !(group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE) ) - return false; - - if( flag == prevflag ) - {// protection against attempts to read an empty backup / write to a full backup - ShowError("skill_dance_switch: Attempted to %s (skill_id=%d, skill_lv=%d, src_id=%d).\n", - flag ? "read an empty backup" : "write to a full backup", - group->skill_id, group->skill_lv, group->src_id); - return false; - } - prevflag = flag; - - if( !flag ) - { //Transform - uint16 skill_id = unit->val2&UF_SONG ? BA_DISSONANCE : DC_UGLYDANCE; - - // backup - backup.skill_id = group->skill_id; - backup.skill_lv = group->skill_lv; - backup.unit_id = group->unit_id; - backup.target_flag = group->target_flag; - backup.bl_flag = group->bl_flag; - backup.interval = group->interval; - - // replace - group->skill_id = skill_id; - group->skill_lv = 1; - group->unit_id = skill_get_unit_id(skill_id,0); - group->target_flag = skill_get_unit_target(skill_id); - group->bl_flag = skill_get_unit_bl_target(skill_id); - group->interval = skill_get_unit_interval(skill_id); - } - else - { //Restore - group->skill_id = backup.skill_id; - group->skill_lv = backup.skill_lv; - group->unit_id = backup.unit_id; - group->target_flag = backup.target_flag; - group->bl_flag = backup.bl_flag; - group->interval = backup.interval; - } - - return true; -} -/** - * Upon Ice Wall cast it checks all nearby mobs to find any who may be blocked by the IW - **/ -static int skill_icewall_block(struct block_list *bl,va_list ap) { - struct block_list *target = NULL; - struct mob_data *md = ((TBL_MOB*)bl); - - nullpo_ret(bl); - nullpo_ret(md); - if( !md->target_id || ( target = map_id2bl(md->target_id) ) == NULL ) - return 0; - - if( path_search_long(NULL,bl->m,bl->x,bl->y,target->x,target->y,CELL_CHKICEWALL) ) - return 0; - - if( !check_distance_bl(bl, target, status_get_range(bl) ) ) { - mob_unlocktarget(md,gettick()); - mob_stop_walking(md,1); - } - - return 0; -} -/*========================================== - * Initializes and sets a ground skill. - * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active) - *------------------------------------------*/ -struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag) -{ - struct skill_unit_group *group; - int i,limit,val1=0,val2=0,val3=0; - int target,interval,range,unit_flag,req_item=0; - struct s_skill_unit_layout *layout; - struct map_session_data *sd; - struct status_data *status; - struct status_change *sc; - int active_flag=1; - int subunt=0; - - nullpo_retr(NULL, src); - - limit = skill_get_time(skill_id,skill_lv); - range = skill_get_unit_range(skill_id,skill_lv); - interval = skill_get_unit_interval(skill_id); - target = skill_get_unit_target(skill_id); - unit_flag = skill_get_unit_flag(skill_id); - layout = skill_get_unit_layout(skill_id,skill_lv,src,x,y); - - sd = BL_CAST(BL_PC, src); - status = status_get_status_data(src); - sc = status_get_sc(src); // for traps, firewall and fogwall - celest - - switch( skill_id ) { - case MH_STEINWAND: - val2 = 4 + skill_lv; //nb of attack blocked - break; - case MG_SAFETYWALL: - #ifdef RENEWAL - /** - * According to data provided in RE, SW life is equal to 3 times caster's health - **/ - val2 = status_get_max_hp(src) * 3; - #else - val2 = skill_lv+1; - #endif - break; - case MG_FIREWALL: - if(sc && sc->data[SC_VIOLENTGALE]) - limit = limit*3/2; - val2=4+skill_lv; - break; - - case AL_WARP: - val1=skill_lv+6; - if(!(flag&1)) - limit=2000; - else // previous implementation (not used anymore) - { //Warp Portal morphing to active mode, extract relevant data from src. [Skotlex] - if( src->type != BL_SKILL ) return NULL; - group = ((TBL_SKILL*)src)->group; - src = map_id2bl(group->src_id); - if( !src ) return NULL; - val2 = group->val2; //Copy the (x,y) position you warp to - val3 = group->val3; //as well as the mapindex to warp to. - } - break; - case HP_BASILICA: - val1 = src->id; // Store caster id. - break; - - case PR_SANCTUARY: - case NPC_EVILLAND: - val1=(skill_lv+3)*2; - break; - - case WZ_FIREPILLAR: - if((flag&1)!=0) - limit=1000; - val1=skill_lv+2; - break; - case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex] - case AM_DEMONSTRATION: - case GN_HELLS_PLANT: - if (map_flag_vs(src->m) && battle_config.vs_traps_bctall - && (src->type&battle_config.vs_traps_bctall)) - target = BCT_ALL; - break; - case HT_SHOCKWAVE: - val1=skill_lv*15+10; - case HT_SANDMAN: - case MA_SANDMAN: - case HT_CLAYMORETRAP: - case HT_SKIDTRAP: - case MA_SKIDTRAP: - case HT_LANDMINE: - case MA_LANDMINE: - case HT_ANKLESNARE: - case HT_FLASHER: - 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: - { - struct skill_condition req = skill_get_requirement(sd,skill_id,skill_lv); - ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, i, req.itemid[i] && (req.itemid[i] == ITEMID_TRAP || req.itemid[i] == ITEMID_TRAP_ALLOY)); - if( req.itemid[i] ) - req_item = req.itemid[i]; - 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) ) - target = BCT_ALL; - } - break; - - case SA_LANDPROTECTOR: - case SA_VOLCANO: - case SA_DELUGE: - case SA_VIOLENTGALE: - { - struct skill_unit_group *old_sg; - if ((old_sg = skill_locate_element_field(src)) != NULL) - { //HelloKitty confirmed that these are interchangeable, - //so you can change element and not consume gemstones. - if (( - old_sg->skill_id == SA_VOLCANO || - old_sg->skill_id == SA_DELUGE || - old_sg->skill_id == SA_VIOLENTGALE - ) && old_sg->limit > 0) - { //Use the previous limit (minus the elapsed time) [Skotlex] - limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick); - if (limit < 0) //This can happen... - limit = skill_get_time(skill_id,skill_lv); - } - skill_clear_group(src,1); - } - break; - } - - case BA_DISSONANCE: - case DC_UGLYDANCE: - val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex] - break; - case BA_WHISTLE: - val1 = skill_lv +status->agi/10; // Flee increase - val2 = ((skill_lv+1)/2)+status->luk/10; // Perfect dodge increase - if(sd){ - val1 += pc_checkskill(sd,BA_MUSICALLESSON); - val2 += pc_checkskill(sd,BA_MUSICALLESSON); - } - break; - case DC_HUMMING: - val1 = 2*skill_lv+status->dex/10; // Hit increase - #ifdef RENEWAL - val1 *= 2; - #endif - if(sd) - val1 += pc_checkskill(sd,DC_DANCINGLESSON); - break; - case BA_POEMBRAGI: - val1 = 3*skill_lv+status->dex/10; // Casting time reduction - //For some reason at level 10 the base delay reduction is 50%. - val2 = (skill_lv<10?3*skill_lv:50)+status->int_/5; // After-cast delay reduction - if(sd){ - val1 += 2*pc_checkskill(sd,BA_MUSICALLESSON); - val2 += 2*pc_checkskill(sd,BA_MUSICALLESSON); - } - break; - case DC_DONTFORGETME: - val1 = status->dex/10 + 3*skill_lv + 5; // ASPD decrease - val2 = status->agi/10 + 3*skill_lv + 5; // Movement speed adjustment. - if(sd){ - val1 += pc_checkskill(sd,DC_DANCINGLESSON); - val2 += pc_checkskill(sd,DC_DANCINGLESSON); - } - break; - case BA_APPLEIDUN: - val1 = 5+2*skill_lv+status->vit/10; // MaxHP percent increase - if(sd) - val1 += pc_checkskill(sd,BA_MUSICALLESSON); - break; - case DC_SERVICEFORYOU: - val1 = 15+skill_lv+(status->int_/10); // MaxSP percent increase TO-DO: this INT bonus value is guessed - val2 = 20+3*skill_lv+(status->int_/10); // SP cost reduction - if(sd){ - val1 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO This bonus value is guessed - val2 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO Should be half this value - } - break; - case BA_ASSASSINCROSS: - val1 = 100+(10*skill_lv)+(status->agi/10); // ASPD increase - if(sd) - val1 += 5*pc_checkskill(sd,BA_MUSICALLESSON); - break; - case DC_FORTUNEKISS: - val1 = 10+skill_lv+(status->luk/10); // Critical increase - if(sd) - val1 += pc_checkskill(sd,DC_DANCINGLESSON); - val1*=10; //Because every 10 crit is an actual cri point. - break; - case BD_DRUMBATTLEFIELD: - #ifdef RENEWAL - val1 = (skill_lv+5)*25; //Watk increase - val2 = skill_lv*10; //Def increase - #else - val1 = (skill_lv+1)*25; //Watk increase - val2 = (skill_lv+1)*2; //Def increase - #endif - break; - case BD_RINGNIBELUNGEN: - val1 = (skill_lv+2)*25; //Watk increase - break; - case BD_RICHMANKIM: - val1 = 25 + 11*skill_lv; //Exp increase bonus. - break; - case BD_SIEGFRIED: - val1 = 55 + skill_lv*5; //Elemental Resistance - val2 = skill_lv*10; //Status ailment resistance - break; - case WE_CALLPARTNER: - if (sd) val1 = sd->status.partner_id; - break; - case WE_CALLPARENT: - if (sd) { - val1 = sd->status.father; - val2 = sd->status.mother; - } - break; - case WE_CALLBABY: - if (sd) val1 = sd->status.child; - break; - case NJ_KAENSIN: - skill_clear_group(src, 1); //Delete previous Kaensins/Suitons - val2 = (skill_lv+1)/2 + 4; - break; - case NJ_SUITON: - skill_clear_group(src, 1); - break; - - case GS_GROUNDDRIFT: - { - int element[5]={ELE_WIND,ELE_DARK,ELE_POISON,ELE_WATER,ELE_FIRE}; - - val1 = status->rhw.ele; - if (!val1) - val1=element[rnd()%5]; - - switch (val1) - { - case ELE_FIRE: - subunt++; - case ELE_WATER: - subunt++; - case ELE_POISON: - subunt++; - case ELE_DARK: - subunt++; - case ELE_WIND: - break; - default: - subunt=rnd()%5; - break; - } - - break; - } - case GC_POISONSMOKE: - if( !(sc && sc->data[SC_POISONINGWEAPON]) ) - return NULL; - val2 = sc->data[SC_POISONINGWEAPON]->val2; // Type of Poison - val3 = sc->data[SC_POISONINGWEAPON]->val1; - limit = 4000 + 2000 * skill_lv; - break; - case GD_LEADERSHIP: - case GD_GLORYWOUNDS: - case GD_SOULCOLD: - case GD_HAWKEYES: - limit = 1000000;//it doesn't matter - break; - case LG_BANDING: - limit = -1; - break; - case WM_REVERBERATION: - interval = limit; - val2 = 1; - case WM_POEMOFNETHERWORLD: // Can't be placed on top of Land Protector. - if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) ) - return NULL; - break; - case SO_CLOUD_KILL: - skill_clear_group(src, 4); - break; - case SO_WARMER: - skill_clear_group(src, 8); - break; - case SO_VACUUM_EXTREME: - range++; - - break; - case SC_BLOODYLUST: - skill_clear_group(src, 32); - break; - case GN_WALLOFTHORN: - if( flag&1 ) - limit = 3000; - val3 = (x<<16)|y; - break; - case KO_ZENKAI: - if( sd ){ - ARR_FIND(1, 6, i, sd->talisman[i] > 0); - if( i < 5 ){ - val1 = sd->talisman[i]; // no. of aura - val2 = i; // aura type - limit += val1 * 1000; - subunt = i - 1; - pc_del_talisman(sd, sd->talisman[i], i); - } - } - break; - } - - nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skill_id,skill_lv,skill_get_unit_id(skill_id,flag&1)+subunt, limit, interval)); - group->val1=val1; - group->val2=val2; - group->val3=val3; - group->target_flag=target; - group->bl_flag= skill_get_unit_bl_target(skill_id); - group->state.ammo_consume = (sd && sd->state.arrow_atk && skill_id != GS_GROUNDDRIFT); //Store if this skill needs to consume ammo. - group->state.song_dance = (unit_flag&(UF_DANCE|UF_SONG)?1:0)|(unit_flag&UF_ENSEMBLE?2:0); //Signals if this is a song/dance/duet - group->state.guildaura = ( skill_id >= GD_LEADERSHIP && skill_id <= GD_HAWKEYES )?1:0; - group->item_id = req_item; - //if tick is greater than current, do not invoke onplace function just yet. [Skotlex] - if (DIFF_TICK(group->tick, gettick()) > SKILLUNITTIMER_INTERVAL) - active_flag = 0; - - if(skill_id==HT_TALKIEBOX || skill_id==RG_GRAFFITI){ - group->valstr=(char *) aMalloc(MESSAGE_SIZE*sizeof(char)); - if (sd) - safestrncpy(group->valstr, sd->message, MESSAGE_SIZE); - else //Eh... we have to write something here... even though mobs shouldn't use this. [Skotlex] - safestrncpy(group->valstr, "Boo!", MESSAGE_SIZE); - } - - if (group->state.song_dance) { - if(sd){ - sd->skill_id_dance = skill_id; - sd->skill_lv_dance = skill_lv; - } - if ( - sc_start4(src, SC_DANCING, 100, skill_id, group->group_id, skill_lv, - (group->state.song_dance&2?BCT_SELF:0), limit+1000) && - sd && group->state.song_dance&2 && skill_id != CG_HERMODE //Hermod is a encore with a warp! - ) - skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1); - } - - limit = group->limit; - for( i = 0; i < layout->count; i++ ) - { - struct skill_unit *unit; - int ux = x + layout->dx[i]; - int uy = y + layout->dy[i]; - int val1 = skill_lv; - int val2 = 0; - int alive = 1; - - if( !group->state.song_dance && !map_getcell(src->m,ux,uy,CELL_CHKREACH) ) - continue; // don't place skill units on walls (except for songs/dances/encores) - if( battle_config.skill_wall_check && skill_get_unit_flag(skill_id)&UF_PATHCHECK && !path_search_long(NULL,src->m,ux,uy,x,y,CELL_CHKWALL) ) - continue; // no path between cell and center of casting. - - switch( skill_id ) - { - case MG_FIREWALL: - case NJ_KAENSIN: - val2=group->val2; - break; - case WZ_ICEWALL: - val1 = (skill_lv <= 1) ? 500 : 200 + 200*skill_lv; - val2 = map_getcell(src->m, ux, uy, CELL_GETTYPE); - break; - case HT_LANDMINE: - case MA_LANDMINE: - case HT_ANKLESNARE: - case HT_SHOCKWAVE: - case HT_SANDMAN: - case MA_SANDMAN: - case HT_FLASHER: - case HT_FREEZINGTRAP: - case MA_FREEZINGTRAP: - case HT_TALKIEBOX: - case HT_SKIDTRAP: - case MA_SKIDTRAP: - case HT_CLAYMORETRAP: - 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: - val1 = 3500; - break; - case GS_DESPERADO: - val1 = abs(layout->dx[i]); - val2 = abs(layout->dy[i]); - if (val1 < 2 || val2 < 2) { //Nearby cross, linear decrease with no diagonals - if (val2 > val1) val1 = val2; - if (val1) val1--; - val1 = 36 -12*val1; - } else //Diagonal edges - val1 = 28 -4*val1 -4*val2; - if (val1 < 1) val1 = 1; - val2 = 0; - break; - case WM_REVERBERATION: - val1 = 1 + skill_lv; - break; - case GN_WALLOFTHORN: - val1 = 1000 * skill_lv; // Need official value. [LimitLine] - break; - default: - if (group->state.song_dance&0x1) - val2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance - break; - } - if (skill_get_unit_flag(skill_id) & UF_RANGEDSINGLEUNIT && i == (layout->count / 2)) - val2 |= UF_RANGEDSINGLEUNIT; // center. - - if( range <= 0 ) - map_foreachincell(skill_cell_overlap,src->m,ux,uy,BL_SKILL,skill_id, &alive, src); - if( !alive ) - continue; - - nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy,val1,val2)); - unit->limit=limit; - unit->range=range; - - if (skill_id == PF_FOGWALL && alive == 2) - { //Double duration of cells on top of Deluge/Suiton - unit->limit *= 2; - group->limit = unit->limit; - } - - // execute on all targets standing on this cell - if (range==0 && active_flag) - map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1); - } - - if (!group->alive_count) - { //No cells? Something that was blocked completely by Land Protector? - skill_delunitgroup(group); - return NULL; - } - - //success, unit created. - switch( skill_id ) { - case WZ_ICEWALL: - map_foreachinrange(skill_icewall_block, src, AREA_SIZE, BL_MOB); - break; - case NJ_TATAMIGAESHI: //Store number of tiles. - group->val1 = group->alive_count; - break; - } - - return group; -} - -/*========================================== - * - *------------------------------------------*/ -void ext_skill_unit_onplace(struct skill_unit *src, struct block_list *bl, unsigned int tick){skill_unit_onplace(src, bl, tick);} -static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, unsigned int tick) -{ - struct skill_unit_group *sg; - struct block_list *ss; - struct status_change *sc; - struct status_change_entry *sce; - enum sc_type type; - uint16 skill_id; - - nullpo_ret(src); - nullpo_ret(bl); - - if(bl->prev==NULL || !src->alive || status_isdead(bl)) - return 0; - - nullpo_ret(sg=src->group); - nullpo_ret(ss=map_id2bl(sg->src_id)); - - if( skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR ) - return 0; //AoE skills are ineffective. [Skotlex] - - sc = status_get_sc(bl); - - 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; - skill_id = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still. - switch (sg->unit_id) - { - case UNT_SPIDERWEB: - if( sc && sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1 > 0 ) - { // If you are fiberlocked and can't move, it will only increase your fireweakness level. [Inkfish] - sc->data[SC_SPIDERWEB]->val2++; - break; - } - else if( sc ) - { - int sec = skill_get_time2(sg->skill_id,sg->skill_lv); - if( status_change_start(bl,type,10000,sg->skill_lv,1,sg->group_id,0,sec,8) ) - { - const struct TimerData* td = sc->data[type]?get_timer(sc->data[type]->timer):NULL; - if( td ) - sec = DIFF_TICK(td->tick, tick); - map_moveblock(bl, src->bl.x, src->bl.y, tick); - clif_fixpos(bl); - sg->val2 = bl->id; - } - else - sec = 3000; //Couldn't trap it? - sg->limit = DIFF_TICK(tick,sg->tick)+sec; - } - break; - case UNT_SAFETYWALL: - if (!sce) - sc_start4(bl,type,100,sg->skill_lv,sg->skill_id,sg->group_id,0,sg->limit); - break; - - case UNT_PNEUMA: - case UNT_CHAOSPANIC: - case UNT_MAELSTROM: - if (!sce) - sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit); - break; - case UNT_BLOODYLUST: - if (sg->src_id == bl->id) - break; //Does not affect the caster. - if (!sce) { - TBL_PC *sd = BL_CAST(BL_PC, bl); //prevent fullheal exploit - if (sd && sd->bloodylust_tick && DIFF_TICK(gettick(), sd->bloodylust_tick) < skill_get_time2(SC_BLOODYLUST, 1)) - clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv, - sc_start4(bl, type, 100, sg->skill_lv, 1, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv))); - else { - if (sd) sd->bloodylust_tick = gettick(); - clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv, - sc_start4(bl, type, 100, sg->skill_lv, 0, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv))); - } - } - break; - - case UNT_WARP_WAITING: { - int working = sg->val1&0xffff; - - if(bl->type==BL_PC && !working){ - struct map_session_data *sd = (struct map_session_data *)bl; - if((!sd->chatID || battle_config.chat_warpportal) - && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y) - { - int x = sg->val2>>16; - int y = sg->val2&0xffff; - int count = sg->val1>>16; - unsigned short m = sg->val3; - - if( --count <= 0 ) - skill_delunitgroup(sg); - - if ( map_mapindex2mapid(sg->val3) == sd->bl.m && x == sd->bl.x && y == sd->bl.y ) - working = 1;/* we break it because officials break it, lovely stuff. */ - - sg->val1 = (count<<16)|working; - - pc_setpos(sd,m,x,y,CLR_TELEPORT); - } - } else if(bl->type == BL_MOB && battle_config.mob_warp&2) { - int16 m = map_mapindex2mapid(sg->val3); - if (m < 0) break; //Map not available on this map-server. - unit_warp(bl,m,sg->val2>>16,sg->val2&0xffff,CLR_TELEPORT); - } - } - break; - - case UNT_QUAGMIRE: - if( !sce && battle_check_target(&sg->unit->bl,bl,sg->target_flag) > 0 ) - sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit); - break; - - case UNT_VOLCANO: - case UNT_DELUGE: - case UNT_VIOLENTGALE: - if(!sce) - sc_start(bl,type,100,sg->skill_lv,sg->limit); - break; - - case UNT_SUITON: - if(!sce) - sc_start4(bl,type,100,sg->skill_lv, - map_flag_vs(bl->m) || battle_check_target(&src->bl,bl,BCT_ENEMY)>0?1:0, //Send val3 =1 to reduce agi. - 0,0,sg->limit); - break; - - case UNT_HERMODE: - if (sg->src_id!=bl->id && battle_check_target(&src->bl,bl,BCT_PARTY|BCT_GUILD) > 0) - status_change_clear_buffs(bl,1); //Should dispell only allies. - case UNT_RICHMANKIM: - case UNT_ETERNALCHAOS: - case UNT_DRUMBATTLEFIELD: - case UNT_RINGNIBELUNGEN: - case UNT_ROKISWEIL: - case UNT_INTOABYSS: - case UNT_SIEGFRIED: - //Needed to check when a dancer/bard leaves their ensemble area. - if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER)) - return skill_id; - if (!sce) - sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); - break; - case UNT_WHISTLE: - case UNT_ASSASSINCROSS: - case UNT_POEMBRAGI: - case UNT_APPLEIDUN: - case UNT_HUMMING: - case UNT_DONTFORGETME: - case UNT_FORTUNEKISS: - case UNT_SERVICEFORYOU: - if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER)) - return 0; - - if (!sc) return 0; - if (!sce) - sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); - else if (sce->val4 == 1) { - //Readjust timers since the effect will not last long. - sce->val4 = 0; - delete_timer(sce->timer, status_change_timer); - sce->timer = add_timer(tick+sg->limit, status_change_timer, bl->id, type); - } - break; - - case UNT_FOGWALL: - if (!sce) - { - sc_start4(bl, type, 100, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit); - if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) - skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, ATK_DEF, tick); - } - break; - - case UNT_GRAVITATION: - if (!sce) - sc_start4(bl,type,100,sg->skill_lv,0,BCT_ENEMY,sg->group_id,sg->limit); - break; - -// officially, icewall has no problems existing on occupied cells [ultramage] -// case UNT_ICEWALL: //Destroy the cell. [Skotlex] -// src->val1 = 0; -// if(src->limit + sg->tick > tick + 700) -// src->limit = DIFF_TICK(tick+700,sg->tick); -// break; - - case UNT_MOONLIT: - //Knockback out of area if affected char isn't in Moonlit effect - if (sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT) - break; - if (ss == bl) //Also needed to prevent infinite loop crash. - break; - skill_blown(ss,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0); - break; - - case UNT_WALLOFTHORN: - if( status_get_mode(bl)&MD_BOSS ) - break; // iRO Wiki says that this skill don't affect to Boss monsters. - if( map_flag_vs(bl->m) || bl->id == src->bl.id || battle_check_target(&src->bl,bl, BCT_ENEMY) == 1 ) - skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - break; - - case UNT_VOLCANIC_ASH: - if (!sce) - sc_start(bl, SC_ASH, 100, sg->skill_lv, skill_get_time(MH_VOLCANIC_ASH, sg->skill_lv)); - break; - - case UNT_GD_LEADERSHIP: - case UNT_GD_GLORYWOUNDS: - case UNT_GD_SOULCOLD: - case UNT_GD_HAWKEYES: - if ( !sce ) - sc_start4(bl,type,100,sg->skill_lv,0,0,0,1000); - break; - } - return skill_id; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, unsigned int tick) -{ - struct skill_unit_group *sg; - struct block_list *ss; - TBL_PC* tsd; - struct status_data *tstatus; - struct status_change *tsc; - struct skill_unit_group_tickset *ts; - enum sc_type type; - uint16 skill_id; - int diff=0; - - nullpo_ret(src); - nullpo_ret(bl); - - if (bl->prev==NULL || !src->alive || status_isdead(bl)) - return 0; - - nullpo_ret(sg=src->group); - nullpo_ret(ss=map_id2bl(sg->src_id)); - tsd = BL_CAST(BL_PC, bl); - tsc = status_get_sc(bl); - - if ( tsc && tsc->data[SC_HOVERING] ) - return 0; //Under hovering characters are immune to trap and ground target skills. - - tstatus = status_get_status_data(bl); - type = status_skill2sc(sg->skill_id); - skill_id = sg->skill_id; - - if (sg->interval == -1) { - switch (sg->unit_id) { - case UNT_ANKLESNARE: //These happen when a trap is splash-triggered by multiple targets on the same cell. - case UNT_FIREPILLAR_ACTIVE: - case UNT_ELECTRICSHOCKER: - case UNT_MANHOLE: - return 0; - default: - ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id); - return 0; - } - } - - if ((ts = skill_unitgrouptickset_search(bl,sg,tick))) - { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex] - diff = DIFF_TICK(tick,ts->tick); - if (diff < 0) - return 0; - ts->tick = tick+sg->interval; - - if ((skill_id==CR_GRANDCROSS || skill_id==NPC_GRANDDARKNESS) && !battle_config.gx_allhit) - ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,BL_CHAR)-1); - } - - switch (sg->unit_id) - { - case UNT_FIREWALL: - case UNT_KAEN: - { - int count=0; - const int x = bl->x, y = bl->y; - - if( sg->skill_id == GN_WALLOFTHORN && !map_flag_vs(bl->m) ) - break; - - //Take into account these hit more times than the timer interval can handle. - do - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0); - while(--src->val2 && x == bl->x && y == bl->y && - ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl)); - - if (src->val2<=0) - skill_delunit(src); - } - break; - - case UNT_SANCTUARY: - if( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race==RC_DEMON ) - { //Only damage enemies with offensive Sanctuary. [Skotlex] - if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 && skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0) ) - sg->val1 -= 2; // reduce healing count if this was meant for damaging [hekate] - } - else - { - int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true); - struct mob_data *md = BL_CAST(BL_MOB, bl); -#ifdef RENEWAL - if( md && md->class_ == MOBID_EMPERIUM ) - break; -#endif - if( md && mob_is_battleground(md) ) - break; - if( tstatus->hp >= tstatus->max_hp ) - break; - if( status_isimmune(bl) ) - heal = 0; - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - if( tsc && tsc->data[SC_AKAITSUKI] && heal ) - heal = ~heal + 1; - status_heal(bl, heal, 0, 0); - if( diff >= 500 ) - sg->val1--; - } - if( sg->val1 <= 0 ) - skill_delunitgroup(sg); - break; - - case UNT_EVILLAND: - //Will heal demon and undead element monsters, but not players. - if ((bl->type == BL_PC) || (!battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race!=RC_DEMON)) - { //Damage enemies - if(battle_check_target(&src->bl,bl,BCT_ENEMY)>0) - skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - } else { - int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true); - if (tstatus->hp >= tstatus->max_hp) - break; - if (status_isimmune(bl)) - heal = 0; - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - status_heal(bl, heal, 0, 0); - } - break; - - case UNT_MAGNUS: - if (!battle_check_undead(tstatus->race,tstatus->def_ele) && tstatus->race!=RC_DEMON) - break; - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - - case UNT_DUMMYSKILL: - switch (sg->skill_id) - { - case SG_SUN_WARM: //SG skills [Komurka] - case SG_MOON_WARM: - case SG_STAR_WARM: - { - int count = 0; - const int x = bl->x, y = bl->y; - - //If target isn't knocked back it should hit every "interval" ms [Playtester] - do - { - if( bl->type == BL_PC ) - status_zap(bl, 0, 15); // sp damage to players - else // mobs - if( status_charge(ss, 0, 2) ) // costs 2 SP per hit - { - if( !skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0) ) - status_charge(ss, 0, 8); //costs additional 8 SP if miss - } - else - { //should end when out of sp. - sg->limit = DIFF_TICK(tick,sg->tick); - break; - } - } while( x == bl->x && y == bl->y && - ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl) ); - } - break; - /** - * The storm gust counter was dropped in renewal - **/ - #ifndef RENEWAL - case WZ_STORMGUST: //SG counter does not reset per stormgust. IE: One hit from a SG and two hits from another will freeze you. - if (tsc) - tsc->sg_counter++; //SG hit counter. - if (skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc) - tsc->sg_counter=0; //Attack absorbed. - break; - #endif - case GS_DESPERADO: - if (rnd()%100 < src->val1) - skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - case GN_CRAZYWEED_ATK: - if( bl->type == BL_SKILL ){ - struct skill_unit *su = (struct skill_unit *)bl; - if( su && !(skill_get_inf2(su->group->skill_id)&INF2_TRAP) ) - break; - } - default: - skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - } - break; - - case UNT_FIREPILLAR_WAITING: - skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1); - skill_delunit(src); - break; - - case UNT_SKIDTRAP: - { - skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0); - sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); - sg->limit=DIFF_TICK(tick,sg->tick)+1500; - } - break; - - case UNT_ANKLESNARE: - case UNT_MANHOLE: - if( sg->val2 == 0 && tsc && (sg->unit_id == UNT_ANKLESNARE || bl->id != sg->src_id) ) { - int sec = skill_get_time2(sg->skill_id,sg->skill_lv); - if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,sec, 8) ) { - const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL; - if( td ) - sec = DIFF_TICK(td->tick, tick); - unit_movepos(bl, src->bl.x, src->bl.y, 0, 0); - clif_fixpos(bl); - sg->val2 = bl->id; - } else - sec = 3000; //Couldn't trap it? - if( sg->unit_id == UNT_ANKLESNARE ) { - clif_skillunit_update(&src->bl); - /** - * If you're snared from a trap that was invisible this makes the trap be - * visible again -- being you stepped on it (w/o this the trap remains invisible and you go "WTF WHY I CANT MOVE") - * bugreport:3961 - **/ - clif_changetraplook(&src->bl, UNT_ANKLESNARE); - } - sg->limit = DIFF_TICK(tick,sg->tick)+sec; - sg->interval = -1; - src->range = 0; - } - break; - - case UNT_ELECTRICSHOCKER: - if( bl->id != ss->id ) { - if( status_get_mode(bl)&MD_BOSS ) - break; - if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id, sg->skill_lv), 8) ) { - - map_moveblock(bl, src->bl.x, src->bl.y, tick); - clif_fixpos(bl); - - } - - map_foreachinrange(skill_trap_splash, &src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl, tick); - sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again. - } - break; - - case UNT_VENOMDUST: - if(tsc && !tsc->data[type]) - status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0); - break; - - - case UNT_MAGENTATRAP: - case UNT_COBALTTRAP: - case UNT_MAIZETRAP: - case UNT_VERDURETRAP: - if( bl->type == BL_PC )// it won't work on players - break; - case UNT_FIRINGTRAP: - case UNT_ICEBOUNDTRAP: - case UNT_CLUSTERBOMB: - if( bl->id == ss->id )// it won't trigger on caster - break; - case UNT_LANDMINE: - case UNT_CLAYMORETRAP: - case UNT_BLASTMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_FREEZINGTRAP: - case UNT_FIREPILLAR_ACTIVE: - 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); - sg->limit=DIFF_TICK(tick,sg->tick)+1500 + - (sg->unit_id== UNT_CLUSTERBOMB || sg->unit_id== UNT_ICEBOUNDTRAP?1000:0);// Cluster Bomb/Icebound has 1s to disappear once activated. - sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again. - break; - - case UNT_TALKIEBOX: - if (sg->src_id == bl->id) - break; - if (sg->val2 == 0){ - clif_talkiebox(&src->bl, sg->valstr); - sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); - sg->limit = DIFF_TICK(tick, sg->tick) + 5000; - sg->val2 = -1; - } - break; - - case UNT_LULLABY: - if (ss->id == bl->id) - break; - skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick); - break; - - case UNT_UGLYDANCE: //Ugly Dance [Skotlex] - if (ss->id != bl->id) - skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick); - break; - - case UNT_DISSONANCE: - skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - break; - - case UNT_APPLEIDUN: //Apple of Idun [Skotlex] - { - int heal; -#ifdef RENEWAL - struct mob_data *md = BL_CAST(BL_MOB, bl); - if( md && md->class_ == MOBID_EMPERIUM ) - break; -#endif - if( sg->src_id == bl->id && !(tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_BARDDANCER) ) - break; // affects self only when soullinked - heal = skill_calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true); - if( tsc->data[SC_AKAITSUKI] && heal ) - heal = ~heal + 1; - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - status_heal(bl, heal, 0, 0); - break; - } - - case UNT_TATAMIGAESHI: - case UNT_DEMONSTRATION: - skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - - case UNT_GOSPEL: - if (rnd()%100 > sg->skill_lv*10 || ss == bl) - break; - if (battle_check_target(ss,bl,BCT_PARTY)>0) - { // Support Effect only on party, not guild - int heal; - int i = rnd()%13; // Positive buff count - int time = skill_get_time2(sg->skill_id, sg->skill_lv); //Duration - switch (i) - { - case 0: // Heal 1~9999 HP - heal = rnd() %9999+1; - clif_skill_nodamage(ss,bl,AL_HEAL,heal,1); - status_heal(bl,heal,0,0); - break; - case 1: // End all negative status - status_change_clear_buffs(bl,6); - if (tsd) clif_gospel_info(tsd, 0x15); - break; - case 2: // Immunity to all status - sc_start(bl,SC_SCRESIST,100,100,time); - if (tsd) clif_gospel_info(tsd, 0x16); - break; - case 3: // MaxHP +100% - sc_start(bl,SC_INCMHPRATE,100,100,time); - if (tsd) clif_gospel_info(tsd, 0x17); - break; - case 4: // MaxSP +100% - sc_start(bl,SC_INCMSPRATE,100,100,time); - if (tsd) clif_gospel_info(tsd, 0x18); - break; - case 5: // All stats +20 - sc_start(bl,SC_INCALLSTATUS,100,20,time); - if (tsd) clif_gospel_info(tsd, 0x19); - break; - case 6: // Level 10 Blessing - sc_start(bl,SC_BLESSING,100,10,time); - break; - case 7: // Level 10 Increase AGI - sc_start(bl,SC_INCREASEAGI,100,10,time); - break; - case 8: // Enchant weapon with Holy element - sc_start(bl,SC_ASPERSIO,100,1,time); - if (tsd) clif_gospel_info(tsd, 0x1c); - break; - case 9: // Enchant armor with Holy element - sc_start(bl,SC_BENEDICTIO,100,1,time); - if (tsd) clif_gospel_info(tsd, 0x1d); - break; - case 10: // DEF +25% - sc_start(bl,SC_INCDEFRATE,100,25,time); - if (tsd) clif_gospel_info(tsd, 0x1e); - break; - case 11: // ATK +100% - sc_start(bl,SC_INCATKRATE,100,100,time); - if (tsd) clif_gospel_info(tsd, 0x1f); - break; - case 12: // HIT/Flee +50 - sc_start(bl,SC_INCHIT,100,50,time); - sc_start(bl,SC_INCFLEE,100,50,time); - if (tsd) clif_gospel_info(tsd, 0x20); - break; - } - } - else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) - { // Offensive Effect - int i = rnd()%9; // Negative buff count - int time = skill_get_time2(sg->skill_id, sg->skill_lv); - switch (i) - { - case 0: // Deal 1~9999 damage - skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - case 1: // Curse - sc_start(bl,SC_CURSE,100,1,time); - break; - case 2: // Blind - sc_start(bl,SC_BLIND,100,1,time); - break; - case 3: // Poison - sc_start(bl,SC_POISON,100,1,time); - break; - case 4: // Level 10 Provoke - sc_start(bl,SC_PROVOKE,100,10,time); - break; - case 5: // DEF -100% - sc_start(bl,SC_INCDEFRATE,100,-100,time); - break; - case 6: // ATK -100% - sc_start(bl,SC_INCATKRATE,100,-100,time); - break; - case 7: // Flee -100% - sc_start(bl,SC_INCFLEERATE,100,-100,time); - break; - case 8: // Speed/ASPD -25% - sc_start4(bl,SC_GOSPEL,100,1,0,0,BCT_ENEMY,time); - break; - } - } - break; - - case UNT_BASILICA: - { - int i = battle_check_target(&src->bl, bl, BCT_ENEMY); - if( i > 0 && !(status_get_mode(bl)&MD_BOSS) ) - { // knock-back any enemy except Boss - skill_blown(&src->bl, bl, 2, unit_getdir(bl), 0); - clif_fixpos(bl); - } - - if( sg->src_id != bl->id && i <= 0 ) - sc_start4(bl, type, 100, 0, 0, 0, src->bl.id, sg->interval + 100); - } - 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; - - case UNT_GROUNDDRIFT_WIND: - case UNT_GROUNDDRIFT_DARK: - case UNT_GROUNDDRIFT_POISON: - case UNT_GROUNDDRIFT_WATER: - case UNT_GROUNDDRIFT_FIRE: - map_foreachinrange(skill_trap_splash,&src->bl, - skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, - &src->bl,tick); - sg->unit_id = UNT_USED_TRAPS; - //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]) && rnd()%100 < 20 ) - sc_start(bl,sg->val2,100,sg->val3,skill_get_time2(GC_POISONINGWEAPON, 1)); - break; - - case UNT_EPICLESIS: - if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) - { - if( ++sg->val2 % 3 == 0 ) { - int hp, sp; - switch( sg->skill_lv ) - { - 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, 2); - sc_start(bl, type, 100, sg->skill_lv, (sg->interval * 3) + 100); - } - // Reveal hidden players every 5 seconds. - if( sg->val2 % 5 == 0 ) { - // TODO: check if other hidden status can be removed. - status_change_end(bl,SC_HIDING,INVALID_TIMER); - status_change_end(bl,SC_CLOAKING,INVALID_TIMER); - } - } - /* 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; - - 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)+1000; - sg->unit_id = UNT_USED_TRAPS; - 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; - case UNT_NETHERWORLD: - if( !(status_get_mode(bl)&MD_BOSS) && ss != bl && battle_check_target(&src->bl, bl, BCT_PARTY) ) { - if( !(tsc && tsc->data[type]) ){ - sc_start(bl, type, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv)); - sg->limit = DIFF_TICK(tick,sg->tick); - sg->unit_id = UNT_USED_TRAPS; - } - } - break; - case UNT_THORNS_TRAP: - if( tsc ) { - if( !sg->val2 ) { - int sec = skill_get_time2(sg->skill_id, sg->skill_lv); - if( sc_start(bl, type, 100, sg->skill_lv, sec) ) { - const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL; - if( td ) - sec = DIFF_TICK(td->tick, tick); - ///map_moveblock(bl, src->bl.x, src->bl.y, tick); // in official server it doesn't behave like this. [malufett] - clif_fixpos(bl); - sg->val2 = bl->id; - } else - sec = 3000; // Couldn't trap it? - sg->limit = DIFF_TICK(tick, sg->tick) + sec; - } else if( tsc->data[SC_THORNSTRAP] && bl->id == sg->val2 ) - skill_attack(skill_get_type(GN_THORNS_TRAP), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION); - } - break; - - case UNT_DEMONIC_FIRE: { - TBL_PC* sd = BL_CAST(BL_PC, ss); - switch( sg->val2 ) { - case 1: - case 2: - default: - sc_start(bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv, - skill_get_time2(sg->skill_id, sg->skill_lv)); - skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl, - sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0); - break; - case 3: - skill_attack(skill_get_type(CR_ACIDDEMONSTRATION), ss, &src->bl, bl, - CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : sg->skill_lv, tick, 0); - break; - - } - } - break; - - case UNT_FIRE_EXPANSION_SMOKE_POWDER: - sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_SMOKE_POWDER), 100, sg->skill_lv, 1000); - break; - - case UNT_FIRE_EXPANSION_TEAR_GAS: - sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_TEAR_GAS), 100, sg->skill_lv, 1000); - break; - - case UNT_HELLS_PLANT: - if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 ) - skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0); - if( ss != bl) //The caster is the only one who can step on the Plants, without destroying them - sg->limit = DIFF_TICK(tick, sg->tick) + 100; - break; - - case UNT_CLOUD_KILL: - if(tsc && !tsc->data[type]) - status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8); - skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - - case UNT_WARMER: - if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) { - int hp = 125 * sg->skill_lv; // Officially is 125 * skill_lv. - struct status_change *ssc = status_get_sc(ss); - if( ssc && ssc->data[SC_HEATER_OPTION] ) - hp += hp * ssc->data[SC_HEATER_OPTION]->val3 / 100; - if( tstatus->hp != tstatus->max_hp ) - clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0); - if( tsc && tsc->data[SC_AKAITSUKI] && hp ) - hp = ~hp + 1; - status_heal(bl, hp, 0, 0); - sc_start(bl, SC_WARMER, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv)); - } - break; - - case UNT_FIRE_INSIGNIA: - case UNT_WATER_INSIGNIA: - case UNT_WIND_INSIGNIA: - case UNT_EARTH_INSIGNIA: - case UNT_ZEPHYR: - sc_start(bl,type, 100, sg->skill_lv, sg->interval); - if (sg->unit_id != UNT_ZEPHYR && !battle_check_undead(tstatus->race, tstatus->def_ele)) { - int hp = tstatus->max_hp / 100; //+1% each 5s - if ((sg->val3) % 5) { //each 5s - if (tstatus->def_ele == skill_get_ele(sg->skill_id,sg->skill_lv)){ - status_heal(bl, hp, 0, 2); - } else if((sg->unit_id == UNT_FIRE_INSIGNIA && tstatus->def_ele == ELE_EARTH) - ||(sg->unit_id == UNT_WATER_INSIGNIA && tstatus->def_ele == ELE_FIRE) - ||(sg->unit_id == UNT_WIND_INSIGNIA && tstatus->def_ele == ELE_WATER) - ||(sg->unit_id == UNT_EARTH_INSIGNIA && tstatus->def_ele == ELE_WIND) - ){ - status_heal(bl, -hp, 0, 0); - } - } - sg->val3++; //timer - if (sg->val3 > 5) sg->val3 = 0; - } - break; - - case UNT_VACUUM_EXTREME: - {// TODO: official behavior in gvg area. [malufett] - int sec = sg->limit - DIFF_TICK(tick, sg->tick); - int range = skill_get_unit_range(sg->skill_id, sg->skill_lv); - - if( tsc && !tsc->data[type] && - distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range)// don't consider outer bounderies - sc_start(bl, type, 100, sg->skill_lv, sec); - - if( unit_is_walking(bl) && // wait until target stop walking - ( tsc && tsc->data[type] && tsc->data[type]->val4 >= tsc->data[type]->val3-range )) - break; - - if( tsc && ( !tsc->data[type] || (tsc->data[type] && tsc->data[type]->val4 < 1 ) ) ) - break; - - if( unit_is_walking(bl) && - distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) > range )// going outside of boundaries? then force it to stop - unit_stop_walking(bl,1); - - if( !unit_is_walking(bl) && - distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range && // only snap if the target is inside the range or - src->bl.x != bl->x && src->bl.y != bl->y){// diagonal position parallel to VE's center - unit_movepos(bl, src->bl.x, src->bl.y, 0, 0); - clif_fixpos(bl); - } - } - break; - - case UNT_FIRE_MANTLE: - if( battle_check_target(&src->bl, bl, BCT_ENEMY) ) - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - - case UNT_ZENKAI_WATER: - case UNT_ZENKAI_LAND: - case UNT_ZENKAI_FIRE: - case UNT_ZENKAI_WIND: - if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 ){ - switch( sg->unit_id ){ - case UNT_ZENKAI_WATER: - sc_start(bl, SC_CRYSTALIZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - sc_start(bl, SC_FREEZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - sc_start(bl, SC_FREEZING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case UNT_ZENKAI_LAND: - sc_start(bl, SC_STONE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - sc_start(bl, SC_POISON, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case UNT_ZENKAI_FIRE: - sc_start(bl, SC_BURNING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case UNT_ZENKAI_WIND: - sc_start(bl, SC_SILENCE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - sc_start(bl, SC_SLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - sc_start(bl, SC_DEEPSLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - } - }else - sc_start2(bl,type,100,sg->val1,sg->val2,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - - case UNT_MAKIBISHI: - skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - sg->limit = DIFF_TICK(tick, sg->tick); - sg->unit_id = UNT_USED_TRAPS; - break; - - case UNT_LAVA_SLIDE: - skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - if(++sg->val1 > 4) //after 5 stop hit and destroy me - sg->limit = DIFF_TICK(tick, sg->tick); - break; - case UNT_POISON_MIST: - skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - status_change_start(bl, SC_BLIND, rnd() % 100 > sg->skill_lv * 10, sg->skill_lv, sg->skill_id, 0, 0, skill_get_time2(sg->skill_id, sg->skill_lv), 2|8); - break; - } - - if (bl->type == BL_MOB && ss != bl) - mobskill_event((TBL_MOB*)bl, ss, tick, MSC_SKILLUSED|(skill_id<<16)); - - return skill_id; -} -/*========================================== - * Triggered when a char steps out of a skill cell - *------------------------------------------*/ -int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned int tick) -{ - struct skill_unit_group *sg; - struct status_change *sc; - struct status_change_entry *sce; - enum sc_type type; - - nullpo_ret(src); - nullpo_ret(bl); - nullpo_ret(sg=src->group); - sc = status_get_sc(bl); - type = status_skill2sc(sg->skill_id); - sce = (sc && type != -1)?sc->data[type]:NULL; - - if( bl->prev==NULL || - (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB) ) //Need to delete the trap if the source died. - return 0; - - switch(sg->unit_id){ - case UNT_SAFETYWALL: - case UNT_PNEUMA: - case UNT_EPICLESIS://Arch Bishop - case UNT_NEUTRALBARRIER: - case UNT_STEALTHFIELD: - if (sce) - status_change_end(bl, type, INVALID_TIMER); - break; - - case UNT_BASILICA: - 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); - break; - - case UNT_SPIDERWEB: - { - struct block_list *target = map_id2bl(sg->val2); - if (target && target==bl) - { - if (sce && sce->val3 == sg->group_id) - status_change_end(bl, type, INVALID_TIMER); - sg->limit = DIFF_TICK(tick,sg->tick)+1000; - } - break; - } - } - return sg->skill_id; -} - -/*========================================== - * Triggered when a char steps out of a skill group (entirely) [Skotlex] - *------------------------------------------*/ -static int skill_unit_onleft (uint16 skill_id, struct block_list *bl, unsigned int tick) -{ - struct status_change *sc; - struct status_change_entry *sce; - enum sc_type type; - - sc = status_get_sc(bl); - if (sc && !sc->count) - sc = NULL; - - type = status_skill2sc(skill_id); - sce = (sc && type != -1)?sc->data[type]:NULL; - - switch (skill_id) - { - case WZ_QUAGMIRE: - if (bl->type==BL_MOB) - break; - if (sce) - status_change_end(bl, type, INVALID_TIMER); - break; - - case BD_LULLABY: - case BD_RICHMANKIM: - case BD_ETERNALCHAOS: - case BD_DRUMBATTLEFIELD: - case BD_RINGNIBELUNGEN: - case BD_ROKISWEIL: - case BD_INTOABYSS: - case BD_SIEGFRIED: - if(sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == skill_id) - { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex] - //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance. - //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner, - //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble - //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel - //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it. - status_change_end(bl, SC_DANCING, INVALID_TIMER); - } - case MH_STEINWAND: - case MG_SAFETYWALL: - case AL_PNEUMA: - case SA_VOLCANO: - case SA_DELUGE: - case SA_VIOLENTGALE: - case CG_HERMODE: - case HW_GRAVITATION: - case NJ_SUITON: - case SC_MAELSTROM: - case EL_WATER_BARRIER: - case EL_ZEPHYR: - case EL_POWER_OF_GAIA: - case SO_FIRE_INSIGNIA: - case SO_WATER_INSIGNIA: - case SO_WIND_INSIGNIA: - case SO_EARTH_INSIGNIA: - if (sce) - status_change_end(bl, type, INVALID_TIMER); - break; - case SC_BLOODYLUST: - if (sce) { - status_change_end(bl, type, INVALID_TIMER); - status_set_sp(bl, 0, 0); //set sp to 0 when quitting zone - } - break; - - case BA_POEMBRAGI: - case BA_WHISTLE: - case BA_ASSASSINCROSS: - case BA_APPLEIDUN: - case DC_HUMMING: - case DC_DONTFORGETME: - case DC_FORTUNEKISS: - case DC_SERVICEFORYOU: - if (sce) - { - delete_timer(sce->timer, status_change_timer); - //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas... - //not possible on our current implementation. - sce->val4 = 1; //Store the fact that this is a "reduced" duration effect. - sce->timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type); - } - break; - case PF_FOGWALL: - if (sce) - { - status_change_end(bl, type, INVALID_TIMER); - if ((sce=sc->data[SC_BLIND])) - { - if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex] - status_change_end(bl, SC_BLIND, INVALID_TIMER); - else { - delete_timer(sce->timer, status_change_timer); - sce->timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND); - } - } - } - break; - case GD_LEADERSHIP: - case GD_GLORYWOUNDS: - case GD_SOULCOLD: - case GD_HAWKEYES: - if( !(sce && sce->val4) ) - status_change_end(bl, type, INVALID_TIMER); - break; - } - - return skill_id; -} - -/*========================================== - * Invoked when a unit cell has been placed/removed/deleted. - * flag values: - * flag&1: Invoke onplace function (otherwise invoke onout) - * flag&4: Invoke a onleft call (the unit might be scheduled for deletion) - *------------------------------------------*/ -static int skill_unit_effect (struct block_list* bl, va_list ap) -{ - struct skill_unit* unit = va_arg(ap,struct skill_unit*); - struct skill_unit_group* group = unit->group; - unsigned int tick = va_arg(ap,unsigned int); - unsigned int flag = va_arg(ap,unsigned int); - uint16 skill_id; - bool dissonance; - - if( (!unit->alive && !(flag&4)) || bl->prev == NULL ) - return 0; - - nullpo_ret(group); - - dissonance = skill_dance_switch(unit, 0); - - //Necessary in case the group is deleted after calling on_place/on_out [Skotlex] - skill_id = group->skill_id; - //Target-type check. - if( !(group->bl_flag&bl->type && battle_check_target(&unit->bl,bl,group->target_flag)>0) && (flag&4) ) { - if( group->state.song_dance&0x1 || (group->src_id == bl->id && group->state.song_dance&0x2) ) - skill_unit_onleft(skill_id, bl, tick);//Ensemble check to terminate it. - } else { - if( flag&1 ) - skill_unit_onplace(unit,bl,tick); - else - skill_unit_onout(unit,bl,tick); - - if( flag&4 ) - skill_unit_onleft(skill_id, bl, tick); - } - - if( dissonance ) skill_dance_switch(unit, 1); - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_unit_ondamaged (struct skill_unit *src, struct block_list *bl, int damage, unsigned int tick) -{ - struct skill_unit_group *sg; - - nullpo_ret(src); - nullpo_ret(sg=src->group); - - switch( sg->unit_id ) { - case UNT_BLASTMINE: - case UNT_SKIDTRAP: - case UNT_LANDMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_CLAYMORETRAP: - case UNT_FREEZINGTRAP: - case UNT_TALKIEBOX: - case UNT_ANKLESNARE: - case UNT_ICEWALL: - case UNT_REVERBERATION: - case UNT_WALLOFTHORN: - src->val1-=damage; - break; - default: - damage = 0; - break; - } - return damage; -} - -/*========================================== - * - *------------------------------------------*/ -static int skill_check_condition_char_sub (struct block_list *bl, va_list ap) -{ - int *c, skill_id; - struct block_list *src; - struct map_session_data *sd; - struct map_session_data *tsd; - int *p_sd; //Contains the list of characters found. - - nullpo_ret(bl); - nullpo_ret(tsd=(struct map_session_data*)bl); - nullpo_ret(src=va_arg(ap,struct block_list *)); - nullpo_ret(sd=(struct map_session_data*)src); - - c=va_arg(ap,int *); - p_sd = va_arg(ap, int *); - skill_id = va_arg(ap,int); - - if ( ((skill_id != PR_BENEDICTIO && *c >=1) || *c >=2) && !(skill_get_inf2(skill_id)&INF2_CHORUS_SKILL) ) - return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex] - - if (bl == src) - return 0; - - if(pc_isdead(tsd)) - return 0; - - if (tsd->sc.data[SC_SILENCE] || ( tsd->sc.opt1 && tsd->sc.opt1 != OPT1_BURNING )) - return 0; - - if( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL ) { - if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_MINSTRELWANDERER ) - p_sd[(*c)++] = tsd->bl.id; - return 1; - } else { - - switch(skill_id) { - case PR_BENEDICTIO: { - uint8 dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y); - dir = (unit_getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing. - if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest. - && sd->status.sp >= 10) - p_sd[(*c)++]=tsd->bl.id; - return 1; - } - case AB_ADORAMUS: - // 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( ( sd->class_&MAPID_THIRDMASK ) == MAPID_WARLOCK ) - p_sd[(*c)++] = tsd->bl.id; - return 1; - case LG_RAYOFGENESIS: - if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD && - tsd->sc.data[SC_BANDING] ) - p_sd[(*c)++] = tsd->bl.id; - return 1; - default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex] - { - uint16 skill_lv; - if(pc_issit(tsd) || !unit_can_move(&tsd->bl)) - return 0; - if (sd->status.sex != tsd->status.sex && - (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && - (skill_lv = pc_checkskill(tsd, skill_id)) > 0 && - (tsd->weapontype1==W_MUSICAL || tsd->weapontype1==W_WHIP) && - sd->status.party_id && tsd->status.party_id && - sd->status.party_id == tsd->status.party_id && - !tsd->sc.data[SC_DANCING]) - { - p_sd[(*c)++]=tsd->bl.id; - return skill_lv; - } else { - return 0; - } - } - break; - } - - } - return 0; -} - -/*========================================== - * Checks and stores partners for ensemble skills [Skotlex] - *------------------------------------------*/ -int skill_check_pc_partner (struct map_session_data *sd, uint16 skill_id, short* skill_lv, int range, int cast_flag) -{ - static int c=0; - static int p_sd[2] = { 0, 0 }; - int i; - bool is_chorus = ( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL ); - - if (!battle_config.player_skill_partner_check || pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL)) - return is_chorus ? MAX_PARTY : 99; //As if there were infinite partners. - - if (cast_flag) { //Execute the skill on the partners. - struct map_session_data* tsd; - switch (skill_id) { - case PR_BENEDICTIO: - for (i = 0; i < c; i++) { - if ((tsd = map_id2sd(p_sd[i])) != NULL) - status_charge(&tsd->bl, 0, 10); - } - return c; - case AB_ADORAMUS: - if( c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL ) { - i = 2 * (*skill_lv); - status_charge(&tsd->bl, 0, i); - } - break; - case WM_GREAT_ECHO: - for( i = 0; i < c; i++ ) { - if( (tsd = map_id2sd(p_sd[i])) != NULL ) - status_zap(&tsd->bl,0,skill_get_sp(skill_id,*skill_lv)/c); - } - break; - default: //Warning: Assuming Ensemble skills here (for speed) - if( is_chorus ) - break;//Chorus skills are not to be parsed as ensambles - if (c > 0 && sd->sc.data[SC_DANCING] && (tsd = map_id2sd(p_sd[0])) != NULL) { - sd->sc.data[SC_DANCING]->val4 = tsd->bl.id; - sc_start4(&tsd->bl,SC_DANCING,100,skill_id,sd->sc.data[SC_DANCING]->val2,*skill_lv,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000); - clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1); - tsd->skill_id_dance = skill_id; - tsd->skill_lv_dance = *skill_lv; - } - return c; - } - } - - //Else: new search for partners. - c = 0; - memset (p_sd, 0, sizeof(p_sd)); - if( is_chorus ) - i = party_foreachsamemap(skill_check_condition_char_sub,sd,AREA_SIZE,&sd->bl, &c, &p_sd, skill_id, *skill_lv); - else - 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 && skill_id != WL_COMET ) //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; -} - -/*========================================== - * - *------------------------------------------*/ -static int skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap) -{ - int *c,src_id,mob_class,skill; - struct mob_data *md; - - md=(struct mob_data*)bl; - src_id=va_arg(ap,int); - mob_class=va_arg(ap,int); - skill=va_arg(ap,int); - c=va_arg(ap,int *); - - if( md->master_id != src_id || md->special_state.ai != (unsigned)(skill == AM_SPHEREMINE?2:skill == KO_ZANZOU?4:3) ) - return 0; //Non alchemist summoned mobs have nothing to do here. - - if(md->class_==mob_class) - (*c)++; - - return 1; -} - -/*========================================== - * Determines if a given skill should be made to consume ammo - * when used by the player. [Skotlex] - *------------------------------------------*/ -int skill_isammotype (struct map_session_data *sd, int skill) -{ - return ( - battle_config.arrow_decrement==2 && - (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) && - skill != HT_PHANTASMIC && - skill_get_type(skill) == BF_WEAPON && - !(skill_get_nk(skill)&NK_NO_DAMAGE) && - !skill_get_spiritball(skill,1) //Assume spirit spheres are used as ammo instead. - ); -} - -int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) { - struct status_data *status; - struct status_change *sc; - struct skill_condition require; - int i; - - nullpo_ret(sd); - - if (sd->chatID) return 0; - - if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id ) - { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] - sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check. - sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. - return 1; - } - - switch( sd->menuskill_id ) { - case AM_PHARMACY: - switch( skill_id ) { - case AM_PHARMACY: - case AC_MAKINGARROW: - case BS_REPAIRWEAPON: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - return 0; - } - break; - case GN_MIX_COOKING: - case GN_MAKEBOMB: - case GN_S_PHARMACY: - case GN_CHANGEMATERIAL: - if( sd->menuskill_id != skill_id ) - return 0; - break; - } - status = &sd->battle_status; - sc = &sd->sc; - if( !sc->count ) - sc = NULL; - - if( sd->skillitem == skill_id ) - { - if( sd->state.abra_flag ) // Hocus-Pocus was used. [Inkfish] - sd->state.abra_flag = 0; - else - { // When a target was selected, consume items that were skipped in pc_use_item [Skotlex] - if( (i = sd->itemindex) == -1 || - sd->status.inventory[i].nameid != sd->itemid || - sd->inventory_data[i] == NULL || - !sd->inventory_data[i]->flag.delay_consume || - sd->status.inventory[i].amount < 1 - ) - { //Something went wrong, item exploit? - sd->itemid = sd->itemindex = -1; - return 0; - } - //Consume - sd->itemid = sd->itemindex = -1; - if( skill_id == WZ_EARTHSPIKE && sc && sc->data[SC_EARTHSCROLL] && rnd()%100 > sc->data[SC_EARTHSCROLL]->val2 ) // [marquis007] - ; //Do not consume item. - else if( sd->status.inventory[i].expire_time == 0 ) - pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); // Rental usable items are not consumed until expiration - } - return 1; - } - - if( pc_is90overweight(sd) ) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0); - return 0; - } - - if( sc && ( sc->data[SC__SHADOWFORM] || sc->data[SC__IGNORANCE] ) ) - return 0; - - switch( skill_id ) { // Turn off check. - case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD: - case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL: - case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN: - case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case RA_WUGDASH: case KO_YAMIKUMO: - if( sc && sc->data[status_skill2sc(skill_id)] ) - return 1; - } - - // Check the skills that can be used while mounted on a warg - if( pc_isridingwug(sd) ) { - switch( skill_id ) { - case HT_SKIDTRAP: case HT_LANDMINE: case HT_ANKLESNARE: case HT_SHOCKWAVE: - case HT_SANDMAN: case HT_FLASHER: case HT_FREEZINGTRAP: case HT_BLASTMINE: - case HT_CLAYMORETRAP: case HT_SPRINGTRAP: case RA_DETONATOR: case RA_CLUSTERBOMB: - case HT_TALKIEBOX: case RA_FIRINGTRAP: case RA_ICEBOUNDTRAP: - case RA_WUGDASH: case RA_WUGRIDER: case RA_WUGSTRIKE: - break; - default: // in official there is no message. - return 0; - } - - } - if( pc_ismadogear(sd) ) { - switch( skill_id ) { //None Mado skills are unusable when Mado is equipped. [Jobbie] - case BS_REPAIRWEAPON: case WS_MELTDOWN: - case BS_HAMMERFALL: case WS_CARTBOOST: - case BS_ADRENALINE: case WS_WEAPONREFINE: - case BS_WEAPONPERFECT: case WS_CARTTERMINATION: - case BS_OVERTHRUST: case WS_OVERTHRUSTMAX: - case BS_MAXIMIZE: case NC_AXEBOOMERANG: - case BS_ADRENALINE2: case NC_POWERSWING: - case BS_UNFAIRLYTRICK: case NC_AXETORNADO: - case BS_GREED: - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - default: //Only Mechanic exlcusive skill can be used. - break; - } - } - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) - return 0; - - require = skill_get_requirement(sd,skill_id,skill_lv); - - //Can only update state when weapon/arrow info is checked. - sd->state.arrow_atk = require.ammo?1:0; - - // perform skill-specific checks (and actions) - switch( skill_id ) { - case SO_SPELLFIST: - if(sd->skill_id_old != MG_FIREBOLT && sd->skill_id_old != MG_COLDBOLT && sd->skill_id_old != MG_LIGHTNINGBOLT){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - case SA_CASTCANCEL: - if(sd->ud.skilltimer == INVALID_TIMER) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case AL_WARP: - if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza] - char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_WARP)); - clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel." - return 0; - } - break; - case MO_CALLSPIRITS: - if(sc && sc->data[SC_RAISINGDRAGON]) - skill_lv += sc->data[SC_RAISINGDRAGON]->val1; - if(sd->spiritball >= skill_lv) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case MO_FINGEROFFENSIVE: - case GS_FLING: - case SR_RAMPAGEBLASTER: - case SR_RIDEINLIGHTNING: - if( sd->spiritball > 0 && sd->spiritball < require.spiritball ) - sd->spiritball_old = require.spiritball = sd->spiritball; - else - sd->spiritball_old = require.spiritball; - break; - case MO_CHAINCOMBO: - if(!sc) - return 0; - if(sc->data[SC_BLADESTOP]) - break; - if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK) - break; - return 0; - case MO_COMBOFINISH: - if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO)) - return 0; - break; - case CH_TIGERFIST: - if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH)) - return 0; - break; - case CH_CHAINCRUSH: - if(!(sc && sc->data[SC_COMBO])) - return 0; - if(sc->data[SC_COMBO]->val1 != MO_COMBOFINISH && sc->data[SC_COMBO]->val1 != CH_TIGERFIST) - return 0; - break; - case MO_EXTREMITYFIST: - // if(sc && sc->data[SC_EXTREMITYFIST]) //To disable Asura during the 5 min skill block uncomment this... - // return 0; - if( sc && (sc->data[SC_BLADESTOP] || sc->data[SC_CURSEDCIRCLE_ATKER]) ) - break; - if( sc && sc->data[SC_COMBO] ) - { - switch(sc->data[SC_COMBO]->val1) { - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - break; - default: - return 0; - } - } - else if( !unit_can_move(&sd->bl) ) - { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex] - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - - case TK_MISSION: - if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON ) - {// Cannot be used by Non-Taekwon classes - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - - case TK_READYCOUNTER: - case TK_READYDOWN: - case TK_READYSTORM: - case TK_READYTURN: - case TK_JUMPKICK: - if( (sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER ) - {// Soul Linkers cannot use this skill - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - - case TK_TURNKICK: - case TK_STORMKICK: - case TK_DOWNKICK: - case TK_COUNTER: - if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) - return 0; //Anti-Soul Linker check in case you job-changed with Stances active. - if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK) - return 0; //Combo needs to be ready - - if (sc->data[SC_COMBO]->val3) { //Kick chain - //Do not repeat a kick. - if (sc->data[SC_COMBO]->val3 != skill_id) - break; - status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER); - return 0; - } - if(sc->data[SC_COMBO]->val1 != skill_id && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait. - unit_cancel_combo(&sd->bl); - return 0; - } - break; //Combo ready. - case BD_ADAPTATION: - { - int time; - if(!(sc && sc->data[SC_DANCING])) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - time = 1000*(sc->data[SC_DANCING]->val3>>16); - if (skill_get_time( - (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID - (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV - - time < skill_get_time2(skill_id,skill_lv)) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - } - break; - - case PR_BENEDICTIO: - if (skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 0) < 2) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - - case SL_SMA: - if(!(sc && sc->data[SC_SMA])) - return 0; - break; - - case HT_POWER: - if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id)) - return 0; - break; - - case CG_HERMODE: - if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill_id, skill_lv))) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex] - { - int i,x,y,range = skill_get_splash(skill_id, skill_lv)+1; - int size = range*2+1; - for (i=0;i<size*size;i++) { - x = sd->bl.x+(i%size-range); - y = sd->bl.y+(i/size-range); - if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - } - } - break; - case PR_REDEMPTIO: - { - int exp; - if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) || - ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); //Not enough exp. - return 0; - } - break; - } - case AM_TWILIGHT2: - case AM_TWILIGHT3: - if (!party_skill_check(sd, sd->status.party_id, skill_id, skill_lv)) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case SG_SUN_WARM: - case SG_MOON_WARM: - case SG_STAR_WARM: - if (sc && sc->data[SC_MIRACLE]) - break; - i = skill_id-SG_SUN_WARM; - if (sd->bl.m == sd->feel_map[i].m) - break; - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - break; - case SG_SUN_COMFORT: - case SG_MOON_COMFORT: - case SG_STAR_COMFORT: - if (sc && sc->data[SC_MIRACLE]) - break; - i = skill_id-SG_SUN_COMFORT; - if (sd->bl.m == sd->feel_map[i].m && - (battle_config.allow_skill_without_day || sg_info[i].day_func())) - break; - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - case SG_FUSION: - if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR) - break; - //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex] - //Only invoke on skill begin cast (instant cast skill). [Kevin] - if( require.sp > 0 ) - { - if (status->sp < (unsigned int)require.sp) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0); - else - status_zap(&sd->bl, 0, require.sp); - } - return 0; - case GD_BATTLEORDER: - case GD_REGENERATION: - case GD_RESTORE: - if (!map_flag_gvg2(sd->bl.m)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - case GD_EMERGENCYCALL: - // other checks were already done in skillnotok() - if (!sd->status.guild_id || !sd->state.gmaster_flag) - return 0; - break; - - case GS_GLITTERING: - if(sd->spiritball >= 10) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - - case NJ_ISSEN: -#ifdef RENEWAL - if (status->hp < (status->hp/100)) { -#else - if (status->hp < 2) { -#endif - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - case NJ_BUNSINJYUTSU: - if (!(sc && sc->data[SC_NEN])) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - - case NJ_ZENYNAGE: - case KO_MUCHANAGE: - if(sd->status.zeny < require.zeny) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0); - return 0; - } - break; - case PF_HPCONVERSION: - if (status->sp == status->max_sp) - return 0; //Unusable when at full SP. - break; - case AM_CALLHOMUN: //Can't summon if a hom is already out - if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80% - if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100)) - { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - /** - * Arch Bishop - **/ - case AB_ANCILLA: - { - 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_id, USESKILL_FAIL_ANCILLA_NUMOVER, 0); - return 0; - } - } - break; - /** - * 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_id,USESKILL_FAIL_LEVEL,0); - // return 0; - // } - // break; - - case AB_ADORAMUS: - /** - * Warlock - **/ - case WL_COMET: - if( skill_check_pc_partner(sd,skill_id,&skill_lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) ) - { - //clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]); - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case WL_SUMMONFB: - case WL_SUMMONBL: - case WL_SUMMONWB: - case WL_SUMMONSTONE: - if( sc ) - { - 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_id,USESKILL_FAIL_SUMMON,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_id,USESKILL_FAIL_LEVEL,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_id, USESKILL_FAIL_GC_WEAPONBLOCKING, 0); - return 0; - } - break; - /** - * Ranger - **/ - case RA_WUGMASTERY: - if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case RA_WUGSTRIKE: - if( !pc_iswug(sd) && !pc_isridingwug(sd) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case RA_WUGRIDER: - if( pc_isfalcon(sd) || ( !pc_isridingwug(sd) && !pc_iswug(sd) ) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case RA_WUGDASH: - if(!pc_isridingwug(sd)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - /** - * Royal Guard - **/ - case LG_BANDING: - if( sc && sc->data[SC_INSPIRATION] ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case LG_PRESTIGE: - if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case LG_RAGEBURST: - if( sd->spiritball == 0 ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_SKILLINTERVAL,0); - return 0; - } - sd->spiritball_old = require.spiritball = sd->spiritball; - break; - case LG_RAYOFGENESIS: - if( sc && sc->data[SC_INSPIRATION] ) - return 1; // Don't check for partner. - if( !(sc && sc->data[SC_BANDING]) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL,0); - return 0; - } else if( skill_check_pc_partner(sd,skill_id,&skill_lv,skill_get_range(skill_id,skill_lv),0) < 1 ) - return 0; // Just fails, no msg here. - break; - case LG_HESPERUSLIT: - if( !sc || !sc->data[SC_BANDING] ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case SR_FALLENEMPIRE: - if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) ) - return 0; - break; - - case SR_CRESCENTELBOW: - if( sc && sc->data[SC_CRESCENTELBOW] ) { - clif_skill_fail(sd, skill_id, USESKILL_FAIL_DUPLICATE, 0); - return 0; - } - break; - case SR_CURSEDCIRCLE: - if (map_flag_gvg(sd->bl.m)) { - if (map_foreachinrange(mob_count_sub, &sd->bl, skill_get_splash(skill_id, skill_lv), BL_MOB, - MOBID_EMPERIUM, MOBID_GUARIDAN_STONE1, MOBID_GUARIDAN_STONE2)) { - char output[128]; - sprintf(output, "You're too close to a stone or emperium to do this skill"); - clif_colormes(sd, COLOR_RED, output); - return 0; - } - } - if( sd->spiritball > 0 ) - sd->spiritball_old = require.spiritball = sd->spiritball; - else { - clif_skill_fail(sd,skill_id,0,0); - return 0; - } - break; - case SR_GATEOFHELL: - if( sd->spiritball > 0 ) - sd->spiritball_old = require.spiritball; - break; - case SC_MANHOLE: - case SC_DIMENSIONDOOR: - if( sc && sc->data[SC_MAGNETICFIELD] ) { - clif_skill_fail(sd,skill_id,0,0); - return 0; - } - break; - case WM_GREAT_ECHO: { - int count; - count = skill_check_pc_partner(sd, skill_id, &skill_lv, skill_get_splash(skill_id,skill_lv), 0); - if( count < 1 ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0); - return 0; - } else - require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party. - } - break; - case SO_FIREWALK: - case SO_ELECTRICWALK: // Can't be casted until you've walked all cells. - if( sc && sc->data[SC_PROPERTYWALK] && - sc->data[SC_PROPERTYWALK]->val3 < skill_get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) { - clif_skill_fail(sd,skill_id,0x0,0); - return 0; - } - break; - case SO_EL_CONTROL: - if( !sd->status.ele_id || !sd->ed ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case RETURN_TO_ELDICASTES: - if( pc_ismadogear(sd) ) { //Cannot be used if Mado is equipped. - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case LG_REFLECTDAMAGE: - case CR_REFLECTSHIELD: - if( sc && sc->data[SC_KYOMU] && rand()%100 < 30){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case KO_KAHU_ENTEN: - case KO_HYOUHU_HUBUKI: - case KO_KAZEHU_SEIRAN: - case KO_DOHU_KOUKAI: - { - int ttype = skill_get_ele(skill_id, skill_lv); - ARR_FIND(1, 5, i, sd->talisman[i] > 0 && i != ttype); - if( (i < 5 && i != ttype) || sd->talisman[ttype] >= 10 ){ - clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); - return 0; - } - } - break; - case KO_KAIHOU: - case KO_ZENKAI: - ARR_FIND(1, 6, i, sd->talisman[i] > 0); - if( i > 4 ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - } - - switch(require.state) { - case ST_HIDING: - if(!(sc && sc->option&OPTION_HIDE)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_CLOAKING: - if(!pc_iscloaking(sd)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_HIDDEN: - if(!pc_ishiding(sd)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_RIDING: - if(!pc_isriding(sd)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_FALCON: - if(!pc_isfalcon(sd)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_CARTBOOST: - if(!(sc && sc->data[SC_CARTBOOST])) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - case ST_CART: - if(!pc_iscarton(sd)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_SHIELD: - if(sd->status.shield <= 0) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_SIGHT: - if(!(sc && sc->data[SC_SIGHT])) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_EXPLOSIONSPIRITS: - if(!(sc && sc->data[SC_EXPLOSIONSPIRITS])) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_RECOV_WEIGHT_RATE: - if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= (unsigned int)battle_config.natural_heal_weight_rate) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_MOVE_ENABLE: - if (sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id) - sd->ud.canmove_tick = gettick(); //When using a combo, cancel the can't move delay to enable the skill. [Skotlex] - - if (!unit_can_move(&sd->bl)) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - case ST_WATER: - if (sc && (sc->data[SC_DELUGE] || sc->data[SC_SUITON])) - break; - if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER)) - break; - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - /** - * Rune Knight - **/ - case ST_RIDINGDRAGON: - if( !pc_isridingdragon(sd) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - /** - * Wug - **/ - case ST_WUG: - if( !pc_iswug(sd) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - /** - * Riding Wug - **/ - case ST_RIDINGWUG: - if( !pc_isridingwug(sd) ){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - /** - * Mechanic - **/ - case ST_MADO: - if( !pc_ismadogear(sd) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - break; - /** - * Sorcerer - **/ - case ST_ELEMENTALSPIRIT: - if(!sd->ed) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_EL_SUMMON,0); - return 0; - } - break; - case ST_POISONINGWEAPON: - if (!(sc && sc->data[SC_POISONINGWEAPON])) { - clif_skill_fail(sd, skill_id, USESKILL_FAIL_GC_POISONINGWEAPON, 0); - return 0; - } - break; - case ST_ROLLINGCUTTER: - if (!(sc && sc->data[SC_ROLLINGCUTTER])) { - clif_skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0); - return 0; - } - break; - case ST_MH_FIGHTING: - if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_FIGHTING)){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - case ST_MH_GRAPPLING: - if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_GRAPPLING)){ - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - } - - if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) { - //mhp is the max-hp-requirement, that is, - //you must have this % or less of HP to cast it. - clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); - return 0; - } - - if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0); - return 0; - } - - if( require.sp > 0 && status->sp < (unsigned int)require.sp) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0); - return 0; - } - - if( require.zeny > 0 && sd->status.zeny < require.zeny ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0); - return 0; - } - - if( require.spiritball > 0 && sd->spiritball < require.spiritball) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_SPIRITS,require.spiritball); - return 0; - } - - return 1; -} - -int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) -{ - struct skill_condition require; - struct status_data *status; - int i; - int index[MAX_SKILL_ITEM_REQUIRE]; - - nullpo_ret(sd); - - if( sd->chatID ) - return 0; - - if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id ) { - //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] - sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check. - sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. - return 1; - } - - switch( sd->menuskill_id ) { // Cast start or cast end?? - case AM_PHARMACY: - switch( skill_id ) { - case AM_PHARMACY: - case AC_MAKINGARROW: - case BS_REPAIRWEAPON: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - return 0; - } - break; - case GN_MIX_COOKING: - case GN_MAKEBOMB: - case GN_S_PHARMACY: - case GN_CHANGEMATERIAL: - if( sd->menuskill_id != skill_id ) - return 0; - break; - } - - if( sd->skillitem == skill_id ) // Casting finished (Item skill or Hocus-Pocus) - return 1; - - if( pc_is90overweight(sd) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0); - return 0; - } - - // perform skill-specific checks (and actions) - switch( skill_id ) { - case PR_BENEDICTIO: - skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1); - break; - case AM_CANNIBALIZE: - case AM_SPHEREMINE: { - int c=0; - int summons[5] = { 1589, 1579, 1575, 1555, 1590 }; - //int summons[5] = { 1020, 1068, 1118, 1500, 1368 }; - int maxcount = (skill_id==AM_CANNIBALIZE)? 6-skill_lv : skill_get_maxcount(skill_id,skill_lv); - int mob_class = (skill_id==AM_CANNIBALIZE)? summons[skill_lv-1] :1142; - if(battle_config.land_skill_limit && maxcount>0 && (battle_config.land_skill_limit&BL_PC)) { - i = map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c); - if(c >= maxcount || - (skill_id==AM_CANNIBALIZE && c != i && battle_config.summon_flora&2)) - { //Fails when: exceed max limit. There are other plant types already out. - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - } - break; - } - case NC_SILVERSNIPER: - case NC_MAGICDECOY: { - int c = 0, j; - int maxcount = skill_get_maxcount(skill_id,skill_lv); - int mob_class = 2042; - if( skill_id == NC_MAGICDECOY ) - mob_class = 2043; - - if( battle_config.land_skill_limit && maxcount > 0 && ( battle_config.land_skill_limit&BL_PC ) ) { - if( skill_id == NC_MAGICDECOY ) { - for( j = mob_class; j <= 2046; j++ ) - map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, j, skill_id, &c); - } else - map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c); - if( c >= maxcount ) { - clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0); - return 0; - } - } - } - break; - case KO_ZANZOU: { - int c = 0; - i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, 2308, skill_id, &c); - if( c >= skill_get_maxcount(skill_id,skill_lv) || c != i) - { - clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0); - return 0; - } - } - break; - } - - status = &sd->battle_status; - - require = skill_get_requirement(sd,skill_id,skill_lv); - - if( require.hp > 0 && status->hp <= (unsigned int)require.hp) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0); - return 0; - } - - if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) { - clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0); - return 0; - } - - if( require.ammo ) { //Skill requires stuff equipped in the arrow slot. - if((i=sd->equip_index[EQI_AMMO]) < 0 || !sd->inventory_data[i] ) { - clif_arrow_fail(sd,0); - return 0; - } else if( sd->status.inventory[i].amount < require.ammo_qty ) { - char e_msg[100]; - sprintf(e_msg,"Skill Failed. [%s] requires %dx %s.", - skill_get_desc(skill_id), - require.ammo_qty, - itemdb_jname(sd->status.inventory[i].nameid)); - clif_colormes(sd,COLOR_RED,e_msg); - return 0; - } - if (!(require.ammo&1<<sd->inventory_data[i]->look)) { //Ammo type check. Send the "wrong weapon type" message - //which is the closest we have to wrong ammo type. [Skotlex] - clif_arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex] - //clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0); - return 0; - } - } - - for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i ) { - if( !require.itemid[i] ) - continue; - index[i] = pc_search_inventory(sd,require.itemid[i]); - if( index[i] < 0 || sd->status.inventory[index[i]].amount < require.amount[i] ) { - if( require.itemid[i] == ITEMID_RED_GEMSTONE ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_REDJAMSTONE,0);// red gemstone required - else if( require.itemid[i] == ITEMID_BLUE_GEMSTONE ) - clif_skill_fail(sd,skill_id,USESKILL_FAIL_BLUEJAMSTONE,0);// blue gemstone required - else - clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - return 0; - } - } - - return 1; -} - -// type&2: consume items (after skill was used) -// type&1: consume the others (before skill was used) -int skill_consume_requirement( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type) -{ - struct skill_condition req; - - nullpo_ret(sd); - - req = skill_get_requirement(sd,skill_id,skill_lv); - - if( type&1 ) - { - if( skill_id == CG_TAROTCARD || sd->state.autocast ) - req.sp = 0; // TarotCard will consume sp in skill_cast_nodamage_id [Inkfish] - if(req.hp || req.sp) - status_zap(&sd->bl, req.hp, req.sp); - - if(req.spiritball > 0) - pc_delspiritball(sd,req.spiritball,0); - - if(req.zeny > 0) - { - if( skill_id == NJ_ZENYNAGE ) - req.zeny = 0; //Zeny is reduced on skill_attack. - if( sd->status.zeny < req.zeny ) - req.zeny = sd->status.zeny; - pc_payzeny(sd,req.zeny,LOG_TYPE_CONSUME,NULL); - } - } - - if( type&2 ) - { - struct status_change *sc = &sd->sc; - int n,i; - - if( !sc->count ) - sc = NULL; - - for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i ) - { - if( !req.itemid[i] ) - continue; - - if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD ) - continue; //Gemstones are checked, but not substracted from inventory. - - switch( skill_id ){ - case SA_SEISMICWEAPON: - if( sc && sc->data[SC_UPHEAVAL_OPTION] && rnd()%100 < 50 ) - continue; - break; - case SA_FLAMELAUNCHER: - case SA_VOLCANO: - if( sc && sc->data[SC_TROPIC_OPTION] && rnd()%100 < 50 ) - continue; - break; - case SA_FROSTWEAPON: - case SA_DELUGE: - if( sc && sc->data[SC_CHILLY_AIR_OPTION] && rnd()%100 < 50 ) - continue; - break; - case SA_LIGHTNINGLOADER: - case SA_VIOLENTGALE: - if( sc && sc->data[SC_WILD_STORM_OPTION] && rnd()%100 < 50 ) - continue; - break; - } - - if( (n = pc_search_inventory(sd,req.itemid[i])) >= 0 ) - pc_delitem(sd,n,req.amount[i],0,1,LOG_TYPE_CONSUME); - } - } - - return 1; -} - -struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) -{ - struct skill_condition req; - struct status_data *status; - struct status_change *sc; - int i,hp_rate,sp_rate, sp_skill_rate_bonus = 100; - uint16 idx; - - memset(&req,0,sizeof(req)); - - if( !sd ) - return req; - - if( sd->skillitem == skill_id ) - return req; // Item skills and Hocus-Pocus don't have requirements.[Inkfish] - - sc = &sd->sc; - if( !sc->count ) - sc = NULL; - - switch( skill_id ) - { // Turn off check. - case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD: - case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL: - case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN: - case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case KO_YAMIKUMO: - if( sc && sc->data[status_skill2sc(skill_id)] ) - return req; - } - - idx = skill_get_index(skill_id); - if( idx == 0 ) // invalid skill id - return req; - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) - return req; - - status = &sd->battle_status; - - req.hp = skill_db[idx].hp[skill_lv-1]; - hp_rate = skill_db[idx].hp_rate[skill_lv-1]; - if(hp_rate > 0) - req.hp += (status->hp * hp_rate)/100; - else - req.hp += (status->max_hp * (-hp_rate))/100; - - req.sp = skill_db[idx].sp[skill_lv-1]; - if((sd->skill_id_old == BD_ENCORE) && skill_id == sd->skill_id_dance) - req.sp /= 2; - sp_rate = skill_db[idx].sp_rate[skill_lv-1]; - if(sp_rate > 0) - req.sp += (status->sp * sp_rate)/100; - else - req.sp += (status->max_sp * (-sp_rate))/100; - if( sd->dsprate != 100 ) - req.sp = req.sp * sd->dsprate / 100; - - ARR_FIND(0, ARRAYLENGTH(sd->skillusesprate), i, sd->skillusesprate[i].id == skill_id); - if( i < ARRAYLENGTH(sd->skillusesprate) ) - sp_skill_rate_bonus += sd->skillusesprate[i].val; - ARR_FIND(0, ARRAYLENGTH(sd->skillusesp), i, sd->skillusesp[i].id == skill_id); - if( i < ARRAYLENGTH(sd->skillusesp) ) - req.sp -= sd->skillusesp[i].val; - - req.sp = cap_value(req.sp * sp_skill_rate_bonus / 100, 0, SHRT_MAX); - - if( sc ) { - if( sc->data[SC__LAZINESS] ) - req.sp += req.sp + sc->data[SC__LAZINESS]->val1 * 10; - if (sc->data[SC_UNLIMITEDHUMMINGVOICE]) - req.sp += req.sp * sc->data[SC_UNLIMITEDHUMMINGVOICE]->val2 / 100; - if( sc->data[SC_RECOGNIZEDSPELL] ) - req.sp += req.sp / 4; - } - - req.zeny = skill_db[idx].zeny[skill_lv-1]; - - if( sc && sc->data[SC__UNLUCKY] ) - req.zeny += sc->data[SC__UNLUCKY]->val1 * 500; - - req.spiritball = skill_db[idx].spiritball[skill_lv-1]; - - req.state = skill_db[idx].state; - - req.mhp = skill_db[idx].mhp[skill_lv-1]; - - req.weapon = skill_db[idx].weapon; - - req.ammo_qty = skill_db[idx].ammo_qty[skill_lv-1]; - if (req.ammo_qty) - req.ammo = skill_db[idx].ammo; - - if (!req.ammo && skill_id && skill_isammotype(sd, skill_id)) - { //Assume this skill is using the weapon, therefore it requires arrows. - req.ammo = 0xFFFFFFFF; //Enable use on all ammo types. - req.ammo_qty = 1; - } - - for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) { - if( (skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != skill_lv%11 - 1 ) - continue; - - switch( skill_id ) { - case AM_CALLHOMUN: - 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 (skill_lv <= 5) // no gems required at level 1-5 - continue; - break; - case AB_ADORAMUS: - if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2) ) - continue; - break; - case WL_COMET: - if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0) ) - continue; - break; - case GN_FIRE_EXPANSION: - if( i < 5 ) - continue; - break; - case SO_SUMMON_AGNI: - case SO_SUMMON_AQUA: - case SO_SUMMON_VENTUS: - case SO_SUMMON_TERA: - case SO_WATER_INSIGNIA: - case SO_FIRE_INSIGNIA: - case SO_WIND_INSIGNIA: - case SO_EARTH_INSIGNIA: - if( i < 3 ) - continue; - break; - } - - req.itemid[i] = skill_db[idx].itemid[i]; - req.amount[i] = skill_db[idx].amount[i]; - - if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN ) - { - 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; - } - if(sc && sc->data[SC_INTOABYSS]) - { - if( skill_id != SA_ABRACADABRA ) - req.itemid[i] = req.amount[i] = 0; - else if( --req.amount[i] < 1 ) - req.amount[i] = 1; // Hocus Pocus allways use at least 1 gem - } - } - if( skill_id >= HT_SKIDTRAP && skill_id <= HT_TALKIEBOX && pc_checkskill(sd, RA_RESEARCHTRAP) > 0){ - int16 itIndex; - if( (itIndex = pc_search_inventory(sd,req.itemid[i])) < 0 || ( itIndex >= 0 && sd->status.inventory[itIndex].amount < req.amount[i] ) ){ - req.itemid[i] = ITEMID_TRAP_ALLOY; - req.amount[i] = 1; - } - break; - } - } - - /* requirements are level-dependent */ - switch( skill_id ) { - case NC_SHAPESHIFT: - case GN_FIRE_EXPANSION: - case SO_SUMMON_AGNI: - case SO_SUMMON_AQUA: - case SO_SUMMON_VENTUS: - case SO_SUMMON_TERA: - case SO_WATER_INSIGNIA: - case SO_FIRE_INSIGNIA: - case SO_WIND_INSIGNIA: - case SO_EARTH_INSIGNIA: - req.itemid[skill_lv-1] = skill_db[idx].itemid[skill_lv-1]; - req.amount[skill_lv-1] = skill_db[idx].amount[skill_lv-1]; - break; - } - - // Check for cost reductions due to skills & SCs - switch(skill_id) { - case MC_MAMMONITE: - if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0) - req.zeny -= req.zeny*10/100; - break; - case AL_HOLYLIGHT: - if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_PRIEST) - req.sp *= 5; - break; - case SL_SMA: - case SL_STUN: - case SL_STIN: - { - int kaina_lv = pc_checkskill(sd,SL_KAINA); - - if(kaina_lv==0 || sd->status.base_level<70) - break; - if(sd->status.base_level>=90) - req.sp -= req.sp*7*kaina_lv/100; - else if(sd->status.base_level>=80) - req.sp -= req.sp*5*kaina_lv/100; - else if(sd->status.base_level>=70) - req.sp -= req.sp*3*kaina_lv/100; - } - break; - case MO_TRIPLEATTACK: - case MO_CHAINCOMBO: - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK) - req.sp -= req.sp*25/100; //FIXME: Need real data. this is a custom value. - break; - case MO_BODYRELOCATION: - if( sc && sc->data[SC_EXPLOSIONSPIRITS] ) - req.spiritball = 0; - break; - case MO_EXTREMITYFIST: - if( sc ) - { - if( sc->data[SC_BLADESTOP] ) - req.spiritball--; - else if( sc->data[SC_COMBO] ) - { - switch( sc->data[SC_COMBO]->val1 ) - { - case MO_COMBOFINISH: - req.spiritball = 4; - break; - case CH_TIGERFIST: - req.spiritball = 3; - break; - case CH_CHAINCRUSH: //It should consume whatever is left as long as it's at least 1. - req.spiritball = sd->spiritball?sd->spiritball:1; - break; - } - }else if( sc->data[SC_RAISINGDRAGON] && sd->spiritball > 5) - req.spiritball = sd->spiritball; // must consume all regardless of the amount required - } - break; - case SR_RAMPAGEBLASTER: - req.spiritball = sd->spiritball?sd->spiritball:15; - break; - case SR_GATEOFHELL: - if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE ) - req.sp -= req.sp * 10 / 100; - break; - case SO_SUMMON_AGNI: - case SO_SUMMON_AQUA: - case SO_SUMMON_VENTUS: - case SO_SUMMON_TERA: - req.sp -= req.sp * (5 + 5 * pc_checkskill(sd,SO_EL_SYMPATHY)) / 100; - break; - case SO_PSYCHIC_WAVE: - if( sc && sc->data[SC_BLAST_OPTION] ) - req.sp += req.sp * 150 / 100; - break; - } - - return req; -} - -/*========================================== - * Does cast-time reductions based on dex, item bonuses and config setting - *------------------------------------------*/ -int skill_castfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { - int time = skill_get_cast(skill_id, skill_lv); - - nullpo_ret(bl); -#ifndef RENEWAL_CAST - { - struct map_session_data *sd; - - sd = BL_CAST(BL_PC, bl); - - // calculate base cast time (reduced by dex) - if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) { - int scale = battle_config.castrate_dex_scale - status_get_dex(bl); - if( scale > 0 ) // not instant cast - time = time * scale / battle_config.castrate_dex_scale; - else - return 0; // instant cast - } - - // calculate cast time reduced by item/card bonuses - if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd ) - { - int i; - if( sd->castrate != 100 ) - time = time * sd->castrate / 100; - for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ ) - { - if( sd->skillcast[i].id == skill_id ) - { - time+= time * sd->skillcast[i].val / 100; - break; - } - } - } - - } -#endif - // config cast time multiplier - if (battle_config.cast_rate != 100) - time = time * battle_config.cast_rate / 100; - // return final cast time - time = max(time, 0); - -// ShowInfo("Castime castfix = %d\n",time); - return time; -} - -/*========================================== - * Does cast-time reductions based on sc data. - *------------------------------------------*/ -int skill_castfix_sc (struct block_list *bl, int time) -{ - struct status_change *sc = status_get_sc(bl); - - if( time < 0 ) - return 0; - - if (sc && sc->count) { - if (sc->data[SC_SLOWCAST]) - time += time * sc->data[SC_SLOWCAST]->val2 / 100; - if (sc->data[SC_PARALYSIS]) - time += sc->data[SC_PARALYSIS]->val3; - 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 (sc->data[SC_IZAYOI]) - time -= time * 50 / 100; - } - time = max(time, 0); - -// ShowInfo("Castime castfix_sc = %d\n",time); - return time; -} -#ifdef RENEWAL_CAST -int skill_vfcastfix (struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv) -{ - struct status_change *sc = status_get_sc(bl); - struct map_session_data *sd = BL_CAST(BL_PC,bl); - int fixed = skill_get_fixed_cast(skill_id, skill_lv), fixcast_r = 0, varcast_r = 0, i = 0; - - if( time < 0 ) - return 0; - - if( fixed == 0 ){ - fixed = (int)time * 20 / 100; // fixed time - time = time * 80 / 100; // variable time - }else if( fixed < 0 ) // no fixed cast time - fixed = 0; - - if(sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){ // Increases/Decreases fixed/variable cast time of a skill by item/card bonuses. - if( sd->bonus.varcastrate < 0 ) - VARCAST_REDUCTION(sd->bonus.varcastrate); - for (i = 0; i < ARRAYLENGTH(sd->skillfixcast) && sd->skillfixcast[i].id; i++) - if (sd->skillfixcast[i].id == skill_id){ // bonus2 bSkillFixedCast - fixed += sd->skillfixcast[i].val; - break; - } - for( i = 0; i < ARRAYLENGTH(sd->skillvarcast) && sd->skillvarcast[i].id; i++ ) - if( sd->skillvarcast[i].id == skill_id ){ // bonus2 bSkillVariableCast - time += sd->skillvarcast[i].val; - break; - } - for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ ) - if( sd->skillcast[i].id == skill_id ){ // bonus2 bVariableCastrate - if( (i=sd->skillcast[i].val) < 0) - VARCAST_REDUCTION(i); - break; - } - } - - if (sc && sc->count && !(skill_get_castnodex(skill_id, skill_lv)&2) ) { - // All variable cast additive bonuses must come first - if (sc->data[SC_SLOWCAST]) - VARCAST_REDUCTION(-sc->data[SC_SLOWCAST]->val2); - - // Variable cast reduction bonuses - if (sc->data[SC_SUFFRAGIUM]) { - VARCAST_REDUCTION(sc->data[SC_SUFFRAGIUM]->val2); - status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER); - } - if (sc->data[SC_MEMORIZE]) { - VARCAST_REDUCTION(50); - if ((--sc->data[SC_MEMORIZE]->val2) <= 0) - status_change_end(bl, SC_MEMORIZE, INVALID_TIMER); - } - if (sc->data[SC_POEMBRAGI]) - VARCAST_REDUCTION(sc->data[SC_POEMBRAGI]->val2); - if (sc->data[SC_IZAYOI]) - VARCAST_REDUCTION(50); - if (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WATER)) - VARCAST_REDUCTION(30); //Reduces 30% Variable Cast Time of Water spells. - // Fixed cast reduction bonuses - if( sc->data[SC__LAZINESS] ) - fixcast_r = max(fixcast_r, sc->data[SC__LAZINESS]->val2); - if( sc->data[SC_SECRAMENT] ) - fixcast_r = max(fixcast_r, sc->data[SC_SECRAMENT]->val2); - if( sd && ( skill_lv = pc_checkskill(sd, WL_RADIUS) ) && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP ) - fixcast_r = max(fixcast_r, 5 + skill_lv * 5); - // Fixed cast non percentage bonuses - if( sc->data[SC_MANDRAGORA] && (skill_id >= SM_BASH && skill_id <= RETURN_TO_ELDICASTES) ) - fixed += sc->data[SC_MANDRAGORA]->val1 * 1000 / 2; - if (sc->data[SC_IZAYOI] && (skill_id >= NJ_TOBIDOUGU && skill_id <= NJ_ISSEN)) - fixed = 0; - if( sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION] ) - fixed -= 1000; - } - - if( sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){ - VARCAST_REDUCTION( max(sd->bonus.varcastrate, 0) + max(i, 0) ); - fixcast_r = max(fixcast_r, sd->bonus.fixcastrate) + min(sd->bonus.fixcastrate,0); - } - - if( varcast_r < 0 ) // now compute overall factors - time = time * (1 - (float)varcast_r / 100); - if( !(skill_get_castnodex(skill_id, skill_lv)&1) )// reduction from status point - time = (1 - sqrt( ((float)(status_get_dex(bl)*2 + status_get_int(bl)) / battle_config.vcast_stat_scale) )) * time; - // underflow checking/capping - time = max(time, 0) + (1 - (float)min(fixcast_r, 100) / 100) * max(fixed,0); - - return (int)time; -} -#endif - -/*========================================== - * Does delay reductions based on dex/agi, sc data, item bonuses, ... - *------------------------------------------*/ -int skill_delayfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) -{ - int delaynodex = skill_get_delaynodex(skill_id, skill_lv); - int time = skill_get_delay(skill_id, skill_lv); - struct map_session_data *sd; - struct status_change *sc = status_get_sc(bl); - - nullpo_ret(bl); - sd = BL_CAST(BL_PC, bl); - - if (skill_id == SA_ABRACADABRA || skill_id == WM_RANDOMIZESPELL) - return 0; //Will use picked skill's delay. - - if (bl->type&battle_config.no_skill_delay) - return battle_config.min_skill_delay_limit; - - if (time < 0) - time = -time + status_get_amotion(bl); // If set to <0, add to attack motion. - - // Delay reductions - switch (skill_id) { //Monk combo skills have their delay reduced by agi/dex. - case MO_TRIPLEATTACK: - case MO_CHAINCOMBO: - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - case SR_DRAGONCOMBO: - case SR_FALLENEMPIRE: - time -= 4*status_get_agi(bl) - 2*status_get_dex(bl); - break; - case HP_BASILICA: - 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] ) - { - switch (skill_id) { - case CR_SHIELDBOOMERANG: - if (sc->data[SC_SPIRIT]->val2 == SL_CRUSADER) - time /= 2; - break; - case AS_SONICBLOW: - if (!map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && sc->data[SC_SPIRIT]->val2 == SL_ASSASIN) - time /= 2; - break; - } - } - - if (!(delaynodex&2)) - { - if (sc && sc->count) { - if (sc->data[SC_POEMBRAGI]) - time -= time * sc->data[SC_POEMBRAGI]->val3 / 100; - if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WIND)) - time /= 2; // After Delay of Wind element spells reduced by 50%. - } - - } - - if( !(delaynodex&4) && sd && sd->delayrate != 100 ) - time = time * sd->delayrate / 100; - - if (battle_config.delay_rate != 100) - time = time * battle_config.delay_rate / 100; - - //min delay - time = max(time, status_get_amotion(bl)); // Delay can never be below amotion [Playtester] - time = max(time, battle_config.min_skill_delay_limit); - -// ShowInfo("Delay delayfix = %d\n",time); - return time; -} - -/*========================================= - * - *-----------------------------------------*/ -struct square { - int val1[5]; - int val2[5]; -}; - -static void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y) -{ - nullpo_retv(tc); - - if(dir == 0){ - tc->val1[0]=x-2; - tc->val1[1]=x-1; - tc->val1[2]=x; - tc->val1[3]=x+1; - tc->val1[4]=x+2; - tc->val2[0]= - tc->val2[1]= - tc->val2[2]= - tc->val2[3]= - tc->val2[4]=y-1; - } - else if(dir==2){ - tc->val1[0]= - tc->val1[1]= - tc->val1[2]= - tc->val1[3]= - tc->val1[4]=x+1; - tc->val2[0]=y+2; - tc->val2[1]=y+1; - tc->val2[2]=y; - tc->val2[3]=y-1; - tc->val2[4]=y-2; - } - else if(dir==4){ - tc->val1[0]=x-2; - tc->val1[1]=x-1; - tc->val1[2]=x; - tc->val1[3]=x+1; - tc->val1[4]=x+2; - tc->val2[0]= - tc->val2[1]= - tc->val2[2]= - tc->val2[3]= - tc->val2[4]=y+1; - } - else if(dir==6){ - tc->val1[0]= - tc->val1[1]= - tc->val1[2]= - tc->val1[3]= - tc->val1[4]=x-1; - tc->val2[0]=y+2; - tc->val2[1]=y+1; - tc->val2[2]=y; - tc->val2[3]=y-1; - tc->val2[4]=y-2; - } - else if(dir==1){ - tc->val1[0]=x-1; - tc->val1[1]=x; - tc->val1[2]=x+1; - tc->val1[3]=x+2; - tc->val1[4]=x+3; - tc->val2[0]=y-4; - tc->val2[1]=y-3; - tc->val2[2]=y-1; - tc->val2[3]=y; - tc->val2[4]=y+1; - } - else if(dir==3){ - tc->val1[0]=x+3; - tc->val1[1]=x+2; - tc->val1[2]=x+1; - tc->val1[3]=x; - tc->val1[4]=x-1; - tc->val2[0]=y-1; - tc->val2[1]=y; - tc->val2[2]=y+1; - tc->val2[3]=y+2; - tc->val2[4]=y+3; - } - else if(dir==5){ - tc->val1[0]=x+1; - tc->val1[1]=x; - tc->val1[2]=x-1; - tc->val1[3]=x-2; - tc->val1[4]=x-3; - tc->val2[0]=y+3; - tc->val2[1]=y+2; - tc->val2[2]=y+1; - tc->val2[3]=y; - tc->val2[4]=y-1; - } - else if(dir==7){ - tc->val1[0]=x-3; - tc->val1[1]=x-2; - tc->val1[2]=x-1; - tc->val1[3]=x; - tc->val1[4]=x+1; - tc->val2[1]=y; - tc->val2[0]=y+1; - tc->val2[2]=y-1; - tc->val2[3]=y-2; - tc->val2[4]=y-3; - } - -} - -static void skill_brandishspear_dir (struct square* tc, uint8 dir, int are) -{ - int c; - nullpo_retv(tc); - - for( c = 0; c < 5; c++ ) - { - switch( dir ) - { - case 0: tc->val2[c]+=are; break; - case 1: tc->val1[c]-=are; tc->val2[c]+=are; break; - case 2: tc->val1[c]-=are; break; - case 3: tc->val1[c]-=are; tc->val2[c]-=are; break; - case 4: tc->val2[c]-=are; break; - case 5: tc->val1[c]+=are; tc->val2[c]-=are; break; - case 6: tc->val1[c]+=are; break; - case 7: tc->val1[c]+=are; tc->val2[c]+=are; break; - } - } -} - -void skill_brandishspear(struct block_list* src, struct block_list* bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag) -{ - int c,n=4; - uint8 dir = map_calc_dir(src,bl->x,bl->y); - struct square tc; - int x=bl->x,y=bl->y; - skill_brandishspear_first(&tc,dir,x,y); - skill_brandishspear_dir(&tc,dir,4); - skill_area_temp[1] = bl->id; - - if(skill_lv > 9){ - for(c=1;c<4;c++){ - map_foreachincell(skill_area_sub, - bl->m,tc.val1[c],tc.val2[c],BL_CHAR, - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n, - skill_castend_damage_id); - } - } - if(skill_lv > 6){ - skill_brandishspear_dir(&tc,dir,-1); - n--; - }else{ - skill_brandishspear_dir(&tc,dir,-2); - n-=2; - } - - if(skill_lv > 3){ - for(c=0;c<5;c++){ - map_foreachincell(skill_area_sub, - bl->m,tc.val1[c],tc.val2[c],BL_CHAR, - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n, - skill_castend_damage_id); - if(skill_lv > 6 && n==3 && c==4){ - skill_brandishspear_dir(&tc,dir,-1); - n--;c=-1; - } - } - } - for(c=0;c<10;c++){ - if(c==0||c==5) skill_brandishspear_dir(&tc,dir,-1); - map_foreachincell(skill_area_sub, - bl->m,tc.val1[c%5],tc.val2[c%5],BL_CHAR, - src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - } -} - -/*========================================== - * Weapon Repair [Celest/DracoRPG] - *------------------------------------------*/ -void skill_repairweapon (struct map_session_data *sd, int idx) { - int material; - int materials[4] = { 1002, 998, 999, 756 }; - struct item *item; - struct map_session_data *target_sd; - - nullpo_retv(sd); - - if ( !( target_sd = map_id2sd(sd->menuskill_val) ) ) //Failed.... - return; - - if( idx == 0xFFFF ) // No item selected ('Cancel' clicked) - return; - if( idx < 0 || idx >= MAX_INVENTORY ) - return; //Invalid index?? - - item = &target_sd->status.inventory[idx]; - if( item->nameid <= 0 || item->attribute == 0 ) - return; //Again invalid item.... - - if( sd != target_sd && !battle_check_range(&sd->bl,&target_sd->bl, skill_get_range2(&sd->bl, sd->menuskill_id,sd->menuskill_val2) ) ){ - clif_item_repaireffect(sd,idx,1); - return; - } - - if ( target_sd->inventory_data[idx]->type == IT_WEAPON ) - material = materials [ target_sd->inventory_data[idx]->wlv - 1 ]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon - else - material = materials [2]; // Armors consume 1 Steel - if ( pc_search_inventory(sd,material) < 0 ) { - clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0); - return; - } - - clif_skill_nodamage(&sd->bl,&target_sd->bl,sd->menuskill_id,1,1); - - item->attribute = 0;/* clear broken state */ - - clif_equiplist(target_sd); - - pc_delitem(sd,pc_search_inventory(sd,material),1,0,0,LOG_TYPE_CONSUME); - - clif_item_repaireffect(sd,idx,0); - - if( sd != target_sd ) - clif_item_repaireffect(target_sd,idx,0); -} - -/*========================================== - * Item Appraisal - *------------------------------------------*/ -void skill_identify (struct map_session_data *sd, int idx) -{ - int flag=1; - - nullpo_retv(sd); - - if(idx >= 0 && idx < MAX_INVENTORY) { - if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){ - flag=0; - sd->status.inventory[idx].identify=1; - } - } - clif_item_identified(sd,idx,flag); -} - -/*========================================== - * Weapon Refine [Celest] - *------------------------------------------*/ -void skill_weaponrefine (struct map_session_data *sd, int idx) -{ - nullpo_retv(sd); - - if (idx >= 0 && idx < MAX_INVENTORY) - { - int i = 0, ep = 0, per; - int material[5] = { 0, 1010, 1011, 984, 984 }; - struct item *item; - struct item_data *ditem = sd->inventory_data[idx]; - item = &sd->status.inventory[idx]; - - if(item->nameid > 0 && ditem->type == IT_WEAPON) - { - if( item->refine >= sd->menuskill_val - || item->refine >= 10 // if it's no longer refineable - || ditem->flag.no_refine // if the item isn't refinable - || (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 ) - { - clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0); - return; - } - - per = status_get_refine_chance(ditem->wlv, (int)item->refine); - per += (((signed int)sd->status.job_level)-50)/2; //Updated per the new kro descriptions. [Skotlex] - - pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER); - if (per > rnd() % 100) { - log_pick_pc(sd, LOG_TYPE_OTHER, -1, item); - item->refine++; - log_pick_pc(sd, LOG_TYPE_OTHER, 1, item); - if(item->equip) { - ep = item->equip; - pc_unequipitem(sd,idx,3); - } - clif_refine(sd->fd,0,idx,item->refine); - clif_delitem(sd,idx,1,3); - clif_additem(sd,idx,1,0); - if (ep) - pc_equipitem(sd,idx,ep); - clif_misceffect(&sd->bl,3); - if(item->refine == 10 && - item->card[0] == CARD0_FORGE && - (int)MakeDWord(item->card[2],item->card[3]) == sd->status.char_id) - { // Fame point system [DracoRPG] - switch(ditem->wlv){ - case 1: - pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point - break; - case 2: - pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point - break; - case 3: - pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point - break; - } - } - } else { - item->refine = 0; - if(item->equip) - pc_unequipitem(sd,idx,3); - clif_refine(sd->fd,1,idx,item->refine); - pc_delitem(sd,idx,1,0,2, LOG_TYPE_OTHER); - clif_misceffect(&sd->bl,2); - clif_emotion(&sd->bl, E_OMG); - } - } - } -} - -/*========================================== - * - *------------------------------------------*/ -int skill_autospell (struct map_session_data *sd, uint16 skill_id) -{ - uint16 skill_lv; - int maxlv=1,lv; - - nullpo_ret(sd); - - skill_lv = sd->menuskill_val; - lv=pc_checkskill(sd,skill_id); - - if(!skill_lv || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance] - - if(skill_id==MG_NAPALMBEAT) maxlv=3; - else if(skill_id==MG_COLDBOLT || skill_id==MG_FIREBOLT || skill_id==MG_LIGHTNINGBOLT){ - if (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SAGE) - maxlv =10; //Soul Linker bonus. [Skotlex] - else if(skill_lv==2) maxlv=1; - else if(skill_lv==3) maxlv=2; - else if(skill_lv>=4) maxlv=3; - } - else if(skill_id==MG_SOULSTRIKE){ - if(skill_lv==5) maxlv=1; - else if(skill_lv==6) maxlv=2; - else if(skill_lv>=7) maxlv=3; - } - else if(skill_id==MG_FIREBALL){ - if(skill_lv==8) maxlv=1; - else if(skill_lv>=9) maxlv=2; - } - else if(skill_id==MG_FROSTDIVER) maxlv=1; - else return 0; - - if(maxlv > lv) - maxlv = lv; - - sc_start4(&sd->bl,SC_AUTOSPELL,100,skill_lv,skill_id,maxlv,0, - skill_get_time(SA_AUTOSPELL,skill_lv)); - return 0; -} - -/*========================================== - * Sitting skills functions. - *------------------------------------------*/ -static int skill_sit_count (struct block_list *bl, va_list ap) -{ - struct map_session_data *sd; - int type =va_arg(ap,int); - sd=(struct map_session_data*)bl; - - if(!pc_issit(sd)) - return 0; - - if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0) - return 1; - - if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0)) - return 1; - - return 0; -} - -static int skill_sit_in (struct block_list *bl, va_list ap) -{ - struct map_session_data *sd; - int type =va_arg(ap,int); - - sd=(struct map_session_data*)bl; - - if(!pc_issit(sd)) - return 0; - - if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0) - sd->state.gangsterparadise=1; - - if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 )) - { - sd->state.rest=1; - status_calc_regen(bl, &sd->battle_status, &sd->regen); - status_calc_regen_rate(bl, &sd->regen, &sd->sc); - } - - return 0; -} - -static int skill_sit_out (struct block_list *bl, va_list ap) -{ - struct map_session_data *sd; - int type =va_arg(ap,int); - sd=(struct map_session_data*)bl; - if(sd->state.gangsterparadise && type&1) - sd->state.gangsterparadise=0; - if(sd->state.rest && type&2) { - sd->state.rest=0; - status_calc_regen(bl, &sd->battle_status, &sd->regen); - status_calc_regen_rate(bl, &sd->regen, &sd->sc); - } - return 0; -} - -int skill_sit (struct map_session_data *sd, int type) -{ - int flag = 0; - int range = 0, lv; - nullpo_ret(sd); - - - if((lv = pc_checkskill(sd,RG_GANGSTER)) > 0) { - flag|=1; - range = skill_get_splash(RG_GANGSTER, lv); - } - if((lv = pc_checkskill(sd,TK_HPTIME)) > 0) { - flag|=2; - range = skill_get_splash(TK_HPTIME, lv); - } - else if ((lv = pc_checkskill(sd,TK_SPTIME)) > 0) { - flag|=2; - range = skill_get_splash(TK_SPTIME, lv); - } - - if( type ) { - clif_status_load(&sd->bl,SI_SIT,1); - } else { - clif_status_load(&sd->bl,SI_SIT,0); - } - - if (!flag) return 0; - - if(type) { - if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) > 1) - map_foreachinrange(skill_sit_in,&sd->bl, range, BL_PC, flag); - } else { - if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) < 2) - map_foreachinrange(skill_sit_out,&sd->bl, range, BL_PC, flag); - } - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_frostjoke_scream (struct block_list *bl, va_list ap) -{ - struct block_list *src; - uint16 skill_id,skill_lv; - unsigned int tick; - - nullpo_ret(bl); - nullpo_ret(src=va_arg(ap,struct block_list*)); - - skill_id=va_arg(ap,int); - skill_lv=va_arg(ap,int); - if(!skill_lv) return 0; - tick=va_arg(ap,unsigned int); - - if (src == bl || status_isdead(bl)) - return 0; - if (bl->type == BL_PC) { - struct map_session_data *sd = (struct map_session_data *)bl; - 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) - skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick); - else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rnd()%100 < 10) - skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick); - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -static void skill_unitsetmapcell (struct skill_unit *src, uint16 skill_id, uint16 skill_lv, cell_t cell, bool flag) -{ - int range = skill_get_unit_range(skill_id,skill_lv); - int x,y; - - for( y = src->bl.y - range; y <= src->bl.y + range; ++y ) - for( x = src->bl.x - range; x <= src->bl.x + range; ++x ) - map_setcell(src->bl.m, x, y, cell, flag); -} - -/*========================================== - * - *------------------------------------------*/ -int skill_attack_area (struct block_list *bl, va_list ap) -{ - struct block_list *src,*dsrc; - int atk_type,skill_id,skill_lv,flag,type; - unsigned int tick; - - if(status_isdead(bl)) - return 0; - - atk_type = va_arg(ap,int); - src=va_arg(ap,struct block_list*); - dsrc=va_arg(ap,struct block_list*); - skill_id=va_arg(ap,int); - skill_lv=va_arg(ap,int); - tick=va_arg(ap,unsigned int); - flag=va_arg(ap,int); - type=va_arg(ap,int); - - - if (skill_area_temp[1] == bl->id) //This is the target of the skill, do a full attack and skip target checks. - return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag); - - if(battle_check_target(dsrc,bl,type) <= 0 || - !status_check_skilluse(NULL, bl, skill_id, 2)) - return 0; - - - switch (skill_id) { - case WZ_FROSTNOVA: //Skills that don't require the animation to be removed - case NPC_ACIDBREATH: - case NPC_DARKNESSBREATH: - case NPC_FIREBREATH: - case NPC_ICEBREATH: - case NPC_THUNDERBREATH: - return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag); - default: - //Area-splash, disable skill animation. - return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION); - } -} -/*========================================== - * - *------------------------------------------*/ -int skill_clear_group (struct block_list *bl, int flag) -{ - struct unit_data *ud = unit_bl2ud(bl); - struct skill_unit_group *group[MAX_SKILLUNITGROUP]; - int i, count=0; - - nullpo_ret(bl); - if (!ud) return 0; - - //All groups to be deleted are first stored on an array since the array elements shift around when you delete them. [Skotlex] - for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++) - { - switch (ud->skillunit[i]->skill_id) { - case SA_DELUGE: - case SA_VOLCANO: - case SA_VIOLENTGALE: - case SA_LANDPROTECTOR: - case NJ_SUITON: - case NJ_KAENSIN: - if (flag&1) - group[count++]= ud->skillunit[i]; - break; - case SO_WARMER: - if( flag&8 ) - group[count++]= ud->skillunit[i]; - break; - case SC_BLOODYLUST: - if (flag & 32) - group[count++] = ud->skillunit[i]; - break; - default: - if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP) - group[count++]= ud->skillunit[i]; - break; - } - - } - for (i=0;i<count;i++) - skill_delunitgroup(group[i]); - return count; -} - -/*========================================== - * Returns the first element field found [Skotlex] - *------------------------------------------*/ -struct skill_unit_group *skill_locate_element_field(struct block_list *bl) -{ - struct unit_data *ud = unit_bl2ud(bl); - int i; - nullpo_ret(bl); - if (!ud) return NULL; - - for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++) { - switch (ud->skillunit[i]->skill_id) { - case SA_DELUGE: - case SA_VOLCANO: - case SA_VIOLENTGALE: - case SA_LANDPROTECTOR: - case NJ_SUITON: - case SO_WARMER: - case SC_BLOODYLUST: - return ud->skillunit[i]; - } - } - return NULL; -} - -// for graffiti cleaner [Valaris] -int skill_graffitiremover (struct block_list *bl, va_list ap) -{ - struct skill_unit *unit=NULL; - - nullpo_ret(bl); - nullpo_ret(ap); - - if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL) - return 0; - - if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI)) - skill_delunit(unit); - - return 0; -} - -int skill_greed (struct block_list *bl, va_list ap) -{ - struct block_list *src; - struct map_session_data *sd=NULL; - struct flooritem_data *fitem=NULL; - - nullpo_ret(bl); - nullpo_ret(src = va_arg(ap, struct block_list *)); - - if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl)) - pc_takeitem(sd, fitem); - - return 0; -} -//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->group->limit = DIFF_TICK(gettick(),unit->group->tick) + - (unit_id == UNT_TALKIEBOX ? 5000 : (unit_id == UNT_CLUSTERBOMB || unit_id == UNT_ICEBOUNDTRAP? 2500 : 1500) ); - break; - } - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -static int skill_cell_overlap(struct block_list *bl, va_list ap) -{ - uint16 skill_id; - int *alive; - struct skill_unit *unit; - - skill_id = va_arg(ap,int); - alive = va_arg(ap,int *); - unit = (struct skill_unit *)bl; - - if (unit == NULL || unit->group == NULL || (*alive) == 0) - return 0; - - switch (skill_id) { - case SA_LANDPROTECTOR: - if( unit->group->skill_id == SA_LANDPROTECTOR ) {//Check for offensive Land Protector to delete both. [Skotlex] - (*alive) = 0; - skill_delunit(unit); - return 1; - } - if( !(skill_get_inf2(unit->group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP)) ) { //It deletes everything except songs/dances and traps - skill_delunit(unit); - return 1; - } - break; - case HW_GANBANTEIN: - case LG_EARTHDRIVE: - if( !(unit->group->state.song_dance&0x1) ) {// Don't touch song/dance. - skill_delunit(unit); - return 1; - } - break; - case SA_VOLCANO: - case SA_DELUGE: - case SA_VIOLENTGALE: -// The official implementation makes them fail to appear when casted on top of ANYTHING -// but I wonder if they didn't actually meant to fail when casted on top of each other? -// hence, I leave the alternate implementation here, commented. [Skotlex] - if (unit->range <= 0) - { - (*alive) = 0; - return 1; - } -/* - switch (unit->group->skill_id) - { //These cannot override each other. - case SA_VOLCANO: - case SA_DELUGE: - case SA_VIOLENTGALE: - (*alive) = 0; - return 1; - } -*/ - break; - case PF_FOGWALL: - switch(unit->group->skill_id) { - case SA_VOLCANO: //Can't be placed on top of these - case SA_VIOLENTGALE: - (*alive) = 0; - return 1; - case SA_DELUGE: - case NJ_SUITON: - //Cheap 'hack' to notify the calling function that duration should be doubled [Skotlex] - (*alive) = 2; - break; - } - break; - case HP_BASILICA: - if (unit->group->skill_id == HP_BASILICA) - { //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex] - (*alive) = 0; - return 1; - } - break; - case GN_CRAZYWEED_ATK: - switch(unit->group->unit_id){ //TODO: look for other ground skills that are affected. - case UNT_WALLOFTHORN: - case UNT_THORNS_TRAP: - case UNT_BLOODYLUST: - case UNT_CHAOSPANIC: - case UNT_MAELSTROM: - case UNT_FIREPILLAR_ACTIVE: - case UNT_LANDPROTECTOR: - case UNT_VOLCANO: - case UNT_DELUGE: - case UNT_VIOLENTGALE: - case UNT_SAFETYWALL: - case UNT_PNEUMA: - skill_delunit(unit); - return 1; - } - break; - } - - if (unit->group->skill_id == SA_LANDPROTECTOR && !(skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_TRAP))) { //It deletes everything except songs/dances/traps - (*alive) = 0; - return 1; - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap) -{ - struct mob_data* md; - struct unit_data*ud = unit_bl2ud(bl); - struct block_list *from_bl; - struct block_list *to_bl; - md = (struct mob_data*)bl; - from_bl = va_arg(ap,struct block_list *); - to_bl = va_arg(ap,struct block_list *); - - if(ud && ud->target == from_bl->id) - ud->target = to_bl->id; - - if(md->bl.type == BL_MOB && md->target_id == from_bl->id) - md->target_id = to_bl->id; - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -static int skill_trap_splash (struct block_list *bl, va_list ap) -{ - struct block_list *src; - int tick; - struct skill_unit *unit; - struct skill_unit_group *sg; - struct block_list *ss; - src = va_arg(ap,struct block_list *); - unit = (struct skill_unit *)src; - tick = va_arg(ap,int); - - if( !unit->alive || bl->prev == NULL ) - return 0; - - nullpo_ret(sg = unit->group); - nullpo_ret(ss = map_id2bl(sg->src_id)); - - if(battle_check_target(src,bl,sg->target_flag) <= 0) - return 0; - - switch(sg->unit_id){ - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,ATK_DEF,tick); - break; - case UNT_GROUNDDRIFT_WIND: - if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) - sc_start(bl,SC_STUN,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case UNT_GROUNDDRIFT_DARK: - if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) - sc_start(bl,SC_BLIND,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case UNT_GROUNDDRIFT_POISON: - if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) - sc_start(bl,SC_POISON,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case UNT_GROUNDDRIFT_WATER: - if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1)) - sc_start(bl,SC_FREEZE,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case UNT_GROUNDDRIFT_FIRE: - 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( ss != bl ) - skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1|SD_LEVEL); - break; - case UNT_MAGENTATRAP: - case UNT_COBALTTRAP: - case UNT_MAIZETRAP: - case UNT_VERDURETRAP: - if( bl->type != BL_PC && !is_boss(bl) ) - sc_start2(bl,SC_ELEMENTALCHANGE,100,sg->skill_lv,skill_get_ele(sg->skill_id,sg->skill_lv),skill_get_time2(sg->skill_id,sg->skill_lv)); - break; - case UNT_REVERBERATION: - skill_addtimerskill(ss,tick+50,bl->id,0,0,WM_REVERBERATION_MELEE,sg->skill_lv,BF_WEAPON,0); // for proper skill delay animation when use with Dominion Impulse - skill_addtimerskill(ss,tick+250,bl->id,0,0,WM_REVERBERATION_MAGIC,sg->skill_lv,BF_MAGIC,0); - break; - default: - skill_attack(skill_get_type(sg->skill_id),ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - } - return 1; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_enchant_elemental_end (struct block_list *bl, int type) -{ - struct status_change *sc; - const enum sc_type scs[] = { SC_ENCPOISON, SC_ASPERSIO, SC_FIREWEAPON, SC_WATERWEAPON, SC_WINDWEAPON, SC_EARTHWEAPON, SC_SHADOWWEAPON, SC_GHOSTWEAPON, SC_ENCHANTARMS, SC_EXEEDBREAK }; - int i; - nullpo_ret(bl); - nullpo_ret(sc= status_get_sc(bl)); - - if (!sc->count) return 0; - - for (i = 0; i < ARRAYLENGTH(scs); i++) - if (type != scs[i] && sc->data[scs[i]]) - status_change_end(bl, scs[i], INVALID_TIMER); - - return 0; -} - -bool skill_check_cloaking(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; - - if( (bl->type == BL_PC && battle_config.pc_cloak_check_type&1) - || (bl->type != BL_PC && battle_config.monster_cloak_check_type&1) ) - { //Check for walls. - int i; - 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 cloaking. - status_change_end(bl, SC_CLOAKING, INVALID_TIMER); - else - if( sce->val4&1 ) - { //Remove wall bonus - sce->val4&=~1; - status_calc_bl(bl,SCB_SPEED); - } - } - else - { - if( !(sce->val4&1) ) - { //Add wall speed bonus - sce->val4|=1; - status_calc_bl(bl,SCB_SPEED); - } - } - } - - 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; - - if( bl->type == BL_PC ) - { //Check for walls. - int i; - 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, INVALID_TIMER); - else - if( sce->val3&1 ) - { //Remove wall bonus - sce->val3&=~1; - status_calc_bl(bl,SCB_SPEED); - } - } - } - - return wall; -} - -/*========================================== - * - *------------------------------------------*/ -struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2) -{ - struct skill_unit *unit; - - nullpo_retr(NULL, group); - nullpo_retr(NULL, group->unit); // crash-protection against poor coding - nullpo_retr(NULL, unit=&group->unit[idx]); - - if(!unit->alive) - group->alive_count++; - - unit->bl.id=map_get_new_object_id(); - unit->bl.type=BL_SKILL; - unit->bl.m=group->map; - unit->bl.x=x; - unit->bl.y=y; - unit->group=group; - unit->alive=1; - unit->val1=val1; - unit->val2=val2; - - idb_put(skillunit_db, unit->bl.id, unit); - map_addiddb(&unit->bl); - map_addblock(&unit->bl); - - // perform oninit actions - switch (group->skill_id) { - case WZ_ICEWALL: - map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,5); - clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,5,AREA); - skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,true); - map[unit->bl.m].icewall_num++; - break; - case SA_LANDPROTECTOR: - skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,true); - break; - case HP_BASILICA: - skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,true); - break; - case SC_MAELSTROM: - skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,true); - break; - default: - if (group->state.song_dance&0x1) //Check for dissonance. - skill_dance_overlap(unit, 1); - break; - } - - clif_skill_setunit(unit); - - return unit; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_delunit (struct skill_unit* unit) -{ - struct skill_unit_group *group; - - nullpo_ret(unit); - if( !unit->alive ) - return 0; - unit->alive=0; - - nullpo_ret(group=unit->group); - - if( group->state.song_dance&0x1 ) //Cancel dissonance effect. - skill_dance_overlap(unit, 0); - - // invoke onout event - if( !unit->range ) - map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4); - - // perform ondelete actions - switch (group->skill_id) { - case HT_ANKLESNARE: { - struct block_list* target = map_id2bl(group->val2); - if( target ) - status_change_end(target, SC_ANKLE, INVALID_TIMER); - } - break; - case WZ_ICEWALL: - map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,unit->val2); - clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,ALL_SAMEMAP); // hack to avoid clientside cell bug - skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,false); - map[unit->bl.m].icewall_num--; - break; - case SA_LANDPROTECTOR: - skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,false); - break; - case HP_BASILICA: - skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,false); - break; - case RA_ELECTRICSHOCKER: { - struct block_list* target = map_id2bl(group->val2); - if( target ) - status_change_end(target, SC_ELECTRICSHOCKER, INVALID_TIMER); - } - break; - case SC_MAELSTROM: - skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,false); - break; - case SC_MANHOLE: // Note : Removing the unit don't remove the status (official info) - if( group->val2 ) { // Someone Traped - struct status_change *tsc = status_get_sc( map_id2bl(group->val2)); - if( tsc && tsc->data[SC__MANHOLE] ) - tsc->data[SC__MANHOLE]->val4 = 0; // Remove the Unit ID - } - break; - } - - clif_skill_delunit(unit); - - unit->group=NULL; - map_delblock(&unit->bl); // don't free yet - map_deliddb(&unit->bl); - idb_remove(skillunit_db, unit->bl.id); - if(--group->alive_count==0) - skill_delunitgroup(group); - - return 0; -} -/*========================================== - * - *------------------------------------------*/ -static DBMap* group_db = NULL;// int group_id -> struct skill_unit_group* - -/// Returns the target skill_unit_group or NULL if not found. -struct skill_unit_group* skill_id2group(int group_id) -{ - return (struct skill_unit_group*)idb_get(group_db, group_id); -} - - -static int skill_unit_group_newid = MAX_SKILL_DB; - -/// Returns a new group_id that isn't being used in group_db. -/// Fatal error if nothing is available. -static int skill_get_new_group_id(void) -{ - if( skill_unit_group_newid >= MAX_SKILL_DB && skill_id2group(skill_unit_group_newid) == NULL ) - return skill_unit_group_newid++;// available - {// find next id - int base_id = skill_unit_group_newid; - while( base_id != ++skill_unit_group_newid ) - { - if( skill_unit_group_newid < MAX_SKILL_DB ) - skill_unit_group_newid = MAX_SKILL_DB; - if( skill_id2group(skill_unit_group_newid) == NULL ) - return skill_unit_group_newid++;// available - } - // full loop, nothing available - ShowFatalError("skill_get_new_group_id: All ids are taken. Exiting..."); - exit(1); - } -} - -struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval) -{ - struct unit_data* ud = unit_bl2ud( src ); - struct skill_unit_group* group; - int i; - - if(!(skill_id && skill_lv)) return 0; - - nullpo_retr(NULL, src); - nullpo_retr(NULL, ud); - - // find a free spot to store the new unit group - ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == NULL ); - if(i == MAX_SKILLUNITGROUP) - { - // array is full, make room by discarding oldest group - int j=0; - unsigned maxdiff=0,x,tick=gettick(); - for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++) - if((x=DIFF_TICK(tick,ud->skillunit[i]->tick))>maxdiff){ - maxdiff=x; - j=i; - } - skill_delunitgroup(ud->skillunit[j]); - //Since elements must have shifted, we use the last slot. - i = MAX_SKILLUNITGROUP-1; - } - - group = ers_alloc(skill_unit_ers, struct skill_unit_group); - group->src_id = src->id; - group->party_id = status_get_party_id(src); - group->guild_id = status_get_guild_id(src); - group->bg_id = bg_team_get_id(src); - group->group_id = skill_get_new_group_id(); - group->unit = (struct skill_unit *)aCalloc(count,sizeof(struct skill_unit)); - group->unit_count = count; - group->alive_count = 0; - group->val1 = 0; - group->val2 = 0; - group->val3 = 0; - group->skill_id = skill_id; - group->skill_lv = skill_lv; - group->unit_id = unit_id; - group->map = src->m; - group->limit = limit; - group->interval = interval; - group->tick = gettick(); - group->valstr = NULL; - - ud->skillunit[i] = group; - - if (skill_id == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex] - group->tick += 1500; - - idb_put(group_db, group->group_id, group); - return group; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func) -{ - struct block_list* src; - struct unit_data *ud; - int i,j; - - if( group == NULL ) - { - ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func); - return 0; - } - - src=map_id2bl(group->src_id); - ud = unit_bl2ud(src); - if(!src || !ud) { - ShowError("skill_delunitgroup: Group's source not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); - return 0; - } - - if( !status_isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) { - switch( group->skill_id ) { - case BA_DISSONANCE: - case BA_POEMBRAGI: - case BA_WHISTLE: - case BA_ASSASSINCROSS: - case BA_APPLEIDUN: - case DC_UGLYDANCE: - case DC_HUMMING: - case DC_DONTFORGETME: - case DC_FORTUNEKISS: - case DC_SERVICEFORYOU: - skill_usave_add(((TBL_PC*)src), group->skill_id, group->skill_lv); - break; - } - } - - if (skill_get_unit_flag(group->skill_id)&(UF_DANCE|UF_SONG|UF_ENSEMBLE)) - { - struct status_change* sc = status_get_sc(src); - if (sc && sc->data[SC_DANCING]) - { - sc->data[SC_DANCING]->val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex] - status_change_end(src, SC_DANCING, INVALID_TIMER); - } - } - - // end Gospel's status change on 'src' - // (needs to be done when the group is deleted by other means than skill deactivation) - if (group->unit_id == UNT_GOSPEL) { - struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_GOSPEL]) { - sc->data[SC_GOSPEL]->val3 = 0; //Remove reference to this group. [Skotlex] - status_change_end(src, SC_GOSPEL, 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,INVALID_TIMER); - } - } - 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,INVALID_TIMER); - } - } - break; - case LG_BANDING: - { - struct status_change *sc = NULL; - if( (sc = status_get_sc(src)) && sc->data[SC_BANDING] ) { - sc->data[SC_BANDING]->val4 = 0; - status_change_end(src,SC_BANDING,INVALID_TIMER); - } - } - break; - } - - if (src->type==BL_PC && group->state.ammo_consume) - battle_consume_ammo((TBL_PC*)src, group->skill_id, group->skill_lv); - - group->alive_count=0; - - // remove all unit cells - if(group->unit != NULL) - for( i = 0; i < group->unit_count; i++ ) - skill_delunit(&group->unit[i]); - - // clear Talkie-box string - if( group->valstr != NULL ) - { - aFree(group->valstr); - group->valstr = NULL; - } - - idb_remove(group_db, group->group_id); - map_freeblock(&group->unit->bl); // schedules deallocation of whole array (HACK) - group->unit=NULL; - group->group_id=0; - group->unit_count=0; - - // locate this group, swap with the last entry and delete it - ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == group ); - ARR_FIND( i, MAX_SKILLUNITGROUP, j, ud->skillunit[j] == NULL ); j--; - if( i < MAX_SKILLUNITGROUP ) - { - ud->skillunit[i] = ud->skillunit[j]; - ud->skillunit[j] = NULL; - ers_free(skill_unit_ers, group); - } - else - ShowError("skill_delunitgroup: Group not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); - - return 1; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_clear_unitgroup (struct block_list *src) -{ - struct unit_data *ud = unit_bl2ud(src); - - nullpo_ret(ud); - - while (ud->skillunit[0]) - skill_delunitgroup(ud->skillunit[0]); - - return 1; -} - -/*========================================== - * - *------------------------------------------*/ -struct skill_unit_group_tickset *skill_unitgrouptickset_search (struct block_list *bl, struct skill_unit_group *group, int tick) -{ - int i,j=-1,k,s,id; - struct unit_data *ud; - struct skill_unit_group_tickset *set; - - nullpo_ret(bl); - if (group->interval==-1) - return NULL; - - ud = unit_bl2ud(bl); - if (!ud) return NULL; - - set = ud->skillunittick; - - if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP) - id = s = group->skill_id; - else - id = s = group->group_id; - - for (i=0; i<MAX_SKILLUNITGROUPTICKSET; i++) { - k = (i+s) % MAX_SKILLUNITGROUPTICKSET; - if (set[k].id == id) - return &set[k]; - else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || set[k].id==0)) - j=k; - } - - if (j == -1) { - ShowWarning ("skill_unitgrouptickset_search: tickset is full\n"); - j = id % MAX_SKILLUNITGROUPTICKSET; - } - - set[j].id = id; - set[j].tick = tick; - return &set[j]; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_unit_timer_sub_onplace (struct block_list* bl, va_list ap) -{ - struct skill_unit* unit = va_arg(ap,struct skill_unit *); - struct skill_unit_group* group = unit->group; - unsigned int tick = va_arg(ap,unsigned int); - - if( !unit->alive || bl->prev == NULL ) - return 0; - - nullpo_ret(group); - - if( !(skill_get_inf2(group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP|INF2_NOLP)) && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) ) - return 0; //AoE skills are ineffective. [Skotlex] - - if( battle_check_target(&unit->bl,bl,group->target_flag) <= 0 ) - return 0; - - skill_unit_onplace_timer(unit,bl,tick); - - return 1; -} - -/** - * @see DBApply - */ -static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap) -{ - struct skill_unit* unit = db_data2ptr(data); - struct skill_unit_group* group = unit->group; - unsigned int tick = va_arg(ap,unsigned int); - bool dissonance; - struct block_list* bl = &unit->bl; - - if( !unit->alive ) - return 0; - - nullpo_ret(group); - - // check for expiration - if( !group->state.guildaura && (DIFF_TICK(tick,group->tick) >= group->limit || DIFF_TICK(tick,group->tick) >= unit->limit) ) - {// skill unit expired (inlined from skill_unit_onlimit()) - switch( group->unit_id ) - { - case UNT_BLASTMINE: -#ifdef RENEWAL - case UNT_CLAYMORETRAP: -#endif - case UNT_GROUNDDRIFT_WIND: - case UNT_GROUNDDRIFT_DARK: - case UNT_GROUNDDRIFT_POISON: - case UNT_GROUNDDRIFT_WATER: - case UNT_GROUNDDRIFT_FIRE: - group->unit_id = UNT_USED_TRAPS; - //clif_changetraplook(bl, UNT_FIREPILLAR_ACTIVE); - group->limit=DIFF_TICK(tick+1500,group->tick); - unit->limit=DIFF_TICK(tick+1500,group->tick); - break; - - case UNT_ANKLESNARE: - case UNT_ELECTRICSHOCKER: - if( group->val2 > 0 ) { - // Used Trap don't returns back to item - skill_delunit(unit); - break; - } - case UNT_SKIDTRAP: - case UNT_LANDMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_FREEZINGTRAP: -#ifndef RENEWAL - case UNT_CLAYMORETRAP: -#endif - 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 ) - { // revert unit back into a trap - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = group->item_id?group->item_id:ITEMID_TRAP; - item_tmp.identify = 1; - map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0); - } - skill_delunit(unit); - } - break; - - case UNT_WARP_ACTIVE: - // warp portal opens (morph to a UNT_WARP_WAITING cell) - group->unit_id = skill_get_unit_id(group->skill_id, 1); // UNT_WARP_WAITING - clif_changelook(&unit->bl, LOOK_BASE, group->unit_id); - // restart timers - group->limit = skill_get_time(group->skill_id,group->skill_lv); - unit->limit = skill_get_time(group->skill_id,group->skill_lv); - // apply effect to all units standing on it - map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1); - break; - - case UNT_CALLFAMILY: - { - struct map_session_data *sd = NULL; - if(group->val1) { - sd = map_charid2sd(group->val1); - group->val1 = 0; - if (sd && !map[sd->bl.m].flag.nowarp) - pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT); - } - if(group->val2) { - sd = map_charid2sd(group->val2); - group->val2 = 0; - if (sd && !map[sd->bl.m].flag.nowarp) - pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT); - } - skill_delunit(unit); - } - break; - - case UNT_REVERBERATION: - if( unit->val1 <= 0 ) { // If it was deactivated. - skill_delunit(unit); - break; - } - clif_changetraplook(bl,UNT_USED_TRAPS); - map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick); - group->limit = DIFF_TICK(tick,group->tick)+1000; - unit->limit = DIFF_TICK(tick,group->tick)+1000; - group->unit_id = UNT_USED_TRAPS; - break; - - case UNT_FEINTBOMB: { - struct block_list *src = map_id2bl(group->src_id); - if( src ) - map_foreachinrange(skill_area_sub, &group->unit->bl, unit->range, splash_target(src), src, SC_FEINTBOMB, group->skill_lv, tick, BCT_ENEMY|SD_ANIMATION|1, skill_castend_damage_id); - skill_delunit(unit); - break; - } - - case UNT_BANDING: - { - struct block_list *src = map_id2bl(group->src_id); - struct status_change *sc; - if( !src || (sc = status_get_sc(src)) == NULL || !sc->data[SC_BANDING] ) - { - skill_delunit(unit); - break; - } - // This unit isn't removed while SC_BANDING is active. - group->limit = DIFF_TICK(tick+group->interval,group->tick); - unit->limit = DIFF_TICK(tick+group->interval,group->tick); - } - break; - - default: - skill_delunit(unit); - } - } - else - {// skill unit is still active - switch( group->unit_id ) - { - case UNT_ICEWALL: - // icewall loses 50 hp every second - unit->val1 -= SKILLUNITTIMER_INTERVAL/20; // trap's hp - if( unit->val1 <= 0 && unit->limit + group->tick > tick + 700 ) - unit->limit = DIFF_TICK(tick+700,group->tick); - break; - case UNT_BLASTMINE: - case UNT_SKIDTRAP: - case UNT_LANDMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_CLAYMORETRAP: - case UNT_FREEZINGTRAP: - case UNT_TALKIEBOX: - case UNT_ANKLESNARE: - if( unit->val1 <= 0 ) { - if( group->unit_id == UNT_ANKLESNARE && group->val2 > 0 ) - skill_delunit(unit); - else { - clif_changetraplook(bl, group->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS); - group->limit = DIFF_TICK(tick, group->tick) + 1500; - group->unit_id = UNT_USED_TRAPS; - } - } - break; - case UNT_REVERBERATION: - if( unit->val1 <= 0 ){ - clif_changetraplook(bl,UNT_USED_TRAPS); - map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick); - group->limit = DIFF_TICK(tick,group->tick)+1000; - unit->limit = DIFF_TICK(tick,group->tick)+1000; - group->unit_id = UNT_USED_TRAPS; - } - break; - case UNT_WALLOFTHORN: - if( unit->val1 <= 0 ) { - group->unit_id = UNT_USED_TRAPS; - group->limit = DIFF_TICK(tick, group->tick) + 1500; - } - break; - } - } - - //Don't continue if unit or even group is expired and has been deleted. - if( !group || !unit->alive ) - return 0; - - dissonance = skill_dance_switch(unit, 0); - - if( unit->range >= 0 && group->interval != -1 ) - { - if( battle_config.skill_wall_check ) - map_foreachinshootrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick); - else - map_foreachinrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick); - - if(unit->range == -1) //Unit disabled, but it should not be deleted yet. - group->unit_id = UNT_USED_TRAPS; - - if( group->unit_id == UNT_TATAMIGAESHI ) - { - unit->range = -1; //Disable processed cell. - if (--group->val1 <= 0) // number of live cells - { //All tiles were processed, disable skill. - group->target_flag=BCT_NOONE; - group->bl_flag= BL_NUL; - } - } - } - - if( dissonance ) skill_dance_switch(unit, 1); - - return 0; -} -/*========================================== - * Executes on all skill units every SKILLUNITTIMER_INTERVAL miliseconds. - *------------------------------------------*/ -int skill_unit_timer(int tid, unsigned int tick, int id, intptr_t data) -{ - map_freeblock_lock(); - - skillunit_db->foreach(skillunit_db, skill_unit_timer_sub, tick); - - map_freeblock_unlock(); - - return 0; -} - -static int skill_unit_temp[20]; // temporary storage for tracking skill unit skill ids as players move in/out of them -/*========================================== - * - *------------------------------------------*/ -int skill_unit_move_sub (struct block_list* bl, va_list ap) -{ - struct skill_unit* unit = (struct skill_unit *)bl; - struct skill_unit_group* group = unit->group; - - struct block_list* target = va_arg(ap,struct block_list*); - unsigned int tick = va_arg(ap,unsigned int); - int flag = va_arg(ap,int); - - bool dissonance; - uint16 skill_id; - int i; - - nullpo_ret(group); - - if( !unit->alive || target->prev == NULL ) - return 0; - - if( flag&1 && ( unit->group->skill_id == PF_SPIDERWEB || unit->group->skill_id == GN_THORNS_TRAP ) ) - return 0; // Fiberlock is never supposed to trigger on skill_unit_move. [Inkfish] - - dissonance = skill_dance_switch(unit, 0); - - //Necessary in case the group is deleted after calling on_place/on_out [Skotlex] - skill_id = unit->group->skill_id; - - if( unit->group->interval != -1 && !(skill_get_unit_flag(skill_id)&UF_DUALMODE) && skill_id != BD_LULLABY ) //Lullaby is the exception, bugreport:411 - { //Non-dualmode unit skills with a timer don't trigger when walking, so just return - if( dissonance ) skill_dance_switch(unit, 1); - return 0; - } - - //Target-type check. - if( !(group->bl_flag&target->type && battle_check_target(&unit->bl,target,group->target_flag) > 0) ) - { - if( group->src_id == target->id && group->state.song_dance&0x2 ) - { //Ensemble check to see if they went out/in of the area [Skotlex] - if( flag&1 ) - { - if( flag&2 ) - { //Clear this skill id. - ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == skill_id ); - if( i < ARRAYLENGTH(skill_unit_temp) ) - skill_unit_temp[i] = 0; - } - } - else - { - if( flag&2 ) - { //Store this skill id. - ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 ); - if( i < ARRAYLENGTH(skill_unit_temp) ) - skill_unit_temp[i] = skill_id; - else - ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n"); - } - - } - - if( flag&4 ) - skill_unit_onleft(skill_id,target,tick); - } - - if( dissonance ) skill_dance_switch(unit, 1); - - return 0; - } - else - { - if( flag&1 ) - { - int result = skill_unit_onplace(unit,target,tick); - if( flag&2 && result ) - { //Clear skill ids we have stored in onout. - ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == result ); - if( i < ARRAYLENGTH(skill_unit_temp) ) - skill_unit_temp[i] = 0; - } - } - else - { - int result = skill_unit_onout(unit,target,tick); - if( flag&2 && result ) - { //Store this unit id. - ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 ); - if( i < ARRAYLENGTH(skill_unit_temp) ) - skill_unit_temp[i] = skill_id; - else - ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n"); - } - } - - //TODO: Normally, this is dangerous since the unit and group could be freed - //inside the onout/onplace functions. Currently it is safe because we know song/dance - //cells do not get deleted within them. [Skotlex] - if( dissonance ) skill_dance_switch(unit, 1); - - if( flag&4 ) - skill_unit_onleft(skill_id,target,tick); - - return 1; - } -} - -/*========================================== - * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft) - * Flag values: - * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout) - * flag&2: this function is being invoked twice as a bl moves, store in memory the affected - * units to figure out when they have left a group. - * flag&4: Force a onleft event (triggered when the bl is killed, for example) - *------------------------------------------*/ -int skill_unit_move (struct block_list *bl, unsigned int tick, int flag) -{ - nullpo_ret(bl); - - if( bl->prev == NULL ) - return 0; - - if( flag&2 && !(flag&1) ) - { //Onout, clear data - memset(skill_unit_temp, 0, sizeof(skill_unit_temp)); - } - - map_foreachincell(skill_unit_move_sub,bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag); - - if( flag&2 && flag&1 ) - { //Onplace, check any skill units you have left. - int i; - for( i = 0; i < ARRAYLENGTH(skill_unit_temp); i++ ) - if( skill_unit_temp[i] ) - skill_unit_onleft(skill_unit_temp[i], bl, tick); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 dx, int16 dy) -{ - int i,j; - unsigned int tick = gettick(); - int *m_flag; - struct skill_unit *unit1; - struct skill_unit *unit2; - - if (group == NULL) - return 0; - if (group->unit_count<=0) - return 0; - if (group->unit==NULL) - return 0; - - if (skill_get_unit_flag(group->skill_id)&UF_ENSEMBLE) - return 0; //Ensembles may not be moved around. - - if( group->unit_id == UNT_ICEWALL || group->unit_id == UNT_WALLOFTHORN ) - return 0; //Icewalls and Wall of Thorns don't get knocked back - - m_flag = (int *) aCalloc(group->unit_count, sizeof(int)); - // m_flag - // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed) - // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed) - // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed) - // 3: Both 1+2. - for(i=0;i<group->unit_count;i++){ - unit1=&group->unit[i]; - if (!unit1->alive || unit1->bl.m!=m) - continue; - for(j=0;j<group->unit_count;j++){ - unit2=&group->unit[j]; - if (!unit2->alive) - continue; - if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){ - m_flag[i] |= 0x1; - } - if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){ - m_flag[i] |= 0x2; - } - } - } - j = 0; - for (i=0;i<group->unit_count;i++) { - unit1=&group->unit[i]; - if (!unit1->alive) - continue; - if (!(m_flag[i]&0x2)) { - if (group->state.song_dance&0x1) //Cancel dissonance effect. - skill_dance_overlap(unit1, 0); - map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4); - } - //Move Cell using "smart" criteria (avoid useless moving around) - switch(m_flag[i]) - { - case 0: - //Cell moves independently, safely move it. - map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick); - break; - case 1: - //Cell moves unto another cell, look for a replacement cell that won't collide - //and has no cell moving into it (flag == 2) - for(;j<group->unit_count;j++) - { - if(m_flag[j]!=2 || !group->unit[j].alive) - continue; - //Move to where this cell would had moved. - unit2 = &group->unit[j]; - map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick); - j++; //Skip this cell as we have used it. - break; - } - break; - case 2: - case 3: - break; //Don't move the cell as a cell will end on this tile anyway. - } - if (!(m_flag[i]&0x2)) { //We only moved the cell in 0-1 - if (group->state.song_dance&0x1) //Check for dissonance effect. - skill_dance_overlap(unit1, 1); - clif_skill_setunit(unit1); - map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1); - } - } - aFree(m_flag); - return 0; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger, int qty) -{ - int i,j; - - nullpo_ret(sd); - - if(nameid<=0) - return 0; - - for(i=0;i<MAX_SKILL_PRODUCE_DB;i++){ - if(skill_produce_db[i].nameid == nameid ){ - if((j=skill_produce_db[i].req_skill)>0 && - pc_checkskill(sd,j) < skill_produce_db[i].req_skill_lv) - continue; // must iterate again to check other skills that produce it. [malufett] - if( j > 0 && sd->menuskill_id > 0 && sd->menuskill_id != j ) - continue; // special case - break; - } - } - - if( i >= MAX_SKILL_PRODUCE_DB ) - return 0; - - if( pc_checkadditem(sd, nameid, qty) == ADDITEM_OVERAMOUNT ) - {// cannot carry the produced stuff - return 0; - } - - if(trigger>=0){ - if(trigger>20) { // Non-weapon, non-food item (itemlv must match) - if(skill_produce_db[i].itemlv!=trigger) - return 0; - } else if(trigger>10) { // Food (any item level between 10 and 20 will do) - if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>20) - return 0; - } else { // Weapon (itemlv must be higher or equal) - if(skill_produce_db[i].itemlv>trigger) - return 0; - } - } - - for(j=0;j<MAX_PRODUCE_RESOURCE;j++){ - int id,x,y; - if( (id=skill_produce_db[i].mat_id[j]) <= 0 ) - continue; - if(skill_produce_db[i].mat_amount[j] <= 0) { - if(pc_search_inventory(sd,id) < 0) - return 0; - } - else { - for(y=0,x=0;y<MAX_INVENTORY;y++) - if( sd->status.inventory[y].nameid == id ) - x+=sd->status.inventory[y].amount; - if(x<qty*skill_produce_db[i].mat_amount[j]) - return 0; - } - } - return i+1; -} - -/*========================================== - * - *------------------------------------------*/ -int skill_produce_mix (struct map_session_data *sd, uint16 skill_id, int nameid, int slot1, int slot2, int slot3, int qty) -{ - int slot[3]; - int i,sc,ele,idx,equip,wlv,make_per = 0,flag = 0,skill_lv = 0; - int num = -1; // exclude the recipe - struct status_data *status; - struct item_data* data; - - nullpo_ret(sd); - status = status_get_status_data(&sd->bl); - - if( sd->skill_id_old == skill_id ) - skill_lv = sd->skill_lv_old; - - if( !(idx=skill_can_produce_mix(sd,nameid,-1, qty)) ) - return 0; - idx--; - - if (qty < 1) - qty = 1; - - if (!skill_id) //A skill can be specified for some override cases. - skill_id = skill_produce_db[idx].req_skill; - - if( skill_id == GC_RESEARCHNEWPOISON ) - skill_id = GC_CREATENEWPOISON; - - slot[0]=slot1; - slot[1]=slot2; - slot[2]=slot3; - - for(i=0,sc=0,ele=0;i<3;i++){ //Note that qty should always be one if you are using these! - int j; - if( slot[i]<=0 ) - continue; - j = pc_search_inventory(sd,slot[i]); - if(j < 0) - continue; - if(slot[i]==1000){ /* Star Crumb */ - pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE); - sc++; - } - if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */ - static const int ele_table[4]={3,1,4,2}; - pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE); - ele=ele_table[slot[i]-994]; - } - } - - if( skill_id == RK_RUNEMASTERY ) { - int temp_qty, skill_lv = pc_checkskill(sd,skill_id); - data = itemdb_search(nameid); - - if( skill_lv == 10 ) temp_qty = 1 + rnd()%3; - else if( skill_lv > 5 ) temp_qty = 1 + rnd()%2; - else temp_qty = 1; - - if (data->stack.inventory) { - for( i = 0; i < MAX_INVENTORY; i++ ) { - if( sd->status.inventory[i].nameid == nameid ) { - if( sd->status.inventory[i].amount >= data->stack.amount ) { - 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 >= data->stack.amount ) - temp_qty = data->stack.amount - sd->status.inventory[i].amount; - } - break; - } - } - } - qty = temp_qty; - } - - for(i=0;i<MAX_PRODUCE_RESOURCE;i++){ - int j,id,x; - if( (id=skill_produce_db[idx].mat_id[i]) <= 0 ) - continue; - num++; - x=( skill_id == RK_RUNEMASTERY ? 1 : qty)*skill_produce_db[idx].mat_amount[i]; - do{ - int y=0; - j = pc_search_inventory(sd,id); - - if(j >= 0){ - y = sd->status.inventory[j].amount; - if(y>x)y=x; - pc_delitem(sd,j,y,0,0,LOG_TYPE_PRODUCE); - } else - ShowError("skill_produce_mix: material item error\n"); - - x-=y; - }while( j>=0 && x>0 ); - } - - if( (equip = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB )) ) - wlv = itemdb_wlv(nameid); - if(!equip) { - switch(skill_id){ - case BS_IRON: - case BS_STEEL: - case BS_ENCHANTEDSTONE: - // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG] - i = pc_checkskill(sd,skill_id); - make_per = sd->status.job_level*20 + status->dex*10 + status->luk*10; //Base chance - switch(nameid){ - case 998: // Iron - make_per += 4000+i*500; // Temper Iron bonus: +26/+32/+38/+44/+50 - break; - case 999: // Steel - make_per += 3000+i*500; // Temper Steel bonus: +35/+40/+45/+50/+55 - break; - case 1000: //Star Crumb - make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex] - break; - default: // Enchanted Stones - make_per += 1000+i*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35 - break; - } - break; - case ASC_CDP: - 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] - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*50 - + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20 - + (status->int_/2)*10 + status->dex*10+status->luk*10; - if(merc_is_hom_active(sd->hd)) {//Player got a homun - int skill; - if((skill=merc_hom_checkskill(sd->hd,HVAN_INSTRUCT)) > 0) //His homun is a vanil with instruction change - make_per += skill*100; //+1% bonus per level - } - switch(nameid){ - case 501: // Red Potion - case 503: // Yellow Potion - case 504: // White Potion - make_per += (1+rnd()%100)*10 + 2000; - break; - case 970: // Alcohol - make_per += (1+rnd()%100)*10 + 1000; - break; - case 7135: // Bottle Grenade - case 7136: // Acid Bottle - case 7137: // Plant Bottle - case 7138: // Marine Sphere Bottle - make_per += (1+rnd()%100)*10; - break; - case 546: // Condensed Yellow Potion - make_per -= (1+rnd()%50)*10; - break; - case 547: // Condensed White Potion - case 7139: // Glistening Coat - make_per -= (1+rnd()%100)*10; - break; - //Common items, recieve no bonus or penalty, listed just because they are commonly produced - case 505: // Blue Potion - case 545: // Condensed Red Potion - case 605: // Anodyne - case 606: // Aloevera - default: - break; - } - if(battle_config.pp_rate != 100) - make_per = make_per * battle_config.pp_rate / 100; - break; - case SA_CREATECON: // Elemental Converter Creation - make_per = 100000; // should be 100% success rate - break; - /** - * Rune Knight - **/ - case RK_RUNEMASTERY: - { - int A = 100 * (51 + 2 * pc_checkskill(sd, skill_id)); - int B = 100 * status->dex / 30 + 10 * (status->luk + sd->status.job_level); - int C = 100 * cap_value(sd->itemid,0,100); //itemid depend on makerune() - int D = 0; - switch (nameid) { //rune rank it_diff 9 craftable rune - case ITEMID_BERKANA: - D = -2000; - break; //Rank S - case ITEMID_NAUTHIZ: - case ITEMID_URUZ: - D = -1500; - break; //Rank A - case ITEMID_ISA: - case ITEMID_WYRD: - D = -1000; - break; //Rank B - case ITEMID_RAIDO: - case ITEMID_THURISAZ: - case ITEMID_HAGALAZ: - case ITEMID_OTHILA: - D = -500; - break; //Rank C - default: D = -1500; - break; //not specified =-15% - } - make_per = A + B + C + D; - break; - } - /** - * Guilotine Cross - **/ - case GC_CREATENEWPOISON: - make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON); - qty = 1+rnd()%pc_checkskill(sd,GC_RESEARCHNEWPOISON); - break; - case GN_CHANGEMATERIAL: - for(i=0; i<MAX_SKILL_PRODUCE_DB; i++) - if( skill_changematerial_db[i].itemid == nameid ){ - make_per = skill_changematerial_db[i].rate * 10; - break; - } - break; - case GN_S_PHARMACY: - { - int difficulty = 0; - - difficulty = (620 - 20 * skill_lv);// (620 - 20 * Skill Level) - - make_per = status->int_ + status->dex/2 + status->luk + sd->status.job_level + (30+rnd()%120) + // (Caster?s INT) + (Caster?s DEX / 2) + (Caster?s LUK) + (Caster?s Job Level) + Random number between (30 ~ 150) + - (sd->status.base_level-100) + pc_checkskill(sd, AM_LEARNINGPOTION) + pc_checkskill(sd, CR_FULLPROTECTION)*(4+rnd()%6); // (Caster?s Base Level - 100) + (Potion Research x 5) + (Full Chemical Protection Skill Level) x (Random number between 4 ~ 10) - - switch(nameid){// difficulty factor - case 12422: case 12425: - case 12428: - difficulty += 10; - break; - case 6212: case 12426: - difficulty += 15; - break; - case 13264: case 12423: - case 12427: case 12436: - difficulty += 20; - break; - case 6210: case 6211: - case 12437: - difficulty += 30; - break; - case 12424: case 12475: - difficulty += 40; - break; - } - - if( make_per >= 400 && make_per > difficulty) - qty = 10; - else if( make_per >= 300 && make_per > difficulty) - qty = 7; - else if( make_per >= 100 && make_per > difficulty) - qty = 6; - else if( make_per >= 1 && make_per > difficulty) - qty = 5; - else - qty = 4; - make_per = 10000; - } - break; - case GN_MAKEBOMB: - case GN_MIX_COOKING: - { - int difficulty = 30 + rnd()%120; // Random number between (30 ~ 150) - - make_per = sd->status.job_level / 4 + status->luk / 2 + status->dex / 3; // (Caster?s Job Level / 4) + (Caster?s LUK / 2) + (Caster?s DEX / 3) - qty = ~(5 + rnd()%5) + 1; - - switch(nameid){// difficulty factor - case 13260: - difficulty += 5; - break; - case 13261: case 13262: - difficulty += 10; - break; - case 12429: case 12430: case 12431: - case 12432: case 12433: case 12434: - case 13263: - difficulty += 15; - break; - case 13264: - difficulty += 20; - break; - } - - if( make_per >= 30 && make_per > difficulty) - qty = 10 + rnd()%2; - else if( make_per >= 10 && make_per > difficulty) - qty = 10; - else if( make_per == 10 && make_per > difficulty) - qty = 8; - else if( (make_per >= 50 || make_per < 30) && make_per < difficulty) - ;// Food/Bomb creation fails. - else if( make_per >= 30 && make_per < difficulty) - qty = 5; - - if( qty < 0 || (skill_lv == 1 && make_per < difficulty)){ - qty = ~qty + 1; - make_per = 0; - }else - make_per = 10000; - qty = (skill_lv > 1 ? qty : 1); - } - break; - default: - if (sd->menuskill_id == AM_PHARMACY && - sd->menuskill_val > 10 && sd->menuskill_val <= 20) - { //Assume Cooking Dish - if (sd->menuskill_val >= 15) //Legendary Cooking Set. - make_per = 10000; //100% Success - else - make_per = 1200 * (sd->menuskill_val - 10) - + 20 * (sd->status.base_level + 1) - + 20 * (status->dex + 1) - + 100 * (rnd()%(30+5*(sd->cook_mastery/400) - (6+sd->cook_mastery/80)) + (6+sd->cook_mastery/80)) - - 400 * (skill_produce_db[idx].itemlv - 11 + 1) - - 10 * (100 - status->luk + 1) - - 500 * (num - 1) - - 100 * (rnd()%4 + 1); - break; - } - make_per = 5000; - break; - } - } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG] - make_per = 5000 + sd->status.job_level*20 + status->dex*10 + status->luk*10; // Base - make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15 - make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5 - make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30 - if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10 - else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5 - else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3 - else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0? - if(battle_config.wp_rate != 100) - make_per = make_per * battle_config.wp_rate / 100; - } - - if (sd->class_&JOBL_BABY) //if it's a Baby Class - make_per = (make_per * 50) / 100; //Baby penalty is 50% (bugreport:4847) - - if(make_per < 1) make_per = 1; - - - if(rnd()%10000 < make_per || qty > 1){ //Success, or crafting multiple items. - struct item tmp_item; - memset(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid=nameid; - tmp_item.amount=1; - tmp_item.identify=1; - if(equip){ - tmp_item.card[0]=CARD0_FORGE; - tmp_item.card[1]=((sc*5)<<8)+ele; - tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId - tmp_item.card[3]=GetWord(sd->status.char_id,1); - } else { - //Flag is only used on the end, so it can be used here. [Skotlex] - switch (skill_id) { - case BS_DAGGER: - case BS_SWORD: - case BS_TWOHANDSWORD: - case BS_AXE: - case BS_MACE: - case BS_KNUCKLE: - case BS_SPEAR: - flag = battle_config.produce_item_name_input&0x1; - break; - case AM_PHARMACY: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - 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: - flag = battle_config.produce_item_name_input&0x10; - break; - default: - flag = battle_config.produce_item_name_input&0x80; - break; - } - if (flag) { - tmp_item.card[0]=CARD0_CREATE; - tmp_item.card[1]=0; - tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId - tmp_item.card[3]=GetWord(sd->status.char_id,1); - } - } - -// if(log_config.produce > 0) -// log_produce(sd,nameid,slot1,slot2,slot3,1); -//TODO update PICKLOG - - if(equip){ - clif_produceeffect(sd,0,nameid); - clif_misceffect(&sd->bl,3); - if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG] - pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point - } else { - int fame = 0; - tmp_item.amount = 0; - - for (i=0; i< qty; i++) { //Apply quantity modifiers. - if( (skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY) && make_per > 1){ - tmp_item.amount = qty; - break; - } - if (rnd()%10000 < make_per || qty == 1) { //Success - tmp_item.amount++; - if(nameid < 545 || nameid > 547) - continue; - if( skill_id != AM_PHARMACY && - skill_id != AM_TWILIGHT1 && - skill_id != AM_TWILIGHT2 && - skill_id != AM_TWILIGHT3 ) - continue; - //Add fame as needed. - switch(++sd->potion_success_counter) { - case 3: - fame+=1; // Success to prepare 3 Condensed Potions in a row - break; - case 5: - fame+=3; // Success to prepare 5 Condensed Potions in a row - break; - case 7: - fame+=10; // Success to prepare 7 Condensed Potions in a row - break; - case 10: - fame+=50; // Success to prepare 10 Condensed Potions in a row - sd->potion_success_counter = 0; - break; - } - } else //Failure - sd->potion_success_counter = 0; - } - - if (fame) - pc_addfame(sd,fame); - //Visual effects and the like. - switch (skill_id) { - case AM_PHARMACY: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - case ASC_CDP: - clif_produceeffect(sd,2,nameid); - clif_misceffect(&sd->bl,5); - break; - case BS_IRON: - case BS_STEEL: - case BS_ENCHANTEDSTONE: - clif_produceeffect(sd,0,nameid); - clif_misceffect(&sd->bl,3); - break; - 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. - clif_specialeffect(&sd->bl, 608, AREA); - if( sd->cook_mastery < 1999 ) - pc_setglobalreg(sd, "COOK_MASTERY",sd->cook_mastery + ( 1 << ( (skill_produce_db[idx].itemlv - 11) / 2 ) ) * 5); - } - break; - } - } - if ( skill_id == GN_CHANGEMATERIAL && tmp_item.amount) { //Success - int j, k = 0; - for(i=0; i<MAX_SKILL_PRODUCE_DB; i++) - if( skill_changematerial_db[i].itemid == nameid ){ - for(j=0; j<5; j++){ - if( rnd()%1000 < skill_changematerial_db[i].qty_rate[j] ){ - tmp_item.amount = qty * skill_changematerial_db[i].qty[j]; - if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - k++; - } - } - break; - } - if( k ){ - clif_msg_skill(sd,skill_id,0x627); - return 1; - } - } else if (tmp_item.amount) { //Success - if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - if( skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY ) - clif_msg_skill(sd,skill_id,0x627); - return 1; - } - } - //Failure -// if(log_config.produce) -// log_produce(sd,nameid,slot1,slot2,slot3,0); -//TODO update PICKLOG - - if(equip){ - clif_produceeffect(sd,1,nameid); - clif_misceffect(&sd->bl,2); - } else { - switch (skill_id) { - case ASC_CDP: //25% Damage yourself, and display same effect as failed potion. - status_percent_damage(NULL, &sd->bl, -25, 0, true); - case AM_PHARMACY: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - clif_produceeffect(sd,3,nameid); - clif_misceffect(&sd->bl,6); - sd->potion_success_counter = 0; // Fame point system [DracoRPG] - break; - case BS_IRON: - case BS_STEEL: - case BS_ENCHANTEDSTONE: - clif_produceeffect(sd,1,nameid); - clif_misceffect(&sd->bl,2); - break; - case RK_RUNEMASTERY: - case GC_CREATENEWPOISON: - clif_produceeffect(sd,3,nameid); - clif_misceffect(&sd->bl,6); - break; - case GN_MIX_COOKING: { - struct item tmp_item; - const int compensation[5] = {13265, 13266, 13267, 12435, 13268}; - int rate = rnd()%500; - memset(&tmp_item,0,sizeof(tmp_item)); - if( rate < 50) i = 4; - else if( rate < 100) i = 2+rnd()%1; - else if( rate < 250 ) i = 1; - else if( rate < 500 ) i = 0; - tmp_item.nameid = compensation[i]; - tmp_item.amount = qty; - tmp_item.identify = 1; - if( pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE) ) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - clif_msg_skill(sd,skill_id,0x628); - } - break; - case GN_MAKEBOMB: - case GN_S_PHARMACY: - case GN_CHANGEMATERIAL: - clif_msg_skill(sd,skill_id,0x628); - break; - default: - if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 ) - { //Cooking items. - clif_specialeffect(&sd->bl, 609, AREA); - if( sd->cook_mastery > 0 ) - pc_setglobalreg(sd, "COOK_MASTERY", sd->cook_mastery - ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 )); - } - } - } - return 0; -} - -int skill_arrow_create (struct map_session_data *sd, int nameid) -{ - int i,j,flag,index=-1; - struct item tmp_item; - - nullpo_ret(sd); - - if(nameid <= 0) - return 1; - - for(i=0;i<MAX_SKILL_ARROW_DB;i++) - if(nameid == skill_arrow_db[i].nameid) { - index = i; - break; - } - - if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0) - return 1; - - pc_delitem(sd,j,1,0,0,LOG_TYPE_PRODUCE); - for(i=0;i<MAX_ARROW_RESOURCE;i++) { - memset(&tmp_item,0,sizeof(tmp_item)); - tmp_item.identify = 1; - tmp_item.nameid = skill_arrow_db[index].cre_id[i]; - tmp_item.amount = skill_arrow_db[index].cre_amount[i]; - if(battle_config.produce_item_name_input&0x4) { - tmp_item.card[0]=CARD0_CREATE; - tmp_item.card[1]=0; - tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId - tmp_item.card[3]=GetWord(sd->status.char_id,1); - } - if(tmp_item.nameid <= 0 || tmp_item.amount <= 0) - continue; - if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - - return 0; -} -int skill_poisoningweapon( struct map_session_data *sd, int nameid) { - sc_type type; - int chance, i; - nullpo_ret(sd); - if( nameid <= 0 || (i = pc_search_inventory(sd,nameid)) < 0 || pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME) ) { - clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0); - return 0; - } - switch( nameid ) - { // t_lv used to take duration from skill_get_time2 - case PO_PARALYSE: type = SC_PARALYSE; break; - case PO_PYREXIA: type = SC_PYREXIA; break; - case PO_DEATHHURT: type = SC_DEATHHURT; break; - case PO_LEECHESEND: type = SC_LEECHESEND; break; - case PO_VENOMBLEED: type = SC_VENOMBLEED; break; - case PO_TOXIN: type = SC_TOXIN; break; - case PO_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; break; - case PO_OBLIVIONCURSE: type = SC_OBLIVIONCURSE; break; - default: - clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0); - return 0; - } - - chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv - sc_start4(&sd->bl, SC_POISONINGWEAPON, 100, pc_checkskill(sd, GC_RESEARCHNEWPOISON), //in Aegis it store the level of GC_RESEARCHNEWPOISON in val1 - type, chance, 0, skill_get_time(GC_POISONINGWEAPON, sd->menuskill_val)); - - return 0; -} - -static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id) -{ - struct status_change *sc = status_get_sc(bl); - - // non-offensive and non-magic skills do not affect the status - if (skill_get_nk(skill_id)&NK_NO_DAMAGE || !(skill_get_type(skill_id)&BF_MAGIC)) - return; - - if (sc && sc->count && sc->data[SC_MAGICPOWER]) - { - if (sc->data[SC_MAGICPOWER]->val4) - { - status_change_end(bl, SC_MAGICPOWER, INVALID_TIMER); - } - else - { - sc->data[SC_MAGICPOWER]->val4 = 1; - status_calc_bl(bl, status_sc2scb_flag(SC_MAGICPOWER)); -#ifndef RENEWAL - if(bl->type == BL_PC){// update current display. - clif_updatestatus(((TBL_PC *)bl),SP_MATK1); - clif_updatestatus(((TBL_PC *)bl),SP_MATK2); - } -#endif - } - } -} - - -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,LOG_TYPE_CONSUME) ) - { - clif_skill_fail(sd,NC_MAGICDECOY,USESKILL_FAIL_LEVEL,0); - return 0; - } - - // Spawn Position - pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); - 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_, "", SZ_SMALL, AI_NONE); - if( md ) { - md->master_id = sd->bl.id; - md->special_state.ai = AI_FLORA; - 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, max_preserve, skill_id, point; - struct status_change *sc; - - nullpo_ret(sd); - - sc = status_get_sc(&sd->bl); - status_change_end(&sd->bl, SC_STOP, INVALID_TIMER); - - for(i=SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) if( sc && !sc->data[i] ) break; - if( i > SC_MAXSPELLBOOK ) - { - clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 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 ) return 0; - - if( !pc_checkskill(sd, (skill_id = skill_spellbook_db[i].skill_id)) ) - { // 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, USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP, 0); - return 0; - } - - max_preserve = 4 * pc_checkskill(sd, WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10; - point = skill_spellbook_db[i].point; - - if( sc && sc->data[SC_READING_SB] ){ - if( (sc->data[SC_READING_SB]->val2 + point) > max_preserve ) - { - clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT, 0); - return 0; - } - for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett] - if( !sc->data[i] ){ - sc->data[SC_READING_SB]->val2 += point; // increase points - sc_start4(&sd->bl, (sc_type)i, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER); - break; - } - } - }else{ - sc_start2(&sd->bl, SC_READING_SB, 100, 0, point, INVALID_TIMER); - sc_start4(&sd->bl, SC_MAXSPELLBOOK, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER); - } - - return 1; -} -int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { - int id, lv, prob, aslvl = 0; - nullpo_ret(sd); - - if (sd->sc.data[SC_STOP]) { - aslvl = sd->sc.data[SC_STOP]->val1; - status_change_end(&sd->bl,SC_STOP,INVALID_TIMER); - } - - if( skill_id >= GS_GLITTERING || skill_get_type(skill_id) != BF_MAGIC || - (id = sd->status.skill[skill_id].id) == 0 || sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED ) { - clif_skill_fail(sd,SC_AUTOSHADOWSPELL,0,0); - return 0; - } - - lv = (aslvl + 1) / 2; // The level the skill will be autocasted - lv = min(lv,sd->status.skill[skill_id].lv); - prob = (aslvl == 10) ? 15 : (32 - 2 * aslvl); // Probability at level 10 was increased to 15. - sc_start4(&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl)); - return 0; -} -int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv, unsigned short* item_list) { - int i; - - nullpo_ret(sd); - nullpo_ret(item_list); - - if( n <= 0 ) - return 1; - - for( i = 0; i < n; i++ ) { - int nameid, add_amount, del_amount, idx, product, flag; - struct item tmp_item; - - idx = item_list[i*2+0]-2; - del_amount = item_list[i*2+1]; - - if( skill_lv == 2 ) - del_amount -= (del_amount % 10); - add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ; - - if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) { - clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); - return 1; - } - - switch( nameid ) { - // Level 1 - case 994: product = 990; break; // Flame Heart -> Red Blood. - case 995: product = 991; break; // Mystic Frozen -> Crystal Blue. - case 996: product = 992; break; // Rough Wind -> Wind of Verdure. - case 997: product = 993; break; // Great Nature -> Green Live. - // Level 2 - case 990: product = 994; break; // Red Blood -> Flame Heart. - case 991: product = 995; break; // Crystal Blue -> Mystic Frozen. - case 992: product = 996; break; // Wind of Verdure -> Rough Wind. - case 993: product = 997; break; // Green Live -> Great Nature. - default: - clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); - return 1; - } - - if( pc_delitem(sd,idx,del_amount,0,1,LOG_TYPE_CONSUME) ) { - clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); - return 1; - } - - if( skill_lv == 2 && rnd()%100 < 25 ) { // At level 2 have a fail chance. You loose your items if it fails. - clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0); - return 1; - } - - - memset(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid = product; - tmp_item.amount = add_amount; - tmp_item.identify = 1; - - if( tmp_item.amount ) { - if( (flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME)) ) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - - } - - return 0; -} - -int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) { - int i, j, k, c, p = 0, nameid, amount; - - nullpo_ret(sd); - nullpo_ret(item_list); - - // Search for objects that can be created. - for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) { - if( skill_produce_db[i].itemlv == 26 ) { - p = 0; - do { - c = 0; - // Verification of overlap between the objects required and the list submitted. - for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) { - if( skill_produce_db[i].mat_id[j] > 0 ) { - for( k = 0; k < n; k++ ) { - int idx = item_list[k*2+0]-2; - nameid = sd->status.inventory[idx].nameid; - amount = item_list[k*2+1]; - if( nameid > 0 && sd->status.inventory[idx].identify == 0 ){ - clif_msg_skill(sd,GN_CHANGEMATERIAL,0x62D); - return 0; - } - if( nameid == skill_produce_db[i].mat_id[j] && (amount-p*skill_produce_db[i].mat_amount[j]) >= skill_produce_db[i].mat_amount[j] - && (amount-p*skill_produce_db[i].mat_amount[j])%skill_produce_db[i].mat_amount[j] == 0 ) // must be in exact amount - c++; // match - } - } - else - break; // No more items required - } - p++; - } while(n == j && c == n); - p--; - if ( p > 0 ) { - skill_produce_mix(sd,GN_CHANGEMATERIAL,skill_produce_db[i].nameid,0,0,0,p); - return 1; - } - } - } - - if( p == 0) - clif_msg_skill(sd,GN_CHANGEMATERIAL,0x623); - - return 0; -} -/** - * for Royal Guard's LG_TRAMPLE - **/ -static int skill_destroy_trap( struct block_list *bl, va_list ap ) { - struct skill_unit *su = (struct skill_unit *)bl; - struct skill_unit_group *sg; - unsigned int tick; - - nullpo_ret(su); - tick = va_arg(ap, unsigned int); - - if (su->alive && (sg = su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP) { - switch( sg->unit_id ) { - case UNT_LANDMINE: - case UNT_CLAYMORETRAP: - case UNT_BLASTMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_FREEZINGTRAP: - case UNT_CLUSTERBOMB: - case UNT_FIRINGTRAP: - case UNT_ICEBOUNDTRAP: - map_foreachinrange(skill_trap_splash,&su->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &su->bl,tick); - break; - } - // Traps aren't recovered. - skill_delunit(su); - } - return 0; -} -/*========================================== - * - *------------------------------------------*/ -int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data) -{ - struct map_session_data *sd = map_id2sd(id); - struct skill_cd * cd = NULL; - - if (data <= 0 || data >= MAX_SKILL) - return 0; - if (!sd) return 0; - if (sd->blockskill[data] != (0x1|(tid&0xFE))) return 0; - - if( ( cd = idb_get(skillcd_db,sd->status.char_id) ) ) { - int i,cursor; - ARR_FIND( 0, cd->cursor+1, cursor, cd->skidx[cursor] == data ); - cd->duration[cursor] = 0; - cd->skidx[cursor] = 0; - cd->nameid[cursor] = 0; - // compact the cool down list - for( i = 0, cursor = 0; i < cd->cursor; i++ ) { - if( cd->duration[i] == 0 ) - continue; - if( cursor != i ) { - cd->duration[cursor] = cd->duration[i]; - cd->skidx[cursor] = cd->skidx[i]; - cd->nameid[cursor] = cd->nameid[i]; - } - cursor++; - } - if( cursor == 0 ) - idb_remove(skillcd_db,sd->status.char_id); - else - cd->cursor = cursor; - } - - sd->blockskill[data] = 0; - return 1; -} - -/** - * flags a singular skill as being blocked from persistent usage. - * @param sd the player the skill delay affects - * @param skill_id the skill which should be delayed - * @param tick the length of time the delay should last - * @param load whether this assignment is being loaded upon player login - * @return 0 if successful, -1 otherwise - */ -int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick, bool load) -{ - int oskill_id = skill_id; - struct skill_cd* cd = NULL; - uint16 idx = skill_get_index(skill_id); - - nullpo_retr (-1, sd); - - if (idx == 0) - return -1; - - if (tick < 1) { - sd->blockskill[idx] = 0; - return -1; - } - - if( battle_config.display_status_timers ) - clif_skill_cooldown(sd, idx, tick); - - if( !load ) - {// not being loaded initially so ensure the skill delay is recorded - if( !(cd = idb_get(skillcd_db,sd->status.char_id)) ) - {// create a new skill cooldown object for map storage - CREATE( cd, struct skill_cd, 1 ); - idb_put( skillcd_db, sd->status.char_id, cd ); - } - - // record the skill duration in the database map - cd->duration[cd->cursor] = tick; - cd->skidx[cd->cursor] = idx; - cd->nameid[cd->cursor] = oskill_id; - cd->cursor++; - } - - sd->blockskill[idx] = 0x1|(0xFE&add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,idx)); - return 0; -} - -int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data) //[orn] -{ - struct homun_data *hd = (TBL_HOM*) map_id2bl(id); - if (data <= 0 || data >= MAX_SKILL) - return 0; - if (hd) hd->blockskill[data] = 0; - - return 1; -} - -int skill_blockhomun_start(struct homun_data *hd, uint16 skill_id, int tick) //[orn] -{ - uint16 idx = skill_get_index(skill_id); - nullpo_retr (-1, hd); - - - if (idx == 0) - return -1; - - if (tick < 1) { - hd->blockskill[idx] = 0; - return -1; - } - hd->blockskill[idx] = 1; - return add_timer(gettick() + tick, skill_blockhomun_end, hd->bl.id, idx); -} - -int skill_blockmerc_end(int tid, unsigned int tick, int id, intptr_t data) //[orn] -{ - struct mercenary_data *md = (TBL_MER*)map_id2bl(id); - if( data <= 0 || data >= MAX_SKILL ) - return 0; - if( md ) md->blockskill[data] = 0; - - return 1; -} - -int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick) -{ - uint16 idx = skill_get_index(skill_id); - nullpo_retr (-1, md); - - if (idx == 0) - return -1; - if( tick < 1 ) - { - md->blockskill[idx] = 0; - return -1; - } - md->blockskill[idx] = 1; - return add_timer(gettick() + tick, skill_blockmerc_end, md->bl.id, idx); -} -/** - * Adds a new skill unit entry for this player to recast after map load - **/ -void skill_usave_add(struct map_session_data * sd, uint16 skill_id, uint16 skill_lv) { - struct skill_usave * sus = NULL; - - if( idb_exists(skillusave_db,sd->status.char_id) ) { - idb_remove(skillusave_db,sd->status.char_id); - } - - CREATE( sus, struct skill_usave, 1 ); - idb_put( skillusave_db, sd->status.char_id, sus ); - - sus->skill_id = skill_id; - sus->skill_lv = skill_lv; - - return; -} -void skill_usave_trigger(struct map_session_data *sd) { - struct skill_usave * sus = NULL; - - if( ! (sus = idb_get(skillusave_db,sd->status.char_id)) ) { - return; - } - - skill_unitsetting(&sd->bl,sus->skill_id,sus->skill_lv,sd->bl.x,sd->bl.y,0); - - idb_remove(skillusave_db,sd->status.char_id); - - return; -} -/* - * - */ -int skill_split_str (char *str, char **val, int num) -{ - int i; - - for( i = 0; i < num && str; i++ ) - { - val[i] = str; - str = strchr(str,','); - if( str ) - *str++=0; - } - - return i; -} -/* - * - */ -int skill_split_atoi (char *str, int *val) -{ - int i, j, diff, step = 1; - - for (i=0; i<MAX_SKILL_LEVEL; i++) { - if (!str) break; - val[i] = atoi(str); - str = strchr(str,':'); - if (str) - *str++=0; - } - if(i==0) //No data found. - return 0; - if(i==1) - { //Single value, have the whole range have the same value. - for (; i < MAX_SKILL_LEVEL; i++) - val[i] = val[i-1]; - return i; - } - //Check for linear change with increasing steps until we reach half of the data acquired. - for (step = 1; step <= i/2; step++) - { - diff = val[i-1] - val[i-step-1]; - for(j = i-1; j >= step; j--) - if ((val[j]-val[j-step]) != diff) - break; - - if (j>=step) //No match, try next step. - continue; - - for(; i < MAX_SKILL_LEVEL; i++) - { //Apply linear increase - val[i] = val[i-step]+diff; - if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases. - { val[i] = 1; diff = 0; step = 1; } - } - return i; - } - //Okay.. we can't figure this one out, just fill out the stuff with the previous value. - for (;i<MAX_SKILL_LEVEL; i++) - val[i] = val[i-1]; - return i; -} - -/* - * - */ -void skill_init_unit_layout (void) -{ - int i,j,size,pos = 0; - - memset(skill_unit_layout,0,sizeof(skill_unit_layout)); - - // standard square layouts go first - for (i=0; i<=MAX_SQUARE_LAYOUT; i++) { - size = i*2+1; - skill_unit_layout[i].count = size*size; - for (j=0; j<size*size; j++) { - skill_unit_layout[i].dx[j] = (j%size-i); - skill_unit_layout[i].dy[j] = (j/size-i); - } - } - - // afterwards add special ones - pos = i; - for (i=0;i<MAX_SKILL_DB;i++) { - if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1) - continue; - if( i >= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) { - int skill = i; - - if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) { - skill -= EL_SKILLRANGEMIN; - skill += EL_SKILLBASE; - } - if( skill == EL_FIRE_MANTLE ) { - static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1}; - static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0}; - skill_unit_layout[pos].count = 8; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - } else { - switch (i) { - case MG_FIREWALL: - case WZ_ICEWALL: - case WL_EARTHSTRAIN://Warlock - // these will be handled later - break; - case PR_SANCTUARY: - case NPC_EVILLAND: { - static const int dx[] = { - -1, 0, 1,-2,-1, 0, 1, 2,-2,-1, - 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1}; - static const int dy[]={ - -2,-2,-2,-1,-1,-1,-1,-1, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2}; - skill_unit_layout[pos].count = 21; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - case PR_MAGNUS: { - static const int dx[] = { - -1, 0, 1,-1, 0, 1,-3,-2,-1, 0, - 1, 2, 3,-3,-2,-1, 0, 1, 2, 3, - -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1}; - static const int dy[] = { - -3,-3,-3,-2,-2,-2,-1,-1,-1,-1, - -1,-1,-1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3}; - skill_unit_layout[pos].count = 33; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - case MH_POISON_MIST: - case AS_VENOMDUST: { - static const int dx[] = {-1, 0, 0, 0, 1}; - static const int dy[] = { 0,-1, 0, 1, 0}; - skill_unit_layout[pos].count = 5; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: { - static const int dx[] = { - 0, 0,-1, 0, 1,-2,-1, 0, 1, 2, - -4,-3,-2,-1, 0, 1, 2, 3, 4,-2, - -1, 0, 1, 2,-1, 0, 1, 0, 0}; - static const int dy[] = { - -4,-3,-2,-2,-2,-1,-1,-1,-1,-1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 2, 2, 2, 3, 4}; - skill_unit_layout[pos].count = 29; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - case PF_FOGWALL: { - static const int dx[] = { - -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2}; - static const int dy[] = { - -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; - skill_unit_layout[pos].count = 15; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - case PA_GOSPEL: { - static const int dx[] = { - -1, 0, 1,-1, 0, 1,-3,-2,-1, 0, - 1, 2, 3,-3,-2,-1, 0, 1, 2, 3, - -3,-2,-1, 0, 1, 2, 3,-1, 0, 1, - -1, 0, 1}; - static const int dy[] = { - -3,-3,-3,-2,-2,-2,-1,-1,-1,-1, - -1,-1,-1, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, - 3, 3, 3}; - skill_unit_layout[pos].count = 33; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - case NJ_KAENSIN: { - static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2}; - static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2}; - skill_unit_layout[pos].count = 24; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - case NJ_TATAMIGAESHI: { - //Level 1 (count 4, cross of 3x3) - static const int dx1[] = {-1, 1, 0, 0}; - static const int dy1[] = { 0, 0,-1, 1}; - //Level 2-3 (count 8, cross of 5x5) - static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0}; - static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2}; - //Level 4-5 (count 12, cross of 7x7 - static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0}; - static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3}; - //lv1 - j = 0; - skill_unit_layout[pos].count = 4; - memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1)); - memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1)); - skill_db[i].unit_layout_type[j] = pos; - //lv2/3 - j++; - pos++; - skill_unit_layout[pos].count = 8; - memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2)); - memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2)); - skill_db[i].unit_layout_type[j] = pos; - skill_db[i].unit_layout_type[++j] = pos; - //lv4/5 - j++; - pos++; - skill_unit_layout[pos].count = 12; - memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3)); - memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3)); - skill_db[i].unit_layout_type[j] = pos; - skill_db[i].unit_layout_type[++j] = pos; - //Fill in the rest using lv 5. - for (;j<MAX_SKILL_LEVEL;j++) - skill_db[i].unit_layout_type[j] = pos; - //Skip, this way the check below will fail and continue to the next skill. - pos++; - } - break; - case GN_WALLOFTHORN: { - static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0}; - static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2}; - skill_unit_layout[pos].count = 16; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - break; - default: - ShowError("unknown unit layout at skill %d\n",i); - break; - } - } - if (!skill_unit_layout[pos].count) - continue; - for (j=0;j<MAX_SKILL_LEVEL;j++) - skill_db[i].unit_layout_type[j] = pos; - pos++; - } - - // firewall and icewall have 8 layouts (direction-dependent) - firewall_unit_pos = pos; - for (i=0;i<8;i++) { - if (i&1) { - skill_unit_layout[pos].count = 5; - if (i&0x2) { - int dx[] = {-1,-1, 0, 0, 1}; - int dy[] = { 1, 0, 0,-1,-1}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } else { - int dx[] = { 1, 1 ,0, 0,-1}; - int dy[] = { 1, 0, 0,-1,-1}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - } else { - skill_unit_layout[pos].count = 3; - if (i%4==0) { - int dx[] = {-1, 0, 1}; - int dy[] = { 0, 0, 0}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } else { - int dx[] = { 0, 0, 0}; - int dy[] = {-1, 0, 1}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - } - pos++; - } - icewall_unit_pos = pos; - for (i=0;i<8;i++) { - skill_unit_layout[pos].count = 5; - if (i&1) { - if (i&0x2) { - int dx[] = {-2,-1, 0, 1, 2}; - int dy[] = { 2, 1, 0,-1,-2}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } else { - int dx[] = { 2, 1 ,0,-1,-2}; - int dy[] = { 2, 1, 0,-1,-2}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - } else { - if (i%4==0) { - int dx[] = {-2,-1, 0, 1, 2}; - int dy[] = { 0, 0, 0, 0, 0}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } else { - int dx[] = { 0, 0, 0, 0, 0}; - int dy[] = {-2,-1, 0, 1, 2}; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } - } - pos++; - } - earthstrain_unit_pos = pos; - for( i = 0; i < 8; i++ ) - { // For each Direction - skill_unit_layout[pos].count = 15; - switch( i ) - { - case 0: case 1: case 3: case 4: case 5: case 7: - { - int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}; - 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - 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++; - } - -} - -int skill_block_check(struct block_list *bl, sc_type type , uint16 skill_id) { - int inf = 0; - struct status_change *sc = status_get_sc(bl); - - if( !sc || !bl || !skill_id ) - return 0; // Can do it - - switch(type){ - case SC_STASIS: - inf = skill_get_inf2(skill_id); - if( inf == INF2_SONG_DANCE || /*skill_get_inf2(skill_id) == INF2_CHORUS_SKILL ||*/ inf == INF2_SPIRIT_SKILL ) - return 1; // Can't do it. - switch( skill_id ) - { - 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: - - // 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. - } - break; - case SC_KAGEHUMI: - switch(skill_id){ - case TF_HIDING: case AS_CLOAKING: case GC_CLOAKINGEXCEED: case SC_SHADOWFORM: - case MI_HARMONIZE: case CG_MARIONETTE: case AL_TELEPORT: case TF_BACKSLIDING: - case RA_CAMOUFLAGE: case ST_CHASEWALK: case GD_EMERGENCYCALL: - return 1; // needs more info - } - break; - } - - return 0; -} - -int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) { - int type = 0; - - switch( skill_id ) { - case SO_SUMMON_AGNI: type = 2114; break; - case SO_SUMMON_AQUA: type = 2117; break; - case SO_SUMMON_VENTUS: type = 2120; break; - case SO_SUMMON_TERA: type = 2123; break; - } - - type += skill_lv - 1; - - return type; -} - -/** - * reload stored skill cooldowns when a player logs in. - * @param sd the affected player structure - */ -void skill_cooldown_load(struct map_session_data * sd) -{ - int i; - struct skill_cd* cd = NULL; - - // always check to make sure the session properly exists - nullpo_retv(sd); - - if( !(cd = idb_get(skillcd_db, sd->status.char_id)) ) - {// no skill cooldown is associated with this character - return; - } - - // process each individual cooldown associated with the character - for( i = 0; i < cd->cursor; i++ ) - { - // block the skill from usage but ensure it is not recorded (load = true) - skill_blockpc_start_( sd, cd->nameid[i], cd->duration[i], true ); - } -} - -/*========================================== - * sub-function of DB reading. - * skill_db.txt - *------------------------------------------*/ - -static bool skill_parse_row_skilldb(char* split[], int columns, int current) -{// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description - uint16 skill_id = atoi(split[0]); - uint16 idx; - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) { - ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id); - return false; - } - - idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].range); - skill_db[idx].hit = atoi(split[2]); - skill_db[idx].inf = atoi(split[3]); - skill_split_atoi(split[4],skill_db[idx].element); - skill_db[idx].nk = (int)strtol(split[5], NULL, 0); - skill_split_atoi(split[6],skill_db[idx].splash); - skill_db[idx].max = atoi(split[7]); - skill_split_atoi(split[8],skill_db[idx].num); - - if( strcmpi(split[9],"yes") == 0 ) - skill_db[idx].castcancel = 1; - else - skill_db[idx].castcancel = 0; - skill_db[idx].cast_def_rate = atoi(split[10]); - skill_db[idx].inf2 = (int)strtol(split[11], NULL, 0); - skill_split_atoi(split[12],skill_db[idx].maxcount); - if( strcmpi(split[13],"weapon") == 0 ) - skill_db[idx].skill_type = BF_WEAPON; - else if( strcmpi(split[13],"magic") == 0 ) - skill_db[idx].skill_type = BF_MAGIC; - else if( strcmpi(split[13],"misc") == 0 ) - skill_db[idx].skill_type = BF_MISC; - else - skill_db[idx].skill_type = 0; - skill_split_atoi(split[14],skill_db[idx].blewcount); - safestrncpy(skill_db[idx].name, trim(split[15]), sizeof(skill_db[idx].name)); - safestrncpy(skill_db[idx].desc, trim(split[16]), sizeof(skill_db[idx].desc)); - strdb_iput(skilldb_name2id, skill_db[idx].name, skill_id); - - return true; -} - -static bool skill_parse_row_requiredb(char* split[], int columns, int current) -{// skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10 - char* p; - int j; - - uint16 skill_id = atoi(split[0]); - uint16 idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].hp); - skill_split_atoi(split[2],skill_db[idx].mhp); - skill_split_atoi(split[3],skill_db[idx].sp); - skill_split_atoi(split[4],skill_db[idx].hp_rate); - skill_split_atoi(split[5],skill_db[idx].sp_rate); - skill_split_atoi(split[6],skill_db[idx].zeny); - - //Wich weapon type are required, see doc/item_db for types - p = split[7]; - for( j = 0; j < 32; j++ ) - { - int l = atoi(p); - if( l == 99 ) // Any weapon - { - skill_db[idx].weapon = 0; - break; - } - else - skill_db[idx].weapon |= 1<<l; - p = strchr(p,':'); - if(!p) - break; - p++; - } - - //FIXME: document this - p = split[8]; - for( j = 0; j < 32; j++ ) - { - int l = atoi(p); - if( l == 99 ) // Any ammo type - { - skill_db[idx].ammo = 0xFFFFFFFF; - break; - } - else if( l ) // 0 stands for no requirement - skill_db[idx].ammo |= 1<<l; - p = strchr(p,':'); - if( !p ) - break; - p++; - } - skill_split_atoi(split[9],skill_db[idx].ammo_qty); - - if( strcmpi(split[10],"hiding")==0 ) skill_db[idx].state = ST_HIDING; - else if( strcmpi(split[10],"cloaking")==0 ) skill_db[idx].state = ST_CLOAKING; - else if( strcmpi(split[10],"hidden")==0 ) skill_db[idx].state = ST_HIDDEN; - else if( strcmpi(split[10],"riding")==0 ) skill_db[idx].state = ST_RIDING; - else if( strcmpi(split[10],"falcon")==0 ) skill_db[idx].state = ST_FALCON; - else if( strcmpi(split[10],"cart")==0 ) skill_db[idx].state = ST_CART; - else if( strcmpi(split[10],"shield")==0 ) skill_db[idx].state = ST_SHIELD; - else if( strcmpi(split[10],"sight")==0 ) skill_db[idx].state = ST_SIGHT; - else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[idx].state = ST_EXPLOSIONSPIRITS; - else if( strcmpi(split[10],"cartboost")==0 ) skill_db[idx].state = ST_CARTBOOST; - else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[idx].state = ST_RECOV_WEIGHT_RATE; - else if( strcmpi(split[10],"move_enable")==0 ) skill_db[idx].state = ST_MOVE_ENABLE; - else if( strcmpi(split[10],"water")==0 ) skill_db[idx].state = ST_WATER; - /** - * New States - **/ - else if( strcmpi(split[10],"dragon")==0 ) skill_db[idx].state = ST_RIDINGDRAGON; - else if( strcmpi(split[10],"warg")==0 ) skill_db[idx].state = ST_WUG; - else if( strcmpi(split[10],"ridingwarg")==0 ) skill_db[idx].state = ST_RIDINGWUG; - else if( strcmpi(split[10],"mado")==0 ) skill_db[idx].state = ST_MADO; - else if( strcmpi(split[10],"elementalspirit")==0 ) skill_db[idx].state = ST_ELEMENTALSPIRIT; - else if (strcmpi(split[10], "poisonweapon") == 0) skill_db[idx].state = ST_POISONINGWEAPON; - else if (strcmpi(split[10], "rollingcutter") == 0) skill_db[idx].state = ST_ROLLINGCUTTER; - else if (strcmpi(split[10], "mh_fighting") == 0) skill_db[idx].state = ST_MH_FIGHTING; - else if (strcmpi(split[10], "mh_grappling") == 0) skill_db[idx].state = ST_MH_GRAPPLING; - - /** - * Unknown or no state - **/ - else skill_db[idx].state = ST_NONE; - - skill_split_atoi(split[11],skill_db[idx].spiritball); - for( j = 0; j < MAX_SKILL_ITEM_REQUIRE; j++ ) { - skill_db[idx].itemid[j] = atoi(split[12+ 2*j]); - skill_db[idx].amount[j] = atoi(split[13+ 2*j]); - } - - return true; -} - -static bool skill_parse_row_castdb(char* split[], int columns, int current) -{// skill_id,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2 - uint16 skill_id = atoi(split[0]); - uint16 idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].cast); - skill_split_atoi(split[2],skill_db[idx].delay); - skill_split_atoi(split[3],skill_db[idx].walkdelay); - skill_split_atoi(split[4],skill_db[idx].upkeep_time); - skill_split_atoi(split[5],skill_db[idx].upkeep_time2); - skill_split_atoi(split[6],skill_db[idx].cooldown); -#ifdef RENEWAL_CAST - skill_split_atoi(split[7],skill_db[idx].fixed_cast); -#endif - return true; -} - -static bool skill_parse_row_castnodexdb(char* split[], int columns, int current) -{// Skill id,Cast,Delay (optional) - uint16 skill_id = atoi(split[0]); - uint16 idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].castnodex); - if( split[2] ) // optional column - skill_split_atoi(split[2],skill_db[idx].delaynodex); - - return true; -} - -static bool skill_parse_row_nocastdb(char* split[], int columns, int current) -{// skill_id,Flag - uint16 skill_id = atoi(split[0]); - uint16 idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill_db[idx].nocast |= atoi(split[1]); - - return true; -} - -static bool skill_parse_row_unitdb(char* split[], int columns, int current) -{// ID,unit ID,unit ID 2,layout,range,interval,target,flag - uint16 skill_id = atoi(split[0]); - uint16 idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill_db[idx].unit_id[0] = strtol(split[1],NULL,16); - skill_db[idx].unit_id[1] = strtol(split[2],NULL,16); - skill_split_atoi(split[3],skill_db[idx].unit_layout_type); - skill_split_atoi(split[4],skill_db[idx].unit_range); - skill_db[idx].unit_interval = atoi(split[5]); - - if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"friend")==0 ) skill_db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"party")==0 ) skill_db[idx].unit_target = BCT_PARTY; - else if( strcmpi(split[6],"ally")==0 ) skill_db[idx].unit_target = BCT_PARTY|BCT_GUILD; - else if( strcmpi(split[6],"guild")==0 ) skill_db[idx].unit_target = BCT_GUILD; - else if( strcmpi(split[6],"all")==0 ) skill_db[idx].unit_target = BCT_ALL; - else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx].unit_target = BCT_ENEMY; - else if( strcmpi(split[6],"self")==0 ) skill_db[idx].unit_target = BCT_SELF; - else if( strcmpi(split[6],"noone")==0 ) skill_db[idx].unit_target = BCT_NOONE; - else skill_db[idx].unit_target = strtol(split[6],NULL,16); - - skill_db[idx].unit_flag = strtol(split[7],NULL,16); - - if (skill_db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) - skill_db[idx].unit_target = BCT_NOENEMY; - - //By default, target just characters. - skill_db[idx].unit_target |= BL_CHAR; - if (skill_db[idx].unit_flag&UF_NOPC) - skill_db[idx].unit_target &= ~BL_PC; - if (skill_db[idx].unit_flag&UF_NOMOB) - skill_db[idx].unit_target &= ~BL_MOB; - if (skill_db[idx].unit_flag&UF_SKILL) - skill_db[idx].unit_target |= BL_SKILL; - - return true; -} - -static bool skill_parse_row_producedb(char* split[], int columns, int current) -{// ProduceItemID,ItemLV,RequireSkill,Requireskill_lv,MaterialID1,MaterialAmount1,...... - int x,y; - - int i = atoi(split[0]); - if( !i ) - return false; - - skill_produce_db[current].nameid = i; - skill_produce_db[current].itemlv = atoi(split[1]); - skill_produce_db[current].req_skill = atoi(split[2]); - skill_produce_db[current].req_skill_lv = atoi(split[3]); - - for( x = 4, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_PRODUCE_RESOURCE; x += 2, y++ ) - { - skill_produce_db[current].mat_id[y] = atoi(split[x]); - skill_produce_db[current].mat_amount[y] = atoi(split[x+1]); - } - - return true; -} - -static bool skill_parse_row_createarrowdb(char* split[], int columns, int current) -{// SourceID,MakeID1,MakeAmount1,...,MakeID5,MakeAmount5 - int x,y; - - int i = atoi(split[0]); - if( !i ) - return false; - - skill_arrow_db[current].nameid = i; - - for( x = 1, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_ARROW_RESOURCE; x += 2, y++ ) - { - skill_arrow_db[current].cre_id[y] = atoi(split[x]); - skill_arrow_db[current].cre_amount[y] = atoi(split[x+1]); - } - - return true; -} -static bool skill_parse_row_spellbookdb(char* split[], int columns, int current) -{// skill_id,PreservePoints - - uint16 skill_id = atoi(split[0]); - int points = atoi(split[1]); - int nameid = atoi(split[2]); - - if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) - ShowError("spellbook_db: Invalid skill ID %d\n", skill_id); - if ( !skill_get_inf(skill_id) ) - ShowError("spellbook_db: Passive skills cannot be memorized (%d/%s)\n", skill_id, skill_get_name(skill_id)); - if( points < 1 ) - ShowError("spellbook_db: PreservePoints have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id)); - else - { - skill_spellbook_db[current].skill_id = skill_id; - skill_spellbook_db[current].point = points; - skill_spellbook_db[current].nameid = nameid; - - return true; - } - - return false; -} -static bool skill_parse_row_improvisedb(char* split[], int columns, int current) -{// SkillID,Rate - uint16 skill_id = atoi(split[0]); - short j = atoi(split[1]); - - if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) { - ShowError("skill_improvise_db: Invalid skill ID %d\n", skill_id); - return false; - } - if ( !skill_get_inf(skill_id) ) { - ShowError("skill_improvise_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id)); - return false; - } - if( j < 1 ) { - ShowError("skill_improvise_db: Chances have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id)); - return false; - } - if( current >= MAX_SKILL_IMPROVISE_DB ) { - ShowError("skill_improvise_db: Maximum amount of entries reached (%d), increase MAX_SKILL_IMPROVISE_DB\n",MAX_SKILL_IMPROVISE_DB); - } - skill_improvise_db[current].skill_id = skill_id; - skill_improvise_db[current].per = j; // Still need confirm it. - - return true; -} -static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current) -{// SkillID - uint16 skill_id = atoi(split[0]); - - if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) - { - ShowError("magicmushroom_db: Invalid skill ID %d\n", skill_id); - return false; - } - if ( !skill_get_inf(skill_id) ) - { - ShowError("magicmushroom_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id)); - return false; - } - - skill_magicmushroom_db[current].skill_id = skill_id; - - return true; -} - -static bool skill_parse_row_reproducedb(char* split[], int column, int current) { - uint16 skill_id = atoi(split[0]); - uint16 idx = skill_get_index(skill_id); - if( !idx ) - return false; - - skill_reproduce_db[idx] = true; - - return true; -} - - -static bool skill_parse_row_abradb(char* split[], int columns, int current) -{// skill_id,DummyName,RequiredHocusPocusLevel,Rate - uint16 skill_id = atoi(split[0]); - if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) - { - ShowError("abra_db: Invalid skill ID %d\n", skill_id); - return false; - } - if ( !skill_get_inf(skill_id) ) - { - ShowError("abra_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id)); - return false; - } - - skill_abra_db[current].skill_id = skill_id; - skill_abra_db[current].req_lv = atoi(split[2]); - skill_abra_db[current].per = atoi(split[3]); - - return true; -} - -static bool skill_parse_row_changematerialdb(char* split[], int columns, int current) -{// ProductID,BaseRate,MakeAmount1,MakeAmountRate1...,MakeAmount5,MakeAmountRate5 - uint16 skill_id = atoi(split[0]); - short j = atoi(split[1]); - int x,y; - - for(x=0; x<MAX_SKILL_PRODUCE_DB; x++){ - if( skill_produce_db[x].nameid == skill_id ) - if( skill_produce_db[x].req_skill == GN_CHANGEMATERIAL ) - break; - } - - if( x >= MAX_SKILL_PRODUCE_DB ){ - ShowError("changematerial_db: Not supported item ID(%d) for Change Material. \n", skill_id); - return false; - } - - if( current >= MAX_SKILL_PRODUCE_DB ) { - ShowError("skill_changematerial_db: Maximum amount of entries reached (%d), increase MAX_SKILL_PRODUCE_DB\n",MAX_SKILL_PRODUCE_DB); - } - - skill_changematerial_db[current].itemid = skill_id; - skill_changematerial_db[current].rate = j; - - for( x = 2, y = 0; x+1 < columns && split[x] && split[x+1] && y < 5; x += 2, y++ ) - { - skill_changematerial_db[current].qty[y] = atoi(split[x]); - skill_changematerial_db[current].qty_rate[y] = atoi(split[x+1]); - } - - return true; -} - -/*=============================== - * DB reading. - * skill_db.txt - * skill_require_db.txt - * skill_cast_db.txt - * skill_castnodex_db.txt - * skill_nocast_db.txt - * skill_unit_db.txt - * produce_db.txt - * create_arrow_db.txt - * abra_db.txt - *------------------------------*/ -static void skill_readdb(void) -{ - // init skill db structures - db_clear(skilldb_name2id); - memset(skill_db,0,sizeof(skill_db)); - 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)); - memset(skill_reproduce_db,0,sizeof(skill_reproduce_db)); - memset(skill_changematerial_db,0,sizeof(skill_changematerial_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, DBPATH"skill_db.txt" , ',', 17, 17, MAX_SKILL_DB, skill_parse_row_skilldb); - sv_readdb(db_path, DBPATH"skill_require_db.txt" , ',', 32, 32, MAX_SKILL_DB, skill_parse_row_requiredb); -#ifdef RENEWAL_CAST - sv_readdb(db_path, "re/skill_cast_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_castdb); -#else - sv_readdb(db_path, "pre-re/skill_cast_db.txt" , ',', 7, 7, MAX_SKILL_DB, skill_parse_row_castdb); -#endif - sv_readdb(db_path, DBPATH"skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill_parse_row_castnodexdb); - sv_readdb(db_path, DBPATH"skill_unit_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_unitdb); - - sv_readdb(db_path, DBPATH"skill_nocast_db.txt" , ',', 2, 2, MAX_SKILL_DB, skill_parse_row_nocastdb); - - skill_init_unit_layout(); - 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); - sv_readdb(db_path, "skill_reproduce_db.txt", ',', 1, 1, MAX_SKILL_DB, skill_parse_row_reproducedb); - sv_readdb(db_path, "skill_improvise_db.txt" , ',', 2, 2, MAX_SKILL_IMPROVISE_DB, skill_parse_row_improvisedb); - sv_readdb(db_path, "skill_changematerial_db.txt" , ',', 4, 4+2*5, MAX_SKILL_PRODUCE_DB, skill_parse_row_changematerialdb); - -} - -void skill_reload (void) { - struct s_mapiterator *iter; - struct map_session_data *sd; - skill_readdb(); - /* lets update all players skill tree : so that if any skill modes were changed they're properly updated */ - iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) - clif_skillinfoblock(sd); - mapit_free(iter); - -} - -/*========================================== - * - *------------------------------------------*/ -int do_init_skill (void) -{ - skilldb_name2id = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, 0); - skill_readdb(); - - group_db = idb_alloc(DB_OPT_BASE); - skillunit_db = idb_alloc(DB_OPT_BASE); - skillcd_db = idb_alloc(DB_OPT_RELEASE_DATA); - skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA); - skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_NONE); - skill_timer_ers = ers_new(sizeof(struct skill_timerskill),"skill.c::skill_timer_ers",ERS_OPT_NONE); - - add_timer_func_list(skill_unit_timer,"skill_unit_timer"); - add_timer_func_list(skill_castend_id,"skill_castend_id"); - add_timer_func_list(skill_castend_pos,"skill_castend_pos"); - add_timer_func_list(skill_timerskill,"skill_timerskill"); - add_timer_func_list(skill_blockpc_end, "skill_blockpc_end"); - - add_timer_interval(gettick()+SKILLUNITTIMER_INTERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INTERVAL); - - return 0; -} - -int do_final_skill(void) -{ - db_destroy(skilldb_name2id); - db_destroy(group_db); - db_destroy(skillunit_db); - db_destroy(skillcd_db); - db_destroy(skillusave_db); - ers_destroy(skill_unit_ers); - ers_destroy(skill_timer_ers); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "../common/utils.h"
+#include "../common/ers.h"
+
+#include "map.h"
+#include "path.h"
+#include "clif.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "pet.h"
+#include "homunculus.h"
+#include "mercenary.h"
+#include "elemental.h"
+#include "mob.h"
+#include "npc.h"
+#include "battle.h"
+#include "battleground.h"
+#include "party.h"
+#include "itemdb.h"
+#include "script.h"
+#include "intif.h"
+#include "log.h"
+#include "chrif.h"
+#include "guild.h"
+#include "date.h"
+#include "unit.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+
+
+#define SKILLUNITTIMER_INTERVAL 100
+
+// ranges reserved for mapping skill ids to skilldb offsets
+#define HM_SKILLRANGEMIN 700
+#define HM_SKILLRANGEMAX HM_SKILLRANGEMIN + MAX_HOMUNSKILL
+#define MC_SKILLRANGEMIN HM_SKILLRANGEMAX + 1
+#define MC_SKILLRANGEMAX MC_SKILLRANGEMIN + MAX_MERCSKILL
+#define EL_SKILLRANGEMIN MC_SKILLRANGEMAX + 1
+#define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL
+#define GD_SKILLRANGEMIN EL_SKILLRANGEMAX + 1
+#define GD_SKILLRANGEMAX GD_SKILLRANGEMIN + MAX_GUILDSKILL
+
+#if GD_SKILLRANGEMAX > 999
+ #error GD_SKILLRANGEMAX is greater than 999
+#endif
+static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex]
+static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex]
+
+DBMap* skillunit_db = NULL; // int id -> struct skill_unit*
+
+DBMap* skilldb_name2id = NULL;
+
+/**
+ * Skill Cool Down Delay Saving
+ * Struct skill_cd is not a member of struct map_session_data
+ * to keep cooldowns in memory between player log-ins.
+ * All cooldowns are reset when server is restarted.
+ **/
+DBMap* skillcd_db = NULL; // char_id -> struct skill_cd
+struct skill_cd {
+ int duration[MAX_SKILL_TREE];//milliseconds
+ short skidx[MAX_SKILL_TREE];//the skill index entries belong to
+ short nameid[MAX_SKILL_TREE];//skill id
+ unsigned char cursor;
+};
+
+/**
+ * Skill Unit Persistency during endack routes (mostly for songs see bugreport:4574)
+ **/
+DBMap* skillusave_db = NULL; // char_id -> struct skill_usave
+struct skill_usave {
+ uint16 skill_id, skill_lv;
+};
+
+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];
+struct s_skill_improvise_db {
+ uint16 skill_id;
+ short per;//1-10000
+};
+struct s_skill_improvise_db skill_improvise_db[MAX_SKILL_IMPROVISE_DB];
+bool skill_reproduce_db[MAX_SKILL_DB];
+struct s_skill_changematerial_db {
+ int itemid;
+ short rate;
+ int qty[5];
+ short qty_rate[5];
+};
+struct s_skill_changematerial_db skill_changematerial_db[MAX_SKILL_PRODUCE_DB];
+
+//Warlock
+struct s_skill_spellbook_db {
+ int nameid;
+ uint16 skill_id;
+ int point;
+};
+
+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_block_check(struct block_list *bl, enum sc_type type, uint16 skill_id);
+static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
+static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
+static int skill_destroy_trap( struct block_list *bl, va_list ap );
+//Since only mob-casted splash skills can hit ice-walls
+static inline int splash_target(struct block_list* bl)
+{
+#ifndef RENEWAL
+ return ( bl->type == BL_MOB ) ? BL_SKILL|BL_CHAR : BL_CHAR;
+#else // Some skills can now hit ground skills(traps, ice wall & etc.)
+ return BL_SKILL|BL_CHAR;
+#endif
+}
+
+/// Returns the id of the skill, or 0 if not found.
+int skill_name2id(const char* name)
+{
+ if( name == NULL )
+ return 0;
+
+ return strdb_iget(skilldb_name2id, name);
+}
+
+/// Maps skill ids to skill db offsets.
+/// Returns the skill's array index, or 0 (Unknown Skill).
+int skill_get_index( uint16 skill_id )
+{
+ // avoid ranges reserved for mapping guild/homun/mercenary skills
+ if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX)
+ || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX)
+ || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX)
+ || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) )
+ return 0;
+
+ // map skill id to skill db index
+ if( skill_id >= GD_SKILLBASE )
+ skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE;
+ else if( skill_id >= EL_SKILLBASE )
+ skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE;
+ else if( skill_id >= MC_SKILLBASE )
+ skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE;
+ else if( skill_id >= HM_SKILLBASE )
+ skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE;
+
+ // validate result
+ if( !skill_id || skill_id >= MAX_SKILL_DB )
+ return 0;
+
+ return skill_id;
+}
+
+const char* skill_get_name( uint16 skill_id )
+{
+ return skill_db[skill_get_index(skill_id)].name;
+}
+
+const char* skill_get_desc( uint16 skill_id )
+{
+ return skill_db[skill_get_index(skill_id)].desc;
+}
+
+// out of bounds error checking [celest]
+static void skill_chk(int16* skill_id, uint16 skill_lv)
+{
+ *skill_id = skill_get_index(*skill_id); // checks/adjusts id
+ if( skill_lv > MAX_SKILL_LEVEL ) *skill_id = 0;
+}
+
+#define skill_get(var,id,lv) { skill_chk(&id,lv); if(!id) return 0; return var; }
+
+// Skill DB
+int skill_get_hit( uint16 skill_id ) { skill_get (skill_db[skill_id].hit, skill_id, 1); }
+int skill_get_inf( uint16 skill_id ) { skill_get (skill_db[skill_id].inf, skill_id, 1); }
+int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].element[skill_lv-1], skill_id, skill_lv); }
+int skill_get_nk( uint16 skill_id ) { skill_get (skill_db[skill_id].nk, skill_id, 1); }
+int skill_get_max( uint16 skill_id ) { skill_get (skill_db[skill_id].max, skill_id, 1); }
+int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].range[skill_lv-1], skill_id, skill_lv); }
+int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { skill_get ( (skill_db[skill_id].splash[skill_lv-1]>=0?skill_db[skill_id].splash[skill_lv-1]:AREA_SIZE), skill_id, skill_lv); }
+int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].hp[skill_lv-1], skill_id, skill_lv); }
+int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].sp[skill_lv-1], skill_id, skill_lv); }
+int skill_get_hp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].hp_rate[skill_lv-1], skill_id, skill_lv); }
+int skill_get_sp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].sp_rate[skill_lv-1], skill_id, skill_lv); }
+int skill_get_state(uint16 skill_id) { skill_get (skill_db[skill_id].state, skill_id, 1); }
+int skill_get_spiritball(uint16 skill_id, uint16 skill_lv) { skill_get (skill_db[skill_id].spiritball[skill_lv-1], skill_id, skill_lv); }
+int skill_get_itemid(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].itemid[idx], skill_id, 1); }
+int skill_get_itemqty(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].amount[idx], skill_id, 1); }
+int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].zeny[skill_lv-1], skill_id, skill_lv); }
+int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].num[skill_lv-1], skill_id, skill_lv); }
+int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].cast[skill_lv-1], skill_id, skill_lv); }
+int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].delay[skill_lv-1], skill_id, skill_lv); }
+int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].walkdelay[skill_lv-1], skill_id, skill_lv); }
+int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time[skill_lv-1], skill_id, skill_lv); }
+int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time2[skill_lv-1], skill_id, skill_lv); }
+int skill_get_castdef( uint16 skill_id ) { skill_get (skill_db[skill_id].cast_def_rate, skill_id, 1); }
+int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_db[skill_id].weapon, skill_id, 1); }
+int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_db[skill_id].ammo, skill_id, 1); }
+int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].ammo_qty[skill_lv-1], skill_id, skill_lv); }
+int skill_get_inf2( uint16 skill_id ) { skill_get (skill_db[skill_id].inf2, skill_id, 1); }
+int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_db[skill_id].castcancel, skill_id, 1); }
+int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].maxcount[skill_lv-1], skill_id, skill_lv); }
+int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].blewcount[skill_lv-1], skill_id, skill_lv); }
+int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].mhp[skill_lv-1], skill_id, skill_lv); }
+int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].castnodex[skill_lv-1], skill_id, skill_lv); }
+int skill_get_delaynodex( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].delaynodex[skill_lv-1], skill_id, skill_lv); }
+int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_db[skill_id].nocast, skill_id, 1); }
+int skill_get_type( uint16 skill_id ) { skill_get (skill_db[skill_id].skill_type, skill_id, 1); }
+int skill_get_unit_id ( uint16 skill_id, int flag ){ skill_get (skill_db[skill_id].unit_id[flag], skill_id, 1); }
+int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_interval, skill_id, 1); }
+int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_range[skill_lv-1], skill_id, skill_lv); }
+int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BCT_ALL, skill_id, 1); }
+int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BL_ALL, skill_id, 1); }
+int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_flag, skill_id, 1); }
+int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_layout_type[skill_lv-1], skill_id, skill_lv); }
+int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].cooldown[skill_lv-1], skill_id, skill_lv); }
+#ifdef RENEWAL_CAST
+int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].fixed_cast[skill_lv-1], skill_id, skill_lv); }
+#endif
+int skill_tree_get_max(uint16 skill_id, int b_class)
+{
+ int i;
+ b_class = pc_class2idx(b_class);
+
+ ARR_FIND( 0, MAX_SKILL_TREE, i, skill_tree[b_class][i].id == 0 || skill_tree[b_class][i].id == skill_id );
+ if( i < MAX_SKILL_TREE && skill_tree[b_class][i].id == skill_id )
+ return skill_tree[b_class][i].max;
+ else
+ return skill_get_max(skill_id);
+}
+
+int skill_frostjoke_scream(struct block_list *bl,va_list ap);
+int skill_attack_area(struct block_list *bl,va_list ap);
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex]
+int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris]
+int skill_greed(struct block_list *bl, va_list ap);
+static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id);
+static int skill_cell_overlap(struct block_list *bl, va_list ap);
+static int skill_trap_splash(struct block_list *bl, va_list ap);
+struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick);
+static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick);
+static int skill_unit_onleft(uint16 skill_id, struct block_list *bl,unsigned int tick);
+static int skill_unit_effect(struct block_list *bl,va_list ap);
+
+int enchant_eff[5] = { 10, 14, 17, 19, 20 };
+int deluge_eff[5] = { 5, 9, 12, 14, 15 };
+
+int skill_get_casttype (uint16 skill_id)
+{
+ int inf = skill_get_inf(skill_id);
+ if (inf&(INF_GROUND_SKILL))
+ return CAST_GROUND;
+ if (inf&INF_SUPPORT_SKILL)
+ return CAST_NODAMAGE;
+ if (inf&INF_SELF_SKILL) {
+ if(skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF)
+ return CAST_DAMAGE; //Combo skill.
+ return CAST_NODAMAGE;
+ }
+ if (skill_get_nk(skill_id)&NK_NO_DAMAGE)
+ return CAST_NODAMAGE;
+ return CAST_DAMAGE;
+}
+
+//Returns actual skill range taking into account attack range and AC_OWL [Skotlex]
+int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv)
+{
+ int range;
+ if( bl->type == BL_MOB && battle_config.mob_ai&0x400 )
+ return 9; //Mobs have a range of 9 regardless of skill used.
+
+ range = skill_get_range(skill_id, skill_lv);
+
+ if( range < 0 )
+ {
+ if( battle_config.use_weapon_skill_range&bl->type )
+ return status_get_range(bl);
+ range *=-1;
+ }
+
+ //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE
+ switch( skill_id )
+ {
+ case AC_SHOWER: case MA_SHOWER:
+ case AC_DOUBLE: case MA_DOUBLE:
+ case HT_BLITZBEAT:
+ case AC_CHARGEARROW:
+ 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
+ range += 10; //Assume level 10?
+ break;
+ // added to allow GS skills to be effected by the range of Snake Eyes [Reddozen]
+ case GS_RAPIDSHOWER:
+ case GS_PIERCINGSHOT:
+ case GS_FULLBUSTER:
+ case GS_SPREADATTACK:
+ case GS_GROUNDDRIFT:
+ if (bl->type == BL_PC)
+ range += pc_checkskill((TBL_PC*)bl, GS_SNAKEEYE);
+ else
+ range += 10; //Assume level 10?
+ break;
+ case NJ_KIRIKAGE:
+ 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 )
+ return 9; // Enable non players to use self skills on others. [Skotlex]
+ return range;
+}
+
+int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal) {
+ 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;
+
+ switch( skill_id ) {
+ case BA_APPLEIDUN:
+ #ifdef RENEWAL
+ hp = 100+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery
+ #else
+ hp = 30+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery
+ #endif
+ if( sd )
+ hp += 5*pc_checkskill(sd,BA_MUSICALLESSON);
+ break;
+ case PR_SANCTUARY:
+ hp = (skill_lv>6)?777:skill_lv*100;
+ break;
+ case NPC_EVILLAND:
+ hp = (skill_lv>6)?666:skill_lv*100;
+ break;
+ default:
+ if (skill_lv >= battle_config.max_heal_lv)
+ return battle_config.max_heal;
+ #ifdef RENEWAL
+ /**
+ * Renewal Heal Formula
+ * Formula: ( [(Base Level + INT) / 5] × 30 ) × (Heal Level / 10) × (Modifiers) + MATK
+ **/
+ hp = (status_get_lv(src) + status_get_int(src)) / 5 * 30 * skill_lv / 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) )
+ hp += hp * skill * 2 / 100;
+ else if( src->type == BL_HOM && (skill = merc_hom_checkskill(((TBL_HOM*)src), HLIF_BRAIN)) > 0 )
+ hp += hp * skill * 2 / 100;
+ break;
+ }
+
+ if( ( (target && target->type == BL_MER) || !heal ) && skill_id != NPC_EVILLAND )
+ hp >>= 1;
+
+ if( sd && (skill = pc_skillheal_bonus(sd, skill_id)) )
+ hp += hp*skill/100;
+
+ if( tsd && (skill = pc_skillheal2_bonus(tsd, skill_id)) )
+ 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]
+ hp -= hp * sc->data[SC_CRITICALWOUND]->val2/100;
+ if( sc->data[SC_DEATHHURT] && heal )
+ hp -= hp * 20/100;
+ if( sc->data[SC_INCHEALRATE] && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN )
+ hp += hp * sc->data[SC_INCHEALRATE]->val1/100; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish]
+ if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
+ hp += hp / 10;
+ }
+
+#ifdef RENEWAL
+ // MATK part of the RE heal formula [malufett]
+ // Note: in this part matk bonuses from items or skills are not applied
+ switch( skill_id ) {
+ case BA_APPLEIDUN: case PR_SANCTUARY:
+ case NPC_EVILLAND: break;
+ default:
+ {
+ struct status_data *status = status_get_status_data(src);
+ int min, max, wMatk, variance;
+
+ min = max = status_base_matk(status, status_get_lv(src));
+ if( status->rhw.matk > 0 ){
+ wMatk = status->rhw.matk;
+ variance = wMatk * status->rhw.wlv / 10;
+ min += wMatk - variance;
+ max += wMatk + variance;
+ }
+
+ if( sc && sc->data[SC_RECOGNIZEDSPELL] )
+ min = max;
+
+ if( sd && sd->right_weapon.overrefine > 0 ){
+ min++;
+ max += sd->right_weapon.overrefine - 1;
+ }
+
+ if(max > min)
+ hp += min+rnd()%(max-min);
+ else
+ hp += min;
+ }
+ }
+#endif
+ return hp;
+}
+
+// Making plagiarize check its own function [Aru]
+int can_copy (struct map_session_data *sd, uint16 skill_id, struct block_list* bl)
+{
+ // Never copy NPC/Wedding Skills
+ if (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL))
+ return 0;
+
+ // High-class skills
+ if((skill_id >= LK_AURABLADE && skill_id <= ASC_CDP) || (skill_id >= ST_PRESERVE && skill_id <= CR_CULTIVATION))
+ {
+ if(battle_config.copyskill_restrict == 2)
+ return 0;
+ else if(battle_config.copyskill_restrict)
+ return (sd->status.class_ == JOB_STALKER);
+ }
+
+ //Added so plagarize can't copy agi/bless if you're undead since it damages you
+ if ((skill_id == AL_INCAGI || skill_id == AL_BLESSING ||
+ skill_id == CASH_BLESSING || skill_id == CASH_INCAGI ||
+ skill_id == MER_INCAGI || skill_id == MER_BLESSING))
+ return 0;
+
+ // Couldn't preserve 3rd Class skills except only when using Reproduce skill. [Jobbie]
+ if( !(sd->sc.data[SC__REPRODUCE]) && (skill_id >= RK_ENCHANTBLADE && skill_id <= SR_RIDEINLIGHTNING) )
+ return 0;
+ // Reproduce will only copy skills according on the list. [Jobbie]
+ else if( sd->sc.data[SC__REPRODUCE] && !skill_reproduce_db[skill_id] )
+ return 0;
+
+ return 1;
+}
+
+// [MouseJstr] - skill ok to cast? and when?
+int skillnotok (uint16 skill_id, struct map_session_data *sd)
+{
+ int16 idx,m;
+ nullpo_retr (1, sd);
+ m = sd->bl.m;
+ idx = skill_get_index(skill_id);
+
+ if (idx == 0)
+ return 1; // invalid skill id
+
+ if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL))
+ return 0; // can do any damn thing they want
+
+ if( skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2 )
+ return 0; // Teleport lv 3 bypasses this check.[Inkfish]
+
+ // Epoque:
+ // This code will compare the player's attack motion value which is influenced by ASPD before
+ // allowing a skill to be cast. This is to prevent no-delay ACT files from spamming skills such as
+ // AC_DOUBLE which do not have a skill delay and are not regarded in terms of attack motion.
+ if( !sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick &&
+ DIFF_TICK(gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100) )
+ {// attempted to cast a skill before the attack motion has finished
+ return 1;
+ }
+
+ if (sd->blockskill[idx] > 0){
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return 1;
+ }
+ /**
+ * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions above
+ * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map
+ **/
+ if( sd->skillitem == skill_id )
+ return 0;
+ // Check skill restrictions [Celest]
+ if( (!map_flag_vs(m) && skill_get_nocast (skill_id) & 1) ||
+ (map[m].flag.pvp && skill_get_nocast (skill_id) & 2) ||
+ (map_flag_gvg(m) && skill_get_nocast (skill_id) & 4) ||
+ (map[m].flag.battleground && skill_get_nocast (skill_id) & 8) ||
+ (map[m].flag.restricted && map[m].zone && skill_get_nocast (skill_id) & (8*map[m].zone)) ){
+ clif_msg(sd, 0x536); // This skill cannot be used within this area
+ 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 (skill_id) {
+ case AL_WARP:
+ case RETURN_TO_ELDICASTES:
+ case ALL_GUARDIAN_RECALL:
+ if(map[m].flag.nowarp) {
+ clif_skill_teleportmessage(sd,0);
+ return 1;
+ }
+ return 0;
+ case AL_TELEPORT:
+ case SC_FATALMENACE:
+ case SC_DIMENSIONDOOR:
+ if(map[m].flag.noteleport) {
+ clif_skill_teleportmessage(sd,0);
+ return 1;
+ }
+ return 0; // gonna be checked in 'skill_castend_nodamage_id'
+ case WE_CALLPARTNER:
+ case WE_CALLPARENT:
+ case WE_CALLBABY:
+ if (map[m].flag.nomemo) {
+ clif_skill_teleportmessage(sd,1);
+ return 1;
+ }
+ break;
+ case MC_VENDING:
+ case MC_IDENTIFY:
+ case ALL_BUYING_STORE:
+ return 0; // always allowed
+ case WZ_ICEWALL:
+ // noicewall flag [Valaris]
+ if (map[m].flag.noicewall) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+ case GC_DARKILLUSION:
+ if( map_flag_gvg(m) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+ case GD_EMERGENCYCALL:
+ if (
+ !(battle_config.emergency_call&((agit_flag || agit2_flag)?2:1)) ||
+ !(battle_config.emergency_call&(map[m].flag.gvg || map[m].flag.gvg_castle?8:4)) ||
+ (battle_config.emergency_call&16 && map[m].flag.nowarpto && !map[m].flag.gvg_castle)
+ ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ 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( pc_ismadogear(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+
+ case WM_SIRCLEOFNATURE:
+ case WM_SOUND_OF_DESTRUCTION:
+ case SC_MANHOLE:
+ case WM_LULLABY_DEEPSLEEP:
+ case WM_SATURDAY_NIGHT_FEVER:
+ if( !map_flag_vs(m) ) {
+ clif_skill_teleportmessage(sd,2); // This skill uses this msg instead of skill fails.
+ return 1;
+ }
+ break;
+
+ }
+ return (map[m].flag.noskill);
+}
+
+int skillnotok_hom(uint16 skill_id, struct homun_data *hd)
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr(1,hd);
+
+ if (idx == 0)
+ return 1; // invalid skill id
+
+ if (hd->blockskill[idx] > 0)
+ return 1;
+ switch(skill_id){
+ case MH_LIGHT_OF_REGENE:
+ if(hd->homunculus.intimacy <= 750) //if not cordial
+ return 1;
+ break;
+ case MH_OVERED_BOOST:
+ if(hd->homunculus.hunger <= 1) //if we starving
+ return 1;
+ case MH_GOLDENE_FERSE: //can be used with angriff
+ if(hd->sc.data[SC_ANGRIFFS_MODUS])
+ return 1;
+ case MH_ANGRIFFS_MODUS:
+ if(hd->sc.data[SC_GOLDENE_FERSE])
+ return 1;
+ break;
+ }
+
+ //Use master's criteria.
+ return skillnotok(skill_id, hd->master);
+}
+
+int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md)
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr(1,md);
+
+ if( idx == 0 )
+ return 1; // Invalid Skill ID
+ if( md->blockskill[idx] > 0 )
+ return 1;
+
+ return skillnotok(skill_id, md->master);
+}
+
+struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill_lv, struct block_list* src, int x, int y)
+{
+ int pos = skill_get_unit_layout_type(skill_id,skill_lv);
+ uint8 dir;
+
+ if (pos < -1 || pos >= MAX_SKILL_UNIT_LAYOUT) {
+ ShowError("skill_get_unit_layout: unsupported layout type %d for skill %d (level %d)\n", pos, skill_id, skill_lv);
+ pos = cap_value(pos, 0, MAX_SQUARE_LAYOUT); // cap to nearest square layout
+ }
+
+ if (pos != -1) // simple single-definition layout
+ return &skill_unit_layout[pos];
+
+ dir = (src->x == x && src->y == y) ? 6 : map_calc_dir(src,x,y); // 6 - default aegis direction
+
+ if (skill_id == MG_FIREWALL)
+ return &skill_unit_layout [firewall_unit_pos + dir];
+ else if (skill_id == WZ_ICEWALL)
+ return &skill_unit_layout [icewall_unit_pos + dir];
+ else if( skill_id == 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", skill_id, skill_lv);
+ return &skill_unit_layout[0]; // default 1x1 layout
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int dmg_lv, unsigned int tick)
+{
+ struct map_session_data *sd, *dstsd;
+ struct mob_data *md, *dstmd;
+ struct status_data *sstatus, *tstatus;
+ struct status_change *sc, *tsc;
+
+ enum sc_type status;
+ int skill;
+ int rate;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest
+
+ if( dmg_lv < ATK_BLOCK ) // Don't apply effect if miss.
+ return 0;
+
+ sd = BL_CAST(BL_PC, src);
+ md = BL_CAST(BL_MOB, src);
+ dstsd = BL_CAST(BL_PC, bl);
+ dstmd = BL_CAST(BL_MOB, bl);
+
+ sc = status_get_sc(src);
+ tsc = status_get_sc(bl);
+ sstatus = status_get_status_data(src);
+ tstatus = status_get_status_data(bl);
+ if (!tsc) //skill additional effect is about adding effects to the target...
+ //So if the target can't be inflicted with statuses, this is pointless.
+ return 0;
+
+ if( sd )
+ { // These statuses would be applied anyway even if the damage was blocked by some skills. [Inkfish]
+ if( skill_id != WS_CARTTERMINATION && skill_id != AM_DEMONSTRATION && skill_id != CR_REFLECTSHIELD && skill_id != MS_REFLECTSHIELD && skill_id != ASC_BREAKER )
+ { // Trigger status effects
+ enum sc_type type;
+ int i;
+ for( i = 0; i < ARRAYLENGTH(sd->addeff) && sd->addeff[i].flag; i++ )
+ {
+ rate = sd->addeff[i].rate;
+ if( attack_type&BF_LONG ) // Any ranged physical attack takes status arrows into account (Grimtooth...) [DracoRPG]
+ rate += sd->addeff[i].arrow_rate;
+ if( !rate ) continue;
+
+ if( (sd->addeff[i].flag&(ATF_WEAPON|ATF_MAGIC|ATF_MISC)) != (ATF_WEAPON|ATF_MAGIC|ATF_MISC) )
+ { // Trigger has attack type consideration.
+ if( (sd->addeff[i].flag&ATF_WEAPON && attack_type&BF_WEAPON) ||
+ (sd->addeff[i].flag&ATF_MAGIC && attack_type&BF_MAGIC) ||
+ (sd->addeff[i].flag&ATF_MISC && attack_type&BF_MISC) ) ;
+ else
+ continue;
+ }
+
+ if( (sd->addeff[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT) )
+ { // Trigger has range consideration.
+ if((sd->addeff[i].flag&ATF_LONG && !(attack_type&BF_LONG)) ||
+ (sd->addeff[i].flag&ATF_SHORT && !(attack_type&BF_SHORT)))
+ continue; //Range Failed.
+ }
+
+ type = sd->addeff[i].id;
+ skill = skill_get_time2(status_sc2skill(type),7);
+
+ if (sd->addeff[i].flag&ATF_TARGET)
+ status_change_start(bl,type,rate,7,0,0,0,skill,0);
+
+ if (sd->addeff[i].flag&ATF_SELF)
+ status_change_start(src,type,rate,7,0,0,0,skill,0);
+ }
+ }
+
+ if( skill_id )
+ { // Trigger status effects on skills
+ enum sc_type type;
+ int i;
+ for( i = 0; i < ARRAYLENGTH(sd->addeff3) && sd->addeff3[i].skill; i++ )
+ {
+ if( skill_id != sd->addeff3[i].skill || !sd->addeff3[i].rate )
+ continue;
+ type = sd->addeff3[i].id;
+ skill = skill_get_time2(status_sc2skill(type),7);
+
+ if( sd->addeff3[i].target&ATF_TARGET )
+ status_change_start(bl,type,sd->addeff3[i].rate,7,0,0,0,skill,0);
+ if( sd->addeff3[i].target&ATF_SELF )
+ status_change_start(src,type,sd->addeff3[i].rate,7,0,0,0,skill,0);
+ }
+ }
+ }
+
+ if( dmg_lv < ATK_DEF ) // no damage, return;
+ return 0;
+
+ switch(skill_id)
+ {
+ case 0: // Normal attacks (no skill used)
+ {
+ if( attack_type&BF_SKILL )
+ break; // If a normal attack is a skill, it's splash damage. [Inkfish]
+ if(sd) {
+ // Automatic trigger of Blitz Beat
+ if (pc_isfalcon(sd) && sd->status.weapon == W_BOW && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 &&
+ rnd()%1000 <= sstatus->luk*10/3+1 ) {
+ 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 && rnd()%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 &&
+ (skill*15 + 55) + pc_checkskill(sd,TF_STEAL)*10 > rnd()%1000) {
+ if(pc_steal_item(sd,bl,pc_checkskill(sd,TF_STEAL)))
+ clif_skill_nodamage(src,bl,TF_STEAL,skill,1);
+ else
+ clif_skill_fail(sd,RG_SNATCHER,USESKILL_FAIL_LEVEL,0);
+ }
+ // Chance to trigger Taekwon kicks [Dralnu]
+ if(sc && !sc->data[SC_COMBO]) {
+ if(sc->data[SC_READYSTORM] &&
+ sc_start(src,SC_COMBO, 15, TK_STORMKICK,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex)))
+ ; //Stance triggered
+ else if(sc->data[SC_READYDOWN] &&
+ sc_start(src,SC_COMBO, 15, TK_DOWNKICK,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex)))
+ ; //Stance triggered
+ else if(sc->data[SC_READYTURN] &&
+ sc_start(src,SC_COMBO, 15, TK_TURNKICK,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex)))
+ ; //Stance triggered
+ else if (sc->data[SC_READYCOUNTER]) { //additional chance from SG_FRIEND [Komurka]
+ rate = 20;
+ if (sc->data[SC_SKILLRATE_UP] && sc->data[SC_SKILLRATE_UP]->val1 == TK_COUNTER) {
+ rate += rate*sc->data[SC_SKILLRATE_UP]->val2/100;
+ status_change_end(src, SC_SKILLRATE_UP, INVALID_TIMER);
+ }
+ sc_start2(src, SC_COMBO, rate, TK_COUNTER, bl->id,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex));
+ }
+ }
+ if(sc && sc->data[SC_PYROCLASTIC] && (rnd() % 1000 <= sstatus->luk * 10 / 3 + 1) )
+ skill_castend_pos2(src, bl->x, bl->y, BS_HAMMERFALL,sc->data[SC_PYROCLASTIC]->val1, tick, 0);
+ }
+
+ if (sc) {
+ struct status_change_entry *sce;
+ // Enchant Poison gives a chance to poison attacked enemies
+ if((sce=sc->data[SC_ENCPOISON])) //Don't use sc_start since chance comes in 1/10000 rate.
+ status_change_start(bl,SC_POISON,sce->val2, sce->val1,src->id,0,0,
+ skill_get_time2(AS_ENCHANTPOISON,sce->val1),0);
+ // Enchant Deadly Poison gives a chance to deadly poison attacked enemies
+ if((sce=sc->data[SC_EDP]))
+ sc_start4(bl,SC_DPOISON,sce->val2, sce->val1,src->id,0,0,
+ skill_get_time2(ASC_EDP,sce->val1));
+ }
+ }
+ break;
+
+ case SM_BASH:
+ if( sd && skill_lv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){
+ //TODO: How much % per base level it actually is?
+ sc_start(bl,SC_STUN,(5*(skill_lv-5)+(int)sd->status.base_level/10),
+ skill_lv,skill_get_time2(SM_FATALBLOW,skill_lv));
+ }
+ break;
+
+ case MER_CRASH:
+ sc_start(bl,SC_STUN,(6*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case AS_VENOMKNIFE:
+ if (sd) //Poison chance must be that of Envenom. [Skotlex]
+ skill_lv = pc_checkskill(sd, TF_POISON);
+ case TF_POISON:
+ case AS_SPLASHER:
+ if(!sc_start2(bl,SC_POISON,(4*skill_lv+10),skill_lv,src->id,skill_get_time2(skill_id,skill_lv))
+ && sd && skill_id==TF_POISON
+ )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case AS_SONICBLOW:
+ sc_start(bl,SC_STUN,(2*skill_lv+10),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case WZ_FIREPILLAR:
+ unit_set_walkdelay(bl, tick, skill_get_time2(skill_id, skill_lv), 1);
+ break;
+
+ case MG_FROSTDIVER:
+#ifndef RENEWAL
+ case WZ_FROSTNOVA:
+#endif
+ sc_start(bl,SC_FREEZE,skill_lv*3+35,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+#ifdef RENEWAL
+ case WZ_FROSTNOVA:
+ sc_start(bl,SC_FREEZE,skill_lv*5+33,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+#endif
+
+ case WZ_STORMGUST:
+ /**
+ * Storm Gust counter was dropped in renewal
+ **/
+ #ifdef RENEWAL
+ sc_start(bl,SC_FREEZE,65-(5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ #else
+ //Tharis pointed out that this is normal freeze chance with a base of 300%
+ if(tsc->sg_counter >= 3 &&
+ sc_start(bl,SC_FREEZE,300,skill_lv,skill_get_time2(skill_id,skill_lv)))
+ tsc->sg_counter = 0;
+ /**
+ * being it only resets on success it'd keep stacking and eventually overflowing on mvps, so we reset at a high value
+ **/
+ else if( tsc->sg_counter > 250 )
+ tsc->sg_counter = 0;
+ #endif
+ break;
+
+ case WZ_METEOR:
+ sc_start(bl,SC_STUN,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case WZ_VERMILION:
+ sc_start(bl,SC_BLIND,4*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ sc_start(bl,SC_FREEZE,(3*skill_lv+35),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_FLASHER:
+ sc_start(bl,SC_BLIND,(10*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ sc_start(bl,SC_STUN,(5*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_SHOCKWAVE:
+ status_percent_damage(src, bl, 0, 15*skill_lv+5, false);
+ break;
+
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ sc_start(bl,SC_SLEEP,(10*skill_lv+40),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case TF_SPRINKLESAND:
+ sc_start(bl,SC_BLIND,20,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case TF_THROWSTONE:
+ sc_start(bl,SC_STUN,3,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_BLIND,3,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case NPC_DARKCROSS:
+ case CR_HOLYCROSS:
+ sc_start(bl,SC_BLIND,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ //Chance to cause blind status vs demon and undead element, but not against players
+ if(!dstsd && (battle_check_undead(tstatus->race,tstatus->def_ele) || tstatus->race == RC_DEMON))
+ sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ attack_type |= BF_WEAPON;
+ break;
+
+ case AM_ACIDTERROR:
+ sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv));
+ if (skill_break_equip(bl, EQP_ARMOR, 100*skill_get_time(skill_id,skill_lv), BCT_ENEMY))
+ clif_emotion(bl,E_OMG);
+ break;
+
+ case AM_DEMONSTRATION:
+ skill_break_equip(bl, EQP_WEAPON, 100*skill_lv, BCT_ENEMY);
+ break;
+
+ case CR_SHIELDCHARGE:
+ sc_start(bl,SC_STUN,(15+skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case PA_PRESSURE:
+ status_percent_damage(src, bl, 0, 15+5*skill_lv, false);
+ break;
+
+ case RG_RAID:
+ sc_start(bl,SC_STUN,(10+3*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_BLIND,(10+3*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+
+#ifdef RENEWAL
+ sc_start(bl,SC_RAID,100,7,5000);
+ break;
+
+ case RG_BACKSTAP:
+ sc_start(bl,SC_STUN,(5+2*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
+#endif
+ break;
+
+ case BA_FROSTJOKER:
+ sc_start(bl,SC_FREEZE,(15+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case DC_SCREAM:
+ sc_start(bl,SC_STUN,(25+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case BD_LULLABY:
+ sc_start(bl,SC_SLEEP,15,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case DC_UGLYDANCE:
+ rate = 5+5*skill_lv;
+ if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON)))
+ rate += 5+skill;
+ status_zap(bl, 0, rate);
+ break;
+ case SL_STUN:
+ if (tstatus->size==SZ_MEDIUM) //Only stuns mid-sized mobs.
+ sc_start(bl,SC_STUN,(30+10*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case NPC_PETRIFYATTACK:
+ sc_start4(bl,status_skill2sc(skill_id),50+10*skill_lv,
+ skill_lv,0,0,skill_get_time(skill_id,skill_lv),
+ skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_CURSEATTACK:
+ case NPC_SLEEPATTACK:
+ case NPC_BLINDATTACK:
+ case NPC_POISON:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ case NPC_HELLPOWER:
+ sc_start(bl,status_skill2sc(skill_id),50+10*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_ACIDBREATH:
+ case NPC_ICEBREATH:
+ sc_start(bl,status_skill2sc(skill_id),70,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_BLEEDING:
+ sc_start(bl,SC_BLEEDING,(20*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_MENTALBREAKER:
+ { //Based on observations by Tharis, Mental Breaker should do SP damage
+ //equal to Matk*skLevel.
+ rate = sstatus->matk_min;
+ if (rate < sstatus->matk_max)
+ rate += rnd()%(sstatus->matk_max - sstatus->matk_min);
+ rate*=skill_lv;
+ status_zap(bl, 0, rate);
+ break;
+ }
+ // Equipment breaking monster skills [Celest]
+ case NPC_WEAPONBRAKER:
+ skill_break_equip(bl, EQP_WEAPON, 150*skill_lv, BCT_ENEMY);
+ break;
+ case NPC_ARMORBRAKE:
+ skill_break_equip(bl, EQP_ARMOR, 150*skill_lv, BCT_ENEMY);
+ break;
+ case NPC_HELMBRAKE:
+ skill_break_equip(bl, EQP_HELM, 150*skill_lv, BCT_ENEMY);
+ break;
+ case NPC_SHIELDBRAKE:
+ skill_break_equip(bl, EQP_SHIELD, 150*skill_lv, BCT_ENEMY);
+ break;
+
+ case CH_TIGERFIST:
+ sc_start(bl,SC_STOP,(10+skill_lv*10),0,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case LK_SPIRALPIERCE:
+ case ML_SPIRALPIERCE:
+ sc_start(bl,SC_STOP,(15+skill_lv*5),0,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case ST_REJECTSWORD:
+ sc_start(bl,SC_AUTOCOUNTER,(skill_lv*15),skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case PF_FOGWALL:
+ if (src != bl && !tsc->data[SC_DELUGE])
+ sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case LK_HEADCRUSH: //Headcrush has chance of causing Bleeding status, except on demon and undead element
+ if (!(battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON))
+ sc_start(bl, SC_BLEEDING,50, skill_lv, skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case LK_JOINTBEAT:
+ status = status_skill2sc(skill_id);
+ if (tsc->jb_flag) {
+ sc_start2(bl,status,(5*skill_lv+5),skill_lv,tsc->jb_flag&BREAK_FLAGS,skill_get_time2(skill_id,skill_lv));
+ tsc->jb_flag = 0;
+ }
+ break;
+ case ASC_METEORASSAULT:
+ //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*skill_lv% chance.
+ switch(rnd()%3) {
+ case 0:
+ sc_start(bl,SC_BLIND,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,1));
+ break;
+ case 1:
+ sc_start(bl,SC_STUN,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,2));
+ break;
+ default:
+ sc_start(bl,SC_BLEEDING,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,3));
+ }
+ break;
+
+ case HW_NAPALMVULCAN:
+ sc_start(bl,SC_CURSE,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case WS_CARTTERMINATION: // Cart termination
+ sc_start(bl,SC_STUN,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case CR_ACIDDEMONSTRATION:
+ skill_break_equip(bl, EQP_WEAPON|EQP_ARMOR, 100*skill_lv, BCT_ENEMY);
+ break;
+
+ case TK_DOWNKICK:
+ sc_start(bl,SC_STUN,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case TK_JUMPKICK:
+ if( dstsd && dstsd->class_ != MAPID_SOUL_LINKER && !tsc->data[SC_PRESERVE] )
+ {// debuff the following statuses
+ status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
+ status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER);
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ status_change_end(bl, SC_KAAHI, INVALID_TIMER);
+ status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
+ }
+ break;
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs.
+ if(attack_type&BF_MISC) //70% base stun chance...
+ sc_start(bl,SC_STUN,70,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case GS_BULLSEYE: //0.1% coma rate.
+ if(tstatus->race == RC_BRUTE || tstatus->race == RC_DEMIHUMAN)
+ status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0);
+ break;
+ case GS_PIERCINGSHOT:
+ sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NJ_HYOUSYOURAKU:
+ sc_start(bl,SC_FREEZE,(10+10*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case GS_FLING:
+ sc_start(bl,SC_FLING,100, sd?sd->spiritball_old:5,skill_get_time(skill_id,skill_lv));
+ break;
+ case GS_DISARM:
+ rate = 3*skill_lv;
+ if (sstatus->dex > tstatus->dex)
+ rate += (sstatus->dex - tstatus->dex)/5; //TODO: Made up formula
+ skill_strip_equip(bl, EQP_WEAPON, rate, skill_lv, skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case NPC_EVILLAND:
+ sc_start(bl,SC_BLIND,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_HELLJUDGEMENT:
+ sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_CRITICALWOUND:
+ sc_start(bl,SC_CRITICALWOUND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ 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 * skill_lv;
+ if( rnd()%100 < rate )
+ skill_castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0);
+ break;
+ case RK_WINDCUTTER:
+ sc_start(bl,SC_FEAR,3+2*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case RK_DRAGONBREATH:
+ sc_start4(bl,SC_BURNING,5+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
+ break;
+ case AB_ADORAMUS:
+ if( tsc && !tsc->data[SC_DECREASEAGI] ) //Prevent duplicate agi-down effect.
+ sc_start(bl, SC_ADORAMUS, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case WL_CRIMSONROCK:
+ sc_start(bl, SC_STUN, 40, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case WL_COMET:
+ sc_start4(bl,SC_BURNING,100,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
+ break;
+ case WL_EARTHSTRAIN:
+ {
+ int rate = 0, i;
+ const int pos[5] = { EQP_WEAPON, EQP_HELM, EQP_SHIELD, EQP_ARMOR, EQP_ACC };
+ rate = 6 * skill_lv + 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 < skill_lv; i++ )
+ skill_strip_equip(bl,pos[i],rate,skill_lv,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case WL_JACKFROST:
+ sc_start(bl,SC_FREEZE,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case RA_WUGBITE:
+ sc_start(bl, SC_BITE, (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*2 : 0), skill_lv, (skill_get_time(skill_id,skill_lv) + (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*500 : 0)) );
+ break;
+ case RA_SENSITIVEKEEN:
+ if( rnd()%100 < 8 * skill_lv )
+ skill_castend_damage_id(src, bl, RA_WUGBITE, sd ? pc_checkskill(sd, RA_WUGBITE):skill_lv, tick, SD_ANIMATION);
+ break;
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ sc_start(bl, (skill_id == RA_FIRINGTRAP) ? SC_BURNING:SC_FREEZING, 40 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case NC_PILEBUNKER:
+ if( rnd()%100 < 5 + 15*skill_lv )
+ { //Deactivatable Statuses: Kyrie Eleison, Auto Guard, Steel Body, Assumptio, and Millennium Shield
+ status_change_end(bl, SC_KYRIE, INVALID_TIMER);
+ status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER);
+ status_change_end(bl, SC_STEELBODY, INVALID_TIMER);
+ status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
+ status_change_end(bl, SC_MILLENNIUMSHIELD, INVALID_TIMER);
+ }
+ break;
+ case NC_FLAMELAUNCHER:
+ sc_start4(bl, SC_BURNING, 50 + 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time2(skill_id, skill_lv));
+ break;
+ case NC_COLDSLOWER:
+ sc_start(bl, SC_FREEZE, 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ sc_start(bl, SC_FREEZING, 20 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case NC_POWERSWING:
+ sc_start(bl, SC_STUN, 5*skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ if( rnd()%100 < 5*skill_lv )
+ skill_castend_damage_id(src, bl, NC_AXEBOOMERANG, pc_checkskill(sd, NC_AXEBOOMERANG), tick, 1);
+ break;
+ case GC_WEAPONCRUSH:
+ skill_castend_nodamage_id(src,bl,skill_id,skill_lv,tick,BCT_ENEMY);
+ break;
+ case LG_SHIELDPRESS:
+ sc_start(bl, SC_STUN, 30 + 8 * skill_lv, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case LG_PINPOINTATTACK:
+ rate = 30 + (((5 * (sd?pc_checkskill(sd,LG_PINPOINTATTACK):skill_lv)) + (sstatus->agi + status_get_lv(src))) / 10);
+ switch( skill_lv ) {
+ case 1:
+ sc_start(bl,SC_BLEEDING,rate,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case 2:
+ if( dstsd && dstsd->spiritball && rnd()%100 < rate )
+ pc_delspiritball(dstsd, dstsd->spiritball, 0);
+ break;
+ default:
+ skill_break_equip(bl,(skill_lv == 3) ? EQP_SHIELD : (skill_lv == 4) ? EQP_ARMOR : EQP_WEAPON,rate * 100,BCT_ENEMY);
+ break;
+ }
+ break;
+ case LG_MOONSLASHER:
+ rate = 32 + 8 * skill_lv;
+ if( rnd()%100 < rate && dstsd ) // Uses skill_addtimerskill to avoid damage and setsit packet overlaping. Officially clif_setsit is received about 500 ms after damage packet.
+ skill_addtimerskill(src,tick+500,bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0);
+ else if( dstmd && !is_boss(bl) )
+ sc_start(bl,SC_STOP,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case LG_RAYOFGENESIS: // 50% chance to cause Blind on Undead and Demon monsters.
+ if ( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON )
+ sc_start(bl, SC_BLIND,50, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case LG_EARTHDRIVE:
+ skill_break_equip(src, EQP_SHIELD, 500, BCT_SELF);
+ sc_start(bl, SC_EARTHDRIVE, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_DRAGONCOMBO:
+ sc_start(bl, SC_STUN, 1 + skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_FALLENEMPIRE:
+ sc_start(bl, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_WINDMILL:
+ if( dstsd )
+ skill_addtimerskill(src,tick+status_get_amotion(src),bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0);
+ else if( dstmd && !is_boss(bl) )
+ sc_start(bl, SC_STUN, 100, skill_lv, 1000 + 1000 * (rnd() %3));
+ break;
+ case SR_GENTLETOUCH_QUIET: // [(Skill Level x 5) + (Caster?s DEX + Caster?s Base Level) / 10]
+ sc_start(bl, SC_SILENCE, 5 * skill_lv + (sstatus->dex + status_get_lv(src)) / 10, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_EARTHSHAKER:
+ sc_start(bl,SC_STUN, 25 + 5 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case SR_HOWLINGOFLION:
+ sc_start(bl, SC_FEAR, 5 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case WM_SOUND_OF_DESTRUCTION:
+ if( rnd()%100 < 5 + 5 * skill_lv ) { // Temporarly Check Until We Get the Official Formula
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ status_change_end(bl, SC_RICHMANKIM, INVALID_TIMER);
+ status_change_end(bl, SC_ETERNALCHAOS, INVALID_TIMER);
+ status_change_end(bl, SC_DRUMBATTLE, INVALID_TIMER);
+ status_change_end(bl, SC_NIBELUNGEN, INVALID_TIMER);
+ status_change_end(bl, SC_INTOABYSS, INVALID_TIMER);
+ status_change_end(bl, SC_SIEGFRIED, INVALID_TIMER);
+ status_change_end(bl, SC_WHISTLE, INVALID_TIMER);
+ status_change_end(bl, SC_ASSNCROS, INVALID_TIMER);
+ status_change_end(bl, SC_POEMBRAGI, INVALID_TIMER);
+ status_change_end(bl, SC_APPLEIDUN, INVALID_TIMER);
+ status_change_end(bl, SC_HUMMING, INVALID_TIMER);
+ status_change_end(bl, SC_FORTUNE, INVALID_TIMER);
+ status_change_end(bl, SC_SERVICE4U, INVALID_TIMER);
+ status_change_end(bl, SC_LONGING, INVALID_TIMER);
+ status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
+ status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
+ status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
+ status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
+ status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
+ status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ status_change_end(bl, SC_WINKCHARM, INVALID_TIMER);
+ status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER);
+ status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
+ status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
+ status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
+ status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
+ status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
+ }
+ break;
+ case SO_EARTHGRAVE:
+ sc_start(bl, SC_BLEEDING, 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); // Need official rate. [LimitLine]
+ break;
+ case SO_DIAMONDDUST:
+ rate = 5 + 5 * skill_lv;
+ if( sc && sc->data[SC_COOLER_OPTION] )
+ rate += rate * sc->data[SC_COOLER_OPTION]->val2 / 100;
+ sc_start(bl, SC_CRYSTALIZE, rate, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case SO_VARETYR_SPEAR:
+ sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ if( sd ) {
+ switch( sd->itemid ) { // Starting SCs here instead of do it in skill_additional_effect to simplify the code.
+ case 13261:
+ sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv));
+ sc_start(bl, SC_BLEEDING, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv));
+ break;
+ case 13262:
+ sc_start(bl, SC_MELON_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces ASPD and moviment speed
+ break;
+ case 13264:
+ sc_start(bl, SC_BANANA_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces LUK ??Needed confirm it, may be it's bugged in kRORE?
+ sc_start(bl, SC_BANANA_BOMB_SITDOWN, 75, skill_lv, skill_get_time(GN_SLINGITEM_RANGEMELEEATK,skill_lv)); // Sitdown for 3 seconds.
+ break;
+ }
+ sd->itemid = -1;
+ }
+ break;
+ case GN_HELLS_PLANT_ATK:
+ sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ sc_start(bl, SC_BLEEDING, 20 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case EL_WIND_SLASH: // Non confirmed rate.
+ sc_start(bl, SC_BLEEDING, 25, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case EL_STONE_HAMMER:
+ rate = 10 * skill_lv;
+ sc_start(bl, SC_STUN, rate, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ sc_start(bl,status_skill2sc(skill_id),50,skill_lv,skill_get_time(EL_ROCK_CRUSHER,skill_lv));
+ break;
+ case EL_TYPOON_MIS:
+ sc_start(bl,SC_SILENCE,10*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case KO_JYUMONJIKIRI: // needs more info
+ sc_start(bl,SC_JYUMONJIKIRI,25,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case KO_MAKIBISHI:
+ sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(skill_id,skill_lv));
+ break;
+ case MH_LAVA_SLIDE:
+ if (tsc && !tsc->data[SC_BURNING]) sc_start4(bl, SC_BURNING, 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time(skill_id, skill_lv));
+ break;
+ case MH_STAHL_HORN:
+ sc_start(bl, SC_STUN, (20 + 4 * (skill_lv-1)), skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case MH_NEEDLE_OF_PARALYZE:
+ sc_start(bl, SC_PARALYSIS, 40 + (5*skill_lv), skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ }
+
+ if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai)
+ { //Pass heritage to Master for status causing effects. [Skotlex]
+ sd = map_id2sd(md->master_id);
+ src = sd?&sd->bl:src;
+ }
+
+ if( attack_type&BF_WEAPON )
+ { // Coma, Breaking Equipment
+ if( sd && sd->special_state.bonus_coma )
+ {
+ rate = sd->weapon_coma_ele[tstatus->def_ele];
+ rate += sd->weapon_coma_race[tstatus->race];
+ rate += sd->weapon_coma_race[tstatus->mode&MD_BOSS?RC_BOSS:RC_NONBOSS];
+ if (rate)
+ status_change_start(bl, SC_COMA, rate, 0, 0, src->id, 0, 0, 0);
+ }
+ if( sd && battle_config.equip_self_break_rate )
+ { // Self weapon breaking
+ rate = battle_config.equip_natural_break_rate;
+ if( sc )
+ {
+ if(sc->data[SC_OVERTHRUST])
+ rate += 10;
+ if(sc->data[SC_MAXOVERTHRUST])
+ rate += 10;
+ }
+ if( rate )
+ skill_break_equip(src, EQP_WEAPON, rate, BCT_SELF);
+ }
+ if( battle_config.equip_skill_break_rate && skill_id != WS_CARTTERMINATION && skill_id != ITM_TOMAHAWK )
+ { // Cart Termination/Tomahawk won't trigger breaking data. Why? No idea, go ask Gravity.
+ // Target weapon breaking
+ rate = 0;
+ if( sd )
+ rate += sd->bonus.break_weapon_rate;
+ if( sc && sc->data[SC_MELTDOWN] )
+ rate += sc->data[SC_MELTDOWN]->val2;
+ if( rate )
+ skill_break_equip(bl, EQP_WEAPON, rate, BCT_ENEMY);
+
+ // Target armor breaking
+ rate = 0;
+ if( sd )
+ rate += sd->bonus.break_armor_rate;
+ if( sc && sc->data[SC_MELTDOWN] )
+ rate += sc->data[SC_MELTDOWN]->val3;
+ if( rate )
+ skill_break_equip(bl, EQP_ARMOR, rate, BCT_ENEMY);
+ }
+ }
+
+ if( sd && sd->ed && sc && !status_isdead(bl) && !skill_id ){
+ struct unit_data *ud = unit_bl2ud(src);
+
+ if( sc->data[SC_WILD_STORM_OPTION] )
+ skill = sc->data[SC_WILD_STORM_OPTION]->val2;
+ else if( sc->data[SC_UPHEAVAL_OPTION] )
+ skill = sc->data[SC_UPHEAVAL_OPTION]->val2;
+ else if( sc->data[SC_TROPIC_OPTION] )
+ skill = sc->data[SC_TROPIC_OPTION]->val3;
+ else if( sc->data[SC_CHILLY_AIR_OPTION] )
+ skill = sc->data[SC_CHILLY_AIR_OPTION]->val3;
+ else
+ skill = 0;
+
+ if ( rnd()%100 < 25 && skill ){
+ skill_castend_damage_id(src, bl, skill, 5, tick, 0);
+
+ if (ud) {
+ rate = skill_delayfix(src, skill, skill_lv);
+ if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
+ ud->canact_tick = tick+rate;
+ if ( battle_config.display_status_timers )
+ clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
+ }
+ }
+ }
+ }
+
+ // Autospell when attacking
+ if( sd && !status_isdead(bl) && sd->autospell[0].id )
+ {
+ struct block_list *tbl;
+ struct unit_data *ud;
+ int i, skill_lv, type, notok;
+
+ for (i = 0; i < ARRAYLENGTH(sd->autospell) && sd->autospell[i].id; i++) {
+
+ if(!(sd->autospell[i].flag&attack_type&BF_WEAPONMASK &&
+ sd->autospell[i].flag&attack_type&BF_RANGEMASK &&
+ sd->autospell[i].flag&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+
+ skill = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id;
+
+ sd->state.autocast = 1;
+ notok = skillnotok(skill, sd);
+ sd->state.autocast = 0;
+
+ if ( notok )
+ continue;
+
+ skill_lv = sd->autospell[i].lv?sd->autospell[i].lv:1;
+ if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv);
+
+ rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2;
+
+ if (rnd()%1000 >= rate)
+ continue;
+
+ tbl = (sd->autospell[i].id < 0) ? src : bl;
+
+ if( (type = skill_get_casttype(skill)) == CAST_GROUND ) {
+ int maxcount = 0;
+ if( !(BL_PC&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(skill)&UF_NOREITERATION &&
+ skill_check_unit_range(src,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.skill_nofootset &&
+ skill_get_unit_flag(skill)&UF_NOFOOTSET &&
+ skill_check_unit_range2(src,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(skill, skill_lv)) > 0
+ ) {
+ int v;
+ for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) {
+ if(sd->ud.skillunit[v]->skill_id == skill)
+ maxcount--;
+ }
+ if( maxcount == 0 ) {
+ continue;
+ }
+ }
+ }
+ if( battle_config.autospell_check_range &&
+ !battle_check_range(src, tbl, skill_get_range2(src, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) )
+ continue;
+
+ if (skill == AS_SONICBLOW)
+ pc_stop_attack(sd); //Special case, Sonic Blow autospell should stop the player attacking.
+ if (skill == PF_SPIDERWEB) //Special case, due to its nature of coding.
+ type = CAST_GROUND;
+
+ sd->state.autocast = 1;
+ skill_consume_requirement(sd,skill,skill_lv,1);
+ skill_toggle_magicpower(src, skill);
+ switch (type) {
+ case CAST_GROUND:
+ skill_castend_pos2(src, tbl->x, tbl->y, skill, skill_lv, tick, 0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(src, tbl, skill, skill_lv, tick, 0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(src, tbl, skill, skill_lv, tick, 0);
+ break;
+ }
+ sd->state.autocast = 0;
+ //Set canact delay. [Skotlex]
+ ud = unit_bl2ud(src);
+ if (ud) {
+ rate = skill_delayfix(src, skill, skill_lv);
+ 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, 0, 0, 0);
+ }
+ }
+ }
+ }
+
+ //Autobonus when attacking
+ if( sd && sd->autobonus[0].rate )
+ {
+ int i;
+ for( i = 0; i < ARRAYLENGTH(sd->autobonus); i++ )
+ {
+ if( rnd()%1000 >= sd->autobonus[i].rate )
+ continue;
+ if( sd->autobonus[i].active != INVALID_TIMER )
+ continue;
+ if(!(sd->autobonus[i].atk_type&attack_type&BF_WEAPONMASK &&
+ sd->autobonus[i].atk_type&attack_type&BF_RANGEMASK &&
+ sd->autobonus[i].atk_type&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+ pc_exeautobonus(sd,&sd->autobonus[i]);
+ }
+ }
+
+ //Polymorph
+ if(sd && sd->bonus.classchange && attack_type&BF_WEAPON &&
+ dstmd && !(tstatus->mode&MD_BOSS) &&
+ (rnd()%10000 < sd->bonus.classchange))
+ {
+ struct mob_db *mob;
+ int class_;
+ skill = 0;
+ do {
+ do {
+ class_ = rnd() % MAX_MOB_DB;
+ } while (!mobdb_checkid(class_));
+
+ rate = rnd() % 1000000;
+ mob = mob_db(class_);
+ } while (
+ (mob->status.mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= rate) &&
+ (skill++) < 2000);
+ if (skill < 2000)
+ mob_class_change(dstmd,class_);
+ }
+
+ return 0;
+}
+
+int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint16 skill_id, unsigned int tick) {
+ int skill, skill_lv, i, type, notok;
+ struct block_list *tbl;
+
+ if( sd == NULL || !skill_id )
+ return 0;
+
+ for( i = 0; i < ARRAYLENGTH(sd->autospell3) && sd->autospell3[i].flag; i++ ) {
+ if( sd->autospell3[i].flag != skill_id )
+ continue;
+
+ if( sd->autospell3[i].lock )
+ continue; // autospell already being executed
+
+ skill = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id;
+
+ sd->state.autocast = 1;
+ notok = skillnotok(skill, sd);
+ sd->state.autocast = 0;
+
+ if ( notok )
+ continue;
+
+ skill_lv = sd->autospell3[i].lv ? sd->autospell3[i].lv : 1;
+ if( skill_lv < 0 ) skill_lv = 1 + rnd()%(-skill_lv);
+
+ if( sd->autospell3[i].id >= 0 && bl == NULL )
+ continue; // No target
+ if( rnd()%1000 >= sd->autospell3[i].rate )
+ continue;
+
+ tbl = (sd->autospell3[i].id < 0) ? &sd->bl : bl;
+
+ if( (type = skill_get_casttype(skill)) == CAST_GROUND ) {
+ int maxcount = 0;
+ if( !(BL_PC&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(skill)&UF_NOREITERATION &&
+ skill_check_unit_range(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.skill_nofootset &&
+ skill_get_unit_flag(skill)&UF_NOFOOTSET &&
+ skill_check_unit_range2(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(skill, skill_lv)) > 0
+ ) {
+ int v;
+ for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) {
+ if(sd->ud.skillunit[v]->skill_id == skill)
+ maxcount--;
+ }
+ if( maxcount == 0 ) {
+ continue;
+ }
+ }
+ }
+ if( battle_config.autospell_check_range &&
+ !battle_check_range(&sd->bl, tbl, skill_get_range2(&sd->bl, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) )
+ continue;
+
+ sd->state.autocast = 1;
+ sd->autospell3[i].lock = true;
+ skill_consume_requirement(sd,skill,skill_lv,1);
+ switch( type )
+ {
+ case CAST_GROUND: skill_castend_pos2(&sd->bl, tbl->x, tbl->y, skill, skill_lv, tick, 0); break;
+ case CAST_NODAMAGE: skill_castend_nodamage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break;
+ case CAST_DAMAGE: skill_castend_damage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break;
+ }
+ sd->autospell3[i].lock = false;
+ sd->state.autocast = 0;
+ }
+
+ if( sd && sd->autobonus3[0].rate )
+ {
+ for( i = 0; i < ARRAYLENGTH(sd->autobonus3); i++ )
+ {
+ if( rnd()%1000 >= sd->autobonus3[i].rate )
+ continue;
+ if( sd->autobonus3[i].active != INVALID_TIMER )
+ continue;
+ if( sd->autobonus3[i].atk_type != skill_id )
+ continue;
+ pc_exeautobonus(sd,&sd->autobonus3[i]);
+ }
+ }
+
+ return 1;
+}
+
+/* Splitted off from skill_additional_effect, which is never called when the
+ * attack skill kills the enemy. Place in this function counter status effects
+ * when using skills (eg: Asura's sp regen penalty, or counter-status effects
+ * from cards) that will take effect on the source, not the target. [Skotlex]
+ * Note: Currently this function only applies to Extremity Fist and BF_WEAPON
+ * type of skills, so not every instance of skill_additional_effect needs a call
+ * to this one.
+ */
+int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, unsigned int tick)
+{
+ int rate;
+ struct map_session_data *sd=NULL;
+ struct map_session_data *dstsd=NULL;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest
+
+ sd = BL_CAST(BL_PC, src);
+ dstsd = BL_CAST(BL_PC, bl);
+
+ if(dstsd && attack_type&BF_WEAPON)
+ { //Counter effects.
+ enum sc_type type;
+ int i, time;
+ for(i=0; i < ARRAYLENGTH(dstsd->addeff2) && dstsd->addeff2[i].flag; i++)
+ {
+ rate = dstsd->addeff2[i].rate;
+ if (attack_type&BF_LONG)
+ rate+=dstsd->addeff2[i].arrow_rate;
+ if (!rate) continue;
+
+ if ((dstsd->addeff2[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT))
+ { //Trigger has range consideration.
+ if((dstsd->addeff2[i].flag&ATF_LONG && !(attack_type&BF_LONG)) ||
+ (dstsd->addeff2[i].flag&ATF_SHORT && !(attack_type&BF_SHORT)))
+ continue; //Range Failed.
+ }
+ type = dstsd->addeff2[i].id;
+ time = skill_get_time2(status_sc2skill(type),7);
+
+ if (dstsd->addeff2[i].flag&ATF_TARGET)
+ status_change_start(src,type,rate,7,0,0,0,time,0);
+
+ if (dstsd->addeff2[i].flag&ATF_SELF && !status_isdead(bl))
+ status_change_start(bl,type,rate,7,0,0,0,time,0);
+ }
+ }
+
+ switch(skill_id){
+ case MO_EXTREMITYFIST:
+ sc_start(src,SC_EXTREMITYFIST,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case GS_FULLBUSTER:
+ sc_start(src,SC_BLIND,2*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case HFLI_SBR44: //[orn]
+ case HVAN_EXPLOSION:
+ if(src->type == BL_HOM){
+ TBL_HOM *hd = (TBL_HOM*)src;
+ hd->homunculus.intimacy = 200;
+ if (hd->master)
+ clif_send_homdata(hd->master,SP_INTIMATE,hd->homunculus.intimacy/100);
+ }
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ attack_type |= BF_WEAPON;
+ break;
+ }
+
+ if(sd && (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR &&
+ rnd()%10000 < battle_config.sg_miracle_skill_ratio) //SG_MIRACLE [Komurka]
+ sc_start(src,SC_MIRACLE,100,1,battle_config.sg_miracle_skill_duration);
+
+ if(sd && skill_id && attack_type&BF_MAGIC && status_isdead(bl) &&
+ !(skill_get_inf(skill_id)&(INF_GROUND_SKILL|INF_SELF_SKILL)) &&
+ (rate=pc_checkskill(sd,HW_SOULDRAIN))>0
+ ){ //Soul Drain should only work on targetted spells [Skotlex]
+ if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex]
+ clif_skill_nodamage(src,bl,HW_SOULDRAIN,rate,1);
+ status_heal(src, 0, status_get_lv(bl)*(95+15*rate)/100, 2);
+ }
+
+ if( sd && status_isdead(bl) ) {
+ int sp = 0, hp = 0;
+ if( attack_type&BF_WEAPON ) {
+ sp += sd->bonus.sp_gain_value;
+ sp += sd->sp_gain_race[status_get_race(bl)];
+ sp += sd->sp_gain_race[is_boss(bl)?RC_BOSS:RC_NONBOSS];
+ hp += sd->bonus.hp_gain_value;
+ }
+ if( attack_type&BF_MAGIC ) {
+ sp += sd->bonus.magic_sp_gain_value;
+ hp += sd->bonus.magic_hp_gain_value;
+ if( skill_id == WZ_WATERBALL ) {//(bugreport:5303)
+ struct status_change *sc = NULL;
+ if( ( sc = status_get_sc(src) ) ) {
+ if(sc->data[SC_SPIRIT] &&
+ sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
+ sc->data[SC_SPIRIT]->val3 == WZ_WATERBALL)
+ sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
+ }
+ }
+ }
+ if( hp || sp ) { // updated to force healing to allow healing through berserk
+ status_heal(src, hp, sp, battle_config.show_hp_sp_gain ? 3 : 1);
+ }
+ }
+
+ // Trigger counter-spells to retaliate against damage causing skills.
+ if(dstsd && !status_isdead(bl) && dstsd->autospell2[0].id &&
+ !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE))
+ {
+ struct block_list *tbl;
+ struct unit_data *ud;
+ int i, skill_id, skill_lv, rate, type, notok;
+
+ for (i = 0; i < ARRAYLENGTH(dstsd->autospell2) && dstsd->autospell2[i].id; i++) {
+
+ if(!(dstsd->autospell2[i].flag&attack_type&BF_WEAPONMASK &&
+ dstsd->autospell2[i].flag&attack_type&BF_RANGEMASK &&
+ dstsd->autospell2[i].flag&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+
+ skill_id = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id;
+ skill_lv = dstsd->autospell2[i].lv?dstsd->autospell2[i].lv:1;
+ if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv);
+
+ rate = dstsd->autospell2[i].rate;
+ if (attack_type&BF_LONG)
+ rate>>=1;
+
+ dstsd->state.autocast = 1;
+ notok = skillnotok(skill_id, dstsd);
+ dstsd->state.autocast = 0;
+
+ if ( notok )
+ continue;
+
+ if (rnd()%1000 >= rate)
+ continue;
+
+ tbl = (dstsd->autospell2[i].id < 0) ? bl : src;
+
+ if( (type = skill_get_casttype(skill_id)) == CAST_GROUND ) {
+ int maxcount = 0;
+ if( !(BL_PC&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(skill_id)&UF_NOREITERATION &&
+ skill_check_unit_range(bl,tbl->x,tbl->y,skill_id,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.skill_nofootset &&
+ skill_get_unit_flag(skill_id)&UF_NOFOOTSET &&
+ skill_check_unit_range2(bl,tbl->x,tbl->y,skill_id,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(skill_id, skill_lv)) > 0
+ ) {
+ int v;
+ for(v=0;v<MAX_SKILLUNITGROUP && dstsd->ud.skillunit[v] && maxcount;v++) {
+ if(dstsd->ud.skillunit[v]->skill_id == skill_id)
+ maxcount--;
+ }
+ if( maxcount == 0 ) {
+ continue;
+ }
+ }
+ }
+
+ if( !battle_check_range(src, tbl, skill_get_range2(src, skill_id,skill_lv) + (skill_id == RG_CLOSECONFINE?0:1)) && battle_config.autospell_check_range )
+ continue;
+
+ dstsd->state.autocast = 1;
+ skill_consume_requirement(dstsd,skill_id,skill_lv,1);
+ switch (type) {
+ case CAST_GROUND:
+ skill_castend_pos2(bl, tbl->x, tbl->y, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(bl, tbl, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(bl, tbl, skill_id, skill_lv, tick, 0);
+ break;
+ }
+ dstsd->state.autocast = 0;
+ //Set canact delay. [Skotlex]
+ ud = unit_bl2ud(bl);
+ if (ud) {
+ rate = skill_delayfix(bl, skill_id, skill_lv);
+ 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, 0, 0, 0);
+ }
+ }
+ }
+ }
+
+ //Autobonus when attacked
+ if( dstsd && !status_isdead(bl) && dstsd->autobonus2[0].rate && !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE) )
+ {
+ int i;
+ for( i = 0; i < ARRAYLENGTH(dstsd->autobonus2); i++ )
+ {
+ if( rnd()%1000 >= dstsd->autobonus2[i].rate )
+ continue;
+ if( dstsd->autobonus2[i].active != INVALID_TIMER )
+ continue;
+ if(!(dstsd->autobonus2[i].atk_type&attack_type&BF_WEAPONMASK &&
+ dstsd->autobonus2[i].atk_type&attack_type&BF_RANGEMASK &&
+ dstsd->autobonus2[i].atk_type&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+ pc_exeautobonus(dstsd,&dstsd->autobonus2[i]);
+ }
+ }
+
+ return 0;
+}
+/*=========================================================================
+ Breaks equipment. On-non players causes the corresponding strip effect.
+ - rate goes from 0 to 10000 (100.00%)
+ - flag is a BCT_ flag to indicate which type of adjustment should be used
+ (BCT_ENEMY/BCT_PARTY/BCT_SELF) are the valid values.
+--------------------------------------------------------------------------*/
+int skill_break_equip (struct block_list *bl, unsigned short where, int rate, int flag)
+{
+ const int where_list[4] = {EQP_WEAPON, EQP_ARMOR, EQP_SHIELD, EQP_HELM};
+ const enum sc_type scatk[4] = {SC_STRIPWEAPON, SC_STRIPARMOR, SC_STRIPSHIELD, SC_STRIPHELM};
+ const enum sc_type scdef[4] = {SC_CP_WEAPON, SC_CP_ARMOR, SC_CP_SHIELD, SC_CP_HELM};
+ struct status_change *sc = status_get_sc(bl);
+ int i,j;
+ TBL_PC *sd;
+ sd = BL_CAST(BL_PC, bl);
+ if (sc && !sc->count)
+ sc = NULL;
+
+ if (sd) {
+ if (sd->bonus.unbreakable_equip)
+ where &= ~sd->bonus.unbreakable_equip;
+ if (sd->bonus.unbreakable)
+ rate -= rate*sd->bonus.unbreakable/100;
+ if (where&EQP_WEAPON) {
+ switch (sd->status.weapon) {
+ case W_FIST: //Bare fists should not break :P
+ case W_1HAXE:
+ case W_2HAXE:
+ case W_MACE: // Axes and Maces can't be broken [DracoRPG]
+ case W_2HMACE:
+ case W_STAFF:
+ case W_2HSTAFF:
+ case W_BOOK: //Rods and Books can't be broken [Skotlex]
+ case W_HUUMA:
+ where &= ~EQP_WEAPON;
+ }
+ }
+ }
+ if (flag&BCT_ENEMY) {
+ if (battle_config.equip_skill_break_rate != 100)
+ rate = rate*battle_config.equip_skill_break_rate/100;
+ } else if (flag&(BCT_PARTY|BCT_SELF)) {
+ if (battle_config.equip_self_break_rate != 100)
+ rate = rate*battle_config.equip_self_break_rate/100;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (where&where_list[i]) {
+ if (sc && sc->count && sc->data[scdef[i]])
+ where&=~where_list[i];
+ else if (rnd()%10000 >= rate)
+ where&=~where_list[i];
+ else if (!sd && !(status_get_mode(bl)&MD_BOSS)) //Cause Strip effect.
+ sc_start(bl,scatk[i],100,0,skill_get_time(status_sc2skill(scatk[i]),1));
+ }
+ }
+ if (!where) //Nothing to break.
+ return 0;
+ if (sd) {
+ for (i = 0; i < EQI_MAX; i++) {
+ j = sd->equip_index[i];
+ if (j < 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j])
+ continue;
+
+ switch(i) {
+ case EQI_HEAD_TOP: //Upper Head
+ flag = (where&EQP_HELM);
+ break;
+ case EQI_ARMOR: //Body
+ flag = (where&EQP_ARMOR);
+ break;
+ case EQI_HAND_R: //Left/Right hands
+ case EQI_HAND_L:
+ flag = (
+ (where&EQP_WEAPON && sd->inventory_data[j]->type == IT_WEAPON) ||
+ (where&EQP_SHIELD && sd->inventory_data[j]->type == IT_ARMOR));
+ break;
+ case EQI_SHOES:
+ flag = (where&EQP_SHOES);
+ break;
+ case EQI_GARMENT:
+ flag = (where&EQP_GARMENT);
+ break;
+ default:
+ continue;
+ }
+ if (flag) {
+ sd->status.inventory[j].attribute = 1;
+ pc_unequipitem(sd, j, 3);
+ }
+ }
+ clif_equiplist(sd);
+ }
+
+ return where; //Return list of pieces broken.
+}
+
+int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int lv, int time)
+{
+ struct status_change *sc;
+ const int pos[5] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HELM, EQP_ACC};
+ const enum sc_type sc_atk[5] = {SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC__STRIPACCESSORY};
+ const enum sc_type sc_def[5] = {SC_CP_WEAPON, SC_CP_SHIELD, SC_CP_ARMOR, SC_CP_HELM, 0};
+ int i;
+
+ if (rnd()%100 >= rate)
+ return 0;
+
+ sc = status_get_sc(bl);
+ if (!sc || sc->option&OPTION_MADOGEAR ) //Mado Gear cannot be divested [Ind]
+ return 0;
+
+ for (i = 0; i < ARRAYLENGTH(pos); i++) {
+ if (where&pos[i] && sc->data[sc_def[i]])
+ where&=~pos[i];
+ }
+ if (!where) return 0;
+
+ for (i = 0; i < ARRAYLENGTH(pos); i++) {
+ if (where&pos[i] && !sc_start(bl, sc_atk[i], 100, lv, time))
+ where&=~pos[i];
+ }
+ 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
+ - 'direction' indicates the way OPPOSITE to the knockback direction (or -1 for default behavior)
+ - if 'flag&0x1', position update packets must not be sent.
+ - if 'flag&0x2', skill blown ignores players' special_state.no_knockback
+ -------------------------------------------------------------------------*/
+int skill_blown(struct block_list* src, struct block_list* target, int count, int8 dir, int flag)
+{
+ int dx = 0, dy = 0;
+ struct skill_unit* su = NULL;
+
+ nullpo_ret(src);
+
+ if (src != target && (map_flag_gvg(target->m) || map[target->m].flag.battleground))
+ return 0; //No knocking back in WoE
+ if (count == 0)
+ return 0; //Actual knockback distance is 0.
+
+ switch (target->type) {
+ case BL_MOB: {
+ struct mob_data* md = BL_CAST(BL_MOB, target);
+ if( md->class_ == MOBID_EMPERIUM )
+ return 0;
+ if(src != target && is_boss(target)) //Bosses can't be knocked-back
+ return 0;
+ }
+ break;
+ case BL_PC: {
+ struct map_session_data *sd = BL_CAST(BL_PC, target);
+ if( sd->sc.data[SC_BASILICA] && sd->sc.data[SC_BASILICA]->val4 == sd->bl.id && !is_boss(src))
+ return 0; // Basilica caster can't be knocked-back by normal monsters.
+ if( !(flag&0x2) && src != target && sd->special_state.no_knockback )
+ return 0;
+ }
+ break;
+ case BL_SKILL:
+ su = (struct skill_unit *)target;
+ if( su && su->group && su->group->unit_id == UNT_ANKLESNARE )
+ return 0; // ankle snare cannot be knocked back
+ break;
+ }
+
+ if (dir == -1) // <optimized>: do the computation here instead of outside
+ dir = map_calc_dir(target, src->x, src->y); // direction from src to target, reversed
+
+ if (dir >= 0 && dir < 8)
+ { // take the reversed 'direction' and reverse it
+ dx = -dirx[dir];
+ dy = -diry[dir];
+ }
+
+ return unit_blown(target, dx, dy, count, flag); // send over the proper flag
+}
+
+
+//Checks if 'bl' should reflect back a spell cast by 'src'.
+//type is the type of magic attack: 0: indirect (aoe), 1: direct (targetted)
+static int skill_magic_reflect(struct block_list* src, struct block_list* bl, int type)
+{
+ struct status_change *sc = status_get_sc(bl);
+ struct map_session_data* sd = BL_CAST(BL_PC, bl);
+
+ if( sc && sc->data[SC_KYOMU] ) // Nullify reflecting ability
+ return 0;
+
+ // item-based reflection
+ if( sd && sd->bonus.magic_damage_return && type && rnd()%100 < sd->bonus.magic_damage_return )
+ return 1;
+
+ if( is_boss(src) )
+ return 0;
+
+ // status-based reflection
+ if( !sc || sc->count == 0 )
+ return 0;
+
+ if( sc->data[SC_MAGICMIRROR] && rnd()%100 < sc->data[SC_MAGICMIRROR]->val2 )
+ return 1;
+
+ if( sc->data[SC_KAITE] && (src->type == BL_PC || status_get_lv(src) <= 80) )
+ {// Kaite only works against non-players if they are low-level.
+ clif_specialeffect(bl, 438, AREA);
+ if( --sc->data[SC_KAITE]->val2 <= 0 )
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ return 2;
+ }
+
+ return 0;
+}
+
+/*
+ * =========================================================================
+ * Does a skill attack with the given properties.
+ * src is the master behind the attack (player/mob/pet)
+ * dsrc is the actual originator of the damage, can be the same as src, or a BL_SKILL
+ * bl is the target to be attacked.
+ * flag can hold a bunch of information:
+ * flag&0xFFF is passed to the underlying battle_calc_attack for processing
+ * (usually holds number of targets, or just 1 for simple splash attacks)
+ * flag&0x1000 is used to tag that this is a splash-attack (so the damage
+ * packet shouldn't display a skill animation)
+ * flag&0x2000 is used to signal that the skill_lv should be passed as -1 to the
+ * client (causes player characters to not scream skill name)
+ *-------------------------------------------------------------------------*/
+int skill_attack (int attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct Damage dmg;
+ struct status_data *sstatus, *tstatus;
+ struct status_change *sc;
+ struct map_session_data *sd, *tsd;
+ int type,damage,rdamage=0;
+ int8 rmdamage=0;//magic reflected
+
+ if(skill_id > 0 && !skill_lv) return 0;
+
+ nullpo_ret(src); //Source is the master behind the attack (player/mob/pet)
+ nullpo_ret(dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src.
+ nullpo_ret(bl); //Target to be attacked.
+
+ if (src != dsrc) {
+ //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex]
+ if (!status_check_skilluse(battle_config.skill_caster_check?src:NULL, bl, skill_id, 2))
+ return 0;
+ } else if ((flag&SD_ANIMATION) && skill_get_nk(skill_id)&NK_SPLASH) {
+ //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex]
+ if (!status_check_skilluse(src, bl, skill_id, 2))
+ return 0;
+ }
+
+ sd = BL_CAST(BL_PC, src);
+ tsd = BL_CAST(BL_PC, bl);
+
+ sstatus = status_get_status_data(src);
+ tstatus = status_get_status_data(bl);
+ sc= status_get_sc(bl);
+ if (sc && !sc->count) sc = NULL; //Don't need it.
+
+ // Is this check really needed? FrostNova won't hurt you if you step right where the caster is?
+ if(skill_id == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y)
+ return 0;
+ //Trick Dead protects you from damage, but not from buffs and the like, hence it's placed here.
+ if (sc && sc->data[SC_TRICKDEAD] && !(sstatus->mode&MD_BOSS))
+ return 0;
+
+ dmg = battle_calc_attack(attack_type,src,bl,skill_id,skill_lv,flag&0xFFF);
+
+ //Skotlex: Adjusted to the new system
+ if(src->type==BL_PET)
+ { // [Valaris]
+ struct pet_data *pd = (TBL_PET*)src;
+ if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skill_id)
+ {
+ int element = skill_get_ele(skill_id, skill_lv);
+ /*if (skill_id == -1) Does it ever worked?
+ element = sstatus->rhw.ele;*/
+ if (element != ELE_NEUTRAL || !(battle_config.attack_attr_none&BL_PET))
+ dmg.damage=battle_attr_fix(src, bl, skill_lv, element, tstatus->def_ele, tstatus->ele_lv);
+ else
+ dmg.damage= skill_lv;
+ dmg.damage2=0;
+ dmg.div_= pd->a_skill->div_;
+ }
+ }
+
+ if( dmg.flag&BF_MAGIC && ( skill_id != NPC_EARTHQUAKE || (battle_config.eq_single_target_reflectable && (flag&0xFFF) == 1) ) )
+ { // Earthquake on multiple targets is not counted as a target skill. [Inkfish]
+ if( (dmg.damage || dmg.damage2) && (type = skill_magic_reflect(src, bl, src==dsrc)) )
+ { //Magic reflection, switch caster/target
+ struct block_list *tbl = bl;
+ rmdamage = 1;
+ bl = src;
+ src = tbl;
+ sd = BL_CAST(BL_PC, src);
+ tsd = BL_CAST(BL_PC, bl);
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL; //Don't need it.
+ /* bugreport:2564 flag&2 disables double casting trigger */
+ flag |= 2;
+
+ //Spirit of Wizard blocks Kaite's reflection
+ if( type == 2 && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD )
+ { //Consume one Fragment per hit of the casted skill? [Skotlex]
+ type = tsd?pc_search_inventory (tsd, 7321):0;
+ if (type >= 0) {
+ if ( tsd ) pc_delitem(tsd, type, 1, 0, 1, LOG_TYPE_CONSUME);
+ dmg.damage = dmg.damage2 = 0;
+ dmg.dmg_lv = ATK_MISS;
+ sc->data[SC_SPIRIT]->val3 = skill_id;
+ sc->data[SC_SPIRIT]->val4 = dsrc->id;
+ }
+ }
+
+ /**
+ * Official Magic Reflection Behavior : damage reflected depends on gears caster wears, not target
+ **/
+ #if MAGIC_REFLECTION_TYPE
+ if( dmg.dmg_lv != ATK_MISS )//Wiz SL cancelled and consumed fragment
+ dmg = battle_calc_attack(BF_MAGIC,bl,bl,skill_id,skill_lv,flag&0xFFF);
+ #endif
+ }
+ if(sc && sc->data[SC_MAGICROD] && src == dsrc) {
+ int sp = skill_get_sp(skill_id,skill_lv);
+ dmg.damage = dmg.damage2 = 0;
+ dmg.dmg_lv = ATK_MISS; //This will prevent skill additional effect from taking effect. [Skotlex]
+ sp = sp * sc->data[SC_MAGICROD]->val2 / 100;
+ if(skill_id == WZ_WATERBALL && skill_lv > 1)
+ sp = sp/((skill_lv|1)*(skill_lv|1)); //Estimate SP cost of a single water-ball
+ status_heal(bl, 0, sp, 2);
+ }
+ }
+
+ damage = dmg.damage + dmg.damage2;
+
+ if( (skill_id == AL_INCAGI || skill_id == AL_BLESSING ||
+ skill_id == CASH_BLESSING || skill_id == CASH_INCAGI ||
+ skill_id == MER_INCAGI || skill_id == MER_BLESSING) && tsd->sc.data[SC_CHANGEUNDEAD] )
+ damage = 1;
+
+ if( damage > 0 && (( dmg.flag&BF_WEAPON && src != bl && ( src == dsrc || ( dsrc->type == BL_SKILL && ( skill_id == SG_SUN_WARM || skill_id == SG_MOON_WARM || skill_id == SG_STAR_WARM ) ) ))
+ || (sc && sc->data[SC_REFLECTDAMAGE])) )
+ rdamage = battle_calc_return_damage(bl,src, &damage, dmg.flag, skill_id);
+
+ if( damage && sc && sc->data[SC_GENSOU] && dmg.flag&BF_MAGIC ){
+ struct block_list *nbl = NULL;
+ nbl = battle_getenemyarea(bl,bl->x,bl->y,2,BL_CHAR,bl->id);
+ if( nbl ){ // Only one target is chosen.
+ damage = damage / 2; // Deflect half of the damage to a target nearby
+ clif_skill_damage(bl, nbl, tick, status_get_amotion(src), 0, status_fix_damage(bl,nbl,damage,0), dmg.div_, OB_OBOROGENSOU_TRANSITION_ATK, -1, 6);
+ }
+ }
+
+ //Skill hit type
+ type=(skill_id==0)?5:skill_get_hit(skill_id);
+
+ if(damage < dmg.div_
+ //Only skills that knockback even when they miss. [Skotlex]
+ && skill_id != CH_PALMSTRIKE)
+ dmg.blewcount = 0;
+
+ if(skill_id == CR_GRANDCROSS||skill_id == NPC_GRANDDARKNESS) {
+ if(battle_config.gx_disptype) dsrc = src;
+ if(src == bl) type = 4;
+ else flag|=SD_ANIMATION;
+ }
+ if(skill_id == NJ_TATAMIGAESHI) {
+ dsrc = src; //For correct knockback.
+ flag|=SD_ANIMATION;
+ }
+
+ if(sd) {
+ int flag = 0; //Used to signal if this skill can be combo'ed later on.
+ struct status_change_entry *sce;
+ if ((sce = sd->sc.data[SC_COMBO])) {//End combo state after skill is invoked. [Skotlex]
+ switch (skill_id) {
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if (pc_famerank(sd->status.char_id,MAPID_TAEKWON)) {//Extend combo time.
+ sce->val1 = skill_id; //Update combo-skill
+ sce->val3 = skill_id;
+ if( sce->timer != INVALID_TIMER )
+ delete_timer(sce->timer, status_change_timer);
+ sce->timer = add_timer(tick+sce->val4, status_change_timer, src->id, SC_COMBO);
+ break;
+ }
+ unit_cancel_combo(src); // Cancel combo wait
+ break;
+ default:
+ if( src == dsrc ) // Ground skills are exceptions. [Inkfish]
+ status_change_end(src, SC_COMBO, INVALID_TIMER);
+ }
+ }
+ switch(skill_id) {
+ case MO_TRIPLEATTACK:
+ if (pc_checkskill(sd, MO_CHAINCOMBO) > 0 || pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+ flag=1;
+ break;
+ case MO_CHAINCOMBO:
+ if(pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0)
+ flag=1;
+ break;
+ case MO_COMBOFINISH:
+ if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka]
+ party_skill_check(sd, sd->status.party_id, MO_COMBOFINISH, skill_lv);
+ if (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0)
+ flag=1;
+ case CH_TIGERFIST:
+ if (!flag && pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)
+ flag=1;
+ case CH_CHAINCRUSH:
+ if (!flag && pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball > 0 && sd->sc.data[SC_EXPLOSIONSPIRITS])
+ flag=1;
+ break;
+ case AC_DOUBLE:
+ if( (tstatus->race == RC_BRUTE || tstatus->race == RC_INSECT) && pc_checkskill(sd, HT_POWER))
+ {
+ //TODO: This code was taken from Triple Blows, is this even how it should be? [Skotlex]
+ sc_start2(src,SC_COMBO,100,HT_POWER,bl->id,2000);
+ clif_combo_delay(src,2000);
+ }
+ break;
+ case TK_COUNTER:
+ { //bonus from SG_FRIEND [Komurka]
+ int level;
+ if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
+ party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
+ }
+ break;
+ case SL_STIN:
+ case SL_STUN:
+ if (skill_lv >= 7 && !sd->sc.data[SC_SMA])
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv));
+ break;
+ case GS_FULLBUSTER:
+ //Can't attack nor use items until skill's delay expires. [Skotlex]
+ sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick;
+ break;
+ case SR_DRAGONCOMBO:
+ if( pc_checkskill(sd, SR_FALLENEMPIRE) > 0 )
+ flag = 1;
+ break;
+ case SR_FALLENEMPIRE:
+ if( pc_checkskill(sd, SR_TIGERCANNON) > 0 || pc_checkskill(sd, SR_GATEOFHELL) > 0 )
+ flag = 1;
+ break;
+ } //Switch End
+ if (flag) { //Possible to chain
+ flag = DIFF_TICK(sd->ud.canact_tick, tick);
+ if (flag < 1) flag = 1;
+ sc_start2(src,SC_COMBO,100,skill_id,bl->id,flag);
+ clif_combo_delay(src, flag);
+ }
+ }
+
+ //Display damage.
+ switch( skill_id )
+ {
+ case PA_GOSPEL: //Should look like Holy Cross [Skotlex]
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5);
+ break;
+ //Skills that need be passed as a normal attack for the client to display correctly.
+ case HVAN_EXPLOSION:
+ case NPC_SELFDESTRUCTION:
+ if(src->type==BL_PC)
+ dmg.blewcount = 10;
+ dmg.amotion = 0; //Disable delay or attack will do no damage since source is dead by the time it takes effect. [Skotlex]
+ // fall through
+ case KN_AUTOCOUNTER:
+ case NPC_CRITICALSLASH:
+ case TF_DOUBLE:
+ case GS_CHAINACTION:
+ dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2);
+ break;
+
+ case AS_SPLASHER:
+ if( flag&SD_ANIMATION ) // the surrounding targets
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, 5); // needs -1 as skill level
+ else // the central target doesn't display an animation
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, 5); // needs -2(!) as skill level
+ break;
+ case WL_HELLINFERNO:
+ case SR_EARTHSHAKER:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skill_id,-2,6);
+ break;
+ case WL_SOULEXPANSION:
+ case WL_COMET:
+ case KO_MUCHANAGE:
+ case NJ_HUUMA:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,skill_lv,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 LG_OVERBRAND_BRANDISH:
+ case LG_OVERBRAND_PLUSATK:
+ case EL_FIRE_BOMB:
+ case EL_FIRE_BOMB_ATK:
+ case EL_FIRE_WAVE:
+ case EL_FIRE_WAVE_ATK:
+ case EL_FIRE_MANTLE:
+ case EL_CIRCLE_OF_FIRE:
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WATER_SCREW:
+ case EL_WATER_SCREW_ATK:
+ case EL_WIND_SLASH:
+ case EL_TIDAL_WEAPON:
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ case EL_HURRICANE:
+ case EL_HURRICANE_ATK:
+ case KO_BAKURETSU:
+ case GN_CRAZYWEED_ATK:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6);
+ break;
+ case EL_STONE_RAIN:
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,(flag&1)?8:5);
+ break;
+ case WM_SEVERE_RAINSTORM_MELEE:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,skill_lv,5);
+ break;
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_REVERBERATION,-2,6);
+ break;
+ case HT_CLAYMORETRAP:
+ case HT_BLASTMINE:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case RA_CLUSTERBOMB:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id,flag&SD_LEVEL?-1:skill_lv, 5);
+ if( dsrc != src ) // avoid damage display redundancy
+ break;
+ case HT_LANDMINE:
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, type);
+ break;
+ case WZ_SIGHTBLASTER:
+ dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
+ break;
+ case AB_DUPLELIGHT_MELEE:
+ case AB_DUPLELIGHT_MAGIC:
+ dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */
+ default:
+ if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit.
+ type = 5;
+ if( bl->type == BL_SKILL ){
+ TBL_SKILL *su = (TBL_SKILL*)bl;
+ if( su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP )// show damage on trap targets
+ clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
+ }
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, type);
+ break;
+ }
+
+ map_freeblock_lock();
+
+ if(damage > 0 && dmg.flag&BF_SKILL && tsd
+ && pc_checkskill(tsd,RG_PLAGIARISM)
+ && (!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]
+ int copy_skill = skill_id;
+ /**
+ * Copy Referal: dummy skills should point to their source upon copying
+ **/
+ switch( skill_id ) {
+ case AB_DUPLELIGHT_MELEE:
+ case AB_DUPLELIGHT_MAGIC:
+ copy_skill = AB_DUPLELIGHT;
+ break;
+ case WL_CHAINLIGHTNING_ATK:
+ copy_skill = WL_CHAINLIGHTNING;
+ break;
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ copy_skill = WM_REVERBERATION;
+ break;
+ case WM_SEVERE_RAINSTORM_MELEE:
+ copy_skill = WM_SEVERE_RAINSTORM;
+ break;
+ case GN_CRAZYWEED_ATK:
+ copy_skill = GN_CRAZYWEED;
+ break;
+ case GN_HELLS_PLANT_ATK:
+ copy_skill = GN_HELLS_PLANT;
+ break;
+ case LG_OVERBRAND_BRANDISH:
+ case LG_OVERBRAND_PLUSATK:
+ copy_skill = LG_OVERBRAND;
+ 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;
+ if( sc && sc->data[SC__REPRODUCE] && (lv = sc->data[SC__REPRODUCE]->val1) ) {
+ //Level dependent and limitation.
+ lv = min(lv,skill_get_max(copy_skill));
+ if( tsd->reproduceskill_id && tsd->status.skill[tsd->reproduceskill_id].flag == SKILL_FLAG_PLAGIARIZED ) {
+ tsd->status.skill[tsd->reproduceskill_id].id = 0;
+ tsd->status.skill[tsd->reproduceskill_id].lv = 0;
+ tsd->status.skill[tsd->reproduceskill_id].flag = 0;
+ clif_deleteskill(tsd,tsd->reproduceskill_id);
+ }
+
+ tsd->reproduceskill_id = copy_skill;
+ pc_setglobalreg(tsd, "REPRODUCE_SKILL", copy_skill);
+ pc_setglobalreg(tsd, "REPRODUCE_SKILL_LV", lv);
+
+ tsd->status.skill[copy_skill].id = copy_skill;
+ tsd->status.skill[copy_skill].lv = lv;
+ tsd->status.skill[copy_skill].flag = SKILL_FLAG_PLAGIARIZED;
+ clif_addskill(tsd,copy_skill);
+ } else {
+ lv = skill_lv;
+ if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED){
+ tsd->status.skill[tsd->cloneskill_id].id = 0;
+ tsd->status.skill[tsd->cloneskill_id].lv = 0;
+ tsd->status.skill[tsd->cloneskill_id].flag = 0;
+ clif_deleteskill(tsd,tsd->cloneskill_id);
+ }
+
+ if ((type = pc_checkskill(tsd,RG_PLAGIARISM)) < lv)
+ lv = type;
+
+ tsd->cloneskill_id = copy_skill;
+ pc_setglobalreg(tsd, "CLONE_SKILL", copy_skill);
+ pc_setglobalreg(tsd, "CLONE_SKILL_LV", lv);
+
+ tsd->status.skill[skill_id].id = copy_skill;
+ tsd->status.skill[skill_id].lv = lv;
+ tsd->status.skill[skill_id].flag = SKILL_FLAG_PLAGIARIZED;
+ clif_addskill(tsd,skill_id);
+ }
+ }
+ }
+
+ if (dmg.dmg_lv >= ATK_MISS && (type = skill_get_walkdelay(skill_id, skill_lv)) > 0)
+ { //Skills with can't walk delay also stop normal attacking for that
+ //duration when the attack connects. [Skotlex]
+ struct unit_data *ud = unit_bl2ud(src);
+ if (ud && DIFF_TICK(ud->attackabletime, tick + type) < 0)
+ ud->attackabletime = tick + type;
+ }
+
+ if( !dmg.amotion )
+ { //Instant damage
+ if( !sc || (!sc->data[SC_DEVOTION] && skill_id != CR_REFLECTSHIELD) )
+ status_fix_damage(src,bl,damage,dmg.dmotion); //Deal damage before knockback to allow stuff like firewall+storm gust combo.
+ if( !status_isdead(bl) )
+ skill_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,dmg.dmg_lv,tick);
+ if( damage > 0 ) //Counter status effects [Skotlex]
+ skill_counter_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,tick);
+ }
+ // Hell Inferno burning status only starts if Fire part hits.
+ if( skill_id == WL_HELLINFERNO && dmg.damage > 0 )
+ sc_start4(bl,SC_BURNING,55+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
+ // Apply knock back chance in SC_TRIANGLESHOT skill.
+ else if( skill_id == SC_TRIANGLESHOT && rnd()%100 > (1 + skill_lv) )
+ 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)
+ if (dmg.blewcount > 0 && bl!=dsrc && !status_isdead(bl)) {
+ int8 dir = -1; // default
+ switch(skill_id) {//direction
+ case MG_FIREWALL:
+ case PR_SANCTUARY:
+ case SC_TRIANGLESHOT:
+ case LG_OVERBRAND:
+ case SR_KNUCKLEARROW:
+ case GN_WALLOFTHORN:
+ case EL_FIRE_MANTLE:
+ dir = unit_getdir(bl);// backwards
+ break;
+ // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics.
+ case WZ_STORMGUST:
+ dir = rand()%8;
+ break;
+ case WL_CRIMSONROCK:
+ dir = map_calc_dir(bl,skill_area_temp[4],skill_area_temp[5]);
+ break;
+
+ }
+ //blown-specific handling
+ switch( skill_id ) {
+ case LG_OVERBRAND:
+ if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) ) {
+ short dir_x, dir_y;
+ dir_x = dirx[(dir+4)%8];
+ dir_y = diry[(dir+4)%8];
+ if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 )
+ skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag );
+ } else
+ skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag );
+ break;
+ case SR_KNUCKLEARROW:
+ if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) && !(flag&4) ) {
+ short dir_x, dir_y;
+ dir_x = dirx[(dir+4)%8];
+ dir_y = diry[(dir+4)%8];
+ if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 )
+ skill_addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4);
+ }
+ break;
+ case GN_WALLOFTHORN:
+ unit_stop_walking(bl,1);
+ skill_blown(dsrc,bl,dmg.blewcount,dir, 0x2 );
+ clif_fixpos(bl);
+ break;
+ default:
+ skill_blown(dsrc,bl,dmg.blewcount,dir, 0x0 );
+ if ( !dmg.blewcount && bl->type == BL_SKILL && damage > 0 ){
+ TBL_SKILL *su = (TBL_SKILL*)bl;
+ if( su->group && su->group->skill_id == HT_BLASTMINE)
+ skill_blown(src, bl, 3, -1, 0);
+ }
+ break;
+ }
+ }
+
+ //Delayed damage must be dealt after the knockback (it needs to know actual position of target)
+ if (dmg.amotion)
+ battle_delay_damage(tick, dmg.amotion,src,bl,dmg.flag,skill_id,skill_lv,damage,dmg.dmg_lv,dmg.dmotion);
+
+ if( sc && sc->data[SC_DEVOTION] && skill_id != PA_PRESSURE )
+ {
+ struct status_change_entry *sce = sc->data[SC_DEVOTION];
+ struct block_list *d_bl = map_id2bl(sce->val1);
+
+ if( d_bl && (
+ (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == bl->id) ||
+ (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == bl->id)
+ ) && check_distance_bl(bl, d_bl, sce->val3) )
+ {
+ if(!rmdamage){
+ clif_damage(d_bl,d_bl, gettick(), 0, 0, damage, 0, 0, 0);
+ status_fix_damage(NULL,d_bl, damage, 0);
+ }
+ else{//Reflected magics are done directly on the target not on paladin
+ //This check is only for magical skill.
+ //For BF_WEAPON skills types track var rdamage and function battle_calc_return_damage
+ clif_damage(bl,bl, gettick(), 0, 0, damage, 0, 0, 0);
+ status_fix_damage(bl,bl, damage, 0);
+ }
+ }
+ else {
+ status_change_end(bl, SC_DEVOTION, INVALID_TIMER);
+ if( !dmg.amotion )
+ status_fix_damage(src,bl,damage,dmg.dmotion);
+ }
+ }
+
+ if(damage > 0 && !(tstatus->mode&MD_BOSS)) {
+ if( skill_id == RG_INTIMIDATE ) {
+ int rate = 50 + skill_lv * 5;
+ rate = rate + (status_get_lv(src) - status_get_lv(bl));
+ if(rnd()%100 < rate)
+ skill_addtimerskill(src,tick + 800,bl->id,0,0,skill_id,skill_lv,0,flag);
+ } else if( skill_id == SC_FATALMENACE )
+ skill_addtimerskill(src,tick + 800,bl->id,skill_area_temp[4],skill_area_temp[5],skill_id,skill_lv,0,flag);
+ }
+
+ if(skill_id == CR_GRANDCROSS || skill_id == NPC_GRANDDARKNESS)
+ dmg.flag |= BF_WEAPON;
+
+ if( sd && src != bl && damage > 0 && ( dmg.flag&BF_WEAPON ||
+ (dmg.flag&BF_MISC && (skill_id == RA_CLUSTERBOMB || skill_id == RA_FIRINGTRAP || skill_id == RA_ICEBOUNDTRAP || skill_id == RK_DRAGONBREATH)) ) )
+ {
+ if (battle_config.left_cardfix_to_right)
+ battle_drain(sd, bl, dmg.damage, dmg.damage, tstatus->race, tstatus->mode&MD_BOSS);
+ else
+ battle_drain(sd, bl, dmg.damage, dmg.damage2, tstatus->race, tstatus->mode&MD_BOSS);
+ }
+
+ if( rdamage > 0 ) {
+ if( sc && sc->data[SC_REFLECTDAMAGE] ) {
+ if( src != bl )// Don't reflect your own damage (Grand Cross)
+ map_foreachinshootrange(battle_damage_area,bl,skill_get_splash(LG_REFLECTDAMAGE,1),BL_CHAR,tick,bl,dmg.amotion,sstatus->dmotion,rdamage,tstatus->race);
+ } else {
+ if( dmg.amotion )
+ battle_delay_damage(tick, dmg.amotion,bl,src,0,CR_REFLECTSHIELD,0,rdamage,ATK_DEF,0);
+ else
+ status_fix_damage(bl,src,rdamage,0);
+ clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0); // in aegis damage reflected is shown in single hit.
+ //Use Reflect Shield to signal this kind of skill trigger. [Skotlex]
+ if( tsd && src != bl )
+ 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 ) {
+ /**
+ * Post-damage effects
+ **/
+ switch( skill_id ) {
+ case RK_CRUSHSTRIKE:
+ skill_break_equip(src,EQP_WEAPON,2000,BCT_SELF); // 20% chance to destroy the weapon.
+ break;
+ case GC_VENOMPRESSURE: {
+ struct status_change *ssc = status_get_sc(src);
+ if( ssc && ssc->data[SC_POISONINGWEAPON] && rnd()%100 < 70 + 5*skill_lv ) {
+ sc_start(bl,ssc->data[SC_POISONINGWEAPON]->val2,100,ssc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON, 1));
+ status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ }
+ break;
+ case WM_METALICSOUND:
+ status_zap(bl, 0, damage*100/(100*(110-pc_checkskill(sd,WM_LESSON)*10)));
+ break;
+ case SR_TIGERCANNON:
+ status_zap(bl, 0, damage/10); // 10% of damage dealt
+ break;
+ }
+ if( sd )
+ skill_onskillusage(sd, bl, skill_id, tick);
+ }
+
+ if (!(flag&2) &&
+ (
+ skill_id == MG_COLDBOLT || skill_id == MG_FIREBOLT || skill_id == MG_LIGHTNINGBOLT
+ ) &&
+ (sc = status_get_sc(src)) &&
+ sc->data[SC_DOUBLECAST] &&
+ rnd() % 100 < sc->data[SC_DOUBLECAST]->val2)
+ {
+// skill_addtimerskill(src, tick + dmg.div_*dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2);
+ skill_addtimerskill(src, tick + dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2);
+ }
+
+ map_freeblock_unlock();
+
+ return damage;
+}
+
+/*==========================================
+ * sub fonction for recursive skill call.
+ * Checking bl battle flag and display dammage
+ * then call func with source,target,skill_id,skill_lv,tick,flag
+ *------------------------------------------*/
+typedef int (*SkillFunc)(struct block_list *, struct block_list *, int, int, unsigned int, int);
+int skill_area_sub (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ uint16 skill_id,skill_lv;
+ int flag;
+ unsigned int tick;
+ SkillFunc func;
+
+ nullpo_ret(bl);
+
+ src=va_arg(ap,struct block_list *);
+ skill_id=va_arg(ap,int);
+ skill_lv=va_arg(ap,int);
+ tick=va_arg(ap,unsigned int);
+ flag=va_arg(ap,int);
+ func=va_arg(ap,SkillFunc);
+
+ if(battle_check_target(src,bl,flag) > 0)
+ {
+ // several splash skills need this initial dummy packet to display correctly
+ if (flag&SD_PREAMBLE && skill_area_temp[2] == 0)
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+
+ if (flag&(SD_SPLASH|SD_PREAMBLE))
+ skill_area_temp[2]++;
+
+ return func(src,bl,skill_id,skill_lv,tick,flag);
+ }
+ return 0;
+}
+
+static int skill_check_unit_range_sub (struct block_list *bl, va_list ap)
+{
+ struct skill_unit *unit;
+ uint16 skill_id,g_skill_id;
+
+ unit = (struct skill_unit *)bl;
+
+ if(bl->prev == NULL || bl->type != BL_SKILL)
+ return 0;
+
+ if(!unit->alive)
+ return 0;
+
+ skill_id = va_arg(ap,int);
+ g_skill_id = unit->group->skill_id;
+
+ switch (skill_id) {
+ case MH_STEINWAND:
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ case SC_MAELSTROM:
+ if(g_skill_id != MH_STEINWAND && g_skill_id != MG_SAFETYWALL && g_skill_id != AL_PNEUMA && g_skill_id != SC_MAELSTROM)
+ return 0;
+ break;
+ case AL_WARP:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ case HT_CLAYMORETRAP:
+ case HT_TALKIEBOX:
+ case HP_BASILICA:
+ case RA_ELECTRICSHOCKER:
+ case RA_CLUSTERBOMB:
+ case RA_MAGENTATRAP:
+ case RA_COBALTTRAP:
+ case RA_MAIZETRAP:
+ case RA_VERDURETRAP:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ case SC_DIMENSIONDOOR:
+ case SC_BLOODYLUST:
+ //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set)
+ if (skill_id != g_skill_id && !(skill_get_inf2(g_skill_id)&INF2_TRAP) && g_skill_id != AS_VENOMDUST && g_skill_id != MH_POISON_MIST)
+ return 0;
+ break;
+ default: //Avoid stacking with same kind of trap. [Skotlex]
+ if (g_skill_id != skill_id)
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv)
+{
+ //Non players do not check for the skill's splash-trigger area.
+ int range = bl->type==BL_PC?skill_get_unit_range(skill_id, skill_lv):0;
+ int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skill_id);
+ return 0;
+ }
+
+ range += layout_type;
+ return map_foreachinarea(skill_check_unit_range_sub,bl->m,x-range,y-range,x+range,y+range,BL_SKILL,skill_id);
+}
+
+static int skill_check_unit_range2_sub (struct block_list *bl, va_list ap)
+{
+ uint16 skill_id;
+
+ if(bl->prev == NULL)
+ return 0;
+
+ skill_id = va_arg(ap,int);
+
+ if( status_isdead(bl) && skill_id != AL_WARP )
+ return 0;
+
+ if( skill_id == HP_BASILICA && bl->type == BL_PC )
+ return 0;
+
+ if( skill_id == AM_DEMONSTRATION && bl->type == BL_MOB && ((TBL_MOB*)bl)->class_ == MOBID_EMPERIUM )
+ return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex]
+ return 1;
+}
+
+static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv)
+{
+ int range, type;
+
+ switch (skill_id) { // to be expanded later
+ case WZ_ICEWALL:
+ range = 2;
+ break;
+ default:
+ {
+ int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skill_id);
+ return 0;
+ }
+ range = skill_get_unit_range(skill_id,skill_lv) + layout_type;
+ }
+ break;
+ }
+
+ // if the caster is a monster/NPC, only check for players
+ // otherwise just check characters
+ if (bl->type == BL_PC)
+ type = BL_CHAR;
+ else
+ type = BL_PC;
+
+ return map_foreachinarea(skill_check_unit_range2_sub, bl->m,
+ x - range, y - range, x + range, y + range,
+ type, skill_id);
+}
+
+int skill_guildaura_sub (struct map_session_data* sd, int id, int strvit, int agidex)
+{
+ if(id == sd->bl.id && battle_config.guild_aura&16)
+ return 0; // Do not affect guild leader
+
+ if (sd->sc.data[SC_GUILDAURA]) {
+ struct status_change_entry *sce = sd->sc.data[SC_GUILDAURA];
+ if( sce->val3 != strvit || sce->val4 != agidex ) {
+ sce->val3 = strvit;
+ sce->val4 = agidex;
+ status_calc_bl(&sd->bl, status_sc2scb_flag(SC_GUILDAURA));
+ }
+ return 0;
+ }
+ sc_start4(&sd->bl, SC_GUILDAURA,100, 1, id, strvit, agidex, 1000);
+ return 1;
+}
+
+/*==========================================
+ * Checks that you have the requirements for casting a skill for homunculus/mercenary.
+ * Flag:
+ * &1: finished casting the skill (invoke hp/sp/item consumption)
+ * &2: picked menu entry (Warp Portal, Teleport and other menu based skills)
+ *------------------------------------------*/
+static int skill_check_condition_mercenary(struct block_list *bl, int skill, int lv, int type)
+{
+ struct status_data *status;
+ struct map_session_data *sd = NULL;
+ int i, hp, sp, hp_rate, sp_rate, state, mhp;
+ uint16 idx;
+ int itemid[MAX_SKILL_ITEM_REQUIRE],amount[ARRAYLENGTH(itemid)],index[ARRAYLENGTH(itemid)];
+
+ if( lv < 1 || lv > MAX_SKILL_LEVEL )
+ return 0;
+ nullpo_ret(bl);
+
+ switch( bl->type )
+ {
+ case BL_HOM: sd = ((TBL_HOM*)bl)->master; break;
+ case BL_MER: sd = ((TBL_MER*)bl)->master; break;
+ }
+
+ status = status_get_status_data(bl);
+ if( (idx = skill_get_index(skill)) == 0 )
+ return 0;
+
+ // Requeriments
+ for( i = 0; i < ARRAYLENGTH(itemid); i++ )
+ {
+ itemid[i] = skill_db[idx].itemid[i];
+ amount[i] = skill_db[idx].amount[i];
+ }
+ hp = skill_db[idx].hp[lv-1];
+ sp = skill_db[idx].sp[lv-1];
+ hp_rate = skill_db[idx].hp_rate[lv-1];
+ sp_rate = skill_db[idx].sp_rate[lv-1];
+ state = skill_db[idx].state;
+ if( (mhp = skill_db[idx].mhp[lv-1]) > 0 )
+ hp += (status->max_hp * mhp) / 100;
+ if( hp_rate > 0 )
+ hp += (status->hp * hp_rate) / 100;
+ else
+ hp += (status->max_hp * (-hp_rate)) / 100;
+ if( sp_rate > 0 )
+ sp += (status->sp * sp_rate) / 100;
+ else
+ sp += (status->max_sp * (-sp_rate)) / 100;
+
+ if( bl->type == BL_HOM )
+ { // Intimacy Requeriments
+ struct homun_data *hd = BL_CAST(BL_HOM, bl);
+ switch( skill )
+ {
+ case HFLI_SBR44:
+ if( hd->homunculus.intimacy <= 200 )
+ return 0;
+ break;
+ case HVAN_EXPLOSION:
+ if( hd->homunculus.intimacy < (unsigned int)battle_config.hvan_explosion_intimate )
+ return 0;
+ break;
+ }
+ }
+
+ if( !(type&2) )
+ {
+ if( hp > 0 && status->hp <= (unsigned int)hp )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_HP_INSUFFICIENT, 0);
+ return 0;
+ }
+ if( sp > 0 && status->sp <= (unsigned int)sp )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_SP_INSUFFICIENT, 0);
+ return 0;
+ }
+ }
+
+ if( !type )
+ switch( state )
+ {
+ case ST_MOVE_ENABLE:
+ if( !unit_can_move(bl) )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ break;
+ }
+ if( !(type&1) )
+ return 1;
+
+ // Check item existences
+ for( i = 0; i < ARRAYLENGTH(itemid); i++ )
+ {
+ index[i] = -1;
+ if( itemid[i] < 1 ) continue; // No item
+ index[i] = pc_search_inventory(sd, itemid[i]);
+ if( index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i] )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+
+ // Consume items
+ for( i = 0; i < ARRAYLENGTH(itemid); i++ )
+ {
+ if( index[i] >= 0 ) pc_delitem(sd, index[i], amount[i], 0, 1, LOG_TYPE_CONSUME);
+ }
+
+ if( type&2 )
+ return 1;
+
+ if( sp || hp )
+ status_zap(bl, hp, sp);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_area_sub_count (struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *src = map_id2bl(id),*target;
+ struct unit_data *ud = unit_bl2ud(src);
+ struct skill_timerskill *skl = NULL;
+ int range;
+
+ nullpo_ret(src);
+ nullpo_ret(ud);
+ skl = ud->skilltimerskill[data];
+ nullpo_ret(skl);
+ ud->skilltimerskill[data] = NULL;
+
+ do {
+ if(src->prev == NULL)
+ break; // Source not on Map
+ if(skl->target_id) {
+ target = map_id2bl(skl->target_id);
+ if( ( skl->skill_id == RG_INTIMIDATE || skl->skill_id == SC_FATALMENACE ) && (!target || target->prev == NULL || !check_distance_bl(src,target,AREA_SIZE)) )
+ target = src; //Required since it has to warp.
+ if(target == NULL)
+ break; // Target offline?
+ if(target->prev == NULL)
+ break; // Target not on Map
+ if(src->m != target->m)
+ break; // Different Maps
+ if(status_isdead(src))
+ break; // Caster is Dead
+ if(status_isdead(target) && skl->skill_id != RG_INTIMIDATE && skl->skill_id != WZ_WATERBALL)
+ break;
+
+ switch(skl->skill_id) {
+ case RG_INTIMIDATE:
+ if (unit_warp(src,-1,-1,-1,CLR_TELEPORT) == 0) {
+ short x,y;
+ map_search_freecell(src, 0, &x, &y, 1, 1, 0);
+ if (target != src && !status_isdead(target))
+ unit_warp(target, -1, x, y, CLR_TELEPORT);
+ }
+ break;
+ case BA_FROSTJOKER:
+ case DC_SCREAM:
+ range= skill_get_splash(skl->skill_id, skl->skill_lv);
+ map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range,
+ skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick);
+ break;
+ case NPC_EARTHQUAKE:
+ if( skl->type > 1 )
+ skill_addtimerskill(src,tick+250,src->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
+ skill_area_temp[0] = map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR, src, skl->skill_id, skl->skill_lv, tick, BCT_ENEMY, skill_area_sub_count);
+ skill_area_temp[1] = src->id;
+ skill_area_temp[2] = 0;
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), splash_target(src), src, skl->skill_id, skl->skill_lv, tick, skl->flag, skill_castend_damage_id);
+ break;
+ case WZ_WATERBALL:
+ skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
+ if (!status_isdead(target))
+ skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
+ if (skl->type>1 && !status_isdead(target) && !status_isdead(src)) {
+ skill_addtimerskill(src,tick+125,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
+ } else {
+ struct status_change *sc = status_get_sc(src);
+ if(sc) {
+ if(sc->data[SC_SPIRIT] &&
+ sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
+ sc->data[SC_SPIRIT]->val3 == skl->skill_id)
+ sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
+ }
+ }
+ break;
+ /**
+ * Warlock
+ **/
+ case WL_CHAINLIGHTNING_ATK:
+ {
+ 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
+ skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
+ 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|SD_ANIMATION);
+ skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
+ if( skl->type >= 3 )
+ { // Final Hit
+ 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[rnd()%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;
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ skill_castend_damage_id(src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL); // damage should split among targets
+ break;
+ case SC_FATALMENACE:
+ if( src == target ) // Casters Part
+ unit_warp(src, -1, skl->x, skl->y, 3);
+ else { // Target's Part
+ short x = skl->x, y = skl->y;
+ map_search_freecell(NULL, target->m, &x, &y, 2, 2, 1);
+ unit_warp(target,-1,x,y,3);
+ }
+ break;
+ case LG_MOONSLASHER:
+ case SR_WINDMILL:
+ if( target->type == BL_PC ) {
+ struct map_session_data *tsd = NULL;
+ if( (tsd = ((TBL_PC*)target)) && !pc_issit(tsd) ) {
+ pc_setsit(tsd);
+ skill_sit(tsd,1);
+ clif_sitting(&tsd->bl);
+ }
+ }
+ break;
+ case LG_OVERBRAND_BRANDISH:
+ case LG_OVERBRAND_PLUSATK:
+ case SR_KNUCKLEARROW:
+ skill_attack(BF_WEAPON, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL);
+ break;
+ case GN_SPORE_EXPLOSION:
+ map_foreachinrange(skill_area_sub, target, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR,
+ src, skl->skill_id, skl->skill_lv, 0, skl->flag|1|BCT_ENEMY, skill_castend_damage_id);
+ break;
+ case CH_PALMSTRIKE:
+ {
+ struct status_change* tsc = status_get_sc(target);
+ struct status_change* sc = status_get_sc(src);
+ if( tsc && tsc->option&OPTION_HIDE ||
+ sc && sc->option&OPTION_HIDE ){
+ skill_blown(src,target,skill_get_blewcount(skl->skill_id, skl->skill_lv), -1, 0x0 );
+ break;
+ }
+ }
+ default:
+ skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
+ break;
+ }
+ }
+ else {
+ if(src->m != skl->map)
+ break;
+ switch( skl->skill_id )
+ {
+ case WZ_METEOR:
+ if( skl->type >= 0 )
+ {
+ int x = skl->type>>16, y = skl->type&0xFFFF;
+ if( path_search_long(NULL, src->m, src->x, src->y, x, y, CELL_CHKWALL) )
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,x,y,skl->flag);
+ if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
+ clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick);
+ }
+ 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 GN_CRAZYWEED_ATK:
+ {
+ int dummy = 1, i = skill_get_unit_range(skl->skill_id,skl->skill_lv);
+ map_foreachinarea(skill_cell_overlap, src->m, skl->x-i, skl->y-i, skl->x+i, skl->y+i, BL_SKILL, skl->skill_id, &dummy, src);
+ }
+ case WL_EARTHSTRAIN:
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag);
+ break;
+
+ }
+ }
+ } while (0);
+ //Free skl now that it is no longer needed.
+ ers_free(skill_timer_ers, skl);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_addtimerskill (struct block_list *src, unsigned int tick, int target, int x,int y, uint16 skill_id, uint16 skill_lv, int type, int flag)
+{
+ int i;
+ struct unit_data *ud;
+ nullpo_retr(1, src);
+ if (src->prev == NULL)
+ return 0;
+ ud = unit_bl2ud(src);
+ nullpo_retr(1, ud);
+
+ ARR_FIND( 0, MAX_SKILLTIMERSKILL, i, ud->skilltimerskill[i] == 0 );
+ if( i == MAX_SKILLTIMERSKILL ) return 1;
+
+ ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill);
+ ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i);
+ ud->skilltimerskill[i]->src_id = src->id;
+ ud->skilltimerskill[i]->target_id = target;
+ ud->skilltimerskill[i]->skill_id = skill_id;
+ ud->skilltimerskill[i]->skill_lv = skill_lv;
+ ud->skilltimerskill[i]->map = src->m;
+ ud->skilltimerskill[i]->x = x;
+ ud->skilltimerskill[i]->y = y;
+ ud->skilltimerskill[i]->type = type;
+ ud->skilltimerskill[i]->flag = flag;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_cleartimerskill (struct block_list *src)
+{
+ int i;
+ struct unit_data *ud;
+ nullpo_ret(src);
+ ud = unit_bl2ud(src);
+ nullpo_ret(ud);
+
+ for(i=0;i<MAX_SKILLTIMERSKILL;i++) {
+ if(ud->skilltimerskill[i]) {
+ delete_timer(ud->skilltimerskill[i]->timer, skill_timerskill);
+ ers_free(skill_timer_ers, ud->skilltimerskill[i]);
+ ud->skilltimerskill[i]=NULL;
+ }
+ }
+ return 1;
+}
+static int skill_ative_reverberation( struct block_list *bl, va_list ap) {
+ struct skill_unit *su = (TBL_SKILL*)bl;
+ struct skill_unit_group *sg;
+ if( bl->type != BL_SKILL )
+ return 0;
+ if( su->alive && (sg = su->group) && sg->skill_id == WM_REVERBERATION ) {
+ map_foreachinrange(skill_trap_splash, bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, bl, gettick());
+ su->limit=DIFF_TICK(gettick(),sg->tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ }
+ return 0;
+}
+
+static int skill_reveal_trap (struct block_list *bl, va_list ap)
+{
+ TBL_SKILL *su = (TBL_SKILL*)bl;
+ if (su->alive && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP)
+ { //Reveal trap.
+ //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex]
+ //clif_changetraplook(bl, su->group->unit_id);
+ clif_skill_setunit(su);
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *
+ *------------------------------------------*/
+int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_data *tstatus;
+ struct status_change *sc;
+
+ if (skill_id > 0 && !skill_lv) return 0;
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ if (bl->prev == NULL)
+ return 1;
+
+ sd = BL_CAST(BL_PC, src);
+
+ if (status_isdead(bl))
+ return 1;
+
+ if (skill_id && skill_get_type(skill_id) == BF_MAGIC && status_isimmune(bl) == 100)
+ { //GTB makes all targetted magic display miss with a single bolt.
+ sc_type sct = status_skill2sc(skill_id);
+ if(sct != SC_NONE)
+ status_change_end(bl, sct, INVALID_TIMER);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), status_get_dmotion(bl), 0, 1, skill_id, skill_lv, skill_get_hit(skill_id));
+ return 1;
+ }
+
+ sc = status_get_sc(src);
+ if (sc && !sc->count)
+ sc = NULL; //Unneeded
+
+ tstatus = status_get_status_data(bl);
+
+ map_freeblock_lock();
+
+ switch(skill_id)
+ {
+ case MER_CRASH:
+ case SM_BASH:
+ case MS_BASH:
+ case MC_MAMMONITE:
+ case TF_DOUBLE:
+ case AC_DOUBLE:
+ case MA_DOUBLE:
+ case AS_SONICBLOW:
+ case KN_PIERCE:
+ case ML_PIERCE:
+ case KN_SPEARBOOMERANG:
+ case TF_POISON:
+ case TF_SPRINKLESAND:
+ case AC_CHARGEARROW:
+ case MA_CHARGEARROW:
+ case RG_INTIMIDATE:
+ case AM_ACIDTERROR:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ case BA_DISSONANCE:
+ case CR_HOLYCROSS:
+ case NPC_DARKCROSS:
+ case CR_SHIELDCHARGE:
+ case CR_SHIELDBOOMERANG:
+ case NPC_PIERCINGATT:
+ case NPC_MENTALBREAKER:
+ case NPC_RANGEATTACK:
+ case NPC_CRITICALSLASH:
+ case NPC_COMBOATTACK:
+ case NPC_GUIDEDATTACK:
+ case NPC_POISON:
+ case NPC_RANDOMATTACK:
+ case NPC_WATERATTACK:
+ case NPC_GROUNDATTACK:
+ case NPC_FIREATTACK:
+ case NPC_WINDATTACK:
+ case NPC_POISONATTACK:
+ case NPC_HOLYATTACK:
+ case NPC_DARKNESSATTACK:
+ case NPC_TELEKINESISATTACK:
+ case NPC_UNDEADATTACK:
+ case NPC_ARMORBRAKE:
+ case NPC_WEAPONBRAKER:
+ case NPC_HELMBRAKE:
+ case NPC_SHIELDBRAKE:
+ case NPC_BLINDATTACK:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ case NPC_PETRIFYATTACK:
+ case NPC_CURSEATTACK:
+ case NPC_SLEEPATTACK:
+ case LK_AURABLADE:
+ case LK_SPIRALPIERCE:
+ case ML_SPIRALPIERCE:
+ case LK_HEADCRUSH:
+ case CG_ARROWVULCAN:
+ case HW_MAGICCRASHER:
+ case ITM_TOMAHAWK:
+ case MO_TRIPLEATTACK:
+ case CH_CHAINCRUSH:
+ case CH_TIGERFIST:
+ case PA_SHIELDCHAIN: // Shield Chain
+ case PA_SACRIFICE:
+ case WS_CARTTERMINATION: // Cart Termination
+ case AS_VENOMKNIFE:
+ case HT_PHANTASMIC:
+ case HT_POWER:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ case GS_CHAINACTION:
+ case GS_TRIPLEACTION:
+ case GS_MAGICALBULLET:
+ case GS_TRACKING:
+ case GS_PIERCINGSHOT:
+ case GS_RAPIDSHOWER:
+ case GS_DUST:
+ case GS_DISARM: // Added disarm. [Reddozen]
+ case GS_FULLBUSTER:
+ case NJ_SYURIKEN:
+ case NJ_KUNAI:
+ case ASC_BREAKER:
+ case HFLI_MOON: //[orn]
+ case HFLI_SBR44: //[orn]
+ case NPC_BLEEDING:
+ case NPC_CRITICALWOUND:
+ case NPC_HELLPOWER:
+ case RK_SONICWAVE:
+ case RK_HUNDREDSPEAR:
+ case AB_DUPLELIGHT_MELEE:
+ case RA_AIMEDBOLT:
+ case NC_AXEBOOMERANG:
+ case NC_POWERSWING:
+ case GC_CROSSIMPACT:
+ case GC_VENOMPRESSURE:
+ case SC_TRIANGLESHOT:
+ case SC_FEINTBOMB:
+ case LG_BANISHINGPOINT:
+ case LG_SHIELDPRESS:
+ case LG_RAGEBURST:
+ case LG_RAYOFGENESIS:
+ case LG_HESPERUSLIT:
+ case SR_FALLENEMPIRE:
+ case SR_CRESCENTELBOW_AUTOSPELL:
+ case SR_GATEOFHELL:
+ case SR_GENTLETOUCH_QUIET:
+ case WM_SEVERE_RAINSTORM_MELEE:
+ case WM_GREAT_ECHO:
+ case GN_SLINGITEM_RANGEMELEEATK:
+ case KO_JYUMONJIKIRI:
+ case KO_SETSUDAN:
+ case KO_KAIHOU:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,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);
+ case RK_WINDCUTTER:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
+ break;
+
+ case LK_JOINTBEAT: // decide the ailment first (affects attack damage and effect)
+ switch( rnd()%6 ){
+ case 0: flag |= BREAK_ANKLE; break;
+ case 1: flag |= BREAK_WRIST; break;
+ case 2: flag |= BREAK_KNEE; break;
+ case 3: flag |= BREAK_SHOULDER; break;
+ case 4: flag |= BREAK_WAIST; break;
+ case 5: flag |= BREAK_NECK; break;
+ }
+ //TODO: is there really no cleaner way to do this?
+ sc = status_get_sc(bl);
+ if (sc) sc->jb_flag = flag;
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case MO_COMBOFINISH:
+ if (!(flag&1) && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK)
+ { //Becomes a splash attack when Soul Linked.
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv),splash_target(src),
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ } else
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case TK_STORMKICK: // Taekwon kicks [Dralnu]
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_area_temp[1] = 0;
+ map_foreachinrange(skill_attack_area, src,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ BF_WEAPON, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY);
+ break;
+
+ case KN_CHARGEATK:
+ {
+ bool path = path_search_long(NULL, src->m, src->x, src->y, bl->x, bl->y,CELL_CHKWALL);
+ unsigned int dist = distance_bl(src, bl);
+ uint8 dir = map_calc_dir(bl, src->x, src->y);
+
+ // teleport to target (if not on WoE grounds)
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 0, 1) )
+ clif_slide(src, bl->x, bl->y);
+
+ // cause damage and knockback if the path to target was a straight one
+ if( path )
+ {
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, dist);
+ skill_blown(src, bl, dist, dir, 0);
+ //HACK: since knockback officially defaults to the left, the client also turns to the left... therefore,
+ // make the caster look in the direction of the target
+ unit_setdir(src, (dir+4)%8);
+ }
+
+ }
+ break;
+
+ case NC_FLAMELAUNCHER:
+ if (sd) pc_overheat(sd,1);
+ case SN_SHARPSHOOTING:
+ case MA_SHARPSHOOTING:
+ case NJ_KAMAITACHI:
+ case LG_CANNONSPEAR:
+ //It won't shoot through walls since on castend there has to be a direct
+ //line of sight between caster and target.
+ skill_area_temp[1] = bl->id;
+ map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,
+ skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src),
+ skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY);
+ break;
+
+ case NPC_ACIDBREATH:
+ case NPC_DARKNESSBREATH:
+ case NPC_FIREBREATH:
+ case NPC_ICEBREATH:
+ case NPC_THUNDERBREATH:
+ skill_area_temp[1] = bl->id;
+ map_foreachinpath(skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,
+ skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src),
+ skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY);
+ break;
+
+ case MO_INVESTIGATE:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+ break;
+
+ case RG_BACKSTAP:
+ {
+ uint8 dir = map_calc_dir(src, bl->x, bl->y), t_dir = unit_getdir(bl);
+ if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) {
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest]
+ unit_setdir(bl,dir);
+ }
+ else if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case MO_FINGEROFFENSIVE:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ if (battle_config.finger_offensive_type && sd) {
+ int i;
+ for (i = 1; i < sd->spiritball_old; i++)
+ skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
+ }
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+ break;
+
+ case MO_CHAINCOMBO:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+ break;
+
+ case NJ_ISSEN:
+ status_change_end(src, SC_NEN, INVALID_TIMER);
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ // fall through
+ case MO_EXTREMITYFIST:
+ {
+ short x, y, i = 2; // Move 2 cells for Issen(from target)
+ struct block_list *mbl = bl;
+ short dir = 0;
+
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+
+ if( skill_id == MO_EXTREMITYFIST )
+ {
+ mbl = src;
+ i = 3; // for Asura(from caster)
+ status_set_sp(src, 0, 0);
+ status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+#ifdef RENEWAL
+ sc_start(src,SC_EXTREMITYFIST2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+#endif
+ }else
+ status_set_hp(src,
+#ifdef RENEWAL
+ max(status_get_max_hp(src)/100, 1)
+#else
+ 1
+#endif
+ , 0);
+
+ dir = map_calc_dir(src,bl->x,bl->y);
+ if( dir > 0 && dir < 4) x = -i;
+ else if( dir > 4 ) x = i;
+ else x = 0;
+ if( dir > 2 && dir < 6 ) y = -i;
+ else if( dir == 7 || dir < 2 ) y = i;
+ else y = 0;
+ if( (mbl == src || !map_flag_gvg(src->m) && !map[src->m].flag.battleground) && // only NJ_ISSEN don't have slide effect in GVG
+ unit_movepos(src, mbl->x+x, mbl->y+y, 1, 1) ) {
+ clif_slide(src, src->x, src->y);
+ //uncomment this if you want to remove MO_EXTREMITYFIST glitchy walking effect. [malufett]
+ //clif_fixpos(src);
+ }
+ }
+ break;
+
+ //Splash attack skills.
+ case AS_GRIMTOOTH:
+ case MC_CARTREVOLUTION:
+ case NPC_SPLASHATTACK:
+ flag |= SD_PREAMBLE; // a fake packet will be sent for the first target to be hit
+ case AS_SPLASHER:
+ case SM_MAGNUM:
+ case MS_MAGNUM:
+ case HT_BLITZBEAT:
+ case AC_SHOWER:
+ case MA_SHOWER:
+ case MG_NAPALMBEAT:
+ case MG_FIREBALL:
+ case RG_RAID:
+ case HW_NAPALMVULCAN:
+ case NJ_HUUMA:
+ case NJ_BAKUENRYU:
+ case ASC_METEORASSAULT:
+ case GS_DESPERADO:
+ case GS_SPREADATTACK:
+ case NPC_EARTHQUAKE:
+ case NPC_PULSESTRIKE:
+ case NPC_HELLJUDGEMENT:
+ case NPC_VAMPIRE_GIFT:
+ case RK_IGNITIONBREAK:
+ case AB_JUDEX:
+ case WL_SOULEXPANSION:
+ case WL_CRIMSONROCK:
+ case WL_COMET:
+ case WL_JACKFROST:
+ case RA_ARROWSTORM:
+ case RA_WUGDASH:
+ case NC_SELFDESTRUCTION:
+ case NC_AXETORNADO:
+ case GC_ROLLINGCUTTER:
+ case GC_COUNTERSLASH:
+ case LG_MOONSLASHER:
+ case LG_EARTHDRIVE:
+ case SR_TIGERCANNON:
+ case SR_RAMPAGEBLASTER:
+ case SR_SKYNETBLOW:
+ case SR_WINDMILL:
+ case SR_RIDEINLIGHTNING:
+ case WM_SOUND_OF_DESTRUCTION:
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ case SO_VARETYR_SPEAR:
+ case GN_CART_TORNADO:
+ case GN_CARTCANNON:
+ case KO_HAPPOKUNAI:
+ case KO_HUUMARANKA:
+ case KO_MUCHANAGE:
+ case KO_BAKURETSU:
+ if( flag&1 ) {//Recursive invocation
+ // skill_area_temp[0] holds number of targets in area
+ // skill_area_temp[1] holds the id of the original target
+ // skill_area_temp[2] counts how many targets have already been processed
+ int sflag = skill_area_temp[0] & 0xFFF, heal;
+ if( flag&SD_LEVEL )
+ sflag |= SD_LEVEL; // -1 will be used in packets instead of the skill level
+ if( skill_area_temp[1] != bl->id && !(skill_get_inf2(skill_id)&INF2_NPC_SKILL) )
+ sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills)
+
+ heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, sflag);
+ if( skill_id == NPC_VAMPIRE_GIFT && heal > 0 ) {
+ clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
+ status_heal(src,heal,0,0);
+ }
+ } else {
+ switch ( skill_id ) {
+ case NJ_BAKUENRYU:
+ case LG_EARTHDRIVE:
+ case GN_CARTCANNON:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case LG_MOONSLASHER:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ break;
+ case NPC_EARTHQUAKE://FIXME: Isn't EarthQuake a ground skill after all?
+ skill_addtimerskill(src,tick+250,src->id,0,0,skill_id,skill_lv,2,flag|BCT_ENEMY|SD_SPLASH|1);
+ default:
+ break;
+ }
+
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ skill_area_temp[2] = 0;
+ if( skill_id == WL_CRIMSONROCK ) {
+ skill_area_temp[4] = bl->x;
+ skill_area_temp[5] = bl->y;
+ }
+ if( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC )
+ skill_area_temp[1] = 0;
+ // 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
+ if( flag&SD_LEVEL || skill_get_nk(skill_id)&NK_SPLASHSPLIT )
+ skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, (skill_id == AS_SPLASHER)?1:skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, 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(skill_id, skill_lv), ( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC )?BL_CHAR:splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ }
+ break;
+
+ case KN_BRANDISHSPEAR:
+ case ML_BRANDISH:
+ //Coded apart for it needs the flag passed to the damage calculation.
+ if (skill_area_temp[1] != bl->id)
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION);
+ else
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case KN_BOWLINGBASH:
+ case MS_BOWLINGBASH:
+ if(flag&1){
+ if(bl->id==skill_area_temp[1])
+ break;
+ //two hits for 500%
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION);
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION);
+ } else {
+ int i,c;
+ c = skill_get_blewcount(skill_id,skill_lv);
+ // keep moving target in the direction that src is looking, square by square
+ for(i=0;i<c;i++){
+ if (!skill_blown(src,bl,1,(unit_getdir(src)+4)%8,0x1))
+ break; //Can't knockback
+ skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_area_sub_count);
+ if( skill_area_temp[0] > 1 ) break; // collision
+ }
+ clif_blown(bl); //Update target pos.
+ if (i!=c) { //Splash
+ skill_area_temp[1] = bl->id;
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ }
+ //Weirdo dual-hit property, two attacks for 500%
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0);
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0);
+ }
+ break;
+
+ case KN_SPEARSTAB:
+ if(flag&1) {
+ if (bl->id==skill_area_temp[1])
+ break;
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION))
+ skill_blown(src,bl,skill_area_temp[2],-1,0);
+ } else {
+ int x=bl->x,y=bl->y,i,dir;
+ dir = map_calc_dir(bl,src->x,src->y);
+ skill_area_temp[1] = bl->id;
+ skill_area_temp[2] = skill_get_blewcount(skill_id,skill_lv);
+ // all the enemies between the caster and the target are hit, as well as the target
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0))
+ skill_blown(src,bl,skill_area_temp[2],-1,0);
+ for (i=0;i<4;i++) {
+ map_foreachincell(skill_area_sub,bl->m,x,y,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ x += dirx[dir];
+ y += diry[dir];
+ }
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex]
+ {
+ skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target.
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag))
+ map_foreachinrange(skill_area_sub,bl,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex]
+ // clif_skill_nodamage(src,bl,skill_id,skill_lv,0); //Can't make this one display the correct attack animation delay :/
+ clif_damage(src,bl,tick,status_get_amotion(src),0,-1,1,4,0); //Display an absorbed damage attack.
+ skill_addtimerskill(src, tick + (1000+status_get_amotion(src)), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
+ break;
+
+ case PR_TURNUNDEAD:
+ case ALL_RESURRECTION:
+ if (!battle_check_undead(tstatus->race, tstatus->def_ele))
+ break;
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case MG_SOULSTRIKE:
+ case NPC_DARKSTRIKE:
+ case MG_COLDBOLT:
+ case MG_FIREBOLT:
+ case MG_LIGHTNINGBOLT:
+ case WZ_EARTHSPIKE:
+ case AL_HEAL:
+ case AL_HOLYLIGHT:
+ case WZ_JUPITEL:
+ case NPC_DARKTHUNDER:
+ case PR_ASPERSIO:
+ case MG_FROSTDIVER:
+ case WZ_SIGHTBLASTER:
+ case WZ_SIGHTRASHER:
+ case NJ_KOUENKA:
+ case NJ_HYOUSENSOU:
+ case NJ_HUUJIN:
+ case AB_ADORAMUS:
+ case AB_RENOVATIO:
+ case AB_HIGHNESSHEAL:
+ case AB_DUPLELIGHT_MAGIC:
+ case WM_METALICSOUND:
+ case MH_ERASER_CUTTER:
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case NPC_MAGICALATTACK:
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ sc_start(src,status_skill2sc(skill_id),100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case HVAN_CAPRICE: //[blackhole89]
+ {
+ int ran=rnd()%4;
+ int sid = 0;
+ switch(ran)
+ {
+ case 0: sid=MG_COLDBOLT; break;
+ case 1: sid=MG_FIREBOLT; break;
+ case 2: sid=MG_LIGHTNINGBOLT; break;
+ case 3: sid=WZ_EARTHSPIKE; break;
+ }
+ skill_attack(BF_MAGIC,src,src,bl,sid,skill_lv,tick,flag|SD_LEVEL);
+ }
+ break;
+ case WZ_WATERBALL:
+ {
+ int range = skill_lv / 2;
+ int maxlv = skill_get_max(skill_id); // learnable level
+ int count = 0;
+ int x, y;
+ struct skill_unit* unit;
+
+ if( skill_lv > maxlv )
+ {
+ if( src->type == BL_MOB && skill_lv == 10 )
+ range = 4;
+ else
+ range = maxlv / 2;
+ }
+
+ for( y = src->y - range; y <= src->y + range; ++y )
+ for( x = src->x - range; x <= src->x + range; ++x )
+ {
+ if( !map_find_skill_unit_oncell(src,x,y,SA_LANDPROTECTOR,NULL,1) )
+ {
+ if( src->type != BL_PC || map_getcell(src->m,x,y,CELL_CHKWATER) ) // non-players bypass the water requirement
+ count++; // natural water cell
+ else if( (unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL,1)) != NULL || (unit = map_find_skill_unit_oncell(src,x,y,NJ_SUITON,NULL,1)) != NULL )
+ {
+ count++; // skill-induced water cell
+ skill_delunit(unit); // consume cell
+ }
+ }
+ }
+
+ if( count > 1 ) // queue the remaining count - 1 timerskill Waterballs
+ skill_addtimerskill(src,tick+150,bl->id,0,0,skill_id,skill_lv,count-1,flag);
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case PR_BENEDICTIO:
+ //Should attack undead and demons. [Skotlex]
+ if (battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON)
+ skill_attack(BF_MAGIC, src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case SL_SMA:
+ status_change_end(src, SC_SMA, INVALID_TIMER);
+ case SL_STIN:
+ case SL_STUN:
+ if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) {
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case NPC_DARKBREATH:
+ clif_emotion(src,E_AG);
+ case SN_FALCONASSAULT:
+ case PA_PRESSURE:
+ case CR_ACIDDEMONSTRATION:
+ case TF_THROWSTONE:
+ case NPC_SMOKING:
+ case GS_FLING:
+ case NJ_ZENYNAGE:
+ case GN_THORNS_TRAP:
+ case GN_HELLS_PLANT_ATK:
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ /**
+ * Rune Knight
+ **/
+ case RK_DRAGONBREATH: {
+ struct status_change *tsc = NULL;
+ if( (tsc = status_get_sc(bl)) && (tsc->data[SC_HIDING] )) {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ } else
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ }
+ break;
+
+ case NPC_SELFDESTRUCTION: {
+ struct status_change *tsc = NULL;
+ if( (tsc = status_get_sc(bl)) && tsc->data[SC_HIDING] )
+ break;
+ }
+ case HVAN_EXPLOSION:
+ if (src != bl)
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ // Celest
+ case PF_SOULBURN:
+ if (rnd()%100 < (skill_lv < 5 ? 30 + skill_lv * 10 : 70)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (skill_lv == 5)
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_percent_damage(src, bl, 0, 100, false);
+ } else {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ if (skill_lv == 5)
+ skill_attack(BF_MAGIC,src,src,src,skill_id,skill_lv,tick,flag);
+ status_percent_damage(src, src, 0, 100, false);
+ }
+ break;
+
+ case NPC_BLOODDRAIN:
+ case NPC_ENERGYDRAIN:
+ {
+ int heal = skill_attack( (skill_id == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC,
+ src, src, bl, skill_id, skill_lv, tick, flag);
+ if (heal > 0){
+ clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
+ status_heal(src, heal, 0, 0);
+ }
+ }
+ break;
+
+ case GS_BULLSEYE:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case NJ_KASUMIKIRI:
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag) > 0)
+ sc_start(src,SC_HIDING,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case NJ_KIRIKAGE:
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground )
+ { //You don't move on GVG grounds.
+ short x, y;
+ map_search_freecell(bl, 0, &x, &y, 1, 1, 0);
+ if (unit_movepos(src, x, y, 0, 0))
+ clif_slide(src,src->x,src->y);
+ }
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ case RK_PHANTOMTHRUST:
+ unit_setdir(src,map_calc_dir(src, bl->x, bl->y));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+
+ 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,skill_id,skill_lv,tick,flag);
+ break;
+
+ case RK_STORMBLAST:
+ case RK_CRUSHSTRIKE:
+ if( sd ) {
+ if( pc_checkskill(sd,RK_RUNEMASTERY) >= ( skill_id == RK_CRUSHSTRIKE ? 7 : 3 ) )
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ } else //non-sd support
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ case GC_DARKILLUSION:
+ {
+ short x, y;
+ short dir = map_calc_dir(src,bl->x,bl->y);
+
+ if( dir > 0 && dir < 4) x = 2;
+ else if( dir > 4 ) x = -2;
+ else x = 0;
+ if( dir > 2 && dir < 6 ) y = 2;
+ else if( dir == 7 || dir < 2 ) y = -2;
+ 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,skill_id,skill_lv,tick,flag);
+ if( rnd()%100 < 4 * skill_lv )
+ skill_castend_damage_id(src,bl,GC_CROSSIMPACT,skill_lv,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,skill_id,skill_lv,tick,flag);
+ else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0);
+ break;
+
+ case GC_CROSSRIPPERSLASHER:
+ if( sd && !(sc && sc->data[SC_ROLLINGCUTTER]) )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0);
+ else
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(src,SC_ROLLINGCUTTER,INVALID_TIMER);
+ }
+ 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,skill_id,skill_lv,tick,flag);
+ }
+ break;
+ case WL_CHAINLIGHTNING:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_addtimerskill(src,tick + 150,bl->id,3,0,WL_CHAINLIGHTNING_ATK,skill_lv,4+skill_lv,flag);
+ break;
+ case WL_DRAINLIFE:
+ {
+ int heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ int rate = 70 + 5 * skill_lv;
+
+ heal = heal * (5 + 5 * skill_lv) / 100;
+
+ if( bl->type == BL_SKILL )
+ heal = 0; // Don't absorb heal from Ice Walls or other skill units.
+
+ if( heal && rnd()%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,skill_id,USESKILL_FAIL_LEVEL,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 + i * 200, bl->id, k, 0, subskill, skill_lv, i, flag);
+ clif_skill_nodamage(src, bl, subskill, skill_lv, 1);
+ status_change_end(src, spheres[i], INVALID_TIMER);
+ }
+ }
+ break;
+
+ case WL_RELEASE:
+ if( sd )
+ {
+ int i;
+ // Priority is to release SpellBook
+ if( sc && sc->data[SC_READING_SB] )
+ { // SpellBook
+ uint16 skill_id, skill_lv, point, s = 0;
+ int spell[SC_MAXSPELLBOOK-SC_SPELLBOOK1 + 1];
+
+ for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--) // List all available spell to be released
+ if( sc->data[i] ) spell[s++] = i;
+
+ if ( s == 0 )
+ break;
+
+ i = spell[s==1?0:rand()%s];// Random select of spell to be released.
+ if( s && sc->data[i] ){// Now extract the data from the preserved spell
+ skill_id = sc->data[i]->val1;
+ skill_lv = sc->data[i]->val2;
+ point = sc->data[i]->val3;
+ status_change_end(src, (sc_type)i, INVALID_TIMER);
+ }else //something went wrong :(
+ break;
+
+ if( sc->data[SC_READING_SB]->val2 > point )
+ sc->data[SC_READING_SB]->val2 -= point;
+ else // Last spell to be released
+ status_change_end(src, SC_READING_SB, INVALID_TIMER);
+
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ if( !skill_check_condition_castbegin(sd, skill_id, skill_lv) )
+ break;
+
+ switch( skill_get_casttype(skill_id) )
+ {
+ case CAST_GROUND:
+ skill_castend_pos2(src, bl->x, bl->y, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(src, bl, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(src, bl, skill_id, skill_lv, tick, 0);
+ break;
+ }
+
+ sd->ud.canact_tick = tick + skill_delayfix(src, skill_id, skill_lv);
+ clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, skill_id, skill_lv), 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,skill_id,USESKILL_FAIL_SUMMON_NONE,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]);
+ }
+
+ if( skill_lv == 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,skill_id,0,1);
+ }
+ }
+ break;
+ case WL_FROSTMISTY:
+ // Causes Freezing status through walls.
+ sc_start(bl,status_skill2sc(skill_id),20+12*skill_lv+(sd ? sd->status.job_level : 50)/5,skill_lv,skill_get_time(skill_id,skill_lv));
+ // 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,skill_id,skill_lv,tick,flag|SD_ANIMATION);
+ break;
+ case WL_HELLINFERNO:
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|ELE_DARK);
+ break;
+ case RA_WUGSTRIKE:
+ if( sd && pc_isridingwug(sd) ){
+ short x[8]={0,-1,-1,-1,0,1,1,1};
+ short y[8]={1,1,0,-1,-1,-1,0,1};
+ uint8 dir = map_calc_dir(bl, src->x, src->y);
+
+ if( unit_movepos(src, bl->x+x[dir], bl->y+y[dir], 1, 1) )
+ {
+ clif_slide(src, bl->x+x[dir], bl->y+y[dir]);
+ clif_fixpos(src);
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ }
+ break;
+ }
+ case RA_WUGBITE:
+ if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKNOREACH) ) {
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ }else if( sd && skill_id == RA_WUGBITE ) // Only RA_WUGBITE has the skill fail message.
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+
+ 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) ){
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ }
+ }
+ 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 )
+ {
+ if( !(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->item_id?sg->item_id:ITEMID_TRAP;
+ item_tmp.identify = 1;
+ if( item_tmp.nameid )
+ map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0);
+ }
+ skill_delunit(su);
+ }
+ }
+ break;
+ case NC_INFRAREDSCAN:
+ if( flag&1 )
+ { //TODO: Need a confirmation if the other type of hidden status is included to be scanned. [Jobbie]
+ if( rnd()%100 < 50 )
+ sc_start(bl, SC_INFRAREDSCAN, 10000, skill_lv, skill_get_time(skill_id, skill_lv));
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); // Need confirm it.
+ }
+ else
+ {
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( sd ) pc_overheat(sd,1);
+ }
+ break;
+
+ case NC_MAGNETICFIELD:
+ sc_start2(bl,SC_MAGNETICFIELD,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv));
+ break;
+ case SC_FATALMENACE:
+ if( flag&1 )
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ else
+ {
+ short x, y;
+ map_search_freecell(src, 0, &x, &y, -1, -1, 0);
+ // Destination area
+ skill_area_temp[4] = x;
+ skill_area_temp[5] = y;
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ skill_addtimerskill(src,tick + 800,src->id,x,y,skill_id,skill_lv,0,flag); // To teleport Self
+ clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
+ }
+ break;
+ case LG_PINPOINTATTACK:
+ if( !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,skill_id,skill_lv,tick,flag);
+ break;
+
+ case LG_SHIELDSPELL:
+ // flag&1: Phisycal Attack, flag&2: Magic Attack.
+ skill_attack((flag&1)?BF_WEAPON:BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case LG_OVERBRAND:
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL);
+ break;
+
+ case LG_OVERBRAND_BRANDISH:
+ skill_addtimerskill(src, tick + status_get_amotion(src)*8/10, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL);
+ break;
+ case SR_DRAGONCOMBO:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case SR_KNUCKLEARROW:
+ if( !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);
+ clif_fixpos(src); // Aegis send this packet too.
+ }
+
+ if( flag&1 )
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL);
+ else
+ skill_addtimerskill(src, tick + 300, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL|2);
+ break;
+
+ case SR_HOWLINGOFLION:
+ status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
+ status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
+ status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
+ status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
+ status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
+ status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ status_change_end(bl, SC_SIRCLEOFNATURE, INVALID_TIMER);
+ status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
+ status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
+ status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
+ status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
+ status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
+ status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION);
+ break;
+
+ case SR_EARTHSHAKER:
+ if( flag&1 ) { //by default cloaking skills are remove by aoe skills so no more checking/removing except hiding and cloaking exceed.
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ } else{
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ }
+ break;
+
+ case SO_POISON_BUSTER: {
+ struct status_change *tsc = status_get_sc(bl);
+ if( tsc && tsc->data[SC_POISON] ) {
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ }
+ else if( sd )
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ }
+ break;
+
+ case GN_SPORE_EXPLOSION:
+ if( flag&1 )
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ else {
+ clif_skill_nodamage(src, bl, skill_id, 0, 1);
+ skill_addtimerskill(src, gettick() + skill_get_time(skill_id, skill_lv) - 1000, bl->id, 0, 0, skill_id, skill_lv, 0, 0);
+ }
+ break;
+
+ case EL_FIRE_BOMB:
+ case EL_FIRE_WAVE:
+ case EL_WATER_SCREW:
+ case EL_HURRICANE:
+ case EL_TYPOON_MIS:
+ if( flag&1 )
+ skill_attack(skill_get_type(skill_id+1),src,src,bl,skill_id+1,skill_lv,tick,flag);
+ else {
+ int i = skill_get_splash(skill_id,skill_lv);
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rnd()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ }
+ break;
+
+ case EL_ROCK_CRUSHER:
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rnd()%100 < 50 )
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,EL_ROCK_CRUSHER_ATK,skill_lv,tick,flag);
+ break;
+
+ case EL_STONE_RAIN:
+ if( flag&1 )
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ else {
+ int i = skill_get_splash(skill_id,skill_lv);
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rnd()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ }
+ break;
+
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WIND_SLASH:
+ case EL_STONE_HAMMER:
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case EL_TIDAL_WEAPON:
+ if( src->type == BL_ELEM ) {
+ struct elemental_data *ele = BL_CAST(BL_ELEM,src);
+ struct status_change *sc = status_get_sc(&ele->bl);
+ struct status_change *tsc = status_get_sc(bl);
+ sc_type type = status_skill2sc(skill_id), type2;
+ type2 = type-1;
+
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skill_id);
+ }
+ if( rnd()%100 < 50 )
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ else {
+ sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(battle_get_master(src),type,100,ele->bl.id,skill_get_time(skill_id,skill_lv));
+ }
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ }
+ break;
+
+
+ //recursive homon skill
+ case MH_MAGMA_FLOW:
+ case MH_XENO_SLASHER:
+ case MH_HEILIGE_STANGE:
+ if(flag & 1)
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ else {
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id);
+ }
+ break;
+
+ case MH_STAHL_HORN:
+ case MH_NEEDLE_OF_PARALYZE:
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+ case MH_TINDER_BREAKER:
+ if (unit_movepos(src, bl->x, bl->y, 1, 1)) {
+#if PACKETVER >= 20111005
+ clif_snap(src, bl->x, bl->y);
+#else
+ clif_skill_poseffect(src,skill_id,skill_lv,bl->x,bl->y,tick);
+#endif
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,SC_CLOSECONFINE2,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv)));
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case 0:/* no skill - basic/normal attack */
+ if(sd) {
+ if (flag & 3){
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, SD_LEVEL|flag);
+ } else {
+ skill_area_temp[1] = bl->id;
+ map_foreachinrange(skill_area_sub, bl,
+ sd->bonus.splash_range, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag | BCT_ENEMY | 1,
+ skill_castend_damage_id);
+ flag|=1; //Set flag to 1 so ammo is not double-consumed. [Skotlex]
+ }
+ }
+ break;
+
+ default:
+ ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n",skill_id);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), tstatus->dmotion,
+ 0, abs(skill_get_num(skill_id, skill_lv)),
+ skill_id, skill_lv, skill_get_hit(skill_id));
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+
+ map_freeblock_unlock();
+
+ if( sd && !(flag&1) )
+ {// ensure that the skill last-cast tick is recorded
+ sd->canskill_tick = gettick();
+
+ if( sd->state.arrow_atk )
+ {// consume arrow on last invocation to this skill.
+ battle_consume_ammo(sd, skill_id, skill_lv);
+ }
+
+ // perform skill requirement consumption
+ skill_consume_requirement(sd,skill_id,skill_lv,2);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct map_session_data *sd, *dstsd;
+ struct mob_data *md, *dstmd;
+ struct homun_data *hd;
+ struct mercenary_data *mer;
+ struct status_data *sstatus, *tstatus;
+ struct status_change *tsc;
+ struct status_change_entry *tsce;
+
+ int i = 0;
+ enum sc_type type;
+
+ if(skill_id > 0 && !skill_lv) return 0; // celest
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ sd = BL_CAST(BL_PC, src);
+ hd = BL_CAST(BL_HOM, src);
+ md = BL_CAST(BL_MOB, src);
+ mer = BL_CAST(BL_MER, src);
+
+ dstsd = BL_CAST(BL_PC, bl);
+ dstmd = BL_CAST(BL_MOB, bl);
+
+ if(bl->prev == NULL)
+ return 1;
+ if(status_isdead(src))
+ return 1;
+
+ if( src != bl && status_isdead(bl) ) {
+ /**
+ * Skills that may be cast on dead targets
+ **/
+ switch( skill_id ) {
+ case NPC_WIDESOULDRAIN:
+ case PR_REDEMPTIO:
+ case ALL_RESURRECTION:
+ case WM_DEADHILLHERE:
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ tstatus = status_get_status_data(bl);
+ sstatus = status_get_status_data(src);
+
+ //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex]
+ switch (skill_id) {
+ case HLIF_HEAL: //[orn]
+ if (bl->type != BL_HOM) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0) ;
+ break ;
+ }
+ 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.
+ if (sd && battle_check_undead(tstatus->race,tstatus->def_ele)) {
+ if (battle_check_target(src, bl, BCT_ENEMY) < 1) {
+ //Offensive heal does not works on non-enemies. [Skotlex]
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag);
+ }
+ break;
+ case NPC_SMOKING: //Since it is a self skill, this one ends here rather than in damage_id. [Skotlex]
+ return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag);
+ case MH_STEINWAND: {
+ struct block_list *s_src = battle_get_master(src);
+ short ret = 0;
+ if(!skill_check_unit_range(src, src->x, src->y, skill_id, skill_lv)) //prevent reiteration
+ ret = skill_castend_pos2(src,src->x,src->y,skill_id,skill_lv,tick,flag); //cast on homon
+ if(s_src && !skill_check_unit_range(s_src, s_src->x, s_src->y, skill_id, skill_lv))
+ ret |= skill_castend_pos2(s_src,s_src->x,s_src->y,skill_id,skill_lv,tick,flag); //cast on master
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ return ret;
+ }
+ break;
+ default:
+ //Skill is actually ground placed.
+ if (src == bl && skill_get_unit_id(skill_id,0))
+ return skill_castend_pos2(src,bl->x,bl->y,skill_id,skill_lv,tick,0);
+ }
+
+ type = status_skill2sc(skill_id);
+ tsc = status_get_sc(bl);
+ tsce = (tsc && type != -1)?tsc->data[type]:NULL;
+
+ if (src!=bl && type > -1 &&
+ (i = skill_get_ele(skill_id, skill_lv)) > ELE_NEUTRAL &&
+ skill_get_inf(skill_id) != INF_SUPPORT_SKILL &&
+ battle_attr_fix(NULL, NULL, 100, i, tstatus->def_ele, tstatus->ele_lv) <= 0)
+ return 1; //Skills that cause an status should be blocked if the target element blocks its element.
+
+ map_freeblock_lock();
+ switch(skill_id)
+ {
+ case HLIF_HEAL: //[orn]
+ case AL_HEAL:
+ /**
+ * Arch Bishop
+ **/
+ case AB_HIGHNESSHEAL:
+ {
+ int heal = skill_calc_heal(src, bl, (skill_id == AB_HIGHNESSHEAL)?AL_HEAL:skill_id, (skill_id == AB_HIGHNESSHEAL)?10:skill_lv, true);
+ int heal_get_jobexp;
+ //Highness Heal: starts at 1.5 boost + 0.5 for each level
+ if( skill_id == AB_HIGHNESSHEAL ) {
+ heal = heal * ( 15 + 5 * skill_lv ) / 10;
+ }
+ if( status_isimmune(bl) ||
+ (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) ||
+ (dstsd && pc_ismadogear(dstsd)) )//Mado is immune to 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 )
+ heal = heal*2;
+
+ if( tsc && tsc->count )
+ {
+ if( tsc->data[SC_KAITE] && !(sstatus->mode&MD_BOSS) )
+ { //Bounce back heal
+ if (--tsc->data[SC_KAITE]->val2 <= 0)
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ if (src == bl)
+ heal=0; //When you try to heal yourself under Kaite, the heal is voided.
+ else {
+ bl = src;
+ dstsd = sd;
+ }
+ }
+ else if (tsc->data[SC_BERSERK] || tsc->data[SC_SATURDAYNIGHTFEVER] || tsc->data[SC__BLOODYLUST])
+ heal = 0; //Needed so that it actually displays 0 when healing.
+ }
+ clif_skill_nodamage (src, bl, skill_id, heal, 1);
+ if( tsc && tsc->data[SC_AKAITSUKI] && heal && skill_id != HLIF_HEAL )
+ heal = ~heal + 1;
+ heal_get_jobexp = status_heal(bl,heal,0,0);
+
+ if(sd && dstsd && heal > 0 && sd != dstsd && battle_config.heal_exp > 0){
+ heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100;
+ if (heal_get_jobexp <= 0)
+ heal_get_jobexp = 1;
+ pc_gainexp (sd, bl, 0, heal_get_jobexp, false);
+ }
+ }
+ break;
+
+ case PR_REDEMPTIO:
+ if (sd && !(flag&1)) {
+ if (sd->status.party_id == 0) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ skill_area_temp[0] = 0;
+ party_foreachsamemap(skill_area_sub,
+ sd,skill_get_splash(skill_id, skill_lv),
+ src,skill_id,skill_lv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ if (skill_area_temp[0] == 0) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty...
+ if (skill_area_temp[0] > 0 && !map[src->m].flag.noexppenalty) { //Apply penalty
+ sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000); //0.2% penalty per each.
+ sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000);
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ status_set_hp(src, 1, 0);
+ status_set_sp(src, 0, 0);
+ break;
+ } else if (status_isdead(bl) && flag&1) { //Revive
+ skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code.
+ skill_lv = 3; //Resurrection level 3 is used
+ } else //Invalid target, skip resurrection.
+ break;
+
+ case ALL_RESURRECTION:
+ if(sd && (map_flag_gvg(bl->m) || map[bl->m].flag.battleground))
+ { //No reviving in WoE grounds!
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if (!status_isdead(bl))
+ break;
+ {
+ int per = 0, sper = 0;
+ if (tsc && tsc->data[SC_HELLPOWER])
+ break;
+
+ if (map[bl->m].flag.pvp && dstsd && dstsd->pvp_point < 0)
+ break;
+
+ switch(skill_lv){
+ case 1: per=10; break;
+ case 2: per=30; break;
+ case 3: per=50; break;
+ case 4: per=80; break;
+ }
+ if(dstsd && dstsd->special_state.restart_full_recover)
+ per = sper = 100;
+ if (status_revive(bl, per, sper))
+ {
+ clif_skill_nodamage(src,bl,ALL_RESURRECTION,skill_lv,1); //Both Redemptio and Res show this skill-animation.
+ if(sd && dstsd && battle_config.resurrection_exp > 0)
+ {
+ int exp = 0,jexp = 0;
+ int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level;
+ if(lv > 0 && pc_nextbaseexp(dstsd)) {
+ exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (exp < 1) exp = 1;
+ }
+ if(jlv > 0 && pc_nextjobexp(dstsd)) {
+ jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (jexp < 1) jexp = 1;
+ }
+ if(exp > 0 || jexp > 0)
+ pc_gainexp (sd, bl, exp, jexp, false);
+ }
+ }
+ }
+ break;
+
+ case AL_DECAGI:
+ case MER_DECAGI:
+ clif_skill_nodamage (src, bl, skill_id, skill_lv,
+ sc_start(bl, type, (40 + skill_lv * 2 + (status_get_lv(src) + sstatus->int_)/5), skill_lv, skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case AL_CRUCIS:
+ if (flag&1)
+ sc_start(bl,type, 23+skill_lv*4 +status_get_lv(src) -status_get_lv(bl), skill_lv,skill_get_time(skill_id,skill_lv));
+ else {
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case PR_LEXDIVINA:
+ case MER_LEXDIVINA:
+ if( tsce )
+ status_change_end(bl,type, INVALID_TIMER);
+ else
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+ break;
+
+ case SA_ABRACADABRA:
+ {
+ int abra_skill_id = 0, abra_skill_lv;
+ do {
+ i = rnd() % MAX_SKILL_ABRA_DB;
+ abra_skill_id = skill_abra_db[i].skill_id;
+ } while (abra_skill_id == 0 ||
+ skill_abra_db[i].req_lv > skill_lv || //Required lv for it to appear
+ rnd()%10000 >= skill_abra_db[i].per
+ );
+ abra_skill_lv = min(skill_lv, skill_get_max(abra_skill_id));
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+
+ if( sd )
+ {// player-casted
+ sd->state.abra_flag = 1;
+ sd->skillitem = abra_skill_id;
+ sd->skillitemlv = abra_skill_lv;
+ clif_item_skill(sd, abra_skill_id, abra_skill_lv);
+ }
+ else
+ {// mob-casted
+ struct unit_data *ud = unit_bl2ud(src);
+ int inf = skill_get_inf(abra_skill_id);
+ int target_id = 0;
+ if (!ud) break;
+ if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
+ if (src->type == BL_PET)
+ bl = (struct block_list*)((TBL_PET*)src)->msd;
+ if (!bl) bl = src;
+ unit_skilluse_id(src, bl->id, abra_skill_id, abra_skill_lv);
+ } else { //Assume offensive skills
+ if (ud->target)
+ target_id = ud->target;
+ else switch (src->type) {
+ case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break;
+ case BL_PET: target_id = ((TBL_PET*)src)->target_id; break;
+ }
+ if (!target_id)
+ break;
+ if (skill_get_casttype(abra_skill_id) == CAST_GROUND) {
+ bl = map_id2bl(target_id);
+ if (!bl) bl = src;
+ unit_skilluse_pos(src, bl->x, bl->y, abra_skill_id, abra_skill_lv);
+ } else
+ unit_skilluse_id(src, target_id, abra_skill_id, abra_skill_lv);
+ }
+ }
+ }
+ break;
+
+ case SA_COMA:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+ case SA_FULLRECOVERY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (status_isimmune(bl))
+ break;
+ status_percent_heal(bl, 100, 100);
+ break;
+ case NPC_ALLHEAL:
+ {
+ int heal;
+ if( status_isimmune(bl) )
+ break;
+ heal = status_percent_heal(bl, 100, 0);
+ clif_skill_nodamage(NULL, bl, AL_HEAL, heal, 1);
+ if( dstmd )
+ { // Reset Damage Logs
+ memset(dstmd->dmglog, 0, sizeof(dstmd->dmglog));
+ dstmd->tdmg = 0;
+ }
+ }
+ break;
+ case SA_SUMMONMONSTER:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (sd) mob_once_spawn(sd, src->m, src->x, src->y," --ja--", -1, 1, "", SZ_SMALL, AI_NONE);
+ break;
+ case SA_LEVELUP:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, NULL, pc_nextbaseexp(sd) * 10 / 100, 0, false);
+ break;
+ case SA_INSTANTDEATH:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_set_hp(bl,1,0);
+ break;
+ case SA_QUESTION:
+ case SA_GRAVITY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case SA_CLASSCHANGE:
+ case SA_MONOCELL:
+ if (dstmd)
+ {
+ int class_;
+ if ( sd && dstmd->status.mode&MD_BOSS )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ class_ = skill_id==SA_MONOCELL?1002:mob_get_random_id(4, 1, 0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ mob_class_change(dstmd,class_);
+ if( tsc && dstmd->status.mode&MD_BOSS )
+ {
+ const enum sc_type scs[] = { SC_QUAGMIRE, SC_PROVOKE, SC_ROKISWEIL, SC_GRAVITATION, SC_SUITON, SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC_BLADESTOP };
+ for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++)
+ if (tsc->data[i]) status_change_end(bl, (sc_type)i, INVALID_TIMER);
+ for (i = 0; i < ARRAYLENGTH(scs); i++)
+ if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
+ }
+ }
+ break;
+ case SA_DEATH:
+ if ( sd && dstmd && dstmd->status.mode&MD_BOSS )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_kill(bl);
+ break;
+ case SA_REVERSEORCISH:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)));
+ break;
+ case SA_FORTUNE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(sd) pc_getzeny(sd,status_get_lv(bl)*100,LOG_TYPE_STEAL,NULL);
+ break;
+ case SA_TAMINGMONSTER:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (sd && dstmd) {
+ ARR_FIND( 0, MAX_PET_DB, i, dstmd->class_ == pet_db[i].class_ );
+ if( i < MAX_PET_DB )
+ pet_catch_process1(sd, dstmd->class_);
+ }
+ break;
+
+ case CR_PROVIDENCE:
+ if(sd && dstsd){ //Check they are not another crusader [Skotlex]
+ if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case CG_MARIONETTE:
+ {
+ struct status_change* sc = status_get_sc(src);
+
+ if( sd && dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && dstsd->status.sex == sd->status.sex )
+ {// Cannot cast on another bard/dancer-type class of the same gender as caster
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if( sc && tsc )
+ {
+ if( !sc->data[SC_MARIONETTE] && !tsc->data[SC_MARIONETTE2] )
+ {
+ sc_start(src,SC_MARIONETTE,100,bl->id,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_MARIONETTE2,100,src->id,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ else
+ if( sc->data[SC_MARIONETTE ] && sc->data[SC_MARIONETTE ]->val1 == bl->id &&
+ tsc->data[SC_MARIONETTE2] && tsc->data[SC_MARIONETTE2]->val1 == src->id )
+ {
+ status_change_end(src, SC_MARIONETTE, INVALID_TIMER);
+ status_change_end(bl, SC_MARIONETTE2, INVALID_TIMER);
+ }
+ else
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case RG_CLOSECONFINE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv)));
+ break;
+ case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris]
+ case SA_FROSTWEAPON:
+ case SA_LIGHTNINGLOADER:
+ case SA_SEISMICWEAPON:
+ if (dstsd) {
+ if(dstsd->status.weapon == W_FIST ||
+ (dstsd->sc.count && !dstsd->sc.data[type] &&
+ ( //Allow re-enchanting to lenghten time. [Skotlex]
+ dstsd->sc.data[SC_FIREWEAPON] ||
+ dstsd->sc.data[SC_WATERWEAPON] ||
+ dstsd->sc.data[SC_WINDWEAPON] ||
+ dstsd->sc.data[SC_EARTHWEAPON] ||
+ dstsd->sc.data[SC_SHADOWWEAPON] ||
+ dstsd->sc.data[SC_GHOSTWEAPON] ||
+ dstsd->sc.data[SC_ENCPOISON]
+ ))
+ ) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ }
+ // 100% success rate at lv4 & 5, but lasts longer at lv5
+ if(!clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,type,(60+skill_lv*10),skill_lv, skill_get_time(skill_id,skill_lv)))) {
+ if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ if (skill_break_equip(bl, EQP_WEAPON, 10000, BCT_PARTY) && sd && sd != dstsd)
+ clif_displaymessage(sd->fd, msg_txt(669));
+ }
+ break;
+
+ case PR_ASPERSIO:
+ if (sd && dstmd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case ITEM_ENCHANTARMS:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,
+ skill_get_ele(skill_id,skill_lv), skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case TK_SEVENWIND:
+ switch(skill_get_ele(skill_id,skill_lv)) {
+ case ELE_EARTH : type = SC_EARTHWEAPON; break;
+ case ELE_WIND : type = SC_WINDWEAPON; break;
+ case ELE_WATER : type = SC_WATERWEAPON; break;
+ case ELE_FIRE : type = SC_FIREWEAPON; break;
+ case ELE_GHOST : type = SC_GHOSTWEAPON; break;
+ case ELE_DARK : type = SC_SHADOWWEAPON; break;
+ case ELE_HOLY : type = SC_ASPERSIO; break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+
+ sc_start(bl,SC_SEVENWIND,100,skill_lv,skill_get_time(skill_id,skill_lv));
+
+ break;
+
+ case PR_KYRIE:
+ case MER_KYRIE:
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ //Passive Magnum, should had been casted on yourself.
+ case SM_MAGNUM:
+ case MS_MAGNUM:
+ skill_area_temp[1] = 0;
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_SKILL|BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ clif_skill_nodamage (src,src,skill_id,skill_lv,1);
+ // Initiate 10% of your damage becomes fire element.
+ sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skill_id, skill_lv));
+ if( sd )
+ skill_blockpc_start(sd, skill_id, skill_get_time(skill_id, skill_lv));
+ else if( bl->type == BL_MER )
+ skill_blockmerc_start((TBL_MER*)bl, skill_id, skill_get_time(skill_id, skill_lv));
+ break;
+
+ case TK_JUMPKICK:
+ /* Check if the target is an enemy; if not, skill should fail so the character doesn't unit_movepos (exploitable) */
+ if( battle_check_target(src, bl, BCT_ENEMY) > 0 )
+ {
+ if( unit_movepos(src, bl->x, bl->y, 1, 1) )
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ clif_slide(src,bl->x,bl->y);
+ }
+ }
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ break;
+
+ case AL_INCAGI:
+ case AL_BLESSING:
+ case MER_INCAGI:
+ case MER_BLESSING:
+ if (dstsd != NULL && tsc->data[SC_CHANGEUNDEAD]) {
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ }
+ case PR_SLOWPOISON:
+ case PR_IMPOSITIO:
+ case PR_LEXAETERNA:
+ case PR_SUFFRAGIUM:
+ case PR_BENEDICTIO:
+ case LK_BERSERK:
+ case MS_BERSERK:
+ case KN_AUTOCOUNTER:
+ case KN_TWOHANDQUICKEN:
+ case KN_ONEHAND:
+ case MER_QUICKEN:
+ case CR_SPEARQUICKEN:
+ case CR_REFLECTSHIELD:
+ case MS_REFLECTSHIELD:
+ case AS_POISONREACT:
+ case MC_LOUD:
+ case MG_ENERGYCOAT:
+ case MO_EXPLOSIONSPIRITS:
+ case MO_STEELBODY:
+ case MO_BLADESTOP:
+ case LK_AURABLADE:
+ case LK_PARRYING:
+ case MS_PARRYING:
+ case LK_CONCENTRATION:
+ case WS_CARTBOOST:
+ case SN_SIGHT:
+ case WS_MELTDOWN:
+ case WS_OVERTHRUSTMAX:
+ case ST_REJECTSWORD:
+ case HW_MAGICPOWER:
+ case PF_MEMORIZE:
+ case PA_SACRIFICE:
+ case ASC_EDP:
+ case PF_DOUBLECASTING:
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ case NPC_HALLUCINATION:
+ case GS_MADNESSCANCEL:
+ case GS_ADJUSTMENT:
+ case GS_INCREASING:
+ case NJ_KASUMIKIRI:
+ case NJ_UTSUSEMI:
+ case NJ_NEN:
+ case NPC_DEFENDER:
+ case NPC_MAGICMIRROR:
+ case ST_PRESERVE:
+ case NPC_INVINCIBLE:
+ case NPC_INVINCIBLEOFF:
+ case RK_DEATHBOUND:
+ case AB_RENOVATIO:
+ case AB_EXPIATIO:
+ case AB_DUPLELIGHT:
+ case AB_SECRAMENT:
+ case NC_ACCELERATION:
+ case NC_HOVERING:
+ case NC_SHAPESHIFT:
+ case WL_RECOGNIZEDSPELL:
+ case GC_VENOMIMPRESS:
+ case SC_DEADLYINFECT:
+ case LG_EXEEDBREAK:
+ case LG_PRESTIGE:
+ case SR_CRESCENTELBOW:
+ case SR_LIGHTNINGWALK:
+ case SR_GENTLETOUCH_ENERGYGAIN:
+ case GN_CARTBOOST:
+ case KO_MEIKYOUSISUI:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case SO_STRIKING:
+ if (sd) {
+ int bonus = 25 + 10 * skill_lv;
+ bonus += (pc_checkskill(sd, SA_FLAMELAUNCHER)+pc_checkskill(sd, SA_FROSTWEAPON)+pc_checkskill(sd, SA_LIGHTNINGLOADER)+pc_checkskill(sd, SA_SEISMICWEAPON))*5;
+ clif_skill_nodamage( src, bl, skill_id, skill_lv,
+ battle_check_target(src,bl,BCT_PARTY) ?
+ sc_start2(bl, type, 100, skill_lv, bonus, skill_get_time(skill_id,skill_lv)) :
+ 0
+ );
+ }
+ break;
+
+ case NPC_STOP:
+ if( clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)) ) )
+ sc_start2(src,type,100,skill_lv,bl->id,skill_get_time(skill_id,skill_lv));
+ break;
+ case HP_ASSUMPTIO:
+ if( sd && dstmd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ else
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case MG_SIGHT:
+ case MER_SIGHT:
+ case AL_RUWACH:
+ case WZ_SIGHTBLASTER:
+ case NPC_WIDESIGHT:
+ case NPC_STONESKIN:
+ case NPC_ANTIMAGIC:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,skill_id,skill_get_time(skill_id,skill_lv)));
+ break;
+ case HLIF_AVOID:
+ case HAMI_DEFENCE:
+ i = skill_get_time(skill_id,skill_lv);
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,i)); // Master
+ clif_skill_nodamage(src,src,skill_id,skill_lv,sc_start(src,type,100,skill_lv,i)); // Homunc
+ break;
+ case NJ_BUNSINJYUTSU:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ status_change_end(bl, SC_NEN, INVALID_TIMER);
+ break;
+/* Was modified to only affect targetted char. [Skotlex]
+ case HP_ASSUMPTIO:
+ if (flag&1)
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else
+ {
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src, skill_id, skill_lv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+*/
+ case SM_ENDURE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if (sd)
+ skill_blockpc_start (sd, skill_id, skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case AS_ENCHANTPOISON: // Prevent spamming [Valaris]
+ if (sd && dstsd && dstsd->sc.count) {
+ if (dstsd->sc.data[SC_FIREWEAPON] ||
+ dstsd->sc.data[SC_WATERWEAPON] ||
+ dstsd->sc.data[SC_WINDWEAPON] ||
+ dstsd->sc.data[SC_EARTHWEAPON] ||
+ dstsd->sc.data[SC_SHADOWWEAPON] ||
+ dstsd->sc.data[SC_GHOSTWEAPON]
+ // dstsd->sc.data[SC_ENCPOISON] //People say you should be able to recast to lengthen the timer. [Skotlex]
+ ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case LK_TENSIONRELAX:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,0,0,skill_get_time2(skill_id,skill_lv),
+ skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case MC_CHANGECART:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case TK_MISSION:
+ if (sd) {
+ int id;
+ if (sd->mission_mobid && (sd->mission_count || rnd()%100)) { //Cannot change target when already have one
+ clif_mission_info(sd, sd->mission_mobid, sd->mission_count);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ id = mob_get_random_id(0,0xF, sd->status.base_level);
+ if (!id) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ sd->mission_mobid = id;
+ sd->mission_count = 0;
+ pc_setglobalreg(sd,"TK_MISSION_ID", id);
+ clif_mission_info(sd, id, 0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case AC_CONCENTRATION:
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ map_foreachinrange( status_change_timer_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src,NULL,type,tick);
+ }
+ break;
+
+ case SM_PROVOKE:
+ case SM_SELFPROVOKE:
+ case MER_PROVOKE:
+ if( (tstatus->mode&MD_BOSS) || battle_check_undead(tstatus->race,tstatus->def_ele) )
+ {
+ map_freeblock_unlock();
+ return 1;
+ }
+ //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id == SM_SELFPROVOKE ? SM_PROVOKE : skill_id,skill_lv,
+ (i = sc_start(bl,type, skill_id == SM_SELFPROVOKE ? 100:( 50 + 3*skill_lv + status_get_lv(src) - status_get_lv(bl)), skill_lv, skill_get_time(skill_id,skill_lv))));
+ if( !i )
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ unit_skillcastcancel(bl, 2);
+
+ if( tsc && tsc->count )
+ {
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ if( tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE )
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_TRICKDEAD, INVALID_TIMER);
+ }
+
+ if( dstmd )
+ {
+ dstmd->state.provoke_flag = src->id;
+ mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv));
+ }
+ break;
+
+ case ML_DEVOTION:
+ case CR_DEVOTION:
+ {
+ int count, lv;
+ if( !dstsd || (!sd && !mer) )
+ { // Only players can be devoted
+ if( sd )
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+
+ if( (lv = status_get_lv(src) - dstsd->status.base_level) < 0 )
+ lv = -lv;
+ if( lv > battle_config.devotion_level_difference || // Level difference requeriments
+ (dstsd->sc.data[type] && dstsd->sc.data[type]->val1 != src->id) || // Cannot Devote a player devoted from another source
+ (skill_id == ML_DEVOTION && (!mer || mer != dstsd->md)) || // Mercenary only can devote owner
+ (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER || // Crusader Cannot be devoted
+ (dstsd->sc.data[SC_HELLPOWER])) // Players affected by SC_HELLPOWERR cannot be devoted.
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ i = 0;
+ count = (sd)? min(skill_lv,5) : 1; // Mercenary only can Devote owner
+ if( sd )
+ { // Player Devoting Player
+ ARR_FIND(0, count, i, sd->devotion[i] == bl->id );
+ if( i == count )
+ {
+ ARR_FIND(0, count, i, sd->devotion[i] == 0 );
+ if( i == count )
+ { // No free slots, skill Fail
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+
+ sd->devotion[i] = bl->id;
+ }
+ else
+ mer->devotion_flag = 1; // Mercenary Devoting Owner
+
+ clif_skill_nodamage(src, bl, skill_id, skill_lv,
+ sc_start4(bl, type, 100, src->id, i, skill_get_range2(src,skill_id,skill_lv),0, skill_get_time2(skill_id, skill_lv)));
+ clif_devotion(src, NULL);
+ }
+ break;
+
+ case MO_CALLSPIRITS:
+ if(sd) {
+ int limit = skill_lv;
+ if( sd->sc.data[SC_RAISINGDRAGON] )
+ limit += sd->sc.data[SC_RAISINGDRAGON]->val1;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit);
+ }
+ break;
+
+ case CH_SOULCOLLECT:
+ if(sd) {
+ int limit = 5;
+ if( sd->sc.data[SC_RAISINGDRAGON] )
+ limit += sd->sc.data[SC_RAISINGDRAGON]->val1;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ for (i = 0; i < limit; i++)
+ pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit);
+ }
+ break;
+
+ case MO_KITRANSLATION:
+ if(dstsd && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) {
+ pc_addspiritball(dstsd,skill_get_time(skill_id,skill_lv),5);
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex]
+ if (skill_area_temp[1] != bl->id) {
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),-1,0);
+ skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick); //Use Misc rather than weapon to signal passive pushback
+ }
+ break;
+
+ case MO_ABSORBSPIRITS:
+ i = 0;
+ if (dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER)
+ { // split the if for readability, and included gunslingers in the check so that their coins cannot be removed [Reddozen]
+ i = dstsd->spiritball * 7;
+ pc_delspiritball(dstsd,dstsd->spiritball,0);
+ } else if (dstmd && !(tstatus->mode&MD_BOSS) && rnd() % 100 < 20)
+ { // check if target is a monster and not a Boss, for the 20% chance to absorb 2 SP per monster's level [Reddozen]
+ i = 2 * dstmd->level;
+ mob_target(dstmd,src,0);
+ }
+ if (i) status_heal(src, 0, i, 3);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i?1:0);
+ break;
+
+ case AC_MAKINGARROW:
+ if(sd) {
+ clif_arrow_create_list(sd);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case AM_PHARMACY:
+ if(sd) {
+ clif_skill_produce_mix_list(sd,skill_id,22);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SA_CREATECON:
+ if(sd) {
+ clif_elementalconverter_list(sd);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case BS_HAMMERFALL:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,SC_STUN,(20 + 10 * skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+ case RG_RAID:
+ skill_area_temp[1] = 0;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ break;
+
+ case ASC_METEORASSAULT:
+ case GS_SPREADATTACK:
+ case RK_STORMBLAST:
+ case NC_AXETORNADO:
+ case GC_COUNTERSLASH:
+ case SR_SKYNETBLOW:
+ case SR_RAMPAGEBLASTER:
+ case SR_HOWLINGOFLION:
+ case KO_HAPPOKUNAI:
+ skill_area_temp[1] = 0;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ i = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src),
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ if( !i && ( skill_id == NC_AXETORNADO || skill_id == SR_SKYNETBLOW || skill_id == KO_HAPPOKUNAI ) )
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ break;
+
+ case NC_EMERGENCYCOOL:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_change_end(src,SC_OVERHEAT_LIMITPOINT,INVALID_TIMER);
+ status_change_end(src,SC_OVERHEAT,INVALID_TIMER);
+ break;
+ case SR_WINDMILL:
+ case GN_CART_TORNADO:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ case SR_EARTHSHAKER:
+ case NC_INFRAREDSCAN:
+ case NPC_EARTHQUAKE:
+ case NPC_VAMPIRE_GIFT:
+ case NPC_HELLJUDGEMENT:
+ case NPC_PULSESTRIKE:
+ case LG_MOONSLASHER:
+ skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag);
+ break;
+
+ case KN_BRANDISHSPEAR:
+ case ML_BRANDISH:
+ skill_brandishspear(src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case WZ_SIGHTRASHER:
+ //Passive side of the attack.
+ status_change_end(src, SC_SIGHT, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub,src,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR|BL_SKILL,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case NJ_HYOUSYOURAKU:
+ case NJ_RAIGEKISAI:
+ case WZ_FROSTNOVA:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_area_temp[1] = 0;
+ map_foreachinrange(skill_attack_area, src,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ BF_MAGIC, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY);
+ break;
+
+ case HVAN_EXPLOSION: //[orn]
+ case NPC_SELFDESTRUCTION:
+ //Self Destruction hits everyone in range (allies+enemies)
+ //Except for Summoned Marine spheres on non-versus maps, where it's just enemy.
+ i = ((!md || md->special_state.ai == 2) && !map_flag_vs(src->m))?
+ BCT_ENEMY:BCT_ALL;
+ clif_skill_nodamage(src, src, skill_id, -1, 1);
+ map_delblock(src); //Required to prevent chain-self-destructions hitting back.
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ src, skill_id, skill_lv, tick, flag|i,
+ skill_castend_damage_id);
+ map_addblock(src);
+ status_damage(src, src, sstatus->max_hp,0,0,1);
+ break;
+
+ case AL_ANGELUS:
+ case PR_MAGNIFICAT:
+ case PR_GLORIA:
+ case SN_WINDWALK:
+ case CASH_BLESSING:
+ case CASH_INCAGI:
+ case CASH_ASSUMPTIO:
+ if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+ case MER_MAGNIFICAT:
+ if( mer != NULL )
+ {
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if( mer->master && mer->master->status.party_id != 0 && !(flag&1) )
+ party_foreachsamemap(skill_area_sub, mer->master, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ else if( mer->master && !(flag&1) )
+ clif_skill_nodamage(src, &mer->master->bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+
+ case BS_ADRENALINE:
+ case BS_ADRENALINE2:
+ case BS_WEAPONPERFECT:
+ case BS_OVERTHRUST:
+ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,(src == bl)? 1:0,skill_get_time(skill_id,skill_lv)));
+ } else if (sd) {
+ party_foreachsamemap(skill_area_sub,
+ sd,skill_get_splash(skill_id, skill_lv),
+ src,skill_id,skill_lv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case BS_MAXIMIZE:
+ case NV_TRICKDEAD:
+ case CR_DEFENDER:
+ case ML_DEFENDER:
+ case CR_AUTOGUARD:
+ case ML_AUTOGUARD:
+ case TK_READYSTORM:
+ case TK_READYDOWN:
+ case TK_READYTURN:
+ case TK_READYCOUNTER:
+ case TK_DODGE:
+ case CR_SHRINK:
+ case SG_FUSION:
+ case GS_GATLINGFEVER:
+ if( tsce )
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
+ map_freeblock_unlock();
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case SL_KAITE:
+ case SL_KAAHI:
+ case SL_KAIZEL:
+ case SL_KAUPE:
+ if (sd) {
+ if (!dstsd || !(
+ (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SOULLINKER) ||
+ (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER ||
+ dstsd->status.char_id == sd->status.char_id ||
+ dstsd->status.char_id == sd->status.partner_id ||
+ dstsd->status.char_id == sd->status.child
+ )) {
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,8);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)));
+ break;
+ case SM_AUTOBERSERK:
+ case MER_AUTOBERSERK:
+ if( tsce )
+ i = status_change_end(bl, type, INVALID_TIMER);
+ else
+ i = sc_start(bl,type,100,skill_lv,60000);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
+ break;
+ case TF_HIDING:
+ case ST_CHASEWALK:
+ case KO_YAMIKUMO:
+ if (tsce)
+ {
+ clif_skill_nodamage(src,bl,skill_id,-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,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,-1,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case TK_RUN:
+ if (tsce)
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
+ map_freeblock_unlock();
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,0));
+ if (sd) // If the client receives a skill-use packet inmediately before a walkok packet, it will discard the walk packet! [Skotlex]
+ clif_walkok(sd); // So aegis has to resend the walk ok.
+ break;
+ case AS_CLOAKING:
+ case GC_CLOAKINGEXCEED:
+ case LG_FORCEOFVANGUARD:
+ case SC_REPRODUCE:
+ case SC_INVISIBILITY:
+ if (tsce) {
+ i = status_change_end(bl, type, INVALID_TIMER);
+ if( i )
+ clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i);
+ else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ case RA_CAMOUFLAGE:
+ i = sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ if( i )
+ clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i);
+ else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case BD_ADAPTATION:
+ if(tsc && tsc->data[SC_DANCING]){
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ }
+ break;
+
+ case BA_FROSTJOKER:
+ case DC_SCREAM:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skill_id,skill_lv,0,flag);
+
+ if (md) {
+ // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves
+ //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches)
+ char temp[70];
+ snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_id].desc);
+ clif_message(&md->bl,temp);
+ }
+ break;
+
+ case BA_PANGVOICE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,50,7,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case DC_WINKCHARM:
+ if( dstsd )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,30,7,skill_get_time2(skill_id,skill_lv)));
+ else
+ if( dstmd )
+ {
+ if( status_get_lv(src) > status_get_lv(bl)
+ && (tstatus->race == RC_DEMON || tstatus->race == RC_DEMIHUMAN || tstatus->race == RC_ANGEL)
+ && !(tstatus->mode&MD_BOSS) )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start2(bl,type,70,skill_lv,src->id,skill_get_time(skill_id,skill_lv)));
+ else
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ }
+ break;
+
+ case TF_STEAL:
+ if(sd) {
+ if(pc_steal_item(sd,bl,skill_lv))
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ }
+ break;
+
+ case RG_STEALCOIN:
+ if(sd) {
+ if(pc_steal_coin(sd,bl))
+ {
+ dstmd->state.provoke_flag = src->id;
+ mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+
+ }
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case MG_STONECURSE:
+ {
+ int brate = 0;
+ if (tstatus->mode&MD_BOSS) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if(status_isimmune(bl) || !tsc)
+ break;
+
+ if (sd && sd->sc.data[SC_PETROLOGY_OPTION])
+ brate = sd->sc.data[SC_PETROLOGY_OPTION]->val3;
+
+ if (tsc->data[SC_STONE]) {
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if (sc_start4(bl,SC_STONE,(skill_lv*4+20)+brate,
+ skill_lv, 0, 0, skill_get_time(skill_id, skill_lv),
+ skill_get_time2(skill_id,skill_lv)))
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ else if(sd) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ // Level 6-10 doesn't consume a red gem if it fails [celest]
+ if (skill_lv > 5)
+ { // not to consume items
+ map_freeblock_unlock();
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case NV_FIRSTAID:
+ clif_skill_nodamage(src,bl,skill_id,5,1);
+ status_heal(bl,5,0,0);
+ break;
+
+ case AL_CURE:
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ status_change_end(bl, SC_CONFUSION, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case TF_DETOXIFY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ status_change_end(bl, SC_DPOISON, INVALID_TIMER);
+ break;
+
+ case PR_STRECOVERY:
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ if (tsc && tsc->opt1) {
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER);
+ }
+ //Is this equation really right? It looks so... special.
+ if(battle_check_undead(tstatus->race,tstatus->def_ele))
+ {
+ status_change_start(bl, SC_BLIND,
+ 100*(100-(tstatus->int_/2+tstatus->vit/3+tstatus->luk/10)),
+ 1,0,0,0,
+ skill_get_time2(skill_id, skill_lv) * (100-(tstatus->int_+tstatus->vit)/2)/100,0);
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(dstmd)
+ mob_unlocktarget(dstmd,tick);
+ break;
+
+ // Mercenary Supportive Skills
+ case MER_BENEDICTION:
+ status_change_end(bl, SC_CURSE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_COMPRESS:
+ status_change_end(bl, SC_BLEEDING, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_MENTALCURE:
+ status_change_end(bl, SC_CONFUSION, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_RECUPERATE:
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_REGAIN:
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_TENDER:
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case MER_SCAPEGOAT:
+ if( mer && mer->master )
+ {
+ status_heal(&mer->master->bl, mer->battle_status.hp, 0, 2);
+ status_damage(src, src, mer->battle_status.max_hp, 0, 0, 1);
+ }
+ break;
+
+ case MER_ESTIMATION:
+ if( !mer )
+ break;
+ sd = mer->master;
+ case WZ_ESTIMATION:
+ if( sd == NULL )
+ break;
+ if( dstsd )
+ { // Fail on Players
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if( dstmd && dstmd->class_ == MOBID_EMPERIUM )
+ break; // Cannot be Used on Emperium
+
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ clif_skill_estimation(sd, bl);
+ if( skill_id == MER_ESTIMATION )
+ sd = NULL;
+ break;
+
+ case BS_REPAIRWEAPON:
+ if(sd && dstsd)
+ clif_item_repair_list(sd,dstsd,skill_lv);
+ break;
+
+ case MC_IDENTIFY:
+ if(sd)
+ clif_item_identify_list(sd);
+ break;
+
+ // Weapon Refining [Celest]
+ case WS_WEAPONREFINE:
+ if(sd)
+ clif_item_refine_list(sd);
+ break;
+
+ case MC_VENDING:
+ if(sd)
+ { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex]
+ if ( !pc_can_give_items(sd) )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ else {
+ sd->state.prevend = 1;
+ clif_openvendingreq(sd,2+skill_lv);
+ }
+ }
+ break;
+
+ case AL_TELEPORT:
+ if(sd)
+ {
+ if (map[bl->m].flag.noteleport && skill_lv <= 2) {
+ clif_skill_teleportmessage(sd,0);
+ break;
+ }
+ if(!battle_config.duel_allow_teleport && sd->duel_group && skill_lv <= 2) { // duel restriction [LuzZza]
+ char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_TELEPORT));
+ clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel."
+ break;
+ }
+
+ if( sd->state.autocast || ( (sd->skillitem == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1 ) || skill_lv == 3 )
+ {
+ if( skill_lv == 1 )
+ pc_randomwarp(sd,CLR_TELEPORT);
+ else
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ }
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if( skill_lv == 1 )
+ clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,0,0,0);
+ else
+ clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,sd->status.save_point.map,0,0);
+ } else
+ unit_warp(bl,-1,-1,-1,CLR_TELEPORT);
+ break;
+
+ case NPC_EXPULSION:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ unit_warp(bl,-1,-1,-1,CLR_TELEPORT);
+ break;
+
+ case AL_HOLYWATER:
+ if(sd) {
+ if (skill_produce_mix(sd, skill_id, 523, 0, 0, 0, 1))
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case TF_PICKSTONE:
+ if(sd) {
+ int eflag;
+ struct item item_tmp;
+ struct block_list tbl;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ memset(&item_tmp,0,sizeof(item_tmp));
+ memset(&tbl,0,sizeof(tbl)); // [MouseJstr]
+ item_tmp.nameid = ITEMID_STONE;
+ item_tmp.identify = 1;
+ tbl.id = 0;
+ clif_takeitem(&sd->bl,&tbl);
+ eflag = pc_additem(sd,&item_tmp,1,LOG_TYPE_PRODUCE);
+ if(eflag) {
+ clif_additem(sd,0,0,eflag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ break;
+ case ASC_CDP:
+ if(sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_produce_mix(sd, skill_id, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle.
+ }
+ break;
+
+ case RG_STRIPWEAPON:
+ case RG_STRIPSHIELD:
+ case RG_STRIPARMOR:
+ case RG_STRIPHELM:
+ case ST_FULLSTRIP:
+ case GC_WEAPONCRUSH:
+ case SC_STRIPACCESSARY: {
+ unsigned short location = 0;
+ int d = 0;
+
+ //Rate in percent
+ if ( skill_id == ST_FULLSTRIP ) {
+ i = 5 + 2*skill_lv + (sstatus->dex - tstatus->dex)/5;
+ } else if( skill_id == SC_STRIPACCESSARY ) {
+ i = 12 + 2 * skill_lv + (sstatus->dex - tstatus->dex)/5;
+ } else {
+ i = 5 + 5*skill_lv + (sstatus->dex - tstatus->dex)/5;
+ }
+
+ if (i < 5) i = 5; //Minimum rate 5%
+
+ //Duration in ms
+ if( skill_id == GC_WEAPONCRUSH){
+ d = skill_get_time(skill_id,skill_lv);
+ if(bl->type == BL_PC)
+ d += skill_lv * 15 + (sstatus->dex - tstatus->dex);
+ else
+ d += skill_lv * 30 + (sstatus->dex - tstatus->dex) / 2;
+ }else
+ d = skill_get_time(skill_id,skill_lv) + (sstatus->dex - tstatus->dex)*500;
+
+ if (d < 0) d = 0; //Minimum duration 0ms
+
+ switch (skill_id) {
+ case RG_STRIPWEAPON:
+ case GC_WEAPONCRUSH:
+ location = EQP_WEAPON;
+ break;
+ case RG_STRIPSHIELD:
+ location = EQP_SHIELD;
+ break;
+ case RG_STRIPARMOR:
+ location = EQP_ARMOR;
+ break;
+ case RG_STRIPHELM:
+ location = EQP_HELM;
+ break;
+ case ST_FULLSTRIP:
+ location = EQP_WEAPON|EQP_SHIELD|EQP_ARMOR|EQP_HELM;
+ break;
+ case SC_STRIPACCESSARY:
+ location = EQP_ACC;
+ break;
+ }
+
+ //Special message when trying to use strip on FCP [Jobbie]
+ if( sd && skill_id == 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, skill_lv, d)) || (skill_id != ST_FULLSTRIP && skill_id != GC_WEAPONCRUSH ) )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
+
+ //Nothing stripped.
+ if( sd && !i )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+
+ case AM_BERSERKPITCHER:
+ case AM_POTIONPITCHER: {
+ int i,x,hp = 0,sp = 0,bonus=100;
+ if( dstmd && dstmd->class_ == MOBID_EMPERIUM ) {
+ map_freeblock_unlock();
+ return 1;
+ }
+ if( sd ) {
+ x = skill_lv%11 - 1;
+ i = pc_search_inventory(sd,skill_db[skill_id].itemid[x]);
+ if( i < 0 || skill_db[skill_id].itemid[x] <= 0 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skill_id].amount[x]) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if( skill_id == AM_BERSERKPITCHER ) {
+ if( dstsd && dstsd->status.base_level < (unsigned int)sd->inventory_data[i]->elv ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ potion_flag = 1;
+ potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0;
+ potion_target = bl->id;
+ run_script(sd->inventory_data[i]->script,0,sd->bl.id,0);
+ potion_flag = potion_target = 0;
+ if( sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_ALCHEMIST )
+ bonus += sd->status.base_level;
+ if( potion_per_hp > 0 || potion_per_sp > 0 ) {
+ hp = tstatus->max_hp * potion_per_hp / 100;
+ hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ if( dstsd ) {
+ sp = dstsd->status.max_sp * potion_per_sp / 100;
+ sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ }
+ } else {
+ if( potion_hp > 0 ) {
+ hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ hp = hp * (100 + (tstatus->vit<<1)) / 100;
+ if( dstsd )
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ if( potion_sp > 0 ) {
+ sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ sp = sp * (100 + (tstatus->int_<<1)) / 100;
+ if( dstsd )
+ sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100;
+ }
+ }
+
+ if (sd->itemgrouphealrate[IG_POTION]>0) {
+ hp += hp * sd->itemgrouphealrate[IG_POTION] / 100;
+ sp += sp * sd->itemgrouphealrate[IG_POTION] / 100;
+ }
+
+ if( (i = pc_skillheal_bonus(sd, skill_id)) ) {
+ hp += hp * i / 100;
+ sp += sp * i / 100;
+ }
+ } else {
+ hp = (1 + rnd()%400) * (100 + skill_lv*10) / 100;
+ hp = hp * (100 + (tstatus->vit<<1)) / 100;
+ if( dstsd )
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ if( dstsd && (i = pc_skillheal2_bonus(dstsd, skill_id)) ) {
+ hp += hp * i / 100;
+ sp += sp * i / 100;
+ }
+ if( tsc && tsc->count ) {
+ if( tsc->data[SC_CRITICALWOUND] ) {
+ hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ }
+ if( tsc->data[SC_DEATHHURT] ) {
+ hp -= hp * 20 / 100;
+ sp -= sp * 20 / 100;
+ }
+ if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2 ) {
+ hp += hp / 10;
+ sp += sp / 10;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if( hp > 0 || (skill_id == AM_POTIONPITCHER && sp <= 0) )
+ clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1);
+ if( sp > 0 )
+ clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1);
+#ifdef RENEWAL
+ if( tsc && tsc->data[SC_EXTREMITYFIST2] )
+ sp = 0;
+#endif
+ status_heal(bl,hp,sp,0);
+ }
+ break;
+ case AM_CP_WEAPON:
+ case AM_CP_SHIELD:
+ case AM_CP_ARMOR:
+ case AM_CP_HELM:
+ {
+ unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP};
+
+ if( sd && ( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[skill_id - AM_CP_WEAPON]) < 0 ) ) ){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock(); // Don't consume item requirements
+ return 0;
+ }
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+ case AM_TWILIGHT1:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ //Prepare 200 White Potions.
+ if (!skill_produce_mix(sd, skill_id, 504, 0, 0, 0, 200))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+ case AM_TWILIGHT2:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ //Prepare 200 Slim White Potions.
+ if (!skill_produce_mix(sd, skill_id, 547, 0, 0, 0, 200))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+ case AM_TWILIGHT3:
+ if (sd) {
+ int ebottle = pc_search_inventory(sd,713);
+ if( ebottle >= 0 )
+ ebottle = sd->status.inventory[ebottle].amount;
+ //check if you can produce all three, if not, then fail:
+ if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol
+ || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle
+ || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle
+ || ebottle < 200 //200 empty bottle are required at total.
+ ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_produce_mix(sd, skill_id, 970, 0, 0, 0, 100);
+ skill_produce_mix(sd, skill_id, 7136, 0, 0, 0, 50);
+ skill_produce_mix(sd, skill_id, 7135, 0, 0, 0, 50);
+ }
+ break;
+ case SA_DISPELL:
+ if (flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1)
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,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.
+ || rnd()%100 >= 50+10*skill_lv
+ || ( tsc && tsc->option&OPTION_MADOGEAR ) )//Mado Gear is immune to dispell according to bug report 49 [Ind]
+ {
+ if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,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_EDP: 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_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_SEVENWIND:
+ case SC_MIRACLE: case SC_S_LIFEPOTION: case SC_L_LIFEPOTION:
+ case SC_INCHEALRATE: case SC_ELECTRICSHOCKER: case SC__STRIPACCESSORY:
+ //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_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER: case SC_STEALTHFIELD_MASTER:
+ case SC_STEALTHFIELD: case SC_GIANTGROWTH: case SC_MILLENNIUMSHIELD:
+ case SC_REFRESH: case SC_STONEHARDSKIN: case SC_VITALITYACTIVATION:
+ case SC_FIGHTINGSPIRIT: case SC_ABUNDANCE: case SC__SHADOWFORM:
+ case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD:
+ case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART:
+ case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE:
+ case SC_GT_REVITALIZE: case SC_REFLECTDAMAGE: case SC_INSPIRATION:
+ case SC_EXEEDBREAK: case SC_FORCEOFVANGUARD: case SC_BANDING:
+ case SC_DUPLELIGHT: case SC_EXPIATIO: case SC_LAUDAAGNUS:
+ case SC_LAUDARAMUS: case SC_GATLINGFEVER: case SC_INCREASING:
+ case SC_ADJUSTMENT: case SC_MADNESSCANCEL:
+#ifdef RENEWAL
+ case SC_EXTREMITYFIST2:
+#endif
+ continue;
+ /**
+ * bugreport:4888 these songs may only be dispelled if you're not in their song area anymore
+ **/
+ case SC_WHISTLE:
+ case SC_ASSNCROS:
+ case SC_POEMBRAGI:
+ case SC_APPLEIDUN:
+ case SC_HUMMING:
+ case SC_DONTFORGETME:
+ case SC_FORTUNE:
+ case SC_SERVICE4U:
+ if( tsc->data[i]->val4 ) //val4 = out-of-song-area
+ continue;
+ break;
+ 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, INVALID_TIMER);
+ }
+ break;
+ }
+ //Affect all targets on splash area.
+ map_foreachinrange(skill_area_sub, bl, i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|1,
+ skill_castend_damage_id);
+ break;
+
+ case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0);
+ break;
+
+ case TK_HIGHJUMP:
+ {
+ int x,y, dir = unit_getdir(src);
+
+ //Fails on noteleport maps, except for GvG and BG maps [Skotlex]
+ if( map[src->m].flag.noteleport &&
+ !(map[src->m].flag.battleground || map_flag_gvg2(src->m) )
+ ) {
+ x = src->x;
+ y = src->y;
+ } else {
+ x = src->x + dirx[dir]*skill_lv*2;
+ y = src->y + diry[dir]*skill_lv*2;
+ }
+
+ clif_skill_nodamage(src,bl,TK_HIGHJUMP,skill_lv,1);
+ if(!map_count_oncell(src->m,x,y,BL_PC|BL_NPC|BL_MOB) && map_getcell(src->m,x,y,CELL_CHKREACH)) {
+ clif_slide(src,x,y);
+ unit_movepos(src, x, y, 1, 0);
+ }
+ }
+ break;
+
+ case SA_CASTCANCEL:
+ case SO_SPELLFIST:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ unit_skillcastcancel(src,1);
+ if(sd) {
+ int sp = skill_get_sp(sd->skill_id_old,sd->skill_lv_old);
+ if( skill_id == SO_SPELLFIST ){
+ sc_start4(src,type,100,skill_lv+1,skill_lv,sd->skill_id_old,sd->skill_lv_old,skill_get_time(skill_id,skill_lv));
+ sd->skill_id_old = sd->skill_lv_old = 0;
+ break;
+ }
+ sp = sp * (90 - (skill_lv-1)*20) / 100;
+ if(sp < 0) sp = 0;
+ status_zap(src, 0, sp);
+ }
+ break;
+ case SA_SPELLBREAKER:
+ {
+ int sp;
+ if(tsc && tsc->data[SC_MAGICROD]) {
+ sp = skill_get_sp(skill_id,skill_lv);
+ sp = sp * tsc->data[SC_MAGICROD]->val2 / 100;
+ if(sp < 1) sp = 1;
+ status_heal(bl,0,sp,2);
+ status_percent_damage(bl, src, 0, -20, false); //20% max SP damage.
+ } else {
+ struct unit_data *ud = unit_bl2ud(bl);
+ int bl_skill_id=0,bl_skill_lv=0,hp = 0;
+ if (!ud || ud->skilltimer == INVALID_TIMER)
+ break; //Nothing to cancel.
+ bl_skill_id = ud->skill_id;
+ bl_skill_lv = ud->skill_lv;
+ if (tstatus->mode & MD_BOSS)
+ { //Only 10% success chance against bosses. [Skotlex]
+ if (rnd()%100 < 90)
+ {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ } else if (!dstsd || map_flag_vs(bl->m)) //HP damage only on pvp-maps when against players.
+ hp = tstatus->max_hp/50; //Recover 2% HP [Skotlex]
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ unit_skillcastcancel(bl,0);
+ sp = skill_get_sp(bl_skill_id,bl_skill_lv);
+ status_zap(bl, hp, sp);
+
+ if (hp && skill_lv >= 5)
+ hp>>=1; //Recover half damaged HP at level 5 [Skotlex]
+ else
+ hp = 0;
+
+ if (sp) //Recover some of the SP used
+ sp = sp*(25*(skill_lv-1))/100;
+
+ if(hp || sp)
+ status_heal(src, hp, sp, 2);
+ }
+ }
+ break;
+ case SA_MAGICROD:
+ clif_skill_nodamage(src,src,SA_MAGICROD,skill_lv,1);
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case SA_AUTOSPELL:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(sd)
+ clif_autospell(sd,skill_lv);
+ else {
+ int maxlv=1,spellid=0;
+ static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT };
+ if(skill_lv >= 10) {
+ spellid = MG_FROSTDIVER;
+// if (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SA_SAGE)
+// maxlv = 10;
+// else
+ maxlv = skill_lv - 9;
+ }
+ else if(skill_lv >=8) {
+ spellid = MG_FIREBALL;
+ maxlv = skill_lv - 7;
+ }
+ else if(skill_lv >=5) {
+ spellid = MG_SOULSTRIKE;
+ maxlv = skill_lv - 4;
+ }
+ else if(skill_lv >=2) {
+ int i = rnd()%3;
+ spellid = spellarray[i];
+ maxlv = skill_lv - 1;
+ }
+ else if(skill_lv > 0) {
+ spellid = MG_NAPALMBEAT;
+ maxlv = 3;
+ }
+ if(spellid > 0)
+ sc_start4(src,SC_AUTOSPELL,100,skill_lv,spellid,maxlv,0,
+ skill_get_time(SA_AUTOSPELL,skill_lv));
+ }
+ break;
+
+ case BS_GREED:
+ if(sd){
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_greed,bl,
+ skill_get_splash(skill_id, skill_lv),BL_ITEM,bl);
+ }
+ break;
+
+ case SA_ELEMENTWATER:
+ case SA_ELEMENTFIRE:
+ case SA_ELEMENTGROUND:
+ case SA_ELEMENTWIND:
+ if(sd && !dstmd) //Only works on monsters.
+ break;
+ if(tstatus->mode&MD_BOSS)
+ break;
+ case NPC_ATTRICHANGE:
+ case NPC_CHANGEWATER:
+ case NPC_CHANGEGROUND:
+ case NPC_CHANGEFIRE:
+ case NPC_CHANGEWIND:
+ case NPC_CHANGEPOISON:
+ case NPC_CHANGEHOLY:
+ case NPC_CHANGEDARKNESS:
+ case NPC_CHANGETELEKINESIS:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv),
+ skill_get_time(skill_id, skill_lv)));
+ break;
+ case NPC_CHANGEUNDEAD:
+ //This skill should fail if target is wearing bathory/evil druid card [Brainstorm]
+ //TO-DO This is ugly, fix it
+ if(tstatus->def_ele==ELE_UNDEAD || tstatus->def_ele==ELE_DARK) break;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv),
+ skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case NPC_PROVOCATION:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (md) mob_unlocktarget(md, tick);
+ break;
+
+ case NPC_KEEPING:
+ case NPC_BARRIER:
+ {
+ int skill_time = skill_get_time(skill_id,skill_lv);
+ struct unit_data *ud = unit_bl2ud(bl);
+ if (clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_time))
+ && ud) { //Disable attacking/acting/moving for skill's duration.
+ ud->attackabletime =
+ ud->canact_tick =
+ ud->canmove_tick = tick + skill_time;
+ }
+ }
+ break;
+
+ case NPC_REBIRTH:
+ if( md && md->state.rebirth )
+ break; // only works once
+ sc_start(bl,type,100,skill_lv,-1);
+ break;
+
+ case NPC_DARKBLESSING:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,(50+skill_lv*5),skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+
+ case NPC_LICK:
+ status_zap(bl, 0, 100);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,(skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+
+ case NPC_SUICIDE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_kill(src); //When suiciding, neither exp nor drops is given.
+ break;
+
+ case NPC_SUMMONSLAVE:
+ case NPC_SUMMONMONSTER:
+ if(md && md->skill_idx >= 0)
+ mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv,skill_id);
+ break;
+
+ case NPC_CALLSLAVE:
+ mob_warpslave(src,MOB_SLAVEDISTANCE);
+ break;
+
+ case NPC_RANDOMMOVE:
+ if (md) {
+ md->next_walktime = tick - 1;
+ mob_randomwalk(md,tick);
+ }
+ break;
+
+ case NPC_SPEEDUP:
+ {
+ // or does it increase casting rate? just a guess xD
+ int i = SC_ASPDPOTION0 + skill_lv - 1;
+ if (i > SC_ASPDPOTION3)
+ i = SC_ASPDPOTION3;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,(sc_type)i,100,skill_lv,skill_lv * 60000));
+ }
+ break;
+
+ case NPC_REVENGE:
+ // not really needed... but adding here anyway ^^
+ if (md && md->master_id > 0) {
+ struct block_list *mbl, *tbl;
+ if ((mbl = map_id2bl(md->master_id)) == NULL ||
+ (tbl = battle_gettargeted(mbl)) == NULL)
+ break;
+ md->state.provoke_flag = tbl->id;
+ mob_target(md, tbl, sstatus->rhw.range);
+ }
+ break;
+
+ case NPC_RUN:
+ {
+ const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}};
+ uint8 dir = (bl == src)?unit_getdir(src):map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away.
+ unit_stop_attack(src);
+ //Run skillv tiles overriding the can-move check.
+ if (unit_walktoxy(src, src->x + skill_lv * mask[dir][0], src->y + skill_lv * mask[dir][1], 2) && md)
+ md->state.skillstate = MSS_WALK; //Otherwise it isn't updated in the ai.
+ }
+ break;
+
+ case NPC_TRANSFORMATION:
+ case NPC_METAMORPHOSIS:
+ if(md && md->skill_idx >= 0) {
+ int class_ = mob_random_class (md->db->skill[md->skill_idx].val,0);
+ if (skill_lv > 1) //Multiply the rest of mobs. [Skotlex]
+ mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv-1,skill_id);
+ if (class_) mob_class_change(md, class_);
+ }
+ break;
+
+ case NPC_EMOTION_ON:
+ case NPC_EMOTION:
+ //va[0] is the emotion to use.
+ //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
+ //val[1] 'sets' the mode
+ //val[2] adds to the current mode
+ //val[3] removes from the current mode
+ //val[4] if set, asks to delete the previous mode change.
+ if(md && md->skill_idx >= 0 && tsc)
+ {
+ clif_emotion(bl, md->db->skill[md->skill_idx].val[0]);
+ if(md->db->skill[md->skill_idx].val[4] && tsce)
+ status_change_end(bl, type, INVALID_TIMER);
+
+ if(md->db->skill[md->skill_idx].val[1] || md->db->skill[md->skill_idx].val[2])
+ sc_start4(src, type, 100, skill_lv,
+ md->db->skill[md->skill_idx].val[1],
+ md->db->skill[md->skill_idx].val[2],
+ md->db->skill[md->skill_idx].val[3],
+ skill_get_time(skill_id, skill_lv));
+ }
+ break;
+
+ case NPC_POWERUP:
+ sc_start(bl,SC_INCATKRATE,100,200,skill_get_time(skill_id, skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case NPC_AGIUP:
+ sc_start(bl,SC_SPEEDUP1,100,skill_lv,skill_get_time(skill_id, skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case NPC_INVISIBLE:
+ //Have val4 passed as 6 is for "infinite cloak" (do not end on attack/skill use).
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,0,0,6,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case NPC_SIEGEMODE:
+ // not sure what it does
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case WE_MALE:
+ {
+ int hp_rate=(!skill_lv)? 0:skill_db[skill_id].hp_rate[skill_lv-1];
+ int gain_hp= tstatus->max_hp*abs(hp_rate)/100; // The earned is the same % of the target HP than it costed the caster. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id,status_heal(bl, gain_hp, 0, 0),1);
+ }
+ break;
+ case WE_FEMALE:
+ {
+ int sp_rate=(!skill_lv)? 0:skill_db[skill_id].sp_rate[skill_lv-1];
+ int gain_sp=tstatus->max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id,status_heal(bl, 0, gain_sp, 0),1);
+ }
+ break;
+
+ // parent-baby skills
+ case WE_BABY:
+ if(sd){
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+ // if neither was found
+ if(!f_sd && !m_sd){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SC_STUN,10000,skill_lv,0,0,0,skill_get_time2(skill_id,skill_lv),8);
+ if (f_sd) sc_start(&f_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ if (m_sd) sc_start(&m_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ break;
+
+ case PF_HPCONVERSION:
+ {
+ int hp, sp;
+ hp = sstatus->max_hp/10;
+ sp = hp * 10 * skill_lv / 100;
+ if (!status_charge(src,hp,0)) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ status_heal(bl,0,sp,2);
+ }
+ break;
+
+ case MA_REMOVETRAP:
+ case HT_REMOVETRAP:
+ {
+ struct skill_unit* su;
+ struct skill_unit_group* sg;
+ su = BL_CAST(BL_SKILL, bl);
+
+ // Mercenaries can remove any trap
+ // Players can only remove their own traps or traps on Vs maps.
+ if( su && (sg = su->group) && (src->type == BL_MER || sg->src_id == src->id || map_flag_vs(bl->m)) && (skill_get_inf2(sg->skill_id)&INF2_TRAP) )
+ {
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ if( sd && !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) )
+ { // prevent picking up expired traps
+ if( battle_config.skill_removetrap_type )
+ { // get back all items used to deploy the trap
+ for( i = 0; i < 10; i++ )
+ {
+ if( skill_db[su->group->skill_id].itemid[i] > 0 )
+ {
+ int flag;
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = skill_db[su->group->skill_id].itemid[i];
+ item_tmp.identify = 1;
+ if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i],LOG_TYPE_OTHER)) )
+ {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ }
+ else
+ { // get back 1 trap
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = su->group->item_id?su->group->item_id:ITEMID_TRAP;
+ item_tmp.identify = 1;
+ if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_OTHER)) )
+ {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ skill_delunit(su);
+ }else if(sd)
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+
+ }
+ break;
+ case HT_SPRINGTRAP:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ {
+ struct skill_unit *su=NULL;
+ if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){
+ switch(su->group->unit_id){
+ case UNT_ANKLESNARE: // ankle snare
+ if (su->group->val2 != 0)
+ // if it is already trapping something don't spring it,
+ // remove trap should be used instead
+ break;
+ // otherwise fallthrough to below
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ case UNT_TALKIEBOX:
+ su->group->unit_id = UNT_USED_TRAPS;
+ clif_changetraplook(bl, UNT_USED_TRAPS);
+ su->group->limit=DIFF_TICK(tick+1500,su->group->tick);
+ su->limit=DIFF_TICK(tick+1500,su->group->tick);
+ }
+ }
+ }
+ break;
+ case BD_ENCORE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(sd)
+ unit_skilluse_id(src,src->id,sd->skill_id_dance,sd->skill_lv_dance);
+ break;
+
+ case AS_SPLASHER:
+ if(tstatus->mode&MD_BOSS
+ /**
+ * Renewal dropped the 3/4 hp requirement
+ **/
+ #ifndef RENEWAL
+ || tstatus-> hp > tstatus->max_hp*3/4
+ #endif
+ ) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,skill_id,src->id,skill_get_time(skill_id,skill_lv),1000));
+#ifndef RENEWAL
+ if (sd) skill_blockpc_start (sd, skill_id, skill_get_time(skill_id, skill_lv)+3000);
+#endif
+ break;
+
+ case PF_MINDBREAKER:
+ {
+ if(tstatus->mode&MD_BOSS || battle_check_undead(tstatus->race,tstatus->def_ele))
+ {
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if (tsce)
+ { //HelloKitty2 (?) explained that this silently fails when target is
+ //already inflicted. [Skotlex]
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ //Has a 55% + skill_lv*5% success chance.
+ if (!clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,55+5*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv))))
+ {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+
+ unit_skillcastcancel(bl,0);
+
+ if(tsc && tsc->count){
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ if(tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ }
+
+ if(dstmd)
+ mob_target(dstmd,src,skill_get_range2(src,skill_id,skill_lv));
+ }
+ break;
+
+ case PF_SOULCHANGE:
+ {
+ unsigned int sp1 = 0, sp2 = 0;
+ if (dstmd) {
+ if (dstmd->state.soul_change_flag) {
+ if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ dstmd->state.soul_change_flag = 1;
+ sp2 = sstatus->max_sp * 3 /100;
+ status_heal(src, 0, sp2, 2);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ }
+ sp1 = sstatus->sp;
+ sp2 = tstatus->sp;
+ #ifdef RENEWAL
+ sp1 = sp1 / 2;
+ sp2 = sp2 / 2;
+ if( tsc && tsc->data[SC_EXTREMITYFIST2] )
+ sp1 = tstatus->sp;
+ #endif
+ status_set_sp(src, sp2, 3);
+ status_set_sp(bl, sp1, 3);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ // Slim Pitcher
+ case CR_SLIMPITCHER:
+ // Updated to block Slim Pitcher from working on barricades and guardian stones.
+ if( dstmd && (dstmd->class_ == MOBID_EMPERIUM || (dstmd->class_ >= MOBID_BARRICADE1 && dstmd->class_ <= MOBID_GUARIDAN_STONE2)) )
+ break;
+ if (potion_hp || potion_sp) {
+ int hp = potion_hp, sp = potion_sp;
+ hp = hp * (100 + (tstatus->vit<<1))/100;
+ sp = sp * (100 + (tstatus->int_<<1))/100;
+ if (dstsd) {
+ if (hp)
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100;
+ if (sp)
+ sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100;
+ }
+ if( tsc && tsc->count ) {
+ if (tsc->data[SC_CRITICALWOUND]) {
+ hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ }
+ if (tsc->data[SC_DEATHHURT]) {
+ hp -= hp * 20 / 100;
+ sp -= sp * 20 / 100;
+ }
+ if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2) {
+ hp += hp / 10;
+ sp += sp / 10;
+ }
+ }
+ if(hp > 0)
+ clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1);
+ if(sp > 0)
+ clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1);
+ status_heal(bl,hp,sp,0);
+ }
+ break;
+ // Full Chemical Protection
+ case CR_FULLPROTECTION:
+ {
+ unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP};
+ int i, s = 0, skilltime = skill_get_time(skill_id,skill_lv);
+
+ for (i=0 ; i<4; i++) {
+ if( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[i]) < 0 ) )
+ continue;
+ sc_start(bl,(sc_type)(SC_CP_WEAPON + i),100,skill_lv,skilltime);
+ s++;
+ }
+ if( sd && !s ){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock(); // Don't consume item requirements
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case RG_CLEANER: //AppleGirl
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case CG_LONGINGFREEDOM:
+ {
+ if (tsc && !tsce && (tsce=tsc->data[SC_DANCING]) && tsce->val4
+ && (tsce->val1&0xFFFF) != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex]
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ }
+ break;
+
+ case CG_TAROTCARD:
+ {
+ int eff, count = -1;
+ if( rnd() % 100 > skill_lv * 8 || (dstmd && ((dstmd->guardian_data && dstmd->class_ == MOBID_EMPERIUM) || mob_is_battleground(dstmd))) )
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_zap(src,0,skill_db[skill_get_index(skill_id)].sp[skill_lv]); // consume sp only if succeeded [Inkfish]
+ do {
+ eff = rnd() % 14;
+ clif_specialeffect(bl, 523 + eff, AREA);
+ switch (eff)
+ {
+ case 0: // heals SP to 0
+ status_percent_damage(src, bl, 0, 100, false);
+ break;
+ case 1: // matk halved
+ sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 2: // all buffs removed
+ status_change_clear_buffs(bl,1);
+ break;
+ case 3: // 1000 damage, random armor destroyed
+ {
+ int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM, EQP_SHOES, EQP_GARMENT };
+ status_fix_damage(src, bl, 1000, 0);
+ clif_damage(src,bl,tick,0,0,1000,0,0,0);
+ if( !status_isdead(bl) )
+ skill_break_equip(bl, where[rnd()%5], 10000, BCT_ENEMY);
+ }
+ break;
+ case 4: // atk halved
+ sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 5: // 2000HP heal, random teleported
+ status_heal(src, 2000, 0, 0);
+ if( !map_flag_vs(bl->m) )
+ unit_warp(bl, -1,-1,-1, CLR_TELEPORT);
+ break;
+ case 6: // random 2 other effects
+ if (count == -1)
+ count = 3;
+ else
+ count++; //Should not retrigger this one.
+ break;
+ case 7: // stop freeze or stoned
+ {
+ enum sc_type sc[] = { SC_STOP, SC_FREEZE, SC_STONE };
+ sc_start(bl,sc[rnd()%3],100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case 8: // curse coma and poison
+ sc_start(bl,SC_COMA,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_POISON,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 9: // confusion
+ sc_start(bl,SC_CONFUSION,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 10: // 6666 damage, atk matk halved, cursed
+ status_fix_damage(src, bl, 6666, 0);
+ clif_damage(src,bl,tick,0,0,6666,0,0,0);
+ sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_CURSE,skill_lv,100,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 11: // 4444 damage
+ status_fix_damage(src, bl, 4444, 0);
+ clif_damage(src,bl,tick,0,0,4444,0,0,0);
+ break;
+ case 12: // stun
+ sc_start(bl,SC_STUN,100,skill_lv,5000);
+ break;
+ case 13: // atk,matk,hit,flee,def reduced
+ sc_start(bl,SC_INCATKRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCMATKRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCHITRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCFLEERATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCDEFRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ break;
+ default:
+ break;
+ }
+ } while ((--count) > 0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SL_ALCHEMIST:
+ case SL_ASSASIN:
+ case SL_BARDDANCER:
+ case SL_BLACKSMITH:
+ case SL_CRUSADER:
+ case SL_HUNTER:
+ case SL_KNIGHT:
+ case SL_MONK:
+ case SL_PRIEST:
+ case SL_ROGUE:
+ case SL_SAGE:
+ case SL_SOULLINKER:
+ case SL_STAR:
+ case SL_SUPERNOVICE:
+ case SL_WIZARD:
+ //NOTE: here, 'type' has the value of the associated MAPID, not of the SC_SPIRIT constant.
+ if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == type)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100))
+ { //Erase death count 1% of the casts
+ dstsd->die_counter = 0;
+ pc_setglobalreg(dstsd,"PC_DIE_COUNTER", 0);
+ clif_specialeffect(bl, 0x152, AREA);
+ //SC_SPIRIT invokes status_calc_pc for us.
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,SC_SPIRIT,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv)));
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
+ break;
+ case SL_HIGH:
+ if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv)));
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
+ break;
+
+ case SL_SWOO:
+ if (tsce) {
+ if(sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,10000,8);
+ status_change_end(bl, SC_SWOO, INVALID_TIMER);
+ break;
+ }
+ case SL_SKA: // [marquis007]
+ case SL_SKE:
+ if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if (skill_id == SL_SKE)
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
+ break;
+
+ // New guild skills [Celest]
+ case GD_BATTLEORDER:
+ if(flag&1) {
+ if (status_get_guild_id(src) == status_get_guild_id(bl))
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv));
+ } else if (status_get_guild_id(src)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case GD_REGENERATION:
+ if(flag&1) {
+ if (status_get_guild_id(src) == status_get_guild_id(bl))
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv));
+ } else if (status_get_guild_id(src)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case GD_RESTORE:
+ if(flag&1) {
+ if (status_get_guild_id(src) == status_get_guild_id(bl))
+ clif_skill_nodamage(src,bl,AL_HEAL,status_percent_heal(bl,90,90),1);
+ } else if (status_get_guild_id(src)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case GD_EMERGENCYCALL:
+ {
+ int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0};
+ int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0};
+ int j = 0;
+ struct guild *g = NULL;
+ // i don't know if it actually summons in a circle, but oh well. ;P
+ g = sd?sd->state.gmaster_flag:guild_search(status_get_guild_id(src));
+ if (!g)
+ break;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ for(i = 0; i < g->max_member; i++, j++) {
+ if (j>8) j=0;
+ if ((dstsd = g->member[i].sd) != NULL && sd != dstsd && !dstsd->state.autotrade && !pc_isdead(dstsd)) {
+ if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg2(dstsd->bl.m))
+ continue;
+ if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH))
+ dx[j] = dy[j] = 0;
+ pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN);
+ }
+ }
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+
+ case SG_FEEL:
+ //AuronX reported you CAN memorize the same map as all three. [Skotlex]
+ if (sd) {
+ if(!sd->feel_map[skill_lv-1].index)
+ clif_feel_req(sd->fd,sd, skill_lv);
+ else
+ clif_feel_info(sd, skill_lv-1, 1);
+ }
+ break;
+
+ case SG_HATE:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (!pc_set_hate_mob(sd, skill_lv-1, bl))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case GS_GLITTERING:
+ if(sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(rnd()%100 < (20+10*skill_lv))
+ pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),10);
+ else if(sd->spiritball > 0)
+ pc_delspiritball(sd,1,0);
+ }
+ break;
+
+ case GS_CRACKER:
+ /* per official standards, this skill works on players and mobs. */
+ if (sd && (dstsd || dstmd))
+ {
+ i =65 -5*distance_bl(src,bl); //Base rate
+ if (i < 30) i = 30;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ sc_start(bl,SC_STUN, i,skill_lv,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+
+ case AM_CALLHOMUN: //[orn]
+ if (sd && !merc_call_homunculus(sd))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case AM_REST:
+ if (sd) {
+ if (merc_hom_vaporize(sd,1))
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case HAMI_CASTLE: //[orn]
+ if(rnd()%100 < 20*skill_lv && src != bl)
+ {
+ int x,y;
+ x = src->x;
+ y = src->y;
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv));
+
+ if (unit_movepos(src,bl->x,bl->y,0,0)) {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1); // Homunc
+ clif_slide(src,bl->x,bl->y) ;
+ if (unit_movepos(bl,x,y,0,0))
+ {
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,1); // Master
+ clif_slide(bl,x,y) ;
+ }
+
+ //TODO: Shouldn't also players and the like switch targets?
+ map_foreachinrange(skill_chastle_mob_changetarget,src,
+ AREA_SIZE, BL_MOB, bl, src);
+ }
+ }
+ // Failed
+ else if (hd && hd->master)
+ clif_skill_fail(hd->master, skill_id, USESKILL_FAIL_LEVEL, 0);
+ else if (sd)
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ case HVAN_CHAOTIC: //[orn]
+ {
+ static const int per[5][2]={{20,50},{50,60},{25,75},{60,64},{34,67}};
+ int r = rnd()%100;
+ i = (skill_lv-1)%5;
+ if(r<per[i][0]) //Self
+ bl = src;
+ else if(r<per[i][1]) //Master
+ bl = battle_get_master(src);
+ else //Enemy
+ bl = map_id2bl(battle_gettarget(src));
+
+ if (!bl) bl = src;
+ i = skill_calc_heal(src, bl, skill_id, 1+rnd()%skill_lv, true);
+ //Eh? why double skill packet?
+ clif_skill_nodamage(src,bl,AL_HEAL,i,1);
+ clif_skill_nodamage(src,bl,skill_id,i,1);
+ status_heal(bl, i, 0, 0);
+ }
+ break;
+ //Homun single-target support skills [orn]
+ case HAMI_BLOODLUST:
+ case HFLI_FLEET:
+ case HFLI_SPEED:
+ case HLIF_CHANGE:
+ case MH_ANGRIFFS_MODUS:
+ case MH_GOLDENE_FERSE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case NPC_DRAGONFEAR:
+ if (flag&1) {
+ const enum sc_type sc[] = { SC_STUN, SC_SILENCE, SC_CONFUSION, SC_BLEEDING };
+ int j;
+ j = i = rnd()%ARRAYLENGTH(sc);
+ while ( !sc_start(bl,sc[i],100,skill_lv,skill_get_time2(skill_id,i+1)) ) {
+ i++;
+ if ( i == ARRAYLENGTH(sc) )
+ i = 0;
+ if (i == j)
+ break;
+ }
+ break;
+ }
+ case NPC_WIDEBLEEDING:
+ case NPC_WIDECONFUSE:
+ case NPC_WIDECURSE:
+ case NPC_WIDEFREEZE:
+ case NPC_WIDESLEEP:
+ case NPC_WIDESILENCE:
+ case NPC_WIDESTONE:
+ case NPC_WIDESTUN:
+ case NPC_SLOWCAST:
+ case NPC_WIDEHELLDIGNITY:
+ if (flag&1)
+ sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ else {
+ skill_area_temp[2] = 0; //For SD_PREAMBLE
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case NPC_WIDESOULDRAIN:
+ if (flag&1)
+ status_percent_damage(src,bl,0,((skill_lv-1)%5+1)*20,false);
+ else {
+ skill_area_temp[2] = 0; //For SD_PREAMBLE
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case ALL_PARTYFLEE:
+ if( sd && !(flag&1) )
+ {
+ if( !sd->status.party_id )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ }
+ else
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case NPC_TALK:
+ case ALL_WEWISH:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case ALL_BUYING_STORE:
+ if( sd )
+ {// players only, skill allows 5 buying slots
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS));
+ }
+ break;
+ case RK_ENCHANTBLADE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,// formula not confirmed
+ sc_start2(bl,type,100,skill_lv,100+20*skill_lv/*+sstatus->int_/2+status_get_lv(bl)/10*/,skill_get_time(skill_id,skill_lv)));
+ break;
+ case RK_DRAGONHOWLING:
+ if( flag&1)
+ sc_start(bl,type,50 + 6 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ else
+ {
+ skill_area_temp[2] = 0;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id,skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_PREAMBLE|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case RK_IGNITIONBREAK:
+ case LG_EARTHDRIVE:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ i = skill_get_splash(skill_id,skill_lv);
+ if( skill_id == LG_EARTHDRIVE ) {
+ int dummy = 1;
+ 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,i,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+ case RK_STONEHARDSKIN:
+ if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 4 )
+ {
+ int heal = sstatus->hp / 4; // 25% HP
+ if( status_charge(bl,heal,0) )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start2(bl,type,100,skill_lv,heal,skill_get_time(skill_id,skill_lv)));
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+ case RK_REFRESH:
+ if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 8 )
+ {
+ int heal = status_get_max_hp(bl) * 25 / 100;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ status_heal(bl,heal,0,1);
+ status_change_clear_buffs(bl,4);
+ }
+ break;
+
+ case RK_MILLENNIUMSHIELD:
+ if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 9 )
+ {
+ short shields = (rnd()%100<50) ? 4 : ((rnd()%100<80) ? 3 : 2);
+ sc_start4(bl,type,100,skill_lv,shields,1000,0,skill_get_time(skill_id,skill_lv));
+ clif_millenniumshield(sd,shields);
+ clif_skill_nodamage(src,bl,skill_id,1,1);
+ }
+ break;
+
+ case RK_GIANTGROWTH:
+ case RK_VITALITYACTIVATION:
+ case RK_ABUNDANCE:
+ case RK_CRUSHSTRIKE:
+ if( sd )
+ {
+ int lv = 1; // RK_GIANTGROWTH
+ if( skill_id == RK_VITALITYACTIVATION )
+ lv = 2;
+ else if( skill_id == RK_ABUNDANCE )
+ lv = 6;
+ else if( skill_id == RK_CRUSHSTRIKE )
+ lv = 7;
+ if( pc_checkskill(sd,RK_RUNEMASTERY) >= lv )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ 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(skill_id,skill_lv));
+ else
+ sc_start(bl,type,100,skill_area_temp[5]/4,skill_get_time(skill_id,skill_lv));
+ } else if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 5 ) {
+ if( sd->status.party_id ) {
+ i = party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,BCT_PARTY,skill_area_sub_count);
+ skill_area_temp[5] = 7 * i; // ATK
+ party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id);
+ } else
+ sc_start2(bl,type,100,7,5,skill_get_time(skill_id,skill_lv));
+ }
+ clif_skill_nodamage(src,bl,skill_id,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(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,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(skill_id,skill_lv));
+ clif_skill_nodamage(src,src,skill_id,skill_lv,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,skill_lv,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case GC_CREATENEWPOISON:
+ if( sd )
+ {
+ clif_skill_produce_mix_list(sd,skill_id,25);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case GC_POISONINGWEAPON:
+ if( sd ) {
+ clif_poison_list(sd,skill_lv);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case GC_ANTIDOTE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,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, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+
+ case GC_HALLUCINATIONWALK:
+ {
+ 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,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ break;
+ }
+ if( !status_charge(bl,heal,0) )
+ {
+ if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case AB_ANCILLA:
+ if( sd ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_produce_mix(sd, skill_id, ITEMID_ANCILLA, 0, 0, 0, 1);
+ }
+ break;
+
+ case AB_CLEMENTIA:
+ case AB_CANTO:
+ {
+ int bless_lv = pc_checkskill(sd,AL_BLESSING) + (sd->status.job_level / 10);
+ int agi_lv = pc_checkskill(sd,AL_INCAGI) + (sd->status.job_level / 10);
+ if( sd == NULL || sd->status.party_id == 0 || flag&1 )
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,
+ (skill_id == AB_CLEMENTIA)? bless_lv : (skill_id == AB_CANTO)? agi_lv : skill_lv, skill_get_time(skill_id,skill_lv)));
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, 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, skill_id, skill_lv, sc_start4(bl, type, 100, skill_lv, 0, 0, 1, skill_get_time(skill_id, skill_lv)));
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_CHEAL:
+ if( sd == NULL || sd->status.party_id == 0 || flag&1 )
+ {
+ if( sd && tstatus && !battle_check_undead(tstatus->race, tstatus->def_ele) )
+ {
+ i = skill_calc_heal(src, bl, AL_HEAL, pc_checkskill(sd, AL_HEAL), true);
+
+ if( (dstsd && pc_ismadogear(dstsd)) || status_isimmune(bl))
+ i = 0; // Should heal by 0 or won't do anything?? in iRO it breaks the healing to members.. [malufett]
+
+ clif_skill_nodamage(bl, bl, skill_id, i, 1);
+ if( tsc && tsc->data[SC_AKAITSUKI] && i )
+ i = ~i + 1;
+ status_heal(bl, i, 0, 0);
+ }
+ }
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_ORATIO:
+ if( flag&1 )
+ sc_start(bl, type, 40 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ else
+ {
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case AB_LAUDAAGNUS:
+ if( flag&1 || sd == NULL ) {
+ if( tsc && (tsc->data[SC_FREEZE] || tsc->data[SC_STONE] || tsc->data[SC_BLIND] ||
+ tsc->data[SC_BURNING] || tsc->data[SC_FREEZING] || tsc->data[SC_CRYSTALIZE])) {
+ // Success Chance: (40 + 10 * Skill Level) %
+ if( rnd()%100 > 40+10*skill_lv ) break;
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ status_change_end(bl, SC_BURNING, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER);
+ }else //Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv,
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ } else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv),
+ src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_LAUDARAMUS:
+ if( flag&1 || sd == NULL ) {
+ if( tsc && (tsc->data[SC_SLEEP] || tsc->data[SC_STUN] || tsc->data[SC_MANDRAGORA] || tsc->data[SC_SILENCE]) ){
+ // Success Chance: (40 + 10 * Skill Level) %
+ if( rnd()%100 > 40+10*skill_lv ) break;
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ status_change_end(bl, SC_MANDRAGORA, INVALID_TIMER);
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ }else // Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv,
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ } else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv),
+ src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_CLEARANCE:
+ if( flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1 )
+ { //As of the behavior in official server Clearance is just a super version of Dispell skill. [Jobbie]
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rnd()%100 >= 30 + 10 * skill_lv)
+ {
+ if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,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_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:
+ case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD:
+ case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART:
+ case SC_PARTYFLEE: case SC_GT_REVITALIZE:
+ case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE:
+#ifdef RENEWAL
+ case SC_EXTREMITYFIST2:
+#endif
+ 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,INVALID_TIMER);
+ }
+ break;
+ }
+ map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skill_id, skill_lv, 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(skill_id, skill_lv), BL_CHAR,
+ src, PR_LEXDIVINA, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ break;
+ /**
+ * Warlock
+ **/
+ case WL_STASIS:
+ if( flag&1 )
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else
+ {
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id, skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,(map_flag_vs(src->m)?BCT_ALL:BCT_ENEMY|BCT_SELF)|flag|1,skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case WL_WHITEIMPRISON:
+ if( (src == bl || battle_check_target(src, bl, BCT_ENEMY)) && !is_boss(bl) )// Should not work with bosses.
+ {
+ int rate = ( sd? sd->status.job_level : 50 ) / 4;
+
+ if( src == bl ) rate = 100; // Success Chance: On self, 100%
+ else if(bl->type == BL_PC) rate += 20 + 10 * skill_lv; // On Players, (20 + 10 * Skill Level) %
+ else rate += 40 + 10 * skill_lv; // On Monsters, (40 + 10 * Skill Level) %
+
+ if( sd )
+ skill_blockpc_start(sd,skill_id,4000);
+
+ if( !(tsc && tsc->data[type]) ){
+ i = sc_start2(bl,type,rate,skill_lv,src->id,(src == bl)?5000:(bl->type == BL_PC)?skill_get_time(skill_id,skill_lv):skill_get_time2(skill_id, skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
+ if( !i )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ }else
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0);
+ break;
+
+ case WL_FROSTMISTY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id);
+ break;
+
+ case WL_JACKFROST:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinshootrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,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, skill_id, skill_lv,
+ sc_start4(bl, type, 100, skill_lv, status_get_int(src), sd ? sd->status.job_level : 50, 0,
+ skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case WL_SIENNAEXECRATE:
+ if( status_isimmune(bl) || !tsc )
+ break;
+
+ if( flag&1 ) {
+ 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,INVALID_TIMER);
+ else
+ status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2);
+ } else {
+ int rate = 40 + 8 * skill_lv + ( sd? sd->status.job_level : 50 ) / 4;
+ // IroWiki says Rate should be reduced by target stats, but currently unknown
+ if( rnd()%100 < rate ) { // Success on First Target
+ if( !tsc->data[SC_STONE] )
+ rate = status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2);
+ else {
+ rate = 1;
+ status_change_end(bl,SC_STONE,INVALID_TIMER);
+ }
+
+ if( rate ) {
+ skill_area_temp[1] = bl->id;
+ map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,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,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case WL_SUMMONFB:
+ case WL_SUMMONBL:
+ case WL_SUMMONWB:
+ case WL_SUMMONSTONE:
+ {
+ 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,skill_id,USESKILL_FAIL_SUMMON,0);
+ break;
+ }
+
+ pos++; // Used in val2 for SC. Indicates the order of this ball
+ switch( skill_id )
+ { // 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,skill_lv,0,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,0,0);
+ }
+ break;
+
+ case WL_READING_SB:
+ if( sd ) {
+ struct status_change *sc = status_get_sc(bl);
+
+ for( i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++)
+ if( sc && !sc->data[i] )
+ break;
+ if( i == SC_MAXSPELLBOOK ) {
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0);
+ break;
+ }
+
+ sc_start(bl, SC_STOP, 100, skill_lv, INVALID_TIMER); //Can't move while selecting a spellbook.
+ clif_spellbook_list(sd);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+ /**
+ * Ranger
+ **/
+ case RA_FEARBREEZE:
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case RA_WUGMASTERY:
+ if( sd ) {
+ 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,skill_id,skill_lv,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);
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case RA_WUGDASH:
+ if( tsce ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
+ map_freeblock_unlock();
+ return 0;
+ }
+ if( sd && pc_isridingwug(sd) ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,1));
+ clif_walkok(sd);
+ }
+ break;
+
+ case RA_SENSITIVEKEEN:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id);
+ break;
+ /**
+ * Mechanic
+ **/
+ case NC_F_SIDESLIDE:
+ case NC_B_SIDESLIDE:
+ {
+ uint8 dir = (skill_id == NC_F_SIDESLIDE) ? (unit_getdir(src)+4)%8 : unit_getdir(src);
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),dir,0x1);
+ clif_slide(src,src->x,src->y);
+ clif_fixpos(src); //Aegis sent this packet
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case NC_SELFDESTRUCTION:
+ if( sd ) {
+ if( pc_ismadogear(sd) )
+ pc_setmadogear(sd, 0);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag);
+ status_set_sp(src, 0, 0);
+ }
+ break;
+
+ case NC_ANALYZE:
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv,
+ sc_start(bl,type, 30 + 12 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if( sd ) pc_overheat(sd,1);
+ break;
+
+ case NC_MAGNETICFIELD:
+ if( (i = sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv))) )
+ {
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill_castend_damage_id);;
+ clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
+ if (sd) pc_overheat(sd,1);
+ }
+ clif_skill_nodamage(src,src,skill_id,skill_lv,i);
+ break;
+
+ case NC_REPAIR:
+ if( sd )
+ {
+ int heal;
+ if( dstsd && pc_ismadogear(dstsd) )
+ {
+ heal = dstsd->status.max_hp * (3+3*skill_lv) / 100;
+ status_heal(bl,heal,0,2);
+ } else {
+ heal = sd->status.max_hp * (3+3*skill_lv) / 100;
+ status_heal(src,heal,0,2);
+ }
+
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, heal);
+ }
+ break;
+
+ case NC_DISJOINT:
+ {
+ if( bl->type != BL_MOB ) break;
+ md = map_id2md(bl->id);
+ if( md && md->class_ >= MOBID_SILVERSNIPER && md->class_ <= MOBID_MAGICDECOY_WIND )
+ status_kill(bl);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+ case SC_AUTOSHADOWSPELL:
+ if( sd ) {
+ if( sd->status.skill[sd->reproduceskill_id].id || sd->status.skill[sd->cloneskill_id].id ) {
+ sc_start(src,SC_STOP,100,skill_lv,-1);// The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax]
+ clif_autoshadowspell_list(sd);
+ clif_skill_nodamage(src,bl,skill_id,1,1);
+ }
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_IMITATION_SKILL_NONE,0);
+ }
+ break;
+
+ case SC_SHADOWFORM:
+ if( sd && dstsd && src != bl && !dstsd->shadowform_id ) {
+ if( clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(src,type,100,skill_lv,bl->id,4+skill_lv,0,skill_get_time(skill_id, skill_lv))) )
+ dstsd->shadowform_id = src->id;
+ }
+ else if( sd )
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+
+ case SC_BODYPAINT:
+ if( flag&1 ) {
+ if( tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
+ tsc->data[SC_CHASEWALK] || tsc->data[SC_CLOAKINGEXCEED] ||
+ tsc->data[SC__INVISIBILITY]) ) {
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(bl, SC_CHASEWALK, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
+
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_BLIND,53 + 2 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ } else {
+ clif_skill_nodamage(src, bl, skill_id, 0, 1);
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case SC_ENERVATION:
+ case SC_GROOMY:
+ case SC_LAZINESS:
+ case SC_UNLUCKY:
+ case SC_WEAKNESS:
+ if( !(tsc && tsc->data[type]) ) {
+ //((rand(myDEX / 12, myDEX / 4) + myJobLevel + 10 * skLevel) + myLevel / 10) - (targetLevel / 10 + targetLUK / 10 + (targetMaxWeight - targetWeight) / 1000 + rand(targetAGI / 6, targetAGI / 3))
+ int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10
+ - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3);
+ rate = cap_value(rate, skill_lv+sstatus->dex/20, 100);
+ clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv)));
+ } else if( sd )
+ clif_skill_fail(sd,skill_id,0,0);
+ break;
+
+ case SC_IGNORANCE:
+ if( !(tsc && tsc->data[type]) ) {
+ int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10
+ - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3);
+ rate = cap_value(rate, skill_lv+sstatus->dex/20, 100);
+ if (clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv)))) {
+ int sp = 200 * skill_lv;
+ if( dstmd ) sp = dstmd->level * 2;
+ if( status_zap(bl,0,sp) )
+ status_heal(src,0,sp/2,3);
+ }
+ else if( sd ) clif_skill_fail(sd,skill_id,0,0);
+ } else if( sd )
+ clif_skill_fail(sd,skill_id,0,0);
+ break;
+
+ case LG_TRAMPLE:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ map_foreachinrange(skill_destroy_trap,bl,skill_get_splash(skill_id,skill_lv),BL_SKILL,tick);
+ break;
+
+ case LG_REFLECTDAMAGE:
+ if( tsc && tsc->data[type] )
+ status_change_end(bl,type,INVALID_TIMER);
+ else
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case LG_SHIELDSPELL:
+ if( flag&1 ) {
+ int duration = (sd) ? sd->bonus.shieldmdef * 2000 : 10000;
+ sc_start(bl,SC_SILENCE,100,skill_lv,duration);
+ } else if( sd ) {
+ int opt = skill_lv;
+ int rate = rnd()%100;
+ int val, brate;
+ switch( skill_lv ) {
+ case 1:
+ {
+ struct item_data *shield_data = sd->inventory_data[sd->equip_index[EQI_HAND_L]];
+ if( !shield_data || shield_data->type != IT_ARMOR ) { // No shield?
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+ brate = shield_data->def * 10;
+ if( rate < 50 )
+ opt = 1;
+ else if( rate < 75 )
+ opt = 2;
+ else
+ opt = 3;
+
+ switch( opt ) {
+ case 1:
+ sc_start(bl,SC_SHIELDSPELL_DEF,100,opt,-1);
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rate < brate )
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ status_change_end(bl,SC_SHIELDSPELL_DEF,INVALID_TIMER);
+ break;
+ case 2:
+ val = shield_data->def / 10; // % Reflected damage.
+ sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 1000);
+ break;
+ case 3:
+ val = shield_data->def; // Attack increase.
+ sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 3000);
+ break;
+ }
+ }
+ break;
+
+ case 2:
+ brate = sd->bonus.shieldmdef * 20;
+ if( rate < 30 )
+ opt = 1;
+ else if( rate < 60 )
+ opt = 2;
+ else
+ opt = 3;
+ switch( opt ) {
+ case 1:
+ sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1);
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rate < brate )
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|2,skill_castend_damage_id);
+ status_change_end(bl,SC_SHIELDSPELL_MDEF,INVALID_TIMER);
+ break;
+ case 2:
+ sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1);
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rate < brate )
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id);
+ break;
+ case 3:
+ if( sc_start(bl,SC_SHIELDSPELL_MDEF,brate,opt,sd->bonus.shieldmdef * 30000) )
+ clif_skill_nodamage(src,bl,PR_MAGNIFICAT,skill_lv,
+ sc_start(bl,SC_MAGNIFICAT,100,1,sd->bonus.shieldmdef * 30000));
+ break;
+ }
+ break;
+
+ case 3:
+ {
+ struct item *it = &sd->status.inventory[sd->equip_index[EQI_HAND_L]];
+ if( !it ) { // No shield?
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ brate = it->refine * 5;
+ if( rate < 25 )
+ opt = 1;
+ else if( rate < 50 )
+ opt = 2;
+ else
+ opt = 3;
+ switch( opt ) {
+ case 1:
+ val = 105 * it->refine / 10;
+ sc_start2(bl,SC_SHIELDSPELL_REF,brate,opt,val,skill_get_time(skill_id,skill_lv));
+ break;
+ case 2: case 3:
+ if( rate < brate )
+ {
+ val = sstatus->max_hp * (11 + it->refine) / 100;
+ status_heal(bl, val, 0, 3);
+ }
+ break;
+ /*case 3:
+ // Full protection. I need confirm what effect should be here. Moved to case 2 to until we got it.
+ break;*/
+ }
+ }
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case LG_PIETY:
+ if( flag&1 )
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else {
+ skill_area_temp[2] = 0;
+ map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_PC,src,skill_id,skill_lv,tick,flag|SD_PREAMBLE|BCT_PARTY|BCT_SELF|1,skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case LG_INSPIRATION:
+ if( sd && !map[sd->bl.m].flag.noexppenalty && sd->status.base_level != MAX_LEVEL ) {
+ sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * 1 / 100); // 1% penalty.
+ sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * 1 / 100);
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ clif_skill_nodamage(bl,src,skill_id,skill_lv,
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ break;
+ case SR_CURSEDCIRCLE:
+ if( flag&1 ) {
+ if( is_boss(bl) ) break;
+ if( sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id, skill_lv))) {
+ if( bl->type == BL_MOB )
+ mob_unlocktarget((TBL_MOB*)bl,gettick());
+ unit_stop_attack(bl);
+ clif_bladestop(src, bl->id, 1);
+ map_freeblock_unlock();
+ return 1;
+ }
+ } else {
+ int count = 0;
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ count = map_forcountinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv), (sd)?sd->spiritball_old:15, // Assume 15 spiritballs in non-charactors
+ BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ if( sd ) pc_delspiritball(sd, count, 0);
+ clif_skill_nodamage(src, src, skill_id, skill_lv,
+ sc_start2(src, SC_CURSEDCIRCLE_ATKER, 100, skill_lv, count, skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+
+ case SR_RAISINGDRAGON:
+ if( sd ) {
+ short max = 5 + skill_lv;
+ sc_start(bl, SC_EXPLOSIONSPIRITS, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ for( i = 0; i < max; i++ ) // Don't call more than max available spheres.
+ pc_addspiritball(sd, skill_get_time(skill_id, skill_lv), max);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv)));
+ }
+ break;
+
+ case SR_ASSIMILATEPOWER:
+ if( flag&1 ) {
+ i = 0;
+ if( dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER )
+ {
+ i = dstsd->spiritball; //1%sp per spiritball.
+ pc_delspiritball(dstsd, dstsd->spiritball, 0);
+ }
+ if( i ) status_percent_heal(src, 0, i);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, i ? 1:0);
+ } else {
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF|SD_SPLASH|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case SR_POWERVELOCITY:
+ if( !dstsd )
+ break;
+ if( sd && dstsd->spiritball <= 5 ) {
+ for(i = 0; i <= 5; i++) {
+ pc_addspiritball(dstsd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), i);
+ pc_delspiritball(sd, sd->spiritball, 0);
+ }
+ }
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ break;
+
+ case SR_GENTLETOUCH_CURE:
+ {
+ int heal;
+
+ if( status_isimmune(bl) )
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+
+ heal = 120 * skill_lv + status_get_max_hp(bl) * (2 + skill_lv) / 100;
+ status_heal(bl, heal, 0, 0);
+
+ if( (tsc && tsc->opt1) && (rnd()%100 < ((skill_lv * 5) + (status_get_dex(src) + status_get_lv(src)) / 4) - (1 + (rnd() % 10))) )
+ {
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ status_change_end(bl, SC_HALLUCINATION, INVALID_TIMER);
+ status_change_end(bl, SC_BURNING, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ }
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+ case SR_GENTLETOUCH_CHANGE:
+ case SR_GENTLETOUCH_REVITALIZE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)));
+ break;
+ case WA_SWING_DANCE:
+ case WA_MOONLIT_SERENADE:
+ if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else if( sd ) { // Only shows effects on caster.
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case WA_SYMPHONY_OF_LOVER:
+ case MI_RUSH_WINDMILL:
+ case MI_ECHOSONG:
+ if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
+ sc_start4(bl,type,100,skill_lv,6*skill_lv,(sd?pc_checkskill(sd,WM_LESSON):0),(sd?sd->status.job_level:0),skill_get_time(skill_id,skill_lv));
+ else if( sd ) { // Only shows effects on caster.
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case MI_HARMONIZE:
+ if( src != bl )
+ clif_skill_nodamage(src, src, skill_id, skill_lv, sc_start(src, type, 100, skill_lv, skill_get_time(skill_id,skill_lv)));
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case WM_DEADHILLHERE:
+ if( bl->type == BL_PC ) {
+ if( !status_isdead(bl) )
+ break;
+
+ if( rnd()%100 < 88 + 2 * skill_lv ) {
+ int heal = tstatus->sp;
+ if( heal <= 0 )
+ heal = 1;
+ tstatus->hp = heal;
+ tstatus->sp -= tstatus->sp * ( 120 - 20 * skill_lv ) / 100;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ pc_revive((TBL_PC*)bl,heal,0);
+ clif_resurrection(bl,1);
+ }
+ }
+ break;
+
+ case WM_SIRCLEOFNATURE:
+ flag |= BCT_SELF|BCT_PARTY|BCT_GUILD;
+ case WM_VOICEOFSIREN:
+ if( skill_id != WM_SIRCLEOFNATURE )
+ flag &= ~BCT_SELF;
+ if( flag&1 ) {
+ sc_start2(bl,type,(skill_id==WM_VOICEOFSIREN)?20+10*skill_lv:100,skill_lv,(skill_id==WM_VOICEOFSIREN)?src->id:0,skill_get_time(skill_id,skill_lv));
+ } else {
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),(skill_id==WM_VOICEOFSIREN)?BL_CHAR|BL_SKILL:BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case WM_GLOOMYDAY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if( dstsd && ( pc_checkskill(dstsd,KN_BRANDISHSPEAR) || pc_checkskill(dstsd,LK_SPIRALPIERCE) ||
+ pc_checkskill(dstsd,CR_SHIELDCHARGE) || pc_checkskill(dstsd,CR_SHIELDBOOMERANG) ||
+ pc_checkskill(dstsd,PA_SHIELDCHAIN) || pc_checkskill(dstsd,LG_SHIELDPRESS) ) )
+ {
+ sc_start(bl,SC_GLOOMYDAY_SK,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ }
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case WM_SATURDAY_NIGHT_FEVER:
+ if( flag&1 ) { // Affect to all targets arround the caster and caster too.
+ if( !(tsc && tsc->data[type]) )
+ sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv));
+ } else if( flag&2 ) {
+ if( src->id != bl->id && battle_check_target(src,bl,BCT_ENEMY) > 0 )
+ status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
+ } else if( sd ) {
+ short chance = sstatus->int_/6 + sd->status.job_level/5 + skill_lv*4;
+ if( !sd->status.party_id || (rnd()%100 > chance)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0);
+ break;
+ }
+ if( map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id,skill_lv),
+ BL_PC, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count) > 7 )
+ flag |= 2;
+ else
+ flag |= 1;
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv,
+ sc_start(src,SC_STOP,100,skill_lv,skill_get_time2(skill_id,skill_lv)));
+ if( flag&2 ) // Dealed here to prevent conflicts
+ status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
+ }
+ break;
+
+ case WM_SONG_OF_MANA:
+ case WM_DANCE_WITH_WUG:
+ case WM_LERADS_DEW:
+ if( flag&1 ) { // These affect to to all party members near the caster.
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->data[type] ) {
+ sc_start2(bl,type,100,skill_lv,sc->data[type]->val2,skill_get_time(skill_id,skill_lv));
+ }
+ } else if( sd ) {
+ short lv = (short)skill_lv;
+ int count = skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1);
+ if( sc_start2(bl,type,100,skill_lv,count,skill_get_time(skill_id,skill_lv)) )
+ party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+
+ }
+ break;
+
+ case WM_MELODYOFSINK:
+ case WM_BEYOND_OF_WARCRY:
+ case WM_UNLIMITED_HUMMING_VOICE:
+ if( flag&1 ) {
+ sc_start2(bl,type,100,skill_lv,skill_area_temp[0],skill_get_time(skill_id,skill_lv));
+ } else { // These affect to all targets arround the caster.
+ short lv = (short)skill_lv;
+ skill_area_temp[0] = (sd) ? skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1) : 50; // 50% chance in non BL_PC (clones).
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case WM_RANDOMIZESPELL: {
+ int improv_skill_id = 0, improv_skill_lv;
+ do {
+ i = rnd() % MAX_SKILL_IMPROVISE_DB;
+ improv_skill_id = skill_improvise_db[i].skill_id;
+ } while( improv_skill_id == 0 || rnd()%10000 >= skill_improvise_db[i].per );
+ improv_skill_lv = 4 + skill_lv;
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+
+ if( sd ) {
+ sd->state.abra_flag = 2;
+ sd->skillitem = improv_skill_id;
+ sd->skillitemlv = improv_skill_lv;
+ clif_item_skill(sd, improv_skill_id, improv_skill_lv);
+ } else {
+ struct unit_data *ud = unit_bl2ud(src);
+ int inf = skill_get_inf(improv_skill_id);
+ int target_id = 0;
+ if (!ud) break;
+ if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
+ if (src->type == BL_PET)
+ bl = (struct block_list*)((TBL_PET*)src)->msd;
+ if (!bl) bl = src;
+ unit_skilluse_id(src, bl->id, improv_skill_id, improv_skill_lv);
+ } else {
+ if (ud->target)
+ target_id = ud->target;
+ else switch (src->type) {
+ case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break;
+ case BL_PET: target_id = ((TBL_PET*)src)->target_id; break;
+ }
+ if (!target_id)
+ break;
+ if (skill_get_casttype(improv_skill_id) == CAST_GROUND) {
+ bl = map_id2bl(target_id);
+ if (!bl) bl = src;
+ unit_skilluse_pos(src, bl->x, bl->y, improv_skill_id, improv_skill_lv);
+ } else
+ unit_skilluse_id(src, target_id, improv_skill_id, improv_skill_lv);
+ }
+ }
+ }
+ break;
+
+
+ case RETURN_TO_ELDICASTES:
+ case ALL_GUARDIAN_RECALL:
+ if( sd )
+ {
+ short x, y; // Destiny position.
+ unsigned short mapindex;
+
+ if( skill_id == RETURN_TO_ELDICASTES)
+ {
+ x = 198;
+ y = 187;
+ mapindex = mapindex_name2id(MAP_DICASTES);
+ }
+ else
+ {
+ x = 44;
+ y = 151;
+ mapindex = mapindex_name2id(MAP_MORA);
+ }
+
+ if(!mapindex)
+ { //Given map not found?
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ pc_setpos(sd, mapindex, x, y, CLR_TELEPORT);
+ }
+ break;
+
+ case GM_SANDMAN:
+ if( tsc ) {
+ if( tsc->opt1 == OPT1_SLEEP )
+ tsc->opt1 = 0;
+ else
+ tsc->opt1 = OPT1_SLEEP;
+ clif_changeoption(bl);
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case SO_ARRULLO:
+ {
+ // [(15 + 5 * Skill Level) + ( Caster’s INT / 5 ) + ( Caster’s Job Level / 5 ) - ( Target’s INT / 6 ) - ( Target’s LUK / 10 )] %
+ int rate = (15 + 5 * skill_lv) + status_get_int(src)/5 + (sd)?sd->status.job_level:0;
+ rate -= status_get_int(bl)/6 - status_get_luk(bl)/10;
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ sc_start2(bl, type, rate, skill_lv, 1, skill_get_time(skill_id, skill_lv));
+ }
+ break;
+
+ case WM_LULLABY_DEEPSLEEP:
+ if( flag&1 ){
+ //[(Skill Level x 4) + (Voice Lessons Skill Level x 2) + (Caster’s Base Level / 15) + (Caster’s Job Level / 5)] %
+ int rate = (4 * skill_lv) + ( (sd) ? pc_checkskill(sd,WM_LESSON)*2 + sd->status.job_level/5 : 0 ) + status_get_lv(src) / 15;
+ if( bl != src )
+ sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv));
+ }else {
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ALL|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ if( sd ) {
+ int elemental_class = skill_get_elemental_type(skill_id,skill_lv);
+
+ // Remove previous elemental fisrt.
+ if( sd->ed )
+ elemental_delete(sd->ed,0);
+
+ // Summoning the new one.
+ if( !elemental_create(sd,elemental_class,skill_get_time(skill_id,skill_lv)) ) {
+ clif_skill_fail(sd,skill_id,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SO_EL_CONTROL:
+ if( sd ) {
+ int mode = EL_MODE_PASSIVE; // Standard mode.
+
+ if( !sd->ed ) break;
+
+ if( skill_lv == 4 ) {// At level 4 delete elementals.
+ elemental_delete(sd->ed, 0);
+ break;
+ }
+ switch( skill_lv ) {// Select mode bassed on skill level used.
+ case 2: mode = EL_MODE_ASSIST; break;
+ case 3: mode = EL_MODE_AGGRESSIVE; break;
+ }
+ if( !elemental_change_mode(sd->ed,mode) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SO_EL_ACTION:
+ if( sd ) {
+ int duration = 3000;
+ if( !sd->ed ) break;
+ sd->skill_id_old = skill_id;
+ elemental_action(sd->ed, bl, tick);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ switch(sd->ed->db->class_){
+ case 2115:case 2124:
+ case 2118:case 2121:
+ duration = 6000;
+ break;
+ case 2116:case 2119:
+ case 2122:case 2125:
+ duration = 9000;
+ break;
+ }
+ skill_blockpc_start(sd, skill_id, duration);
+ }
+ break;
+
+ case SO_EL_CURE:
+ if( sd ) {
+ struct elemental_data *ed = sd->ed;
+ int s_hp = sd->battle_status.hp * 10 / 100, s_sp = sd->battle_status.sp * 10 / 100;
+ int e_hp, e_sp;
+
+ if( !ed ) break;
+ if( !status_charge(&sd->bl,s_hp,s_sp) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ e_hp = ed->battle_status.max_hp * 10 / 100;
+ e_sp = ed->battle_status.max_sp * 10 / 100;
+ status_heal(&ed->bl,e_hp,e_sp,3);
+ clif_skill_nodamage(src,&ed->bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case GN_CHANGEMATERIAL:
+ case SO_EL_ANALYSIS:
+ if( sd ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ clif_skill_itemlistwindow(sd,skill_id,skill_lv);
+ }
+ break;
+
+ case GN_BLOOD_SUCKER:
+ {
+ struct status_change *sc = status_get_sc(src);
+
+ if( sc && sc->bs_counter < skill_get_maxcount( skill_id , skill_lv) ) {
+ if( tsc && tsc->data[type] ){
+ (sc->bs_counter)--;
+ status_change_end(src, type, INVALID_TIMER); // the first one cancels and the last one will take effect resetting the timer
+ }
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id,skill_lv));
+ (sc->bs_counter)++;
+ } else if( sd ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+ }
+ break;
+
+ case GN_MANDRAGORA:
+ if( flag&1 ) {
+ if ( clif_skill_nodamage(bl, src, skill_id, skill_lv,
+ sc_start(bl, type, 25 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv))) )
+ status_zap(bl, 0, status_get_max_sp(bl) * (25 + 5 * skill_lv) / 100);
+ } else
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ break;
+
+ case GN_SLINGITEM:
+ if( sd ) {
+ short ammo_id;
+ i = sd->equip_index[EQI_AMMO];
+ if( i <= 0 )
+ break; // No ammo.
+ ammo_id = sd->inventory_data[i]->nameid;
+ if( ammo_id <= 0 )
+ break;
+ sd->itemid = ammo_id;
+ if( itemdb_is_GNbomb(ammo_id) ) {
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0) {// Only attack if the target is an enemy.
+ if( ammo_id == 13263 )
+ map_foreachincell(skill_area_sub,bl->m,bl->x,bl->y,BL_CHAR,src,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag);
+ } else //Otherwise, it fails, shows animation and removes items.
+ clif_skill_fail(sd,GN_SLINGITEM_RANGEMELEEATK,0xa,0);
+ } else if( itemdb_is_GNthrowable(ammo_id) ){
+ struct script_code *script = sd->inventory_data[i]->script;
+ if( !script )
+ break;
+ if( dstsd )
+ run_script(script,0,dstsd->bl.id,fake_nd->bl.id);
+ else
+ run_script(script,0,src->id,0);
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);// This packet is received twice actually, I think it is to show the animation.
+ break;
+
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ if( sd ) {
+ int qty = 1;
+ sd->skill_id_old = skill_id;
+ sd->skill_lv_old = skill_lv;
+ if( skill_id != GN_S_PHARMACY && skill_lv > 1 )
+ qty = 10;
+ clif_cooking_list(sd,(skill_id - GN_MIX_COOKING) + 27,skill_id,qty,skill_id==GN_MAKEBOMB?5:6);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+ case EL_CIRCLE_OF_FIRE:
+ case EL_PYROTECHNIC:
+ case EL_HEATER:
+ case EL_TROPIC:
+ case EL_AQUAPLAY:
+ case EL_COOLER:
+ case EL_CHILLY_AIR:
+ case EL_GUST:
+ case EL_BLAST:
+ case EL_WILD_STORM:
+ case EL_PETROLOGY:
+ case EL_CURSED_SOIL:
+ case EL_UPHEAVAL:
+ case EL_FIRE_CLOAK:
+ case EL_WATER_DROP:
+ case EL_WIND_CURTAIN:
+ case EL_SOLID_SKIN:
+ case EL_STONE_SHIELD:
+ case EL_WIND_STEP: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ sc_type type2 = type-1;
+ struct status_change *sc = status_get_sc(&ele->bl);
+
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skill_id);
+ } else {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ clif_skill_damage(src, ( skill_id == EL_GUST || skill_id == EL_BLAST || skill_id == EL_WILD_STORM )?src:bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( skill_id == EL_WIND_STEP ) // There aren't teleport, just push the master away.
+ skill_blown(src,bl,(rnd()%skill_get_blewcount(skill_id,skill_lv))+1,rand()%8,0);
+ sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ }
+ }
+ break;
+
+ case EL_FIRE_MANTLE:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ skill_unitsetting(src,skill_id,skill_lv,bl->x,bl->y,0);
+ break;
+
+ case EL_WATER_SCREEN: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ struct status_change *sc = status_get_sc(&ele->bl);
+ sc_type type2 = type-1;
+
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skill_id);
+ } else {
+ // This not heals at the end.
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,type,100,src->id,skill_get_time(skill_id,skill_lv));
+ }
+ }
+ }
+ break;
+
+ case KO_KAHU_ENTEN:
+ case KO_HYOUHU_HUBUKI:
+ case KO_KAZEHU_SEIRAN:
+ case KO_DOHU_KOUKAI:
+ if(sd) {
+ int ttype = skill_get_ele(skill_id, skill_lv);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ pc_add_talisman(sd, skill_get_time(skill_id, skill_lv), 10, ttype);
+ }
+ break;
+
+ case KO_ZANZOU:
+ if(sd){
+ struct mob_data *md;
+
+ md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), 2308, "", SZ_SMALL, AI_NONE);
+ if( md )
+ {
+ md->master_id = src->id;
+ md->special_state.ai = AI_ZANZOU;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn( md );
+ pc_setinvincibletimer(sd,500);// unlock target lock
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0);
+ }
+ }
+ break;
+
+ case KO_KYOUGAKU:
+ if( dstsd && tsc && !tsc->data[type] && rand()%100 < tstatus->int_/2 ){
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case KO_JYUSATSU:
+ if( dstsd && tsc && !tsc->data[type] &&
+ rand()%100 < ((45+5*skill_lv) + skill_lv*5 - status_get_int(bl)/2) ){//[(Base chance of success) + (Skill Level x 5) - (int / 2)]%.
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ status_change_start(bl,type,10000,skill_lv,0,0,0,skill_get_time(skill_id,skill_lv),1));
+ status_zap(bl, tstatus->max_hp*skill_lv*5/100 , 0);
+ if( status_get_lv(bl) <= status_get_lv(src) )
+ status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0);
+ }else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case KO_GENWAKU:
+ if ( !map_flag_gvg(src->m) && ( dstsd || dstmd ) && battle_check_target(src,bl,BCT_ENEMY) > 0 ) {
+ int x = src->x, y = src->y;
+
+ if( sd && rnd()%100 > ((45+5*skill_lv) - status_get_int(bl)/10) ){//[(Base chance of success) - (Intelligence Objectives / 10)]%.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+
+ if (unit_movepos(src,bl->x,bl->y,0,0)) {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ clif_slide(src,bl->x,bl->y) ;
+ sc_start(src,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv));
+ if (unit_movepos(bl,x,y,0,0))
+ {
+ clif_skill_damage(bl,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, -1, 6);
+ if( bl->type == BL_PC && pc_issit((TBL_PC*)bl))
+ clif_sitting(bl); //Avoid sitting sync problem
+ clif_slide(bl,x,y) ;
+ sc_start(bl,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ }
+ }
+ break;
+
+ case OB_AKAITSUKI:
+ case OB_OBOROGENSOU:
+ if( sd && ( (skill_id == OB_OBOROGENSOU && bl->type == BL_MOB) // This skill does not work on monsters.
+ || is_boss(bl) ) ){ // Does not work on Boss monsters.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ case KO_IZAYOI:
+ case OB_ZANGETSU:
+ case KG_KYOMU:
+ case KG_KAGEMUSYA:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ break;
+
+ case KG_KAGEHUMI:
+ if( flag&1 ){
+ if(tsc && ( tsc->option&(OPTION_CLOAK|OPTION_HIDE) ||
+ tsc->data[SC_CAMOUFLAGE] || tsc->data[SC__SHADOWFORM] ||
+ tsc->data[SC_MARIONETTE] || tsc->data[SC_HARMONIZE])){
+ sc_start(src, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ 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);
+ status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
+ status_change_end(bl, SC_MARIONETTE, INVALID_TIMER);
+ status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ }
+ if( skill_area_temp[2] == 1 ){
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ sc_start(src, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ }
+ }else{
+ skill_area_temp[2] = 0;
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case MH_SILENT_BREEZE: {
+ struct status_change *ssc = status_get_sc(src);
+ struct block_list *m_bl = battle_get_master(src);
+ const enum sc_type scs[] = {
+ SC_MANDRAGORA, SC_HARMONIZE, SC_DEEPSLEEP, SC_VOICEOFSIREN, SC_SLEEP, SC_CONFUSION, SC_HALLUCINATION
+ };
+ int heal;
+ if(tsc){
+ for (i = 0; i < ARRAYLENGTH(scs); i++) {
+ if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
+ }
+ if (!tsc->data[SC_SILENCE]) //put inavoidable silence on target
+ status_change_start(bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+ }
+ heal = status_get_matk_min(src)*4;
+ status_heal(bl, heal, 0, 7);
+
+ //now inflict silence on everyone
+ if(ssc && !ssc->data[SC_SILENCE]) //put inavoidable silence on homun
+ status_change_start(src, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+ if(m_bl){
+ struct status_change *msc = status_get_sc(m_bl);
+ if(msc && !msc->data[SC_SILENCE]) //put inavoidable silence on master
+ status_change_start(m_bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+ }
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+ case MH_OVERED_BOOST:
+ if (hd){
+ struct block_list *s_bl = battle_get_master(src);
+ if(hd->homunculus.hunger>50) //reduce hunger
+ hd->homunculus.hunger = hd->homunculus.hunger/2;
+ else
+ hd->homunculus.hunger = min(1,hd->homunculus.hunger);
+ if(s_bl && s_bl->type==BL_PC){
+ status_set_sp(s_bl,status_get_max_sp(s_bl)/2,0); //master drain 50% sp
+ clif_send_homdata(((TBL_PC *)s_bl), SP_HUNGRY, hd->homunculus.hunger); //refresh hunger info
+ sc_start(s_bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); //gene bonus
+ }
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+ case MH_GRANITIC_ARMOR:
+ case MH_PYROCLASTIC: {
+ struct block_list *s_bl = battle_get_master(src);
+ if(s_bl) sc_start2(s_bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv)); //start on master
+ sc_start2(bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv));
+ if (hd) skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+
+ case MH_LIGHT_OF_REGENE:
+ if(hd){
+ hd->homunculus.intimacy = 251; //change to neutral (can't be cast if < 750)
+ if(sd) clif_send_homdata(sd, SP_INTIMATE, hd->homunculus.intimacy); //refresh intimacy info
+ }
+ //don't break need to start status and start block timer
+ case MH_STYLE_CHANGE:
+ case MH_MAGMA_FLOW:
+ case MH_PAIN_KILLER:
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ break;
+ case MH_SUMMON_LEGION:
+ {
+ int summons[5] = {1004, 1303, 1303, 1994, 1994};
+ int qty[5] = {3 , 3 , 4 , 4 , 5};
+ struct mob_data *md;
+ int i;
+
+ for(i=0; i<qty[skill_lv - 1]; i++){ //easy way
+ md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), summons[skill_lv - 1], "", SZ_SMALL, AI_ATTACK);
+ if (md) {
+ md->master_id = src->id;
+ if (md->deletetimer != INVALID_TIMER)
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer(gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn(md); //Now it is ready for spawning.
+ sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_ASSIST, 0, 60000);
+ }
+ }
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+ default:
+ ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skill_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if(skill_id != SR_CURSEDCIRCLE){
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] )//Should only remove after the skill had been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+ }
+
+ if (dstmd) { //Mob skill event for no damage skills (damage ones are handled in battle_calc_damage) [Skotlex]
+ mob_log_damage(dstmd, src, 0); //Log interaction (counts as 'attacker' for the exp bonus)
+ mobskill_event(dstmd, src, tick, MSC_SKILLUSED|(skill_id<<16));
+ }
+
+ if( sd && !(flag&1) )
+ {// ensure that the skill last-cast tick is recorded
+ sd->canskill_tick = gettick();
+
+ if( sd->state.arrow_atk )
+ {// consume arrow on last invocation to this skill.
+ battle_consume_ammo(sd, skill_id, skill_lv);
+ }
+ skill_onskillusage(sd, bl, skill_id, tick);
+ // perform skill requirement consumption
+ skill_consume_requirement(sd,skill_id,skill_lv,2);
+ }
+
+ map_freeblock_unlock();
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *target, *src;
+ struct map_session_data *sd;
+ struct mob_data *md;
+ struct unit_data *ud;
+ struct status_change *sc = NULL;
+ int inf,inf2,flag = 0;
+
+ src = map_id2bl(id);
+ if( src == NULL )
+ {
+ ShowDebug("skill_castend_id: src == NULL (tid=%d, id=%d)\n", tid, id);
+ return 0;// not found
+ }
+
+ ud = unit_bl2ud(src);
+ if( ud == NULL )
+ {
+ ShowDebug("skill_castend_id: ud == NULL (tid=%d, id=%d)\n", tid, id);
+ return 0;// ???
+ }
+
+ sd = BL_CAST(BL_PC, src);
+ md = BL_CAST(BL_MOB, src);
+
+ if( src->prev == NULL ) {
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if(ud->skill_id != SA_CASTCANCEL && ud->skill_id != SO_SPELLFIST) {// otherwise handled in unit_skillcastcancel()
+ if( ud->skilltimer != tid ) {
+ ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid);
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if( sd && ud->skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK) )
+ {// restore original walk speed
+ ud->skilltimer = INVALID_TIMER;
+ status_calc_bl(&sd->bl, SCB_SPEED);
+ }
+
+ ud->skilltimer = INVALID_TIMER;
+ }
+
+ if (ud->skilltarget == id)
+ target = src;
+ else
+ target = map_id2bl(ud->skilltarget);
+
+ // Use a do so that you can break out of it when the skill fails.
+ do {
+ if(!target || target->prev==NULL) break;
+
+ if(src->m != target->m || status_isdead(src)) break;
+
+ switch (ud->skill_id) {
+ //These should become skill_castend_pos
+ case WE_CALLPARTNER:
+ if(sd) clif_callpartner(sd);
+ case WE_CALLPARENT:
+ case WE_CALLBABY:
+ case AM_RESURRECTHOMUN:
+ case PF_SPIDERWEB:
+ //Find a random spot to place the skill. [Skotlex]
+ inf2 = skill_get_splash(ud->skill_id, ud->skill_lv);
+ ud->skillx = target->x + inf2;
+ ud->skilly = target->y + inf2;
+ if (inf2 && !map_random_dir(target, &ud->skillx, &ud->skilly)) {
+ ud->skillx = target->x;
+ ud->skilly = target->y;
+ }
+ ud->skilltimer=tid;
+ return skill_castend_pos(tid,tick,id,data);
+ case GN_WALLOFTHORN:
+ ud->skillx = target->x;
+ ud->skilly = target->y;
+ ud->skilltimer = tid;
+ return skill_castend_pos(tid,tick,id,data);
+ }
+
+ if(ud->skill_id == RG_BACKSTAP) {
+ uint8 dir = map_calc_dir(src,target->x,target->y),t_dir = unit_getdir(target);
+ if(check_distance_bl(src, target, 0) || map_check_dir(dir,t_dir)) {
+ break;
+ }
+ }
+
+ if( ud->skill_id == PR_TURNUNDEAD )
+ {
+ struct status_data *tstatus = status_get_status_data(target);
+ if( !battle_check_undead(tstatus->race, tstatus->def_ele) )
+ break;
+ }
+
+ if( ud->skill_id == RA_WUGSTRIKE ){
+ if( !path_search(NULL,src->m,src->x,src->y,target->x,target->y,1,CELL_CHKNOREACH))
+ break;
+ }
+
+ if( ud->skill_id == PR_LEXDIVINA || ud->skill_id == MER_LEXDIVINA )
+ {
+ sc = status_get_sc(target);
+ if( battle_check_target(src,target, BCT_ENEMY) <= 0 && (!sc || !sc->data[SC_SILENCE]) )
+ { //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex]
+ clif_skill_nodamage (src, target, ud->skill_id, ud->skill_lv, 0);
+ break;
+ }
+ }
+ else
+ { // Check target validity.
+ inf = skill_get_inf(ud->skill_id);
+ inf2 = skill_get_inf2(ud->skill_id);
+
+ if(inf&INF_ATTACK_SKILL ||
+ (inf&INF_SELF_SKILL && inf2&INF2_NO_TARGET_SELF) //Combo skills
+ ) // Casted through combo.
+ inf = BCT_ENEMY; //Offensive skill.
+ else if(inf2&INF2_NO_ENEMY)
+ inf = BCT_NOENEMY;
+ else
+ inf = 0;
+
+ if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && src != target)
+ {
+ inf |=
+ (inf2&INF2_PARTY_ONLY?BCT_PARTY:0)|
+ (inf2&INF2_GUILD_ONLY?BCT_GUILD:0);
+ //Remove neutral targets (but allow enemy if skill is designed to be so)
+ inf &= ~BCT_NEUTRAL;
+ }
+
+ if( ud->skill_id >= SL_SKE && ud->skill_id <= SL_SKA && target->type == BL_MOB )
+ {
+ if( ((TBL_MOB*)target)->class_ == MOBID_EMPERIUM )
+ break;
+ }
+ else if (inf && battle_check_target(src, target, inf) <= 0){
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+
+ if(inf&BCT_ENEMY && (sc = status_get_sc(target)) &&
+ sc->data[SC_FOGWALL] &&
+ rnd() % 100 < 75) { //Fogwall makes all offensive-type targetted skills fail at 75%
+ if (sd) clif_skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+ }
+
+ //Avoid doing double checks for instant-cast skills.
+ if (tid != INVALID_TIMER && !status_check_skilluse(src, target, ud->skill_id, 1))
+ break;
+
+ if(md) {
+ md->last_thinktime=tick +MIN_MOBTHINKTIME;
+ if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0)
+ clif_emotion(src, md->db->skill[md->skill_idx].emotion);
+ }
+
+ if(src != target && battle_config.skill_add_range &&
+ !check_distance_bl(src, target, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range))
+ {
+ if (sd) {
+ clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex]
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3);
+ }
+ break;
+ }
+
+ if( sd )
+ {
+ if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) )
+ break;
+ else
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1);
+ }
+#ifdef OFFICIAL_WALKPATH
+ if( !path_search_long(NULL, src->m, src->x, src->y, target->x, target->y, CELL_CHKWALL) )
+ break;
+#endif
+ if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) )
+ break;
+
+ if (ud->state.running && ud->skill_id == TK_JUMPKICK)
+ {
+ ud->state.running = 0;
+ status_change_end(src, SC_RUN, INVALID_TIMER);
+ flag = 1;
+ }
+
+ if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH)
+ unit_stop_walking(src,1);
+
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish]
+ if (sd) { //Cooldown application
+ int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv);
+ for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses.
+ if (sd->skillcooldown[i].id == ud->skill_id){
+ cooldown += sd->skillcooldown[i].val;
+ break;
+ }
+ }
+ if(cooldown)
+ skill_blockpc_start(sd, ud->skill_id, cooldown);
+ }
+ if( battle_config.display_status_timers && sd )
+ clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0);
+ if( sd )
+ {
+ switch( ud->skill_id )
+ {
+ case GS_DESPERADO:
+ sd->canequip_tick = tick + skill_get_time(ud->skill_id, ud->skill_lv);
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ if( (sc = status_get_sc(src)) && sc->data[SC_STRIPSHIELD] )
+ {
+ const struct TimerData *timer = get_timer(sc->data[SC_STRIPSHIELD]->timer);
+ if( timer && timer->func == status_change_timer && DIFF_TICK(timer->tick,gettick()+skill_get_time(ud->skill_id, ud->skill_lv)) > 0 )
+ break;
+ }
+ sc_start2(src, SC_STRIPSHIELD, 100, 0, 1, skill_get_time(ud->skill_id, ud->skill_lv));
+ break;
+ }
+ }
+ if (skill_get_state(ud->skill_id) != ST_MOVE_ENABLE)
+ unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1);
+
+ if(battle_config.skill_log && battle_config.skill_log&src->type)
+ ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n",
+ src->type, src->id, ud->skill_id, ud->skill_lv, target->id);
+
+ map_freeblock_lock();
+
+ // SC_MAGICPOWER needs to switch states before any damage is actually dealt
+ skill_toggle_magicpower(src, ud->skill_id);
+ if( ud->skill_id != RA_CAMOUFLAGE ) // only normal attack and auto cast skills benefit from its bonuses
+ status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER);
+
+ if (skill_get_casttype(ud->skill_id) == CAST_NODAMAGE)
+ skill_castend_nodamage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag);
+ else
+ skill_castend_damage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag);
+
+ sc = status_get_sc(src);
+ if(sc && sc->count) {
+ if(sc->data[SC_SPIRIT] &&
+ sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
+ sc->data[SC_SPIRIT]->val3 == ud->skill_id &&
+ ud->skill_id != WZ_WATERBALL)
+ sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
+
+ if( sc->data[SC_DANCING] && skill_get_inf2(ud->skill_id)&INF2_SONG_DANCE && sd )
+ skill_blockpc_start(sd,BD_ADAPTATION,3000);
+ }
+
+ if( sd && ud->skill_id != SA_ABRACADABRA && ud->skill_id != WM_RANDOMIZESPELL ) // they just set the data so leave it as it is.[Inkfish]
+ sd->skillitem = sd->skillitemlv = 0;
+
+ if (ud->skilltimer == INVALID_TIMER) {
+ if(md) md->skill_idx = -1;
+ else ud->skill_id = 0; //mobs can't clear this one as it is used for skill condition 'afterskill'
+ ud->skill_lv = ud->skilltarget = 0;
+ }
+ map_freeblock_unlock();
+ return 1;
+ } while(0);
+
+ //Skill failed.
+ if (ud->skill_id == MO_EXTREMITYFIST && sd && !(sc && sc->data[SC_FOGWALL]))
+ { //When Asura fails... (except when it fails from Fog of Wall)
+ //Consume SP/spheres
+ skill_consume_requirement(sd,ud->skill_id, ud->skill_lv,1);
+ status_set_sp(src, 0, 0);
+ sc = &sd->sc;
+ if (sc->count)
+ { //End states
+ status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+#ifdef RENEWAL
+ sc_start(src, SC_EXTREMITYFIST2, 100, ud->skill_lv, skill_get_time(ud->skill_id, ud->skill_lv));
+#endif
+ }
+ if (target && target->m == src->m)
+ { //Move character to target anyway.
+ if (unit_movepos(src, src->x+3, src->y+3, 1, 1))
+ { //Display movement + animation.
+ clif_slide(src,src->x,src->y);
+ clif_skill_damage(src,target,tick,sd->battle_status.amotion,0,0,1,ud->skill_id, ud->skill_lv, 5);
+ }
+ clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ }
+
+ ud->skill_id = ud->skill_lv = ud->skilltarget = 0;
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick;
+ //You can't place a skill failed packet here because it would be
+ //sent in ALL cases, even cases where skill_check_condition fails
+ //which would lead to double 'skill failed' messages u.u [Skotlex]
+ if(sd)
+ sd->skillitem = sd->skillitemlv = 0;
+ else if(md)
+ md->skill_idx = -1;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list* src = map_id2bl(id);
+ int maxcount;
+ struct map_session_data *sd;
+ struct unit_data *ud = unit_bl2ud(src);
+ struct mob_data *md;
+
+ nullpo_ret(ud);
+
+ sd = BL_CAST(BL_PC , src);
+ md = BL_CAST(BL_MOB, src);
+
+ if( src->prev == NULL ) {
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if( ud->skilltimer != tid )
+ {
+ ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", ud->skilltimer, tid);
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if( sd && ud->skilltimer != INVALID_TIMER && ( pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK ) )
+ {// restore original walk speed
+ ud->skilltimer = INVALID_TIMER;
+ status_calc_bl(&sd->bl, SCB_SPEED);
+ }
+ ud->skilltimer = INVALID_TIMER;
+
+ do {
+ if( status_isdead(src) )
+ break;
+
+ if( !(src->type&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(ud->skill_id)&UF_NOREITERATION &&
+ skill_check_unit_range(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv)
+ )
+ {
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if( src->type&battle_config.skill_nofootset &&
+ skill_get_unit_flag(ud->skill_id)&UF_NOFOOTSET &&
+ skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv)
+ )
+ {
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if( src->type&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(ud->skill_id, ud->skill_lv)) > 0
+ ) {
+ int i;
+ for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i] && maxcount;i++) {
+ if(ud->skillunit[i]->skill_id == ud->skill_id)
+ maxcount--;
+ }
+ if( maxcount == 0 )
+ {
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+
+ if(tid != INVALID_TIMER)
+ { //Avoid double checks on instant cast skills. [Skotlex]
+ if (!status_check_skilluse(src, NULL, ud->skill_id, 1))
+ break;
+ if(battle_config.skill_add_range &&
+ !check_distance_blxy(src, ud->skillx, ud->skilly, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range)) {
+ if (sd && battle_config.skill_out_range_consume) //Consume items anyway.
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3);
+ break;
+ }
+ }
+
+ if( sd )
+ {
+ if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) )
+ break;
+ else
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1);
+ }
+
+ if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) )
+ break;
+
+ if(md) {
+ md->last_thinktime=tick +MIN_MOBTHINKTIME;
+ if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0)
+ clif_emotion(src, md->db->skill[md->skill_idx].emotion);
+ }
+
+ if(battle_config.skill_log && battle_config.skill_log&src->type)
+ ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n",
+ src->type, src->id, ud->skill_id, ud->skill_lv, ud->skillx, ud->skilly);
+
+ if (ud->walktimer != INVALID_TIMER)
+ unit_stop_walking(src,1);
+
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv);
+ if (sd) { //Cooldown application
+ int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv);
+ for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses.
+ if (sd->skillcooldown[i].id == ud->skill_id){
+ cooldown += sd->skillcooldown[i].val;
+ break;
+ }
+ }
+ if(cooldown)
+ skill_blockpc_start(sd, ud->skill_id, cooldown);
+ }
+ if( battle_config.display_status_timers && sd )
+ clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0);
+// if( sd )
+// {
+// switch( ud->skill_id )
+// {
+// case ????:
+// sd->canequip_tick = tick + ????;
+// break;
+// }
+// }
+ unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1);
+ status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER);// only normal attack and auto cast skills benefit from its bonuses
+ map_freeblock_lock();
+ skill_castend_pos2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv,tick,0);
+
+ if( sd && sd->skillitem != AL_WARP ) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish]
+ sd->skillitem = sd->skillitemlv = 0;
+
+ if (ud->skilltimer == INVALID_TIMER) {
+ if (md) md->skill_idx = -1;
+ else ud->skill_id = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill'
+ ud->skill_lv = ud->skillx = ud->skilly = 0;
+ }
+
+ map_freeblock_unlock();
+ return 1;
+ } while(0);
+
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick;
+ ud->skill_id = ud->skill_lv = 0;
+ if(sd)
+ sd->skillitem = sd->skillitemlv = 0;
+ else if(md)
+ md->skill_idx = -1;
+ return 0;
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct map_session_data* sd;
+ struct status_change* sc;
+ struct status_change_entry *sce;
+ struct skill_unit_group* sg;
+ enum sc_type type;
+ int i;
+
+ //if(skill_lv <= 0) return 0;
+ if(skill_id > 0 && !skill_lv) return 0; // celest
+
+ nullpo_ret(src);
+
+ if(status_isdead(src))
+ return 0;
+
+ sd = BL_CAST(BL_PC, src);
+
+ sc = status_get_sc(src);
+ type = status_skill2sc(skill_id);
+ sce = (sc && type != -1)?sc->data[type]:NULL;
+
+ switch (skill_id) { //Skill effect.
+ case WZ_METEOR:
+ case MO_BODYRELOCATION:
+ case CR_CULTIVATION:
+ case HW_GANBANTEIN:
+ case LG_EARTHDRIVE:
+ break; //Effect is displayed on respective switch case.
+ default:
+ if(skill_get_inf(skill_id)&INF_SELF_SKILL)
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ else
+ clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
+ }
+
+ // SC_MAGICPOWER needs to switch states before any damage is actually dealt
+ skill_toggle_magicpower(src, skill_id);
+
+ switch(skill_id)
+ {
+ case PR_BENEDICTIO:
+ skill_area_temp[1] = src->id;
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub,
+ src->m, x-i, y-i, x+i, y+i, BL_PC,
+ src, skill_id, skill_lv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ map_foreachinarea(skill_area_sub,
+ src->m, x-i, y-i, x+i, y+i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case BS_HAMMERFALL:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea (skill_area_sub,
+ src->m, x-i, y-i, x+i, y+i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|2,
+ skill_castend_nodamage_id);
+ break;
+
+ case HT_DETECTING:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea( status_change_timer_sub,
+ src->m, x-i, y-i, x+i,y+i,BL_CHAR,
+ src,NULL,SC_SIGHT,tick);
+ if(battle_config.traps_setting&1)
+ map_foreachinarea( skill_reveal_trap,
+ src->m, x-i, y-i, x+i,y+i,BL_SKILL);
+ break;
+
+ case SR_RIDEINLIGHTNING:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub, src->m, x-i, y-i, x+i, y+i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ break;
+
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ { //Does not consumes if the skill is already active. [Skotlex]
+ struct skill_unit_group *sg;
+ if ((sg= skill_locate_element_field(src)) != NULL && ( sg->skill_id == SA_VOLCANO || sg->skill_id == SA_DELUGE || sg->skill_id == SA_VIOLENTGALE ))
+ {
+ if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0)
+ {
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ return 0; // not to consume items
+ }
+ else
+ sg->limit = 0; //Disable it.
+ }
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ break;
+ }
+ case MG_SAFETYWALL:
+ case MG_FIREWALL:
+ case MG_THUNDERSTORM:
+
+ case AL_PNEUMA:
+ case WZ_ICEWALL:
+ case WZ_FIREPILLAR:
+ case WZ_QUAGMIRE:
+ case WZ_VERMILION:
+ case WZ_STORMGUST:
+ case WZ_HEAVENDRIVE:
+ case PR_SANCTUARY:
+ case PR_MAGNUS:
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ case HT_CLAYMORETRAP:
+ case AS_VENOMDUST:
+ case AM_DEMONSTRATION:
+ case PF_FOGWALL:
+ case PF_SPIDERWEB:
+ case HT_TALKIEBOX:
+ case WE_CALLPARTNER:
+ case WE_CALLPARENT:
+ case WE_CALLBABY:
+ case AC_SHOWER: //Ground-placed skill implementation.
+ case MA_SHOWER:
+ case SA_LANDPROTECTOR:
+ case BD_LULLABY:
+ case BD_RICHMANKIM:
+ case BD_ETERNALCHAOS:
+ case BD_DRUMBATTLEFIELD:
+ case BD_RINGNIBELUNGEN:
+ case BD_ROKISWEIL:
+ case BD_INTOABYSS:
+ case BD_SIEGFRIED:
+ case BA_DISSONANCE:
+ case BA_POEMBRAGI:
+ case BA_WHISTLE:
+ case BA_ASSASSINCROSS:
+ case BA_APPLEIDUN:
+ case DC_UGLYDANCE:
+ case DC_HUMMING:
+ case DC_DONTFORGETME:
+ case DC_FORTUNEKISS:
+ case DC_SERVICEFORYOU:
+ case CG_MOONLIT:
+ case GS_DESPERADO:
+ case NJ_KAENSIN:
+ case NJ_BAKUENRYU:
+ case NJ_SUITON:
+ case NJ_HYOUSYOURAKU:
+ case NJ_RAIGEKISAI:
+ case NJ_KAMAITACHI:
+#ifdef RENEWAL
+ case NJ_HUUMA:
+#endif
+ case NPC_EVILLAND:
+ case RA_ELECTRICSHOCKER:
+ case RA_CLUSTERBOMB:
+ case RA_MAGENTATRAP:
+ case RA_COBALTTRAP:
+ case RA_MAIZETRAP:
+ case RA_VERDURETRAP:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ case SC_MANHOLE:
+ case SC_DIMENSIONDOOR:
+ case SC_CHAOSPANIC:
+ case SC_MAELSTROM:
+ case WM_REVERBERATION:
+ case WM_SEVERE_RAINSTORM:
+ case WM_POEMOFNETHERWORLD:
+ case SO_PSYCHIC_WAVE:
+ case SO_VACUUM_EXTREME:
+ case GN_WALLOFTHORN:
+ case GN_THORNS_TRAP:
+ case GN_DEMONIC_FIRE:
+ case GN_HELLS_PLANT:
+ case SO_EARTHGRAVE:
+ case SO_DIAMONDDUST:
+ case SO_FIRE_INSIGNIA:
+ case SO_WATER_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ case KO_HUUMARANKA:
+ case KO_MUCHANAGE:
+ case KO_BAKURETSU:
+ case KO_ZENKAI:
+ case MH_LAVA_SLIDE:
+ case MH_VOLCANIC_ASH:
+ case MH_POISON_MIST:
+ case MH_STEINWAND:
+ case MH_XENO_SLASHER:
+ 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,skill_id,skill_lv,x,y,0);
+ break;
+ case RG_GRAFFITI: /* Graffiti [Valaris] */
+ skill_clear_unitgroup(src);
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ flag|=1;
+ break;
+ case HP_BASILICA:
+ if( sc->data[SC_BASILICA] )
+ status_change_end(src, SC_BASILICA, INVALID_TIMER); // Cancel Basilica
+ else
+ { // Create Basilica. Start SC on caster. Unit timer start SC on others.
+ skill_clear_unitgroup(src);
+ if( skill_unitsetting(src,skill_id,skill_lv,x,y,0) )
+ sc_start4(src,type,100,skill_lv,0,0,src->id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ }
+ break;
+ case CG_HERMODE:
+ skill_clear_unitgroup(src);
+ if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0)))
+ sc_start4(src,SC_DANCING,100,
+ skill_id,0,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ break;
+ case RG_CLEANER: // [Valaris]
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL);
+ break;
+
+ case SO_WARMER:
+ flag|= 8;
+ case SO_CLOUD_KILL:
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ break;
+
+ case WZ_METEOR: {
+ int area = skill_get_splash(skill_id, skill_lv);
+ short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0;
+
+ for( i = 0; i < 2 + (skill_lv>>1); i++ ) {
+ // Creates a random Cell in the Splash Area
+ tmpx = x - area + rnd()%(area * 2 + 1);
+ tmpy = y - area + rnd()%(area * 2 + 1);
+
+ if( i == 0 && path_search_long(NULL, src->m, src->x, src->y, tmpx, tmpy, CELL_CHKWALL) )
+ clif_skill_poseffect(src,skill_id,skill_lv,tmpx,tmpy,tick);
+
+ if( i > 0 )
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,(x1<<16)|y1,0);
+
+ x1 = tmpx;
+ y1 = tmpy;
+ }
+
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,-1,0);
+ }
+ break;
+
+ case AL_WARP:
+ if(sd)
+ {
+ clif_skill_warppoint(sd, skill_id, skill_lv, sd->status.save_point.map,
+ (skill_lv >= 2) ? sd->status.memo_point[0].map : 0,
+ (skill_lv >= 3) ? sd->status.memo_point[1].map : 0,
+ (skill_lv >= 4) ? sd->status.memo_point[2].map : 0
+ );
+ }
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+ return 0; // not to consume item.
+
+ case MO_BODYRELOCATION:
+ if (unit_movepos(src, x, y, 1, 1)) {
+#if PACKETVER >= 20111005
+ clif_snap(src, src->x, src->y);
+#else
+ clif_skill_poseffect(src,skill_id,skill_lv,src->x,src->y,tick);
+#endif
+ if (sd)
+ skill_blockpc_start (sd, MO_EXTREMITYFIST, 2000);
+ }
+ break;
+ case NJ_SHADOWJUMP:
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground ) { //You don't move on GVG grounds.
+ unit_movepos(src, x, y, 1, 0);
+ clif_slide(src,x,y);
+ }
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ break;
+ case AM_SPHEREMINE:
+ case AM_CANNIBALIZE:
+ {
+ int summons[5] = { 1589, 1579, 1575, 1555, 1590 };
+ //int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ int class_ = skill_id==AM_SPHEREMINE?1142:summons[skill_lv-1];
+ struct mob_data *md;
+
+ // Correct info, don't change any of this! [celest]
+ md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE);
+ if (md) {
+ md->master_id = src->id;
+ md->special_state.ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (gettick() + skill_get_time(skill_id,skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn (md); //Now it is ready for spawning.
+ }
+ }
+ break;
+
+ // Slim Pitcher [Celest]
+ case CR_SLIMPITCHER:
+ if (sd) {
+ int i = skill_lv%11 - 1;
+ int j = pc_search_inventory(sd,skill_db[skill_id].itemid[i]);
+ if( j < 0 || skill_db[skill_id].itemid[i] <= 0 || sd->inventory_data[j] == NULL || sd->status.inventory[j].amount < skill_db[skill_id].amount[i] )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ potion_flag = 1;
+ potion_hp = 0;
+ potion_sp = 0;
+ run_script(sd->inventory_data[j]->script,0,sd->bl.id,0);
+ potion_flag = 0;
+ //Apply skill bonuses
+ i = pc_checkskill(sd,CR_SLIMPITCHER)*10
+ + pc_checkskill(sd,AM_POTIONPITCHER)*10
+ + pc_checkskill(sd,AM_LEARNINGPOTION)*5
+ + pc_skillheal_bonus(sd, skill_id);
+
+ potion_hp = potion_hp * (100+i)/100;
+ potion_sp = potion_sp * (100+i)/100;
+
+ if(potion_hp > 0 || potion_sp > 0) {
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub,
+ src->m,x-i,y-i,x+i,y+i,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ }
+ } else {
+ int i = skill_lv%11 - 1;
+ struct item_data *item;
+ i = skill_db[skill_id].itemid[i];
+ item = itemdb_search(i);
+ potion_flag = 1;
+ potion_hp = 0;
+ potion_sp = 0;
+ run_script(item->script,0,src->id,0);
+ potion_flag = 0;
+ i = skill_get_max(CR_SLIMPITCHER)*10;
+
+ potion_hp = potion_hp * (100+i)/100;
+ potion_sp = potion_sp * (100+i)/100;
+
+ if(potion_hp > 0 || potion_sp > 0) {
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub,
+ src->m,x-i,y-i,x+i,y+i,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ }
+ }
+ break;
+
+ case HW_GANBANTEIN:
+ if (rnd()%100 < 80) {
+ int dummy = 1;
+ clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_cell_overlap, src->m, x-i, y-i, x+i, y+i, BL_SKILL, HW_GANBANTEIN, &dummy, src);
+ } else {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+
+ case HW_GRAVITATION:
+ if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0)))
+ sc_start4(src,type,100,skill_lv,0,BCT_SELF,sg->group_id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ break;
+
+ // Plant Cultivation [Celest]
+ case CR_CULTIVATION:
+ if (sd) {
+ if( map_count_oncell(src->m,x,y,BL_CHAR) > 0 )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
+ if (rnd()%100 < 50) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ } else {
+ TBL_MOB* md = mob_once_spawn_sub(src, src->m, x, y, "--ja--",(skill_lv < 2 ? 1084+rnd()%2 : 1078+rnd()%6),"", SZ_SMALL, AI_NONE);
+ int i;
+ if (!md) break;
+ if ((i = skill_get_time(skill_id, skill_lv)) > 0)
+ {
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (tick + i, mob_timer_delete, md->bl.id, 0);
+ }
+ mob_spawn (md);
+ }
+ }
+ break;
+
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ skill_clear_unitgroup(src);
+ if ((sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)))
+ sc_start4(src,type,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ break;
+
+ case PA_GOSPEL:
+ if (sce && sce->val4 == BCT_SELF)
+ {
+ status_change_end(src, SC_GOSPEL, INVALID_TIMER);
+ return 0;
+ }
+ else
+ {
+ sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0);
+ if (!sg) break;
+ if (sce)
+ status_change_end(src, type, INVALID_TIMER); //Was under someone else's Gospel. [Skotlex]
+ sc_start4(src,type,100,skill_lv,0,sg->group_id,BCT_SELF,skill_get_time(skill_id,skill_lv));
+ clif_skill_poseffect(src, skill_id, skill_lv, 0, 0, tick); // PA_GOSPEL music packet
+ }
+ break;
+ case NJ_TATAMIGAESHI:
+ if (skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0))
+ sc_start(src,type,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case AM_RESURRECTHOMUN: //[orn]
+ if (sd)
+ {
+ if (!merc_resurrect_homunculus(sd, 20*skill_lv, x, y))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+ break;
+
+ case RK_WINDCUTTER:
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ case NC_COLDSLOWER:
+ case NC_ARMSCANNON:
+ case RK_DRAGONBREATH:
+ i = skill_get_splash(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+
+ case SO_ARRULLO:
+ i = skill_get_splash(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ break;
+ /**
+ * Guilotine Cross
+ **/
+ case GC_POISONSMOKE:
+ if( !(sc && sc->data[SC_POISONINGWEAPON]) ) {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_GC_POISONINGWEAPON,0);
+ return 0;
+ }
+ clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
+ skill_unitsetting(src, skill_id, skill_lv, x, y, flag);
+ //status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER); // 08/31/2011 - When using poison smoke, you no longer lose the poisoning weapon effect.
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case AB_EPICLESIS:
+ if( (sg = skill_unitsetting(src, skill_id, skill_lv, 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(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+
+ case WL_EARTHSTRAIN:
+ {
+ int i, wave = skill_lv + 4, dir = map_calc_dir(src,x,y);
+ int sx = x = src->x, sy = y = src->y; // Store first caster's location to avoid glitch on unit setting
+
+ for( i = 1; i <= wave; i++ )
+ {
+ switch( dir ){
+ case 0: case 1: case 7: sy = y + i; break;
+ case 3: case 4: case 5: sy = y - i; break;
+ case 2: sx = x - i; break;
+ case 6: sx = x + i; break;
+ }
+ skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skill_id,skill_lv,dir,flag&2);
+ }
+ }
+ break;
+ /**
+ * Ranger
+ **/
+ case RA_DETONATOR:
+ i = skill_get_splash(skill_id, skill_lv);
+ 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, skill_id, skill_lv, 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,skill_id,skill_lv,src->x,src->y,0)) != NULL ) {
+ sc_start2(src,skill_id == NC_NEUTRALBARRIER ? SC_NEUTRALBARRIER_MASTER : SC_STEALTHFIELD_MASTER,100,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv));
+ if( sd ) pc_overheat(sd,1);
+ }
+ break;
+
+ case NC_SILVERSNIPER:
+ {
+ int class_ = 2042;
+ struct mob_data *md;
+
+ md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE);
+ if( md )
+ {
+ md->master_id = src->id;
+ md->special_state.ai = AI_FLORA;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn( md );
+ }
+ }
+ break;
+
+ case NC_MAGICDECOY:
+ if( sd ) clif_magicdecoy_list(sd,skill_lv,x,y);
+ break;
+
+ case SC_FEINTBOMB:
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0); // Set bomb on current Position
+ if( skill_blown(src,src,6,unit_getdir(src),0) )
+ skill_castend_nodamage_id(src,src,TF_HIDING,1,tick,0);
+ break;
+
+ case LG_OVERBRAND:
+ {
+ int width;//according to data from irowiki it actually is a square
+ for( width = 0; width < 7; width++ )
+ for( i = 0; i < 7; i++ )
+ map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, LG_OVERBRAND_BRANDISH, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id);
+ for( width = 0; width < 7; width++ )
+ for( i = 0; i < 7; i++ )
+ map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id);
+ }
+ break;
+
+ case LG_BANDING:
+ if( sc && sc->data[SC_BANDING] )
+ status_change_end(src,SC_BANDING,INVALID_TIMER);
+ else if( (sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)) != NULL ) {
+ sc_start4(src,SC_BANDING,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv));
+ if( sd ) pc_banding(sd,skill_lv);
+ }
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ break;
+
+ case LG_RAYOFGENESIS:
+ if( status_charge(src,status_get_max_hp(src)*3*skill_lv / 100,0) ) {
+ i = skill_get_splash(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ } else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ break;
+
+ case WM_DOMINION_IMPULSE:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea( skill_ative_reverberation,
+ src->m, x-i, y-i, x+i,y+i,BL_SKILL);
+ break;
+
+ case WM_GREAT_ECHO:
+ flag|=1; // Should counsume 1 item per skill usage.
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_castend_damage_id);
+ break;
+ case GN_CRAZYWEED: {
+ int area = skill_get_splash(GN_CRAZYWEED_ATK, skill_lv);
+ short x1 = 0, y1 = 0;
+
+ for( i = 0; i < 3 + (skill_lv/2); i++ ) {
+ x1 = x - area + rnd()%(area * 2 + 1);
+ y1 = y - area + rnd()%(area * 2 + 1);
+ skill_addtimerskill(src,tick+i*150,0,x1,y1,GN_CRAZYWEED_ATK,skill_lv,-1,0);
+ }
+ }
+ break;
+ case GN_FIRE_EXPANSION: {
+ int i;
+ struct unit_data *ud = unit_bl2ud(src);
+
+ if( !ud ) break;
+
+ for( i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i ++ ) {
+ if( ud->skillunit[i]->skill_id == GN_DEMONIC_FIRE &&
+ distance_xy(x, y, ud->skillunit[i]->unit->bl.x, ud->skillunit[i]->unit->bl.y) < 4 ) {
+ switch( skill_lv ) {
+ case 3:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_SMOKE_POWDER;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_SMOKE_POWDER);
+ break;
+ case 4:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_TEAR_GAS;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_TEAR_GAS);
+ break;
+ case 5:
+ map_foreachinarea(skill_area_sub, src->m,
+ ud->skillunit[i]->unit->bl.x - 3, ud->skillunit[i]->unit->bl.y - 3,
+ ud->skillunit[i]->unit->bl.x + 3, ud->skillunit[i]->unit->bl.y + 3, BL_CHAR,
+ src, CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : skill_lv, tick, flag|BCT_ENEMY|1|SD_LEVEL, skill_castend_damage_id);
+ skill_delunit(ud->skillunit[i]->unit);
+ break;
+ default:
+ ud->skillunit[i]->unit->val2 = skill_lv;
+ ud->skillunit[i]->unit->group->val2 = skill_lv;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK:
+ if( sc && sc->data[type] )
+ status_change_end(src,type,INVALID_TIMER);
+ clif_skill_nodamage(src, src ,skill_id, skill_lv,
+ sc_start2(src, type, 100, skill_id, skill_lv, skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case SC_BLOODYLUST: //set in another group so instance will move if recasted
+ flag |= 33;
+ skill_unitsetting(src, skill_id, skill_lv, x, y, 0);
+ break;
+
+ case KO_MAKIBISHI:
+ for( i = 0; i < (skill_lv+2); i++ ) {
+ x = src->x - 1 + rnd()%3;
+ y = src->y - 1 + rnd()%3;
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ }
+ break;
+
+ default:
+ ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skill_id);
+ return 1;
+ }
+
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+
+ if( sd )
+ {// ensure that the skill last-cast tick is recorded
+ sd->canskill_tick = gettick();
+
+ if( sd->state.arrow_atk && !(flag&1) )
+ {// consume arrow if this is a ground skill
+ battle_consume_ammo(sd, skill_id, skill_lv);
+ }
+
+ // perform skill requirement consumption
+ skill_consume_requirement(sd,skill_id,skill_lv,2);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char *map)
+{
+ nullpo_ret(sd);
+
+//Simplify skill_failed code.
+#define skill_failed(sd) { sd->menuskill_id = sd->menuskill_val = 0; }
+ if(skill_id != sd->menuskill_id)
+ return 0;
+
+ if( sd->bl.prev == NULL || pc_isdead(sd) ) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if( ( sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING ) || sd->sc.option&OPTION_HIDE ) {
+ skill_failed(sd);
+ return 0;
+ }
+ if(sd->sc.count && (
+ sd->sc.data[SC_SILENCE] ||
+ sd->sc.data[SC_ROKISWEIL] ||
+ sd->sc.data[SC_AUTOCOUNTER] ||
+ sd->sc.data[SC_STEELBODY] ||
+ (sd->sc.data[SC_DANCING] && skill_id < RK_ENCHANTBLADE && !pc_checkskill(sd, WM_LESSON)) ||
+ sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] ||
+ sd->sc.data[SC_BASILICA] ||
+ sd->sc.data[SC_MARIONETTE] ||
+ sd->sc.data[SC_WHITEIMPRISON] ||
+ (sd->sc.data[SC_STASIS] && skill_block_check(&sd->bl, SC_STASIS, skill_id)) ||
+ (sd->sc.data[SC_KAGEHUMI] && skill_block_check(&sd->bl, SC_KAGEHUMI, skill_id)) ||
+ sd->sc.data[SC_OBLIVIONCURSE] ||
+ sd->sc.data[SC__MANHOLE] ||
+ (sd->sc.data[SC_ASH] && rnd()%2) //50% fail chance under ASH
+ )) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ pc_stop_attack(sd);
+ pc_stop_walking(sd,0);
+
+ if(battle_config.skill_log && battle_config.skill_log&BL_PC)
+ ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_id,map);
+
+ if(strcmp(map,"cancel")==0) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ switch(skill_id)
+ {
+ case AL_TELEPORT:
+ if(strcmp(map,"Random")==0)
+ pc_randomwarp(sd,CLR_TELEPORT);
+ else if (sd->menuskill_val > 1) //Need lv2 to be able to warp here.
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+
+ case AL_WARP:
+ {
+ const struct point *p[4];
+ struct skill_unit_group *group;
+ int i, lv, wx, wy;
+ int maxcount=0;
+ int x,y;
+ unsigned short mapindex;
+
+ mapindex = mapindex_name2id((char*)map);
+ if(!mapindex) { //Given map not found?
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ skill_failed(sd);
+ return 0;
+ }
+ p[0] = &sd->status.save_point;
+ p[1] = &sd->status.memo_point[0];
+ p[2] = &sd->status.memo_point[1];
+ p[3] = &sd->status.memo_point[2];
+
+ if((maxcount = skill_get_maxcount(skill_id, sd->menuskill_val)) > 0) {
+ for(i=0;i<MAX_SKILLUNITGROUP && sd->ud.skillunit[i] && maxcount;i++) {
+ if(sd->ud.skillunit[i]->skill_id == skill_id)
+ maxcount--;
+ }
+ if(!maxcount) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ lv = sd->skillitem==skill_id?sd->skillitemlv:pc_checkskill(sd,skill_id);
+ wx = sd->menuskill_val>>16;
+ wy = sd->menuskill_val&0xffff;
+
+ if( lv <= 0 ) return 0;
+ if( lv > 4 ) lv = 4; // crash prevention
+
+ // check if the chosen map exists in the memo list
+ ARR_FIND( 0, lv, i, mapindex == p[i]->map );
+ if( i < lv ) {
+ x=p[i]->x;
+ y=p[i]->y;
+ } else {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(!skill_check_condition_castend(sd, sd->menuskill_id, lv))
+ { // This checks versus skill_id/skill_lv...
+ skill_failed(sd);
+ return 0;
+ }
+
+ skill_consume_requirement(sd,sd->menuskill_id,lv,2);
+ sd->skillitem = sd->skillitemlv = 0; // Clear data that's skipped in 'skill_castend_pos' [Inkfish]
+
+ if((group=skill_unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==NULL) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ group->val1 = (group->val1<<16)|(short)0;
+ // record the destination coordinates
+ group->val2 = (x<<16)|y;
+ group->val3 = mapindex;
+ }
+ break;
+ }
+
+ sd->menuskill_id = sd->menuskill_val = 0;
+ return 0;
+#undef skill_failed
+}
+
+/// transforms 'target' skill unit into dissonance (if conditions are met)
+static int skill_dance_overlap_sub(struct block_list* bl, va_list ap)
+{
+ struct skill_unit* target = (struct skill_unit*)bl;
+ struct skill_unit* src = va_arg(ap, struct skill_unit*);
+ int flag = va_arg(ap, int);
+
+ if (src == target)
+ return 0;
+ if (!target->group || !(target->group->state.song_dance&0x1))
+ return 0;
+ if (!(target->val2 & src->val2 & ~UF_ENSEMBLE)) //They don't match (song + dance) is valid.
+ return 0;
+
+ if (flag) //Set dissonance
+ target->val2 |= UF_ENSEMBLE; //Add ensemble to signal this unit is overlapping.
+ else //Remove dissonance
+ target->val2 &= ~UF_ENSEMBLE;
+
+ clif_skill_setunit(target); //Update look of affected cell.
+
+ return 1;
+}
+
+//Does the song/dance overlapping -> dissonance check. [Skotlex]
+//When flag is 0, this unit is about to be removed, cancel the dissonance effect
+//When 1, this unit has been positioned, so start the cancel effect.
+int skill_dance_overlap(struct skill_unit* unit, int flag)
+{
+ if (!unit || !unit->group || !(unit->group->state.song_dance&0x1))
+ return 0;
+ if (!flag && !(unit->val2&UF_ENSEMBLE))
+ return 0; //Nothing to remove, this unit is not overlapped.
+
+ if (unit->val1 != unit->group->skill_id)
+ { //Reset state
+ unit->val1 = unit->group->skill_id;
+ unit->val2 &= ~UF_ENSEMBLE;
+ }
+
+ return map_foreachincell(skill_dance_overlap_sub, unit->bl.m,unit->bl.x,unit->bl.y,BL_SKILL, unit,flag);
+}
+
+/*==========================================
+ * Converts this group information so that it is handled as a Dissonance or Ugly Dance cell.
+ * Flag: 0 - Convert, 1 - Revert.
+ *------------------------------------------*/
+static bool skill_dance_switch(struct skill_unit* unit, int flag)
+{
+ static int prevflag = 1; // by default the backup is empty
+ static struct skill_unit_group backup;
+ struct skill_unit_group* group = unit->group;
+
+ // val2&UF_ENSEMBLE is a hack to indicate dissonance
+ if ( !(group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE) )
+ return false;
+
+ if( flag == prevflag )
+ {// protection against attempts to read an empty backup / write to a full backup
+ ShowError("skill_dance_switch: Attempted to %s (skill_id=%d, skill_lv=%d, src_id=%d).\n",
+ flag ? "read an empty backup" : "write to a full backup",
+ group->skill_id, group->skill_lv, group->src_id);
+ return false;
+ }
+ prevflag = flag;
+
+ if( !flag )
+ { //Transform
+ uint16 skill_id = unit->val2&UF_SONG ? BA_DISSONANCE : DC_UGLYDANCE;
+
+ // backup
+ backup.skill_id = group->skill_id;
+ backup.skill_lv = group->skill_lv;
+ backup.unit_id = group->unit_id;
+ backup.target_flag = group->target_flag;
+ backup.bl_flag = group->bl_flag;
+ backup.interval = group->interval;
+
+ // replace
+ group->skill_id = skill_id;
+ group->skill_lv = 1;
+ group->unit_id = skill_get_unit_id(skill_id,0);
+ group->target_flag = skill_get_unit_target(skill_id);
+ group->bl_flag = skill_get_unit_bl_target(skill_id);
+ group->interval = skill_get_unit_interval(skill_id);
+ }
+ else
+ { //Restore
+ group->skill_id = backup.skill_id;
+ group->skill_lv = backup.skill_lv;
+ group->unit_id = backup.unit_id;
+ group->target_flag = backup.target_flag;
+ group->bl_flag = backup.bl_flag;
+ group->interval = backup.interval;
+ }
+
+ return true;
+}
+/**
+ * Upon Ice Wall cast it checks all nearby mobs to find any who may be blocked by the IW
+ **/
+static int skill_icewall_block(struct block_list *bl,va_list ap) {
+ struct block_list *target = NULL;
+ struct mob_data *md = ((TBL_MOB*)bl);
+
+ nullpo_ret(bl);
+ nullpo_ret(md);
+ if( !md->target_id || ( target = map_id2bl(md->target_id) ) == NULL )
+ return 0;
+
+ if( path_search_long(NULL,bl->m,bl->x,bl->y,target->x,target->y,CELL_CHKICEWALL) )
+ return 0;
+
+ if( !check_distance_bl(bl, target, status_get_range(bl) ) ) {
+ mob_unlocktarget(md,gettick());
+ mob_stop_walking(md,1);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Initializes and sets a ground skill.
+ * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
+ *------------------------------------------*/
+struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag)
+{
+ struct skill_unit_group *group;
+ int i,limit,val1=0,val2=0,val3=0;
+ int target,interval,range,unit_flag,req_item=0;
+ struct s_skill_unit_layout *layout;
+ struct map_session_data *sd;
+ struct status_data *status;
+ struct status_change *sc;
+ int active_flag=1;
+ int subunt=0;
+
+ nullpo_retr(NULL, src);
+
+ limit = skill_get_time(skill_id,skill_lv);
+ range = skill_get_unit_range(skill_id,skill_lv);
+ interval = skill_get_unit_interval(skill_id);
+ target = skill_get_unit_target(skill_id);
+ unit_flag = skill_get_unit_flag(skill_id);
+ layout = skill_get_unit_layout(skill_id,skill_lv,src,x,y);
+
+ sd = BL_CAST(BL_PC, src);
+ status = status_get_status_data(src);
+ sc = status_get_sc(src); // for traps, firewall and fogwall - celest
+
+ switch( skill_id ) {
+ case MH_STEINWAND:
+ val2 = 4 + skill_lv; //nb of attack blocked
+ break;
+ case MG_SAFETYWALL:
+ #ifdef RENEWAL
+ /**
+ * According to data provided in RE, SW life is equal to 3 times caster's health
+ **/
+ val2 = status_get_max_hp(src) * 3;
+ #else
+ val2 = skill_lv+1;
+ #endif
+ break;
+ case MG_FIREWALL:
+ if(sc && sc->data[SC_VIOLENTGALE])
+ limit = limit*3/2;
+ val2=4+skill_lv;
+ break;
+
+ case AL_WARP:
+ val1=skill_lv+6;
+ if(!(flag&1))
+ limit=2000;
+ else // previous implementation (not used anymore)
+ { //Warp Portal morphing to active mode, extract relevant data from src. [Skotlex]
+ if( src->type != BL_SKILL ) return NULL;
+ group = ((TBL_SKILL*)src)->group;
+ src = map_id2bl(group->src_id);
+ if( !src ) return NULL;
+ val2 = group->val2; //Copy the (x,y) position you warp to
+ val3 = group->val3; //as well as the mapindex to warp to.
+ }
+ break;
+ case HP_BASILICA:
+ val1 = src->id; // Store caster id.
+ break;
+
+ case PR_SANCTUARY:
+ case NPC_EVILLAND:
+ val1=(skill_lv+3)*2;
+ break;
+
+ case WZ_FIREPILLAR:
+ if((flag&1)!=0)
+ limit=1000;
+ val1=skill_lv+2;
+ break;
+ case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex]
+ case AM_DEMONSTRATION:
+ case GN_HELLS_PLANT:
+ if (map_flag_vs(src->m) && battle_config.vs_traps_bctall
+ && (src->type&battle_config.vs_traps_bctall))
+ target = BCT_ALL;
+ break;
+ case HT_SHOCKWAVE:
+ val1=skill_lv*15+10;
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_CLAYMORETRAP:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_FLASHER:
+ 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:
+ {
+ struct skill_condition req = skill_get_requirement(sd,skill_id,skill_lv);
+ ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, i, req.itemid[i] && (req.itemid[i] == ITEMID_TRAP || req.itemid[i] == ITEMID_TRAP_ALLOY));
+ if( req.itemid[i] )
+ req_item = req.itemid[i];
+ 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) )
+ target = BCT_ALL;
+ }
+ break;
+
+ case SA_LANDPROTECTOR:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ {
+ struct skill_unit_group *old_sg;
+ if ((old_sg = skill_locate_element_field(src)) != NULL)
+ { //HelloKitty confirmed that these are interchangeable,
+ //so you can change element and not consume gemstones.
+ if ((
+ old_sg->skill_id == SA_VOLCANO ||
+ old_sg->skill_id == SA_DELUGE ||
+ old_sg->skill_id == SA_VIOLENTGALE
+ ) && old_sg->limit > 0)
+ { //Use the previous limit (minus the elapsed time) [Skotlex]
+ limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick);
+ if (limit < 0) //This can happen...
+ limit = skill_get_time(skill_id,skill_lv);
+ }
+ skill_clear_group(src,1);
+ }
+ break;
+ }
+
+ case BA_DISSONANCE:
+ case DC_UGLYDANCE:
+ val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex]
+ break;
+ case BA_WHISTLE:
+ val1 = skill_lv +status->agi/10; // Flee increase
+ val2 = ((skill_lv+1)/2)+status->luk/10; // Perfect dodge increase
+ if(sd){
+ val1 += pc_checkskill(sd,BA_MUSICALLESSON);
+ val2 += pc_checkskill(sd,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_HUMMING:
+ val1 = 2*skill_lv+status->dex/10; // Hit increase
+ #ifdef RENEWAL
+ val1 *= 2;
+ #endif
+ if(sd)
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON);
+ break;
+ case BA_POEMBRAGI:
+ val1 = 3*skill_lv+status->dex/10; // Casting time reduction
+ //For some reason at level 10 the base delay reduction is 50%.
+ val2 = (skill_lv<10?3*skill_lv:50)+status->int_/5; // After-cast delay reduction
+ if(sd){
+ val1 += 2*pc_checkskill(sd,BA_MUSICALLESSON);
+ val2 += 2*pc_checkskill(sd,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_DONTFORGETME:
+ val1 = status->dex/10 + 3*skill_lv + 5; // ASPD decrease
+ val2 = status->agi/10 + 3*skill_lv + 5; // Movement speed adjustment.
+ if(sd){
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON);
+ val2 += pc_checkskill(sd,DC_DANCINGLESSON);
+ }
+ break;
+ case BA_APPLEIDUN:
+ val1 = 5+2*skill_lv+status->vit/10; // MaxHP percent increase
+ if(sd)
+ val1 += pc_checkskill(sd,BA_MUSICALLESSON);
+ break;
+ case DC_SERVICEFORYOU:
+ val1 = 15+skill_lv+(status->int_/10); // MaxSP percent increase TO-DO: this INT bonus value is guessed
+ val2 = 20+3*skill_lv+(status->int_/10); // SP cost reduction
+ if(sd){
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO This bonus value is guessed
+ val2 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO Should be half this value
+ }
+ break;
+ case BA_ASSASSINCROSS:
+ val1 = 100+(10*skill_lv)+(status->agi/10); // ASPD increase
+ if(sd)
+ val1 += 5*pc_checkskill(sd,BA_MUSICALLESSON);
+ break;
+ case DC_FORTUNEKISS:
+ val1 = 10+skill_lv+(status->luk/10); // Critical increase
+ if(sd)
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON);
+ val1*=10; //Because every 10 crit is an actual cri point.
+ break;
+ case BD_DRUMBATTLEFIELD:
+ #ifdef RENEWAL
+ val1 = (skill_lv+5)*25; //Watk increase
+ val2 = skill_lv*10; //Def increase
+ #else
+ val1 = (skill_lv+1)*25; //Watk increase
+ val2 = (skill_lv+1)*2; //Def increase
+ #endif
+ break;
+ case BD_RINGNIBELUNGEN:
+ val1 = (skill_lv+2)*25; //Watk increase
+ break;
+ case BD_RICHMANKIM:
+ val1 = 25 + 11*skill_lv; //Exp increase bonus.
+ break;
+ case BD_SIEGFRIED:
+ val1 = 55 + skill_lv*5; //Elemental Resistance
+ val2 = skill_lv*10; //Status ailment resistance
+ break;
+ case WE_CALLPARTNER:
+ if (sd) val1 = sd->status.partner_id;
+ break;
+ case WE_CALLPARENT:
+ if (sd) {
+ val1 = sd->status.father;
+ val2 = sd->status.mother;
+ }
+ break;
+ case WE_CALLBABY:
+ if (sd) val1 = sd->status.child;
+ break;
+ case NJ_KAENSIN:
+ skill_clear_group(src, 1); //Delete previous Kaensins/Suitons
+ val2 = (skill_lv+1)/2 + 4;
+ break;
+ case NJ_SUITON:
+ skill_clear_group(src, 1);
+ break;
+
+ case GS_GROUNDDRIFT:
+ {
+ int element[5]={ELE_WIND,ELE_DARK,ELE_POISON,ELE_WATER,ELE_FIRE};
+
+ val1 = status->rhw.ele;
+ if (!val1)
+ val1=element[rnd()%5];
+
+ switch (val1)
+ {
+ case ELE_FIRE:
+ subunt++;
+ case ELE_WATER:
+ subunt++;
+ case ELE_POISON:
+ subunt++;
+ case ELE_DARK:
+ subunt++;
+ case ELE_WIND:
+ break;
+ default:
+ subunt=rnd()%5;
+ break;
+ }
+
+ break;
+ }
+ case GC_POISONSMOKE:
+ if( !(sc && sc->data[SC_POISONINGWEAPON]) )
+ return NULL;
+ val2 = sc->data[SC_POISONINGWEAPON]->val2; // Type of Poison
+ val3 = sc->data[SC_POISONINGWEAPON]->val1;
+ limit = 4000 + 2000 * skill_lv;
+ break;
+ case GD_LEADERSHIP:
+ case GD_GLORYWOUNDS:
+ case GD_SOULCOLD:
+ case GD_HAWKEYES:
+ limit = 1000000;//it doesn't matter
+ break;
+ case LG_BANDING:
+ limit = -1;
+ break;
+ case WM_REVERBERATION:
+ interval = limit;
+ val2 = 1;
+ case WM_POEMOFNETHERWORLD: // Can't be placed on top of Land Protector.
+ if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) )
+ return NULL;
+ break;
+ case SO_CLOUD_KILL:
+ skill_clear_group(src, 4);
+ break;
+ case SO_WARMER:
+ skill_clear_group(src, 8);
+ break;
+ case SO_VACUUM_EXTREME:
+ range++;
+
+ break;
+ case SC_BLOODYLUST:
+ skill_clear_group(src, 32);
+ break;
+ case GN_WALLOFTHORN:
+ if( flag&1 )
+ limit = 3000;
+ val3 = (x<<16)|y;
+ break;
+ case KO_ZENKAI:
+ if( sd ){
+ ARR_FIND(1, 6, i, sd->talisman[i] > 0);
+ if( i < 5 ){
+ val1 = sd->talisman[i]; // no. of aura
+ val2 = i; // aura type
+ limit += val1 * 1000;
+ subunt = i - 1;
+ pc_del_talisman(sd, sd->talisman[i], i);
+ }
+ }
+ break;
+ }
+
+ nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skill_id,skill_lv,skill_get_unit_id(skill_id,flag&1)+subunt, limit, interval));
+ group->val1=val1;
+ group->val2=val2;
+ group->val3=val3;
+ group->target_flag=target;
+ group->bl_flag= skill_get_unit_bl_target(skill_id);
+ group->state.ammo_consume = (sd && sd->state.arrow_atk && skill_id != GS_GROUNDDRIFT); //Store if this skill needs to consume ammo.
+ group->state.song_dance = (unit_flag&(UF_DANCE|UF_SONG)?1:0)|(unit_flag&UF_ENSEMBLE?2:0); //Signals if this is a song/dance/duet
+ group->state.guildaura = ( skill_id >= GD_LEADERSHIP && skill_id <= GD_HAWKEYES )?1:0;
+ group->item_id = req_item;
+ //if tick is greater than current, do not invoke onplace function just yet. [Skotlex]
+ if (DIFF_TICK(group->tick, gettick()) > SKILLUNITTIMER_INTERVAL)
+ active_flag = 0;
+
+ if(skill_id==HT_TALKIEBOX || skill_id==RG_GRAFFITI){
+ group->valstr=(char *) aMalloc(MESSAGE_SIZE*sizeof(char));
+ if (sd)
+ safestrncpy(group->valstr, sd->message, MESSAGE_SIZE);
+ else //Eh... we have to write something here... even though mobs shouldn't use this. [Skotlex]
+ safestrncpy(group->valstr, "Boo!", MESSAGE_SIZE);
+ }
+
+ if (group->state.song_dance) {
+ if(sd){
+ sd->skill_id_dance = skill_id;
+ sd->skill_lv_dance = skill_lv;
+ }
+ if (
+ sc_start4(src, SC_DANCING, 100, skill_id, group->group_id, skill_lv,
+ (group->state.song_dance&2?BCT_SELF:0), limit+1000) &&
+ sd && group->state.song_dance&2 && skill_id != CG_HERMODE //Hermod is a encore with a warp!
+ )
+ skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1);
+ }
+
+ limit = group->limit;
+ for( i = 0; i < layout->count; i++ )
+ {
+ struct skill_unit *unit;
+ int ux = x + layout->dx[i];
+ int uy = y + layout->dy[i];
+ int val1 = skill_lv;
+ int val2 = 0;
+ int alive = 1;
+
+ if( !group->state.song_dance && !map_getcell(src->m,ux,uy,CELL_CHKREACH) )
+ continue; // don't place skill units on walls (except for songs/dances/encores)
+ if( battle_config.skill_wall_check && skill_get_unit_flag(skill_id)&UF_PATHCHECK && !path_search_long(NULL,src->m,ux,uy,x,y,CELL_CHKWALL) )
+ continue; // no path between cell and center of casting.
+
+ switch( skill_id )
+ {
+ case MG_FIREWALL:
+ case NJ_KAENSIN:
+ val2=group->val2;
+ break;
+ case WZ_ICEWALL:
+ val1 = (skill_lv <= 1) ? 500 : 200 + 200*skill_lv;
+ val2 = map_getcell(src->m, ux, uy, CELL_GETTYPE);
+ break;
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ case HT_TALKIEBOX:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_CLAYMORETRAP:
+ 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:
+ val1 = 3500;
+ break;
+ case GS_DESPERADO:
+ val1 = abs(layout->dx[i]);
+ val2 = abs(layout->dy[i]);
+ if (val1 < 2 || val2 < 2) { //Nearby cross, linear decrease with no diagonals
+ if (val2 > val1) val1 = val2;
+ if (val1) val1--;
+ val1 = 36 -12*val1;
+ } else //Diagonal edges
+ val1 = 28 -4*val1 -4*val2;
+ if (val1 < 1) val1 = 1;
+ val2 = 0;
+ break;
+ case WM_REVERBERATION:
+ val1 = 1 + skill_lv;
+ break;
+ case GN_WALLOFTHORN:
+ val1 = 1000 * skill_lv; // Need official value. [LimitLine]
+ break;
+ default:
+ if (group->state.song_dance&0x1)
+ val2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance
+ break;
+ }
+ if (skill_get_unit_flag(skill_id) & UF_RANGEDSINGLEUNIT && i == (layout->count / 2))
+ val2 |= UF_RANGEDSINGLEUNIT; // center.
+
+ if( range <= 0 )
+ map_foreachincell(skill_cell_overlap,src->m,ux,uy,BL_SKILL,skill_id, &alive, src);
+ if( !alive )
+ continue;
+
+ nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy,val1,val2));
+ unit->limit=limit;
+ unit->range=range;
+
+ if (skill_id == PF_FOGWALL && alive == 2)
+ { //Double duration of cells on top of Deluge/Suiton
+ unit->limit *= 2;
+ group->limit = unit->limit;
+ }
+
+ // execute on all targets standing on this cell
+ if (range==0 && active_flag)
+ map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
+ }
+
+ if (!group->alive_count)
+ { //No cells? Something that was blocked completely by Land Protector?
+ skill_delunitgroup(group);
+ return NULL;
+ }
+
+ //success, unit created.
+ switch( skill_id ) {
+ case WZ_ICEWALL:
+ map_foreachinrange(skill_icewall_block, src, AREA_SIZE, BL_MOB);
+ break;
+ case NJ_TATAMIGAESHI: //Store number of tiles.
+ group->val1 = group->alive_count;
+ break;
+ }
+
+ return group;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void ext_skill_unit_onplace(struct skill_unit *src, struct block_list *bl, unsigned int tick){skill_unit_onplace(src, bl, tick);}
+static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ enum sc_type type;
+ uint16 skill_id;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if(bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_ret(sg=src->group);
+ nullpo_ret(ss=map_id2bl(sg->src_id));
+
+ if( skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR )
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ sc = status_get_sc(bl);
+
+ 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;
+ skill_id = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still.
+ switch (sg->unit_id)
+ {
+ case UNT_SPIDERWEB:
+ if( sc && sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1 > 0 )
+ { // If you are fiberlocked and can't move, it will only increase your fireweakness level. [Inkfish]
+ sc->data[SC_SPIDERWEB]->val2++;
+ break;
+ }
+ else if( sc )
+ {
+ int sec = skill_get_time2(sg->skill_id,sg->skill_lv);
+ if( status_change_start(bl,type,10000,sg->skill_lv,1,sg->group_id,0,sec,8) )
+ {
+ const struct TimerData* td = sc->data[type]?get_timer(sc->data[type]->timer):NULL;
+ if( td )
+ sec = DIFF_TICK(td->tick, tick);
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+ sg->val2 = bl->id;
+ }
+ else
+ sec = 3000; //Couldn't trap it?
+ sg->limit = DIFF_TICK(tick,sg->tick)+sec;
+ }
+ break;
+ case UNT_SAFETYWALL:
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->skill_id,sg->group_id,0,sg->limit);
+ break;
+
+ case UNT_PNEUMA:
+ case UNT_CHAOSPANIC:
+ case UNT_MAELSTROM:
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit);
+ break;
+ case UNT_BLOODYLUST:
+ if (sg->src_id == bl->id)
+ break; //Does not affect the caster.
+ if (!sce) {
+ TBL_PC *sd = BL_CAST(BL_PC, bl); //prevent fullheal exploit
+ if (sd && sd->bloodylust_tick && DIFF_TICK(gettick(), sd->bloodylust_tick) < skill_get_time2(SC_BLOODYLUST, 1))
+ clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
+ sc_start4(bl, type, 100, sg->skill_lv, 1, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
+ else {
+ if (sd) sd->bloodylust_tick = gettick();
+ clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
+ sc_start4(bl, type, 100, sg->skill_lv, 0, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
+ }
+ }
+ break;
+
+ case UNT_WARP_WAITING: {
+ int working = sg->val1&0xffff;
+
+ if(bl->type==BL_PC && !working){
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if((!sd->chatID || battle_config.chat_warpportal)
+ && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y)
+ {
+ int x = sg->val2>>16;
+ int y = sg->val2&0xffff;
+ int count = sg->val1>>16;
+ unsigned short m = sg->val3;
+
+ if( --count <= 0 )
+ skill_delunitgroup(sg);
+
+ if ( map_mapindex2mapid(sg->val3) == sd->bl.m && x == sd->bl.x && y == sd->bl.y )
+ working = 1;/* we break it because officials break it, lovely stuff. */
+
+ sg->val1 = (count<<16)|working;
+
+ pc_setpos(sd,m,x,y,CLR_TELEPORT);
+ }
+ } else if(bl->type == BL_MOB && battle_config.mob_warp&2) {
+ int16 m = map_mapindex2mapid(sg->val3);
+ if (m < 0) break; //Map not available on this map-server.
+ unit_warp(bl,m,sg->val2>>16,sg->val2&0xffff,CLR_TELEPORT);
+ }
+ }
+ break;
+
+ case UNT_QUAGMIRE:
+ if( !sce && battle_check_target(&sg->unit->bl,bl,sg->target_flag) > 0 )
+ sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit);
+ break;
+
+ case UNT_VOLCANO:
+ case UNT_DELUGE:
+ case UNT_VIOLENTGALE:
+ if(!sce)
+ sc_start(bl,type,100,sg->skill_lv,sg->limit);
+ break;
+
+ case UNT_SUITON:
+ if(!sce)
+ sc_start4(bl,type,100,sg->skill_lv,
+ map_flag_vs(bl->m) || battle_check_target(&src->bl,bl,BCT_ENEMY)>0?1:0, //Send val3 =1 to reduce agi.
+ 0,0,sg->limit);
+ break;
+
+ case UNT_HERMODE:
+ if (sg->src_id!=bl->id && battle_check_target(&src->bl,bl,BCT_PARTY|BCT_GUILD) > 0)
+ status_change_clear_buffs(bl,1); //Should dispell only allies.
+ case UNT_RICHMANKIM:
+ case UNT_ETERNALCHAOS:
+ case UNT_DRUMBATTLEFIELD:
+ case UNT_RINGNIBELUNGEN:
+ case UNT_ROKISWEIL:
+ case UNT_INTOABYSS:
+ case UNT_SIEGFRIED:
+ //Needed to check when a dancer/bard leaves their ensemble area.
+ if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER))
+ return skill_id;
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
+ break;
+ case UNT_WHISTLE:
+ case UNT_ASSASSINCROSS:
+ case UNT_POEMBRAGI:
+ case UNT_APPLEIDUN:
+ case UNT_HUMMING:
+ case UNT_DONTFORGETME:
+ case UNT_FORTUNEKISS:
+ case UNT_SERVICEFORYOU:
+ if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER))
+ return 0;
+
+ if (!sc) return 0;
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
+ else if (sce->val4 == 1) {
+ //Readjust timers since the effect will not last long.
+ sce->val4 = 0;
+ delete_timer(sce->timer, status_change_timer);
+ sce->timer = add_timer(tick+sg->limit, status_change_timer, bl->id, type);
+ }
+ break;
+
+ case UNT_FOGWALL:
+ if (!sce)
+ {
+ sc_start4(bl, type, 100, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit);
+ if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, ATK_DEF, tick);
+ }
+ break;
+
+ case UNT_GRAVITATION:
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,0,BCT_ENEMY,sg->group_id,sg->limit);
+ break;
+
+// officially, icewall has no problems existing on occupied cells [ultramage]
+// case UNT_ICEWALL: //Destroy the cell. [Skotlex]
+// src->val1 = 0;
+// if(src->limit + sg->tick > tick + 700)
+// src->limit = DIFF_TICK(tick+700,sg->tick);
+// break;
+
+ case UNT_MOONLIT:
+ //Knockback out of area if affected char isn't in Moonlit effect
+ if (sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT)
+ break;
+ if (ss == bl) //Also needed to prevent infinite loop crash.
+ break;
+ skill_blown(ss,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0);
+ break;
+
+ case UNT_WALLOFTHORN:
+ if( status_get_mode(bl)&MD_BOSS )
+ break; // iRO Wiki says that this skill don't affect to Boss monsters.
+ if( map_flag_vs(bl->m) || bl->id == src->bl.id || battle_check_target(&src->bl,bl, BCT_ENEMY) == 1 )
+ skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_VOLCANIC_ASH:
+ if (!sce)
+ sc_start(bl, SC_ASH, 100, sg->skill_lv, skill_get_time(MH_VOLCANIC_ASH, sg->skill_lv));
+ break;
+
+ case UNT_GD_LEADERSHIP:
+ case UNT_GD_GLORYWOUNDS:
+ case UNT_GD_SOULCOLD:
+ case UNT_GD_HAWKEYES:
+ if ( !sce )
+ sc_start4(bl,type,100,sg->skill_lv,0,0,0,1000);
+ break;
+ }
+ return skill_id;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ TBL_PC* tsd;
+ struct status_data *tstatus;
+ struct status_change *tsc;
+ struct skill_unit_group_tickset *ts;
+ enum sc_type type;
+ uint16 skill_id;
+ int diff=0;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if (bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_ret(sg=src->group);
+ nullpo_ret(ss=map_id2bl(sg->src_id));
+ tsd = BL_CAST(BL_PC, bl);
+ tsc = status_get_sc(bl);
+
+ if ( tsc && tsc->data[SC_HOVERING] )
+ return 0; //Under hovering characters are immune to trap and ground target skills.
+
+ tstatus = status_get_status_data(bl);
+ type = status_skill2sc(sg->skill_id);
+ skill_id = sg->skill_id;
+
+ if (sg->interval == -1) {
+ switch (sg->unit_id) {
+ case UNT_ANKLESNARE: //These happen when a trap is splash-triggered by multiple targets on the same cell.
+ case UNT_FIREPILLAR_ACTIVE:
+ case UNT_ELECTRICSHOCKER:
+ case UNT_MANHOLE:
+ return 0;
+ default:
+ ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id);
+ return 0;
+ }
+ }
+
+ if ((ts = skill_unitgrouptickset_search(bl,sg,tick)))
+ { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex]
+ diff = DIFF_TICK(tick,ts->tick);
+ if (diff < 0)
+ return 0;
+ ts->tick = tick+sg->interval;
+
+ if ((skill_id==CR_GRANDCROSS || skill_id==NPC_GRANDDARKNESS) && !battle_config.gx_allhit)
+ ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,BL_CHAR)-1);
+ }
+
+ switch (sg->unit_id)
+ {
+ case UNT_FIREWALL:
+ case UNT_KAEN:
+ {
+ int count=0;
+ const int x = bl->x, y = bl->y;
+
+ if( sg->skill_id == GN_WALLOFTHORN && !map_flag_vs(bl->m) )
+ break;
+
+ //Take into account these hit more times than the timer interval can handle.
+ do
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0);
+ while(--src->val2 && x == bl->x && y == bl->y &&
+ ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl));
+
+ if (src->val2<=0)
+ skill_delunit(src);
+ }
+ break;
+
+ case UNT_SANCTUARY:
+ if( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race==RC_DEMON )
+ { //Only damage enemies with offensive Sanctuary. [Skotlex]
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 && skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0) )
+ sg->val1 -= 2; // reduce healing count if this was meant for damaging [hekate]
+ }
+ else
+ {
+ int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true);
+ struct mob_data *md = BL_CAST(BL_MOB, bl);
+#ifdef RENEWAL
+ if( md && md->class_ == MOBID_EMPERIUM )
+ break;
+#endif
+ if( md && mob_is_battleground(md) )
+ break;
+ if( tstatus->hp >= tstatus->max_hp )
+ break;
+ if( status_isimmune(bl) )
+ heal = 0;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ if( tsc && tsc->data[SC_AKAITSUKI] && heal )
+ heal = ~heal + 1;
+ status_heal(bl, heal, 0, 0);
+ if( diff >= 500 )
+ sg->val1--;
+ }
+ if( sg->val1 <= 0 )
+ skill_delunitgroup(sg);
+ break;
+
+ case UNT_EVILLAND:
+ //Will heal demon and undead element monsters, but not players.
+ if ((bl->type == BL_PC) || (!battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race!=RC_DEMON))
+ { //Damage enemies
+ if(battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ } else {
+ int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true);
+ if (tstatus->hp >= tstatus->max_hp)
+ break;
+ if (status_isimmune(bl))
+ heal = 0;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ status_heal(bl, heal, 0, 0);
+ }
+ break;
+
+ case UNT_MAGNUS:
+ if (!battle_check_undead(tstatus->race,tstatus->def_ele) && tstatus->race!=RC_DEMON)
+ break;
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_DUMMYSKILL:
+ switch (sg->skill_id)
+ {
+ case SG_SUN_WARM: //SG skills [Komurka]
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ {
+ int count = 0;
+ const int x = bl->x, y = bl->y;
+
+ //If target isn't knocked back it should hit every "interval" ms [Playtester]
+ do
+ {
+ if( bl->type == BL_PC )
+ status_zap(bl, 0, 15); // sp damage to players
+ else // mobs
+ if( status_charge(ss, 0, 2) ) // costs 2 SP per hit
+ {
+ if( !skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0) )
+ status_charge(ss, 0, 8); //costs additional 8 SP if miss
+ }
+ else
+ { //should end when out of sp.
+ sg->limit = DIFF_TICK(tick,sg->tick);
+ break;
+ }
+ } while( x == bl->x && y == bl->y &&
+ ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl) );
+ }
+ break;
+ /**
+ * The storm gust counter was dropped in renewal
+ **/
+ #ifndef RENEWAL
+ case WZ_STORMGUST: //SG counter does not reset per stormgust. IE: One hit from a SG and two hits from another will freeze you.
+ if (tsc)
+ tsc->sg_counter++; //SG hit counter.
+ if (skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc)
+ tsc->sg_counter=0; //Attack absorbed.
+ break;
+ #endif
+ case GS_DESPERADO:
+ if (rnd()%100 < src->val1)
+ skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ case GN_CRAZYWEED_ATK:
+ if( bl->type == BL_SKILL ){
+ struct skill_unit *su = (struct skill_unit *)bl;
+ if( su && !(skill_get_inf2(su->group->skill_id)&INF2_TRAP) )
+ break;
+ }
+ default:
+ skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ }
+ break;
+
+ case UNT_FIREPILLAR_WAITING:
+ skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1);
+ skill_delunit(src);
+ break;
+
+ case UNT_SKIDTRAP:
+ {
+ skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changetraplook(&src->bl, UNT_USED_TRAPS);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ }
+ break;
+
+ case UNT_ANKLESNARE:
+ case UNT_MANHOLE:
+ if( sg->val2 == 0 && tsc && (sg->unit_id == UNT_ANKLESNARE || bl->id != sg->src_id) ) {
+ int sec = skill_get_time2(sg->skill_id,sg->skill_lv);
+ if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,sec, 8) ) {
+ const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
+ if( td )
+ sec = DIFF_TICK(td->tick, tick);
+ unit_movepos(bl, src->bl.x, src->bl.y, 0, 0);
+ clif_fixpos(bl);
+ sg->val2 = bl->id;
+ } else
+ sec = 3000; //Couldn't trap it?
+ if( sg->unit_id == UNT_ANKLESNARE ) {
+ clif_skillunit_update(&src->bl);
+ /**
+ * If you're snared from a trap that was invisible this makes the trap be
+ * visible again -- being you stepped on it (w/o this the trap remains invisible and you go "WTF WHY I CANT MOVE")
+ * bugreport:3961
+ **/
+ clif_changetraplook(&src->bl, UNT_ANKLESNARE);
+ }
+ sg->limit = DIFF_TICK(tick,sg->tick)+sec;
+ sg->interval = -1;
+ src->range = 0;
+ }
+ break;
+
+ case UNT_ELECTRICSHOCKER:
+ if( bl->id != ss->id ) {
+ if( status_get_mode(bl)&MD_BOSS )
+ break;
+ if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id, sg->skill_lv), 8) ) {
+
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+
+ }
+
+ map_foreachinrange(skill_trap_splash, &src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl, tick);
+ sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again.
+ }
+ break;
+
+ case UNT_VENOMDUST:
+ if(tsc && !tsc->data[type])
+ status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0);
+ break;
+
+
+ case UNT_MAGENTATRAP:
+ case UNT_COBALTTRAP:
+ case UNT_MAIZETRAP:
+ case UNT_VERDURETRAP:
+ if( bl->type == BL_PC )// it won't work on players
+ break;
+ case UNT_FIRINGTRAP:
+ case UNT_ICEBOUNDTRAP:
+ case UNT_CLUSTERBOMB:
+ if( bl->id == ss->id )// it won't trigger on caster
+ break;
+ case UNT_LANDMINE:
+ case UNT_CLAYMORETRAP:
+ case UNT_BLASTMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_FIREPILLAR_ACTIVE:
+ 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);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500 +
+ (sg->unit_id== UNT_CLUSTERBOMB || sg->unit_id== UNT_ICEBOUNDTRAP?1000:0);// Cluster Bomb/Icebound has 1s to disappear once activated.
+ sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again.
+ break;
+
+ case UNT_TALKIEBOX:
+ if (sg->src_id == bl->id)
+ break;
+ if (sg->val2 == 0){
+ clif_talkiebox(&src->bl, sg->valstr);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changetraplook(&src->bl, UNT_USED_TRAPS);
+ sg->limit = DIFF_TICK(tick, sg->tick) + 5000;
+ sg->val2 = -1;
+ }
+ break;
+
+ case UNT_LULLABY:
+ if (ss->id == bl->id)
+ break;
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick);
+ break;
+
+ case UNT_UGLYDANCE: //Ugly Dance [Skotlex]
+ if (ss->id != bl->id)
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick);
+ break;
+
+ case UNT_DISSONANCE:
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_APPLEIDUN: //Apple of Idun [Skotlex]
+ {
+ int heal;
+#ifdef RENEWAL
+ struct mob_data *md = BL_CAST(BL_MOB, bl);
+ if( md && md->class_ == MOBID_EMPERIUM )
+ break;
+#endif
+ if( sg->src_id == bl->id && !(tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_BARDDANCER) )
+ break; // affects self only when soullinked
+ heal = skill_calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true);
+ if( tsc->data[SC_AKAITSUKI] && heal )
+ heal = ~heal + 1;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ status_heal(bl, heal, 0, 0);
+ break;
+ }
+
+ case UNT_TATAMIGAESHI:
+ case UNT_DEMONSTRATION:
+ skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_GOSPEL:
+ if (rnd()%100 > sg->skill_lv*10 || ss == bl)
+ break;
+ if (battle_check_target(ss,bl,BCT_PARTY)>0)
+ { // Support Effect only on party, not guild
+ int heal;
+ int i = rnd()%13; // Positive buff count
+ int time = skill_get_time2(sg->skill_id, sg->skill_lv); //Duration
+ switch (i)
+ {
+ case 0: // Heal 1~9999 HP
+ heal = rnd() %9999+1;
+ clif_skill_nodamage(ss,bl,AL_HEAL,heal,1);
+ status_heal(bl,heal,0,0);
+ break;
+ case 1: // End all negative status
+ status_change_clear_buffs(bl,6);
+ if (tsd) clif_gospel_info(tsd, 0x15);
+ break;
+ case 2: // Immunity to all status
+ sc_start(bl,SC_SCRESIST,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x16);
+ break;
+ case 3: // MaxHP +100%
+ sc_start(bl,SC_INCMHPRATE,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x17);
+ break;
+ case 4: // MaxSP +100%
+ sc_start(bl,SC_INCMSPRATE,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x18);
+ break;
+ case 5: // All stats +20
+ sc_start(bl,SC_INCALLSTATUS,100,20,time);
+ if (tsd) clif_gospel_info(tsd, 0x19);
+ break;
+ case 6: // Level 10 Blessing
+ sc_start(bl,SC_BLESSING,100,10,time);
+ break;
+ case 7: // Level 10 Increase AGI
+ sc_start(bl,SC_INCREASEAGI,100,10,time);
+ break;
+ case 8: // Enchant weapon with Holy element
+ sc_start(bl,SC_ASPERSIO,100,1,time);
+ if (tsd) clif_gospel_info(tsd, 0x1c);
+ break;
+ case 9: // Enchant armor with Holy element
+ sc_start(bl,SC_BENEDICTIO,100,1,time);
+ if (tsd) clif_gospel_info(tsd, 0x1d);
+ break;
+ case 10: // DEF +25%
+ sc_start(bl,SC_INCDEFRATE,100,25,time);
+ if (tsd) clif_gospel_info(tsd, 0x1e);
+ break;
+ case 11: // ATK +100%
+ sc_start(bl,SC_INCATKRATE,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x1f);
+ break;
+ case 12: // HIT/Flee +50
+ sc_start(bl,SC_INCHIT,100,50,time);
+ sc_start(bl,SC_INCFLEE,100,50,time);
+ if (tsd) clif_gospel_info(tsd, 0x20);
+ break;
+ }
+ }
+ else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ { // Offensive Effect
+ int i = rnd()%9; // Negative buff count
+ int time = skill_get_time2(sg->skill_id, sg->skill_lv);
+ switch (i)
+ {
+ case 0: // Deal 1~9999 damage
+ skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ case 1: // Curse
+ sc_start(bl,SC_CURSE,100,1,time);
+ break;
+ case 2: // Blind
+ sc_start(bl,SC_BLIND,100,1,time);
+ break;
+ case 3: // Poison
+ sc_start(bl,SC_POISON,100,1,time);
+ break;
+ case 4: // Level 10 Provoke
+ sc_start(bl,SC_PROVOKE,100,10,time);
+ break;
+ case 5: // DEF -100%
+ sc_start(bl,SC_INCDEFRATE,100,-100,time);
+ break;
+ case 6: // ATK -100%
+ sc_start(bl,SC_INCATKRATE,100,-100,time);
+ break;
+ case 7: // Flee -100%
+ sc_start(bl,SC_INCFLEERATE,100,-100,time);
+ break;
+ case 8: // Speed/ASPD -25%
+ sc_start4(bl,SC_GOSPEL,100,1,0,0,BCT_ENEMY,time);
+ break;
+ }
+ }
+ break;
+
+ case UNT_BASILICA:
+ {
+ int i = battle_check_target(&src->bl, bl, BCT_ENEMY);
+ if( i > 0 && !(status_get_mode(bl)&MD_BOSS) )
+ { // knock-back any enemy except Boss
+ skill_blown(&src->bl, bl, 2, unit_getdir(bl), 0);
+ clif_fixpos(bl);
+ }
+
+ if( sg->src_id != bl->id && i <= 0 )
+ sc_start4(bl, type, 100, 0, 0, 0, src->bl.id, sg->interval + 100);
+ }
+ 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;
+
+ case UNT_GROUNDDRIFT_WIND:
+ case UNT_GROUNDDRIFT_DARK:
+ case UNT_GROUNDDRIFT_POISON:
+ case UNT_GROUNDDRIFT_WATER:
+ case UNT_GROUNDDRIFT_FIRE:
+ map_foreachinrange(skill_trap_splash,&src->bl,
+ skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag,
+ &src->bl,tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ //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]) && rnd()%100 < 20 )
+ sc_start(bl,sg->val2,100,sg->val3,skill_get_time2(GC_POISONINGWEAPON, 1));
+ break;
+
+ case UNT_EPICLESIS:
+ if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON )
+ {
+ if( ++sg->val2 % 3 == 0 ) {
+ int hp, sp;
+ switch( sg->skill_lv )
+ {
+ 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, 2);
+ sc_start(bl, type, 100, sg->skill_lv, (sg->interval * 3) + 100);
+ }
+ // Reveal hidden players every 5 seconds.
+ if( sg->val2 % 5 == 0 ) {
+ // TODO: check if other hidden status can be removed.
+ status_change_end(bl,SC_HIDING,INVALID_TIMER);
+ status_change_end(bl,SC_CLOAKING,INVALID_TIMER);
+ }
+ }
+ /* 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;
+
+ 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)+1000;
+ sg->unit_id = UNT_USED_TRAPS;
+ 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;
+ case UNT_NETHERWORLD:
+ if( !(status_get_mode(bl)&MD_BOSS) && ss != bl && battle_check_target(&src->bl, bl, BCT_PARTY) ) {
+ if( !(tsc && tsc->data[type]) ){
+ sc_start(bl, type, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
+ sg->limit = DIFF_TICK(tick,sg->tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ }
+ }
+ break;
+ case UNT_THORNS_TRAP:
+ if( tsc ) {
+ if( !sg->val2 ) {
+ int sec = skill_get_time2(sg->skill_id, sg->skill_lv);
+ if( sc_start(bl, type, 100, sg->skill_lv, sec) ) {
+ const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
+ if( td )
+ sec = DIFF_TICK(td->tick, tick);
+ ///map_moveblock(bl, src->bl.x, src->bl.y, tick); // in official server it doesn't behave like this. [malufett]
+ clif_fixpos(bl);
+ sg->val2 = bl->id;
+ } else
+ sec = 3000; // Couldn't trap it?
+ sg->limit = DIFF_TICK(tick, sg->tick) + sec;
+ } else if( tsc->data[SC_THORNSTRAP] && bl->id == sg->val2 )
+ skill_attack(skill_get_type(GN_THORNS_TRAP), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION);
+ }
+ break;
+
+ case UNT_DEMONIC_FIRE: {
+ TBL_PC* sd = BL_CAST(BL_PC, ss);
+ switch( sg->val2 ) {
+ case 1:
+ case 2:
+ default:
+ sc_start(bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv,
+ skill_get_time2(sg->skill_id, sg->skill_lv));
+ skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl,
+ sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0);
+ break;
+ case 3:
+ skill_attack(skill_get_type(CR_ACIDDEMONSTRATION), ss, &src->bl, bl,
+ CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : sg->skill_lv, tick, 0);
+ break;
+
+ }
+ }
+ break;
+
+ case UNT_FIRE_EXPANSION_SMOKE_POWDER:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_SMOKE_POWDER), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_FIRE_EXPANSION_TEAR_GAS:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_TEAR_GAS), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_HELLS_PLANT:
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 )
+ skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0);
+ if( ss != bl) //The caster is the only one who can step on the Plants, without destroying them
+ sg->limit = DIFF_TICK(tick, sg->tick) + 100;
+ break;
+
+ case UNT_CLOUD_KILL:
+ if(tsc && !tsc->data[type])
+ status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8);
+ skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_WARMER:
+ if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) {
+ int hp = 125 * sg->skill_lv; // Officially is 125 * skill_lv.
+ struct status_change *ssc = status_get_sc(ss);
+ if( ssc && ssc->data[SC_HEATER_OPTION] )
+ hp += hp * ssc->data[SC_HEATER_OPTION]->val3 / 100;
+ if( tstatus->hp != tstatus->max_hp )
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0);
+ if( tsc && tsc->data[SC_AKAITSUKI] && hp )
+ hp = ~hp + 1;
+ status_heal(bl, hp, 0, 0);
+ sc_start(bl, SC_WARMER, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
+ }
+ break;
+
+ case UNT_FIRE_INSIGNIA:
+ case UNT_WATER_INSIGNIA:
+ case UNT_WIND_INSIGNIA:
+ case UNT_EARTH_INSIGNIA:
+ case UNT_ZEPHYR:
+ sc_start(bl,type, 100, sg->skill_lv, sg->interval);
+ if (sg->unit_id != UNT_ZEPHYR && !battle_check_undead(tstatus->race, tstatus->def_ele)) {
+ int hp = tstatus->max_hp / 100; //+1% each 5s
+ if ((sg->val3) % 5) { //each 5s
+ if (tstatus->def_ele == skill_get_ele(sg->skill_id,sg->skill_lv)){
+ status_heal(bl, hp, 0, 2);
+ } else if((sg->unit_id == UNT_FIRE_INSIGNIA && tstatus->def_ele == ELE_EARTH)
+ ||(sg->unit_id == UNT_WATER_INSIGNIA && tstatus->def_ele == ELE_FIRE)
+ ||(sg->unit_id == UNT_WIND_INSIGNIA && tstatus->def_ele == ELE_WATER)
+ ||(sg->unit_id == UNT_EARTH_INSIGNIA && tstatus->def_ele == ELE_WIND)
+ ){
+ status_heal(bl, -hp, 0, 0);
+ }
+ }
+ sg->val3++; //timer
+ if (sg->val3 > 5) sg->val3 = 0;
+ }
+ break;
+
+ case UNT_VACUUM_EXTREME:
+ {// TODO: official behavior in gvg area. [malufett]
+ int sec = sg->limit - DIFF_TICK(tick, sg->tick);
+ int range = skill_get_unit_range(sg->skill_id, sg->skill_lv);
+
+ if( tsc && !tsc->data[type] &&
+ distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range)// don't consider outer bounderies
+ sc_start(bl, type, 100, sg->skill_lv, sec);
+
+ if( unit_is_walking(bl) && // wait until target stop walking
+ ( tsc && tsc->data[type] && tsc->data[type]->val4 >= tsc->data[type]->val3-range ))
+ break;
+
+ if( tsc && ( !tsc->data[type] || (tsc->data[type] && tsc->data[type]->val4 < 1 ) ) )
+ break;
+
+ if( unit_is_walking(bl) &&
+ distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) > range )// going outside of boundaries? then force it to stop
+ unit_stop_walking(bl,1);
+
+ if( !unit_is_walking(bl) &&
+ distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range && // only snap if the target is inside the range or
+ src->bl.x != bl->x && src->bl.y != bl->y){// diagonal position parallel to VE's center
+ unit_movepos(bl, src->bl.x, src->bl.y, 0, 0);
+ clif_fixpos(bl);
+ }
+ }
+ break;
+
+ case UNT_FIRE_MANTLE:
+ if( battle_check_target(&src->bl, bl, BCT_ENEMY) )
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_ZENKAI_WATER:
+ case UNT_ZENKAI_LAND:
+ case UNT_ZENKAI_FIRE:
+ case UNT_ZENKAI_WIND:
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 ){
+ switch( sg->unit_id ){
+ case UNT_ZENKAI_WATER:
+ sc_start(bl, SC_CRYSTALIZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_FREEZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_FREEZING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_ZENKAI_LAND:
+ sc_start(bl, SC_STONE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_POISON, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_ZENKAI_FIRE:
+ sc_start(bl, SC_BURNING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_ZENKAI_WIND:
+ sc_start(bl, SC_SILENCE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_SLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_DEEPSLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ }
+ }else
+ sc_start2(bl,type,100,sg->val1,sg->val2,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+
+ case UNT_MAKIBISHI:
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ sg->limit = DIFF_TICK(tick, sg->tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ break;
+
+ case UNT_LAVA_SLIDE:
+ skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ if(++sg->val1 > 4) //after 5 stop hit and destroy me
+ sg->limit = DIFF_TICK(tick, sg->tick);
+ break;
+ case UNT_POISON_MIST:
+ skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ status_change_start(bl, SC_BLIND, rnd() % 100 > sg->skill_lv * 10, sg->skill_lv, sg->skill_id, 0, 0, skill_get_time2(sg->skill_id, sg->skill_lv), 2|8);
+ break;
+ }
+
+ if (bl->type == BL_MOB && ss != bl)
+ mobskill_event((TBL_MOB*)bl, ss, tick, MSC_SKILLUSED|(skill_id<<16));
+
+ return skill_id;
+}
+/*==========================================
+ * Triggered when a char steps out of a skill cell
+ *------------------------------------------*/
+int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ enum sc_type type;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+ nullpo_ret(sg=src->group);
+ sc = status_get_sc(bl);
+ type = status_skill2sc(sg->skill_id);
+ sce = (sc && type != -1)?sc->data[type]:NULL;
+
+ if( bl->prev==NULL ||
+ (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB) ) //Need to delete the trap if the source died.
+ return 0;
+
+ switch(sg->unit_id){
+ case UNT_SAFETYWALL:
+ case UNT_PNEUMA:
+ case UNT_EPICLESIS://Arch Bishop
+ case UNT_NEUTRALBARRIER:
+ case UNT_STEALTHFIELD:
+ if (sce)
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+
+ case UNT_BASILICA:
+ 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);
+ break;
+
+ case UNT_SPIDERWEB:
+ {
+ struct block_list *target = map_id2bl(sg->val2);
+ if (target && target==bl)
+ {
+ if (sce && sce->val3 == sg->group_id)
+ status_change_end(bl, type, INVALID_TIMER);
+ sg->limit = DIFF_TICK(tick,sg->tick)+1000;
+ }
+ break;
+ }
+ }
+ return sg->skill_id;
+}
+
+/*==========================================
+ * Triggered when a char steps out of a skill group (entirely) [Skotlex]
+ *------------------------------------------*/
+static int skill_unit_onleft (uint16 skill_id, struct block_list *bl, unsigned int tick)
+{
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ enum sc_type type;
+
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL;
+
+ type = status_skill2sc(skill_id);
+ sce = (sc && type != -1)?sc->data[type]:NULL;
+
+ switch (skill_id)
+ {
+ case WZ_QUAGMIRE:
+ if (bl->type==BL_MOB)
+ break;
+ if (sce)
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+
+ case BD_LULLABY:
+ case BD_RICHMANKIM:
+ case BD_ETERNALCHAOS:
+ case BD_DRUMBATTLEFIELD:
+ case BD_RINGNIBELUNGEN:
+ case BD_ROKISWEIL:
+ case BD_INTOABYSS:
+ case BD_SIEGFRIED:
+ if(sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == skill_id)
+ { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex]
+ //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance.
+ //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner,
+ //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble
+ //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel
+ //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it.
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ }
+ case MH_STEINWAND:
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ case CG_HERMODE:
+ case HW_GRAVITATION:
+ case NJ_SUITON:
+ case SC_MAELSTROM:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
+ case SO_FIRE_INSIGNIA:
+ case SO_WATER_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ if (sce)
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+ case SC_BLOODYLUST:
+ if (sce) {
+ status_change_end(bl, type, INVALID_TIMER);
+ status_set_sp(bl, 0, 0); //set sp to 0 when quitting zone
+ }
+ break;
+
+ case BA_POEMBRAGI:
+ case BA_WHISTLE:
+ case BA_ASSASSINCROSS:
+ case BA_APPLEIDUN:
+ case DC_HUMMING:
+ case DC_DONTFORGETME:
+ case DC_FORTUNEKISS:
+ case DC_SERVICEFORYOU:
+ if (sce)
+ {
+ delete_timer(sce->timer, status_change_timer);
+ //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas...
+ //not possible on our current implementation.
+ sce->val4 = 1; //Store the fact that this is a "reduced" duration effect.
+ sce->timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type);
+ }
+ break;
+ case PF_FOGWALL:
+ if (sce)
+ {
+ status_change_end(bl, type, INVALID_TIMER);
+ if ((sce=sc->data[SC_BLIND]))
+ {
+ if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex]
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ else {
+ delete_timer(sce->timer, status_change_timer);
+ sce->timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND);
+ }
+ }
+ }
+ break;
+ case GD_LEADERSHIP:
+ case GD_GLORYWOUNDS:
+ case GD_SOULCOLD:
+ case GD_HAWKEYES:
+ if( !(sce && sce->val4) )
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+ }
+
+ return skill_id;
+}
+
+/*==========================================
+ * Invoked when a unit cell has been placed/removed/deleted.
+ * flag values:
+ * flag&1: Invoke onplace function (otherwise invoke onout)
+ * flag&4: Invoke a onleft call (the unit might be scheduled for deletion)
+ *------------------------------------------*/
+static int skill_unit_effect (struct block_list* bl, va_list ap)
+{
+ struct skill_unit* unit = va_arg(ap,struct skill_unit*);
+ struct skill_unit_group* group = unit->group;
+ unsigned int tick = va_arg(ap,unsigned int);
+ unsigned int flag = va_arg(ap,unsigned int);
+ uint16 skill_id;
+ bool dissonance;
+
+ if( (!unit->alive && !(flag&4)) || bl->prev == NULL )
+ return 0;
+
+ nullpo_ret(group);
+
+ dissonance = skill_dance_switch(unit, 0);
+
+ //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
+ skill_id = group->skill_id;
+ //Target-type check.
+ if( !(group->bl_flag&bl->type && battle_check_target(&unit->bl,bl,group->target_flag)>0) && (flag&4) ) {
+ if( group->state.song_dance&0x1 || (group->src_id == bl->id && group->state.song_dance&0x2) )
+ skill_unit_onleft(skill_id, bl, tick);//Ensemble check to terminate it.
+ } else {
+ if( flag&1 )
+ skill_unit_onplace(unit,bl,tick);
+ else
+ skill_unit_onout(unit,bl,tick);
+
+ if( flag&4 )
+ skill_unit_onleft(skill_id, bl, tick);
+ }
+
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_ondamaged (struct skill_unit *src, struct block_list *bl, int damage, unsigned int tick)
+{
+ struct skill_unit_group *sg;
+
+ nullpo_ret(src);
+ nullpo_ret(sg=src->group);
+
+ switch( sg->unit_id ) {
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_CLAYMORETRAP:
+ case UNT_FREEZINGTRAP:
+ case UNT_TALKIEBOX:
+ case UNT_ANKLESNARE:
+ case UNT_ICEWALL:
+ case UNT_REVERBERATION:
+ case UNT_WALLOFTHORN:
+ src->val1-=damage;
+ break;
+ default:
+ damage = 0;
+ break;
+ }
+ return damage;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_check_condition_char_sub (struct block_list *bl, va_list ap)
+{
+ int *c, skill_id;
+ struct block_list *src;
+ struct map_session_data *sd;
+ struct map_session_data *tsd;
+ int *p_sd; //Contains the list of characters found.
+
+ nullpo_ret(bl);
+ nullpo_ret(tsd=(struct map_session_data*)bl);
+ nullpo_ret(src=va_arg(ap,struct block_list *));
+ nullpo_ret(sd=(struct map_session_data*)src);
+
+ c=va_arg(ap,int *);
+ p_sd = va_arg(ap, int *);
+ skill_id = va_arg(ap,int);
+
+ if ( ((skill_id != PR_BENEDICTIO && *c >=1) || *c >=2) && !(skill_get_inf2(skill_id)&INF2_CHORUS_SKILL) )
+ return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex]
+
+ if (bl == src)
+ return 0;
+
+ if(pc_isdead(tsd))
+ return 0;
+
+ if (tsd->sc.data[SC_SILENCE] || ( tsd->sc.opt1 && tsd->sc.opt1 != OPT1_BURNING ))
+ return 0;
+
+ if( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL ) {
+ if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_MINSTRELWANDERER )
+ p_sd[(*c)++] = tsd->bl.id;
+ return 1;
+ } else {
+
+ switch(skill_id) {
+ case PR_BENEDICTIO: {
+ uint8 dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y);
+ dir = (unit_getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing.
+ if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest.
+ && sd->status.sp >= 10)
+ p_sd[(*c)++]=tsd->bl.id;
+ return 1;
+ }
+ case AB_ADORAMUS:
+ // 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( ( sd->class_&MAPID_THIRDMASK ) == MAPID_WARLOCK )
+ p_sd[(*c)++] = tsd->bl.id;
+ return 1;
+ case LG_RAYOFGENESIS:
+ if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD &&
+ tsd->sc.data[SC_BANDING] )
+ p_sd[(*c)++] = tsd->bl.id;
+ return 1;
+ default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex]
+ {
+ uint16 skill_lv;
+ if(pc_issit(tsd) || !unit_can_move(&tsd->bl))
+ return 0;
+ if (sd->status.sex != tsd->status.sex &&
+ (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER &&
+ (skill_lv = pc_checkskill(tsd, skill_id)) > 0 &&
+ (tsd->weapontype1==W_MUSICAL || tsd->weapontype1==W_WHIP) &&
+ sd->status.party_id && tsd->status.party_id &&
+ sd->status.party_id == tsd->status.party_id &&
+ !tsd->sc.data[SC_DANCING])
+ {
+ p_sd[(*c)++]=tsd->bl.id;
+ return skill_lv;
+ } else {
+ return 0;
+ }
+ }
+ break;
+ }
+
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks and stores partners for ensemble skills [Skotlex]
+ *------------------------------------------*/
+int skill_check_pc_partner (struct map_session_data *sd, uint16 skill_id, short* skill_lv, int range, int cast_flag)
+{
+ static int c=0;
+ static int p_sd[2] = { 0, 0 };
+ int i;
+ bool is_chorus = ( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL );
+
+ if (!battle_config.player_skill_partner_check || pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL))
+ return is_chorus ? MAX_PARTY : 99; //As if there were infinite partners.
+
+ if (cast_flag) { //Execute the skill on the partners.
+ struct map_session_data* tsd;
+ switch (skill_id) {
+ case PR_BENEDICTIO:
+ for (i = 0; i < c; i++) {
+ if ((tsd = map_id2sd(p_sd[i])) != NULL)
+ status_charge(&tsd->bl, 0, 10);
+ }
+ return c;
+ case AB_ADORAMUS:
+ if( c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL ) {
+ i = 2 * (*skill_lv);
+ status_charge(&tsd->bl, 0, i);
+ }
+ break;
+ case WM_GREAT_ECHO:
+ for( i = 0; i < c; i++ ) {
+ if( (tsd = map_id2sd(p_sd[i])) != NULL )
+ status_zap(&tsd->bl,0,skill_get_sp(skill_id,*skill_lv)/c);
+ }
+ break;
+ default: //Warning: Assuming Ensemble skills here (for speed)
+ if( is_chorus )
+ break;//Chorus skills are not to be parsed as ensambles
+ if (c > 0 && sd->sc.data[SC_DANCING] && (tsd = map_id2sd(p_sd[0])) != NULL) {
+ sd->sc.data[SC_DANCING]->val4 = tsd->bl.id;
+ sc_start4(&tsd->bl,SC_DANCING,100,skill_id,sd->sc.data[SC_DANCING]->val2,*skill_lv,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000);
+ clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1);
+ tsd->skill_id_dance = skill_id;
+ tsd->skill_lv_dance = *skill_lv;
+ }
+ return c;
+ }
+ }
+
+ //Else: new search for partners.
+ c = 0;
+ memset (p_sd, 0, sizeof(p_sd));
+ if( is_chorus )
+ i = party_foreachsamemap(skill_check_condition_char_sub,sd,AREA_SIZE,&sd->bl, &c, &p_sd, skill_id, *skill_lv);
+ else
+ 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 && skill_id != WL_COMET ) //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;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap)
+{
+ int *c,src_id,mob_class,skill;
+ struct mob_data *md;
+
+ md=(struct mob_data*)bl;
+ src_id=va_arg(ap,int);
+ mob_class=va_arg(ap,int);
+ skill=va_arg(ap,int);
+ c=va_arg(ap,int *);
+
+ if( md->master_id != src_id || md->special_state.ai != (unsigned)(skill == AM_SPHEREMINE?2:skill == KO_ZANZOU?4:3) )
+ return 0; //Non alchemist summoned mobs have nothing to do here.
+
+ if(md->class_==mob_class)
+ (*c)++;
+
+ return 1;
+}
+
+/*==========================================
+ * Determines if a given skill should be made to consume ammo
+ * when used by the player. [Skotlex]
+ *------------------------------------------*/
+int skill_isammotype (struct map_session_data *sd, int skill)
+{
+ return (
+ battle_config.arrow_decrement==2 &&
+ (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) &&
+ skill != HT_PHANTASMIC &&
+ skill_get_type(skill) == BF_WEAPON &&
+ !(skill_get_nk(skill)&NK_NO_DAMAGE) &&
+ !skill_get_spiritball(skill,1) //Assume spirit spheres are used as ammo instead.
+ );
+}
+
+int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) {
+ struct status_data *status;
+ struct status_change *sc;
+ struct skill_condition require;
+ int i;
+
+ nullpo_ret(sd);
+
+ if (sd->chatID) return 0;
+
+ if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id )
+ { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
+ sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check.
+ sd->spiritball_old = sd->spiritball; //Need to do Spiritball check.
+ return 1;
+ }
+
+ switch( sd->menuskill_id ) {
+ case AM_PHARMACY:
+ switch( skill_id ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill_id )
+ return 0;
+ break;
+ }
+ status = &sd->battle_status;
+ sc = &sd->sc;
+ if( !sc->count )
+ sc = NULL;
+
+ if( sd->skillitem == skill_id )
+ {
+ if( sd->state.abra_flag ) // Hocus-Pocus was used. [Inkfish]
+ sd->state.abra_flag = 0;
+ else
+ { // When a target was selected, consume items that were skipped in pc_use_item [Skotlex]
+ if( (i = sd->itemindex) == -1 ||
+ sd->status.inventory[i].nameid != sd->itemid ||
+ sd->inventory_data[i] == NULL ||
+ !sd->inventory_data[i]->flag.delay_consume ||
+ sd->status.inventory[i].amount < 1
+ )
+ { //Something went wrong, item exploit?
+ sd->itemid = sd->itemindex = -1;
+ return 0;
+ }
+ //Consume
+ sd->itemid = sd->itemindex = -1;
+ if( skill_id == WZ_EARTHSPIKE && sc && sc->data[SC_EARTHSCROLL] && rnd()%100 > sc->data[SC_EARTHSCROLL]->val2 ) // [marquis007]
+ ; //Do not consume item.
+ else if( sd->status.inventory[i].expire_time == 0 )
+ pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); // Rental usable items are not consumed until expiration
+ }
+ return 1;
+ }
+
+ if( pc_is90overweight(sd) )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0);
+ return 0;
+ }
+
+ if( sc && ( sc->data[SC__SHADOWFORM] || sc->data[SC__IGNORANCE] ) )
+ return 0;
+
+ switch( skill_id ) { // Turn off check.
+ case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD:
+ case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL:
+ case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN:
+ case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case RA_WUGDASH: case KO_YAMIKUMO:
+ if( sc && sc->data[status_skill2sc(skill_id)] )
+ return 1;
+ }
+
+ // Check the skills that can be used while mounted on a warg
+ if( pc_isridingwug(sd) ) {
+ switch( skill_id ) {
+ case HT_SKIDTRAP: case HT_LANDMINE: case HT_ANKLESNARE: case HT_SHOCKWAVE:
+ case HT_SANDMAN: case HT_FLASHER: case HT_FREEZINGTRAP: case HT_BLASTMINE:
+ case HT_CLAYMORETRAP: case HT_SPRINGTRAP: case RA_DETONATOR: case RA_CLUSTERBOMB:
+ case HT_TALKIEBOX: case RA_FIRINGTRAP: case RA_ICEBOUNDTRAP:
+ case RA_WUGDASH: case RA_WUGRIDER: case RA_WUGSTRIKE:
+ break;
+ default: // in official there is no message.
+ return 0;
+ }
+
+ }
+ if( pc_ismadogear(sd) ) {
+ switch( skill_id ) { //None Mado skills are unusable when Mado is equipped. [Jobbie]
+ case BS_REPAIRWEAPON: case WS_MELTDOWN:
+ case BS_HAMMERFALL: case WS_CARTBOOST:
+ case BS_ADRENALINE: case WS_WEAPONREFINE:
+ case BS_WEAPONPERFECT: case WS_CARTTERMINATION:
+ case BS_OVERTHRUST: case WS_OVERTHRUSTMAX:
+ case BS_MAXIMIZE: case NC_AXEBOOMERANG:
+ case BS_ADRENALINE2: case NC_POWERSWING:
+ case BS_UNFAIRLYTRICK: case NC_AXETORNADO:
+ case BS_GREED:
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ default: //Only Mechanic exlcusive skill can be used.
+ break;
+ }
+ }
+ if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL )
+ return 0;
+
+ require = skill_get_requirement(sd,skill_id,skill_lv);
+
+ //Can only update state when weapon/arrow info is checked.
+ sd->state.arrow_atk = require.ammo?1:0;
+
+ // perform skill-specific checks (and actions)
+ switch( skill_id ) {
+ case SO_SPELLFIST:
+ if(sd->skill_id_old != MG_FIREBOLT && sd->skill_id_old != MG_COLDBOLT && sd->skill_id_old != MG_LIGHTNINGBOLT){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case SA_CASTCANCEL:
+ if(sd->ud.skilltimer == INVALID_TIMER) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case AL_WARP:
+ if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
+ char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_WARP));
+ clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel."
+ return 0;
+ }
+ break;
+ case MO_CALLSPIRITS:
+ if(sc && sc->data[SC_RAISINGDRAGON])
+ skill_lv += sc->data[SC_RAISINGDRAGON]->val1;
+ if(sd->spiritball >= skill_lv) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case MO_FINGEROFFENSIVE:
+ case GS_FLING:
+ case SR_RAMPAGEBLASTER:
+ case SR_RIDEINLIGHTNING:
+ if( sd->spiritball > 0 && sd->spiritball < require.spiritball )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else
+ sd->spiritball_old = require.spiritball;
+ break;
+ case MO_CHAINCOMBO:
+ if(!sc)
+ return 0;
+ if(sc->data[SC_BLADESTOP])
+ break;
+ if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK)
+ break;
+ return 0;
+ case MO_COMBOFINISH:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO))
+ return 0;
+ break;
+ case CH_TIGERFIST:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH))
+ return 0;
+ break;
+ case CH_CHAINCRUSH:
+ if(!(sc && sc->data[SC_COMBO]))
+ return 0;
+ if(sc->data[SC_COMBO]->val1 != MO_COMBOFINISH && sc->data[SC_COMBO]->val1 != CH_TIGERFIST)
+ return 0;
+ break;
+ case MO_EXTREMITYFIST:
+ // if(sc && sc->data[SC_EXTREMITYFIST]) //To disable Asura during the 5 min skill block uncomment this...
+ // return 0;
+ if( sc && (sc->data[SC_BLADESTOP] || sc->data[SC_CURSEDCIRCLE_ATKER]) )
+ break;
+ if( sc && sc->data[SC_COMBO] )
+ {
+ switch(sc->data[SC_COMBO]->val1) {
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ break;
+ default:
+ return 0;
+ }
+ }
+ else if( !unit_can_move(&sd->bl) )
+ { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex]
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_MISSION:
+ if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON )
+ {// Cannot be used by Non-Taekwon classes
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_READYCOUNTER:
+ case TK_READYDOWN:
+ case TK_READYSTORM:
+ case TK_READYTURN:
+ case TK_JUMPKICK:
+ if( (sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER )
+ {// Soul Linkers cannot use this skill
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
+ return 0; //Anti-Soul Linker check in case you job-changed with Stances active.
+ if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK)
+ return 0; //Combo needs to be ready
+
+ if (sc->data[SC_COMBO]->val3) { //Kick chain
+ //Do not repeat a kick.
+ if (sc->data[SC_COMBO]->val3 != skill_id)
+ break;
+ status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER);
+ return 0;
+ }
+ if(sc->data[SC_COMBO]->val1 != skill_id && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait.
+ unit_cancel_combo(&sd->bl);
+ return 0;
+ }
+ break; //Combo ready.
+ case BD_ADAPTATION:
+ {
+ int time;
+ if(!(sc && sc->data[SC_DANCING]))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ time = 1000*(sc->data[SC_DANCING]->val3>>16);
+ if (skill_get_time(
+ (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID
+ (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV
+ - time < skill_get_time2(skill_id,skill_lv))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ break;
+
+ case PR_BENEDICTIO:
+ if (skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 0) < 2)
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case SL_SMA:
+ if(!(sc && sc->data[SC_SMA]))
+ return 0;
+ break;
+
+ case HT_POWER:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id))
+ return 0;
+ break;
+
+ case CG_HERMODE:
+ if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill_id, skill_lv)))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex]
+ {
+ int i,x,y,range = skill_get_splash(skill_id, skill_lv)+1;
+ int size = range*2+1;
+ for (i=0;i<size*size;i++) {
+ x = sd->bl.x+(i%size-range);
+ y = sd->bl.y+(i/size-range);
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case PR_REDEMPTIO:
+ {
+ int exp;
+ if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) ||
+ ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); //Not enough exp.
+ return 0;
+ }
+ break;
+ }
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ if (!party_skill_check(sd, sd->status.party_id, skill_id, skill_lv))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill_id-SG_SUN_WARM;
+ if (sd->bl.m == sd->feel_map[i].m)
+ break;
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ break;
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill_id-SG_SUN_COMFORT;
+ if (sd->bl.m == sd->feel_map[i].m &&
+ (battle_config.allow_skill_without_day || sg_info[i].day_func()))
+ break;
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ case SG_FUSION:
+ if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR)
+ break;
+ //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex]
+ //Only invoke on skill begin cast (instant cast skill). [Kevin]
+ if( require.sp > 0 )
+ {
+ if (status->sp < (unsigned int)require.sp)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0);
+ else
+ status_zap(&sd->bl, 0, require.sp);
+ }
+ return 0;
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ if (!map_flag_gvg2(sd->bl.m)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case GD_EMERGENCYCALL:
+ // other checks were already done in skillnotok()
+ if (!sd->status.guild_id || !sd->state.gmaster_flag)
+ return 0;
+ break;
+
+ case GS_GLITTERING:
+ if(sd->spiritball >= 10) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ISSEN:
+#ifdef RENEWAL
+ if (status->hp < (status->hp/100)) {
+#else
+ if (status->hp < 2) {
+#endif
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case NJ_BUNSINJYUTSU:
+ if (!(sc && sc->data[SC_NEN])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ZENYNAGE:
+ case KO_MUCHANAGE:
+ if(sd->status.zeny < require.zeny) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0);
+ return 0;
+ }
+ break;
+ case PF_HPCONVERSION:
+ if (status->sp == status->max_sp)
+ return 0; //Unusable when at full SP.
+ break;
+ case AM_CALLHOMUN: //Can't summon if a hom is already out
+ if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80%
+ if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case AB_ANCILLA:
+ {
+ 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_id, USESKILL_FAIL_ANCILLA_NUMOVER, 0);
+ return 0;
+ }
+ }
+ break;
+ /**
+ * 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_id,USESKILL_FAIL_LEVEL,0);
+ // return 0;
+ // }
+ // break;
+
+ case AB_ADORAMUS:
+ /**
+ * Warlock
+ **/
+ case WL_COMET:
+ if( skill_check_pc_partner(sd,skill_id,&skill_lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) )
+ {
+ //clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case WL_SUMMONFB:
+ case WL_SUMMONBL:
+ case WL_SUMMONWB:
+ case WL_SUMMONSTONE:
+ if( sc )
+ {
+ 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_id,USESKILL_FAIL_SUMMON,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_id,USESKILL_FAIL_LEVEL,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_id, USESKILL_FAIL_GC_WEAPONBLOCKING, 0);
+ return 0;
+ }
+ break;
+ /**
+ * Ranger
+ **/
+ case RA_WUGMASTERY:
+ if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGSTRIKE:
+ if( !pc_iswug(sd) && !pc_isridingwug(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGRIDER:
+ if( pc_isfalcon(sd) || ( !pc_isridingwug(sd) && !pc_iswug(sd) ) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGDASH:
+ if(!pc_isridingwug(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Royal Guard
+ **/
+ case LG_BANDING:
+ if( sc && sc->data[SC_INSPIRATION] ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_PRESTIGE:
+ if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_RAGEBURST:
+ if( sd->spiritball == 0 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SKILLINTERVAL,0);
+ return 0;
+ }
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ break;
+ case LG_RAYOFGENESIS:
+ if( sc && sc->data[SC_INSPIRATION] )
+ return 1; // Don't check for partner.
+ if( !(sc && sc->data[SC_BANDING]) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ return 0;
+ } else if( skill_check_pc_partner(sd,skill_id,&skill_lv,skill_get_range(skill_id,skill_lv),0) < 1 )
+ return 0; // Just fails, no msg here.
+ break;
+ case LG_HESPERUSLIT:
+ if( !sc || !sc->data[SC_BANDING] ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case SR_FALLENEMPIRE:
+ if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) )
+ return 0;
+ break;
+
+ case SR_CRESCENTELBOW:
+ if( sc && sc->data[SC_CRESCENTELBOW] ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_DUPLICATE, 0);
+ return 0;
+ }
+ break;
+ case SR_CURSEDCIRCLE:
+ if (map_flag_gvg(sd->bl.m)) {
+ if (map_foreachinrange(mob_count_sub, &sd->bl, skill_get_splash(skill_id, skill_lv), BL_MOB,
+ MOBID_EMPERIUM, MOBID_GUARIDAN_STONE1, MOBID_GUARIDAN_STONE2)) {
+ char output[128];
+ sprintf(output, "You're too close to a stone or emperium to do this skill");
+ clif_colormes(sd, COLOR_RED, output);
+ return 0;
+ }
+ }
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else {
+ clif_skill_fail(sd,skill_id,0,0);
+ return 0;
+ }
+ break;
+ case SR_GATEOFHELL:
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball;
+ break;
+ case SC_MANHOLE:
+ case SC_DIMENSIONDOOR:
+ if( sc && sc->data[SC_MAGNETICFIELD] ) {
+ clif_skill_fail(sd,skill_id,0,0);
+ return 0;
+ }
+ break;
+ case WM_GREAT_ECHO: {
+ int count;
+ count = skill_check_pc_partner(sd, skill_id, &skill_lv, skill_get_splash(skill_id,skill_lv), 0);
+ if( count < 1 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0);
+ return 0;
+ } else
+ require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party.
+ }
+ break;
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK: // Can't be casted until you've walked all cells.
+ if( sc && sc->data[SC_PROPERTYWALK] &&
+ sc->data[SC_PROPERTYWALK]->val3 < skill_get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) {
+ clif_skill_fail(sd,skill_id,0x0,0);
+ return 0;
+ }
+ break;
+ case SO_EL_CONTROL:
+ if( !sd->status.ele_id || !sd->ed ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RETURN_TO_ELDICASTES:
+ if( pc_ismadogear(sd) ) { //Cannot be used if Mado is equipped.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_REFLECTDAMAGE:
+ case CR_REFLECTSHIELD:
+ if( sc && sc->data[SC_KYOMU] && rand()%100 < 30){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case KO_KAHU_ENTEN:
+ case KO_HYOUHU_HUBUKI:
+ case KO_KAZEHU_SEIRAN:
+ case KO_DOHU_KOUKAI:
+ {
+ int ttype = skill_get_ele(skill_id, skill_lv);
+ ARR_FIND(1, 5, i, sd->talisman[i] > 0 && i != ttype);
+ if( (i < 5 && i != ttype) || sd->talisman[ttype] >= 10 ){
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+ break;
+ case KO_KAIHOU:
+ case KO_ZENKAI:
+ ARR_FIND(1, 6, i, sd->talisman[i] > 0);
+ if( i > 4 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ }
+
+ switch(require.state) {
+ case ST_HIDING:
+ if(!(sc && sc->option&OPTION_HIDE)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_CLOAKING:
+ if(!pc_iscloaking(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_HIDDEN:
+ if(!pc_ishiding(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_RIDING:
+ if(!pc_isriding(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_FALCON:
+ if(!pc_isfalcon(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_CARTBOOST:
+ if(!(sc && sc->data[SC_CARTBOOST])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case ST_CART:
+ if(!pc_iscarton(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_SHIELD:
+ if(sd->status.shield <= 0) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_SIGHT:
+ if(!(sc && sc->data[SC_SIGHT])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_EXPLOSIONSPIRITS:
+ if(!(sc && sc->data[SC_EXPLOSIONSPIRITS])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_RECOV_WEIGHT_RATE:
+ if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= (unsigned int)battle_config.natural_heal_weight_rate) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_MOVE_ENABLE:
+ if (sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id)
+ sd->ud.canmove_tick = gettick(); //When using a combo, cancel the can't move delay to enable the skill. [Skotlex]
+
+ if (!unit_can_move(&sd->bl)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_WATER:
+ if (sc && (sc->data[SC_DELUGE] || sc->data[SC_SUITON]))
+ break;
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER))
+ break;
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ /**
+ * Rune Knight
+ **/
+ case ST_RIDINGDRAGON:
+ if( !pc_isridingdragon(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Wug
+ **/
+ case ST_WUG:
+ if( !pc_iswug(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Riding Wug
+ **/
+ case ST_RIDINGWUG:
+ if( !pc_isridingwug(sd) ){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Mechanic
+ **/
+ case ST_MADO:
+ if( !pc_ismadogear(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Sorcerer
+ **/
+ case ST_ELEMENTALSPIRIT:
+ if(!sd->ed) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_EL_SUMMON,0);
+ return 0;
+ }
+ break;
+ case ST_POISONINGWEAPON:
+ if (!(sc && sc->data[SC_POISONINGWEAPON])) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_GC_POISONINGWEAPON, 0);
+ return 0;
+ }
+ break;
+ case ST_ROLLINGCUTTER:
+ if (!(sc && sc->data[SC_ROLLINGCUTTER])) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0);
+ return 0;
+ }
+ break;
+ case ST_MH_FIGHTING:
+ if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_FIGHTING)){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case ST_MH_GRAPPLING:
+ if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_GRAPPLING)){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+
+ if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) {
+ //mhp is the max-hp-requirement, that is,
+ //you must have this % or less of HP to cast it.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ return 0;
+ }
+
+ if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
+ return 0;
+ }
+
+ if( require.sp > 0 && status->sp < (unsigned int)require.sp) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0);
+ return 0;
+ }
+
+ if( require.zeny > 0 && sd->status.zeny < require.zeny ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0);
+ return 0;
+ }
+
+ if( require.spiritball > 0 && sd->spiritball < require.spiritball) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SPIRITS,require.spiritball);
+ return 0;
+ }
+
+ return 1;
+}
+
+int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv)
+{
+ struct skill_condition require;
+ struct status_data *status;
+ int i;
+ int index[MAX_SKILL_ITEM_REQUIRE];
+
+ nullpo_ret(sd);
+
+ if( sd->chatID )
+ return 0;
+
+ if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id ) {
+ //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
+ sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check.
+ sd->spiritball_old = sd->spiritball; //Need to do Spiritball check.
+ return 1;
+ }
+
+ switch( sd->menuskill_id ) { // Cast start or cast end??
+ case AM_PHARMACY:
+ switch( skill_id ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill_id )
+ return 0;
+ break;
+ }
+
+ if( sd->skillitem == skill_id ) // Casting finished (Item skill or Hocus-Pocus)
+ return 1;
+
+ if( pc_is90overweight(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0);
+ return 0;
+ }
+
+ // perform skill-specific checks (and actions)
+ switch( skill_id ) {
+ case PR_BENEDICTIO:
+ skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1);
+ break;
+ case AM_CANNIBALIZE:
+ case AM_SPHEREMINE: {
+ int c=0;
+ int summons[5] = { 1589, 1579, 1575, 1555, 1590 };
+ //int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ int maxcount = (skill_id==AM_CANNIBALIZE)? 6-skill_lv : skill_get_maxcount(skill_id,skill_lv);
+ int mob_class = (skill_id==AM_CANNIBALIZE)? summons[skill_lv-1] :1142;
+ if(battle_config.land_skill_limit && maxcount>0 && (battle_config.land_skill_limit&BL_PC)) {
+ i = map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c);
+ if(c >= maxcount ||
+ (skill_id==AM_CANNIBALIZE && c != i && battle_config.summon_flora&2))
+ { //Fails when: exceed max limit. There are other plant types already out.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ break;
+ }
+ case NC_SILVERSNIPER:
+ case NC_MAGICDECOY: {
+ int c = 0, j;
+ int maxcount = skill_get_maxcount(skill_id,skill_lv);
+ int mob_class = 2042;
+ if( skill_id == NC_MAGICDECOY )
+ mob_class = 2043;
+
+ if( battle_config.land_skill_limit && maxcount > 0 && ( battle_config.land_skill_limit&BL_PC ) ) {
+ if( skill_id == NC_MAGICDECOY ) {
+ for( j = mob_class; j <= 2046; j++ )
+ map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, j, skill_id, &c);
+ } else
+ map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c);
+ if( c >= maxcount ) {
+ clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case KO_ZANZOU: {
+ int c = 0;
+ i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, 2308, skill_id, &c);
+ if( c >= skill_get_maxcount(skill_id,skill_lv) || c != i)
+ {
+ clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ status = &sd->battle_status;
+
+ require = skill_get_requirement(sd,skill_id,skill_lv);
+
+ if( require.hp > 0 && status->hp <= (unsigned int)require.hp) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ return 0;
+ }
+
+ if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
+ return 0;
+ }
+
+ if( require.ammo ) { //Skill requires stuff equipped in the arrow slot.
+ if((i=sd->equip_index[EQI_AMMO]) < 0 || !sd->inventory_data[i] ) {
+ clif_arrow_fail(sd,0);
+ return 0;
+ } else if( sd->status.inventory[i].amount < require.ammo_qty ) {
+ char e_msg[100];
+ sprintf(e_msg,"Skill Failed. [%s] requires %dx %s.",
+ skill_get_desc(skill_id),
+ require.ammo_qty,
+ itemdb_jname(sd->status.inventory[i].nameid));
+ clif_colormes(sd,COLOR_RED,e_msg);
+ return 0;
+ }
+ if (!(require.ammo&1<<sd->inventory_data[i]->look)) { //Ammo type check. Send the "wrong weapon type" message
+ //which is the closest we have to wrong ammo type. [Skotlex]
+ clif_arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex]
+ //clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
+ return 0;
+ }
+ }
+
+ for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i ) {
+ if( !require.itemid[i] )
+ continue;
+ index[i] = pc_search_inventory(sd,require.itemid[i]);
+ if( index[i] < 0 || sd->status.inventory[index[i]].amount < require.amount[i] ) {
+ if( require.itemid[i] == ITEMID_RED_GEMSTONE )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_REDJAMSTONE,0);// red gemstone required
+ else if( require.itemid[i] == ITEMID_BLUE_GEMSTONE )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_BLUEJAMSTONE,0);// blue gemstone required
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+// type&2: consume items (after skill was used)
+// type&1: consume the others (before skill was used)
+int skill_consume_requirement( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type)
+{
+ struct skill_condition req;
+
+ nullpo_ret(sd);
+
+ req = skill_get_requirement(sd,skill_id,skill_lv);
+
+ if( type&1 )
+ {
+ if( skill_id == CG_TAROTCARD || sd->state.autocast )
+ req.sp = 0; // TarotCard will consume sp in skill_cast_nodamage_id [Inkfish]
+ if(req.hp || req.sp)
+ status_zap(&sd->bl, req.hp, req.sp);
+
+ if(req.spiritball > 0)
+ pc_delspiritball(sd,req.spiritball,0);
+
+ if(req.zeny > 0)
+ {
+ if( skill_id == NJ_ZENYNAGE )
+ req.zeny = 0; //Zeny is reduced on skill_attack.
+ if( sd->status.zeny < req.zeny )
+ req.zeny = sd->status.zeny;
+ pc_payzeny(sd,req.zeny,LOG_TYPE_CONSUME,NULL);
+ }
+ }
+
+ if( type&2 )
+ {
+ struct status_change *sc = &sd->sc;
+ int n,i;
+
+ if( !sc->count )
+ sc = NULL;
+
+ for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i )
+ {
+ if( !req.itemid[i] )
+ continue;
+
+ if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD )
+ continue; //Gemstones are checked, but not substracted from inventory.
+
+ switch( skill_id ){
+ case SA_SEISMICWEAPON:
+ if( sc && sc->data[SC_UPHEAVAL_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ case SA_FLAMELAUNCHER:
+ case SA_VOLCANO:
+ if( sc && sc->data[SC_TROPIC_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ case SA_FROSTWEAPON:
+ case SA_DELUGE:
+ if( sc && sc->data[SC_CHILLY_AIR_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ case SA_LIGHTNINGLOADER:
+ case SA_VIOLENTGALE:
+ if( sc && sc->data[SC_WILD_STORM_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ }
+
+ if( (n = pc_search_inventory(sd,req.itemid[i])) >= 0 )
+ pc_delitem(sd,n,req.amount[i],0,1,LOG_TYPE_CONSUME);
+ }
+ }
+
+ return 1;
+}
+
+struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv)
+{
+ struct skill_condition req;
+ struct status_data *status;
+ struct status_change *sc;
+ int i,hp_rate,sp_rate, sp_skill_rate_bonus = 100;
+ uint16 idx;
+
+ memset(&req,0,sizeof(req));
+
+ if( !sd )
+ return req;
+
+ if( sd->skillitem == skill_id )
+ return req; // Item skills and Hocus-Pocus don't have requirements.[Inkfish]
+
+ sc = &sd->sc;
+ if( !sc->count )
+ sc = NULL;
+
+ switch( skill_id )
+ { // Turn off check.
+ case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD:
+ case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL:
+ case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN:
+ case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case KO_YAMIKUMO:
+ if( sc && sc->data[status_skill2sc(skill_id)] )
+ return req;
+ }
+
+ idx = skill_get_index(skill_id);
+ if( idx == 0 ) // invalid skill id
+ return req;
+ if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL )
+ return req;
+
+ status = &sd->battle_status;
+
+ req.hp = skill_db[idx].hp[skill_lv-1];
+ hp_rate = skill_db[idx].hp_rate[skill_lv-1];
+ if(hp_rate > 0)
+ req.hp += (status->hp * hp_rate)/100;
+ else
+ req.hp += (status->max_hp * (-hp_rate))/100;
+
+ req.sp = skill_db[idx].sp[skill_lv-1];
+ if((sd->skill_id_old == BD_ENCORE) && skill_id == sd->skill_id_dance)
+ req.sp /= 2;
+ sp_rate = skill_db[idx].sp_rate[skill_lv-1];
+ if(sp_rate > 0)
+ req.sp += (status->sp * sp_rate)/100;
+ else
+ req.sp += (status->max_sp * (-sp_rate))/100;
+ if( sd->dsprate != 100 )
+ req.sp = req.sp * sd->dsprate / 100;
+
+ ARR_FIND(0, ARRAYLENGTH(sd->skillusesprate), i, sd->skillusesprate[i].id == skill_id);
+ if( i < ARRAYLENGTH(sd->skillusesprate) )
+ sp_skill_rate_bonus += sd->skillusesprate[i].val;
+ ARR_FIND(0, ARRAYLENGTH(sd->skillusesp), i, sd->skillusesp[i].id == skill_id);
+ if( i < ARRAYLENGTH(sd->skillusesp) )
+ req.sp -= sd->skillusesp[i].val;
+
+ req.sp = cap_value(req.sp * sp_skill_rate_bonus / 100, 0, SHRT_MAX);
+
+ if( sc ) {
+ if( sc->data[SC__LAZINESS] )
+ req.sp += req.sp + sc->data[SC__LAZINESS]->val1 * 10;
+ if (sc->data[SC_UNLIMITEDHUMMINGVOICE])
+ req.sp += req.sp * sc->data[SC_UNLIMITEDHUMMINGVOICE]->val2 / 100;
+ if( sc->data[SC_RECOGNIZEDSPELL] )
+ req.sp += req.sp / 4;
+ }
+
+ req.zeny = skill_db[idx].zeny[skill_lv-1];
+
+ if( sc && sc->data[SC__UNLUCKY] )
+ req.zeny += sc->data[SC__UNLUCKY]->val1 * 500;
+
+ req.spiritball = skill_db[idx].spiritball[skill_lv-1];
+
+ req.state = skill_db[idx].state;
+
+ req.mhp = skill_db[idx].mhp[skill_lv-1];
+
+ req.weapon = skill_db[idx].weapon;
+
+ req.ammo_qty = skill_db[idx].ammo_qty[skill_lv-1];
+ if (req.ammo_qty)
+ req.ammo = skill_db[idx].ammo;
+
+ if (!req.ammo && skill_id && skill_isammotype(sd, skill_id))
+ { //Assume this skill is using the weapon, therefore it requires arrows.
+ req.ammo = 0xFFFFFFFF; //Enable use on all ammo types.
+ req.ammo_qty = 1;
+ }
+
+ for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) {
+ if( (skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != skill_lv%11 - 1 )
+ continue;
+
+ switch( skill_id ) {
+ case AM_CALLHOMUN:
+ 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 (skill_lv <= 5) // no gems required at level 1-5
+ continue;
+ break;
+ case AB_ADORAMUS:
+ if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2) )
+ continue;
+ break;
+ case WL_COMET:
+ if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0) )
+ continue;
+ break;
+ case GN_FIRE_EXPANSION:
+ if( i < 5 )
+ continue;
+ break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ case SO_WATER_INSIGNIA:
+ case SO_FIRE_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ if( i < 3 )
+ continue;
+ break;
+ }
+
+ req.itemid[i] = skill_db[idx].itemid[i];
+ req.amount[i] = skill_db[idx].amount[i];
+
+ if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN )
+ {
+ 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;
+ }
+ if(sc && sc->data[SC_INTOABYSS])
+ {
+ if( skill_id != SA_ABRACADABRA )
+ req.itemid[i] = req.amount[i] = 0;
+ else if( --req.amount[i] < 1 )
+ req.amount[i] = 1; // Hocus Pocus allways use at least 1 gem
+ }
+ }
+ if( skill_id >= HT_SKIDTRAP && skill_id <= HT_TALKIEBOX && pc_checkskill(sd, RA_RESEARCHTRAP) > 0){
+ int16 itIndex;
+ if( (itIndex = pc_search_inventory(sd,req.itemid[i])) < 0 || ( itIndex >= 0 && sd->status.inventory[itIndex].amount < req.amount[i] ) ){
+ req.itemid[i] = ITEMID_TRAP_ALLOY;
+ req.amount[i] = 1;
+ }
+ break;
+ }
+ }
+
+ /* requirements are level-dependent */
+ switch( skill_id ) {
+ case NC_SHAPESHIFT:
+ case GN_FIRE_EXPANSION:
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ case SO_WATER_INSIGNIA:
+ case SO_FIRE_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ req.itemid[skill_lv-1] = skill_db[idx].itemid[skill_lv-1];
+ req.amount[skill_lv-1] = skill_db[idx].amount[skill_lv-1];
+ break;
+ }
+
+ // Check for cost reductions due to skills & SCs
+ switch(skill_id) {
+ case MC_MAMMONITE:
+ if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0)
+ req.zeny -= req.zeny*10/100;
+ break;
+ case AL_HOLYLIGHT:
+ if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_PRIEST)
+ req.sp *= 5;
+ break;
+ case SL_SMA:
+ case SL_STUN:
+ case SL_STIN:
+ {
+ int kaina_lv = pc_checkskill(sd,SL_KAINA);
+
+ if(kaina_lv==0 || sd->status.base_level<70)
+ break;
+ if(sd->status.base_level>=90)
+ req.sp -= req.sp*7*kaina_lv/100;
+ else if(sd->status.base_level>=80)
+ req.sp -= req.sp*5*kaina_lv/100;
+ else if(sd->status.base_level>=70)
+ req.sp -= req.sp*3*kaina_lv/100;
+ }
+ break;
+ case MO_TRIPLEATTACK:
+ case MO_CHAINCOMBO:
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK)
+ req.sp -= req.sp*25/100; //FIXME: Need real data. this is a custom value.
+ break;
+ case MO_BODYRELOCATION:
+ if( sc && sc->data[SC_EXPLOSIONSPIRITS] )
+ req.spiritball = 0;
+ break;
+ case MO_EXTREMITYFIST:
+ if( sc )
+ {
+ if( sc->data[SC_BLADESTOP] )
+ req.spiritball--;
+ else if( sc->data[SC_COMBO] )
+ {
+ switch( sc->data[SC_COMBO]->val1 )
+ {
+ case MO_COMBOFINISH:
+ req.spiritball = 4;
+ break;
+ case CH_TIGERFIST:
+ req.spiritball = 3;
+ break;
+ case CH_CHAINCRUSH: //It should consume whatever is left as long as it's at least 1.
+ req.spiritball = sd->spiritball?sd->spiritball:1;
+ break;
+ }
+ }else if( sc->data[SC_RAISINGDRAGON] && sd->spiritball > 5)
+ req.spiritball = sd->spiritball; // must consume all regardless of the amount required
+ }
+ break;
+ case SR_RAMPAGEBLASTER:
+ req.spiritball = sd->spiritball?sd->spiritball:15;
+ break;
+ case SR_GATEOFHELL:
+ if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE )
+ req.sp -= req.sp * 10 / 100;
+ break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ req.sp -= req.sp * (5 + 5 * pc_checkskill(sd,SO_EL_SYMPATHY)) / 100;
+ break;
+ case SO_PSYCHIC_WAVE:
+ if( sc && sc->data[SC_BLAST_OPTION] )
+ req.sp += req.sp * 150 / 100;
+ break;
+ }
+
+ return req;
+}
+
+/*==========================================
+ * Does cast-time reductions based on dex, item bonuses and config setting
+ *------------------------------------------*/
+int skill_castfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) {
+ int time = skill_get_cast(skill_id, skill_lv);
+
+ nullpo_ret(bl);
+#ifndef RENEWAL_CAST
+ {
+ struct map_session_data *sd;
+
+ sd = BL_CAST(BL_PC, bl);
+
+ // calculate base cast time (reduced by dex)
+ if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) {
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if( scale > 0 ) // not instant cast
+ time = time * scale / battle_config.castrate_dex_scale;
+ else
+ return 0; // instant cast
+ }
+
+ // calculate cast time reduced by item/card bonuses
+ if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd )
+ {
+ int i;
+ if( sd->castrate != 100 )
+ time = time * sd->castrate / 100;
+ for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
+ {
+ if( sd->skillcast[i].id == skill_id )
+ {
+ time+= time * sd->skillcast[i].val / 100;
+ break;
+ }
+ }
+ }
+
+ }
+#endif
+ // config cast time multiplier
+ if (battle_config.cast_rate != 100)
+ time = time * battle_config.cast_rate / 100;
+ // return final cast time
+ time = max(time, 0);
+
+// ShowInfo("Castime castfix = %d\n",time);
+ return time;
+}
+
+/*==========================================
+ * Does cast-time reductions based on sc data.
+ *------------------------------------------*/
+int skill_castfix_sc (struct block_list *bl, int time)
+{
+ struct status_change *sc = status_get_sc(bl);
+
+ if( time < 0 )
+ return 0;
+
+ if (sc && sc->count) {
+ if (sc->data[SC_SLOWCAST])
+ time += time * sc->data[SC_SLOWCAST]->val2 / 100;
+ if (sc->data[SC_PARALYSIS])
+ time += sc->data[SC_PARALYSIS]->val3;
+ 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 (sc->data[SC_IZAYOI])
+ time -= time * 50 / 100;
+ }
+ time = max(time, 0);
+
+// ShowInfo("Castime castfix_sc = %d\n",time);
+ return time;
+}
+#ifdef RENEWAL_CAST
+int skill_vfcastfix (struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv)
+{
+ struct status_change *sc = status_get_sc(bl);
+ struct map_session_data *sd = BL_CAST(BL_PC,bl);
+ int fixed = skill_get_fixed_cast(skill_id, skill_lv), fixcast_r = 0, varcast_r = 0, i = 0;
+
+ if( time < 0 )
+ return 0;
+
+ if( fixed == 0 ){
+ fixed = (int)time * 20 / 100; // fixed time
+ time = time * 80 / 100; // variable time
+ }else if( fixed < 0 ) // no fixed cast time
+ fixed = 0;
+
+ if(sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){ // Increases/Decreases fixed/variable cast time of a skill by item/card bonuses.
+ if( sd->bonus.varcastrate < 0 )
+ VARCAST_REDUCTION(sd->bonus.varcastrate);
+ for (i = 0; i < ARRAYLENGTH(sd->skillfixcast) && sd->skillfixcast[i].id; i++)
+ if (sd->skillfixcast[i].id == skill_id){ // bonus2 bSkillFixedCast
+ fixed += sd->skillfixcast[i].val;
+ break;
+ }
+ for( i = 0; i < ARRAYLENGTH(sd->skillvarcast) && sd->skillvarcast[i].id; i++ )
+ if( sd->skillvarcast[i].id == skill_id ){ // bonus2 bSkillVariableCast
+ time += sd->skillvarcast[i].val;
+ break;
+ }
+ for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
+ if( sd->skillcast[i].id == skill_id ){ // bonus2 bVariableCastrate
+ if( (i=sd->skillcast[i].val) < 0)
+ VARCAST_REDUCTION(i);
+ break;
+ }
+ }
+
+ if (sc && sc->count && !(skill_get_castnodex(skill_id, skill_lv)&2) ) {
+ // All variable cast additive bonuses must come first
+ if (sc->data[SC_SLOWCAST])
+ VARCAST_REDUCTION(-sc->data[SC_SLOWCAST]->val2);
+
+ // Variable cast reduction bonuses
+ if (sc->data[SC_SUFFRAGIUM]) {
+ VARCAST_REDUCTION(sc->data[SC_SUFFRAGIUM]->val2);
+ status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
+ }
+ if (sc->data[SC_MEMORIZE]) {
+ VARCAST_REDUCTION(50);
+ if ((--sc->data[SC_MEMORIZE]->val2) <= 0)
+ status_change_end(bl, SC_MEMORIZE, INVALID_TIMER);
+ }
+ if (sc->data[SC_POEMBRAGI])
+ VARCAST_REDUCTION(sc->data[SC_POEMBRAGI]->val2);
+ if (sc->data[SC_IZAYOI])
+ VARCAST_REDUCTION(50);
+ if (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WATER))
+ VARCAST_REDUCTION(30); //Reduces 30% Variable Cast Time of Water spells.
+ // Fixed cast reduction bonuses
+ if( sc->data[SC__LAZINESS] )
+ fixcast_r = max(fixcast_r, sc->data[SC__LAZINESS]->val2);
+ if( sc->data[SC_SECRAMENT] )
+ fixcast_r = max(fixcast_r, sc->data[SC_SECRAMENT]->val2);
+ if( sd && ( skill_lv = pc_checkskill(sd, WL_RADIUS) ) && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP )
+ fixcast_r = max(fixcast_r, 5 + skill_lv * 5);
+ // Fixed cast non percentage bonuses
+ if( sc->data[SC_MANDRAGORA] )
+ fixed += sc->data[SC_MANDRAGORA]->val1 * 1000 / 2;
+ if (sc->data[SC_IZAYOI] && (skill_id >= NJ_TOBIDOUGU && skill_id <= NJ_ISSEN))
+ fixed = 0;
+ if( sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION] )
+ fixed -= 1000;
+ }
+
+ if( sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){
+ VARCAST_REDUCTION( max(sd->bonus.varcastrate, 0) + max(i, 0) );
+ fixcast_r = max(fixcast_r, sd->bonus.fixcastrate) + min(sd->bonus.fixcastrate,0);
+ }
+
+ if( varcast_r < 0 ) // now compute overall factors
+ time = time * (1 - (float)varcast_r / 100);
+ if( !(skill_get_castnodex(skill_id, skill_lv)&1) )// reduction from status point
+ time = (1 - sqrt( ((float)(status_get_dex(bl)*2 + status_get_int(bl)) / battle_config.vcast_stat_scale) )) * time;
+ // underflow checking/capping
+ time = max(time, 0) + (1 - (float)min(fixcast_r, 100) / 100) * max(fixed,0);
+
+ return (int)time;
+}
+#endif
+
+/*==========================================
+ * Does delay reductions based on dex/agi, sc data, item bonuses, ...
+ *------------------------------------------*/
+int skill_delayfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv)
+{
+ int delaynodex = skill_get_delaynodex(skill_id, skill_lv);
+ int time = skill_get_delay(skill_id, skill_lv);
+ struct map_session_data *sd;
+ struct status_change *sc = status_get_sc(bl);
+
+ nullpo_ret(bl);
+ sd = BL_CAST(BL_PC, bl);
+
+ if (skill_id == SA_ABRACADABRA || skill_id == WM_RANDOMIZESPELL)
+ return 0; //Will use picked skill's delay.
+
+ if (bl->type&battle_config.no_skill_delay)
+ return battle_config.min_skill_delay_limit;
+
+ if (time < 0)
+ time = -time + status_get_amotion(bl); // If set to <0, add to attack motion.
+
+ // Delay reductions
+ switch (skill_id) { //Monk combo skills have their delay reduced by agi/dex.
+ case MO_TRIPLEATTACK:
+ case MO_CHAINCOMBO:
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ case SR_DRAGONCOMBO:
+ case SR_FALLENEMPIRE:
+ time -= 4*status_get_agi(bl) - 2*status_get_dex(bl);
+ break;
+ case HP_BASILICA:
+ 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] )
+ {
+ switch (skill_id) {
+ case CR_SHIELDBOOMERANG:
+ if (sc->data[SC_SPIRIT]->val2 == SL_CRUSADER)
+ time /= 2;
+ break;
+ case AS_SONICBLOW:
+ if (!map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && sc->data[SC_SPIRIT]->val2 == SL_ASSASIN)
+ time /= 2;
+ break;
+ }
+ }
+
+ if (!(delaynodex&2))
+ {
+ if (sc && sc->count) {
+ if (sc->data[SC_POEMBRAGI])
+ time -= time * sc->data[SC_POEMBRAGI]->val3 / 100;
+ if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WIND))
+ time /= 2; // After Delay of Wind element spells reduced by 50%.
+ }
+
+ }
+
+ if( !(delaynodex&4) && sd && sd->delayrate != 100 )
+ time = time * sd->delayrate / 100;
+
+ if (battle_config.delay_rate != 100)
+ time = time * battle_config.delay_rate / 100;
+
+ //min delay
+ time = max(time, status_get_amotion(bl)); // Delay can never be below amotion [Playtester]
+ time = max(time, battle_config.min_skill_delay_limit);
+
+// ShowInfo("Delay delayfix = %d\n",time);
+ return time;
+}
+
+/*=========================================
+ *
+ *-----------------------------------------*/
+struct square {
+ int val1[5];
+ int val2[5];
+};
+
+static void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 y)
+{
+ nullpo_retv(tc);
+
+ if(dir == 0){
+ tc->val1[0]=x-2;
+ tc->val1[1]=x-1;
+ tc->val1[2]=x;
+ tc->val1[3]=x+1;
+ tc->val1[4]=x+2;
+ tc->val2[0]=
+ tc->val2[1]=
+ tc->val2[2]=
+ tc->val2[3]=
+ tc->val2[4]=y-1;
+ }
+ else if(dir==2){
+ tc->val1[0]=
+ tc->val1[1]=
+ tc->val1[2]=
+ tc->val1[3]=
+ tc->val1[4]=x+1;
+ tc->val2[0]=y+2;
+ tc->val2[1]=y+1;
+ tc->val2[2]=y;
+ tc->val2[3]=y-1;
+ tc->val2[4]=y-2;
+ }
+ else if(dir==4){
+ tc->val1[0]=x-2;
+ tc->val1[1]=x-1;
+ tc->val1[2]=x;
+ tc->val1[3]=x+1;
+ tc->val1[4]=x+2;
+ tc->val2[0]=
+ tc->val2[1]=
+ tc->val2[2]=
+ tc->val2[3]=
+ tc->val2[4]=y+1;
+ }
+ else if(dir==6){
+ tc->val1[0]=
+ tc->val1[1]=
+ tc->val1[2]=
+ tc->val1[3]=
+ tc->val1[4]=x-1;
+ tc->val2[0]=y+2;
+ tc->val2[1]=y+1;
+ tc->val2[2]=y;
+ tc->val2[3]=y-1;
+ tc->val2[4]=y-2;
+ }
+ else if(dir==1){
+ tc->val1[0]=x-1;
+ tc->val1[1]=x;
+ tc->val1[2]=x+1;
+ tc->val1[3]=x+2;
+ tc->val1[4]=x+3;
+ tc->val2[0]=y-4;
+ tc->val2[1]=y-3;
+ tc->val2[2]=y-1;
+ tc->val2[3]=y;
+ tc->val2[4]=y+1;
+ }
+ else if(dir==3){
+ tc->val1[0]=x+3;
+ tc->val1[1]=x+2;
+ tc->val1[2]=x+1;
+ tc->val1[3]=x;
+ tc->val1[4]=x-1;
+ tc->val2[0]=y-1;
+ tc->val2[1]=y;
+ tc->val2[2]=y+1;
+ tc->val2[3]=y+2;
+ tc->val2[4]=y+3;
+ }
+ else if(dir==5){
+ tc->val1[0]=x+1;
+ tc->val1[1]=x;
+ tc->val1[2]=x-1;
+ tc->val1[3]=x-2;
+ tc->val1[4]=x-3;
+ tc->val2[0]=y+3;
+ tc->val2[1]=y+2;
+ tc->val2[2]=y+1;
+ tc->val2[3]=y;
+ tc->val2[4]=y-1;
+ }
+ else if(dir==7){
+ tc->val1[0]=x-3;
+ tc->val1[1]=x-2;
+ tc->val1[2]=x-1;
+ tc->val1[3]=x;
+ tc->val1[4]=x+1;
+ tc->val2[1]=y;
+ tc->val2[0]=y+1;
+ tc->val2[2]=y-1;
+ tc->val2[3]=y-2;
+ tc->val2[4]=y-3;
+ }
+
+}
+
+static void skill_brandishspear_dir (struct square* tc, uint8 dir, int are)
+{
+ int c;
+ nullpo_retv(tc);
+
+ for( c = 0; c < 5; c++ )
+ {
+ switch( dir )
+ {
+ case 0: tc->val2[c]+=are; break;
+ case 1: tc->val1[c]-=are; tc->val2[c]+=are; break;
+ case 2: tc->val1[c]-=are; break;
+ case 3: tc->val1[c]-=are; tc->val2[c]-=are; break;
+ case 4: tc->val2[c]-=are; break;
+ case 5: tc->val1[c]+=are; tc->val2[c]-=are; break;
+ case 6: tc->val1[c]+=are; break;
+ case 7: tc->val1[c]+=are; tc->val2[c]+=are; break;
+ }
+ }
+}
+
+void skill_brandishspear(struct block_list* src, struct block_list* bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ int c,n=4;
+ uint8 dir = map_calc_dir(src,bl->x,bl->y);
+ struct square tc;
+ int x=bl->x,y=bl->y;
+ skill_brandishspear_first(&tc,dir,x,y);
+ skill_brandishspear_dir(&tc,dir,4);
+ skill_area_temp[1] = bl->id;
+
+ if(skill_lv > 9){
+ for(c=1;c<4;c++){
+ map_foreachincell(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ }
+ }
+ if(skill_lv > 6){
+ skill_brandishspear_dir(&tc,dir,-1);
+ n--;
+ }else{
+ skill_brandishspear_dir(&tc,dir,-2);
+ n-=2;
+ }
+
+ if(skill_lv > 3){
+ for(c=0;c<5;c++){
+ map_foreachincell(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ if(skill_lv > 6 && n==3 && c==4){
+ skill_brandishspear_dir(&tc,dir,-1);
+ n--;c=-1;
+ }
+ }
+ }
+ for(c=0;c<10;c++){
+ if(c==0||c==5) skill_brandishspear_dir(&tc,dir,-1);
+ map_foreachincell(skill_area_sub,
+ bl->m,tc.val1[c%5],tc.val2[c%5],BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+}
+
+/*==========================================
+ * Weapon Repair [Celest/DracoRPG]
+ *------------------------------------------*/
+void skill_repairweapon (struct map_session_data *sd, int idx) {
+ int material;
+ int materials[4] = { 1002, 998, 999, 756 };
+ struct item *item;
+ struct map_session_data *target_sd;
+
+ nullpo_retv(sd);
+
+ if ( !( target_sd = map_id2sd(sd->menuskill_val) ) ) //Failed....
+ return;
+
+ if( idx == 0xFFFF ) // No item selected ('Cancel' clicked)
+ return;
+ if( idx < 0 || idx >= MAX_INVENTORY )
+ return; //Invalid index??
+
+ item = &target_sd->status.inventory[idx];
+ if( item->nameid <= 0 || item->attribute == 0 )
+ return; //Again invalid item....
+
+ if( sd != target_sd && !battle_check_range(&sd->bl,&target_sd->bl, skill_get_range2(&sd->bl, sd->menuskill_id,sd->menuskill_val2) ) ){
+ clif_item_repaireffect(sd,idx,1);
+ return;
+ }
+
+ if ( target_sd->inventory_data[idx]->type == IT_WEAPON )
+ material = materials [ target_sd->inventory_data[idx]->wlv - 1 ]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon
+ else
+ material = materials [2]; // Armors consume 1 Steel
+ if ( pc_search_inventory(sd,material) < 0 ) {
+ clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0);
+ return;
+ }
+
+ clif_skill_nodamage(&sd->bl,&target_sd->bl,sd->menuskill_id,1,1);
+
+ item->attribute = 0;/* clear broken state */
+
+ clif_equiplist(target_sd);
+
+ pc_delitem(sd,pc_search_inventory(sd,material),1,0,0,LOG_TYPE_CONSUME);
+
+ clif_item_repaireffect(sd,idx,0);
+
+ if( sd != target_sd )
+ clif_item_repaireffect(target_sd,idx,0);
+}
+
+/*==========================================
+ * Item Appraisal
+ *------------------------------------------*/
+void skill_identify (struct map_session_data *sd, int idx)
+{
+ int flag=1;
+
+ nullpo_retv(sd);
+
+ if(idx >= 0 && idx < MAX_INVENTORY) {
+ if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){
+ flag=0;
+ sd->status.inventory[idx].identify=1;
+ }
+ }
+ clif_item_identified(sd,idx,flag);
+}
+
+/*==========================================
+ * Weapon Refine [Celest]
+ *------------------------------------------*/
+void skill_weaponrefine (struct map_session_data *sd, int idx)
+{
+ nullpo_retv(sd);
+
+ if (idx >= 0 && idx < MAX_INVENTORY)
+ {
+ int i = 0, ep = 0, per;
+ int material[5] = { 0, 1010, 1011, 984, 984 };
+ struct item *item;
+ struct item_data *ditem = sd->inventory_data[idx];
+ item = &sd->status.inventory[idx];
+
+ if(item->nameid > 0 && ditem->type == IT_WEAPON)
+ {
+ if( item->refine >= sd->menuskill_val
+ || item->refine >= 10 // if it's no longer refineable
+ || ditem->flag.no_refine // if the item isn't refinable
+ || (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 )
+ {
+ clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0);
+ return;
+ }
+
+ per = status_get_refine_chance(ditem->wlv, (int)item->refine);
+ per += (((signed int)sd->status.job_level)-50)/2; //Updated per the new kro descriptions. [Skotlex]
+
+ pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER);
+ if (per > rnd() % 100) {
+ log_pick_pc(sd, LOG_TYPE_OTHER, -1, item);
+ item->refine++;
+ log_pick_pc(sd, LOG_TYPE_OTHER, 1, item);
+ if(item->equip) {
+ ep = item->equip;
+ pc_unequipitem(sd,idx,3);
+ }
+ clif_refine(sd->fd,0,idx,item->refine);
+ clif_delitem(sd,idx,1,3);
+ clif_additem(sd,idx,1,0);
+ if (ep)
+ pc_equipitem(sd,idx,ep);
+ clif_misceffect(&sd->bl,3);
+ if(item->refine == 10 &&
+ item->card[0] == CARD0_FORGE &&
+ (int)MakeDWord(item->card[2],item->card[3]) == sd->status.char_id)
+ { // Fame point system [DracoRPG]
+ switch(ditem->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ } else {
+ item->refine = 0;
+ if(item->equip)
+ pc_unequipitem(sd,idx,3);
+ clif_refine(sd->fd,1,idx,item->refine);
+ pc_delitem(sd,idx,1,0,2, LOG_TYPE_OTHER);
+ clif_misceffect(&sd->bl,2);
+ clif_emotion(&sd->bl, E_OMG);
+ }
+ }
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_autospell (struct map_session_data *sd, uint16 skill_id)
+{
+ uint16 skill_lv;
+ int maxlv=1,lv;
+
+ nullpo_ret(sd);
+
+ skill_lv = sd->menuskill_val;
+ lv=pc_checkskill(sd,skill_id);
+
+ if(!skill_lv || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance]
+
+ if(skill_id==MG_NAPALMBEAT) maxlv=3;
+ else if(skill_id==MG_COLDBOLT || skill_id==MG_FIREBOLT || skill_id==MG_LIGHTNINGBOLT){
+ if (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SAGE)
+ maxlv =10; //Soul Linker bonus. [Skotlex]
+ else if(skill_lv==2) maxlv=1;
+ else if(skill_lv==3) maxlv=2;
+ else if(skill_lv>=4) maxlv=3;
+ }
+ else if(skill_id==MG_SOULSTRIKE){
+ if(skill_lv==5) maxlv=1;
+ else if(skill_lv==6) maxlv=2;
+ else if(skill_lv>=7) maxlv=3;
+ }
+ else if(skill_id==MG_FIREBALL){
+ if(skill_lv==8) maxlv=1;
+ else if(skill_lv>=9) maxlv=2;
+ }
+ else if(skill_id==MG_FROSTDIVER) maxlv=1;
+ else return 0;
+
+ if(maxlv > lv)
+ maxlv = lv;
+
+ sc_start4(&sd->bl,SC_AUTOSPELL,100,skill_lv,skill_id,maxlv,0,
+ skill_get_time(SA_AUTOSPELL,skill_lv));
+ return 0;
+}
+
+/*==========================================
+ * Sitting skills functions.
+ *------------------------------------------*/
+static int skill_sit_count (struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd;
+ int type =va_arg(ap,int);
+ sd=(struct map_session_data*)bl;
+
+ if(!pc_issit(sd))
+ return 0;
+
+ if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0)
+ return 1;
+
+ if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0))
+ return 1;
+
+ return 0;
+}
+
+static int skill_sit_in (struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd;
+ int type =va_arg(ap,int);
+
+ sd=(struct map_session_data*)bl;
+
+ if(!pc_issit(sd))
+ return 0;
+
+ if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0)
+ sd->state.gangsterparadise=1;
+
+ if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 ))
+ {
+ sd->state.rest=1;
+ status_calc_regen(bl, &sd->battle_status, &sd->regen);
+ status_calc_regen_rate(bl, &sd->regen, &sd->sc);
+ }
+
+ return 0;
+}
+
+static int skill_sit_out (struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd;
+ int type =va_arg(ap,int);
+ sd=(struct map_session_data*)bl;
+ if(sd->state.gangsterparadise && type&1)
+ sd->state.gangsterparadise=0;
+ if(sd->state.rest && type&2) {
+ sd->state.rest=0;
+ status_calc_regen(bl, &sd->battle_status, &sd->regen);
+ status_calc_regen_rate(bl, &sd->regen, &sd->sc);
+ }
+ return 0;
+}
+
+int skill_sit (struct map_session_data *sd, int type)
+{
+ int flag = 0;
+ int range = 0, lv;
+ nullpo_ret(sd);
+
+
+ if((lv = pc_checkskill(sd,RG_GANGSTER)) > 0) {
+ flag|=1;
+ range = skill_get_splash(RG_GANGSTER, lv);
+ }
+ if((lv = pc_checkskill(sd,TK_HPTIME)) > 0) {
+ flag|=2;
+ range = skill_get_splash(TK_HPTIME, lv);
+ }
+ else if ((lv = pc_checkskill(sd,TK_SPTIME)) > 0) {
+ flag|=2;
+ range = skill_get_splash(TK_SPTIME, lv);
+ }
+
+ if( type ) {
+ clif_status_load(&sd->bl,SI_SIT,1);
+ } else {
+ clif_status_load(&sd->bl,SI_SIT,0);
+ }
+
+ if (!flag) return 0;
+
+ if(type) {
+ if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) > 1)
+ map_foreachinrange(skill_sit_in,&sd->bl, range, BL_PC, flag);
+ } else {
+ if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) < 2)
+ map_foreachinrange(skill_sit_out,&sd->bl, range, BL_PC, flag);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_frostjoke_scream (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ uint16 skill_id,skill_lv;
+ unsigned int tick;
+
+ nullpo_ret(bl);
+ nullpo_ret(src=va_arg(ap,struct block_list*));
+
+ skill_id=va_arg(ap,int);
+ skill_lv=va_arg(ap,int);
+ if(!skill_lv) return 0;
+ tick=va_arg(ap,unsigned int);
+
+ if (src == bl || status_isdead(bl))
+ return 0;
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ 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)
+ skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick);
+ else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rnd()%100 < 10)
+ skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static void skill_unitsetmapcell (struct skill_unit *src, uint16 skill_id, uint16 skill_lv, cell_t cell, bool flag)
+{
+ int range = skill_get_unit_range(skill_id,skill_lv);
+ int x,y;
+
+ for( y = src->bl.y - range; y <= src->bl.y + range; ++y )
+ for( x = src->bl.x - range; x <= src->bl.x + range; ++x )
+ map_setcell(src->bl.m, x, y, cell, flag);
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_attack_area (struct block_list *bl, va_list ap)
+{
+ struct block_list *src,*dsrc;
+ int atk_type,skill_id,skill_lv,flag,type;
+ unsigned int tick;
+
+ if(status_isdead(bl))
+ return 0;
+
+ atk_type = va_arg(ap,int);
+ src=va_arg(ap,struct block_list*);
+ dsrc=va_arg(ap,struct block_list*);
+ skill_id=va_arg(ap,int);
+ skill_lv=va_arg(ap,int);
+ tick=va_arg(ap,unsigned int);
+ flag=va_arg(ap,int);
+ type=va_arg(ap,int);
+
+
+ if (skill_area_temp[1] == bl->id) //This is the target of the skill, do a full attack and skip target checks.
+ return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag);
+
+ if(battle_check_target(dsrc,bl,type) <= 0 ||
+ !status_check_skilluse(NULL, bl, skill_id, 2))
+ return 0;
+
+
+ switch (skill_id) {
+ case WZ_FROSTNOVA: //Skills that don't require the animation to be removed
+ case NPC_ACIDBREATH:
+ case NPC_DARKNESSBREATH:
+ case NPC_FIREBREATH:
+ case NPC_ICEBREATH:
+ case NPC_THUNDERBREATH:
+ return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag);
+ default:
+ //Area-splash, disable skill animation.
+ return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
+ }
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_clear_group (struct block_list *bl, int flag)
+{
+ struct unit_data *ud = unit_bl2ud(bl);
+ struct skill_unit_group *group[MAX_SKILLUNITGROUP];
+ int i, count=0;
+
+ nullpo_ret(bl);
+ if (!ud) return 0;
+
+ //All groups to be deleted are first stored on an array since the array elements shift around when you delete them. [Skotlex]
+ for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++)
+ {
+ switch (ud->skillunit[i]->skill_id) {
+ case SA_DELUGE:
+ case SA_VOLCANO:
+ case SA_VIOLENTGALE:
+ case SA_LANDPROTECTOR:
+ case NJ_SUITON:
+ case NJ_KAENSIN:
+ if (flag&1)
+ group[count++]= ud->skillunit[i];
+ break;
+ case SO_WARMER:
+ if( flag&8 )
+ group[count++]= ud->skillunit[i];
+ break;
+ case SC_BLOODYLUST:
+ if (flag & 32)
+ group[count++] = ud->skillunit[i];
+ break;
+ default:
+ if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP)
+ group[count++]= ud->skillunit[i];
+ break;
+ }
+
+ }
+ for (i=0;i<count;i++)
+ skill_delunitgroup(group[i]);
+ return count;
+}
+
+/*==========================================
+ * Returns the first element field found [Skotlex]
+ *------------------------------------------*/
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
+{
+ struct unit_data *ud = unit_bl2ud(bl);
+ int i;
+ nullpo_ret(bl);
+ if (!ud) return NULL;
+
+ for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++) {
+ switch (ud->skillunit[i]->skill_id) {
+ case SA_DELUGE:
+ case SA_VOLCANO:
+ case SA_VIOLENTGALE:
+ case SA_LANDPROTECTOR:
+ case NJ_SUITON:
+ case SO_WARMER:
+ case SC_BLOODYLUST:
+ return ud->skillunit[i];
+ }
+ }
+ return NULL;
+}
+
+// for graffiti cleaner [Valaris]
+int skill_graffitiremover (struct block_list *bl, va_list ap)
+{
+ struct skill_unit *unit=NULL;
+
+ nullpo_ret(bl);
+ nullpo_ret(ap);
+
+ if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL)
+ return 0;
+
+ if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI))
+ skill_delunit(unit);
+
+ return 0;
+}
+
+int skill_greed (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ struct map_session_data *sd=NULL;
+ struct flooritem_data *fitem=NULL;
+
+ nullpo_ret(bl);
+ nullpo_ret(src = va_arg(ap, struct block_list *));
+
+ if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl))
+ pc_takeitem(sd, fitem);
+
+ return 0;
+}
+//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->group->limit = DIFF_TICK(gettick(),unit->group->tick) +
+ (unit_id == UNT_TALKIEBOX ? 5000 : (unit_id == UNT_CLUSTERBOMB || unit_id == UNT_ICEBOUNDTRAP? 2500 : 1500) );
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_cell_overlap(struct block_list *bl, va_list ap)
+{
+ uint16 skill_id;
+ int *alive;
+ struct skill_unit *unit;
+
+ skill_id = va_arg(ap,int);
+ alive = va_arg(ap,int *);
+ unit = (struct skill_unit *)bl;
+
+ if (unit == NULL || unit->group == NULL || (*alive) == 0)
+ return 0;
+
+ switch (skill_id) {
+ case SA_LANDPROTECTOR:
+ if( unit->group->skill_id == SA_LANDPROTECTOR ) {//Check for offensive Land Protector to delete both. [Skotlex]
+ (*alive) = 0;
+ skill_delunit(unit);
+ return 1;
+ }
+ if( !(skill_get_inf2(unit->group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP)) ) { //It deletes everything except songs/dances and traps
+ skill_delunit(unit);
+ return 1;
+ }
+ break;
+ case HW_GANBANTEIN:
+ case LG_EARTHDRIVE:
+ if( !(unit->group->state.song_dance&0x1) ) {// Don't touch song/dance.
+ skill_delunit(unit);
+ return 1;
+ }
+ break;
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+// The official implementation makes them fail to appear when casted on top of ANYTHING
+// but I wonder if they didn't actually meant to fail when casted on top of each other?
+// hence, I leave the alternate implementation here, commented. [Skotlex]
+ if (unit->range <= 0)
+ {
+ (*alive) = 0;
+ return 1;
+ }
+/*
+ switch (unit->group->skill_id)
+ { //These cannot override each other.
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ (*alive) = 0;
+ return 1;
+ }
+*/
+ break;
+ case PF_FOGWALL:
+ switch(unit->group->skill_id) {
+ case SA_VOLCANO: //Can't be placed on top of these
+ case SA_VIOLENTGALE:
+ (*alive) = 0;
+ return 1;
+ case SA_DELUGE:
+ case NJ_SUITON:
+ //Cheap 'hack' to notify the calling function that duration should be doubled [Skotlex]
+ (*alive) = 2;
+ break;
+ }
+ break;
+ case HP_BASILICA:
+ if (unit->group->skill_id == HP_BASILICA)
+ { //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex]
+ (*alive) = 0;
+ return 1;
+ }
+ break;
+ case GN_CRAZYWEED_ATK:
+ switch(unit->group->unit_id){ //TODO: look for other ground skills that are affected.
+ case UNT_WALLOFTHORN:
+ case UNT_THORNS_TRAP:
+ case UNT_BLOODYLUST:
+ case UNT_CHAOSPANIC:
+ case UNT_MAELSTROM:
+ case UNT_FIREPILLAR_ACTIVE:
+ case UNT_LANDPROTECTOR:
+ case UNT_VOLCANO:
+ case UNT_DELUGE:
+ case UNT_VIOLENTGALE:
+ case UNT_SAFETYWALL:
+ case UNT_PNEUMA:
+ skill_delunit(unit);
+ return 1;
+ }
+ break;
+ }
+
+ if (unit->group->skill_id == SA_LANDPROTECTOR && !(skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_TRAP))) { //It deletes everything except songs/dances/traps
+ (*alive) = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap)
+{
+ struct mob_data* md;
+ struct unit_data*ud = unit_bl2ud(bl);
+ struct block_list *from_bl;
+ struct block_list *to_bl;
+ md = (struct mob_data*)bl;
+ from_bl = va_arg(ap,struct block_list *);
+ to_bl = va_arg(ap,struct block_list *);
+
+ if(ud && ud->target == from_bl->id)
+ ud->target = to_bl->id;
+
+ if(md->bl.type == BL_MOB && md->target_id == from_bl->id)
+ md->target_id = to_bl->id;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_trap_splash (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ int tick;
+ struct skill_unit *unit;
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ src = va_arg(ap,struct block_list *);
+ unit = (struct skill_unit *)src;
+ tick = va_arg(ap,int);
+
+ if( !unit->alive || bl->prev == NULL )
+ return 0;
+
+ nullpo_ret(sg = unit->group);
+ nullpo_ret(ss = map_id2bl(sg->src_id));
+
+ if(battle_check_target(src,bl,sg->target_flag) <= 0)
+ return 0;
+
+ switch(sg->unit_id){
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,ATK_DEF,tick);
+ break;
+ case UNT_GROUNDDRIFT_WIND:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_STUN,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_DARK:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_BLIND,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_POISON:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_POISON,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_WATER:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_FREEZE,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_FIRE:
+ 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( ss != bl )
+ skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1|SD_LEVEL);
+ break;
+ case UNT_MAGENTATRAP:
+ case UNT_COBALTTRAP:
+ case UNT_MAIZETRAP:
+ case UNT_VERDURETRAP:
+ if( bl->type != BL_PC && !is_boss(bl) )
+ sc_start2(bl,SC_ELEMENTALCHANGE,100,sg->skill_lv,skill_get_ele(sg->skill_id,sg->skill_lv),skill_get_time2(sg->skill_id,sg->skill_lv));
+ break;
+ case UNT_REVERBERATION:
+ skill_addtimerskill(ss,tick+50,bl->id,0,0,WM_REVERBERATION_MELEE,sg->skill_lv,BF_WEAPON,0); // for proper skill delay animation when use with Dominion Impulse
+ skill_addtimerskill(ss,tick+250,bl->id,0,0,WM_REVERBERATION_MAGIC,sg->skill_lv,BF_MAGIC,0);
+ break;
+ default:
+ skill_attack(skill_get_type(sg->skill_id),ss,src,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ }
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_enchant_elemental_end (struct block_list *bl, int type)
+{
+ struct status_change *sc;
+ const enum sc_type scs[] = { SC_ENCPOISON, SC_ASPERSIO, SC_FIREWEAPON, SC_WATERWEAPON, SC_WINDWEAPON, SC_EARTHWEAPON, SC_SHADOWWEAPON, SC_GHOSTWEAPON, SC_ENCHANTARMS, SC_EXEEDBREAK };
+ int i;
+ nullpo_ret(bl);
+ nullpo_ret(sc= status_get_sc(bl));
+
+ if (!sc->count) return 0;
+
+ for (i = 0; i < ARRAYLENGTH(scs); i++)
+ if (type != scs[i] && sc->data[scs[i]])
+ status_change_end(bl, scs[i], INVALID_TIMER);
+
+ return 0;
+}
+
+bool skill_check_cloaking(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;
+
+ if( (bl->type == BL_PC && battle_config.pc_cloak_check_type&1)
+ || (bl->type != BL_PC && battle_config.monster_cloak_check_type&1) )
+ { //Check for walls.
+ int i;
+ 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 cloaking.
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ else
+ if( sce->val4&1 )
+ { //Remove wall bonus
+ sce->val4&=~1;
+ status_calc_bl(bl,SCB_SPEED);
+ }
+ }
+ else
+ {
+ if( !(sce->val4&1) )
+ { //Add wall speed bonus
+ sce->val4|=1;
+ status_calc_bl(bl,SCB_SPEED);
+ }
+ }
+ }
+
+ 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;
+
+ if( bl->type == BL_PC )
+ { //Check for walls.
+ int i;
+ 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, INVALID_TIMER);
+ else
+ if( sce->val3&1 )
+ { //Remove wall bonus
+ sce->val3&=~1;
+ status_calc_bl(bl,SCB_SPEED);
+ }
+ }
+ }
+
+ return wall;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2)
+{
+ struct skill_unit *unit;
+
+ nullpo_retr(NULL, group);
+ nullpo_retr(NULL, group->unit); // crash-protection against poor coding
+ nullpo_retr(NULL, unit=&group->unit[idx]);
+
+ if(!unit->alive)
+ group->alive_count++;
+
+ unit->bl.id=map_get_new_object_id();
+ unit->bl.type=BL_SKILL;
+ unit->bl.m=group->map;
+ unit->bl.x=x;
+ unit->bl.y=y;
+ unit->group=group;
+ unit->alive=1;
+ unit->val1=val1;
+ unit->val2=val2;
+
+ idb_put(skillunit_db, unit->bl.id, unit);
+ map_addiddb(&unit->bl);
+ map_addblock(&unit->bl);
+
+ // perform oninit actions
+ switch (group->skill_id) {
+ case WZ_ICEWALL:
+ map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,5);
+ clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,5,AREA);
+ skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,true);
+ map[unit->bl.m].icewall_num++;
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,true);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,true);
+ break;
+ case SC_MAELSTROM:
+ skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,true);
+ break;
+ default:
+ if (group->state.song_dance&0x1) //Check for dissonance.
+ skill_dance_overlap(unit, 1);
+ break;
+ }
+
+ clif_skill_setunit(unit);
+
+ return unit;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_delunit (struct skill_unit* unit)
+{
+ struct skill_unit_group *group;
+
+ nullpo_ret(unit);
+ if( !unit->alive )
+ return 0;
+ unit->alive=0;
+
+ nullpo_ret(group=unit->group);
+
+ if( group->state.song_dance&0x1 ) //Cancel dissonance effect.
+ skill_dance_overlap(unit, 0);
+
+ // invoke onout event
+ if( !unit->range )
+ map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4);
+
+ // perform ondelete actions
+ switch (group->skill_id) {
+ case HT_ANKLESNARE: {
+ struct block_list* target = map_id2bl(group->val2);
+ if( target )
+ status_change_end(target, SC_ANKLE, INVALID_TIMER);
+ }
+ break;
+ case WZ_ICEWALL:
+ map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,unit->val2);
+ clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,ALL_SAMEMAP); // hack to avoid clientside cell bug
+ skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,false);
+ map[unit->bl.m].icewall_num--;
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,false);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,false);
+ break;
+ case RA_ELECTRICSHOCKER: {
+ struct block_list* target = map_id2bl(group->val2);
+ if( target )
+ status_change_end(target, SC_ELECTRICSHOCKER, INVALID_TIMER);
+ }
+ break;
+ case SC_MAELSTROM:
+ skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,false);
+ break;
+ case SC_MANHOLE: // Note : Removing the unit don't remove the status (official info)
+ if( group->val2 ) { // Someone Traped
+ struct status_change *tsc = status_get_sc( map_id2bl(group->val2));
+ if( tsc && tsc->data[SC__MANHOLE] )
+ tsc->data[SC__MANHOLE]->val4 = 0; // Remove the Unit ID
+ }
+ break;
+ }
+
+ clif_skill_delunit(unit);
+
+ unit->group=NULL;
+ map_delblock(&unit->bl); // don't free yet
+ map_deliddb(&unit->bl);
+ idb_remove(skillunit_db, unit->bl.id);
+ if(--group->alive_count==0)
+ skill_delunitgroup(group);
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+static DBMap* group_db = NULL;// int group_id -> struct skill_unit_group*
+
+/// Returns the target skill_unit_group or NULL if not found.
+struct skill_unit_group* skill_id2group(int group_id)
+{
+ return (struct skill_unit_group*)idb_get(group_db, group_id);
+}
+
+
+static int skill_unit_group_newid = MAX_SKILL_DB;
+
+/// Returns a new group_id that isn't being used in group_db.
+/// Fatal error if nothing is available.
+static int skill_get_new_group_id(void)
+{
+ if( skill_unit_group_newid >= MAX_SKILL_DB && skill_id2group(skill_unit_group_newid) == NULL )
+ return skill_unit_group_newid++;// available
+ {// find next id
+ int base_id = skill_unit_group_newid;
+ while( base_id != ++skill_unit_group_newid )
+ {
+ if( skill_unit_group_newid < MAX_SKILL_DB )
+ skill_unit_group_newid = MAX_SKILL_DB;
+ if( skill_id2group(skill_unit_group_newid) == NULL )
+ return skill_unit_group_newid++;// available
+ }
+ // full loop, nothing available
+ ShowFatalError("skill_get_new_group_id: All ids are taken. Exiting...");
+ exit(1);
+ }
+}
+
+struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval)
+{
+ struct unit_data* ud = unit_bl2ud( src );
+ struct skill_unit_group* group;
+ int i;
+
+ if(!(skill_id && skill_lv)) return 0;
+
+ nullpo_retr(NULL, src);
+ nullpo_retr(NULL, ud);
+
+ // find a free spot to store the new unit group
+ ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == NULL );
+ if(i == MAX_SKILLUNITGROUP)
+ {
+ // array is full, make room by discarding oldest group
+ int j=0;
+ unsigned maxdiff=0,x,tick=gettick();
+ for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++)
+ if((x=DIFF_TICK(tick,ud->skillunit[i]->tick))>maxdiff){
+ maxdiff=x;
+ j=i;
+ }
+ skill_delunitgroup(ud->skillunit[j]);
+ //Since elements must have shifted, we use the last slot.
+ i = MAX_SKILLUNITGROUP-1;
+ }
+
+ group = ers_alloc(skill_unit_ers, struct skill_unit_group);
+ group->src_id = src->id;
+ group->party_id = status_get_party_id(src);
+ group->guild_id = status_get_guild_id(src);
+ group->bg_id = bg_team_get_id(src);
+ group->group_id = skill_get_new_group_id();
+ group->unit = (struct skill_unit *)aCalloc(count,sizeof(struct skill_unit));
+ group->unit_count = count;
+ group->alive_count = 0;
+ group->val1 = 0;
+ group->val2 = 0;
+ group->val3 = 0;
+ group->skill_id = skill_id;
+ group->skill_lv = skill_lv;
+ group->unit_id = unit_id;
+ group->map = src->m;
+ group->limit = limit;
+ group->interval = interval;
+ group->tick = gettick();
+ group->valstr = NULL;
+
+ ud->skillunit[i] = group;
+
+ if (skill_id == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex]
+ group->tick += 1500;
+
+ idb_put(group_db, group->group_id, group);
+ return group;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func)
+{
+ struct block_list* src;
+ struct unit_data *ud;
+ int i,j;
+
+ if( group == NULL )
+ {
+ ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func);
+ return 0;
+ }
+
+ src=map_id2bl(group->src_id);
+ ud = unit_bl2ud(src);
+ if(!src || !ud) {
+ ShowError("skill_delunitgroup: Group's source not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id);
+ return 0;
+ }
+
+ if( !status_isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) {
+ switch( group->skill_id ) {
+ case BA_DISSONANCE:
+ case BA_POEMBRAGI:
+ case BA_WHISTLE:
+ case BA_ASSASSINCROSS:
+ case BA_APPLEIDUN:
+ case DC_UGLYDANCE:
+ case DC_HUMMING:
+ case DC_DONTFORGETME:
+ case DC_FORTUNEKISS:
+ case DC_SERVICEFORYOU:
+ skill_usave_add(((TBL_PC*)src), group->skill_id, group->skill_lv);
+ break;
+ }
+ }
+
+ if (skill_get_unit_flag(group->skill_id)&(UF_DANCE|UF_SONG|UF_ENSEMBLE))
+ {
+ struct status_change* sc = status_get_sc(src);
+ if (sc && sc->data[SC_DANCING])
+ {
+ sc->data[SC_DANCING]->val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex]
+ status_change_end(src, SC_DANCING, INVALID_TIMER);
+ }
+ }
+
+ // end Gospel's status change on 'src'
+ // (needs to be done when the group is deleted by other means than skill deactivation)
+ if (group->unit_id == UNT_GOSPEL) {
+ struct status_change *sc = status_get_sc(src);
+ if(sc && sc->data[SC_GOSPEL]) {
+ sc->data[SC_GOSPEL]->val3 = 0; //Remove reference to this group. [Skotlex]
+ status_change_end(src, SC_GOSPEL, 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,INVALID_TIMER);
+ }
+ }
+ 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,INVALID_TIMER);
+ }
+ }
+ break;
+ case LG_BANDING:
+ {
+ struct status_change *sc = NULL;
+ if( (sc = status_get_sc(src)) && sc->data[SC_BANDING] ) {
+ sc->data[SC_BANDING]->val4 = 0;
+ status_change_end(src,SC_BANDING,INVALID_TIMER);
+ }
+ }
+ break;
+ }
+
+ if (src->type==BL_PC && group->state.ammo_consume)
+ battle_consume_ammo((TBL_PC*)src, group->skill_id, group->skill_lv);
+
+ group->alive_count=0;
+
+ // remove all unit cells
+ if(group->unit != NULL)
+ for( i = 0; i < group->unit_count; i++ )
+ skill_delunit(&group->unit[i]);
+
+ // clear Talkie-box string
+ if( group->valstr != NULL )
+ {
+ aFree(group->valstr);
+ group->valstr = NULL;
+ }
+
+ idb_remove(group_db, group->group_id);
+ map_freeblock(&group->unit->bl); // schedules deallocation of whole array (HACK)
+ group->unit=NULL;
+ group->group_id=0;
+ group->unit_count=0;
+
+ // locate this group, swap with the last entry and delete it
+ ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == group );
+ ARR_FIND( i, MAX_SKILLUNITGROUP, j, ud->skillunit[j] == NULL ); j--;
+ if( i < MAX_SKILLUNITGROUP )
+ {
+ ud->skillunit[i] = ud->skillunit[j];
+ ud->skillunit[j] = NULL;
+ ers_free(skill_unit_ers, group);
+ }
+ else
+ ShowError("skill_delunitgroup: Group not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_clear_unitgroup (struct block_list *src)
+{
+ struct unit_data *ud = unit_bl2ud(src);
+
+ nullpo_ret(ud);
+
+ while (ud->skillunit[0])
+ skill_delunitgroup(ud->skillunit[0]);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+struct skill_unit_group_tickset *skill_unitgrouptickset_search (struct block_list *bl, struct skill_unit_group *group, int tick)
+{
+ int i,j=-1,k,s,id;
+ struct unit_data *ud;
+ struct skill_unit_group_tickset *set;
+
+ nullpo_ret(bl);
+ if (group->interval==-1)
+ return NULL;
+
+ ud = unit_bl2ud(bl);
+ if (!ud) return NULL;
+
+ set = ud->skillunittick;
+
+ if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP)
+ id = s = group->skill_id;
+ else
+ id = s = group->group_id;
+
+ for (i=0; i<MAX_SKILLUNITGROUPTICKSET; i++) {
+ k = (i+s) % MAX_SKILLUNITGROUPTICKSET;
+ if (set[k].id == id)
+ return &set[k];
+ else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || set[k].id==0))
+ j=k;
+ }
+
+ if (j == -1) {
+ ShowWarning ("skill_unitgrouptickset_search: tickset is full\n");
+ j = id % MAX_SKILLUNITGROUPTICKSET;
+ }
+
+ set[j].id = id;
+ set[j].tick = tick;
+ return &set[j];
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_timer_sub_onplace (struct block_list* bl, va_list ap)
+{
+ struct skill_unit* unit = va_arg(ap,struct skill_unit *);
+ struct skill_unit_group* group = unit->group;
+ unsigned int tick = va_arg(ap,unsigned int);
+
+ if( !unit->alive || bl->prev == NULL )
+ return 0;
+
+ nullpo_ret(group);
+
+ if( !(skill_get_inf2(group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP|INF2_NOLP)) && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) )
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ if( battle_check_target(&unit->bl,bl,group->target_flag) <= 0 )
+ return 0;
+
+ skill_unit_onplace_timer(unit,bl,tick);
+
+ return 1;
+}
+
+/**
+ * @see DBApply
+ */
+static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap)
+{
+ struct skill_unit* unit = db_data2ptr(data);
+ struct skill_unit_group* group = unit->group;
+ unsigned int tick = va_arg(ap,unsigned int);
+ bool dissonance;
+ struct block_list* bl = &unit->bl;
+
+ if( !unit->alive )
+ return 0;
+
+ nullpo_ret(group);
+
+ // check for expiration
+ if( !group->state.guildaura && (DIFF_TICK(tick,group->tick) >= group->limit || DIFF_TICK(tick,group->tick) >= unit->limit) )
+ {// skill unit expired (inlined from skill_unit_onlimit())
+ switch( group->unit_id )
+ {
+ case UNT_BLASTMINE:
+#ifdef RENEWAL
+ case UNT_CLAYMORETRAP:
+#endif
+ case UNT_GROUNDDRIFT_WIND:
+ case UNT_GROUNDDRIFT_DARK:
+ case UNT_GROUNDDRIFT_POISON:
+ case UNT_GROUNDDRIFT_WATER:
+ case UNT_GROUNDDRIFT_FIRE:
+ group->unit_id = UNT_USED_TRAPS;
+ //clif_changetraplook(bl, UNT_FIREPILLAR_ACTIVE);
+ group->limit=DIFF_TICK(tick+1500,group->tick);
+ unit->limit=DIFF_TICK(tick+1500,group->tick);
+ break;
+
+ case UNT_ANKLESNARE:
+ case UNT_ELECTRICSHOCKER:
+ if( group->val2 > 0 ) {
+ // Used Trap don't returns back to item
+ skill_delunit(unit);
+ break;
+ }
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+#ifndef RENEWAL
+ case UNT_CLAYMORETRAP:
+#endif
+ 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 )
+ { // revert unit back into a trap
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = group->item_id?group->item_id:ITEMID_TRAP;
+ item_tmp.identify = 1;
+ map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0);
+ }
+ skill_delunit(unit);
+ }
+ break;
+
+ case UNT_WARP_ACTIVE:
+ // warp portal opens (morph to a UNT_WARP_WAITING cell)
+ group->unit_id = skill_get_unit_id(group->skill_id, 1); // UNT_WARP_WAITING
+ clif_changelook(&unit->bl, LOOK_BASE, group->unit_id);
+ // restart timers
+ group->limit = skill_get_time(group->skill_id,group->skill_lv);
+ unit->limit = skill_get_time(group->skill_id,group->skill_lv);
+ // apply effect to all units standing on it
+ map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
+ break;
+
+ case UNT_CALLFAMILY:
+ {
+ struct map_session_data *sd = NULL;
+ if(group->val1) {
+ sd = map_charid2sd(group->val1);
+ group->val1 = 0;
+ if (sd && !map[sd->bl.m].flag.nowarp)
+ pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
+ }
+ if(group->val2) {
+ sd = map_charid2sd(group->val2);
+ group->val2 = 0;
+ if (sd && !map[sd->bl.m].flag.nowarp)
+ pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
+ }
+ skill_delunit(unit);
+ }
+ break;
+
+ case UNT_REVERBERATION:
+ if( unit->val1 <= 0 ) { // If it was deactivated.
+ skill_delunit(unit);
+ break;
+ }
+ clif_changetraplook(bl,UNT_USED_TRAPS);
+ map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick);
+ group->limit = DIFF_TICK(tick,group->tick)+1000;
+ unit->limit = DIFF_TICK(tick,group->tick)+1000;
+ group->unit_id = UNT_USED_TRAPS;
+ break;
+
+ case UNT_FEINTBOMB: {
+ struct block_list *src = map_id2bl(group->src_id);
+ if( src )
+ map_foreachinrange(skill_area_sub, &group->unit->bl, unit->range, splash_target(src), src, SC_FEINTBOMB, group->skill_lv, tick, BCT_ENEMY|SD_ANIMATION|1, skill_castend_damage_id);
+ skill_delunit(unit);
+ break;
+ }
+
+ case UNT_BANDING:
+ {
+ struct block_list *src = map_id2bl(group->src_id);
+ struct status_change *sc;
+ if( !src || (sc = status_get_sc(src)) == NULL || !sc->data[SC_BANDING] )
+ {
+ skill_delunit(unit);
+ break;
+ }
+ // This unit isn't removed while SC_BANDING is active.
+ group->limit = DIFF_TICK(tick+group->interval,group->tick);
+ unit->limit = DIFF_TICK(tick+group->interval,group->tick);
+ }
+ break;
+
+ default:
+ skill_delunit(unit);
+ }
+ }
+ else
+ {// skill unit is still active
+ switch( group->unit_id )
+ {
+ case UNT_ICEWALL:
+ // icewall loses 50 hp every second
+ unit->val1 -= SKILLUNITTIMER_INTERVAL/20; // trap's hp
+ if( unit->val1 <= 0 && unit->limit + group->tick > tick + 700 )
+ unit->limit = DIFF_TICK(tick+700,group->tick);
+ break;
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_CLAYMORETRAP:
+ case UNT_FREEZINGTRAP:
+ case UNT_TALKIEBOX:
+ case UNT_ANKLESNARE:
+ if( unit->val1 <= 0 ) {
+ if( group->unit_id == UNT_ANKLESNARE && group->val2 > 0 )
+ skill_delunit(unit);
+ else {
+ clif_changetraplook(bl, group->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS);
+ group->limit = DIFF_TICK(tick, group->tick) + 1500;
+ group->unit_id = UNT_USED_TRAPS;
+ }
+ }
+ break;
+ case UNT_REVERBERATION:
+ if( unit->val1 <= 0 ){
+ clif_changetraplook(bl,UNT_USED_TRAPS);
+ map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick);
+ group->limit = DIFF_TICK(tick,group->tick)+1000;
+ unit->limit = DIFF_TICK(tick,group->tick)+1000;
+ group->unit_id = UNT_USED_TRAPS;
+ }
+ break;
+ case UNT_WALLOFTHORN:
+ if( unit->val1 <= 0 ) {
+ group->unit_id = UNT_USED_TRAPS;
+ group->limit = DIFF_TICK(tick, group->tick) + 1500;
+ }
+ break;
+ }
+ }
+
+ //Don't continue if unit or even group is expired and has been deleted.
+ if( !group || !unit->alive )
+ return 0;
+
+ dissonance = skill_dance_switch(unit, 0);
+
+ if( unit->range >= 0 && group->interval != -1 )
+ {
+ if( battle_config.skill_wall_check )
+ map_foreachinshootrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick);
+ else
+ map_foreachinrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick);
+
+ if(unit->range == -1) //Unit disabled, but it should not be deleted yet.
+ group->unit_id = UNT_USED_TRAPS;
+
+ if( group->unit_id == UNT_TATAMIGAESHI )
+ {
+ unit->range = -1; //Disable processed cell.
+ if (--group->val1 <= 0) // number of live cells
+ { //All tiles were processed, disable skill.
+ group->target_flag=BCT_NOONE;
+ group->bl_flag= BL_NUL;
+ }
+ }
+ }
+
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ return 0;
+}
+/*==========================================
+ * Executes on all skill units every SKILLUNITTIMER_INTERVAL miliseconds.
+ *------------------------------------------*/
+int skill_unit_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ map_freeblock_lock();
+
+ skillunit_db->foreach(skillunit_db, skill_unit_timer_sub, tick);
+
+ map_freeblock_unlock();
+
+ return 0;
+}
+
+static int skill_unit_temp[20]; // temporary storage for tracking skill unit skill ids as players move in/out of them
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_move_sub (struct block_list* bl, va_list ap)
+{
+ struct skill_unit* unit = (struct skill_unit *)bl;
+ struct skill_unit_group* group = unit->group;
+
+ struct block_list* target = va_arg(ap,struct block_list*);
+ unsigned int tick = va_arg(ap,unsigned int);
+ int flag = va_arg(ap,int);
+
+ bool dissonance;
+ uint16 skill_id;
+ int i;
+
+ nullpo_ret(group);
+
+ if( !unit->alive || target->prev == NULL )
+ return 0;
+
+ if( flag&1 && ( unit->group->skill_id == PF_SPIDERWEB || unit->group->skill_id == GN_THORNS_TRAP ) )
+ return 0; // Fiberlock is never supposed to trigger on skill_unit_move. [Inkfish]
+
+ dissonance = skill_dance_switch(unit, 0);
+
+ //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
+ skill_id = unit->group->skill_id;
+
+ if( unit->group->interval != -1 && !(skill_get_unit_flag(skill_id)&UF_DUALMODE) && skill_id != BD_LULLABY ) //Lullaby is the exception, bugreport:411
+ { //Non-dualmode unit skills with a timer don't trigger when walking, so just return
+ if( dissonance ) skill_dance_switch(unit, 1);
+ return 0;
+ }
+
+ //Target-type check.
+ if( !(group->bl_flag&target->type && battle_check_target(&unit->bl,target,group->target_flag) > 0) )
+ {
+ if( group->src_id == target->id && group->state.song_dance&0x2 )
+ { //Ensemble check to see if they went out/in of the area [Skotlex]
+ if( flag&1 )
+ {
+ if( flag&2 )
+ { //Clear this skill id.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == skill_id );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = 0;
+ }
+ }
+ else
+ {
+ if( flag&2 )
+ { //Store this skill id.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = skill_id;
+ else
+ ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n");
+ }
+
+ }
+
+ if( flag&4 )
+ skill_unit_onleft(skill_id,target,tick);
+ }
+
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ return 0;
+ }
+ else
+ {
+ if( flag&1 )
+ {
+ int result = skill_unit_onplace(unit,target,tick);
+ if( flag&2 && result )
+ { //Clear skill ids we have stored in onout.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == result );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = 0;
+ }
+ }
+ else
+ {
+ int result = skill_unit_onout(unit,target,tick);
+ if( flag&2 && result )
+ { //Store this unit id.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = skill_id;
+ else
+ ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n");
+ }
+ }
+
+ //TODO: Normally, this is dangerous since the unit and group could be freed
+ //inside the onout/onplace functions. Currently it is safe because we know song/dance
+ //cells do not get deleted within them. [Skotlex]
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ if( flag&4 )
+ skill_unit_onleft(skill_id,target,tick);
+
+ return 1;
+ }
+}
+
+/*==========================================
+ * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft)
+ * Flag values:
+ * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout)
+ * flag&2: this function is being invoked twice as a bl moves, store in memory the affected
+ * units to figure out when they have left a group.
+ * flag&4: Force a onleft event (triggered when the bl is killed, for example)
+ *------------------------------------------*/
+int skill_unit_move (struct block_list *bl, unsigned int tick, int flag)
+{
+ nullpo_ret(bl);
+
+ if( bl->prev == NULL )
+ return 0;
+
+ if( flag&2 && !(flag&1) )
+ { //Onout, clear data
+ memset(skill_unit_temp, 0, sizeof(skill_unit_temp));
+ }
+
+ map_foreachincell(skill_unit_move_sub,bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag);
+
+ if( flag&2 && flag&1 )
+ { //Onplace, check any skill units you have left.
+ int i;
+ for( i = 0; i < ARRAYLENGTH(skill_unit_temp); i++ )
+ if( skill_unit_temp[i] )
+ skill_unit_onleft(skill_unit_temp[i], bl, tick);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 dx, int16 dy)
+{
+ int i,j;
+ unsigned int tick = gettick();
+ int *m_flag;
+ struct skill_unit *unit1;
+ struct skill_unit *unit2;
+
+ if (group == NULL)
+ return 0;
+ if (group->unit_count<=0)
+ return 0;
+ if (group->unit==NULL)
+ return 0;
+
+ if (skill_get_unit_flag(group->skill_id)&UF_ENSEMBLE)
+ return 0; //Ensembles may not be moved around.
+
+ if( group->unit_id == UNT_ICEWALL || group->unit_id == UNT_WALLOFTHORN )
+ return 0; //Icewalls and Wall of Thorns don't get knocked back
+
+ m_flag = (int *) aCalloc(group->unit_count, sizeof(int));
+ // m_flag
+ // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed)
+ // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed)
+ // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed)
+ // 3: Both 1+2.
+ for(i=0;i<group->unit_count;i++){
+ unit1=&group->unit[i];
+ if (!unit1->alive || unit1->bl.m!=m)
+ continue;
+ for(j=0;j<group->unit_count;j++){
+ unit2=&group->unit[j];
+ if (!unit2->alive)
+ continue;
+ if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){
+ m_flag[i] |= 0x1;
+ }
+ if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){
+ m_flag[i] |= 0x2;
+ }
+ }
+ }
+ j = 0;
+ for (i=0;i<group->unit_count;i++) {
+ unit1=&group->unit[i];
+ if (!unit1->alive)
+ continue;
+ if (!(m_flag[i]&0x2)) {
+ if (group->state.song_dance&0x1) //Cancel dissonance effect.
+ skill_dance_overlap(unit1, 0);
+ map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4);
+ }
+ //Move Cell using "smart" criteria (avoid useless moving around)
+ switch(m_flag[i])
+ {
+ case 0:
+ //Cell moves independently, safely move it.
+ map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick);
+ break;
+ case 1:
+ //Cell moves unto another cell, look for a replacement cell that won't collide
+ //and has no cell moving into it (flag == 2)
+ for(;j<group->unit_count;j++)
+ {
+ if(m_flag[j]!=2 || !group->unit[j].alive)
+ continue;
+ //Move to where this cell would had moved.
+ unit2 = &group->unit[j];
+ map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick);
+ j++; //Skip this cell as we have used it.
+ break;
+ }
+ break;
+ case 2:
+ case 3:
+ break; //Don't move the cell as a cell will end on this tile anyway.
+ }
+ if (!(m_flag[i]&0x2)) { //We only moved the cell in 0-1
+ if (group->state.song_dance&0x1) //Check for dissonance effect.
+ skill_dance_overlap(unit1, 1);
+ clif_skill_setunit(unit1);
+ map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1);
+ }
+ }
+ aFree(m_flag);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger, int qty)
+{
+ int i,j;
+
+ nullpo_ret(sd);
+
+ if(nameid<=0)
+ return 0;
+
+ for(i=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if(skill_produce_db[i].nameid == nameid ){
+ if((j=skill_produce_db[i].req_skill)>0 &&
+ pc_checkskill(sd,j) < skill_produce_db[i].req_skill_lv)
+ continue; // must iterate again to check other skills that produce it. [malufett]
+ if( j > 0 && sd->menuskill_id > 0 && sd->menuskill_id != j )
+ continue; // special case
+ break;
+ }
+ }
+
+ if( i >= MAX_SKILL_PRODUCE_DB )
+ return 0;
+
+ if( pc_checkadditem(sd, nameid, qty) == ADDITEM_OVERAMOUNT )
+ {// cannot carry the produced stuff
+ return 0;
+ }
+
+ if(trigger>=0){
+ if(trigger>20) { // Non-weapon, non-food item (itemlv must match)
+ if(skill_produce_db[i].itemlv!=trigger)
+ return 0;
+ } else if(trigger>10) { // Food (any item level between 10 and 20 will do)
+ if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>20)
+ return 0;
+ } else { // Weapon (itemlv must be higher or equal)
+ if(skill_produce_db[i].itemlv>trigger)
+ return 0;
+ }
+ }
+
+ for(j=0;j<MAX_PRODUCE_RESOURCE;j++){
+ int id,x,y;
+ if( (id=skill_produce_db[i].mat_id[j]) <= 0 )
+ continue;
+ if(skill_produce_db[i].mat_amount[j] <= 0) {
+ if(pc_search_inventory(sd,id) < 0)
+ return 0;
+ }
+ else {
+ for(y=0,x=0;y<MAX_INVENTORY;y++)
+ if( sd->status.inventory[y].nameid == id )
+ x+=sd->status.inventory[y].amount;
+ if(x<qty*skill_produce_db[i].mat_amount[j])
+ return 0;
+ }
+ }
+ return i+1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_produce_mix (struct map_session_data *sd, uint16 skill_id, int nameid, int slot1, int slot2, int slot3, int qty)
+{
+ int slot[3];
+ int i,sc,ele,idx,equip,wlv,make_per = 0,flag = 0,skill_lv = 0;
+ int num = -1; // exclude the recipe
+ struct status_data *status;
+ struct item_data* data;
+
+ nullpo_ret(sd);
+ status = status_get_status_data(&sd->bl);
+
+ if( sd->skill_id_old == skill_id )
+ skill_lv = sd->skill_lv_old;
+
+ if( !(idx=skill_can_produce_mix(sd,nameid,-1, qty)) )
+ return 0;
+ idx--;
+
+ if (qty < 1)
+ qty = 1;
+
+ if (!skill_id) //A skill can be specified for some override cases.
+ skill_id = skill_produce_db[idx].req_skill;
+
+ if( skill_id == GC_RESEARCHNEWPOISON )
+ skill_id = GC_CREATENEWPOISON;
+
+ slot[0]=slot1;
+ slot[1]=slot2;
+ slot[2]=slot3;
+
+ for(i=0,sc=0,ele=0;i<3;i++){ //Note that qty should always be one if you are using these!
+ int j;
+ if( slot[i]<=0 )
+ continue;
+ j = pc_search_inventory(sd,slot[i]);
+ if(j < 0)
+ continue;
+ if(slot[i]==1000){ /* Star Crumb */
+ pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
+ sc++;
+ }
+ if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */
+ static const int ele_table[4]={3,1,4,2};
+ pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
+ ele=ele_table[slot[i]-994];
+ }
+ }
+
+ if( skill_id == RK_RUNEMASTERY ) {
+ int temp_qty, skill_lv = pc_checkskill(sd,skill_id);
+ data = itemdb_search(nameid);
+
+ if( skill_lv == 10 ) temp_qty = 1 + rnd()%3;
+ else if( skill_lv > 5 ) temp_qty = 1 + rnd()%2;
+ else temp_qty = 1;
+
+ if (data->stack.inventory) {
+ for( i = 0; i < MAX_INVENTORY; i++ ) {
+ if( sd->status.inventory[i].nameid == nameid ) {
+ if( sd->status.inventory[i].amount >= data->stack.amount ) {
+ 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 >= data->stack.amount )
+ temp_qty = data->stack.amount - sd->status.inventory[i].amount;
+ }
+ break;
+ }
+ }
+ }
+ qty = temp_qty;
+ }
+
+ for(i=0;i<MAX_PRODUCE_RESOURCE;i++){
+ int j,id,x;
+ if( (id=skill_produce_db[idx].mat_id[i]) <= 0 )
+ continue;
+ num++;
+ x=( skill_id == RK_RUNEMASTERY ? 1 : qty)*skill_produce_db[idx].mat_amount[i];
+ do{
+ int y=0;
+ j = pc_search_inventory(sd,id);
+
+ if(j >= 0){
+ y = sd->status.inventory[j].amount;
+ if(y>x)y=x;
+ pc_delitem(sd,j,y,0,0,LOG_TYPE_PRODUCE);
+ } else
+ ShowError("skill_produce_mix: material item error\n");
+
+ x-=y;
+ }while( j>=0 && x>0 );
+ }
+
+ if( (equip = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB )) )
+ wlv = itemdb_wlv(nameid);
+ if(!equip) {
+ switch(skill_id){
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG]
+ i = pc_checkskill(sd,skill_id);
+ make_per = sd->status.job_level*20 + status->dex*10 + status->luk*10; //Base chance
+ switch(nameid){
+ case 998: // Iron
+ make_per += 4000+i*500; // Temper Iron bonus: +26/+32/+38/+44/+50
+ break;
+ case 999: // Steel
+ make_per += 3000+i*500; // Temper Steel bonus: +35/+40/+45/+50/+55
+ break;
+ case 1000: //Star Crumb
+ make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex]
+ break;
+ default: // Enchanted Stones
+ make_per += 1000+i*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35
+ break;
+ }
+ break;
+ case ASC_CDP:
+ 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]
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*50
+ + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20
+ + (status->int_/2)*10 + status->dex*10+status->luk*10;
+ if(merc_is_hom_active(sd->hd)) {//Player got a homun
+ int skill;
+ if((skill=merc_hom_checkskill(sd->hd,HVAN_INSTRUCT)) > 0) //His homun is a vanil with instruction change
+ make_per += skill*100; //+1% bonus per level
+ }
+ switch(nameid){
+ case 501: // Red Potion
+ case 503: // Yellow Potion
+ case 504: // White Potion
+ make_per += (1+rnd()%100)*10 + 2000;
+ break;
+ case 970: // Alcohol
+ make_per += (1+rnd()%100)*10 + 1000;
+ break;
+ case 7135: // Bottle Grenade
+ case 7136: // Acid Bottle
+ case 7137: // Plant Bottle
+ case 7138: // Marine Sphere Bottle
+ make_per += (1+rnd()%100)*10;
+ break;
+ case 546: // Condensed Yellow Potion
+ make_per -= (1+rnd()%50)*10;
+ break;
+ case 547: // Condensed White Potion
+ case 7139: // Glistening Coat
+ make_per -= (1+rnd()%100)*10;
+ break;
+ //Common items, recieve no bonus or penalty, listed just because they are commonly produced
+ case 505: // Blue Potion
+ case 545: // Condensed Red Potion
+ case 605: // Anodyne
+ case 606: // Aloevera
+ default:
+ break;
+ }
+ if(battle_config.pp_rate != 100)
+ make_per = make_per * battle_config.pp_rate / 100;
+ break;
+ case SA_CREATECON: // Elemental Converter Creation
+ make_per = 100000; // should be 100% success rate
+ break;
+ /**
+ * Rune Knight
+ **/
+ case RK_RUNEMASTERY:
+ {
+ int A = 100 * (51 + 2 * pc_checkskill(sd, skill_id));
+ int B = 100 * status->dex / 30 + 10 * (status->luk + sd->status.job_level);
+ int C = 100 * cap_value(sd->itemid,0,100); //itemid depend on makerune()
+ int D = 0;
+ switch (nameid) { //rune rank it_diff 9 craftable rune
+ case ITEMID_BERKANA:
+ D = -2000;
+ break; //Rank S
+ case ITEMID_NAUTHIZ:
+ case ITEMID_URUZ:
+ D = -1500;
+ break; //Rank A
+ case ITEMID_ISA:
+ case ITEMID_WYRD:
+ D = -1000;
+ break; //Rank B
+ case ITEMID_RAIDO:
+ case ITEMID_THURISAZ:
+ case ITEMID_HAGALAZ:
+ case ITEMID_OTHILA:
+ D = -500;
+ break; //Rank C
+ default: D = -1500;
+ break; //not specified =-15%
+ }
+ make_per = A + B + C + D;
+ break;
+ }
+ /**
+ * Guilotine Cross
+ **/
+ case GC_CREATENEWPOISON:
+ make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON);
+ qty = 1+rnd()%pc_checkskill(sd,GC_RESEARCHNEWPOISON);
+ break;
+ case GN_CHANGEMATERIAL:
+ for(i=0; i<MAX_SKILL_PRODUCE_DB; i++)
+ if( skill_changematerial_db[i].itemid == nameid ){
+ make_per = skill_changematerial_db[i].rate * 10;
+ break;
+ }
+ break;
+ case GN_S_PHARMACY:
+ {
+ int difficulty = 0;
+
+ difficulty = (620 - 20 * skill_lv);// (620 - 20 * Skill Level)
+
+ make_per = status->int_ + status->dex/2 + status->luk + sd->status.job_level + (30+rnd()%120) + // (Caster?s INT) + (Caster?s DEX / 2) + (Caster?s LUK) + (Caster?s Job Level) + Random number between (30 ~ 150) +
+ (sd->status.base_level-100) + pc_checkskill(sd, AM_LEARNINGPOTION) + pc_checkskill(sd, CR_FULLPROTECTION)*(4+rnd()%6); // (Caster?s Base Level - 100) + (Potion Research x 5) + (Full Chemical Protection Skill Level) x (Random number between 4 ~ 10)
+
+ switch(nameid){// difficulty factor
+ case 12422: case 12425:
+ case 12428:
+ difficulty += 10;
+ break;
+ case 6212: case 12426:
+ difficulty += 15;
+ break;
+ case 13264: case 12423:
+ case 12427: case 12436:
+ difficulty += 20;
+ break;
+ case 6210: case 6211:
+ case 12437:
+ difficulty += 30;
+ break;
+ case 12424: case 12475:
+ difficulty += 40;
+ break;
+ }
+
+ if( make_per >= 400 && make_per > difficulty)
+ qty = 10;
+ else if( make_per >= 300 && make_per > difficulty)
+ qty = 7;
+ else if( make_per >= 100 && make_per > difficulty)
+ qty = 6;
+ else if( make_per >= 1 && make_per > difficulty)
+ qty = 5;
+ else
+ qty = 4;
+ make_per = 10000;
+ }
+ break;
+ case GN_MAKEBOMB:
+ case GN_MIX_COOKING:
+ {
+ int difficulty = 30 + rnd()%120; // Random number between (30 ~ 150)
+
+ make_per = sd->status.job_level / 4 + status->luk / 2 + status->dex / 3; // (Caster?s Job Level / 4) + (Caster?s LUK / 2) + (Caster?s DEX / 3)
+ qty = ~(5 + rnd()%5) + 1;
+
+ switch(nameid){// difficulty factor
+ case 13260:
+ difficulty += 5;
+ break;
+ case 13261: case 13262:
+ difficulty += 10;
+ break;
+ case 12429: case 12430: case 12431:
+ case 12432: case 12433: case 12434:
+ case 13263:
+ difficulty += 15;
+ break;
+ case 13264:
+ difficulty += 20;
+ break;
+ }
+
+ if( make_per >= 30 && make_per > difficulty)
+ qty = 10 + rnd()%2;
+ else if( make_per >= 10 && make_per > difficulty)
+ qty = 10;
+ else if( make_per == 10 && make_per > difficulty)
+ qty = 8;
+ else if( (make_per >= 50 || make_per < 30) && make_per < difficulty)
+ ;// Food/Bomb creation fails.
+ else if( make_per >= 30 && make_per < difficulty)
+ qty = 5;
+
+ if( qty < 0 || (skill_lv == 1 && make_per < difficulty)){
+ qty = ~qty + 1;
+ make_per = 0;
+ }else
+ make_per = 10000;
+ qty = (skill_lv > 1 ? qty : 1);
+ }
+ break;
+ default:
+ if (sd->menuskill_id == AM_PHARMACY &&
+ sd->menuskill_val > 10 && sd->menuskill_val <= 20)
+ { //Assume Cooking Dish
+ if (sd->menuskill_val >= 15) //Legendary Cooking Set.
+ make_per = 10000; //100% Success
+ else
+ make_per = 1200 * (sd->menuskill_val - 10)
+ + 20 * (sd->status.base_level + 1)
+ + 20 * (status->dex + 1)
+ + 100 * (rnd()%(30+5*(sd->cook_mastery/400) - (6+sd->cook_mastery/80)) + (6+sd->cook_mastery/80))
+ - 400 * (skill_produce_db[idx].itemlv - 11 + 1)
+ - 10 * (100 - status->luk + 1)
+ - 500 * (num - 1)
+ - 100 * (rnd()%4 + 1);
+ break;
+ }
+ make_per = 5000;
+ break;
+ }
+ } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG]
+ make_per = 5000 + sd->status.job_level*20 + status->dex*10 + status->luk*10; // Base
+ make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15
+ make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5
+ make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30
+ if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10
+ else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5
+ else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3
+ else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0?
+ if(battle_config.wp_rate != 100)
+ make_per = make_per * battle_config.wp_rate / 100;
+ }
+
+ if (sd->class_&JOBL_BABY) //if it's a Baby Class
+ make_per = (make_per * 50) / 100; //Baby penalty is 50% (bugreport:4847)
+
+ if(make_per < 1) make_per = 1;
+
+
+ if(rnd()%10000 < make_per || qty > 1){ //Success, or crafting multiple items.
+ struct item tmp_item;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid=nameid;
+ tmp_item.amount=1;
+ tmp_item.identify=1;
+ if(equip){
+ tmp_item.card[0]=CARD0_FORGE;
+ tmp_item.card[1]=((sc*5)<<8)+ele;
+ tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->status.char_id,1);
+ } else {
+ //Flag is only used on the end, so it can be used here. [Skotlex]
+ switch (skill_id) {
+ case BS_DAGGER:
+ case BS_SWORD:
+ case BS_TWOHANDSWORD:
+ case BS_AXE:
+ case BS_MACE:
+ case BS_KNUCKLE:
+ case BS_SPEAR:
+ flag = battle_config.produce_item_name_input&0x1;
+ break;
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ 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:
+ flag = battle_config.produce_item_name_input&0x10;
+ break;
+ default:
+ flag = battle_config.produce_item_name_input&0x80;
+ break;
+ }
+ if (flag) {
+ tmp_item.card[0]=CARD0_CREATE;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->status.char_id,1);
+ }
+ }
+
+// if(log_config.produce > 0)
+// log_produce(sd,nameid,slot1,slot2,slot3,1);
+//TODO update PICKLOG
+
+ if(equip){
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG]
+ pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point
+ } else {
+ int fame = 0;
+ tmp_item.amount = 0;
+
+ for (i=0; i< qty; i++) { //Apply quantity modifiers.
+ if( (skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY) && make_per > 1){
+ tmp_item.amount = qty;
+ break;
+ }
+ if (rnd()%10000 < make_per || qty == 1) { //Success
+ tmp_item.amount++;
+ if(nameid < 545 || nameid > 547)
+ continue;
+ if( skill_id != AM_PHARMACY &&
+ skill_id != AM_TWILIGHT1 &&
+ skill_id != AM_TWILIGHT2 &&
+ skill_id != AM_TWILIGHT3 )
+ continue;
+ //Add fame as needed.
+ switch(++sd->potion_success_counter) {
+ case 3:
+ fame+=1; // Success to prepare 3 Condensed Potions in a row
+ break;
+ case 5:
+ fame+=3; // Success to prepare 5 Condensed Potions in a row
+ break;
+ case 7:
+ fame+=10; // Success to prepare 7 Condensed Potions in a row
+ break;
+ case 10:
+ fame+=50; // Success to prepare 10 Condensed Potions in a row
+ sd->potion_success_counter = 0;
+ break;
+ }
+ } else //Failure
+ sd->potion_success_counter = 0;
+ }
+
+ if (fame)
+ pc_addfame(sd,fame);
+ //Visual effects and the like.
+ switch (skill_id) {
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ case ASC_CDP:
+ clif_produceeffect(sd,2,nameid);
+ clif_misceffect(&sd->bl,5);
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ break;
+ 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.
+ clif_specialeffect(&sd->bl, 608, AREA);
+ if( sd->cook_mastery < 1999 )
+ pc_setglobalreg(sd, "COOK_MASTERY",sd->cook_mastery + ( 1 << ( (skill_produce_db[idx].itemlv - 11) / 2 ) ) * 5);
+ }
+ break;
+ }
+ }
+ if ( skill_id == GN_CHANGEMATERIAL && tmp_item.amount) { //Success
+ int j, k = 0;
+ for(i=0; i<MAX_SKILL_PRODUCE_DB; i++)
+ if( skill_changematerial_db[i].itemid == nameid ){
+ for(j=0; j<5; j++){
+ if( rnd()%1000 < skill_changematerial_db[i].qty_rate[j] ){
+ tmp_item.amount = qty * skill_changematerial_db[i].qty[j];
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ k++;
+ }
+ }
+ break;
+ }
+ if( k ){
+ clif_msg_skill(sd,skill_id,0x627);
+ return 1;
+ }
+ } else if (tmp_item.amount) { //Success
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ if( skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY )
+ clif_msg_skill(sd,skill_id,0x627);
+ return 1;
+ }
+ }
+ //Failure
+// if(log_config.produce)
+// log_produce(sd,nameid,slot1,slot2,slot3,0);
+//TODO update PICKLOG
+
+ if(equip){
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ } else {
+ switch (skill_id) {
+ case ASC_CDP: //25% Damage yourself, and display same effect as failed potion.
+ status_percent_damage(NULL, &sd->bl, -25, 0, true);
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ clif_produceeffect(sd,3,nameid);
+ clif_misceffect(&sd->bl,6);
+ sd->potion_success_counter = 0; // Fame point system [DracoRPG]
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ break;
+ case RK_RUNEMASTERY:
+ case GC_CREATENEWPOISON:
+ clif_produceeffect(sd,3,nameid);
+ clif_misceffect(&sd->bl,6);
+ break;
+ case GN_MIX_COOKING: {
+ struct item tmp_item;
+ const int compensation[5] = {13265, 13266, 13267, 12435, 13268};
+ int rate = rnd()%500;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ if( rate < 50) i = 4;
+ else if( rate < 100) i = 2+rnd()%1;
+ else if( rate < 250 ) i = 1;
+ else if( rate < 500 ) i = 0;
+ tmp_item.nameid = compensation[i];
+ tmp_item.amount = qty;
+ tmp_item.identify = 1;
+ if( pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ clif_msg_skill(sd,skill_id,0x628);
+ }
+ break;
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ clif_msg_skill(sd,skill_id,0x628);
+ break;
+ default:
+ if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 )
+ { //Cooking items.
+ clif_specialeffect(&sd->bl, 609, AREA);
+ if( sd->cook_mastery > 0 )
+ pc_setglobalreg(sd, "COOK_MASTERY", sd->cook_mastery - ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 ));
+ }
+ }
+ }
+ return 0;
+}
+
+int skill_arrow_create (struct map_session_data *sd, int nameid)
+{
+ int i,j,flag,index=-1;
+ struct item tmp_item;
+
+ nullpo_ret(sd);
+
+ if(nameid <= 0)
+ return 1;
+
+ for(i=0;i<MAX_SKILL_ARROW_DB;i++)
+ if(nameid == skill_arrow_db[i].nameid) {
+ index = i;
+ break;
+ }
+
+ if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0)
+ return 1;
+
+ pc_delitem(sd,j,1,0,0,LOG_TYPE_PRODUCE);
+ for(i=0;i<MAX_ARROW_RESOURCE;i++) {
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.identify = 1;
+ tmp_item.nameid = skill_arrow_db[index].cre_id[i];
+ tmp_item.amount = skill_arrow_db[index].cre_amount[i];
+ if(battle_config.produce_item_name_input&0x4) {
+ tmp_item.card[0]=CARD0_CREATE;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->status.char_id,1);
+ }
+ if(tmp_item.nameid <= 0 || tmp_item.amount <= 0)
+ continue;
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ return 0;
+}
+int skill_poisoningweapon( struct map_session_data *sd, int nameid) {
+ sc_type type;
+ int chance, i;
+ nullpo_ret(sd);
+ if( nameid <= 0 || (i = pc_search_inventory(sd,nameid)) < 0 || pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME) ) {
+ clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ switch( nameid )
+ { // t_lv used to take duration from skill_get_time2
+ case PO_PARALYSE: type = SC_PARALYSE; break;
+ case PO_PYREXIA: type = SC_PYREXIA; break;
+ case PO_DEATHHURT: type = SC_DEATHHURT; break;
+ case PO_LEECHESEND: type = SC_LEECHESEND; break;
+ case PO_VENOMBLEED: type = SC_VENOMBLEED; break;
+ case PO_TOXIN: type = SC_TOXIN; break;
+ case PO_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; break;
+ case PO_OBLIVIONCURSE: type = SC_OBLIVIONCURSE; break;
+ default:
+ clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+
+ chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv
+ sc_start4(&sd->bl, SC_POISONINGWEAPON, 100, pc_checkskill(sd, GC_RESEARCHNEWPOISON), //in Aegis it store the level of GC_RESEARCHNEWPOISON in val1
+ type, chance, 0, skill_get_time(GC_POISONINGWEAPON, sd->menuskill_val));
+
+ return 0;
+}
+
+static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id)
+{
+ struct status_change *sc = status_get_sc(bl);
+
+ // non-offensive and non-magic skills do not affect the status
+ if (skill_get_nk(skill_id)&NK_NO_DAMAGE || !(skill_get_type(skill_id)&BF_MAGIC))
+ return;
+
+ if (sc && sc->count && sc->data[SC_MAGICPOWER])
+ {
+ if (sc->data[SC_MAGICPOWER]->val4)
+ {
+ status_change_end(bl, SC_MAGICPOWER, INVALID_TIMER);
+ }
+ else
+ {
+ sc->data[SC_MAGICPOWER]->val4 = 1;
+ status_calc_bl(bl, status_sc2scb_flag(SC_MAGICPOWER));
+#ifndef RENEWAL
+ if(bl->type == BL_PC){// update current display.
+ clif_updatestatus(((TBL_PC *)bl),SP_MATK1);
+ clif_updatestatus(((TBL_PC *)bl),SP_MATK2);
+ }
+#endif
+ }
+ }
+}
+
+
+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,LOG_TYPE_CONSUME) )
+ {
+ clif_skill_fail(sd,NC_MAGICDECOY,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+
+ // Spawn Position
+ pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME);
+ 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_, "", SZ_SMALL, AI_NONE);
+ if( md ) {
+ md->master_id = sd->bl.id;
+ md->special_state.ai = AI_FLORA;
+ 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, max_preserve, skill_id, point;
+ struct status_change *sc;
+
+ nullpo_ret(sd);
+
+ sc = status_get_sc(&sd->bl);
+ status_change_end(&sd->bl, SC_STOP, INVALID_TIMER);
+
+ for(i=SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) if( sc && !sc->data[i] ) break;
+ if( i > SC_MAXSPELLBOOK )
+ {
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 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 ) return 0;
+
+ if( !pc_checkskill(sd, (skill_id = skill_spellbook_db[i].skill_id)) )
+ { // 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, USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP, 0);
+ return 0;
+ }
+
+ max_preserve = 4 * pc_checkskill(sd, WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10;
+ point = skill_spellbook_db[i].point;
+
+ if( sc && sc->data[SC_READING_SB] ){
+ if( (sc->data[SC_READING_SB]->val2 + point) > max_preserve )
+ {
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT, 0);
+ return 0;
+ }
+ for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett]
+ if( !sc->data[i] ){
+ sc->data[SC_READING_SB]->val2 += point; // increase points
+ sc_start4(&sd->bl, (sc_type)i, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
+ break;
+ }
+ }
+ }else{
+ sc_start2(&sd->bl, SC_READING_SB, 100, 0, point, INVALID_TIMER);
+ sc_start4(&sd->bl, SC_MAXSPELLBOOK, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
+ }
+
+ return 1;
+}
+int skill_select_menu(struct map_session_data *sd,uint16 skill_id) {
+ int id, lv, prob, aslvl = 0;
+ nullpo_ret(sd);
+
+ if (sd->sc.data[SC_STOP]) {
+ aslvl = sd->sc.data[SC_STOP]->val1;
+ status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
+ }
+
+ if( skill_id >= GS_GLITTERING || skill_get_type(skill_id) != BF_MAGIC ||
+ (id = sd->status.skill[skill_id].id) == 0 || sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED ) {
+ clif_skill_fail(sd,SC_AUTOSHADOWSPELL,0,0);
+ return 0;
+ }
+
+ lv = (aslvl + 1) / 2; // The level the skill will be autocasted
+ lv = min(lv,sd->status.skill[skill_id].lv);
+ prob = (aslvl == 10) ? 15 : (32 - 2 * aslvl); // Probability at level 10 was increased to 15.
+ sc_start4(&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl));
+ return 0;
+}
+int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv, unsigned short* item_list) {
+ int i;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ if( n <= 0 )
+ return 1;
+
+ for( i = 0; i < n; i++ ) {
+ int nameid, add_amount, del_amount, idx, product, flag;
+ struct item tmp_item;
+
+ idx = item_list[i*2+0]-2;
+ del_amount = item_list[i*2+1];
+
+ if( skill_lv == 2 )
+ del_amount -= (del_amount % 10);
+ add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ;
+
+ if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+ switch( nameid ) {
+ // Level 1
+ case 994: product = 990; break; // Flame Heart -> Red Blood.
+ case 995: product = 991; break; // Mystic Frozen -> Crystal Blue.
+ case 996: product = 992; break; // Rough Wind -> Wind of Verdure.
+ case 997: product = 993; break; // Great Nature -> Green Live.
+ // Level 2
+ case 990: product = 994; break; // Red Blood -> Flame Heart.
+ case 991: product = 995; break; // Crystal Blue -> Mystic Frozen.
+ case 992: product = 996; break; // Wind of Verdure -> Rough Wind.
+ case 993: product = 997; break; // Green Live -> Great Nature.
+ default:
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+ if( pc_delitem(sd,idx,del_amount,0,1,LOG_TYPE_CONSUME) ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+ if( skill_lv == 2 && rnd()%100 < 25 ) { // At level 2 have a fail chance. You loose your items if it fails.
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = product;
+ tmp_item.amount = add_amount;
+ tmp_item.identify = 1;
+
+ if( tmp_item.amount ) {
+ if( (flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME)) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) {
+ int i, j, k, c, p = 0, nameid, amount;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ // Search for objects that can be created.
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( skill_produce_db[i].itemlv == 26 ) {
+ p = 0;
+ do {
+ c = 0;
+ // Verification of overlap between the objects required and the list submitted.
+ for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) {
+ if( skill_produce_db[i].mat_id[j] > 0 ) {
+ for( k = 0; k < n; k++ ) {
+ int idx = item_list[k*2+0]-2;
+ nameid = sd->status.inventory[idx].nameid;
+ amount = item_list[k*2+1];
+ if( nameid > 0 && sd->status.inventory[idx].identify == 0 ){
+ clif_msg_skill(sd,GN_CHANGEMATERIAL,0x62D);
+ return 0;
+ }
+ if( nameid == skill_produce_db[i].mat_id[j] && (amount-p*skill_produce_db[i].mat_amount[j]) >= skill_produce_db[i].mat_amount[j]
+ && (amount-p*skill_produce_db[i].mat_amount[j])%skill_produce_db[i].mat_amount[j] == 0 ) // must be in exact amount
+ c++; // match
+ }
+ }
+ else
+ break; // No more items required
+ }
+ p++;
+ } while(n == j && c == n);
+ p--;
+ if ( p > 0 ) {
+ skill_produce_mix(sd,GN_CHANGEMATERIAL,skill_produce_db[i].nameid,0,0,0,p);
+ return 1;
+ }
+ }
+ }
+
+ if( p == 0)
+ clif_msg_skill(sd,GN_CHANGEMATERIAL,0x623);
+
+ return 0;
+}
+/**
+ * for Royal Guard's LG_TRAMPLE
+ **/
+static int skill_destroy_trap( struct block_list *bl, va_list ap ) {
+ struct skill_unit *su = (struct skill_unit *)bl;
+ struct skill_unit_group *sg;
+ unsigned int tick;
+
+ nullpo_ret(su);
+ tick = va_arg(ap, unsigned int);
+
+ if (su->alive && (sg = su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP) {
+ switch( sg->unit_id ) {
+ case UNT_LANDMINE:
+ case UNT_CLAYMORETRAP:
+ case UNT_BLASTMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLUSTERBOMB:
+ case UNT_FIRINGTRAP:
+ case UNT_ICEBOUNDTRAP:
+ map_foreachinrange(skill_trap_splash,&su->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &su->bl,tick);
+ break;
+ }
+ // Traps aren't recovered.
+ skill_delunit(su);
+ }
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct map_session_data *sd = map_id2sd(id);
+ struct skill_cd * cd = NULL;
+
+ if (data <= 0 || data >= MAX_SKILL)
+ return 0;
+ if (!sd) return 0;
+ if (sd->blockskill[data] != (0x1|(tid&0xFE))) return 0;
+
+ if( ( cd = idb_get(skillcd_db,sd->status.char_id) ) ) {
+ int i,cursor;
+ ARR_FIND( 0, cd->cursor+1, cursor, cd->skidx[cursor] == data );
+ cd->duration[cursor] = 0;
+ cd->skidx[cursor] = 0;
+ cd->nameid[cursor] = 0;
+ // compact the cool down list
+ for( i = 0, cursor = 0; i < cd->cursor; i++ ) {
+ if( cd->duration[i] == 0 )
+ continue;
+ if( cursor != i ) {
+ cd->duration[cursor] = cd->duration[i];
+ cd->skidx[cursor] = cd->skidx[i];
+ cd->nameid[cursor] = cd->nameid[i];
+ }
+ cursor++;
+ }
+ if( cursor == 0 )
+ idb_remove(skillcd_db,sd->status.char_id);
+ else
+ cd->cursor = cursor;
+ }
+
+ sd->blockskill[data] = 0;
+ return 1;
+}
+
+/**
+ * flags a singular skill as being blocked from persistent usage.
+ * @param sd the player the skill delay affects
+ * @param skill_id the skill which should be delayed
+ * @param tick the length of time the delay should last
+ * @param load whether this assignment is being loaded upon player login
+ * @return 0 if successful, -1 otherwise
+ */
+int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick, bool load)
+{
+ int oskill_id = skill_id;
+ struct skill_cd* cd = NULL;
+ uint16 idx = skill_get_index(skill_id);
+
+ nullpo_retr (-1, sd);
+
+ if (idx == 0)
+ return -1;
+
+ if (tick < 1) {
+ sd->blockskill[idx] = 0;
+ return -1;
+ }
+
+ if( battle_config.display_status_timers )
+ clif_skill_cooldown(sd, idx, tick);
+
+ if( !load )
+ {// not being loaded initially so ensure the skill delay is recorded
+ if( !(cd = idb_get(skillcd_db,sd->status.char_id)) )
+ {// create a new skill cooldown object for map storage
+ CREATE( cd, struct skill_cd, 1 );
+ idb_put( skillcd_db, sd->status.char_id, cd );
+ }
+
+ // record the skill duration in the database map
+ cd->duration[cd->cursor] = tick;
+ cd->skidx[cd->cursor] = idx;
+ cd->nameid[cd->cursor] = oskill_id;
+ cd->cursor++;
+ }
+
+ sd->blockskill[idx] = 0x1|(0xFE&add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,idx));
+ return 0;
+}
+
+int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data) //[orn]
+{
+ struct homun_data *hd = (TBL_HOM*) map_id2bl(id);
+ if (data <= 0 || data >= MAX_SKILL)
+ return 0;
+ if (hd) hd->blockskill[data] = 0;
+
+ return 1;
+}
+
+int skill_blockhomun_start(struct homun_data *hd, uint16 skill_id, int tick) //[orn]
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr (-1, hd);
+
+
+ if (idx == 0)
+ return -1;
+
+ if (tick < 1) {
+ hd->blockskill[idx] = 0;
+ return -1;
+ }
+ hd->blockskill[idx] = 1;
+ return add_timer(gettick() + tick, skill_blockhomun_end, hd->bl.id, idx);
+}
+
+int skill_blockmerc_end(int tid, unsigned int tick, int id, intptr_t data) //[orn]
+{
+ struct mercenary_data *md = (TBL_MER*)map_id2bl(id);
+ if( data <= 0 || data >= MAX_SKILL )
+ return 0;
+ if( md ) md->blockskill[data] = 0;
+
+ return 1;
+}
+
+int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick)
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr (-1, md);
+
+ if (idx == 0)
+ return -1;
+ if( tick < 1 )
+ {
+ md->blockskill[idx] = 0;
+ return -1;
+ }
+ md->blockskill[idx] = 1;
+ return add_timer(gettick() + tick, skill_blockmerc_end, md->bl.id, idx);
+}
+/**
+ * Adds a new skill unit entry for this player to recast after map load
+ **/
+void skill_usave_add(struct map_session_data * sd, uint16 skill_id, uint16 skill_lv) {
+ struct skill_usave * sus = NULL;
+
+ if( idb_exists(skillusave_db,sd->status.char_id) ) {
+ idb_remove(skillusave_db,sd->status.char_id);
+ }
+
+ CREATE( sus, struct skill_usave, 1 );
+ idb_put( skillusave_db, sd->status.char_id, sus );
+
+ sus->skill_id = skill_id;
+ sus->skill_lv = skill_lv;
+
+ return;
+}
+void skill_usave_trigger(struct map_session_data *sd) {
+ struct skill_usave * sus = NULL;
+
+ if( ! (sus = idb_get(skillusave_db,sd->status.char_id)) ) {
+ return;
+ }
+
+ skill_unitsetting(&sd->bl,sus->skill_id,sus->skill_lv,sd->bl.x,sd->bl.y,0);
+
+ idb_remove(skillusave_db,sd->status.char_id);
+
+ return;
+}
+/*
+ *
+ */
+int skill_split_str (char *str, char **val, int num)
+{
+ int i;
+
+ for( i = 0; i < num && str; i++ )
+ {
+ val[i] = str;
+ str = strchr(str,',');
+ if( str )
+ *str++=0;
+ }
+
+ return i;
+}
+/*
+ *
+ */
+int skill_split_atoi (char *str, int *val)
+{
+ int i, j, diff, step = 1;
+
+ for (i=0; i<MAX_SKILL_LEVEL; i++) {
+ if (!str) break;
+ val[i] = atoi(str);
+ str = strchr(str,':');
+ if (str)
+ *str++=0;
+ }
+ if(i==0) //No data found.
+ return 0;
+ if(i==1)
+ { //Single value, have the whole range have the same value.
+ for (; i < MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+ }
+ //Check for linear change with increasing steps until we reach half of the data acquired.
+ for (step = 1; step <= i/2; step++)
+ {
+ diff = val[i-1] - val[i-step-1];
+ for(j = i-1; j >= step; j--)
+ if ((val[j]-val[j-step]) != diff)
+ break;
+
+ if (j>=step) //No match, try next step.
+ continue;
+
+ for(; i < MAX_SKILL_LEVEL; i++)
+ { //Apply linear increase
+ val[i] = val[i-step]+diff;
+ if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases.
+ { val[i] = 1; diff = 0; step = 1; }
+ }
+ return i;
+ }
+ //Okay.. we can't figure this one out, just fill out the stuff with the previous value.
+ for (;i<MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+}
+
+/*
+ *
+ */
+void skill_init_unit_layout (void)
+{
+ int i,j,size,pos = 0;
+
+ memset(skill_unit_layout,0,sizeof(skill_unit_layout));
+
+ // standard square layouts go first
+ for (i=0; i<=MAX_SQUARE_LAYOUT; i++) {
+ size = i*2+1;
+ skill_unit_layout[i].count = size*size;
+ for (j=0; j<size*size; j++) {
+ skill_unit_layout[i].dx[j] = (j%size-i);
+ skill_unit_layout[i].dy[j] = (j/size-i);
+ }
+ }
+
+ // afterwards add special ones
+ pos = i;
+ for (i=0;i<MAX_SKILL_DB;i++) {
+ if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1)
+ continue;
+ if( i >= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ int skill = i;
+
+ if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ skill -= EL_SKILLRANGEMIN;
+ skill += EL_SKILLBASE;
+ }
+ if( skill == EL_FIRE_MANTLE ) {
+ static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1};
+ static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0};
+ skill_unit_layout[pos].count = 8;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else {
+ switch (i) {
+ case MG_FIREWALL:
+ case WZ_ICEWALL:
+ case WL_EARTHSTRAIN://Warlock
+ // these will be handled later
+ break;
+ case PR_SANCTUARY:
+ case NPC_EVILLAND: {
+ static const int dx[] = {
+ -1, 0, 1,-2,-1, 0, 1, 2,-2,-1,
+ 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1};
+ static const int dy[]={
+ -2,-2,-2,-1,-1,-1,-1,-1, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 21;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PR_MAGNUS: {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case MH_POISON_MIST:
+ case AS_VENOMDUST: {
+ static const int dx[] = {-1, 0, 0, 0, 1};
+ static const int dy[] = { 0,-1, 0, 1, 0};
+ skill_unit_layout[pos].count = 5;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS: {
+ static const int dx[] = {
+ 0, 0,-1, 0, 1,-2,-1, 0, 1, 2,
+ -4,-3,-2,-1, 0, 1, 2, 3, 4,-2,
+ -1, 0, 1, 2,-1, 0, 1, 0, 0};
+ static const int dy[] = {
+ -4,-3,-2,-2,-2,-1,-1,-1,-1,-1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 2, 2, 2, 3, 4};
+ skill_unit_layout[pos].count = 29;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PF_FOGWALL: {
+ static const int dx[] = {
+ -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = {
+ -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
+ skill_unit_layout[pos].count = 15;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PA_GOSPEL: {
+ static const int dx[] = {
+ -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
+ 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
+ -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,
+ -1, 0, 1};
+ static const int dy[] = {
+ -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
+ -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case NJ_KAENSIN: {
+ static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2};
+ skill_unit_layout[pos].count = 24;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case NJ_TATAMIGAESHI: {
+ //Level 1 (count 4, cross of 3x3)
+ static const int dx1[] = {-1, 1, 0, 0};
+ static const int dy1[] = { 0, 0,-1, 1};
+ //Level 2-3 (count 8, cross of 5x5)
+ static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0};
+ static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2};
+ //Level 4-5 (count 12, cross of 7x7
+ static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0};
+ static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3};
+ //lv1
+ j = 0;
+ skill_unit_layout[pos].count = 4;
+ memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1));
+ memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1));
+ skill_db[i].unit_layout_type[j] = pos;
+ //lv2/3
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 8;
+ memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2));
+ memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //lv4/5
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 12;
+ memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3));
+ memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //Fill in the rest using lv 5.
+ for (;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ //Skip, this way the check below will fail and continue to the next skill.
+ pos++;
+ }
+ break;
+ case GN_WALLOFTHORN: {
+ static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0};
+ static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 16;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ default:
+ ShowError("unknown unit layout at skill %d\n",i);
+ break;
+ }
+ }
+ if (!skill_unit_layout[pos].count)
+ continue;
+ for (j=0;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ pos++;
+ }
+
+ // firewall and icewall have 8 layouts (direction-dependent)
+ firewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ if (i&1) {
+ skill_unit_layout[pos].count = 5;
+ if (i&0x2) {
+ int dx[] = {-1,-1, 0, 0, 1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 1, 1 ,0, 0,-1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else {
+ skill_unit_layout[pos].count = 3;
+ if (i%4==0) {
+ int dx[] = {-1, 0, 1};
+ int dy[] = { 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 0, 0, 0};
+ int dy[] = {-1, 0, 1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+ icewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ skill_unit_layout[pos].count = 5;
+ if (i&1) {
+ if (i&0x2) {
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 2, 1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 2, 1 ,0,-1,-2};
+ int dy[] = { 2, 1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else {
+ if (i%4==0) {
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 0, 0, 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 0, 0, 0, 0, 0};
+ int dy[] = {-2,-1, 0, 1, 2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+ earthstrain_unit_pos = pos;
+ for( i = 0; i < 8; i++ )
+ { // For each Direction
+ skill_unit_layout[pos].count = 15;
+ switch( i )
+ {
+ case 0: case 1: case 3: case 4: case 5: case 7:
+ {
+ int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
+ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ 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++;
+ }
+
+}
+
+int skill_block_check(struct block_list *bl, sc_type type , uint16 skill_id) {
+ int inf = 0;
+ struct status_change *sc = status_get_sc(bl);
+
+ if( !sc || !bl || !skill_id )
+ return 0; // Can do it
+
+ switch(type){
+ case SC_STASIS:
+ inf = skill_get_inf2(skill_id);
+ if( inf == INF2_SONG_DANCE || /*skill_get_inf2(skill_id) == INF2_CHORUS_SKILL ||*/ inf == INF2_SPIRIT_SKILL )
+ return 1; // Can't do it.
+ switch( skill_id )
+ {
+ 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:
+
+ // 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.
+ }
+ break;
+ case SC_KAGEHUMI:
+ switch(skill_id){
+ case TF_HIDING: case AS_CLOAKING: case GC_CLOAKINGEXCEED: case SC_SHADOWFORM:
+ case MI_HARMONIZE: case CG_MARIONETTE: case AL_TELEPORT: case TF_BACKSLIDING:
+ case RA_CAMOUFLAGE: case ST_CHASEWALK: case GD_EMERGENCYCALL:
+ return 1; // needs more info
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) {
+ int type = 0;
+
+ switch( skill_id ) {
+ case SO_SUMMON_AGNI: type = 2114; break;
+ case SO_SUMMON_AQUA: type = 2117; break;
+ case SO_SUMMON_VENTUS: type = 2120; break;
+ case SO_SUMMON_TERA: type = 2123; break;
+ }
+
+ type += skill_lv - 1;
+
+ return type;
+}
+
+/**
+ * reload stored skill cooldowns when a player logs in.
+ * @param sd the affected player structure
+ */
+void skill_cooldown_load(struct map_session_data * sd)
+{
+ int i;
+ struct skill_cd* cd = NULL;
+
+ // always check to make sure the session properly exists
+ nullpo_retv(sd);
+
+ if( !(cd = idb_get(skillcd_db, sd->status.char_id)) )
+ {// no skill cooldown is associated with this character
+ return;
+ }
+
+ // process each individual cooldown associated with the character
+ for( i = 0; i < cd->cursor; i++ )
+ {
+ // block the skill from usage but ensure it is not recorded (load = true)
+ skill_blockpc_start_( sd, cd->nameid[i], cd->duration[i], true );
+ }
+}
+
+/*==========================================
+ * sub-function of DB reading.
+ * skill_db.txt
+ *------------------------------------------*/
+
+static bool skill_parse_row_skilldb(char* split[], int columns, int current)
+{// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx;
+ if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX)
+ || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX)
+ || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX)
+ || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) {
+ ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id);
+ return false;
+ }
+
+ idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].range);
+ skill_db[idx].hit = atoi(split[2]);
+ skill_db[idx].inf = atoi(split[3]);
+ skill_split_atoi(split[4],skill_db[idx].element);
+ skill_db[idx].nk = (int)strtol(split[5], NULL, 0);
+ skill_split_atoi(split[6],skill_db[idx].splash);
+ skill_db[idx].max = atoi(split[7]);
+ skill_split_atoi(split[8],skill_db[idx].num);
+
+ if( strcmpi(split[9],"yes") == 0 )
+ skill_db[idx].castcancel = 1;
+ else
+ skill_db[idx].castcancel = 0;
+ skill_db[idx].cast_def_rate = atoi(split[10]);
+ skill_db[idx].inf2 = (int)strtol(split[11], NULL, 0);
+ skill_split_atoi(split[12],skill_db[idx].maxcount);
+ if( strcmpi(split[13],"weapon") == 0 )
+ skill_db[idx].skill_type = BF_WEAPON;
+ else if( strcmpi(split[13],"magic") == 0 )
+ skill_db[idx].skill_type = BF_MAGIC;
+ else if( strcmpi(split[13],"misc") == 0 )
+ skill_db[idx].skill_type = BF_MISC;
+ else
+ skill_db[idx].skill_type = 0;
+ skill_split_atoi(split[14],skill_db[idx].blewcount);
+ safestrncpy(skill_db[idx].name, trim(split[15]), sizeof(skill_db[idx].name));
+ safestrncpy(skill_db[idx].desc, trim(split[16]), sizeof(skill_db[idx].desc));
+ strdb_iput(skilldb_name2id, skill_db[idx].name, skill_id);
+
+ return true;
+}
+
+static bool skill_parse_row_requiredb(char* split[], int columns, int current)
+{// skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10
+ char* p;
+ int j;
+
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].hp);
+ skill_split_atoi(split[2],skill_db[idx].mhp);
+ skill_split_atoi(split[3],skill_db[idx].sp);
+ skill_split_atoi(split[4],skill_db[idx].hp_rate);
+ skill_split_atoi(split[5],skill_db[idx].sp_rate);
+ skill_split_atoi(split[6],skill_db[idx].zeny);
+
+ //Wich weapon type are required, see doc/item_db for types
+ p = split[7];
+ for( j = 0; j < 32; j++ )
+ {
+ int l = atoi(p);
+ if( l == 99 ) // Any weapon
+ {
+ skill_db[idx].weapon = 0;
+ break;
+ }
+ else
+ skill_db[idx].weapon |= 1<<l;
+ p = strchr(p,':');
+ if(!p)
+ break;
+ p++;
+ }
+
+ //FIXME: document this
+ p = split[8];
+ for( j = 0; j < 32; j++ )
+ {
+ int l = atoi(p);
+ if( l == 99 ) // Any ammo type
+ {
+ skill_db[idx].ammo = 0xFFFFFFFF;
+ break;
+ }
+ else if( l ) // 0 stands for no requirement
+ skill_db[idx].ammo |= 1<<l;
+ p = strchr(p,':');
+ if( !p )
+ break;
+ p++;
+ }
+ skill_split_atoi(split[9],skill_db[idx].ammo_qty);
+
+ if( strcmpi(split[10],"hiding")==0 ) skill_db[idx].state = ST_HIDING;
+ else if( strcmpi(split[10],"cloaking")==0 ) skill_db[idx].state = ST_CLOAKING;
+ else if( strcmpi(split[10],"hidden")==0 ) skill_db[idx].state = ST_HIDDEN;
+ else if( strcmpi(split[10],"riding")==0 ) skill_db[idx].state = ST_RIDING;
+ else if( strcmpi(split[10],"falcon")==0 ) skill_db[idx].state = ST_FALCON;
+ else if( strcmpi(split[10],"cart")==0 ) skill_db[idx].state = ST_CART;
+ else if( strcmpi(split[10],"shield")==0 ) skill_db[idx].state = ST_SHIELD;
+ else if( strcmpi(split[10],"sight")==0 ) skill_db[idx].state = ST_SIGHT;
+ else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[idx].state = ST_EXPLOSIONSPIRITS;
+ else if( strcmpi(split[10],"cartboost")==0 ) skill_db[idx].state = ST_CARTBOOST;
+ else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[idx].state = ST_RECOV_WEIGHT_RATE;
+ else if( strcmpi(split[10],"move_enable")==0 ) skill_db[idx].state = ST_MOVE_ENABLE;
+ else if( strcmpi(split[10],"water")==0 ) skill_db[idx].state = ST_WATER;
+ /**
+ * New States
+ **/
+ else if( strcmpi(split[10],"dragon")==0 ) skill_db[idx].state = ST_RIDINGDRAGON;
+ else if( strcmpi(split[10],"warg")==0 ) skill_db[idx].state = ST_WUG;
+ else if( strcmpi(split[10],"ridingwarg")==0 ) skill_db[idx].state = ST_RIDINGWUG;
+ else if( strcmpi(split[10],"mado")==0 ) skill_db[idx].state = ST_MADO;
+ else if( strcmpi(split[10],"elementalspirit")==0 ) skill_db[idx].state = ST_ELEMENTALSPIRIT;
+ else if (strcmpi(split[10], "poisonweapon") == 0) skill_db[idx].state = ST_POISONINGWEAPON;
+ else if (strcmpi(split[10], "rollingcutter") == 0) skill_db[idx].state = ST_ROLLINGCUTTER;
+ else if (strcmpi(split[10], "mh_fighting") == 0) skill_db[idx].state = ST_MH_FIGHTING;
+ else if (strcmpi(split[10], "mh_grappling") == 0) skill_db[idx].state = ST_MH_GRAPPLING;
+
+ /**
+ * Unknown or no state
+ **/
+ else skill_db[idx].state = ST_NONE;
+
+ skill_split_atoi(split[11],skill_db[idx].spiritball);
+ for( j = 0; j < MAX_SKILL_ITEM_REQUIRE; j++ ) {
+ skill_db[idx].itemid[j] = atoi(split[12+ 2*j]);
+ skill_db[idx].amount[j] = atoi(split[13+ 2*j]);
+ }
+
+ return true;
+}
+
+static bool skill_parse_row_castdb(char* split[], int columns, int current)
+{// skill_id,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].cast);
+ skill_split_atoi(split[2],skill_db[idx].delay);
+ skill_split_atoi(split[3],skill_db[idx].walkdelay);
+ skill_split_atoi(split[4],skill_db[idx].upkeep_time);
+ skill_split_atoi(split[5],skill_db[idx].upkeep_time2);
+ skill_split_atoi(split[6],skill_db[idx].cooldown);
+#ifdef RENEWAL_CAST
+ skill_split_atoi(split[7],skill_db[idx].fixed_cast);
+#endif
+ return true;
+}
+
+static bool skill_parse_row_castnodexdb(char* split[], int columns, int current)
+{// Skill id,Cast,Delay (optional)
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].castnodex);
+ if( split[2] ) // optional column
+ skill_split_atoi(split[2],skill_db[idx].delaynodex);
+
+ return true;
+}
+
+static bool skill_parse_row_nocastdb(char* split[], int columns, int current)
+{// skill_id,Flag
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_db[idx].nocast |= atoi(split[1]);
+
+ return true;
+}
+
+static bool skill_parse_row_unitdb(char* split[], int columns, int current)
+{// ID,unit ID,unit ID 2,layout,range,interval,target,flag
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_db[idx].unit_id[0] = strtol(split[1],NULL,16);
+ skill_db[idx].unit_id[1] = strtol(split[2],NULL,16);
+ skill_split_atoi(split[3],skill_db[idx].unit_layout_type);
+ skill_split_atoi(split[4],skill_db[idx].unit_range);
+ skill_db[idx].unit_interval = atoi(split[5]);
+
+ if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx].unit_target = BCT_NOENEMY;
+ else if( strcmpi(split[6],"friend")==0 ) skill_db[idx].unit_target = BCT_NOENEMY;
+ else if( strcmpi(split[6],"party")==0 ) skill_db[idx].unit_target = BCT_PARTY;
+ else if( strcmpi(split[6],"ally")==0 ) skill_db[idx].unit_target = BCT_PARTY|BCT_GUILD;
+ else if( strcmpi(split[6],"guild")==0 ) skill_db[idx].unit_target = BCT_GUILD;
+ else if( strcmpi(split[6],"all")==0 ) skill_db[idx].unit_target = BCT_ALL;
+ else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx].unit_target = BCT_ENEMY;
+ else if( strcmpi(split[6],"self")==0 ) skill_db[idx].unit_target = BCT_SELF;
+ else if( strcmpi(split[6],"noone")==0 ) skill_db[idx].unit_target = BCT_NOONE;
+ else skill_db[idx].unit_target = strtol(split[6],NULL,16);
+
+ skill_db[idx].unit_flag = strtol(split[7],NULL,16);
+
+ if (skill_db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
+ skill_db[idx].unit_target = BCT_NOENEMY;
+
+ //By default, target just characters.
+ skill_db[idx].unit_target |= BL_CHAR;
+ if (skill_db[idx].unit_flag&UF_NOPC)
+ skill_db[idx].unit_target &= ~BL_PC;
+ if (skill_db[idx].unit_flag&UF_NOMOB)
+ skill_db[idx].unit_target &= ~BL_MOB;
+ if (skill_db[idx].unit_flag&UF_SKILL)
+ skill_db[idx].unit_target |= BL_SKILL;
+
+ return true;
+}
+
+static bool skill_parse_row_producedb(char* split[], int columns, int current)
+{// ProduceItemID,ItemLV,RequireSkill,Requireskill_lv,MaterialID1,MaterialAmount1,......
+ int x,y;
+
+ int i = atoi(split[0]);
+ if( !i )
+ return false;
+
+ skill_produce_db[current].nameid = i;
+ skill_produce_db[current].itemlv = atoi(split[1]);
+ skill_produce_db[current].req_skill = atoi(split[2]);
+ skill_produce_db[current].req_skill_lv = atoi(split[3]);
+
+ for( x = 4, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_PRODUCE_RESOURCE; x += 2, y++ )
+ {
+ skill_produce_db[current].mat_id[y] = atoi(split[x]);
+ skill_produce_db[current].mat_amount[y] = atoi(split[x+1]);
+ }
+
+ return true;
+}
+
+static bool skill_parse_row_createarrowdb(char* split[], int columns, int current)
+{// SourceID,MakeID1,MakeAmount1,...,MakeID5,MakeAmount5
+ int x,y;
+
+ int i = atoi(split[0]);
+ if( !i )
+ return false;
+
+ skill_arrow_db[current].nameid = i;
+
+ for( x = 1, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_ARROW_RESOURCE; x += 2, y++ )
+ {
+ skill_arrow_db[current].cre_id[y] = atoi(split[x]);
+ skill_arrow_db[current].cre_amount[y] = atoi(split[x+1]);
+ }
+
+ return true;
+}
+static bool skill_parse_row_spellbookdb(char* split[], int columns, int current)
+{// skill_id,PreservePoints
+
+ uint16 skill_id = atoi(split[0]);
+ int points = atoi(split[1]);
+ int nameid = atoi(split[2]);
+
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
+ ShowError("spellbook_db: Invalid skill ID %d\n", skill_id);
+ if ( !skill_get_inf(skill_id) )
+ ShowError("spellbook_db: Passive skills cannot be memorized (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ if( points < 1 )
+ ShowError("spellbook_db: PreservePoints have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ else
+ {
+ skill_spellbook_db[current].skill_id = skill_id;
+ skill_spellbook_db[current].point = points;
+ skill_spellbook_db[current].nameid = nameid;
+
+ return true;
+ }
+
+ return false;
+}
+static bool skill_parse_row_improvisedb(char* split[], int columns, int current)
+{// SkillID,Rate
+ uint16 skill_id = atoi(split[0]);
+ short j = atoi(split[1]);
+
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) {
+ ShowError("skill_improvise_db: Invalid skill ID %d\n", skill_id);
+ return false;
+ }
+ if ( !skill_get_inf(skill_id) ) {
+ ShowError("skill_improvise_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+ if( j < 1 ) {
+ ShowError("skill_improvise_db: Chances have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+ if( current >= MAX_SKILL_IMPROVISE_DB ) {
+ ShowError("skill_improvise_db: Maximum amount of entries reached (%d), increase MAX_SKILL_IMPROVISE_DB\n",MAX_SKILL_IMPROVISE_DB);
+ }
+ skill_improvise_db[current].skill_id = skill_id;
+ skill_improvise_db[current].per = j; // Still need confirm it.
+
+ return true;
+}
+static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current)
+{// SkillID
+ uint16 skill_id = atoi(split[0]);
+
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
+ {
+ ShowError("magicmushroom_db: Invalid skill ID %d\n", skill_id);
+ return false;
+ }
+ if ( !skill_get_inf(skill_id) )
+ {
+ ShowError("magicmushroom_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+
+ skill_magicmushroom_db[current].skill_id = skill_id;
+
+ return true;
+}
+
+static bool skill_parse_row_reproducedb(char* split[], int column, int current) {
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx )
+ return false;
+
+ skill_reproduce_db[idx] = true;
+
+ return true;
+}
+
+
+static bool skill_parse_row_abradb(char* split[], int columns, int current)
+{// skill_id,DummyName,RequiredHocusPocusLevel,Rate
+ uint16 skill_id = atoi(split[0]);
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
+ {
+ ShowError("abra_db: Invalid skill ID %d\n", skill_id);
+ return false;
+ }
+ if ( !skill_get_inf(skill_id) )
+ {
+ ShowError("abra_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+
+ skill_abra_db[current].skill_id = skill_id;
+ skill_abra_db[current].req_lv = atoi(split[2]);
+ skill_abra_db[current].per = atoi(split[3]);
+
+ return true;
+}
+
+static bool skill_parse_row_changematerialdb(char* split[], int columns, int current)
+{// ProductID,BaseRate,MakeAmount1,MakeAmountRate1...,MakeAmount5,MakeAmountRate5
+ uint16 skill_id = atoi(split[0]);
+ short j = atoi(split[1]);
+ int x,y;
+
+ for(x=0; x<MAX_SKILL_PRODUCE_DB; x++){
+ if( skill_produce_db[x].nameid == skill_id )
+ if( skill_produce_db[x].req_skill == GN_CHANGEMATERIAL )
+ break;
+ }
+
+ if( x >= MAX_SKILL_PRODUCE_DB ){
+ ShowError("changematerial_db: Not supported item ID(%d) for Change Material. \n", skill_id);
+ return false;
+ }
+
+ if( current >= MAX_SKILL_PRODUCE_DB ) {
+ ShowError("skill_changematerial_db: Maximum amount of entries reached (%d), increase MAX_SKILL_PRODUCE_DB\n",MAX_SKILL_PRODUCE_DB);
+ }
+
+ skill_changematerial_db[current].itemid = skill_id;
+ skill_changematerial_db[current].rate = j;
+
+ for( x = 2, y = 0; x+1 < columns && split[x] && split[x+1] && y < 5; x += 2, y++ )
+ {
+ skill_changematerial_db[current].qty[y] = atoi(split[x]);
+ skill_changematerial_db[current].qty_rate[y] = atoi(split[x+1]);
+ }
+
+ return true;
+}
+
+/*===============================
+ * DB reading.
+ * skill_db.txt
+ * skill_require_db.txt
+ * skill_cast_db.txt
+ * skill_castnodex_db.txt
+ * skill_nocast_db.txt
+ * skill_unit_db.txt
+ * produce_db.txt
+ * create_arrow_db.txt
+ * abra_db.txt
+ *------------------------------*/
+static void skill_readdb(void)
+{
+ // init skill db structures
+ db_clear(skilldb_name2id);
+ memset(skill_db,0,sizeof(skill_db));
+ 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));
+ memset(skill_reproduce_db,0,sizeof(skill_reproduce_db));
+ memset(skill_changematerial_db,0,sizeof(skill_changematerial_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, DBPATH"skill_db.txt" , ',', 17, 17, MAX_SKILL_DB, skill_parse_row_skilldb);
+ sv_readdb(db_path, DBPATH"skill_require_db.txt" , ',', 32, 32, MAX_SKILL_DB, skill_parse_row_requiredb);
+#ifdef RENEWAL_CAST
+ sv_readdb(db_path, "re/skill_cast_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_castdb);
+#else
+ sv_readdb(db_path, "pre-re/skill_cast_db.txt" , ',', 7, 7, MAX_SKILL_DB, skill_parse_row_castdb);
+#endif
+ sv_readdb(db_path, DBPATH"skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill_parse_row_castnodexdb);
+ sv_readdb(db_path, DBPATH"skill_unit_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_unitdb);
+
+ sv_readdb(db_path, DBPATH"skill_nocast_db.txt" , ',', 2, 2, MAX_SKILL_DB, skill_parse_row_nocastdb);
+
+ skill_init_unit_layout();
+ 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);
+ sv_readdb(db_path, "skill_reproduce_db.txt", ',', 1, 1, MAX_SKILL_DB, skill_parse_row_reproducedb);
+ sv_readdb(db_path, "skill_improvise_db.txt" , ',', 2, 2, MAX_SKILL_IMPROVISE_DB, skill_parse_row_improvisedb);
+ sv_readdb(db_path, "skill_changematerial_db.txt" , ',', 4, 4+2*5, MAX_SKILL_PRODUCE_DB, skill_parse_row_changematerialdb);
+
+}
+
+void skill_reload (void) {
+ struct s_mapiterator *iter;
+ struct map_session_data *sd;
+ skill_readdb();
+ /* lets update all players skill tree : so that if any skill modes were changed they're properly updated */
+ iter = mapit_getallusers();
+ for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
+ clif_skillinfoblock(sd);
+ mapit_free(iter);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int do_init_skill (void)
+{
+ skilldb_name2id = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, 0);
+ skill_readdb();
+
+ group_db = idb_alloc(DB_OPT_BASE);
+ skillunit_db = idb_alloc(DB_OPT_BASE);
+ skillcd_db = idb_alloc(DB_OPT_RELEASE_DATA);
+ skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA);
+ skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_NONE);
+ skill_timer_ers = ers_new(sizeof(struct skill_timerskill),"skill.c::skill_timer_ers",ERS_OPT_NONE);
+
+ add_timer_func_list(skill_unit_timer,"skill_unit_timer");
+ add_timer_func_list(skill_castend_id,"skill_castend_id");
+ add_timer_func_list(skill_castend_pos,"skill_castend_pos");
+ add_timer_func_list(skill_timerskill,"skill_timerskill");
+ add_timer_func_list(skill_blockpc_end, "skill_blockpc_end");
+
+ add_timer_interval(gettick()+SKILLUNITTIMER_INTERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INTERVAL);
+
+ return 0;
+}
+
+int do_final_skill(void)
+{
+ db_destroy(skilldb_name2id);
+ db_destroy(group_db);
+ db_destroy(skillunit_db);
+ db_destroy(skillcd_db);
+ db_destroy(skillusave_db);
+ ers_destroy(skill_unit_ers);
+ ers_destroy(skill_timer_ers);
+ return 0;
+}
diff --git a/src/map/status.c b/src/map/status.c index 649cfa1ae..cc322b686 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -1,11294 +1,11290 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include "../common/cbasetypes.h" -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/random.h" -#include "../common/showmsg.h" -#include "../common/malloc.h" -#include "../common/utils.h" -#include "../common/ers.h" -#include "../common/strlib.h" - -#include "map.h" -#include "path.h" -#include "pc.h" -#include "pet.h" -#include "npc.h" -#include "mob.h" -#include "clif.h" -#include "guild.h" -#include "skill.h" -#include "itemdb.h" -#include "battle.h" -#include "chrif.h" -#include "skill.h" -#include "status.h" -#include "script.h" -#include "unit.h" -#include "homunculus.h" -#include "mercenary.h" -#include "elemental.h" -#include "vending.h" - -#include <time.h> -#include <stdio.h> -#include <stdlib.h> -#include <memory.h> -#include <string.h> -#include <math.h> - -//Regen related flags. -enum e_regen -{ - RGN_HP = 0x01, - RGN_SP = 0x02, - RGN_SHP = 0x04, - RGN_SSP = 0x08, -}; - -static int max_weight_base[CLASS_COUNT]; -static int hp_coefficient[CLASS_COUNT]; -static int hp_coefficient2[CLASS_COUNT]; -static int hp_sigma_val[CLASS_COUNT][MAX_LEVEL+1]; -static int sp_coefficient[CLASS_COUNT]; -#ifdef RENEWAL_ASPD -static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE+1]; -#else -static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE]; //[blackhole89] -#endif - -// bonus values and upgrade chances for refining equipment -static struct { - int chance[MAX_REFINE]; // success chance - int bonus[MAX_REFINE]; // cumulative fixed bonus damage - int randombonus_max[MAX_REFINE]; // cumulative maximum random bonus damage -} refine_info[REFINE_TYPE_MAX]; - -static int atkmods[3][MAX_WEAPON_TYPE]; //ATK weapon modification for size (size_fix.txt) -static char job_bonus[CLASS_COUNT][MAX_LEVEL]; - -static struct eri *sc_data_ers; //For sc_data entries -static struct status_data dummy_status; - -int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus] -int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex] -//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only -//to avoid cards exploits - -static sc_type SkillStatusChangeTable[MAX_SKILL]; // skill -> status -static int StatusIconChangeTable[SC_MAX]; // status -> "icon" (icon is a bit of a misnomer, since there exist values with no icon associated) -static unsigned int StatusChangeFlagTable[SC_MAX]; // status -> flags -static int StatusSkillChangeTable[SC_MAX]; // status -> skill -static int StatusRelevantBLTypes[SI_MAX]; // "icon" -> enum bl_type (for clif_status_change to identify for which bl types to send packets) -static unsigned int StatusChangeStateTable[SC_MAX]; // status -> flags - - -/** - * Returns the status change associated with a skill. - * @param skill The skill to look up - * @return The status registered for this skill - **/ -sc_type status_skill2sc(int skill) -{ - int idx = skill_get_index(skill); - if( idx == 0 ) { - ShowError("status_skill2sc: Unsupported skill id %d\n", skill); - return SC_NONE; - } - return SkillStatusChangeTable[idx]; -} - -/** - * Returns the FIRST skill (in order of definition in initChangeTables) to use a given status change. - * Utilized for various duration lookups. Use with caution! - * @param sc The status to look up - * @return A skill associated with the status - **/ -int status_sc2skill(sc_type sc) -{ - if( sc < 0 || sc >= SC_MAX ) { - ShowError("status_sc2skill: Unsupported status change id %d\n", sc); - return 0; - } - - return StatusSkillChangeTable[sc]; -} - -/** - * Returns the status calculation flag associated with a given status change. - * @param sc The status to look up - * @return The scb_flag registered for this status (see enum scb_flag) - **/ -unsigned int status_sc2scb_flag(sc_type sc) -{ - if( sc < 0 || sc >= SC_MAX ) { - ShowError("status_sc2scb_flag: Unsupported status change id %d\n", sc); - return SCB_NONE; - } - - return StatusChangeFlagTable[sc]; -} - -/** - * Returns the bl types which require a status change packet to be sent for a given client status identifier. - * @param type The client-side status identifier to look up (see enum si_type) - * @return The bl types relevant to the type (see enum bl_type) - **/ -int status_type2relevant_bl_types(int type) -{ - if( type < 0 || type >= SI_MAX ) { - ShowError("status_type2relevant_bl_types: Unsupported type %d\n", type); - return SI_BLANK; - } - - return StatusRelevantBLTypes[type]; -} - -#define add_sc(skill,sc) set_sc(skill,sc,SI_BLANK,SCB_NONE) -// indicates that the status displays a visual effect for the affected unit, and should be sent to the client for all supported units -#define set_sc_with_vfx(skill, sc, icon, flag) set_sc((skill), (sc), (icon), (flag)); if((icon) < SI_MAX) StatusRelevantBLTypes[(icon)] |= BL_SCEFFECT - -static void set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag) -{ - uint16 idx = skill_get_index(skill_id); - if( idx == 0 ) { - ShowError("set_sc: Unsupported skill id %d\n", skill_id); - return; - } - if( sc < 0 || sc >= SC_MAX ) { - ShowError("set_sc: Unsupported status change id %d\n", sc); - return; - } - - if( StatusSkillChangeTable[sc] == 0 ) - StatusSkillChangeTable[sc] = skill_id; - if( StatusIconChangeTable[sc] == SI_BLANK ) - StatusIconChangeTable[sc] = icon; - StatusChangeFlagTable[sc] |= flag; - - if( SkillStatusChangeTable[idx] == SC_NONE ) - SkillStatusChangeTable[idx] = sc; -} - -void initChangeTables(void) { - int i; - - for (i = 0; i < SC_MAX; i++) - StatusIconChangeTable[i] = SI_BLANK; - - for (i = 0; i < MAX_SKILL; i++) - SkillStatusChangeTable[i] = SC_NONE; - - for (i = 0; i < SI_MAX; i++) - StatusRelevantBLTypes[i] = BL_PC; - - memset(StatusSkillChangeTable, 0, sizeof(StatusSkillChangeTable)); - memset(StatusChangeFlagTable, 0, sizeof(StatusChangeFlagTable)); - memset(StatusChangeStateTable, 0, sizeof(StatusChangeStateTable)); - - - //First we define the skill for common ailments. These are used in skill_additional_effect through sc cards. [Skotlex] - set_sc( NPC_PETRIFYATTACK , SC_STONE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF ); - set_sc( NPC_WIDEFREEZE , SC_FREEZE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF ); - set_sc( NPC_STUNATTACK , SC_STUN , SI_BLANK , SCB_NONE ); - set_sc( NPC_SLEEPATTACK , SC_SLEEP , SI_BLANK , SCB_NONE ); - set_sc( NPC_POISON , SC_POISON , SI_BLANK , SCB_DEF2|SCB_REGEN ); - set_sc( NPC_CURSEATTACK , SC_CURSE , SI_BLANK , SCB_LUK|SCB_BATK|SCB_WATK|SCB_SPEED ); - set_sc( NPC_SILENCEATTACK , SC_SILENCE , SI_BLANK , SCB_NONE ); - set_sc( NPC_WIDECONFUSE , SC_CONFUSION , SI_BLANK , SCB_NONE ); - set_sc( NPC_BLINDATTACK , SC_BLIND , SI_BLANK , SCB_HIT|SCB_FLEE ); - set_sc( NPC_BLEEDING , SC_BLEEDING , SI_BLEEDING , SCB_REGEN ); - set_sc( NPC_POISON , SC_DPOISON , SI_BLANK , SCB_DEF2|SCB_REGEN ); - - //The main status definitions - add_sc( SM_BASH , SC_STUN ); - set_sc( SM_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); - add_sc( SM_MAGNUM , SC_WATK_ELEMENT ); - set_sc( SM_ENDURE , SC_ENDURE , SI_ENDURE , SCB_MDEF|SCB_DSPD ); - add_sc( MG_SIGHT , SC_SIGHT ); - add_sc( MG_SAFETYWALL , SC_SAFETYWALL ); - add_sc( MG_FROSTDIVER , SC_FREEZE ); - add_sc( MG_STONECURSE , SC_STONE ); - add_sc( AL_RUWACH , SC_RUWACH ); - add_sc( AL_PNEUMA , SC_PNEUMA ); - set_sc( AL_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED ); - set_sc( AL_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED ); - set_sc( AL_CRUCIS , SC_SIGNUMCRUCIS , SI_SIGNUMCRUCIS , SCB_DEF ); - set_sc( AL_ANGELUS , SC_ANGELUS , SI_ANGELUS , SCB_DEF2 ); - set_sc( AL_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); - set_sc( AC_CONCENTRATION , SC_CONCENTRATE , SI_CONCENTRATE , SCB_AGI|SCB_DEX ); - set_sc( TF_HIDING , SC_HIDING , SI_HIDING , SCB_SPEED ); - add_sc( TF_POISON , SC_POISON ); - set_sc( KN_TWOHANDQUICKEN , SC_TWOHANDQUICKEN , SI_TWOHANDQUICKEN , SCB_ASPD ); - add_sc( KN_AUTOCOUNTER , SC_AUTOCOUNTER ); - set_sc( PR_IMPOSITIO , SC_IMPOSITIO , SI_IMPOSITIO , SCB_WATK ); - set_sc( PR_SUFFRAGIUM , SC_SUFFRAGIUM , SI_SUFFRAGIUM , SCB_NONE ); - set_sc( PR_ASPERSIO , SC_ASPERSIO , SI_ASPERSIO , SCB_ATK_ELE ); - set_sc( PR_BENEDICTIO , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE ); - set_sc( PR_SLOWPOISON , SC_SLOWPOISON , SI_SLOWPOISON , SCB_REGEN ); - set_sc( PR_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE ); - set_sc( PR_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN ); - set_sc( PR_GLORIA , SC_GLORIA , SI_GLORIA , SCB_LUK ); - add_sc( PR_LEXDIVINA , SC_SILENCE ); - set_sc( PR_LEXAETERNA , SC_AETERNA , SI_AETERNA , SCB_NONE ); - add_sc( WZ_METEOR , SC_STUN ); - add_sc( WZ_VERMILION , SC_BLIND ); - add_sc( WZ_FROSTNOVA , SC_FREEZE ); - add_sc( WZ_STORMGUST , SC_FREEZE ); - set_sc( WZ_QUAGMIRE , SC_QUAGMIRE , SI_QUAGMIRE , SCB_AGI|SCB_DEX|SCB_ASPD|SCB_SPEED ); - set_sc( BS_ADRENALINE , SC_ADRENALINE , SI_ADRENALINE , SCB_ASPD ); - set_sc( BS_WEAPONPERFECT , SC_WEAPONPERFECTION, SI_WEAPONPERFECTION, SCB_NONE ); - set_sc( BS_OVERTHRUST , SC_OVERTHRUST , SI_OVERTHRUST , SCB_NONE ); - set_sc( BS_MAXIMIZE , SC_MAXIMIZEPOWER , SI_MAXIMIZEPOWER , SCB_REGEN ); - add_sc( HT_LANDMINE , SC_STUN ); - add_sc( HT_ANKLESNARE , SC_ANKLE ); - add_sc( HT_SANDMAN , SC_SLEEP ); - add_sc( HT_FLASHER , SC_BLIND ); - add_sc( HT_FREEZINGTRAP , SC_FREEZE ); - set_sc( AS_CLOAKING , SC_CLOAKING , SI_CLOAKING , SCB_CRI|SCB_SPEED ); - add_sc( AS_SONICBLOW , SC_STUN ); - set_sc( AS_ENCHANTPOISON , SC_ENCPOISON , SI_ENCPOISON , SCB_ATK_ELE ); - set_sc( AS_POISONREACT , SC_POISONREACT , SI_POISONREACT , SCB_NONE ); - add_sc( AS_VENOMDUST , SC_POISON ); - add_sc( AS_SPLASHER , SC_SPLASHER ); - set_sc( NV_TRICKDEAD , SC_TRICKDEAD , SI_TRICKDEAD , SCB_REGEN ); - set_sc( SM_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE ); - add_sc( TF_SPRINKLESAND , SC_BLIND ); - add_sc( TF_THROWSTONE , SC_STUN ); - set_sc( MC_LOUD , SC_LOUD , SI_LOUD , SCB_STR ); - set_sc( MG_ENERGYCOAT , SC_ENERGYCOAT , SI_ENERGYCOAT , SCB_NONE ); - set_sc( NPC_EMOTION , SC_MODECHANGE , SI_BLANK , SCB_MODE ); - add_sc( NPC_EMOTION_ON , SC_MODECHANGE ); - set_sc( NPC_ATTRICHANGE , SC_ELEMENTALCHANGE , SI_ARMOR_PROPERTY , SCB_DEF_ELE ); - add_sc( NPC_CHANGEWATER , SC_ELEMENTALCHANGE ); - add_sc( NPC_CHANGEGROUND , SC_ELEMENTALCHANGE ); - add_sc( NPC_CHANGEFIRE , SC_ELEMENTALCHANGE ); - add_sc( NPC_CHANGEWIND , SC_ELEMENTALCHANGE ); - add_sc( NPC_CHANGEPOISON , SC_ELEMENTALCHANGE ); - add_sc( NPC_CHANGEHOLY , SC_ELEMENTALCHANGE ); - add_sc( NPC_CHANGEDARKNESS , SC_ELEMENTALCHANGE ); - add_sc( NPC_CHANGETELEKINESIS, SC_ELEMENTALCHANGE ); - add_sc( NPC_POISON , SC_POISON ); - add_sc( NPC_BLINDATTACK , SC_BLIND ); - add_sc( NPC_SILENCEATTACK , SC_SILENCE ); - add_sc( NPC_STUNATTACK , SC_STUN ); - add_sc( NPC_PETRIFYATTACK , SC_STONE ); - add_sc( NPC_CURSEATTACK , SC_CURSE ); - add_sc( NPC_SLEEPATTACK , SC_SLEEP ); - add_sc( NPC_MAGICALATTACK , SC_MAGICALATTACK ); - set_sc( NPC_KEEPING , SC_KEEPING , SI_BLANK , SCB_DEF ); - add_sc( NPC_DARKBLESSING , SC_COMA ); - set_sc( NPC_BARRIER , SC_BARRIER , SI_BLANK , SCB_MDEF|SCB_DEF ); - add_sc( NPC_DEFENDER , SC_ARMOR ); - add_sc( NPC_LICK , SC_STUN ); - set_sc( NPC_HALLUCINATION , SC_HALLUCINATION , SI_HALLUCINATION , SCB_NONE ); - add_sc( NPC_REBIRTH , SC_REBIRTH ); - add_sc( RG_RAID , SC_STUN ); -#ifdef RENEWAL - add_sc( RG_RAID , SC_RAID ); - add_sc( RG_BACKSTAP , SC_STUN ); -#endif - set_sc( RG_STRIPWEAPON , SC_STRIPWEAPON , SI_STRIPWEAPON , SCB_WATK ); - set_sc( RG_STRIPSHIELD , SC_STRIPSHIELD , SI_STRIPSHIELD , SCB_DEF ); - set_sc( RG_STRIPARMOR , SC_STRIPARMOR , SI_STRIPARMOR , SCB_VIT ); - set_sc( RG_STRIPHELM , SC_STRIPHELM , SI_STRIPHELM , SCB_INT ); - add_sc( AM_ACIDTERROR , SC_BLEEDING ); - set_sc( AM_CP_WEAPON , SC_CP_WEAPON , SI_CP_WEAPON , SCB_NONE ); - set_sc( AM_CP_SHIELD , SC_CP_SHIELD , SI_CP_SHIELD , SCB_NONE ); - set_sc( AM_CP_ARMOR , SC_CP_ARMOR , SI_CP_ARMOR , SCB_NONE ); - set_sc( AM_CP_HELM , SC_CP_HELM , SI_CP_HELM , SCB_NONE ); - set_sc( CR_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE ); - add_sc( CR_SHIELDCHARGE , SC_STUN ); - set_sc( CR_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE ); - add_sc( CR_HOLYCROSS , SC_BLIND ); - add_sc( CR_GRANDCROSS , SC_BLIND ); - add_sc( CR_DEVOTION , SC_DEVOTION ); - set_sc( CR_PROVIDENCE , SC_PROVIDENCE , SI_PROVIDENCE , SCB_ALL ); - set_sc( CR_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD ); - set_sc( CR_SPEARQUICKEN , SC_SPEARQUICKEN , SI_SPEARQUICKEN , SCB_ASPD|SCB_CRI|SCB_FLEE ); - set_sc( MO_STEELBODY , SC_STEELBODY , SI_STEELBODY , SCB_DEF|SCB_MDEF|SCB_ASPD|SCB_SPEED ); - add_sc( MO_BLADESTOP , SC_BLADESTOP_WAIT ); - add_sc( MO_BLADESTOP , SC_BLADESTOP ); - set_sc( MO_EXPLOSIONSPIRITS , SC_EXPLOSIONSPIRITS, SI_EXPLOSIONSPIRITS, SCB_CRI|SCB_REGEN ); - set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST , SI_BLANK , SCB_REGEN ); -#ifdef RENEWAL - set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST2 , SI_EXTREMITYFIST , SCB_NONE ); -#endif - add_sc( SA_MAGICROD , SC_MAGICROD ); - set_sc( SA_AUTOSPELL , SC_AUTOSPELL , SI_AUTOSPELL , SCB_NONE ); - set_sc( SA_FLAMELAUNCHER , SC_FIREWEAPON , SI_FIREWEAPON , SCB_ATK_ELE ); - set_sc( SA_FROSTWEAPON , SC_WATERWEAPON , SI_WATERWEAPON , SCB_ATK_ELE ); - set_sc( SA_LIGHTNINGLOADER , SC_WINDWEAPON , SI_WINDWEAPON , SCB_ATK_ELE ); - set_sc( SA_SEISMICWEAPON , SC_EARTHWEAPON , SI_EARTHWEAPON , SCB_ATK_ELE ); - set_sc( SA_VOLCANO , SC_VOLCANO , SI_LANDENDOW , SCB_WATK ); - set_sc( SA_DELUGE , SC_DELUGE , SI_LANDENDOW , SCB_MAXHP ); - set_sc( SA_VIOLENTGALE , SC_VIOLENTGALE , SI_LANDENDOW , SCB_FLEE ); - add_sc( SA_REVERSEORCISH , SC_ORCISH ); - add_sc( SA_COMA , SC_COMA ); - set_sc( BD_ENCORE , SC_DANCING , SI_BLANK , SCB_SPEED|SCB_REGEN ); - add_sc( BD_RICHMANKIM , SC_RICHMANKIM ); - set_sc( BD_ETERNALCHAOS , SC_ETERNALCHAOS , SI_BLANK , SCB_DEF2 ); - set_sc( BD_DRUMBATTLEFIELD , SC_DRUMBATTLE , SI_BLANK , SCB_WATK|SCB_DEF ); - set_sc( BD_RINGNIBELUNGEN , SC_NIBELUNGEN , SI_BLANK , SCB_WATK ); - add_sc( BD_ROKISWEIL , SC_ROKISWEIL ); - add_sc( BD_INTOABYSS , SC_INTOABYSS ); - set_sc( BD_SIEGFRIED , SC_SIEGFRIED , SI_BLANK , SCB_ALL ); - add_sc( BA_FROSTJOKER , SC_FREEZE ); - set_sc( BA_WHISTLE , SC_WHISTLE , SI_BLANK , SCB_FLEE|SCB_FLEE2 ); - set_sc( BA_ASSASSINCROSS , SC_ASSNCROS , SI_BLANK , SCB_ASPD ); - add_sc( BA_POEMBRAGI , SC_POEMBRAGI ); - set_sc( BA_APPLEIDUN , SC_APPLEIDUN , SI_BLANK , SCB_MAXHP ); - add_sc( DC_SCREAM , SC_STUN ); - set_sc( DC_HUMMING , SC_HUMMING , SI_BLANK , SCB_HIT ); - set_sc( DC_DONTFORGETME , SC_DONTFORGETME , SI_BLANK , SCB_SPEED|SCB_ASPD ); - set_sc( DC_FORTUNEKISS , SC_FORTUNE , SI_BLANK , SCB_CRI ); - set_sc( DC_SERVICEFORYOU , SC_SERVICE4U , SI_BLANK , SCB_ALL ); - add_sc( NPC_DARKCROSS , SC_BLIND ); - add_sc( NPC_GRANDDARKNESS , SC_BLIND ); - set_sc( NPC_STOP , SC_STOP , SI_STOP , SCB_NONE ); - set_sc( NPC_WEAPONBRAKER , SC_BROKENWEAPON , SI_BROKENWEAPON , SCB_NONE ); - set_sc( NPC_ARMORBRAKE , SC_BROKENARMOR , SI_BROKENARMOR , SCB_NONE ); - set_sc( NPC_CHANGEUNDEAD , SC_CHANGEUNDEAD , SI_UNDEAD , SCB_DEF_ELE ); - set_sc( NPC_POWERUP , SC_INCHITRATE , SI_BLANK , SCB_HIT ); - set_sc( NPC_AGIUP , SC_INCFLEERATE , SI_BLANK , SCB_FLEE ); - add_sc( NPC_INVISIBLE , SC_CLOAKING ); - set_sc( LK_AURABLADE , SC_AURABLADE , SI_AURABLADE , SCB_NONE ); - set_sc( LK_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE ); - set_sc( LK_CONCENTRATION , SC_CONCENTRATION , SI_CONCENTRATION , SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_DSPD ); - set_sc( LK_TENSIONRELAX , SC_TENSIONRELAX , SI_TENSIONRELAX , SCB_REGEN ); - set_sc( LK_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN ); - set_sc( HP_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE ); - add_sc( HP_BASILICA , SC_BASILICA ); - set_sc( HW_MAGICPOWER , SC_MAGICPOWER , SI_MAGICPOWER , SCB_MATK ); - add_sc( PA_SACRIFICE , SC_SACRIFICE ); - set_sc( PA_GOSPEL , SC_GOSPEL , SI_BLANK , SCB_SPEED|SCB_ASPD ); - add_sc( PA_GOSPEL , SC_SCRESIST ); - add_sc( CH_TIGERFIST , SC_STOP ); - set_sc( ASC_EDP , SC_EDP , SI_EDP , SCB_NONE ); - set_sc( SN_SIGHT , SC_TRUESIGHT , SI_TRUESIGHT , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_CRI|SCB_HIT ); - set_sc( SN_WINDWALK , SC_WINDWALK , SI_WINDWALK , SCB_FLEE|SCB_SPEED ); - set_sc( WS_MELTDOWN , SC_MELTDOWN , SI_MELTDOWN , SCB_NONE ); - set_sc( WS_CARTBOOST , SC_CARTBOOST , SI_CARTBOOST , SCB_SPEED ); - set_sc( ST_CHASEWALK , SC_CHASEWALK , SI_BLANK , SCB_SPEED ); - set_sc( ST_REJECTSWORD , SC_REJECTSWORD , SI_REJECTSWORD , SCB_NONE ); - add_sc( ST_REJECTSWORD , SC_AUTOCOUNTER ); - set_sc( CG_MARIONETTE , SC_MARIONETTE , SI_MARIONETTE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); - set_sc( CG_MARIONETTE , SC_MARIONETTE2 , SI_MARIONETTE2 , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); - add_sc( LK_SPIRALPIERCE , SC_STOP ); - add_sc( LK_HEADCRUSH , SC_BLEEDING ); - set_sc( LK_JOINTBEAT , SC_JOINTBEAT , SI_JOINTBEAT , SCB_BATK|SCB_DEF2|SCB_SPEED|SCB_ASPD ); - add_sc( HW_NAPALMVULCAN , SC_CURSE ); - set_sc( PF_MINDBREAKER , SC_MINDBREAKER , SI_BLANK , SCB_MATK|SCB_MDEF2 ); - add_sc( PF_MEMORIZE , SC_MEMORIZE ); - add_sc( PF_FOGWALL , SC_FOGWALL ); - set_sc( PF_SPIDERWEB , SC_SPIDERWEB , SI_BLANK , SCB_FLEE ); - set_sc( WE_BABY , SC_BABY , SI_BABY , SCB_NONE ); - set_sc( TK_RUN , SC_RUN , SI_RUN , SCB_SPEED|SCB_DSPD ); - set_sc( TK_RUN , SC_SPURT , SI_SPURT , SCB_STR ); - set_sc( TK_READYSTORM , SC_READYSTORM , SI_READYSTORM , SCB_NONE ); - set_sc( TK_READYDOWN , SC_READYDOWN , SI_READYDOWN , SCB_NONE ); - add_sc( TK_DOWNKICK , SC_STUN ); - set_sc( TK_READYTURN , SC_READYTURN , SI_READYTURN , SCB_NONE ); - set_sc( TK_READYCOUNTER , SC_READYCOUNTER , SI_READYCOUNTER , SCB_NONE ); - set_sc( TK_DODGE , SC_DODGE , SI_DODGE , SCB_NONE ); - set_sc( TK_SPTIME , SC_EARTHSCROLL , SI_EARTHSCROLL , SCB_NONE ); - add_sc( TK_SEVENWIND , SC_SEVENWIND ); - set_sc( TK_SEVENWIND , SC_GHOSTWEAPON , SI_GHOSTWEAPON , SCB_ATK_ELE ); - set_sc( TK_SEVENWIND , SC_SHADOWWEAPON , SI_SHADOWWEAPON , SCB_ATK_ELE ); - set_sc( SG_SUN_WARM , SC_WARM , SI_WARM , SCB_NONE ); - add_sc( SG_MOON_WARM , SC_WARM ); - add_sc( SG_STAR_WARM , SC_WARM ); - set_sc( SG_SUN_COMFORT , SC_SUN_COMFORT , SI_SUN_COMFORT , SCB_DEF2 ); - set_sc( SG_MOON_COMFORT , SC_MOON_COMFORT , SI_MOON_COMFORT , SCB_FLEE ); - set_sc( SG_STAR_COMFORT , SC_STAR_COMFORT , SI_STAR_COMFORT , SCB_ASPD ); - add_sc( SG_FRIEND , SC_SKILLRATE_UP ); - set_sc( SG_KNOWLEDGE , SC_KNOWLEDGE , SI_BLANK , SCB_ALL ); - set_sc( SG_FUSION , SC_FUSION , SI_BLANK , SCB_SPEED ); - set_sc( BS_ADRENALINE2 , SC_ADRENALINE2 , SI_ADRENALINE2 , SCB_ASPD ); - set_sc( SL_KAIZEL , SC_KAIZEL , SI_KAIZEL , SCB_NONE ); - set_sc( SL_KAAHI , SC_KAAHI , SI_KAAHI , SCB_NONE ); - set_sc( SL_KAUPE , SC_KAUPE , SI_KAUPE , SCB_NONE ); - set_sc( SL_KAITE , SC_KAITE , SI_KAITE , SCB_NONE ); - add_sc( SL_STUN , SC_STUN ); - set_sc( SL_SWOO , SC_SWOO , SI_BLANK , SCB_SPEED ); - set_sc( SL_SKE , SC_SKE , SI_BLANK , SCB_BATK|SCB_WATK|SCB_DEF|SCB_DEF2 ); - set_sc( SL_SKA , SC_SKA , SI_BLANK , SCB_DEF|SCB_MDEF|SCB_ASPD ); - set_sc( SL_SMA , SC_SMA , SI_SMA , SCB_NONE ); - set_sc( SM_SELFPROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); - set_sc( ST_PRESERVE , SC_PRESERVE , SI_PRESERVE , SCB_NONE ); - set_sc( PF_DOUBLECASTING , SC_DOUBLECAST , SI_DOUBLECAST , SCB_NONE ); - set_sc( HW_GRAVITATION , SC_GRAVITATION , SI_BLANK , SCB_ASPD ); - add_sc( WS_CARTTERMINATION , SC_STUN ); - set_sc( WS_OVERTHRUSTMAX , SC_MAXOVERTHRUST , SI_MAXOVERTHRUST , SCB_NONE ); - set_sc( CG_LONGINGFREEDOM , SC_LONGING , SI_BLANK , SCB_SPEED|SCB_ASPD ); - add_sc( CG_HERMODE , SC_HERMODE ); - set_sc( ITEM_ENCHANTARMS , SC_ENCHANTARMS , SI_BLANK , SCB_ATK_ELE ); - set_sc( SL_HIGH , SC_SPIRIT , SI_SPIRIT , SCB_ALL ); - set_sc( KN_ONEHAND , SC_ONEHAND , SI_ONEHAND , SCB_ASPD ); - set_sc( GS_FLING , SC_FLING , SI_BLANK , SCB_DEF|SCB_DEF2 ); - add_sc( GS_CRACKER , SC_STUN ); - add_sc( GS_DISARM , SC_STRIPWEAPON ); - add_sc( GS_PIERCINGSHOT , SC_BLEEDING ); - set_sc( GS_MADNESSCANCEL , SC_MADNESSCANCEL , SI_MADNESSCANCEL , SCB_BATK|SCB_ASPD ); - set_sc( GS_ADJUSTMENT , SC_ADJUSTMENT , SI_ADJUSTMENT , SCB_HIT|SCB_FLEE ); - set_sc( GS_INCREASING , SC_INCREASING , SI_ACCURACY , SCB_AGI|SCB_DEX|SCB_HIT ); - set_sc( GS_GATLINGFEVER , SC_GATLINGFEVER , SI_GATLINGFEVER , SCB_BATK|SCB_FLEE|SCB_SPEED|SCB_ASPD ); - set_sc( NJ_TATAMIGAESHI , SC_TATAMIGAESHI , SI_BLANK , SCB_NONE ); - set_sc( NJ_SUITON , SC_SUITON , SI_BLANK , SCB_AGI|SCB_SPEED ); - add_sc( NJ_HYOUSYOURAKU , SC_FREEZE ); - set_sc( NJ_NEN , SC_NEN , SI_NEN , SCB_STR|SCB_INT ); - set_sc( NJ_UTSUSEMI , SC_UTSUSEMI , SI_UTSUSEMI , SCB_NONE ); - set_sc( NJ_BUNSINJYUTSU , SC_BUNSINJYUTSU , SI_BUNSINJYUTSU , SCB_DYE ); - - add_sc( NPC_ICEBREATH , SC_FREEZE ); - add_sc( NPC_ACIDBREATH , SC_POISON ); - add_sc( NPC_HELLJUDGEMENT , SC_CURSE ); - add_sc( NPC_WIDESILENCE , SC_SILENCE ); - add_sc( NPC_WIDEFREEZE , SC_FREEZE ); - add_sc( NPC_WIDEBLEEDING , SC_BLEEDING ); - add_sc( NPC_WIDESTONE , SC_STONE ); - add_sc( NPC_WIDECONFUSE , SC_CONFUSION ); - add_sc( NPC_WIDESLEEP , SC_SLEEP ); - add_sc( NPC_WIDESIGHT , SC_SIGHT ); - add_sc( NPC_EVILLAND , SC_BLIND ); - add_sc( NPC_MAGICMIRROR , SC_MAGICMIRROR ); - set_sc( NPC_SLOWCAST , SC_SLOWCAST , SI_SLOWCAST , SCB_NONE ); - set_sc( NPC_CRITICALWOUND , SC_CRITICALWOUND , SI_CRITICALWOUND , SCB_NONE ); - set_sc( NPC_STONESKIN , SC_ARMORCHANGE , SI_BLANK , SCB_DEF|SCB_MDEF ); - add_sc( NPC_ANTIMAGIC , SC_ARMORCHANGE ); - add_sc( NPC_WIDECURSE , SC_CURSE ); - add_sc( NPC_WIDESTUN , SC_STUN ); - - set_sc( NPC_HELLPOWER , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE ); - set_sc( NPC_WIDEHELLDIGNITY , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE ); - set_sc( NPC_INVINCIBLE , SC_INVINCIBLE , SI_INVINCIBLE , SCB_SPEED ); - set_sc( NPC_INVINCIBLEOFF , SC_INVINCIBLEOFF , SI_BLANK , SCB_SPEED ); - - set_sc( CASH_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); - 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_NONE ); - set_sc( ALL_ODINS_POWER , SC_ODINS_POWER , SI_ODINS_POWER , SCB_MATK|SCB_BATK|SCB_MDEF|SCB_DEF ); - - set_sc( CR_SHRINK , SC_SHRINK , SI_SHRINK , SCB_NONE ); - set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE ); - set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE ); - set_sc( WZ_SIGHTBLASTER , SC_SIGHTBLASTER , SI_SIGHTBLASTER , SCB_NONE ); - set_sc( DC_WINKCHARM , SC_WINKCHARM , SI_WINKCHARM , SCB_NONE ); - add_sc( MO_BALKYOUNG , SC_STUN ); - add_sc( SA_ELEMENTWATER , SC_ELEMENTALCHANGE ); - add_sc( SA_ELEMENTFIRE , SC_ELEMENTALCHANGE ); - add_sc( SA_ELEMENTGROUND , SC_ELEMENTALCHANGE ); - add_sc( SA_ELEMENTWIND , SC_ELEMENTALCHANGE ); - - 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 ); - set_sc( HFLI_SPEED , SC_SPEED , SI_BLANK , SCB_FLEE ); - set_sc( HAMI_DEFENCE , SC_DEFENCE , SI_BLANK , SCB_DEF ); - set_sc( HAMI_BLOODLUST , SC_BLOODLUST , SI_BLANK , SCB_BATK|SCB_WATK ); - - // Homunculus S - add_sc(MH_STAHL_HORN, SC_STUN); - set_sc(MH_ANGRIFFS_MODUS, SC_ANGRIFFS_MODUS, SI_ANGRIFFS_MODUS, SCB_BATK | SCB_DEF | SCB_FLEE | SCB_MAXHP); - set_sc(MH_GOLDENE_FERSE, SC_GOLDENE_FERSE, SI_GOLDENE_FERSE, SCB_ASPD|SCB_MAXHP); - add_sc( MH_STEINWAND, SC_SAFETYWALL ); - add_sc(MH_ERASER_CUTTER, SC_ERASER_CUTTER); - set_sc(MH_OVERED_BOOST, SC_OVERED_BOOST, SI_BLANK, SCB_FLEE|SCB_ASPD); - add_sc(MH_LIGHT_OF_REGENE, SC_LIGHT_OF_REGENE); - set_sc(MH_VOLCANIC_ASH, SC_ASH, SI_VOLCANIC_ASH, SCB_DEF|SCB_DEF2|SCB_HIT|SCB_BATK|SCB_FLEE); - set_sc(MH_GRANITIC_ARMOR, SC_GRANITIC_ARMOR, SI_GRANITIC_ARMOR, SCB_NONE); - set_sc(MH_MAGMA_FLOW, SC_MAGMA_FLOW, SI_MAGMA_FLOW, SCB_NONE); - set_sc(MH_PYROCLASTIC, SC_PYROCLASTIC, SI_PYROCLASTIC, SCB_BATK|SCB_ATK_ELE); - add_sc(MH_LAVA_SLIDE, SC_BURNING); - set_sc(MH_NEEDLE_OF_PARALYZE, SC_PARALYSIS, SI_NEEDLE_OF_PARALYZE, SCB_DEF2); - add_sc(MH_POISON_MIST, SC_BLIND); - set_sc(MH_PAIN_KILLER, SC_PAIN_KILLER, SI_PAIN_KILLER, SCB_ASPD); - - add_sc(MH_STYLE_CHANGE, SC_STYLE_CHANGE); - set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE ); - set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE ); - - - add_sc( MER_CRASH , SC_STUN ); - set_sc( MER_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); - add_sc( MS_MAGNUM , SC_WATK_ELEMENT ); - add_sc( MER_SIGHT , SC_SIGHT ); - set_sc( MER_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED ); - set_sc( MER_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN ); - add_sc( MER_LEXDIVINA , SC_SILENCE ); - add_sc( MA_LANDMINE , SC_STUN ); - add_sc( MA_SANDMAN , SC_SLEEP ); - add_sc( MA_FREEZINGTRAP , SC_FREEZE ); - set_sc( MER_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE ); - set_sc( ML_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE ); - set_sc( MS_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE ); - set_sc( ML_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD ); - set_sc( MS_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE ); - set_sc( MS_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN ); - add_sc( ML_SPIRALPIERCE , SC_STOP ); - set_sc( MER_QUICKEN , SC_MERC_QUICKEN , SI_BLANK , SCB_ASPD ); - add_sc( ML_DEVOTION , SC_DEVOTION ); - set_sc( MER_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE ); - set_sc( MER_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); - set_sc( MER_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED ); - - set_sc( GD_LEADERSHIP , SC_LEADERSHIP , SI_BLANK , SCB_STR ); - set_sc( GD_GLORYWOUNDS , SC_GLORYWOUNDS , SI_BLANK , SCB_VIT ); - set_sc( GD_SOULCOLD , SC_SOULCOLD , SI_BLANK , SCB_AGI ); - set_sc( GD_HAWKEYES , SC_HAWKEYES , SI_BLANK , 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 ); - set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , SI_CRUSHSTRIKE , SCB_NONE ); - /** - * GC Guillotine Cross - **/ - set_sc_with_vfx( 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_with_vfx( 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_with_vfx( 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_MATK); - 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_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_with_vfx( 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 ); - /** - * 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_DEF ); - 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); - set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_DEF , SI_SHIELDSPELL_DEF , SCB_WATK ); - set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_REF , SI_SHIELDSPELL_REF , SCB_DEF ); - /** - * 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_BODYPAINT , 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_STRIPACCESSARY , SCB_DEX|SCB_INT|SCB_LUK ); - set_sc_with_vfx( SC_MANHOLE , SC__MANHOLE , SI_MANHOLE , SCB_NONE ); - add_sc( SC_CHAOSPANIC , SC_CONFUSION ); - set_sc_with_vfx( SC_BLOODYLUST , SC__BLOODYLUST , SI_BLOODYLUST , SCB_DEF | SCB_DEF2 | SCB_MDEF | SCB_MDEF2 | SCB_FLEE | SCB_SPEED | SCB_ASPD | SCB_MAXHP | SCB_REGEN ); - /** - * 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_with_vfx( 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 ); - 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_ASPD|SCB_MDEF|SCB_MAXHP ); - set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GT_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_REGEN ); - /** - * Wanderer / Minstrel - **/ - 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_with_vfx( WM_POEMOFNETHERWORLD , SC_NETHERWORLD , SI_NETHERWORLD , SCB_NONE ); - set_sc_with_vfx( WM_VOICEOFSIREN , SC_VOICEOFSIREN , SI_VOICEOFSIREN , SCB_NONE ); - set_sc_with_vfx( 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_with_vfx( SO_DIAMONDDUST , SC_CRYSTALIZE , SI_COLD , SCB_NONE ); // it does show the snow icon on mobs but doesn't affect it. - add_sc( SO_CLOUD_KILL , SC_POISON ); - 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 ); - set_sc( SO_FIRE_INSIGNIA , SC_FIRE_INSIGNIA , SI_FIRE_INSIGNIA , SCB_MATK | SCB_BATK | SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); - set_sc( SO_WATER_INSIGNIA , SC_WATER_INSIGNIA , SI_WATER_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); - set_sc( SO_WIND_INSIGNIA , SC_WIND_INSIGNIA , SI_WIND_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); - set_sc( SO_EARTH_INSIGNIA , SC_EARTH_INSIGNIA , SI_EARTH_INSIGNIA , SCB_MDEF|SCB_DEF|SCB_MAXHP|SCB_MAXSP|SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); - /** - * 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_with_vfx( 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 ); - - // Elemental Spirit summoner's 'side' status changes. - set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SI_CIRCLE_OF_FIRE_OPTION, SCB_NONE ); - set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SI_FIRE_CLOAK_OPTION , SCB_ALL ); - set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SI_WATER_SCREEN_OPTION , SCB_NONE ); - set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SI_WATER_DROP_OPTION , SCB_ALL ); - set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SI_WATER_BARRIER , SCB_MDEF|SCB_WATK|SCB_MATK|SCB_FLEE ); - set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SI_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE ); - set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SI_WIND_CURTAIN_OPTION , SCB_ALL ); - set_sc( EL_ZEPHYR , SC_ZEPHYR , SI_ZEPHYR , SCB_FLEE ); - set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SI_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP ); - set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SI_STONE_SHIELD_OPTION , SCB_ALL ); - set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SI_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED ); - set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SI_PYROTECHNIC_OPTION , SCB_WATK ); - set_sc( EL_HEATER , SC_HEATER_OPTION , SI_HEATER_OPTION , SCB_WATK ); - set_sc( EL_TROPIC , SC_TROPIC_OPTION , SI_TROPIC_OPTION , SCB_WATK ); - set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SI_AQUAPLAY_OPTION , SCB_MATK ); - set_sc( EL_COOLER , SC_COOLER_OPTION , SI_COOLER_OPTION , SCB_MATK ); - set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SI_CHILLY_AIR_OPTION , SCB_MATK ); - set_sc( EL_GUST , SC_GUST_OPTION , SI_GUST_OPTION , SCB_ASPD ); - set_sc( EL_BLAST , SC_BLAST_OPTION , SI_BLAST_OPTION , SCB_ASPD ); - set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SI_WILD_STORM_OPTION , SCB_ASPD ); - set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SI_PETROLOGY_OPTION , SCB_MAXHP ); - set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SI_CURSED_SOIL_OPTION , SCB_NONE ); - set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SI_UPHEAVAL_OPTION , SCB_NONE ); - set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SI_TIDAL_WEAPON_OPTION , SCB_ALL ); - set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SI_ROCK_CRUSHER , SCB_DEF ); - set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SI_ROCK_CRUSHER_ATK , SCB_SPEED ); - - add_sc( KO_YAMIKUMO , SC_HIDING ); - set_sc_with_vfx( KO_JYUMONJIKIRI , SC_JYUMONJIKIRI , SI_KO_JYUMONJIKIRI , SCB_NONE ); - add_sc( KO_MAKIBISHI , SC_STUN ); - set_sc( KO_MEIKYOUSISUI , SC_MEIKYOUSISUI , SI_MEIKYOUSISUI , SCB_NONE ); - set_sc( KO_KYOUGAKU , SC_KYOUGAKU , SI_KYOUGAKU , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); - add_sc( KO_JYUSATSU , SC_CURSE ); - set_sc( KO_ZENKAI , SC_ZENKAI , SI_ZENKAI , SCB_NONE ); - set_sc( KO_IZAYOI , SC_IZAYOI , SI_IZAYOI , SCB_MATK ); - set_sc( KG_KYOMU , SC_KYOMU , SI_KYOMU , SCB_NONE ); - set_sc( KG_KAGEMUSYA , SC_KAGEMUSYA , SI_KAGEMUSYA , SCB_NONE ); - set_sc( KG_KAGEHUMI , SC_KAGEHUMI , SI_KG_KAGEHUMI , SCB_NONE ); - set_sc( OB_ZANGETSU , SC_ZANGETSU , SI_ZANGETSU , SCB_MATK|SCB_BATK ); - set_sc_with_vfx( OB_AKAITSUKI , SC_AKAITSUKI , SI_AKAITSUKI , SCB_NONE ); - set_sc( OB_OBOROGENSOU , SC_GENSOU , SI_GENSOU , SCB_NONE ); - - // Storing the target job rather than simply SC_SPIRIT simplifies code later on. - SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST, - SkillStatusChangeTable[SL_MONK] = (sc_type)MAPID_MONK, - SkillStatusChangeTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR, - SkillStatusChangeTable[SL_SAGE] = (sc_type)MAPID_SAGE, - SkillStatusChangeTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER, - SkillStatusChangeTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE, - SkillStatusChangeTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT, - SkillStatusChangeTable[SL_WIZARD] = (sc_type)MAPID_WIZARD, - SkillStatusChangeTable[SL_PRIEST] = (sc_type)MAPID_PRIEST, - SkillStatusChangeTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER, - SkillStatusChangeTable[SL_ROGUE] = (sc_type)MAPID_ROGUE, - SkillStatusChangeTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN, - SkillStatusChangeTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH, - SkillStatusChangeTable[SL_HUNTER] = (sc_type)MAPID_HUNTER, - SkillStatusChangeTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER, - - //Status that don't have a skill associated. - StatusIconChangeTable[SC_WEIGHT50] = SI_WEIGHT50; - StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90; - StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION0; - StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION1; - StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION2; - StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTIONINFINITY; - StatusIconChangeTable[SC_SPEEDUP0] = SI_MOVHASTE_HORSE; - StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION1; - StatusIconChangeTable[SC_INCSTR] = SI_INCSTR; - StatusIconChangeTable[SC_MIRACLE] = SI_SPIRIT; - StatusIconChangeTable[SC_INTRAVISION] = SI_INTRAVISION; - StatusIconChangeTable[SC_STRFOOD] = SI_FOODSTR; - StatusIconChangeTable[SC_AGIFOOD] = SI_FOODAGI; - StatusIconChangeTable[SC_VITFOOD] = SI_FOODVIT; - StatusIconChangeTable[SC_INTFOOD] = SI_FOODINT; - StatusIconChangeTable[SC_DEXFOOD] = SI_FOODDEX; - StatusIconChangeTable[SC_LUKFOOD] = SI_FOODLUK; - StatusIconChangeTable[SC_FLEEFOOD]= SI_FOODFLEE; - StatusIconChangeTable[SC_HITFOOD] = SI_FOODHIT; - StatusIconChangeTable[SC_MANU_ATK] = SI_MANU_ATK; - StatusIconChangeTable[SC_MANU_DEF] = SI_MANU_DEF; - StatusIconChangeTable[SC_SPL_ATK] = SI_SPL_ATK; - StatusIconChangeTable[SC_SPL_DEF] = SI_SPL_DEF; - StatusIconChangeTable[SC_MANU_MATK] = SI_MANU_MATK; - StatusIconChangeTable[SC_SPL_MATK] = SI_SPL_MATK; - StatusIconChangeTable[SC_ATKPOTION] = SI_PLUSATTACKPOWER; - StatusIconChangeTable[SC_MATKPOTION] = SI_PLUSMAGICPOWER; - //Cash Items - StatusIconChangeTable[SC_FOOD_STR_CASH] = SI_FOOD_STR_CASH; - StatusIconChangeTable[SC_FOOD_AGI_CASH] = SI_FOOD_AGI_CASH; - StatusIconChangeTable[SC_FOOD_VIT_CASH] = SI_FOOD_VIT_CASH; - StatusIconChangeTable[SC_FOOD_DEX_CASH] = SI_FOOD_DEX_CASH; - StatusIconChangeTable[SC_FOOD_INT_CASH] = SI_FOOD_INT_CASH; - StatusIconChangeTable[SC_FOOD_LUK_CASH] = SI_FOOD_LUK_CASH; - StatusIconChangeTable[SC_EXPBOOST] = SI_EXPBOOST; - StatusIconChangeTable[SC_ITEMBOOST] = SI_ITEMBOOST; - StatusIconChangeTable[SC_JEXPBOOST] = SI_CASH_PLUSONLYJOBEXP; - StatusIconChangeTable[SC_LIFEINSURANCE] = SI_LIFEINSURANCE; - StatusIconChangeTable[SC_BOSSMAPINFO] = SI_BOSSMAPINFO; - StatusIconChangeTable[SC_DEF_RATE] = SI_DEF_RATE; - StatusIconChangeTable[SC_MDEF_RATE] = SI_MDEF_RATE; - StatusIconChangeTable[SC_INCCRI] = SI_INCCRI; - StatusIconChangeTable[SC_INCFLEE2] = SI_PLUSAVOIDVALUE; - StatusIconChangeTable[SC_INCHEALRATE] = SI_INCHEALRATE; - StatusIconChangeTable[SC_S_LIFEPOTION] = SI_S_LIFEPOTION; - StatusIconChangeTable[SC_L_LIFEPOTION] = SI_L_LIFEPOTION; - StatusIconChangeTable[SC_SPCOST_RATE] = SI_ATKER_BLOOD; - StatusIconChangeTable[SC_COMMONSC_RESIST] = SI_TARGET_BLOOD; - // Mercenary Bonus Effects - StatusIconChangeTable[SC_MERC_FLEEUP] = SI_MERC_FLEEUP; - StatusIconChangeTable[SC_MERC_ATKUP] = SI_MERC_ATKUP; - 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; - // Warlock Preserved spells - StatusIconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1; - StatusIconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2; - StatusIconChangeTable[SC_SPELLBOOK3] = SI_SPELLBOOK3; - StatusIconChangeTable[SC_SPELLBOOK4] = SI_SPELLBOOK4; - StatusIconChangeTable[SC_SPELLBOOK5] = SI_SPELLBOOK5; - StatusIconChangeTable[SC_SPELLBOOK6] = SI_SPELLBOOK6; - StatusIconChangeTable[SC_MAXSPELLBOOK] = SI_SPELLBOOK7; - - 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; - StatusIconChangeTable[SC_PUSH_CART] = SI_ON_PUSH_CART; - - //Other SC which are not necessarily associated to skills. - StatusChangeFlagTable[SC_ASPDPOTION0] = SCB_ASPD; - StatusChangeFlagTable[SC_ASPDPOTION1] = SCB_ASPD; - StatusChangeFlagTable[SC_ASPDPOTION2] = SCB_ASPD; - StatusChangeFlagTable[SC_ASPDPOTION3] = SCB_ASPD; - StatusChangeFlagTable[SC_SPEEDUP0] = SCB_SPEED; - StatusChangeFlagTable[SC_SPEEDUP1] = SCB_SPEED; - StatusChangeFlagTable[SC_ATKPOTION] = SCB_BATK; - StatusChangeFlagTable[SC_MATKPOTION] = SCB_MATK; - StatusChangeFlagTable[SC_INCALLSTATUS] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK; - StatusChangeFlagTable[SC_INCSTR] |= SCB_STR; - StatusChangeFlagTable[SC_INCAGI] |= SCB_AGI; - StatusChangeFlagTable[SC_INCVIT] |= SCB_VIT; - StatusChangeFlagTable[SC_INCINT] |= SCB_INT; - StatusChangeFlagTable[SC_INCDEX] |= SCB_DEX; - StatusChangeFlagTable[SC_INCLUK] |= SCB_LUK; - StatusChangeFlagTable[SC_INCHIT] |= SCB_HIT; - StatusChangeFlagTable[SC_INCHITRATE] |= SCB_HIT; - StatusChangeFlagTable[SC_INCFLEE] |= SCB_FLEE; - StatusChangeFlagTable[SC_INCFLEERATE] |= SCB_FLEE; - StatusChangeFlagTable[SC_INCCRI] |= SCB_CRI; - StatusChangeFlagTable[SC_INCASPDRATE] |= SCB_ASPD; - StatusChangeFlagTable[SC_INCFLEE2] |= SCB_FLEE2; - StatusChangeFlagTable[SC_INCMHPRATE] |= SCB_MAXHP; - StatusChangeFlagTable[SC_INCMSPRATE] |= SCB_MAXSP; - StatusChangeFlagTable[SC_INCMHP] |= SCB_MAXHP; - StatusChangeFlagTable[SC_INCMSP] |= SCB_MAXSP; - StatusChangeFlagTable[SC_INCATKRATE] |= SCB_BATK|SCB_WATK; - StatusChangeFlagTable[SC_INCMATKRATE] |= SCB_MATK; - StatusChangeFlagTable[SC_INCDEFRATE] |= SCB_DEF; - StatusChangeFlagTable[SC_STRFOOD] |= SCB_STR; - StatusChangeFlagTable[SC_AGIFOOD] |= SCB_AGI; - StatusChangeFlagTable[SC_VITFOOD] |= SCB_VIT; - StatusChangeFlagTable[SC_INTFOOD] |= SCB_INT; - StatusChangeFlagTable[SC_DEXFOOD] |= SCB_DEX; - StatusChangeFlagTable[SC_LUKFOOD] |= SCB_LUK; - StatusChangeFlagTable[SC_HITFOOD] |= SCB_HIT; - StatusChangeFlagTable[SC_FLEEFOOD] |= SCB_FLEE; - StatusChangeFlagTable[SC_BATKFOOD] |= SCB_BATK; - StatusChangeFlagTable[SC_WATKFOOD] |= SCB_WATK; - StatusChangeFlagTable[SC_MATKFOOD] |= SCB_MATK; - StatusChangeFlagTable[SC_ARMOR_ELEMENT] |= SCB_ALL; - StatusChangeFlagTable[SC_ARMOR_RESIST] |= SCB_ALL; - StatusChangeFlagTable[SC_SPCOST_RATE] |= SCB_ALL; - StatusChangeFlagTable[SC_WALKSPEED] |= SCB_SPEED; - StatusChangeFlagTable[SC_ITEMSCRIPT] |= SCB_ALL; - // Cash Items - StatusChangeFlagTable[SC_FOOD_STR_CASH] = SCB_STR; - StatusChangeFlagTable[SC_FOOD_AGI_CASH] = SCB_AGI; - StatusChangeFlagTable[SC_FOOD_VIT_CASH] = SCB_VIT; - StatusChangeFlagTable[SC_FOOD_DEX_CASH] = SCB_DEX; - StatusChangeFlagTable[SC_FOOD_INT_CASH] = SCB_INT; - StatusChangeFlagTable[SC_FOOD_LUK_CASH] = SCB_LUK; - // Mercenary Bonus Effects - StatusChangeFlagTable[SC_MERC_FLEEUP] |= SCB_FLEE; - StatusChangeFlagTable[SC_MERC_ATKUP] |= SCB_WATK; - StatusChangeFlagTable[SC_MERC_HPUP] |= SCB_MAXHP; - StatusChangeFlagTable[SC_MERC_SPUP] |= SCB_MAXSP; - StatusChangeFlagTable[SC_MERC_HITUP] |= SCB_HIT; - // Guillotine Cross Poison Effects - StatusChangeFlagTable[SC_PARALYSE] |= SCB_ASPD|SCB_FLEE|SCB_SPEED; - StatusChangeFlagTable[SC_DEATHHURT] |= SCB_REGEN; - StatusChangeFlagTable[SC_VENOMBLEED] |= SCB_MAXHP; - StatusChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN; - - StatusChangeFlagTable[SC_SAVAGE_STEAK] |= SCB_STR; - StatusChangeFlagTable[SC_COCKTAIL_WARG_BLOOD] |= SCB_INT; - StatusChangeFlagTable[SC_MINOR_BBQ] |= SCB_VIT; - StatusChangeFlagTable[SC_SIROMA_ICE_TEA] |= SCB_DEX; - StatusChangeFlagTable[SC_DROCERA_HERB_STEAMED] |= SCB_AGI; - StatusChangeFlagTable[SC_PUTTI_TAILS_NOODLES] |= SCB_LUK; - StatusChangeFlagTable[SC_BOOST500] |= SCB_ASPD; - StatusChangeFlagTable[SC_FULL_SWING_K] |= SCB_BATK; - StatusChangeFlagTable[SC_MANA_PLUS] |= SCB_MATK; - StatusChangeFlagTable[SC_MUSTLE_M] |= SCB_MAXHP; - StatusChangeFlagTable[SC_LIFE_FORCE_F] |= SCB_MAXSP; - StatusChangeFlagTable[SC_EXTRACT_WHITE_POTION_Z] |= SCB_REGEN; - StatusChangeFlagTable[SC_VITATA_500] |= SCB_REGEN; - StatusChangeFlagTable[SC_EXTRACT_SALAMINE_JUICE] |= SCB_ASPD; - -#ifdef RENEWAL_EDP - // renewal EDP increases your weapon atk - StatusChangeFlagTable[SC_EDP] |= SCB_WATK; -#endif - - if( !battle_config.display_hallucination ) //Disable Hallucination. - StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK; - - /* StatusChangeState (SCS_) NOMOVE */ - StatusChangeStateTable[SC_ANKLE] |= SCS_NOMOVE; - StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NOMOVE; - StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOMOVE; - StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOMOVE; - StatusChangeStateTable[SC_BLADESTOP_WAIT] |= SCS_NOMOVE; - StatusChangeStateTable[SC_GOSPEL] |= SCS_NOMOVE|SCS_NOMOVECOND; - StatusChangeStateTable[SC_BASILICA] |= SCS_NOMOVE|SCS_NOMOVECOND; - StatusChangeStateTable[SC_STOP] |= SCS_NOMOVE; - StatusChangeStateTable[SC_CLOSECONFINE] |= SCS_NOMOVE; - StatusChangeStateTable[SC_CLOSECONFINE2] |= SCS_NOMOVE; - StatusChangeStateTable[SC_MADNESSCANCEL] |= SCS_NOMOVE; - StatusChangeStateTable[SC_GRAVITATION] |= SCS_NOMOVE|SCS_NOMOVECOND; - StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOMOVE; - StatusChangeStateTable[SC_ELECTRICSHOCKER] |= SCS_NOMOVE; - StatusChangeStateTable[SC_BITE] |= SCS_NOMOVE; - StatusChangeStateTable[SC_THORNSTRAP] |= SCS_NOMOVE; - StatusChangeStateTable[SC_MAGNETICFIELD] |= SCS_NOMOVE; - StatusChangeStateTable[SC__MANHOLE] |= SCS_NOMOVE; - StatusChangeStateTable[SC_CURSEDCIRCLE_ATKER] |= SCS_NOMOVE; - StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOMOVE; - StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOMOVE|SCS_NOMOVECOND; - StatusChangeStateTable[SC_NETHERWORLD] |= SCS_NOMOVE; - StatusChangeStateTable[SC_CAMOUFLAGE] |= SCS_NOMOVE|SCS_NOMOVECOND; - StatusChangeStateTable[SC_MEIKYOUSISUI] |= SCS_NOMOVE; - StatusChangeStateTable[SC_KAGEHUMI] |= SCS_NOMOVE; - StatusChangeStateTable[SC_KYOUGAKU] |= SCS_NOMOVE; - - /* StatusChangeState (SCS_) NOPICKUPITEMS */ - StatusChangeStateTable[SC_HIDING] |= SCS_NOPICKITEM; - StatusChangeStateTable[SC_CLOAKING] |= SCS_NOPICKITEM; - StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOPICKITEM; - StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOPICKITEM; - StatusChangeStateTable[SC_CLOAKINGEXCEED] |= SCS_NOPICKITEM; - StatusChangeStateTable[SC_NOCHAT] |= SCS_NOPICKITEM|SCS_NOPICKITEMCOND; - - /* StatusChangeState (SCS_) NODROPITEMS */ - StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NODROPITEM; - StatusChangeStateTable[SC_BLADESTOP] |= SCS_NODROPITEM; - StatusChangeStateTable[SC_NOCHAT] |= SCS_NODROPITEM|SCS_NODROPITEMCOND; - - /* StatusChangeState (SCS_) NOCAST (skills) */ - StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST; - StatusChangeStateTable[SC_STEELBODY] |= SCS_NOCAST; - StatusChangeStateTable[SC_BERSERK] |= SCS_NOCAST; - StatusChangeStateTable[SC__BLOODYLUST] |= SCS_NOCAST; - StatusChangeStateTable[SC_OBLIVIONCURSE] |= SCS_NOCAST; - StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOCAST; - StatusChangeStateTable[SC__INVISIBILITY] |= SCS_NOCAST; - StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOCAST|SCS_NOCASTCOND; - StatusChangeStateTable[SC__IGNORANCE] |= SCS_NOCAST; - StatusChangeStateTable[SC_DEEPSLEEP] |= SCS_NOCAST; - StatusChangeStateTable[SC_SATURDAYNIGHTFEVER] |= SCS_NOCAST; - StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOCAST; - StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST; - - //Homon S - StatusChangeStateTable[SC_PARALYSIS] |= SCS_NOMOVE; - -} - -static void initDummyData(void) -{ - memset(&dummy_status, 0, sizeof(dummy_status)); - dummy_status.hp = - dummy_status.max_hp = - dummy_status.max_sp = - dummy_status.str = - dummy_status.agi = - dummy_status.vit = - dummy_status.int_ = - dummy_status.dex = - dummy_status.luk = - dummy_status.hit = 1; - dummy_status.speed = 2000; - dummy_status.adelay = 4000; - dummy_status.amotion = 2000; - dummy_status.dmotion = 2000; - dummy_status.ele_lv = 1; //Min elemental level. - 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))); -} - -//Sets HP to given value. Flag is the flag passed to status_heal in case -//final value is higher than current (use 2 to make a healing effect display -//on players) It will always succeed (overrides Berserk block), but it can't kill. -int status_set_hp(struct block_list *bl, unsigned int hp, int flag) -{ - struct status_data *status; - if (hp < 1) return 0; - status = status_get_status_data(bl); - if (status == &dummy_status) - return 0; - - if (hp > status->max_hp) hp = status->max_hp; - if (hp == status->hp) return 0; - if (hp > status->hp) - return status_heal(bl, hp - status->hp, 0, 1|flag); - return status_zap(bl, status->hp - hp, 0); -} - -//Sets SP to given value. Flag is the flag passed to status_heal in case -//final value is higher than current (use 2 to make a healing effect display -//on players) -int status_set_sp(struct block_list *bl, unsigned int sp, int flag) -{ - struct status_data *status; - - status = status_get_status_data(bl); - if (status == &dummy_status) - return 0; - - if (sp > status->max_sp) sp = status->max_sp; - if (sp == status->sp) return 0; - if (sp > status->sp) - return status_heal(bl, 0, sp - status->sp, 1|flag); - return status_zap(bl, 0, status->sp - sp); -} - -int status_charge(struct block_list* bl, int hp, int sp) -{ - if(!(bl->type&BL_CONSUME)) - return hp+sp; //Assume all was charged so there are no 'not enough' fails. - return status_damage(NULL, bl, hp, sp, 0, 3); -} - -//Inflicts damage on the target with the according walkdelay. -//If flag&1, damage is passive and does not triggers cancelling status changes. -//If flag&2, fail if target does not has enough to substract. -//If flag&4, if killed, mob must not give exp/loot. -//flag will be set to &8 when damaging sp of a dead character -int status_damage(struct block_list *src,struct block_list *target,int hp, int sp, int walkdelay, int flag) -{ - struct status_data *status; - struct status_change *sc; - - if(sp && !(target->type&BL_CONSUME)) - sp = 0; //Not a valid SP target. - - if (hp < 0) { //Assume absorbed damage. - status_heal(target, -hp, 0, 1); - hp = 0; - } - - if (sp < 0) { - status_heal(target, 0, -sp, 1); - sp = 0; - } - - if (target->type == BL_SKILL) - return skill_unit_ondamaged((struct skill_unit *)target, src, hp, gettick()); - - status = status_get_status_data(target); - if( status == &dummy_status ) - return 0; - - if ((unsigned int)hp >= status->hp) { - if (flag&2) return 0; - hp = status->hp; - } - - if ((unsigned int)sp > status->sp) { - if (flag&2) return 0; - sp = status->sp; - } - - if (!hp && !sp) - return 0; - - if( !status->hp ) - flag |= 8; - -// Let through. battle.c/skill.c have the whole logic of when it's possible or -// not to hurt someone (and this check breaks pet catching) [Skotlex] -// if (!target->prev && !(flag&2)) -// return 0; //Cannot damage a bl not on a map, except when "charging" hp/sp - - sc = status_get_sc(target); - if( hp && battle_config.invincible_nodamage && src && sc && sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] ) - hp = 1; - - if( hp && !(flag&1) ) { - if( sc ) { - struct status_change_entry *sce; - if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) - status_change_end(target, SC_STONE, INVALID_TIMER); - status_change_end(target, SC_FREEZE, INVALID_TIMER); - status_change_end(target, SC_SLEEP, INVALID_TIMER); - status_change_end(target, SC_WINKCHARM, INVALID_TIMER); - status_change_end(target, SC_CONFUSION, INVALID_TIMER); - status_change_end(target, SC_TRICKDEAD, INVALID_TIMER); - 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); - status_change_end(target, SC__INVISIBILITY, INVALID_TIMER); - status_change_end(target, SC_DEEPSLEEP, 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] - if (src && src->type != BL_PC && !map_flag_gvg(target->m) && !map[target->m].flag.battleground && --(sce->val2) < 0) - status_change_end(target, SC_ENDURE, INVALID_TIMER); - } - if ((sce=sc->data[SC_GRAVITATION]) && sce->val3 == BCT_SELF) { - struct skill_unit_group* sg = skill_id2group(sce->val4); - if (sg) { - skill_delunitgroup(sg); - sce->val4 = 0; - status_change_end(target, SC_GRAVITATION, INVALID_TIMER); - } - } - 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, INVALID_TIMER); - if(sc->data[SC_KAGEMUSYA] && --(sc->data[SC_KAGEMUSYA]->val3) <= 0) - status_change_end(target, SC_KAGEMUSYA, INVALID_TIMER); - } - unit_skillcastcancel(target, 2); - } - - status->hp-= hp; - status->sp-= sp; - - if (sc && hp && status->hp) { - if (sc->data[SC_AUTOBERSERK] && - (!sc->data[SC_PROVOKE] || !sc->data[SC_PROVOKE]->val2) && - status->hp < status->max_hp>>2) - sc_start4(target,SC_PROVOKE,100,10,1,0,0,0); - if (sc->data[SC_BERSERK] && status->hp <= 100) - status_change_end(target, SC_BERSERK, INVALID_TIMER); - if( sc->data[SC_RAISINGDRAGON] && status->hp <= 1000 ) - status_change_end(target, SC_RAISINGDRAGON, INVALID_TIMER); - if (sc->data[SC_SATURDAYNIGHTFEVER] && status->hp <= 100) - status_change_end(target, SC_SATURDAYNIGHTFEVER, INVALID_TIMER); - if (sc->data[SC__BLOODYLUST] && status->hp <= 100) - status_change_end(target, SC__BLOODYLUST, INVALID_TIMER); - } - - switch (target->type) { - case BL_PC: pc_damage((TBL_PC*)target,src,hp,sp); break; - case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break; - case BL_HOM: merc_damage((TBL_HOM*)target); break; - case BL_MER: mercenary_heal((TBL_MER*)target,hp,sp); break; - case BL_ELEM: elemental_heal((TBL_ELEM*)target,hp,sp); break; - } - - if( src && target->type == BL_PC && ((TBL_PC*)target)->disguise ) {// stop walking when attacked in disguise to prevent walk-delay bug - unit_stop_walking( target, 1 ); - } - - if( status->hp || (flag&8) ) - { //Still lives or has been dead before this damage. - if (walkdelay) - unit_set_walkdelay(target, gettick(), walkdelay, 0); - return hp+sp; - } - - status->hp = 1; //To let the dead function cast skills and all that. - //NOTE: These dead functions should return: [Skotlex] - //0: Death cancelled, auto-revived. - //Non-zero: Standard death. Clear status, cancel move/attack, etc - //&2: Also remove object from map. - //&4: Also delete object from memory. - switch (target->type) { - case BL_PC: flag = pc_dead((TBL_PC*)target,src); break; - case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break; - case BL_HOM: flag = merc_hom_dead((TBL_HOM*)target); break; - case BL_MER: flag = mercenary_dead((TBL_MER*)target); break; - case BL_ELEM: flag = elemental_dead((TBL_ELEM*)target); break; - default: //Unhandled case, do nothing to object. - flag = 0; - break; - } - - if(!flag) //Death cancelled. - return hp+sp; - - //Normal death - status->hp = 0; - if (battle_config.clear_unit_ondeath && - battle_config.clear_unit_ondeath&target->type) - skill_clear_unitgroup(target); - - if(target->type&BL_REGEN) - { //Reset regen ticks. - struct regen_data *regen = status_get_regen_data(target); - if (regen) { - memset(®en->tick, 0, sizeof(regen->tick)); - if (regen->sregen) - memset(®en->sregen->tick, 0, sizeof(regen->sregen->tick)); - if (regen->ssregen) - memset(®en->ssregen->tick, 0, sizeof(regen->ssregen->tick)); - } - } - - if( sc && sc->data[SC_KAIZEL] && !map_flag_gvg(target->m) ) - { //flag&8 = disable Kaizel - int time = skill_get_time2(SL_KAIZEL,sc->data[SC_KAIZEL]->val1); - //Look for Osiris Card's bonus effect on the character and revive 100% or revive normally - if ( target->type == BL_PC && BL_CAST(BL_PC,target)->special_state.restart_full_recover ) - status_revive(target, 100, 100); - else - status_revive(target, sc->data[SC_KAIZEL]->val2, 0); - status_change_clear(target,0); - clif_skill_nodamage(target,target,ALL_RESURRECTION,1,1); - sc_start(target,status_skill2sc(PR_KYRIE),100,10,time); - - if( target->type == BL_MOB ) - ((TBL_MOB*)target)->state.rebirth = 1; - - return hp+sp; - } - if(target->type == BL_PC){ - TBL_PC *sd = BL_CAST(BL_PC,target); - TBL_HOM *hd = sd->hd; - if(hd && hd->sc.data[SC_LIGHT_OF_REGENE]){ - clif_skillcasting(&hd->bl, hd->bl.id, target->id, 0,0, MH_LIGHT_OF_REGENE, skill_get_ele(MH_LIGHT_OF_REGENE, 1), 10); //just to display usage - clif_skill_nodamage(&sd->bl, target, ALL_RESURRECTION, 1, status_revive(&sd->bl,10*hd->sc.data[SC_LIGHT_OF_REGENE]->val1,0)); - status_change_end(&sd->hd->bl,SC_LIGHT_OF_REGENE,INVALID_TIMER); - return hp + sp; - } - } - if (target->type == BL_MOB && sc && sc->data[SC_REBIRTH] && !((TBL_MOB*) target)->state.rebirth) {// Ensure the monster has not already rebirthed before doing so. - status_revive(target, sc->data[SC_REBIRTH]->val2, 0); - status_change_clear(target,0); - ((TBL_MOB*)target)->state.rebirth = 1; - - return hp+sp; - } - - status_change_clear(target,0); - - if(flag&4) //Delete from memory. (also invokes map removal code) - unit_free(target,CLR_DEAD); - else - if(flag&2) //remove from map - unit_remove_map(target,CLR_DEAD); - else - { //Some death states that would normally be handled by unit_remove_map - unit_stop_attack(target); - unit_stop_walking(target,1); - unit_skillcastcancel(target,0); - clif_clearunit_area(target,CLR_DEAD); - skill_unit_move(target,gettick(),4); - skill_cleartimerskill(target); - } - - return hp+sp; -} - -//Heals a character. If flag&1, this is forced healing (otherwise stuff like Berserk can block it) -//If flag&2, when the player is healed, show the HP/SP heal effect. -int status_heal(struct block_list *bl,int hp,int sp, int flag) -{ - struct status_data *status; - struct status_change *sc; - - status = status_get_status_data(bl); - - if (status == &dummy_status || !status->hp) - return 0; - - sc = status_get_sc(bl); - if (sc && !sc->count) - sc = NULL; - - if (hp < 0) { - if (hp == INT_MIN) hp++; //-INT_MIN == INT_MIN in some architectures! - status_damage(NULL, bl, -hp, 0, 0, 1); - hp = 0; - } - - if(hp) { - if( sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) ) { - if( flag&1 ) - flag &= ~2; - else - hp = 0; - } - - if((unsigned int)hp > status->max_hp - status->hp) - hp = status->max_hp - status->hp; - } - - if(sp < 0) { - if (sp==INT_MIN) sp++; - status_damage(NULL, bl, 0, -sp, 0, 1); - sp = 0; - } - - if(sp) { - if((unsigned int)sp > status->max_sp - status->sp) - sp = status->max_sp - status->sp; - } - - if(!sp && !hp) return 0; - - status->hp+= hp; - status->sp+= sp; - - if(hp && sc && - sc->data[SC_AUTOBERSERK] && - sc->data[SC_PROVOKE] && - sc->data[SC_PROVOKE]->val2==1 && - status->hp>=status->max_hp>>2 - ) //End auto berserk. - status_change_end(bl, SC_PROVOKE, INVALID_TIMER); - - // send hp update to client - switch(bl->type) { - case BL_PC: pc_heal((TBL_PC*)bl,hp,sp,flag&2?1:0); break; - case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break; - case BL_HOM: merc_hom_heal((TBL_HOM*)bl); break; - case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break; - case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break; - } - - return hp+sp; -} - -//Does percentual non-flinching damage/heal. If mob is killed this way, -//no exp/drops will be awarded if there is no src (or src is target) -//If rates are > 0, percent is of current HP/SP -//If rates are < 0, percent is of max HP/SP -//If !flag, this is heal, otherwise it is damage. -//Furthermore, if flag==2, then the target must not die from the substraction. -int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag) -{ - struct status_data *status; - unsigned int hp =0, sp = 0; - - status = status_get_status_data(target); - - - //It's safe now [MarkZD] - if (hp_rate > 99) - hp = status->hp; - else if (hp_rate > 0) - hp = status->hp>10000? - hp_rate*(status->hp/100): - ((int64)hp_rate*status->hp)/100; - else if (hp_rate < -99) - hp = status->max_hp; - else if (hp_rate < 0) - hp = status->max_hp>10000? - (-hp_rate)*(status->max_hp/100): - ((int64)-hp_rate*status->max_hp)/100; - if (hp_rate && !hp) - hp = 1; - - if (flag == 2 && hp >= status->hp) - hp = status->hp-1; //Must not kill target. - - if (sp_rate > 99) - sp = status->sp; - else if (sp_rate > 0) - sp = ((int64)sp_rate*status->sp)/100; - else if (sp_rate < -99) - sp = status->max_sp; - else if (sp_rate < 0) - sp = ((int64)-sp_rate)*status->max_sp/100; - if (sp_rate && !sp) - sp = 1; - - //Ugly check in case damage dealt is too much for the received args of - //status_heal / status_damage. [Skotlex] - if (hp > INT_MAX) { - hp -= INT_MAX; - if (flag) - status_damage(src, target, INT_MAX, 0, 0, (!src||src==target?5:1)); - else - status_heal(target, INT_MAX, 0, 0); - } - if (sp > INT_MAX) { - sp -= INT_MAX; - if (flag) - status_damage(src, target, 0, INT_MAX, 0, (!src||src==target?5:1)); - else - status_heal(target, 0, INT_MAX, 0); - } - if (flag) - return status_damage(src, target, hp, sp, 0, (!src||src==target?5:1)); - return status_heal(target, hp, sp, 0); -} - -int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp) -{ - struct status_data *status; - unsigned int hp, sp; - if (!status_isdead(bl)) return 0; - - status = status_get_status_data(bl); - if (status == &dummy_status) - return 0; //Invalid target. - - hp = (int64)status->max_hp * per_hp/100; - sp = (int64)status->max_sp * per_sp/100; - - if(hp > status->max_hp - status->hp) - hp = status->max_hp - status->hp; - else if (per_hp && !hp) - hp = 1; - - if(sp > status->max_sp - status->sp) - sp = status->max_sp - status->sp; - else if (per_sp && !sp) - sp = 1; - - status->hp += hp; - status->sp += sp; - - if (bl->prev) //Animation only if character is already on a map. - clif_resurrection(bl, 1); - switch (bl->type) { - case BL_PC: pc_revive((TBL_PC*)bl, hp, sp); break; - case BL_MOB: mob_revive((TBL_MOB*)bl, hp); break; - case BL_HOM: merc_hom_revive((TBL_HOM*)bl, hp, sp); break; - } - return 1; -} - -/*========================================== - * Checks whether the src can use the skill on the target, - * taking into account status/option of both source/target. [Skotlex] - * flag: - * 0 - Trying to use skill on target. - * 1 - Cast bar is done. - * 2 - Skill already pulled off, check is due to ground-based skills or splash-damage ones. - * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack. - * target MAY Be null, in which case the checks are only to see - * whether the source can cast or not the skill on the ground. - *------------------------------------------*/ -int status_check_skilluse(struct block_list *src, struct block_list *target, uint16 skill_id, int flag) -{ - struct status_data *status; - struct status_change *sc=NULL, *tsc; - int hide_flag; - - status = src?status_get_status_data(src):&dummy_status; - - if (src && src->type != BL_PC && status_isdead(src)) - return 0; - - if (!skill_id) { //Normal attack checks. - if (!(status->mode&MD_CANATTACK)) - return 0; //This mode is only needed for melee attacking. - //Dead state is not checked for skills as some skills can be used - //on dead characters, said checks are left to skill.c [Skotlex] - if (target && status_isdead(target)) - return 0; - if( src && (sc = status_get_sc(src)) && sc->data[SC_CRYSTALIZE] && src->type != BL_MOB) - return 0; - } - - switch( skill_id ) { - case PA_PRESSURE: - if( flag && target ) { - //Gloria Avoids pretty much everything.... - tsc = status_get_sc(target); - if(tsc && tsc->option&OPTION_HIDE) - return 0; - } - break; - case GN_WALLOFTHORN: - if( target && status_isdead(target) ) - return 0; - break; - case AL_TELEPORT: - //Should fail when used on top of Land Protector [Skotlex] - if (src && map_getcell(src->m, src->x, src->y, CELL_CHKLANDPROTECTOR) - && !(status->mode&MD_BOSS) - && (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id)) - return 0; - break; - default: - break; - } - - if ( src ) sc = status_get_sc(src); - - if( sc && sc->count ) { - - if (skill_id != RK_REFRESH && sc->opt1 >0 && (sc->opt1 != OPT1_CRYSTALIZE && src->type != BL_MOB) && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { //Stuned/Frozen/etc - if (flag != 1) //Can't cast, casted stuff can't damage. - return 0; - if (!(skill_get_inf(skill_id)&INF_GROUND_SKILL)) - return 0; //Targetted spells can't come off. - } - - if ( - (sc->data[SC_TRICKDEAD] && skill_id != NV_TRICKDEAD) - || (sc->data[SC_AUTOCOUNTER] && !flag) - || (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF && skill_id != PA_GOSPEL) - || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF && flag != 2) - ) - return 0; - - if (sc->data[SC_WINKCHARM] && target && !flag) { //Prevents skill usage - if( unit_bl2ud(src) && (unit_bl2ud(src))->walktimer == INVALID_TIMER ) - unit_walktobl(src, map_id2bl(sc->data[SC_WINKCHARM]->val2), 3, 1); - clif_emotion(src, E_LV); - return 0; - } - - if (sc->data[SC_BLADESTOP]) { - switch (sc->data[SC_BLADESTOP]->val1) - { - case 5: if (skill_id == MO_EXTREMITYFIST) break; - case 4: if (skill_id == MO_CHAINCOMBO) break; - case 3: if (skill_id == MO_INVESTIGATE) break; - case 2: if (skill_id == MO_FINGEROFFENSIVE) break; - default: return 0; - } - } - - if (sc->data[SC_DANCING] && flag!=2) { - if( src->type == BL_PC && skill_id >= WA_SWING_DANCE && skill_id <= WM_UNLIMITED_HUMMING_VOICE ) - { // Lvl 5 Lesson or higher allow you use 3rd job skills while dancing.v - if( pc_checkskill((TBL_PC*)src,WM_LESSON) < 5 ) - return 0; - } else if(sc->data[SC_LONGING]) { //Allow everything except dancing/re-dancing. [Skotlex] - if (skill_id == BD_ENCORE || - skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_ENSEMBLE_SKILL) - ) - return 0; - } else { - switch (skill_id) { - case BD_ADAPTATION: - case CG_LONGINGFREEDOM: - case BA_MUSICALSTRIKE: - case DC_THROWARROW: - break; - default: - return 0; - } - } - if ((sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE && skill_id == BD_ADAPTATION) - return 0; //Can't amp out of Wand of Hermode :/ [Skotlex] - } - - if (skill_id && //Do not block item-casted skills. - (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id) - ) { //Skills blocked through status changes... - if (!flag && ( //Blocked only from using the skill (stuff like autospell may still go through - sc->cant.cast || - (sc->data[SC_MARIONETTE] && skill_id != CG_MARIONETTE) || //Only skill you can use is marionette again to cancel it - (sc->data[SC_MARIONETTE2] && skill_id == CG_MARIONETTE) || //Cannot use marionette if you are being buffed by another - (sc->data[SC_STASIS] && skill_block_check(src, SC_STASIS, skill_id)) || - (sc->data[SC_KAGEHUMI] && skill_block_check(src, SC_KAGEHUMI, skill_id)) - )) - return 0; - - //Skill blocking. - if ( - (sc->data[SC_VOLCANO] && skill_id == WZ_ICEWALL) || - (sc->data[SC_ROKISWEIL] && skill_id != BD_ADAPTATION) || - (sc->data[SC_HERMODE] && skill_get_inf(skill_id) & INF_SUPPORT_SKILL) || - (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOSKILL) - ) - return 0; - - if( sc->data[SC__MANHOLE] || ((tsc = status_get_sc(target)) && tsc->data[SC__MANHOLE]) ) { - switch(skill_id) {//##TODO## make this a flag in skill_db? - // Skills that can be used even under Man Hole effects. - case SC_SHADOWFORM: - case SC_STRIPACCESSARY: - break; - default: - return 0; - } - } - - } - } - - if (sc && sc->option) - { - if (sc->option&OPTION_HIDE) - switch (skill_id) { //Usable skills while hiding. - case TF_HIDING: - case AS_GRIMTOOTH: - case RG_BACKSTAP: - case RG_RAID: - case NJ_SHADOWJUMP: - case NJ_KIRIKAGE: - case KO_YAMIKUMO: - break; - default: - //Non players can use all skills while hidden. - if (!skill_id || src->type == BL_PC) - return 0; - } - if (sc->option&OPTION_CHASEWALK && skill_id != 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; - - tsc = status_get_sc(target); - - if(tsc && tsc->count) { - /* attacks in invincible are capped to 1 damage and handled in batte.c; allow spell break and eske for sealed shrine GDB when in INVINCIBLE state. */ - if( tsc->data[SC_INVINCIBLE] && !tsc->data[SC_INVINCIBLEOFF] && skill_id && !(skill_id&(SA_SPELLBREAKER|SL_SKE)) ) - return 0; - if(!skill_id && tsc->data[SC_TRICKDEAD]) - return 0; - if((skill_id == WZ_STORMGUST || skill_id == WZ_FROSTNOVA || skill_id == NJ_HYOUSYOURAKU) - && tsc->data[SC_FREEZE]) - return 0; - if(skill_id == PR_LEXAETERNA && (tsc->data[SC_FREEZE] || (tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE))) - return 0; - } - - //If targetting, cloak+hide protect you, otherwise only hiding does. - hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK); - - //You cannot hide from ground skills. - if( skill_get_ele(skill_id,1) == ELE_EARTH ) //TODO: Need Skill Lv here :/ - hide_flag &= ~OPTION_HIDE; - - switch( target->type ) { - case BL_PC: { - struct map_session_data *sd = (TBL_PC*) target; - bool is_boss = (status->mode&MD_BOSS); - bool is_detect = ((status->mode&MD_DETECTOR)?true:false);//god-knows-why gcc doesn't shut up until this happens - if (pc_isinvisible(sd)) - return 0; - if (tsc->option&hide_flag && !is_boss && - ((sd->special_state.perfect_hiding || !is_detect) || - (tsc->data[SC_CLOAKINGEXCEED] && is_detect))) - return 0; - if( tsc->data[SC_CAMOUFLAGE] && !(is_boss || is_detect) && !skill_id ) - return 0; - if( tsc->data[SC_STEALTHFIELD] && !is_boss ) - return 0; - } - break; - case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them). - //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex] - if (status->mode&MD_LOOTER) - return 1; - return 0; - case BL_HOM: - case BL_MER: - case BL_ELEM: - if( target->type == BL_HOM && skill_id && battle_config.hom_setting&0x1 && skill_get_inf(skill_id)&INF_SUPPORT_SKILL && battle_get_master(target) != src ) - return 0; // Can't use support skills on Homunculus (only Master/Self) - if( target->type == BL_MER && (skill_id == PR_ASPERSIO || (skill_id >= SA_FLAMELAUNCHER && skill_id <= SA_SEISMICWEAPON)) && battle_get_master(target) != src ) - return 0; // Can't use Weapon endow skills on Mercenary (only Master) - if( skill_id == AM_POTIONPITCHER && ( target->type == BL_MER || target->type == BL_ELEM) ) - return 0; // Can't use Potion Pitcher on Mercenaries - default: - //Check for chase-walk/hiding/cloaking opponents. - if( tsc ) { - if( tsc->option&hide_flag && !(status->mode&(MD_BOSS|MD_DETECTOR))) - return 0; - if( tsc->data[SC_STEALTHFIELD] && !(status->mode&MD_BOSS) ) - return 0; - } - } - return 1; -} - -//Checks whether the source can see and chase target. -int status_check_visibility(struct block_list *src, struct block_list *target) -{ - int view_range; - struct status_data* status = status_get_status_data(src); - struct status_change* tsc = status_get_sc(target); - switch (src->type) { - case BL_MOB: - view_range = ((TBL_MOB*)src)->min_chase; - break; - case BL_PET: - view_range = ((TBL_PET*)src)->db->range2; - break; - default: - view_range = AREA_SIZE; - } - - if (src->m != target->m || !check_distance_bl(src, target, view_range)) - return 0; - - if( tsc && tsc->data[SC_STEALTHFIELD] ) - return 0; - - switch (target->type) - { //Check for chase-walk/hiding/cloaking opponents. - case BL_PC: - if ( tsc->data[SC_CLOAKINGEXCEED] && !(status->mode&MD_BOSS) ) - return 0; - if( (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY] || 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) || tsc->data[SC__INVISIBILITY] || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&(MD_BOSS|MD_DETECTOR)) ) - return 0; - - } - - return 1; -} - -// Basic ASPD value -int status_base_amotion_pc(struct map_session_data* sd, struct status_data* status) -{ - int amotion; -#ifdef RENEWAL_ASPD - short mod = -1; - - switch( sd->weapontype2 ){ // adjustment for dual weilding - case W_DAGGER: mod = 0; break; // 0, 1, 1 - case W_1HSWORD: - case W_1HAXE: mod = 1; - if( (sd->class_&MAPID_THIRDMASK) == MAPID_GUILLOTINE_CROSS ) // 0, 2, 3 - mod = sd->weapontype2 / W_1HSWORD + W_1HSWORD / sd->weapontype2 ; - } - - amotion = ( sd->status.weapon < MAX_WEAPON_TYPE && mod < 0 ) - ? (aspd_base[pc_class2idx(sd->status.class_)][sd->status.weapon]) // single weapon - : ((aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2] // dual-wield - + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2]) * 6 / 10 + 10 * mod - - aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2] - + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype1]); - - if ( sd->status.shield ) - amotion += ( 2000 - aspd_base[pc_class2idx(sd->status.class_)][W_FIST] ) + - ( aspd_base[pc_class2idx(sd->status.class_)][MAX_WEAPON_TYPE] - 2000 ); - -#else - // 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 - - // percentual delay reduction from stats - amotion -= amotion * (4*status->agi + status->dex)/1000; -#endif - // raw delay adjustment from bAspd bonus - amotion += sd->bonus.aspd_add; - - return amotion; -} - -static unsigned short status_base_atk(const struct block_list *bl, const struct status_data *status) -{ - int flag = 0, str, dex, -#ifdef RENEWAL - rstr, -#endif - dstr; - - - if(!(bl->type&battle_config.enable_baseatk)) - return 0; - - if (bl->type == BL_PC) - switch(((TBL_PC*)bl)->status.weapon){ - case W_BOW: - case W_MUSICAL: - case W_WHIP: - case W_REVOLVER: - case W_RIFLE: - case W_GATLING: - case W_SHOTGUN: - case W_GRENADE: - flag = 1; - } - if (flag) { -#ifdef RENEWAL - rstr = -#endif - str = status->dex; - dex = status->str; - } else { -#ifdef RENEWAL - rstr = -#endif - str = status->str; - dex = status->dex; - } - //Normally only players have base-atk, but homunc have a different batk - // equation, hinting that perhaps non-players should use this for batk. - // [Skotlex] - dstr = str/10; - str += dstr*dstr; - if (bl->type == BL_PC) -#ifdef RENEWAL - str = (rstr*10 + dex*10/5 + status->luk*10/3 + ((TBL_PC*)bl)->status.base_level*10/4)/10; -#else - str+= dex/5 + status->luk/5; -#endif - return cap_value(str, 0, USHRT_MAX); -} - -#ifndef RENEWAL -static inline unsigned short status_base_matk_min(const struct status_data* status){ return status->int_+(status->int_/7)*(status->int_/7); } -static inline unsigned short status_base_matk_max(const struct status_data* status){ return status->int_+(status->int_/5)*(status->int_/5); } -#else -unsigned short status_base_matk(const struct status_data* status, int level){ return status->int_+(status->int_/2)+(status->dex/5)+(status->luk/3)+(level/4); } -#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) -{ - //Non players get the value set, players need to stack with previous bonuses. - if( bl->type != BL_PC ) - status->batk = - status->hit = status->flee = - status->def2 = status->mdef2 = - status->cri = status->flee2 = 0; - -#ifdef RENEWAL // renewal formulas - status->matk_min = status->matk_max = status_base_matk(status, level); - status->hit += level + status->dex + status->luk/3 + 175; //base level + ( every 1 dex = +1 hit ) + (every 3 luk = +1 hit) + 175 - status->flee += level + status->agi + status->luk/5 + 100; //base level + ( every 1 agi = +1 flee ) + (every 5 luk = +1 flee) + 100 - status->def2 += (int)(((float)level + status->vit)/2 + ((float)status->agi/5)); //base level + (every 2 vit = +1 def) + (every 5 agi = +1 def) - status->mdef2 += (int)(status->int_ + ((float)level/4) + ((float)status->dex/5) + ((float)status->vit/5)); //(every 4 base level = +1 mdef) + (every 1 int = +1 mdef) + (every 5 dex = +1 mdef) + (every 5 vit = +1 mdef) -#else - status->matk_min = status_base_matk_min(status); - status->matk_max = status_base_matk_max(status); - 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 += 10 + (status->luk*10/3); //(every 1 luk = +0.3 critical) - else - status->cri = 0; - - if (bl->type&battle_config.enable_perfect_flee) - status->flee2 += status->luk + 10; //(every 10 luk = +1 perfect flee) - else - status->flee2 = 0; - - if (status->batk) { - int temp = status->batk + status_base_atk(bl, status); - status->batk = cap_value(temp, 0, USHRT_MAX); - } else - status->batk = status_base_atk(bl, status); - if (status->cri) - switch (bl->type) { - case BL_MOB: - if(battle_config.mob_critical_rate != 100) - status->cri = status->cri*battle_config.mob_critical_rate/100; - if(!status->cri && battle_config.mob_critical_rate) - status->cri = 10; - break; - case BL_PC: - //Players don't have a critical adjustment setting as of yet. - break; - default: - if(battle_config.critical_rate != 100) - status->cri = status->cri*battle_config.critical_rate/100; - if (!status->cri && battle_config.critical_rate) - status->cri = 10; - } - if(bl->type&BL_REGEN) - status_calc_regen(bl, status, status_get_regen_data(bl)); -} - -//Skotlex: Calculates the initial status for the given mob -//first will only be false when the mob leveled up or got a GuardUp level. -int status_calc_mob_(struct mob_data* md, bool first) -{ - struct status_data *status; - struct block_list *mbl = NULL; - int flag=0; - - if(first) - { //Set basic level on respawn. - if (md->level > 0 && md->level <= MAX_LEVEL && md->level != md->db->lv) - ; - else - md->level = md->db->lv; - } - - //Check if we need custom base-status - if (battle_config.mobs_level_up && md->level > md->db->lv) - flag|=1; - - if (md->special_state.size) - flag|=2; - - if (md->guardian_data && md->guardian_data->guardup_lv) - flag|=4; - if (md->class_ == MOBID_EMPERIUM) - flag|=4; - - if (battle_config.slaves_inherit_speed && md->master_id) - flag|=8; - - if (md->master_id && md->special_state.ai>1) - flag|=16; - - if (!flag) - { //No special status required. - if (md->base_status) { - aFree(md->base_status); - md->base_status = NULL; - } - if(first) - memcpy(&md->status, &md->db->status, sizeof(struct status_data)); - return 0; - } - if (!md->base_status) - md->base_status = (struct status_data*)aCalloc(1, sizeof(struct status_data)); - - status = md->base_status; - memcpy(status, &md->db->status, sizeof(struct status_data)); - - if (flag&(8|16)) - mbl = map_id2bl(md->master_id); - - if (flag&8 && mbl) { - struct status_data *mstatus = status_get_base_status(mbl); - if (mstatus && - battle_config.slaves_inherit_speed&(mstatus->mode&MD_CANMOVE?1:2)) - status->speed = mstatus->speed; - if( status->speed < 2 ) /* minimum for the unit to function properly */ - status->speed = 2; - } - - if (flag&16 && mbl) - { //Max HP setting from Summon Flora/marine Sphere - struct unit_data *ud = unit_bl2ud(mbl); - //Remove special AI when this is used by regular mobs. - if (mbl->type == BL_MOB && !((TBL_MOB*)mbl)->special_state.ai) - md->special_state.ai = 0; - if (ud) - { // different levels of HP according to skill level - if (ud->skill_id == AM_SPHEREMINE) { - status->max_hp = 2000 + 400*ud->skill_lv; - } else if(ud->skill_id == KO_ZANZOU){ - status->max_hp = 3000 + 3000 * ud->skill_lv; - } else { //AM_CANNIBALIZE - status->max_hp = 1500 + 200*ud->skill_lv + 10*status_get_lv(mbl); - status->mode|= MD_CANATTACK|MD_AGGRESSIVE; - } - status->hp = status->max_hp; - } - } - - if (flag&1) - { // increase from mobs leveling up [Valaris] - int diff = md->level - md->db->lv; - status->str+= diff; - status->agi+= diff; - status->vit+= diff; - status->int_+= diff; - status->dex+= diff; - status->luk+= diff; - status->max_hp += diff*status->vit; - status->max_sp += diff*status->int_; - status->hp = status->max_hp; - status->sp = status->max_sp; - status->speed -= cap_value(diff, 0, status->speed - 10); - } - - - if (flag&2 && battle_config.mob_size_influence) - { // change for sized monsters [Valaris] - if (md->special_state.size==SZ_MEDIUM) { - status->max_hp>>=1; - status->max_sp>>=1; - if (!status->max_hp) status->max_hp = 1; - if (!status->max_sp) status->max_sp = 1; - status->hp=status->max_hp; - status->sp=status->max_sp; - status->str>>=1; - status->agi>>=1; - status->vit>>=1; - status->int_>>=1; - status->dex>>=1; - status->luk>>=1; - if (!status->str) status->str = 1; - if (!status->agi) status->agi = 1; - if (!status->vit) status->vit = 1; - if (!status->int_) status->int_ = 1; - if (!status->dex) status->dex = 1; - if (!status->luk) status->luk = 1; - } else if (md->special_state.size==SZ_BIG) { - status->max_hp<<=1; - status->max_sp<<=1; - status->hp=status->max_hp; - status->sp=status->max_sp; - status->str<<=1; - status->agi<<=1; - status->vit<<=1; - status->int_<<=1; - status->dex<<=1; - status->luk<<=1; - } - } - - status_calc_misc(&md->bl, status, md->level); - - if(flag&4) - { // Strengthen Guardians - custom value +10% / lv - struct guild_castle *gc; - gc=guild_mapname2gc(map[md->bl.m].name); - if (!gc) - ShowError("status_calc_mob: No castle set at map %s\n", map[md->bl.m].name); - else - if(gc->castle_id < 24 || md->class_ == MOBID_EMPERIUM) { -#ifdef RENEWAL - status->max_hp += 50 * gc->defense; - status->max_sp += 70 * gc->defense; -#else - status->max_hp += 1000 * gc->defense; - status->max_sp += 200 * gc->defense; -#endif - status->hp = status->max_hp; - status->sp = status->max_sp; - 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; - status->rhw.atk += status->rhw.atk * 10*md->guardian_data->guardup_lv/100; - status->rhw.atk2 += status->rhw.atk2 * 10*md->guardian_data->guardup_lv/100; - status->aspd_rate -= 100*md->guardian_data->guardup_lv; - } - } - - if( first ) //Initial battle status - memcpy(&md->status, status, sizeof(struct status_data)); - - return 1; -} - -//Skotlex: Calculates the stats of the given pet. -int status_calc_pet_(struct pet_data *pd, bool first) -{ - nullpo_ret(pd); - - if (first) { - memcpy(&pd->status, &pd->db->status, sizeof(struct status_data)); - pd->status.mode = MD_CANMOVE; // pets discard all modes, except walking - pd->status.speed = pd->petDB->speed; - - if(battle_config.pet_attack_support || battle_config.pet_damage_support) - {// attack support requires the pet to be able to attack - pd->status.mode|= MD_CANATTACK; - } - } - - if (battle_config.pet_lv_rate && pd->msd) - { - struct map_session_data *sd = pd->msd; - int lv; - - lv =sd->status.base_level*battle_config.pet_lv_rate/100; - if (lv < 0) - lv = 1; - if (lv != pd->pet.level || first) - { - struct status_data *bstat = &pd->db->status, *status = &pd->status; - pd->pet.level = lv; - if (!first) //Lv Up animation - clif_misceffect(&pd->bl, 0); - status->rhw.atk = (bstat->rhw.atk*lv)/pd->db->lv; - status->rhw.atk2 = (bstat->rhw.atk2*lv)/pd->db->lv; - status->str = (bstat->str*lv)/pd->db->lv; - status->agi = (bstat->agi*lv)/pd->db->lv; - status->vit = (bstat->vit*lv)/pd->db->lv; - status->int_ = (bstat->int_*lv)/pd->db->lv; - status->dex = (bstat->dex*lv)/pd->db->lv; - status->luk = (bstat->luk*lv)/pd->db->lv; - - status->rhw.atk = cap_value(status->rhw.atk, 1, battle_config.pet_max_atk1); - status->rhw.atk2 = cap_value(status->rhw.atk2, 2, battle_config.pet_max_atk2); - status->str = cap_value(status->str,1,battle_config.pet_max_stats); - status->agi = cap_value(status->agi,1,battle_config.pet_max_stats); - status->vit = cap_value(status->vit,1,battle_config.pet_max_stats); - status->int_= cap_value(status->int_,1,battle_config.pet_max_stats); - status->dex = cap_value(status->dex,1,battle_config.pet_max_stats); - status->luk = cap_value(status->luk,1,battle_config.pet_max_stats); - - status_calc_misc(&pd->bl, &pd->status, lv); - - if (!first) //Not done the first time because the pet is not visible yet - clif_send_petstatus(sd); - } - } else if (first) { - status_calc_misc(&pd->bl, &pd->status, pd->db->lv); - if (!battle_config.pet_lv_rate && pd->pet.level != pd->db->lv) - pd->pet.level = pd->db->lv; - } - - //Support rate modifier (1000 = 100%) - pd->rate_fix = 1000*(pd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500; - if(battle_config.pet_support_rate != 100) - pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100; - - return 1; -} - -/// Helper function for status_base_pc_maxhp(), used to pre-calculate the hp_sigma_val[] array -static void status_calc_sigma(void) -{ - int i,j; - - for(i = 0; i < CLASS_COUNT; i++) - { - unsigned int k = 0; - hp_sigma_val[i][0] = hp_sigma_val[i][1] = 0; - for(j = 2; j <= MAX_LEVEL; j++) - { - k += (hp_coefficient[i]*j + 50) / 100; - hp_sigma_val[i][j] = k; - if (k >= INT_MAX) - break; //Overflow protection. [Skotlex] - } - for(; j <= MAX_LEVEL; j++) - hp_sigma_val[i][j] = INT_MAX; - } -} - -/// Calculates base MaxHP value according to class and base level -/// The recursive equation used to calculate level bonus is (using integer operations) -/// f(0) = 35 | f(x+1) = f(x) + A + (x + B)*C/D -/// which reduces to something close to -/// f(x) = 35 + x*(A + B*C/D) + sum(i=2..x){ i*C/D } -static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data* status) -{ - uint64 val = pc_class2idx(sd->status.class_); - val = 35 + sd->status.base_level*(int64)hp_coefficient2[val]/100 + hp_sigma_val[val][sd->status.base_level]; - - if((sd->class_&MAPID_UPPERMASK) == MAPID_NINJA || (sd->class_&MAPID_UPPERMASK) == MAPID_GUNSLINGER) - val += 100; //Since their HP can't be approximated well enough without this. - if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON)) - val *= 3; //Triple max HP for top ranking Taekwons over level 90. - if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99) - val += 2000; //Supernovice lvl99 hp bonus. - - val += val * status->vit/100; // +1% per each point of VIT - - if (sd->class_&JOBL_UPPER) - val += val * 25/100; //Trans classes get a 25% hp bonus - else if (sd->class_&JOBL_BABY) - val -= val * 30/100; //Baby classes get a 30% hp penalty - return (unsigned int)val; -} - -static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct status_data *status) -{ - uint64 val; - - val = 10 + sd->status.base_level*(int64)sp_coefficient[pc_class2idx(sd->status.class_)]/100; - val += val * status->int_/100; - - if (sd->class_&JOBL_UPPER) - val += val * 25/100; - else if (sd->class_&JOBL_BABY) - val -= val * 30/100; - if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON)) - val *= 3; //Triple max SP for top ranking Taekwons over level 90. - - return (unsigned int)val; -} - -//Calculates player data from scratch without counting SC adjustments. -//Should be invoked whenever players raise stats, learn passive skills or change equipment. -int status_calc_pc_(struct map_session_data* sd, bool first) -{ - static int calculating = 0; //Check for recursive call preemption. [Skotlex] - struct status_data *status; // pointer to the player's base status - const struct status_change *sc = &sd->sc; - struct s_skill b_skill[MAX_SKILL]; // previous skill tree - int b_weight, b_max_weight, b_cart_weight_max, // previous weight - i, index, skill,refinedef=0; - int64 i64; - - if (++calculating > 10) //Too many recursive calls! - return -1; - - // remember player-specific values that are currently being shown to the client (for refresh purposes) - memcpy(b_skill, &sd->status.skill, sizeof(b_skill)); - b_weight = sd->weight; - b_max_weight = sd->max_weight; - b_cart_weight_max = sd->cart_weight_max; - - pc_calc_skilltree(sd); // SkillTree calculation - - sd->max_weight = max_weight_base[pc_class2idx(sd->status.class_)]+sd->status.str*300; - - if(first) { - //Load Hp/SP from char-received data. - sd->battle_status.hp = sd->status.hp; - sd->battle_status.sp = sd->status.sp; - sd->regen.sregen = &sd->sregen; - sd->regen.ssregen = &sd->ssregen; - sd->weight=0; - for(i=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL) - continue; - sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount; - } - sd->cart_weight=0; - sd->cart_num=0; - for(i=0;i<MAX_CART;i++){ - if(sd->status.cart[i].nameid==0) - continue; - sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount; - sd->cart_num++; - } - } - - status = &sd->base_status; - // these are not zeroed. [zzo] - sd->hprate=100; - sd->sprate=100; - sd->castrate=100; - sd->delayrate=100; - sd->dsprate=100; - sd->hprecov_rate = 100; - sd->sprecov_rate = 100; - sd->matk_rate = 100; - sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100; - sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100; - sd->regen.state.block = 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] - memset (sd->param_bonus, 0, sizeof(sd->param_bonus) - + sizeof(sd->param_equip) - + sizeof(sd->subele) - + sizeof(sd->subrace) - + sizeof(sd->subrace2) - + sizeof(sd->subsize) - + sizeof(sd->reseff) - + sizeof(sd->weapon_coma_ele) - + sizeof(sd->weapon_coma_race) - + sizeof(sd->weapon_atk) - + sizeof(sd->weapon_atk_rate) - + sizeof(sd->arrow_addele) - + sizeof(sd->arrow_addrace) - + sizeof(sd->arrow_addsize) - + sizeof(sd->magic_addele) - + sizeof(sd->magic_addrace) - + sizeof(sd->magic_addsize) - + sizeof(sd->magic_atk_ele) - + sizeof(sd->critaddrace) - + sizeof(sd->expaddrace) - + sizeof(sd->ignore_mdef) - + sizeof(sd->ignore_def) - + sizeof(sd->itemgrouphealrate) - + sizeof(sd->sp_gain_race) - + sizeof(sd->sp_gain_race_attack) - + sizeof(sd->hp_gain_race_attack) - ); - - memset (&sd->right_weapon.overrefine, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods)); - memset (&sd->left_weapon.overrefine, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods)); - - if (sd->special_state.intravision && !sd->sc.data[SC_INTRAVISION]) //Clear intravision as long as nothing else is using it - clif_status_load(&sd->bl, SI_INTRAVISION, 0); - - memset(&sd->special_state,0,sizeof(sd->special_state)); - memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp))); - - //FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex] - status->speed = DEFAULT_WALK_SPEED; - //Give them all modes except these (useful for clones) - status->mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK); - - status->size = (sd->class_&JOBL_BABY)?SZ_SMALL:SZ_MEDIUM; - if (battle_config.character_size && pc_isriding(sd)) { //[Lupus] - if (sd->class_&JOBL_BABY) { - if (battle_config.character_size&SZ_BIG) - status->size++; - } else - if(battle_config.character_size&SZ_MEDIUM) - status->size++; - } - status->aspd_rate = 1000; - status->ele_lv = 1; - status->race = RC_DEMIHUMAN; - - //zero up structures... - memset(&sd->autospell,0,sizeof(sd->autospell) - + sizeof(sd->autospell2) - + sizeof(sd->autospell3) - + sizeof(sd->addeff) - + sizeof(sd->addeff2) - + sizeof(sd->addeff3) - + sizeof(sd->skillatk) - + sizeof(sd->skillusesprate) - + sizeof(sd->skillusesp) - + sizeof(sd->skillheal) - + sizeof(sd->skillheal2) - + sizeof(sd->hp_loss) - + sizeof(sd->sp_loss) - + sizeof(sd->hp_regen) - + sizeof(sd->sp_regen) - + sizeof(sd->skillblown) - + sizeof(sd->skillcast) - + sizeof(sd->add_def) - + sizeof(sd->add_mdef) - + sizeof(sd->add_mdmg) - + sizeof(sd->add_drop) - + sizeof(sd->itemhealrate) - + sizeof(sd->subele2) - + sizeof(sd->skillcooldown) - + sizeof(sd->skillfixcast) - + sizeof(sd->skillvarcast) - ); - - memset (&sd->bonus, 0,sizeof(sd->bonus)); - - // Autobonus - pc_delautobonus(sd,sd->autobonus,ARRAYLENGTH(sd->autobonus),true); - pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus2),true); - pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus3),true); - - // Parse equipment. - for(i=0;i<EQI_MAX-1;i++) { - current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus] - if(index < 0) - continue; - if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) - continue; - if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) - continue; - if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) - continue; - if(i == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index) - continue; - if(i == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index)) - continue; - if(!sd->inventory_data[index]) - continue; - - status->def += sd->inventory_data[index]->def; - - if(first && sd->inventory_data[index]->equip_script) - { //Execute equip-script on login - run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0); - if (!calculating) - return 1; - } - - // sanitize the refine level in case someone decreased the value inbetween - if (sd->status.inventory[index].refine > MAX_REFINE) - sd->status.inventory[index].refine = MAX_REFINE; - - if(sd->inventory_data[index]->type == IT_WEAPON) { - int r,wlv = sd->inventory_data[index]->wlv; - struct weapon_data *wd; - struct weapon_atk *wa; - if (wlv >= REFINE_TYPE_MAX) - wlv = REFINE_TYPE_MAX - 1; - if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L) { - wd = &sd->left_weapon; // Left-hand weapon - wa = &status->lhw; - } else { - wd = &sd->right_weapon; - wa = &status->rhw; - } - wa->atk += sd->inventory_data[index]->atk; - if ( (r = sd->status.inventory[index].refine) ) - wa->atk2 = refine_info[wlv].bonus[r-1] / 100; - -#ifdef RENEWAL - wa->matk += sd->inventory_data[index]->matk; - wa->wlv = wlv; - if( r ) // renewal magic attack refine bonus - wa->matk += refine_info[wlv].bonus[r-1] / 100; -#endif - - //Overrefine bonus. - if (r) - wd->overrefine = refine_info[wlv].randombonus_max[r-1] / 100; - - wa->range += sd->inventory_data[index]->range; - if(sd->inventory_data[index]->script) { - if (wd == &sd->left_weapon) { - sd->state.lr_flag = 1; - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - sd->state.lr_flag = 0; - } else - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - if (!calculating) //Abort, run_script retriggered this. [Skotlex] - return 1; - } - - if(sd->status.inventory[index].card[0]==CARD0_FORGE) - { // Forged weapon - wd->star += (sd->status.inventory[index].card[1]>>8); - if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg - if(pc_famerank(MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH)) - wd->star += 10; - - if (!wa->ele) //Do not overwrite element from previous bonuses. - wa->ele = (sd->status.inventory[index].card[1]&0x0f); - } - } - else if(sd->inventory_data[index]->type == IT_ARMOR) { - int r; - if ( (r = sd->status.inventory[index].refine) ) - refinedef += refine_info[REFINE_TYPE_ARMOR].bonus[r-1]; - if(sd->inventory_data[index]->script) { - if( i == EQI_HAND_L ) //Shield - sd->state.lr_flag = 3; - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - if( i == EQI_HAND_L ) //Shield - sd->state.lr_flag = 0; - if (!calculating) //Abort, run_script retriggered this. [Skotlex] - return 1; - } - } - } - - if(sd->equip_index[EQI_AMMO] >= 0){ - index = sd->equip_index[EQI_AMMO]; - if(sd->inventory_data[index]){ // Arrows - sd->bonus.arrow_atk += sd->inventory_data[index]->atk; - sd->state.lr_flag = 2; - if( !itemdb_is_GNthrowable(sd->inventory_data[index]->nameid) ) //don't run scripts on throwable items - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - sd->state.lr_flag = 0; - if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex] - return 1; - } - } - - /* we've got combos to process */ - if( sd->combos.count ) { - for( i = 0; i < sd->combos.count; i++ ) { - run_script(sd->combos.bonus[i],0,sd->bl.id,0); - if (!calculating) //Abort, run_script retriggered this. - return 1; - } - } - - //Store equipment script bonuses - memcpy(sd->param_equip,sd->param_bonus,sizeof(sd->param_equip)); - memset(sd->param_bonus, 0, sizeof(sd->param_bonus)); - - status->def += (refinedef+50)/100; - - //Parse Cards - for(i=0;i<EQI_MAX-1;i++) { - current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus] - if(index < 0) - continue; - if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) - continue; - if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) - continue; - if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) - continue; - - if(sd->inventory_data[index]) { - int j,c; - struct item_data *data; - - //Card script execution. - if(itemdb_isspecial(sd->status.inventory[index].card[0])) - continue; - for(j=0;j<MAX_SLOTS;j++){ // Uses MAX_SLOTS to support Soul Bound system [Inkfish] - current_equip_card_id= c= sd->status.inventory[index].card[j]; - if(!c) - continue; - data = itemdb_exists(c); - if(!data) - continue; - if(first && data->equip_script) - { //Execute equip-script on login - run_script(data->equip_script,0,sd->bl.id,0); - if (!calculating) - return 1; - } - if(!data->script) - continue; - if(data->flag.no_equip) { //Card restriction checks. - if(map[sd->bl.m].flag.restricted && data->flag.no_equip&(8*map[sd->bl.m].zone)) - continue; - if(!map_flag_vs(sd->bl.m) && data->flag.no_equip&1) - continue; - if(map[sd->bl.m].flag.pvp && data->flag.no_equip&2) - continue; - if(map_flag_gvg(sd->bl.m) && data->flag.no_equip&4) - continue; - if(map[sd->bl.m].flag.battleground && data->flag.no_equip&8) - continue; - } - if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L) - { //Left hand status. - sd->state.lr_flag = 1; - run_script(data->script,0,sd->bl.id,0); - sd->state.lr_flag = 0; - } else - run_script(data->script,0,sd->bl.id,0); - if (!calculating) //Abort, run_script his function. [Skotlex] - return 1; - } - } - } - - if( sc->count && sc->data[SC_ITEMSCRIPT] ) - { - struct item_data *data = itemdb_exists(sc->data[SC_ITEMSCRIPT]->val1); - if( data && data->script ) - run_script(data->script,0,sd->bl.id,0); - } - - if( sd->pd ) - { // Pet Bonus - struct pet_data *pd = sd->pd; - if( pd && pd->petDB && pd->petDB->equip_script && pd->pet.intimate >= battle_config.pet_equip_min_friendly ) - run_script(pd->petDB->equip_script,0,sd->bl.id,0); - if( pd && pd->pet.intimate > 0 && (!battle_config.pet_equip_required || pd->pet.equip > 0) && pd->state.skillbonus == 1 && pd->bonus ) - pc_bonus(sd,pd->bonus->type, pd->bonus->val); - } - - //param_bonus now holds card bonuses. - if(status->rhw.range < 1) status->rhw.range = 1; - if(status->lhw.range < 1) status->lhw.range = 1; - if(status->rhw.range < status->lhw.range) - status->rhw.range = status->lhw.range; - - sd->bonus.double_rate += sd->bonus.double_add_rate; - sd->bonus.perfect_hit += sd->bonus.perfect_hit_add; - sd->bonus.splash_range += sd->bonus.splash_add_range; - - // Damage modifiers from weapon type - sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1]; - sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1]; - sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1]; - sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2]; - sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2]; - sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2]; - - 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. - sd->right_weapon.atkmods[1] = sd->right_weapon.atkmods[2]; - sd->left_weapon.atkmods[1] = sd->left_weapon.atkmods[2]; - } - -// ----- STATS CALCULATION ----- - - // Job bonuses - index = pc_class2idx(sd->status.class_); - for(i=0;i<(int)sd->status.job_level && i<MAX_LEVEL;i++){ - if(!job_bonus[index][i]) - continue; - switch(job_bonus[index][i]) { - case 1: status->str++; break; - case 2: status->agi++; break; - case 3: status->vit++; break; - case 4: status->int_++; break; - case 5: status->dex++; break; - case 6: status->luk++; break; - } - } - - // If a Super Novice has never died and is at least joblv 70, he gets all stats +10 - if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){ - status->str += 10; - status->agi += 10; - status->vit += 10; - status->int_+= 10; - status->dex += 10; - status->luk += 10; - } - - // Absolute modifiers from passive skills - if(pc_checkskill(sd,BS_HILTBINDING)>0) - status->str++; - if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0) - status->int_ += (skill+1)/2; // +1 INT / 2 lv - if((skill=pc_checkskill(sd,AC_OWL))>0) - status->dex += skill; - if((skill = pc_checkskill(sd,RA_RESEARCHTRAP))>0) - status->int_ += skill; - - // Bonuses from cards and equipment as well as base stat, remember to avoid overflows. - i = status->str + sd->status.str + sd->param_bonus[0] + sd->param_equip[0]; - status->str = cap_value(i,0,USHRT_MAX); - i = status->agi + sd->status.agi + sd->param_bonus[1] + sd->param_equip[1]; - status->agi = cap_value(i,0,USHRT_MAX); - i = status->vit + sd->status.vit + sd->param_bonus[2] + sd->param_equip[2]; - status->vit = cap_value(i,0,USHRT_MAX); - i = status->int_+ sd->status.int_+ sd->param_bonus[3] + sd->param_equip[3]; - status->int_ = cap_value(i,0,USHRT_MAX); - i = status->dex + sd->status.dex + sd->param_bonus[4] + sd->param_equip[4]; - status->dex = cap_value(i,0,USHRT_MAX); - i = status->luk + sd->status.luk + sd->param_bonus[5] + sd->param_equip[5]; - status->luk = cap_value(i,0,USHRT_MAX); - -// ------ BASE ATTACK CALCULATION ------ - - // Base batk value is set on status_calc_misc - // weapon-type bonus (FIXME: Why is the weapon_atk bonus applied to base attack?) - if (sd->status.weapon < MAX_WEAPON_TYPE && sd->weapon_atk[sd->status.weapon]) - status->batk += sd->weapon_atk[sd->status.weapon]; - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,BS_HILTBINDING))>0) - status->batk += 4; - -// ----- HP MAX CALCULATION ----- - - // Basic MaxHP value - //We hold the standard Max HP here to make it faster to recalculate on vit changes. - sd->status.max_hp = status_base_pc_maxhp(sd,status); - //This is done to handle underflows from negative Max HP bonuses - i64 = sd->status.max_hp + (int)status->max_hp; - status->max_hp = (unsigned int)cap_value(i64, 0, INT_MAX); - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,CR_TRUST))>0) - status->max_hp += skill*200; - - // Apply relative modifiers from equipment - if(sd->hprate < 0) - sd->hprate = 0; - if(sd->hprate!=100) - status->max_hp = (int64)status->max_hp * sd->hprate/100; - if(battle_config.hp_rate != 100) - status->max_hp = (int64)status->max_hp * battle_config.hp_rate/100; - - if(status->max_hp > (unsigned int)battle_config.max_hp) - status->max_hp = battle_config.max_hp; - else if(!status->max_hp) - status->max_hp = 1; - -// ----- SP MAX CALCULATION ----- - - // Basic MaxSP value - sd->status.max_sp = status_base_pc_maxsp(sd,status); - //This is done to handle underflows from negative Max SP bonuses - i64 = sd->status.max_sp + (int)status->max_sp; - status->max_sp = (unsigned int)cap_value(i64, 0, INT_MAX); - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,SL_KAINA))>0) - status->max_sp += 30*skill; - if((skill=pc_checkskill(sd,HP_MEDITATIO))>0) - status->max_sp += (int64)status->max_sp * skill/100; - if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0) - status->max_sp += (int64)status->max_sp * 2*skill/100; - if( (skill = pc_checkskill(sd,RA_RESEARCHTRAP)) > 0 ) - status->max_sp += 200 + 20 * skill; - if( (skill = pc_checkskill(sd,WM_LESSON)) > 0 ) - status->max_sp += 30 * skill; - - - // Apply relative modifiers from equipment - if(sd->sprate < 0) - sd->sprate = 0; - if(sd->sprate!=100) - status->max_sp = (int64)status->max_sp * sd->sprate/100; - if(battle_config.sp_rate != 100) - status->max_sp = (int64)status->max_sp * battle_config.sp_rate/100; - - if(status->max_sp > (unsigned int)battle_config.max_sp) - status->max_sp = battle_config.max_sp; - else if(!status->max_sp) - status->max_sp = 1; - -// ----- RESPAWN HP/SP ----- -// - //Calc respawn hp and store it on base_status - if (sd->special_state.restart_full_recover) - { - status->hp = status->max_hp; - status->sp = status->max_sp; - } else { - if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2) - && battle_config.restart_hp_rate < 50) - status->hp = status->max_hp>>1; - else - status->hp = (int64)status->max_hp * battle_config.restart_hp_rate/100; - if(!status->hp) - status->hp = 1; - - status->sp = (int64)status->max_sp * battle_config.restart_sp_rate /100; - - if( !status->sp ) /* the minimum for the respawn setting is SP:1 */ - status->sp = 1; - } - -// ----- MISC CALCULATION ----- - status_calc_misc(&sd->bl, status, sd->status.base_level); - - //Equipment modifiers for misc settings - if(sd->matk_rate < 0) - sd->matk_rate = 0; - - if(sd->matk_rate != 100){ - status->matk_max = status->matk_max * sd->matk_rate/100; - status->matk_min = status->matk_min * sd->matk_rate/100; - } - - if(sd->hit_rate < 0) - sd->hit_rate = 0; - if(sd->hit_rate != 100) - status->hit = status->hit * sd->hit_rate/100; - - if(sd->flee_rate < 0) - sd->flee_rate = 0; - if(sd->flee_rate != 100) - status->flee = status->flee * sd->flee_rate/100; - - if(sd->def2_rate < 0) - sd->def2_rate = 0; - if(sd->def2_rate != 100) - status->def2 = status->def2 * sd->def2_rate/100; - - if(sd->mdef2_rate < 0) - sd->mdef2_rate = 0; - if(sd->mdef2_rate != 100) - status->mdef2 = status->mdef2 * sd->mdef2_rate/100; - - if(sd->critical_rate < 0) - sd->critical_rate = 0; - if(sd->critical_rate != 100) - status->cri = status->cri * sd->critical_rate/100; - - if(sd->flee2_rate < 0) - sd->flee2_rate = 0; - if(sd->flee2_rate != 100) - status->flee2 = status->flee2 * sd->flee2_rate/100; - -// ----- HIT CALCULATION ----- - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0) - status->hit += skill*2; - if((skill=pc_checkskill(sd,AC_VULTURE))>0){ -#ifndef RENEWAL - status->hit += skill; -#endif - if(sd->status.weapon == W_BOW) - status->rhw.range += skill; - } - if(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE) - { - if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0) - status->hit += 2*skill; - if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) { - status->hit += skill; - status->rhw.range += skill; - } - } - -// ----- FLEE CALCULATION ----- - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,TF_MISS))>0) - 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 - if(sd->def_rate < 0) - sd->def_rate = 0; - if(sd->def_rate != 100) { - i = status->def * sd->def_rate/100; - status->def = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX); - } - -#ifndef RENEWAL - 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 char)battle_config.max_def; - } -#endif - -// ----- EQUIPMENT-MDEF CALCULATION ----- - - // Apply relative modifiers from equipment - if(sd->mdef_rate < 0) - sd->mdef_rate = 0; - if(sd->mdef_rate != 100) { - i = status->mdef * sd->mdef_rate/100; - status->mdef = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX); - } - -#ifndef RENEWAL - 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 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 - - // Basic ASPD value - i = status_base_amotion_pc(sd,status); - status->amotion = cap_value(i,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000); - - // Relative modifiers from passive skills -#ifndef RENEWAL_ASPD - if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK) - status->aspd_rate -= 5*skill; - if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd)) - status->aspd_rate -= 30*skill; - if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 && - (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) - status->aspd_rate -= ((skill+1)/2) * 10; - if(pc_isriding(sd)) - status->aspd_rate += 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY); - else if(pc_isridingdragon(sd)) - status->aspd_rate += 250-50*pc_checkskill(sd,RK_DRAGONTRAINING); -#else // needs more info - if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK) - status->aspd_rate += 5*skill; - if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd)) - status->aspd_rate += 30*skill; - if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 && - (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) - status->aspd_rate += ((skill+1)/2) * 10; - if(pc_isriding(sd)) - status->aspd_rate -= 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY); - else if(pc_isridingdragon(sd)) - status->aspd_rate -= 250-50*pc_checkskill(sd,RK_DRAGONTRAINING); -#endif - status->adelay = 2*status->amotion; - - -// ----- DMOTION ----- -// - i = 800-status->agi*4; - status->dmotion = cap_value(i, 400, 800); - if(battle_config.pc_damage_delay_rate != 100) - status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100; - -// ----- MISC CALCULATIONS ----- - - // Weight - if((skill=pc_checkskill(sd,MC_INCCARRY))>0) - sd->max_weight += 2000*skill; - if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0) - sd->max_weight += 10000; - else if(pc_isridingdragon(sd)) - sd->max_weight += 5000+2000*pc_checkskill(sd,RK_DRAGONTRAINING); - 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) - sd->max_weight += 2000*skill; - - sd->cart_weight_max = battle_config.max_cart_weight + (pc_checkskill(sd, GN_REMODELING_CART)*5000); - - if (pc_checkskill(sd,SM_MOVINGRECOVERY)>0) - sd->regen.state.walk = 1; - else - sd->regen.state.walk = 0; - - // Skill SP cost - if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 ) - sd->dsprate -= 4*skill; - - if(sc->data[SC_SERVICE4U]) - sd->dsprate -= sc->data[SC_SERVICE4U]->val3; - - if(sc->data[SC_SPCOST_RATE]) - sd->dsprate -= sc->data[SC_SPCOST_RATE]->val1; - - //Underflow protections. - if(sd->dsprate < 0) - sd->dsprate = 0; - if(sd->castrate < 0) - sd->castrate = 0; - if(sd->delayrate < 0) - sd->delayrate = 0; - if(sd->hprecov_rate < 0) - sd->hprecov_rate = 0; - if(sd->sprecov_rate < 0) - sd->sprecov_rate = 0; - - // Anti-element and anti-race - if((skill=pc_checkskill(sd,CR_TRUST))>0) - sd->subele[ELE_HOLY] += skill*5; - if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) { - sd->subele[ELE_NEUTRAL] += skill; - sd->subele[ELE_FIRE] += skill*4; - } - if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){ - skill = skill*4; - sd->right_weapon.addrace[RC_DRAGON]+=skill; - sd->left_weapon.addrace[RC_DRAGON]+=skill; - sd->magic_addrace[RC_DRAGON]+=skill; - sd->subrace[RC_DRAGON]+=skill; - } - - if(sc->count){ - if(sc->data[SC_CONCENTRATE]) { //Update the card-bonus data - sc->data[SC_CONCENTRATE]->val3 = sd->param_bonus[1]; //Agi - sc->data[SC_CONCENTRATE]->val4 = sd->param_bonus[4]; //Dex - } - if(sc->data[SC_SIEGFRIED]){ - i = sc->data[SC_SIEGFRIED]->val2; - sd->subele[ELE_WATER] += i; - sd->subele[ELE_EARTH] += i; - sd->subele[ELE_FIRE] += i; - sd->subele[ELE_WIND] += i; - sd->subele[ELE_POISON] += i; - sd->subele[ELE_HOLY] += i; - sd->subele[ELE_DARK] += i; - sd->subele[ELE_GHOST] += i; - sd->subele[ELE_UNDEAD] += i; - } - if(sc->data[SC_PROVIDENCE]){ - sd->subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2; - sd->subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2; - } - if(sc->data[SC_ARMOR_ELEMENT]) { //This status change should grant card-type elemental resist. - sd->subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT]->val1; - sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT]->val2; - sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT]->val3; - sd->subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT]->val4; - } - if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll - sd->subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1; - sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2; - sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3; - sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4; - } - if( sc->data[SC_FIRE_CLOAK_OPTION] ) { - i = sc->data[SC_FIRE_CLOAK_OPTION]->val2; - sd->subele[ELE_FIRE] += i; - sd->subele[ELE_WATER] -= i; - } - if( sc->data[SC_WATER_DROP_OPTION] ) { - i = sc->data[SC_WATER_DROP_OPTION]->val2; - sd->subele[ELE_WATER] += i; - sd->subele[ELE_WIND] -= i; - } - if( sc->data[SC_WIND_CURTAIN_OPTION] ) { - i = sc->data[SC_WIND_CURTAIN_OPTION]->val2; - sd->subele[ELE_WIND] += i; - sd->subele[ELE_EARTH] -= i; - } - if( sc->data[SC_STONE_SHIELD_OPTION] ) { - i = sc->data[SC_STONE_SHIELD_OPTION]->val2; - sd->subele[ELE_EARTH] += i; - sd->subele[ELE_FIRE] -= i; - } - if( sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3 ) - sd->magic_addele[ELE_FIRE] += 25; - if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 ) - sd->magic_addele[ELE_WATER] += 25; - if( sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 ) - sd->magic_addele[ELE_WIND] += 25; - if( sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3 ) - sd->magic_addele[ELE_EARTH] += 25; - } - status_cpy(&sd->battle_status, status); - -// ----- CLIENT-SIDE REFRESH ----- - if(!sd->bl.prev) { - //Will update on LoadEndAck - calculating = 0; - return 0; - } - if(memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill))) - clif_skillinfoblock(sd); - if(b_weight != sd->weight) - clif_updatestatus(sd,SP_WEIGHT); - if(b_max_weight != sd->max_weight) { - clif_updatestatus(sd,SP_MAXWEIGHT); - pc_updateweightstatus(sd); - } - if( b_cart_weight_max != sd->cart_weight_max ) { - clif_updatestatus(sd,SP_CARTINFO); - } - - calculating = 0; - - return 0; -} - -int status_calc_mercenary_(struct mercenary_data *md, bool first) -{ - struct status_data *status = &md->base_status; - struct s_mercenary *merc = &md->mercenary; - - if( first ) - { - memcpy(status, &md->db->status, sizeof(struct status_data)); - status->mode = MD_CANMOVE|MD_CANATTACK; - status->hp = status->max_hp; - status->sp = status->max_sp; - md->battle_status.hp = merc->hp; - md->battle_status.sp = merc->sp; - } - - status_calc_misc(&md->bl, status, md->db->lv); - status_cpy(&md->battle_status, status); - - return 0; -} - -int status_calc_homunculus_(struct homun_data *hd, bool first) -{ - struct status_data *status = &hd->base_status; - struct s_homunculus *hom = &hd->homunculus; - int skill; - int amotion; - - status->str = hom->str / 10; - status->agi = hom->agi / 10; - status->vit = hom->vit / 10; - status->dex = hom->dex / 10; - status->int_ = hom->int_ / 10; - status->luk = hom->luk / 10; - - if (first) { //[orn] - const struct s_homunculus_db *db = hd->homunculusDB; - status->def_ele = db->element; - status->ele_lv = 1; - status->race = db->race; - status->size = (hom->class_ == db->evo_class)?db->evo_size:db->base_size; - status->rhw.range = 1 + status->size; - status->mode = MD_CANMOVE|MD_CANATTACK; - status->speed = DEFAULT_WALK_SPEED; - if (battle_config.hom_setting&0x8 && hd->master) - status->speed = status_get_speed(&hd->master->bl); - - status->hp = 1; - status->sp = 1; - } - skill = hom->level/10 + status->vit/5; - status->def = cap_value(skill, 0, 99); - - skill = hom->level/10 + status->int_/5; - status->mdef = cap_value(skill, 0, 99); - - status->max_hp = hom->max_hp ; - status->max_sp = hom->max_sp ; - - merc_hom_calc_skilltree(hd, 0); - - if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0) - status->def += skill * 4; - - if((skill = merc_hom_checkskill(hd,HVAN_INSTRUCT)) > 0) - { - status->int_ += 1 +skill/2 +skill/4 +skill/5; - status->str += 1 +skill/3 +skill/3 +skill/4; - } - - if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0) - status->max_hp += skill * 2 * status->max_hp / 100; - - if((skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0) - status->max_sp += (1 +skill/2 -skill/4 +skill/5) * status->max_sp / 100 ; - - if (first) { - hd->battle_status.hp = hom->hp ; - hd->battle_status.sp = hom->sp ; - } - - status->rhw.atk = status->dex; - status->rhw.atk2 = status->str + hom->level; - - status->aspd_rate = 1000; - - amotion = (1000 -4*status->agi -status->dex) * hd->homunculusDB->baseASPD/1000; - status->amotion = cap_value(amotion,battle_config.max_aspd,2000); - status->adelay = status->amotion; //It seems adelay = amotion for Homunculus. - - status_calc_misc(&hd->bl, status, hom->level); - -#ifdef RENEWAL - status->matk_max = status->matk_min; -#endif - - status_cpy(&hd->battle_status, status); - return 1; -} - -int status_calc_elemental_(struct elemental_data *ed, bool first) { - struct status_data *status = &ed->base_status; - struct s_elemental *ele = &ed->elemental; - struct map_session_data *sd = ed->master; - - if( !sd ) - return 0; - - if( first ) { - memcpy(status, &ed->db->status, sizeof(struct status_data)); - if( !ele->mode ) - status->mode = EL_MODE_PASSIVE; - else - status->mode = ele->mode; - - status_calc_misc(&ed->bl, status, 0); - - status->max_hp = ele->max_hp; - status->max_sp = ele->max_sp; - status->hp = ele->hp; - status->sp = ele->sp; - status->rhw.atk = ele->atk; - status->rhw.atk2 = ele->atk2; - - status->matk_min += ele->matk; - status->def += ele->def; - status->mdef += ele->mdef; - status->flee = ele->flee; - status->hit = ele->hit; - - memcpy(&ed->battle_status,status,sizeof(struct status_data)); - } else { - status_calc_misc(&ed->bl, status, 0); - status_cpy(&ed->battle_status, status); - } - - return 0; -} - -int status_calc_npc_(struct npc_data *nd, bool first) { - struct status_data *status = &nd->status; - - if (!nd) - return 0; - - if (first) { - status->hp = 1; - status->sp = 1; - status->max_hp = 1; - status->max_sp = 1; - - status->def_ele = ELE_NEUTRAL; - status->ele_lv = 1; - status->race = RC_DEMIHUMAN; - status->size = nd->size; - status->rhw.range = 1 + status->size; - status->mode = MD_CANMOVE|MD_CANATTACK; - status->speed = nd->speed; - } - - status->str = nd->stat_point; - status->agi = nd->stat_point; - status->vit = nd->stat_point; - status->int_= nd->stat_point; - status->dex = nd->stat_point; - status->luk = nd->stat_point; - - status_calc_misc(&nd->bl, status, nd->level); - status_cpy(&nd->status, status); - - return 0; -} - -static unsigned short status_calc_str(struct block_list *,struct status_change *,int); -static unsigned short status_calc_agi(struct block_list *,struct status_change *,int); -static unsigned short status_calc_vit(struct block_list *,struct status_change *,int); -static unsigned short status_calc_int(struct block_list *,struct status_change *,int); -static unsigned short status_calc_dex(struct block_list *,struct status_change *,int); -static unsigned short status_calc_luk(struct block_list *,struct status_change *,int); -static unsigned short status_calc_batk(struct block_list *,struct status_change *,int); -static unsigned short status_calc_watk(struct block_list *,struct status_change *,int); -static unsigned short status_calc_matk(struct block_list *,struct status_change *,int); -static signed short status_calc_hit(struct block_list *,struct status_change *,int); -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 defType status_calc_def(struct block_list *bl, struct status_change *sc, int); -static signed short status_calc_def2(struct block_list *,struct status_change *,int); -static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int); -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); -static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion); -#ifdef RENEWAL_ASPD -static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag); -#endif -static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int); -static unsigned int status_calc_maxhp(struct block_list *,struct status_change *, uint64); -static unsigned int status_calc_maxsp(struct block_list *,struct status_change *,unsigned int); -static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element); -static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv); -static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode); -#ifdef RENEWAL -static unsigned short status_calc_ematk(struct block_list *,struct status_change *,int); -#endif - -//Calculates base regen values. -void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen) -{ - struct map_session_data *sd; - int val, skill, reg_flag; - - if( !(bl->type&BL_REGEN) || !regen ) - return; - - sd = BL_CAST(BL_PC,bl); - val = 1 + (status->vit/5) + (status->max_hp/200); - - if( sd && sd->hprecov_rate != 100 ) - val = val*sd->hprecov_rate/100; - - reg_flag = bl->type == BL_PC ? 0 : 1; - - regen->hp = cap_value(val, reg_flag, SHRT_MAX); - - val = 1 + (status->int_/6) + (status->max_sp/100); - if( status->int_ >= 120 ) - val += ((status->int_-120)>>1) + 4; - - if( sd && sd->sprecov_rate != 100 ) - val = val*sd->sprecov_rate/100; - - regen->sp = cap_value(val, reg_flag, SHRT_MAX); - - if( sd ) - { - struct regen_data_sub *sregen; - if( (skill=pc_checkskill(sd,HP_MEDITATIO)) > 0 ) - { - val = regen->sp*(100+3*skill)/100; - regen->sp = cap_value(val, 1, SHRT_MAX); - } - //Only players have skill/sitting skill regen for now. - sregen = regen->sregen; - - val = 0; - if( (skill=pc_checkskill(sd,SM_RECOVERY)) > 0 ) - val += skill*5 + skill*status->max_hp/500; - sregen->hp = cap_value(val, 0, SHRT_MAX); - - val = 0; - if( (skill=pc_checkskill(sd,MG_SRECOVERY)) > 0 ) - val += skill*3 + skill*status->max_sp/500; - if( (skill=pc_checkskill(sd,NJ_NINPOU)) > 0 ) - val += skill*3 + skill*status->max_sp/500; - if( (skill=pc_checkskill(sd,WM_LESSON)) > 0 ) - val += 3 + 3 * skill; - - sregen->sp = cap_value(val, 0, SHRT_MAX); - - // Skill-related recovery (only when sit) - sregen = regen->ssregen; - - val = 0; - if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 ) - val += skill*4 + skill*status->max_hp/500; - - if( (skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest ) - val += skill*30 + skill*status->max_hp/500; - sregen->hp = cap_value(val, 0, SHRT_MAX); - - val = 0; - if( (skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest ) - { - val += skill*3 + skill*status->max_sp/500; - if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest - val += (30+10*skill)*val/100; - } - if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 ) - val += skill*2 + skill*status->max_sp/500; - sregen->sp = cap_value(val, 0, SHRT_MAX); - } - - if( bl->type == BL_HOM ) { - struct homun_data *hd = (TBL_HOM*)bl; - if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 ) { - val = regen->hp*(100+5*skill)/100; - regen->hp = cap_value(val, 1, SHRT_MAX); - } - if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 ) { - val = regen->sp*(100+3*skill)/100; - regen->sp = cap_value(val, 1, SHRT_MAX); - } - } else if( bl->type == BL_MER ) { - val = (status->max_hp * status->vit / 10000 + 1) * 6; - regen->hp = cap_value(val, 1, SHRT_MAX); - - val = (status->max_sp * (status->int_ + 10) / 750) + 1; - regen->sp = cap_value(val, 1, SHRT_MAX); - } else if( bl->type == BL_ELEM ) { - val = (status->max_hp * status->vit / 10000 + 1) * 6; - regen->hp = cap_value(val, 1, SHRT_MAX); - - val = (status->max_sp * (status->int_ + 10) / 750) + 1; - regen->sp = cap_value(val, 1, SHRT_MAX); - } -} - -//Calculates SC related regen rates. -void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc) -{ - if (!(bl->type&BL_REGEN) || !regen) - return; - - regen->flag = RGN_HP|RGN_SP; - if(regen->sregen) - { - if (regen->sregen->hp) - regen->flag|=RGN_SHP; - - if (regen->sregen->sp) - regen->flag|=RGN_SSP; - regen->sregen->rate.hp = regen->sregen->rate.sp = 1; - } - if (regen->ssregen) - { - if (regen->ssregen->hp) - regen->flag|=RGN_SHP; - - if (regen->ssregen->sp) - regen->flag|=RGN_SSP; - regen->ssregen->rate.hp = regen->ssregen->rate.sp = 1; - } - regen->rate.hp = regen->rate.sp = 1; - - if (!sc || !sc->count) - return; - - if ( - (sc->data[SC_POISON] && !sc->data[SC_SLOWPOISON]) - || (sc->data[SC_DPOISON] && !sc->data[SC_SLOWPOISON]) - || sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST] - || sc->data[SC_TRICKDEAD] - || sc->data[SC_BLEEDING] - || sc->data[SC_MAGICMUSHROOM] - || sc->data[SC_RAISINGDRAGON] - || sc->data[SC_SATURDAYNIGHTFEVER] - ) //No regen - regen->flag = 0; - - if ( - sc->data[SC_DANCING] || sc->data[SC_OBLIVIONCURSE] || sc->data[SC_MAXIMIZEPOWER] - || ( - (bl->type == BL_PC && ((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK && - (sc->data[SC_EXTREMITYFIST] || (sc->data[SC_EXPLOSIONSPIRITS] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK))) - ) - ) //No natural SP regen - regen->flag &=~RGN_SP; - - if( - sc->data[SC_TENSIONRELAX] - ) { - regen->rate.hp += 2; - if (regen->sregen) - regen->sregen->rate.hp += 3; - } - if (sc->data[SC_MAGNIFICAT]) - { - regen->rate.hp += 1; - regen->rate.sp += 1; - } - if (sc->data[SC_REGENERATION]) - { - const struct status_change_entry *sce = sc->data[SC_REGENERATION]; - if (!sce->val4) - { - regen->rate.hp += sce->val2; - regen->rate.sp += sce->val3; - } else - regen->flag&=~sce->val4; //Remove regen as specified by val4 - } - if(sc->data[SC_GT_REVITALIZE]){ - regen->hp = cap_value(regen->hp*sc->data[SC_GT_REVITALIZE]->val3/100, 1, SHRT_MAX); - regen->state.walk= 1; - } - if ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1) //if insignia lvl 1 - || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1) - || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1) - || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1)) - regen->rate.hp *= 2; - -} -void status_calc_state( struct block_list *bl, struct status_change *sc, enum scs_flag flag, bool start ) { - - /* no sc at all, we can zero without any extra weight over our conciousness */ - if( !sc->count ) { - memset(&sc->cant, 0, sizeof (sc->cant)); - return; - } - - /* can move? */ - if( flag&SCS_NOMOVE ) { - if( !(flag&SCS_NOMOVECOND) ) { - sc->cant.move += ( start ? 1 : -1 ); - } else if( - (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF) // cannot move while gospel is in effect - || (sc->data[SC_BASILICA] && sc->data[SC_BASILICA]->val4 == bl->id) // Basilica caster cannot move - || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF) - || (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB) - || (sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 < 3 - && !(sc->data[SC_CAMOUFLAGE]->val3&1)) - ) { - sc->cant.move += ( start ? 1 : -1 ); - } - } - - /* can't use skills */ - if( flag&SCS_NOCAST ) { - if( !(flag&SCS_NOCASTCOND) ) { - sc->cant.cast += ( start ? 1 : -1 ); - } else if( (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB) ){ - sc->cant.cast += ( start ? 1 : -1 ); - } - } - - /* player-only states */ - if( bl->type == BL_PC ) { - - /* can pick items? */ - if( flag&SCS_NOPICKITEM ) { - if( !(flag&SCS_NOPICKITEMCOND) ) { - sc->cant.pickup += ( start ? 1 : -1 ); - } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) { - sc->cant.pickup += ( start ? 1 : -1 ); - } - } - - /* can drop items? */ - if( flag&SCS_NODROPITEM ) { - if( !(flag&SCS_NODROPITEMCOND) ) { - sc->cant.drop += ( start ? 1 : -1 ); - } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) { - sc->cant.drop += ( start ? 1 : -1 ); - } - } - } - - return; -} -/// Recalculates parts of an object's battle status according to the specified flags. -/// @param flag bitfield of values from enum scb_flag -void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag) -{ - const struct status_data *b_status = status_get_base_status(bl); - struct status_data *status = status_get_status_data(bl); - struct status_change *sc = status_get_sc(bl); - TBL_PC *sd = BL_CAST(BL_PC,bl); - int temp; - - if (!b_status || !status) - return; - - if((!(bl->type&BL_REGEN)) && (!sc || !sc->count)) { //No difference. - status_cpy(status, b_status); - return; - } - - if(flag&SCB_STR) { - status->str = status_calc_str(bl, sc, b_status->str); - flag|=SCB_BATK; - if( bl->type&BL_HOM ) - flag |= SCB_WATK; - } - - if(flag&SCB_AGI) { - status->agi = status_calc_agi(bl, sc, b_status->agi); - flag|=SCB_FLEE -#ifdef RENEWAL - |SCB_DEF2 -#endif - ; - if( bl->type&(BL_PC|BL_HOM) ) - flag |= SCB_ASPD|SCB_DSPD; - } - - if(flag&SCB_VIT) { - status->vit = status_calc_vit(bl, sc, b_status->vit); - flag|=SCB_DEF2|SCB_MDEF2; - if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) ) - flag |= SCB_MAXHP; - if( bl->type&BL_HOM ) - flag |= SCB_DEF; - } - - if(flag&SCB_INT) { - status->int_ = status_calc_int(bl, sc, b_status->int_); - flag|=SCB_MATK|SCB_MDEF2; - if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) ) - flag |= SCB_MAXSP; - if( bl->type&BL_HOM ) - flag |= SCB_MDEF; - } - - if(flag&SCB_DEX) { - status->dex = status_calc_dex(bl, sc, b_status->dex); - flag|=SCB_BATK|SCB_HIT -#ifdef RENEWAL - |SCB_MATK|SCB_MDEF2 -#endif - ; - if( bl->type&(BL_PC|BL_HOM) ) - flag |= SCB_ASPD; - if( bl->type&BL_HOM ) - flag |= SCB_WATK; - } - - if(flag&SCB_LUK) { - status->luk = status_calc_luk(bl, sc, b_status->luk); - flag|=SCB_BATK|SCB_CRI|SCB_FLEE2 -#ifdef RENEWAL - |SCB_MATK|SCB_HIT|SCB_FLEE -#endif - ; - } - - if(flag&SCB_BATK && b_status->batk) { - status->batk = status_base_atk(bl,status); - temp = b_status->batk - status_base_atk(bl,b_status); - if (temp) - { - temp += status->batk; - status->batk = cap_value(temp, 0, USHRT_MAX); - } - status->batk = status_calc_batk(bl, sc, status->batk); - } - - if(flag&SCB_WATK) { - - status->rhw.atk = status_calc_watk(bl, sc, b_status->rhw.atk); - if (!sd) //Should not affect weapon refine bonus - status->rhw.atk2 = status_calc_watk(bl, sc, b_status->rhw.atk2); - - if(b_status->lhw.atk) { - if (sd) { - sd->state.lr_flag = 1; - status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk); - sd->state.lr_flag = 0; - } else { - status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk); - status->lhw.atk2= status_calc_watk(bl, sc, b_status->lhw.atk2); - } - } - - if( bl->type&BL_HOM ) - { - status->rhw.atk += (status->dex - b_status->dex); - status->rhw.atk2 += (status->str - b_status->str); - if( status->rhw.atk2 < status->rhw.atk ) - status->rhw.atk2 = status->rhw.atk; - } - } - - if(flag&SCB_HIT) { - if (status->dex == b_status->dex -#ifdef RENEWAL - && status->luk == b_status->luk -#endif - ) - status->hit = status_calc_hit(bl, sc, b_status->hit); - else - status->hit = status_calc_hit(bl, sc, b_status->hit + (status->dex - b_status->dex) -#ifdef RENEWAL - + (status->luk/3 - b_status->luk/3) -#endif - ); - } - - if(flag&SCB_FLEE) { - if (status->agi == b_status->agi -#ifdef RENEWAL - && status->luk == b_status->luk -#endif - ) - status->flee = status_calc_flee(bl, sc, b_status->flee); - else - status->flee = status_calc_flee(bl, sc, b_status->flee +(status->agi - b_status->agi) -#ifdef RENEWAL - + (status->luk/5 - b_status->luk/5) -#endif - ); - } - - if(flag&SCB_DEF) - { - status->def = status_calc_def(bl, sc, b_status->def); - - if( bl->type&BL_HOM ) - status->def += (status->vit/5 - b_status->vit/5); - } - - if(flag&SCB_DEF2) { - if (status->vit == b_status->vit -#ifdef RENEWAL - && status->agi == b_status->agi -#endif - ) - status->def2 = status_calc_def2(bl, sc, b_status->def2); - else - status->def2 = status_calc_def2(bl, sc, b_status->def2 -#ifdef RENEWAL - + (int)( ((float)status->vit/2 + (float)b_status->vit/2) + ((float)status->agi/5 + (float)b_status->agi/5) ) -#else - + (status->vit - b_status->vit) -#endif - ); - } - - if(flag&SCB_MDEF) - { - status->mdef = status_calc_mdef(bl, sc, b_status->mdef); - - if( bl->type&BL_HOM ) - status->mdef += (status->int_/5 - b_status->int_/5); - } - - if(flag&SCB_MDEF2) { - if (status->int_ == b_status->int_ && status->vit == b_status->vit -#ifdef RENEWAL - && status->dex == b_status->dex -#endif - ) - status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2); - else - status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2 +(status->int_ - b_status->int_) -#ifdef RENEWAL - + (int)( ((float)status->dex/5 - (float)b_status->dex/5) + ((float)status->vit/5 + (float)b_status->vit/5) ) -#else - + ((status->vit - b_status->vit)>>1) -#endif - ); - } - - if(flag&SCB_SPEED) { - struct unit_data *ud = unit_bl2ud(bl); - status->speed = status_calc_speed(bl, sc, b_status->speed); - - //Re-walk to adjust speed (we do not check if walktimer != INVALID_TIMER - //because if you step on something while walking, the moment this - //piece of code triggers the walk-timer is set on INVALID_TIMER) [Skotlex] - if (ud) - ud->state.change_walk_target = ud->state.speed_changed = 1; - - if( bl->type&BL_PC && status->speed < battle_config.max_walk_speed ) - status->speed = battle_config.max_walk_speed; - - if( bl->type&BL_HOM && battle_config.hom_setting&0x8 && ((TBL_HOM*)bl)->master) - status->speed = status_get_speed(&((TBL_HOM*)bl)->master->bl); - - - } - - if(flag&SCB_CRI && b_status->cri) { - if (status->luk == b_status->luk) - status->cri = status_calc_critical(bl, sc, b_status->cri); - else - status->cri = status_calc_critical(bl, sc, b_status->cri + 3*(status->luk - b_status->luk)); - /** - * after status_calc_critical so the bonus is applied despite if you have or not a sc bugreport:5240 - **/ - if( bl->type == BL_PC && ((TBL_PC*)bl)->status.weapon == W_KATAR ) - status->cri <<= 1; - - } - - if(flag&SCB_FLEE2 && b_status->flee2) { - if (status->luk == b_status->luk) - status->flee2 = status_calc_flee2(bl, sc, b_status->flee2); - else - status->flee2 = status_calc_flee2(bl, sc, b_status->flee2 +(status->luk - b_status->luk)); - } - - if(flag&SCB_ATK_ELE) { - status->rhw.ele = status_calc_attack_element(bl, sc, b_status->rhw.ele); - if (sd) sd->state.lr_flag = 1; - status->lhw.ele = status_calc_attack_element(bl, sc, b_status->lhw.ele); - if (sd) sd->state.lr_flag = 0; - } - - if(flag&SCB_DEF_ELE) { - status->def_ele = status_calc_element(bl, sc, b_status->def_ele); - status->ele_lv = status_calc_element_lv(bl, sc, b_status->ele_lv); - } - - if(flag&SCB_MODE) - { - status->mode = status_calc_mode(bl, sc, b_status->mode); - //Since mode changed, reset their state. - if (!(status->mode&MD_CANATTACK)) - unit_stop_attack(bl); - if (!(status->mode&MD_CANMOVE)) - unit_stop_walking(bl,1); - } - -// No status changes alter these yet. -// if(flag&SCB_SIZE) -// if(flag&SCB_RACE) -// if(flag&SCB_RANGE) - - if(flag&SCB_MAXHP) { - if( bl->type&BL_PC ) - { - status->max_hp = status_base_pc_maxhp(sd,status); - status->max_hp += b_status->max_hp - sd->status.max_hp; - - status->max_hp = status_calc_maxhp(bl, sc, status->max_hp); - - if( status->max_hp > (unsigned int)battle_config.max_hp ) - status->max_hp = (unsigned int)battle_config.max_hp; - } - else - { - status->max_hp = status_calc_maxhp(bl, sc, b_status->max_hp); - } - - if( status->hp > status->max_hp ) //FIXME: Should perhaps a status_zap should be issued? - { - status->hp = status->max_hp; - if( sd ) clif_updatestatus(sd,SP_HP); - } - } - - if(flag&SCB_MAXSP) { - if( bl->type&BL_PC ) - { - status->max_sp = status_base_pc_maxsp(sd,status); - status->max_sp += b_status->max_sp - sd->status.max_sp; - - status->max_sp = status_calc_maxsp(&sd->bl, &sd->sc, status->max_sp); - - if( status->max_sp > (unsigned int)battle_config.max_sp ) - status->max_sp = (unsigned int)battle_config.max_sp; - } - else - { - status->max_sp = status_calc_maxsp(bl, sc, b_status->max_sp); - } - - if( status->sp > status->max_sp ) - { - status->sp = status->max_sp; - if( sd ) clif_updatestatus(sd,SP_SP); - } - } - - if(flag&SCB_MATK) { -#ifndef RENEWAL - status->matk_min = status_base_matk_min(status) + (sd?sd->bonus.ematk:0); - status->matk_max = status_base_matk_max(status) + (sd?sd->bonus.ematk:0); -#else - /** - * RE MATK Formula (from irowiki:http://irowiki.org/wiki/MATK) - * MATK = (sMATK + wMATK + eMATK) * Multiplicative Modifiers - **/ - status->matk_min = status->matk_max = status_base_matk(status, status_get_lv(bl)); - if( bl->type&BL_PC ){ - // Any +MATK you get from skills and cards, including cards in weapon, is added here. - if( sd->bonus.ematk > 0 ){ - status->matk_max += sd->bonus.ematk; - status->matk_min += sd->bonus.ematk; - } - status->matk_min = status_calc_ematk(bl, sc, status->matk_min); - status->matk_max = status_calc_ematk(bl, sc, status->matk_max); - //This is the only portion in MATK that varies depending on the weapon level and refinement rate. - if( status->rhw.matk > 0 ){ - int wMatk = status->rhw.matk; - int variance = wMatk * status->rhw.wlv / 10; - status->matk_min += wMatk - variance; - status->matk_max += wMatk + variance; - } - } -#endif - if (bl->type&BL_PC && sd->matk_rate != 100) { - status->matk_max = status->matk_max * sd->matk_rate/100; - 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 ((bl->type&BL_HOM && battle_config.hom_setting&0x20) //Hom Min Matk is always the same as Max Matk - || sc->data[SC_RECOGNIZEDSPELL]) - status->matk_min = status->matk_max; - -#ifdef RENEWAL - if( sd && sd->right_weapon.overrefine > 0){ - status->matk_min++; - status->matk_max += sd->right_weapon.overrefine - 1; - } -#endif - - } - - if(flag&SCB_ASPD) { - int amotion; - if( bl->type&BL_PC ) - { - amotion = status_base_amotion_pc(sd,status); -#ifndef RENEWAL_ASPD - status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate); - - if(status->aspd_rate != 1000) - amotion = amotion*status->aspd_rate/1000; -#else - // aspd = baseaspd + floor(sqrt((agi^2/2) + (dex^2/5))/4 + (potskillbonus*agi/200)) - amotion -= (int)(sqrt( (pow(status->agi, 2) / 2) + (pow(status->dex, 2) / 5) ) / 4 + (status_calc_aspd(bl, sc, 1) * status->agi / 200)) * 10; - - if( (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) != 0 ) // RE ASPD percertage modifier - amotion -= ( amotion - ((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd) ) - * (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) / 100; - - if(status->aspd_rate != 1000) // absolute percentage modifier - amotion = ( 200 - (200-amotion/10) * status->aspd_rate / 1000 ) * 10; -#endif - amotion = status_calc_fix_aspd(bl, sc, amotion); - status->amotion = cap_value(amotion,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000); - - status->adelay = 2*status->amotion; - } - else - if( bl->type&BL_HOM ) - { - amotion = (1000 -4*status->agi -status->dex) * ((TBL_HOM*)bl)->homunculusDB->baseASPD/1000; - status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate); - - if(status->aspd_rate != 1000) - amotion = amotion*status->aspd_rate/1000; - - amotion = status_calc_fix_aspd(bl, sc, amotion); - status->amotion = cap_value(amotion,battle_config.max_aspd,2000); - - status->adelay = status->amotion; - } - else // mercenary and mobs - { - amotion = b_status->amotion; - status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate); - - if(status->aspd_rate != 1000) - amotion = amotion*status->aspd_rate/1000; - - amotion = status_calc_fix_aspd(bl, sc, amotion); - status->amotion = cap_value(amotion, battle_config.monster_max_aspd, 2000); - - temp = b_status->adelay*status->aspd_rate/1000; - status->adelay = cap_value(temp, battle_config.monster_max_aspd*2, 4000); - } - } - - if(flag&SCB_DSPD) { - int dmotion; - if( bl->type&BL_PC ) - { - if (b_status->agi == status->agi) - status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion); - else { - dmotion = 800-status->agi*4; - status->dmotion = cap_value(dmotion, 400, 800); - if(battle_config.pc_damage_delay_rate != 100) - status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100; - //It's safe to ignore b_status->dmotion since no bonus affects it. - status->dmotion = status_calc_dmotion(bl, sc, status->dmotion); - } - } - else - if( bl->type&BL_HOM ) - { - dmotion = 800-status->agi*4; - status->dmotion = cap_value(dmotion, 400, 800); - status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion); - } - else // mercenary and mobs - { - status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion); - } - } - - 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 -/// @param first if true, will cause status_calc_* functions to run their base status initialization code -void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first) -{ - struct status_data b_status; // previous battle status - struct status_data* status; // pointer to current battle status - - // remember previous values - status = status_get_status_data(bl); - memcpy(&b_status, status, sizeof(struct status_data)); - - if( flag&SCB_BASE ) {// calculate the object's base status too - switch( bl->type ) { - case BL_PC: status_calc_pc_(BL_CAST(BL_PC,bl), first); break; - case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), first); break; - case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), first); break; - case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl), first); break; - case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), first); break; - case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), first); break; - case BL_NPC: status_calc_npc_(BL_CAST(BL_NPC,bl), first); break; - } - } - - if( bl->type == BL_PET ) - return; // pets are not affected by statuses - - if( first && bl->type == BL_MOB ) - return; // assume there will be no statuses active - - status_calc_bl_main(bl, flag); - - if( first && bl->type == BL_HOM ) - return; // client update handled by caller - - // compare against new values and send client updates - if( bl->type == BL_PC ) - { - TBL_PC* sd = BL_CAST(BL_PC, bl); - if(b_status.str != status->str) - clif_updatestatus(sd,SP_STR); - if(b_status.agi != status->agi) - clif_updatestatus(sd,SP_AGI); - if(b_status.vit != status->vit) - clif_updatestatus(sd,SP_VIT); - if(b_status.int_ != status->int_) - clif_updatestatus(sd,SP_INT); - if(b_status.dex != status->dex) - clif_updatestatus(sd,SP_DEX); - if(b_status.luk != status->luk) - clif_updatestatus(sd,SP_LUK); - if(b_status.hit != status->hit) - clif_updatestatus(sd,SP_HIT); - if(b_status.flee != status->flee) - clif_updatestatus(sd,SP_FLEE1); - if(b_status.amotion != status->amotion) - clif_updatestatus(sd,SP_ASPD); - if(b_status.speed != status->speed) - clif_updatestatus(sd,SP_SPEED); - - if(b_status.batk != status->batk -#ifndef RENEWAL - || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk -#endif - ) - clif_updatestatus(sd,SP_ATK1); - - if(b_status.def != status->def){ - clif_updatestatus(sd,SP_DEF1); -#ifdef RENEWAL - clif_updatestatus(sd,SP_DEF2); -#endif - } - - if(b_status.rhw.atk2 != status->rhw.atk2 || b_status.lhw.atk2 != status->lhw.atk2 -#ifdef RENEWAL - || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk -#endif - ) - clif_updatestatus(sd,SP_ATK2); - - if(b_status.def2 != status->def2){ - clif_updatestatus(sd,SP_DEF2); -#ifdef RENEWAL - clif_updatestatus(sd,SP_DEF1); -#endif - } - if(b_status.flee2 != status->flee2) - clif_updatestatus(sd,SP_FLEE2); - if(b_status.cri != status->cri) - clif_updatestatus(sd,SP_CRITICAL); -#ifndef RENEWAL - 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); -#else - if(b_status.matk_max != status->matk_max || b_status.matk_min != status->matk_min){ - clif_updatestatus(sd,SP_MATK2); - clif_updatestatus(sd,SP_MATK1); - } -#endif - if(b_status.mdef != status->mdef){ - clif_updatestatus(sd,SP_MDEF1); -#ifdef RENEWAL - clif_updatestatus(sd,SP_MDEF2); -#endif - } - if(b_status.mdef2 != status->mdef2){ - clif_updatestatus(sd,SP_MDEF2); -#ifdef RENEWAL - clif_updatestatus(sd,SP_MDEF1); -#endif - } - if(b_status.rhw.range != status->rhw.range) - clif_updatestatus(sd,SP_ATTACKRANGE); - if(b_status.max_hp != status->max_hp) - clif_updatestatus(sd,SP_MAXHP); - if(b_status.max_sp != status->max_sp) - clif_updatestatus(sd,SP_MAXSP); - if(b_status.hp != status->hp) - clif_updatestatus(sd,SP_HP); - if(b_status.sp != status->sp) - clif_updatestatus(sd,SP_SP); - } else if( bl->type == BL_HOM ) { - TBL_HOM* hd = BL_CAST(BL_HOM, bl); - if( hd->master && memcmp(&b_status, status, sizeof(struct status_data)) != 0 ) - clif_hominfo(hd->master,hd,0); - } else if( bl->type == BL_MER ) { - TBL_MER* md = BL_CAST(BL_MER, bl); - if( b_status.rhw.atk != status->rhw.atk || b_status.rhw.atk2 != status->rhw.atk2 ) - clif_mercenary_updatestatus(md->master, SP_ATK1); - if( b_status.matk_max != status->matk_max ) - clif_mercenary_updatestatus(md->master, SP_MATK1); - if( b_status.hit != status->hit ) - clif_mercenary_updatestatus(md->master, SP_HIT); - if( b_status.cri != status->cri ) - clif_mercenary_updatestatus(md->master, SP_CRITICAL); - if( b_status.def != status->def ) - clif_mercenary_updatestatus(md->master, SP_DEF1); - if( b_status.mdef != status->mdef ) - clif_mercenary_updatestatus(md->master, SP_MDEF1); - if( b_status.flee != status->flee ) - clif_mercenary_updatestatus(md->master, SP_MERCFLEE); - if( b_status.amotion != status->amotion ) - clif_mercenary_updatestatus(md->master, SP_ASPD); - if( b_status.max_hp != status->max_hp ) - clif_mercenary_updatestatus(md->master, SP_MAXHP); - if( b_status.max_sp != status->max_sp ) - clif_mercenary_updatestatus(md->master, SP_MAXSP); - if( b_status.hp != status->hp ) - clif_mercenary_updatestatus(md->master, SP_HP); - if( b_status.sp != status->sp ) - clif_mercenary_updatestatus(md->master, SP_SP); - } else if( bl->type == BL_ELEM ) { - TBL_ELEM* ed = BL_CAST(BL_ELEM, bl); - if( b_status.max_hp != status->max_hp ) - clif_elemental_updatestatus(ed->master, SP_MAXHP); - if( b_status.max_sp != status->max_sp ) - clif_elemental_updatestatus(ed->master, SP_MAXSP); - if( b_status.hp != status->hp ) - clif_elemental_updatestatus(ed->master, SP_HP); - if( b_status.sp != status->sp ) - clif_mercenary_updatestatus(ed->master, SP_SP); - } -} - -/*========================================== - * Apply shared stat mods from status changes [DracoRPG] - *------------------------------------------*/ -static unsigned short status_calc_str(struct block_list *bl, struct status_change *sc, int str) -{ - if(!sc || !sc->count) - return cap_value(str,0,USHRT_MAX); - - if(sc->data[SC_HARMONIZE]) { - str -= sc->data[SC_HARMONIZE]->val2; - return (unsigned short)cap_value(str,0,USHRT_MAX); - } - if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && str < 50) - return 50; - if(sc->data[SC_INCALLSTATUS]) - str += sc->data[SC_INCALLSTATUS]->val1; - if(sc->data[SC_INCSTR]) - str += sc->data[SC_INCSTR]->val1; - if(sc->data[SC_STRFOOD]) - str += sc->data[SC_STRFOOD]->val1; - if(sc->data[SC_FOOD_STR_CASH]) - str += sc->data[SC_FOOD_STR_CASH]->val1; - if(sc->data[SC_BATTLEORDERS]) - str += 5; - if(sc->data[SC_LEADERSHIP]) - str += sc->data[SC_LEADERSHIP]->val1; - if(sc->data[SC_LOUD]) - str += 4; - if(sc->data[SC_TRUESIGHT]) - str += 5; - if(sc->data[SC_SPURT]) - str += 10; - if(sc->data[SC_NEN]) - str += sc->data[SC_NEN]->val1; - if(sc->data[SC_BLESSING]){ - if(sc->data[SC_BLESSING]->val2) - str += sc->data[SC_BLESSING]->val2; - else - str >>= 1; - } - if(sc->data[SC_MARIONETTE]) - str -= ((sc->data[SC_MARIONETTE]->val3)>>16)&0xFF; - if(sc->data[SC_MARIONETTE2]) - str += ((sc->data[SC_MARIONETTE2]->val3)>>16)&0xFF; - if(sc->data[SC_GIANTGROWTH]) - str += 30; - if(sc->data[SC_SAVAGE_STEAK]) - str += sc->data[SC_SAVAGE_STEAK]->val1; - if(sc->data[SC_INSPIRATION]) - str += sc->data[SC_INSPIRATION]->val3; - if(sc->data[SC_STOMACHACHE]) - str -= sc->data[SC_STOMACHACHE]->val1; - if(sc->data[SC_KYOUGAKU]) - str -= sc->data[SC_KYOUGAKU]->val2; - - return (unsigned short)cap_value(str,0,USHRT_MAX); -} - -static unsigned short status_calc_agi(struct block_list *bl, struct status_change *sc, int agi) -{ - if(!sc || !sc->count) - return cap_value(agi,0,USHRT_MAX); - - if(sc->data[SC_HARMONIZE]) { - agi -= sc->data[SC_HARMONIZE]->val2; - return (unsigned short)cap_value(agi,0,USHRT_MAX); - } - if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && agi < 50) - return 50; - if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE]) - agi += (agi-sc->data[SC_CONCENTRATE]->val3)*sc->data[SC_CONCENTRATE]->val2/100; - if(sc->data[SC_INCALLSTATUS]) - agi += sc->data[SC_INCALLSTATUS]->val1; - if(sc->data[SC_INCAGI]) - agi += sc->data[SC_INCAGI]->val1; - if(sc->data[SC_AGIFOOD]) - agi += sc->data[SC_AGIFOOD]->val1; - if(sc->data[SC_FOOD_AGI_CASH]) - agi += sc->data[SC_FOOD_AGI_CASH]->val1; - if(sc->data[SC_SOULCOLD]) - agi += sc->data[SC_SOULCOLD]->val1; - if(sc->data[SC_TRUESIGHT]) - agi += 5; - if(sc->data[SC_INCREASEAGI]) - agi += sc->data[SC_INCREASEAGI]->val2; - if(sc->data[SC_INCREASING]) - agi += 4; // added based on skill updates [Reddozen] - if(sc->data[SC_DECREASEAGI]) - agi -= sc->data[SC_DECREASEAGI]->val2; - if(sc->data[SC_QUAGMIRE]) - agi -= sc->data[SC_QUAGMIRE]->val2; - if(sc->data[SC_SUITON] && sc->data[SC_SUITON]->val3) - agi -= sc->data[SC_SUITON]->val2; - if(sc->data[SC_MARIONETTE]) - agi -= ((sc->data[SC_MARIONETTE]->val3)>>8)&0xFF; - if(sc->data[SC_MARIONETTE2]) - agi += ((sc->data[SC_MARIONETTE2]->val3)>>8)&0xFF; - if(sc->data[SC_ADORAMUS]) - agi -= sc->data[SC_ADORAMUS]->val2; - if(sc->data[SC_DROCERA_HERB_STEAMED]) - agi += sc->data[SC_DROCERA_HERB_STEAMED]->val1; - if(sc->data[SC_INSPIRATION]) - agi += sc->data[SC_INSPIRATION]->val3; - if(sc->data[SC_STOMACHACHE]) - agi -= sc->data[SC_STOMACHACHE]->val1; - if(sc->data[SC_KYOUGAKU]) - agi -= sc->data[SC_KYOUGAKU]->val2; - - return (unsigned short)cap_value(agi,0,USHRT_MAX); -} - -static unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc, int vit) -{ - if(!sc || !sc->count) - return cap_value(vit,0,USHRT_MAX); - - if(sc->data[SC_HARMONIZE]) { - vit -= sc->data[SC_HARMONIZE]->val2; - return (unsigned short)cap_value(vit,0,USHRT_MAX); - } - if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && vit < 50) - return 50; - if(sc->data[SC_INCALLSTATUS]) - vit += sc->data[SC_INCALLSTATUS]->val1; - if(sc->data[SC_INCVIT]) - vit += sc->data[SC_INCVIT]->val1; - if(sc->data[SC_VITFOOD]) - vit += sc->data[SC_VITFOOD]->val1; - if(sc->data[SC_FOOD_VIT_CASH]) - vit += sc->data[SC_FOOD_VIT_CASH]->val1; - if(sc->data[SC_CHANGE]) - vit += sc->data[SC_CHANGE]->val2; - if(sc->data[SC_GLORYWOUNDS]) - vit += sc->data[SC_GLORYWOUNDS]->val1; - if(sc->data[SC_TRUESIGHT]) - vit += 5; - if(sc->data[SC_MARIONETTE]) - vit -= sc->data[SC_MARIONETTE]->val3&0xFF; - if(sc->data[SC_MARIONETTE2]) - vit += sc->data[SC_MARIONETTE2]->val3&0xFF; - if(sc->data[SC_LAUDAAGNUS]) - vit += 4 + sc->data[SC_LAUDAAGNUS]->val1; - if(sc->data[SC_MINOR_BBQ]) - vit += sc->data[SC_MINOR_BBQ]->val1; - if(sc->data[SC_INSPIRATION]) - vit += sc->data[SC_INSPIRATION]->val3; - if(sc->data[SC_STOMACHACHE]) - vit -= sc->data[SC_STOMACHACHE]->val1; - if(sc->data[SC_KYOUGAKU]) - vit -= sc->data[SC_KYOUGAKU]->val2; - - if(sc->data[SC_STRIPARMOR]) - vit -= vit * sc->data[SC_STRIPARMOR]->val2/100; - - return (unsigned short)cap_value(vit,0,USHRT_MAX); -} - -static unsigned short status_calc_int(struct block_list *bl, struct status_change *sc, int int_) -{ - if(!sc || !sc->count) - return cap_value(int_,0,USHRT_MAX); - - if(sc->data[SC_HARMONIZE]) { - int_ -= sc->data[SC_HARMONIZE]->val2; - return (unsigned short)cap_value(int_,0,USHRT_MAX); - } - if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && int_ < 50) - return 50; - if(sc->data[SC_INCALLSTATUS]) - int_ += sc->data[SC_INCALLSTATUS]->val1; - if(sc->data[SC_INCINT]) - int_ += sc->data[SC_INCINT]->val1; - if(sc->data[SC_INTFOOD]) - int_ += sc->data[SC_INTFOOD]->val1; - if(sc->data[SC_FOOD_INT_CASH]) - int_ += sc->data[SC_FOOD_INT_CASH]->val1; - if(sc->data[SC_CHANGE]) - int_ += sc->data[SC_CHANGE]->val3; - if(sc->data[SC_BATTLEORDERS]) - int_ += 5; - if(sc->data[SC_TRUESIGHT]) - int_ += 5; - if(sc->data[SC_BLESSING]){ - if (sc->data[SC_BLESSING]->val2) - int_ += sc->data[SC_BLESSING]->val2; - else - int_ >>= 1; - } - if(sc->data[SC_NEN]) - int_ += sc->data[SC_NEN]->val1; - if(sc->data[SC_MARIONETTE]) - int_ -= ((sc->data[SC_MARIONETTE]->val4)>>16)&0xFF; - if(sc->data[SC_MARIONETTE2]) - int_ += ((sc->data[SC_MARIONETTE2]->val4)>>16)&0xFF; - if(sc->data[SC_MANDRAGORA]) - int_ -= 5 + 5 * sc->data[SC_MANDRAGORA]->val1; - if(sc->data[SC_COCKTAIL_WARG_BLOOD]) - int_ += sc->data[SC_COCKTAIL_WARG_BLOOD]->val1; - if(sc->data[SC_INSPIRATION]) - int_ += sc->data[SC_INSPIRATION]->val3; - if(sc->data[SC_STOMACHACHE]) - int_ -= sc->data[SC_STOMACHACHE]->val1; - if(sc->data[SC_KYOUGAKU]) - int_ -= sc->data[SC_KYOUGAKU]->val2; - - if(sc->data[SC_STRIPHELM]) - int_ -= int_ * sc->data[SC_STRIPHELM]->val2/100; - if(sc->data[SC__STRIPACCESSORY]) - int_ -= int_ * sc->data[SC__STRIPACCESSORY]->val2 / 100; - - return (unsigned short)cap_value(int_,0,USHRT_MAX); -} - -static unsigned short status_calc_dex(struct block_list *bl, struct status_change *sc, int dex) -{ - if(!sc || !sc->count) - return cap_value(dex,0,USHRT_MAX); - - if(sc->data[SC_HARMONIZE]) { - dex -= sc->data[SC_HARMONIZE]->val2; - return (unsigned short)cap_value(dex,0,USHRT_MAX); - } - if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && dex < 50) - return 50; - if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE]) - dex += (dex-sc->data[SC_CONCENTRATE]->val4)*sc->data[SC_CONCENTRATE]->val2/100; - if(sc->data[SC_INCALLSTATUS]) - dex += sc->data[SC_INCALLSTATUS]->val1; - if(sc->data[SC_INCDEX]) - dex += sc->data[SC_INCDEX]->val1; - if(sc->data[SC_DEXFOOD]) - dex += sc->data[SC_DEXFOOD]->val1; - if(sc->data[SC_FOOD_DEX_CASH]) - dex += sc->data[SC_FOOD_DEX_CASH]->val1; - if(sc->data[SC_BATTLEORDERS]) - dex += 5; - if(sc->data[SC_HAWKEYES]) - dex += sc->data[SC_HAWKEYES]->val1; - if(sc->data[SC_TRUESIGHT]) - dex += 5; - if(sc->data[SC_QUAGMIRE]) - dex -= sc->data[SC_QUAGMIRE]->val2; - if(sc->data[SC_BLESSING]){ - if (sc->data[SC_BLESSING]->val2) - dex += sc->data[SC_BLESSING]->val2; - else - dex >>= 1; - } - if(sc->data[SC_INCREASING]) - dex += 4; // added based on skill updates [Reddozen] - if(sc->data[SC_MARIONETTE]) - dex -= ((sc->data[SC_MARIONETTE]->val4)>>8)&0xFF; - if(sc->data[SC_MARIONETTE2]) - dex += ((sc->data[SC_MARIONETTE2]->val4)>>8)&0xFF; - if(sc->data[SC_SIROMA_ICE_TEA]) - dex += sc->data[SC_SIROMA_ICE_TEA]->val1; - if(sc->data[SC_INSPIRATION]) - dex += sc->data[SC_INSPIRATION]->val3; - if(sc->data[SC_STOMACHACHE]) - dex -= sc->data[SC_STOMACHACHE]->val1; - if(sc->data[SC_KYOUGAKU]) - dex -= sc->data[SC_KYOUGAKU]->val2; - - if(sc->data[SC__STRIPACCESSORY]) - dex -= dex * sc->data[SC__STRIPACCESSORY]->val2 / 100; - - return (unsigned short)cap_value(dex,0,USHRT_MAX); -} - -static unsigned short status_calc_luk(struct block_list *bl, struct status_change *sc, int luk) -{ - if(!sc || !sc->count) - return cap_value(luk,0,USHRT_MAX); - - if(sc->data[SC_HARMONIZE]) { - luk -= sc->data[SC_HARMONIZE]->val2; - return (unsigned short)cap_value(luk,0,USHRT_MAX); - } - if(sc->data[SC_CURSE]) - return 0; - if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && luk < 50) - return 50; - if(sc->data[SC_INCALLSTATUS]) - luk += sc->data[SC_INCALLSTATUS]->val1; - if(sc->data[SC_INCLUK]) - luk += sc->data[SC_INCLUK]->val1; - if(sc->data[SC_LUKFOOD]) - luk += sc->data[SC_LUKFOOD]->val1; - if(sc->data[SC_FOOD_LUK_CASH]) - luk += sc->data[SC_FOOD_LUK_CASH]->val1; - if(sc->data[SC_TRUESIGHT]) - luk += 5; - if(sc->data[SC_GLORIA]) - luk += 30; - if(sc->data[SC_MARIONETTE]) - luk -= sc->data[SC_MARIONETTE]->val4&0xFF; - if(sc->data[SC_MARIONETTE2]) - luk += sc->data[SC_MARIONETTE2]->val4&0xFF; - if(sc->data[SC_PUTTI_TAILS_NOODLES]) - luk += sc->data[SC_PUTTI_TAILS_NOODLES]->val1; - if(sc->data[SC_INSPIRATION]) - luk += sc->data[SC_INSPIRATION]->val3; - if(sc->data[SC_STOMACHACHE]) - luk -= sc->data[SC_STOMACHACHE]->val1; - if(sc->data[SC_KYOUGAKU]) - luk -= sc->data[SC_KYOUGAKU]->val2; - if(sc->data[SC_LAUDARAMUS]) - luk += 4 + sc->data[SC_LAUDARAMUS]->val1; - - if(sc->data[SC__STRIPACCESSORY]) - luk -= luk * sc->data[SC__STRIPACCESSORY]->val2 / 100; - if(sc->data[SC_BANANA_BOMB]) - luk -= luk * sc->data[SC_BANANA_BOMB]->val1 / 100; - - return (unsigned short)cap_value(luk,0,USHRT_MAX); -} - -static unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, int batk) -{ - if(!sc || !sc->count) - return cap_value(batk,0,USHRT_MAX); - - if(sc->data[SC_ATKPOTION]) - batk += sc->data[SC_ATKPOTION]->val1; - if(sc->data[SC_BATKFOOD]) - batk += sc->data[SC_BATKFOOD]->val1; - if(sc->data[SC_GATLINGFEVER]) - batk += sc->data[SC_GATLINGFEVER]->val3; - if(sc->data[SC_MADNESSCANCEL]) - batk += 100; - if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2) - batk += 50; - if(bl->type == BL_ELEM - && ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1) - || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1) - || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1) - || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1)) - ) - batk += batk / 5; - if(sc->data[SC_FULL_SWING_K]) - batk += sc->data[SC_FULL_SWING_K]->val1; - if(sc->data[SC_ODINS_POWER]) - batk += 70; - if(sc->data[SC_ASH] && (bl->type==BL_MOB)){ - if(status_get_element(bl) == ELE_WATER) //water type - batk /= 2; - } - if(sc->data[SC_PYROCLASTIC]) - batk += sc->data[SC_PYROCLASTIC]->val2; - if (sc->data[SC_ANGRIFFS_MODUS]) - batk += sc->data[SC_ANGRIFFS_MODUS]->val2; - - if(sc->data[SC_INCATKRATE]) - batk += batk * sc->data[SC_INCATKRATE]->val1/100; - if(sc->data[SC_PROVOKE]) - batk += batk * sc->data[SC_PROVOKE]->val3/100; - if(sc->data[SC_CONCENTRATION]) - batk += batk * sc->data[SC_CONCENTRATION]->val2/100; - if(sc->data[SC_SKE]) - batk += batk * 3; - if(sc->data[SC_BLOODLUST]) - batk += batk * sc->data[SC_BLOODLUST]->val2/100; - if(sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST) - batk -= batk * 25/100; - if(sc->data[SC_CURSE]) - batk -= batk * 25/100; -//Curse shouldn't effect on this? <- Curse OR Bleeding?? -// if(sc->data[SC_BLEEDING]) -// batk -= batk * 25/100; - if(sc->data[SC_FLEET]) - batk += batk * sc->data[SC_FLEET]->val3/100; - if(sc->data[SC__ENERVATION]) - batk -= batk * sc->data[SC__ENERVATION]->val2 / 100; - if(sc->data[SC_RUSHWINDMILL]) - batk += batk * sc->data[SC_RUSHWINDMILL]->val2/100; - if(sc->data[SC_SATURDAYNIGHTFEVER]) - batk += 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1; - if(sc->data[SC_MELODYOFSINK]) - batk -= batk * sc->data[SC_MELODYOFSINK]->val3/100; - if(sc->data[SC_BEYONDOFWARCRY]) - batk += batk * sc->data[SC_BEYONDOFWARCRY]->val3/100; - if( sc->data[SC_ZANGETSU] ) - batk += batk * sc->data[SC_ZANGETSU]->val2 / 100; - - return (unsigned short)cap_value(batk,0,USHRT_MAX); -} - -static unsigned short status_calc_watk(struct block_list *bl, struct status_change *sc, int watk) -{ - if(!sc || !sc->count) - return cap_value(watk,0,USHRT_MAX); - - if(sc->data[SC_IMPOSITIO]) - watk += sc->data[SC_IMPOSITIO]->val2; - if(sc->data[SC_WATKFOOD]) - watk += sc->data[SC_WATKFOOD]->val1; - if(sc->data[SC_DRUMBATTLE]) - watk += sc->data[SC_DRUMBATTLE]->val2; - if(sc->data[SC_VOLCANO]) - watk += sc->data[SC_VOLCANO]->val2; - if(sc->data[SC_MERC_ATKUP]) - watk += sc->data[SC_MERC_ATKUP]->val2; - if(sc->data[SC_FIGHTINGSPIRIT]) - watk += sc->data[SC_FIGHTINGSPIRIT]->val1; - if(sc->data[SC_STRIKING]) - watk += sc->data[SC_STRIKING]->val2; - if(sc->data[SC_SHIELDSPELL_DEF] && sc->data[SC_SHIELDSPELL_DEF]->val1 == 3) - watk += sc->data[SC_SHIELDSPELL_DEF]->val2; - if(sc->data[SC_INSPIRATION]) - watk += sc->data[SC_INSPIRATION]->val2; - if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 ) - watk += (10 + 10 * sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2); - if( sc->data[SC_TROPIC_OPTION] ) - watk += sc->data[SC_TROPIC_OPTION]->val2; - if( sc->data[SC_HEATER_OPTION] ) - watk += sc->data[SC_HEATER_OPTION]->val2; - if( sc->data[SC_WATER_BARRIER] ) - watk -= sc->data[SC_WATER_BARRIER]->val3; - if( sc->data[SC_PYROTECHNIC_OPTION] ) - watk += sc->data[SC_PYROTECHNIC_OPTION]->val2; - if(sc->data[SC_NIBELUNGEN]) { - if (bl->type != BL_PC) - watk += sc->data[SC_NIBELUNGEN]->val2; - else { - #ifndef RENEWAL - TBL_PC *sd = (TBL_PC*)bl; - int index = sd->equip_index[sd->state.lr_flag?EQI_HAND_L:EQI_HAND_R]; - if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4) - #endif - watk += sc->data[SC_NIBELUNGEN]->val2; - } - } - - if(sc->data[SC_INCATKRATE]) - watk += watk * sc->data[SC_INCATKRATE]->val1/100; - if(sc->data[SC_PROVOKE]) - watk += watk * sc->data[SC_PROVOKE]->val3/100; - if(sc->data[SC_CONCENTRATION]) - watk += watk * sc->data[SC_CONCENTRATION]->val2/100; - if(sc->data[SC_SKE]) - watk += watk * 3; - if(sc->data[SC__ENERVATION]) - watk -= watk * sc->data[SC__ENERVATION]->val2 / 100; - if(sc->data[SC_FLEET]) - watk += watk * sc->data[SC_FLEET]->val3/100; - if(sc->data[SC_CURSE]) - watk -= watk * 25/100; - if(sc->data[SC_STRIPWEAPON]) - watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100; - if(sc->data[SC__ENERVATION]) - watk -= watk * sc->data[SC__ENERVATION]->val2 / 100; - if((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2) - || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) - || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2) - || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) - ) - watk += watk / 10; - if( sc && sc->data[SC_TIDAL_WEAPON] ) - watk += watk * sc->data[SC_TIDAL_WEAPON]->val2 / 100; - if(sc->data[SC_ANGRIFFS_MODUS]) - watk += watk * sc->data[SC_ANGRIFFS_MODUS]->val2/100; -#ifdef RENEWAL_EDP - if( sc->data[SC_EDP] ) - watk = watk * (100 + sc->data[SC_EDP]->val1 * 80) / 100; -#endif - - return (unsigned short)cap_value(watk,0,USHRT_MAX); -} -#ifdef RENEWAL -static unsigned short status_calc_ematk(struct block_list *bl, struct status_change *sc, int matk) -{ - - if (!sc || !sc->count) - return cap_value(matk,0,USHRT_MAX); - if (sc->data[SC_MATKPOTION]) - matk += sc->data[SC_MATKPOTION]->val1; - if (sc->data[SC_MATKFOOD]) - matk += sc->data[SC_MATKFOOD]->val1; - if(sc->data[SC_MANA_PLUS]) - matk += sc->data[SC_MANA_PLUS]->val1; - if(sc->data[SC_AQUAPLAY_OPTION]) - matk += sc->data[SC_AQUAPLAY_OPTION]->val2; - if(sc->data[SC_CHILLY_AIR_OPTION]) - matk += sc->data[SC_CHILLY_AIR_OPTION]->val2; - if(sc->data[SC_WATER_BARRIER]) - matk -= sc->data[SC_WATER_BARRIER]->val3; - if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3) - matk += 50; - if(sc->data[SC_ODINS_POWER]) - matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2 - if(sc->data[SC_IZAYOI]) - matk += 50 * sc->data[SC_IZAYOI]->val1; - return (unsigned short)cap_value(matk,0,USHRT_MAX); -} -#endif -static unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, int matk) -{ - if(!sc || !sc->count) - return cap_value(matk,0,USHRT_MAX); -#ifndef RENEWAL - // take note fixed value first before % modifiers - if (sc->data[SC_MATKPOTION]) - matk += sc->data[SC_MATKPOTION]->val1; - if (sc->data[SC_MATKFOOD]) - matk += sc->data[SC_MATKFOOD]->val1; - if (sc->data[SC_MANA_PLUS]) - matk += sc->data[SC_MANA_PLUS]->val1; - if (sc->data[SC_AQUAPLAY_OPTION]) - matk += sc->data[SC_AQUAPLAY_OPTION]->val2; - if (sc->data[SC_CHILLY_AIR_OPTION]) - matk += sc->data[SC_CHILLY_AIR_OPTION]->val2; - if (sc->data[SC_WATER_BARRIER]) - matk -= sc->data[SC_WATER_BARRIER]->val3; - if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3) - matk += 50; - if (sc->data[SC_ODINS_POWER]) - matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2 - if (sc->data[SC_IZAYOI]) - matk += 50 * sc->data[SC_IZAYOI]->val1; -#endif - if (sc->data[SC_MAGICPOWER]) - matk += matk * sc->data[SC_MAGICPOWER]->val3/100; - if (sc->data[SC_MINDBREAKER]) - matk += matk * sc->data[SC_MINDBREAKER]->val2/100; - if (sc->data[SC_INCMATKRATE]) - matk += matk * sc->data[SC_INCMATKRATE]->val1/100; - if (sc->data[SC_MOONLITSERENADE]) - matk += matk * sc->data[SC_MOONLITSERENADE]->val2/100; - if (sc->data[SC_MELODYOFSINK]) - matk += matk * sc->data[SC_MELODYOFSINK]->val3/100; - if (sc->data[SC_BEYONDOFWARCRY]) - matk -= matk * sc->data[SC_BEYONDOFWARCRY]->val3/100; - if( sc->data[SC_ZANGETSU] ) - matk += matk * sc->data[SC_ZANGETSU]->val2 / 100; - - return (unsigned short)cap_value(matk,0,USHRT_MAX); -} - -static signed short status_calc_critical(struct block_list *bl, struct status_change *sc, int critical) { - - if(!sc || !sc->count) - return cap_value(critical,10,SHRT_MAX); - - if (sc->data[SC_INCCRI]) - critical += sc->data[SC_INCCRI]->val2; - if (sc->data[SC_EXPLOSIONSPIRITS]) - critical += sc->data[SC_EXPLOSIONSPIRITS]->val2; - if (sc->data[SC_FORTUNE]) - critical += sc->data[SC_FORTUNE]->val2; - if (sc->data[SC_TRUESIGHT]) - critical += sc->data[SC_TRUESIGHT]->val2; - if(sc->data[SC_CLOAKING]) - critical += critical; - if(sc->data[SC_STRIKING]) - critical += sc->data[SC_STRIKING]->val1; -#ifdef RENEWAL - if (sc->data[SC_SPEARQUICKEN]) - critical += 3*sc->data[SC_SPEARQUICKEN]->val1*10; -#endif - - if(sc->data[SC__INVISIBILITY]) - critical += critical * sc->data[SC__INVISIBILITY]->val3 / 100; - if(sc->data[SC__UNLUCKY]) - critical -= critical * sc->data[SC__UNLUCKY]->val2 / 100; - - return (short)cap_value(critical,10,SHRT_MAX); -} - -static signed short status_calc_hit(struct block_list *bl, struct status_change *sc, int hit) -{ - - if(!sc || !sc->count) - return cap_value(hit,1,SHRT_MAX); - - if(sc->data[SC_INCHIT]) - hit += sc->data[SC_INCHIT]->val1; - if(sc->data[SC_HITFOOD]) - hit += sc->data[SC_HITFOOD]->val1; - if(sc->data[SC_TRUESIGHT]) - hit += sc->data[SC_TRUESIGHT]->val3; - if(sc->data[SC_HUMMING]) - hit += sc->data[SC_HUMMING]->val2; - if(sc->data[SC_CONCENTRATION]) - hit += sc->data[SC_CONCENTRATION]->val3; - if(sc->data[SC_INSPIRATION]) - hit += 5 * sc->data[SC_INSPIRATION]->val1; - if(sc->data[SC_ADJUSTMENT]) - hit -= 30; - if(sc->data[SC_INCREASING]) - hit += 20; // RockmanEXE; changed based on updated [Reddozen] - if(sc->data[SC_MERC_HITUP]) - hit += sc->data[SC_MERC_HITUP]->val2; - - if(sc->data[SC_INCHITRATE]) - hit += hit * sc->data[SC_INCHITRATE]->val1/100; - if(sc->data[SC_BLIND]) - hit -= hit * 25/100; - if(sc->data[SC__GROOMY]) - hit -= hit * sc->data[SC__GROOMY]->val3 / 100; - if(sc->data[SC_FEAR]) - hit -= hit * 20 / 100; - if (sc->data[SC_ASH]) - hit /= 2; - - return (short)cap_value(hit,1,SHRT_MAX); -} - -static signed short status_calc_flee(struct block_list *bl, struct status_change *sc, int flee) -{ - if( bl->type == BL_PC ) - { - if( map_flag_gvg(bl->m) ) - flee -= flee * battle_config.gvg_flee_penalty/100; - else if( map[bl->m].flag.battleground ) - flee -= flee * battle_config.bg_flee_penalty/100; - } - - if(!sc || !sc->count) - return cap_value(flee,1,SHRT_MAX); - - if(sc->data[SC_INCFLEE]) - flee += sc->data[SC_INCFLEE]->val1; - if(sc->data[SC_FLEEFOOD]) - flee += sc->data[SC_FLEEFOOD]->val1; - if(sc->data[SC_WHISTLE]) - flee += sc->data[SC_WHISTLE]->val2; - if(sc->data[SC_WINDWALK]) - flee += sc->data[SC_WINDWALK]->val2; - if(sc->data[SC_VIOLENTGALE]) - flee += sc->data[SC_VIOLENTGALE]->val2; - if(sc->data[SC_MOON_COMFORT]) //SG skill [Komurka] - flee += sc->data[SC_MOON_COMFORT]->val2; - if(sc->data[SC_CLOSECONFINE]) - flee += 10; - if (sc->data[SC_ANGRIFFS_MODUS]) - flee -= sc->data[SC_ANGRIFFS_MODUS]->val3; - if (sc->data[SC_OVERED_BOOST]) - flee = max(flee,sc->data[SC_OVERED_BOOST]->val2); - if(sc->data[SC_ADJUSTMENT]) - flee += 30; - if(sc->data[SC_SPEED]) - flee += 10 + sc->data[SC_SPEED]->val1 * 10; - if(sc->data[SC_GATLINGFEVER]) - flee -= sc->data[SC_GATLINGFEVER]->val4; - 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_HALLUCINATIONWALK] ) - flee += sc->data[SC_HALLUCINATIONWALK]->val2; - if( sc->data[SC_WATER_BARRIER] ) - flee -= sc->data[SC_WATER_BARRIER]->val3; - 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); -#ifdef RENEWAL - if( sc->data[SC_SPEARQUICKEN] ) - flee += 2 * sc->data[SC_SPEARQUICKEN]->val1; -#endif - - if(sc->data[SC_INCFLEERATE]) - flee += flee * sc->data[SC_INCFLEERATE]->val1/100; - if(sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1) - flee -= flee * 50/100; - if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) - flee -= flee * 50/100; - if(sc->data[SC_BLIND]) - flee -= flee * 25/100; - if(sc->data[SC_FEAR]) - flee -= flee * 20 / 100; - if(sc->data[SC_PARALYSE]) - flee -= flee * 10 / 100; // 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_SATURDAYNIGHTFEVER] ) - flee -= flee * (40 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100; - 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_ASH] && (bl->type==BL_MOB)){ //mob - if(status_get_element(bl) == ELE_WATER) //water type - flee /= 2; - } - - return (short)cap_value(flee,1,SHRT_MAX); -} - -static signed short status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2) -{ - if(!sc || !sc->count) - return cap_value(flee2,10,SHRT_MAX); - - if(sc->data[SC_INCFLEE2]) - flee2 += sc->data[SC_INCFLEE2]->val2; - if(sc->data[SC_WHISTLE]) - flee2 += sc->data[SC_WHISTLE]->val3*10; - if(sc->data[SC__UNLUCKY]) - flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100; - - return (short)cap_value(flee2,10,SHRT_MAX); -} -static defType status_calc_def(struct block_list *bl, struct status_change *sc, int def) { - - if(!sc || !sc->count) - return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX); - - if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) - return 0; - if(sc->data[SC_SKA]) - return sc->data[SC_SKA]->val3; - if(sc->data[SC_BARRIER]) - return 100; - if(sc->data[SC_KEEPING]) - return 90; -#ifndef RENEWAL // does not provide 90 DEF in renewal mode - if(sc->data[SC_STEELBODY]) - return 90; -#endif - - if(sc->data[SC_ARMORCHANGE]) - def += sc->data[SC_ARMORCHANGE]->val2; - if(sc->data[SC_DRUMBATTLE]) - def += sc->data[SC_DRUMBATTLE]->val3; - if(sc->data[SC_DEFENCE]) //[orn] - def += sc->data[SC_DEFENCE]->val2 ; - if(sc->data[SC_INCDEFRATE]) - def += def * sc->data[SC_INCDEFRATE]->val1/100; - if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) - def += 50; - if(sc->data[SC_ODINS_POWER]) - def -= 20; - if( sc->data[SC_ANGRIFFS_MODUS] ) - def -= 30 + 20 * sc->data[SC_ANGRIFFS_MODUS]->val1; - if(sc->data[SC_STONEHARDSKIN])// Final DEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech] - def += sc->data[SC_STONEHARDSKIN]->val1; - if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) - def >>=1; - if(sc->data[SC_FREEZE]) - def >>=1; - if(sc->data[SC_SIGNUMCRUCIS]) - def -= def * sc->data[SC_SIGNUMCRUCIS]->val2/100; - if(sc->data[SC_CONCENTRATION]) - def -= def * sc->data[SC_CONCENTRATION]->val4/100; - if(sc->data[SC_SKE]) - def >>=1; - if(sc->data[SC_PROVOKE] && bl->type != BL_PC) // Provoke doesn't alter player defense-> - def -= def * sc->data[SC_PROVOKE]->val4/100; - if(sc->data[SC_STRIPSHIELD]) - def -= def * sc->data[SC_STRIPSHIELD]->val2/100; - if (sc->data[SC_FLING]) - def -= def * (sc->data[SC_FLING]->val2)/100; - if( sc->data[SC_FREEZING] ) - def -= def * 10 / 100; - 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_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_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( sc->data[SC_PRESTIGE] ) - def += def * sc->data[SC_PRESTIGE]->val1 / 100; - if(sc->data[SC_ASH] && (bl->type==BL_MOB)){ - if(status_get_race(bl)==RC_PLANT) - def /= 2; - } - - return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);; -} - -static signed short status_calc_def2(struct block_list *bl, struct status_change *sc, int def2) -{ - if(!sc || !sc->count) -#ifdef RENEWAL - return (short)cap_value(def2,SHRT_MIN,SHRT_MAX); -#else - return (short)cap_value(def2,1,SHRT_MAX); -#endif - - if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) - return 0; - if(sc->data[SC_ETERNALCHAOS]) - return 0; - if(sc->data[SC_SUN_COMFORT]) - def2 += sc->data[SC_SUN_COMFORT]->val2; - 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_ANGELUS]) -#ifdef RENEWAL //in renewal only the VIT stat bonus is boosted by angelus - def2 += status_get_vit(bl) / 2 * sc->data[SC_ANGELUS]->val2/100; -#else - def2 += def2 * sc->data[SC_ANGELUS]->val2/100; -#endif - if(sc->data[SC_CONCENTRATION]) - def2 -= def2 * sc->data[SC_CONCENTRATION]->val4/100; - if(sc->data[SC_POISON]) - def2 -= def2 * 25/100; - if(sc->data[SC_DPOISON]) - def2 -= def2 * 25/100; - if(sc->data[SC_SKE]) - def2 -= def2 * 50/100; - if(sc->data[SC_PROVOKE]) - def2 -= def2 * sc->data[SC_PROVOKE]->val4/100; - if(sc->data[SC_JOINTBEAT]) - def2 -= def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_SHOULDER ? 50 : 0 ) / 100 - + 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_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_GT_REVITALIZE] && sc->data[SC_GT_REVITALIZE]->val4) - def2 += def2 * sc->data[SC_GT_REVITALIZE]->val4 / 100; - if(sc->data[SC_ASH] && (bl->type==BL_MOB)){ - if(status_get_race(bl)==RC_PLANT) - def2 /= 2; - } - if (sc->data[SC_PARALYSIS]) - def2 -= def2 * sc->data[SC_PARALYSIS]->val2 / 100; - -#ifdef RENEWAL - return (short)cap_value(def2,SHRT_MIN,SHRT_MAX); -#else - return (short)cap_value(def2,1,SHRT_MAX); -#endif -} - - -static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) { - - if(!sc || !sc->count) - return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX); - - if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) - return 0; - if(sc->data[SC_BARRIER]) - return 100; - -#ifndef RENEWAL // no longer provides 90 MDEF in renewal mode - if(sc->data[SC_STEELBODY]) - return 90; -#endif - - if(sc->data[SC_ARMORCHANGE]) - mdef += sc->data[SC_ARMORCHANGE]->val3; - if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3) - mdef += 50; - if(sc->data[SC_ENDURE])// It has been confirmed that eddga card grants 1 MDEF, not 0, not 10, but 1. - mdef += (sc->data[SC_ENDURE]->val4 == 0) ? sc->data[SC_ENDURE]->val1 : 1; - if(sc->data[SC_CONCENTRATION]) - mdef += 1; //Skill info says it adds a fixed 1 Mdef point. - if(sc->data[SC_STONEHARDSKIN])// Final MDEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech] - mdef += sc->data[SC_STONEHARDSKIN]->val1; - if(sc->data[SC_WATER_BARRIER]) - mdef += sc->data[SC_WATER_BARRIER]->val2; - if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) - mdef += 25*mdef/100; - if(sc->data[SC_FREEZE]) - mdef += 25*mdef/100; - 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] && sc->data[SC_GT_CHANGE]->val4) - mdef -= mdef * sc->data[SC_GT_CHANGE]->val4 / 100; - if (sc->data[SC_ODINS_POWER]) - mdef -= 20 * sc->data[SC_ODINS_POWER]->val1; - - return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX); -} - -static signed short status_calc_mdef2(struct block_list *bl, struct status_change *sc, int mdef2) -{ - if(!sc || !sc->count) -#ifdef RENEWAL - return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX); -#else - return (short)cap_value(mdef2,1,SHRT_MAX); -#endif - - - if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) - return 0; - if(sc->data[SC_SKA]) - return 90; - if(sc->data[SC_MINDBREAKER]) - mdef2 -= mdef2 * sc->data[SC_MINDBREAKER]->val3/100; - if(sc->data[SC_ANALYZE]) - mdef2 -= mdef2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100; - -#ifdef RENEWAL - return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX); -#else - return (short)cap_value(mdef2,1,SHRT_MAX); -#endif -} - -static unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc, int speed) -{ - TBL_PC* sd = BL_CAST(BL_PC, bl); - int speed_rate; - - if( sc == NULL ) - return cap_value(speed,10,USHRT_MAX); - - if( sd && sd->ud.skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || sd->ud.skill_id == LG_EXEEDBREAK) ) - { - if( sd->ud.skill_id == LG_EXEEDBREAK ) - speed_rate = 100 + 60 - (sd->ud.skill_lv * 10); - else - speed_rate = 175 - 5 * pc_checkskill(sd,SA_FREECAST); - } - else - { - speed_rate = 100; - - //GetMoveHasteValue2() - { - int val = 0; - - if( sc->data[SC_FUSION] ) - val = 25; - else if( sd ) { - if( pc_isriding(sd) || sd->sc.option&(OPTION_DRAGON|OPTION_MOUNTING) ) - val = 25;//Same bonus - else if( pc_isridingwug(sd) ) - val = 15 + 5 * pc_checkskill(sd, RA_WUGRIDER); - else if( pc_ismadogear(sd) ) { - val = (- 10 * (5 - pc_checkskill(sd,NC_MADOLICENCE))); - if( sc->data[SC_ACCELERATION] ) - val += 25; - } - } - - speed_rate -= val; - } - - //GetMoveSlowValue() - { - int val = 0; - - if( sd && sc->data[SC_HIDING] && pc_checkskill(sd,RG_TUNNELDRIVE) > 0 ) - val = 120 - 6 * pc_checkskill(sd,RG_TUNNELDRIVE); - else - if( sd && sc->data[SC_CHASEWALK] && sc->data[SC_CHASEWALK]->val3 < 0 ) - val = sc->data[SC_CHASEWALK]->val3; - else - { - // Longing for Freedom cancels song/dance penalty - if( sc->data[SC_LONGING] ) - val = max( val, 50 - 10 * sc->data[SC_LONGING]->val1 ); - else - if( sd && sc->data[SC_DANCING] ) - val = max( val, 500 - (40 + 10 * (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER)) * pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON)) ); - - if( sc->data[SC_DECREASEAGI] ) - val = max( val, 25 ); - if( sc->data[SC_QUAGMIRE] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY] || (sc->data[SC_GLOOMYDAY] && sc->data[SC_GLOOMYDAY]->val4) ) - val = max( val, 50 ); - if( sc->data[SC_DONTFORGETME] ) - val = max( val, sc->data[SC_DONTFORGETME]->val3 ); - if( sc->data[SC_CURSE] ) - val = max( val, 300 ); - if( sc->data[SC_CHASEWALK] ) - val = max( val, sc->data[SC_CHASEWALK]->val3 ); - if( sc->data[SC_WEDDING] ) - val = max( val, 100 ); - if( sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&(BREAK_ANKLE|BREAK_KNEE) ) - val = max( val, (sc->data[SC_JOINTBEAT]->val2&BREAK_ANKLE ? 50 : 0) + (sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ? 30 : 0) ); - if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 0 ) - val = max( val, sc->data[SC_CLOAKING]->val1 < 3 ? 300 : 30 - 3 * sc->data[SC_CLOAKING]->val1 ); - if( sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY ) - val = max( val, 75 ); - if( sc->data[SC_SLOWDOWN] ) // Slow Potion - val = max( val, 100 ); - if( sc->data[SC_GATLINGFEVER] ) - val = max( val, 100 ); - if( sc->data[SC_SUITON] ) - val = max( val, sc->data[SC_SUITON]->val3 ); - if( sc->data[SC_SWOO] ) - val = max( val, 300 ); - 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 ? 0 : 25 * (5 - 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->bonus.speed_rate + sd->bonus.speed_add_rate > 0 ) // permanent item-based speedup - val = max( val, sd->bonus.speed_rate + sd->bonus.speed_add_rate ); - } - - speed_rate += val; - } - - //GetMoveHasteValue1() - { - int val = 0; - - if( sc->data[SC_SPEEDUP1] ) //FIXME: used both by NPC_AGIUP and Speed Potion script - val = max( val, 50 ); - if( sc->data[SC_INCREASEAGI] ) - val = max( val, 25 ); - if( sc->data[SC_WINDWALK] ) - val = max( val, 2 * sc->data[SC_WINDWALK]->val1 ); - if( sc->data[SC_CARTBOOST] ) - val = max( val, 20 ); - if( sd && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && pc_checkskill(sd,TF_MISS) > 0 ) - val = max( val, 1 * pc_checkskill(sd,TF_MISS) ); - if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 1 ) - val = max( val, sc->data[SC_CLOAKING]->val1 >= 10 ? 25 : 3 * sc->data[SC_CLOAKING]->val1 - 3 ); - if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) - val = max( val, 25 ); - if( sc->data[SC_RUN] ) - val = max( val, 55 ); - if( sc->data[SC_AVOID] ) - val = max( val, 10 * sc->data[SC_AVOID]->val1 ); - if( sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] ) - val = max( val, 75 ); - if( sc->data[SC_CLOAKINGEXCEED] ) - val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3); - if( sc->data[SC_HOVERING] ) - val = max( val, 10 ); - if( sc->data[SC_GN_CARTBOOST] ) - val = max( val, sc->data[SC_GN_CARTBOOST]->val2 ); - if( sc->data[SC_SWINGDANCE] ) - val = max( val, sc->data[SC_SWINGDANCE]->val2 ); - if( sc->data[SC_WIND_STEP_OPTION] ) - val = max( val, sc->data[SC_WIND_STEP_OPTION]->val2 ); - - //FIXME: official items use a single bonus for this [ultramage] - if( sc->data[SC_SPEEDUP0] ) // temporary item-based speedup - val = max( val, 25 ); - if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate < 0 ) // permanent item-based speedup - val = max( val, -(sd->bonus.speed_rate + sd->bonus.speed_add_rate) ); - - speed_rate -= val; - } - - if( speed_rate < 40 ) - speed_rate = 40; - } - - //GetSpeed() - { - if( sd && pc_iscarton(sd) ) - speed += speed * (50 - 5 * pc_checkskill(sd,MC_PUSHCART)) / 100; - if( sc->data[SC_PARALYSE] ) - speed += speed * 50 / 100; - if( speed_rate != 100 ) - speed = speed * speed_rate / 100; - if( sc->data[SC_STEELBODY] ) - speed = 200; - if( sc->data[SC_DEFENDER] ) - speed = max(speed, 200); - if( sc->data[SC_WALKSPEED] && sc->data[SC_WALKSPEED]->val1 > 0 ) // ChangeSpeed - speed = speed * 100 / sc->data[SC_WALKSPEED]->val1; - } - - return (short)cap_value(speed,10,USHRT_MAX); -} - -#ifdef RENEWAL_ASPD -// flag&1 - fixed value [malufett] -// flag&2 - percentage value -static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag) -{ - int i, pots = 0, skills1 = 0, skills2 = 0; - - if(!sc || !sc->count) - return 0; - - if(sc->data[i=SC_ASPDPOTION3] || - sc->data[i=SC_ASPDPOTION2] || - sc->data[i=SC_ASPDPOTION1] || - sc->data[i=SC_ASPDPOTION0]) - pots += sc->data[i]->val1; - - if( !sc->data[SC_QUAGMIRE] ){ - if(sc->data[SC_STAR_COMFORT]) - skills1 = 5; // needs more info - - if(sc->data[SC_TWOHANDQUICKEN] && skills1 < 7) - skills1 = 7; - - if(sc->data[SC_ONEHAND] && skills1 < 7) skills1 = 7; - - if(sc->data[SC_MERC_QUICKEN] && skills1 < 7) // needs more info - skills1 = 7; - - if(sc->data[SC_ADRENALINE2] && skills1 < 6) - skills1 = 6; - - if(sc->data[SC_ADRENALINE] && skills1 < 7) - skills1 = 7; - - if(sc->data[SC_SPEARQUICKEN] && skills1 < 7) - skills1 = 7; - - if(sc->data[SC_GATLINGFEVER] && skills1 < 9) // needs more info - skills1 = 9; - - if(sc->data[SC_FLEET] && skills1 < 5) - skills1 = 5; - - if(sc->data[SC_ASSNCROS] && - skills1 < 5+1*sc->data[SC_ASSNCROS]->val1) // needs more info - { - if (bl->type!=BL_PC) - skills1 = 4+1*sc->data[SC_ASSNCROS]->val1; - else - switch(((TBL_PC*)bl)->status.weapon) - { - case W_BOW: - case W_REVOLVER: - case W_RIFLE: - case W_GATLING: - case W_SHOTGUN: - case W_GRENADE: - break; - default: - skills1 = 5+1*sc->data[SC_ASSNCROS]->val1; - } - } - } - - if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) && skills1 < 15) - skills1 = 15; - else if(sc->data[SC_MADNESSCANCEL] && skills1 < 15) // needs more info - skills1 = 15; - - if(sc->data[SC_DONTFORGETME]) - skills2 -= sc->data[SC_DONTFORGETME]->val2; // needs more info - if(sc->data[SC_LONGING]) - skills2 -= sc->data[SC_LONGING]->val2; // needs more info - if(sc->data[SC_STEELBODY]) - skills2 -= 25; - if(sc->data[SC_SKA]) - skills2 -= 25; - if(sc->data[SC_DEFENDER]) - skills2 -= sc->data[SC_DEFENDER]->val4; // needs more info - if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY) // needs more info - skills2 -= 25; - if(sc->data[SC_GRAVITATION]) - skills2 -= sc->data[SC_GRAVITATION]->val2; // needs more info - if(sc->data[SC_JOINTBEAT]) { // needs more info - if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST ) - skills2 -= 25; - if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ) - skills2 -= 10; - } - if( sc->data[SC_FREEZING] ) - skills2 -= 30; - if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] ) - skills2 -= 50; - if( sc->data[SC_PARALYSE] ) - skills2 -= 10; - if( sc->data[SC__BODYPAINT] ) - skills2 -= 2 + 5 * sc->data[SC__BODYPAINT]->val1; - if( sc->data[SC__INVISIBILITY] ) - skills2 -= sc->data[SC__INVISIBILITY]->val2 ; - if( sc->data[SC__GROOMY] ) - skills2 -= sc->data[SC__GROOMY]->val2; - if( sc->data[SC_SWINGDANCE] ) - skills2 += sc->data[SC_SWINGDANCE]->val2; - if( sc->data[SC_DANCEWITHWUG] ) - skills2 += sc->data[SC_DANCEWITHWUG]->val3; - if( sc->data[SC_GLOOMYDAY] ) - skills2 -= sc->data[SC_GLOOMYDAY]->val3; - if( sc->data[SC_EARTHDRIVE] ) - skills2 -= 25; - if( sc->data[SC_GT_CHANGE] ) - skills2 += sc->data[SC_GT_CHANGE]->val3; - if( sc->data[SC_MELON_BOMB] ) - skills2 -= sc->data[SC_MELON_BOMB]->val1; - if( sc->data[SC_BOOST500] ) - skills2 += sc->data[SC_BOOST500]->val1; - if( sc->data[SC_EXTRACT_SALAMINE_JUICE] ) - skills2 += sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1; - if( sc->data[SC_INCASPDRATE] ) - skills2 += sc->data[SC_INCASPDRATE]->val1; - - return ( flag&1? (skills1 + pots) : skills2 ); -} -#endif - -static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int aspd) { - if (!sc || !sc->count) - return cap_value(aspd, 0, 2000); - - if (!sc->data[SC_QUAGMIRE]) { - if (sc->data[SC_OVERED_BOOST]) - aspd = 2000 - sc->data[SC_OVERED_BOOST]->val3*10; - } - - if ((sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] - || sc->data[SC_WILD_STORM_OPTION])) - aspd -= 50; // +5 ASPD - if( sc && sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 ) - aspd -= (bl->type==BL_PC?pc_checkskill((TBL_PC *)bl, RK_RUNEMASTERY):10) / 10 * 40; - - return cap_value(aspd, 0, 2000); // will be recap for proper bl anyway -} - -/// Calculates an object's ASPD modifier (alters the base amotion value). -/// 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) -{ - int i; - - if(!sc || !sc->count) - return cap_value(aspd_rate,0,SHRT_MAX); - - if( !sc->data[SC_QUAGMIRE] ){ - int max = 0; - if(sc->data[SC_STAR_COMFORT]) - max = sc->data[SC_STAR_COMFORT]->val2; - - if(sc->data[SC_TWOHANDQUICKEN] && - max < sc->data[SC_TWOHANDQUICKEN]->val2) - max = sc->data[SC_TWOHANDQUICKEN]->val2; - - if(sc->data[SC_ONEHAND] && - max < sc->data[SC_ONEHAND]->val2) - max = sc->data[SC_ONEHAND]->val2; - - if(sc->data[SC_MERC_QUICKEN] && - max < sc->data[SC_MERC_QUICKEN]->val2) - max = sc->data[SC_MERC_QUICKEN]->val2; - - if(sc->data[SC_ADRENALINE2] && - max < sc->data[SC_ADRENALINE2]->val3) - max = sc->data[SC_ADRENALINE2]->val3; - - if(sc->data[SC_ADRENALINE] && - max < sc->data[SC_ADRENALINE]->val3) - max = sc->data[SC_ADRENALINE]->val3; - - if(sc->data[SC_SPEARQUICKEN] && - max < sc->data[SC_SPEARQUICKEN]->val2) - max = sc->data[SC_SPEARQUICKEN]->val2; - - if(sc->data[SC_GATLINGFEVER] && - max < sc->data[SC_GATLINGFEVER]->val2) - max = sc->data[SC_GATLINGFEVER]->val2; - - if(sc->data[SC_FLEET] && - max < sc->data[SC_FLEET]->val2) - max = sc->data[SC_FLEET]->val2; - - if(sc->data[SC_ASSNCROS] && - max < sc->data[SC_ASSNCROS]->val2) - { - if (bl->type!=BL_PC) - max = sc->data[SC_ASSNCROS]->val2; - else - switch(((TBL_PC*)bl)->status.weapon) - { - case W_BOW: - case W_REVOLVER: - case W_RIFLE: - case W_GATLING: - case W_SHOTGUN: - case W_GRENADE: - break; - default: - max = sc->data[SC_ASSNCROS]->val2; - } - } - aspd_rate -= max; - - if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])) - aspd_rate -= 300; - else if(sc->data[SC_MADNESSCANCEL]) - aspd_rate -= 200; - } - - 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; - - if(sc->data[SC_DONTFORGETME]) - aspd_rate += 10 * sc->data[SC_DONTFORGETME]->val2; - if(sc->data[SC_LONGING]) - aspd_rate += sc->data[SC_LONGING]->val2; - if(sc->data[SC_STEELBODY]) - aspd_rate += 250; - if(sc->data[SC_SKA]) - aspd_rate += 250; - if(sc->data[SC_DEFENDER]) - aspd_rate += sc->data[SC_DEFENDER]->val4; - if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY) - aspd_rate += 250; - if(sc->data[SC_GRAVITATION]) - aspd_rate += sc->data[SC_GRAVITATION]->val2; - if(sc->data[SC_JOINTBEAT]) { - if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST ) - aspd_rate += 250; - if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ) - aspd_rate += 100; - } - 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 += 200 + 50 * sc->data[SC__BODYPAINT]->val1; - if( sc->data[SC__INVISIBILITY] ) - aspd_rate += sc->data[SC__INVISIBILITY]->val2 * 10 ; - if( sc->data[SC__GROOMY] ) - aspd_rate += sc->data[SC__GROOMY]->val2 * 10; - if( sc->data[SC_SWINGDANCE] ) - aspd_rate -= sc->data[SC_SWINGDANCE]->val2 * 10; - if( sc->data[SC_DANCEWITHWUG] ) - aspd_rate -= sc->data[SC_DANCEWITHWUG]->val3 * 10; - if( sc->data[SC_GLOOMYDAY] ) - aspd_rate += sc->data[SC_GLOOMYDAY]->val3 * 10; - if( sc->data[SC_EARTHDRIVE] ) - aspd_rate += 250; - if( sc->data[SC_GT_CHANGE] ) - aspd_rate -= sc->data[SC_GT_CHANGE]->val3 * 10; - if( sc->data[SC_MELON_BOMB] ) - aspd_rate += sc->data[SC_MELON_BOMB]->val1 * 10; - if( sc->data[SC_BOOST500] ) - aspd_rate -= sc->data[SC_BOOST500]->val1 *10; - if( sc->data[SC_EXTRACT_SALAMINE_JUICE] ) - aspd_rate -= sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1 * 10; - if( sc->data[SC_INCASPDRATE] ) - aspd_rate -= sc->data[SC_INCASPDRATE]->val1 * 10; - if( sc->data[SC_PAIN_KILLER]) - aspd_rate += sc->data[SC_PAIN_KILLER]->val2 * 10; - if( sc->data[SC_GOLDENE_FERSE]) - aspd_rate -= sc->data[SC_GOLDENE_FERSE]->val3 * 10; - - return (short)cap_value(aspd_rate,0,SHRT_MAX); -} - -static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion) -{ - if( !sc || !sc->count || map_flag_gvg(bl->m) || map[bl->m].flag.battleground ) - return cap_value(dmotion,0,USHRT_MAX); - /** - * It has been confirmed on official servers that MvP mobs have no dmotion even without endure - **/ - if( sc->data[SC_ENDURE] || ( bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS) ) ) - return 0; - if( sc->data[SC_CONCENTRATION] ) - return 0; - if( sc->data[SC_RUN] || sc->data[SC_WUGDASH] ) - return 0; - - return (unsigned short)cap_value(dmotion,0,USHRT_MAX); -} - -static unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc, uint64 maxhp) -{ - if(!sc || !sc->count) - return (unsigned int)cap_value(maxhp,1,UINT_MAX); - - if(sc->data[SC_INCMHPRATE]) - maxhp += maxhp * sc->data[SC_INCMHPRATE]->val1/100; - if(sc->data[SC_INCMHP]) - maxhp += (sc->data[SC_INCMHP]->val1); - if(sc->data[SC_APPLEIDUN]) - maxhp += maxhp * sc->data[SC_APPLEIDUN]->val2/100; - if(sc->data[SC_DELUGE]) - maxhp += maxhp * sc->data[SC_DELUGE]->val2/100; - if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) - maxhp += maxhp * 2; - if(sc->data[SC_MARIONETTE]) - maxhp -= 1000; - if(sc->data[SC_SOLID_SKIN_OPTION]) - maxhp += 2000;// Fix amount. - if(sc->data[SC_POWER_OF_GAIA]) - maxhp += 3000; - if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) - maxhp += 500; - - if(sc->data[SC_MERC_HPUP]) - maxhp += maxhp * sc->data[SC_MERC_HPUP]->val2/100; - - if(sc->data[SC_EPICLESIS]) - maxhp += maxhp * 5 * sc->data[SC_EPICLESIS]->val1 / 100; - if(sc->data[SC_VENOMBLEED]) - maxhp -= maxhp * 15 / 100; - if(sc->data[SC__WEAKNESS]) - maxhp -= maxhp * sc->data[SC__WEAKNESS]->val2 / 100; - if(sc->data[SC_LERADSDEW]) - maxhp += maxhp * sc->data[SC_LERADSDEW]->val3 / 100; - if(sc->data[SC_FORCEOFVANGUARD]) - maxhp += maxhp * 3 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100; - if(sc->data[SC_INSPIRATION]) //Custom value. - maxhp += maxhp * 3 * sc->data[SC_INSPIRATION]->val1 / 100; - if(sc->data[SC_RAISINGDRAGON]) - maxhp += maxhp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100; - if(sc->data[SC_GT_CHANGE]) // Max HP decrease: [Skill Level x 4] % - maxhp -= maxhp * (4 * sc->data[SC_GT_CHANGE]->val1) / 100; - if(sc->data[SC_GT_REVITALIZE])// Max HP increase: [Skill Level x 2] % - maxhp += maxhp * (2 * sc->data[SC_GT_REVITALIZE]->val1) / 100; - if(sc->data[SC_MUSTLE_M]) - maxhp += maxhp * sc->data[SC_MUSTLE_M]->val1/100; - if(sc->data[SC_MYSTERIOUS_POWDER]) - maxhp -= sc->data[SC_MYSTERIOUS_POWDER]->val1 / 100; - if(sc->data[SC_PETROLOGY_OPTION]) - maxhp += maxhp * sc->data[SC_PETROLOGY_OPTION]->val2 / 100; - if (sc->data[SC_ANGRIFFS_MODUS]) - maxhp += maxhp * 5 * sc->data[SC_ANGRIFFS_MODUS]->val1 /100; - if (sc->data[SC_GOLDENE_FERSE]) - maxhp += maxhp * sc->data[SC_GOLDENE_FERSE]->val2 / 100; - - return (unsigned int)cap_value(maxhp,1,UINT_MAX); -} - -static unsigned int status_calc_maxsp(struct block_list *bl, struct status_change *sc, unsigned int maxsp) -{ - if(!sc || !sc->count) - return cap_value(maxsp,1,UINT_MAX); - - if(sc->data[SC_INCMSPRATE]) - maxsp += maxsp * sc->data[SC_INCMSPRATE]->val1/100; - if(sc->data[SC_INCMSP]) - maxsp += (sc->data[SC_INCMSP]->val1); - if(sc->data[SC_SERVICE4U]) - maxsp += maxsp * sc->data[SC_SERVICE4U]->val2/100; - if(sc->data[SC_MERC_SPUP]) - maxsp += maxsp * sc->data[SC_MERC_SPUP]->val2/100; - if(sc->data[SC_RAISINGDRAGON]) - maxsp += maxsp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100; - if(sc->data[SC_LIFE_FORCE_F]) - maxsp += maxsp * sc->data[SC_LIFE_FORCE_F]->val1/100; - if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3) - maxsp += 50; - - return cap_value(maxsp,1,UINT_MAX); -} - -static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element) -{ - if(!sc || !sc->count) - return element; - - if(sc->data[SC_FREEZE]) - return ELE_WATER; - if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) - return ELE_EARTH; - if(sc->data[SC_BENEDICTIO]) - return ELE_HOLY; - if(sc->data[SC_CHANGEUNDEAD]) - 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); -} - -static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv) -{ - if(!sc || !sc->count) - return lv; - - if(sc->data[SC_FREEZE]) - return 1; - if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) - return 1; - if(sc->data[SC_BENEDICTIO]) - return 1; - if(sc->data[SC_CHANGEUNDEAD]) - return 1; - if(sc->data[SC_ELEMENTALCHANGE]) - return sc->data[SC_ELEMENTALCHANGE]->val1; - if(sc->data[SC_SHAPESHIFT]) - return 1; - if(sc->data[SC__INVISIBILITY]) - return 1; - - return (unsigned char)cap_value(lv,1,4); -} - - -unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element) -{ - if(!sc || !sc->count) - return element; - if(sc->data[SC_ENCHANTARMS]) - return sc->data[SC_ENCHANTARMS]->val2; - if(sc->data[SC_WATERWEAPON] - || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) ) - return ELE_WATER; - if(sc->data[SC_EARTHWEAPON] - || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) ) - return ELE_EARTH; - if(sc->data[SC_FIREWEAPON] - || (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2) ) - return ELE_FIRE; - if(sc->data[SC_WINDWEAPON] - || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2) ) - return ELE_WIND; - if(sc->data[SC_ENCPOISON]) - return ELE_POISON; - if(sc->data[SC_ASPERSIO]) - return ELE_HOLY; - if(sc->data[SC_SHADOWWEAPON]) - return ELE_DARK; - if(sc->data[SC_GHOSTWEAPON] || sc->data[SC__INVISIBILITY]) - return ELE_GHOST; - if(sc->data[SC_TIDAL_WEAPON_OPTION] || sc->data[SC_TIDAL_WEAPON] ) - return ELE_WATER; - if(sc->data[SC_PYROCLASTIC]) - return ELE_FIRE; - return (unsigned char)cap_value(element,0,UCHAR_MAX); -} - -static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode) -{ - if(!sc || !sc->count) - return mode; - if(sc->data[SC_MODECHANGE]) { - if (sc->data[SC_MODECHANGE]->val2) - mode = sc->data[SC_MODECHANGE]->val2; //Set mode - if (sc->data[SC_MODECHANGE]->val3) - mode|= sc->data[SC_MODECHANGE]->val3; //Add mode - if (sc->data[SC_MODECHANGE]->val4) - mode&=~sc->data[SC_MODECHANGE]->val4; //Del mode - } - return cap_value(mode,0,USHRT_MAX); -} - -const char* status_get_name(struct block_list *bl) { - nullpo_ret(bl); - switch (bl->type) { - case BL_PC: return ((TBL_PC *)bl)->fakename[0] != '\0' ? ((TBL_PC*)bl)->fakename : ((TBL_PC*)bl)->status.name; - case BL_MOB: return ((TBL_MOB*)bl)->name; - case BL_PET: return ((TBL_PET*)bl)->pet.name; - case BL_HOM: return ((TBL_HOM*)bl)->homunculus.name; - case BL_NPC: return ((TBL_NPC*)bl)->name; - } - return "Unknown"; -} - -/*========================================== - * Get the class of the current bl - * return - * 0 = fail - * class_id = success - *------------------------------------------*/ -int status_get_class(struct block_list *bl) { - nullpo_ret(bl); - switch( bl->type ) { - case BL_PC: return ((TBL_PC*)bl)->status.class_; - case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob. - case BL_PET: return ((TBL_PET*)bl)->pet.class_; - case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_; - case BL_MER: return ((TBL_MER*)bl)->mercenary.class_; - case BL_NPC: return ((TBL_NPC*)bl)->class_; - case BL_ELEM: return ((TBL_ELEM*)bl)->elemental.class_; - } - return 0; -} -/*========================================== - * Get the base level of the current bl - * return - * 1 = fail - * level = success - *------------------------------------------*/ -int status_get_lv(struct block_list *bl) { - nullpo_ret(bl); - switch (bl->type) { - case BL_PC: return ((TBL_PC*)bl)->status.base_level; - case BL_MOB: return ((TBL_MOB*)bl)->level; - case BL_PET: return ((TBL_PET*)bl)->pet.level; - case BL_HOM: return ((TBL_HOM*)bl)->homunculus.level; - case BL_MER: return ((TBL_MER*)bl)->db->lv; - case BL_ELEM: return ((TBL_ELEM*)bl)->db->lv; - case BL_NPC: return ((TBL_NPC*)bl)->level; - } - return 1; -} - -struct regen_data *status_get_regen_data(struct block_list *bl) -{ - nullpo_retr(NULL, bl); - switch (bl->type) { - case BL_PC: return &((TBL_PC*)bl)->regen; - case BL_HOM: return &((TBL_HOM*)bl)->regen; - case BL_MER: return &((TBL_MER*)bl)->regen; - case BL_ELEM: return &((TBL_ELEM*)bl)->regen; - default: - return NULL; - } -} - -struct status_data *status_get_status_data(struct block_list *bl) -{ - nullpo_retr(&dummy_status, bl); - - switch (bl->type) { - case BL_PC: return &((TBL_PC*)bl)->battle_status; - case BL_MOB: return &((TBL_MOB*)bl)->status; - case BL_PET: return &((TBL_PET*)bl)->status; - case BL_HOM: return &((TBL_HOM*)bl)->battle_status; - case BL_MER: return &((TBL_MER*)bl)->battle_status; - case BL_ELEM: return &((TBL_ELEM*)bl)->battle_status; - case BL_NPC: return ((mobdb_checkid(((TBL_NPC*)bl)->class_) == 0) ? &((TBL_NPC*)bl)->status : &dummy_status); - default: - return &dummy_status; - } -} - -struct status_data *status_get_base_status(struct block_list *bl) -{ - nullpo_retr(NULL, bl); - switch (bl->type) { - case BL_PC: return &((TBL_PC*)bl)->base_status; - case BL_MOB: return ((TBL_MOB*)bl)->base_status ? ((TBL_MOB*)bl)->base_status : &((TBL_MOB*)bl)->db->status; - case BL_PET: return &((TBL_PET*)bl)->db->status; - case BL_HOM: return &((TBL_HOM*)bl)->base_status; - case BL_MER: return &((TBL_MER*)bl)->base_status; - case BL_ELEM: return &((TBL_ELEM*)bl)->base_status; - case BL_NPC: return ((mobdb_checkid(((TBL_NPC*)bl)->class_) == 0) ? &((TBL_NPC*)bl)->status : NULL); - default: - return NULL; - } -} -defType status_get_def(struct block_list *bl) { - struct unit_data *ud; - struct status_data *status = status_get_status_data(bl); - int def = status?status->def:0; - ud = unit_bl2ud(bl); - if (ud && ud->skilltimer != INVALID_TIMER) - def -= def * skill_get_castdef(ud->skill_id)/100; - - return cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX); -} - -unsigned short status_get_speed(struct block_list *bl) -{ - if(bl->type==BL_NPC)//Only BL with speed data but no status_data [Skotlex] - return ((struct npc_data *)bl)->speed; - return status_get_status_data(bl)->speed; -} - -int status_get_party_id(struct block_list *bl) { - nullpo_ret(bl); - switch (bl->type) { - case BL_PC: - return ((TBL_PC*)bl)->status.party_id; - case BL_PET: - if (((TBL_PET*)bl)->msd) - return ((TBL_PET*)bl)->msd->status.party_id; - break; - case BL_MOB: { - struct mob_data *md=(TBL_MOB*)bl; - if( md->master_id > 0 ) { - struct map_session_data *msd; - if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL) - return msd->status.party_id; - return -md->master_id; - } - } - break; - case BL_HOM: - if (((TBL_HOM*)bl)->master) - return ((TBL_HOM*)bl)->master->status.party_id; - break; - case BL_MER: - if (((TBL_MER*)bl)->master) - return ((TBL_MER*)bl)->master->status.party_id; - break; - case BL_SKILL: - return ((TBL_SKILL*)bl)->group->party_id; - case BL_ELEM: - if (((TBL_ELEM*)bl)->master) - return ((TBL_ELEM*)bl)->master->status.party_id; - break; - } - return 0; -} - -int status_get_guild_id(struct block_list *bl) { - nullpo_ret(bl); - switch (bl->type) { - case BL_PC: - return ((TBL_PC*)bl)->status.guild_id; - case BL_PET: - if (((TBL_PET*)bl)->msd) - return ((TBL_PET*)bl)->msd->status.guild_id; - break; - case BL_MOB: { - struct map_session_data *msd; - struct mob_data *md = (struct mob_data *)bl; - if (md->guardian_data) //Guardian's guild [Skotlex] - return md->guardian_data->guild_id; - if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL) - return msd->status.guild_id; //Alchemist's mobs [Skotlex] - } - break; - case BL_HOM: - if (((TBL_HOM*)bl)->master) - return ((TBL_HOM*)bl)->master->status.guild_id; - break; - case BL_MER: - if (((TBL_MER*)bl)->master) - return ((TBL_MER*)bl)->master->status.guild_id; - break; - case BL_NPC: - if (((TBL_NPC*)bl)->subtype == SCRIPT) - return ((TBL_NPC*)bl)->u.scr.guild_id; - break; - case BL_SKILL: - return ((TBL_SKILL*)bl)->group->guild_id; - case BL_ELEM: - if (((TBL_ELEM*)bl)->master) - return ((TBL_ELEM*)bl)->master->status.guild_id; - break; - } - return 0; -} - -int status_get_emblem_id(struct block_list *bl) { - nullpo_ret(bl); - switch (bl->type) { - case BL_PC: - return ((TBL_PC*)bl)->guild_emblem_id; - case BL_PET: - if (((TBL_PET*)bl)->msd) - return ((TBL_PET*)bl)->msd->guild_emblem_id; - break; - case BL_MOB: { - struct map_session_data *msd; - struct mob_data *md = (struct mob_data *)bl; - if (md->guardian_data) //Guardian's guild [Skotlex] - return md->guardian_data->emblem_id; - if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL) - return msd->guild_emblem_id; //Alchemist's mobs [Skotlex] - } - break; - case BL_HOM: - if (((TBL_HOM*)bl)->master) - return ((TBL_HOM*)bl)->master->guild_emblem_id; - break; - case BL_MER: - if (((TBL_MER*)bl)->master) - return ((TBL_MER*)bl)->master->guild_emblem_id; - break; - case BL_NPC: - if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) { - struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id); - if (g) - return g->emblem_id; - } - break; - case BL_ELEM: - if (((TBL_ELEM*)bl)->master) - return ((TBL_ELEM*)bl)->master->guild_emblem_id; - break; - } - return 0; -} - -int status_get_mexp(struct block_list *bl) -{ - nullpo_ret(bl); - if(bl->type==BL_MOB) - return ((struct mob_data *)bl)->db->mexp; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->mexp; - return 0; -} -int status_get_race2(struct block_list *bl) -{ - nullpo_ret(bl); - if(bl->type == BL_MOB) - return ((struct mob_data *)bl)->db->race2; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->race2; - return 0; -} - -int status_isdead(struct block_list *bl) -{ - nullpo_ret(bl); - return status_get_status_data(bl)->hp == 0; -} - -int status_isimmune(struct block_list *bl) -{ - struct status_change *sc =status_get_sc(bl); - if (sc && sc->data[SC_HERMODE]) - return 100; - - if (bl->type == BL_PC && - ((TBL_PC*)bl)->special_state.no_magic_damage >= battle_config.gtb_sc_immunity) - return ((TBL_PC*)bl)->special_state.no_magic_damage; - return 0; -} - -struct view_data* status_get_viewdata(struct block_list *bl) -{ - nullpo_retr(NULL, bl); - switch (bl->type) { - case BL_PC: return &((TBL_PC*)bl)->vd; - case BL_MOB: return ((TBL_MOB*)bl)->vd; - case BL_PET: return &((TBL_PET*)bl)->vd; - case BL_NPC: return ((TBL_NPC*)bl)->vd; - case BL_HOM: return ((TBL_HOM*)bl)->vd; - case BL_MER: return ((TBL_MER*)bl)->vd; - case BL_ELEM: return ((TBL_ELEM*)bl)->vd; - } - return NULL; -} - -void status_set_viewdata(struct block_list *bl, int class_) -{ - struct view_data* vd; - nullpo_retv(bl); - if (mobdb_checkid(class_) || mob_is_clone(class_)) - vd = mob_get_viewdata(class_); - else if (npcdb_checkid(class_) || (bl->type == BL_NPC && class_ == WARP_CLASS)) - vd = npc_get_viewdata(class_); - else if (homdb_checkid(class_)) - vd = merc_get_hom_viewdata(class_); - else if (merc_class(class_)) - vd = merc_get_viewdata(class_); - else if (elemental_class(class_)) - vd = elemental_get_viewdata(class_); - else - vd = NULL; - - switch (bl->type) { - case BL_PC: - { - TBL_PC* sd = (TBL_PC*)bl; - if (pcdb_checkid(class_)) { - if (sd->sc.option&OPTION_WEDDING) - class_ = JOB_WEDDING; - else if (sd->sc.option&OPTION_SUMMER) - class_ = JOB_SUMMER; - else if (sd->sc.option&OPTION_XMAS) - class_ = JOB_XMAS; - else if (sd->sc.option&OPTION_RIDING) { - switch (class_) { //Adapt class to a Mounted one. - case JOB_KNIGHT: - class_ = JOB_KNIGHT2; - break; - case JOB_CRUSADER: - class_ = JOB_CRUSADER2; - break; - case JOB_LORD_KNIGHT: - class_ = JOB_LORD_KNIGHT2; - break; - case JOB_PALADIN: - class_ = JOB_PALADIN2; - break; - case JOB_BABY_KNIGHT: - class_ = JOB_BABY_KNIGHT2; - break; - case JOB_BABY_CRUSADER: - class_ = JOB_BABY_CRUSADER2; - break; - } - } - sd->vd.class_ = class_; - clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield); - sd->vd.head_top = sd->status.head_top; - sd->vd.head_mid = sd->status.head_mid; - sd->vd.head_bottom = sd->status.head_bottom; - sd->vd.hair_style = cap_value(sd->status.hair,0,battle_config.max_hair_style); - sd->vd.hair_color = cap_value(sd->status.hair_color,0,battle_config.max_hair_color); - sd->vd.cloth_color = cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color); - sd->vd.sex = sd->status.sex; - } else if (vd) - memcpy(&sd->vd, vd, sizeof(struct view_data)); - else - ShowError("status_set_viewdata (PC): No view data for class %d\n", class_); - } - break; - case BL_MOB: - { - TBL_MOB* md = (TBL_MOB*)bl; - if (vd) - md->vd = vd; - else - ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_); - } - break; - case BL_PET: - { - TBL_PET* pd = (TBL_PET*)bl; - if (vd) { - memcpy(&pd->vd, vd, sizeof(struct view_data)); - if (!pcdb_checkid(vd->class_)) { - pd->vd.hair_style = battle_config.pet_hair_style; - if(pd->pet.equip) { - pd->vd.head_bottom = itemdb_viewid(pd->pet.equip); - if (!pd->vd.head_bottom) - pd->vd.head_bottom = pd->pet.equip; - } - } - } else - ShowError("status_set_viewdata (PET): No view data for class %d\n", class_); - } - break; - case BL_NPC: - { - TBL_NPC* nd = (TBL_NPC*)bl; - if (vd) - nd->vd = vd; - else - ShowError("status_set_viewdata (NPC): No view data for class %d\n", class_); - } - break; - case BL_HOM: //[blackhole89] - { - struct homun_data *hd = (struct homun_data*)bl; - if (vd) - hd->vd = vd; - else - ShowError("status_set_viewdata (HOMUNCULUS): No view data for class %d\n", class_); - } - break; - case BL_MER: - { - struct mercenary_data *md = (struct mercenary_data*)bl; - if (vd) - md->vd = vd; - else - ShowError("status_set_viewdata (MERCENARY): No view data for class %d\n", class_); - } - break; - case BL_ELEM: - { - struct elemental_data *ed = (struct elemental_data*)bl; - if (vd) - ed->vd = vd; - else - ShowError("status_set_viewdata (ELEMENTAL): No view data for class %d\n", class_); - } - break; - } - vd = status_get_viewdata(bl); - if (vd && vd->cloth_color && ( - (vd->class_==JOB_WEDDING && battle_config.wedding_ignorepalette) - || (vd->class_==JOB_XMAS && battle_config.xmas_ignorepalette) - || (vd->class_==JOB_SUMMER && battle_config.summer_ignorepalette) - )) - vd->cloth_color = 0; -} - -/// Returns the status_change data of bl or NULL if it doesn't exist. -struct status_change *status_get_sc(struct block_list *bl) { - if( bl ) - switch (bl->type) { - case BL_PC: return &((TBL_PC*)bl)->sc; - case BL_MOB: return &((TBL_MOB*)bl)->sc; - case BL_NPC: return &((TBL_NPC*)bl)->sc; - case BL_HOM: return &((TBL_HOM*)bl)->sc; - case BL_MER: return &((TBL_MER*)bl)->sc; - case BL_ELEM: return &((TBL_ELEM*)bl)->sc; - } - return NULL; -} - -void status_change_init(struct block_list *bl) -{ - struct status_change *sc = status_get_sc(bl); - nullpo_retv(sc); - memset(sc, 0, sizeof (struct status_change)); -} - -//Applies SC defense to a given status change. -//Returns the adjusted duration based on flag values. -//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 = 0, tick_def = 0; - struct status_data* status; - struct status_change* sc; - struct map_session_data *sd; - - nullpo_ret(bl); - - //Status that are blocked by Golden Thief Bug card or Wand of Hermod - if (status_isimmune(bl)) - switch (type) { - case SC_DECREASEAGI: - case SC_SILENCE: - case SC_COMA: - case SC_INCREASEAGI: - case SC_BLESSING: - case SC_SLOWPOISON: - case SC_IMPOSITIO: - case SC_AETERNA: - case SC_SUFFRAGIUM: - case SC_BENEDICTIO: - case SC_PROVIDENCE: - case SC_KYRIE: - case SC_ASSUMPTIO: - case SC_ANGELUS: - case SC_MAGNIFICAT: - case SC_GLORIA: - case SC_WINDWALK: - case SC_MAGICROD: - case SC_HALLUCINATION: - case SC_STONE: - case SC_QUAGMIRE: - case SC_SUITON: - case SC_SWINGDANCE: - case SC__ENERVATION: - case SC__GROOMY: - case SC__IGNORANCE: - case SC__LAZINESS: - case SC__UNLUCKY: - case SC__WEAKNESS: - case SC__BLOODYLUST: - return 0; - } - - sd = BL_CAST(BL_PC,bl); - status = status_get_status_data(bl); - sc = status_get_sc(bl); - if( sc && !sc->count ) - sc = NULL; - switch (type) { - case SC_STUN: - case SC_POISON: - if( sc && sc->data[SC__UNLUCKY] ) - return tick; - case SC_DPOISON: - case SC_SILENCE: - case SC_BLEEDING: - sc_def = 3 +status->vit; - break; - case SC_SLEEP: - sc_def = 3 +status->int_; - break; - case SC_DEEPSLEEP: - tick_def = status->int_ / 10 + status_get_lv(bl) * 65 / 1000; // Seems to be -1 sec every 10 int and -5% chance every 10 int. - sc_def = 5 * status->int_ /10; - break; - case SC_DECREASEAGI: - case SC_ADORAMUS://Arch Bishop - if (sd) tick>>=1; //Half duration for players. - case SC_STONE: - case SC_FREEZE: - sc_def = 3 +status->mdef; - break; - case SC_CURSE: - //Special property: inmunity when luk is greater than level or zero - if (status->luk > status_get_lv(bl) || status->luk == 0) - return 0; - else - sc_def = 3 +status->luk; - tick_def = status->vit; - break; - case SC_BLIND: - if( sc && sc->data[SC__UNLUCKY] ) - return tick; - sc_def = 3 +(status->vit + status->int_)/2; - break; - case SC_CONFUSION: - sc_def = 3 +(status->str + status->int_)/2; - break; - case SC_ANKLE: - if(status->mode&MD_BOSS) // Lasts 5 times less on bosses - tick /= 5; - sc_def = status->agi / 2; - break; - case SC_MAGICMIRROR: - case SC_ARMORCHANGE: - if (sd) //Duration greatly reduced for players. - tick /= 15; - //No defense against it (buff). - rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; // Lineal Reduction of Rate - break; - case SC_MARSHOFABYSS: - //5 second (Fixed) + 25 second - {( INT + LUK ) / 20 second } - tick -= (status->int_ + status->luk) / 20 * 1000; - break; - case SC_STASIS: - //5 second (fixed) + { Stasis Skill level * 5 - (Target�s VIT + DEX) / 20 } - tick -= (status->vit + status->dex) / 20 * 1000; - break; - case SC_WHITEIMPRISON: - if( tick == 5000 ) // 100% on caster - break; - if( bl->type == BL_PC ) - tick -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; - else - tick -= (status->vit + status->luk) / 20 * 1000; - 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: // 100% - (100 - 0.8 x INT) - sc_def = 100 - ( 100 - status->int_* 8 / 10 ); - sc_def = max(sc_def, 5); // minimum of 5% - break; - case SC_BITE: // {(Base Success chance) - (Target's AGI / 4)} - rate -= status->agi*1000/4; - rate = max(rate,50000); // minimum of 50% - break; - case SC_ELECTRICSHOCKER: - if( bl->type == BL_MOB ) - tick -= 1000 * (status->agi/10); - break; - case SC_CRYSTALIZE: - tick -= (1000*(status->vit/10))+(status_get_lv(bl)/50); - break; - case SC_MANDRAGORA: - sc_def = (status->vit+status->luk)/5; - break; - case SC_KYOUGAKU: - tick -= 30*status->int_; - break; - case SC_PARALYSIS: - tick -= 50 * (status->vit + status->luk); //(1000/20); - break; - default: - //Effect that cannot be reduced? Likely a buff. - if (!(rnd()%10000 < rate)) - return 0; - return tick?tick:1; - } - - if (sd) { - - if (battle_config.pc_sc_def_rate != 100) - sc_def = sc_def*battle_config.pc_sc_def_rate/100; - - if (sc_def < battle_config.pc_max_sc_def) - sc_def += (battle_config.pc_max_sc_def - sc_def)* - status->luk/battle_config.pc_luk_sc_def; - else - sc_def = battle_config.pc_max_sc_def; - - if (tick_def) { - if (battle_config.pc_sc_def_rate != 100) - tick_def = tick_def*battle_config.pc_sc_def_rate/100; - } - - } else { - - if (battle_config.mob_sc_def_rate != 100) - sc_def = sc_def*battle_config.mob_sc_def_rate/100; - - if (sc_def < battle_config.mob_max_sc_def) - sc_def += (battle_config.mob_max_sc_def - sc_def)* - status->luk/battle_config.mob_luk_sc_def; - else - sc_def = battle_config.mob_max_sc_def; - - if (tick_def) { - if (battle_config.mob_sc_def_rate != 100) - tick_def = tick_def*battle_config.mob_sc_def_rate/100; - } - } - - if (sc) { - if (sc->data[SC_SCRESIST]) - sc_def += sc->data[SC_SCRESIST]->val1; //Status resist - else if (sc->data[SC_SIEGFRIED]) - sc_def += sc->data[SC_SIEGFRIED]->val3; //Status resistance. - } - - //When no tick def, reduction is the same for both. - if( !tick_def && type != SC_STONE ) //Recent tests show duration of petrify isn't reduced by MDEF. [Inkfish] - tick_def = sc_def; - - //Natural resistance - if (!(flag&8)) { - rate -= rate*sc_def/100; - - //Item resistance (only applies to rate%) - if(sd && SC_COMMON_MIN <= type && type <= SC_COMMON_MAX) - { - if( sd->reseff[type-SC_COMMON_MIN] > 0 ) - rate -= rate*sd->reseff[type-SC_COMMON_MIN]/10000; - if( sd->sc.data[SC_COMMONSC_RESIST] ) - rate -= rate*sd->sc.data[SC_COMMONSC_RESIST]->val1/100; - } - } - if (!(rnd()%10000 < rate)) - return 0; - - //Why would a status start with no duration? Presume it has - //duration defined elsewhere. - if (!tick) return 1; - - //Rate reduction - if (flag&2) - return tick; - - tick -= tick*tick_def/100; - // Changed to 5 seconds according to recent tests [Playtester] - if (type == SC_ANKLE && tick < 5000) - tick = 5000; - return tick<=0?0:tick; -} - -/*========================================== - * Starts a status change. - * 'type' = type, 'val1~4' depend on the type. - * 'rate' = base success rate. 10000 = 100% - * 'tick' is base duration - * 'flag': - * &1: Cannot be avoided (it has to start) - * &2: Tick should not be reduced (by vit, luk, lv, etc) - * &4: sc_data loaded, no value has to be altered. - * &8: rate should not be reduced - *------------------------------------------*/ -int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,int tick,int flag) -{ - struct map_session_data *sd = NULL; - struct status_change* sc; - struct status_change_entry* sce; - struct status_data *status; - struct view_data *vd; - int opt_flag, calc_flag, undead_flag, val_flag = 0, tick_time = 0; - bool sc_isnew = true; - - nullpo_ret(bl); - sc = status_get_sc(bl); - status = status_get_status_data(bl); - - if( type <= SC_NONE || type >= SC_MAX ) - { - ShowError("status_change_start: invalid status change (%d)!\n", type); - return 0; - } - - if( !sc ) - return 0; //Unable to receive status changes - - if( status_isdead(bl) && type != SC_NOCHAT ) // SC_NOCHAT should work even on dead characters - return 0; - - if( bl->type == BL_MOB) - { - struct mob_data *md = BL_CAST(BL_MOB,bl); - if(md && (md->class_ == MOBID_EMPERIUM || mob_is_battleground(md)) && type != SC_SAFETYWALL && type != SC_PNEUMA) - return 0; //Emperium/BG Monsters can't be afflicted by status changes - // if(md && mob_is_gvg(md) && status_sc2scb_flag(type)&SCB_MAXHP) - // return 0; //prevent status addinh hp to gvg mob (like bloodylust=hp*3 etc... - } - - if( sc->data[SC_REFRESH] ) { - if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX) // Confirmed. - return 0; // Immune to status ailements - switch( type ) { - case SC_QUAGMIRE://Tester said it protects against this and decrease agi. - case SC_DECREASEAGI: - case SC_BURNING: - case SC_FREEZING: - //case SC_WHITEIMPRISON://Need confirm. Protected against this in the past. [Rytech] - case SC_MARSHOFABYSS: - 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_CRYSTALIZE: ////08/31/2011 - Class Balance Changes - case SC_DEEPSLEEP: - case SC_MANDRAGORA: - return 0; - } - } - else if( sc->data[SC_INSPIRATION] ) { - if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX ) - return 0; // Immune to status ailements - switch( type ) { - case SC_DEEPSLEEP: - case SC_SATURDAYNIGHTFEVER: - case SC_PYREXIA: - case SC_DEATHHURT: - case SC_MAGICMUSHROOM: - case SC_VENOMBLEED: - case SC_TOXIN: - case SC_OBLIVIONCURSE: - case SC_LEECHESEND: - case SC__ENERVATION: - case SC__GROOMY: - case SC__LAZINESS: - case SC__UNLUCKY: - case SC__WEAKNESS: - case SC__BODYPAINT: - case SC__IGNORANCE: - return 0; - } - } - - sd = BL_CAST(BL_PC, bl); - - //Adjust tick according to status resistances - if( !(flag&(1|4)) ) - { - tick = status_get_sc_def(bl, type, rate, tick, flag); - if( !tick ) return 0; - } - - undead_flag = battle_check_undead(status->race,status->def_ele); - //Check for inmunities / sc fails - switch (type) { - case SC_ANGRIFFS_MODUS: - case SC_GOLDENE_FERSE: - if ((type==SC_GOLDENE_FERSE && sc->data[SC_ANGRIFFS_MODUS]) - || (type==SC_ANGRIFFS_MODUS && sc->data[SC_GOLDENE_FERSE]) - ) - return 0; - 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_DEEPSLEEP: - case SC_SLEEP: - case SC_STUN: - case SC_FREEZING: - case SC_CRYSTALIZE: - if (sc->opt1) - return 0; //Cannot override other opt1 status changes. [Skotlex] - if((type == SC_FREEZE || type == SC_FREEZING || type == SC_CRYSTALIZE) && sc->data[SC_WARMER]) - return 0; //Immune to Frozen and Freezing status if under Warmer status. [Jobbie] - break; - - //There all like berserk, do not everlap each other - case SC__BLOODYLUST: - if(!sd) return 0; //should only affect player - case SC_BERSERK: - if (((type == SC_BERSERK) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC__BLOODYLUST])) - || ((type == SC__BLOODYLUST) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC_BERSERK])) - ) - return 0; - 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) - return 0; - break; - case SC_AETERNA: - if( (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) || sc->data[SC_FREEZE] ) - return 0; - break; - case SC_KYRIE: - if (bl->type == BL_MOB) - return 0; - break; - 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->option&OPTION_MADOGEAR //Adrenaline doesn't affect Mado Gear [Ind] - ) - return 0; - break; - case SC_ADRENALINE2: - if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE2))) - return 0; - if (sc->data[SC_QUAGMIRE] || - sc->data[SC_DECREASEAGI] - ) - return 0; - break; - case SC_MAGNIFICAT: - if( sc->option&OPTION_MADOGEAR ) //Mado is immune to magnificat - return 0; - break; - case SC_ONEHAND: - case SC_MERC_QUICKEN: - case SC_TWOHANDQUICKEN: - if(sc->data[SC_DECREASEAGI]) - return 0; - - case SC_INCREASEAGI: - if(sd && pc_issit(sd)){ - pc_setstand(sd); - } - - case SC_CONCENTRATE: - case SC_SPEARQUICKEN: - case SC_TRUESIGHT: - case SC_WINDWALK: - case SC_CARTBOOST: - 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] - //Due to the cloaking card, we have to check the wall versus to known - //skill level rather than the used one. [Skotlex] - //if (sd && val1 < 3 && skill_check_cloaking(bl,NULL)) - if( sd && pc_checkskill(sd, AS_CLOAKING) < 3 && !skill_check_cloaking(bl,NULL) ) - return 0; - break; - case SC_MODECHANGE: - { - int mode; - struct status_data *bstatus = status_get_base_status(bl); - if (!bstatus) return 0; - if (sc->data[type]) - { //Pile up with previous values. - if(!val2) val2 = sc->data[type]->val2; - val3 |= sc->data[type]->val3; - val4 |= sc->data[type]->val4; - } - mode = val2?val2:bstatus->mode; //Base mode - if (val4) mode&=~val4; //Del mode - if (val3) mode|= val3; //Add mode - if (mode == bstatus->mode) { //No change. - if (sc->data[type]) //Abort previous status - return status_change_end(bl, type, INVALID_TIMER); - return 0; - } - } - break; - //Strip skills, need to divest something or it fails. - case SC_STRIPWEAPON: - if (sd && !(flag&4)) { //apply sc anyway if loading saved sc_data - int i; - opt_flag = 0; //Reuse to check success condition. - if(sd->bonus.unstripable_equip&EQP_WEAPON) - return 0; - i = sd->equip_index[EQI_HAND_L]; - if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) { - opt_flag|=1; - pc_unequipitem(sd,i,3); //L-hand weapon - } - - i = sd->equip_index[EQI_HAND_R]; - if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) { - opt_flag|=2; - pc_unequipitem(sd,i,3); - } - if (!opt_flag) return 0; - } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC - break; - case SC_STRIPSHIELD: - if( val2 == 1 ) val2 = 0; //GX effect. Do not take shield off.. - else - if (sd && !(flag&4)) { - int i; - if(sd->bonus.unstripable_equip&EQP_SHIELD) - return 0; - i = sd->equip_index[EQI_HAND_L]; - if ( i < 0 || !sd->inventory_data[i] || sd->inventory_data[i]->type != IT_ARMOR ) - return 0; - pc_unequipitem(sd,i,3); - } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC - break; - case SC_STRIPARMOR: - if (sd && !(flag&4)) { - int i; - if(sd->bonus.unstripable_equip&EQP_ARMOR) - return 0; - i = sd->equip_index[EQI_ARMOR]; - if ( i < 0 || !sd->inventory_data[i] ) - return 0; - pc_unequipitem(sd,i,3); - } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC - break; - case SC_STRIPHELM: - if (sd && !(flag&4)) { - int i; - if(sd->bonus.unstripable_equip&EQP_HELM) - return 0; - i = sd->equip_index[EQI_HEAD_TOP]; - if ( i < 0 || !sd->inventory_data[i] ) - return 0; - pc_unequipitem(sd,i,3); - } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC - break; - case SC_MERC_FLEEUP: - case SC_MERC_ATKUP: - case SC_MERC_HPUP: - case SC_MERC_SPUP: - case SC_MERC_HITUP: - if( bl->type != BL_MER ) - return 0; // Stats only for Mercenaries - break; - case SC_STRFOOD: - if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 > val1) - return 0; - break; - case SC_AGIFOOD: - if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 > val1) - return 0; - break; - case SC_VITFOOD: - if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 > val1) - return 0; - break; - case SC_INTFOOD: - if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 > val1) - return 0; - break; - case SC_DEXFOOD: - if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 > val1) - return 0; - break; - case SC_LUKFOOD: - if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 > val1) - return 0; - break; - case SC_FOOD_STR_CASH: - if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 > val1) - return 0; - break; - case SC_FOOD_AGI_CASH: - if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 > val1) - return 0; - break; - case SC_FOOD_VIT_CASH: - if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 > val1) - return 0; - break; - case SC_FOOD_INT_CASH: - if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 > val1) - return 0; - break; - case SC_FOOD_DEX_CASH: - if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 > val1) - return 0; - break; - case SC_FOOD_LUK_CASH: - if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1) - return 0; - break; - case SC_CAMOUFLAGE: - if( sd && pc_checkskill(sd, RA_CAMOUFLAGE) < 3 && !skill_check_camouflage(bl,NULL) ) - return 0; - break; - case SC__STRIPACCESSORY: - if( sd ) { - int i = -1; - if( !(sd->bonus.unstripable_equip&EQI_ACC_L) ) { - i = sd->equip_index[EQI_ACC_L]; - if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR ) - pc_unequipitem(sd,i,3); //L-Accessory - } if( !(sd->bonus.unstripable_equip&EQI_ACC_R) ) { - i = sd->equip_index[EQI_ACC_R]; - if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR ) - pc_unequipitem(sd,i,3); //R-Accessory - } - if( i < 0 ) - return 0; - } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC - break; - case SC_TOXIN: - case SC_PARALYSE: - case SC_VENOMBLEED: - case SC_MAGICMUSHROOM: - case SC_DEATHHURT: - case SC_PYREXIA: - case SC_OBLIVIONCURSE: - case SC_LEECHESEND: - { // it doesn't stack or even renewed - int i = SC_TOXIN; - for(; i<= SC_LEECHESEND; i++) - if(sc->data[i]) return 0; - } - break; - case SC_SATURDAYNIGHTFEVER: - if (sc->data[SC_BERSERK] || sc->data[SC_INSPIRATION] || sc->data[SC__BLOODYLUST]) - return 0; - break; - } - - //Check for BOSS resistances - if(status->mode&MD_BOSS && !(flag&1)) { - if (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX) - return 0; - switch (type) { - case SC_BLESSING: - case SC_DECREASEAGI: - case SC_PROVOKE: - case SC_COMA: - case SC_GRAVITATION: - case SC_SUITON: - case SC_RICHMANKIM: - case SC_ROKISWEIL: - case SC_FOGWALL: - case SC_FREEZING: - case SC_BURNING: - case SC_MARSHOFABYSS: - case SC_ADORAMUS: - case SC_PARALYSIS: - case SC_DEEPSLEEP: - case SC_CRYSTALIZE: - - // Exploit 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; - } - } - - //Before overlapping fail, one must check for status cured. - switch (type) { - case SC_BLESSING: - //TO-DO Blessing and Agi up should do 1 damage against players on Undead Status, even on PvM - //but cannot be plagiarized (this requires aegis investigation on packets and official behavior) [Brainstorm] - if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) { - status_change_end(bl, SC_CURSE, INVALID_TIMER); - if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) - status_change_end(bl, SC_STONE, INVALID_TIMER); - } - break; - case SC_INCREASEAGI: - status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER); - break; - case SC_QUAGMIRE: - status_change_end(bl, SC_CONCENTRATE, INVALID_TIMER); - status_change_end(bl, SC_TRUESIGHT, INVALID_TIMER); - status_change_end(bl, SC_WINDWALK, INVALID_TIMER); - //Also blocks the ones below... - case SC_DECREASEAGI: - status_change_end(bl, SC_CARTBOOST, INVALID_TIMER); - //Also blocks the ones below... - case SC_DONTFORGETME: - status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER); - status_change_end(bl, SC_ADRENALINE, INVALID_TIMER); - status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER); - status_change_end(bl, SC_SPEARQUICKEN, INVALID_TIMER); - 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] - status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER); - break; - case SC_MAXOVERTHRUST: - //Cancels Normal Overthrust. [Skotlex] - status_change_end(bl, SC_OVERTHRUST, INVALID_TIMER); - break; - case SC_KYRIE: - //Cancels Assumptio - status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER); - break; - case SC_DELUGE: - if (sc->data[SC_FOGWALL] && sc->data[SC_BLIND]) - status_change_end(bl, SC_BLIND, INVALID_TIMER); - break; - case SC_SILENCE: - if (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF) - status_change_end(bl, SC_GOSPEL, INVALID_TIMER); - break; - case SC_HIDING: - status_change_end(bl, SC_CLOSECONFINE, INVALID_TIMER); - status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER); - break; - case SC__BLOODYLUST: - case SC_BERSERK: - if(battle_config.berserk_cancels_buffs) { - status_change_end(bl, SC_ONEHAND, INVALID_TIMER); - status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER); - status_change_end(bl, SC_CONCENTRATION, INVALID_TIMER); - status_change_end(bl, SC_PARRYING, INVALID_TIMER); - status_change_end(bl, SC_AURABLADE, INVALID_TIMER); - status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER); - } -#ifdef RENEWAL - else { - status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER); - } -#endif - break; - case SC_ASSUMPTIO: - status_change_end(bl, SC_KYRIE, INVALID_TIMER); - status_change_end(bl, SC_KAITE, INVALID_TIMER); - break; - case SC_KAITE: - status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER); - break; - case SC_CARTBOOST: - if(sc->data[SC_DECREASEAGI]) - { //Cancel Decrease Agi, but take no further effect [Skotlex] - status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER); - return 0; - } - break; - case SC_FUSION: - status_change_end(bl, SC_SPIRIT, INVALID_TIMER); - break; - case SC_ADJUSTMENT: - status_change_end(bl, SC_MADNESSCANCEL, INVALID_TIMER); - break; - case SC_MADNESSCANCEL: - status_change_end(bl, SC_ADJUSTMENT, INVALID_TIMER); - break; - //NPC_CHANGEUNDEAD will debuff Blessing and Agi Up - case SC_CHANGEUNDEAD: - status_change_end(bl, SC_BLESSING, INVALID_TIMER); - status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER); - break; - case SC_STRFOOD: - status_change_end(bl, SC_FOOD_STR_CASH, INVALID_TIMER); - break; - case SC_AGIFOOD: - status_change_end(bl, SC_FOOD_AGI_CASH, INVALID_TIMER); - break; - case SC_VITFOOD: - status_change_end(bl, SC_FOOD_VIT_CASH, INVALID_TIMER); - break; - case SC_INTFOOD: - status_change_end(bl, SC_FOOD_INT_CASH, INVALID_TIMER); - break; - case SC_DEXFOOD: - status_change_end(bl, SC_FOOD_DEX_CASH, INVALID_TIMER); - break; - case SC_LUKFOOD: - status_change_end(bl, SC_FOOD_LUK_CASH, INVALID_TIMER); - break; - case SC_FOOD_STR_CASH: - status_change_end(bl, SC_STRFOOD, INVALID_TIMER); - break; - case SC_FOOD_AGI_CASH: - status_change_end(bl, SC_AGIFOOD, INVALID_TIMER); - break; - case SC_FOOD_VIT_CASH: - status_change_end(bl, SC_VITFOOD, INVALID_TIMER); - break; - case SC_FOOD_INT_CASH: - status_change_end(bl, SC_INTFOOD, INVALID_TIMER); - break; - case SC_FOOD_DEX_CASH: - status_change_end(bl, SC_DEXFOOD, INVALID_TIMER); - break; - case SC_FOOD_LUK_CASH: - status_change_end(bl, SC_LUKFOOD, INVALID_TIMER); - break; - case SC_FIGHTINGSPIRIT: - status_change_end(bl, type, INVALID_TIMER); // Remove previous one. - break; - case SC_MARSHOFABYSS: - status_change_end(bl, SC_INCAGI, INVALID_TIMER); - status_change_end(bl, SC_WINDWALK, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER); - status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER); - break; - case SC_SWINGDANCE: - case SC_SYMPHONYOFLOVER: - case SC_MOONLITSERENADE: - case SC_RUSHWINDMILL: - case SC_ECHOSONG: - case SC_HARMONIZE: //group A doesn't overlap - if (type != SC_SWINGDANCE) status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER); - if (type != SC_SYMPHONYOFLOVER) status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER); - if (type != SC_MOONLITSERENADE) status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER); - if (type != SC_RUSHWINDMILL) status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER); - if (type != SC_ECHOSONG) status_change_end(bl, SC_ECHOSONG, INVALID_TIMER); - if (type != SC_HARMONIZE) status_change_end(bl, SC_HARMONIZE, INVALID_TIMER); - break; - case SC_VOICEOFSIREN: - case SC_DEEPSLEEP: - case SC_GLOOMYDAY: - case SC_SONGOFMANA: - case SC_DANCEWITHWUG: - case SC_SATURDAYNIGHTFEVER: - case SC_LERADSDEW: - case SC_MELODYOFSINK: - case SC_BEYONDOFWARCRY: - case SC_UNLIMITEDHUMMINGVOICE: //group B - if (type != SC_VOICEOFSIREN) status_change_end(bl, SC_VOICEOFSIREN, INVALID_TIMER); - if (type != SC_DEEPSLEEP) status_change_end(bl, SC_DEEPSLEEP, INVALID_TIMER); - if (type != SC_LERADSDEW) status_change_end(bl, SC_LERADSDEW, INVALID_TIMER); - if (type != SC_MELODYOFSINK) status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER); - if (type != SC_BEYONDOFWARCRY) status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER); - if (type != SC_UNLIMITEDHUMMINGVOICE) status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER); - if (type != SC_GLOOMYDAY) { - status_change_end(bl, SC_GLOOMYDAY, INVALID_TIMER); - status_change_end(bl, SC_GLOOMYDAY_SK, INVALID_TIMER); - } - if (type != SC_SONGOFMANA) status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER); - if (type != SC_DANCEWITHWUG) status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER); - if (type != SC_SATURDAYNIGHTFEVER) { - if (sc->data[SC_SATURDAYNIGHTFEVER]) { - sc->data[SC_SATURDAYNIGHTFEVER]->val2 = 0; //mark to not lose hp - status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER); - } - } - break; - case SC_REFLECTSHIELD: - status_change_end(bl, SC_REFLECTDAMAGE, INVALID_TIMER); - break; - case SC_REFLECTDAMAGE: - status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER); - break; - case SC_SHIELDSPELL_DEF: - case SC_SHIELDSPELL_MDEF: - case SC_SHIELDSPELL_REF: - status_change_end(bl, SC_MAGNIFICAT, INVALID_TIMER); - if( type != SC_SHIELDSPELL_DEF ) - status_change_end(bl, SC_SHIELDSPELL_DEF, INVALID_TIMER); - if( type != SC_SHIELDSPELL_MDEF ) - status_change_end(bl, SC_SHIELDSPELL_MDEF, INVALID_TIMER); - if( type != SC_SHIELDSPELL_REF ) - status_change_end(bl, SC_SHIELDSPELL_REF, INVALID_TIMER); - break; - case SC_GT_ENERGYGAIN: - case SC_GT_CHANGE: - case SC_GT_REVITALIZE: - if( type != SC_GT_REVITALIZE ) - status_change_end(bl, SC_GT_REVITALIZE, INVALID_TIMER); - if( type != SC_GT_ENERGYGAIN ) - status_change_end(bl, SC_GT_ENERGYGAIN, INVALID_TIMER); - if( type != SC_GT_CHANGE ) - status_change_end(bl, SC_GT_CHANGE, INVALID_TIMER); - break; - case SC_INVINCIBLE: - status_change_end(bl, SC_INVINCIBLEOFF, INVALID_TIMER); - break; - case SC_INVINCIBLEOFF: - status_change_end(bl, SC_INVINCIBLE, INVALID_TIMER); - break; - case SC_MAGICPOWER: - status_change_end(bl, type, INVALID_TIMER); - break; - } - - //Check for overlapping fails - if( (sce = sc->data[type]) ) { - switch( type ) { - case SC_MERC_FLEEUP: - case SC_MERC_ATKUP: - case SC_MERC_HPUP: - case SC_MERC_SPUP: - case SC_MERC_HITUP: - if( sce->val1 > val1 ) - val1 = sce->val1; - break; - case SC_ADRENALINE: - case SC_ADRENALINE2: - case SC_WEAPONPERFECTION: - case SC_OVERTHRUST: - if (sce->val2 > val2) - return 0; - break; - case SC_S_LIFEPOTION: - case SC_L_LIFEPOTION: - case SC_BOSSMAPINFO: - case SC_STUN: - case SC_SLEEP: - case SC_POISON: - case SC_CURSE: - case SC_SILENCE: - case SC_CONFUSION: - case SC_BLIND: - case SC_BLEEDING: - case SC_DPOISON: - case SC_CLOSECONFINE2: //Can't be re-closed in. - case SC_MARIONETTE: - 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__INVISIBILITY: - case SC__ENERVATION: - case SC__GROOMY: - case SC__IGNORANCE: - case SC__LAZINESS: - case SC__WEAKNESS: - case SC__UNLUCKY: - return 0; - case SC_COMBO: - case SC_DANCING: - case SC_DEVOTION: - case SC_ASPDPOTION0: - case SC_ASPDPOTION1: - case SC_ASPDPOTION2: - case SC_ASPDPOTION3: - case SC_ATKPOTION: - case SC_MATKPOTION: - case SC_ENCHANTARMS: - case SC_ARMOR_ELEMENT: - case SC_ARMOR_RESIST: - break; - case SC_GOSPEL: - //Must not override a casting gospel char. - if(sce->val4 == BCT_SELF) - return 0; - if(sce->val1 > val1) - return 1; - break; - case SC_ENDURE: - if(sce->val4 && !val4) - return 1; //Don't let you override infinite endure. - if(sce->val1 > val1) - return 1; - break; - case SC_KAAHI: - //Kaahi overwrites previous level regardless of existing level. - //Delete timer if it exists. - if (sce->val4 != INVALID_TIMER) { - delete_timer(sce->val4,kaahi_heal_timer); - sce->val4 = INVALID_TIMER; - } - break; - case SC_JAILED: - //When a player is already jailed, do not edit the jail data. - val2 = sce->val2; - val3 = sce->val3; - val4 = sce->val4; - break; - case SC_LERADSDEW: - if (sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])) - return 0; - case SC_SHAPESHIFT: - case SC_PROPERTYWALK: - break; - case SC_LEADERSHIP: - case SC_GLORYWOUNDS: - case SC_SOULCOLD: - case SC_HAWKEYES: - if( sce->val4 && !val4 )//you cannot override master guild aura - return 0; - break; - case SC_JOINTBEAT: - val2 |= sce->val2; // stackable ailments - default: - if(sce->val1 > val1) - return 1; //Return true to not mess up skill animations. [Skotlex] - } - } - - vd = status_get_viewdata(bl); - calc_flag = StatusChangeFlagTable[type]; - if(!(flag&4)) //&4 - Do not parse val settings when loading SCs - switch(type) - { - case SC_DECREASEAGI: - case SC_INCREASEAGI: - val2 = 2 + val1; //Agi change - break; - case SC_ENDURE: - val2 = 7; // Hit-count [Celest] - if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) && !map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && !val4 ) - { - struct map_session_data *tsd; - if( sd ) - { - int i; - for( i = 0; i < 5; i++ ) - { - if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) ) - status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1); - } - } - else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) ) - status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1); - } - //val4 signals infinite endure (if val4 == 2 it is infinite endure from Berserk) - if( val4 ) - tick = -1; - break; - case SC_AUTOBERSERK: - if (status->hp < status->max_hp>>2 && - (!sc->data[SC_PROVOKE] || sc->data[SC_PROVOKE]->val2==0)) - sc_start4(bl,SC_PROVOKE,100,10,1,0,0,60000); - tick = -1; - break; - case SC_SIGNUMCRUCIS: - val2 = 10 + 4*val1; //Def reduction - tick = -1; - clif_emotion(bl,E_SWT); - break; - case SC_MAXIMIZEPOWER: - tick_time = val2 = tick>0?tick:60000; - tick = -1; // duration sent to the client should be infinite - break; - case SC_EDP: // [Celest] - val2 = val1 + 2; //Chance to Poison enemies. -#ifndef RENEWAL_EDP - val3 = 50*(val1+1); //Damage increase (+50 +50*lv%) -#endif - 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] - val3=50; // + 5*val1; //Chance to counter. [Skotlex] - break; - case SC_MAGICROD: - val2 = val1*20; //SP gained - break; - case SC_KYRIE: - val2 = (int64)status->max_hp * (val1 * 2 + 10) / 100; //%Max HP to absorb - val3 = (val1 / 2 + 5); //Hits - break; - case SC_MAGICPOWER: - //val1: Skill lv - val2 = 1; //Lasts 1 invocation - val3 = 5*val1; //Matk% increase - val4 = 0; // 0 = ready to be used, 1 = activated and running - break; - case SC_SACRIFICE: - val2 = 5; //Lasts 5 hits - tick = -1; - break; - case SC_ENCPOISON: - val2= 250+50*val1; //Poisoning Chance (2.5+0.5%) in 1/10000 rate - case SC_ASPERSIO: - case SC_FIREWEAPON: - case SC_WATERWEAPON: - case SC_WINDWEAPON: - case SC_EARTHWEAPON: - case SC_SHADOWWEAPON: - case SC_GHOSTWEAPON: - skill_enchant_elemental_end(bl,type); - break; - case SC_ELEMENTALCHANGE: - // val1 : Element Lvl (if called by skill lvl 1, takes random value between 1 and 4) - // val2 : Element (When no element, random one is picked) - // val3 : 0 = called by skill 1 = called by script (fixed level) - if( !val2 ) val2 = rnd()%ELE_MAX; - - if( val1 == 1 && val3 == 0 ) - val1 = 1 + rnd()%4; - else if( val1 > 4 ) - val1 = 4; // Max Level - val3 = 0; // Not need to keep this info. - break; - case SC_PROVIDENCE: - val2=val1*5; //Race/Ele resist - break; - case SC_REFLECTSHIELD: - val2=10+val1*3; // %Dmg reflected - if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) ) - { - struct map_session_data *tsd; - if( sd ) - { - int i; - for( i = 0; i < 5; i++ ) - { - if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) ) - status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1); - } - } - else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) ) - status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1); - } - break; - case SC_STRIPWEAPON: - if (!sd) //Watk reduction - val2 = 25; - break; - case SC_STRIPSHIELD: - if (!sd) //Def reduction - val2 = 15; - break; - case SC_STRIPARMOR: - if (!sd) //Vit reduction - val2 = 40; - break; - case SC_STRIPHELM: - if (!sd) //Int reduction - val2 = 40; - break; - case SC_AUTOSPELL: - //Val1 Skill LV of Autospell - //Val2 Skill ID to cast - //Val3 Max Lv to cast - val4 = 5 + val1*2; //Chance of casting - break; - case SC_VOLCANO: - val2 = val1*10; //Watk increase -#ifndef RENEWAL - if (status->def_ele != ELE_FIRE) - val2 = 0; -#endif - break; - case SC_VIOLENTGALE: - val2 = val1*3; //Flee increase - #ifndef RENEWAL - if (status->def_ele != ELE_WIND) - val2 = 0; - #endif - break; - case SC_DELUGE: - val2 = deluge_eff[val1-1]; //HP increase -#ifndef RENEWAL - if(status->def_ele != ELE_WATER) - val2 = 0; -#endif - break; - case SC_SUITON: - if (!val2 || (sd && (sd->class_&MAPID_UPPERMASK) == MAPID_NINJA)) { - //No penalties. - val2 = 0; //Agi penalty - val3 = 0; //Walk speed penalty - break; - } - val3 = 50; - val2 = 3*((val1+1)/3); - if (val1 > 4) val2--; - break; - case SC_ONEHAND: - case SC_TWOHANDQUICKEN: - val2 = 300; - if (val1 > 10) //For boss casted skills [Skotlex] - val2 += 20*(val1-10); - break; - case SC_MERC_QUICKEN: - val2 = 300; - break; -#ifndef RENEWAL - case SC_SPEARQUICKEN: - val2 = 200+10*val1; - break; -#endif - case SC_DANCING: - //val1 : Skill ID + LV - //val2 : Skill Group of the Dance. - //val3 : Brings the skill_lv (merged into val1 here) - //val4 : Partner - if (val1 == CG_MOONLIT) - clif_status_change(bl,SI_MOONLIT,1,tick,0, 0, 0); - val1|= (val3<<16); - val3 = tick/1000; //Tick duration - tick_time = 1000; // [GodLesZ] tick time - break; - case SC_LONGING: - val2 = 500-100*val1; //Aspd penalty. - break; - case SC_EXPLOSIONSPIRITS: - val2 = 75 + 25*val1; //Cri bonus - break; - - case SC_ASPDPOTION0: - case SC_ASPDPOTION1: - case SC_ASPDPOTION2: - case SC_ASPDPOTION3: - val2 = 50*(2+type-SC_ASPDPOTION0); - break; - - case SC_WEDDING: - case SC_XMAS: - case SC_SUMMER: - if (!vd) return 0; - //Store previous values as they could be removed. - val1 = vd->class_; - val2 = vd->weapon; - val3 = vd->shield; - val4 = vd->cloth_color; - unit_stop_attack(bl); - clif_changelook(bl,LOOK_WEAPON,0); - clif_changelook(bl,LOOK_SHIELD,0); - clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER); - 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) - { - clif_changestatus(sd,SP_MANNER,sd->status.manner); - clif_updatestatus(sd,SP_MANNER); - } - break; - - case SC_STONE: - val3 = tick/1000; //Petrified HP-damage iterations. - if(val3 < 1) val3 = 1; - tick = val4; //Petrifying time. - tick = max(tick, 1000); //Min time - calc_flag = 0; //Actual status changes take effect on petrified state. - break; - - case SC_DPOISON: - //Lose 10/15% of your life as long as it doesn't brings life below 25% - if (status->hp > status->max_hp>>2) { - int diff = status->max_hp*(bl->type==BL_PC?10:15)/100; - if (status->hp - diff < status->max_hp>>2) - diff = status->hp - (status->max_hp>>2); - if( val2 && bl->type == BL_MOB ) { - struct block_list* src = map_id2bl(val2); - if( src ) - mob_log_damage((TBL_MOB*)bl,src,diff); - } - status_zap(bl, diff, 0); - } - // fall through - case SC_POISON: - val3 = tick/1000; //Damage iterations - if(val3 < 1) val3 = 1; - 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; - else - val4 = (type == SC_DPOISON) ? 3 + status->max_hp/100 : 3 + status->max_hp/200; - - break; - case SC_CONFUSION: - clif_emotion(bl,E_WHAT); - break; - case SC_BLEEDING: - val4 = tick/10000; - if (!val4) val4 = 1; - tick_time = 10000; // [GodLesZ] tick time - break; - case SC_S_LIFEPOTION: - case SC_L_LIFEPOTION: - if( val1 == 0 ) return 0; - // val1 = heal percent/amout - // val2 = seconds between heals - // val4 = total of heals - if( val2 < 1 ) val2 = 1; - if( (val4 = tick/(val2 * 1000)) < 1 ) - val4 = 1; - tick_time = val2 * 1000; // [GodLesZ] tick time - break; - case SC_BOSSMAPINFO: - if( sd != NULL ) - { - struct mob_data *boss_md = map_getmob_boss(bl->m); // Search for Boss on this Map - if( boss_md == NULL || boss_md->bl.prev == NULL ) - { // No MVP on this map - MVP is dead - clif_bossmapinfo(sd->fd, boss_md, 1); - return 0; // No need to start SC - } - val1 = boss_md->bl.id; - if( (val4 = tick/1000) < 1 ) - val4 = 1; - tick_time = 1000; // [GodLesZ] tick time - } - break; - case SC_HIDING: - val2 = tick/1000; - tick_time = 1000; // [GodLesZ] tick time - val3 = 0; // unused, previously speed adjustment - val4 = val1+3; //Seconds before SP substraction happen. - break; - case SC_CHASEWALK: - val2 = tick>0?tick:10000; //Interval at which SP is drained. - val3 = 35 - 5 * val1; //Speed adjustment. - if (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE) - val3 -= 40; - val4 = 10+val1*2; //SP cost. - if (map_flag_gvg(bl->m) || map[bl->m].flag.battleground) val4 *= 5; - break; - case SC_CLOAKING: - if (!sd) //Monsters should be able to walk with no penalties. [Skotlex] - val1 = 10; - tick_time = val2 = tick>0?tick:60000; //SP consumption rate. - tick = -1; // duration sent to the client should be infinite - val3 = 0; // unused, previously walk speed adjustment - //val4&1 signals the presence of a wall. - //val4&2 makes cloak not end on normal attacks [Skotlex] - //val4&4 makes cloak not end on using skills - if (bl->type == BL_PC || (bl->type == BL_MOB && ((TBL_MOB*)bl)->special_state.clone) ) //Standard cloaking. - val4 |= battle_config.pc_cloak_check_type&7; - else - val4 |= battle_config.monster_cloak_check_type&7; - break; - case SC_SIGHT: /* splash status */ - case SC_RUWACH: - case SC_SIGHTBLASTER: - val3 = skill_get_splash(val2, val1); //Val2 should bring the skill-id. - val2 = tick/250; - tick_time = 10; // [GodLesZ] tick time - break; - - //Permanent effects. - case SC_AETERNA: - case SC_MODECHANGE: - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_BROKENWEAPON: - case SC_BROKENARMOR: - case SC_READYSTORM: - case SC_READYDOWN: - case SC_READYCOUNTER: - case SC_READYTURN: - case SC_DODGE: - case SC_PUSH_CART: - tick = -1; - break; - - case SC_AUTOGUARD: - if( !(flag&1) ) - { - struct map_session_data *tsd; - int i,t; - for( i = val2 = 0; i < val1; i++) - { - t = 5-(i>>1); - val2 += (t < 0)? 1:t; - } - - if( bl->type&(BL_PC|BL_MER) ) - { - if( sd ) - { - for( i = 0; i < 5; i++ ) - { - if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) ) - status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1); - } - } - else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) ) - status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1); - } - } - break; - - case SC_DEFENDER: - if (!(flag&1)) - { - val2 = 5 + 15*val1; //Damage reduction - val3 = 0; // unused, previously speed adjustment - val4 = 250 - 50*val1; //Aspd adjustment - - if (sd) - { - struct map_session_data *tsd; - int i; - for (i = 0; i < 5; i++) - { //See if there are devoted characters, and pass the status to them. [Skotlex] - if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) - status_change_start(&tsd->bl,type,10000,val1,5+val1*5,val3,val4,tick,1); - } - } - } - break; - - case SC_TENSIONRELAX: - if (sd) { - pc_setsit(sd); - clif_sitting(&sd->bl); - } - val2 = 12; //SP cost - val4 = 10000; //Decrease at 10secs intervals. - val3 = tick/val4; - tick = -1; // duration sent to the client should be infinite - tick_time = val4; // [GodLesZ] tick time - break; - case SC_PARRYING: - val2 = 20 + val1*3; //Block Chance - break; - - case SC_WINDWALK: - val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5 - break; - - case SC_JOINTBEAT: - if( val2&BREAK_NECK ) - sc_start(bl,SC_BLEEDING,100,val1,skill_get_time2(status_sc2skill(type),val1)); - break; - - case SC_BERSERK: - if (!sc->data[SC_ENDURE] || !sc->data[SC_ENDURE]->val4) - sc_start4(bl, SC_ENDURE, 100,10,0,0,2, tick); - case SC__BLOODYLUST: - //HP healing is performing after the calc_status call. - //Val2 holds HP penalty - 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_time = val4; // [GodLesZ] tick time - break; - - case SC_GOSPEL: - if(val4 == BCT_SELF) { // self effect - val2 = tick/10000; - tick_time = 10000; // [GodLesZ] tick time - status_change_clear_buffs(bl,3); //Remove buffs/debuffs - } - break; - - case SC_MARIONETTE: - { - int stat; - - val3 = 0; - val4 = 0; - stat = ( sd ? sd->status.str : status_get_base_status(bl)->str ) / 2; val3 |= cap_value(stat,0,0xFF)<<16; - stat = ( sd ? sd->status.agi : status_get_base_status(bl)->agi ) / 2; val3 |= cap_value(stat,0,0xFF)<<8; - stat = ( sd ? sd->status.vit : status_get_base_status(bl)->vit ) / 2; val3 |= cap_value(stat,0,0xFF); - stat = ( sd ? sd->status.int_: status_get_base_status(bl)->int_) / 2; val4 |= cap_value(stat,0,0xFF)<<16; - stat = ( sd ? sd->status.dex : status_get_base_status(bl)->dex ) / 2; val4 |= cap_value(stat,0,0xFF)<<8; - stat = ( sd ? sd->status.luk : status_get_base_status(bl)->luk ) / 2; val4 |= cap_value(stat,0,0xFF); - break; - } - case SC_MARIONETTE2: - { - int stat,max_stat; - // fetch caster information - struct block_list *pbl = map_id2bl(val1); - struct status_change *psc = pbl?status_get_sc(pbl):NULL; - struct status_change_entry *psce = psc?psc->data[SC_MARIONETTE]:NULL; - // fetch target's stats - struct status_data* status = status_get_status_data(bl); // battle status - - if (!psce) - return 0; - - val3 = 0; - val4 = 0; - max_stat = battle_config.max_parameter; //Cap to 99 (default) - stat = (psce->val3 >>16)&0xFF; stat = min(stat, max_stat - status->str ); val3 |= cap_value(stat,0,0xFF)<<16; - stat = (psce->val3 >> 8)&0xFF; stat = min(stat, max_stat - status->agi ); val3 |= cap_value(stat,0,0xFF)<<8; - stat = (psce->val3 >> 0)&0xFF; stat = min(stat, max_stat - status->vit ); val3 |= cap_value(stat,0,0xFF); - stat = (psce->val4 >>16)&0xFF; stat = min(stat, max_stat - status->int_); val4 |= cap_value(stat,0,0xFF)<<16; - stat = (psce->val4 >> 8)&0xFF; stat = min(stat, max_stat - status->dex ); val4 |= cap_value(stat,0,0xFF)<<8; - stat = (psce->val4 >> 0)&0xFF; stat = min(stat, max_stat - status->luk ); val4 |= cap_value(stat,0,0xFF); - break; - } - case SC_REJECTSWORD: - val2 = 15*val1; //Reflect chance - val3 = 3; //Reflections - tick = -1; - break; - - case SC_MEMORIZE: - val2 = 5; //Memorized casts. - tick = -1; - break; - - case SC_GRAVITATION: - val2 = 50*val1; //aspd reduction - break; - - case SC_REGENERATION: - if (val1 == 1) - val2 = 2; - else - val2 = val1; //HP Regerenation rate: 200% 200% 300% - val3 = val1; //SP Regeneration Rate: 100% 200% 300% - //if val4 comes set, this blocks regen rather than increase it. - break; - - case SC_DEVOTION: - { - struct block_list *d_bl; - struct status_change *d_sc; - - if( (d_bl = map_id2bl(val1)) && (d_sc = status_get_sc(d_bl)) && d_sc->count ) - { // Inherits Status From Source - const enum sc_type types[] = { SC_AUTOGUARD, SC_DEFENDER, SC_REFLECTSHIELD, SC_ENDURE }; - enum sc_type type2; - int i = (map_flag_gvg(bl->m) || map[bl->m].flag.battleground)?2:3; - while( i >= 0 ) - { - type2 = types[i]; - if( d_sc->data[type2] ) - sc_start(bl, type2, 100, d_sc->data[type2]->val1, skill_get_time(status_sc2skill(type2),d_sc->data[type2]->val1)); - i--; - } - } - break; - } - - case SC_COMA: //Coma. Sends a char to 1HP. If val2, do not zap sp - if( val3 && bl->type == BL_MOB ) { - struct block_list* src = map_id2bl(val3); - if( src ) - mob_log_damage((TBL_MOB*)bl,src,status->hp - 1); - } - status_zap(bl, status->hp-1, val2?0:status->sp); - return 1; - break; - case SC_CLOSECONFINE2: - { - struct block_list *src = val2?map_id2bl(val2):NULL; - struct status_change *sc2 = src?status_get_sc(src):NULL; - struct status_change_entry *sce2 = sc2?sc2->data[SC_CLOSECONFINE]:NULL; - if (src && sc2) { - if (!sce2) //Start lock on caster. - sc_start4(src,SC_CLOSECONFINE,100,val1,1,0,0,tick+1000); - else { //Increase count of locked enemies and refresh time. - (sce2->val2)++; - delete_timer(sce2->timer, status_change_timer); - sce2->timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE); - } - } else //Status failed. - return 0; - } - break; - case SC_KAITE: - val2 = 1+val1/5; //Number of bounces: 1 + skill_lv/5 - break; - case SC_KAUPE: - switch (val1) { - case 3: //33*3 + 1 -> 100% - val2++; - case 1: - case 2: //33, 66% - val2 += 33*val1; - val3 = 1; //Dodge 1 attack total. - break; - default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex] - val2 = 100; - val3 = val1-2; - break; - } - break; - - case SC_COMBO: { - //val1: Skill ID - //val2: When given, target (for autotargetting skills) - //val3: When set, this combo time should NOT delay attack/movement - //val3: TK: Last used kick - //val4: TK: Combo time - struct unit_data *ud = unit_bl2ud(bl); - if (ud && !val3) { - tick += 300 * battle_config.combo_delay_rate/100; - ud->attackabletime = gettick()+tick; - unit_set_walkdelay(bl, gettick(), tick, 1); - } - val3 = 0; - val4 = tick; - } - break; - case SC_EARTHSCROLL: - val2 = 11-val1; //Chance to consume: 11-skill_lv% - break; - case SC_RUN: - val4 = gettick(); //Store time at which you started running. - tick = -1; - break; - case SC_KAAHI: - val2 = 200*val1; //HP heal - val3 = 5*val1; //SP cost - val4 = INVALID_TIMER; //Kaahi Timer. - break; - case SC_BLESSING: - if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) - val2 = val1; - else - val2 = 0; //0 -> Half stat. - break; - case SC_TRICKDEAD: - if (vd) vd->dead_sit = 1; - tick = -1; - break; - case SC_CONCENTRATE: - val2 = 2 + val1; - if (sd) { //Store the card-bonus data that should not count in the % - val3 = sd->param_bonus[1]; //Agi - val4 = sd->param_bonus[4]; //Dex - } else { - val3 = val4 = 0; - } - break; - case SC_MAXOVERTHRUST: - val2 = 20*val1; //Power increase - break; - case SC_OVERTHRUST: - //val2 holds if it was casted on self, or is bonus received from others - val3 = 5*val1; //Power increase - if(sd && pc_checkskill(sd,BS_HILTBINDING)>0) - tick += tick / 10; - break; - case SC_ADRENALINE2: - case SC_ADRENALINE: - val3 = (val2) ? 300 : 200; // aspd increase - case SC_WEAPONPERFECTION: - if(sd && pc_checkskill(sd,BS_HILTBINDING)>0) - tick += tick / 10; - break; - case SC_CONCENTRATION: - val2 = 5*val1; //Batk/Watk Increase - val3 = 10*val1; //Hit Increase - val4 = 5*val1; //Def reduction - break; - case SC_ANGELUS: - val2 = 5*val1; //def increase - break; - case SC_IMPOSITIO: - val2 = 5*val1; //watk increase - break; - case SC_MELTDOWN: - val2 = 100*val1; //Chance to break weapon - val3 = 70*val1; //Change to break armor - break; - case SC_TRUESIGHT: - val2 = 10*val1; //Critical increase - val3 = 3*val1; //Hit increase - break; - case SC_SUN_COMFORT: - val2 = (status_get_lv(bl) + status->dex + status->luk)/2; //def increase - break; - case SC_MOON_COMFORT: - val2 = (status_get_lv(bl) + status->dex + status->luk)/10; //flee increase - break; - case SC_STAR_COMFORT: - val2 = (status_get_lv(bl) + status->dex + status->luk); //Aspd increase - break; - case SC_QUAGMIRE: - val2 = (sd?5:10)*val1; //Agi/Dex decrease. - break; - - // gs_something1 [Vicious] - case SC_GATLINGFEVER: - val2 = 20*val1; //Aspd increase - val3 = 20+10*val1; //Batk increase - val4 = 5*val1; //Flee decrease - break; - - case SC_FLING: - if (bl->type == BL_PC) - val2 = 0; //No armor reduction to players. - else - val2 = 5*val1; //Def reduction - val3 = 5*val1; //Def2 reduction - break; - case SC_PROVOKE: - //val2 signals autoprovoke. - val3 = 2+3*val1; //Atk increase - val4 = 5+5*val1; //Def reduction. - break; - case SC_AVOID: - //val2 = 10*val1; //Speed change rate. - break; - case SC_DEFENCE: - val2 = 2*val1; //Def bonus - break; - case SC_BLOODLUST: - val2 = 20+10*val1; //Atk rate change. - val3 = 3*val1; //Leech chance - val4 = 20; //Leech percent - break; - case SC_FLEET: - val2 = 30*val1; //Aspd change - val3 = 5+5*val1; //bAtk/wAtk rate change - break; - case SC_MINDBREAKER: - val2 = 20*val1; //matk increase. - val3 = 12*val1; //mdef2 reduction. - break; - case SC_SKA: - val2 = tick/1000; - val3 = rnd()%100; //Def changes randomly every second... - tick_time = 1000; // [GodLesZ] tick time - break; - case SC_JAILED: - //Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time. - tick = val1>0?1000:250; - if (sd) - { - if (sd->mapindex != val2) - { - int pos = (bl->x&0xFFFF)|(bl->y<<16), //Current Coordinates - map = sd->mapindex; //Current Map - //1. Place in Jail (val2 -> Jail Map, val3 -> x, val4 -> y - pc_setpos(sd,(unsigned short)val2,val3,val4, CLR_TELEPORT); - //2. Set restore point (val3 -> return map, val4 return coords - val3 = map; - val4 = pos; - } else if (!val3 || val3 == sd->mapindex) { //Use save point. - val3 = sd->status.save_point.map; - val4 = (sd->status.save_point.x&0xFFFF) - |(sd->status.save_point.y<<16); - } - } - break; - case SC_UTSUSEMI: - val2=(val1+1)/2; // number of hits blocked - val3=skill_get_blewcount(NJ_UTSUSEMI, val1); //knockback value. - break; - case SC_BUNSINJYUTSU: - val2=(val1+1)/2; // number of hits blocked - break; - case SC_CHANGE: - val2= 30*val1; //Vit increase - val3= 20*val1; //Int increase - break; - case SC_SWOO: - if(status->mode&MD_BOSS) - tick /= 5; //TODO: Reduce skill's duration. But for how long? - break; - case SC_SPIDERWEB: - if( bl->type == BL_PC ) - tick /= 2; - break; - case SC_ARMOR: - //NPC_DEFENDER: - val2 = 80; //Damage reduction - //Attack requirements to be blocked: - val3 = BF_LONG; //Range - val4 = BF_WEAPON|BF_MISC; //Type - break; - case SC_ENCHANTARMS: - //end previous enchants - skill_enchant_elemental_end(bl,type); - //Make sure the received element is valid. - if (val2 >= ELE_MAX) - val2 = val2%ELE_MAX; - else if (val2 < 0) - val2 = rnd()%ELE_MAX; - break; - case SC_CRITICALWOUND: - val2 = 20*val1; //Heal effectiveness decrease - break; - case SC_MAGICMIRROR: - case SC_SLOWCAST: - val2 = 20*val1; //Magic reflection/cast rate - break; - - case SC_ARMORCHANGE: - if (val2 == NPC_ANTIMAGIC) - { //Boost mdef - val2 =-20; - val3 = 20; - } else { //Boost def - val2 = 20; - val3 =-20; - } - val2*=val1; //20% per level - val3*=val1; - break; - case SC_EXPBOOST: - case SC_JEXPBOOST: - if (val1 < 0) - val1 = 0; - break; - case SC_INCFLEE2: - case SC_INCCRI: - val2 = val1*10; //Actual boost (since 100% = 1000) - break; - case SC_SUFFRAGIUM: - val2 = 15 * val1; //Speed cast decrease - break; - case SC_INCHEALRATE: - if (val1 < 1) - val1 = 1; - break; - case SC_HALLUCINATION: - val2 = 5+val1; //Factor by which displayed damage is increased by - break; - case SC_DOUBLECAST: - val2 = 30+10*val1; //Trigger rate - break; - case SC_KAIZEL: - val2 = 10*val1; //% of life to be revived with - break; - // case SC_ARMOR_ELEMENT: - // case SC_ARMOR_RESIST: - // Mod your resistance against elements: - // val1 = water | val2 = earth | val3 = fire | val4 = wind - // break; - //case ????: - //Place here SCs that have no SCB_* data, no skill associated, no ICON - //associated, and yet are not wrong/unknown. [Skotlex] - //break; - - case SC_MERC_FLEEUP: - case SC_MERC_ATKUP: - case SC_MERC_HITUP: - val2 = 15 * val1; - break; - case SC_MERC_HPUP: - case SC_MERC_SPUP: - val2 = 5 * val1; - break; - case SC_REBIRTH: - val2 = 20*val1; //% of life to be revived with - break; - - case SC_MANU_DEF: - case SC_MANU_ATK: - case SC_MANU_MATK: - val2 = 1; // Manuk group - break; - case SC_SPL_DEF: - case SC_SPL_ATK: - 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: - status_change_start(bl,SC_BLIND,10000,val1,0,0,0,30000,11); // Blind status that last for 30 seconds - val4 = tick / 3000; - tick_time = 3000; // [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 = 90 + val1 * 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, INVALID_TIMER); - status_change_end(bl, SC_FREEZING, INVALID_TIMER); - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_STONE, INVALID_TIMER); - break; - case SC_FREEZING: - status_change_end(bl, SC_BURNING, INVALID_TIMER); - break; - case SC_READING_SB: - // val2 = sp reduction per second - tick_time = 5000; // [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: - case SC_MEIKYOUSISUI: - val4 = tick / 1000; - if( val4 < 1 ) - val4 = 1; - tick_time = 1000; // [GodLesZ] tick time - break; - case SC_CAMOUFLAGE: - val4 = tick/1000; - 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) ) pc_setriding(sd, 0); - if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON); - if( pc_iswug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUG); - if( pc_isridingwug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUGRIDER); - 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; - // bypasses coating protection and MADO - sc_start(bl,SC_STRIPWEAPON,100,val1,tick); - sc_start(bl,SC_STRIPSHIELD,100,val1,tick); - break; - 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, INVALID_TIMER); - status_change_end(bl, SC_FREEZING, INVALID_TIMER); - status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER); - break; - case SC_STRIKING: - val1 = 6 - val1;//spcost = 6 - level (lvl1:5 ... lvl 5: 1) - 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_VACUUM_EXTREME: - tick -= (status->str / 20) * 1000; - val4 = val3 = tick / 100; - tick_time = 100; // [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 = 5 + 5 * 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 = 20 + 5 * val1; // Flee reduction. - val3 = 15 + 5 * val1; // ASPD reduction. - if( sd && rand()%100 < val1 ){ // (Skill Lv) % - val4 = 1; // reduce walk speed by half. - if( pc_isriding(sd) ) pc_setriding(sd, 0); - if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON); - } - break; - case SC_GLOOMYDAY_SK: - // Random number between [15 ~ (Voice Lesson Skill Level x 5) + (Skill Level x 10)] %. - val2 = 15 + rand()%( (sd?pc_checkskill(sd, WM_LESSON)*5:0) + val1*10 ); - 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 = -1; //endless duration in the client - tick_time = 6000; // [GodLesZ] tick time - val_flag |= 1|2|4; - break; - case SC_EXEEDBREAK: - val1 *= 150; // 150 * skill_lv - if( sd && sd->inventory_data[sd->equip_index[EQI_HAND_R]] ) { // Chars. - val1 += (sd->inventory_data[sd->equip_index[EQI_HAND_R]]->weight/10 * sd->inventory_data[sd->equip_index[EQI_HAND_R]]->wlv * status_get_lv(bl) / 100); - val1 += 15 * (sd ? sd->status.job_level:50) + 100; - } - 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: // [(Job Level / 2) + (40 + 5 * Skill Level)] % - val1 = (sd?sd->status.job_level:2)/2 + 40 + 5 * val1; - val_flag |= 1; - break; - case SC_RAISINGDRAGON: - val3 = tick / 5000; - tick_time = 5000; // [GodLesZ] tick time - break; - case SC_GT_CHANGE: - {// take note there is no def increase as skill desc says. [malufett] - struct block_list * src; - val3 = status->agi * val1 / 60; // ASPD increase: [(Target AGI x Skill Level) / 60] % - if( (src = map_id2bl(val2)) ) - val4 = ( 200/status_get_int(src) ) * val1;// MDEF decrease: MDEF [(200 / Caster INT) x Skill Level] - } - break; - case SC_GT_REVITALIZE: - {// take note there is no vit,aspd,speed increase as skill desc says. [malufett] - struct block_list * src; - val3 = val1 * 30 + 150; // Natural HP recovery increase: [(Skill Level x 30) + 50] % - if( (src = map_id2bl(val2)) ) // the stat def is not shown in the status window and it is process differently - val4 = ( status_get_vit(src)/4 ) * val1; // STAT DEF increase: [(Caster VIT / 4) x Skill Level] - } - break; - case SC_PYROTECHNIC_OPTION: - 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; - 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: - val_flag |= 1|2; - break; - case SC_WIND_STEP_OPTION: - val2 = 50; // % Increase speed and flee. - break; - case SC_BLAST_OPTION: - val2 = 20; - val3 = ELE_WIND; - 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 = 50; - 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; - case SC_KYOUGAKU: - val2 = 2*val1 + rand()%val1; - clif_status_change(bl,SI_ACTIVE_MONSTER_TRANSFORM,1,0,1002,0,0); - break; - case SC_KAGEMUSYA: - val3 = val1 * 2; - case SC_IZAYOI: - val2 = tick/1000; - tick_time = 1000; - break; - case SC_ZANGETSU: - if( (status_get_hp(bl)+status_get_sp(bl)) % 2 == 0) - val2 = status_get_lv(bl) / 2 + 50; - else - val2 -= 50; - break; - case SC_GENSOU: - { - int hp = status_get_hp(bl), lv = 5; - short per = 100 / (status_get_max_hp(bl) / hp); - - if( per <= 15 ) - lv = 1; - else if( per <= 30 ) - lv = 2; - else if( per <= 50 ) - lv = 3; - else if( per <= 75 ) - lv = 4; - if( hp % 2 == 0) - status_heal(bl, hp * (6-lv) * 4 / 100, status_get_sp(bl) * (6-lv) * 3 / 100, 1); - else - status_zap(bl, hp * (lv*4) / 100, status_get_sp(bl) * (lv*3) / 100); - } - break; - case SC_ANGRIFFS_MODUS: - val2 = 50 + 20 * val1; //atk bonus - val3 = 40 + 20 * val1; // Flee reduction. - val4 = tick/1000; // hp/sp reduction timer - tick_time = 1000; - break; - case SC_GOLDENE_FERSE: - val2 = 10 + 10*val1; //max hp bonus - val3 = 6 + 4 * val1; // Aspd Bonus - val4 = 2 + 2 * val1; // Chance of holy attack - break; - case SC_OVERED_BOOST: - val2 = 300 + 40*val1; //flee bonus - val3 = 179 + 2*val1; //aspd bonus - break; - case SC_GRANITIC_ARMOR: - val2 = 2*val1; //dmg reduction - val3 = 6*val1; //dmg on status end - break; - case SC_MAGMA_FLOW: - val2 = 3*val1; //activation chance - break; - case SC_PYROCLASTIC: - val2 += 10*val1; //atk bonus - break; - case SC_PARALYSIS: //[Lighta] need real info - val2 = 2*val1; //def reduction - val3 = 500*val1; //varcast augmentation - break; - case SC_PAIN_KILLER: //[Lighta] need real info - val2 = 2*val1; //aspd reduction % - val3 = 2*val1; //dmg reduction % - if(sc->data[SC_PARALYSIS]) - sc_start(bl, SC_ENDURE, 100, val1, tick); //start endure for same duration - break; - case SC_STYLE_CHANGE: //[Lighta] need real info - tick = -1; - if(val2 == MH_MD_FIGHTING) val2 = MH_MD_GRAPPLING; - else val2 = MH_MD_FIGHTING; - break; - default: - if( calc_flag == SCB_NONE && StatusSkillChangeTable[type] == 0 && StatusIconChangeTable[type] == 0 ) - { //Status change with no calc, no icon, and no skill associated...? - ShowError("UnknownStatusChange [%d]\n", type); - return 0; - } - } - else //Special considerations when loading SC data. - switch( type ) - { - case SC_WEDDING: - case SC_XMAS: - case SC_SUMMER: - clif_changelook(bl,LOOK_WEAPON,0); - clif_changelook(bl,LOOK_SHIELD,0); - clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER); - clif_changelook(bl,LOOK_CLOTHES_COLOR,val4); - break; - case SC_KAAHI: - val4 = INVALID_TIMER; - break; - } - - //Those that make you stop attacking/walking.... - switch (type) { - case SC_FREEZE: - case SC_STUN: - case SC_SLEEP: - case SC_STONE: - case SC_DEEPSLEEP: - if (sd && pc_issit(sd)) //Avoid sprite sync problems. - pc_setstand(sd); - case SC_TRICKDEAD: - status_change_end(bl, SC_DANCING, INVALID_TIMER); - // Cancel cast when get status [LuzZza] - if (battle_config.sc_castcancel&bl->type) - unit_skillcastcancel(bl, 0); - case SC_WHITEIMPRISON: - unit_stop_attack(bl); - case SC_STOP: - case SC_CONFUSION: - case SC_CLOSECONFINE: - case SC_CLOSECONFINE2: - case SC_ANKLE: - case SC_SPIDERWEB: - case SC_ELECTRICSHOCKER: - case SC_BITE: - case SC_THORNSTRAP: - case SC__MANHOLE: - case SC_CRYSTALIZE: - case SC_CURSEDCIRCLE_ATKER: - case SC_CURSEDCIRCLE_TARGET: - case SC_FEAR: - case SC_NETHERWORLD: - case SC_MEIKYOUSISUI: - case SC_KYOUGAKU: - case SC_PARALYSIS: - unit_stop_walking(bl,1); - break; - case SC_HIDING: - case SC_CLOAKING: - case SC_CLOAKINGEXCEED: - case SC_CHASEWALK: - case SC_WEIGHT90: - case SC_CAMOUFLAGE: - case SC_VOICEOFSIREN: - unit_stop_attack(bl); - break; - case SC_SILENCE: - if (battle_config.sc_castcancel&bl->type) - unit_skillcastcancel(bl, 0); - break; - } - - // Set option as needed. - opt_flag = 1; - 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_DEEPSLEEP: opt_flag = 0; - 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; - case SC_CRYSTALIZE: sc->opt1 = OPT1_CRYSTALIZE; break; - //OPT2 - case SC_POISON: sc->opt2 |= OPT2_POISON; break; - case SC_CURSE: sc->opt2 |= OPT2_CURSE; break; - case SC_SILENCE: sc->opt2 |= OPT2_SILENCE; break; - - case SC_SIGNUMCRUCIS: - sc->opt2 |= OPT2_SIGNUMCRUCIS; - break; - - case SC_BLIND: sc->opt2 |= OPT2_BLIND; break; - case SC_ANGELUS: sc->opt2 |= OPT2_ANGELUS; break; - case SC_BLEEDING: sc->opt2 |= OPT2_BLEEDING; break; - case SC_DPOISON: sc->opt2 |= OPT2_DPOISON; break; - //OPT3 - case SC_TWOHANDQUICKEN: - case SC_ONEHAND: - case SC_SPEARQUICKEN: - case SC_CONCENTRATION: - case SC_MERC_QUICKEN: - sc->opt3 |= OPT3_QUICKEN; - opt_flag = 0; - break; - case SC_MAXOVERTHRUST: - case SC_OVERTHRUST: - case SC_SWOO: //Why does it shares the same opt as Overthrust? Perhaps we'll never know... - sc->opt3 |= OPT3_OVERTHRUST; - opt_flag = 0; - break; - case SC_ENERGYCOAT: - case SC_SKE: - sc->opt3 |= OPT3_ENERGYCOAT; - opt_flag = 0; - break; - case SC_INCATKRATE: - //Simulate Explosion Spirits effect for NPC_POWERUP [Skotlex] - if (bl->type != BL_MOB) { - opt_flag = 0; - break; - } - case SC_EXPLOSIONSPIRITS: - sc->opt3 |= OPT3_EXPLOSIONSPIRITS; - opt_flag = 0; - break; - case SC_STEELBODY: - case SC_SKA: - sc->opt3 |= OPT3_STEELBODY; - opt_flag = 0; - break; - case SC_BLADESTOP: - sc->opt3 |= OPT3_BLADESTOP; - opt_flag = 0; - break; - case SC_AURABLADE: - sc->opt3 |= OPT3_AURABLADE; - opt_flag = 0; - break; - case SC_BERSERK: - opt_flag = 0; -// case SC__BLOODYLUST: - sc->opt3 |= OPT3_BERSERK; - break; -// case ???: // doesn't seem to do anything -// sc->opt3 |= OPT3_LIGHTBLADE; -// opt_flag = 0; -// break; - case SC_DANCING: - if ((val1&0xFFFF) == CG_MOONLIT) - sc->opt3 |= OPT3_MOONLIT; - opt_flag = 0; - break; - case SC_MARIONETTE: - case SC_MARIONETTE2: - sc->opt3 |= OPT3_MARIONETTE; - opt_flag = 0; - break; - case SC_ASSUMPTIO: - sc->opt3 |= OPT3_ASSUMPTIO; - opt_flag = 0; - break; - case SC_WARM: //SG skills [Komurka] - sc->opt3 |= OPT3_WARM; - opt_flag = 0; - break; - case SC_KAITE: - sc->opt3 |= OPT3_KAITE; - opt_flag = 0; - break; - case SC_BUNSINJYUTSU: - sc->opt3 |= OPT3_BUNSIN; - opt_flag = 0; - break; - case SC_SPIRIT: - sc->opt3 |= OPT3_SOULLINK; - opt_flag = 0; - break; - case SC_CHANGEUNDEAD: - sc->opt3 |= OPT3_UNDEAD; - opt_flag = 0; - break; -// case ???: // from DA_CONTRACT (looks like biolab mobs aura) -// sc->opt3 |= OPT3_CONTRACT; -// opt_flag = 0; -// break; - //OPTION - case SC_HIDING: - sc->option |= OPTION_HIDE; - opt_flag = 2; - break; - case SC_CLOAKING: - case SC_CLOAKINGEXCEED: - case SC__INVISIBILITY: - sc->option |= OPTION_CLOAK; - opt_flag = 2; - break; - case SC_CHASEWALK: - sc->option |= OPTION_CHASEWALK|OPTION_CLOAK; - opt_flag = 2; - break; - case SC_SIGHT: - sc->option |= OPTION_SIGHT; - break; - case SC_RUWACH: - sc->option |= OPTION_RUWACH; - break; - case SC_WEDDING: - sc->option |= OPTION_WEDDING; - break; - case SC_XMAS: - sc->option |= OPTION_XMAS; - break; - case SC_SUMMER: - sc->option |= OPTION_SUMMER; - break; - case SC_ORCISH: - sc->option |= OPTION_ORCISH; - break; - case SC_FUSION: - sc->option |= OPTION_FLYING; - break; - default: - opt_flag = 0; - } - - //On Aegis, when turning on a status change, first goes the option packet, then the sc packet. - if(opt_flag) - clif_changeoption(bl); - - if (calc_flag&SCB_DYE) - { //Reset DYE color - if (vd && vd->cloth_color) - { - val4 = vd->cloth_color; - clif_changelook(bl,LOOK_CLOTHES_COLOR,0); - } - calc_flag&=~SCB_DYE; - } - - clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0); - - /** - * 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])) {// reuse old sc - if( sce->timer != INVALID_TIMER ) - delete_timer(sce->timer, status_change_timer); - sc_isnew = false; - } else {// new sc - ++(sc->count); - sce = sc->data[type] = ers_alloc(sc_data_ers, struct status_change_entry); - } - sce->val1 = val1; - sce->val2 = val2; - sce->val3 = val3; - sce->val4 = val4; - if (tick >= 0) - sce->timer = add_timer(gettick() + tick, status_change_timer, bl->id, type); - else - sce->timer = INVALID_TIMER; //Infinite duration - - if (calc_flag) - status_calc_bl(bl,calc_flag); - - if ( sc_isnew && StatusChangeStateTable[type] ) /* non-zero */ - status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],true); - - - if(sd && sd->pd) - pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing - - switch (type) { - case SC__BLOODYLUST: - case SC_BERSERK: - if (!(sce->val2)) { //don't heal if already set - status_heal(bl, status->max_hp, 0, 1); //Do not use percent_heal as this healing must override BERSERK's block. - status_set_sp(bl, 0, 0); //Damage all SP - } - sce->val2 = 5 * status->max_hp / 100; - break; - case SC_CHANGE: - status_percent_heal(bl, 100, 100); - break; - case SC_RUN: - { - struct unit_data *ud = unit_bl2ud(bl); - if( ud ) - ud->state.running = unit_run(bl); - } - break; - case SC_BOSSMAPINFO: - clif_bossmapinfo(sd->fd, map_id2boss(sce->val1), 0); // First Message - break; - case SC_MERC_HPUP: - status_percent_heal(bl, 100, 0); // Recover Full HP - break; - 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: - clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1); - break; - case TK_DOWNKICK: - clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1); - break; - case TK_TURNKICK: - clif_skill_nodamage(bl,bl,TK_READYTURN,1,1); - break; - case TK_COUNTER: - clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1); - break; - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - if (sd) - clif_skillinfo(sd,MO_EXTREMITYFIST, INF_SELF_SKILL); - break; - case TK_JUMPKICK: - if (sd) - clif_skillinfo(sd,TK_JUMPKICK, INF_SELF_SKILL); - break; - case MO_TRIPLEATTACK: - if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0) - clif_skillinfo(sd,SR_DRAGONCOMBO, INF_SELF_SKILL); - break; - case SR_FALLENEMPIRE: - if (sd){ - clif_skillinfo(sd,SR_GATEOFHELL, INF_SELF_SKILL); - clif_skillinfo(sd,SR_TIGERCANNON, INF_SELF_SKILL); - } - break; - } - break; - case SC_RAISINGDRAGON: - sce->val2 = status->max_hp / 100;// Officially tested its 1%hp drain. [Jobbie] - break; - } - - if( opt_flag&2 && sd && sd->touching_id ) - npc_touchnext_areanpc(sd,false); // run OnTouch_ on next char in range - - return 1; -} - -/*========================================== - * Ending all status except those listed. - * @TODO maybe usefull for dispel instead reseting a liste there. - * type: - * 0 - PC killed -> Place here statuses that do not dispel on death. - * 1 - If for some reason status_change_end decides to still keep the status when quitting. - * 2 - Do clif - * 3 - Do not remove some permanent/time-independent effects - *------------------------------------------*/ -int status_change_clear(struct block_list* bl, int type) -{ - struct status_change* sc; - int i; - - sc = status_get_sc(bl); - - if (!sc || !sc->count) - return 0; - - for(i = 0; i < SC_MAX; i++) - { - if(!sc->data[i]) - continue; - - if(type == 0) - switch (i) - { //Type 0: PC killed -> Place here statuses that do not dispel on death. - case SC_ELEMENTALCHANGE://Only when its Holy or Dark that it doesn't dispell on death - if( sc->data[i]->val2 != ELE_HOLY && sc->data[i]->val2 != ELE_DARK ) - break; - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_EDP: - case SC_MELTDOWN: - case SC_XMAS: - case SC_SUMMER: - case SC_NOCHAT: - case SC_FUSION: - case SC_EARTHSCROLL: - case SC_READYSTORM: - case SC_READYDOWN: - case SC_READYCOUNTER: - case SC_READYTURN: - case SC_DODGE: - case SC_JAILED: - case SC_EXPBOOST: - case SC_ITEMBOOST: - case SC_HELLPOWER: - case SC_JEXPBOOST: - case SC_AUTOTRADE: - 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_DEF_RATE: - case SC_MDEF_RATE: - case SC_INCHEALRATE: - case SC_INCFLEE2: - case SC_INCHIT: - case SC_ATKPOTION: - case SC_MATKPOTION: - case SC_S_LIFEPOTION: - case SC_L_LIFEPOTION: - case SC_PUSH_CART: - continue; - - } - - if( type == 3 ) - { - switch (i) - {// TODO: This list may be incomplete - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_NOCHAT: - case SC_PUSH_CART: - continue; - } - } - - status_change_end(bl, (sc_type)i, INVALID_TIMER); - - if( type == 1 && sc->data[i] ) - { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex] - (sc->count)--; - if (sc->data[i]->timer != INVALID_TIMER) - delete_timer(sc->data[i]->timer, status_change_timer); - ers_free(sc_data_ers, sc->data[i]); - sc->data[i] = NULL; - } - } - - sc->opt1 = 0; - sc->opt2 = 0; - sc->opt3 = 0; - sc->option &= OPTION_MASK; - - if( type == 0 || type == 2 ) - clif_changeoption(bl); - - return 1; -} - -/*========================================== - * Special condition we want to effectuate, check before ending a status. - *------------------------------------------*/ -int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const char* file, int line) -{ - struct map_session_data *sd; - struct status_change *sc; - struct status_change_entry *sce; - struct status_data *status; - struct view_data *vd; - int opt_flag=0, calc_flag; - - nullpo_ret(bl); - - sc = status_get_sc(bl); - status = status_get_status_data(bl); - - if(type < 0 || type >= SC_MAX || !sc || !(sce = sc->data[type])) - return 0; - - sd = BL_CAST(BL_PC,bl); - - if (sce->timer != tid && tid != INVALID_TIMER) - return 0; - - if (tid == INVALID_TIMER) { - 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); - if (sc->opt1) - switch (type) { - //"Ugly workaround" [Skotlex] - //delays status change ending so that a skill that sets opt1 fails to - //trigger when it also removed one - case SC_STONE: - sce->val3 = 0; //Petrify time counter. - case SC_FREEZE: - case SC_STUN: - case SC_SLEEP: - if (sce->val1) { - //Removing the 'level' shouldn't affect anything in the code - //since these SC are not affected by it, and it lets us know - //if we have already delayed this attack or not. - sce->val1 = 0; - sce->timer = add_timer(gettick()+10, status_change_timer, bl->id, type); - return 1; - } - } - } - - (sc->count)--; - - if ( StatusChangeStateTable[type] ) - status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],false); - - sc->data[type] = NULL; - - vd = status_get_viewdata(bl); - calc_flag = StatusChangeFlagTable[type]; - switch(type){ - case SC_GRANITIC_ARMOR:{ - int dammage = status->max_hp*sce->val3/100; - if(status->hp < dammage) //to not kill him - dammage = status->hp-1; - status_damage(NULL, bl, dammage,0,0,1); - break; - } - case SC_PYROCLASTIC: - if(bl->type == BL_PC) - skill_break_equip(bl,EQP_WEAPON,10000,BCT_SELF); - break; - case SC_WEDDING: - case SC_XMAS: - case SC_SUMMER: - if (!vd) break; - if (sd) - { //Load data from sd->status.* as the stored values could have changed. - //Must remove OPTION to prevent class being rechanged. - sc->option &= type==SC_WEDDING?~OPTION_WEDDING:type==SC_XMAS?~OPTION_XMAS:~OPTION_SUMMER; - clif_changeoption(&sd->bl); - status_set_viewdata(bl, sd->status.class_); - } else { - vd->class_ = sce->val1; - vd->weapon = sce->val2; - vd->shield = sce->val3; - vd->cloth_color = sce->val4; - } - clif_changelook(bl,LOOK_BASE,vd->class_); - clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); - clif_changelook(bl,LOOK_WEAPON,vd->weapon); - clif_changelook(bl,LOOK_SHIELD,vd->shield); - if(sd) clif_skillinfoblock(sd); - break; - case SC_RUN: - { - struct unit_data *ud = unit_bl2ud(bl); - bool begin_spurt = true; - if (ud) { - if(!ud->state.running) - begin_spurt = false; - ud->state.running = 0; - if (ud->walktimer != INVALID_TIMER) - unit_stop_walking(bl,1); - } - if (begin_spurt && sce->val1 >= 7 && - DIFF_TICK(gettick(), sce->val4) <= 1000 && - (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0)) - ) - sc_start(bl,SC_SPURT,100,sce->val1,skill_get_time2(status_sc2skill(type), sce->val1)); - } - break; - case SC_AUTOBERSERK: - if (sc->data[SC_PROVOKE] && sc->data[SC_PROVOKE]->val2 == 1) - status_change_end(bl, SC_PROVOKE, INVALID_TIMER); - break; - - case SC_ENDURE: - case SC_DEFENDER: - case SC_REFLECTSHIELD: - case SC_AUTOGUARD: - { - struct map_session_data *tsd; - if( bl->type == BL_PC ) - { // Clear Status from others - int i; - for( i = 0; i < 5; i++ ) - { - if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type] ) - status_change_end(&tsd->bl, type, INVALID_TIMER); - } - } - else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag ) - { // Clear Status from Master - tsd = ((TBL_MER*)bl)->master; - if( tsd && tsd->sc.data[type] ) - status_change_end(&tsd->bl, type, INVALID_TIMER); - } - } - break; - case SC_DEVOTION: - { - struct block_list *d_bl = map_id2bl(sce->val1); - if( d_bl ) - { - if( d_bl->type == BL_PC ) - ((TBL_PC*)d_bl)->devotion[sce->val2] = 0; - else if( d_bl->type == BL_MER ) - ((TBL_MER*)d_bl)->devotion_flag = 0; - clif_devotion(d_bl, NULL); - } - - status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER); - status_change_end(bl, SC_DEFENDER, INVALID_TIMER); - status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER); - status_change_end(bl, SC_ENDURE, INVALID_TIMER); - } - break; - - case SC_BLADESTOP: - if(sce->val4) - { - int tid = sce->val4; - struct block_list *tbl = map_id2bl(tid); - struct status_change *tsc = status_get_sc(tbl); - sce->val4 = 0; - if(tbl && tsc && tsc->data[SC_BLADESTOP]) - { - tsc->data[SC_BLADESTOP]->val4 = 0; - status_change_end(tbl, SC_BLADESTOP, INVALID_TIMER); - } - clif_bladestop(bl, tid, 0); - } - break; - case SC_DANCING: - { - const char* prevfile = "<unknown>"; - int prevline = 0; - struct map_session_data *dsd; - struct status_change_entry *dsc; - struct skill_unit_group *group; - - if( sd ) - { - if( sd->delunit_prevfile ) - {// initially this is NULL, when a character logs in - prevfile = sd->delunit_prevfile; - prevline = sd->delunit_prevline; - } - else - { - prevfile = "<none>"; - } - sd->delunit_prevfile = file; - sd->delunit_prevline = line; - } - - if(sce->val4 && sce->val4 != BCT_SELF && (dsd=map_id2sd(sce->val4))) - {// end status on partner as well - dsc = dsd->sc.data[SC_DANCING]; - if(dsc) { - - //This will prevent recursive loops. - dsc->val2 = dsc->val4 = 0; - - status_change_end(&dsd->bl, SC_DANCING, INVALID_TIMER); - } - } - - if(sce->val2) - {// erase associated land skill - group = skill_id2group(sce->val2); - - if( group == NULL ) - { - ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d, prev=%s:%d, from=%s:%d). Please report this! (#3504)\n", - sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid, - sd ? sd->status.char_id : 0, - mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y, - prevfile, prevline, - file, line); - } - - sce->val2 = 0; - skill_delunitgroup(group); - } - - if((sce->val1&0xFFFF) == CG_MOONLIT) - clif_status_change(bl,SI_MOONLIT,0,0,0,0,0); - - status_change_end(bl, SC_LONGING, INVALID_TIMER); - } - break; - case SC_NOCHAT: - if (sd && sd->status.manner < 0 && tid != INVALID_TIMER) - sd->status.manner = 0; - if (sd && tid == INVALID_TIMER) - { - clif_changestatus(sd,SP_MANNER,sd->status.manner); - clif_updatestatus(sd,SP_MANNER); - } - break; - case SC_SPLASHER: - { - struct block_list *src=map_id2bl(sce->val3); - if(src && tid != INVALID_TIMER) - skill_castend_damage_id(src, bl, sce->val2, sce->val1, gettick(), SD_LEVEL ); - } - break; - case SC_CLOSECONFINE2: - { - struct block_list *src = sce->val2?map_id2bl(sce->val2):NULL; - struct status_change *sc2 = src?status_get_sc(src):NULL; - if (src && sc2 && sc2->data[SC_CLOSECONFINE]) { - //If status was already ended, do nothing. - //Decrease count - if (--(sc2->data[SC_CLOSECONFINE]->val1) <= 0) //No more holds, free him up. - status_change_end(src, SC_CLOSECONFINE, INVALID_TIMER); - } - } - case SC_CLOSECONFINE: - if (sce->val2 > 0) { - //Caster has been unlocked... nearby chars need to be unlocked. - int range = 1 - +skill_get_range2(bl, status_sc2skill(type), sce->val1) - +skill_get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold.... - map_foreachinarea(status_change_timer_sub, - bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sce,type,gettick()); - } - break; - case SC_COMBO: - if( sd ) - switch (sce->val1) { - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - clif_skillinfo(sd, MO_EXTREMITYFIST, 0); - break; - case TK_JUMPKICK: - clif_skillinfo(sd, TK_JUMPKICK, 0); - break; - case MO_TRIPLEATTACK: - if (pc_checkskill(sd, SR_DRAGONCOMBO) > 0) - clif_skillinfo(sd, SR_DRAGONCOMBO, 0); - break; - case SR_FALLENEMPIRE: - clif_skillinfo(sd, SR_GATEOFHELL, 0); - clif_skillinfo(sd, SR_TIGERCANNON, 0); - break; - } - break; - - case SC_MARIONETTE: - case SC_MARIONETTE2: /// Marionette target - if (sce->val1) - { // check for partner and end their marionette status as well - enum sc_type type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE; - struct block_list *pbl = map_id2bl(sce->val1); - struct status_change* sc2 = pbl?status_get_sc(pbl):NULL; - - if (sc2 && sc2->data[type2]) - { - sc2->data[type2]->val1 = 0; - status_change_end(pbl, type2, INVALID_TIMER); - } - } - break; - - case SC_BERSERK: - case SC_SATURDAYNIGHTFEVER: - //If val2 is removed, no HP penalty (dispelled?) [Skotlex] - if (status->hp > 100 && sce->val2) - status_set_hp(bl, 100, 0); - if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2) - { - sc->data[SC_ENDURE]->val4 = 0; - status_change_end(bl, SC_ENDURE, INVALID_TIMER); - } - case SC__BLOODYLUST: - sc_start4(bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill_get_time(LK_BERSERK, sce->val1)); - if( type == SC_SATURDAYNIGHTFEVER ) //Sit down force of Saturday Night Fever has the duration of only 3 seconds. - sc_start(bl,SC_SITDOWN_FORCE,100,sce->val1,skill_get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1)); - break; - case SC_GOSPEL: - if (sce->val3) { //Clear the group. - struct skill_unit_group* group = skill_id2group(sce->val3); - sce->val3 = 0; - skill_delunitgroup(group); - } - break; - case SC_HERMODE: - if(sce->val3 == BCT_SELF) - skill_clear_unitgroup(bl); - break; - case SC_BASILICA: //Clear the skill area. [Skotlex] - skill_clear_unitgroup(bl); - break; - case SC_TRICKDEAD: - if (vd) vd->dead_sit = 0; - break; - case SC_WARM: - case SC__MANHOLE: - if (sce->val4) { //Clear the group. - struct skill_unit_group* group = skill_id2group(sce->val4); - sce->val4 = 0; - if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill_delunitgroup(group); - } - break; - case SC_KAAHI: - //Delete timer if it exists. - if (sce->val4 != INVALID_TIMER) - delete_timer(sce->val4,kaahi_heal_timer); - break; - case SC_JAILED: - if(tid == INVALID_TIMER) - break; - //natural expiration. - if(sd && sd->mapindex == sce->val2) - pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT); - break; //guess hes not in jail :P - case SC_CHANGE: - if (tid == INVALID_TIMER) - break; - // "lose almost all their HP and SP" on natural expiration. - status_set_hp(bl, 10, 0); - status_set_sp(bl, 10, 0); - break; - case SC_AUTOTRADE: - if (tid == INVALID_TIMER) - break; - // Note: vending/buying is closed by unit_remove_map, no - // need to do it here. - map_quit(sd); - // Because map_quit calls status_change_end with tid -1 - // from here it's not neccesary to continue - return 1; - break; - case SC_STOP: - if( sce->val2 ) - { - struct block_list* tbl = map_id2bl(sce->val2); - sce->val2 = 0; - if( tbl && (sc = status_get_sc(tbl)) && sc->data[SC_STOP] && sc->data[SC_STOP]->val2 == bl->id ) - status_change_end(tbl, SC_STOP, INVALID_TIMER); - } - break; - /** - * 3rd Stuff - **/ - case SC_MILLENNIUMSHIELD: - 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: - { - struct block_list* src = map_id2bl(sce->val2); - if( tid == -1 || !src) - break; // Terminated by Damage - status_fix_damage(src,bl,400*sce->val1,clif_damage(bl,bl,gettick(),0,0,400*sce->val1,0,0,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, INVALID_TIMER); - 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); - } - break; - case SC_NEUTRALBARRIER_MASTER: - case SC_STEALTHFIELD_MASTER: - if( sce->val2 ) { - struct skill_unit_group* group = skill_id2group(sce->val2); - sce->val2 = 0; - if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill_delunitgroup(group); - } - break; - case SC_BANDING: - if(sce->val4) { - struct skill_unit_group *group = skill_id2group(sce->val4); - sce->val4 = 0; - if( group ) /* might have been cleared before status ended, e.g. land protector */ - skill_delunitgroup(group); - } - break; - case SC_CURSEDCIRCLE_ATKER: - if( sce->val2 ) // used the default area size cause there is a chance the caster could knock back and can't clear the target. - map_foreachinrange(status_change_timer_sub, bl, battle_config.area_size,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, INVALID_TIMER); - while( i > 0 ) { - pc_addspiritball(sd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), 5); - --i; - } - } - break; - case SC_CURSEDCIRCLE_TARGET: - { - struct block_list *src = map_id2bl(sce->val2); - struct status_change *sc = status_get_sc(src); - if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] && --(sc->data[SC_CURSEDCIRCLE_ATKER]->val2) == 0 ){ - status_change_end(src, SC_CURSEDCIRCLE_ATKER, INVALID_TIMER); - clif_bladestop(bl, sce->val2, 0); - } - } - break; - case SC_BLOODSUCKER: - if( sce->val2 ){ - struct block_list *src = map_id2bl(sce->val2); - if(src){ - struct status_change *sc = status_get_sc(src); - sc->bs_counter--; - } - } - break; - case SC_VACUUM_EXTREME: - if(sc && sc->cant.move > 0) sc->cant.move--; - break; - case SC_KYOUGAKU: - clif_status_load(bl, SI_KYOUGAKU, 0); // Avoid client crash - clif_status_load(bl, SI_ACTIVE_MONSTER_TRANSFORM, 0); - break; - case SC_INTRAVISION: - calc_flag = SCB_ALL;/* required for overlapping */ - break; - } - - opt_flag = 1; - switch(type){ - case SC_STONE: - case SC_FREEZE: - case SC_STUN: - case SC_SLEEP: - case SC_DEEPSLEEP: - case SC_BURNING: - case SC_WHITEIMPRISON: - case SC_CRYSTALIZE: - sc->opt1 = 0; - break; - - case SC_POISON: - case SC_CURSE: - case SC_SILENCE: - case SC_BLIND: - sc->opt2 &= ~(1<<(type-SC_POISON)); - break; - case SC_DPOISON: - sc->opt2 &= ~OPT2_DPOISON; - break; - case SC_SIGNUMCRUCIS: - sc->opt2 &= ~OPT2_SIGNUMCRUCIS; - 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: - case SC__INVISIBILITY: - sc->option &= ~OPTION_CLOAK; - case SC_CAMOUFLAGE: - opt_flag|= 2; - break; - case SC_CHASEWALK: - sc->option &= ~(OPTION_CHASEWALK|OPTION_CLOAK); - opt_flag|= 2; - break; - case SC_SIGHT: - sc->option &= ~OPTION_SIGHT; - break; - case SC_WEDDING: - sc->option &= ~OPTION_WEDDING; - break; - case SC_XMAS: - sc->option &= ~OPTION_XMAS; - break; - case SC_SUMMER: - sc->option &= ~OPTION_SUMMER; - break; - case SC_ORCISH: - sc->option &= ~OPTION_ORCISH; - break; - case SC_RUWACH: - sc->option &= ~OPTION_RUWACH; - break; - case SC_FUSION: - sc->option &= ~OPTION_FLYING; - break; - //opt3 - case SC_TWOHANDQUICKEN: - case SC_ONEHAND: - case SC_SPEARQUICKEN: - case SC_CONCENTRATION: - case SC_MERC_QUICKEN: - sc->opt3 &= ~OPT3_QUICKEN; - opt_flag = 0; - break; - case SC_OVERTHRUST: - case SC_MAXOVERTHRUST: - case SC_SWOO: - sc->opt3 &= ~OPT3_OVERTHRUST; - if( type == SC_SWOO ) - opt_flag = 8; - else - opt_flag = 0; - break; - case SC_ENERGYCOAT: - case SC_SKE: - sc->opt3 &= ~OPT3_ENERGYCOAT; - opt_flag = 0; - break; - case SC_INCATKRATE: //Simulated Explosion spirits effect. - if (bl->type != BL_MOB) - { - opt_flag = 0; - break; - } - case SC_EXPLOSIONSPIRITS: - sc->opt3 &= ~OPT3_EXPLOSIONSPIRITS; - opt_flag = 0; - break; - case SC_STEELBODY: - case SC_SKA: - sc->opt3 &= ~OPT3_STEELBODY; - opt_flag = 0; - break; - case SC_BLADESTOP: - sc->opt3 &= ~OPT3_BLADESTOP; - opt_flag = 0; - break; - case SC_AURABLADE: - sc->opt3 &= ~OPT3_AURABLADE; - opt_flag = 0; - break; - case SC_BERSERK: - opt_flag = 0; -// case SC__BLOODYLUST: - sc->opt3 &= ~OPT3_BERSERK; - break; -// case ???: // doesn't seem to do anything -// sc->opt3 &= ~OPT3_LIGHTBLADE; -// opt_flag = 0; -// break; - case SC_DANCING: - if ((sce->val1&0xFFFF) == CG_MOONLIT) - sc->opt3 &= ~OPT3_MOONLIT; - opt_flag = 0; - break; - case SC_MARIONETTE: - case SC_MARIONETTE2: - sc->opt3 &= ~OPT3_MARIONETTE; - opt_flag = 0; - break; - case SC_ASSUMPTIO: - sc->opt3 &= ~OPT3_ASSUMPTIO; - opt_flag = 0; - break; - case SC_WARM: //SG skills [Komurka] - sc->opt3 &= ~OPT3_WARM; - opt_flag = 0; - break; - case SC_KAITE: - sc->opt3 &= ~OPT3_KAITE; - opt_flag = 0; - break; - case SC_BUNSINJYUTSU: - sc->opt3 &= ~OPT3_BUNSIN; - opt_flag = 0; - break; - case SC_SPIRIT: - sc->opt3 &= ~OPT3_SOULLINK; - opt_flag = 0; - break; - case SC_CHANGEUNDEAD: - sc->opt3 &= ~OPT3_UNDEAD; - opt_flag = 0; - break; -// case ???: // from DA_CONTRACT (looks like biolab mobs aura) -// sc->opt3 &= ~OPT3_CONTRACT; -// opt_flag = 0; -// break; - default: - opt_flag = 0; - } - - if (calc_flag&SCB_DYE) - { //Restore DYE color - if (vd && !vd->cloth_color && sce->val4) - clif_changelook(bl,LOOK_CLOTHES_COLOR,sce->val4); - calc_flag&=~SCB_DYE; - } - - //On Aegis, when turning off a status change, first goes the sc packet, then the option packet. - clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0); - - if( opt_flag&8 ) //bugreport:681 - clif_changeoption2(bl); - else if(opt_flag) - clif_changeoption(bl); - - if (calc_flag) - status_calc_bl(bl,calc_flag); - - if(opt_flag&4) //Out of hiding, invoke on place. - skill_unit_move(bl,gettick(),1); - - if(opt_flag&2 && sd && map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC)) - npc_touch_areanpc(sd,bl->m,bl->x,bl->y); //Trigger on-touch event. - - ers_free(sc_data_ers, sce); - return 1; -} - -int kaahi_heal_timer(int tid, unsigned int tick, int id, intptr_t data) -{ - struct block_list *bl; - struct status_change *sc; - struct status_change_entry *sce; - struct status_data *status; - int hp; - - if(!((bl=map_id2bl(id))&& - (sc=status_get_sc(bl)) && - (sce = sc->data[SC_KAAHI]))) - return 0; - - if(sce->val4 != tid) { - ShowError("kaahi_heal_timer: Timer mismatch: %d != %d\n", tid, sce->val4); - sce->val4 = INVALID_TIMER; - return 0; - } - - status=status_get_status_data(bl); - if(!status_charge(bl, 0, sce->val3)) { - sce->val4 = INVALID_TIMER; - return 0; - } - - hp = status->max_hp - status->hp; - if (hp > sce->val2) - hp = sce->val2; - if (hp) - status_heal(bl, hp, 0, 2); - sce->val4 = INVALID_TIMER; - return 1; -} - -/*========================================== - * For recusive status, like for each 5s we drop sp etc. - * Reseting the end timer. - *------------------------------------------*/ -int status_change_timer(int tid, unsigned int tick, int id, intptr_t data) -{ - enum sc_type type = (sc_type)data; - struct block_list *bl; - struct map_session_data *sd; - struct status_data *status; - struct status_change *sc; - struct status_change_entry *sce; - - bl = map_id2bl(id); - if(!bl) - { - ShowDebug("status_change_timer: Null pointer id: %d data: %d\n", id, data); - return 0; - } - sc = status_get_sc(bl); - status = status_get_status_data(bl); - - if(!(sc && (sce = sc->data[type]))) - { - ShowDebug("status_change_timer: Null pointer id: %d data: %d bl-type: %d\n", id, data, bl->type); - return 0; - } - - if( sce->timer != tid ) - { - ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sce->timer, bl->id); - return 0; - } - - sd = BL_CAST(BL_PC, bl); - -// set the next timer of the sce (don't assume the status still exists) -#define sc_timer_next(t,f,i,d) \ - if( (sce=sc->data[type]) ) \ - sce->timer = add_timer(t,f,i,d); \ - else \ - ShowError("status_change_timer: Unexpected NULL status change id: %d data: %d\n", id, data) - - switch(type) - { - case SC_MAXIMIZEPOWER: - case SC_CLOAKING: - if(!status_charge(bl, 0, 1)) - break; //Not enough SP to continue. - sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data); - return 0; - - case SC_CHASEWALK: - if(!status_charge(bl, 0, sce->val4)) - break; //Not enough SP to continue. - - if (!sc->data[SC_INCSTR]) { - sc_start(bl, SC_INCSTR,100,1<<(sce->val1-1), - (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration - *skill_get_time2(status_sc2skill(type),sce->val1)); - } - sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data); - return 0; - break; - - case SC_SKA: - if(--(sce->val2)>0){ - sce->val3 = rnd()%100; //Random defense. - sc_timer_next(1000+tick, status_change_timer,bl->id, data); - return 0; - } - break; - - case SC_HIDING: - if(--(sce->val2)>0){ - - if(sce->val2 % sce->val4 == 0 && !status_charge(bl, 0, 1)) - break; //Fail if it's time to substract SP and there isn't. - - sc_timer_next(1000+tick, status_change_timer,bl->id, data); - return 0; - } - break; - - case SC_SIGHT: - case SC_RUWACH: - case SC_SIGHTBLASTER: - if(type == SC_SIGHTBLASTER) - map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick); - else - map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick); - - if( --(sce->val2)>0 ){ - sce->val4 += 250; // use for Shadow Form 2 seconds checking. - sc_timer_next(250+tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_PROVOKE: - if(sce->val2) { //Auto-provoke (it is ended in status_heal) - sc_timer_next(1000*60+tick,status_change_timer, bl->id, data ); - return 0; - } - break; - - case SC_STONE: - if(sc->opt1 == OPT1_STONEWAIT && sce->val3) { - sce->val4 = 0; - unit_stop_walking(bl,1); - unit_stop_attack(bl); - sc->opt1 = OPT1_STONE; - clif_changeoption(bl); - sc_timer_next(1000+tick,status_change_timer, bl->id, data ); - status_calc_bl(bl, StatusChangeFlagTable[type]); - return 0; - } - if(--(sce->val3) > 0) { - if(++(sce->val4)%5 == 0 && status->hp > status->max_hp/4) - status_percent_damage(NULL, bl, 1, 0, false); - sc_timer_next(1000+tick,status_change_timer, bl->id, data ); - return 0; - } - break; - - case SC_POISON: - if(status->hp <= max(status->max_hp>>2, sce->val4)) //Stop damaging after 25% HP left. - break; - case SC_DPOISON: - if (--(sce->val3) > 0) { - if (!sc->data[SC_SLOWPOISON]) { - if( sce->val2 && bl->type == BL_MOB ) { - struct block_list* src = map_id2bl(sce->val2); - if( src ) - mob_log_damage((TBL_MOB*)bl,src,sce->val4); - } - map_freeblock_lock(); - status_zap(bl, sce->val4, 0); - if (sc->data[type]) { // Check if the status still last ( can be dead since then ). - sc_timer_next(1000 + tick, status_change_timer, bl->id, data ); - } - map_freeblock_unlock(); - } - return 0; - } - break; - - case SC_TENSIONRELAX: - if(status->max_hp > status->hp && --(sce->val3) > 0){ - sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_KNOWLEDGE: - if (!sd) break; - if(bl->m == sd->feel_map[0].m || - bl->m == sd->feel_map[1].m || - bl->m == sd->feel_map[2].m) - { //Timeout will be handled by pc_setpos - sce->timer = INVALID_TIMER; - return 0; - } - break; - - case SC_BLEEDING: - if (--(sce->val4) >= 0) { - int hp = rnd()%600 + 200; - map_freeblock_lock(); - status_fix_damage(NULL, bl, sd||hp<status->hp?hp:status->hp-1, 1); - if( sc->data[type] ) { - if( status->hp == 1 ) { - map_freeblock_unlock(); - break; - } - sc_timer_next(10000 + tick, status_change_timer, bl->id, data); - } - map_freeblock_unlock(); - return 0; - } - break; - - case SC_S_LIFEPOTION: - case SC_L_LIFEPOTION: - if( sd && --(sce->val4) >= 0 ) - { - // val1 < 0 = per max% | val1 > 0 = exact amount - int hp = 0; - if( status->hp < status->max_hp ) - hp = (sce->val1 < 0) ? (int)(sd->status.max_hp * -1 * sce->val1 / 100.) : sce->val1 ; - status_heal(bl, hp, 0, 2); - sc_timer_next((sce->val2 * 1000) + tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_BOSSMAPINFO: - if( sd && --(sce->val4) >= 0 ) - { - struct mob_data *boss_md = map_id2boss(sce->val1); - if( boss_md && sd->bl.m == boss_md->bl.m ) - { - clif_bossmapinfo(sd->fd, boss_md, 1); // Update X - Y on minimap - if (boss_md->bl.prev != NULL) { - sc_timer_next(5000 + tick, status_change_timer, bl->id, data); - return 0; - } - } - } - break; - - case SC_DANCING: //SP consumption by time of dancing skills - { - int s = 0; - int sp = 1; - if (--sce->val3 <= 0) - break; - switch(sce->val1&0xFFFF){ - case BD_RICHMANKIM: - case BD_DRUMBATTLEFIELD: - case BD_RINGNIBELUNGEN: - case BD_SIEGFRIED: - case BA_DISSONANCE: - case BA_ASSASSINCROSS: - case DC_UGLYDANCE: - s=3; - break; - case BD_LULLABY: - case BD_ETERNALCHAOS: - case BD_ROKISWEIL: - case DC_FORTUNEKISS: - s=4; - break; - case CG_HERMODE: - case BD_INTOABYSS: - case BA_WHISTLE: - case DC_HUMMING: - case BA_POEMBRAGI: - case DC_SERVICEFORYOU: - s=5; - break; - case BA_APPLEIDUN: - #ifdef RENEWAL - s=5; - #else - s=6; - #endif - break; - case CG_MOONLIT: - //Moonlit's cost is 4sp*skill_lv [Skotlex] - sp= 4*(sce->val1>>16); - //Upkeep is also every 10 secs. - case DC_DONTFORGETME: - s=10; - break; - } - if( s != 0 && sce->val3 % s == 0 ) - { - if (sc->data[SC_LONGING]) - sp*= 3; - if (!status_charge(bl, 0, sp)) - break; - } - sc_timer_next(1000+tick, status_change_timer, bl->id, data); - return 0; - } - break; - case SC__BLOODYLUST: - case SC_BERSERK: - // 5% every 10 seconds [DracoRPG] - if( --( sce->val3 ) > 0 && status_charge(bl, sce->val2, 0) && status->hp > 100 ) - { - sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_NOCHAT: - if(sd){ - sd->status.manner++; - clif_changestatus(sd,SP_MANNER,sd->status.manner); - clif_updatestatus(sd,SP_MANNER); - if (sd->status.manner < 0) - { //Every 60 seconds your manner goes up by 1 until it gets back to 0. - sc_timer_next(60000+tick, status_change_timer, bl->id, data); - return 0; - } - } - break; - - case SC_SPLASHER: - // custom Venom Splasher countdown timer - //if (sce->val4 % 1000 == 0) { - // char timer[10]; - // snprintf (timer, 10, "%d", sce->val4/1000); - // clif_message(bl, timer); - //} - if((sce->val4 -= 500) > 0) { - sc_timer_next(500 + tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_MARIONETTE: - case SC_MARIONETTE2: - { - struct block_list *pbl = map_id2bl(sce->val1); - if( pbl && check_distance_bl(bl, pbl, 7) ) - { - sc_timer_next(1000 + tick, status_change_timer, bl->id, data); - return 0; - } - } - break; - - case SC_GOSPEL: - if(sce->val4 == BCT_SELF && --(sce->val2) > 0) - { - int hp, sp; - hp = (sce->val1 > 5) ? 45 : 30; - sp = (sce->val1 > 5) ? 35 : 20; - if(!status_charge(bl, hp, sp)) - break; - sc_timer_next(10000+tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_JAILED: - if(sce->val1 == INT_MAX || --(sce->val1) > 0) - { - sc_timer_next(60000+tick, status_change_timer, bl->id,data); - return 0; - } - break; - - case SC_BLIND: - if(sc->data[SC_FOGWALL]) - { //Blind lasts forever while you are standing on the fog. - sc_timer_next(5000+tick, status_change_timer, bl->id, data); - return 0; - } - break; - case SC_ABUNDANCE: - if(--(sce->val4) > 0) { - 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 ) { - map_freeblock_lock(); - clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,0,0); - status_fix_damage(NULL,bl,100,0); - if( sc->data[type] ) { - sc_timer_next(3000+tick,status_change_timer,bl->id,data); - } - map_freeblock_unlock(); - return 0; - } - break; - - case SC_LEECHESEND: - if( --(sce->val4) >= 0 ) { - int damage = status->max_hp/100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100) - damage += status->vit * (sce->val1 - 3); - unit_skillcastcancel(bl,2); - map_freeblock_lock(); - status_damage(bl, bl, damage, 0, clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,0,0), 1); - if( sc->data[type] ) { - sc_timer_next(1000 + tick, status_change_timer, bl->id, data ); - } - map_freeblock_unlock(); - 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 && !pc_issit(sd)) { //can't cast if sit - int mushroom_skill_id = 0, i; - unit_stop_attack(bl); - unit_skillcastcancel(bl,1); - do { - i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB; - mushroom_skill_id = skill_magicmushroom_db[i].skill_id; - } - while( mushroom_skill_id == 0 ); - - switch( skill_get_casttype(mushroom_skill_id) ) { // Magic Mushroom skills are buffs or area damage - case CAST_GROUND: - skill_castend_pos2(bl,bl->x,bl->y,mushroom_skill_id,1,tick,0); - break; - case CAST_NODAMAGE: - skill_castend_nodamage_id(bl,bl,mushroom_skill_id,1,tick,0); - break; - case CAST_DAMAGE: - skill_castend_damage_id(bl,bl,mushroom_skill_id,1,tick,0); - break; - } - } - - clif_emotion(bl,E_HEH); - 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. - 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, 0); //cancel dmg only if cancelable - if( sc->data[type] ) { - sc_timer_next(10000 + tick, status_change_timer, bl->id, data ); - } - map_freeblock_unlock(); - return 0; - } - break; - - case SC_OBLIVIONCURSE: - if( --(sce->val4) >= 0 ) - { - clif_emotion(bl,E_WHAT); - 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->val4) >= 0 ) - { - int heal = status->max_hp * 3 / 100; - if( sc && sc->data[SC_AKAITSUKI] && heal ) - heal = ~heal + 1; - status_heal(bl, heal, 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 damage = 1000 + 3 * status_get_max_hp(bl) / 100; // Deals fixed (1000 + 3%*MaxHP) - - map_freeblock_lock(); - clif_damage(bl,bl,tick,0,0,damage,1,9,0); //damage is like endure effect with no walk delay - status_damage(src, bl, damage, 0, 0, 1); - - if( sc->data[type]){ // Target still lives. [LimitLine] - sc_timer_next(2000 + tick, status_change_timer, bl->id, data); - } - map_freeblock_unlock(); - return 0; - } - break; - - case SC_FEAR: - if( --(sce->val4) >= 0 ) - { - if( sce->val2 > 0 ) - sce->val2--; - sc_timer_next(1000 + tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_SPHERE_1: - case SC_SPHERE_2: - case SC_SPHERE_3: - case SC_SPHERE_4: - case SC_SPHERE_5: - if( --(sce->val4) >= 0 ) - { - 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) ){ - int i; - for(i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) // Also remove stored spell as well. - status_change_end(bl, (sc_type)i, INVALID_TIMER); - break; - } - sc_timer_next(5000 + 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(--(sce->val4) > 0){ - status_charge(bl,0,7 - sce->val1); - sc_timer_next(1000 + tick, status_change_timer, bl->id, data); - return 0; - } - break; - - 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% - skill_lv. - 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; - case SC_VACUUM_EXTREME: - if( --(sce->val4) >= 0 ){ - if( !unit_is_walking(bl) && !sce->val2 ){ - sc->cant.move++; - sce->val2 = 1; - } - sc_timer_next(100 + tick, status_change_timer, bl->id, data); - return 0; - } - break; - case SC_BLOODSUCKER: - if( --(sce->val4) >= 0 ) { - struct block_list *src = map_id2bl(sce->val2); - int damage; - if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) ) - break; - map_freeblock_lock(); - damage = 200 + 100 * sce->val1 + status_get_int(src); - status_damage(src, bl, damage, 0, clif_damage(bl,bl,tick,status->amotion,status->dmotion+200,damage,1,0,0), 1); - unit_skillcastcancel(bl,1); - if ( sc->data[type] ) { - sc_timer_next(1000 + tick, status_change_timer, bl->id, data); - } - map_freeblock_unlock(); - status_heal(src, damage*(5 + 5 * sce->val1)/100, 0, 0); // 5 + 5% per level - return 0; - } - break; - - case SC_VOICEOFSIREN: - if( --(sce->val4) >= 0 ) - { - clif_emotion(bl,E_LV); - 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 val4 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. - if( bl->type != BL_MOB) // doesn't work on mobs - 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 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)); - if( sc->data[type] ) { - sc_timer_next(1000 + tick, status_change_timer, bl->id, data); - } - map_freeblock_unlock(); - } - 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,INVALID_TIMER); - status_change_end(bl,type,INVALID_TIMER); - 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); - return 0; - } - break; - case SC_LEADERSHIP: - case SC_GLORYWOUNDS: - case SC_SOULCOLD: - case SC_HAWKEYES: - /* they only end by status_change_end */ - sc_timer_next(600000 + tick, status_change_timer, bl->id, data); - return 0; - case SC_MEIKYOUSISUI: - if( --(sce->val4) > 0 ){ - status_heal(bl, status->max_hp * (sce->val1+1) / 100, status->max_sp * sce->val1 / 100, 0); - sc_timer_next(1000 + tick, status_change_timer, bl->id, data); - return 0; - } - break; - case SC_IZAYOI: - case SC_KAGEMUSYA: - if( --(sce->val2) > 0 ){ - if(!status_charge(bl, 0, 1)) break; - sc_timer_next(1000+tick, status_change_timer, bl->id, data); - return 0; - } - break; - case SC_ANGRIFFS_MODUS: - if(--(sce->val4) >= 0) { //drain hp/sp - if( !status_charge(bl,100,20) ) break; - sc_timer_next(1000+tick,status_change_timer,bl->id, data); - return 0; - } - break; - } - - // default for all non-handled control paths is to end the status - return status_change_end( bl,type,tid ); -#undef sc_timer_next -} - -/*========================================== - * Foreach iteration of repetitive status - *------------------------------------------*/ -int status_change_timer_sub(struct block_list* bl, va_list ap) -{ - struct status_change* tsc; - - struct block_list* src = va_arg(ap,struct block_list*); - struct status_change_entry* sce = va_arg(ap,struct status_change_entry*); - enum sc_type type = (sc_type)va_arg(ap,int); //gcc: enum args get promoted to int - unsigned int tick = va_arg(ap,unsigned int); - - if (status_isdead(bl)) - return 0; - - tsc = status_get_sc(bl); - - switch( type ) { - case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */ - if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking - rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] % - status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); - 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); - status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER); - break; - case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */ - if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] || - tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED] || - tsc->data[SC__INVISIBILITY])) { - 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); - status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER); - if(battle_check_target( src, bl, BCT_ENEMY ) > 0) - skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0); - } - if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking - rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] % - status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); - break; - case SC_SIGHTBLASTER: - if (battle_check_target( src, bl, BCT_ENEMY ) > 0 && - status_check_skilluse(src, bl, WZ_SIGHTBLASTER, 2)) - { - skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0); - if (sce && !(bl->type&BL_SKILL)) //The hit is not counted if it's against a trap - sce->val2 = 0; //This signals it to end. - } - break; - case SC_CLOSECONFINE: - //Lock char has released the hold on everyone... - if (tsc && tsc->data[SC_CLOSECONFINE2] && tsc->data[SC_CLOSECONFINE2]->val2 == src->id) { - tsc->data[SC_CLOSECONFINE2]->val2 = 0; - status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER); - } - break; - case SC_CURSEDCIRCLE_TARGET: - if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) { - clif_bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0); - status_change_end(bl, type, INVALID_TIMER); - } - break; - } - return 0; -} - -/*========================================== - * Clears buffs/debuffs of a character. - * type&1 -> buffs, type&2 -> debuffs - * type&4 -> especific debuffs(implemented with refresh) - *------------------------------------------*/ -int status_change_clear_buffs (struct block_list* bl, int type) -{ - int i; - struct status_change *sc= status_get_sc(bl); - - if (!sc || !sc->count) - return 0; - - if (type&6) //Debuffs - for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++) - status_change_end(bl, (sc_type)i, INVALID_TIMER); - - for( i = SC_COMMON_MAX+1; i < SC_MAX; i++ ) - { - if(!sc->data[i]) - continue; - - switch (i) { - //Stuff that cannot be removed - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_COMBO: - case SC_SMA: - case SC_DANCING: - case SC_LEADERSHIP: - case SC_GLORYWOUNDS: - case SC_SOULCOLD: - case SC_HAWKEYES: - case SC_GUILDAURA: - case SC_SAFETYWALL: - case SC_PNEUMA: - case SC_NOCHAT: - case SC_JAILED: - case SC_ANKLE: - case SC_BLADESTOP: - case SC_CP_WEAPON: - case SC_CP_SHIELD: - case SC_CP_ARMOR: - case SC_CP_HELM: - 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_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_EXPBOOST: - case SC_JEXPBOOST: - case SC_ITEMBOOST: - case SC_ELECTRICSHOCKER: - case SC__MANHOLE: - case SC_GIANTGROWTH: - case SC_MILLENNIUMSHIELD: - case SC_REFRESH: - case SC_STONEHARDSKIN: - case SC_VITALITYACTIVATION: - case SC_FIGHTINGSPIRIT: - case SC_ABUNDANCE: - case SC_CURSEDCIRCLE_ATKER: - case SC_CURSEDCIRCLE_TARGET: - continue; - - //Debuffs that can be removed. - case SC_DEEPSLEEP: - case SC_BURNING: - case SC_FREEZING: - case SC_CRYSTALIZE: - 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_MARSHOFABYSS: - case SC_MANDRAGORA: - if(!(type&4)) - continue; - break; - case SC_HALLUCINATION: - case SC_QUAGMIRE: - case SC_SIGNUMCRUCIS: - case SC_DECREASEAGI: - case SC_SLOWDOWN: - case SC_MINDBREAKER: - case SC_WINKCHARM: - case SC_STOP: - case SC_ORCISH: - case SC_STRIPWEAPON: - case SC_STRIPSHIELD: - case SC_STRIPARMOR: - case SC_STRIPHELM: - case SC_BITE: - case SC_ADORAMUS: - case SC_VACUUM_EXTREME: - case SC_FEAR: - case SC_MAGNETICFIELD: - case SC_NETHERWORLD: - if (!(type&2)) - continue; - break; - //The rest are buffs that can be removed. - case SC__BLOODYLUST: - case SC_BERSERK: - case SC_SATURDAYNIGHTFEVER: - if (!(type&1)) - continue; - sc->data[i]->val2 = 0; - break; - default: - if (!(type&1)) - continue; - break; - } - status_change_end(bl, (sc_type)i, INVALID_TIMER); - } - return 0; -} - -int status_change_spread( struct block_list *src, struct block_list *bl ) { - int i, flag = 0; - struct status_change *sc = status_get_sc(src); - const struct TimerData *timer; - unsigned int tick; - struct status_change_data data; - - if( !sc || !sc->count ) - return 0; - - tick = gettick(); - - for( i = SC_COMMON_MIN; i < SC_MAX; i++ ) { - if( !sc->data[i] || i == SC_COMMON_MAX ) - continue; - - switch( i ) { - //Debuffs that can be spreaded. - // NOTE: We'll add/delte SCs when we are able to confirm it. - case SC_CURSE: - case SC_SILENCE: - case SC_CONFUSION: - case SC_BLIND: - case SC_NOCHAT: - case SC_HALLUCINATION: - case SC_SIGNUMCRUCIS: - case SC_DECREASEAGI: - case SC_SLOWDOWN: - case SC_MINDBREAKER: - case SC_WINKCHARM: - case SC_STOP: - case SC_ORCISH: - //case SC_STRIPWEAPON://Omg I got infected and had the urge to strip myself physically. - //case SC_STRIPSHIELD://No this is stupid and shouldnt be spreadable at all. - //case SC_STRIPARMOR:// Disabled until I can confirm if it does or not. [Rytech] - //case SC_STRIPHELM: - //case SC__STRIPACCESSORY: - case SC_BITE: - case SC_FREEZING: - case SC_VENOMBLEED: - case SC_DEATHHURT: - case SC_PARALYSE: - if( sc->data[i]->timer != INVALID_TIMER ) { - timer = get_timer(sc->data[i]->timer); - if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0) - continue; - data.tick = DIFF_TICK(timer->tick,tick); - } else - data.tick = INVALID_TIMER; - break; - // Special cases - case SC_POISON: - case SC_DPOISON: - data.tick = sc->data[i]->val3 * 1000; - break; - case SC_FEAR: - case SC_LEECHESEND: - data.tick = sc->data[i]->val4 * 1000; - break; - case SC_BURNING: - data.tick = sc->data[i]->val4 * 2000; - break; - case SC_PYREXIA: - case SC_OBLIVIONCURSE: - data.tick = sc->data[i]->val4 * 3000; - break; - case SC_MAGICMUSHROOM: - data.tick = sc->data[i]->val4 * 4000; - break; - case SC_TOXIN: - case SC_BLEEDING: - data.tick = sc->data[i]->val4 * 10000; - break; - default: - continue; - break; - } - if( i ){ - data.val1 = sc->data[i]->val1; - data.val2 = sc->data[i]->val2; - data.val3 = sc->data[i]->val3; - data.val4 = sc->data[i]->val4; - status_change_start(bl,(sc_type)i,10000,data.val1,data.val2,data.val3,data.val4,data.tick,1|2|8); - flag = 1; - } - } - - return flag; -} - -//Natural regen related stuff. -static unsigned int natural_heal_prev_tick,natural_heal_diff_tick; -static int status_natural_heal(struct block_list* bl, va_list args) -{ - struct regen_data *regen; - struct status_data *status; - struct status_change *sc; - struct unit_data *ud; - struct view_data *vd = NULL; - struct regen_data_sub *sregen; - struct map_session_data *sd; - int val,rate,bonus = 0,flag; - - regen = status_get_regen_data(bl); - if (!regen) return 0; - status = status_get_status_data(bl); - sc = status_get_sc(bl); - if (sc && !sc->count) - sc = NULL; - sd = BL_CAST(BL_PC,bl); - - flag = regen->flag; - if (flag&RGN_HP && (status->hp >= status->max_hp || regen->state.block&1)) - flag&=~(RGN_HP|RGN_SHP); - if (flag&RGN_SP && (status->sp >= status->max_sp || regen->state.block&2)) - flag&=~(RGN_SP|RGN_SSP); - - if (flag && ( - status_isdead(bl) || - (sc && (sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || sc->data[SC__INVISIBILITY])) - )) - flag=0; - - if (sd) { - if (sd->hp_loss.value || sd->sp_loss.value) - pc_bleeding(sd, natural_heal_diff_tick); - if (sd->hp_regen.value || sd->sp_regen.value) - pc_regen(sd, natural_heal_diff_tick); - } - - if(flag&(RGN_SHP|RGN_SSP) && regen->ssregen && - (vd = status_get_viewdata(bl)) && vd->dead_sit == 2) - { //Apply sitting regen bonus. - sregen = regen->ssregen; - if(flag&(RGN_SHP)) - { //Sitting HP regen - val = natural_heal_diff_tick * sregen->rate.hp; - if (regen->state.overweight) - val>>=1; //Half as fast when overweight. - sregen->tick.hp += val; - while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval) - { - sregen->tick.hp -= battle_config.natural_heal_skill_interval; - if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp) - { //Full - flag&=~(RGN_HP|RGN_SHP); - break; - } - } - } - if(flag&(RGN_SSP)) - { //Sitting SP regen - val = natural_heal_diff_tick * sregen->rate.sp; - if (regen->state.overweight) - val>>=1; //Half as fast when overweight. - sregen->tick.sp += val; - while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval) - { - sregen->tick.sp -= battle_config.natural_heal_skill_interval; - if(status_heal(bl, 0, sregen->sp, 3) < sregen->sp) - { //Full - flag&=~(RGN_SP|RGN_SSP); - break; - } - } - } - } - - if (flag && regen->state.overweight) - flag=0; - - ud = unit_bl2ud(bl); - - if (flag&(RGN_HP|RGN_SHP|RGN_SSP) && ud && ud->walktimer != INVALID_TIMER) - { - flag&=~(RGN_SHP|RGN_SSP); - if(!regen->state.walk) - flag&=~RGN_HP; - } - - if (!flag) - return 0; - - if (flag&(RGN_HP|RGN_SP)) - { - if(!vd) vd = status_get_viewdata(bl); - if(vd && vd->dead_sit == 2) - bonus++; - if(regen->state.gc) - bonus++; - } - - //Natural Hp regen - if (flag&RGN_HP) - { - rate = natural_heal_diff_tick*(regen->rate.hp+bonus); - if (ud && ud->walktimer != INVALID_TIMER) - rate/=2; - // Homun HP regen fix (they should regen as if they were sitting (twice as fast) - if(bl->type==BL_HOM) rate *=2; - - regen->tick.hp += rate; - - if(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval) - { - val = 0; - do { - val += regen->hp; - regen->tick.hp -= battle_config.natural_healhp_interval; - } while(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval); - if (status_heal(bl, val, 0, 1) < val) - flag&=~RGN_SHP; //full. - } - } - - //Natural SP regen - if(flag&RGN_SP) - { - rate = natural_heal_diff_tick*(regen->rate.sp+bonus); - // Homun SP regen fix (they should regen as if they were sitting (twice as fast) - if(bl->type==BL_HOM) rate *=2; - - regen->tick.sp += rate; - - if(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval) - { - val = 0; - do { - val += regen->sp; - regen->tick.sp -= battle_config.natural_healsp_interval; - } while(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval); - if (status_heal(bl, 0, val, 1) < val) - flag&=~RGN_SSP; //full. - } - } - - if (!regen->sregen) - return flag; - - //Skill regen - sregen = regen->sregen; - - if(flag&RGN_SHP) - { //Skill HP regen - sregen->tick.hp += natural_heal_diff_tick * sregen->rate.hp; - - while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval) - { - sregen->tick.hp -= battle_config.natural_heal_skill_interval; - if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp) - break; //Full - } - } - if(flag&RGN_SSP) - { //Skill SP regen - sregen->tick.sp += natural_heal_diff_tick * sregen->rate.sp; - while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval) - { - val = sregen->sp; - if (sd && sd->state.doridori) { - val*=2; - sd->state.doridori = 0; - if ((rate = pc_checkskill(sd,TK_SPTIME))) - sc_start(bl,status_skill2sc(TK_SPTIME), - 100,rate,skill_get_time(TK_SPTIME, rate)); - if ( - (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR && - rnd()%10000 < battle_config.sg_angel_skill_ratio - ) { //Angel of the Sun/Moon/Star - clif_feel_hate_reset(sd); - pc_resethate(sd); - pc_resetfeel(sd); - } - } - sregen->tick.sp -= battle_config.natural_heal_skill_interval; - if(status_heal(bl, 0, val, 3) < val) - break; //Full - } - } - return flag; -} - -//Natural heal main timer. -static int status_natural_heal_timer(int tid, unsigned int tick, int id, intptr_t data) -{ - natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick); - map_foreachregen(status_natural_heal); - natural_heal_prev_tick = tick; - return 0; -} - -/** - * Get the chance to upgrade a piece of equipment. - * @param wlv The weapon type of the item to refine (see see enum refine_type) - * @param refine The target refine level - * @return The chance to refine the item, in percent (0~100) - **/ -int status_get_refine_chance(enum refine_type wlv, int refine) { - - if ( refine < 0 || refine >= MAX_REFINE) - return 0; - - return refine_info[wlv].chance[refine]; -} - - -/*------------------------------------------ - * DB reading. - * job_db1.txt - weight, hp, sp, aspd - * job_db2.txt - job level stat bonuses - * size_fix.txt - size adjustment table for weapons - * refine_db.txt - refining data table - *------------------------------------------*/ -static bool status_readdb_job1(char* fields[], int columns, int current) -{// Job-specific values (weight, HP, SP, ASPD) - int idx, class_; - unsigned int i; - - class_ = atoi(fields[0]); - - if(!pcdb_checkid(class_)) - { - ShowWarning("status_readdb_job1: Invalid job class %d specified.\n", class_); - return false; - } - idx = pc_class2idx(class_); - - max_weight_base[idx] = atoi(fields[1]); - hp_coefficient[idx] = atoi(fields[2]); - hp_coefficient2[idx] = atoi(fields[3]); - sp_coefficient[idx] = atoi(fields[4]); -#ifdef RENEWAL_ASPD - for(i = 0; i <= MAX_WEAPON_TYPE; i++) -#else - for(i = 0; i < MAX_WEAPON_TYPE; i++) -#endif - { - aspd_base[idx][i] = atoi(fields[i+5]); - } - return true; -} - -static bool status_readdb_job2(char* fields[], int columns, int current) -{ - int idx, class_, i; - - class_ = atoi(fields[0]); - - if(!pcdb_checkid(class_)) - { - ShowWarning("status_readdb_job2: Invalid job class %d specified.\n", class_); - return false; - } - idx = pc_class2idx(class_); - - for(i = 1; i < columns; i++) - { - job_bonus[idx][i-1] = atoi(fields[i]); - } - return true; -} - -static bool status_readdb_sizefix(char* fields[], int columns, int current) -{ - unsigned int i; - - for(i = 0; i < MAX_WEAPON_TYPE; i++) - { - atkmods[current][i] = atoi(fields[i]); - } - return true; -} - -static bool status_readdb_refine(char* fields[], int columns, int current) -{ - int i, bonus_per_level, random_bonus, random_bonus_start_level; - - current = atoi(fields[0]); - - if (current < 0 || current >= REFINE_TYPE_MAX) - return false; - - bonus_per_level = atoi(fields[1]); - random_bonus_start_level = atoi(fields[2]); - random_bonus = atoi(fields[3]); - - for(i = 0; i < MAX_REFINE; i++) - { - char* delim; - - if (!(delim = strchr(fields[4+i], ':'))) - return false; - - *delim = '\0'; - - refine_info[current].chance[i] = atoi(fields[4+i]); - - if (i >= random_bonus_start_level - 1) - refine_info[current].randombonus_max[i] = random_bonus * (i - random_bonus_start_level + 2); - - refine_info[current].bonus[i] = bonus_per_level + atoi(delim+1); - if (i > 0) - refine_info[current].bonus[i] += refine_info[current].bonus[i-1]; - } - return true; -} - -/* -* Read status db -* job1.txt -* job2.txt -* size_fixe.txt -* refine_db.txt -*/ -int status_readdb(void) -{ - int i, j; - - // initialize databases to default - // - - // reset job_db1.txt data - memset(max_weight_base, 0, sizeof(max_weight_base)); - memset(hp_coefficient, 0, sizeof(hp_coefficient)); - memset(hp_coefficient2, 0, sizeof(hp_coefficient2)); - memset(sp_coefficient, 0, sizeof(sp_coefficient)); - memset(aspd_base, 0, sizeof(aspd_base)); - // reset job_db2.txt data - memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus - - // size_fix.txt - for(i=0;i<ARRAYLENGTH(atkmods);i++) - for(j=0;j<MAX_WEAPON_TYPE;j++) - atkmods[i][j]=100; - - // refine_db.txt - for(i=0;i<ARRAYLENGTH(refine_info);i++) - { - for(j=0;j<MAX_REFINE; j++) - { - refine_info[i].chance[j] = 100; - refine_info[i].bonus[j] = 0; - refine_info[i].randombonus_max[j] = 0; - } - } - - // read databases - // - - -#ifdef RENEWAL_ASPD - sv_readdb(db_path, "re/job_db1.txt", ',', 6+MAX_WEAPON_TYPE, 6+MAX_WEAPON_TYPE, -1, &status_readdb_job1); -#else - sv_readdb(db_path, "pre-re/job_db1.txt", ',', 5+MAX_WEAPON_TYPE, 5+MAX_WEAPON_TYPE, -1, &status_readdb_job1); -#endif - 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, DBPATH"refine_db.txt", ',', 4+MAX_REFINE, 4+MAX_REFINE, ARRAYLENGTH(refine_info), &status_readdb_refine); - - return 0; -} - -/*========================================== - * Status db init and destroy. - *------------------------------------------*/ -int do_init_status(void) -{ - add_timer_func_list(status_change_timer,"status_change_timer"); - add_timer_func_list(kaahi_heal_timer,"kaahi_heal_timer"); - add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer"); - initChangeTables(); - initDummyData(); - status_readdb(); - status_calc_sigma(); - natural_heal_prev_tick = gettick(); - sc_data_ers = ers_new(sizeof(struct status_change_entry),"status.c::sc_data_ers",ERS_OPT_NONE); - add_timer_interval(natural_heal_prev_tick + NATURAL_HEAL_INTERVAL, status_natural_heal_timer, 0, 0, NATURAL_HEAL_INTERVAL); - return 0; -} -void do_final_status(void) -{ - ers_destroy(sc_data_ers); -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+#include "../common/utils.h"
+#include "../common/ers.h"
+#include "../common/strlib.h"
+
+#include "map.h"
+#include "path.h"
+#include "pc.h"
+#include "pet.h"
+#include "npc.h"
+#include "mob.h"
+#include "clif.h"
+#include "guild.h"
+#include "skill.h"
+#include "itemdb.h"
+#include "battle.h"
+#include "chrif.h"
+#include "skill.h"
+#include "status.h"
+#include "script.h"
+#include "unit.h"
+#include "homunculus.h"
+#include "mercenary.h"
+#include "elemental.h"
+#include "vending.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+//Regen related flags.
+enum e_regen
+{
+ RGN_HP = 0x01,
+ RGN_SP = 0x02,
+ RGN_SHP = 0x04,
+ RGN_SSP = 0x08,
+};
+
+static int max_weight_base[CLASS_COUNT];
+static int hp_coefficient[CLASS_COUNT];
+static int hp_coefficient2[CLASS_COUNT];
+static int hp_sigma_val[CLASS_COUNT][MAX_LEVEL+1];
+static int sp_coefficient[CLASS_COUNT];
+#ifdef RENEWAL_ASPD
+static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE+1];
+#else
+static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE]; //[blackhole89]
+#endif
+
+// bonus values and upgrade chances for refining equipment
+static struct {
+ int chance[MAX_REFINE]; // success chance
+ int bonus[MAX_REFINE]; // cumulative fixed bonus damage
+ int randombonus_max[MAX_REFINE]; // cumulative maximum random bonus damage
+} refine_info[REFINE_TYPE_MAX];
+
+static int atkmods[3][MAX_WEAPON_TYPE]; //ATK weapon modification for size (size_fix.txt)
+static char job_bonus[CLASS_COUNT][MAX_LEVEL];
+
+static struct eri *sc_data_ers; //For sc_data entries
+static struct status_data dummy_status;
+
+int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
+int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex]
+//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only
+//to avoid cards exploits
+
+static sc_type SkillStatusChangeTable[MAX_SKILL]; // skill -> status
+static int StatusIconChangeTable[SC_MAX]; // status -> "icon" (icon is a bit of a misnomer, since there exist values with no icon associated)
+static unsigned int StatusChangeFlagTable[SC_MAX]; // status -> flags
+static int StatusSkillChangeTable[SC_MAX]; // status -> skill
+static int StatusRelevantBLTypes[SI_MAX]; // "icon" -> enum bl_type (for clif_status_change to identify for which bl types to send packets)
+static unsigned int StatusChangeStateTable[SC_MAX]; // status -> flags
+
+
+/**
+ * Returns the status change associated with a skill.
+ * @param skill The skill to look up
+ * @return The status registered for this skill
+ **/
+sc_type status_skill2sc(int skill)
+{
+ int idx = skill_get_index(skill);
+ if( idx == 0 ) {
+ ShowError("status_skill2sc: Unsupported skill id %d\n", skill);
+ return SC_NONE;
+ }
+ return SkillStatusChangeTable[idx];
+}
+
+/**
+ * Returns the FIRST skill (in order of definition in initChangeTables) to use a given status change.
+ * Utilized for various duration lookups. Use with caution!
+ * @param sc The status to look up
+ * @return A skill associated with the status
+ **/
+int status_sc2skill(sc_type sc)
+{
+ if( sc < 0 || sc >= SC_MAX ) {
+ ShowError("status_sc2skill: Unsupported status change id %d\n", sc);
+ return 0;
+ }
+
+ return StatusSkillChangeTable[sc];
+}
+
+/**
+ * Returns the status calculation flag associated with a given status change.
+ * @param sc The status to look up
+ * @return The scb_flag registered for this status (see enum scb_flag)
+ **/
+unsigned int status_sc2scb_flag(sc_type sc)
+{
+ if( sc < 0 || sc >= SC_MAX ) {
+ ShowError("status_sc2scb_flag: Unsupported status change id %d\n", sc);
+ return SCB_NONE;
+ }
+
+ return StatusChangeFlagTable[sc];
+}
+
+/**
+ * Returns the bl types which require a status change packet to be sent for a given client status identifier.
+ * @param type The client-side status identifier to look up (see enum si_type)
+ * @return The bl types relevant to the type (see enum bl_type)
+ **/
+int status_type2relevant_bl_types(int type)
+{
+ if( type < 0 || type >= SI_MAX ) {
+ ShowError("status_type2relevant_bl_types: Unsupported type %d\n", type);
+ return SI_BLANK;
+ }
+
+ return StatusRelevantBLTypes[type];
+}
+
+#define add_sc(skill,sc) set_sc(skill,sc,SI_BLANK,SCB_NONE)
+// indicates that the status displays a visual effect for the affected unit, and should be sent to the client for all supported units
+#define set_sc_with_vfx(skill, sc, icon, flag) set_sc((skill), (sc), (icon), (flag)); if((icon) < SI_MAX) StatusRelevantBLTypes[(icon)] |= BL_SCEFFECT
+
+static void set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag)
+{
+ uint16 idx = skill_get_index(skill_id);
+ if( idx == 0 ) {
+ ShowError("set_sc: Unsupported skill id %d\n", skill_id);
+ return;
+ }
+ if( sc < 0 || sc >= SC_MAX ) {
+ ShowError("set_sc: Unsupported status change id %d\n", sc);
+ return;
+ }
+
+ if( StatusSkillChangeTable[sc] == 0 )
+ StatusSkillChangeTable[sc] = skill_id;
+ if( StatusIconChangeTable[sc] == SI_BLANK )
+ StatusIconChangeTable[sc] = icon;
+ StatusChangeFlagTable[sc] |= flag;
+
+ if( SkillStatusChangeTable[idx] == SC_NONE )
+ SkillStatusChangeTable[idx] = sc;
+}
+
+void initChangeTables(void) {
+ int i;
+
+ for (i = 0; i < SC_MAX; i++)
+ StatusIconChangeTable[i] = SI_BLANK;
+
+ for (i = 0; i < MAX_SKILL; i++)
+ SkillStatusChangeTable[i] = SC_NONE;
+
+ for (i = 0; i < SI_MAX; i++)
+ StatusRelevantBLTypes[i] = BL_PC;
+
+ memset(StatusSkillChangeTable, 0, sizeof(StatusSkillChangeTable));
+ memset(StatusChangeFlagTable, 0, sizeof(StatusChangeFlagTable));
+ memset(StatusChangeStateTable, 0, sizeof(StatusChangeStateTable));
+
+
+ //First we define the skill for common ailments. These are used in skill_additional_effect through sc cards. [Skotlex]
+ set_sc( NPC_PETRIFYATTACK , SC_STONE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF );
+ set_sc( NPC_WIDEFREEZE , SC_FREEZE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF );
+ set_sc( NPC_STUNATTACK , SC_STUN , SI_BLANK , SCB_NONE );
+ set_sc( NPC_SLEEPATTACK , SC_SLEEP , SI_BLANK , SCB_NONE );
+ set_sc( NPC_POISON , SC_POISON , SI_BLANK , SCB_DEF2|SCB_REGEN );
+ set_sc( NPC_CURSEATTACK , SC_CURSE , SI_BLANK , SCB_LUK|SCB_BATK|SCB_WATK|SCB_SPEED );
+ set_sc( NPC_SILENCEATTACK , SC_SILENCE , SI_BLANK , SCB_NONE );
+ set_sc( NPC_WIDECONFUSE , SC_CONFUSION , SI_BLANK , SCB_NONE );
+ set_sc( NPC_BLINDATTACK , SC_BLIND , SI_BLANK , SCB_HIT|SCB_FLEE );
+ set_sc( NPC_BLEEDING , SC_BLEEDING , SI_BLEEDING , SCB_REGEN );
+ set_sc( NPC_POISON , SC_DPOISON , SI_BLANK , SCB_DEF2|SCB_REGEN );
+
+ //The main status definitions
+ add_sc( SM_BASH , SC_STUN );
+ set_sc( SM_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
+ add_sc( SM_MAGNUM , SC_WATK_ELEMENT );
+ set_sc( SM_ENDURE , SC_ENDURE , SI_ENDURE , SCB_MDEF|SCB_DSPD );
+ add_sc( MG_SIGHT , SC_SIGHT );
+ add_sc( MG_SAFETYWALL , SC_SAFETYWALL );
+ add_sc( MG_FROSTDIVER , SC_FREEZE );
+ add_sc( MG_STONECURSE , SC_STONE );
+ add_sc( AL_RUWACH , SC_RUWACH );
+ add_sc( AL_PNEUMA , SC_PNEUMA );
+ set_sc( AL_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
+ set_sc( AL_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED );
+ set_sc( AL_CRUCIS , SC_SIGNUMCRUCIS , SI_SIGNUMCRUCIS , SCB_DEF );
+ set_sc( AL_ANGELUS , SC_ANGELUS , SI_ANGELUS , SCB_DEF2 );
+ set_sc( AL_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
+ set_sc( AC_CONCENTRATION , SC_CONCENTRATE , SI_CONCENTRATE , SCB_AGI|SCB_DEX );
+ set_sc( TF_HIDING , SC_HIDING , SI_HIDING , SCB_SPEED );
+ add_sc( TF_POISON , SC_POISON );
+ set_sc( KN_TWOHANDQUICKEN , SC_TWOHANDQUICKEN , SI_TWOHANDQUICKEN , SCB_ASPD );
+ add_sc( KN_AUTOCOUNTER , SC_AUTOCOUNTER );
+ set_sc( PR_IMPOSITIO , SC_IMPOSITIO , SI_IMPOSITIO , SCB_WATK );
+ set_sc( PR_SUFFRAGIUM , SC_SUFFRAGIUM , SI_SUFFRAGIUM , SCB_NONE );
+ set_sc( PR_ASPERSIO , SC_ASPERSIO , SI_ASPERSIO , SCB_ATK_ELE );
+ set_sc( PR_BENEDICTIO , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE );
+ set_sc( PR_SLOWPOISON , SC_SLOWPOISON , SI_SLOWPOISON , SCB_REGEN );
+ set_sc( PR_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE );
+ set_sc( PR_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN );
+ set_sc( PR_GLORIA , SC_GLORIA , SI_GLORIA , SCB_LUK );
+ add_sc( PR_LEXDIVINA , SC_SILENCE );
+ set_sc( PR_LEXAETERNA , SC_AETERNA , SI_AETERNA , SCB_NONE );
+ add_sc( WZ_METEOR , SC_STUN );
+ add_sc( WZ_VERMILION , SC_BLIND );
+ add_sc( WZ_FROSTNOVA , SC_FREEZE );
+ add_sc( WZ_STORMGUST , SC_FREEZE );
+ set_sc( WZ_QUAGMIRE , SC_QUAGMIRE , SI_QUAGMIRE , SCB_AGI|SCB_DEX|SCB_ASPD|SCB_SPEED );
+ set_sc( BS_ADRENALINE , SC_ADRENALINE , SI_ADRENALINE , SCB_ASPD );
+ set_sc( BS_WEAPONPERFECT , SC_WEAPONPERFECTION, SI_WEAPONPERFECTION, SCB_NONE );
+ set_sc( BS_OVERTHRUST , SC_OVERTHRUST , SI_OVERTHRUST , SCB_NONE );
+ set_sc( BS_MAXIMIZE , SC_MAXIMIZEPOWER , SI_MAXIMIZEPOWER , SCB_REGEN );
+ add_sc( HT_LANDMINE , SC_STUN );
+ add_sc( HT_ANKLESNARE , SC_ANKLE );
+ add_sc( HT_SANDMAN , SC_SLEEP );
+ add_sc( HT_FLASHER , SC_BLIND );
+ add_sc( HT_FREEZINGTRAP , SC_FREEZE );
+ set_sc( AS_CLOAKING , SC_CLOAKING , SI_CLOAKING , SCB_CRI|SCB_SPEED );
+ add_sc( AS_SONICBLOW , SC_STUN );
+ set_sc( AS_ENCHANTPOISON , SC_ENCPOISON , SI_ENCPOISON , SCB_ATK_ELE );
+ set_sc( AS_POISONREACT , SC_POISONREACT , SI_POISONREACT , SCB_NONE );
+ add_sc( AS_VENOMDUST , SC_POISON );
+ add_sc( AS_SPLASHER , SC_SPLASHER );
+ set_sc( NV_TRICKDEAD , SC_TRICKDEAD , SI_TRICKDEAD , SCB_REGEN );
+ set_sc( SM_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE );
+ add_sc( TF_SPRINKLESAND , SC_BLIND );
+ add_sc( TF_THROWSTONE , SC_STUN );
+ set_sc( MC_LOUD , SC_LOUD , SI_LOUD , SCB_STR );
+ set_sc( MG_ENERGYCOAT , SC_ENERGYCOAT , SI_ENERGYCOAT , SCB_NONE );
+ set_sc( NPC_EMOTION , SC_MODECHANGE , SI_BLANK , SCB_MODE );
+ add_sc( NPC_EMOTION_ON , SC_MODECHANGE );
+ set_sc( NPC_ATTRICHANGE , SC_ELEMENTALCHANGE , SI_ARMOR_PROPERTY , SCB_DEF_ELE );
+ add_sc( NPC_CHANGEWATER , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEGROUND , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEFIRE , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEWIND , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEPOISON , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEHOLY , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEDARKNESS , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGETELEKINESIS, SC_ELEMENTALCHANGE );
+ add_sc( NPC_POISON , SC_POISON );
+ add_sc( NPC_BLINDATTACK , SC_BLIND );
+ add_sc( NPC_SILENCEATTACK , SC_SILENCE );
+ add_sc( NPC_STUNATTACK , SC_STUN );
+ add_sc( NPC_PETRIFYATTACK , SC_STONE );
+ add_sc( NPC_CURSEATTACK , SC_CURSE );
+ add_sc( NPC_SLEEPATTACK , SC_SLEEP );
+ add_sc( NPC_MAGICALATTACK , SC_MAGICALATTACK );
+ set_sc( NPC_KEEPING , SC_KEEPING , SI_BLANK , SCB_DEF );
+ add_sc( NPC_DARKBLESSING , SC_COMA );
+ set_sc( NPC_BARRIER , SC_BARRIER , SI_BLANK , SCB_MDEF|SCB_DEF );
+ add_sc( NPC_DEFENDER , SC_ARMOR );
+ add_sc( NPC_LICK , SC_STUN );
+ set_sc( NPC_HALLUCINATION , SC_HALLUCINATION , SI_HALLUCINATION , SCB_NONE );
+ add_sc( NPC_REBIRTH , SC_REBIRTH );
+ add_sc( RG_RAID , SC_STUN );
+#ifdef RENEWAL
+ add_sc( RG_RAID , SC_RAID );
+ add_sc( RG_BACKSTAP , SC_STUN );
+#endif
+ set_sc( RG_STRIPWEAPON , SC_STRIPWEAPON , SI_STRIPWEAPON , SCB_WATK );
+ set_sc( RG_STRIPSHIELD , SC_STRIPSHIELD , SI_STRIPSHIELD , SCB_DEF );
+ set_sc( RG_STRIPARMOR , SC_STRIPARMOR , SI_STRIPARMOR , SCB_VIT );
+ set_sc( RG_STRIPHELM , SC_STRIPHELM , SI_STRIPHELM , SCB_INT );
+ add_sc( AM_ACIDTERROR , SC_BLEEDING );
+ set_sc( AM_CP_WEAPON , SC_CP_WEAPON , SI_CP_WEAPON , SCB_NONE );
+ set_sc( AM_CP_SHIELD , SC_CP_SHIELD , SI_CP_SHIELD , SCB_NONE );
+ set_sc( AM_CP_ARMOR , SC_CP_ARMOR , SI_CP_ARMOR , SCB_NONE );
+ set_sc( AM_CP_HELM , SC_CP_HELM , SI_CP_HELM , SCB_NONE );
+ set_sc( CR_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE );
+ add_sc( CR_SHIELDCHARGE , SC_STUN );
+ set_sc( CR_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE );
+ add_sc( CR_HOLYCROSS , SC_BLIND );
+ add_sc( CR_GRANDCROSS , SC_BLIND );
+ add_sc( CR_DEVOTION , SC_DEVOTION );
+ set_sc( CR_PROVIDENCE , SC_PROVIDENCE , SI_PROVIDENCE , SCB_ALL );
+ set_sc( CR_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD );
+ set_sc( CR_SPEARQUICKEN , SC_SPEARQUICKEN , SI_SPEARQUICKEN , SCB_ASPD|SCB_CRI|SCB_FLEE );
+ set_sc( MO_STEELBODY , SC_STEELBODY , SI_STEELBODY , SCB_DEF|SCB_MDEF|SCB_ASPD|SCB_SPEED );
+ add_sc( MO_BLADESTOP , SC_BLADESTOP_WAIT );
+ add_sc( MO_BLADESTOP , SC_BLADESTOP );
+ set_sc( MO_EXPLOSIONSPIRITS , SC_EXPLOSIONSPIRITS, SI_EXPLOSIONSPIRITS, SCB_CRI|SCB_REGEN );
+ set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST , SI_BLANK , SCB_REGEN );
+#ifdef RENEWAL
+ set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST2 , SI_EXTREMITYFIST , SCB_NONE );
+#endif
+ add_sc( SA_MAGICROD , SC_MAGICROD );
+ set_sc( SA_AUTOSPELL , SC_AUTOSPELL , SI_AUTOSPELL , SCB_NONE );
+ set_sc( SA_FLAMELAUNCHER , SC_FIREWEAPON , SI_FIREWEAPON , SCB_ATK_ELE );
+ set_sc( SA_FROSTWEAPON , SC_WATERWEAPON , SI_WATERWEAPON , SCB_ATK_ELE );
+ set_sc( SA_LIGHTNINGLOADER , SC_WINDWEAPON , SI_WINDWEAPON , SCB_ATK_ELE );
+ set_sc( SA_SEISMICWEAPON , SC_EARTHWEAPON , SI_EARTHWEAPON , SCB_ATK_ELE );
+ set_sc( SA_VOLCANO , SC_VOLCANO , SI_LANDENDOW , SCB_WATK );
+ set_sc( SA_DELUGE , SC_DELUGE , SI_LANDENDOW , SCB_MAXHP );
+ set_sc( SA_VIOLENTGALE , SC_VIOLENTGALE , SI_LANDENDOW , SCB_FLEE );
+ add_sc( SA_REVERSEORCISH , SC_ORCISH );
+ add_sc( SA_COMA , SC_COMA );
+ set_sc( BD_ENCORE , SC_DANCING , SI_BLANK , SCB_SPEED|SCB_REGEN );
+ add_sc( BD_RICHMANKIM , SC_RICHMANKIM );
+ set_sc( BD_ETERNALCHAOS , SC_ETERNALCHAOS , SI_BLANK , SCB_DEF2 );
+ set_sc( BD_DRUMBATTLEFIELD , SC_DRUMBATTLE , SI_BLANK , SCB_WATK|SCB_DEF );
+ set_sc( BD_RINGNIBELUNGEN , SC_NIBELUNGEN , SI_BLANK , SCB_WATK );
+ add_sc( BD_ROKISWEIL , SC_ROKISWEIL );
+ add_sc( BD_INTOABYSS , SC_INTOABYSS );
+ set_sc( BD_SIEGFRIED , SC_SIEGFRIED , SI_BLANK , SCB_ALL );
+ add_sc( BA_FROSTJOKER , SC_FREEZE );
+ set_sc( BA_WHISTLE , SC_WHISTLE , SI_BLANK , SCB_FLEE|SCB_FLEE2 );
+ set_sc( BA_ASSASSINCROSS , SC_ASSNCROS , SI_BLANK , SCB_ASPD );
+ add_sc( BA_POEMBRAGI , SC_POEMBRAGI );
+ set_sc( BA_APPLEIDUN , SC_APPLEIDUN , SI_BLANK , SCB_MAXHP );
+ add_sc( DC_SCREAM , SC_STUN );
+ set_sc( DC_HUMMING , SC_HUMMING , SI_BLANK , SCB_HIT );
+ set_sc( DC_DONTFORGETME , SC_DONTFORGETME , SI_BLANK , SCB_SPEED|SCB_ASPD );
+ set_sc( DC_FORTUNEKISS , SC_FORTUNE , SI_BLANK , SCB_CRI );
+ set_sc( DC_SERVICEFORYOU , SC_SERVICE4U , SI_BLANK , SCB_ALL );
+ add_sc( NPC_DARKCROSS , SC_BLIND );
+ add_sc( NPC_GRANDDARKNESS , SC_BLIND );
+ set_sc( NPC_STOP , SC_STOP , SI_STOP , SCB_NONE );
+ set_sc( NPC_WEAPONBRAKER , SC_BROKENWEAPON , SI_BROKENWEAPON , SCB_NONE );
+ set_sc( NPC_ARMORBRAKE , SC_BROKENARMOR , SI_BROKENARMOR , SCB_NONE );
+ set_sc( NPC_CHANGEUNDEAD , SC_CHANGEUNDEAD , SI_UNDEAD , SCB_DEF_ELE );
+ set_sc( NPC_POWERUP , SC_INCHITRATE , SI_BLANK , SCB_HIT );
+ set_sc( NPC_AGIUP , SC_INCFLEERATE , SI_BLANK , SCB_FLEE );
+ add_sc( NPC_INVISIBLE , SC_CLOAKING );
+ set_sc( LK_AURABLADE , SC_AURABLADE , SI_AURABLADE , SCB_NONE );
+ set_sc( LK_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE );
+ set_sc( LK_CONCENTRATION , SC_CONCENTRATION , SI_CONCENTRATION , SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_DSPD );
+ set_sc( LK_TENSIONRELAX , SC_TENSIONRELAX , SI_TENSIONRELAX , SCB_REGEN );
+ set_sc( LK_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
+ set_sc( HP_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE );
+ add_sc( HP_BASILICA , SC_BASILICA );
+ set_sc( HW_MAGICPOWER , SC_MAGICPOWER , SI_MAGICPOWER , SCB_MATK );
+ add_sc( PA_SACRIFICE , SC_SACRIFICE );
+ set_sc( PA_GOSPEL , SC_GOSPEL , SI_BLANK , SCB_SPEED|SCB_ASPD );
+ add_sc( PA_GOSPEL , SC_SCRESIST );
+ add_sc( CH_TIGERFIST , SC_STOP );
+ set_sc( ASC_EDP , SC_EDP , SI_EDP , SCB_NONE );
+ set_sc( SN_SIGHT , SC_TRUESIGHT , SI_TRUESIGHT , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_CRI|SCB_HIT );
+ set_sc( SN_WINDWALK , SC_WINDWALK , SI_WINDWALK , SCB_FLEE|SCB_SPEED );
+ set_sc( WS_MELTDOWN , SC_MELTDOWN , SI_MELTDOWN , SCB_NONE );
+ set_sc( WS_CARTBOOST , SC_CARTBOOST , SI_CARTBOOST , SCB_SPEED );
+ set_sc( ST_CHASEWALK , SC_CHASEWALK , SI_BLANK , SCB_SPEED );
+ set_sc( ST_REJECTSWORD , SC_REJECTSWORD , SI_REJECTSWORD , SCB_NONE );
+ add_sc( ST_REJECTSWORD , SC_AUTOCOUNTER );
+ set_sc( CG_MARIONETTE , SC_MARIONETTE , SI_MARIONETTE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
+ set_sc( CG_MARIONETTE , SC_MARIONETTE2 , SI_MARIONETTE2 , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
+ add_sc( LK_SPIRALPIERCE , SC_STOP );
+ add_sc( LK_HEADCRUSH , SC_BLEEDING );
+ set_sc( LK_JOINTBEAT , SC_JOINTBEAT , SI_JOINTBEAT , SCB_BATK|SCB_DEF2|SCB_SPEED|SCB_ASPD );
+ add_sc( HW_NAPALMVULCAN , SC_CURSE );
+ set_sc( PF_MINDBREAKER , SC_MINDBREAKER , SI_BLANK , SCB_MATK|SCB_MDEF2 );
+ add_sc( PF_MEMORIZE , SC_MEMORIZE );
+ add_sc( PF_FOGWALL , SC_FOGWALL );
+ set_sc( PF_SPIDERWEB , SC_SPIDERWEB , SI_BLANK , SCB_FLEE );
+ set_sc( WE_BABY , SC_BABY , SI_BABY , SCB_NONE );
+ set_sc( TK_RUN , SC_RUN , SI_RUN , SCB_SPEED|SCB_DSPD );
+ set_sc( TK_RUN , SC_SPURT , SI_SPURT , SCB_STR );
+ set_sc( TK_READYSTORM , SC_READYSTORM , SI_READYSTORM , SCB_NONE );
+ set_sc( TK_READYDOWN , SC_READYDOWN , SI_READYDOWN , SCB_NONE );
+ add_sc( TK_DOWNKICK , SC_STUN );
+ set_sc( TK_READYTURN , SC_READYTURN , SI_READYTURN , SCB_NONE );
+ set_sc( TK_READYCOUNTER , SC_READYCOUNTER , SI_READYCOUNTER , SCB_NONE );
+ set_sc( TK_DODGE , SC_DODGE , SI_DODGE , SCB_NONE );
+ set_sc( TK_SPTIME , SC_EARTHSCROLL , SI_EARTHSCROLL , SCB_NONE );
+ add_sc( TK_SEVENWIND , SC_SEVENWIND );
+ set_sc( TK_SEVENWIND , SC_GHOSTWEAPON , SI_GHOSTWEAPON , SCB_ATK_ELE );
+ set_sc( TK_SEVENWIND , SC_SHADOWWEAPON , SI_SHADOWWEAPON , SCB_ATK_ELE );
+ set_sc( SG_SUN_WARM , SC_WARM , SI_WARM , SCB_NONE );
+ add_sc( SG_MOON_WARM , SC_WARM );
+ add_sc( SG_STAR_WARM , SC_WARM );
+ set_sc( SG_SUN_COMFORT , SC_SUN_COMFORT , SI_SUN_COMFORT , SCB_DEF2 );
+ set_sc( SG_MOON_COMFORT , SC_MOON_COMFORT , SI_MOON_COMFORT , SCB_FLEE );
+ set_sc( SG_STAR_COMFORT , SC_STAR_COMFORT , SI_STAR_COMFORT , SCB_ASPD );
+ add_sc( SG_FRIEND , SC_SKILLRATE_UP );
+ set_sc( SG_KNOWLEDGE , SC_KNOWLEDGE , SI_BLANK , SCB_ALL );
+ set_sc( SG_FUSION , SC_FUSION , SI_BLANK , SCB_SPEED );
+ set_sc( BS_ADRENALINE2 , SC_ADRENALINE2 , SI_ADRENALINE2 , SCB_ASPD );
+ set_sc( SL_KAIZEL , SC_KAIZEL , SI_KAIZEL , SCB_NONE );
+ set_sc( SL_KAAHI , SC_KAAHI , SI_KAAHI , SCB_NONE );
+ set_sc( SL_KAUPE , SC_KAUPE , SI_KAUPE , SCB_NONE );
+ set_sc( SL_KAITE , SC_KAITE , SI_KAITE , SCB_NONE );
+ add_sc( SL_STUN , SC_STUN );
+ set_sc( SL_SWOO , SC_SWOO , SI_BLANK , SCB_SPEED );
+ set_sc( SL_SKE , SC_SKE , SI_BLANK , SCB_BATK|SCB_WATK|SCB_DEF|SCB_DEF2 );
+ set_sc( SL_SKA , SC_SKA , SI_BLANK , SCB_DEF|SCB_MDEF|SCB_ASPD );
+ set_sc( SL_SMA , SC_SMA , SI_SMA , SCB_NONE );
+ set_sc( SM_SELFPROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
+ set_sc( ST_PRESERVE , SC_PRESERVE , SI_PRESERVE , SCB_NONE );
+ set_sc( PF_DOUBLECASTING , SC_DOUBLECAST , SI_DOUBLECAST , SCB_NONE );
+ set_sc( HW_GRAVITATION , SC_GRAVITATION , SI_BLANK , SCB_ASPD );
+ add_sc( WS_CARTTERMINATION , SC_STUN );
+ set_sc( WS_OVERTHRUSTMAX , SC_MAXOVERTHRUST , SI_MAXOVERTHRUST , SCB_NONE );
+ set_sc( CG_LONGINGFREEDOM , SC_LONGING , SI_BLANK , SCB_SPEED|SCB_ASPD );
+ add_sc( CG_HERMODE , SC_HERMODE );
+ set_sc( ITEM_ENCHANTARMS , SC_ENCHANTARMS , SI_BLANK , SCB_ATK_ELE );
+ set_sc( SL_HIGH , SC_SPIRIT , SI_SPIRIT , SCB_ALL );
+ set_sc( KN_ONEHAND , SC_ONEHAND , SI_ONEHAND , SCB_ASPD );
+ set_sc( GS_FLING , SC_FLING , SI_BLANK , SCB_DEF|SCB_DEF2 );
+ add_sc( GS_CRACKER , SC_STUN );
+ add_sc( GS_DISARM , SC_STRIPWEAPON );
+ add_sc( GS_PIERCINGSHOT , SC_BLEEDING );
+ set_sc( GS_MADNESSCANCEL , SC_MADNESSCANCEL , SI_MADNESSCANCEL , SCB_BATK|SCB_ASPD );
+ set_sc( GS_ADJUSTMENT , SC_ADJUSTMENT , SI_ADJUSTMENT , SCB_HIT|SCB_FLEE );
+ set_sc( GS_INCREASING , SC_INCREASING , SI_ACCURACY , SCB_AGI|SCB_DEX|SCB_HIT );
+ set_sc( GS_GATLINGFEVER , SC_GATLINGFEVER , SI_GATLINGFEVER , SCB_BATK|SCB_FLEE|SCB_SPEED|SCB_ASPD );
+ set_sc( NJ_TATAMIGAESHI , SC_TATAMIGAESHI , SI_BLANK , SCB_NONE );
+ set_sc( NJ_SUITON , SC_SUITON , SI_BLANK , SCB_AGI|SCB_SPEED );
+ add_sc( NJ_HYOUSYOURAKU , SC_FREEZE );
+ set_sc( NJ_NEN , SC_NEN , SI_NEN , SCB_STR|SCB_INT );
+ set_sc( NJ_UTSUSEMI , SC_UTSUSEMI , SI_UTSUSEMI , SCB_NONE );
+ set_sc( NJ_BUNSINJYUTSU , SC_BUNSINJYUTSU , SI_BUNSINJYUTSU , SCB_DYE );
+
+ add_sc( NPC_ICEBREATH , SC_FREEZE );
+ add_sc( NPC_ACIDBREATH , SC_POISON );
+ add_sc( NPC_HELLJUDGEMENT , SC_CURSE );
+ add_sc( NPC_WIDESILENCE , SC_SILENCE );
+ add_sc( NPC_WIDEFREEZE , SC_FREEZE );
+ add_sc( NPC_WIDEBLEEDING , SC_BLEEDING );
+ add_sc( NPC_WIDESTONE , SC_STONE );
+ add_sc( NPC_WIDECONFUSE , SC_CONFUSION );
+ add_sc( NPC_WIDESLEEP , SC_SLEEP );
+ add_sc( NPC_WIDESIGHT , SC_SIGHT );
+ add_sc( NPC_EVILLAND , SC_BLIND );
+ add_sc( NPC_MAGICMIRROR , SC_MAGICMIRROR );
+ set_sc( NPC_SLOWCAST , SC_SLOWCAST , SI_SLOWCAST , SCB_NONE );
+ set_sc( NPC_CRITICALWOUND , SC_CRITICALWOUND , SI_CRITICALWOUND , SCB_NONE );
+ set_sc( NPC_STONESKIN , SC_ARMORCHANGE , SI_BLANK , SCB_DEF|SCB_MDEF );
+ add_sc( NPC_ANTIMAGIC , SC_ARMORCHANGE );
+ add_sc( NPC_WIDECURSE , SC_CURSE );
+ add_sc( NPC_WIDESTUN , SC_STUN );
+
+ set_sc( NPC_HELLPOWER , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE );
+ set_sc( NPC_WIDEHELLDIGNITY , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE );
+ set_sc( NPC_INVINCIBLE , SC_INVINCIBLE , SI_INVINCIBLE , SCB_SPEED );
+ set_sc( NPC_INVINCIBLEOFF , SC_INVINCIBLEOFF , SI_BLANK , SCB_SPEED );
+
+ set_sc( CASH_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
+ 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_NONE );
+ set_sc( ALL_ODINS_POWER , SC_ODINS_POWER , SI_ODINS_POWER , SCB_MATK|SCB_BATK|SCB_MDEF|SCB_DEF );
+
+ set_sc( CR_SHRINK , SC_SHRINK , SI_SHRINK , SCB_NONE );
+ set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE );
+ set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE );
+ set_sc( WZ_SIGHTBLASTER , SC_SIGHTBLASTER , SI_SIGHTBLASTER , SCB_NONE );
+ set_sc( DC_WINKCHARM , SC_WINKCHARM , SI_WINKCHARM , SCB_NONE );
+ add_sc( MO_BALKYOUNG , SC_STUN );
+ add_sc( SA_ELEMENTWATER , SC_ELEMENTALCHANGE );
+ add_sc( SA_ELEMENTFIRE , SC_ELEMENTALCHANGE );
+ add_sc( SA_ELEMENTGROUND , SC_ELEMENTALCHANGE );
+ add_sc( SA_ELEMENTWIND , SC_ELEMENTALCHANGE );
+
+ 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 );
+ set_sc( HFLI_SPEED , SC_SPEED , SI_BLANK , SCB_FLEE );
+ set_sc( HAMI_DEFENCE , SC_DEFENCE , SI_BLANK , SCB_DEF );
+ set_sc( HAMI_BLOODLUST , SC_BLOODLUST , SI_BLANK , SCB_BATK|SCB_WATK );
+
+ // Homunculus S
+ add_sc(MH_STAHL_HORN, SC_STUN);
+ set_sc(MH_ANGRIFFS_MODUS, SC_ANGRIFFS_MODUS, SI_ANGRIFFS_MODUS, SCB_BATK | SCB_DEF | SCB_FLEE | SCB_MAXHP);
+ set_sc(MH_GOLDENE_FERSE, SC_GOLDENE_FERSE, SI_GOLDENE_FERSE, SCB_ASPD|SCB_MAXHP);
+ add_sc( MH_STEINWAND, SC_SAFETYWALL );
+ add_sc(MH_ERASER_CUTTER, SC_ERASER_CUTTER);
+ set_sc(MH_OVERED_BOOST, SC_OVERED_BOOST, SI_BLANK, SCB_FLEE|SCB_ASPD);
+ add_sc(MH_LIGHT_OF_REGENE, SC_LIGHT_OF_REGENE);
+ set_sc(MH_VOLCANIC_ASH, SC_ASH, SI_VOLCANIC_ASH, SCB_DEF|SCB_DEF2|SCB_HIT|SCB_BATK|SCB_FLEE);
+ set_sc(MH_GRANITIC_ARMOR, SC_GRANITIC_ARMOR, SI_GRANITIC_ARMOR, SCB_NONE);
+ set_sc(MH_MAGMA_FLOW, SC_MAGMA_FLOW, SI_MAGMA_FLOW, SCB_NONE);
+ set_sc(MH_PYROCLASTIC, SC_PYROCLASTIC, SI_PYROCLASTIC, SCB_BATK|SCB_ATK_ELE);
+ add_sc(MH_LAVA_SLIDE, SC_BURNING);
+ set_sc(MH_NEEDLE_OF_PARALYZE, SC_PARALYSIS, SI_NEEDLE_OF_PARALYZE, SCB_DEF2);
+ add_sc(MH_POISON_MIST, SC_BLIND);
+ set_sc(MH_PAIN_KILLER, SC_PAIN_KILLER, SI_PAIN_KILLER, SCB_ASPD);
+
+ add_sc(MH_STYLE_CHANGE, SC_STYLE_CHANGE);
+ set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE );
+ set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE );
+
+
+ add_sc( MER_CRASH , SC_STUN );
+ set_sc( MER_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
+ add_sc( MS_MAGNUM , SC_WATK_ELEMENT );
+ add_sc( MER_SIGHT , SC_SIGHT );
+ set_sc( MER_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED );
+ set_sc( MER_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN );
+ add_sc( MER_LEXDIVINA , SC_SILENCE );
+ add_sc( MA_LANDMINE , SC_STUN );
+ add_sc( MA_SANDMAN , SC_SLEEP );
+ add_sc( MA_FREEZINGTRAP , SC_FREEZE );
+ set_sc( MER_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE );
+ set_sc( ML_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE );
+ set_sc( MS_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE );
+ set_sc( ML_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD );
+ set_sc( MS_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE );
+ set_sc( MS_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
+ add_sc( ML_SPIRALPIERCE , SC_STOP );
+ set_sc( MER_QUICKEN , SC_MERC_QUICKEN , SI_BLANK , SCB_ASPD );
+ add_sc( ML_DEVOTION , SC_DEVOTION );
+ set_sc( MER_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE );
+ set_sc( MER_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
+ set_sc( MER_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
+
+ set_sc( GD_LEADERSHIP , SC_LEADERSHIP , SI_BLANK , SCB_STR );
+ set_sc( GD_GLORYWOUNDS , SC_GLORYWOUNDS , SI_BLANK , SCB_VIT );
+ set_sc( GD_SOULCOLD , SC_SOULCOLD , SI_BLANK , SCB_AGI );
+ set_sc( GD_HAWKEYES , SC_HAWKEYES , SI_BLANK , 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 );
+ set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , SI_CRUSHSTRIKE , SCB_NONE );
+ /**
+ * GC Guillotine Cross
+ **/
+ set_sc_with_vfx( 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_with_vfx( 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_with_vfx( 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_MATK);
+ 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_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_with_vfx( 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 );
+ /**
+ * 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_DEF );
+ 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);
+ set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_DEF , SI_SHIELDSPELL_DEF , SCB_WATK );
+ set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_REF , SI_SHIELDSPELL_REF , SCB_DEF );
+ /**
+ * 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_BODYPAINT , 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_STRIPACCESSARY , SCB_DEX|SCB_INT|SCB_LUK );
+ set_sc_with_vfx( SC_MANHOLE , SC__MANHOLE , SI_MANHOLE , SCB_NONE );
+ add_sc( SC_CHAOSPANIC , SC_CONFUSION );
+ set_sc_with_vfx( SC_BLOODYLUST , SC__BLOODYLUST , SI_BLOODYLUST , SCB_DEF | SCB_DEF2 | SCB_MDEF | SCB_MDEF2 | SCB_FLEE | SCB_SPEED | SCB_ASPD | SCB_MAXHP | SCB_REGEN );
+ /**
+ * 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_with_vfx( 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 );
+ 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_ASPD|SCB_MDEF|SCB_MAXHP );
+ set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GT_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_REGEN );
+ /**
+ * Wanderer / Minstrel
+ **/
+ 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_with_vfx( WM_POEMOFNETHERWORLD , SC_NETHERWORLD , SI_NETHERWORLD , SCB_NONE );
+ set_sc_with_vfx( WM_VOICEOFSIREN , SC_VOICEOFSIREN , SI_VOICEOFSIREN , SCB_NONE );
+ set_sc_with_vfx( 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_with_vfx( SO_DIAMONDDUST , SC_CRYSTALIZE , SI_COLD , SCB_NONE ); // it does show the snow icon on mobs but doesn't affect it.
+ add_sc( SO_CLOUD_KILL , SC_POISON );
+ 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 );
+ set_sc( SO_FIRE_INSIGNIA , SC_FIRE_INSIGNIA , SI_FIRE_INSIGNIA , SCB_MATK | SCB_BATK | SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ set_sc( SO_WATER_INSIGNIA , SC_WATER_INSIGNIA , SI_WATER_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ set_sc( SO_WIND_INSIGNIA , SC_WIND_INSIGNIA , SI_WIND_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ set_sc( SO_EARTH_INSIGNIA , SC_EARTH_INSIGNIA , SI_EARTH_INSIGNIA , SCB_MDEF|SCB_DEF|SCB_MAXHP|SCB_MAXSP|SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ /**
+ * 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_with_vfx( 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 );
+
+ // Elemental Spirit summoner's 'side' status changes.
+ set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SI_CIRCLE_OF_FIRE_OPTION, SCB_NONE );
+ set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SI_FIRE_CLOAK_OPTION , SCB_ALL );
+ set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SI_WATER_SCREEN_OPTION , SCB_NONE );
+ set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SI_WATER_DROP_OPTION , SCB_ALL );
+ set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SI_WATER_BARRIER , SCB_MDEF|SCB_WATK|SCB_MATK|SCB_FLEE );
+ set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SI_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE );
+ set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SI_WIND_CURTAIN_OPTION , SCB_ALL );
+ set_sc( EL_ZEPHYR , SC_ZEPHYR , SI_ZEPHYR , SCB_FLEE );
+ set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SI_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP );
+ set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SI_STONE_SHIELD_OPTION , SCB_ALL );
+ set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SI_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED );
+ set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SI_PYROTECHNIC_OPTION , SCB_WATK );
+ set_sc( EL_HEATER , SC_HEATER_OPTION , SI_HEATER_OPTION , SCB_WATK );
+ set_sc( EL_TROPIC , SC_TROPIC_OPTION , SI_TROPIC_OPTION , SCB_WATK );
+ set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SI_AQUAPLAY_OPTION , SCB_MATK );
+ set_sc( EL_COOLER , SC_COOLER_OPTION , SI_COOLER_OPTION , SCB_MATK );
+ set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SI_CHILLY_AIR_OPTION , SCB_MATK );
+ set_sc( EL_GUST , SC_GUST_OPTION , SI_GUST_OPTION , SCB_ASPD );
+ set_sc( EL_BLAST , SC_BLAST_OPTION , SI_BLAST_OPTION , SCB_ASPD );
+ set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SI_WILD_STORM_OPTION , SCB_ASPD );
+ set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SI_PETROLOGY_OPTION , SCB_MAXHP );
+ set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SI_CURSED_SOIL_OPTION , SCB_NONE );
+ set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SI_UPHEAVAL_OPTION , SCB_NONE );
+ set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SI_TIDAL_WEAPON_OPTION , SCB_ALL );
+ set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SI_ROCK_CRUSHER , SCB_DEF );
+ set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SI_ROCK_CRUSHER_ATK , SCB_SPEED );
+
+ add_sc( KO_YAMIKUMO , SC_HIDING );
+ set_sc_with_vfx( KO_JYUMONJIKIRI , SC_JYUMONJIKIRI , SI_KO_JYUMONJIKIRI , SCB_NONE );
+ add_sc( KO_MAKIBISHI , SC_STUN );
+ set_sc( KO_MEIKYOUSISUI , SC_MEIKYOUSISUI , SI_MEIKYOUSISUI , SCB_NONE );
+ set_sc( KO_KYOUGAKU , SC_KYOUGAKU , SI_KYOUGAKU , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
+ add_sc( KO_JYUSATSU , SC_CURSE );
+ set_sc( KO_ZENKAI , SC_ZENKAI , SI_ZENKAI , SCB_NONE );
+ set_sc( KO_IZAYOI , SC_IZAYOI , SI_IZAYOI , SCB_MATK );
+ set_sc( KG_KYOMU , SC_KYOMU , SI_KYOMU , SCB_NONE );
+ set_sc( KG_KAGEMUSYA , SC_KAGEMUSYA , SI_KAGEMUSYA , SCB_NONE );
+ set_sc( KG_KAGEHUMI , SC_KAGEHUMI , SI_KG_KAGEHUMI , SCB_NONE );
+ set_sc( OB_ZANGETSU , SC_ZANGETSU , SI_ZANGETSU , SCB_MATK|SCB_BATK );
+ set_sc_with_vfx( OB_AKAITSUKI , SC_AKAITSUKI , SI_AKAITSUKI , SCB_NONE );
+ set_sc( OB_OBOROGENSOU , SC_GENSOU , SI_GENSOU , SCB_NONE );
+
+ // Storing the target job rather than simply SC_SPIRIT simplifies code later on.
+ SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST,
+ SkillStatusChangeTable[SL_MONK] = (sc_type)MAPID_MONK,
+ SkillStatusChangeTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR,
+ SkillStatusChangeTable[SL_SAGE] = (sc_type)MAPID_SAGE,
+ SkillStatusChangeTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER,
+ SkillStatusChangeTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE,
+ SkillStatusChangeTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT,
+ SkillStatusChangeTable[SL_WIZARD] = (sc_type)MAPID_WIZARD,
+ SkillStatusChangeTable[SL_PRIEST] = (sc_type)MAPID_PRIEST,
+ SkillStatusChangeTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER,
+ SkillStatusChangeTable[SL_ROGUE] = (sc_type)MAPID_ROGUE,
+ SkillStatusChangeTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN,
+ SkillStatusChangeTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH,
+ SkillStatusChangeTable[SL_HUNTER] = (sc_type)MAPID_HUNTER,
+ SkillStatusChangeTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER,
+
+ //Status that don't have a skill associated.
+ StatusIconChangeTable[SC_WEIGHT50] = SI_WEIGHT50;
+ StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90;
+ StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION0;
+ StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION1;
+ StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION2;
+ StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTIONINFINITY;
+ StatusIconChangeTable[SC_SPEEDUP0] = SI_MOVHASTE_HORSE;
+ StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION1;
+ StatusIconChangeTable[SC_INCSTR] = SI_INCSTR;
+ StatusIconChangeTable[SC_MIRACLE] = SI_SPIRIT;
+ StatusIconChangeTable[SC_INTRAVISION] = SI_INTRAVISION;
+ StatusIconChangeTable[SC_STRFOOD] = SI_FOODSTR;
+ StatusIconChangeTable[SC_AGIFOOD] = SI_FOODAGI;
+ StatusIconChangeTable[SC_VITFOOD] = SI_FOODVIT;
+ StatusIconChangeTable[SC_INTFOOD] = SI_FOODINT;
+ StatusIconChangeTable[SC_DEXFOOD] = SI_FOODDEX;
+ StatusIconChangeTable[SC_LUKFOOD] = SI_FOODLUK;
+ StatusIconChangeTable[SC_FLEEFOOD]= SI_FOODFLEE;
+ StatusIconChangeTable[SC_HITFOOD] = SI_FOODHIT;
+ StatusIconChangeTable[SC_MANU_ATK] = SI_MANU_ATK;
+ StatusIconChangeTable[SC_MANU_DEF] = SI_MANU_DEF;
+ StatusIconChangeTable[SC_SPL_ATK] = SI_SPL_ATK;
+ StatusIconChangeTable[SC_SPL_DEF] = SI_SPL_DEF;
+ StatusIconChangeTable[SC_MANU_MATK] = SI_MANU_MATK;
+ StatusIconChangeTable[SC_SPL_MATK] = SI_SPL_MATK;
+ StatusIconChangeTable[SC_ATKPOTION] = SI_PLUSATTACKPOWER;
+ StatusIconChangeTable[SC_MATKPOTION] = SI_PLUSMAGICPOWER;
+ //Cash Items
+ StatusIconChangeTable[SC_FOOD_STR_CASH] = SI_FOOD_STR_CASH;
+ StatusIconChangeTable[SC_FOOD_AGI_CASH] = SI_FOOD_AGI_CASH;
+ StatusIconChangeTable[SC_FOOD_VIT_CASH] = SI_FOOD_VIT_CASH;
+ StatusIconChangeTable[SC_FOOD_DEX_CASH] = SI_FOOD_DEX_CASH;
+ StatusIconChangeTable[SC_FOOD_INT_CASH] = SI_FOOD_INT_CASH;
+ StatusIconChangeTable[SC_FOOD_LUK_CASH] = SI_FOOD_LUK_CASH;
+ StatusIconChangeTable[SC_EXPBOOST] = SI_EXPBOOST;
+ StatusIconChangeTable[SC_ITEMBOOST] = SI_ITEMBOOST;
+ StatusIconChangeTable[SC_JEXPBOOST] = SI_CASH_PLUSONLYJOBEXP;
+ StatusIconChangeTable[SC_LIFEINSURANCE] = SI_LIFEINSURANCE;
+ StatusIconChangeTable[SC_BOSSMAPINFO] = SI_BOSSMAPINFO;
+ StatusIconChangeTable[SC_DEF_RATE] = SI_DEF_RATE;
+ StatusIconChangeTable[SC_MDEF_RATE] = SI_MDEF_RATE;
+ StatusIconChangeTable[SC_INCCRI] = SI_INCCRI;
+ StatusIconChangeTable[SC_INCFLEE2] = SI_PLUSAVOIDVALUE;
+ StatusIconChangeTable[SC_INCHEALRATE] = SI_INCHEALRATE;
+ StatusIconChangeTable[SC_S_LIFEPOTION] = SI_S_LIFEPOTION;
+ StatusIconChangeTable[SC_L_LIFEPOTION] = SI_L_LIFEPOTION;
+ StatusIconChangeTable[SC_SPCOST_RATE] = SI_ATKER_BLOOD;
+ StatusIconChangeTable[SC_COMMONSC_RESIST] = SI_TARGET_BLOOD;
+ // Mercenary Bonus Effects
+ StatusIconChangeTable[SC_MERC_FLEEUP] = SI_MERC_FLEEUP;
+ StatusIconChangeTable[SC_MERC_ATKUP] = SI_MERC_ATKUP;
+ 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;
+ // Warlock Preserved spells
+ StatusIconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1;
+ StatusIconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2;
+ StatusIconChangeTable[SC_SPELLBOOK3] = SI_SPELLBOOK3;
+ StatusIconChangeTable[SC_SPELLBOOK4] = SI_SPELLBOOK4;
+ StatusIconChangeTable[SC_SPELLBOOK5] = SI_SPELLBOOK5;
+ StatusIconChangeTable[SC_SPELLBOOK6] = SI_SPELLBOOK6;
+ StatusIconChangeTable[SC_MAXSPELLBOOK] = SI_SPELLBOOK7;
+
+ 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;
+ StatusIconChangeTable[SC_PUSH_CART] = SI_ON_PUSH_CART;
+
+ //Other SC which are not necessarily associated to skills.
+ StatusChangeFlagTable[SC_ASPDPOTION0] = SCB_ASPD;
+ StatusChangeFlagTable[SC_ASPDPOTION1] = SCB_ASPD;
+ StatusChangeFlagTable[SC_ASPDPOTION2] = SCB_ASPD;
+ StatusChangeFlagTable[SC_ASPDPOTION3] = SCB_ASPD;
+ StatusChangeFlagTable[SC_SPEEDUP0] = SCB_SPEED;
+ StatusChangeFlagTable[SC_SPEEDUP1] = SCB_SPEED;
+ StatusChangeFlagTable[SC_ATKPOTION] = SCB_BATK;
+ StatusChangeFlagTable[SC_MATKPOTION] = SCB_MATK;
+ StatusChangeFlagTable[SC_INCALLSTATUS] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK;
+ StatusChangeFlagTable[SC_INCSTR] |= SCB_STR;
+ StatusChangeFlagTable[SC_INCAGI] |= SCB_AGI;
+ StatusChangeFlagTable[SC_INCVIT] |= SCB_VIT;
+ StatusChangeFlagTable[SC_INCINT] |= SCB_INT;
+ StatusChangeFlagTable[SC_INCDEX] |= SCB_DEX;
+ StatusChangeFlagTable[SC_INCLUK] |= SCB_LUK;
+ StatusChangeFlagTable[SC_INCHIT] |= SCB_HIT;
+ StatusChangeFlagTable[SC_INCHITRATE] |= SCB_HIT;
+ StatusChangeFlagTable[SC_INCFLEE] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_INCFLEERATE] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_INCCRI] |= SCB_CRI;
+ StatusChangeFlagTable[SC_INCASPDRATE] |= SCB_ASPD;
+ StatusChangeFlagTable[SC_INCFLEE2] |= SCB_FLEE2;
+ StatusChangeFlagTable[SC_INCMHPRATE] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_INCMSPRATE] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_INCMHP] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_INCMSP] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_INCATKRATE] |= SCB_BATK|SCB_WATK;
+ StatusChangeFlagTable[SC_INCMATKRATE] |= SCB_MATK;
+ StatusChangeFlagTable[SC_INCDEFRATE] |= SCB_DEF;
+ StatusChangeFlagTable[SC_STRFOOD] |= SCB_STR;
+ StatusChangeFlagTable[SC_AGIFOOD] |= SCB_AGI;
+ StatusChangeFlagTable[SC_VITFOOD] |= SCB_VIT;
+ StatusChangeFlagTable[SC_INTFOOD] |= SCB_INT;
+ StatusChangeFlagTable[SC_DEXFOOD] |= SCB_DEX;
+ StatusChangeFlagTable[SC_LUKFOOD] |= SCB_LUK;
+ StatusChangeFlagTable[SC_HITFOOD] |= SCB_HIT;
+ StatusChangeFlagTable[SC_FLEEFOOD] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_BATKFOOD] |= SCB_BATK;
+ StatusChangeFlagTable[SC_WATKFOOD] |= SCB_WATK;
+ StatusChangeFlagTable[SC_MATKFOOD] |= SCB_MATK;
+ StatusChangeFlagTable[SC_ARMOR_ELEMENT] |= SCB_ALL;
+ StatusChangeFlagTable[SC_ARMOR_RESIST] |= SCB_ALL;
+ StatusChangeFlagTable[SC_SPCOST_RATE] |= SCB_ALL;
+ StatusChangeFlagTable[SC_WALKSPEED] |= SCB_SPEED;
+ StatusChangeFlagTable[SC_ITEMSCRIPT] |= SCB_ALL;
+ // Cash Items
+ StatusChangeFlagTable[SC_FOOD_STR_CASH] = SCB_STR;
+ StatusChangeFlagTable[SC_FOOD_AGI_CASH] = SCB_AGI;
+ StatusChangeFlagTable[SC_FOOD_VIT_CASH] = SCB_VIT;
+ StatusChangeFlagTable[SC_FOOD_DEX_CASH] = SCB_DEX;
+ StatusChangeFlagTable[SC_FOOD_INT_CASH] = SCB_INT;
+ StatusChangeFlagTable[SC_FOOD_LUK_CASH] = SCB_LUK;
+ // Mercenary Bonus Effects
+ StatusChangeFlagTable[SC_MERC_FLEEUP] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_MERC_ATKUP] |= SCB_WATK;
+ StatusChangeFlagTable[SC_MERC_HPUP] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_MERC_SPUP] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_MERC_HITUP] |= SCB_HIT;
+ // Guillotine Cross Poison Effects
+ StatusChangeFlagTable[SC_PARALYSE] |= SCB_ASPD|SCB_FLEE|SCB_SPEED;
+ StatusChangeFlagTable[SC_DEATHHURT] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_VENOMBLEED] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN;
+
+ StatusChangeFlagTable[SC_SAVAGE_STEAK] |= SCB_STR;
+ StatusChangeFlagTable[SC_COCKTAIL_WARG_BLOOD] |= SCB_INT;
+ StatusChangeFlagTable[SC_MINOR_BBQ] |= SCB_VIT;
+ StatusChangeFlagTable[SC_SIROMA_ICE_TEA] |= SCB_DEX;
+ StatusChangeFlagTable[SC_DROCERA_HERB_STEAMED] |= SCB_AGI;
+ StatusChangeFlagTable[SC_PUTTI_TAILS_NOODLES] |= SCB_LUK;
+ StatusChangeFlagTable[SC_BOOST500] |= SCB_ASPD;
+ StatusChangeFlagTable[SC_FULL_SWING_K] |= SCB_BATK;
+ StatusChangeFlagTable[SC_MANA_PLUS] |= SCB_MATK;
+ StatusChangeFlagTable[SC_MUSTLE_M] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_LIFE_FORCE_F] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_EXTRACT_WHITE_POTION_Z] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_VITATA_500] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_EXTRACT_SALAMINE_JUICE] |= SCB_ASPD;
+
+#ifdef RENEWAL_EDP
+ // renewal EDP increases your weapon atk
+ StatusChangeFlagTable[SC_EDP] |= SCB_WATK;
+#endif
+
+ if( !battle_config.display_hallucination ) //Disable Hallucination.
+ StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK;
+
+ /* StatusChangeState (SCS_) NOMOVE */
+ StatusChangeStateTable[SC_ANKLE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_BLADESTOP_WAIT] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_GOSPEL] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_BASILICA] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_STOP] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CLOSECONFINE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CLOSECONFINE2] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_MADNESSCANCEL] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_GRAVITATION] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_ELECTRICSHOCKER] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_BITE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_THORNSTRAP] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_MAGNETICFIELD] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC__MANHOLE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CURSEDCIRCLE_ATKER] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_NETHERWORLD] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CAMOUFLAGE] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_MEIKYOUSISUI] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_KAGEHUMI] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_KYOUGAKU] |= SCS_NOMOVE;
+
+ /* StatusChangeState (SCS_) NOPICKUPITEMS */
+ StatusChangeStateTable[SC_HIDING] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_CLOAKING] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_CLOAKINGEXCEED] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_NOCHAT] |= SCS_NOPICKITEM|SCS_NOPICKITEMCOND;
+
+ /* StatusChangeState (SCS_) NODROPITEMS */
+ StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NODROPITEM;
+ StatusChangeStateTable[SC_BLADESTOP] |= SCS_NODROPITEM;
+ StatusChangeStateTable[SC_NOCHAT] |= SCS_NODROPITEM|SCS_NODROPITEMCOND;
+
+ /* StatusChangeState (SCS_) NOCAST (skills) */
+ StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_STEELBODY] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_BERSERK] |= SCS_NOCAST;
+ StatusChangeStateTable[SC__BLOODYLUST] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_OBLIVIONCURSE] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOCAST;
+ StatusChangeStateTable[SC__INVISIBILITY] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOCAST|SCS_NOCASTCOND;
+ StatusChangeStateTable[SC__IGNORANCE] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_DEEPSLEEP] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_SATURDAYNIGHTFEVER] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST;
+
+ //Homon S
+ StatusChangeStateTable[SC_PARALYSIS] |= SCS_NOMOVE;
+
+}
+
+static void initDummyData(void)
+{
+ memset(&dummy_status, 0, sizeof(dummy_status));
+ dummy_status.hp =
+ dummy_status.max_hp =
+ dummy_status.max_sp =
+ dummy_status.str =
+ dummy_status.agi =
+ dummy_status.vit =
+ dummy_status.int_ =
+ dummy_status.dex =
+ dummy_status.luk =
+ dummy_status.hit = 1;
+ dummy_status.speed = 2000;
+ dummy_status.adelay = 4000;
+ dummy_status.amotion = 2000;
+ dummy_status.dmotion = 2000;
+ dummy_status.ele_lv = 1; //Min elemental level.
+ 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)));
+}
+
+//Sets HP to given value. Flag is the flag passed to status_heal in case
+//final value is higher than current (use 2 to make a healing effect display
+//on players) It will always succeed (overrides Berserk block), but it can't kill.
+int status_set_hp(struct block_list *bl, unsigned int hp, int flag)
+{
+ struct status_data *status;
+ if (hp < 1) return 0;
+ status = status_get_status_data(bl);
+ if (status == &dummy_status)
+ return 0;
+
+ if (hp > status->max_hp) hp = status->max_hp;
+ if (hp == status->hp) return 0;
+ if (hp > status->hp)
+ return status_heal(bl, hp - status->hp, 0, 1|flag);
+ return status_zap(bl, status->hp - hp, 0);
+}
+
+//Sets SP to given value. Flag is the flag passed to status_heal in case
+//final value is higher than current (use 2 to make a healing effect display
+//on players)
+int status_set_sp(struct block_list *bl, unsigned int sp, int flag)
+{
+ struct status_data *status;
+
+ status = status_get_status_data(bl);
+ if (status == &dummy_status)
+ return 0;
+
+ if (sp > status->max_sp) sp = status->max_sp;
+ if (sp == status->sp) return 0;
+ if (sp > status->sp)
+ return status_heal(bl, 0, sp - status->sp, 1|flag);
+ return status_zap(bl, 0, status->sp - sp);
+}
+
+int status_charge(struct block_list* bl, int hp, int sp)
+{
+ if(!(bl->type&BL_CONSUME))
+ return hp+sp; //Assume all was charged so there are no 'not enough' fails.
+ return status_damage(NULL, bl, hp, sp, 0, 3);
+}
+
+//Inflicts damage on the target with the according walkdelay.
+//If flag&1, damage is passive and does not triggers cancelling status changes.
+//If flag&2, fail if target does not has enough to substract.
+//If flag&4, if killed, mob must not give exp/loot.
+//flag will be set to &8 when damaging sp of a dead character
+int status_damage(struct block_list *src,struct block_list *target,int hp, int sp, int walkdelay, int flag)
+{
+ struct status_data *status;
+ struct status_change *sc;
+
+ if(sp && !(target->type&BL_CONSUME))
+ sp = 0; //Not a valid SP target.
+
+ if (hp < 0) { //Assume absorbed damage.
+ status_heal(target, -hp, 0, 1);
+ hp = 0;
+ }
+
+ if (sp < 0) {
+ status_heal(target, 0, -sp, 1);
+ sp = 0;
+ }
+
+ if (target->type == BL_SKILL)
+ return skill_unit_ondamaged((struct skill_unit *)target, src, hp, gettick());
+
+ status = status_get_status_data(target);
+ if( status == &dummy_status )
+ return 0;
+
+ if ((unsigned int)hp >= status->hp) {
+ if (flag&2) return 0;
+ hp = status->hp;
+ }
+
+ if ((unsigned int)sp > status->sp) {
+ if (flag&2) return 0;
+ sp = status->sp;
+ }
+
+ if (!hp && !sp)
+ return 0;
+
+ if( !status->hp )
+ flag |= 8;
+
+// Let through. battle.c/skill.c have the whole logic of when it's possible or
+// not to hurt someone (and this check breaks pet catching) [Skotlex]
+// if (!target->prev && !(flag&2))
+// return 0; //Cannot damage a bl not on a map, except when "charging" hp/sp
+
+ sc = status_get_sc(target);
+ if( hp && battle_config.invincible_nodamage && src && sc && sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
+ hp = 1;
+
+ if( hp && !(flag&1) ) {
+ if( sc ) {
+ struct status_change_entry *sce;
+ if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ status_change_end(target, SC_STONE, INVALID_TIMER);
+ status_change_end(target, SC_FREEZE, INVALID_TIMER);
+ status_change_end(target, SC_SLEEP, INVALID_TIMER);
+ status_change_end(target, SC_WINKCHARM, INVALID_TIMER);
+ status_change_end(target, SC_CONFUSION, INVALID_TIMER);
+ status_change_end(target, SC_TRICKDEAD, INVALID_TIMER);
+ 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);
+ status_change_end(target, SC__INVISIBILITY, INVALID_TIMER);
+ status_change_end(target, SC_DEEPSLEEP, 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]
+ if (src && src->type != BL_PC && !map_flag_gvg(target->m) && !map[target->m].flag.battleground && --(sce->val2) < 0)
+ status_change_end(target, SC_ENDURE, INVALID_TIMER);
+ }
+ if ((sce=sc->data[SC_GRAVITATION]) && sce->val3 == BCT_SELF) {
+ struct skill_unit_group* sg = skill_id2group(sce->val4);
+ if (sg) {
+ skill_delunitgroup(sg);
+ sce->val4 = 0;
+ status_change_end(target, SC_GRAVITATION, INVALID_TIMER);
+ }
+ }
+ 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, INVALID_TIMER);
+ if(sc->data[SC_KAGEMUSYA] && --(sc->data[SC_KAGEMUSYA]->val3) <= 0)
+ status_change_end(target, SC_KAGEMUSYA, INVALID_TIMER);
+ }
+ unit_skillcastcancel(target, 2);
+ }
+
+ status->hp-= hp;
+ status->sp-= sp;
+
+ if (sc && hp && status->hp) {
+ if (sc->data[SC_AUTOBERSERK] &&
+ (!sc->data[SC_PROVOKE] || !sc->data[SC_PROVOKE]->val2) &&
+ status->hp < status->max_hp>>2)
+ sc_start4(target,SC_PROVOKE,100,10,1,0,0,0);
+ if (sc->data[SC_BERSERK] && status->hp <= 100)
+ status_change_end(target, SC_BERSERK, INVALID_TIMER);
+ if( sc->data[SC_RAISINGDRAGON] && status->hp <= 1000 )
+ status_change_end(target, SC_RAISINGDRAGON, INVALID_TIMER);
+ if (sc->data[SC_SATURDAYNIGHTFEVER] && status->hp <= 100)
+ status_change_end(target, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
+ if (sc->data[SC__BLOODYLUST] && status->hp <= 100)
+ status_change_end(target, SC__BLOODYLUST, INVALID_TIMER);
+ }
+
+ switch (target->type) {
+ case BL_PC: pc_damage((TBL_PC*)target,src,hp,sp); break;
+ case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break;
+ case BL_HOM: merc_damage((TBL_HOM*)target); break;
+ case BL_MER: mercenary_heal((TBL_MER*)target,hp,sp); break;
+ case BL_ELEM: elemental_heal((TBL_ELEM*)target,hp,sp); break;
+ }
+
+ if( src && target->type == BL_PC && ((TBL_PC*)target)->disguise ) {// stop walking when attacked in disguise to prevent walk-delay bug
+ unit_stop_walking( target, 1 );
+ }
+
+ if( status->hp || (flag&8) )
+ { //Still lives or has been dead before this damage.
+ if (walkdelay)
+ unit_set_walkdelay(target, gettick(), walkdelay, 0);
+ return hp+sp;
+ }
+
+ status->hp = 1; //To let the dead function cast skills and all that.
+ //NOTE: These dead functions should return: [Skotlex]
+ //0: Death cancelled, auto-revived.
+ //Non-zero: Standard death. Clear status, cancel move/attack, etc
+ //&2: Also remove object from map.
+ //&4: Also delete object from memory.
+ switch (target->type) {
+ case BL_PC: flag = pc_dead((TBL_PC*)target,src); break;
+ case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break;
+ case BL_HOM: flag = merc_hom_dead((TBL_HOM*)target); break;
+ case BL_MER: flag = mercenary_dead((TBL_MER*)target); break;
+ case BL_ELEM: flag = elemental_dead((TBL_ELEM*)target); break;
+ default: //Unhandled case, do nothing to object.
+ flag = 0;
+ break;
+ }
+
+ if(!flag) //Death cancelled.
+ return hp+sp;
+
+ //Normal death
+ status->hp = 0;
+ if (battle_config.clear_unit_ondeath &&
+ battle_config.clear_unit_ondeath&target->type)
+ skill_clear_unitgroup(target);
+
+ if(target->type&BL_REGEN)
+ { //Reset regen ticks.
+ struct regen_data *regen = status_get_regen_data(target);
+ if (regen) {
+ memset(®en->tick, 0, sizeof(regen->tick));
+ if (regen->sregen)
+ memset(®en->sregen->tick, 0, sizeof(regen->sregen->tick));
+ if (regen->ssregen)
+ memset(®en->ssregen->tick, 0, sizeof(regen->ssregen->tick));
+ }
+ }
+
+ if( sc && sc->data[SC_KAIZEL] && !map_flag_gvg(target->m) )
+ { //flag&8 = disable Kaizel
+ int time = skill_get_time2(SL_KAIZEL,sc->data[SC_KAIZEL]->val1);
+ //Look for Osiris Card's bonus effect on the character and revive 100% or revive normally
+ if ( target->type == BL_PC && BL_CAST(BL_PC,target)->special_state.restart_full_recover )
+ status_revive(target, 100, 100);
+ else
+ status_revive(target, sc->data[SC_KAIZEL]->val2, 0);
+ status_change_clear(target,0);
+ clif_skill_nodamage(target,target,ALL_RESURRECTION,1,1);
+ sc_start(target,status_skill2sc(PR_KYRIE),100,10,time);
+
+ if( target->type == BL_MOB )
+ ((TBL_MOB*)target)->state.rebirth = 1;
+
+ return hp+sp;
+ }
+ if(target->type == BL_PC){
+ TBL_PC *sd = BL_CAST(BL_PC,target);
+ TBL_HOM *hd = sd->hd;
+ if(hd && hd->sc.data[SC_LIGHT_OF_REGENE]){
+ clif_skillcasting(&hd->bl, hd->bl.id, target->id, 0,0, MH_LIGHT_OF_REGENE, skill_get_ele(MH_LIGHT_OF_REGENE, 1), 10); //just to display usage
+ clif_skill_nodamage(&sd->bl, target, ALL_RESURRECTION, 1, status_revive(&sd->bl,10*hd->sc.data[SC_LIGHT_OF_REGENE]->val1,0));
+ status_change_end(&sd->hd->bl,SC_LIGHT_OF_REGENE,INVALID_TIMER);
+ return hp + sp;
+ }
+ }
+ if (target->type == BL_MOB && sc && sc->data[SC_REBIRTH] && !((TBL_MOB*) target)->state.rebirth) {// Ensure the monster has not already rebirthed before doing so.
+ status_revive(target, sc->data[SC_REBIRTH]->val2, 0);
+ status_change_clear(target,0);
+ ((TBL_MOB*)target)->state.rebirth = 1;
+
+ return hp+sp;
+ }
+
+ status_change_clear(target,0);
+
+ if(flag&4) //Delete from memory. (also invokes map removal code)
+ unit_free(target,CLR_DEAD);
+ else
+ if(flag&2) //remove from map
+ unit_remove_map(target,CLR_DEAD);
+ else
+ { //Some death states that would normally be handled by unit_remove_map
+ unit_stop_attack(target);
+ unit_stop_walking(target,1);
+ unit_skillcastcancel(target,0);
+ clif_clearunit_area(target,CLR_DEAD);
+ skill_unit_move(target,gettick(),4);
+ skill_cleartimerskill(target);
+ }
+
+ return hp+sp;
+}
+
+//Heals a character. If flag&1, this is forced healing (otherwise stuff like Berserk can block it)
+//If flag&2, when the player is healed, show the HP/SP heal effect.
+int status_heal(struct block_list *bl,int hp,int sp, int flag)
+{
+ struct status_data *status;
+ struct status_change *sc;
+
+ status = status_get_status_data(bl);
+
+ if (status == &dummy_status || !status->hp)
+ return 0;
+
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL;
+
+ if (hp < 0) {
+ if (hp == INT_MIN) hp++; //-INT_MIN == INT_MIN in some architectures!
+ status_damage(NULL, bl, -hp, 0, 0, 1);
+ hp = 0;
+ }
+
+ if(hp) {
+ if( sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) ) {
+ if( flag&1 )
+ flag &= ~2;
+ else
+ hp = 0;
+ }
+
+ if((unsigned int)hp > status->max_hp - status->hp)
+ hp = status->max_hp - status->hp;
+ }
+
+ if(sp < 0) {
+ if (sp==INT_MIN) sp++;
+ status_damage(NULL, bl, 0, -sp, 0, 1);
+ sp = 0;
+ }
+
+ if(sp) {
+ if((unsigned int)sp > status->max_sp - status->sp)
+ sp = status->max_sp - status->sp;
+ }
+
+ if(!sp && !hp) return 0;
+
+ status->hp+= hp;
+ status->sp+= sp;
+
+ if(hp && sc &&
+ sc->data[SC_AUTOBERSERK] &&
+ sc->data[SC_PROVOKE] &&
+ sc->data[SC_PROVOKE]->val2==1 &&
+ status->hp>=status->max_hp>>2
+ ) //End auto berserk.
+ status_change_end(bl, SC_PROVOKE, INVALID_TIMER);
+
+ // send hp update to client
+ switch(bl->type) {
+ case BL_PC: pc_heal((TBL_PC*)bl,hp,sp,flag&2?1:0); break;
+ case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
+ case BL_HOM: merc_hom_heal((TBL_HOM*)bl); break;
+ case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
+ case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break;
+ }
+
+ return hp+sp;
+}
+
+//Does percentual non-flinching damage/heal. If mob is killed this way,
+//no exp/drops will be awarded if there is no src (or src is target)
+//If rates are > 0, percent is of current HP/SP
+//If rates are < 0, percent is of max HP/SP
+//If !flag, this is heal, otherwise it is damage.
+//Furthermore, if flag==2, then the target must not die from the substraction.
+int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag)
+{
+ struct status_data *status;
+ unsigned int hp =0, sp = 0;
+
+ status = status_get_status_data(target);
+
+
+ //It's safe now [MarkZD]
+ if (hp_rate > 99)
+ hp = status->hp;
+ else if (hp_rate > 0)
+ hp = status->hp>10000?
+ hp_rate*(status->hp/100):
+ ((int64)hp_rate*status->hp)/100;
+ else if (hp_rate < -99)
+ hp = status->max_hp;
+ else if (hp_rate < 0)
+ hp = status->max_hp>10000?
+ (-hp_rate)*(status->max_hp/100):
+ ((int64)-hp_rate*status->max_hp)/100;
+ if (hp_rate && !hp)
+ hp = 1;
+
+ if (flag == 2 && hp >= status->hp)
+ hp = status->hp-1; //Must not kill target.
+
+ if (sp_rate > 99)
+ sp = status->sp;
+ else if (sp_rate > 0)
+ sp = ((int64)sp_rate*status->sp)/100;
+ else if (sp_rate < -99)
+ sp = status->max_sp;
+ else if (sp_rate < 0)
+ sp = ((int64)-sp_rate)*status->max_sp/100;
+ if (sp_rate && !sp)
+ sp = 1;
+
+ //Ugly check in case damage dealt is too much for the received args of
+ //status_heal / status_damage. [Skotlex]
+ if (hp > INT_MAX) {
+ hp -= INT_MAX;
+ if (flag)
+ status_damage(src, target, INT_MAX, 0, 0, (!src||src==target?5:1));
+ else
+ status_heal(target, INT_MAX, 0, 0);
+ }
+ if (sp > INT_MAX) {
+ sp -= INT_MAX;
+ if (flag)
+ status_damage(src, target, 0, INT_MAX, 0, (!src||src==target?5:1));
+ else
+ status_heal(target, 0, INT_MAX, 0);
+ }
+ if (flag)
+ return status_damage(src, target, hp, sp, 0, (!src||src==target?5:1));
+ return status_heal(target, hp, sp, 0);
+}
+
+int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp)
+{
+ struct status_data *status;
+ unsigned int hp, sp;
+ if (!status_isdead(bl)) return 0;
+
+ status = status_get_status_data(bl);
+ if (status == &dummy_status)
+ return 0; //Invalid target.
+
+ hp = (int64)status->max_hp * per_hp/100;
+ sp = (int64)status->max_sp * per_sp/100;
+
+ if(hp > status->max_hp - status->hp)
+ hp = status->max_hp - status->hp;
+ else if (per_hp && !hp)
+ hp = 1;
+
+ if(sp > status->max_sp - status->sp)
+ sp = status->max_sp - status->sp;
+ else if (per_sp && !sp)
+ sp = 1;
+
+ status->hp += hp;
+ status->sp += sp;
+
+ if (bl->prev) //Animation only if character is already on a map.
+ clif_resurrection(bl, 1);
+ switch (bl->type) {
+ case BL_PC: pc_revive((TBL_PC*)bl, hp, sp); break;
+ case BL_MOB: mob_revive((TBL_MOB*)bl, hp); break;
+ case BL_HOM: merc_hom_revive((TBL_HOM*)bl, hp, sp); break;
+ }
+ return 1;
+}
+
+/*==========================================
+ * Checks whether the src can use the skill on the target,
+ * taking into account status/option of both source/target. [Skotlex]
+ * flag:
+ * 0 - Trying to use skill on target.
+ * 1 - Cast bar is done.
+ * 2 - Skill already pulled off, check is due to ground-based skills or splash-damage ones.
+ * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack.
+ * target MAY Be null, in which case the checks are only to see
+ * whether the source can cast or not the skill on the ground.
+ *------------------------------------------*/
+int status_check_skilluse(struct block_list *src, struct block_list *target, uint16 skill_id, int flag)
+{
+ struct status_data *status;
+ struct status_change *sc=NULL, *tsc;
+ int hide_flag;
+
+ status = src?status_get_status_data(src):&dummy_status;
+
+ if (src && src->type != BL_PC && status_isdead(src))
+ return 0;
+
+ if (!skill_id) { //Normal attack checks.
+ if (!(status->mode&MD_CANATTACK))
+ return 0; //This mode is only needed for melee attacking.
+ //Dead state is not checked for skills as some skills can be used
+ //on dead characters, said checks are left to skill.c [Skotlex]
+ if (target && status_isdead(target))
+ return 0;
+ if( src && (sc = status_get_sc(src)) && sc->data[SC_CRYSTALIZE] && src->type != BL_MOB)
+ return 0;
+ }
+
+ switch( skill_id ) {
+ case PA_PRESSURE:
+ if( flag && target ) {
+ //Gloria Avoids pretty much everything....
+ tsc = status_get_sc(target);
+ if(tsc && tsc->option&OPTION_HIDE)
+ return 0;
+ }
+ break;
+ case GN_WALLOFTHORN:
+ if( target && status_isdead(target) )
+ return 0;
+ break;
+ case AL_TELEPORT:
+ //Should fail when used on top of Land Protector [Skotlex]
+ if (src && map_getcell(src->m, src->x, src->y, CELL_CHKLANDPROTECTOR)
+ && !(status->mode&MD_BOSS)
+ && (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id))
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ if ( src ) sc = status_get_sc(src);
+
+ if( sc && sc->count ) {
+
+ if (skill_id != RK_REFRESH && sc->opt1 >0 && (sc->opt1 != OPT1_CRYSTALIZE && src->type != BL_MOB) && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { //Stuned/Frozen/etc
+ if (flag != 1) //Can't cast, casted stuff can't damage.
+ return 0;
+ if (!(skill_get_inf(skill_id)&INF_GROUND_SKILL))
+ return 0; //Targetted spells can't come off.
+ }
+
+ if (
+ (sc->data[SC_TRICKDEAD] && skill_id != NV_TRICKDEAD)
+ || (sc->data[SC_AUTOCOUNTER] && !flag)
+ || (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF && skill_id != PA_GOSPEL)
+ || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF && flag != 2)
+ )
+ return 0;
+
+ if (sc->data[SC_WINKCHARM] && target && !flag) { //Prevents skill usage
+ if( unit_bl2ud(src) && (unit_bl2ud(src))->walktimer == INVALID_TIMER )
+ unit_walktobl(src, map_id2bl(sc->data[SC_WINKCHARM]->val2), 3, 1);
+ clif_emotion(src, E_LV);
+ return 0;
+ }
+
+ if (sc->data[SC_BLADESTOP]) {
+ switch (sc->data[SC_BLADESTOP]->val1)
+ {
+ case 5: if (skill_id == MO_EXTREMITYFIST) break;
+ case 4: if (skill_id == MO_CHAINCOMBO) break;
+ case 3: if (skill_id == MO_INVESTIGATE) break;
+ case 2: if (skill_id == MO_FINGEROFFENSIVE) break;
+ default: return 0;
+ }
+ }
+
+ if (sc->data[SC_DANCING] && flag!=2) {
+ if( src->type == BL_PC && skill_id >= WA_SWING_DANCE && skill_id <= WM_UNLIMITED_HUMMING_VOICE )
+ { // Lvl 5 Lesson or higher allow you use 3rd job skills while dancing.v
+ if( pc_checkskill((TBL_PC*)src,WM_LESSON) < 5 )
+ return 0;
+ } else if(sc->data[SC_LONGING]) { //Allow everything except dancing/re-dancing. [Skotlex]
+ if (skill_id == BD_ENCORE ||
+ skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_ENSEMBLE_SKILL)
+ )
+ return 0;
+ } else {
+ switch (skill_id) {
+ case BD_ADAPTATION:
+ case CG_LONGINGFREEDOM:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ break;
+ default:
+ return 0;
+ }
+ }
+ if ((sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE && skill_id == BD_ADAPTATION)
+ return 0; //Can't amp out of Wand of Hermode :/ [Skotlex]
+ }
+
+ if (skill_id && //Do not block item-casted skills.
+ (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id)
+ ) { //Skills blocked through status changes...
+ if (!flag && ( //Blocked only from using the skill (stuff like autospell may still go through
+ sc->cant.cast ||
+ (sc->data[SC_MARIONETTE] && skill_id != CG_MARIONETTE) || //Only skill you can use is marionette again to cancel it
+ (sc->data[SC_MARIONETTE2] && skill_id == CG_MARIONETTE) || //Cannot use marionette if you are being buffed by another
+ (sc->data[SC_STASIS] && skill_block_check(src, SC_STASIS, skill_id)) ||
+ (sc->data[SC_KAGEHUMI] && skill_block_check(src, SC_KAGEHUMI, skill_id))
+ ))
+ return 0;
+
+ //Skill blocking.
+ if (
+ (sc->data[SC_VOLCANO] && skill_id == WZ_ICEWALL) ||
+ (sc->data[SC_ROKISWEIL] && skill_id != BD_ADAPTATION) ||
+ (sc->data[SC_HERMODE] && skill_get_inf(skill_id) & INF_SUPPORT_SKILL) ||
+ (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOSKILL)
+ )
+ return 0;
+
+ if( sc->data[SC__MANHOLE] || ((tsc = status_get_sc(target)) && tsc->data[SC__MANHOLE]) ) {
+ switch(skill_id) {//##TODO## make this a flag in skill_db?
+ // Skills that can be used even under Man Hole effects.
+ case SC_SHADOWFORM:
+ case SC_STRIPACCESSARY:
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ }
+ }
+
+ if (sc && sc->option)
+ {
+ if (sc->option&OPTION_HIDE)
+ switch (skill_id) { //Usable skills while hiding.
+ case TF_HIDING:
+ case AS_GRIMTOOTH:
+ case RG_BACKSTAP:
+ case RG_RAID:
+ case NJ_SHADOWJUMP:
+ case NJ_KIRIKAGE:
+ case KO_YAMIKUMO:
+ break;
+ default:
+ //Non players can use all skills while hidden.
+ if (!skill_id || src->type == BL_PC)
+ return 0;
+ }
+ if (sc->option&OPTION_CHASEWALK && skill_id != 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;
+
+ tsc = status_get_sc(target);
+
+ if(tsc && tsc->count) {
+ /* attacks in invincible are capped to 1 damage and handled in batte.c; allow spell break and eske for sealed shrine GDB when in INVINCIBLE state. */
+ if( tsc->data[SC_INVINCIBLE] && !tsc->data[SC_INVINCIBLEOFF] && skill_id && !(skill_id&(SA_SPELLBREAKER|SL_SKE)) )
+ return 0;
+ if(!skill_id && tsc->data[SC_TRICKDEAD])
+ return 0;
+ if((skill_id == WZ_STORMGUST || skill_id == WZ_FROSTNOVA || skill_id == NJ_HYOUSYOURAKU)
+ && tsc->data[SC_FREEZE])
+ return 0;
+ if(skill_id == PR_LEXAETERNA && (tsc->data[SC_FREEZE] || (tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)))
+ return 0;
+ }
+
+ //If targetting, cloak+hide protect you, otherwise only hiding does.
+ hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK);
+
+ //You cannot hide from ground skills.
+ if( skill_get_ele(skill_id,1) == ELE_EARTH ) //TODO: Need Skill Lv here :/
+ hide_flag &= ~OPTION_HIDE;
+
+ switch( target->type ) {
+ case BL_PC: {
+ struct map_session_data *sd = (TBL_PC*) target;
+ bool is_boss = (status->mode&MD_BOSS);
+ bool is_detect = ((status->mode&MD_DETECTOR)?true:false);//god-knows-why gcc doesn't shut up until this happens
+ if (pc_isinvisible(sd))
+ return 0;
+ if (tsc->option&hide_flag && !is_boss &&
+ ((sd->special_state.perfect_hiding || !is_detect) ||
+ (tsc->data[SC_CLOAKINGEXCEED] && is_detect)))
+ return 0;
+ if( tsc->data[SC_CAMOUFLAGE] && !(is_boss || is_detect) && !skill_id )
+ return 0;
+ if( tsc->data[SC_STEALTHFIELD] && !is_boss )
+ return 0;
+ }
+ break;
+ case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them).
+ //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex]
+ if (status->mode&MD_LOOTER)
+ return 1;
+ return 0;
+ case BL_HOM:
+ case BL_MER:
+ case BL_ELEM:
+ if( target->type == BL_HOM && skill_id && battle_config.hom_setting&0x1 && skill_get_inf(skill_id)&INF_SUPPORT_SKILL && battle_get_master(target) != src )
+ return 0; // Can't use support skills on Homunculus (only Master/Self)
+ if( target->type == BL_MER && (skill_id == PR_ASPERSIO || (skill_id >= SA_FLAMELAUNCHER && skill_id <= SA_SEISMICWEAPON)) && battle_get_master(target) != src )
+ return 0; // Can't use Weapon endow skills on Mercenary (only Master)
+ if( skill_id == AM_POTIONPITCHER && ( target->type == BL_MER || target->type == BL_ELEM) )
+ return 0; // Can't use Potion Pitcher on Mercenaries
+ default:
+ //Check for chase-walk/hiding/cloaking opponents.
+ if( tsc ) {
+ if( tsc->option&hide_flag && !(status->mode&(MD_BOSS|MD_DETECTOR)))
+ return 0;
+ if( tsc->data[SC_STEALTHFIELD] && !(status->mode&MD_BOSS) )
+ return 0;
+ }
+ }
+ return 1;
+}
+
+//Checks whether the source can see and chase target.
+int status_check_visibility(struct block_list *src, struct block_list *target)
+{
+ int view_range;
+ struct status_data* status = status_get_status_data(src);
+ struct status_change* tsc = status_get_sc(target);
+ switch (src->type) {
+ case BL_MOB:
+ view_range = ((TBL_MOB*)src)->min_chase;
+ break;
+ case BL_PET:
+ view_range = ((TBL_PET*)src)->db->range2;
+ break;
+ default:
+ view_range = AREA_SIZE;
+ }
+
+ if (src->m != target->m || !check_distance_bl(src, target, view_range))
+ return 0;
+
+ if( tsc && tsc->data[SC_STEALTHFIELD] )
+ return 0;
+
+ switch (target->type)
+ { //Check for chase-walk/hiding/cloaking opponents.
+ case BL_PC:
+ if ( tsc->data[SC_CLOAKINGEXCEED] && !(status->mode&MD_BOSS) )
+ return 0;
+ if( (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY] || 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) || tsc->data[SC__INVISIBILITY] || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&(MD_BOSS|MD_DETECTOR)) )
+ return 0;
+
+ }
+
+ return 1;
+}
+
+// Basic ASPD value
+int status_base_amotion_pc(struct map_session_data* sd, struct status_data* status)
+{
+ int amotion;
+#ifdef RENEWAL_ASPD
+ short mod = -1;
+
+ switch( sd->weapontype2 ){ // adjustment for dual weilding
+ case W_DAGGER: mod = 0; break; // 0, 1, 1
+ case W_1HSWORD:
+ case W_1HAXE: mod = 1;
+ if( (sd->class_&MAPID_THIRDMASK) == MAPID_GUILLOTINE_CROSS ) // 0, 2, 3
+ mod = sd->weapontype2 / W_1HSWORD + W_1HSWORD / sd->weapontype2 ;
+ }
+
+ amotion = ( sd->status.weapon < MAX_WEAPON_TYPE && mod < 0 )
+ ? (aspd_base[pc_class2idx(sd->status.class_)][sd->status.weapon]) // single weapon
+ : ((aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2] // dual-wield
+ + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2]) * 6 / 10 + 10 * mod
+ - aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2]
+ + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype1]);
+
+ if ( sd->status.shield )
+ amotion += ( 2000 - aspd_base[pc_class2idx(sd->status.class_)][W_FIST] ) +
+ ( aspd_base[pc_class2idx(sd->status.class_)][MAX_WEAPON_TYPE] - 2000 );
+
+#else
+ // 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
+
+ // percentual delay reduction from stats
+ amotion -= amotion * (4*status->agi + status->dex)/1000;
+#endif
+ // raw delay adjustment from bAspd bonus
+ amotion += sd->bonus.aspd_add;
+
+ return amotion;
+}
+
+static unsigned short status_base_atk(const struct block_list *bl, const struct status_data *status)
+{
+ int flag = 0, str, dex,
+#ifdef RENEWAL
+ rstr,
+#endif
+ dstr;
+
+
+ if(!(bl->type&battle_config.enable_baseatk))
+ return 0;
+
+ if (bl->type == BL_PC)
+ switch(((TBL_PC*)bl)->status.weapon){
+ case W_BOW:
+ case W_MUSICAL:
+ case W_WHIP:
+ case W_REVOLVER:
+ case W_RIFLE:
+ case W_GATLING:
+ case W_SHOTGUN:
+ case W_GRENADE:
+ flag = 1;
+ }
+ if (flag) {
+#ifdef RENEWAL
+ rstr =
+#endif
+ str = status->dex;
+ dex = status->str;
+ } else {
+#ifdef RENEWAL
+ rstr =
+#endif
+ str = status->str;
+ dex = status->dex;
+ }
+ //Normally only players have base-atk, but homunc have a different batk
+ // equation, hinting that perhaps non-players should use this for batk.
+ // [Skotlex]
+ dstr = str/10;
+ str += dstr*dstr;
+ if (bl->type == BL_PC)
+#ifdef RENEWAL
+ str = (rstr*10 + dex*10/5 + status->luk*10/3 + ((TBL_PC*)bl)->status.base_level*10/4)/10;
+#else
+ str+= dex/5 + status->luk/5;
+#endif
+ return cap_value(str, 0, USHRT_MAX);
+}
+
+#ifndef RENEWAL
+static inline unsigned short status_base_matk_min(const struct status_data* status){ return status->int_+(status->int_/7)*(status->int_/7); }
+static inline unsigned short status_base_matk_max(const struct status_data* status){ return status->int_+(status->int_/5)*(status->int_/5); }
+#else
+unsigned short status_base_matk(const struct status_data* status, int level){ return status->int_+(status->int_/2)+(status->dex/5)+(status->luk/3)+(level/4); }
+#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)
+{
+ //Non players get the value set, players need to stack with previous bonuses.
+ if( bl->type != BL_PC )
+ status->batk =
+ status->hit = status->flee =
+ status->def2 = status->mdef2 =
+ status->cri = status->flee2 = 0;
+
+#ifdef RENEWAL // renewal formulas
+ status->matk_min = status->matk_max = status_base_matk(status, level);
+ status->hit += level + status->dex + status->luk/3 + 175; //base level + ( every 1 dex = +1 hit ) + (every 3 luk = +1 hit) + 175
+ status->flee += level + status->agi + status->luk/5 + 100; //base level + ( every 1 agi = +1 flee ) + (every 5 luk = +1 flee) + 100
+ status->def2 += (int)(((float)level + status->vit)/2 + ((float)status->agi/5)); //base level + (every 2 vit = +1 def) + (every 5 agi = +1 def)
+ status->mdef2 += (int)(status->int_ + ((float)level/4) + ((float)status->dex/5) + ((float)status->vit/5)); //(every 4 base level = +1 mdef) + (every 1 int = +1 mdef) + (every 5 dex = +1 mdef) + (every 5 vit = +1 mdef)
+#else
+ status->matk_min = status_base_matk_min(status);
+ status->matk_max = status_base_matk_max(status);
+ 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 += 10 + (status->luk*10/3); //(every 1 luk = +0.3 critical)
+ else
+ status->cri = 0;
+
+ if (bl->type&battle_config.enable_perfect_flee)
+ status->flee2 += status->luk + 10; //(every 10 luk = +1 perfect flee)
+ else
+ status->flee2 = 0;
+
+ if (status->batk) {
+ int temp = status->batk + status_base_atk(bl, status);
+ status->batk = cap_value(temp, 0, USHRT_MAX);
+ } else
+ status->batk = status_base_atk(bl, status);
+ if (status->cri)
+ switch (bl->type) {
+ case BL_MOB:
+ if(battle_config.mob_critical_rate != 100)
+ status->cri = status->cri*battle_config.mob_critical_rate/100;
+ if(!status->cri && battle_config.mob_critical_rate)
+ status->cri = 10;
+ break;
+ case BL_PC:
+ //Players don't have a critical adjustment setting as of yet.
+ break;
+ default:
+ if(battle_config.critical_rate != 100)
+ status->cri = status->cri*battle_config.critical_rate/100;
+ if (!status->cri && battle_config.critical_rate)
+ status->cri = 10;
+ }
+ if(bl->type&BL_REGEN)
+ status_calc_regen(bl, status, status_get_regen_data(bl));
+}
+
+//Skotlex: Calculates the initial status for the given mob
+//first will only be false when the mob leveled up or got a GuardUp level.
+int status_calc_mob_(struct mob_data* md, bool first)
+{
+ struct status_data *status;
+ struct block_list *mbl = NULL;
+ int flag=0;
+
+ if(first)
+ { //Set basic level on respawn.
+ if (md->level > 0 && md->level <= MAX_LEVEL && md->level != md->db->lv)
+ ;
+ else
+ md->level = md->db->lv;
+ }
+
+ //Check if we need custom base-status
+ if (battle_config.mobs_level_up && md->level > md->db->lv)
+ flag|=1;
+
+ if (md->special_state.size)
+ flag|=2;
+
+ if (md->guardian_data && md->guardian_data->guardup_lv)
+ flag|=4;
+ if (md->class_ == MOBID_EMPERIUM)
+ flag|=4;
+
+ if (battle_config.slaves_inherit_speed && md->master_id)
+ flag|=8;
+
+ if (md->master_id && md->special_state.ai>1)
+ flag|=16;
+
+ if (!flag)
+ { //No special status required.
+ if (md->base_status) {
+ aFree(md->base_status);
+ md->base_status = NULL;
+ }
+ if(first)
+ memcpy(&md->status, &md->db->status, sizeof(struct status_data));
+ return 0;
+ }
+ if (!md->base_status)
+ md->base_status = (struct status_data*)aCalloc(1, sizeof(struct status_data));
+
+ status = md->base_status;
+ memcpy(status, &md->db->status, sizeof(struct status_data));
+
+ if (flag&(8|16))
+ mbl = map_id2bl(md->master_id);
+
+ if (flag&8 && mbl) {
+ struct status_data *mstatus = status_get_base_status(mbl);
+ if (mstatus &&
+ battle_config.slaves_inherit_speed&(mstatus->mode&MD_CANMOVE?1:2))
+ status->speed = mstatus->speed;
+ if( status->speed < 2 ) /* minimum for the unit to function properly */
+ status->speed = 2;
+ }
+
+ if (flag&16 && mbl)
+ { //Max HP setting from Summon Flora/marine Sphere
+ struct unit_data *ud = unit_bl2ud(mbl);
+ //Remove special AI when this is used by regular mobs.
+ if (mbl->type == BL_MOB && !((TBL_MOB*)mbl)->special_state.ai)
+ md->special_state.ai = 0;
+ if (ud)
+ { // different levels of HP according to skill level
+ if (ud->skill_id == AM_SPHEREMINE) {
+ status->max_hp = 2000 + 400*ud->skill_lv;
+ } else if(ud->skill_id == KO_ZANZOU){
+ status->max_hp = 3000 + 3000 * ud->skill_lv;
+ } else { //AM_CANNIBALIZE
+ status->max_hp = 1500 + 200*ud->skill_lv + 10*status_get_lv(mbl);
+ status->mode|= MD_CANATTACK|MD_AGGRESSIVE;
+ }
+ status->hp = status->max_hp;
+ }
+ }
+
+ if (flag&1)
+ { // increase from mobs leveling up [Valaris]
+ int diff = md->level - md->db->lv;
+ status->str+= diff;
+ status->agi+= diff;
+ status->vit+= diff;
+ status->int_+= diff;
+ status->dex+= diff;
+ status->luk+= diff;
+ status->max_hp += diff*status->vit;
+ status->max_sp += diff*status->int_;
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ status->speed -= cap_value(diff, 0, status->speed - 10);
+ }
+
+
+ if (flag&2 && battle_config.mob_size_influence)
+ { // change for sized monsters [Valaris]
+ if (md->special_state.size==SZ_MEDIUM) {
+ status->max_hp>>=1;
+ status->max_sp>>=1;
+ if (!status->max_hp) status->max_hp = 1;
+ if (!status->max_sp) status->max_sp = 1;
+ status->hp=status->max_hp;
+ status->sp=status->max_sp;
+ status->str>>=1;
+ status->agi>>=1;
+ status->vit>>=1;
+ status->int_>>=1;
+ status->dex>>=1;
+ status->luk>>=1;
+ if (!status->str) status->str = 1;
+ if (!status->agi) status->agi = 1;
+ if (!status->vit) status->vit = 1;
+ if (!status->int_) status->int_ = 1;
+ if (!status->dex) status->dex = 1;
+ if (!status->luk) status->luk = 1;
+ } else if (md->special_state.size==SZ_BIG) {
+ status->max_hp<<=1;
+ status->max_sp<<=1;
+ status->hp=status->max_hp;
+ status->sp=status->max_sp;
+ status->str<<=1;
+ status->agi<<=1;
+ status->vit<<=1;
+ status->int_<<=1;
+ status->dex<<=1;
+ status->luk<<=1;
+ }
+ }
+
+ status_calc_misc(&md->bl, status, md->level);
+
+ if(flag&4)
+ { // Strengthen Guardians - custom value +10% / lv
+ struct guild_castle *gc;
+ gc=guild_mapname2gc(map[md->bl.m].name);
+ if (!gc)
+ ShowError("status_calc_mob: No castle set at map %s\n", map[md->bl.m].name);
+ else
+ if(gc->castle_id < 24 || md->class_ == MOBID_EMPERIUM) {
+#ifdef RENEWAL
+ status->max_hp += 50 * gc->defense;
+ status->max_sp += 70 * gc->defense;
+#else
+ status->max_hp += 1000 * gc->defense;
+ status->max_sp += 200 * gc->defense;
+#endif
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ 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;
+ status->rhw.atk += status->rhw.atk * 10*md->guardian_data->guardup_lv/100;
+ status->rhw.atk2 += status->rhw.atk2 * 10*md->guardian_data->guardup_lv/100;
+ status->aspd_rate -= 100*md->guardian_data->guardup_lv;
+ }
+ }
+
+ if( first ) //Initial battle status
+ memcpy(&md->status, status, sizeof(struct status_data));
+
+ return 1;
+}
+
+//Skotlex: Calculates the stats of the given pet.
+int status_calc_pet_(struct pet_data *pd, bool first)
+{
+ nullpo_ret(pd);
+
+ if (first) {
+ memcpy(&pd->status, &pd->db->status, sizeof(struct status_data));
+ pd->status.mode = MD_CANMOVE; // pets discard all modes, except walking
+ pd->status.speed = pd->petDB->speed;
+
+ if(battle_config.pet_attack_support || battle_config.pet_damage_support)
+ {// attack support requires the pet to be able to attack
+ pd->status.mode|= MD_CANATTACK;
+ }
+ }
+
+ if (battle_config.pet_lv_rate && pd->msd)
+ {
+ struct map_session_data *sd = pd->msd;
+ int lv;
+
+ lv =sd->status.base_level*battle_config.pet_lv_rate/100;
+ if (lv < 0)
+ lv = 1;
+ if (lv != pd->pet.level || first)
+ {
+ struct status_data *bstat = &pd->db->status, *status = &pd->status;
+ pd->pet.level = lv;
+ if (!first) //Lv Up animation
+ clif_misceffect(&pd->bl, 0);
+ status->rhw.atk = (bstat->rhw.atk*lv)/pd->db->lv;
+ status->rhw.atk2 = (bstat->rhw.atk2*lv)/pd->db->lv;
+ status->str = (bstat->str*lv)/pd->db->lv;
+ status->agi = (bstat->agi*lv)/pd->db->lv;
+ status->vit = (bstat->vit*lv)/pd->db->lv;
+ status->int_ = (bstat->int_*lv)/pd->db->lv;
+ status->dex = (bstat->dex*lv)/pd->db->lv;
+ status->luk = (bstat->luk*lv)/pd->db->lv;
+
+ status->rhw.atk = cap_value(status->rhw.atk, 1, battle_config.pet_max_atk1);
+ status->rhw.atk2 = cap_value(status->rhw.atk2, 2, battle_config.pet_max_atk2);
+ status->str = cap_value(status->str,1,battle_config.pet_max_stats);
+ status->agi = cap_value(status->agi,1,battle_config.pet_max_stats);
+ status->vit = cap_value(status->vit,1,battle_config.pet_max_stats);
+ status->int_= cap_value(status->int_,1,battle_config.pet_max_stats);
+ status->dex = cap_value(status->dex,1,battle_config.pet_max_stats);
+ status->luk = cap_value(status->luk,1,battle_config.pet_max_stats);
+
+ status_calc_misc(&pd->bl, &pd->status, lv);
+
+ if (!first) //Not done the first time because the pet is not visible yet
+ clif_send_petstatus(sd);
+ }
+ } else if (first) {
+ status_calc_misc(&pd->bl, &pd->status, pd->db->lv);
+ if (!battle_config.pet_lv_rate && pd->pet.level != pd->db->lv)
+ pd->pet.level = pd->db->lv;
+ }
+
+ //Support rate modifier (1000 = 100%)
+ pd->rate_fix = 1000*(pd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500;
+ if(battle_config.pet_support_rate != 100)
+ pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100;
+
+ return 1;
+}
+
+/// Helper function for status_base_pc_maxhp(), used to pre-calculate the hp_sigma_val[] array
+static void status_calc_sigma(void)
+{
+ int i,j;
+
+ for(i = 0; i < CLASS_COUNT; i++)
+ {
+ unsigned int k = 0;
+ hp_sigma_val[i][0] = hp_sigma_val[i][1] = 0;
+ for(j = 2; j <= MAX_LEVEL; j++)
+ {
+ k += (hp_coefficient[i]*j + 50) / 100;
+ hp_sigma_val[i][j] = k;
+ if (k >= INT_MAX)
+ break; //Overflow protection. [Skotlex]
+ }
+ for(; j <= MAX_LEVEL; j++)
+ hp_sigma_val[i][j] = INT_MAX;
+ }
+}
+
+/// Calculates base MaxHP value according to class and base level
+/// The recursive equation used to calculate level bonus is (using integer operations)
+/// f(0) = 35 | f(x+1) = f(x) + A + (x + B)*C/D
+/// which reduces to something close to
+/// f(x) = 35 + x*(A + B*C/D) + sum(i=2..x){ i*C/D }
+static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data* status)
+{
+ uint64 val = pc_class2idx(sd->status.class_);
+ val = 35 + sd->status.base_level*(int64)hp_coefficient2[val]/100 + hp_sigma_val[val][sd->status.base_level];
+
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_NINJA || (sd->class_&MAPID_UPPERMASK) == MAPID_GUNSLINGER)
+ val += 100; //Since their HP can't be approximated well enough without this.
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON))
+ val *= 3; //Triple max HP for top ranking Taekwons over level 90.
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99)
+ val += 2000; //Supernovice lvl99 hp bonus.
+
+ val += val * status->vit/100; // +1% per each point of VIT
+
+ if (sd->class_&JOBL_UPPER)
+ val += val * 25/100; //Trans classes get a 25% hp bonus
+ else if (sd->class_&JOBL_BABY)
+ val -= val * 30/100; //Baby classes get a 30% hp penalty
+ return (unsigned int)val;
+}
+
+static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct status_data *status)
+{
+ uint64 val;
+
+ val = 10 + sd->status.base_level*(int64)sp_coefficient[pc_class2idx(sd->status.class_)]/100;
+ val += val * status->int_/100;
+
+ if (sd->class_&JOBL_UPPER)
+ val += val * 25/100;
+ else if (sd->class_&JOBL_BABY)
+ val -= val * 30/100;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON))
+ val *= 3; //Triple max SP for top ranking Taekwons over level 90.
+
+ return (unsigned int)val;
+}
+
+//Calculates player data from scratch without counting SC adjustments.
+//Should be invoked whenever players raise stats, learn passive skills or change equipment.
+int status_calc_pc_(struct map_session_data* sd, bool first)
+{
+ static int calculating = 0; //Check for recursive call preemption. [Skotlex]
+ struct status_data *status; // pointer to the player's base status
+ const struct status_change *sc = &sd->sc;
+ struct s_skill b_skill[MAX_SKILL]; // previous skill tree
+ int b_weight, b_max_weight, b_cart_weight_max, // previous weight
+ i, index, skill,refinedef=0;
+ int64 i64;
+
+ if (++calculating > 10) //Too many recursive calls!
+ return -1;
+
+ // remember player-specific values that are currently being shown to the client (for refresh purposes)
+ memcpy(b_skill, &sd->status.skill, sizeof(b_skill));
+ b_weight = sd->weight;
+ b_max_weight = sd->max_weight;
+ b_cart_weight_max = sd->cart_weight_max;
+
+ pc_calc_skilltree(sd); // SkillTree calculation
+
+ sd->max_weight = max_weight_base[pc_class2idx(sd->status.class_)]+sd->status.str*300;
+
+ if(first) {
+ //Load Hp/SP from char-received data.
+ sd->battle_status.hp = sd->status.hp;
+ sd->battle_status.sp = sd->status.sp;
+ sd->regen.sregen = &sd->sregen;
+ sd->regen.ssregen = &sd->ssregen;
+ sd->weight=0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL)
+ continue;
+ sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount;
+ }
+ sd->cart_weight=0;
+ sd->cart_num=0;
+ for(i=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid==0)
+ continue;
+ sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount;
+ sd->cart_num++;
+ }
+ }
+
+ status = &sd->base_status;
+ // these are not zeroed. [zzo]
+ sd->hprate=100;
+ sd->sprate=100;
+ sd->castrate=100;
+ sd->delayrate=100;
+ sd->dsprate=100;
+ sd->hprecov_rate = 100;
+ sd->sprecov_rate = 100;
+ sd->matk_rate = 100;
+ sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100;
+ sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100;
+ sd->regen.state.block = 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]
+ memset (sd->param_bonus, 0, sizeof(sd->param_bonus)
+ + sizeof(sd->param_equip)
+ + sizeof(sd->subele)
+ + sizeof(sd->subrace)
+ + sizeof(sd->subrace2)
+ + sizeof(sd->subsize)
+ + sizeof(sd->reseff)
+ + sizeof(sd->weapon_coma_ele)
+ + sizeof(sd->weapon_coma_race)
+ + sizeof(sd->weapon_atk)
+ + sizeof(sd->weapon_atk_rate)
+ + sizeof(sd->arrow_addele)
+ + sizeof(sd->arrow_addrace)
+ + sizeof(sd->arrow_addsize)
+ + sizeof(sd->magic_addele)
+ + sizeof(sd->magic_addrace)
+ + sizeof(sd->magic_addsize)
+ + sizeof(sd->magic_atk_ele)
+ + sizeof(sd->critaddrace)
+ + sizeof(sd->expaddrace)
+ + sizeof(sd->ignore_mdef)
+ + sizeof(sd->ignore_def)
+ + sizeof(sd->itemgrouphealrate)
+ + sizeof(sd->sp_gain_race)
+ + sizeof(sd->sp_gain_race_attack)
+ + sizeof(sd->hp_gain_race_attack)
+ );
+
+ memset (&sd->right_weapon.overrefine, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods));
+ memset (&sd->left_weapon.overrefine, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods));
+
+ if (sd->special_state.intravision && !sd->sc.data[SC_INTRAVISION]) //Clear intravision as long as nothing else is using it
+ clif_status_load(&sd->bl, SI_INTRAVISION, 0);
+
+ memset(&sd->special_state,0,sizeof(sd->special_state));
+ memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)));
+
+ //FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex]
+ status->speed = DEFAULT_WALK_SPEED;
+ //Give them all modes except these (useful for clones)
+ status->mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK);
+
+ status->size = (sd->class_&JOBL_BABY)?SZ_SMALL:SZ_MEDIUM;
+ if (battle_config.character_size && pc_isriding(sd)) { //[Lupus]
+ if (sd->class_&JOBL_BABY) {
+ if (battle_config.character_size&SZ_BIG)
+ status->size++;
+ } else
+ if(battle_config.character_size&SZ_MEDIUM)
+ status->size++;
+ }
+ status->aspd_rate = 1000;
+ status->ele_lv = 1;
+ status->race = RC_DEMIHUMAN;
+
+ //zero up structures...
+ memset(&sd->autospell,0,sizeof(sd->autospell)
+ + sizeof(sd->autospell2)
+ + sizeof(sd->autospell3)
+ + sizeof(sd->addeff)
+ + sizeof(sd->addeff2)
+ + sizeof(sd->addeff3)
+ + sizeof(sd->skillatk)
+ + sizeof(sd->skillusesprate)
+ + sizeof(sd->skillusesp)
+ + sizeof(sd->skillheal)
+ + sizeof(sd->skillheal2)
+ + sizeof(sd->hp_loss)
+ + sizeof(sd->sp_loss)
+ + sizeof(sd->hp_regen)
+ + sizeof(sd->sp_regen)
+ + sizeof(sd->skillblown)
+ + sizeof(sd->skillcast)
+ + sizeof(sd->add_def)
+ + sizeof(sd->add_mdef)
+ + sizeof(sd->add_mdmg)
+ + sizeof(sd->add_drop)
+ + sizeof(sd->itemhealrate)
+ + sizeof(sd->subele2)
+ + sizeof(sd->skillcooldown)
+ + sizeof(sd->skillfixcast)
+ + sizeof(sd->skillvarcast)
+ );
+
+ memset (&sd->bonus, 0,sizeof(sd->bonus));
+
+ // Autobonus
+ pc_delautobonus(sd,sd->autobonus,ARRAYLENGTH(sd->autobonus),true);
+ pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus2),true);
+ pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus3),true);
+
+ // Parse equipment.
+ for(i=0;i<EQI_MAX-1;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index)
+ continue;
+ if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index)
+ continue;
+ if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index))
+ continue;
+ if(i == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index)
+ continue;
+ if(i == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index))
+ continue;
+ if(!sd->inventory_data[index])
+ continue;
+
+ status->def += sd->inventory_data[index]->def;
+
+ if(first && sd->inventory_data[index]->equip_script)
+ { //Execute equip-script on login
+ run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0);
+ if (!calculating)
+ return 1;
+ }
+
+ // sanitize the refine level in case someone decreased the value inbetween
+ if (sd->status.inventory[index].refine > MAX_REFINE)
+ sd->status.inventory[index].refine = MAX_REFINE;
+
+ if(sd->inventory_data[index]->type == IT_WEAPON) {
+ int r,wlv = sd->inventory_data[index]->wlv;
+ struct weapon_data *wd;
+ struct weapon_atk *wa;
+ if (wlv >= REFINE_TYPE_MAX)
+ wlv = REFINE_TYPE_MAX - 1;
+ if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L) {
+ wd = &sd->left_weapon; // Left-hand weapon
+ wa = &status->lhw;
+ } else {
+ wd = &sd->right_weapon;
+ wa = &status->rhw;
+ }
+ wa->atk += sd->inventory_data[index]->atk;
+ if ( (r = sd->status.inventory[index].refine) )
+ wa->atk2 = refine_info[wlv].bonus[r-1] / 100;
+
+#ifdef RENEWAL
+ wa->matk += sd->inventory_data[index]->matk;
+ wa->wlv = wlv;
+ if( r ) // renewal magic attack refine bonus
+ wa->matk += refine_info[wlv].bonus[r-1] / 100;
+#endif
+
+ //Overrefine bonus.
+ if (r)
+ wd->overrefine = refine_info[wlv].randombonus_max[r-1] / 100;
+
+ wa->range += sd->inventory_data[index]->range;
+ if(sd->inventory_data[index]->script) {
+ if (wd == &sd->left_weapon) {
+ sd->state.lr_flag = 1;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ } else
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered this. [Skotlex]
+ return 1;
+ }
+
+ if(sd->status.inventory[index].card[0]==CARD0_FORGE)
+ { // Forged weapon
+ wd->star += (sd->status.inventory[index].card[1]>>8);
+ if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg
+ if(pc_famerank(MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
+ wd->star += 10;
+
+ if (!wa->ele) //Do not overwrite element from previous bonuses.
+ wa->ele = (sd->status.inventory[index].card[1]&0x0f);
+ }
+ }
+ else if(sd->inventory_data[index]->type == IT_ARMOR) {
+ int r;
+ if ( (r = sd->status.inventory[index].refine) )
+ refinedef += refine_info[REFINE_TYPE_ARMOR].bonus[r-1];
+ if(sd->inventory_data[index]->script) {
+ if( i == EQI_HAND_L ) //Shield
+ sd->state.lr_flag = 3;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if( i == EQI_HAND_L ) //Shield
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered this. [Skotlex]
+ return 1;
+ }
+ }
+ }
+
+ if(sd->equip_index[EQI_AMMO] >= 0){
+ index = sd->equip_index[EQI_AMMO];
+ if(sd->inventory_data[index]){ // Arrows
+ sd->bonus.arrow_atk += sd->inventory_data[index]->atk;
+ sd->state.lr_flag = 2;
+ if( !itemdb_is_GNthrowable(sd->inventory_data[index]->nameid) ) //don't run scripts on throwable items
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+
+ /* we've got combos to process */
+ if( sd->combos.count ) {
+ for( i = 0; i < sd->combos.count; i++ ) {
+ run_script(sd->combos.bonus[i],0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered this.
+ return 1;
+ }
+ }
+
+ //Store equipment script bonuses
+ memcpy(sd->param_equip,sd->param_bonus,sizeof(sd->param_equip));
+ memset(sd->param_bonus, 0, sizeof(sd->param_bonus));
+
+ status->def += (refinedef+50)/100;
+
+ //Parse Cards
+ for(i=0;i<EQI_MAX-1;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index)
+ continue;
+ if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index)
+ continue;
+ if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index))
+ continue;
+
+ if(sd->inventory_data[index]) {
+ int j,c;
+ struct item_data *data;
+
+ //Card script execution.
+ if(itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue;
+ for(j=0;j<MAX_SLOTS;j++){ // Uses MAX_SLOTS to support Soul Bound system [Inkfish]
+ current_equip_card_id= c= sd->status.inventory[index].card[j];
+ if(!c)
+ continue;
+ data = itemdb_exists(c);
+ if(!data)
+ continue;
+ if(first && data->equip_script)
+ { //Execute equip-script on login
+ run_script(data->equip_script,0,sd->bl.id,0);
+ if (!calculating)
+ return 1;
+ }
+ if(!data->script)
+ continue;
+ if(data->flag.no_equip) { //Card restriction checks.
+ if(map[sd->bl.m].flag.restricted && data->flag.no_equip&(8*map[sd->bl.m].zone))
+ continue;
+ if(!map_flag_vs(sd->bl.m) && data->flag.no_equip&1)
+ continue;
+ if(map[sd->bl.m].flag.pvp && data->flag.no_equip&2)
+ continue;
+ if(map_flag_gvg(sd->bl.m) && data->flag.no_equip&4)
+ continue;
+ if(map[sd->bl.m].flag.battleground && data->flag.no_equip&8)
+ continue;
+ }
+ if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L)
+ { //Left hand status.
+ sd->state.lr_flag = 1;
+ run_script(data->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ } else
+ run_script(data->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script his function. [Skotlex]
+ return 1;
+ }
+ }
+ }
+
+ if( sc->count && sc->data[SC_ITEMSCRIPT] )
+ {
+ struct item_data *data = itemdb_exists(sc->data[SC_ITEMSCRIPT]->val1);
+ if( data && data->script )
+ run_script(data->script,0,sd->bl.id,0);
+ }
+
+ if( sd->pd )
+ { // Pet Bonus
+ struct pet_data *pd = sd->pd;
+ if( pd && pd->petDB && pd->petDB->equip_script && pd->pet.intimate >= battle_config.pet_equip_min_friendly )
+ run_script(pd->petDB->equip_script,0,sd->bl.id,0);
+ if( pd && pd->pet.intimate > 0 && (!battle_config.pet_equip_required || pd->pet.equip > 0) && pd->state.skillbonus == 1 && pd->bonus )
+ pc_bonus(sd,pd->bonus->type, pd->bonus->val);
+ }
+
+ //param_bonus now holds card bonuses.
+ if(status->rhw.range < 1) status->rhw.range = 1;
+ if(status->lhw.range < 1) status->lhw.range = 1;
+ if(status->rhw.range < status->lhw.range)
+ status->rhw.range = status->lhw.range;
+
+ sd->bonus.double_rate += sd->bonus.double_add_rate;
+ sd->bonus.perfect_hit += sd->bonus.perfect_hit_add;
+ sd->bonus.splash_range += sd->bonus.splash_add_range;
+
+ // Damage modifiers from weapon type
+ sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1];
+ sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1];
+ sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1];
+ sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2];
+ sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2];
+ sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2];
+
+ 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.
+ sd->right_weapon.atkmods[1] = sd->right_weapon.atkmods[2];
+ sd->left_weapon.atkmods[1] = sd->left_weapon.atkmods[2];
+ }
+
+// ----- STATS CALCULATION -----
+
+ // Job bonuses
+ index = pc_class2idx(sd->status.class_);
+ for(i=0;i<(int)sd->status.job_level && i<MAX_LEVEL;i++){
+ if(!job_bonus[index][i])
+ continue;
+ switch(job_bonus[index][i]) {
+ case 1: status->str++; break;
+ case 2: status->agi++; break;
+ case 3: status->vit++; break;
+ case 4: status->int_++; break;
+ case 5: status->dex++; break;
+ case 6: status->luk++; break;
+ }
+ }
+
+ // If a Super Novice has never died and is at least joblv 70, he gets all stats +10
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){
+ status->str += 10;
+ status->agi += 10;
+ status->vit += 10;
+ status->int_+= 10;
+ status->dex += 10;
+ status->luk += 10;
+ }
+
+ // Absolute modifiers from passive skills
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ status->str++;
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0)
+ status->int_ += (skill+1)/2; // +1 INT / 2 lv
+ if((skill=pc_checkskill(sd,AC_OWL))>0)
+ status->dex += skill;
+ if((skill = pc_checkskill(sd,RA_RESEARCHTRAP))>0)
+ status->int_ += skill;
+
+ // Bonuses from cards and equipment as well as base stat, remember to avoid overflows.
+ i = status->str + sd->status.str + sd->param_bonus[0] + sd->param_equip[0];
+ status->str = cap_value(i,0,USHRT_MAX);
+ i = status->agi + sd->status.agi + sd->param_bonus[1] + sd->param_equip[1];
+ status->agi = cap_value(i,0,USHRT_MAX);
+ i = status->vit + sd->status.vit + sd->param_bonus[2] + sd->param_equip[2];
+ status->vit = cap_value(i,0,USHRT_MAX);
+ i = status->int_+ sd->status.int_+ sd->param_bonus[3] + sd->param_equip[3];
+ status->int_ = cap_value(i,0,USHRT_MAX);
+ i = status->dex + sd->status.dex + sd->param_bonus[4] + sd->param_equip[4];
+ status->dex = cap_value(i,0,USHRT_MAX);
+ i = status->luk + sd->status.luk + sd->param_bonus[5] + sd->param_equip[5];
+ status->luk = cap_value(i,0,USHRT_MAX);
+
+// ------ BASE ATTACK CALCULATION ------
+
+ // Base batk value is set on status_calc_misc
+ // weapon-type bonus (FIXME: Why is the weapon_atk bonus applied to base attack?)
+ if (sd->status.weapon < MAX_WEAPON_TYPE && sd->weapon_atk[sd->status.weapon])
+ status->batk += sd->weapon_atk[sd->status.weapon];
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_HILTBINDING))>0)
+ status->batk += 4;
+
+// ----- HP MAX CALCULATION -----
+
+ // Basic MaxHP value
+ //We hold the standard Max HP here to make it faster to recalculate on vit changes.
+ sd->status.max_hp = status_base_pc_maxhp(sd,status);
+ //This is done to handle underflows from negative Max HP bonuses
+ i64 = sd->status.max_hp + (int)status->max_hp;
+ status->max_hp = (unsigned int)cap_value(i64, 0, INT_MAX);
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ status->max_hp += skill*200;
+
+ // Apply relative modifiers from equipment
+ if(sd->hprate < 0)
+ sd->hprate = 0;
+ if(sd->hprate!=100)
+ status->max_hp = (int64)status->max_hp * sd->hprate/100;
+ if(battle_config.hp_rate != 100)
+ status->max_hp = (int64)status->max_hp * battle_config.hp_rate/100;
+
+ if(status->max_hp > (unsigned int)battle_config.max_hp)
+ status->max_hp = battle_config.max_hp;
+ else if(!status->max_hp)
+ status->max_hp = 1;
+
+// ----- SP MAX CALCULATION -----
+
+ // Basic MaxSP value
+ sd->status.max_sp = status_base_pc_maxsp(sd,status);
+ //This is done to handle underflows from negative Max SP bonuses
+ i64 = sd->status.max_sp + (int)status->max_sp;
+ status->max_sp = (unsigned int)cap_value(i64, 0, INT_MAX);
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,SL_KAINA))>0)
+ status->max_sp += 30*skill;
+ if((skill=pc_checkskill(sd,HP_MEDITATIO))>0)
+ status->max_sp += (int64)status->max_sp * skill/100;
+ if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0)
+ status->max_sp += (int64)status->max_sp * 2*skill/100;
+ if( (skill = pc_checkskill(sd,RA_RESEARCHTRAP)) > 0 )
+ status->max_sp += 200 + 20 * skill;
+ if( (skill = pc_checkskill(sd,WM_LESSON)) > 0 )
+ status->max_sp += 30 * skill;
+
+
+ // Apply relative modifiers from equipment
+ if(sd->sprate < 0)
+ sd->sprate = 0;
+ if(sd->sprate!=100)
+ status->max_sp = (int64)status->max_sp * sd->sprate/100;
+ if(battle_config.sp_rate != 100)
+ status->max_sp = (int64)status->max_sp * battle_config.sp_rate/100;
+
+ if(status->max_sp > (unsigned int)battle_config.max_sp)
+ status->max_sp = battle_config.max_sp;
+ else if(!status->max_sp)
+ status->max_sp = 1;
+
+// ----- RESPAWN HP/SP -----
+//
+ //Calc respawn hp and store it on base_status
+ if (sd->special_state.restart_full_recover)
+ {
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ } else {
+ if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2)
+ && battle_config.restart_hp_rate < 50)
+ status->hp = status->max_hp>>1;
+ else
+ status->hp = (int64)status->max_hp * battle_config.restart_hp_rate/100;
+ if(!status->hp)
+ status->hp = 1;
+
+ status->sp = (int64)status->max_sp * battle_config.restart_sp_rate /100;
+
+ if( !status->sp ) /* the minimum for the respawn setting is SP:1 */
+ status->sp = 1;
+ }
+
+// ----- MISC CALCULATION -----
+ status_calc_misc(&sd->bl, status, sd->status.base_level);
+
+ //Equipment modifiers for misc settings
+ if(sd->matk_rate < 0)
+ sd->matk_rate = 0;
+
+ if(sd->matk_rate != 100){
+ status->matk_max = status->matk_max * sd->matk_rate/100;
+ status->matk_min = status->matk_min * sd->matk_rate/100;
+ }
+
+ if(sd->hit_rate < 0)
+ sd->hit_rate = 0;
+ if(sd->hit_rate != 100)
+ status->hit = status->hit * sd->hit_rate/100;
+
+ if(sd->flee_rate < 0)
+ sd->flee_rate = 0;
+ if(sd->flee_rate != 100)
+ status->flee = status->flee * sd->flee_rate/100;
+
+ if(sd->def2_rate < 0)
+ sd->def2_rate = 0;
+ if(sd->def2_rate != 100)
+ status->def2 = status->def2 * sd->def2_rate/100;
+
+ if(sd->mdef2_rate < 0)
+ sd->mdef2_rate = 0;
+ if(sd->mdef2_rate != 100)
+ status->mdef2 = status->mdef2 * sd->mdef2_rate/100;
+
+ if(sd->critical_rate < 0)
+ sd->critical_rate = 0;
+ if(sd->critical_rate != 100)
+ status->cri = status->cri * sd->critical_rate/100;
+
+ if(sd->flee2_rate < 0)
+ sd->flee2_rate = 0;
+ if(sd->flee2_rate != 100)
+ status->flee2 = status->flee2 * sd->flee2_rate/100;
+
+// ----- HIT CALCULATION -----
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0)
+ status->hit += skill*2;
+ if((skill=pc_checkskill(sd,AC_VULTURE))>0){
+#ifndef RENEWAL
+ status->hit += skill;
+#endif
+ if(sd->status.weapon == W_BOW)
+ status->rhw.range += skill;
+ }
+ if(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)
+ {
+ if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0)
+ status->hit += 2*skill;
+ if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) {
+ status->hit += skill;
+ status->rhw.range += skill;
+ }
+ }
+
+// ----- FLEE CALCULATION -----
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,TF_MISS))>0)
+ 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
+ if(sd->def_rate < 0)
+ sd->def_rate = 0;
+ if(sd->def_rate != 100) {
+ i = status->def * sd->def_rate/100;
+ status->def = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
+ }
+
+#ifndef RENEWAL
+ 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 char)battle_config.max_def;
+ }
+#endif
+
+// ----- EQUIPMENT-MDEF CALCULATION -----
+
+ // Apply relative modifiers from equipment
+ if(sd->mdef_rate < 0)
+ sd->mdef_rate = 0;
+ if(sd->mdef_rate != 100) {
+ i = status->mdef * sd->mdef_rate/100;
+ status->mdef = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
+ }
+
+#ifndef RENEWAL
+ 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 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
+
+ // Basic ASPD value
+ i = status_base_amotion_pc(sd,status);
+ status->amotion = cap_value(i,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000);
+
+ // Relative modifiers from passive skills
+#ifndef RENEWAL_ASPD
+ if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
+ status->aspd_rate -= 5*skill;
+ if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd))
+ status->aspd_rate -= 30*skill;
+ if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
+ (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
+ status->aspd_rate -= ((skill+1)/2) * 10;
+ if(pc_isriding(sd))
+ status->aspd_rate += 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);
+ else if(pc_isridingdragon(sd))
+ status->aspd_rate += 250-50*pc_checkskill(sd,RK_DRAGONTRAINING);
+#else // needs more info
+ if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
+ status->aspd_rate += 5*skill;
+ if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd))
+ status->aspd_rate += 30*skill;
+ if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
+ (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
+ status->aspd_rate += ((skill+1)/2) * 10;
+ if(pc_isriding(sd))
+ status->aspd_rate -= 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);
+ else if(pc_isridingdragon(sd))
+ status->aspd_rate -= 250-50*pc_checkskill(sd,RK_DRAGONTRAINING);
+#endif
+ status->adelay = 2*status->amotion;
+
+
+// ----- DMOTION -----
+//
+ i = 800-status->agi*4;
+ status->dmotion = cap_value(i, 400, 800);
+ if(battle_config.pc_damage_delay_rate != 100)
+ status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100;
+
+// ----- MISC CALCULATIONS -----
+
+ // Weight
+ if((skill=pc_checkskill(sd,MC_INCCARRY))>0)
+ sd->max_weight += 2000*skill;
+ if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
+ sd->max_weight += 10000;
+ else if(pc_isridingdragon(sd))
+ sd->max_weight += 5000+2000*pc_checkskill(sd,RK_DRAGONTRAINING);
+ 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)
+ sd->max_weight += 2000*skill;
+
+ sd->cart_weight_max = battle_config.max_cart_weight + (pc_checkskill(sd, GN_REMODELING_CART)*5000);
+
+ if (pc_checkskill(sd,SM_MOVINGRECOVERY)>0)
+ sd->regen.state.walk = 1;
+ else
+ sd->regen.state.walk = 0;
+
+ // Skill SP cost
+ if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 )
+ sd->dsprate -= 4*skill;
+
+ if(sc->data[SC_SERVICE4U])
+ sd->dsprate -= sc->data[SC_SERVICE4U]->val3;
+
+ if(sc->data[SC_SPCOST_RATE])
+ sd->dsprate -= sc->data[SC_SPCOST_RATE]->val1;
+
+ //Underflow protections.
+ if(sd->dsprate < 0)
+ sd->dsprate = 0;
+ if(sd->castrate < 0)
+ sd->castrate = 0;
+ if(sd->delayrate < 0)
+ sd->delayrate = 0;
+ if(sd->hprecov_rate < 0)
+ sd->hprecov_rate = 0;
+ if(sd->sprecov_rate < 0)
+ sd->sprecov_rate = 0;
+
+ // Anti-element and anti-race
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ sd->subele[ELE_HOLY] += skill*5;
+ if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) {
+ sd->subele[ELE_NEUTRAL] += skill;
+ sd->subele[ELE_FIRE] += skill*4;
+ }
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){
+ skill = skill*4;
+ sd->right_weapon.addrace[RC_DRAGON]+=skill;
+ sd->left_weapon.addrace[RC_DRAGON]+=skill;
+ sd->magic_addrace[RC_DRAGON]+=skill;
+ sd->subrace[RC_DRAGON]+=skill;
+ }
+
+ if(sc->count){
+ if(sc->data[SC_CONCENTRATE]) { //Update the card-bonus data
+ sc->data[SC_CONCENTRATE]->val3 = sd->param_bonus[1]; //Agi
+ sc->data[SC_CONCENTRATE]->val4 = sd->param_bonus[4]; //Dex
+ }
+ if(sc->data[SC_SIEGFRIED]){
+ i = sc->data[SC_SIEGFRIED]->val2;
+ sd->subele[ELE_WATER] += i;
+ sd->subele[ELE_EARTH] += i;
+ sd->subele[ELE_FIRE] += i;
+ sd->subele[ELE_WIND] += i;
+ sd->subele[ELE_POISON] += i;
+ sd->subele[ELE_HOLY] += i;
+ sd->subele[ELE_DARK] += i;
+ sd->subele[ELE_GHOST] += i;
+ sd->subele[ELE_UNDEAD] += i;
+ }
+ if(sc->data[SC_PROVIDENCE]){
+ sd->subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2;
+ sd->subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2;
+ }
+ if(sc->data[SC_ARMOR_ELEMENT]) { //This status change should grant card-type elemental resist.
+ sd->subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT]->val1;
+ sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT]->val2;
+ sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT]->val3;
+ sd->subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT]->val4;
+ }
+ if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll
+ sd->subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1;
+ sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2;
+ sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3;
+ sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4;
+ }
+ if( sc->data[SC_FIRE_CLOAK_OPTION] ) {
+ i = sc->data[SC_FIRE_CLOAK_OPTION]->val2;
+ sd->subele[ELE_FIRE] += i;
+ sd->subele[ELE_WATER] -= i;
+ }
+ if( sc->data[SC_WATER_DROP_OPTION] ) {
+ i = sc->data[SC_WATER_DROP_OPTION]->val2;
+ sd->subele[ELE_WATER] += i;
+ sd->subele[ELE_WIND] -= i;
+ }
+ if( sc->data[SC_WIND_CURTAIN_OPTION] ) {
+ i = sc->data[SC_WIND_CURTAIN_OPTION]->val2;
+ sd->subele[ELE_WIND] += i;
+ sd->subele[ELE_EARTH] -= i;
+ }
+ if( sc->data[SC_STONE_SHIELD_OPTION] ) {
+ i = sc->data[SC_STONE_SHIELD_OPTION]->val2;
+ sd->subele[ELE_EARTH] += i;
+ sd->subele[ELE_FIRE] -= i;
+ }
+ if( sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_FIRE] += 25;
+ if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_WATER] += 25;
+ if( sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_WIND] += 25;
+ if( sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_EARTH] += 25;
+ }
+ status_cpy(&sd->battle_status, status);
+
+// ----- CLIENT-SIDE REFRESH -----
+ if(!sd->bl.prev) {
+ //Will update on LoadEndAck
+ calculating = 0;
+ return 0;
+ }
+ if(memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill)))
+ clif_skillinfoblock(sd);
+ if(b_weight != sd->weight)
+ clif_updatestatus(sd,SP_WEIGHT);
+ if(b_max_weight != sd->max_weight) {
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ pc_updateweightstatus(sd);
+ }
+ if( b_cart_weight_max != sd->cart_weight_max ) {
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+
+ calculating = 0;
+
+ return 0;
+}
+
+int status_calc_mercenary_(struct mercenary_data *md, bool first)
+{
+ struct status_data *status = &md->base_status;
+ struct s_mercenary *merc = &md->mercenary;
+
+ if( first )
+ {
+ memcpy(status, &md->db->status, sizeof(struct status_data));
+ status->mode = MD_CANMOVE|MD_CANATTACK;
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ md->battle_status.hp = merc->hp;
+ md->battle_status.sp = merc->sp;
+ }
+
+ status_calc_misc(&md->bl, status, md->db->lv);
+ status_cpy(&md->battle_status, status);
+
+ return 0;
+}
+
+int status_calc_homunculus_(struct homun_data *hd, bool first)
+{
+ struct status_data *status = &hd->base_status;
+ struct s_homunculus *hom = &hd->homunculus;
+ int skill;
+ int amotion;
+
+ status->str = hom->str / 10;
+ status->agi = hom->agi / 10;
+ status->vit = hom->vit / 10;
+ status->dex = hom->dex / 10;
+ status->int_ = hom->int_ / 10;
+ status->luk = hom->luk / 10;
+
+ if (first) { //[orn]
+ const struct s_homunculus_db *db = hd->homunculusDB;
+ status->def_ele = db->element;
+ status->ele_lv = 1;
+ status->race = db->race;
+ status->size = (hom->class_ == db->evo_class)?db->evo_size:db->base_size;
+ status->rhw.range = 1 + status->size;
+ status->mode = MD_CANMOVE|MD_CANATTACK;
+ status->speed = DEFAULT_WALK_SPEED;
+ if (battle_config.hom_setting&0x8 && hd->master)
+ status->speed = status_get_speed(&hd->master->bl);
+
+ status->hp = 1;
+ status->sp = 1;
+ }
+ skill = hom->level/10 + status->vit/5;
+ status->def = cap_value(skill, 0, 99);
+
+ skill = hom->level/10 + status->int_/5;
+ status->mdef = cap_value(skill, 0, 99);
+
+ status->max_hp = hom->max_hp ;
+ status->max_sp = hom->max_sp ;
+
+ merc_hom_calc_skilltree(hd, 0);
+
+ if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0)
+ status->def += skill * 4;
+
+ if((skill = merc_hom_checkskill(hd,HVAN_INSTRUCT)) > 0)
+ {
+ status->int_ += 1 +skill/2 +skill/4 +skill/5;
+ status->str += 1 +skill/3 +skill/3 +skill/4;
+ }
+
+ if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0)
+ status->max_hp += skill * 2 * status->max_hp / 100;
+
+ if((skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0)
+ status->max_sp += (1 +skill/2 -skill/4 +skill/5) * status->max_sp / 100 ;
+
+ if (first) {
+ hd->battle_status.hp = hom->hp ;
+ hd->battle_status.sp = hom->sp ;
+ }
+
+ status->rhw.atk = status->dex;
+ status->rhw.atk2 = status->str + hom->level;
+
+ status->aspd_rate = 1000;
+
+ amotion = (1000 -4*status->agi -status->dex) * hd->homunculusDB->baseASPD/1000;
+ status->amotion = cap_value(amotion,battle_config.max_aspd,2000);
+ status->adelay = status->amotion; //It seems adelay = amotion for Homunculus.
+
+ status_calc_misc(&hd->bl, status, hom->level);
+
+#ifdef RENEWAL
+ status->matk_max = status->matk_min;
+#endif
+
+ status_cpy(&hd->battle_status, status);
+ return 1;
+}
+
+int status_calc_elemental_(struct elemental_data *ed, bool first) {
+ struct status_data *status = &ed->base_status;
+ struct s_elemental *ele = &ed->elemental;
+ struct map_session_data *sd = ed->master;
+
+ if( !sd )
+ return 0;
+
+ if( first ) {
+ memcpy(status, &ed->db->status, sizeof(struct status_data));
+ if( !ele->mode )
+ status->mode = EL_MODE_PASSIVE;
+ else
+ status->mode = ele->mode;
+
+ status_calc_misc(&ed->bl, status, 0);
+
+ status->max_hp = ele->max_hp;
+ status->max_sp = ele->max_sp;
+ status->hp = ele->hp;
+ status->sp = ele->sp;
+ status->rhw.atk = ele->atk;
+ status->rhw.atk2 = ele->atk2;
+
+ status->matk_min += ele->matk;
+ status->def += ele->def;
+ status->mdef += ele->mdef;
+ status->flee = ele->flee;
+ status->hit = ele->hit;
+
+ memcpy(&ed->battle_status,status,sizeof(struct status_data));
+ } else {
+ status_calc_misc(&ed->bl, status, 0);
+ status_cpy(&ed->battle_status, status);
+ }
+
+ return 0;
+}
+
+int status_calc_npc_(struct npc_data *nd, bool first) {
+ struct status_data *status = &nd->status;
+
+ if (!nd)
+ return 0;
+
+ if (first) {
+ status->hp = 1;
+ status->sp = 1;
+ status->max_hp = 1;
+ status->max_sp = 1;
+
+ status->def_ele = ELE_NEUTRAL;
+ status->ele_lv = 1;
+ status->race = RC_DEMIHUMAN;
+ status->size = nd->size;
+ status->rhw.range = 1 + status->size;
+ status->mode = (MD_CANMOVE|MD_CANATTACK);
+ status->speed = nd->speed;
+ }
+
+ status->str = nd->stat_point;
+ status->agi = nd->stat_point;
+ status->vit = nd->stat_point;
+ status->int_= nd->stat_point;
+ status->dex = nd->stat_point;
+ status->luk = nd->stat_point;
+
+ status_calc_misc(&nd->bl, status, nd->level);
+ status_cpy(&nd->status, status);
+
+ return 0;
+}
+
+static unsigned short status_calc_str(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_agi(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_vit(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_int(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_dex(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_luk(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_batk(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_watk(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_matk(struct block_list *,struct status_change *,int);
+static signed short status_calc_hit(struct block_list *,struct status_change *,int);
+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 defType status_calc_def(struct block_list *bl, struct status_change *sc, int);
+static signed short status_calc_def2(struct block_list *,struct status_change *,int);
+static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int);
+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);
+static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion);
+#ifdef RENEWAL_ASPD
+static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag);
+#endif
+static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int);
+static unsigned int status_calc_maxhp(struct block_list *,struct status_change *, uint64);
+static unsigned int status_calc_maxsp(struct block_list *,struct status_change *,unsigned int);
+static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element);
+static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv);
+static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode);
+#ifdef RENEWAL
+static unsigned short status_calc_ematk(struct block_list *,struct status_change *,int);
+#endif
+
+//Calculates base regen values.
+void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen)
+{
+ struct map_session_data *sd;
+ int val, skill, reg_flag;
+
+ if( !(bl->type&BL_REGEN) || !regen )
+ return;
+
+ sd = BL_CAST(BL_PC,bl);
+ val = 1 + (status->vit/5) + (status->max_hp/200);
+
+ if( sd && sd->hprecov_rate != 100 )
+ val = val*sd->hprecov_rate/100;
+
+ reg_flag = bl->type == BL_PC ? 0 : 1;
+
+ regen->hp = cap_value(val, reg_flag, SHRT_MAX);
+
+ val = 1 + (status->int_/6) + (status->max_sp/100);
+ if( status->int_ >= 120 )
+ val += ((status->int_-120)>>1) + 4;
+
+ if( sd && sd->sprecov_rate != 100 )
+ val = val*sd->sprecov_rate/100;
+
+ regen->sp = cap_value(val, reg_flag, SHRT_MAX);
+
+ if( sd )
+ {
+ struct regen_data_sub *sregen;
+ if( (skill=pc_checkskill(sd,HP_MEDITATIO)) > 0 )
+ {
+ val = regen->sp*(100+3*skill)/100;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ }
+ //Only players have skill/sitting skill regen for now.
+ sregen = regen->sregen;
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,SM_RECOVERY)) > 0 )
+ val += skill*5 + skill*status->max_hp/500;
+ sregen->hp = cap_value(val, 0, SHRT_MAX);
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,MG_SRECOVERY)) > 0 )
+ val += skill*3 + skill*status->max_sp/500;
+ if( (skill=pc_checkskill(sd,NJ_NINPOU)) > 0 )
+ val += skill*3 + skill*status->max_sp/500;
+ if( (skill=pc_checkskill(sd,WM_LESSON)) > 0 )
+ val += 3 + 3 * skill;
+
+ sregen->sp = cap_value(val, 0, SHRT_MAX);
+
+ // Skill-related recovery (only when sit)
+ sregen = regen->ssregen;
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
+ val += skill*4 + skill*status->max_hp/500;
+
+ if( (skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest )
+ val += skill*30 + skill*status->max_hp/500;
+ sregen->hp = cap_value(val, 0, SHRT_MAX);
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest )
+ {
+ val += skill*3 + skill*status->max_sp/500;
+ if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest
+ val += (30+10*skill)*val/100;
+ }
+ if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
+ val += skill*2 + skill*status->max_sp/500;
+ sregen->sp = cap_value(val, 0, SHRT_MAX);
+ }
+
+ if( bl->type == BL_HOM ) {
+ struct homun_data *hd = (TBL_HOM*)bl;
+ if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 ) {
+ val = regen->hp*(100+5*skill)/100;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+ }
+ if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 ) {
+ val = regen->sp*(100+3*skill)/100;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ }
+ } else if( bl->type == BL_MER ) {
+ val = (status->max_hp * status->vit / 10000 + 1) * 6;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+
+ val = (status->max_sp * (status->int_ + 10) / 750) + 1;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ } else if( bl->type == BL_ELEM ) {
+ val = (status->max_hp * status->vit / 10000 + 1) * 6;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+
+ val = (status->max_sp * (status->int_ + 10) / 750) + 1;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ }
+}
+
+//Calculates SC related regen rates.
+void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc)
+{
+ if (!(bl->type&BL_REGEN) || !regen)
+ return;
+
+ regen->flag = RGN_HP|RGN_SP;
+ if(regen->sregen)
+ {
+ if (regen->sregen->hp)
+ regen->flag|=RGN_SHP;
+
+ if (regen->sregen->sp)
+ regen->flag|=RGN_SSP;
+ regen->sregen->rate.hp = regen->sregen->rate.sp = 1;
+ }
+ if (regen->ssregen)
+ {
+ if (regen->ssregen->hp)
+ regen->flag|=RGN_SHP;
+
+ if (regen->ssregen->sp)
+ regen->flag|=RGN_SSP;
+ regen->ssregen->rate.hp = regen->ssregen->rate.sp = 1;
+ }
+ regen->rate.hp = regen->rate.sp = 1;
+
+ if (!sc || !sc->count)
+ return;
+
+ if (
+ (sc->data[SC_POISON] && !sc->data[SC_SLOWPOISON])
+ || (sc->data[SC_DPOISON] && !sc->data[SC_SLOWPOISON])
+ || sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]
+ || sc->data[SC_TRICKDEAD]
+ || sc->data[SC_BLEEDING]
+ || sc->data[SC_MAGICMUSHROOM]
+ || sc->data[SC_RAISINGDRAGON]
+ || sc->data[SC_SATURDAYNIGHTFEVER]
+ ) //No regen
+ regen->flag = 0;
+
+ if (
+ sc->data[SC_DANCING] || sc->data[SC_OBLIVIONCURSE] || sc->data[SC_MAXIMIZEPOWER]
+ || (
+ (bl->type == BL_PC && ((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK &&
+ (sc->data[SC_EXTREMITYFIST] || (sc->data[SC_EXPLOSIONSPIRITS] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK)))
+ )
+ ) //No natural SP regen
+ regen->flag &=~RGN_SP;
+
+ if(
+ sc->data[SC_TENSIONRELAX]
+ ) {
+ regen->rate.hp += 2;
+ if (regen->sregen)
+ regen->sregen->rate.hp += 3;
+ }
+ if (sc->data[SC_MAGNIFICAT])
+ {
+ regen->rate.hp += 1;
+ regen->rate.sp += 1;
+ }
+ if (sc->data[SC_REGENERATION])
+ {
+ const struct status_change_entry *sce = sc->data[SC_REGENERATION];
+ if (!sce->val4)
+ {
+ regen->rate.hp += sce->val2;
+ regen->rate.sp += sce->val3;
+ } else
+ regen->flag&=~sce->val4; //Remove regen as specified by val4
+ }
+ if(sc->data[SC_GT_REVITALIZE]){
+ regen->hp = cap_value(regen->hp*sc->data[SC_GT_REVITALIZE]->val3/100, 1, SHRT_MAX);
+ regen->state.walk= 1;
+ }
+ if ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1) //if insignia lvl 1
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1))
+ regen->rate.hp *= 2;
+
+}
+void status_calc_state( struct block_list *bl, struct status_change *sc, enum scs_flag flag, bool start ) {
+
+ /* no sc at all, we can zero without any extra weight over our conciousness */
+ if( !sc->count ) {
+ memset(&sc->cant, 0, sizeof (sc->cant));
+ return;
+ }
+
+ /* can move? */
+ if( flag&SCS_NOMOVE ) {
+ if( !(flag&SCS_NOMOVECOND) ) {
+ sc->cant.move += ( start ? 1 : -1 );
+ } else if(
+ (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF) // cannot move while gospel is in effect
+ || (sc->data[SC_BASILICA] && sc->data[SC_BASILICA]->val4 == bl->id) // Basilica caster cannot move
+ || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF)
+ || (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB)
+ || (sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 < 3
+ && !(sc->data[SC_CAMOUFLAGE]->val3&1))
+ ) {
+ sc->cant.move += ( start ? 1 : -1 );
+ }
+ }
+
+ /* can't use skills */
+ if( flag&SCS_NOCAST ) {
+ if( !(flag&SCS_NOCASTCOND) ) {
+ sc->cant.cast += ( start ? 1 : -1 );
+ } else if( (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB) ){
+ sc->cant.cast += ( start ? 1 : -1 );
+ }
+ }
+
+ /* player-only states */
+ if( bl->type == BL_PC ) {
+
+ /* can pick items? */
+ if( flag&SCS_NOPICKITEM ) {
+ if( !(flag&SCS_NOPICKITEMCOND) ) {
+ sc->cant.pickup += ( start ? 1 : -1 );
+ } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) {
+ sc->cant.pickup += ( start ? 1 : -1 );
+ }
+ }
+
+ /* can drop items? */
+ if( flag&SCS_NODROPITEM ) {
+ if( !(flag&SCS_NODROPITEMCOND) ) {
+ sc->cant.drop += ( start ? 1 : -1 );
+ } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) {
+ sc->cant.drop += ( start ? 1 : -1 );
+ }
+ }
+ }
+
+ return;
+}
+/// Recalculates parts of an object's battle status according to the specified flags.
+/// @param flag bitfield of values from enum scb_flag
+void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
+{
+ const struct status_data *b_status = status_get_base_status(bl);
+ struct status_data *status = status_get_status_data(bl);
+ struct status_change *sc = status_get_sc(bl);
+ TBL_PC *sd = BL_CAST(BL_PC,bl);
+ int temp;
+
+ if (!b_status || !status)
+ return;
+
+ if((!(bl->type&BL_REGEN)) && (!sc || !sc->count)) { //No difference.
+ status_cpy(status, b_status);
+ return;
+ }
+
+ if(flag&SCB_STR) {
+ status->str = status_calc_str(bl, sc, b_status->str);
+ flag|=SCB_BATK;
+ if( bl->type&BL_HOM )
+ flag |= SCB_WATK;
+ }
+
+ if(flag&SCB_AGI) {
+ status->agi = status_calc_agi(bl, sc, b_status->agi);
+ flag|=SCB_FLEE
+#ifdef RENEWAL
+ |SCB_DEF2
+#endif
+ ;
+ if( bl->type&(BL_PC|BL_HOM) )
+ flag |= SCB_ASPD|SCB_DSPD;
+ }
+
+ if(flag&SCB_VIT) {
+ status->vit = status_calc_vit(bl, sc, b_status->vit);
+ flag|=SCB_DEF2|SCB_MDEF2;
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
+ flag |= SCB_MAXHP;
+ if( bl->type&BL_HOM )
+ flag |= SCB_DEF;
+ }
+
+ if(flag&SCB_INT) {
+ status->int_ = status_calc_int(bl, sc, b_status->int_);
+ flag|=SCB_MATK|SCB_MDEF2;
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
+ flag |= SCB_MAXSP;
+ if( bl->type&BL_HOM )
+ flag |= SCB_MDEF;
+ }
+
+ if(flag&SCB_DEX) {
+ status->dex = status_calc_dex(bl, sc, b_status->dex);
+ flag|=SCB_BATK|SCB_HIT
+#ifdef RENEWAL
+ |SCB_MATK|SCB_MDEF2
+#endif
+ ;
+ if( bl->type&(BL_PC|BL_HOM) )
+ flag |= SCB_ASPD;
+ if( bl->type&BL_HOM )
+ flag |= SCB_WATK;
+ }
+
+ if(flag&SCB_LUK) {
+ status->luk = status_calc_luk(bl, sc, b_status->luk);
+ flag|=SCB_BATK|SCB_CRI|SCB_FLEE2
+#ifdef RENEWAL
+ |SCB_MATK|SCB_HIT|SCB_FLEE
+#endif
+ ;
+ }
+
+ if(flag&SCB_BATK && b_status->batk) {
+ status->batk = status_base_atk(bl,status);
+ temp = b_status->batk - status_base_atk(bl,b_status);
+ if (temp)
+ {
+ temp += status->batk;
+ status->batk = cap_value(temp, 0, USHRT_MAX);
+ }
+ status->batk = status_calc_batk(bl, sc, status->batk);
+ }
+
+ if(flag&SCB_WATK) {
+
+ status->rhw.atk = status_calc_watk(bl, sc, b_status->rhw.atk);
+ if (!sd) //Should not affect weapon refine bonus
+ status->rhw.atk2 = status_calc_watk(bl, sc, b_status->rhw.atk2);
+
+ if(b_status->lhw.atk) {
+ if (sd) {
+ sd->state.lr_flag = 1;
+ status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
+ sd->state.lr_flag = 0;
+ } else {
+ status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
+ status->lhw.atk2= status_calc_watk(bl, sc, b_status->lhw.atk2);
+ }
+ }
+
+ if( bl->type&BL_HOM )
+ {
+ status->rhw.atk += (status->dex - b_status->dex);
+ status->rhw.atk2 += (status->str - b_status->str);
+ if( status->rhw.atk2 < status->rhw.atk )
+ status->rhw.atk2 = status->rhw.atk;
+ }
+ }
+
+ if(flag&SCB_HIT) {
+ if (status->dex == b_status->dex
+#ifdef RENEWAL
+ && status->luk == b_status->luk
+#endif
+ )
+ status->hit = status_calc_hit(bl, sc, b_status->hit);
+ else
+ status->hit = status_calc_hit(bl, sc, b_status->hit + (status->dex - b_status->dex)
+#ifdef RENEWAL
+ + (status->luk/3 - b_status->luk/3)
+#endif
+ );
+ }
+
+ if(flag&SCB_FLEE) {
+ if (status->agi == b_status->agi
+#ifdef RENEWAL
+ && status->luk == b_status->luk
+#endif
+ )
+ status->flee = status_calc_flee(bl, sc, b_status->flee);
+ else
+ status->flee = status_calc_flee(bl, sc, b_status->flee +(status->agi - b_status->agi)
+#ifdef RENEWAL
+ + (status->luk/5 - b_status->luk/5)
+#endif
+ );
+ }
+
+ if(flag&SCB_DEF)
+ {
+ status->def = status_calc_def(bl, sc, b_status->def);
+
+ if( bl->type&BL_HOM )
+ status->def += (status->vit/5 - b_status->vit/5);
+ }
+
+ if(flag&SCB_DEF2) {
+ if (status->vit == b_status->vit
+#ifdef RENEWAL
+ && status->agi == b_status->agi
+#endif
+ )
+ status->def2 = status_calc_def2(bl, sc, b_status->def2);
+ else
+ status->def2 = status_calc_def2(bl, sc, b_status->def2
+#ifdef RENEWAL
+ + (int)( ((float)status->vit/2 + (float)b_status->vit/2) + ((float)status->agi/5 + (float)b_status->agi/5) )
+#else
+ + (status->vit - b_status->vit)
+#endif
+ );
+ }
+
+ if(flag&SCB_MDEF)
+ {
+ status->mdef = status_calc_mdef(bl, sc, b_status->mdef);
+
+ if( bl->type&BL_HOM )
+ status->mdef += (status->int_/5 - b_status->int_/5);
+ }
+
+ if(flag&SCB_MDEF2) {
+ if (status->int_ == b_status->int_ && status->vit == b_status->vit
+#ifdef RENEWAL
+ && status->dex == b_status->dex
+#endif
+ )
+ status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2);
+ else
+ status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2 +(status->int_ - b_status->int_)
+#ifdef RENEWAL
+ + (int)( ((float)status->dex/5 - (float)b_status->dex/5) + ((float)status->vit/5 + (float)b_status->vit/5) )
+#else
+ + ((status->vit - b_status->vit)>>1)
+#endif
+ );
+ }
+
+ if(flag&SCB_SPEED) {
+ struct unit_data *ud = unit_bl2ud(bl);
+ status->speed = status_calc_speed(bl, sc, b_status->speed);
+
+ //Re-walk to adjust speed (we do not check if walktimer != INVALID_TIMER
+ //because if you step on something while walking, the moment this
+ //piece of code triggers the walk-timer is set on INVALID_TIMER) [Skotlex]
+ if (ud)
+ ud->state.change_walk_target = ud->state.speed_changed = 1;
+
+ if( bl->type&BL_PC && status->speed < battle_config.max_walk_speed )
+ status->speed = battle_config.max_walk_speed;
+
+ if( bl->type&BL_HOM && battle_config.hom_setting&0x8 && ((TBL_HOM*)bl)->master)
+ status->speed = status_get_speed(&((TBL_HOM*)bl)->master->bl);
+
+
+ }
+
+ if(flag&SCB_CRI && b_status->cri) {
+ if (status->luk == b_status->luk)
+ status->cri = status_calc_critical(bl, sc, b_status->cri);
+ else
+ status->cri = status_calc_critical(bl, sc, b_status->cri + 3*(status->luk - b_status->luk));
+ /**
+ * after status_calc_critical so the bonus is applied despite if you have or not a sc bugreport:5240
+ **/
+ if( bl->type == BL_PC && ((TBL_PC*)bl)->status.weapon == W_KATAR )
+ status->cri <<= 1;
+
+ }
+
+ if(flag&SCB_FLEE2 && b_status->flee2) {
+ if (status->luk == b_status->luk)
+ status->flee2 = status_calc_flee2(bl, sc, b_status->flee2);
+ else
+ status->flee2 = status_calc_flee2(bl, sc, b_status->flee2 +(status->luk - b_status->luk));
+ }
+
+ if(flag&SCB_ATK_ELE) {
+ status->rhw.ele = status_calc_attack_element(bl, sc, b_status->rhw.ele);
+ if (sd) sd->state.lr_flag = 1;
+ status->lhw.ele = status_calc_attack_element(bl, sc, b_status->lhw.ele);
+ if (sd) sd->state.lr_flag = 0;
+ }
+
+ if(flag&SCB_DEF_ELE) {
+ status->def_ele = status_calc_element(bl, sc, b_status->def_ele);
+ status->ele_lv = status_calc_element_lv(bl, sc, b_status->ele_lv);
+ }
+
+ if(flag&SCB_MODE)
+ {
+ status->mode = status_calc_mode(bl, sc, b_status->mode);
+ //Since mode changed, reset their state.
+ if (!(status->mode&MD_CANATTACK))
+ unit_stop_attack(bl);
+ if (!(status->mode&MD_CANMOVE))
+ unit_stop_walking(bl,1);
+ }
+
+// No status changes alter these yet.
+// if(flag&SCB_SIZE)
+// if(flag&SCB_RACE)
+// if(flag&SCB_RANGE)
+
+ if(flag&SCB_MAXHP) {
+ if( bl->type&BL_PC )
+ {
+ status->max_hp = status_base_pc_maxhp(sd,status);
+ status->max_hp += b_status->max_hp - sd->status.max_hp;
+
+ status->max_hp = status_calc_maxhp(bl, sc, status->max_hp);
+
+ if( status->max_hp > (unsigned int)battle_config.max_hp )
+ status->max_hp = (unsigned int)battle_config.max_hp;
+ }
+ else
+ {
+ status->max_hp = status_calc_maxhp(bl, sc, b_status->max_hp);
+ }
+
+ if( status->hp > status->max_hp ) //FIXME: Should perhaps a status_zap should be issued?
+ {
+ status->hp = status->max_hp;
+ if( sd ) clif_updatestatus(sd,SP_HP);
+ }
+ }
+
+ if(flag&SCB_MAXSP) {
+ if( bl->type&BL_PC )
+ {
+ status->max_sp = status_base_pc_maxsp(sd,status);
+ status->max_sp += b_status->max_sp - sd->status.max_sp;
+
+ status->max_sp = status_calc_maxsp(&sd->bl, &sd->sc, status->max_sp);
+
+ if( status->max_sp > (unsigned int)battle_config.max_sp )
+ status->max_sp = (unsigned int)battle_config.max_sp;
+ }
+ else
+ {
+ status->max_sp = status_calc_maxsp(bl, sc, b_status->max_sp);
+ }
+
+ if( status->sp > status->max_sp )
+ {
+ status->sp = status->max_sp;
+ if( sd ) clif_updatestatus(sd,SP_SP);
+ }
+ }
+
+ if(flag&SCB_MATK) {
+#ifndef RENEWAL
+ status->matk_min = status_base_matk_min(status) + (sd?sd->bonus.ematk:0);
+ status->matk_max = status_base_matk_max(status) + (sd?sd->bonus.ematk:0);
+#else
+ /**
+ * RE MATK Formula (from irowiki:http://irowiki.org/wiki/MATK)
+ * MATK = (sMATK + wMATK + eMATK) * Multiplicative Modifiers
+ **/
+ status->matk_min = status->matk_max = status_base_matk(status, status_get_lv(bl));
+ if( bl->type&BL_PC ){
+ // Any +MATK you get from skills and cards, including cards in weapon, is added here.
+ if( sd->bonus.ematk > 0 ){
+ status->matk_max += sd->bonus.ematk;
+ status->matk_min += sd->bonus.ematk;
+ }
+ status->matk_min = status_calc_ematk(bl, sc, status->matk_min);
+ status->matk_max = status_calc_ematk(bl, sc, status->matk_max);
+ //This is the only portion in MATK that varies depending on the weapon level and refinement rate.
+ if( status->rhw.matk > 0 ){
+ int wMatk = status->rhw.matk;
+ int variance = wMatk * status->rhw.wlv / 10;
+ status->matk_min += wMatk - variance;
+ status->matk_max += wMatk + variance;
+ }
+ }
+#endif
+ if (bl->type&BL_PC && sd->matk_rate != 100) {
+ status->matk_max = status->matk_max * sd->matk_rate/100;
+ 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 ((bl->type&BL_HOM && battle_config.hom_setting&0x20) //Hom Min Matk is always the same as Max Matk
+ || sc->data[SC_RECOGNIZEDSPELL])
+ status->matk_min = status->matk_max;
+
+#ifdef RENEWAL
+ if( sd && sd->right_weapon.overrefine > 0){
+ status->matk_min++;
+ status->matk_max += sd->right_weapon.overrefine - 1;
+ }
+#endif
+
+ }
+
+ if(flag&SCB_ASPD) {
+ int amotion;
+ if( bl->type&BL_PC )
+ {
+ amotion = status_base_amotion_pc(sd,status);
+#ifndef RENEWAL_ASPD
+ status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
+
+ if(status->aspd_rate != 1000)
+ amotion = amotion*status->aspd_rate/1000;
+#else
+ // aspd = baseaspd + floor(sqrt((agi^2/2) + (dex^2/5))/4 + (potskillbonus*agi/200))
+ amotion -= (int)(sqrt( (pow(status->agi, 2) / 2) + (pow(status->dex, 2) / 5) ) / 4 + (status_calc_aspd(bl, sc, 1) * status->agi / 200)) * 10;
+
+ if( (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) != 0 ) // RE ASPD percertage modifier
+ amotion -= ( amotion - ((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd) )
+ * (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) / 100;
+
+ if(status->aspd_rate != 1000) // absolute percentage modifier
+ amotion = ( 200 - (200-amotion/10) * status->aspd_rate / 1000 ) * 10;
+#endif
+ amotion = status_calc_fix_aspd(bl, sc, amotion);
+ status->amotion = cap_value(amotion,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000);
+
+ status->adelay = 2*status->amotion;
+ }
+ else
+ if( bl->type&BL_HOM )
+ {
+ amotion = (1000 -4*status->agi -status->dex) * ((TBL_HOM*)bl)->homunculusDB->baseASPD/1000;
+ status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
+
+ if(status->aspd_rate != 1000)
+ amotion = amotion*status->aspd_rate/1000;
+
+ amotion = status_calc_fix_aspd(bl, sc, amotion);
+ status->amotion = cap_value(amotion,battle_config.max_aspd,2000);
+
+ status->adelay = status->amotion;
+ }
+ else // mercenary and mobs
+ {
+ amotion = b_status->amotion;
+ status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
+
+ if(status->aspd_rate != 1000)
+ amotion = amotion*status->aspd_rate/1000;
+
+ amotion = status_calc_fix_aspd(bl, sc, amotion);
+ status->amotion = cap_value(amotion, battle_config.monster_max_aspd, 2000);
+
+ temp = b_status->adelay*status->aspd_rate/1000;
+ status->adelay = cap_value(temp, battle_config.monster_max_aspd*2, 4000);
+ }
+ }
+
+ if(flag&SCB_DSPD) {
+ int dmotion;
+ if( bl->type&BL_PC )
+ {
+ if (b_status->agi == status->agi)
+ status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
+ else {
+ dmotion = 800-status->agi*4;
+ status->dmotion = cap_value(dmotion, 400, 800);
+ if(battle_config.pc_damage_delay_rate != 100)
+ status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100;
+ //It's safe to ignore b_status->dmotion since no bonus affects it.
+ status->dmotion = status_calc_dmotion(bl, sc, status->dmotion);
+ }
+ }
+ else
+ if( bl->type&BL_HOM )
+ {
+ dmotion = 800-status->agi*4;
+ status->dmotion = cap_value(dmotion, 400, 800);
+ status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
+ }
+ else // mercenary and mobs
+ {
+ status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
+ }
+ }
+
+ 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
+/// @param first if true, will cause status_calc_* functions to run their base status initialization code
+void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
+{
+ struct status_data b_status; // previous battle status
+ struct status_data* status; // pointer to current battle status
+
+ // remember previous values
+ status = status_get_status_data(bl);
+ memcpy(&b_status, status, sizeof(struct status_data));
+
+ if( flag&SCB_BASE ) {// calculate the object's base status too
+ switch( bl->type ) {
+ case BL_PC: status_calc_pc_(BL_CAST(BL_PC,bl), first); break;
+ case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), first); break;
+ case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), first); break;
+ case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl), first); break;
+ case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), first); break;
+ case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), first); break;
+ case BL_NPC: status_calc_npc_(BL_CAST(BL_NPC,bl), first); break;
+ }
+ }
+
+ if( bl->type == BL_PET )
+ return; // pets are not affected by statuses
+
+ if( first && bl->type == BL_MOB )
+ return; // assume there will be no statuses active
+
+ status_calc_bl_main(bl, flag);
+
+ if( first && bl->type == BL_HOM )
+ return; // client update handled by caller
+
+ // compare against new values and send client updates
+ if( bl->type == BL_PC )
+ {
+ TBL_PC* sd = BL_CAST(BL_PC, bl);
+ if(b_status.str != status->str)
+ clif_updatestatus(sd,SP_STR);
+ if(b_status.agi != status->agi)
+ clif_updatestatus(sd,SP_AGI);
+ if(b_status.vit != status->vit)
+ clif_updatestatus(sd,SP_VIT);
+ if(b_status.int_ != status->int_)
+ clif_updatestatus(sd,SP_INT);
+ if(b_status.dex != status->dex)
+ clif_updatestatus(sd,SP_DEX);
+ if(b_status.luk != status->luk)
+ clif_updatestatus(sd,SP_LUK);
+ if(b_status.hit != status->hit)
+ clif_updatestatus(sd,SP_HIT);
+ if(b_status.flee != status->flee)
+ clif_updatestatus(sd,SP_FLEE1);
+ if(b_status.amotion != status->amotion)
+ clif_updatestatus(sd,SP_ASPD);
+ if(b_status.speed != status->speed)
+ clif_updatestatus(sd,SP_SPEED);
+
+ if(b_status.batk != status->batk
+#ifndef RENEWAL
+ || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk
+#endif
+ )
+ clif_updatestatus(sd,SP_ATK1);
+
+ if(b_status.def != status->def){
+ clif_updatestatus(sd,SP_DEF1);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_DEF2);
+#endif
+ }
+
+ if(b_status.rhw.atk2 != status->rhw.atk2 || b_status.lhw.atk2 != status->lhw.atk2
+#ifdef RENEWAL
+ || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk
+#endif
+ )
+ clif_updatestatus(sd,SP_ATK2);
+
+ if(b_status.def2 != status->def2){
+ clif_updatestatus(sd,SP_DEF2);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_DEF1);
+#endif
+ }
+ if(b_status.flee2 != status->flee2)
+ clif_updatestatus(sd,SP_FLEE2);
+ if(b_status.cri != status->cri)
+ clif_updatestatus(sd,SP_CRITICAL);
+#ifndef RENEWAL
+ 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);
+#else
+ if(b_status.matk_max != status->matk_max || b_status.matk_min != status->matk_min){
+ clif_updatestatus(sd,SP_MATK2);
+ clif_updatestatus(sd,SP_MATK1);
+ }
+#endif
+ if(b_status.mdef != status->mdef){
+ clif_updatestatus(sd,SP_MDEF1);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_MDEF2);
+#endif
+ }
+ if(b_status.mdef2 != status->mdef2){
+ clif_updatestatus(sd,SP_MDEF2);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_MDEF1);
+#endif
+ }
+ if(b_status.rhw.range != status->rhw.range)
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ if(b_status.max_hp != status->max_hp)
+ clif_updatestatus(sd,SP_MAXHP);
+ if(b_status.max_sp != status->max_sp)
+ clif_updatestatus(sd,SP_MAXSP);
+ if(b_status.hp != status->hp)
+ clif_updatestatus(sd,SP_HP);
+ if(b_status.sp != status->sp)
+ clif_updatestatus(sd,SP_SP);
+ } else if( bl->type == BL_HOM ) {
+ TBL_HOM* hd = BL_CAST(BL_HOM, bl);
+ if( hd->master && memcmp(&b_status, status, sizeof(struct status_data)) != 0 )
+ clif_hominfo(hd->master,hd,0);
+ } else if( bl->type == BL_MER ) {
+ TBL_MER* md = BL_CAST(BL_MER, bl);
+ if( b_status.rhw.atk != status->rhw.atk || b_status.rhw.atk2 != status->rhw.atk2 )
+ clif_mercenary_updatestatus(md->master, SP_ATK1);
+ if( b_status.matk_max != status->matk_max )
+ clif_mercenary_updatestatus(md->master, SP_MATK1);
+ if( b_status.hit != status->hit )
+ clif_mercenary_updatestatus(md->master, SP_HIT);
+ if( b_status.cri != status->cri )
+ clif_mercenary_updatestatus(md->master, SP_CRITICAL);
+ if( b_status.def != status->def )
+ clif_mercenary_updatestatus(md->master, SP_DEF1);
+ if( b_status.mdef != status->mdef )
+ clif_mercenary_updatestatus(md->master, SP_MDEF1);
+ if( b_status.flee != status->flee )
+ clif_mercenary_updatestatus(md->master, SP_MERCFLEE);
+ if( b_status.amotion != status->amotion )
+ clif_mercenary_updatestatus(md->master, SP_ASPD);
+ if( b_status.max_hp != status->max_hp )
+ clif_mercenary_updatestatus(md->master, SP_MAXHP);
+ if( b_status.max_sp != status->max_sp )
+ clif_mercenary_updatestatus(md->master, SP_MAXSP);
+ if( b_status.hp != status->hp )
+ clif_mercenary_updatestatus(md->master, SP_HP);
+ if( b_status.sp != status->sp )
+ clif_mercenary_updatestatus(md->master, SP_SP);
+ } else if( bl->type == BL_ELEM ) {
+ TBL_ELEM* ed = BL_CAST(BL_ELEM, bl);
+ if( b_status.max_hp != status->max_hp )
+ clif_elemental_updatestatus(ed->master, SP_MAXHP);
+ if( b_status.max_sp != status->max_sp )
+ clif_elemental_updatestatus(ed->master, SP_MAXSP);
+ if( b_status.hp != status->hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( b_status.sp != status->sp )
+ clif_mercenary_updatestatus(ed->master, SP_SP);
+ }
+}
+
+/*==========================================
+ * Apply shared stat mods from status changes [DracoRPG]
+ *------------------------------------------*/
+static unsigned short status_calc_str(struct block_list *bl, struct status_change *sc, int str)
+{
+ if(!sc || !sc->count)
+ return cap_value(str,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ str -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(str,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && str < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ str += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCSTR])
+ str += sc->data[SC_INCSTR]->val1;
+ if(sc->data[SC_STRFOOD])
+ str += sc->data[SC_STRFOOD]->val1;
+ if(sc->data[SC_FOOD_STR_CASH])
+ str += sc->data[SC_FOOD_STR_CASH]->val1;
+ if(sc->data[SC_BATTLEORDERS])
+ str += 5;
+ if(sc->data[SC_LEADERSHIP])
+ str += sc->data[SC_LEADERSHIP]->val1;
+ if(sc->data[SC_LOUD])
+ str += 4;
+ if(sc->data[SC_TRUESIGHT])
+ str += 5;
+ if(sc->data[SC_SPURT])
+ str += 10;
+ if(sc->data[SC_NEN])
+ str += sc->data[SC_NEN]->val1;
+ if(sc->data[SC_BLESSING]){
+ if(sc->data[SC_BLESSING]->val2)
+ str += sc->data[SC_BLESSING]->val2;
+ else
+ str >>= 1;
+ }
+ if(sc->data[SC_MARIONETTE])
+ str -= ((sc->data[SC_MARIONETTE]->val3)>>16)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ str += ((sc->data[SC_MARIONETTE2]->val3)>>16)&0xFF;
+ if(sc->data[SC_GIANTGROWTH])
+ str += 30;
+ if(sc->data[SC_SAVAGE_STEAK])
+ str += sc->data[SC_SAVAGE_STEAK]->val1;
+ if(sc->data[SC_INSPIRATION])
+ str += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ str -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ str -= sc->data[SC_KYOUGAKU]->val2;
+
+ return (unsigned short)cap_value(str,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_agi(struct block_list *bl, struct status_change *sc, int agi)
+{
+ if(!sc || !sc->count)
+ return cap_value(agi,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ agi -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(agi,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && agi < 50)
+ return 50;
+ if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
+ agi += (agi-sc->data[SC_CONCENTRATE]->val3)*sc->data[SC_CONCENTRATE]->val2/100;
+ if(sc->data[SC_INCALLSTATUS])
+ agi += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCAGI])
+ agi += sc->data[SC_INCAGI]->val1;
+ if(sc->data[SC_AGIFOOD])
+ agi += sc->data[SC_AGIFOOD]->val1;
+ if(sc->data[SC_FOOD_AGI_CASH])
+ agi += sc->data[SC_FOOD_AGI_CASH]->val1;
+ if(sc->data[SC_SOULCOLD])
+ agi += sc->data[SC_SOULCOLD]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ agi += 5;
+ if(sc->data[SC_INCREASEAGI])
+ agi += sc->data[SC_INCREASEAGI]->val2;
+ if(sc->data[SC_INCREASING])
+ agi += 4; // added based on skill updates [Reddozen]
+ if(sc->data[SC_DECREASEAGI])
+ agi -= sc->data[SC_DECREASEAGI]->val2;
+ if(sc->data[SC_QUAGMIRE])
+ agi -= sc->data[SC_QUAGMIRE]->val2;
+ if(sc->data[SC_SUITON] && sc->data[SC_SUITON]->val3)
+ agi -= sc->data[SC_SUITON]->val2;
+ if(sc->data[SC_MARIONETTE])
+ agi -= ((sc->data[SC_MARIONETTE]->val3)>>8)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ agi += ((sc->data[SC_MARIONETTE2]->val3)>>8)&0xFF;
+ if(sc->data[SC_ADORAMUS])
+ agi -= sc->data[SC_ADORAMUS]->val2;
+ if(sc->data[SC_DROCERA_HERB_STEAMED])
+ agi += sc->data[SC_DROCERA_HERB_STEAMED]->val1;
+ if(sc->data[SC_INSPIRATION])
+ agi += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ agi -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ agi -= sc->data[SC_KYOUGAKU]->val2;
+
+ return (unsigned short)cap_value(agi,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc, int vit)
+{
+ if(!sc || !sc->count)
+ return cap_value(vit,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ vit -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(vit,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && vit < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ vit += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCVIT])
+ vit += sc->data[SC_INCVIT]->val1;
+ if(sc->data[SC_VITFOOD])
+ vit += sc->data[SC_VITFOOD]->val1;
+ if(sc->data[SC_FOOD_VIT_CASH])
+ vit += sc->data[SC_FOOD_VIT_CASH]->val1;
+ if(sc->data[SC_CHANGE])
+ vit += sc->data[SC_CHANGE]->val2;
+ if(sc->data[SC_GLORYWOUNDS])
+ vit += sc->data[SC_GLORYWOUNDS]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ vit += 5;
+ if(sc->data[SC_MARIONETTE])
+ vit -= sc->data[SC_MARIONETTE]->val3&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ vit += sc->data[SC_MARIONETTE2]->val3&0xFF;
+ if(sc->data[SC_LAUDAAGNUS])
+ vit += 4 + sc->data[SC_LAUDAAGNUS]->val1;
+ if(sc->data[SC_MINOR_BBQ])
+ vit += sc->data[SC_MINOR_BBQ]->val1;
+ if(sc->data[SC_INSPIRATION])
+ vit += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ vit -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ vit -= sc->data[SC_KYOUGAKU]->val2;
+
+ if(sc->data[SC_STRIPARMOR])
+ vit -= vit * sc->data[SC_STRIPARMOR]->val2/100;
+
+ return (unsigned short)cap_value(vit,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_int(struct block_list *bl, struct status_change *sc, int int_)
+{
+ if(!sc || !sc->count)
+ return cap_value(int_,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ int_ -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(int_,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && int_ < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ int_ += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCINT])
+ int_ += sc->data[SC_INCINT]->val1;
+ if(sc->data[SC_INTFOOD])
+ int_ += sc->data[SC_INTFOOD]->val1;
+ if(sc->data[SC_FOOD_INT_CASH])
+ int_ += sc->data[SC_FOOD_INT_CASH]->val1;
+ if(sc->data[SC_CHANGE])
+ int_ += sc->data[SC_CHANGE]->val3;
+ if(sc->data[SC_BATTLEORDERS])
+ int_ += 5;
+ if(sc->data[SC_TRUESIGHT])
+ int_ += 5;
+ if(sc->data[SC_BLESSING]){
+ if (sc->data[SC_BLESSING]->val2)
+ int_ += sc->data[SC_BLESSING]->val2;
+ else
+ int_ >>= 1;
+ }
+ if(sc->data[SC_NEN])
+ int_ += sc->data[SC_NEN]->val1;
+ if(sc->data[SC_MARIONETTE])
+ int_ -= ((sc->data[SC_MARIONETTE]->val4)>>16)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ int_ += ((sc->data[SC_MARIONETTE2]->val4)>>16)&0xFF;
+ if(sc->data[SC_MANDRAGORA])
+ int_ -= 5 + 5 * sc->data[SC_MANDRAGORA]->val1;
+ if(sc->data[SC_COCKTAIL_WARG_BLOOD])
+ int_ += sc->data[SC_COCKTAIL_WARG_BLOOD]->val1;
+ if(sc->data[SC_INSPIRATION])
+ int_ += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ int_ -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ int_ -= sc->data[SC_KYOUGAKU]->val2;
+
+ if(sc->data[SC_STRIPHELM])
+ int_ -= int_ * sc->data[SC_STRIPHELM]->val2/100;
+ if(sc->data[SC__STRIPACCESSORY])
+ int_ -= int_ * sc->data[SC__STRIPACCESSORY]->val2 / 100;
+
+ return (unsigned short)cap_value(int_,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_dex(struct block_list *bl, struct status_change *sc, int dex)
+{
+ if(!sc || !sc->count)
+ return cap_value(dex,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ dex -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(dex,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && dex < 50)
+ return 50;
+ if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
+ dex += (dex-sc->data[SC_CONCENTRATE]->val4)*sc->data[SC_CONCENTRATE]->val2/100;
+ if(sc->data[SC_INCALLSTATUS])
+ dex += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCDEX])
+ dex += sc->data[SC_INCDEX]->val1;
+ if(sc->data[SC_DEXFOOD])
+ dex += sc->data[SC_DEXFOOD]->val1;
+ if(sc->data[SC_FOOD_DEX_CASH])
+ dex += sc->data[SC_FOOD_DEX_CASH]->val1;
+ if(sc->data[SC_BATTLEORDERS])
+ dex += 5;
+ if(sc->data[SC_HAWKEYES])
+ dex += sc->data[SC_HAWKEYES]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ dex += 5;
+ if(sc->data[SC_QUAGMIRE])
+ dex -= sc->data[SC_QUAGMIRE]->val2;
+ if(sc->data[SC_BLESSING]){
+ if (sc->data[SC_BLESSING]->val2)
+ dex += sc->data[SC_BLESSING]->val2;
+ else
+ dex >>= 1;
+ }
+ if(sc->data[SC_INCREASING])
+ dex += 4; // added based on skill updates [Reddozen]
+ if(sc->data[SC_MARIONETTE])
+ dex -= ((sc->data[SC_MARIONETTE]->val4)>>8)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ dex += ((sc->data[SC_MARIONETTE2]->val4)>>8)&0xFF;
+ if(sc->data[SC_SIROMA_ICE_TEA])
+ dex += sc->data[SC_SIROMA_ICE_TEA]->val1;
+ if(sc->data[SC_INSPIRATION])
+ dex += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ dex -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ dex -= sc->data[SC_KYOUGAKU]->val2;
+
+ if(sc->data[SC__STRIPACCESSORY])
+ dex -= dex * sc->data[SC__STRIPACCESSORY]->val2 / 100;
+
+ return (unsigned short)cap_value(dex,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_luk(struct block_list *bl, struct status_change *sc, int luk)
+{
+ if(!sc || !sc->count)
+ return cap_value(luk,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ luk -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(luk,0,USHRT_MAX);
+ }
+ if(sc->data[SC_CURSE])
+ return 0;
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && luk < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ luk += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCLUK])
+ luk += sc->data[SC_INCLUK]->val1;
+ if(sc->data[SC_LUKFOOD])
+ luk += sc->data[SC_LUKFOOD]->val1;
+ if(sc->data[SC_FOOD_LUK_CASH])
+ luk += sc->data[SC_FOOD_LUK_CASH]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ luk += 5;
+ if(sc->data[SC_GLORIA])
+ luk += 30;
+ if(sc->data[SC_MARIONETTE])
+ luk -= sc->data[SC_MARIONETTE]->val4&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ luk += sc->data[SC_MARIONETTE2]->val4&0xFF;
+ if(sc->data[SC_PUTTI_TAILS_NOODLES])
+ luk += sc->data[SC_PUTTI_TAILS_NOODLES]->val1;
+ if(sc->data[SC_INSPIRATION])
+ luk += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ luk -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ luk -= sc->data[SC_KYOUGAKU]->val2;
+ if(sc->data[SC_LAUDARAMUS])
+ luk += 4 + sc->data[SC_LAUDARAMUS]->val1;
+
+ if(sc->data[SC__STRIPACCESSORY])
+ luk -= luk * sc->data[SC__STRIPACCESSORY]->val2 / 100;
+ if(sc->data[SC_BANANA_BOMB])
+ luk -= luk * sc->data[SC_BANANA_BOMB]->val1 / 100;
+
+ return (unsigned short)cap_value(luk,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, int batk)
+{
+ if(!sc || !sc->count)
+ return cap_value(batk,0,USHRT_MAX);
+
+ if(sc->data[SC_ATKPOTION])
+ batk += sc->data[SC_ATKPOTION]->val1;
+ if(sc->data[SC_BATKFOOD])
+ batk += sc->data[SC_BATKFOOD]->val1;
+ if(sc->data[SC_GATLINGFEVER])
+ batk += sc->data[SC_GATLINGFEVER]->val3;
+ if(sc->data[SC_MADNESSCANCEL])
+ batk += 100;
+ if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
+ batk += 50;
+ if(bl->type == BL_ELEM
+ && ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1))
+ )
+ batk += batk / 5;
+ if(sc->data[SC_FULL_SWING_K])
+ batk += sc->data[SC_FULL_SWING_K]->val1;
+ if(sc->data[SC_ODINS_POWER])
+ batk += 70;
+ if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
+ if(status_get_element(bl) == ELE_WATER) //water type
+ batk /= 2;
+ }
+ if(sc->data[SC_PYROCLASTIC])
+ batk += sc->data[SC_PYROCLASTIC]->val2;
+ if (sc->data[SC_ANGRIFFS_MODUS])
+ batk += sc->data[SC_ANGRIFFS_MODUS]->val2;
+
+ if(sc->data[SC_INCATKRATE])
+ batk += batk * sc->data[SC_INCATKRATE]->val1/100;
+ if(sc->data[SC_PROVOKE])
+ batk += batk * sc->data[SC_PROVOKE]->val3/100;
+ if(sc->data[SC_CONCENTRATION])
+ batk += batk * sc->data[SC_CONCENTRATION]->val2/100;
+ if(sc->data[SC_SKE])
+ batk += batk * 3;
+ if(sc->data[SC_BLOODLUST])
+ batk += batk * sc->data[SC_BLOODLUST]->val2/100;
+ if(sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST)
+ batk -= batk * 25/100;
+ if(sc->data[SC_CURSE])
+ batk -= batk * 25/100;
+//Curse shouldn't effect on this? <- Curse OR Bleeding??
+// if(sc->data[SC_BLEEDING])
+// batk -= batk * 25/100;
+ if(sc->data[SC_FLEET])
+ batk += batk * sc->data[SC_FLEET]->val3/100;
+ if(sc->data[SC__ENERVATION])
+ batk -= batk * sc->data[SC__ENERVATION]->val2 / 100;
+ if(sc->data[SC_RUSHWINDMILL])
+ batk += batk * sc->data[SC_RUSHWINDMILL]->val2/100;
+ if(sc->data[SC_SATURDAYNIGHTFEVER])
+ batk += 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1;
+ if(sc->data[SC_MELODYOFSINK])
+ batk -= batk * sc->data[SC_MELODYOFSINK]->val3/100;
+ if(sc->data[SC_BEYONDOFWARCRY])
+ batk += batk * sc->data[SC_BEYONDOFWARCRY]->val3/100;
+ if( sc->data[SC_ZANGETSU] )
+ batk += batk * sc->data[SC_ZANGETSU]->val2 / 100;
+
+ return (unsigned short)cap_value(batk,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_watk(struct block_list *bl, struct status_change *sc, int watk)
+{
+ if(!sc || !sc->count)
+ return cap_value(watk,0,USHRT_MAX);
+
+ if(sc->data[SC_IMPOSITIO])
+ watk += sc->data[SC_IMPOSITIO]->val2;
+ if(sc->data[SC_WATKFOOD])
+ watk += sc->data[SC_WATKFOOD]->val1;
+ if(sc->data[SC_DRUMBATTLE])
+ watk += sc->data[SC_DRUMBATTLE]->val2;
+ if(sc->data[SC_VOLCANO])
+ watk += sc->data[SC_VOLCANO]->val2;
+ if(sc->data[SC_MERC_ATKUP])
+ watk += sc->data[SC_MERC_ATKUP]->val2;
+ if(sc->data[SC_FIGHTINGSPIRIT])
+ watk += sc->data[SC_FIGHTINGSPIRIT]->val1;
+ if(sc->data[SC_STRIKING])
+ watk += sc->data[SC_STRIKING]->val2;
+ if(sc->data[SC_SHIELDSPELL_DEF] && sc->data[SC_SHIELDSPELL_DEF]->val1 == 3)
+ watk += sc->data[SC_SHIELDSPELL_DEF]->val2;
+ if(sc->data[SC_INSPIRATION])
+ watk += sc->data[SC_INSPIRATION]->val2;
+ if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 )
+ watk += (10 + 10 * sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2);
+ if( sc->data[SC_TROPIC_OPTION] )
+ watk += sc->data[SC_TROPIC_OPTION]->val2;
+ if( sc->data[SC_HEATER_OPTION] )
+ watk += sc->data[SC_HEATER_OPTION]->val2;
+ if( sc->data[SC_WATER_BARRIER] )
+ watk -= sc->data[SC_WATER_BARRIER]->val3;
+ if( sc->data[SC_PYROTECHNIC_OPTION] )
+ watk += sc->data[SC_PYROTECHNIC_OPTION]->val2;
+ if(sc->data[SC_NIBELUNGEN]) {
+ if (bl->type != BL_PC)
+ watk += sc->data[SC_NIBELUNGEN]->val2;
+ else {
+ #ifndef RENEWAL
+ TBL_PC *sd = (TBL_PC*)bl;
+ int index = sd->equip_index[sd->state.lr_flag?EQI_HAND_L:EQI_HAND_R];
+ if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
+ #endif
+ watk += sc->data[SC_NIBELUNGEN]->val2;
+ }
+ }
+
+ if(sc->data[SC_INCATKRATE])
+ watk += watk * sc->data[SC_INCATKRATE]->val1/100;
+ if(sc->data[SC_PROVOKE])
+ watk += watk * sc->data[SC_PROVOKE]->val3/100;
+ if(sc->data[SC_CONCENTRATION])
+ watk += watk * sc->data[SC_CONCENTRATION]->val2/100;
+ if(sc->data[SC_SKE])
+ watk += watk * 3;
+ if(sc->data[SC__ENERVATION])
+ watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
+ if(sc->data[SC_FLEET])
+ watk += watk * sc->data[SC_FLEET]->val3/100;
+ if(sc->data[SC_CURSE])
+ watk -= watk * 25/100;
+ if(sc->data[SC_STRIPWEAPON])
+ watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100;
+ if(sc->data[SC__ENERVATION])
+ watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
+ if((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2)
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
+ )
+ watk += watk / 10;
+ if( sc && sc->data[SC_TIDAL_WEAPON] )
+ watk += watk * sc->data[SC_TIDAL_WEAPON]->val2 / 100;
+ if(sc->data[SC_ANGRIFFS_MODUS])
+ watk += watk * sc->data[SC_ANGRIFFS_MODUS]->val2/100;
+#ifdef RENEWAL_EDP
+ if( sc->data[SC_EDP] )
+ watk = watk * (100 + sc->data[SC_EDP]->val1 * 80) / 100;
+#endif
+
+ return (unsigned short)cap_value(watk,0,USHRT_MAX);
+}
+#ifdef RENEWAL
+static unsigned short status_calc_ematk(struct block_list *bl, struct status_change *sc, int matk)
+{
+
+ if (!sc || !sc->count)
+ return cap_value(matk,0,USHRT_MAX);
+ if (sc->data[SC_MATKPOTION])
+ matk += sc->data[SC_MATKPOTION]->val1;
+ if (sc->data[SC_MATKFOOD])
+ matk += sc->data[SC_MATKFOOD]->val1;
+ if(sc->data[SC_MANA_PLUS])
+ matk += sc->data[SC_MANA_PLUS]->val1;
+ if(sc->data[SC_AQUAPLAY_OPTION])
+ matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
+ if(sc->data[SC_CHILLY_AIR_OPTION])
+ matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
+ if(sc->data[SC_WATER_BARRIER])
+ matk -= sc->data[SC_WATER_BARRIER]->val3;
+ if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
+ matk += 50;
+ if(sc->data[SC_ODINS_POWER])
+ matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2
+ if(sc->data[SC_IZAYOI])
+ matk += 50 * sc->data[SC_IZAYOI]->val1;
+ return (unsigned short)cap_value(matk,0,USHRT_MAX);
+}
+#endif
+static unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, int matk)
+{
+ if(!sc || !sc->count)
+ return cap_value(matk,0,USHRT_MAX);
+#ifndef RENEWAL
+ // take note fixed value first before % modifiers
+ if (sc->data[SC_MATKPOTION])
+ matk += sc->data[SC_MATKPOTION]->val1;
+ if (sc->data[SC_MATKFOOD])
+ matk += sc->data[SC_MATKFOOD]->val1;
+ if (sc->data[SC_MANA_PLUS])
+ matk += sc->data[SC_MANA_PLUS]->val1;
+ if (sc->data[SC_AQUAPLAY_OPTION])
+ matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
+ if (sc->data[SC_CHILLY_AIR_OPTION])
+ matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
+ if (sc->data[SC_WATER_BARRIER])
+ matk -= sc->data[SC_WATER_BARRIER]->val3;
+ if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
+ matk += 50;
+ if (sc->data[SC_ODINS_POWER])
+ matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2
+ if (sc->data[SC_IZAYOI])
+ matk += 50 * sc->data[SC_IZAYOI]->val1;
+#endif
+ if (sc->data[SC_MAGICPOWER])
+ matk += matk * sc->data[SC_MAGICPOWER]->val3/100;
+ if (sc->data[SC_MINDBREAKER])
+ matk += matk * sc->data[SC_MINDBREAKER]->val2/100;
+ if (sc->data[SC_INCMATKRATE])
+ matk += matk * sc->data[SC_INCMATKRATE]->val1/100;
+ if (sc->data[SC_MOONLITSERENADE])
+ matk += matk * sc->data[SC_MOONLITSERENADE]->val2/100;
+ if (sc->data[SC_MELODYOFSINK])
+ matk += matk * sc->data[SC_MELODYOFSINK]->val3/100;
+ if (sc->data[SC_BEYONDOFWARCRY])
+ matk -= matk * sc->data[SC_BEYONDOFWARCRY]->val3/100;
+ if( sc->data[SC_ZANGETSU] )
+ matk += matk * sc->data[SC_ZANGETSU]->val2 / 100;
+
+ return (unsigned short)cap_value(matk,0,USHRT_MAX);
+}
+
+static signed short status_calc_critical(struct block_list *bl, struct status_change *sc, int critical) {
+
+ if(!sc || !sc->count)
+ return cap_value(critical,10,SHRT_MAX);
+
+ if (sc->data[SC_INCCRI])
+ critical += sc->data[SC_INCCRI]->val2;
+ if (sc->data[SC_EXPLOSIONSPIRITS])
+ critical += sc->data[SC_EXPLOSIONSPIRITS]->val2;
+ if (sc->data[SC_FORTUNE])
+ critical += sc->data[SC_FORTUNE]->val2;
+ if (sc->data[SC_TRUESIGHT])
+ critical += sc->data[SC_TRUESIGHT]->val2;
+ if(sc->data[SC_CLOAKING])
+ critical += critical;
+ if(sc->data[SC_STRIKING])
+ critical += sc->data[SC_STRIKING]->val1;
+#ifdef RENEWAL
+ if (sc->data[SC_SPEARQUICKEN])
+ critical += 3*sc->data[SC_SPEARQUICKEN]->val1*10;
+#endif
+
+ if(sc->data[SC__INVISIBILITY])
+ critical += critical * sc->data[SC__INVISIBILITY]->val3 / 100;
+ if(sc->data[SC__UNLUCKY])
+ critical -= critical * sc->data[SC__UNLUCKY]->val2 / 100;
+
+ return (short)cap_value(critical,10,SHRT_MAX);
+}
+
+static signed short status_calc_hit(struct block_list *bl, struct status_change *sc, int hit)
+{
+
+ if(!sc || !sc->count)
+ return cap_value(hit,1,SHRT_MAX);
+
+ if(sc->data[SC_INCHIT])
+ hit += sc->data[SC_INCHIT]->val1;
+ if(sc->data[SC_HITFOOD])
+ hit += sc->data[SC_HITFOOD]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ hit += sc->data[SC_TRUESIGHT]->val3;
+ if(sc->data[SC_HUMMING])
+ hit += sc->data[SC_HUMMING]->val2;
+ if(sc->data[SC_CONCENTRATION])
+ hit += sc->data[SC_CONCENTRATION]->val3;
+ if(sc->data[SC_INSPIRATION])
+ hit += 5 * sc->data[SC_INSPIRATION]->val1;
+ if(sc->data[SC_ADJUSTMENT])
+ hit -= 30;
+ if(sc->data[SC_INCREASING])
+ hit += 20; // RockmanEXE; changed based on updated [Reddozen]
+ if(sc->data[SC_MERC_HITUP])
+ hit += sc->data[SC_MERC_HITUP]->val2;
+
+ if(sc->data[SC_INCHITRATE])
+ hit += hit * sc->data[SC_INCHITRATE]->val1/100;
+ if(sc->data[SC_BLIND])
+ hit -= hit * 25/100;
+ if(sc->data[SC__GROOMY])
+ hit -= hit * sc->data[SC__GROOMY]->val3 / 100;
+ if(sc->data[SC_FEAR])
+ hit -= hit * 20 / 100;
+ if (sc->data[SC_ASH])
+ hit /= 2;
+
+ return (short)cap_value(hit,1,SHRT_MAX);
+}
+
+static signed short status_calc_flee(struct block_list *bl, struct status_change *sc, int flee)
+{
+ if( bl->type == BL_PC )
+ {
+ if( map_flag_gvg(bl->m) )
+ flee -= flee * battle_config.gvg_flee_penalty/100;
+ else if( map[bl->m].flag.battleground )
+ flee -= flee * battle_config.bg_flee_penalty/100;
+ }
+
+ if(!sc || !sc->count)
+ return cap_value(flee,1,SHRT_MAX);
+
+ if(sc->data[SC_INCFLEE])
+ flee += sc->data[SC_INCFLEE]->val1;
+ if(sc->data[SC_FLEEFOOD])
+ flee += sc->data[SC_FLEEFOOD]->val1;
+ if(sc->data[SC_WHISTLE])
+ flee += sc->data[SC_WHISTLE]->val2;
+ if(sc->data[SC_WINDWALK])
+ flee += sc->data[SC_WINDWALK]->val2;
+ if(sc->data[SC_VIOLENTGALE])
+ flee += sc->data[SC_VIOLENTGALE]->val2;
+ if(sc->data[SC_MOON_COMFORT]) //SG skill [Komurka]
+ flee += sc->data[SC_MOON_COMFORT]->val2;
+ if(sc->data[SC_CLOSECONFINE])
+ flee += 10;
+ if (sc->data[SC_ANGRIFFS_MODUS])
+ flee -= sc->data[SC_ANGRIFFS_MODUS]->val3;
+ if (sc->data[SC_OVERED_BOOST])
+ flee = max(flee,sc->data[SC_OVERED_BOOST]->val2);
+ if(sc->data[SC_ADJUSTMENT])
+ flee += 30;
+ if(sc->data[SC_SPEED])
+ flee += 10 + sc->data[SC_SPEED]->val1 * 10;
+ if(sc->data[SC_GATLINGFEVER])
+ flee -= sc->data[SC_GATLINGFEVER]->val4;
+ 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_HALLUCINATIONWALK] )
+ flee += sc->data[SC_HALLUCINATIONWALK]->val2;
+ if( sc->data[SC_WATER_BARRIER] )
+ flee -= sc->data[SC_WATER_BARRIER]->val3;
+ 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);
+#ifdef RENEWAL
+ if( sc->data[SC_SPEARQUICKEN] )
+ flee += 2 * sc->data[SC_SPEARQUICKEN]->val1;
+#endif
+
+ if(sc->data[SC_INCFLEERATE])
+ flee += flee * sc->data[SC_INCFLEERATE]->val1/100;
+ if(sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1)
+ flee -= flee * 50/100;
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ flee -= flee * 50/100;
+ if(sc->data[SC_BLIND])
+ flee -= flee * 25/100;
+ if(sc->data[SC_FEAR])
+ flee -= flee * 20 / 100;
+ if(sc->data[SC_PARALYSE])
+ flee -= flee * 10 / 100; // 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_SATURDAYNIGHTFEVER] )
+ flee -= flee * (40 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100;
+ 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_ASH] && (bl->type==BL_MOB)){ //mob
+ if(status_get_element(bl) == ELE_WATER) //water type
+ flee /= 2;
+ }
+
+ return (short)cap_value(flee,1,SHRT_MAX);
+}
+
+static signed short status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2)
+{
+ if(!sc || !sc->count)
+ return cap_value(flee2,10,SHRT_MAX);
+
+ if(sc->data[SC_INCFLEE2])
+ flee2 += sc->data[SC_INCFLEE2]->val2;
+ if(sc->data[SC_WHISTLE])
+ flee2 += sc->data[SC_WHISTLE]->val3*10;
+ if(sc->data[SC__UNLUCKY])
+ flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100;
+
+ return (short)cap_value(flee2,10,SHRT_MAX);
+}
+static defType status_calc_def(struct block_list *bl, struct status_change *sc, int def) {
+
+ if(!sc || !sc->count)
+ return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_SKA])
+ return sc->data[SC_SKA]->val3;
+ if(sc->data[SC_BARRIER])
+ return 100;
+ if(sc->data[SC_KEEPING])
+ return 90;
+#ifndef RENEWAL // does not provide 90 DEF in renewal mode
+ if(sc->data[SC_STEELBODY])
+ return 90;
+#endif
+
+ if(sc->data[SC_ARMORCHANGE])
+ def += sc->data[SC_ARMORCHANGE]->val2;
+ if(sc->data[SC_DRUMBATTLE])
+ def += sc->data[SC_DRUMBATTLE]->val3;
+ if(sc->data[SC_DEFENCE]) //[orn]
+ def += sc->data[SC_DEFENCE]->val2 ;
+ if(sc->data[SC_INCDEFRATE])
+ def += def * sc->data[SC_INCDEFRATE]->val1/100;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
+ def += 50;
+ if(sc->data[SC_ODINS_POWER])
+ def -= 20;
+ if( sc->data[SC_ANGRIFFS_MODUS] )
+ def -= 30 + 20 * sc->data[SC_ANGRIFFS_MODUS]->val1;
+ if(sc->data[SC_STONEHARDSKIN])// Final DEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech]
+ def += sc->data[SC_STONEHARDSKIN]->val1;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ def >>=1;
+ if(sc->data[SC_FREEZE])
+ def >>=1;
+ if(sc->data[SC_SIGNUMCRUCIS])
+ def -= def * sc->data[SC_SIGNUMCRUCIS]->val2/100;
+ if(sc->data[SC_CONCENTRATION])
+ def -= def * sc->data[SC_CONCENTRATION]->val4/100;
+ if(sc->data[SC_SKE])
+ def >>=1;
+ if(sc->data[SC_PROVOKE] && bl->type != BL_PC) // Provoke doesn't alter player defense->
+ def -= def * sc->data[SC_PROVOKE]->val4/100;
+ if(sc->data[SC_STRIPSHIELD])
+ def -= def * sc->data[SC_STRIPSHIELD]->val2/100;
+ if (sc->data[SC_FLING])
+ def -= def * (sc->data[SC_FLING]->val2)/100;
+ if( sc->data[SC_FREEZING] )
+ def -= def * 10 / 100;
+ 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_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_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( sc->data[SC_PRESTIGE] )
+ def += def * sc->data[SC_PRESTIGE]->val1 / 100;
+ if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
+ if(status_get_race(bl)==RC_PLANT)
+ def /= 2;
+ }
+
+ return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);;
+}
+
+static signed short status_calc_def2(struct block_list *bl, struct status_change *sc, int def2)
+{
+ if(!sc || !sc->count)
+#ifdef RENEWAL
+ return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(def2,1,SHRT_MAX);
+#endif
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_ETERNALCHAOS])
+ return 0;
+ if(sc->data[SC_SUN_COMFORT])
+ def2 += sc->data[SC_SUN_COMFORT]->val2;
+ 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_ANGELUS])
+#ifdef RENEWAL //in renewal only the VIT stat bonus is boosted by angelus
+ def2 += status_get_vit(bl) / 2 * sc->data[SC_ANGELUS]->val2/100;
+#else
+ def2 += def2 * sc->data[SC_ANGELUS]->val2/100;
+#endif
+ if(sc->data[SC_CONCENTRATION])
+ def2 -= def2 * sc->data[SC_CONCENTRATION]->val4/100;
+ if(sc->data[SC_POISON])
+ def2 -= def2 * 25/100;
+ if(sc->data[SC_DPOISON])
+ def2 -= def2 * 25/100;
+ if(sc->data[SC_SKE])
+ def2 -= def2 * 50/100;
+ if(sc->data[SC_PROVOKE])
+ def2 -= def2 * sc->data[SC_PROVOKE]->val4/100;
+ if(sc->data[SC_JOINTBEAT])
+ def2 -= def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_SHOULDER ? 50 : 0 ) / 100
+ + 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_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_ASH] && (bl->type==BL_MOB)){
+ if(status_get_race(bl)==RC_PLANT)
+ def2 /= 2;
+ }
+ if (sc->data[SC_PARALYSIS])
+ def2 -= def2 * sc->data[SC_PARALYSIS]->val2 / 100;
+
+#ifdef RENEWAL
+ return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(def2,1,SHRT_MAX);
+#endif
+}
+
+
+static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) {
+
+ if(!sc || !sc->count)
+ return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_BARRIER])
+ return 100;
+
+#ifndef RENEWAL // no longer provides 90 MDEF in renewal mode
+ if(sc->data[SC_STEELBODY])
+ return 90;
+#endif
+
+ if(sc->data[SC_ARMORCHANGE])
+ mdef += sc->data[SC_ARMORCHANGE]->val3;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
+ mdef += 50;
+ if(sc->data[SC_ENDURE])// It has been confirmed that eddga card grants 1 MDEF, not 0, not 10, but 1.
+ mdef += (sc->data[SC_ENDURE]->val4 == 0) ? sc->data[SC_ENDURE]->val1 : 1;
+ if(sc->data[SC_CONCENTRATION])
+ mdef += 1; //Skill info says it adds a fixed 1 Mdef point.
+ if(sc->data[SC_STONEHARDSKIN])// Final MDEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech]
+ mdef += sc->data[SC_STONEHARDSKIN]->val1;
+ if(sc->data[SC_WATER_BARRIER])
+ mdef += sc->data[SC_WATER_BARRIER]->val2;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ mdef += 25*mdef/100;
+ if(sc->data[SC_FREEZE])
+ mdef += 25*mdef/100;
+ 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] && sc->data[SC_GT_CHANGE]->val4)
+ mdef -= mdef * sc->data[SC_GT_CHANGE]->val4 / 100;
+ if (sc->data[SC_ODINS_POWER])
+ mdef -= 20 * sc->data[SC_ODINS_POWER]->val1;
+
+ return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
+}
+
+static signed short status_calc_mdef2(struct block_list *bl, struct status_change *sc, int mdef2)
+{
+ if(!sc || !sc->count)
+#ifdef RENEWAL
+ return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(mdef2,1,SHRT_MAX);
+#endif
+
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_SKA])
+ return 90;
+ if(sc->data[SC_MINDBREAKER])
+ mdef2 -= mdef2 * sc->data[SC_MINDBREAKER]->val3/100;
+ if(sc->data[SC_ANALYZE])
+ mdef2 -= mdef2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
+
+#ifdef RENEWAL
+ return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(mdef2,1,SHRT_MAX);
+#endif
+}
+
+static unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc, int speed)
+{
+ TBL_PC* sd = BL_CAST(BL_PC, bl);
+ int speed_rate;
+
+ if( sc == NULL )
+ return cap_value(speed,10,USHRT_MAX);
+
+ if( sd && sd->ud.skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || sd->ud.skill_id == LG_EXEEDBREAK) )
+ {
+ if( sd->ud.skill_id == LG_EXEEDBREAK )
+ speed_rate = 100 + 60 - (sd->ud.skill_lv * 10);
+ else
+ speed_rate = 175 - 5 * pc_checkskill(sd,SA_FREECAST);
+ }
+ else
+ {
+ speed_rate = 100;
+
+ //GetMoveHasteValue2()
+ {
+ int val = 0;
+
+ if( sc->data[SC_FUSION] )
+ val = 25;
+ else if( sd ) {
+ if( pc_isriding(sd) || sd->sc.option&(OPTION_DRAGON|OPTION_MOUNTING) )
+ val = 25;//Same bonus
+ else if( pc_isridingwug(sd) )
+ val = 15 + 5 * pc_checkskill(sd, RA_WUGRIDER);
+ else if( pc_ismadogear(sd) ) {
+ val = (- 10 * (5 - pc_checkskill(sd,NC_MADOLICENCE)));
+ if( sc->data[SC_ACCELERATION] )
+ val += 25;
+ }
+ }
+
+ speed_rate -= val;
+ }
+
+ //GetMoveSlowValue()
+ {
+ int val = 0;
+
+ if( sd && sc->data[SC_HIDING] && pc_checkskill(sd,RG_TUNNELDRIVE) > 0 )
+ val = 120 - 6 * pc_checkskill(sd,RG_TUNNELDRIVE);
+ else
+ if( sd && sc->data[SC_CHASEWALK] && sc->data[SC_CHASEWALK]->val3 < 0 )
+ val = sc->data[SC_CHASEWALK]->val3;
+ else
+ {
+ // Longing for Freedom cancels song/dance penalty
+ if( sc->data[SC_LONGING] )
+ val = max( val, 50 - 10 * sc->data[SC_LONGING]->val1 );
+ else
+ if( sd && sc->data[SC_DANCING] )
+ val = max( val, 500 - (40 + 10 * (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER)) * pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON)) );
+
+ if( sc->data[SC_DECREASEAGI] )
+ val = max( val, 25 );
+ if( sc->data[SC_QUAGMIRE] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY] || (sc->data[SC_GLOOMYDAY] && sc->data[SC_GLOOMYDAY]->val4) )
+ val = max( val, 50 );
+ if( sc->data[SC_DONTFORGETME] )
+ val = max( val, sc->data[SC_DONTFORGETME]->val3 );
+ if( sc->data[SC_CURSE] )
+ val = max( val, 300 );
+ if( sc->data[SC_CHASEWALK] )
+ val = max( val, sc->data[SC_CHASEWALK]->val3 );
+ if( sc->data[SC_WEDDING] )
+ val = max( val, 100 );
+ if( sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&(BREAK_ANKLE|BREAK_KNEE) )
+ val = max( val, (sc->data[SC_JOINTBEAT]->val2&BREAK_ANKLE ? 50 : 0) + (sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ? 30 : 0) );
+ if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 0 )
+ val = max( val, sc->data[SC_CLOAKING]->val1 < 3 ? 300 : 30 - 3 * sc->data[SC_CLOAKING]->val1 );
+ if( sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY )
+ val = max( val, 75 );
+ if( sc->data[SC_SLOWDOWN] ) // Slow Potion
+ val = max( val, 100 );
+ if( sc->data[SC_GATLINGFEVER] )
+ val = max( val, 100 );
+ if( sc->data[SC_SUITON] )
+ val = max( val, sc->data[SC_SUITON]->val3 );
+ if( sc->data[SC_SWOO] )
+ val = max( val, 300 );
+ 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 ? 0 : 25 * (5 - 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->bonus.speed_rate + sd->bonus.speed_add_rate > 0 ) // permanent item-based speedup
+ val = max( val, sd->bonus.speed_rate + sd->bonus.speed_add_rate );
+ }
+
+ speed_rate += val;
+ }
+
+ //GetMoveHasteValue1()
+ {
+ int val = 0;
+
+ if( sc->data[SC_SPEEDUP1] ) //FIXME: used both by NPC_AGIUP and Speed Potion script
+ val = max( val, 50 );
+ if( sc->data[SC_INCREASEAGI] )
+ val = max( val, 25 );
+ if( sc->data[SC_WINDWALK] )
+ val = max( val, 2 * sc->data[SC_WINDWALK]->val1 );
+ if( sc->data[SC_CARTBOOST] )
+ val = max( val, 20 );
+ if( sd && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && pc_checkskill(sd,TF_MISS) > 0 )
+ val = max( val, 1 * pc_checkskill(sd,TF_MISS) );
+ if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 1 )
+ val = max( val, sc->data[SC_CLOAKING]->val1 >= 10 ? 25 : 3 * sc->data[SC_CLOAKING]->val1 - 3 );
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ val = max( val, 25 );
+ if( sc->data[SC_RUN] )
+ val = max( val, 55 );
+ if( sc->data[SC_AVOID] )
+ val = max( val, 10 * sc->data[SC_AVOID]->val1 );
+ if( sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
+ val = max( val, 75 );
+ if( sc->data[SC_CLOAKINGEXCEED] )
+ val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3);
+ if( sc->data[SC_HOVERING] )
+ val = max( val, 10 );
+ if( sc->data[SC_GN_CARTBOOST] )
+ val = max( val, sc->data[SC_GN_CARTBOOST]->val2 );
+ if( sc->data[SC_SWINGDANCE] )
+ val = max( val, sc->data[SC_SWINGDANCE]->val2 );
+ if( sc->data[SC_WIND_STEP_OPTION] )
+ val = max( val, sc->data[SC_WIND_STEP_OPTION]->val2 );
+
+ //FIXME: official items use a single bonus for this [ultramage]
+ if( sc->data[SC_SPEEDUP0] ) // temporary item-based speedup
+ val = max( val, 25 );
+ if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate < 0 ) // permanent item-based speedup
+ val = max( val, -(sd->bonus.speed_rate + sd->bonus.speed_add_rate) );
+
+ speed_rate -= val;
+ }
+
+ if( speed_rate < 40 )
+ speed_rate = 40;
+ }
+
+ //GetSpeed()
+ {
+ if( sd && pc_iscarton(sd) )
+ speed += speed * (50 - 5 * pc_checkskill(sd,MC_PUSHCART)) / 100;
+ if( sc->data[SC_PARALYSE] )
+ speed += speed * 50 / 100;
+ if( speed_rate != 100 )
+ speed = speed * speed_rate / 100;
+ if( sc->data[SC_STEELBODY] )
+ speed = 200;
+ if( sc->data[SC_DEFENDER] )
+ speed = max(speed, 200);
+ if( sc->data[SC_WALKSPEED] && sc->data[SC_WALKSPEED]->val1 > 0 ) // ChangeSpeed
+ speed = speed * 100 / sc->data[SC_WALKSPEED]->val1;
+ }
+
+ return (short)cap_value(speed,10,USHRT_MAX);
+}
+
+#ifdef RENEWAL_ASPD
+// flag&1 - fixed value [malufett]
+// flag&2 - percentage value
+static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag)
+{
+ int i, pots = 0, skills1 = 0, skills2 = 0;
+
+ if(!sc || !sc->count)
+ return 0;
+
+ if(sc->data[i=SC_ASPDPOTION3] ||
+ sc->data[i=SC_ASPDPOTION2] ||
+ sc->data[i=SC_ASPDPOTION1] ||
+ sc->data[i=SC_ASPDPOTION0])
+ pots += sc->data[i]->val1;
+
+ if( !sc->data[SC_QUAGMIRE] ){
+ if(sc->data[SC_STAR_COMFORT])
+ skills1 = 5; // needs more info
+
+ if(sc->data[SC_TWOHANDQUICKEN] && skills1 < 7)
+ skills1 = 7;
+
+ if(sc->data[SC_ONEHAND] && skills1 < 7) skills1 = 7;
+
+ if(sc->data[SC_MERC_QUICKEN] && skills1 < 7) // needs more info
+ skills1 = 7;
+
+ if(sc->data[SC_ADRENALINE2] && skills1 < 6)
+ skills1 = 6;
+
+ if(sc->data[SC_ADRENALINE] && skills1 < 7)
+ skills1 = 7;
+
+ if(sc->data[SC_SPEARQUICKEN] && skills1 < 7)
+ skills1 = 7;
+
+ if(sc->data[SC_GATLINGFEVER] && skills1 < 9) // needs more info
+ skills1 = 9;
+
+ if(sc->data[SC_FLEET] && skills1 < 5)
+ skills1 = 5;
+
+ if(sc->data[SC_ASSNCROS] &&
+ skills1 < 5+1*sc->data[SC_ASSNCROS]->val1) // needs more info
+ {
+ if (bl->type!=BL_PC)
+ skills1 = 4+1*sc->data[SC_ASSNCROS]->val1;
+ else
+ switch(((TBL_PC*)bl)->status.weapon)
+ {
+ case W_BOW:
+ case W_REVOLVER:
+ case W_RIFLE:
+ case W_GATLING:
+ case W_SHOTGUN:
+ case W_GRENADE:
+ break;
+ default:
+ skills1 = 5+1*sc->data[SC_ASSNCROS]->val1;
+ }
+ }
+ }
+
+ if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) && skills1 < 15)
+ skills1 = 15;
+ else if(sc->data[SC_MADNESSCANCEL] && skills1 < 15) // needs more info
+ skills1 = 15;
+
+ if(sc->data[SC_DONTFORGETME])
+ skills2 -= sc->data[SC_DONTFORGETME]->val2; // needs more info
+ if(sc->data[SC_LONGING])
+ skills2 -= sc->data[SC_LONGING]->val2; // needs more info
+ if(sc->data[SC_STEELBODY])
+ skills2 -= 25;
+ if(sc->data[SC_SKA])
+ skills2 -= 25;
+ if(sc->data[SC_DEFENDER])
+ skills2 -= sc->data[SC_DEFENDER]->val4; // needs more info
+ if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY) // needs more info
+ skills2 -= 25;
+ if(sc->data[SC_GRAVITATION])
+ skills2 -= sc->data[SC_GRAVITATION]->val2; // needs more info
+ if(sc->data[SC_JOINTBEAT]) { // needs more info
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST )
+ skills2 -= 25;
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE )
+ skills2 -= 10;
+ }
+ if( sc->data[SC_FREEZING] )
+ skills2 -= 30;
+ if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] )
+ skills2 -= 50;
+ if( sc->data[SC_PARALYSE] )
+ skills2 -= 10;
+ if( sc->data[SC__BODYPAINT] )
+ skills2 -= 2 + 5 * sc->data[SC__BODYPAINT]->val1;
+ if( sc->data[SC__INVISIBILITY] )
+ skills2 -= sc->data[SC__INVISIBILITY]->val2 ;
+ if( sc->data[SC__GROOMY] )
+ skills2 -= sc->data[SC__GROOMY]->val2;
+ if( sc->data[SC_SWINGDANCE] )
+ skills2 += sc->data[SC_SWINGDANCE]->val2;
+ if( sc->data[SC_DANCEWITHWUG] )
+ skills2 += sc->data[SC_DANCEWITHWUG]->val3;
+ if( sc->data[SC_GLOOMYDAY] )
+ skills2 -= sc->data[SC_GLOOMYDAY]->val3;
+ if( sc->data[SC_EARTHDRIVE] )
+ skills2 -= 25;
+ if( sc->data[SC_GT_CHANGE] )
+ skills2 += sc->data[SC_GT_CHANGE]->val3;
+ if( sc->data[SC_MELON_BOMB] )
+ skills2 -= sc->data[SC_MELON_BOMB]->val1;
+ if( sc->data[SC_BOOST500] )
+ skills2 += sc->data[SC_BOOST500]->val1;
+ if( sc->data[SC_EXTRACT_SALAMINE_JUICE] )
+ skills2 += sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1;
+ if( sc->data[SC_INCASPDRATE] )
+ skills2 += sc->data[SC_INCASPDRATE]->val1;
+
+ return ( flag&1? (skills1 + pots) : skills2 );
+}
+#endif
+
+static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int aspd) {
+ if (!sc || !sc->count)
+ return cap_value(aspd, 0, 2000);
+
+ if (!sc->data[SC_QUAGMIRE]) {
+ if (sc->data[SC_OVERED_BOOST])
+ aspd = 2000 - sc->data[SC_OVERED_BOOST]->val3*10;
+ }
+
+ if ((sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION]
+ || sc->data[SC_WILD_STORM_OPTION]))
+ aspd -= 50; // +5 ASPD
+ if( sc && sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 )
+ aspd -= (bl->type==BL_PC?pc_checkskill((TBL_PC *)bl, RK_RUNEMASTERY):10) / 10 * 40;
+
+ return cap_value(aspd, 0, 2000); // will be recap for proper bl anyway
+}
+
+/// Calculates an object's ASPD modifier (alters the base amotion value).
+/// 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)
+{
+ int i;
+
+ if(!sc || !sc->count)
+ return cap_value(aspd_rate,0,SHRT_MAX);
+
+ if( !sc->data[SC_QUAGMIRE] ){
+ int max = 0;
+ if(sc->data[SC_STAR_COMFORT])
+ max = sc->data[SC_STAR_COMFORT]->val2;
+
+ if(sc->data[SC_TWOHANDQUICKEN] &&
+ max < sc->data[SC_TWOHANDQUICKEN]->val2)
+ max = sc->data[SC_TWOHANDQUICKEN]->val2;
+
+ if(sc->data[SC_ONEHAND] &&
+ max < sc->data[SC_ONEHAND]->val2)
+ max = sc->data[SC_ONEHAND]->val2;
+
+ if(sc->data[SC_MERC_QUICKEN] &&
+ max < sc->data[SC_MERC_QUICKEN]->val2)
+ max = sc->data[SC_MERC_QUICKEN]->val2;
+
+ if(sc->data[SC_ADRENALINE2] &&
+ max < sc->data[SC_ADRENALINE2]->val3)
+ max = sc->data[SC_ADRENALINE2]->val3;
+
+ if(sc->data[SC_ADRENALINE] &&
+ max < sc->data[SC_ADRENALINE]->val3)
+ max = sc->data[SC_ADRENALINE]->val3;
+
+ if(sc->data[SC_SPEARQUICKEN] &&
+ max < sc->data[SC_SPEARQUICKEN]->val2)
+ max = sc->data[SC_SPEARQUICKEN]->val2;
+
+ if(sc->data[SC_GATLINGFEVER] &&
+ max < sc->data[SC_GATLINGFEVER]->val2)
+ max = sc->data[SC_GATLINGFEVER]->val2;
+
+ if(sc->data[SC_FLEET] &&
+ max < sc->data[SC_FLEET]->val2)
+ max = sc->data[SC_FLEET]->val2;
+
+ if(sc->data[SC_ASSNCROS] &&
+ max < sc->data[SC_ASSNCROS]->val2)
+ {
+ if (bl->type!=BL_PC)
+ max = sc->data[SC_ASSNCROS]->val2;
+ else
+ switch(((TBL_PC*)bl)->status.weapon)
+ {
+ case W_BOW:
+ case W_REVOLVER:
+ case W_RIFLE:
+ case W_GATLING:
+ case W_SHOTGUN:
+ case W_GRENADE:
+ break;
+ default:
+ max = sc->data[SC_ASSNCROS]->val2;
+ }
+ }
+ aspd_rate -= max;
+
+ if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
+ aspd_rate -= 300;
+ else if(sc->data[SC_MADNESSCANCEL])
+ aspd_rate -= 200;
+ }
+
+ 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;
+
+ if(sc->data[SC_DONTFORGETME])
+ aspd_rate += 10 * sc->data[SC_DONTFORGETME]->val2;
+ if(sc->data[SC_LONGING])
+ aspd_rate += sc->data[SC_LONGING]->val2;
+ if(sc->data[SC_STEELBODY])
+ aspd_rate += 250;
+ if(sc->data[SC_SKA])
+ aspd_rate += 250;
+ if(sc->data[SC_DEFENDER])
+ aspd_rate += sc->data[SC_DEFENDER]->val4;
+ if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY)
+ aspd_rate += 250;
+ if(sc->data[SC_GRAVITATION])
+ aspd_rate += sc->data[SC_GRAVITATION]->val2;
+ if(sc->data[SC_JOINTBEAT]) {
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST )
+ aspd_rate += 250;
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE )
+ aspd_rate += 100;
+ }
+ 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 += 200 + 50 * sc->data[SC__BODYPAINT]->val1;
+ if( sc->data[SC__INVISIBILITY] )
+ aspd_rate += sc->data[SC__INVISIBILITY]->val2 * 10 ;
+ if( sc->data[SC__GROOMY] )
+ aspd_rate += sc->data[SC__GROOMY]->val2 * 10;
+ if( sc->data[SC_SWINGDANCE] )
+ aspd_rate -= sc->data[SC_SWINGDANCE]->val2 * 10;
+ if( sc->data[SC_DANCEWITHWUG] )
+ aspd_rate -= sc->data[SC_DANCEWITHWUG]->val3 * 10;
+ if( sc->data[SC_GLOOMYDAY] )
+ aspd_rate += sc->data[SC_GLOOMYDAY]->val3 * 10;
+ if( sc->data[SC_EARTHDRIVE] )
+ aspd_rate += 250;
+ if( sc->data[SC_GT_CHANGE] )
+ aspd_rate -= sc->data[SC_GT_CHANGE]->val3 * 10;
+ if( sc->data[SC_MELON_BOMB] )
+ aspd_rate += sc->data[SC_MELON_BOMB]->val1 * 10;
+ if( sc->data[SC_BOOST500] )
+ aspd_rate -= sc->data[SC_BOOST500]->val1 *10;
+ if( sc->data[SC_EXTRACT_SALAMINE_JUICE] )
+ aspd_rate -= sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1 * 10;
+ if( sc->data[SC_INCASPDRATE] )
+ aspd_rate -= sc->data[SC_INCASPDRATE]->val1 * 10;
+ if( sc->data[SC_PAIN_KILLER])
+ aspd_rate += sc->data[SC_PAIN_KILLER]->val2 * 10;
+ if( sc->data[SC_GOLDENE_FERSE])
+ aspd_rate -= sc->data[SC_GOLDENE_FERSE]->val3 * 10;
+
+ return (short)cap_value(aspd_rate,0,SHRT_MAX);
+}
+
+static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion)
+{
+ if( !sc || !sc->count || map_flag_gvg(bl->m) || map[bl->m].flag.battleground )
+ return cap_value(dmotion,0,USHRT_MAX);
+ /**
+ * It has been confirmed on official servers that MvP mobs have no dmotion even without endure
+ **/
+ if( sc->data[SC_ENDURE] || ( bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS) ) )
+ return 0;
+ if( sc->data[SC_CONCENTRATION] )
+ return 0;
+ if( sc->data[SC_RUN] || sc->data[SC_WUGDASH] )
+ return 0;
+
+ return (unsigned short)cap_value(dmotion,0,USHRT_MAX);
+}
+
+static unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc, uint64 maxhp)
+{
+ if(!sc || !sc->count)
+ return (unsigned int)cap_value(maxhp,1,UINT_MAX);
+
+ if(sc->data[SC_INCMHPRATE])
+ maxhp += maxhp * sc->data[SC_INCMHPRATE]->val1/100;
+ if(sc->data[SC_INCMHP])
+ maxhp += (sc->data[SC_INCMHP]->val1);
+ if(sc->data[SC_APPLEIDUN])
+ maxhp += maxhp * sc->data[SC_APPLEIDUN]->val2/100;
+ if(sc->data[SC_DELUGE])
+ maxhp += maxhp * sc->data[SC_DELUGE]->val2/100;
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ maxhp += maxhp * 2;
+ if(sc->data[SC_MARIONETTE])
+ maxhp -= 1000;
+ if(sc->data[SC_SOLID_SKIN_OPTION])
+ maxhp += 2000;// Fix amount.
+ if(sc->data[SC_POWER_OF_GAIA])
+ maxhp += 3000;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
+ maxhp += 500;
+
+ if(sc->data[SC_MERC_HPUP])
+ maxhp += maxhp * sc->data[SC_MERC_HPUP]->val2/100;
+
+ if(sc->data[SC_EPICLESIS])
+ maxhp += maxhp * 5 * sc->data[SC_EPICLESIS]->val1 / 100;
+ if(sc->data[SC_VENOMBLEED])
+ maxhp -= maxhp * 15 / 100;
+ if(sc->data[SC__WEAKNESS])
+ maxhp -= maxhp * sc->data[SC__WEAKNESS]->val2 / 100;
+ if(sc->data[SC_LERADSDEW])
+ maxhp += maxhp * sc->data[SC_LERADSDEW]->val3 / 100;
+ if(sc->data[SC_FORCEOFVANGUARD])
+ maxhp += maxhp * 3 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100;
+ if(sc->data[SC_INSPIRATION]) //Custom value.
+ maxhp += maxhp * 3 * sc->data[SC_INSPIRATION]->val1 / 100;
+ if(sc->data[SC_RAISINGDRAGON])
+ maxhp += maxhp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100;
+ if(sc->data[SC_GT_CHANGE]) // Max HP decrease: [Skill Level x 4] %
+ maxhp -= maxhp * (4 * sc->data[SC_GT_CHANGE]->val1) / 100;
+ if(sc->data[SC_GT_REVITALIZE])// Max HP increase: [Skill Level x 2] %
+ maxhp += maxhp * (2 * sc->data[SC_GT_REVITALIZE]->val1) / 100;
+ if(sc->data[SC_MUSTLE_M])
+ maxhp += maxhp * sc->data[SC_MUSTLE_M]->val1/100;
+ if(sc->data[SC_MYSTERIOUS_POWDER])
+ maxhp -= sc->data[SC_MYSTERIOUS_POWDER]->val1 / 100;
+ if(sc->data[SC_PETROLOGY_OPTION])
+ maxhp += maxhp * sc->data[SC_PETROLOGY_OPTION]->val2 / 100;
+ if (sc->data[SC_ANGRIFFS_MODUS])
+ maxhp += maxhp * 5 * sc->data[SC_ANGRIFFS_MODUS]->val1 /100;
+ if (sc->data[SC_GOLDENE_FERSE])
+ maxhp += maxhp * sc->data[SC_GOLDENE_FERSE]->val2 / 100;
+
+ return (unsigned int)cap_value(maxhp,1,UINT_MAX);
+}
+
+static unsigned int status_calc_maxsp(struct block_list *bl, struct status_change *sc, unsigned int maxsp)
+{
+ if(!sc || !sc->count)
+ return cap_value(maxsp,1,UINT_MAX);
+
+ if(sc->data[SC_INCMSPRATE])
+ maxsp += maxsp * sc->data[SC_INCMSPRATE]->val1/100;
+ if(sc->data[SC_INCMSP])
+ maxsp += (sc->data[SC_INCMSP]->val1);
+ if(sc->data[SC_SERVICE4U])
+ maxsp += maxsp * sc->data[SC_SERVICE4U]->val2/100;
+ if(sc->data[SC_MERC_SPUP])
+ maxsp += maxsp * sc->data[SC_MERC_SPUP]->val2/100;
+ if(sc->data[SC_RAISINGDRAGON])
+ maxsp += maxsp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100;
+ if(sc->data[SC_LIFE_FORCE_F])
+ maxsp += maxsp * sc->data[SC_LIFE_FORCE_F]->val1/100;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
+ maxsp += 50;
+
+ return cap_value(maxsp,1,UINT_MAX);
+}
+
+static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element)
+{
+ if(!sc || !sc->count)
+ return element;
+
+ if(sc->data[SC_FREEZE])
+ return ELE_WATER;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ return ELE_EARTH;
+ if(sc->data[SC_BENEDICTIO])
+ return ELE_HOLY;
+ if(sc->data[SC_CHANGEUNDEAD])
+ 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);
+}
+
+static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv)
+{
+ if(!sc || !sc->count)
+ return lv;
+
+ if(sc->data[SC_FREEZE])
+ return 1;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ return 1;
+ if(sc->data[SC_BENEDICTIO])
+ return 1;
+ if(sc->data[SC_CHANGEUNDEAD])
+ return 1;
+ if(sc->data[SC_ELEMENTALCHANGE])
+ return sc->data[SC_ELEMENTALCHANGE]->val1;
+ if(sc->data[SC_SHAPESHIFT])
+ return 1;
+ if(sc->data[SC__INVISIBILITY])
+ return 1;
+
+ return (unsigned char)cap_value(lv,1,4);
+}
+
+
+unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element)
+{
+ if(!sc || !sc->count)
+ return element;
+ if(sc->data[SC_ENCHANTARMS])
+ return sc->data[SC_ENCHANTARMS]->val2;
+ if(sc->data[SC_WATERWEAPON]
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) )
+ return ELE_WATER;
+ if(sc->data[SC_EARTHWEAPON]
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) )
+ return ELE_EARTH;
+ if(sc->data[SC_FIREWEAPON]
+ || (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2) )
+ return ELE_FIRE;
+ if(sc->data[SC_WINDWEAPON]
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2) )
+ return ELE_WIND;
+ if(sc->data[SC_ENCPOISON])
+ return ELE_POISON;
+ if(sc->data[SC_ASPERSIO])
+ return ELE_HOLY;
+ if(sc->data[SC_SHADOWWEAPON])
+ return ELE_DARK;
+ if(sc->data[SC_GHOSTWEAPON] || sc->data[SC__INVISIBILITY])
+ return ELE_GHOST;
+ if(sc->data[SC_TIDAL_WEAPON_OPTION] || sc->data[SC_TIDAL_WEAPON] )
+ return ELE_WATER;
+ if(sc->data[SC_PYROCLASTIC])
+ return ELE_FIRE;
+ return (unsigned char)cap_value(element,0,UCHAR_MAX);
+}
+
+static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode)
+{
+ if(!sc || !sc->count)
+ return mode;
+ if(sc->data[SC_MODECHANGE]) {
+ if (sc->data[SC_MODECHANGE]->val2)
+ mode = sc->data[SC_MODECHANGE]->val2; //Set mode
+ if (sc->data[SC_MODECHANGE]->val3)
+ mode|= sc->data[SC_MODECHANGE]->val3; //Add mode
+ if (sc->data[SC_MODECHANGE]->val4)
+ mode&=~sc->data[SC_MODECHANGE]->val4; //Del mode
+ }
+ return cap_value(mode,0,USHRT_MAX);
+}
+
+const char* status_get_name(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC: return ((TBL_PC *)bl)->fakename[0] != '\0' ? ((TBL_PC*)bl)->fakename : ((TBL_PC*)bl)->status.name;
+ case BL_MOB: return ((TBL_MOB*)bl)->name;
+ case BL_PET: return ((TBL_PET*)bl)->pet.name;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.name;
+ case BL_NPC: return ((TBL_NPC*)bl)->name;
+ }
+ return "Unknown";
+}
+
+/*==========================================
+ * Get the class of the current bl
+ * return
+ * 0 = fail
+ * class_id = success
+ *------------------------------------------*/
+int status_get_class(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch( bl->type ) {
+ case BL_PC: return ((TBL_PC*)bl)->status.class_;
+ case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob.
+ case BL_PET: return ((TBL_PET*)bl)->pet.class_;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_;
+ case BL_MER: return ((TBL_MER*)bl)->mercenary.class_;
+ case BL_NPC: return ((TBL_NPC*)bl)->class_;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->elemental.class_;
+ }
+ return 0;
+}
+/*==========================================
+ * Get the base level of the current bl
+ * return
+ * 1 = fail
+ * level = success
+ *------------------------------------------*/
+int status_get_lv(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC: return ((TBL_PC*)bl)->status.base_level;
+ case BL_MOB: return ((TBL_MOB*)bl)->level;
+ case BL_PET: return ((TBL_PET*)bl)->pet.level;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.level;
+ case BL_MER: return ((TBL_MER*)bl)->db->lv;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->db->lv;
+ case BL_NPC: return ((TBL_NPC*)bl)->level;
+ }
+ return 1;
+}
+
+struct regen_data *status_get_regen_data(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->regen;
+ case BL_HOM: return &((TBL_HOM*)bl)->regen;
+ case BL_MER: return &((TBL_MER*)bl)->regen;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->regen;
+ default:
+ return NULL;
+ }
+}
+
+struct status_data *status_get_status_data(struct block_list *bl)
+{
+ nullpo_retr(&dummy_status, bl);
+
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->battle_status;
+ case BL_MOB: return &((TBL_MOB*)bl)->status;
+ case BL_PET: return &((TBL_PET*)bl)->status;
+ case BL_HOM: return &((TBL_HOM*)bl)->battle_status;
+ case BL_MER: return &((TBL_MER*)bl)->battle_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->battle_status;
+ case BL_NPC: return &((TBL_NPC*)bl)->status;
+ default:
+ return &dummy_status;
+ }
+}
+
+struct status_data *status_get_base_status(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->base_status;
+ case BL_MOB: return ((TBL_MOB*)bl)->base_status ? ((TBL_MOB*)bl)->base_status : &((TBL_MOB*)bl)->db->status;
+ case BL_PET: return &((TBL_PET*)bl)->db->status;
+ case BL_HOM: return &((TBL_HOM*)bl)->base_status;
+ case BL_MER: return &((TBL_MER*)bl)->base_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->base_status;
+ case BL_NPC: return &((TBL_NPC*)bl)->status;
+ default:
+ return NULL;
+ }
+}
+defType status_get_def(struct block_list *bl) {
+ struct unit_data *ud;
+ struct status_data *status = status_get_status_data(bl);
+ int def = status?status->def:0;
+ ud = unit_bl2ud(bl);
+ if (ud && ud->skilltimer != INVALID_TIMER)
+ def -= def * skill_get_castdef(ud->skill_id)/100;
+
+ return cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX);
+}
+
+unsigned short status_get_speed(struct block_list *bl)
+{
+ return status_get_status_data(bl)->speed;
+}
+
+int status_get_party_id(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.party_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.party_id;
+ break;
+ case BL_MOB: {
+ struct mob_data *md=(TBL_MOB*)bl;
+ if( md->master_id > 0 ) {
+ struct map_session_data *msd;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.party_id;
+ return -md->master_id;
+ }
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.party_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.party_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->party_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.party_id;
+ break;
+ }
+ return 0;
+}
+
+int status_get_guild_id(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.guild_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.guild_id;
+ break;
+ case BL_MOB: {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->guild_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.guild_id; //Alchemist's mobs [Skotlex]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.guild_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.guild_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT)
+ return ((TBL_NPC*)bl)->u.scr.guild_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->guild_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.guild_id;
+ break;
+ }
+ return 0;
+}
+
+int status_get_emblem_id(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((TBL_PC*)bl)->guild_emblem_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->guild_emblem_id;
+ break;
+ case BL_MOB: {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->emblem_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->guild_emblem_id; //Alchemist's mobs [Skotlex]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->guild_emblem_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->guild_emblem_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
+ struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
+ if (g)
+ return g->emblem_id;
+ }
+ break;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->guild_emblem_id;
+ break;
+ }
+ return 0;
+}
+
+int status_get_mexp(struct block_list *bl)
+{
+ nullpo_ret(bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->mexp;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->mexp;
+ return 0;
+}
+int status_get_race2(struct block_list *bl)
+{
+ nullpo_ret(bl);
+ if(bl->type == BL_MOB)
+ return ((struct mob_data *)bl)->db->race2;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->race2;
+ return 0;
+}
+
+int status_isdead(struct block_list *bl)
+{
+ nullpo_ret(bl);
+ return status_get_status_data(bl)->hp == 0;
+}
+
+int status_isimmune(struct block_list *bl)
+{
+ struct status_change *sc =status_get_sc(bl);
+ if (sc && sc->data[SC_HERMODE])
+ return 100;
+
+ if (bl->type == BL_PC &&
+ ((TBL_PC*)bl)->special_state.no_magic_damage >= battle_config.gtb_sc_immunity)
+ return ((TBL_PC*)bl)->special_state.no_magic_damage;
+ return 0;
+}
+
+struct view_data* status_get_viewdata(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->vd;
+ case BL_MOB: return ((TBL_MOB*)bl)->vd;
+ case BL_PET: return &((TBL_PET*)bl)->vd;
+ case BL_NPC: return ((TBL_NPC*)bl)->vd;
+ case BL_HOM: return ((TBL_HOM*)bl)->vd;
+ case BL_MER: return ((TBL_MER*)bl)->vd;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->vd;
+ }
+ return NULL;
+}
+
+void status_set_viewdata(struct block_list *bl, int class_)
+{
+ struct view_data* vd;
+ nullpo_retv(bl);
+ if (mobdb_checkid(class_) || mob_is_clone(class_))
+ vd = mob_get_viewdata(class_);
+ else if (npcdb_checkid(class_) || (bl->type == BL_NPC && class_ == WARP_CLASS))
+ vd = npc_get_viewdata(class_);
+ else if (homdb_checkid(class_))
+ vd = merc_get_hom_viewdata(class_);
+ else if (merc_class(class_))
+ vd = merc_get_viewdata(class_);
+ else if (elemental_class(class_))
+ vd = elemental_get_viewdata(class_);
+ else
+ vd = NULL;
+
+ switch (bl->type) {
+ case BL_PC:
+ {
+ TBL_PC* sd = (TBL_PC*)bl;
+ if (pcdb_checkid(class_)) {
+ if (sd->sc.option&OPTION_WEDDING)
+ class_ = JOB_WEDDING;
+ else if (sd->sc.option&OPTION_SUMMER)
+ class_ = JOB_SUMMER;
+ else if (sd->sc.option&OPTION_XMAS)
+ class_ = JOB_XMAS;
+ else if (sd->sc.option&OPTION_RIDING) {
+ switch (class_) { //Adapt class to a Mounted one.
+ case JOB_KNIGHT:
+ class_ = JOB_KNIGHT2;
+ break;
+ case JOB_CRUSADER:
+ class_ = JOB_CRUSADER2;
+ break;
+ case JOB_LORD_KNIGHT:
+ class_ = JOB_LORD_KNIGHT2;
+ break;
+ case JOB_PALADIN:
+ class_ = JOB_PALADIN2;
+ break;
+ case JOB_BABY_KNIGHT:
+ class_ = JOB_BABY_KNIGHT2;
+ break;
+ case JOB_BABY_CRUSADER:
+ class_ = JOB_BABY_CRUSADER2;
+ break;
+ }
+ }
+ sd->vd.class_ = class_;
+ clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield);
+ sd->vd.head_top = sd->status.head_top;
+ sd->vd.head_mid = sd->status.head_mid;
+ sd->vd.head_bottom = sd->status.head_bottom;
+ sd->vd.hair_style = cap_value(sd->status.hair,0,battle_config.max_hair_style);
+ sd->vd.hair_color = cap_value(sd->status.hair_color,0,battle_config.max_hair_color);
+ sd->vd.cloth_color = cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color);
+ sd->vd.sex = sd->status.sex;
+ } else if (vd)
+ memcpy(&sd->vd, vd, sizeof(struct view_data));
+ else
+ ShowError("status_set_viewdata (PC): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB* md = (TBL_MOB*)bl;
+ if (vd)
+ md->vd = vd;
+ else
+ ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_PET:
+ {
+ TBL_PET* pd = (TBL_PET*)bl;
+ if (vd) {
+ memcpy(&pd->vd, vd, sizeof(struct view_data));
+ if (!pcdb_checkid(vd->class_)) {
+ pd->vd.hair_style = battle_config.pet_hair_style;
+ if(pd->pet.equip) {
+ pd->vd.head_bottom = itemdb_viewid(pd->pet.equip);
+ if (!pd->vd.head_bottom)
+ pd->vd.head_bottom = pd->pet.equip;
+ }
+ }
+ } else
+ ShowError("status_set_viewdata (PET): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_NPC:
+ {
+ TBL_NPC* nd = (TBL_NPC*)bl;
+ if (vd)
+ nd->vd = vd;
+ else
+ ShowError("status_set_viewdata (NPC): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_HOM: //[blackhole89]
+ {
+ struct homun_data *hd = (struct homun_data*)bl;
+ if (vd)
+ hd->vd = vd;
+ else
+ ShowError("status_set_viewdata (HOMUNCULUS): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_MER:
+ {
+ struct mercenary_data *md = (struct mercenary_data*)bl;
+ if (vd)
+ md->vd = vd;
+ else
+ ShowError("status_set_viewdata (MERCENARY): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_ELEM:
+ {
+ struct elemental_data *ed = (struct elemental_data*)bl;
+ if (vd)
+ ed->vd = vd;
+ else
+ ShowError("status_set_viewdata (ELEMENTAL): No view data for class %d\n", class_);
+ }
+ break;
+ }
+ vd = status_get_viewdata(bl);
+ if (vd && vd->cloth_color && (
+ (vd->class_==JOB_WEDDING && battle_config.wedding_ignorepalette)
+ || (vd->class_==JOB_XMAS && battle_config.xmas_ignorepalette)
+ || (vd->class_==JOB_SUMMER && battle_config.summer_ignorepalette)
+ ))
+ vd->cloth_color = 0;
+}
+
+/// Returns the status_change data of bl or NULL if it doesn't exist.
+struct status_change *status_get_sc(struct block_list *bl) {
+ if( bl )
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->sc;
+ case BL_MOB: return &((TBL_MOB*)bl)->sc;
+ case BL_NPC: return &((TBL_NPC*)bl)->sc;
+ case BL_HOM: return &((TBL_HOM*)bl)->sc;
+ case BL_MER: return &((TBL_MER*)bl)->sc;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->sc;
+ }
+ return NULL;
+}
+
+void status_change_init(struct block_list *bl)
+{
+ struct status_change *sc = status_get_sc(bl);
+ nullpo_retv(sc);
+ memset(sc, 0, sizeof (struct status_change));
+}
+
+//Applies SC defense to a given status change.
+//Returns the adjusted duration based on flag values.
+//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 = 0, tick_def = 0;
+ struct status_data* status;
+ struct status_change* sc;
+ struct map_session_data *sd;
+
+ nullpo_ret(bl);
+
+ //Status that are blocked by Golden Thief Bug card or Wand of Hermod
+ if (status_isimmune(bl))
+ switch (type) {
+ case SC_DECREASEAGI:
+ case SC_SILENCE:
+ case SC_COMA:
+ case SC_INCREASEAGI:
+ case SC_BLESSING:
+ case SC_SLOWPOISON:
+ case SC_IMPOSITIO:
+ case SC_AETERNA:
+ case SC_SUFFRAGIUM:
+ case SC_BENEDICTIO:
+ case SC_PROVIDENCE:
+ case SC_KYRIE:
+ case SC_ASSUMPTIO:
+ case SC_ANGELUS:
+ case SC_MAGNIFICAT:
+ case SC_GLORIA:
+ case SC_WINDWALK:
+ case SC_MAGICROD:
+ case SC_HALLUCINATION:
+ case SC_STONE:
+ case SC_QUAGMIRE:
+ case SC_SUITON:
+ case SC_SWINGDANCE:
+ case SC__ENERVATION:
+ case SC__GROOMY:
+ case SC__IGNORANCE:
+ case SC__LAZINESS:
+ case SC__UNLUCKY:
+ case SC__WEAKNESS:
+ case SC__BLOODYLUST:
+ return 0;
+ }
+
+ sd = BL_CAST(BL_PC,bl);
+ status = status_get_status_data(bl);
+ sc = status_get_sc(bl);
+ if( sc && !sc->count )
+ sc = NULL;
+ switch (type) {
+ case SC_STUN:
+ case SC_POISON:
+ if( sc && sc->data[SC__UNLUCKY] )
+ return tick;
+ case SC_DPOISON:
+ case SC_SILENCE:
+ case SC_BLEEDING:
+ sc_def = 3 +status->vit;
+ break;
+ case SC_SLEEP:
+ sc_def = 3 +status->int_;
+ break;
+ case SC_DEEPSLEEP:
+ tick_def = status->int_ / 10 + status_get_lv(bl) * 65 / 1000; // Seems to be -1 sec every 10 int and -5% chance every 10 int.
+ sc_def = 5 * status->int_ /10;
+ break;
+ case SC_DECREASEAGI:
+ case SC_ADORAMUS://Arch Bishop
+ if (sd) tick>>=1; //Half duration for players.
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def = 3 +status->mdef;
+ break;
+ case SC_CURSE:
+ //Special property: inmunity when luk is greater than level or zero
+ if (status->luk > status_get_lv(bl) || status->luk == 0)
+ return 0;
+ else
+ sc_def = 3 +status->luk;
+ tick_def = status->vit;
+ break;
+ case SC_BLIND:
+ if( sc && sc->data[SC__UNLUCKY] )
+ return tick;
+ sc_def = 3 +(status->vit + status->int_)/2;
+ break;
+ case SC_CONFUSION:
+ sc_def = 3 +(status->str + status->int_)/2;
+ break;
+ case SC_ANKLE:
+ if(status->mode&MD_BOSS) // Lasts 5 times less on bosses
+ tick /= 5;
+ sc_def = status->agi / 2;
+ break;
+ case SC_MAGICMIRROR:
+ case SC_ARMORCHANGE:
+ if (sd) //Duration greatly reduced for players.
+ tick /= 15;
+ //No defense against it (buff).
+ rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; // Lineal Reduction of Rate
+ break;
+ case SC_MARSHOFABYSS:
+ //5 second (Fixed) + 25 second - {( INT + LUK ) / 20 second }
+ tick -= (status->int_ + status->luk) / 20 * 1000;
+ break;
+ case SC_STASIS:
+ //5 second (fixed) + { Stasis Skill level * 5 - (Target�s VIT + DEX) / 20 }
+ tick -= (status->vit + status->dex) / 20 * 1000;
+ break;
+ case SC_WHITEIMPRISON:
+ if( tick == 5000 ) // 100% on caster
+ break;
+ if( bl->type == BL_PC )
+ tick -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100;
+ else
+ tick -= (status->vit + status->luk) / 20 * 1000;
+ 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: // 100% - (100 - 0.8 x INT)
+ sc_def = 100 - ( 100 - status->int_* 8 / 10 );
+ sc_def = max(sc_def, 5); // minimum of 5%
+ break;
+ case SC_BITE: // {(Base Success chance) - (Target's AGI / 4)}
+ rate -= status->agi*1000/4;
+ rate = max(rate,50000); // minimum of 50%
+ break;
+ case SC_ELECTRICSHOCKER:
+ if( bl->type == BL_MOB )
+ tick -= 1000 * (status->agi/10);
+ break;
+ case SC_CRYSTALIZE:
+ tick -= (1000*(status->vit/10))+(status_get_lv(bl)/50);
+ break;
+ case SC_MANDRAGORA:
+ sc_def = (status->vit+status->luk)/5;
+ break;
+ case SC_KYOUGAKU:
+ tick -= 30*status->int_;
+ break;
+ case SC_PARALYSIS:
+ tick -= 50 * (status->vit + status->luk); //(1000/20);
+ break;
+ default:
+ //Effect that cannot be reduced? Likely a buff.
+ if (!(rnd()%10000 < rate))
+ return 0;
+ return tick?tick:1;
+ }
+
+ if (sd) {
+
+ if (battle_config.pc_sc_def_rate != 100)
+ sc_def = sc_def*battle_config.pc_sc_def_rate/100;
+
+ if (sc_def < battle_config.pc_max_sc_def)
+ sc_def += (battle_config.pc_max_sc_def - sc_def)*
+ status->luk/battle_config.pc_luk_sc_def;
+ else
+ sc_def = battle_config.pc_max_sc_def;
+
+ if (tick_def) {
+ if (battle_config.pc_sc_def_rate != 100)
+ tick_def = tick_def*battle_config.pc_sc_def_rate/100;
+ }
+
+ } else {
+
+ if (battle_config.mob_sc_def_rate != 100)
+ sc_def = sc_def*battle_config.mob_sc_def_rate/100;
+
+ if (sc_def < battle_config.mob_max_sc_def)
+ sc_def += (battle_config.mob_max_sc_def - sc_def)*
+ status->luk/battle_config.mob_luk_sc_def;
+ else
+ sc_def = battle_config.mob_max_sc_def;
+
+ if (tick_def) {
+ if (battle_config.mob_sc_def_rate != 100)
+ tick_def = tick_def*battle_config.mob_sc_def_rate/100;
+ }
+ }
+
+ if (sc) {
+ if (sc->data[SC_SCRESIST])
+ sc_def += sc->data[SC_SCRESIST]->val1; //Status resist
+ else if (sc->data[SC_SIEGFRIED])
+ sc_def += sc->data[SC_SIEGFRIED]->val3; //Status resistance.
+ }
+
+ //When no tick def, reduction is the same for both.
+ if( !tick_def && type != SC_STONE ) //Recent tests show duration of petrify isn't reduced by MDEF. [Inkfish]
+ tick_def = sc_def;
+
+ //Natural resistance
+ if (!(flag&8)) {
+ rate -= rate*sc_def/100;
+
+ //Item resistance (only applies to rate%)
+ if(sd && SC_COMMON_MIN <= type && type <= SC_COMMON_MAX)
+ {
+ if( sd->reseff[type-SC_COMMON_MIN] > 0 )
+ rate -= rate*sd->reseff[type-SC_COMMON_MIN]/10000;
+ if( sd->sc.data[SC_COMMONSC_RESIST] )
+ rate -= rate*sd->sc.data[SC_COMMONSC_RESIST]->val1/100;
+ }
+ }
+ if (!(rnd()%10000 < rate))
+ return 0;
+
+ //Why would a status start with no duration? Presume it has
+ //duration defined elsewhere.
+ if (!tick) return 1;
+
+ //Rate reduction
+ if (flag&2)
+ return tick;
+
+ tick -= tick*tick_def/100;
+ // Changed to 5 seconds according to recent tests [Playtester]
+ if (type == SC_ANKLE && tick < 5000)
+ tick = 5000;
+ return tick<=0?0:tick;
+}
+
+/*==========================================
+ * Starts a status change.
+ * 'type' = type, 'val1~4' depend on the type.
+ * 'rate' = base success rate. 10000 = 100%
+ * 'tick' is base duration
+ * 'flag':
+ * &1: Cannot be avoided (it has to start)
+ * &2: Tick should not be reduced (by vit, luk, lv, etc)
+ * &4: sc_data loaded, no value has to be altered.
+ * &8: rate should not be reduced
+ *------------------------------------------*/
+int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,int tick,int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_change* sc;
+ struct status_change_entry* sce;
+ struct status_data *status;
+ struct view_data *vd;
+ int opt_flag, calc_flag, undead_flag, val_flag = 0, tick_time = 0;
+ bool sc_isnew = true;
+
+ nullpo_ret(bl);
+ sc = status_get_sc(bl);
+ status = status_get_status_data(bl);
+
+ if( type <= SC_NONE || type >= SC_MAX )
+ {
+ ShowError("status_change_start: invalid status change (%d)!\n", type);
+ return 0;
+ }
+
+ if( !sc )
+ return 0; //Unable to receive status changes
+
+ if( status_isdead(bl) && type != SC_NOCHAT ) // SC_NOCHAT should work even on dead characters
+ return 0;
+
+ if( bl->type == BL_MOB)
+ {
+ struct mob_data *md = BL_CAST(BL_MOB,bl);
+ if(md && (md->class_ == MOBID_EMPERIUM || mob_is_battleground(md)) && type != SC_SAFETYWALL && type != SC_PNEUMA)
+ return 0; //Emperium/BG Monsters can't be afflicted by status changes
+ // if(md && mob_is_gvg(md) && status_sc2scb_flag(type)&SCB_MAXHP)
+ // return 0; //prevent status addinh hp to gvg mob (like bloodylust=hp*3 etc...
+ }
+
+ if( sc->data[SC_REFRESH] ) {
+ if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX) // Confirmed.
+ return 0; // Immune to status ailements
+ switch( type ) {
+ case SC_QUAGMIRE://Tester said it protects against this and decrease agi.
+ case SC_DECREASEAGI:
+ case SC_BURNING:
+ case SC_FREEZING:
+ //case SC_WHITEIMPRISON://Need confirm. Protected against this in the past. [Rytech]
+ case SC_MARSHOFABYSS:
+ 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_CRYSTALIZE: ////08/31/2011 - Class Balance Changes
+ case SC_DEEPSLEEP:
+ case SC_MANDRAGORA:
+ return 0;
+ }
+ }
+ else if( sc->data[SC_INSPIRATION] ) {
+ if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX )
+ return 0; // Immune to status ailements
+ switch( type ) {
+ case SC_DEEPSLEEP:
+ case SC_SATURDAYNIGHTFEVER:
+ case SC_PYREXIA:
+ case SC_DEATHHURT:
+ case SC_MAGICMUSHROOM:
+ case SC_VENOMBLEED:
+ case SC_TOXIN:
+ case SC_OBLIVIONCURSE:
+ case SC_LEECHESEND:
+ case SC__ENERVATION:
+ case SC__GROOMY:
+ case SC__LAZINESS:
+ case SC__UNLUCKY:
+ case SC__WEAKNESS:
+ case SC__BODYPAINT:
+ case SC__IGNORANCE:
+ return 0;
+ }
+ }
+
+ sd = BL_CAST(BL_PC, bl);
+
+ //Adjust tick according to status resistances
+ if( !(flag&(1|4)) )
+ {
+ tick = status_get_sc_def(bl, type, rate, tick, flag);
+ if( !tick ) return 0;
+ }
+
+ undead_flag = battle_check_undead(status->race,status->def_ele);
+ //Check for inmunities / sc fails
+ switch (type) {
+ case SC_ANGRIFFS_MODUS:
+ case SC_GOLDENE_FERSE:
+ if ((type==SC_GOLDENE_FERSE && sc->data[SC_ANGRIFFS_MODUS])
+ || (type==SC_ANGRIFFS_MODUS && sc->data[SC_GOLDENE_FERSE])
+ )
+ return 0;
+ 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_DEEPSLEEP:
+ case SC_SLEEP:
+ case SC_STUN:
+ case SC_FREEZING:
+ case SC_CRYSTALIZE:
+ if (sc->opt1)
+ return 0; //Cannot override other opt1 status changes. [Skotlex]
+ if((type == SC_FREEZE || type == SC_FREEZING || type == SC_CRYSTALIZE) && sc->data[SC_WARMER])
+ return 0; //Immune to Frozen and Freezing status if under Warmer status. [Jobbie]
+ break;
+
+ //There all like berserk, do not everlap each other
+ case SC__BLOODYLUST:
+ if(!sd) return 0; //should only affect player
+ case SC_BERSERK:
+ if (((type == SC_BERSERK) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC__BLOODYLUST]))
+ || ((type == SC__BLOODYLUST) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC_BERSERK]))
+ )
+ return 0;
+ 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)
+ return 0;
+ break;
+ case SC_AETERNA:
+ if( (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) || sc->data[SC_FREEZE] )
+ return 0;
+ break;
+ case SC_KYRIE:
+ if (bl->type == BL_MOB)
+ return 0;
+ break;
+ 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->option&OPTION_MADOGEAR //Adrenaline doesn't affect Mado Gear [Ind]
+ )
+ return 0;
+ break;
+ case SC_ADRENALINE2:
+ if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE2)))
+ return 0;
+ if (sc->data[SC_QUAGMIRE] ||
+ sc->data[SC_DECREASEAGI]
+ )
+ return 0;
+ break;
+ case SC_MAGNIFICAT:
+ if( sc->option&OPTION_MADOGEAR ) //Mado is immune to magnificat
+ return 0;
+ break;
+ case SC_ONEHAND:
+ case SC_MERC_QUICKEN:
+ case SC_TWOHANDQUICKEN:
+ if(sc->data[SC_DECREASEAGI])
+ return 0;
+
+ case SC_INCREASEAGI:
+ if(sd && pc_issit(sd)){
+ pc_setstand(sd);
+ }
+
+ case SC_CONCENTRATE:
+ case SC_SPEARQUICKEN:
+ case SC_TRUESIGHT:
+ case SC_WINDWALK:
+ case SC_CARTBOOST:
+ 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]
+ //Due to the cloaking card, we have to check the wall versus to known
+ //skill level rather than the used one. [Skotlex]
+ //if (sd && val1 < 3 && skill_check_cloaking(bl,NULL))
+ if( sd && pc_checkskill(sd, AS_CLOAKING) < 3 && !skill_check_cloaking(bl,NULL) )
+ return 0;
+ break;
+ case SC_MODECHANGE:
+ {
+ int mode;
+ struct status_data *bstatus = status_get_base_status(bl);
+ if (!bstatus) return 0;
+ if (sc->data[type])
+ { //Pile up with previous values.
+ if(!val2) val2 = sc->data[type]->val2;
+ val3 |= sc->data[type]->val3;
+ val4 |= sc->data[type]->val4;
+ }
+ mode = val2?val2:bstatus->mode; //Base mode
+ if (val4) mode&=~val4; //Del mode
+ if (val3) mode|= val3; //Add mode
+ if (mode == bstatus->mode) { //No change.
+ if (sc->data[type]) //Abort previous status
+ return status_change_end(bl, type, INVALID_TIMER);
+ return 0;
+ }
+ }
+ break;
+ //Strip skills, need to divest something or it fails.
+ case SC_STRIPWEAPON:
+ if (sd && !(flag&4)) { //apply sc anyway if loading saved sc_data
+ int i;
+ opt_flag = 0; //Reuse to check success condition.
+ if(sd->bonus.unstripable_equip&EQP_WEAPON)
+ return 0;
+ i = sd->equip_index[EQI_HAND_L];
+ if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
+ opt_flag|=1;
+ pc_unequipitem(sd,i,3); //L-hand weapon
+ }
+
+ i = sd->equip_index[EQI_HAND_R];
+ if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
+ opt_flag|=2;
+ pc_unequipitem(sd,i,3);
+ }
+ if (!opt_flag) return 0;
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_STRIPSHIELD:
+ if( val2 == 1 ) val2 = 0; //GX effect. Do not take shield off..
+ else
+ if (sd && !(flag&4)) {
+ int i;
+ if(sd->bonus.unstripable_equip&EQP_SHIELD)
+ return 0;
+ i = sd->equip_index[EQI_HAND_L];
+ if ( i < 0 || !sd->inventory_data[i] || sd->inventory_data[i]->type != IT_ARMOR )
+ return 0;
+ pc_unequipitem(sd,i,3);
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_STRIPARMOR:
+ if (sd && !(flag&4)) {
+ int i;
+ if(sd->bonus.unstripable_equip&EQP_ARMOR)
+ return 0;
+ i = sd->equip_index[EQI_ARMOR];
+ if ( i < 0 || !sd->inventory_data[i] )
+ return 0;
+ pc_unequipitem(sd,i,3);
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_STRIPHELM:
+ if (sd && !(flag&4)) {
+ int i;
+ if(sd->bonus.unstripable_equip&EQP_HELM)
+ return 0;
+ i = sd->equip_index[EQI_HEAD_TOP];
+ if ( i < 0 || !sd->inventory_data[i] )
+ return 0;
+ pc_unequipitem(sd,i,3);
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_MERC_FLEEUP:
+ case SC_MERC_ATKUP:
+ case SC_MERC_HPUP:
+ case SC_MERC_SPUP:
+ case SC_MERC_HITUP:
+ if( bl->type != BL_MER )
+ return 0; // Stats only for Mercenaries
+ break;
+ case SC_STRFOOD:
+ if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_AGIFOOD:
+ if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_VITFOOD:
+ if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_INTFOOD:
+ if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_DEXFOOD:
+ if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_LUKFOOD:
+ if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_STR_CASH:
+ if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_AGI_CASH:
+ if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_VIT_CASH:
+ if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_INT_CASH:
+ if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_DEX_CASH:
+ if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_LUK_CASH:
+ if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_CAMOUFLAGE:
+ if( sd && pc_checkskill(sd, RA_CAMOUFLAGE) < 3 && !skill_check_camouflage(bl,NULL) )
+ return 0;
+ break;
+ case SC__STRIPACCESSORY:
+ if( sd ) {
+ int i = -1;
+ if( !(sd->bonus.unstripable_equip&EQI_ACC_L) ) {
+ i = sd->equip_index[EQI_ACC_L];
+ if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
+ pc_unequipitem(sd,i,3); //L-Accessory
+ } if( !(sd->bonus.unstripable_equip&EQI_ACC_R) ) {
+ i = sd->equip_index[EQI_ACC_R];
+ if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
+ pc_unequipitem(sd,i,3); //R-Accessory
+ }
+ if( i < 0 )
+ return 0;
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_TOXIN:
+ case SC_PARALYSE:
+ case SC_VENOMBLEED:
+ case SC_MAGICMUSHROOM:
+ case SC_DEATHHURT:
+ case SC_PYREXIA:
+ case SC_OBLIVIONCURSE:
+ case SC_LEECHESEND:
+ { // it doesn't stack or even renewed
+ int i = SC_TOXIN;
+ for(; i<= SC_LEECHESEND; i++)
+ if(sc->data[i]) return 0;
+ }
+ break;
+ case SC_SATURDAYNIGHTFEVER:
+ if (sc->data[SC_BERSERK] || sc->data[SC_INSPIRATION] || sc->data[SC__BLOODYLUST])
+ return 0;
+ break;
+ }
+
+ //Check for BOSS resistances
+ if(status->mode&MD_BOSS && !(flag&1)) {
+ if (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX)
+ return 0;
+ switch (type) {
+ case SC_BLESSING:
+ case SC_DECREASEAGI:
+ case SC_PROVOKE:
+ case SC_COMA:
+ case SC_GRAVITATION:
+ case SC_SUITON:
+ case SC_RICHMANKIM:
+ case SC_ROKISWEIL:
+ case SC_FOGWALL:
+ case SC_FREEZING:
+ case SC_BURNING:
+ case SC_MARSHOFABYSS:
+ case SC_ADORAMUS:
+ case SC_PARALYSIS:
+ case SC_DEEPSLEEP:
+ case SC_CRYSTALIZE:
+
+ // Exploit 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;
+ }
+ }
+
+ //Before overlapping fail, one must check for status cured.
+ switch (type) {
+ case SC_BLESSING:
+ //TO-DO Blessing and Agi up should do 1 damage against players on Undead Status, even on PvM
+ //but cannot be plagiarized (this requires aegis investigation on packets and official behavior) [Brainstorm]
+ if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) {
+ status_change_end(bl, SC_CURSE, INVALID_TIMER);
+ if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ }
+ break;
+ case SC_INCREASEAGI:
+ status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER);
+ break;
+ case SC_QUAGMIRE:
+ status_change_end(bl, SC_CONCENTRATE, INVALID_TIMER);
+ status_change_end(bl, SC_TRUESIGHT, INVALID_TIMER);
+ status_change_end(bl, SC_WINDWALK, INVALID_TIMER);
+ //Also blocks the ones below...
+ case SC_DECREASEAGI:
+ status_change_end(bl, SC_CARTBOOST, INVALID_TIMER);
+ //Also blocks the ones below...
+ case SC_DONTFORGETME:
+ status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER);
+ status_change_end(bl, SC_ADRENALINE, INVALID_TIMER);
+ status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER);
+ status_change_end(bl, SC_SPEARQUICKEN, INVALID_TIMER);
+ 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]
+ status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER);
+ break;
+ case SC_MAXOVERTHRUST:
+ //Cancels Normal Overthrust. [Skotlex]
+ status_change_end(bl, SC_OVERTHRUST, INVALID_TIMER);
+ break;
+ case SC_KYRIE:
+ //Cancels Assumptio
+ status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
+ break;
+ case SC_DELUGE:
+ if (sc->data[SC_FOGWALL] && sc->data[SC_BLIND])
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ break;
+ case SC_SILENCE:
+ if (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)
+ status_change_end(bl, SC_GOSPEL, INVALID_TIMER);
+ break;
+ case SC_HIDING:
+ status_change_end(bl, SC_CLOSECONFINE, INVALID_TIMER);
+ status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
+ break;
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ if(battle_config.berserk_cancels_buffs) {
+ status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
+ status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
+ status_change_end(bl, SC_CONCENTRATION, INVALID_TIMER);
+ status_change_end(bl, SC_PARRYING, INVALID_TIMER);
+ status_change_end(bl, SC_AURABLADE, INVALID_TIMER);
+ status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER);
+ }
+#ifdef RENEWAL
+ else {
+ status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
+ }
+#endif
+ break;
+ case SC_ASSUMPTIO:
+ status_change_end(bl, SC_KYRIE, INVALID_TIMER);
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ break;
+ case SC_KAITE:
+ status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
+ break;
+ case SC_CARTBOOST:
+ if(sc->data[SC_DECREASEAGI])
+ { //Cancel Decrease Agi, but take no further effect [Skotlex]
+ status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER);
+ return 0;
+ }
+ break;
+ case SC_FUSION:
+ status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
+ break;
+ case SC_ADJUSTMENT:
+ status_change_end(bl, SC_MADNESSCANCEL, INVALID_TIMER);
+ break;
+ case SC_MADNESSCANCEL:
+ status_change_end(bl, SC_ADJUSTMENT, INVALID_TIMER);
+ break;
+ //NPC_CHANGEUNDEAD will debuff Blessing and Agi Up
+ case SC_CHANGEUNDEAD:
+ status_change_end(bl, SC_BLESSING, INVALID_TIMER);
+ status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER);
+ break;
+ case SC_STRFOOD:
+ status_change_end(bl, SC_FOOD_STR_CASH, INVALID_TIMER);
+ break;
+ case SC_AGIFOOD:
+ status_change_end(bl, SC_FOOD_AGI_CASH, INVALID_TIMER);
+ break;
+ case SC_VITFOOD:
+ status_change_end(bl, SC_FOOD_VIT_CASH, INVALID_TIMER);
+ break;
+ case SC_INTFOOD:
+ status_change_end(bl, SC_FOOD_INT_CASH, INVALID_TIMER);
+ break;
+ case SC_DEXFOOD:
+ status_change_end(bl, SC_FOOD_DEX_CASH, INVALID_TIMER);
+ break;
+ case SC_LUKFOOD:
+ status_change_end(bl, SC_FOOD_LUK_CASH, INVALID_TIMER);
+ break;
+ case SC_FOOD_STR_CASH:
+ status_change_end(bl, SC_STRFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_AGI_CASH:
+ status_change_end(bl, SC_AGIFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_VIT_CASH:
+ status_change_end(bl, SC_VITFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_INT_CASH:
+ status_change_end(bl, SC_INTFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_DEX_CASH:
+ status_change_end(bl, SC_DEXFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_LUK_CASH:
+ status_change_end(bl, SC_LUKFOOD, INVALID_TIMER);
+ break;
+ case SC_FIGHTINGSPIRIT:
+ status_change_end(bl, type, INVALID_TIMER); // Remove previous one.
+ break;
+ case SC_MARSHOFABYSS:
+ status_change_end(bl, SC_INCAGI, INVALID_TIMER);
+ status_change_end(bl, SC_WINDWALK, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER);
+ break;
+ case SC_SWINGDANCE:
+ case SC_SYMPHONYOFLOVER:
+ case SC_MOONLITSERENADE:
+ case SC_RUSHWINDMILL:
+ case SC_ECHOSONG:
+ case SC_HARMONIZE: //group A doesn't overlap
+ if (type != SC_SWINGDANCE) status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
+ if (type != SC_SYMPHONYOFLOVER) status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
+ if (type != SC_MOONLITSERENADE) status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
+ if (type != SC_RUSHWINDMILL) status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
+ if (type != SC_ECHOSONG) status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
+ if (type != SC_HARMONIZE) status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ break;
+ case SC_VOICEOFSIREN:
+ case SC_DEEPSLEEP:
+ case SC_GLOOMYDAY:
+ case SC_SONGOFMANA:
+ case SC_DANCEWITHWUG:
+ case SC_SATURDAYNIGHTFEVER:
+ case SC_LERADSDEW:
+ case SC_MELODYOFSINK:
+ case SC_BEYONDOFWARCRY:
+ case SC_UNLIMITEDHUMMINGVOICE: //group B
+ if (type != SC_VOICEOFSIREN) status_change_end(bl, SC_VOICEOFSIREN, INVALID_TIMER);
+ if (type != SC_DEEPSLEEP) status_change_end(bl, SC_DEEPSLEEP, INVALID_TIMER);
+ if (type != SC_LERADSDEW) status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
+ if (type != SC_MELODYOFSINK) status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
+ if (type != SC_BEYONDOFWARCRY) status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
+ if (type != SC_UNLIMITEDHUMMINGVOICE) status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
+ if (type != SC_GLOOMYDAY) {
+ status_change_end(bl, SC_GLOOMYDAY, INVALID_TIMER);
+ status_change_end(bl, SC_GLOOMYDAY_SK, INVALID_TIMER);
+ }
+ if (type != SC_SONGOFMANA) status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER);
+ if (type != SC_DANCEWITHWUG) status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
+ if (type != SC_SATURDAYNIGHTFEVER) {
+ if (sc->data[SC_SATURDAYNIGHTFEVER]) {
+ sc->data[SC_SATURDAYNIGHTFEVER]->val2 = 0; //mark to not lose hp
+ status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
+ }
+ }
+ break;
+ case SC_REFLECTSHIELD:
+ status_change_end(bl, SC_REFLECTDAMAGE, INVALID_TIMER);
+ break;
+ case SC_REFLECTDAMAGE:
+ status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER);
+ break;
+ case SC_SHIELDSPELL_DEF:
+ case SC_SHIELDSPELL_MDEF:
+ case SC_SHIELDSPELL_REF:
+ status_change_end(bl, SC_MAGNIFICAT, INVALID_TIMER);
+ if( type != SC_SHIELDSPELL_DEF )
+ status_change_end(bl, SC_SHIELDSPELL_DEF, INVALID_TIMER);
+ if( type != SC_SHIELDSPELL_MDEF )
+ status_change_end(bl, SC_SHIELDSPELL_MDEF, INVALID_TIMER);
+ if( type != SC_SHIELDSPELL_REF )
+ status_change_end(bl, SC_SHIELDSPELL_REF, INVALID_TIMER);
+ break;
+ case SC_GT_ENERGYGAIN:
+ case SC_GT_CHANGE:
+ case SC_GT_REVITALIZE:
+ if( type != SC_GT_REVITALIZE )
+ status_change_end(bl, SC_GT_REVITALIZE, INVALID_TIMER);
+ if( type != SC_GT_ENERGYGAIN )
+ status_change_end(bl, SC_GT_ENERGYGAIN, INVALID_TIMER);
+ if( type != SC_GT_CHANGE )
+ status_change_end(bl, SC_GT_CHANGE, INVALID_TIMER);
+ break;
+ case SC_INVINCIBLE:
+ status_change_end(bl, SC_INVINCIBLEOFF, INVALID_TIMER);
+ break;
+ case SC_INVINCIBLEOFF:
+ status_change_end(bl, SC_INVINCIBLE, INVALID_TIMER);
+ break;
+ case SC_MAGICPOWER:
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+ }
+
+ //Check for overlapping fails
+ if( (sce = sc->data[type]) ) {
+ switch( type ) {
+ case SC_MERC_FLEEUP:
+ case SC_MERC_ATKUP:
+ case SC_MERC_HPUP:
+ case SC_MERC_SPUP:
+ case SC_MERC_HITUP:
+ if( sce->val1 > val1 )
+ val1 = sce->val1;
+ break;
+ case SC_ADRENALINE:
+ case SC_ADRENALINE2:
+ case SC_WEAPONPERFECTION:
+ case SC_OVERTHRUST:
+ if (sce->val2 > val2)
+ return 0;
+ break;
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ case SC_BOSSMAPINFO:
+ case SC_STUN:
+ case SC_SLEEP:
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ case SC_BLEEDING:
+ case SC_DPOISON:
+ case SC_CLOSECONFINE2: //Can't be re-closed in.
+ case SC_MARIONETTE:
+ 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__INVISIBILITY:
+ case SC__ENERVATION:
+ case SC__GROOMY:
+ case SC__IGNORANCE:
+ case SC__LAZINESS:
+ case SC__WEAKNESS:
+ case SC__UNLUCKY:
+ return 0;
+ case SC_COMBO:
+ case SC_DANCING:
+ case SC_DEVOTION:
+ case SC_ASPDPOTION0:
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ case SC_ATKPOTION:
+ case SC_MATKPOTION:
+ case SC_ENCHANTARMS:
+ case SC_ARMOR_ELEMENT:
+ case SC_ARMOR_RESIST:
+ break;
+ case SC_GOSPEL:
+ //Must not override a casting gospel char.
+ if(sce->val4 == BCT_SELF)
+ return 0;
+ if(sce->val1 > val1)
+ return 1;
+ break;
+ case SC_ENDURE:
+ if(sce->val4 && !val4)
+ return 1; //Don't let you override infinite endure.
+ if(sce->val1 > val1)
+ return 1;
+ break;
+ case SC_KAAHI:
+ //Kaahi overwrites previous level regardless of existing level.
+ //Delete timer if it exists.
+ if (sce->val4 != INVALID_TIMER) {
+ delete_timer(sce->val4,kaahi_heal_timer);
+ sce->val4 = INVALID_TIMER;
+ }
+ break;
+ case SC_JAILED:
+ //When a player is already jailed, do not edit the jail data.
+ val2 = sce->val2;
+ val3 = sce->val3;
+ val4 = sce->val4;
+ break;
+ case SC_LERADSDEW:
+ if (sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
+ return 0;
+ case SC_SHAPESHIFT:
+ case SC_PROPERTYWALK:
+ break;
+ case SC_LEADERSHIP:
+ case SC_GLORYWOUNDS:
+ case SC_SOULCOLD:
+ case SC_HAWKEYES:
+ if( sce->val4 && !val4 )//you cannot override master guild aura
+ return 0;
+ break;
+ case SC_JOINTBEAT:
+ val2 |= sce->val2; // stackable ailments
+ default:
+ if(sce->val1 > val1)
+ return 1; //Return true to not mess up skill animations. [Skotlex]
+ }
+ }
+
+ vd = status_get_viewdata(bl);
+ calc_flag = StatusChangeFlagTable[type];
+ if(!(flag&4)) //&4 - Do not parse val settings when loading SCs
+ switch(type)
+ {
+ case SC_DECREASEAGI:
+ case SC_INCREASEAGI:
+ val2 = 2 + val1; //Agi change
+ break;
+ case SC_ENDURE:
+ val2 = 7; // Hit-count [Celest]
+ if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) && !map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && !val4 )
+ {
+ struct map_session_data *tsd;
+ if( sd )
+ {
+ int i;
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
+ }
+ //val4 signals infinite endure (if val4 == 2 it is infinite endure from Berserk)
+ if( val4 )
+ tick = -1;
+ break;
+ case SC_AUTOBERSERK:
+ if (status->hp < status->max_hp>>2 &&
+ (!sc->data[SC_PROVOKE] || sc->data[SC_PROVOKE]->val2==0))
+ sc_start4(bl,SC_PROVOKE,100,10,1,0,0,60000);
+ tick = -1;
+ break;
+ case SC_SIGNUMCRUCIS:
+ val2 = 10 + 4*val1; //Def reduction
+ tick = -1;
+ clif_emotion(bl,E_SWT);
+ break;
+ case SC_MAXIMIZEPOWER:
+ tick_time = val2 = tick>0?tick:60000;
+ tick = -1; // duration sent to the client should be infinite
+ break;
+ case SC_EDP: // [Celest]
+ val2 = val1 + 2; //Chance to Poison enemies.
+#ifndef RENEWAL_EDP
+ val3 = 50*(val1+1); //Damage increase (+50 +50*lv%)
+#endif
+ 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]
+ val3=50; // + 5*val1; //Chance to counter. [Skotlex]
+ break;
+ case SC_MAGICROD:
+ val2 = val1*20; //SP gained
+ break;
+ case SC_KYRIE:
+ val2 = (int64)status->max_hp * (val1 * 2 + 10) / 100; //%Max HP to absorb
+ val3 = (val1 / 2 + 5); //Hits
+ break;
+ case SC_MAGICPOWER:
+ //val1: Skill lv
+ val2 = 1; //Lasts 1 invocation
+ val3 = 5*val1; //Matk% increase
+ val4 = 0; // 0 = ready to be used, 1 = activated and running
+ break;
+ case SC_SACRIFICE:
+ val2 = 5; //Lasts 5 hits
+ tick = -1;
+ break;
+ case SC_ENCPOISON:
+ val2= 250+50*val1; //Poisoning Chance (2.5+0.5%) in 1/10000 rate
+ case SC_ASPERSIO:
+ case SC_FIREWEAPON:
+ case SC_WATERWEAPON:
+ case SC_WINDWEAPON:
+ case SC_EARTHWEAPON:
+ case SC_SHADOWWEAPON:
+ case SC_GHOSTWEAPON:
+ skill_enchant_elemental_end(bl,type);
+ break;
+ case SC_ELEMENTALCHANGE:
+ // val1 : Element Lvl (if called by skill lvl 1, takes random value between 1 and 4)
+ // val2 : Element (When no element, random one is picked)
+ // val3 : 0 = called by skill 1 = called by script (fixed level)
+ if( !val2 ) val2 = rnd()%ELE_MAX;
+
+ if( val1 == 1 && val3 == 0 )
+ val1 = 1 + rnd()%4;
+ else if( val1 > 4 )
+ val1 = 4; // Max Level
+ val3 = 0; // Not need to keep this info.
+ break;
+ case SC_PROVIDENCE:
+ val2=val1*5; //Race/Ele resist
+ break;
+ case SC_REFLECTSHIELD:
+ val2=10+val1*3; // %Dmg reflected
+ if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) )
+ {
+ struct map_session_data *tsd;
+ if( sd )
+ {
+ int i;
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ break;
+ case SC_STRIPWEAPON:
+ if (!sd) //Watk reduction
+ val2 = 25;
+ break;
+ case SC_STRIPSHIELD:
+ if (!sd) //Def reduction
+ val2 = 15;
+ break;
+ case SC_STRIPARMOR:
+ if (!sd) //Vit reduction
+ val2 = 40;
+ break;
+ case SC_STRIPHELM:
+ if (!sd) //Int reduction
+ val2 = 40;
+ break;
+ case SC_AUTOSPELL:
+ //Val1 Skill LV of Autospell
+ //Val2 Skill ID to cast
+ //Val3 Max Lv to cast
+ val4 = 5 + val1*2; //Chance of casting
+ break;
+ case SC_VOLCANO:
+ val2 = val1*10; //Watk increase
+#ifndef RENEWAL
+ if (status->def_ele != ELE_FIRE)
+ val2 = 0;
+#endif
+ break;
+ case SC_VIOLENTGALE:
+ val2 = val1*3; //Flee increase
+ #ifndef RENEWAL
+ if (status->def_ele != ELE_WIND)
+ val2 = 0;
+ #endif
+ break;
+ case SC_DELUGE:
+ val2 = deluge_eff[val1-1]; //HP increase
+#ifndef RENEWAL
+ if(status->def_ele != ELE_WATER)
+ val2 = 0;
+#endif
+ break;
+ case SC_SUITON:
+ if (!val2 || (sd && (sd->class_&MAPID_UPPERMASK) == MAPID_NINJA)) {
+ //No penalties.
+ val2 = 0; //Agi penalty
+ val3 = 0; //Walk speed penalty
+ break;
+ }
+ val3 = 50;
+ val2 = 3*((val1+1)/3);
+ if (val1 > 4) val2--;
+ break;
+ case SC_ONEHAND:
+ case SC_TWOHANDQUICKEN:
+ val2 = 300;
+ if (val1 > 10) //For boss casted skills [Skotlex]
+ val2 += 20*(val1-10);
+ break;
+ case SC_MERC_QUICKEN:
+ val2 = 300;
+ break;
+#ifndef RENEWAL
+ case SC_SPEARQUICKEN:
+ val2 = 200+10*val1;
+ break;
+#endif
+ case SC_DANCING:
+ //val1 : Skill ID + LV
+ //val2 : Skill Group of the Dance.
+ //val3 : Brings the skill_lv (merged into val1 here)
+ //val4 : Partner
+ if (val1 == CG_MOONLIT)
+ clif_status_change(bl,SI_MOONLIT,1,tick,0, 0, 0);
+ val1|= (val3<<16);
+ val3 = tick/1000; //Tick duration
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_LONGING:
+ val2 = 500-100*val1; //Aspd penalty.
+ break;
+ case SC_EXPLOSIONSPIRITS:
+ val2 = 75 + 25*val1; //Cri bonus
+ break;
+
+ case SC_ASPDPOTION0:
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ val2 = 50*(2+type-SC_ASPDPOTION0);
+ break;
+
+ case SC_WEDDING:
+ case SC_XMAS:
+ case SC_SUMMER:
+ if (!vd) return 0;
+ //Store previous values as they could be removed.
+ val1 = vd->class_;
+ val2 = vd->weapon;
+ val3 = vd->shield;
+ val4 = vd->cloth_color;
+ unit_stop_attack(bl);
+ clif_changelook(bl,LOOK_WEAPON,0);
+ clif_changelook(bl,LOOK_SHIELD,0);
+ clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER);
+ 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)
+ {
+ clif_changestatus(sd,SP_MANNER,sd->status.manner);
+ clif_updatestatus(sd,SP_MANNER);
+ }
+ break;
+
+ case SC_STONE:
+ val3 = tick/1000; //Petrified HP-damage iterations.
+ if(val3 < 1) val3 = 1;
+ tick = val4; //Petrifying time.
+ tick = max(tick, 1000); //Min time
+ calc_flag = 0; //Actual status changes take effect on petrified state.
+ break;
+
+ case SC_DPOISON:
+ //Lose 10/15% of your life as long as it doesn't brings life below 25%
+ if (status->hp > status->max_hp>>2) {
+ int diff = status->max_hp*(bl->type==BL_PC?10:15)/100;
+ if (status->hp - diff < status->max_hp>>2)
+ diff = status->hp - (status->max_hp>>2);
+ if( val2 && bl->type == BL_MOB ) {
+ struct block_list* src = map_id2bl(val2);
+ if( src )
+ mob_log_damage((TBL_MOB*)bl,src,diff);
+ }
+ status_zap(bl, diff, 0);
+ }
+ // fall through
+ case SC_POISON:
+ val3 = tick/1000; //Damage iterations
+ if(val3 < 1) val3 = 1;
+ 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;
+ else
+ val4 = (type == SC_DPOISON) ? 3 + status->max_hp/100 : 3 + status->max_hp/200;
+
+ break;
+ case SC_CONFUSION:
+ clif_emotion(bl,E_WHAT);
+ break;
+ case SC_BLEEDING:
+ val4 = tick/10000;
+ if (!val4) val4 = 1;
+ tick_time = 10000; // [GodLesZ] tick time
+ break;
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ if( val1 == 0 ) return 0;
+ // val1 = heal percent/amout
+ // val2 = seconds between heals
+ // val4 = total of heals
+ if( val2 < 1 ) val2 = 1;
+ if( (val4 = tick/(val2 * 1000)) < 1 )
+ val4 = 1;
+ tick_time = val2 * 1000; // [GodLesZ] tick time
+ break;
+ case SC_BOSSMAPINFO:
+ if( sd != NULL )
+ {
+ struct mob_data *boss_md = map_getmob_boss(bl->m); // Search for Boss on this Map
+ if( boss_md == NULL || boss_md->bl.prev == NULL )
+ { // No MVP on this map - MVP is dead
+ clif_bossmapinfo(sd->fd, boss_md, 1);
+ return 0; // No need to start SC
+ }
+ val1 = boss_md->bl.id;
+ if( (val4 = tick/1000) < 1 )
+ val4 = 1;
+ tick_time = 1000; // [GodLesZ] tick time
+ }
+ break;
+ case SC_HIDING:
+ val2 = tick/1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ val3 = 0; // unused, previously speed adjustment
+ val4 = val1+3; //Seconds before SP substraction happen.
+ break;
+ case SC_CHASEWALK:
+ val2 = tick>0?tick:10000; //Interval at which SP is drained.
+ val3 = 35 - 5 * val1; //Speed adjustment.
+ if (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE)
+ val3 -= 40;
+ val4 = 10+val1*2; //SP cost.
+ if (map_flag_gvg(bl->m) || map[bl->m].flag.battleground) val4 *= 5;
+ break;
+ case SC_CLOAKING:
+ if (!sd) //Monsters should be able to walk with no penalties. [Skotlex]
+ val1 = 10;
+ tick_time = val2 = tick>0?tick:60000; //SP consumption rate.
+ tick = -1; // duration sent to the client should be infinite
+ val3 = 0; // unused, previously walk speed adjustment
+ //val4&1 signals the presence of a wall.
+ //val4&2 makes cloak not end on normal attacks [Skotlex]
+ //val4&4 makes cloak not end on using skills
+ if (bl->type == BL_PC || (bl->type == BL_MOB && ((TBL_MOB*)bl)->special_state.clone) ) //Standard cloaking.
+ val4 |= battle_config.pc_cloak_check_type&7;
+ else
+ val4 |= battle_config.monster_cloak_check_type&7;
+ break;
+ case SC_SIGHT: /* splash status */
+ case SC_RUWACH:
+ case SC_SIGHTBLASTER:
+ val3 = skill_get_splash(val2, val1); //Val2 should bring the skill-id.
+ val2 = tick/250;
+ tick_time = 10; // [GodLesZ] tick time
+ break;
+
+ //Permanent effects.
+ case SC_AETERNA:
+ case SC_MODECHANGE:
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_BROKENWEAPON:
+ case SC_BROKENARMOR:
+ case SC_READYSTORM:
+ case SC_READYDOWN:
+ case SC_READYCOUNTER:
+ case SC_READYTURN:
+ case SC_DODGE:
+ case SC_PUSH_CART:
+ tick = -1;
+ break;
+
+ case SC_AUTOGUARD:
+ if( !(flag&1) )
+ {
+ struct map_session_data *tsd;
+ int i,t;
+ for( i = val2 = 0; i < val1; i++)
+ {
+ t = 5-(i>>1);
+ val2 += (t < 0)? 1:t;
+ }
+
+ if( bl->type&(BL_PC|BL_MER) )
+ {
+ if( sd )
+ {
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ }
+ break;
+
+ case SC_DEFENDER:
+ if (!(flag&1))
+ {
+ val2 = 5 + 15*val1; //Damage reduction
+ val3 = 0; // unused, previously speed adjustment
+ val4 = 250 - 50*val1; //Aspd adjustment
+
+ if (sd)
+ {
+ struct map_session_data *tsd;
+ int i;
+ for (i = 0; i < 5; i++)
+ { //See if there are devoted characters, and pass the status to them. [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
+ status_change_start(&tsd->bl,type,10000,val1,5+val1*5,val3,val4,tick,1);
+ }
+ }
+ }
+ break;
+
+ case SC_TENSIONRELAX:
+ if (sd) {
+ pc_setsit(sd);
+ clif_sitting(&sd->bl);
+ }
+ val2 = 12; //SP cost
+ val4 = 10000; //Decrease at 10secs intervals.
+ val3 = tick/val4;
+ tick = -1; // duration sent to the client should be infinite
+ tick_time = val4; // [GodLesZ] tick time
+ break;
+ case SC_PARRYING:
+ val2 = 20 + val1*3; //Block Chance
+ break;
+
+ case SC_WINDWALK:
+ val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5
+ break;
+
+ case SC_JOINTBEAT:
+ if( val2&BREAK_NECK )
+ sc_start(bl,SC_BLEEDING,100,val1,skill_get_time2(status_sc2skill(type),val1));
+ break;
+
+ case SC_BERSERK:
+ if (!sc->data[SC_ENDURE] || !sc->data[SC_ENDURE]->val4)
+ sc_start4(bl, SC_ENDURE, 100,10,0,0,2, tick);
+ case SC__BLOODYLUST:
+ //HP healing is performing after the calc_status call.
+ //Val2 holds HP penalty
+ 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_time = val4; // [GodLesZ] tick time
+ break;
+
+ case SC_GOSPEL:
+ if(val4 == BCT_SELF) { // self effect
+ val2 = tick/10000;
+ tick_time = 10000; // [GodLesZ] tick time
+ status_change_clear_buffs(bl,3); //Remove buffs/debuffs
+ }
+ break;
+
+ case SC_MARIONETTE:
+ {
+ int stat;
+
+ val3 = 0;
+ val4 = 0;
+ stat = ( sd ? sd->status.str : status_get_base_status(bl)->str ) / 2; val3 |= cap_value(stat,0,0xFF)<<16;
+ stat = ( sd ? sd->status.agi : status_get_base_status(bl)->agi ) / 2; val3 |= cap_value(stat,0,0xFF)<<8;
+ stat = ( sd ? sd->status.vit : status_get_base_status(bl)->vit ) / 2; val3 |= cap_value(stat,0,0xFF);
+ stat = ( sd ? sd->status.int_: status_get_base_status(bl)->int_) / 2; val4 |= cap_value(stat,0,0xFF)<<16;
+ stat = ( sd ? sd->status.dex : status_get_base_status(bl)->dex ) / 2; val4 |= cap_value(stat,0,0xFF)<<8;
+ stat = ( sd ? sd->status.luk : status_get_base_status(bl)->luk ) / 2; val4 |= cap_value(stat,0,0xFF);
+ break;
+ }
+ case SC_MARIONETTE2:
+ {
+ int stat,max_stat;
+ // fetch caster information
+ struct block_list *pbl = map_id2bl(val1);
+ struct status_change *psc = pbl?status_get_sc(pbl):NULL;
+ struct status_change_entry *psce = psc?psc->data[SC_MARIONETTE]:NULL;
+ // fetch target's stats
+ struct status_data* status = status_get_status_data(bl); // battle status
+
+ if (!psce)
+ return 0;
+
+ val3 = 0;
+ val4 = 0;
+ max_stat = battle_config.max_parameter; //Cap to 99 (default)
+ stat = (psce->val3 >>16)&0xFF; stat = min(stat, max_stat - status->str ); val3 |= cap_value(stat,0,0xFF)<<16;
+ stat = (psce->val3 >> 8)&0xFF; stat = min(stat, max_stat - status->agi ); val3 |= cap_value(stat,0,0xFF)<<8;
+ stat = (psce->val3 >> 0)&0xFF; stat = min(stat, max_stat - status->vit ); val3 |= cap_value(stat,0,0xFF);
+ stat = (psce->val4 >>16)&0xFF; stat = min(stat, max_stat - status->int_); val4 |= cap_value(stat,0,0xFF)<<16;
+ stat = (psce->val4 >> 8)&0xFF; stat = min(stat, max_stat - status->dex ); val4 |= cap_value(stat,0,0xFF)<<8;
+ stat = (psce->val4 >> 0)&0xFF; stat = min(stat, max_stat - status->luk ); val4 |= cap_value(stat,0,0xFF);
+ break;
+ }
+ case SC_REJECTSWORD:
+ val2 = 15*val1; //Reflect chance
+ val3 = 3; //Reflections
+ tick = -1;
+ break;
+
+ case SC_MEMORIZE:
+ val2 = 5; //Memorized casts.
+ tick = -1;
+ break;
+
+ case SC_GRAVITATION:
+ val2 = 50*val1; //aspd reduction
+ break;
+
+ case SC_REGENERATION:
+ if (val1 == 1)
+ val2 = 2;
+ else
+ val2 = val1; //HP Regerenation rate: 200% 200% 300%
+ val3 = val1; //SP Regeneration Rate: 100% 200% 300%
+ //if val4 comes set, this blocks regen rather than increase it.
+ break;
+
+ case SC_DEVOTION:
+ {
+ struct block_list *d_bl;
+ struct status_change *d_sc;
+
+ if( (d_bl = map_id2bl(val1)) && (d_sc = status_get_sc(d_bl)) && d_sc->count )
+ { // Inherits Status From Source
+ const enum sc_type types[] = { SC_AUTOGUARD, SC_DEFENDER, SC_REFLECTSHIELD, SC_ENDURE };
+ enum sc_type type2;
+ int i = (map_flag_gvg(bl->m) || map[bl->m].flag.battleground)?2:3;
+ while( i >= 0 )
+ {
+ type2 = types[i];
+ if( d_sc->data[type2] )
+ sc_start(bl, type2, 100, d_sc->data[type2]->val1, skill_get_time(status_sc2skill(type2),d_sc->data[type2]->val1));
+ i--;
+ }
+ }
+ break;
+ }
+
+ case SC_COMA: //Coma. Sends a char to 1HP. If val2, do not zap sp
+ if( val3 && bl->type == BL_MOB ) {
+ struct block_list* src = map_id2bl(val3);
+ if( src )
+ mob_log_damage((TBL_MOB*)bl,src,status->hp - 1);
+ }
+ status_zap(bl, status->hp-1, val2?0:status->sp);
+ return 1;
+ break;
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = val2?map_id2bl(val2):NULL;
+ struct status_change *sc2 = src?status_get_sc(src):NULL;
+ struct status_change_entry *sce2 = sc2?sc2->data[SC_CLOSECONFINE]:NULL;
+ if (src && sc2) {
+ if (!sce2) //Start lock on caster.
+ sc_start4(src,SC_CLOSECONFINE,100,val1,1,0,0,tick+1000);
+ else { //Increase count of locked enemies and refresh time.
+ (sce2->val2)++;
+ delete_timer(sce2->timer, status_change_timer);
+ sce2->timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE);
+ }
+ } else //Status failed.
+ return 0;
+ }
+ break;
+ case SC_KAITE:
+ val2 = 1+val1/5; //Number of bounces: 1 + skill_lv/5
+ break;
+ case SC_KAUPE:
+ switch (val1) {
+ case 3: //33*3 + 1 -> 100%
+ val2++;
+ case 1:
+ case 2: //33, 66%
+ val2 += 33*val1;
+ val3 = 1; //Dodge 1 attack total.
+ break;
+ default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex]
+ val2 = 100;
+ val3 = val1-2;
+ break;
+ }
+ break;
+
+ case SC_COMBO: {
+ //val1: Skill ID
+ //val2: When given, target (for autotargetting skills)
+ //val3: When set, this combo time should NOT delay attack/movement
+ //val3: TK: Last used kick
+ //val4: TK: Combo time
+ struct unit_data *ud = unit_bl2ud(bl);
+ if (ud && !val3) {
+ tick += 300 * battle_config.combo_delay_rate/100;
+ ud->attackabletime = gettick()+tick;
+ unit_set_walkdelay(bl, gettick(), tick, 1);
+ }
+ val3 = 0;
+ val4 = tick;
+ }
+ break;
+ case SC_EARTHSCROLL:
+ val2 = 11-val1; //Chance to consume: 11-skill_lv%
+ break;
+ case SC_RUN:
+ val4 = gettick(); //Store time at which you started running.
+ tick = -1;
+ break;
+ case SC_KAAHI:
+ val2 = 200*val1; //HP heal
+ val3 = 5*val1; //SP cost
+ val4 = INVALID_TIMER; //Kaahi Timer.
+ break;
+ case SC_BLESSING:
+ if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC)
+ val2 = val1;
+ else
+ val2 = 0; //0 -> Half stat.
+ break;
+ case SC_TRICKDEAD:
+ if (vd) vd->dead_sit = 1;
+ tick = -1;
+ break;
+ case SC_CONCENTRATE:
+ val2 = 2 + val1;
+ if (sd) { //Store the card-bonus data that should not count in the %
+ val3 = sd->param_bonus[1]; //Agi
+ val4 = sd->param_bonus[4]; //Dex
+ } else {
+ val3 = val4 = 0;
+ }
+ break;
+ case SC_MAXOVERTHRUST:
+ val2 = 20*val1; //Power increase
+ break;
+ case SC_OVERTHRUST:
+ //val2 holds if it was casted on self, or is bonus received from others
+ val3 = 5*val1; //Power increase
+ if(sd && pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ break;
+ case SC_ADRENALINE2:
+ case SC_ADRENALINE:
+ val3 = (val2) ? 300 : 200; // aspd increase
+ case SC_WEAPONPERFECTION:
+ if(sd && pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ break;
+ case SC_CONCENTRATION:
+ val2 = 5*val1; //Batk/Watk Increase
+ val3 = 10*val1; //Hit Increase
+ val4 = 5*val1; //Def reduction
+ break;
+ case SC_ANGELUS:
+ val2 = 5*val1; //def increase
+ break;
+ case SC_IMPOSITIO:
+ val2 = 5*val1; //watk increase
+ break;
+ case SC_MELTDOWN:
+ val2 = 100*val1; //Chance to break weapon
+ val3 = 70*val1; //Change to break armor
+ break;
+ case SC_TRUESIGHT:
+ val2 = 10*val1; //Critical increase
+ val3 = 3*val1; //Hit increase
+ break;
+ case SC_SUN_COMFORT:
+ val2 = (status_get_lv(bl) + status->dex + status->luk)/2; //def increase
+ break;
+ case SC_MOON_COMFORT:
+ val2 = (status_get_lv(bl) + status->dex + status->luk)/10; //flee increase
+ break;
+ case SC_STAR_COMFORT:
+ val2 = (status_get_lv(bl) + status->dex + status->luk); //Aspd increase
+ break;
+ case SC_QUAGMIRE:
+ val2 = (sd?5:10)*val1; //Agi/Dex decrease.
+ break;
+
+ // gs_something1 [Vicious]
+ case SC_GATLINGFEVER:
+ val2 = 20*val1; //Aspd increase
+ val3 = 20+10*val1; //Batk increase
+ val4 = 5*val1; //Flee decrease
+ break;
+
+ case SC_FLING:
+ if (bl->type == BL_PC)
+ val2 = 0; //No armor reduction to players.
+ else
+ val2 = 5*val1; //Def reduction
+ val3 = 5*val1; //Def2 reduction
+ break;
+ case SC_PROVOKE:
+ //val2 signals autoprovoke.
+ val3 = 2+3*val1; //Atk increase
+ val4 = 5+5*val1; //Def reduction.
+ break;
+ case SC_AVOID:
+ //val2 = 10*val1; //Speed change rate.
+ break;
+ case SC_DEFENCE:
+ val2 = 2*val1; //Def bonus
+ break;
+ case SC_BLOODLUST:
+ val2 = 20+10*val1; //Atk rate change.
+ val3 = 3*val1; //Leech chance
+ val4 = 20; //Leech percent
+ break;
+ case SC_FLEET:
+ val2 = 30*val1; //Aspd change
+ val3 = 5+5*val1; //bAtk/wAtk rate change
+ break;
+ case SC_MINDBREAKER:
+ val2 = 20*val1; //matk increase.
+ val3 = 12*val1; //mdef2 reduction.
+ break;
+ case SC_SKA:
+ val2 = tick/1000;
+ val3 = rnd()%100; //Def changes randomly every second...
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_JAILED:
+ //Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time.
+ tick = val1>0?1000:250;
+ if (sd)
+ {
+ if (sd->mapindex != val2)
+ {
+ int pos = (bl->x&0xFFFF)|(bl->y<<16), //Current Coordinates
+ map = sd->mapindex; //Current Map
+ //1. Place in Jail (val2 -> Jail Map, val3 -> x, val4 -> y
+ pc_setpos(sd,(unsigned short)val2,val3,val4, CLR_TELEPORT);
+ //2. Set restore point (val3 -> return map, val4 return coords
+ val3 = map;
+ val4 = pos;
+ } else if (!val3 || val3 == sd->mapindex) { //Use save point.
+ val3 = sd->status.save_point.map;
+ val4 = (sd->status.save_point.x&0xFFFF)
+ |(sd->status.save_point.y<<16);
+ }
+ }
+ break;
+ case SC_UTSUSEMI:
+ val2=(val1+1)/2; // number of hits blocked
+ val3=skill_get_blewcount(NJ_UTSUSEMI, val1); //knockback value.
+ break;
+ case SC_BUNSINJYUTSU:
+ val2=(val1+1)/2; // number of hits blocked
+ break;
+ case SC_CHANGE:
+ val2= 30*val1; //Vit increase
+ val3= 20*val1; //Int increase
+ break;
+ case SC_SWOO:
+ if(status->mode&MD_BOSS)
+ tick /= 5; //TODO: Reduce skill's duration. But for how long?
+ break;
+ case SC_SPIDERWEB:
+ if( bl->type == BL_PC )
+ tick /= 2;
+ break;
+ case SC_ARMOR:
+ //NPC_DEFENDER:
+ val2 = 80; //Damage reduction
+ //Attack requirements to be blocked:
+ val3 = BF_LONG; //Range
+ val4 = BF_WEAPON|BF_MISC; //Type
+ break;
+ case SC_ENCHANTARMS:
+ //end previous enchants
+ skill_enchant_elemental_end(bl,type);
+ //Make sure the received element is valid.
+ if (val2 >= ELE_MAX)
+ val2 = val2%ELE_MAX;
+ else if (val2 < 0)
+ val2 = rnd()%ELE_MAX;
+ break;
+ case SC_CRITICALWOUND:
+ val2 = 20*val1; //Heal effectiveness decrease
+ break;
+ case SC_MAGICMIRROR:
+ case SC_SLOWCAST:
+ val2 = 20*val1; //Magic reflection/cast rate
+ break;
+
+ case SC_ARMORCHANGE:
+ if (val2 == NPC_ANTIMAGIC)
+ { //Boost mdef
+ val2 =-20;
+ val3 = 20;
+ } else { //Boost def
+ val2 = 20;
+ val3 =-20;
+ }
+ val2*=val1; //20% per level
+ val3*=val1;
+ break;
+ case SC_EXPBOOST:
+ case SC_JEXPBOOST:
+ if (val1 < 0)
+ val1 = 0;
+ break;
+ case SC_INCFLEE2:
+ case SC_INCCRI:
+ val2 = val1*10; //Actual boost (since 100% = 1000)
+ break;
+ case SC_SUFFRAGIUM:
+ val2 = 15 * val1; //Speed cast decrease
+ break;
+ case SC_INCHEALRATE:
+ if (val1 < 1)
+ val1 = 1;
+ break;
+ case SC_HALLUCINATION:
+ val2 = 5+val1; //Factor by which displayed damage is increased by
+ break;
+ case SC_DOUBLECAST:
+ val2 = 30+10*val1; //Trigger rate
+ break;
+ case SC_KAIZEL:
+ val2 = 10*val1; //% of life to be revived with
+ break;
+ // case SC_ARMOR_ELEMENT:
+ // case SC_ARMOR_RESIST:
+ // Mod your resistance against elements:
+ // val1 = water | val2 = earth | val3 = fire | val4 = wind
+ // break;
+ //case ????:
+ //Place here SCs that have no SCB_* data, no skill associated, no ICON
+ //associated, and yet are not wrong/unknown. [Skotlex]
+ //break;
+
+ case SC_MERC_FLEEUP:
+ case SC_MERC_ATKUP:
+ case SC_MERC_HITUP:
+ val2 = 15 * val1;
+ break;
+ case SC_MERC_HPUP:
+ case SC_MERC_SPUP:
+ val2 = 5 * val1;
+ break;
+ case SC_REBIRTH:
+ val2 = 20*val1; //% of life to be revived with
+ break;
+
+ case SC_MANU_DEF:
+ case SC_MANU_ATK:
+ case SC_MANU_MATK:
+ val2 = 1; // Manuk group
+ break;
+ case SC_SPL_DEF:
+ case SC_SPL_ATK:
+ 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:
+ status_change_start(bl,SC_BLIND,10000,val1,0,0,0,30000,11); // Blind status that last for 30 seconds
+ val4 = tick / 3000;
+ tick_time = 3000; // [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 = 90 + val1 * 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, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ break;
+ case SC_FREEZING:
+ status_change_end(bl, SC_BURNING, INVALID_TIMER);
+ break;
+ case SC_READING_SB:
+ // val2 = sp reduction per second
+ tick_time = 5000; // [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:
+ case SC_MEIKYOUSISUI:
+ val4 = tick / 1000;
+ if( val4 < 1 )
+ val4 = 1;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_CAMOUFLAGE:
+ val4 = tick/1000;
+ 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) ) pc_setriding(sd, 0);
+ if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
+ if( pc_iswug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUG);
+ if( pc_isridingwug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUGRIDER);
+ 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;
+ // bypasses coating protection and MADO
+ sc_start(bl,SC_STRIPWEAPON,100,val1,tick);
+ sc_start(bl,SC_STRIPSHIELD,100,val1,tick);
+ break;
+ 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, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER);
+ break;
+ case SC_STRIKING:
+ val1 = 6 - val1;//spcost = 6 - level (lvl1:5 ... lvl 5: 1)
+ 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_VACUUM_EXTREME:
+ tick -= (status->str / 20) * 1000;
+ val4 = val3 = tick / 100;
+ tick_time = 100; // [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 = 5 + 5 * 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 = 20 + 5 * val1; // Flee reduction.
+ val3 = 15 + 5 * val1; // ASPD reduction.
+ if( sd && rand()%100 < val1 ){ // (Skill Lv) %
+ val4 = 1; // reduce walk speed by half.
+ if( pc_isriding(sd) ) pc_setriding(sd, 0);
+ if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
+ }
+ break;
+ case SC_GLOOMYDAY_SK:
+ // Random number between [15 ~ (Voice Lesson Skill Level x 5) + (Skill Level x 10)] %.
+ val2 = 15 + rand()%( (sd?pc_checkskill(sd, WM_LESSON)*5:0) + val1*10 );
+ 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 = -1; //endless duration in the client
+ tick_time = 6000; // [GodLesZ] tick time
+ val_flag |= 1|2|4;
+ break;
+ case SC_EXEEDBREAK:
+ val1 *= 150; // 150 * skill_lv
+ if( sd && sd->inventory_data[sd->equip_index[EQI_HAND_R]] ) { // Chars.
+ val1 += (sd->inventory_data[sd->equip_index[EQI_HAND_R]]->weight/10 * sd->inventory_data[sd->equip_index[EQI_HAND_R]]->wlv * status_get_lv(bl) / 100);
+ val1 += 15 * (sd ? sd->status.job_level:50) + 100;
+ }
+ 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: // [(Job Level / 2) + (40 + 5 * Skill Level)] %
+ val1 = (sd?sd->status.job_level:2)/2 + 40 + 5 * val1;
+ val_flag |= 1;
+ break;
+ case SC_RAISINGDRAGON:
+ val3 = tick / 5000;
+ tick_time = 5000; // [GodLesZ] tick time
+ break;
+ case SC_GT_CHANGE:
+ {// take note there is no def increase as skill desc says. [malufett]
+ struct block_list * src;
+ val3 = status->agi * val1 / 60; // ASPD increase: [(Target AGI x Skill Level) / 60] %
+ if( (src = map_id2bl(val2)) )
+ val4 = ( 200/status_get_int(src) ) * val1;// MDEF decrease: MDEF [(200 / Caster INT) x Skill Level]
+ }
+ break;
+ case SC_GT_REVITALIZE:
+ {// take note there is no vit,aspd,speed increase as skill desc says. [malufett]
+ struct block_list * src;
+ val3 = val1 * 30 + 150; // Natural HP recovery increase: [(Skill Level x 30) + 50] %
+ if( (src = map_id2bl(val2)) ) // the stat def is not shown in the status window and it is process differently
+ val4 = ( status_get_vit(src)/4 ) * val1; // STAT DEF increase: [(Caster VIT / 4) x Skill Level]
+ }
+ break;
+ case SC_PYROTECHNIC_OPTION:
+ 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;
+ 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:
+ val_flag |= 1|2;
+ break;
+ case SC_WIND_STEP_OPTION:
+ val2 = 50; // % Increase speed and flee.
+ break;
+ case SC_BLAST_OPTION:
+ val2 = 20;
+ val3 = ELE_WIND;
+ 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 = 50;
+ 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;
+ case SC_KYOUGAKU:
+ val2 = 2*val1 + rand()%val1;
+ clif_status_change(bl,SI_ACTIVE_MONSTER_TRANSFORM,1,0,1002,0,0);
+ break;
+ case SC_KAGEMUSYA:
+ val3 = val1 * 2;
+ case SC_IZAYOI:
+ val2 = tick/1000;
+ tick_time = 1000;
+ break;
+ case SC_ZANGETSU:
+ if( (status_get_hp(bl)+status_get_sp(bl)) % 2 == 0)
+ val2 = status_get_lv(bl) / 2 + 50;
+ else
+ val2 -= 50;
+ break;
+ case SC_GENSOU:
+ {
+ int hp = status_get_hp(bl), lv = 5;
+ short per = 100 / (status_get_max_hp(bl) / hp);
+
+ if( per <= 15 )
+ lv = 1;
+ else if( per <= 30 )
+ lv = 2;
+ else if( per <= 50 )
+ lv = 3;
+ else if( per <= 75 )
+ lv = 4;
+ if( hp % 2 == 0)
+ status_heal(bl, hp * (6-lv) * 4 / 100, status_get_sp(bl) * (6-lv) * 3 / 100, 1);
+ else
+ status_zap(bl, hp * (lv*4) / 100, status_get_sp(bl) * (lv*3) / 100);
+ }
+ break;
+ case SC_ANGRIFFS_MODUS:
+ val2 = 50 + 20 * val1; //atk bonus
+ val3 = 40 + 20 * val1; // Flee reduction.
+ val4 = tick/1000; // hp/sp reduction timer
+ tick_time = 1000;
+ break;
+ case SC_GOLDENE_FERSE:
+ val2 = 10 + 10*val1; //max hp bonus
+ val3 = 6 + 4 * val1; // Aspd Bonus
+ val4 = 2 + 2 * val1; // Chance of holy attack
+ break;
+ case SC_OVERED_BOOST:
+ val2 = 300 + 40*val1; //flee bonus
+ val3 = 179 + 2*val1; //aspd bonus
+ break;
+ case SC_GRANITIC_ARMOR:
+ val2 = 2*val1; //dmg reduction
+ val3 = 6*val1; //dmg on status end
+ break;
+ case SC_MAGMA_FLOW:
+ val2 = 3*val1; //activation chance
+ break;
+ case SC_PYROCLASTIC:
+ val2 += 10*val1; //atk bonus
+ break;
+ case SC_PARALYSIS: //[Lighta] need real info
+ val2 = 2*val1; //def reduction
+ val3 = 500*val1; //varcast augmentation
+ break;
+ case SC_PAIN_KILLER: //[Lighta] need real info
+ val2 = 2*val1; //aspd reduction %
+ val3 = 2*val1; //dmg reduction %
+ if(sc->data[SC_PARALYSIS])
+ sc_start(bl, SC_ENDURE, 100, val1, tick); //start endure for same duration
+ break;
+ case SC_STYLE_CHANGE: //[Lighta] need real info
+ tick = -1;
+ if(val2 == MH_MD_FIGHTING) val2 = MH_MD_GRAPPLING;
+ else val2 = MH_MD_FIGHTING;
+ break;
+ default:
+ if( calc_flag == SCB_NONE && StatusSkillChangeTable[type] == 0 && StatusIconChangeTable[type] == 0 )
+ { //Status change with no calc, no icon, and no skill associated...?
+ ShowError("UnknownStatusChange [%d]\n", type);
+ return 0;
+ }
+ }
+ else //Special considerations when loading SC data.
+ switch( type )
+ {
+ case SC_WEDDING:
+ case SC_XMAS:
+ case SC_SUMMER:
+ clif_changelook(bl,LOOK_WEAPON,0);
+ clif_changelook(bl,LOOK_SHIELD,0);
+ clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER);
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,val4);
+ break;
+ case SC_KAAHI:
+ val4 = INVALID_TIMER;
+ break;
+ }
+
+ //Those that make you stop attacking/walking....
+ switch (type) {
+ case SC_FREEZE:
+ case SC_STUN:
+ case SC_SLEEP:
+ case SC_STONE:
+ case SC_DEEPSLEEP:
+ if (sd && pc_issit(sd)) //Avoid sprite sync problems.
+ pc_setstand(sd);
+ case SC_TRICKDEAD:
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ // Cancel cast when get status [LuzZza]
+ if (battle_config.sc_castcancel&bl->type)
+ unit_skillcastcancel(bl, 0);
+ case SC_WHITEIMPRISON:
+ unit_stop_attack(bl);
+ case SC_STOP:
+ case SC_CONFUSION:
+ case SC_CLOSECONFINE:
+ case SC_CLOSECONFINE2:
+ case SC_ANKLE:
+ case SC_SPIDERWEB:
+ case SC_ELECTRICSHOCKER:
+ case SC_BITE:
+ case SC_THORNSTRAP:
+ case SC__MANHOLE:
+ case SC_CRYSTALIZE:
+ case SC_CURSEDCIRCLE_ATKER:
+ case SC_CURSEDCIRCLE_TARGET:
+ case SC_FEAR:
+ case SC_NETHERWORLD:
+ case SC_MEIKYOUSISUI:
+ case SC_KYOUGAKU:
+ case SC_PARALYSIS:
+ unit_stop_walking(bl,1);
+ break;
+ case SC_HIDING:
+ case SC_CLOAKING:
+ case SC_CLOAKINGEXCEED:
+ case SC_CHASEWALK:
+ case SC_WEIGHT90:
+ case SC_CAMOUFLAGE:
+ case SC_VOICEOFSIREN:
+ unit_stop_attack(bl);
+ break;
+ case SC_SILENCE:
+ if (battle_config.sc_castcancel&bl->type)
+ unit_skillcastcancel(bl, 0);
+ break;
+ }
+
+ // Set option as needed.
+ opt_flag = 1;
+ 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_DEEPSLEEP: opt_flag = 0;
+ 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;
+ case SC_CRYSTALIZE: sc->opt1 = OPT1_CRYSTALIZE; break;
+ //OPT2
+ case SC_POISON: sc->opt2 |= OPT2_POISON; break;
+ case SC_CURSE: sc->opt2 |= OPT2_CURSE; break;
+ case SC_SILENCE: sc->opt2 |= OPT2_SILENCE; break;
+
+ case SC_SIGNUMCRUCIS:
+ sc->opt2 |= OPT2_SIGNUMCRUCIS;
+ break;
+
+ case SC_BLIND: sc->opt2 |= OPT2_BLIND; break;
+ case SC_ANGELUS: sc->opt2 |= OPT2_ANGELUS; break;
+ case SC_BLEEDING: sc->opt2 |= OPT2_BLEEDING; break;
+ case SC_DPOISON: sc->opt2 |= OPT2_DPOISON; break;
+ //OPT3
+ case SC_TWOHANDQUICKEN:
+ case SC_ONEHAND:
+ case SC_SPEARQUICKEN:
+ case SC_CONCENTRATION:
+ case SC_MERC_QUICKEN:
+ sc->opt3 |= OPT3_QUICKEN;
+ opt_flag = 0;
+ break;
+ case SC_MAXOVERTHRUST:
+ case SC_OVERTHRUST:
+ case SC_SWOO: //Why does it shares the same opt as Overthrust? Perhaps we'll never know...
+ sc->opt3 |= OPT3_OVERTHRUST;
+ opt_flag = 0;
+ break;
+ case SC_ENERGYCOAT:
+ case SC_SKE:
+ sc->opt3 |= OPT3_ENERGYCOAT;
+ opt_flag = 0;
+ break;
+ case SC_INCATKRATE:
+ //Simulate Explosion Spirits effect for NPC_POWERUP [Skotlex]
+ if (bl->type != BL_MOB) {
+ opt_flag = 0;
+ break;
+ }
+ case SC_EXPLOSIONSPIRITS:
+ sc->opt3 |= OPT3_EXPLOSIONSPIRITS;
+ opt_flag = 0;
+ break;
+ case SC_STEELBODY:
+ case SC_SKA:
+ sc->opt3 |= OPT3_STEELBODY;
+ opt_flag = 0;
+ break;
+ case SC_BLADESTOP:
+ sc->opt3 |= OPT3_BLADESTOP;
+ opt_flag = 0;
+ break;
+ case SC_AURABLADE:
+ sc->opt3 |= OPT3_AURABLADE;
+ opt_flag = 0;
+ break;
+ case SC_BERSERK:
+ opt_flag = 0;
+// case SC__BLOODYLUST:
+ sc->opt3 |= OPT3_BERSERK;
+ break;
+// case ???: // doesn't seem to do anything
+// sc->opt3 |= OPT3_LIGHTBLADE;
+// opt_flag = 0;
+// break;
+ case SC_DANCING:
+ if ((val1&0xFFFF) == CG_MOONLIT)
+ sc->opt3 |= OPT3_MOONLIT;
+ opt_flag = 0;
+ break;
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2:
+ sc->opt3 |= OPT3_MARIONETTE;
+ opt_flag = 0;
+ break;
+ case SC_ASSUMPTIO:
+ sc->opt3 |= OPT3_ASSUMPTIO;
+ opt_flag = 0;
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ sc->opt3 |= OPT3_WARM;
+ opt_flag = 0;
+ break;
+ case SC_KAITE:
+ sc->opt3 |= OPT3_KAITE;
+ opt_flag = 0;
+ break;
+ case SC_BUNSINJYUTSU:
+ sc->opt3 |= OPT3_BUNSIN;
+ opt_flag = 0;
+ break;
+ case SC_SPIRIT:
+ sc->opt3 |= OPT3_SOULLINK;
+ opt_flag = 0;
+ break;
+ case SC_CHANGEUNDEAD:
+ sc->opt3 |= OPT3_UNDEAD;
+ opt_flag = 0;
+ break;
+// case ???: // from DA_CONTRACT (looks like biolab mobs aura)
+// sc->opt3 |= OPT3_CONTRACT;
+// opt_flag = 0;
+// break;
+ //OPTION
+ case SC_HIDING:
+ sc->option |= OPTION_HIDE;
+ opt_flag = 2;
+ break;
+ case SC_CLOAKING:
+ case SC_CLOAKINGEXCEED:
+ case SC__INVISIBILITY:
+ sc->option |= OPTION_CLOAK;
+ opt_flag = 2;
+ break;
+ case SC_CHASEWALK:
+ sc->option |= OPTION_CHASEWALK|OPTION_CLOAK;
+ opt_flag = 2;
+ break;
+ case SC_SIGHT:
+ sc->option |= OPTION_SIGHT;
+ break;
+ case SC_RUWACH:
+ sc->option |= OPTION_RUWACH;
+ break;
+ case SC_WEDDING:
+ sc->option |= OPTION_WEDDING;
+ break;
+ case SC_XMAS:
+ sc->option |= OPTION_XMAS;
+ break;
+ case SC_SUMMER:
+ sc->option |= OPTION_SUMMER;
+ break;
+ case SC_ORCISH:
+ sc->option |= OPTION_ORCISH;
+ break;
+ case SC_FUSION:
+ sc->option |= OPTION_FLYING;
+ break;
+ default:
+ opt_flag = 0;
+ }
+
+ //On Aegis, when turning on a status change, first goes the option packet, then the sc packet.
+ if(opt_flag)
+ clif_changeoption(bl);
+
+ if (calc_flag&SCB_DYE)
+ { //Reset DYE color
+ if (vd && vd->cloth_color)
+ {
+ val4 = vd->cloth_color;
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,0);
+ }
+ calc_flag&=~SCB_DYE;
+ }
+
+ clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0);
+
+ /**
+ * 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])) {// reuse old sc
+ if( sce->timer != INVALID_TIMER )
+ delete_timer(sce->timer, status_change_timer);
+ sc_isnew = false;
+ } else {// new sc
+ ++(sc->count);
+ sce = sc->data[type] = ers_alloc(sc_data_ers, struct status_change_entry);
+ }
+ sce->val1 = val1;
+ sce->val2 = val2;
+ sce->val3 = val3;
+ sce->val4 = val4;
+ if (tick >= 0)
+ sce->timer = add_timer(gettick() + tick, status_change_timer, bl->id, type);
+ else
+ sce->timer = INVALID_TIMER; //Infinite duration
+
+ if (calc_flag)
+ status_calc_bl(bl,calc_flag);
+
+ if ( sc_isnew && StatusChangeStateTable[type] ) /* non-zero */
+ status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],true);
+
+
+ if(sd && sd->pd)
+ pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing
+
+ switch (type) {
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ if (!(sce->val2)) { //don't heal if already set
+ status_heal(bl, status->max_hp, 0, 1); //Do not use percent_heal as this healing must override BERSERK's block.
+ status_set_sp(bl, 0, 0); //Damage all SP
+ }
+ sce->val2 = 5 * status->max_hp / 100;
+ break;
+ case SC_CHANGE:
+ status_percent_heal(bl, 100, 100);
+ break;
+ case SC_RUN:
+ {
+ struct unit_data *ud = unit_bl2ud(bl);
+ if( ud )
+ ud->state.running = unit_run(bl);
+ }
+ break;
+ case SC_BOSSMAPINFO:
+ clif_bossmapinfo(sd->fd, map_id2boss(sce->val1), 0); // First Message
+ break;
+ case SC_MERC_HPUP:
+ status_percent_heal(bl, 100, 0); // Recover Full HP
+ break;
+ 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:
+ clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1);
+ break;
+ case TK_DOWNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1);
+ break;
+ case TK_TURNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYTURN,1,1);
+ break;
+ case TK_COUNTER:
+ clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1);
+ break;
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ if (sd)
+ clif_skillinfo(sd,MO_EXTREMITYFIST, INF_SELF_SKILL);
+ break;
+ case TK_JUMPKICK:
+ if (sd)
+ clif_skillinfo(sd,TK_JUMPKICK, INF_SELF_SKILL);
+ break;
+ case MO_TRIPLEATTACK:
+ if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+ clif_skillinfo(sd,SR_DRAGONCOMBO, INF_SELF_SKILL);
+ break;
+ case SR_FALLENEMPIRE:
+ if (sd){
+ clif_skillinfo(sd,SR_GATEOFHELL, INF_SELF_SKILL);
+ clif_skillinfo(sd,SR_TIGERCANNON, INF_SELF_SKILL);
+ }
+ break;
+ }
+ break;
+ case SC_RAISINGDRAGON:
+ sce->val2 = status->max_hp / 100;// Officially tested its 1%hp drain. [Jobbie]
+ break;
+ }
+
+ if( opt_flag&2 && sd && sd->touching_id )
+ npc_touchnext_areanpc(sd,false); // run OnTouch_ on next char in range
+
+ return 1;
+}
+
+/*==========================================
+ * Ending all status except those listed.
+ * @TODO maybe usefull for dispel instead reseting a liste there.
+ * type:
+ * 0 - PC killed -> Place here statuses that do not dispel on death.
+ * 1 - If for some reason status_change_end decides to still keep the status when quitting.
+ * 2 - Do clif
+ * 3 - Do not remove some permanent/time-independent effects
+ *------------------------------------------*/
+int status_change_clear(struct block_list* bl, int type)
+{
+ struct status_change* sc;
+ int i;
+
+ sc = status_get_sc(bl);
+
+ if (!sc || !sc->count)
+ return 0;
+
+ for(i = 0; i < SC_MAX; i++)
+ {
+ if(!sc->data[i])
+ continue;
+
+ if(type == 0)
+ switch (i)
+ { //Type 0: PC killed -> Place here statuses that do not dispel on death.
+ case SC_ELEMENTALCHANGE://Only when its Holy or Dark that it doesn't dispell on death
+ if( sc->data[i]->val2 != ELE_HOLY && sc->data[i]->val2 != ELE_DARK )
+ break;
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_EDP:
+ case SC_MELTDOWN:
+ case SC_XMAS:
+ case SC_SUMMER:
+ case SC_NOCHAT:
+ case SC_FUSION:
+ case SC_EARTHSCROLL:
+ case SC_READYSTORM:
+ case SC_READYDOWN:
+ case SC_READYCOUNTER:
+ case SC_READYTURN:
+ case SC_DODGE:
+ case SC_JAILED:
+ case SC_EXPBOOST:
+ case SC_ITEMBOOST:
+ case SC_HELLPOWER:
+ case SC_JEXPBOOST:
+ case SC_AUTOTRADE:
+ 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_DEF_RATE:
+ case SC_MDEF_RATE:
+ case SC_INCHEALRATE:
+ case SC_INCFLEE2:
+ case SC_INCHIT:
+ case SC_ATKPOTION:
+ case SC_MATKPOTION:
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ case SC_PUSH_CART:
+ continue;
+
+ }
+
+ if( type == 3 )
+ {
+ switch (i)
+ {// TODO: This list may be incomplete
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_NOCHAT:
+ case SC_PUSH_CART:
+ continue;
+ }
+ }
+
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+
+ if( type == 1 && sc->data[i] )
+ { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex]
+ (sc->count)--;
+ if (sc->data[i]->timer != INVALID_TIMER)
+ delete_timer(sc->data[i]->timer, status_change_timer);
+ ers_free(sc_data_ers, sc->data[i]);
+ sc->data[i] = NULL;
+ }
+ }
+
+ sc->opt1 = 0;
+ sc->opt2 = 0;
+ sc->opt3 = 0;
+ sc->option &= OPTION_MASK;
+
+ if( type == 0 || type == 2 )
+ clif_changeoption(bl);
+
+ return 1;
+}
+
+/*==========================================
+ * Special condition we want to effectuate, check before ending a status.
+ *------------------------------------------*/
+int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const char* file, int line)
+{
+ struct map_session_data *sd;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ struct status_data *status;
+ struct view_data *vd;
+ int opt_flag=0, calc_flag;
+
+ nullpo_ret(bl);
+
+ sc = status_get_sc(bl);
+ status = status_get_status_data(bl);
+
+ if(type < 0 || type >= SC_MAX || !sc || !(sce = sc->data[type]))
+ return 0;
+
+ sd = BL_CAST(BL_PC,bl);
+
+ if (sce->timer != tid && tid != INVALID_TIMER)
+ return 0;
+
+ if (tid == INVALID_TIMER) {
+ 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);
+ if (sc->opt1)
+ switch (type) {
+ //"Ugly workaround" [Skotlex]
+ //delays status change ending so that a skill that sets opt1 fails to
+ //trigger when it also removed one
+ case SC_STONE:
+ sce->val3 = 0; //Petrify time counter.
+ case SC_FREEZE:
+ case SC_STUN:
+ case SC_SLEEP:
+ if (sce->val1) {
+ //Removing the 'level' shouldn't affect anything in the code
+ //since these SC are not affected by it, and it lets us know
+ //if we have already delayed this attack or not.
+ sce->val1 = 0;
+ sce->timer = add_timer(gettick()+10, status_change_timer, bl->id, type);
+ return 1;
+ }
+ }
+ }
+
+ (sc->count)--;
+
+ if ( StatusChangeStateTable[type] )
+ status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],false);
+
+ sc->data[type] = NULL;
+
+ vd = status_get_viewdata(bl);
+ calc_flag = StatusChangeFlagTable[type];
+ switch(type){
+ case SC_GRANITIC_ARMOR:{
+ int dammage = status->max_hp*sce->val3/100;
+ if(status->hp < dammage) //to not kill him
+ dammage = status->hp-1;
+ status_damage(NULL, bl, dammage,0,0,1);
+ break;
+ }
+ case SC_PYROCLASTIC:
+ if(bl->type == BL_PC)
+ skill_break_equip(bl,EQP_WEAPON,10000,BCT_SELF);
+ break;
+ case SC_WEDDING:
+ case SC_XMAS:
+ case SC_SUMMER:
+ if (!vd) break;
+ if (sd)
+ { //Load data from sd->status.* as the stored values could have changed.
+ //Must remove OPTION to prevent class being rechanged.
+ sc->option &= type==SC_WEDDING?~OPTION_WEDDING:type==SC_XMAS?~OPTION_XMAS:~OPTION_SUMMER;
+ clif_changeoption(&sd->bl);
+ status_set_viewdata(bl, sd->status.class_);
+ } else {
+ vd->class_ = sce->val1;
+ vd->weapon = sce->val2;
+ vd->shield = sce->val3;
+ vd->cloth_color = sce->val4;
+ }
+ clif_changelook(bl,LOOK_BASE,vd->class_);
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
+ clif_changelook(bl,LOOK_WEAPON,vd->weapon);
+ clif_changelook(bl,LOOK_SHIELD,vd->shield);
+ if(sd) clif_skillinfoblock(sd);
+ break;
+ case SC_RUN:
+ {
+ struct unit_data *ud = unit_bl2ud(bl);
+ bool begin_spurt = true;
+ if (ud) {
+ if(!ud->state.running)
+ begin_spurt = false;
+ ud->state.running = 0;
+ if (ud->walktimer != INVALID_TIMER)
+ unit_stop_walking(bl,1);
+ }
+ if (begin_spurt && sce->val1 >= 7 &&
+ DIFF_TICK(gettick(), sce->val4) <= 1000 &&
+ (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0))
+ )
+ sc_start(bl,SC_SPURT,100,sce->val1,skill_get_time2(status_sc2skill(type), sce->val1));
+ }
+ break;
+ case SC_AUTOBERSERK:
+ if (sc->data[SC_PROVOKE] && sc->data[SC_PROVOKE]->val2 == 1)
+ status_change_end(bl, SC_PROVOKE, INVALID_TIMER);
+ break;
+
+ case SC_ENDURE:
+ case SC_DEFENDER:
+ case SC_REFLECTSHIELD:
+ case SC_AUTOGUARD:
+ {
+ struct map_session_data *tsd;
+ if( bl->type == BL_PC )
+ { // Clear Status from others
+ int i;
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type] )
+ status_change_end(&tsd->bl, type, INVALID_TIMER);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag )
+ { // Clear Status from Master
+ tsd = ((TBL_MER*)bl)->master;
+ if( tsd && tsd->sc.data[type] )
+ status_change_end(&tsd->bl, type, INVALID_TIMER);
+ }
+ }
+ break;
+ case SC_DEVOTION:
+ {
+ struct block_list *d_bl = map_id2bl(sce->val1);
+ if( d_bl )
+ {
+ if( d_bl->type == BL_PC )
+ ((TBL_PC*)d_bl)->devotion[sce->val2] = 0;
+ else if( d_bl->type == BL_MER )
+ ((TBL_MER*)d_bl)->devotion_flag = 0;
+ clif_devotion(d_bl, NULL);
+ }
+
+ status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER);
+ status_change_end(bl, SC_DEFENDER, INVALID_TIMER);
+ status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER);
+ status_change_end(bl, SC_ENDURE, INVALID_TIMER);
+ }
+ break;
+
+ case SC_BLADESTOP:
+ if(sce->val4)
+ {
+ int tid = sce->val4;
+ struct block_list *tbl = map_id2bl(tid);
+ struct status_change *tsc = status_get_sc(tbl);
+ sce->val4 = 0;
+ if(tbl && tsc && tsc->data[SC_BLADESTOP])
+ {
+ tsc->data[SC_BLADESTOP]->val4 = 0;
+ status_change_end(tbl, SC_BLADESTOP, INVALID_TIMER);
+ }
+ clif_bladestop(bl, tid, 0);
+ }
+ break;
+ case SC_DANCING:
+ {
+ const char* prevfile = "<unknown>";
+ int prevline = 0;
+ struct map_session_data *dsd;
+ struct status_change_entry *dsc;
+ struct skill_unit_group *group;
+
+ if( sd )
+ {
+ if( sd->delunit_prevfile )
+ {// initially this is NULL, when a character logs in
+ prevfile = sd->delunit_prevfile;
+ prevline = sd->delunit_prevline;
+ }
+ else
+ {
+ prevfile = "<none>";
+ }
+ sd->delunit_prevfile = file;
+ sd->delunit_prevline = line;
+ }
+
+ if(sce->val4 && sce->val4 != BCT_SELF && (dsd=map_id2sd(sce->val4)))
+ {// end status on partner as well
+ dsc = dsd->sc.data[SC_DANCING];
+ if(dsc) {
+
+ //This will prevent recursive loops.
+ dsc->val2 = dsc->val4 = 0;
+
+ status_change_end(&dsd->bl, SC_DANCING, INVALID_TIMER);
+ }
+ }
+
+ if(sce->val2)
+ {// erase associated land skill
+ group = skill_id2group(sce->val2);
+
+ if( group == NULL )
+ {
+ ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d, prev=%s:%d, from=%s:%d). Please report this! (#3504)\n",
+ sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid,
+ sd ? sd->status.char_id : 0,
+ mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y,
+ prevfile, prevline,
+ file, line);
+ }
+
+ sce->val2 = 0;
+ skill_delunitgroup(group);
+ }
+
+ if((sce->val1&0xFFFF) == CG_MOONLIT)
+ clif_status_change(bl,SI_MOONLIT,0,0,0,0,0);
+
+ status_change_end(bl, SC_LONGING, INVALID_TIMER);
+ }
+ break;
+ case SC_NOCHAT:
+ if (sd && sd->status.manner < 0 && tid != INVALID_TIMER)
+ sd->status.manner = 0;
+ if (sd && tid == INVALID_TIMER)
+ {
+ clif_changestatus(sd,SP_MANNER,sd->status.manner);
+ clif_updatestatus(sd,SP_MANNER);
+ }
+ break;
+ case SC_SPLASHER:
+ {
+ struct block_list *src=map_id2bl(sce->val3);
+ if(src && tid != INVALID_TIMER)
+ skill_castend_damage_id(src, bl, sce->val2, sce->val1, gettick(), SD_LEVEL );
+ }
+ break;
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = sce->val2?map_id2bl(sce->val2):NULL;
+ struct status_change *sc2 = src?status_get_sc(src):NULL;
+ if (src && sc2 && sc2->data[SC_CLOSECONFINE]) {
+ //If status was already ended, do nothing.
+ //Decrease count
+ if (--(sc2->data[SC_CLOSECONFINE]->val1) <= 0) //No more holds, free him up.
+ status_change_end(src, SC_CLOSECONFINE, INVALID_TIMER);
+ }
+ }
+ case SC_CLOSECONFINE:
+ if (sce->val2 > 0) {
+ //Caster has been unlocked... nearby chars need to be unlocked.
+ int range = 1
+ +skill_get_range2(bl, status_sc2skill(type), sce->val1)
+ +skill_get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold....
+ map_foreachinarea(status_change_timer_sub,
+ bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sce,type,gettick());
+ }
+ break;
+ case SC_COMBO:
+ if( sd )
+ switch (sce->val1) {
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ clif_skillinfo(sd, MO_EXTREMITYFIST, 0);
+ break;
+ case TK_JUMPKICK:
+ clif_skillinfo(sd, TK_JUMPKICK, 0);
+ break;
+ case MO_TRIPLEATTACK:
+ if (pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+ clif_skillinfo(sd, SR_DRAGONCOMBO, 0);
+ break;
+ case SR_FALLENEMPIRE:
+ clif_skillinfo(sd, SR_GATEOFHELL, 0);
+ clif_skillinfo(sd, SR_TIGERCANNON, 0);
+ break;
+ }
+ break;
+
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2: /// Marionette target
+ if (sce->val1)
+ { // check for partner and end their marionette status as well
+ enum sc_type type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE;
+ struct block_list *pbl = map_id2bl(sce->val1);
+ struct status_change* sc2 = pbl?status_get_sc(pbl):NULL;
+
+ if (sc2 && sc2->data[type2])
+ {
+ sc2->data[type2]->val1 = 0;
+ status_change_end(pbl, type2, INVALID_TIMER);
+ }
+ }
+ break;
+
+ case SC_BERSERK:
+ case SC_SATURDAYNIGHTFEVER:
+ //If val2 is removed, no HP penalty (dispelled?) [Skotlex]
+ if (status->hp > 100 && sce->val2)
+ status_set_hp(bl, 100, 0);
+ if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2)
+ {
+ sc->data[SC_ENDURE]->val4 = 0;
+ status_change_end(bl, SC_ENDURE, INVALID_TIMER);
+ }
+ case SC__BLOODYLUST:
+ sc_start4(bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill_get_time(LK_BERSERK, sce->val1));
+ if( type == SC_SATURDAYNIGHTFEVER ) //Sit down force of Saturday Night Fever has the duration of only 3 seconds.
+ sc_start(bl,SC_SITDOWN_FORCE,100,sce->val1,skill_get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1));
+ break;
+ case SC_GOSPEL:
+ if (sce->val3) { //Clear the group.
+ struct skill_unit_group* group = skill_id2group(sce->val3);
+ sce->val3 = 0;
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_HERMODE:
+ if(sce->val3 == BCT_SELF)
+ skill_clear_unitgroup(bl);
+ break;
+ case SC_BASILICA: //Clear the skill area. [Skotlex]
+ skill_clear_unitgroup(bl);
+ break;
+ case SC_TRICKDEAD:
+ if (vd) vd->dead_sit = 0;
+ break;
+ case SC_WARM:
+ case SC__MANHOLE:
+ if (sce->val4) { //Clear the group.
+ struct skill_unit_group* group = skill_id2group(sce->val4);
+ sce->val4 = 0;
+ if( group ) /* might have been cleared before status ended, e.g. land protector */
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_KAAHI:
+ //Delete timer if it exists.
+ if (sce->val4 != INVALID_TIMER)
+ delete_timer(sce->val4,kaahi_heal_timer);
+ break;
+ case SC_JAILED:
+ if(tid == INVALID_TIMER)
+ break;
+ //natural expiration.
+ if(sd && sd->mapindex == sce->val2)
+ pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT);
+ break; //guess hes not in jail :P
+ case SC_CHANGE:
+ if (tid == INVALID_TIMER)
+ break;
+ // "lose almost all their HP and SP" on natural expiration.
+ status_set_hp(bl, 10, 0);
+ status_set_sp(bl, 10, 0);
+ break;
+ case SC_AUTOTRADE:
+ if (tid == INVALID_TIMER)
+ break;
+ // Note: vending/buying is closed by unit_remove_map, no
+ // need to do it here.
+ map_quit(sd);
+ // Because map_quit calls status_change_end with tid -1
+ // from here it's not neccesary to continue
+ return 1;
+ break;
+ case SC_STOP:
+ if( sce->val2 )
+ {
+ struct block_list* tbl = map_id2bl(sce->val2);
+ sce->val2 = 0;
+ if( tbl && (sc = status_get_sc(tbl)) && sc->data[SC_STOP] && sc->data[SC_STOP]->val2 == bl->id )
+ status_change_end(tbl, SC_STOP, INVALID_TIMER);
+ }
+ break;
+ /**
+ * 3rd Stuff
+ **/
+ case SC_MILLENNIUMSHIELD:
+ 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:
+ {
+ struct block_list* src = map_id2bl(sce->val2);
+ if( tid == -1 || !src)
+ break; // Terminated by Damage
+ status_fix_damage(src,bl,400*sce->val1,clif_damage(bl,bl,gettick(),0,0,400*sce->val1,0,0,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, INVALID_TIMER);
+ 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);
+ }
+ break;
+ case SC_NEUTRALBARRIER_MASTER:
+ case SC_STEALTHFIELD_MASTER:
+ if( sce->val2 ) {
+ struct skill_unit_group* group = skill_id2group(sce->val2);
+ sce->val2 = 0;
+ if( group ) /* might have been cleared before status ended, e.g. land protector */
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_BANDING:
+ if(sce->val4) {
+ struct skill_unit_group *group = skill_id2group(sce->val4);
+ sce->val4 = 0;
+ if( group ) /* might have been cleared before status ended, e.g. land protector */
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_CURSEDCIRCLE_ATKER:
+ if( sce->val2 ) // used the default area size cause there is a chance the caster could knock back and can't clear the target.
+ map_foreachinrange(status_change_timer_sub, bl, battle_config.area_size,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, INVALID_TIMER);
+ while( i > 0 ) {
+ pc_addspiritball(sd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), 5);
+ --i;
+ }
+ }
+ break;
+ case SC_CURSEDCIRCLE_TARGET:
+ {
+ struct block_list *src = map_id2bl(sce->val2);
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] && --(sc->data[SC_CURSEDCIRCLE_ATKER]->val2) == 0 ){
+ status_change_end(src, SC_CURSEDCIRCLE_ATKER, INVALID_TIMER);
+ clif_bladestop(bl, sce->val2, 0);
+ }
+ }
+ break;
+ case SC_BLOODSUCKER:
+ if( sce->val2 ){
+ struct block_list *src = map_id2bl(sce->val2);
+ if(src){
+ struct status_change *sc = status_get_sc(src);
+ sc->bs_counter--;
+ }
+ }
+ break;
+ case SC_VACUUM_EXTREME:
+ if(sc && sc->cant.move > 0) sc->cant.move--;
+ break;
+ case SC_KYOUGAKU:
+ clif_status_load(bl, SI_KYOUGAKU, 0); // Avoid client crash
+ clif_status_load(bl, SI_ACTIVE_MONSTER_TRANSFORM, 0);
+ break;
+ case SC_INTRAVISION:
+ calc_flag = SCB_ALL;/* required for overlapping */
+ break;
+ }
+
+ opt_flag = 1;
+ switch(type){
+ case SC_STONE:
+ case SC_FREEZE:
+ case SC_STUN:
+ case SC_SLEEP:
+ case SC_DEEPSLEEP:
+ case SC_BURNING:
+ case SC_WHITEIMPRISON:
+ case SC_CRYSTALIZE:
+ sc->opt1 = 0;
+ break;
+
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_BLIND:
+ sc->opt2 &= ~(1<<(type-SC_POISON));
+ break;
+ case SC_DPOISON:
+ sc->opt2 &= ~OPT2_DPOISON;
+ break;
+ case SC_SIGNUMCRUCIS:
+ sc->opt2 &= ~OPT2_SIGNUMCRUCIS;
+ 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:
+ case SC__INVISIBILITY:
+ sc->option &= ~OPTION_CLOAK;
+ case SC_CAMOUFLAGE:
+ opt_flag|= 2;
+ break;
+ case SC_CHASEWALK:
+ sc->option &= ~(OPTION_CHASEWALK|OPTION_CLOAK);
+ opt_flag|= 2;
+ break;
+ case SC_SIGHT:
+ sc->option &= ~OPTION_SIGHT;
+ break;
+ case SC_WEDDING:
+ sc->option &= ~OPTION_WEDDING;
+ break;
+ case SC_XMAS:
+ sc->option &= ~OPTION_XMAS;
+ break;
+ case SC_SUMMER:
+ sc->option &= ~OPTION_SUMMER;
+ break;
+ case SC_ORCISH:
+ sc->option &= ~OPTION_ORCISH;
+ break;
+ case SC_RUWACH:
+ sc->option &= ~OPTION_RUWACH;
+ break;
+ case SC_FUSION:
+ sc->option &= ~OPTION_FLYING;
+ break;
+ //opt3
+ case SC_TWOHANDQUICKEN:
+ case SC_ONEHAND:
+ case SC_SPEARQUICKEN:
+ case SC_CONCENTRATION:
+ case SC_MERC_QUICKEN:
+ sc->opt3 &= ~OPT3_QUICKEN;
+ opt_flag = 0;
+ break;
+ case SC_OVERTHRUST:
+ case SC_MAXOVERTHRUST:
+ case SC_SWOO:
+ sc->opt3 &= ~OPT3_OVERTHRUST;
+ if( type == SC_SWOO )
+ opt_flag = 8;
+ else
+ opt_flag = 0;
+ break;
+ case SC_ENERGYCOAT:
+ case SC_SKE:
+ sc->opt3 &= ~OPT3_ENERGYCOAT;
+ opt_flag = 0;
+ break;
+ case SC_INCATKRATE: //Simulated Explosion spirits effect.
+ if (bl->type != BL_MOB)
+ {
+ opt_flag = 0;
+ break;
+ }
+ case SC_EXPLOSIONSPIRITS:
+ sc->opt3 &= ~OPT3_EXPLOSIONSPIRITS;
+ opt_flag = 0;
+ break;
+ case SC_STEELBODY:
+ case SC_SKA:
+ sc->opt3 &= ~OPT3_STEELBODY;
+ opt_flag = 0;
+ break;
+ case SC_BLADESTOP:
+ sc->opt3 &= ~OPT3_BLADESTOP;
+ opt_flag = 0;
+ break;
+ case SC_AURABLADE:
+ sc->opt3 &= ~OPT3_AURABLADE;
+ opt_flag = 0;
+ break;
+ case SC_BERSERK:
+ opt_flag = 0;
+// case SC__BLOODYLUST:
+ sc->opt3 &= ~OPT3_BERSERK;
+ break;
+// case ???: // doesn't seem to do anything
+// sc->opt3 &= ~OPT3_LIGHTBLADE;
+// opt_flag = 0;
+// break;
+ case SC_DANCING:
+ if ((sce->val1&0xFFFF) == CG_MOONLIT)
+ sc->opt3 &= ~OPT3_MOONLIT;
+ opt_flag = 0;
+ break;
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2:
+ sc->opt3 &= ~OPT3_MARIONETTE;
+ opt_flag = 0;
+ break;
+ case SC_ASSUMPTIO:
+ sc->opt3 &= ~OPT3_ASSUMPTIO;
+ opt_flag = 0;
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ sc->opt3 &= ~OPT3_WARM;
+ opt_flag = 0;
+ break;
+ case SC_KAITE:
+ sc->opt3 &= ~OPT3_KAITE;
+ opt_flag = 0;
+ break;
+ case SC_BUNSINJYUTSU:
+ sc->opt3 &= ~OPT3_BUNSIN;
+ opt_flag = 0;
+ break;
+ case SC_SPIRIT:
+ sc->opt3 &= ~OPT3_SOULLINK;
+ opt_flag = 0;
+ break;
+ case SC_CHANGEUNDEAD:
+ sc->opt3 &= ~OPT3_UNDEAD;
+ opt_flag = 0;
+ break;
+// case ???: // from DA_CONTRACT (looks like biolab mobs aura)
+// sc->opt3 &= ~OPT3_CONTRACT;
+// opt_flag = 0;
+// break;
+ default:
+ opt_flag = 0;
+ }
+
+ if (calc_flag&SCB_DYE)
+ { //Restore DYE color
+ if (vd && !vd->cloth_color && sce->val4)
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,sce->val4);
+ calc_flag&=~SCB_DYE;
+ }
+
+ //On Aegis, when turning off a status change, first goes the sc packet, then the option packet.
+ clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0);
+
+ if( opt_flag&8 ) //bugreport:681
+ clif_changeoption2(bl);
+ else if(opt_flag)
+ clif_changeoption(bl);
+
+ if (calc_flag)
+ status_calc_bl(bl,calc_flag);
+
+ if(opt_flag&4) //Out of hiding, invoke on place.
+ skill_unit_move(bl,gettick(),1);
+
+ if(opt_flag&2 && sd && map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,bl->m,bl->x,bl->y); //Trigger on-touch event.
+
+ ers_free(sc_data_ers, sce);
+ return 1;
+}
+
+int kaahi_heal_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *bl;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ struct status_data *status;
+ int hp;
+
+ if(!((bl=map_id2bl(id))&&
+ (sc=status_get_sc(bl)) &&
+ (sce = sc->data[SC_KAAHI])))
+ return 0;
+
+ if(sce->val4 != tid) {
+ ShowError("kaahi_heal_timer: Timer mismatch: %d != %d\n", tid, sce->val4);
+ sce->val4 = INVALID_TIMER;
+ return 0;
+ }
+
+ status=status_get_status_data(bl);
+ if(!status_charge(bl, 0, sce->val3)) {
+ sce->val4 = INVALID_TIMER;
+ return 0;
+ }
+
+ hp = status->max_hp - status->hp;
+ if (hp > sce->val2)
+ hp = sce->val2;
+ if (hp)
+ status_heal(bl, hp, 0, 2);
+ sce->val4 = INVALID_TIMER;
+ return 1;
+}
+
+/*==========================================
+ * For recusive status, like for each 5s we drop sp etc.
+ * Reseting the end timer.
+ *------------------------------------------*/
+int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ enum sc_type type = (sc_type)data;
+ struct block_list *bl;
+ struct map_session_data *sd;
+ struct status_data *status;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+
+ bl = map_id2bl(id);
+ if(!bl)
+ {
+ ShowDebug("status_change_timer: Null pointer id: %d data: %d\n", id, data);
+ return 0;
+ }
+ sc = status_get_sc(bl);
+ status = status_get_status_data(bl);
+
+ if(!(sc && (sce = sc->data[type])))
+ {
+ ShowDebug("status_change_timer: Null pointer id: %d data: %d bl-type: %d\n", id, data, bl->type);
+ return 0;
+ }
+
+ if( sce->timer != tid )
+ {
+ ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sce->timer, bl->id);
+ return 0;
+ }
+
+ sd = BL_CAST(BL_PC, bl);
+
+// set the next timer of the sce (don't assume the status still exists)
+#define sc_timer_next(t,f,i,d) \
+ if( (sce=sc->data[type]) ) \
+ sce->timer = add_timer(t,f,i,d); \
+ else \
+ ShowError("status_change_timer: Unexpected NULL status change id: %d data: %d\n", id, data)
+
+ switch(type)
+ {
+ case SC_MAXIMIZEPOWER:
+ case SC_CLOAKING:
+ if(!status_charge(bl, 0, 1))
+ break; //Not enough SP to continue.
+ sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data);
+ return 0;
+
+ case SC_CHASEWALK:
+ if(!status_charge(bl, 0, sce->val4))
+ break; //Not enough SP to continue.
+
+ if (!sc->data[SC_INCSTR]) {
+ sc_start(bl, SC_INCSTR,100,1<<(sce->val1-1),
+ (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration
+ *skill_get_time2(status_sc2skill(type),sce->val1));
+ }
+ sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data);
+ return 0;
+ break;
+
+ case SC_SKA:
+ if(--(sce->val2)>0){
+ sce->val3 = rnd()%100; //Random defense.
+ sc_timer_next(1000+tick, status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_HIDING:
+ if(--(sce->val2)>0){
+
+ if(sce->val2 % sce->val4 == 0 && !status_charge(bl, 0, 1))
+ break; //Fail if it's time to substract SP and there isn't.
+
+ sc_timer_next(1000+tick, status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_SIGHT:
+ case SC_RUWACH:
+ case SC_SIGHTBLASTER:
+ if(type == SC_SIGHTBLASTER)
+ map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick);
+ else
+ map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick);
+
+ if( --(sce->val2)>0 ){
+ sce->val4 += 250; // use for Shadow Form 2 seconds checking.
+ sc_timer_next(250+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_PROVOKE:
+ if(sce->val2) { //Auto-provoke (it is ended in status_heal)
+ sc_timer_next(1000*60+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_STONE:
+ if(sc->opt1 == OPT1_STONEWAIT && sce->val3) {
+ sce->val4 = 0;
+ unit_stop_walking(bl,1);
+ unit_stop_attack(bl);
+ sc->opt1 = OPT1_STONE;
+ clif_changeoption(bl);
+ sc_timer_next(1000+tick,status_change_timer, bl->id, data );
+ status_calc_bl(bl, StatusChangeFlagTable[type]);
+ return 0;
+ }
+ if(--(sce->val3) > 0) {
+ if(++(sce->val4)%5 == 0 && status->hp > status->max_hp/4)
+ status_percent_damage(NULL, bl, 1, 0, false);
+ sc_timer_next(1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_POISON:
+ if(status->hp <= max(status->max_hp>>2, sce->val4)) //Stop damaging after 25% HP left.
+ break;
+ case SC_DPOISON:
+ if (--(sce->val3) > 0) {
+ if (!sc->data[SC_SLOWPOISON]) {
+ if( sce->val2 && bl->type == BL_MOB ) {
+ struct block_list* src = map_id2bl(sce->val2);
+ if( src )
+ mob_log_damage((TBL_MOB*)bl,src,sce->val4);
+ }
+ map_freeblock_lock();
+ status_zap(bl, sce->val4, 0);
+ if (sc->data[type]) { // Check if the status still last ( can be dead since then ).
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data );
+ }
+ map_freeblock_unlock();
+ }
+ return 0;
+ }
+ break;
+
+ case SC_TENSIONRELAX:
+ if(status->max_hp > status->hp && --(sce->val3) > 0){
+ sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_KNOWLEDGE:
+ if (!sd) break;
+ if(bl->m == sd->feel_map[0].m ||
+ bl->m == sd->feel_map[1].m ||
+ bl->m == sd->feel_map[2].m)
+ { //Timeout will be handled by pc_setpos
+ sce->timer = INVALID_TIMER;
+ return 0;
+ }
+ break;
+
+ case SC_BLEEDING:
+ if (--(sce->val4) >= 0) {
+ int hp = rnd()%600 + 200;
+ map_freeblock_lock();
+ status_fix_damage(NULL, bl, sd||hp<status->hp?hp:status->hp-1, 1);
+ if( sc->data[type] ) {
+ if( status->hp == 1 ) {
+ map_freeblock_unlock();
+ break;
+ }
+ sc_timer_next(10000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ if( sd && --(sce->val4) >= 0 )
+ {
+ // val1 < 0 = per max% | val1 > 0 = exact amount
+ int hp = 0;
+ if( status->hp < status->max_hp )
+ hp = (sce->val1 < 0) ? (int)(sd->status.max_hp * -1 * sce->val1 / 100.) : sce->val1 ;
+ status_heal(bl, hp, 0, 2);
+ sc_timer_next((sce->val2 * 1000) + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_BOSSMAPINFO:
+ if( sd && --(sce->val4) >= 0 )
+ {
+ struct mob_data *boss_md = map_id2boss(sce->val1);
+ if( boss_md && sd->bl.m == boss_md->bl.m )
+ {
+ clif_bossmapinfo(sd->fd, boss_md, 1); // Update X - Y on minimap
+ if (boss_md->bl.prev != NULL) {
+ sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case SC_DANCING: //SP consumption by time of dancing skills
+ {
+ int s = 0;
+ int sp = 1;
+ if (--sce->val3 <= 0)
+ break;
+ switch(sce->val1&0xFFFF){
+ case BD_RICHMANKIM:
+ case BD_DRUMBATTLEFIELD:
+ case BD_RINGNIBELUNGEN:
+ case BD_SIEGFRIED:
+ case BA_DISSONANCE:
+ case BA_ASSASSINCROSS:
+ case DC_UGLYDANCE:
+ s=3;
+ break;
+ case BD_LULLABY:
+ case BD_ETERNALCHAOS:
+ case BD_ROKISWEIL:
+ case DC_FORTUNEKISS:
+ s=4;
+ break;
+ case CG_HERMODE:
+ case BD_INTOABYSS:
+ case BA_WHISTLE:
+ case DC_HUMMING:
+ case BA_POEMBRAGI:
+ case DC_SERVICEFORYOU:
+ s=5;
+ break;
+ case BA_APPLEIDUN:
+ #ifdef RENEWAL
+ s=5;
+ #else
+ s=6;
+ #endif
+ break;
+ case CG_MOONLIT:
+ //Moonlit's cost is 4sp*skill_lv [Skotlex]
+ sp= 4*(sce->val1>>16);
+ //Upkeep is also every 10 secs.
+ case DC_DONTFORGETME:
+ s=10;
+ break;
+ }
+ if( s != 0 && sce->val3 % s == 0 )
+ {
+ if (sc->data[SC_LONGING])
+ sp*= 3;
+ if (!status_charge(bl, 0, sp))
+ break;
+ }
+ sc_timer_next(1000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ // 5% every 10 seconds [DracoRPG]
+ if( --( sce->val3 ) > 0 && status_charge(bl, sce->val2, 0) && status->hp > 100 )
+ {
+ sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_NOCHAT:
+ if(sd){
+ sd->status.manner++;
+ clif_changestatus(sd,SP_MANNER,sd->status.manner);
+ clif_updatestatus(sd,SP_MANNER);
+ if (sd->status.manner < 0)
+ { //Every 60 seconds your manner goes up by 1 until it gets back to 0.
+ sc_timer_next(60000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SPLASHER:
+ // custom Venom Splasher countdown timer
+ //if (sce->val4 % 1000 == 0) {
+ // char timer[10];
+ // snprintf (timer, 10, "%d", sce->val4/1000);
+ // clif_message(bl, timer);
+ //}
+ if((sce->val4 -= 500) > 0) {
+ sc_timer_next(500 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2:
+ {
+ struct block_list *pbl = map_id2bl(sce->val1);
+ if( pbl && check_distance_bl(bl, pbl, 7) )
+ {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_GOSPEL:
+ if(sce->val4 == BCT_SELF && --(sce->val2) > 0)
+ {
+ int hp, sp;
+ hp = (sce->val1 > 5) ? 45 : 30;
+ sp = (sce->val1 > 5) ? 35 : 20;
+ if(!status_charge(bl, hp, sp))
+ break;
+ sc_timer_next(10000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_JAILED:
+ if(sce->val1 == INT_MAX || --(sce->val1) > 0)
+ {
+ sc_timer_next(60000+tick, status_change_timer, bl->id,data);
+ return 0;
+ }
+ break;
+
+ case SC_BLIND:
+ if(sc->data[SC_FOGWALL])
+ { //Blind lasts forever while you are standing on the fog.
+ sc_timer_next(5000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_ABUNDANCE:
+ if(--(sce->val4) > 0) {
+ 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 ) {
+ map_freeblock_lock();
+ clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,0,0);
+ status_fix_damage(NULL,bl,100,0);
+ if( sc->data[type] ) {
+ sc_timer_next(3000+tick,status_change_timer,bl->id,data);
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_LEECHESEND:
+ if( --(sce->val4) >= 0 ) {
+ int damage = status->max_hp/100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100)
+ damage += status->vit * (sce->val1 - 3);
+ unit_skillcastcancel(bl,2);
+ map_freeblock_lock();
+ status_damage(bl, bl, damage, 0, clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,0,0), 1);
+ if( sc->data[type] ) {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data );
+ }
+ map_freeblock_unlock();
+ 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 && !pc_issit(sd)) { //can't cast if sit
+ int mushroom_skill_id = 0, i;
+ unit_stop_attack(bl);
+ unit_skillcastcancel(bl,1);
+ do {
+ i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB;
+ mushroom_skill_id = skill_magicmushroom_db[i].skill_id;
+ }
+ while( mushroom_skill_id == 0 );
+
+ switch( skill_get_casttype(mushroom_skill_id) ) { // Magic Mushroom skills are buffs or area damage
+ case CAST_GROUND:
+ skill_castend_pos2(bl,bl->x,bl->y,mushroom_skill_id,1,tick,0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(bl,bl,mushroom_skill_id,1,tick,0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(bl,bl,mushroom_skill_id,1,tick,0);
+ break;
+ }
+ }
+
+ clif_emotion(bl,E_HEH);
+ 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.
+ 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, 0); //cancel dmg only if cancelable
+ if( sc->data[type] ) {
+ sc_timer_next(10000 + tick, status_change_timer, bl->id, data );
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_OBLIVIONCURSE:
+ if( --(sce->val4) >= 0 )
+ {
+ clif_emotion(bl,E_WHAT);
+ 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->val4) >= 0 )
+ {
+ int heal = status->max_hp * 3 / 100;
+ if( sc && sc->data[SC_AKAITSUKI] && heal )
+ heal = ~heal + 1;
+ status_heal(bl, heal, 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 damage = 1000 + 3 * status_get_max_hp(bl) / 100; // Deals fixed (1000 + 3%*MaxHP)
+
+ map_freeblock_lock();
+ clif_damage(bl,bl,tick,0,0,damage,1,9,0); //damage is like endure effect with no walk delay
+ status_damage(src, bl, damage, 0, 0, 1);
+
+ if( sc->data[type]){ // Target still lives. [LimitLine]
+ sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_FEAR:
+ if( --(sce->val4) >= 0 )
+ {
+ if( sce->val2 > 0 )
+ sce->val2--;
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_SPHERE_1:
+ case SC_SPHERE_2:
+ case SC_SPHERE_3:
+ case SC_SPHERE_4:
+ case SC_SPHERE_5:
+ if( --(sce->val4) >= 0 )
+ {
+ 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) ){
+ int i;
+ for(i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) // Also remove stored spell as well.
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+ break;
+ }
+ sc_timer_next(5000 + 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(--(sce->val4) > 0){
+ status_charge(bl,0,7 - sce->val1);
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ 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% - skill_lv.
+ 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;
+ case SC_VACUUM_EXTREME:
+ if( --(sce->val4) >= 0 ){
+ if( !unit_is_walking(bl) && !sce->val2 ){
+ sc->cant.move++;
+ sce->val2 = 1;
+ }
+ sc_timer_next(100 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_BLOODSUCKER:
+ if( --(sce->val4) >= 0 ) {
+ struct block_list *src = map_id2bl(sce->val2);
+ int damage;
+ if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) )
+ break;
+ map_freeblock_lock();
+ damage = 200 + 100 * sce->val1 + status_get_int(src);
+ status_damage(src, bl, damage, 0, clif_damage(bl,bl,tick,status->amotion,status->dmotion+200,damage,1,0,0), 1);
+ unit_skillcastcancel(bl,1);
+ if ( sc->data[type] ) {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ status_heal(src, damage*(5 + 5 * sce->val1)/100, 0, 0); // 5 + 5% per level
+ return 0;
+ }
+ break;
+
+ case SC_VOICEOFSIREN:
+ if( --(sce->val4) >= 0 )
+ {
+ clif_emotion(bl,E_LV);
+ 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 val4 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.
+ if( bl->type != BL_MOB) // doesn't work on mobs
+ 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 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));
+ if( sc->data[type] ) {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ }
+ 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,INVALID_TIMER);
+ status_change_end(bl,type,INVALID_TIMER);
+ 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);
+ return 0;
+ }
+ break;
+ case SC_LEADERSHIP:
+ case SC_GLORYWOUNDS:
+ case SC_SOULCOLD:
+ case SC_HAWKEYES:
+ /* they only end by status_change_end */
+ sc_timer_next(600000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ case SC_MEIKYOUSISUI:
+ if( --(sce->val4) > 0 ){
+ status_heal(bl, status->max_hp * (sce->val1+1) / 100, status->max_sp * sce->val1 / 100, 0);
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_IZAYOI:
+ case SC_KAGEMUSYA:
+ if( --(sce->val2) > 0 ){
+ if(!status_charge(bl, 0, 1)) break;
+ sc_timer_next(1000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_ANGRIFFS_MODUS:
+ if(--(sce->val4) >= 0) { //drain hp/sp
+ if( !status_charge(bl,100,20) ) break;
+ sc_timer_next(1000+tick,status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+ }
+
+ // default for all non-handled control paths is to end the status
+ return status_change_end( bl,type,tid );
+#undef sc_timer_next
+}
+
+/*==========================================
+ * Foreach iteration of repetitive status
+ *------------------------------------------*/
+int status_change_timer_sub(struct block_list* bl, va_list ap)
+{
+ struct status_change* tsc;
+
+ struct block_list* src = va_arg(ap,struct block_list*);
+ struct status_change_entry* sce = va_arg(ap,struct status_change_entry*);
+ enum sc_type type = (sc_type)va_arg(ap,int); //gcc: enum args get promoted to int
+ unsigned int tick = va_arg(ap,unsigned int);
+
+ if (status_isdead(bl))
+ return 0;
+
+ tsc = status_get_sc(bl);
+
+ switch( type ) {
+ case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */
+ if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking
+ rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] %
+ status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
+ 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);
+ status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
+ break;
+ case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */
+ if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
+ tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED] ||
+ tsc->data[SC__INVISIBILITY])) {
+ 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);
+ status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
+ if(battle_check_target( src, bl, BCT_ENEMY ) > 0)
+ skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0);
+ }
+ if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking
+ rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] %
+ status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
+ break;
+ case SC_SIGHTBLASTER:
+ if (battle_check_target( src, bl, BCT_ENEMY ) > 0 &&
+ status_check_skilluse(src, bl, WZ_SIGHTBLASTER, 2))
+ {
+ skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0);
+ if (sce && !(bl->type&BL_SKILL)) //The hit is not counted if it's against a trap
+ sce->val2 = 0; //This signals it to end.
+ }
+ break;
+ case SC_CLOSECONFINE:
+ //Lock char has released the hold on everyone...
+ if (tsc && tsc->data[SC_CLOSECONFINE2] && tsc->data[SC_CLOSECONFINE2]->val2 == src->id) {
+ tsc->data[SC_CLOSECONFINE2]->val2 = 0;
+ status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
+ }
+ break;
+ case SC_CURSEDCIRCLE_TARGET:
+ if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) {
+ clif_bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0);
+ status_change_end(bl, type, INVALID_TIMER);
+ }
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Clears buffs/debuffs of a character.
+ * type&1 -> buffs, type&2 -> debuffs
+ * type&4 -> especific debuffs(implemented with refresh)
+ *------------------------------------------*/
+int status_change_clear_buffs (struct block_list* bl, int type)
+{
+ int i;
+ struct status_change *sc= status_get_sc(bl);
+
+ if (!sc || !sc->count)
+ return 0;
+
+ if (type&6) //Debuffs
+ for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++)
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+
+ for( i = SC_COMMON_MAX+1; i < SC_MAX; i++ )
+ {
+ if(!sc->data[i])
+ continue;
+
+ switch (i) {
+ //Stuff that cannot be removed
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_COMBO:
+ case SC_SMA:
+ case SC_DANCING:
+ case SC_LEADERSHIP:
+ case SC_GLORYWOUNDS:
+ case SC_SOULCOLD:
+ case SC_HAWKEYES:
+ case SC_GUILDAURA:
+ case SC_SAFETYWALL:
+ case SC_PNEUMA:
+ case SC_NOCHAT:
+ case SC_JAILED:
+ case SC_ANKLE:
+ case SC_BLADESTOP:
+ case SC_CP_WEAPON:
+ case SC_CP_SHIELD:
+ case SC_CP_ARMOR:
+ case SC_CP_HELM:
+ 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_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_EXPBOOST:
+ case SC_JEXPBOOST:
+ case SC_ITEMBOOST:
+ case SC_ELECTRICSHOCKER:
+ case SC__MANHOLE:
+ case SC_GIANTGROWTH:
+ case SC_MILLENNIUMSHIELD:
+ case SC_REFRESH:
+ case SC_STONEHARDSKIN:
+ case SC_VITALITYACTIVATION:
+ case SC_FIGHTINGSPIRIT:
+ case SC_ABUNDANCE:
+ case SC_CURSEDCIRCLE_ATKER:
+ case SC_CURSEDCIRCLE_TARGET:
+ continue;
+
+ //Debuffs that can be removed.
+ case SC_DEEPSLEEP:
+ case SC_BURNING:
+ case SC_FREEZING:
+ case SC_CRYSTALIZE:
+ 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_MARSHOFABYSS:
+ case SC_MANDRAGORA:
+ if(!(type&4))
+ continue;
+ break;
+ case SC_HALLUCINATION:
+ case SC_QUAGMIRE:
+ case SC_SIGNUMCRUCIS:
+ case SC_DECREASEAGI:
+ case SC_SLOWDOWN:
+ case SC_MINDBREAKER:
+ case SC_WINKCHARM:
+ case SC_STOP:
+ case SC_ORCISH:
+ case SC_STRIPWEAPON:
+ case SC_STRIPSHIELD:
+ case SC_STRIPARMOR:
+ case SC_STRIPHELM:
+ case SC_BITE:
+ case SC_ADORAMUS:
+ case SC_VACUUM_EXTREME:
+ case SC_FEAR:
+ case SC_MAGNETICFIELD:
+ case SC_NETHERWORLD:
+ if (!(type&2))
+ continue;
+ break;
+ //The rest are buffs that can be removed.
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ case SC_SATURDAYNIGHTFEVER:
+ if (!(type&1))
+ continue;
+ sc->data[i]->val2 = 0;
+ break;
+ default:
+ if (!(type&1))
+ continue;
+ break;
+ }
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+ }
+ return 0;
+}
+
+int status_change_spread( struct block_list *src, struct block_list *bl ) {
+ int i, flag = 0;
+ struct status_change *sc = status_get_sc(src);
+ const struct TimerData *timer;
+ unsigned int tick;
+ struct status_change_data data;
+
+ if( !sc || !sc->count )
+ return 0;
+
+ tick = gettick();
+
+ for( i = SC_COMMON_MIN; i < SC_MAX; i++ ) {
+ if( !sc->data[i] || i == SC_COMMON_MAX )
+ continue;
+
+ switch( i ) {
+ //Debuffs that can be spreaded.
+ // NOTE: We'll add/delte SCs when we are able to confirm it.
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ case SC_NOCHAT:
+ case SC_HALLUCINATION:
+ case SC_SIGNUMCRUCIS:
+ case SC_DECREASEAGI:
+ case SC_SLOWDOWN:
+ case SC_MINDBREAKER:
+ case SC_WINKCHARM:
+ case SC_STOP:
+ case SC_ORCISH:
+ //case SC_STRIPWEAPON://Omg I got infected and had the urge to strip myself physically.
+ //case SC_STRIPSHIELD://No this is stupid and shouldnt be spreadable at all.
+ //case SC_STRIPARMOR:// Disabled until I can confirm if it does or not. [Rytech]
+ //case SC_STRIPHELM:
+ //case SC__STRIPACCESSORY:
+ case SC_BITE:
+ case SC_FREEZING:
+ case SC_VENOMBLEED:
+ case SC_DEATHHURT:
+ case SC_PARALYSE:
+ if( sc->data[i]->timer != INVALID_TIMER ) {
+ timer = get_timer(sc->data[i]->timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+ data.tick = DIFF_TICK(timer->tick,tick);
+ } else
+ data.tick = INVALID_TIMER;
+ break;
+ // Special cases
+ case SC_POISON:
+ case SC_DPOISON:
+ data.tick = sc->data[i]->val3 * 1000;
+ break;
+ case SC_FEAR:
+ case SC_LEECHESEND:
+ data.tick = sc->data[i]->val4 * 1000;
+ break;
+ case SC_BURNING:
+ data.tick = sc->data[i]->val4 * 2000;
+ break;
+ case SC_PYREXIA:
+ case SC_OBLIVIONCURSE:
+ data.tick = sc->data[i]->val4 * 3000;
+ break;
+ case SC_MAGICMUSHROOM:
+ data.tick = sc->data[i]->val4 * 4000;
+ break;
+ case SC_TOXIN:
+ case SC_BLEEDING:
+ data.tick = sc->data[i]->val4 * 10000;
+ break;
+ default:
+ continue;
+ break;
+ }
+ if( i ){
+ data.val1 = sc->data[i]->val1;
+ data.val2 = sc->data[i]->val2;
+ data.val3 = sc->data[i]->val3;
+ data.val4 = sc->data[i]->val4;
+ status_change_start(bl,(sc_type)i,10000,data.val1,data.val2,data.val3,data.val4,data.tick,1|2|8);
+ flag = 1;
+ }
+ }
+
+ return flag;
+}
+
+//Natural regen related stuff.
+static unsigned int natural_heal_prev_tick,natural_heal_diff_tick;
+static int status_natural_heal(struct block_list* bl, va_list args)
+{
+ struct regen_data *regen;
+ struct status_data *status;
+ struct status_change *sc;
+ struct unit_data *ud;
+ struct view_data *vd = NULL;
+ struct regen_data_sub *sregen;
+ struct map_session_data *sd;
+ int val,rate,bonus = 0,flag;
+
+ regen = status_get_regen_data(bl);
+ if (!regen) return 0;
+ status = status_get_status_data(bl);
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL;
+ sd = BL_CAST(BL_PC,bl);
+
+ flag = regen->flag;
+ if (flag&RGN_HP && (status->hp >= status->max_hp || regen->state.block&1))
+ flag&=~(RGN_HP|RGN_SHP);
+ if (flag&RGN_SP && (status->sp >= status->max_sp || regen->state.block&2))
+ flag&=~(RGN_SP|RGN_SSP);
+
+ if (flag && (
+ status_isdead(bl) ||
+ (sc && (sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || sc->data[SC__INVISIBILITY]))
+ ))
+ flag=0;
+
+ if (sd) {
+ if (sd->hp_loss.value || sd->sp_loss.value)
+ pc_bleeding(sd, natural_heal_diff_tick);
+ if (sd->hp_regen.value || sd->sp_regen.value)
+ pc_regen(sd, natural_heal_diff_tick);
+ }
+
+ if(flag&(RGN_SHP|RGN_SSP) && regen->ssregen &&
+ (vd = status_get_viewdata(bl)) && vd->dead_sit == 2)
+ { //Apply sitting regen bonus.
+ sregen = regen->ssregen;
+ if(flag&(RGN_SHP))
+ { //Sitting HP regen
+ val = natural_heal_diff_tick * sregen->rate.hp;
+ if (regen->state.overweight)
+ val>>=1; //Half as fast when overweight.
+ sregen->tick.hp += val;
+ while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ sregen->tick.hp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp)
+ { //Full
+ flag&=~(RGN_HP|RGN_SHP);
+ break;
+ }
+ }
+ }
+ if(flag&(RGN_SSP))
+ { //Sitting SP regen
+ val = natural_heal_diff_tick * sregen->rate.sp;
+ if (regen->state.overweight)
+ val>>=1; //Half as fast when overweight.
+ sregen->tick.sp += val;
+ while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ sregen->tick.sp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, 0, sregen->sp, 3) < sregen->sp)
+ { //Full
+ flag&=~(RGN_SP|RGN_SSP);
+ break;
+ }
+ }
+ }
+ }
+
+ if (flag && regen->state.overweight)
+ flag=0;
+
+ ud = unit_bl2ud(bl);
+
+ if (flag&(RGN_HP|RGN_SHP|RGN_SSP) && ud && ud->walktimer != INVALID_TIMER)
+ {
+ flag&=~(RGN_SHP|RGN_SSP);
+ if(!regen->state.walk)
+ flag&=~RGN_HP;
+ }
+
+ if (!flag)
+ return 0;
+
+ if (flag&(RGN_HP|RGN_SP))
+ {
+ if(!vd) vd = status_get_viewdata(bl);
+ if(vd && vd->dead_sit == 2)
+ bonus++;
+ if(regen->state.gc)
+ bonus++;
+ }
+
+ //Natural Hp regen
+ if (flag&RGN_HP)
+ {
+ rate = natural_heal_diff_tick*(regen->rate.hp+bonus);
+ if (ud && ud->walktimer != INVALID_TIMER)
+ rate/=2;
+ // Homun HP regen fix (they should regen as if they were sitting (twice as fast)
+ if(bl->type==BL_HOM) rate *=2;
+
+ regen->tick.hp += rate;
+
+ if(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval)
+ {
+ val = 0;
+ do {
+ val += regen->hp;
+ regen->tick.hp -= battle_config.natural_healhp_interval;
+ } while(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval);
+ if (status_heal(bl, val, 0, 1) < val)
+ flag&=~RGN_SHP; //full.
+ }
+ }
+
+ //Natural SP regen
+ if(flag&RGN_SP)
+ {
+ rate = natural_heal_diff_tick*(regen->rate.sp+bonus);
+ // Homun SP regen fix (they should regen as if they were sitting (twice as fast)
+ if(bl->type==BL_HOM) rate *=2;
+
+ regen->tick.sp += rate;
+
+ if(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval)
+ {
+ val = 0;
+ do {
+ val += regen->sp;
+ regen->tick.sp -= battle_config.natural_healsp_interval;
+ } while(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval);
+ if (status_heal(bl, 0, val, 1) < val)
+ flag&=~RGN_SSP; //full.
+ }
+ }
+
+ if (!regen->sregen)
+ return flag;
+
+ //Skill regen
+ sregen = regen->sregen;
+
+ if(flag&RGN_SHP)
+ { //Skill HP regen
+ sregen->tick.hp += natural_heal_diff_tick * sregen->rate.hp;
+
+ while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ sregen->tick.hp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp)
+ break; //Full
+ }
+ }
+ if(flag&RGN_SSP)
+ { //Skill SP regen
+ sregen->tick.sp += natural_heal_diff_tick * sregen->rate.sp;
+ while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ val = sregen->sp;
+ if (sd && sd->state.doridori) {
+ val*=2;
+ sd->state.doridori = 0;
+ if ((rate = pc_checkskill(sd,TK_SPTIME)))
+ sc_start(bl,status_skill2sc(TK_SPTIME),
+ 100,rate,skill_get_time(TK_SPTIME, rate));
+ if (
+ (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR &&
+ rnd()%10000 < battle_config.sg_angel_skill_ratio
+ ) { //Angel of the Sun/Moon/Star
+ clif_feel_hate_reset(sd);
+ pc_resethate(sd);
+ pc_resetfeel(sd);
+ }
+ }
+ sregen->tick.sp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, 0, val, 3) < val)
+ break; //Full
+ }
+ }
+ return flag;
+}
+
+//Natural heal main timer.
+static int status_natural_heal_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick);
+ map_foreachregen(status_natural_heal);
+ natural_heal_prev_tick = tick;
+ return 0;
+}
+
+/**
+ * Get the chance to upgrade a piece of equipment.
+ * @param wlv The weapon type of the item to refine (see see enum refine_type)
+ * @param refine The target refine level
+ * @return The chance to refine the item, in percent (0~100)
+ **/
+int status_get_refine_chance(enum refine_type wlv, int refine) {
+
+ if ( refine < 0 || refine >= MAX_REFINE)
+ return 0;
+
+ return refine_info[wlv].chance[refine];
+}
+
+
+/*------------------------------------------
+ * DB reading.
+ * job_db1.txt - weight, hp, sp, aspd
+ * job_db2.txt - job level stat bonuses
+ * size_fix.txt - size adjustment table for weapons
+ * refine_db.txt - refining data table
+ *------------------------------------------*/
+static bool status_readdb_job1(char* fields[], int columns, int current)
+{// Job-specific values (weight, HP, SP, ASPD)
+ int idx, class_;
+ unsigned int i;
+
+ class_ = atoi(fields[0]);
+
+ if(!pcdb_checkid(class_))
+ {
+ ShowWarning("status_readdb_job1: Invalid job class %d specified.\n", class_);
+ return false;
+ }
+ idx = pc_class2idx(class_);
+
+ max_weight_base[idx] = atoi(fields[1]);
+ hp_coefficient[idx] = atoi(fields[2]);
+ hp_coefficient2[idx] = atoi(fields[3]);
+ sp_coefficient[idx] = atoi(fields[4]);
+#ifdef RENEWAL_ASPD
+ for(i = 0; i <= MAX_WEAPON_TYPE; i++)
+#else
+ for(i = 0; i < MAX_WEAPON_TYPE; i++)
+#endif
+ {
+ aspd_base[idx][i] = atoi(fields[i+5]);
+ }
+ return true;
+}
+
+static bool status_readdb_job2(char* fields[], int columns, int current)
+{
+ int idx, class_, i;
+
+ class_ = atoi(fields[0]);
+
+ if(!pcdb_checkid(class_))
+ {
+ ShowWarning("status_readdb_job2: Invalid job class %d specified.\n", class_);
+ return false;
+ }
+ idx = pc_class2idx(class_);
+
+ for(i = 1; i < columns; i++)
+ {
+ job_bonus[idx][i-1] = atoi(fields[i]);
+ }
+ return true;
+}
+
+static bool status_readdb_sizefix(char* fields[], int columns, int current)
+{
+ unsigned int i;
+
+ for(i = 0; i < MAX_WEAPON_TYPE; i++)
+ {
+ atkmods[current][i] = atoi(fields[i]);
+ }
+ return true;
+}
+
+static bool status_readdb_refine(char* fields[], int columns, int current)
+{
+ int i, bonus_per_level, random_bonus, random_bonus_start_level;
+
+ current = atoi(fields[0]);
+
+ if (current < 0 || current >= REFINE_TYPE_MAX)
+ return false;
+
+ bonus_per_level = atoi(fields[1]);
+ random_bonus_start_level = atoi(fields[2]);
+ random_bonus = atoi(fields[3]);
+
+ for(i = 0; i < MAX_REFINE; i++)
+ {
+ char* delim;
+
+ if (!(delim = strchr(fields[4+i], ':')))
+ return false;
+
+ *delim = '\0';
+
+ refine_info[current].chance[i] = atoi(fields[4+i]);
+
+ if (i >= random_bonus_start_level - 1)
+ refine_info[current].randombonus_max[i] = random_bonus * (i - random_bonus_start_level + 2);
+
+ refine_info[current].bonus[i] = bonus_per_level + atoi(delim+1);
+ if (i > 0)
+ refine_info[current].bonus[i] += refine_info[current].bonus[i-1];
+ }
+ return true;
+}
+
+/*
+* Read status db
+* job1.txt
+* job2.txt
+* size_fixe.txt
+* refine_db.txt
+*/
+int status_readdb(void)
+{
+ int i, j;
+
+ // initialize databases to default
+ //
+
+ // reset job_db1.txt data
+ memset(max_weight_base, 0, sizeof(max_weight_base));
+ memset(hp_coefficient, 0, sizeof(hp_coefficient));
+ memset(hp_coefficient2, 0, sizeof(hp_coefficient2));
+ memset(sp_coefficient, 0, sizeof(sp_coefficient));
+ memset(aspd_base, 0, sizeof(aspd_base));
+ // reset job_db2.txt data
+ memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus
+
+ // size_fix.txt
+ for(i=0;i<ARRAYLENGTH(atkmods);i++)
+ for(j=0;j<MAX_WEAPON_TYPE;j++)
+ atkmods[i][j]=100;
+
+ // refine_db.txt
+ for(i=0;i<ARRAYLENGTH(refine_info);i++)
+ {
+ for(j=0;j<MAX_REFINE; j++)
+ {
+ refine_info[i].chance[j] = 100;
+ refine_info[i].bonus[j] = 0;
+ refine_info[i].randombonus_max[j] = 0;
+ }
+ }
+
+ // read databases
+ //
+
+
+#ifdef RENEWAL_ASPD
+ sv_readdb(db_path, "re/job_db1.txt", ',', 6+MAX_WEAPON_TYPE, 6+MAX_WEAPON_TYPE, -1, &status_readdb_job1);
+#else
+ sv_readdb(db_path, "pre-re/job_db1.txt", ',', 5+MAX_WEAPON_TYPE, 5+MAX_WEAPON_TYPE, -1, &status_readdb_job1);
+#endif
+ 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, DBPATH"refine_db.txt", ',', 4+MAX_REFINE, 4+MAX_REFINE, ARRAYLENGTH(refine_info), &status_readdb_refine);
+
+ return 0;
+}
+
+/*==========================================
+ * Status db init and destroy.
+ *------------------------------------------*/
+int do_init_status(void)
+{
+ add_timer_func_list(status_change_timer,"status_change_timer");
+ add_timer_func_list(kaahi_heal_timer,"kaahi_heal_timer");
+ add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer");
+ initChangeTables();
+ initDummyData();
+ status_readdb();
+ status_calc_sigma();
+ natural_heal_prev_tick = gettick();
+ sc_data_ers = ers_new(sizeof(struct status_change_entry),"status.c::sc_data_ers",ERS_OPT_NONE);
+ add_timer_interval(natural_heal_prev_tick + NATURAL_HEAL_INTERVAL, status_natural_heal_timer, 0, 0, NATURAL_HEAL_INTERVAL);
+ return 0;
+}
+void do_final_status(void)
+{
+ ers_destroy(sc_data_ers);
+}
|