diff options
Diffstat (limited to 'src')
36 files changed, 2637 insertions, 1073 deletions
diff --git a/src/char/char.c b/src/char/char.c index 00fc633df..dc5352137 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -81,7 +81,8 @@ struct mmo_map_server { uint32 ip; uint16 port; int users; - unsigned short map[MAX_MAP_PER_SERVER]; + unsigned short *map; + unsigned short maps; } server[MAX_MAP_SERVERS]; int char_fd=-1; @@ -2628,7 +2629,7 @@ int search_mapserver(unsigned short map, uint32 ip, uint16 port); /// Initializes a server structure. void mapif_server_init(int id) { - memset(&server[id], 0, sizeof(server[id])); + //memset(&server[id], 0, sizeof(server[id])); server[id].fd = -1; } @@ -2655,7 +2656,7 @@ void mapif_server_reset(int id) WBUFL(buf,4) = htonl(server[id].ip); WBUFW(buf,8) = htons(server[id].port); j = 0; - for(i = 0; i < MAX_MAP_PER_SERVER; i++) + for(i = 0; i < server[id].maps; i++) if (server[id].map[i]) WBUFW(buf,10+(j++)*4) = server[id].map[i]; if (j > 0) { @@ -2725,8 +2726,11 @@ int parse_frommap(int fd) case 0x2afa: // Receiving map names list from the map-server if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; - - memset(server[id].map, 0, sizeof(server[id].map)); + if( server[id].map != NULL ) { aFree(server[id].map); server[id].map = NULL; } + + server[id].maps = ( RFIFOW(fd, 2) - 4 ) / 4; + CREATE(server[id].map, unsigned short, server[id].maps); + j = 0; for(i = 4; i < RFIFOW(fd,2); i += 4) { server[id].map[j] = RFIFOW(fd,i); @@ -3392,10 +3396,14 @@ int parse_frommap(int fd) if( RFIFOREST(fd) < RFIFOW(fd,4) ) return 0;/* packet wasn't fully received yet (still fragmented) */ else { - int sfd;/* stat server fd */ + int sfd;/* stat server fd */ + struct hSockOpt opt; RFIFOSKIP(fd, 2);/* we skip first 2 bytes which are the 0x3008, so we end up with a buffer equal to the one we send */ - if( (sfd = make_connection(host2ip("stats.hercules.ws"),(uint16)25427,true) ) == -1 ) { + opt.silent = 1; + opt.setTimeo = 1; + + if( (sfd = make_connection(host2ip("stats.hercules.ws"),(uint16)25427,&opt) ) == -1 ) { RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */ RFIFOFLUSH(fd); break;/* connection not possible, we drop the report */ @@ -4275,7 +4283,6 @@ int parse_char(int fd) server[i].ip = ntohl(RFIFOL(fd,54)); server[i].port = ntohs(RFIFOW(fd,58)); server[i].users = 0; - memset(server[i].map, 0, sizeof(server[i].map)); session[fd]->func_parse = parse_frommap; session[fd]->flag.server = 1; realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); @@ -4486,12 +4493,12 @@ int check_connect_login_server(int tid, unsigned int tick, int id, intptr_t data return 0; ShowInfo("Attempt to connect to login-server...\n"); - login_fd = make_connection(login_ip, login_port, false); - if (login_fd == -1) - { //Try again later. [Skotlex] + + if ( (login_fd = make_connection(login_ip, login_port, NULL)) == -1) { //Try again later. [Skotlex] login_fd = 0; return 0; } + session[login_fd]->func_parse = parse_fromlogin; session[login_fd]->flag.server = 1; realloc_fifo(login_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); @@ -4858,8 +4865,8 @@ int char_config_read(const char* cfgName) return 0; } -void do_final(void) -{ +void do_final(void) { + int i; ShowStatus("Terminating...\n"); set_all_offline(-1); @@ -4879,8 +4886,7 @@ void do_final(void) online_char_db->destroy(online_char_db, NULL); auth_db->destroy(auth_db, NULL); - if( char_fd != -1 ) - { + if( char_fd != -1 ) { do_close(char_fd); char_fd = -1; } @@ -4888,6 +4894,10 @@ void do_final(void) SQL->Free(sql_handle); mapindex_final(); + for(i = 0; i < MAX_MAP_SERVERS; i++ ) + if( server[i].map ) + aFree(server[i].map); + ShowStatus("Finished.\n"); } @@ -4923,11 +4933,17 @@ void do_shutdown(void) int do_init(int argc, char **argv) { + int i; memset(&skillid2idx, 0, sizeof(skillid2idx)); + + for(i = 0; i < MAX_MAP_SERVERS; i++ ) + server[i].map = NULL; + //Read map indexes mapindex_init(); start_point.map = mapindex_name2id("new_zone01"); + pincode_defaults(); char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); diff --git a/src/char/char.h b/src/char/char.h index b48ea359c..996a0e845 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -45,7 +45,7 @@ struct online_char_data { DBMap* online_char_db; // int account_id -> struct online_char_data* -#define MAX_MAP_SERVERS 30 +#define MAX_MAP_SERVERS 2 #define DEFAULT_AUTOSAVE_INTERVAL 300*1000 diff --git a/src/common/console.c b/src/common/console.c index ba93b8e09..33d485497 100644 --- a/src/common/console.c +++ b/src/common/console.c @@ -85,6 +85,9 @@ CPCMD(exit) { CPCMD(ers_report) { ers_report(); } +CPCMD(mem_report) { + memmgr_report(line?atoi(line):0); +} CPCMD(help) { unsigned int i = 0; for ( i = 0; i < console->cmd_list_count; i++ ) { @@ -115,6 +118,7 @@ void console_load_defaults(void) { CP_DEF(help), CP_DEF_C(server), CP_DEF_S(ers_report,server), + CP_DEF_S(mem_report,server), CP_DEF_S(malloc_usage,server), CP_DEF_S(exit,server), }; @@ -227,8 +231,10 @@ void console_parse_sub(char *line) { char *tok; char sublist[CP_CMD_LENGTH * 5]; unsigned int i, len = 0; + memcpy(bline, line, 200); tok = strtok(line, " "); + for ( i = 0; i < console->cmd_list_count; i++ ) { if( strcmpi(tok,console->cmd_list[i]->cmd) == 0 ) break; diff --git a/src/common/malloc.c b/src/common/malloc.c index 4874aa0f4..e98ec770a 100644 --- a/src/common/malloc.c +++ b/src/common/malloc.c @@ -200,6 +200,8 @@ static struct unit_head_large *unit_head_large_first = NULL; static struct block* block_malloc(unsigned short hash); static void block_free(struct block* p); static size_t memmgr_usage_bytes; +static size_t memmgr_usage_bytes_t; + #define block2unit(p, n) ((struct unit_head*)(&(p)->data[ p->unit_size * (n) ])) #define memmgr_assert(v) do { if(!(v)) { ShowError("Memory manager: assertion '" #v "' failed!\n"); } } while(0) @@ -245,6 +247,7 @@ void* _mmalloc(size_t size, const char *file, int line, const char *func ) /* At that time, the distinction by assigning NULL to unit_head.block */ if(hash2size(size_hash) > BLOCK_DATA_SIZE - sizeof(struct unit_head)) { struct unit_head_large* p = (struct unit_head_large*)MALLOC(sizeof(struct unit_head_large)+size,file,line,func); + memmgr_usage_bytes_t += size+sizeof(struct unit_head_large); if(p != NULL) { p->size = size; p->unit_head.block = NULL; @@ -401,6 +404,7 @@ void _mfree(void *ptr, const char *file, int line, const char *func ) head_large->next->prev = head_large->prev; } memmgr_usage_bytes -= head_large->size; + memmgr_usage_bytes_t -= head_large->size + sizeof(struct unit_head_large); #ifdef DEBUG_MEMMGR // set freed memory to 0xfd memset(ptr, 0xfd, head_large->size); @@ -457,6 +461,7 @@ static struct block* block_malloc(unsigned short hash) } else { /* Newly allocated space for the block */ p = (struct block*)MALLOC(sizeof(struct block) * (BLOCK_ALLOC), __FILE__, __LINE__, __func__ ); + memmgr_usage_bytes_t += sizeof(struct block) * (BLOCK_ALLOC); if(p == NULL) { ShowFatalError("Memory manager::block_alloc failed.\n"); exit(EXIT_FAILURE); @@ -650,6 +655,86 @@ static void memmgr_final (void) } #endif /* LOG_MEMMGR */ } +/* [Ind/Hercules] */ +void memmgr_report (int extra) { + struct block *block = block_first; + struct unit_head_large *large = unit_head_large_first; + unsigned int count = 0, size = 0; + int j; + unsigned short msize = 1024; + struct { + const char *file; + unsigned short line; + unsigned int size; + unsigned int count; + } data[100]; + memset(&data, 0, sizeof(data)); + + if( extra != 0 ) + msize = extra; + + while (block) { + if (block->unit_used) { + int i; + for (i = 0; i < block->unit_maxused; i++) { + struct unit_head *head = block2unit(block, i); + if( head->block != NULL && head->size > msize ) { + for( j = 0; j < 100; j++ ) { + if( data[j].file == head->file && data[j].line == head->line ) { + data[j].size += head->size; + data[j].count += 1; + break; + } else if( data[j].size == 0 ) { + data[j].file = head->file; + data[j].line = head->line; + data[j].size = head->size; + data[j].count += 1; + break; + } + } + size += (unsigned int)head->size; + count++; + } + } + } + block = block->block_next; + } + + while(large) { + if( large->size > msize ) { + for( j = 0; j < 100; j++ ) { + if( data[j].file == large->unit_head.file && data[j].line == large->unit_head.line ) { + data[j].size += large->size; + data[j].count += 1; + break; + } else if( data[j].size == 0 ) { + data[j].file = large->unit_head.file; + data[j].line = large->unit_head.line; + data[j].size = large->size; + data[j].count += 1; + break; + } + } + size += (unsigned int)large->size; + count++; + } + large = large->next; + } + for( j = 0; j < 100; j++ ) { + if( data[j].size != 0 ) { + ShowMessage("[malloc] : "CL_WHITE"%s"CL_RESET":"CL_WHITE"%d"CL_RESET" %d instances => %.2f MB\n",data[j].file,data[j].line,data[j].count,(double)((data[j].size)/1024)/1024); + } + } + ShowMessage("[malloc] : reporting %u instances | %.2f MB\n",count,(double)((size)/1024)/1024); + ShowMessage("[malloc] : internal usage %.2f MB | %.2f MB\n",(double)((memmgr_usage_bytes_t-memmgr_usage_bytes)/1024)/1024,(double)((memmgr_usage_bytes_t)/1024)/1024); + + if( extra ) { + ShowMessage("[malloc] : unit_head_large: %d bytes\n",sizeof(struct unit_head_large)); + ShowMessage("[malloc] : unit_head: %d bytes\n",sizeof(struct unit_head)); + ShowMessage("[malloc] : block: %d bytes\n",sizeof(struct block)); + } + +} static void memmgr_init (void) { @@ -677,8 +762,7 @@ void malloc_memory_check(void) /// Returns true if a pointer is valid. /// The check is best-effort, false positives are possible. -bool malloc_verify_ptr(void* ptr) -{ +bool malloc_verify_ptr(void* ptr) { #ifdef USE_MEMMGR return memmgr_verify(ptr) && MEMORY_VERIFY(ptr); #else @@ -687,8 +771,7 @@ bool malloc_verify_ptr(void* ptr) } -size_t malloc_usage (void) -{ +size_t malloc_usage (void) { #ifdef USE_MEMMGR return memmgr_usage (); #else @@ -696,16 +779,16 @@ size_t malloc_usage (void) #endif } -void malloc_final (void) -{ +void malloc_final (void) { #ifdef USE_MEMMGR memmgr_final (); #endif MEMORY_CHECK(); } -void malloc_init (void) -{ +void malloc_init (void) { + memmgr_usage_bytes_t = 0; + memmgr_usage_bytes = 0; #if defined(DMALLOC) && defined(CYGWIN) // http://dmalloc.com/docs/latest/online/dmalloc_19.html dmalloc_debug_setup(getenv("DMALLOC_OPTIONS")); @@ -720,8 +803,7 @@ void malloc_init (void) #endif } -void malloc_defaults() -{ +void malloc_defaults(void) { malloclib = &malloclib_s; malloclib->init = malloc_init; malloclib->final = malloc_final; diff --git a/src/common/malloc.h b/src/common/malloc.h index 34a26b56e..1b8e82bd9 100644 --- a/src/common/malloc.h +++ b/src/common/malloc.h @@ -80,5 +80,7 @@ struct malloc_interface { void (*final) (void); } malloclib_s; +void memmgr_report (int extra); + struct malloc_interface *malloclib; #endif /* _MALLOC_H_ */ diff --git a/src/common/mmo.h b/src/common/mmo.h index d45dea212..a86aba723 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -71,7 +71,6 @@ #define MAX_HOTKEYS 38 #endif -#define MAX_MAP_PER_SERVER 1500 // Increased to allow creation of Instance Maps #define MAX_INVENTORY 100 //Max number of characters per account. Note that changing this setting alone is not enough if the client is not hexed to support more characters as well. #define MAX_CHARS 9 @@ -526,6 +525,10 @@ struct guild { /* TODO: still used for something?|: */ unsigned short save_flag; // for TXT saving + + unsigned short *instance; + unsigned short instances; + void *channel; }; diff --git a/src/common/socket.c b/src/common/socket.c index 5126d231b..0459004cc 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -280,9 +280,10 @@ void set_nonblocking(int fd, unsigned long yes) ShowError("set_nonblocking: Failed to set socket #%d to non-blocking mode (%s) - Please report this!!!\n", fd, error_msg()); } -void setsocketopts(int fd) -{ +void setsocketopts(int fd, struct hSockOpt *opt) { int yes = 1; // reuse fix + struct linger lopt; + #if !defined(WIN32) // set SO_REAUSEADDR to true, unix only. on windows this option causes // the previous owner of the socket to give up, which is not desirable @@ -297,15 +298,22 @@ void setsocketopts(int fd) // The RO protocol is mainly single-packet request/response, plus the FIFO model already does packet grouping anyway. sSetsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(yes)); + if( opt && opt->setTimeo ) { + struct timeval timeout; + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + sSetsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout)); + sSetsockopt(fd,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout)); + } + // force the socket into no-wait, graceful-close mode (should be the default, but better make sure) //(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/closesocket_2.asp) - { - struct linger opt; - opt.l_onoff = 0; // SO_DONTLINGER - opt.l_linger = 0; // Do not care - if( sSetsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(opt)) ) + lopt.l_onoff = 0; // SO_DONTLINGER + lopt.l_linger = 0; // Do not care + if( sSetsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&lopt, sizeof(lopt)) ) ShowWarning("setsocketopts: Unable to set SO_LINGER mode for connection #%d!\n", fd); - } } /*====================================== @@ -404,8 +412,7 @@ void flush_fifos(void) /*====================================== * CORE : Connection functions *--------------------------------------*/ -int connect_client(int listen_fd) -{ +int connect_client(int listen_fd) { int fd; struct sockaddr_in client_address; socklen_t len; @@ -417,20 +424,18 @@ int connect_client(int listen_fd) ShowError("connect_client: accept failed (%s)!\n", error_msg()); return -1; } - if( fd == 0 ) - {// reserved + if( fd == 0 ) { // reserved ShowError("connect_client: Socket #0 is reserved - Please report this!!!\n"); sClose(fd); return -1; } - if( fd >= FD_SETSIZE ) - {// socket number too big + if( fd >= FD_SETSIZE ) { // socket number too big ShowError("connect_client: New socket #%d is greater than can we handle! Increase the value of FD_SETSIZE (currently %d) for your OS to fix this!\n", fd, FD_SETSIZE); sClose(fd); return -1; } - setsocketopts(fd); + setsocketopts(fd,NULL); set_nonblocking(fd, 1); #ifndef MINICORE @@ -457,25 +462,22 @@ int make_listen_bind(uint32 ip, uint16 port) fd = sSocket(AF_INET, SOCK_STREAM, 0); - if( fd == -1 ) - { + if( fd == -1 ) { ShowError("make_listen_bind: socket creation failed (%s)!\n", error_msg()); exit(EXIT_FAILURE); } - if( fd == 0 ) - {// reserved + if( fd == 0 ) { // reserved ShowError("make_listen_bind: Socket #0 is reserved - Please report this!!!\n"); sClose(fd); return -1; } - if( fd >= FD_SETSIZE ) - {// socket number too big + if( fd >= FD_SETSIZE ) { // socket number too big ShowError("make_listen_bind: New socket #%d is greater than can we handle! Increase the value of FD_SETSIZE (currently %d) for your OS to fix this!\n", fd, FD_SETSIZE); sClose(fd); return -1; } - setsocketopts(fd); + setsocketopts(fd,NULL); set_nonblocking(fd, 1); server_address.sin_family = AF_INET; @@ -503,7 +505,7 @@ int make_listen_bind(uint32 ip, uint16 port) return fd; } -int make_connection(uint32 ip, uint16 port, bool silent) { +int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt) { struct sockaddr_in remote_address; int fd; int result; @@ -514,31 +516,29 @@ int make_connection(uint32 ip, uint16 port, bool silent) { ShowError("make_connection: socket creation failed (%s)!\n", error_msg()); return -1; } - if( fd == 0 ) - {// reserved + if( fd == 0 ) {// reserved ShowError("make_connection: Socket #0 is reserved - Please report this!!!\n"); sClose(fd); return -1; } - if( fd >= FD_SETSIZE ) - {// socket number too big + if( fd >= FD_SETSIZE ) {// socket number too big ShowError("make_connection: New socket #%d is greater than can we handle! Increase the value of FD_SETSIZE (currently %d) for your OS to fix this!\n", fd, FD_SETSIZE); sClose(fd); return -1; } - setsocketopts(fd); + setsocketopts(fd,opt); remote_address.sin_family = AF_INET; remote_address.sin_addr.s_addr = htonl(ip); remote_address.sin_port = htons(port); - if( !silent ) + if( !( opt && opt->silent ) ) ShowStatus("Connecting to %d.%d.%d.%d:%i\n", CONVIP(ip), port); result = sConnect(fd, (struct sockaddr *)(&remote_address), sizeof(struct sockaddr_in)); if( result == SOCKET_ERROR ) { - if( !silent ) + if( !( opt && opt->silent ) ) ShowError("make_connection: connect failed (socket #%d, %s)!\n", fd, error_msg()); do_close(fd); return -1; diff --git a/src/common/socket.h b/src/common/socket.h index 4879cb109..b58cbdccf 100644 --- a/src/common/socket.h +++ b/src/common/socket.h @@ -1,5 +1,6 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// Portions Copyright (c) Athena Dev Teams #ifndef _SOCKET_H_ #define _SOCKET_H_ @@ -48,6 +49,9 @@ } \ } while(0) +/* [Ind/Hercules] */ +#define RFIFO2PTR(fd,len) (void*)(session[fd]->rdata + len) + // buffer I/O macros #define RBUFP(p,pos) (((uint8*)(p)) + (pos)) #define RBUFB(p,pos) (*(uint8*)RBUFP((p),(pos))) @@ -94,6 +98,10 @@ struct socket_data void* session_data; // stores application-specific data related to the session }; +struct hSockOpt { + unsigned int silent : 1; + unsigned int setTimeo : 1; +}; // Data prototype declaration @@ -113,7 +121,7 @@ extern bool session_isActive(int fd); // Function prototype declaration int make_listen_bind(uint32 ip, uint16 port); -int make_connection(uint32 ip, uint16 port, bool silent); +int make_connection(uint32 ip, uint16 port, struct hSockOpt *opt); int realloc_fifo(int fd, unsigned int rfifo_size, unsigned int wfifo_size); int realloc_writefifo(int fd, size_t addition); int WFIFOSET(int fd, size_t len); diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 65da7aa24..796447633 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -3940,7 +3940,7 @@ ACMD(mapinfo) { for (i = 0; i < map[m_id].npc_num;) { nd = map[m_id].npc[i]; - switch(nd->ud.dir) { + switch(nd->dir) { case 0: strcpy(direction, msg_txt(1101)); break; // North case 1: strcpy(direction, msg_txt(1102)); break; // North West case 2: strcpy(direction, msg_txt(1103)); break; // West diff --git a/src/map/battleground.c b/src/map/battleground.c index 618679406..4bb6035ad 100644 --- a/src/map/battleground.c +++ b/src/map/battleground.c @@ -9,6 +9,7 @@ #include "../common/showmsg.h" #include "../common/socket.h" #include "../common/strlib.h" +#include "../common/conf.h" #include "battleground.h" #include "battle.h" @@ -16,6 +17,7 @@ #include "map.h" #include "npc.h" #include "pc.h" +#include "party.h" #include "pet.h" #include "homunculus.h" #include "mercenary.h" @@ -26,14 +28,12 @@ static DBMap* bg_team_db; // int bg_id -> struct battleground_data* static unsigned int bg_team_counter = 0; // Next bg_id -struct battleground_data* bg_team_search(int bg_id) -{ // Search a BG Team using bg_id +struct battleground_data* bg_team_search(int bg_id) { // Search a BG Team using bg_id if( !bg_id ) return NULL; return (struct battleground_data *)idb_get(bg_team_db, bg_id); } -struct map_session_data* bg_getavailablesd(struct battleground_data *bg) -{ +struct map_session_data* bg_getavailablesd(struct battleground_data *bg) { int i; nullpo_retr(NULL, bg); ARR_FIND(0, MAX_BG_MEMBERS, i, bg->members[i].sd != NULL); @@ -95,8 +95,7 @@ int bg_team_join(int bg_id, struct map_session_data *sd) guild->send_dot_remove(sd); - for( i = 0; i < MAX_BG_MEMBERS; i++ ) - { + for( i = 0; i < MAX_BG_MEMBERS; i++ ) { if( (pl_sd = bg->members[i].sd) != NULL && pl_sd != sd ) clif->hpmeter_single(sd->fd, pl_sd->bl.id, pl_sd->battle_status.hp, pl_sd->battle_status.max_hp); } @@ -152,8 +151,7 @@ int bg_member_respawn(struct map_session_data *sd) return 1; // Warped } -int bg_create(unsigned short mapindex, short rx, short ry, const char *ev, const char *dev) -{ +int bg_create(unsigned short mapindex, short rx, short ry, const char *ev, const char *dev) { struct battleground_data *bg; bg_team_counter++; @@ -226,12 +224,10 @@ int bg_send_xy_timer_sub(DBKey key, DBData *data, va_list ap) struct map_session_data *sd; int i; nullpo_ret(bg); - for( i = 0; i < MAX_BG_MEMBERS; i++ ) - { + for( i = 0; i < MAX_BG_MEMBERS; i++ ) { if( (sd = bg->members[i].sd) == NULL ) continue; - if( sd->bl.x != bg->members[i].x || sd->bl.y != bg->members[i].y ) - { // xy update + if( sd->bl.x != bg->members[i].x || sd->bl.y != bg->members[i].y ) { // xy update bg->members[i].x = sd->bl.x; bg->members[i].y = sd->bl.y; clif->bg_xy(sd); @@ -240,20 +236,496 @@ int bg_send_xy_timer_sub(DBKey key, DBData *data, va_list ap) return 0; } -int bg_send_xy_timer(int tid, unsigned int tick, int id, intptr_t data) -{ +int bg_send_xy_timer(int tid, unsigned int tick, int id, intptr_t data) { bg_team_db->foreach(bg_team_db, bg_send_xy_timer_sub, tick); return 0; } +void bg_config_read(void) { + config_t bg_conf; + config_setting_t *data = NULL; + const char *config_filename = "conf/battlegrounds.conf"; // FIXME hardcoded name + + if (conf_read_file(&bg_conf, config_filename)) + return; + + data = config_lookup(&bg_conf, "battlegrounds"); + + if (data != NULL) { + config_setting_t *settings = config_setting_get_elem(data, 0); + config_setting_t *arenas; + const char *delay_var; + int i, arena_count = 0, total = 0, offline = 0; + + if( !config_setting_lookup_string(settings, "global_delay_var", &delay_var) ) + delay_var = "BG_Delay_Tick"; + + safestrncpy(bg->gdelay_var, delay_var, BG_DELAY_VAR_LENGTH); + + config_setting_lookup_int(settings, "maximum_afk_seconds", &bg->mafksec); + + config_setting_lookup_bool(settings, "feature_off", &offline); + + if( offline == 0 ) + bg->queue_on = true; + + if( (arenas = config_setting_get_member(settings, "arenas")) != NULL ) { + arena_count = config_setting_length(arenas); + CREATE( bg->arena, struct bg_arena *, arena_count ); + for(i = 0; i < arena_count; i++) { + config_setting_t *arena = config_setting_get_elem(arenas, i); + config_setting_t *reward; + const char *aName, *aEvent, *aDelayVar; + int minLevel = 0, maxLevel = 0; + int prizeWin, prizeLoss, prizeDraw; + int minPlayers, maxPlayers, minTeamPlayers; + int maxDuration; + int fillup_duration, pregame_duration; + + bg->arena[i] = NULL; + + if( !config_setting_lookup_string(arena, "name", &aName) ) { + ShowError("bg_config_read: failed to find 'name' for arena #%d\n",i); + continue; + } + + if( !config_setting_lookup_string(arena, "event", &aEvent) ) { + ShowError("bg_config_read: failed to find 'event' for arena #%d\n",i); + continue; + } + + config_setting_lookup_int(arena, "minLevel", &minLevel); + config_setting_lookup_int(arena, "maxLevel", &maxLevel); + + if( minLevel < 0 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' minLevel\n",minLevel,aName); + minLevel = 0; + } + if( maxLevel > MAX_LEVEL ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' maxLevel\n",maxLevel,aName); + maxLevel = MAX_LEVEL; + } + + if( !(reward = config_setting_get_member(settings, "reward")) ) { + ShowError("bg_config_read: failed to find 'reward' for arena '%s'/#%d\n",aName,i); + continue; + } + + config_setting_lookup_int(reward, "win", &prizeWin); + config_setting_lookup_int(reward, "loss", &prizeLoss); + config_setting_lookup_int(reward, "draw", &prizeDraw); + + if( prizeWin < 0 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' reward:win\n",prizeWin,aName); + prizeWin = 0; + } + if( prizeLoss < 0 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' reward:loss\n",prizeLoss,aName); + prizeLoss = 0; + } + if( prizeDraw < 0 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' reward:draw\n",prizeDraw,aName); + prizeDraw = 0; + } + + config_setting_lookup_int(arena, "minPlayers", &minPlayers); + config_setting_lookup_int(arena, "maxPlayers", &maxPlayers); + config_setting_lookup_int(arena, "minTeamPlayers", &minTeamPlayers); + + if( minPlayers < 0 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' minPlayers\n",minPlayers,aName); + minPlayers = 0; + } + if( maxPlayers > MAX_BG_MEMBERS * 2 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' maxPlayers, change #define MAX_BG_MEMBERS\n",maxPlayers,aName); + maxPlayers = 0; + } + if( minTeamPlayers < 0 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' minTeamPlayers\n",minTeamPlayers,aName); + minTeamPlayers = 0; + } + + if( !config_setting_lookup_string(arena, "delay_var", &aDelayVar) ) { + ShowError("bg_config_read: failed to find 'delay_var' for arena '%s'/#%d\n",aName,i); + continue; + } + + config_setting_lookup_int(arena, "maxDuration", &maxDuration); + + if( maxDuration < 0 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' maxDuration\n",maxDuration,aName); + maxDuration = 30; + } + + config_setting_lookup_int(arena, "fillDuration", &fillup_duration); + config_setting_lookup_int(arena, "pGameDuration", &pregame_duration); + + if( fillup_duration < 20 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' fillDuration, minimum has to be 20, defaulting to 20.\n",fillup_duration,aName); + fillup_duration = 20; + } + + if( pregame_duration < 20 ) { + ShowWarning("bg_config_read: invalid %d value for arena '%s' pGameDuration, minimum has to be 20, defaulting to 20.\n",pregame_duration,aName); + pregame_duration = 20; + } + + + CREATE( bg->arena[i], struct bg_arena, 1 ); + + bg->arena[i]->id = i; + safestrncpy(bg->arena[i]->name, aName, NAME_LENGTH); + safestrncpy(bg->arena[i]->npc_event, aEvent, EVENT_NAME_LENGTH); + bg->arena[i]->min_level = minLevel; + bg->arena[i]->max_level = maxLevel; + bg->arena[i]->prize_win = prizeWin; + bg->arena[i]->prize_loss = prizeLoss; + bg->arena[i]->prize_draw = prizeDraw; + bg->arena[i]->min_players = minPlayers; + bg->arena[i]->max_players = maxPlayers; + bg->arena[i]->min_team_players = minTeamPlayers; + safestrncpy(bg->arena[i]->delay_var, aDelayVar, NAME_LENGTH); + bg->arena[i]->maxDuration = maxDuration; + bg->arena[i]->queue_id = -1; + bg->arena[i]->begin_timer = INVALID_TIMER; + bg->arena[i]->fillup_timer = INVALID_TIMER; + bg->arena[i]->pregame_duration = pregame_duration; + bg->arena[i]->fillup_duration = fillup_duration; + + total++; + } + bg->arenas = arena_count; + } + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' arenas in '"CL_WHITE"%s"CL_RESET"'.\n", total, config_filename); + config_destroy(&bg_conf); + } +} +struct bg_arena *bg_name2arena (char *name) { + int i; + for(i = 0; i < bg->arenas; i++) { + if( strcmpi(bg->arena[i]->name,name) == 0 ) + return bg->arena[i]; + } + return NULL; +} +int bg_id2pos ( int queue_id, int account_id ) { + struct hQueue *queue = script->queue(queue_id); + if( queue ) { + int i; + for(i = 0; i < queue->items; i++ ) { + if( queue->item[i] == account_id ) { + return i; + } + } + } + return 0; +} +void bg_queue_player_cleanup(struct map_session_data *sd) { + if ( sd->bg_queue.client_has_bg_data ) { + clif->bgqueue_notice_delete(sd,BGQND_CLOSEWINDOW, sd->bg_queue.arena->id); + } + script->queue_remove(sd->bg_queue.arena->queue_id,sd->status.account_id); + sd->bg_queue.arena = NULL; + sd->bg_queue.ready = 0; + sd->bg_queue.client_has_bg_data = 0; + sd->bg_queue.type = 0; +} +void bg_match_over(struct bg_arena *arena, bool canceled) { + struct hQueue *queue = &script->hq[arena->queue_id]; + int i;//, count = 0; + + /* if( !canceled ) <check time/score> */ + + for( i = 0; i < queue->items; i++ ) { + struct map_session_data * sd = NULL; + + if( ( sd = map_id2sd(queue->item[i]) ) ) { + bg->queue_pc_cleanup(sd); + clif->colormes(sd->fd,COLOR_RED,"BG Match Cancelled: not enough players"); + } + } -void do_init_battleground(void) -{ + bg->arena[i]->begin_timer = INVALID_TIMER; + bg->arena[i]->fillup_timer = INVALID_TIMER; + /* reset queue */ +} +void bg_begin(struct bg_arena *arena) { + struct hQueue *queue = &script->hq[arena->queue_id]; + int i, count = 0; + + for( i = 0; i < queue->items; i++ ) { + struct map_session_data * sd = NULL; + + if( ( sd = map_id2sd(queue->item[i]) ) ) { + if( sd->bg_queue.ready == 1 ) + count++; + else + bg->queue_pc_cleanup(sd); + } + } + + if( count < arena->min_players ) { + bg_match_over(arena,true); + } else { + ; + /* we split evenly? */ + /* but if a party of say 10 joins, it cant be split evenly unless by luck there are 10 soloers in the queue besides them */ + /* not sure how to split T_T needs more info */ + } +} +int bg_begin_timer(int tid, unsigned int tick, int id, intptr_t data) { + bg->begin(bg->arena[id]); + return 0; +} + +void bg_queue_pregame(struct bg_arena *arena) { + struct hQueue *queue = &script->hq[arena->queue_id]; + int i; + + for( i = 0; i < queue->items; i++ ) { + struct map_session_data * sd = NULL; + + if( ( sd = map_id2sd(queue->item[i]) ) ) { + clif->bgqueue_battlebegins(sd,arena->id,SELF); + } + } + arena->begin_timer = add_timer( gettick() + (arena->pregame_duration*1000), bg->begin_timer, arena->id, 0 ); +} +int bg_fillup_timer(int tid, unsigned int tick, int id, intptr_t data) { + bg->queue_pregame(bg->arena[id]); + return 0; +} + +void bg_queue_check(struct bg_arena *arena) { + int count = script->hq[arena->queue_id].items; + + if( count == arena->max_players ) { + if( arena->fillup_timer != INVALID_TIMER ) { + delete_timer(arena->fillup_timer,bg_fillup_timer); + arena->fillup_timer = INVALID_TIMER; + } + bg->queue_pregame(arena); + } else if( count >= arena->min_players && arena->fillup_timer == INVALID_TIMER ) { + arena->fillup_timer = add_timer( gettick() + (arena->fillup_duration*1000), bg->fillup_timer, arena->id, 0 ); + } +} +void bg_queue_add(struct map_session_data *sd, struct bg_arena *arena, enum bg_queue_types type) { + enum BATTLEGROUNDS_QUEUE_ACK result = bg->can_queue(sd,arena,type); + struct hQueue *queue; + int i, count = 0; + + if( arena->begin_timer != INVALID_TIMER ) { + clif->bgqueue_ack(sd,BGQA_FAIL_QUEUING_FINISHED,arena->id); + return; + } + + if( result != BGQA_SUCCESS ) { + clif->bgqueue_ack(sd,result,arena->id); + return; + } + + switch( type ) { /* guild/party already validated in can_queue */ + case BGQT_PARTY: { + struct party_data *p = party_search(sd->status.party_id); + for( i = 0; i < MAX_PARTY; i++ ) { + if( !p->data[i].sd || p->data[i].sd->bg_queue.arena != NULL ) continue; + count++; + } + } + break; + case BGQT_GUILD: + for ( i=0; i<sd->guild->max_member; i++ ) { + if ( !sd->guild->member[i].sd || sd->guild->member[i].sd->bg_queue.arena != NULL ) + continue; + count++; + } + break; + case BGQT_INDIVIDUAL: + count = 1; + break; + } + + if( !(queue = script->queue(arena->queue_id)) || (queue->items+count) >= arena->max_players ) { + clif->bgqueue_ack(sd,BGQA_FAIL_PPL_OVERAMOUNT,arena->id); + return; + } + + switch( type ) { + case BGQT_INDIVIDUAL: + sd->bg_queue.type = type; + sd->bg_queue.arena = arena; + sd->bg_queue.ready = 0; + script->queue_add(arena->queue_id,sd->status.account_id); + clif->bgqueue_joined(sd,script->hq[arena->queue_id].items); + clif->bgqueue_update_info(sd,arena->id,script->hq[arena->queue_id].items); + break; + case BGQT_PARTY: { + struct party_data *p = party_search(sd->status.party_id); + for( i = 0; i < MAX_PARTY; i++ ) { + if( !p->data[i].sd || p->data[i].sd->bg_queue.arena != NULL ) continue; + p->data[i].sd->bg_queue.type = type; + p->data[i].sd->bg_queue.arena = arena; + p->data[i].sd->bg_queue.ready = 0; + script->queue_add(arena->queue_id,p->data[i].sd->status.account_id); + clif->bgqueue_joined(p->data[i].sd,script->hq[arena->queue_id].items); + clif->bgqueue_update_info(p->data[i].sd,arena->id,script->hq[arena->queue_id].items); + } + } + break; + case BGQT_GUILD: + for ( i=0; i<sd->guild->max_member; i++ ) { + if ( !sd->guild->member[i].sd || sd->guild->member[i].sd->bg_queue.arena != NULL ) + continue; + sd->guild->member[i].sd->bg_queue.type = type; + sd->guild->member[i].sd->bg_queue.arena = arena; + sd->guild->member[i].sd->bg_queue.ready = 0; + script->queue_add(arena->queue_id,sd->guild->member[i].sd->status.account_id); + clif->bgqueue_joined(sd->guild->member[i].sd,script->hq[arena->queue_id].items); + clif->bgqueue_update_info(sd->guild->member[i].sd,arena->id,script->hq[arena->queue_id].items); + } + break; + } + + clif->bgqueue_ack(sd,BGQA_SUCCESS,arena->id); + +} +enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, struct bg_arena *arena, enum bg_queue_types type) { + int tick; + unsigned int tsec; + if ( sd->status.base_level > arena->max_level || sd->status.base_level < arena->max_level ) + return BGQA_FAIL_LEVEL_INCORRECT; + + if ( !(sd->class_&JOBL_2) ) /* TODO: maybe make this a per-arena setting, so users may make custom arenas like baby-only,whatever. */ + return BGQA_FAIL_CLASS_INVALID; + + tsec = time(NULL); + + if ( ( tick = pc_readglobalreg(sd, bg->gdelay_var) ) && tsec < tick ) { + char response[100]; + if( (tick-tsec) > 60 ) + sprintf(response, "You are a deserter! Wait %d minute(s) before you can apply again",(tick-tsec)/60); + else + sprintf(response, "You are a deserter! Wait %d seconds before you can apply again",(tick-tsec)); + clif->colormes(sd->fd,COLOR_RED,response); + return BGQA_FAIL_DESERTER; + } + + if ( ( tick = pc_readglobalreg(sd, arena->cooldown_variable) ) && tsec < tick ) { + char response[100]; + if( (tick-tsec) > 60 ) + sprintf(response, "You can't reapply to this arena so fast. Apply to the different arena or wait %d minute(s)",(tick-tsec)/60); + else + sprintf(response, "You can't reapply to this arena so fast. Apply to the different arena or wait %d seconds",(tick-tsec)); + clif->colormes(sd->fd,COLOR_RED,response); + return BGQA_FAIL_COOLDOWN; + } + + if( sd->bg_queue.arena != NULL ) + return BGQA_DUPLICATE_REQUEST; + + switch(type) { + case BGQT_GUILD: + if( !sd->guild || !sd->state.gmaster_flag ) + return BGQA_NOT_PARTY_GUILD_LEADER; + else { + int i, count = 0; + for ( i=0; i<sd->guild->max_member; i++ ) { + if ( !sd->guild->member[i].sd || sd->guild->member[i].sd->bg_queue.arena != NULL ) + continue; + count++; + } + if ( count < arena->min_team_players ) { + char response[100]; + if( count != sd->guild->connect_member && sd->guild->connect_member >= arena->min_team_players ) + sprintf(response, "Can't apply: not enough members in your team/guild that have not entered the queue in individual mode, minimum is %d",arena->min_team_players); + else + sprintf(response, "Can't apply: not enough members in your team/guild, minimum is %d",arena->min_team_players); + clif->colormes(sd->fd,COLOR_RED,response); + return BGQA_FAIL_TEAM_COUNT; + } + } + break; + case BGQT_PARTY: + if( !sd->status.party_id ) + return BGQA_NOT_PARTY_GUILD_LEADER; + else { + struct party_data *p; + if( (p = party_search(sd->status.party_id) ) ) { + int i, count = 0; + bool is_leader = false; + + for(i = 0; i < MAX_PARTY; i++) { + if( !p->data[i].sd ) + continue; + if( p->party.member[i].leader && sd == p->data[i].sd ) + is_leader = true; + if( p->data[i].sd->bg_queue.arena == NULL ) + count++; + } + + if( !is_leader ) + return BGQA_NOT_PARTY_GUILD_LEADER; + + if( count < arena->min_team_players ) { + char response[100]; + if( count != p->party.count && p->party.count >= arena->min_team_players ) + sprintf(response, "Can't apply: not enough members in your team/party that have not entered the queue in individual mode, minimum is %d",arena->min_team_players); + else + sprintf(response, "Can't apply: not enough members in your team/party, minimum is %d",arena->min_team_players); + clif->colormes(sd->fd,COLOR_RED,response); + return BGQA_FAIL_TEAM_COUNT; + } + + } else + return BGQA_NOT_PARTY_GUILD_LEADER; + } + break; + case BGQT_INDIVIDUAL:/* already did */ + break; + default: + ShowDebug("bg_canqueue: unknown/unsupported type %d\n",type); + return BGQA_DUPLICATE_REQUEST; + } + + return BGQA_SUCCESS; +} +void do_init_battleground(void) { bg_team_db = idb_alloc(DB_OPT_RELEASE_DATA); add_timer_func_list(bg_send_xy_timer, "bg_send_xy_timer"); add_timer_interval(gettick() + battle_config.bg_update_interval, bg_send_xy_timer, 0, 0, battle_config.bg_update_interval); } -void do_final_battleground(void) -{ +void do_final_battleground(void) { + int i; + bg_team_db->destroy(bg_team_db, NULL); + + for( i = 0; i < bg->arenas; i++ ) { + if( bg->arena[i] ) + aFree(bg->arena[i]); + } + + if( bg->arena ) + aFree(bg->arena); +} +void battleground_defaults(void) { + bg = &bg_s; + + bg->queue_on = false; + + bg->mafksec = 0; + bg->arena = NULL; + bg->arenas = 0; + /* */ + bg->name2arena = bg_name2arena; + bg->queue_add = bg_queue_add; + bg->can_queue = bg_canqueue; + bg->id2pos = bg_id2pos; + bg->queue_pc_cleanup = bg_queue_player_cleanup; + bg->begin = bg_begin; + bg->begin_timer = bg_begin_timer; + bg->queue_pregame = bg_queue_pregame; + bg->fillup_timer = bg_fillup_timer; + /* */ + bg->config_read = bg_config_read; } diff --git a/src/map/battleground.h b/src/map/battleground.h index c2b74a534..8fe9f3b77 100644 --- a/src/map/battleground.h +++ b/src/map/battleground.h @@ -1,5 +1,6 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// Portions Copyright (c) Athena Dev Teams #ifndef _BATTLEGROUND_H_ #define _BATTLEGROUND_H_ @@ -7,7 +8,21 @@ #include "../common/mmo.h" // struct party #include "guild.h" +/** + * Defines + **/ #define MAX_BG_MEMBERS 30 +#define BG_DELAY_VAR_LENGTH 30 + +/** + * Enumerations + **/ +enum bg_queue_types { + BGQT_INVALID, + BGQT_INDIVIDUAL, + BGQT_PARTY, + BGQT_GUILD +}; struct battleground_member_data { unsigned short x, y; @@ -42,4 +57,51 @@ int bg_team_warp(int bg_id, unsigned short mapindex, short x, short y); int bg_member_respawn(struct map_session_data *sd); int bg_send_message(struct map_session_data *sd, const char *mes, int len); +struct bg_arena { + char name[NAME_LENGTH]; + unsigned char id; + char npc_event[EVENT_NAME_LENGTH]; + short min_level, max_level; + short prize_win, prize_loss, prize_draw; + short min_players; + short max_players; + short min_team_players; + char cooldown_variable[NAME_LENGTH]; + char delay_var[NAME_LENGTH]; + unsigned short maxDuration; + int queue_id; + int begin_timer; + int fillup_timer; + int game_timer; + unsigned short fillup_duration; + unsigned short pregame_duration; +}; + +/* battleground.c interface (incomplete) */ +struct battleground_interface { + bool queue_on; + /* */ + int mafksec; + char gdelay_var[BG_DELAY_VAR_LENGTH]; + /* */ + struct bg_arena **arena; + unsigned char arenas; + /* */ + struct bg_arena *(*name2arena) (char *name); + void (*queue_add) (struct map_session_data *sd, struct bg_arena *arena, enum bg_queue_types type); + enum BATTLEGROUNDS_QUEUE_ACK (*can_queue) (struct map_session_data *sd, struct bg_arena *arena, enum bg_queue_types type); + int (*id2pos) (int queue_id, int account_id); + void (*queue_pc_cleanup) (struct map_session_data *sd); + void (*begin) (struct bg_arena *arena); + int (*begin_timer) (int tid, unsigned int tick, int id, intptr_t data); + void (*queue_pregame) (struct bg_arena *arena); + int (*fillup_timer) (int tid, unsigned int tick, int id, intptr_t data); + /* */ + void (*config_read) (void); +} bg_s; + +struct battleground_interface *bg; + +void battleground_defaults(void); + #endif /* _BATTLEGROUND_H_ */ diff --git a/src/map/chrif.c b/src/map/chrif.c index 06956e731..ee2e252c1 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -344,9 +344,9 @@ int chrif_sendmap(int fd) { ShowStatus("Sending maps to char server...\n"); // Sending normal maps, not instances - WFIFOHEAD(fd, 4 + instance_start * 4); + WFIFOHEAD(fd, 4 + instance->start_id * 4); WFIFOW(fd,0) = 0x2afa; - for(i = 0; i < instance_start; i++) + for(i = 0; i < instance->start_id; i++) WFIFOW(fd,4+i*4) = map[i].index; WFIFOW(fd,2) = 4 + i * 4; WFIFOSET(fd,WFIFOW(fd,2)); @@ -1537,9 +1537,8 @@ static int check_connect_char_server(int tid, unsigned int tick, int id, intptr_ } chrif_state = 0; - char_fd = make_connection(char_ip, char_port,false); - if (char_fd == -1)//Attempt to connect later. [Skotlex] + if ( ( char_fd = make_connection(char_ip, char_port,NULL) ) == -1) //Attempt to connect later. [Skotlex] return 0; session[char_fd]->func_parse = chrif_parse; diff --git a/src/map/clif.c b/src/map/clif.c index 2f69ce2fd..99b4d8c11 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1333,7 +1333,7 @@ int clif_spawn(struct block_list *bl) /** * Hide NPC from maya purple card. **/ - if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) + if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->option&OPTION_INVISIBLE)) return 0; clif->spawn_unit(bl,AREA_WOS); @@ -1612,7 +1612,7 @@ void clif_move(struct unit_data *ud) /** * Hide NPC from maya purple card. **/ - if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) + if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->option&OPTION_INVISIBLE)) return; if (ud->state.speed_changed) { @@ -1666,15 +1666,14 @@ void clif_quitsave(int fd,struct map_session_data *sd) { /// 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) -{ +void clif_changemap(struct map_session_data *sd, short m, 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)); + mapindex_getmapname_ext(map[m].cName ? map[m].cName : map[m].name, (char*)WFIFOP(fd,2)); WFIFOW(fd,18) = x; WFIFOW(fd,20) = y; WFIFOSET(fd,packet_len(0x91)); @@ -1683,8 +1682,7 @@ void clif_changemap(struct map_session_data *sd, short map, int x, int y) /// 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) -{ +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; @@ -4344,7 +4342,7 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl) { /** * Hide NPC from maya purple card. **/ - if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) + if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->option&OPTION_INVISIBLE)) return; if ( ( ud = unit_bl2ud(bl) ) && ud->walktimer != INVALID_TIMER ) @@ -4601,16 +4599,13 @@ void clif_changemapcell(int fd, int16 m, int x, int y, int type, enum send_targe WBUFW(buf,2) = x; WBUFW(buf,4) = y; WBUFW(buf,6) = type; - mapindex_getmapname_ext(map[m].name,(char*)WBUFP(buf,8)); + mapindex_getmapname_ext(map[m].cName ? map[m].cName : map[m].name,(char*)WBUFP(buf,8)); - if( fd ) - { + if( fd ) { WFIFOHEAD(fd,packet_len(0x192)); memcpy(WFIFOP(fd,0), buf, packet_len(0x192)); WFIFOSET(fd,packet_len(0x192)); - } - else - { + } else { struct block_list dummy_bl; dummy_bl.type = BL_NUL; dummy_bl.x = x; @@ -4799,7 +4794,7 @@ int clif_outsight(struct block_list *bl,va_list ap) clif->clearchar_skillunit((struct skill_unit *)bl,tsd->fd); break; case BL_NPC: - if( !(((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE) ) + if( !(((TBL_NPC*)bl)->option&OPTION_INVISIBLE) ) clif->clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd); break; default: @@ -4810,7 +4805,7 @@ int clif_outsight(struct block_list *bl,va_list ap) } 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))) + !(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->option&OPTION_INVISIBLE))) clif->clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd); } return 0; @@ -6529,7 +6524,7 @@ void clif_party_member_info(struct party_data *p, struct map_session_data *sd) 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)); + mapindex_getmapname_ext(map[sd->bl.m].cName ? map[sd->bl.m].cName : 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); @@ -8485,7 +8480,7 @@ 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->changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y); clif->inventorylist(sd); if(pc_iscarton(sd)) { clif->cartlist(sd); @@ -9328,7 +9323,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) if (sd->state.rewarp) { //Rewarp player. sd->state.rewarp = 0; - clif->changemap(sd, sd->mapindex, sd->bl.x, sd->bl.y); + clif->changemap(sd, sd->bl.m, sd->bl.x, sd->bl.y); return; } @@ -9372,9 +9367,9 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) 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); + if( map[sd->bl.m].instance_id >= 0 ) { + instances[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] @@ -15573,48 +15568,61 @@ void clif_font(struct map_session_data *sd) /*========================================== * Instancing Window *------------------------------------------*/ -int clif_instance(int instance_id, int type, int flag) -{ +int clif_instance(int instance_id, int type, int flag) { struct map_session_data *sd; - struct party_data *p; unsigned char buf[255]; + enum send_target target = PARTY; + + switch( instances[instance_id].owner_type ) { + case IOT_NONE: + return 0; + case IOT_GUILD: + target = GUILD; + sd = guild->getavailablesd(guild->search(instances[instance_id].owner_id)); + break; + case IOT_PARTY: + /* default is already PARTY */ + sd = party_getavailablesd(party_search(instances[instance_id].owner_id)); + break; + case IOT_CHAR: + target = SELF; + sd = map_id2sd(instances[instance_id].owner_id); + break; + } - if( (p = party_search(instance[instance_id].party_id)) == NULL || (sd = party_getavailablesd(p)) == NULL ) + if( !sd ) 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); + memcpy(WBUFP(buf,2),instances[instance_id].name,INSTANCE_NAME_LENGTH); WBUFW(buf,63) = flag; - clif->send(buf,packet_len(0x02CB),&sd->bl,PARTY); + clif->send(buf,packet_len(0x02CB),&sd->bl,target); 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); + clif->send(buf,packet_len(0x02CC),&sd->bl,target); 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; + memcpy(WBUFP(buf,2),instances[instance_id].name,61); + if( type == 3 ) { + WBUFL(buf,63) = instances[instance_id].progress_timeout; WBUFL(buf,67) = 0; - } - else - { + } else { WBUFL(buf,63) = 0; - WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout; + WBUFL(buf,67) = instances[instance_id].idle_timeout; } - clif->send(buf,packet_len(0x02CD),&sd->bl,PARTY); + clif->send(buf,packet_len(0x02CD),&sd->bl,target); break; case 5: // S 0x2ce <Message ID>.L @@ -15626,7 +15634,7 @@ int clif_instance(int instance_id, int type, int flag) WBUFW(buf,0) = 0x02CE; WBUFL(buf,2) = flag; //WBUFL(buf,6) = EnterLimitDate; - clif->send(buf,packet_len(0x02CE),&sd->bl,PARTY); + clif->send(buf,packet_len(0x02CE),&sd->bl,target); break; } return 0; @@ -15634,24 +15642,24 @@ int clif_instance(int instance_id, int type, int flag) void clif_instance_join(int fd, int instance_id) { - if( instance[instance_id].idle_timer != INVALID_TIMER ) { + if( instances[instance_id].idle_timer != INVALID_TIMER ) { WFIFOHEAD(fd,packet_len(0x02CD)); WFIFOW(fd,0) = 0x02CD; - memcpy(WFIFOP(fd,2),instance[instance_id].name,61); + memcpy(WFIFOP(fd,2),instances[instance_id].name,61); WFIFOL(fd,63) = 0; - WFIFOL(fd,67) = (uint32)instance[instance_id].idle_timeout; + WFIFOL(fd,67) = instances[instance_id].idle_timeout; WFIFOSET(fd,packet_len(0x02CD)); - } else if( instance[instance_id].progress_timer != INVALID_TIMER ) { + } else if( instances[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;; + memcpy(WFIFOP(fd,2),instances[instance_id].name,61); + WFIFOL(fd,63) = instances[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); + memcpy(WFIFOP(fd,2),instances[instance_id].name,61); WFIFOW(fd,63) = 0; WFIFOSET(fd,packet_len(0x02CB)); } @@ -16949,6 +16957,124 @@ void clif_status_change_end(struct block_list *bl, int tid, enum send_target tar clif->send(&p,sizeof(p), bl, target); } +void clif_bgqueue_ack(struct map_session_data *sd, enum BATTLEGROUNDS_QUEUE_ACK response, unsigned char arena_id) { + + switch (response) { + case BGQA_FAIL_COOLDOWN: + case BGQA_FAIL_DESERTER: + case BGQA_FAIL_TEAM_COUNT: + break; + default: { + struct packet_bgqueue_ack p; + + p.PacketType = bgqueue_ackType; + p.type = response; + safestrncpy(p.bg_name, bg->arena[arena_id]->name, sizeof(p.bg_name)); + + clif->send(&p,sizeof(p), &sd->bl, SELF); + } + break; + } +} + + +void clif_bgqueue_notice_delete(struct map_session_data *sd, enum BATTLEGROUNDS_QUEUE_NOTICE_DELETED response, unsigned char arena_id) { + struct packet_bgqueue_notice_delete p; + + p.PacketType = bgqueue_notice_deleteType; + p.type = response; + safestrncpy(p.bg_name, bg->arena[arena_id]->name, sizeof(p.bg_name)); + + clif->send(&p,sizeof(p), &sd->bl, SELF); +} + +void clif_parse_bgqueue_register(int fd, struct map_session_data *sd) { + struct packet_bgqueue_register *p = P2PTR(fd, bgqueue_registerType); + struct bg_arena *arena = NULL; + + if( !bg->queue_on ) return; /* temp, until feature is complete */ + + if( !(arena = bg->name2arena(p->bg_name)) ) { + clif->bgqueue_ack(sd,BGQA_FAIL_BGNAME_INVALID,0); + return; + } + + switch( (enum bg_queue_types)p->type ) { + case BGQT_INDIVIDUAL: + case BGQT_PARTY: + case BGQT_GUILD: + break; + default: + clif->bgqueue_ack(sd,BGQA_FAIL_TYPE_INVALID, arena->id); + return; + } + + bg->queue_add(sd, arena, (enum bg_queue_types)p->type); +} + +void clif_bgqueue_update_info(struct map_session_data *sd, unsigned char arena_id, int position) { + struct packet_bgqueue_update_info p; + + p.PacketType = bgqueue_updateinfoType; + safestrncpy(p.bg_name, bg->arena[arena_id]->name, sizeof(p.bg_name)); + p.position = position; + + sd->bg_queue.client_has_bg_data = true; // Client creates bg data when this packet arrives + + clif->send(&p,sizeof(p), &sd->bl, SELF); +} + +void clif_parse_bgqueue_checkstate(int fd, struct map_session_data *sd) { + //struct packet_bgqueue_checkstate *p = P2PTR(fd, bgqueue_checkstateType); /* TODO: bgqueue_notice_delete should use this p->bg_name */ + if( !bg->queue_on ) return; /* temp, until feature is complete */ + if ( sd->bg_queue.arena && sd->bg_queue.type ) { + sd->bg_queue.client_has_bg_data = true; + clif->bgqueue_update_info(sd,sd->bg_queue.arena->id,bg->id2pos(sd->bg_queue.arena->queue_id,sd->status.account_id)); + } else + clif->bgqueue_notice_delete(sd, BGQND_FAIL_NOT_QUEUING,0);/* TODO: wrong response, should respond with p->bg_name not id 0 */ +} + +void clif_parse_bgqueue_revoke_req(int fd, struct map_session_data *sd) { + //struct packet_bgqueue_revoke_req *p = P2PTR(fd, bgqueue_revokereqType); + return; + //bg->queue_leave(sd, p->bg_name); +} + +void clif_parse_bgqueue_battlebegin_ack(int fd, struct map_session_data *sd) { + //struct packet_bgqueue_battlebegin_ack *p = P2PTR(fd, bgqueue_checkstateType); + return; + //if ( p->result == 1 ) + // bg->queue_pc_ready(sd); + //else + // bg->queue_leave(sd, p->bg_name); +} + +void clif_bgqueue_joined(struct map_session_data *sd, int pos) { + struct packet_bgqueue_notify_entry p; + + p.PacketType = bgqueue_notify_entryType; + safestrncpy(p.name,sd->status.name,sizeof(p.name)); + p.position = pos; + + clif->send(&p,sizeof(p), &sd->bl, BG_QUEUE); +} + +void clif_bgqueue_pcleft(struct map_session_data *sd) { + /* no idea */ + return; +} + +// Sends BG ready req to all with same bg arena/type as sd +void clif_bgqueue_battlebegins(struct map_session_data *sd, unsigned char arena_id, enum send_target target) { + struct packet_bgqueue_battlebegins p; + + p.PacketType = bgqueue_battlebegins; + safestrncpy(p.bg_name, bg->arena[arena_id]->name, sizeof(p.bg_name)); + safestrncpy(p.game_name, bg->arena[arena_id]->name, sizeof(p.game_name)); + + clif->send(&p,sizeof(p), &sd->bl, target); +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -17124,6 +17250,7 @@ void clif_bc_ready(void) { int do_init_clif(void) { const char* colors[COLOR_MAX] = { "0xFF0000", "0x00ff00", "0xffffff" }; int i; + /** * Setup Color Table (saves unnecessary load of strtoul on every call) **/ @@ -17613,13 +17740,7 @@ void clif_defaults(void) { /* elemental-related */ clif->elemental_info = clif_elemental_info; clif->elemental_updatestatus = clif_elemental_updatestatus; - /* misc-handling */ - clif->adopt_reply = clif_Adopt_reply; - clif->adopt_request = clif_Adopt_request; - clif->readbook = clif_readbook; - clif->notify_time = clif_notify_time; - clif->user_count = clif_user_count; - clif->noask_sub = clif_noask_sub; + /* Hercules Channel System */ clif->chsys_create = clif_hercules_chsys_create; clif->chsys_msg = clif_hercules_chsys_msg; clif->chsys_msg2 = clif_hercules_chsys_msg2; @@ -17632,6 +17753,20 @@ void clif_defaults(void) { clif->chsys_quitg = clif_hercules_chsys_quitg; clif->chsys_gjoin = clif_hercules_chsys_gjoin; clif->chsys_gleave = clif_hercules_chsys_gleave; + /* bgqueue */ + clif->bgqueue_ack = clif_bgqueue_ack; + clif->bgqueue_notice_delete = clif_bgqueue_notice_delete; + clif->bgqueue_update_info = clif_bgqueue_update_info; + clif->bgqueue_joined = clif_bgqueue_joined; + clif->bgqueue_pcleft = clif_bgqueue_pcleft; + clif->bgqueue_battlebegins = clif_bgqueue_battlebegins; + /* misc-handling */ + clif->adopt_reply = clif_Adopt_reply; + clif->adopt_request = clif_Adopt_request; + clif->readbook = clif_readbook; + clif->notify_time = clif_notify_time; + clif->user_count = clif_user_count; + clif->noask_sub = clif_noask_sub; clif->cashshop_load = clif_cashshop_db; clif->bc_ready = clif_bc_ready; clif->undisguise_timer = clif_undisguise_timer; @@ -17837,6 +17972,11 @@ void clif_defaults(void) { clif->pCashShopReqTab = clif_parse_CashShopReqTab; clif->pCashShopSchedule = clif_parse_CashShopSchedule; clif->pCashShopBuy = clif_parse_CashShopBuy; + /* BGQueue */ + clif->pBGQueueRegister = clif_parse_bgqueue_register; + clif->pBGQueueCheckState = clif_parse_bgqueue_checkstate; + clif->pBGQueueRevokeReq = clif_parse_bgqueue_revoke_req; + clif->pBGQueueBattleBeginAck = clif_parse_bgqueue_battlebegin_ack; /* */ clif->pPartyTick = clif_parse_PartyTick; clif->pGuildInvite2 = clif_parse_GuildInvite2; diff --git a/src/map/clif.h b/src/map/clif.h index 3e3db98c1..4347ad743 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -8,6 +8,7 @@ #include "../common/cbasetypes.h" #include "../common/db.h" #include "../common/mmo.h" +#include "../common/socket.h" #include <stdarg.h> /** @@ -42,6 +43,7 @@ struct eri; * Defines **/ #define packet_len(cmd) packet_db[cmd].len +#define P2PTR(fd,cmd) RFIFO2PTR(fd,packet_db[cmd].len) #define clif_menuskill_clear(sd) (sd)->menuskill_id = (sd)->menuskill_val = (sd)->menuskill_val2 = 0; #define HCHSYS_NAME_LENGTH 20 @@ -86,6 +88,8 @@ typedef enum send_target { BG_SAMEMAP_WOS, BG_AREA, BG_AREA_WOS, + + BG_QUEUE, } send_target; typedef enum emotion_type { @@ -365,6 +369,29 @@ enum CASH_SHOP_BUY_RESULT { CSBR_UNKNOWN = 0xb, }; +enum BATTLEGROUNDS_QUEUE_ACK { + BGQA_SUCCESS = 1, + BGQA_FAIL_QUEUING_FINISHED, + BGQA_FAIL_BGNAME_INVALID, + BGQA_FAIL_TYPE_INVALID, + BGQA_FAIL_PPL_OVERAMOUNT, + BGQA_FAIL_LEVEL_INCORRECT, + BGQA_DUPLICATE_REQUEST, + BGQA_PLEASE_RELOGIN, + BGQA_NOT_PARTY_GUILD_LEADER, + BGQA_FAIL_CLASS_INVALID, + /* not official way to respond (gotta find packet?) */ + BGQA_FAIL_DESERTER, + BGQA_FAIL_COOLDOWN, + BGQA_FAIL_TEAM_COUNT, +}; + +enum BATTLEGROUNDS_QUEUE_NOTICE_DELETED { + BGQND_CLOSEWINDOW = 1, + BGQND_FAIL_BGNAME_WRONG = 3, + BGQND_FAIL_NOT_QUEUING = 11, +}; + /** * Structures **/ @@ -499,7 +526,7 @@ struct clif_interface { /* main unit spawn */ int (*spawn) (struct block_list *bl); /* map-related */ - void (*changemap) (struct map_session_data *sd, short map, int x, int y); + void (*changemap) (struct map_session_data *sd, short m, int x, int y); void (*changemapcell) (int fd, int16 m, int x, int y, int type, enum send_target target); void (*map_property) (struct map_session_data* sd, enum map_property property); void (*pvpset) (struct map_session_data *sd, int pvprank, int pvpnum,int type); @@ -860,6 +887,13 @@ struct clif_interface { /* elemental-related */ void (*elemental_info) (struct map_session_data *sd); void (*elemental_updatestatus) (struct map_session_data *sd, int type); + /* bgqueue */ + void (*bgqueue_ack) (struct map_session_data *sd, enum BATTLEGROUNDS_QUEUE_ACK response, unsigned char arena_id); + void (*bgqueue_notice_delete) (struct map_session_data *sd, enum BATTLEGROUNDS_QUEUE_NOTICE_DELETED response, unsigned char arena_id); + void (*bgqueue_update_info) (struct map_session_data *sd, unsigned char arena_id, int position); + void (*bgqueue_joined) (struct map_session_data *sd, int pos); + void (*bgqueue_pcleft) (struct map_session_data *sd); + void (*bgqueue_battlebegins) (struct map_session_data *sd, unsigned char arena_id, enum send_target target); /* misc-handling */ void (*adopt_reply) (struct map_session_data *sd, int type); void (*adopt_request) (struct map_session_data *sd, struct map_session_data *src, int p_id); @@ -1078,6 +1112,11 @@ struct clif_interface { void (*pSkillSelectMenu) (int fd, struct map_session_data *sd); void (*pMoveItem) (int fd, struct map_session_data *sd); void (*pDull) (int fd, struct map_session_data *sd); + /* BGQueue */ + void (*pBGQueueRegister) (int fd, struct map_session_data *sd); + void (*pBGQueueCheckState) (int fd, struct map_session_data *sd); + void (*pBGQueueRevokeReq) (int fd, struct map_session_data *sd); + void (*pBGQueueBattleBeginAck) (int fd, struct map_session_data *sd); /* RagExe Cash Shop [Ind/Hercules] */ void (*pCashShopOpen) (int fd, struct map_session_data *sd); void (*pCashShopClose) (int fd, struct map_session_data *sd); diff --git a/src/map/guild.c b/src/map/guild.c index b83f05f00..a6c873861 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -24,6 +24,7 @@ #include "clif.h" #include "skill.h" #include "log.h" +#include "instance.h" #include <stdio.h> #include <stdlib.h> @@ -503,6 +504,8 @@ int guild_recv_info(struct guild *sg) { if((g = (struct guild*)idb_get(guild_db,sg->guild_id))==NULL) { guild_new = true; g=(struct guild *)aCalloc(1,sizeof(struct guild)); + g->instance = NULL; + g->instances = 0; idb_put(guild_db,sg->guild_id,g); if( hChSys.ally ) { struct hChSysCh *channel; @@ -926,7 +929,8 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c sd->status.guild_id = 0; sd->guild = NULL; sd->guild_emblem_id = 0; - + if( g->instances ) + instance->check_kick(sd); clif->charnameupdate(sd); //Update display name [Skotlex] //TODO: send emblem update to self and people around } @@ -2211,6 +2215,10 @@ void do_final_guild(void) { for( g = dbi_first(iter); dbi_exists(iter); g = dbi_next(iter) ) { if( g->channel != NULL ) clif->chsys_delete((struct hChSysCh *)g->channel); + if( g->instance != NULL ) { + aFree(g->instance); + g->instance = NULL; + } } dbi_destroy(iter); diff --git a/src/map/instance.c b/src/map/instance.c index 8ddde8b3e..7b3e2e800 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -1,5 +1,6 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// Portions Copyright (c) Athena Dev Teams #include "../common/cbasetypes.h" #include "../common/socket.h" @@ -24,20 +25,13 @@ #include <stdarg.h> #include <time.h> -int instance_start = 0; // To keep the last index + 1 of normal map inserted in the map[ARRAY] -struct s_instance instance[MAX_INSTANCE]; - - /// Checks whether given instance id is valid or not. -static bool instance_is_valid(int instance_id) -{ - if( instance_id < 1 || instance_id >= ARRAYLENGTH(instance) ) - {// out of range +bool instance_is_valid(int instance_id) { + if( instance_id < 0 || instance_id >= instance->instances ) {// out of range return false; } - if( instance[instance_id].state == INSTANCE_FREE ) - {// uninitialized/freed instance slot + if( instances[instance_id].state == INSTANCE_FREE ) {// uninitialized/freed instance slot return false; } @@ -48,56 +42,104 @@ static bool instance_is_valid(int instance_id) /*-------------------------------------- * name : instance name * Return value could be - * -4 = already exists | -3 = no free instances | -2 = party not found | -1 = invalid type + * -4 = already exists | -3 = no free instances | -2 = owner not found | -1 = invalid type * On success return instance_id *--------------------------------------*/ -int instance_create(int party_id, const char *name) -{ - int i; - struct party_data* p; - - if( ( p = party_search(party_id) ) == NULL ) - { - ShowError("instance_create: party %d not found for instance '%s'.\n", party_id, name); - return -2; +int instance_create(int owner_id, const char *name, enum instance_owner_type type) { + unsigned short *iptr = NULL, *icptr = NULL; + struct map_session_data *sd = NULL; + struct party_data *p = NULL; + struct guild *g = NULL; + int i, j; + + switch ( type ) { + case IOT_NONE: + break; + case IOT_CHAR: + if( ( sd = map_id2sd(owner_id) ) == NULL ) { + ShowError("instance_create: character %d not found for instance '%s'.\n", owner_id, name); + return -2; + } + iptr = sd->instance; + icptr = &sd->instances; + break; + case IOT_PARTY: + if( ( p = party_search(owner_id) ) == NULL ) { + ShowError("instance_create: party %d not found for instance '%s'.\n", owner_id, name); + return -2; + } + iptr = p->instance; + icptr = &p->instances; + break; + case IOT_GUILD: + if( ( g = guild->search(owner_id) ) == NULL ) { + ShowError("instance_create: guild %d not found for instance '%s'.\n", owner_id, name); + return -2; + } + iptr = g->instance; + icptr = &g->instances; + break; + default: + ShowError("instance_create: unknown type %d for owner_id %d and name %s.\n", type,owner_id,name); + return -1; } - - if( p->instance_id ) - return -4; // Party already instancing - - // Searching a Free Instance - // 0 is ignored as this mean "no instance" on maps - ARR_FIND(1, MAX_INSTANCE, i, instance[i].state == INSTANCE_FREE); - if( i == MAX_INSTANCE ) - { - ShowError("instance_create: no free instances, consider increasing MAX_INSTANCE.\n"); - return -3; + + if( type != IOT_NONE && *icptr ) { + ARR_FIND(0, *icptr, i, strcmp(instances[iptr[i]].name,name) == 0 ); + if( i != *icptr ) + return -4;/* already got this instance */ } - - instance[i].state = INSTANCE_IDLE; - instance[i].instance_id = i; - instance[i].idle_timer = INVALID_TIMER; - instance[i].idle_timeout = instance[i].idle_timeoutval = 0; - instance[i].progress_timer = INVALID_TIMER; - instance[i].progress_timeout = 0; - instance[i].users = 0; - instance[i].party_id = party_id; - instance[i].vars = idb_alloc(DB_OPT_RELEASE_DATA); - - safestrncpy( instance[i].name, name, sizeof(instance[i].name) ); - memset( instance[i].map, 0x00, sizeof(instance[i].map) ); - p->instance_id = i; - + + ARR_FIND(0, instance->instances, i, instances[i].state == INSTANCE_FREE); + + if( i == instance->instances ) + RECREATE(instances, struct instance_data, ++instance->instances); + + instances[i].state = INSTANCE_IDLE; + instances[i].id = i; + instances[i].idle_timer = INVALID_TIMER; + instances[i].idle_timeout = instances[i].idle_timeoutval = 0; + instances[i].progress_timer = INVALID_TIMER; + instances[i].progress_timeout = 0; + instances[i].users = 0; + instances[i].map = NULL; + instances[i].num_map = 0; + instances[i].owner_id = owner_id; + instances[i].owner_type = type; + instances[i].vars = idb_alloc(DB_OPT_RELEASE_DATA); + + safestrncpy( instances[i].name, name, sizeof(instances[i].name) ); + instances[i].map = NULL; + + if( type != IOT_NONE ) { + ARR_FIND(0, *icptr, j, iptr[j] == 0); + if( j == *icptr ) { + switch( type ) { + case IOT_CHAR: + RECREATE(sd->instance, unsigned short, ++*icptr); + sd->instance[sd->instances-1] = i; + break; + case IOT_PARTY: + RECREATE(p->instance, unsigned short, ++*icptr); + p->instance[p->instances-1] = i; + break; + case IOT_GUILD: + RECREATE(g->instance, unsigned short, ++*icptr); + g->instance[g->instances-1] = i; + break; + } + } else + iptr[j] = i; + } + clif->instance(i, 1, 0); // Start instancing window - ShowInfo("[Instance] Created: %s.\n", name); return i; } /*-------------------------------------- * Add a map to the instance using src map "name" *--------------------------------------*/ -int instance_add_map(const char *name, int instance_id, bool usebasename) -{ +int instance_add_map(const char *name, int instance_id, bool usebasename, const char *map_name) { int16 m = map_mapname2mapid(name); int i, im = -1; size_t num_cell, size; @@ -105,42 +147,49 @@ int instance_add_map(const char *name, int instance_id, bool usebasename) if( m < 0 ) return -1; // source map not found - if( !instance_is_valid(instance_id) ) - { + if( !instance->valid(instance_id) ) { ShowError("instance_add_map: trying to attach '%s' map to non-existing instance %d.\n", name, instance_id); return -1; } - if( instance[instance_id].num_map >= MAX_MAP_PER_INSTANCE ) - { - ShowError("instance_add_map: trying to add '%s' map to instance %d (%s) failed. Please increase MAX_MAP_PER_INSTANCE.\n", name, instance_id, instance[instance_id].name); + + if( map_name != NULL && strdb_iget(mapindex_db, map_name) ) { + ShowError("instance_add_map: trying to create instanced map with existent name '%s'\n", map_name); return -2; } - if( map[m].instance_id != 0 ) - { // Source map already belong to a Instance. + + if( map[m].instance_id >= 0 ) { // Source map already belong to a Instance. ShowError("instance_add_map: trying to instance already instanced map %s.\n", name); return -4; } - - ARR_FIND( instance_start, map_num, i, !map[i].name[0] ); // Searching for a Free Map - if( i < map_num ) im = i; // Unused map found (old instance) - else if( map_num - 1 >= MAX_MAP_PER_SERVER ) - { // No more free maps - ShowError("instance_add_map: no more free space to create maps on this server.\n"); - return -5; + + ARR_FIND( instance->start_id, map_num, i, !map[i].name[0] ); // Searching for a Free Map + + if( i < map_num ) + im = i; // Unused map found (old instance) + else { + im = map_num; // Using next map index + RECREATE(map,struct map_data,++map_num); } - else im = map_num++; // Using next map index + + if( map[m].cell == (struct mapcell *)0xdeadbeaf ) + map_cellfromcache(&map[m]); memcpy( &map[im], &map[m], sizeof(struct map_data) ); // Copy source map - snprintf(map[im].name, MAP_NAME_LENGTH, (usebasename ? "%.3d#%s" : "%.3d%s"), instance_id, name); // Generate Name for Instance Map + if( map_name != NULL ) { + snprintf(map[im].name, MAP_NAME_LENGTH, "%s", map_name); + map[im].cName = map[m].name; + } else + snprintf(map[im].name, MAP_NAME_LENGTH, (usebasename ? "%.3d#%s" : "%.3d%s"), instance_id, name); // Generate Name for Instance Map map[im].index = mapindex_addmap(-1, map[im].name); // Add map index - if( !map[im].index ) - { + map[im].channel = NULL; + + if( !map[im].index ) { map[im].name[0] = '\0'; ShowError("instance_add_map: no more free map indexes.\n"); return -3; // No free map index } - + // Reallocate cells num_cell = map[im].xs * map[im].ys; CREATE( map[im].cell, struct mapcell, num_cell ); @@ -161,9 +210,11 @@ int instance_add_map(const char *name, int instance_id, bool usebasename) map[im].instance_src_map = m; map[m].flag.src4instance = 1; // Flag this map as a src map for instances - instance[instance_id].map[instance[instance_id].num_map++] = im; // Attach to actual instance - map_addmap2db(&map[im]); + RECREATE(instances[instance_id].map, unsigned short, ++instances[instance_id].num_map); + instances[instance_id].map[instances[instance_id].num_map - 1] = im; // Attach to actual instance + map_addmap2db(&map[im]); + return im; } @@ -172,19 +223,16 @@ int instance_add_map(const char *name, int instance_id, bool usebasename) * party_id : source party of this instance * type : result (0 = map id | 1 = instance id) *--------------------------------------*/ -int instance_map2imap(int16 m, int instance_id) -{ +int instance_map2imap(int16 m, int instance_id) { int i; - if( !instance_is_valid(instance_id) ) - { + if( !instance->valid(instance_id) ) { return -1; } - for( i = 0; i < instance[instance_id].num_map; i++ ) - { - if( instance[instance_id].map[i] && map[instance[instance_id].map[i]].instance_src_map == m ) - return instance[instance_id].map[i]; + for( i = 0; i < instances[instance_id].num_map; i++ ) { + if( instances[instance_id].map[i] && map[instances[instance_id].map[i]].instance_src_map == m ) + return instances[instance_id].map[i]; } return -1; } @@ -194,59 +242,56 @@ int instance_map2imap(int16 m, int instance_id) * instance_id : where to search * result : mapid of map "m" in this instance *--------------------------------------*/ -int instance_mapid2imapid(int16 m, int instance_id) -{ +int instance_mapid2imapid(int16 m, int instance_id) { if( map[m].flag.src4instance == 0 ) return m; // not instances found for this map - else if( map[m].instance_id ) - { // This map is a instance, not a src map instance + else if( map[m].instance_id >= 0 ) { // This map is a instance, not a src map instance ShowError("map_instance_mapid2imapid: already instanced (%d / %d)\n", m, instance_id); return -1; } - if( !instance_is_valid(instance_id) ) + if( !instance->valid(instance_id) ) return -1; - return instance_map2imap(m, instance_id); + return instance->map2imap(m, instance_id); } /*-------------------------------------- * map_instance_map_npcsub * Used on Init instance. Duplicates each script on source map *--------------------------------------*/ -int instance_map_npcsub(struct block_list* bl, va_list args) -{ +int instance_map_npcsub(struct block_list* bl, va_list args) { struct npc_data* nd = (struct npc_data*)bl; int16 m = va_arg(args, int); // Destination Map - npc_duplicate4instance(nd, m); + if ( npc_duplicate4instance(nd, m) ) + ShowDebug("instance_map_npcsub:npc_duplicate4instance failed (%s/%d)\n",nd->name,m); + return 1; } /*-------------------------------------- * Init all map on the instance. Npcs are created here *--------------------------------------*/ -void instance_init(int instance_id) -{ +void instance_init(int instance_id) { int i; - if( !instance_is_valid(instance_id) ) + if( !instance->valid(instance_id) ) return; // nothing to do - for( i = 0; i < instance[instance_id].num_map; i++ ) - map_foreachinmap(instance_map_npcsub, map[instance[instance_id].map[i]].instance_src_map, BL_NPC, instance[instance_id].map[i]); + for( i = 0; i < instances[instance_id].num_map; i++ ) + map_foreachinmap(instance_map_npcsub, map[instances[instance_id].map[i]].instance_src_map, BL_NPC, instances[instance_id].map[i]); - instance[instance_id].state = INSTANCE_BUSY; - ShowInfo("[Instance] Initialized %s.\n", instance[instance_id].name); + instances[instance_id].state = INSTANCE_BUSY; } /*-------------------------------------- * Used on instance deleting process. * Warps all players on each instance map to its save points. *--------------------------------------*/ -int instance_del_load(struct map_session_data* sd, va_list args) -{ +int instance_del_load(struct map_session_data* sd, va_list args) { int16 m = va_arg(args,int); + if( !sd || sd->bl.m != m ) return 0; @@ -285,12 +330,11 @@ int instance_cleanup_sub(struct block_list *bl, va_list ap) { /*-------------------------------------- * Removes a simple instance map *--------------------------------------*/ -void instance_del_map(int16 m) -{ +void instance_del_map(int16 m) { int i; - if( m <= 0 || !map[m].instance_id ) - { - ShowError("Tried to remove non-existing instance map (%d)\n", m); + + if( m <= 0 || map[m].instance_id == -1 ) { + ShowError("instance_del_map: tried to remove non-existing instance map (%d)\n", m); return; } @@ -299,116 +343,145 @@ void instance_del_map(int16 m) if( map[m].mob_delete_timer != INVALID_TIMER ) delete_timer(map[m].mob_delete_timer, map_removemobs_timer); - + mapindex_removemap( map[m].index ); // Free memory aFree(map[m].cell); aFree(map[m].block); aFree(map[m].block_mob); - + // Remove from instance - for( i = 0; i < instance[map[m].instance_id].num_map; i++ ) - { - if( instance[map[m].instance_id].map[i] == m ) - { - instance[map[m].instance_id].num_map--; - for( ; i < instance[map[m].instance_id].num_map; i++ ) - instance[map[m].instance_id].map[i] = instance[map[m].instance_id].map[i+1]; + for( i = 0; i < instances[map[m].instance_id].num_map; i++ ) { + if( instances[map[m].instance_id].map[i] == m ) { + instances[map[m].instance_id].num_map--; + for( ; i < instances[map[m].instance_id].num_map; i++ ) + instances[map[m].instance_id].map[i] = instances[map[m].instance_id].map[i+1]; i = -1; break; } } - if( i == instance[map[m].instance_id].num_map ) - ShowError("map_instance_del: failed to remove %s from instance list (%s): %d\n", map[m].name, instance[map[m].instance_id].name, m); + + if( i == instances[map[m].instance_id].num_map ) + ShowError("map_instance_del: failed to remove %s from instance list (%s): %d\n", map[m].name, instances[map[m].instance_id].name, m); + + if( map[m].channel ) + clif->chsys_delete(map[m].channel); map_removemapdb(&map[m]); memset(&map[m], 0x00, sizeof(map[0])); - - /* for it is default and makes it not try to delete a non-existent timer since we did not delete this entry. */ + map[m].instance_id = -1; map[m].mob_delete_timer = INVALID_TIMER; } /*-------------------------------------- * Timer to destroy instance by process or idle *--------------------------------------*/ -int instance_destroy_timer(int tid, unsigned int tick, int id, intptr_t data) -{ - instance_destroy(id); +int instance_destroy_timer(int tid, unsigned int tick, int id, intptr_t data) { + instance->destroy(id); return 0; } /*-------------------------------------- * Removes a instance, all its maps and npcs. *--------------------------------------*/ -void instance_destroy(int instance_id) -{ - int last = 0, type; - struct party_data *p; - time_t now = time(NULL); - - if( !instance_is_valid(instance_id) ) +void instance_destroy(int instance_id) { + unsigned short *iptr = NULL, *icptr = NULL; + struct map_session_data *sd = NULL; + struct party_data *p = NULL; + struct guild *g = NULL; + int last = 0, type, j; + unsigned int now = (unsigned int)time(NULL); + + if( !instance->valid(instance_id) ) return; // nothing to do - if( instance[instance_id].progress_timeout && instance[instance_id].progress_timeout <= now ) + if( instances[instance_id].progress_timeout && instances[instance_id].progress_timeout <= now ) type = 1; - else if( instance[instance_id].idle_timeout && instance[instance_id].idle_timeout <= now ) + else if( instances[instance_id].idle_timeout && instances[instance_id].idle_timeout <= now ) type = 2; else type = 3; clif->instance(instance_id, 5, type); // Report users this instance has been destroyed - while( instance[instance_id].num_map && last != instance[instance_id].map[0] ) - { // Remove all maps from instance - last = instance[instance_id].map[0]; - instance_del_map( instance[instance_id].map[0] ); + switch ( instances[instance_id].owner_type ) { + case IOT_NONE: + break; + case IOT_CHAR: + if( ( sd = map_id2sd(instances[instance_id].owner_id) ) == NULL ) { + break; + } + iptr = sd->instance; + icptr = &sd->instances; + break; + case IOT_PARTY: + if( ( p = party_search(instances[instance_id].owner_id) ) == NULL ) { + break; + } + iptr = p->instance; + icptr = &p->instances; + break; + case IOT_GUILD: + if( ( g = guild->search(instances[instance_id].owner_id) ) == NULL ) { + break; + } + iptr = g->instance; + icptr = &g->instances; + break; + default: + ShowError("instance_destroy: unknown type %d for owner_id %d and name %s.\n", instances[instance_id].owner_type,instances[instance_id].owner_id,instances[instance_id].name); + break; + } + + if( iptr != NULL ) { + ARR_FIND(0, *icptr, j, iptr[j] == instance_id); + if( j != *icptr ) + iptr[j] = 0; + } + + while( instances[instance_id].num_map && last != instances[instance_id].map[0] ) { // Remove all maps from instance + last = instances[instance_id].map[0]; + instance->del_map( instances[instance_id].map[0] ); } - if( instance[instance_id].vars ) - db_destroy(instance[instance_id].vars); - - if( instance[instance_id].progress_timer != INVALID_TIMER ) - delete_timer( instance[instance_id].progress_timer, instance_destroy_timer); - if( instance[instance_id].idle_timer != INVALID_TIMER ) - delete_timer( instance[instance_id].idle_timer, instance_destroy_timer); + if( instances[instance_id].vars ) + db_destroy(instances[instance_id].vars); - instance[instance_id].vars = NULL; + if( instances[instance_id].progress_timer != INVALID_TIMER ) + delete_timer( instances[instance_id].progress_timer, instance_destroy_timer); + if( instances[instance_id].idle_timer != INVALID_TIMER ) + delete_timer( instances[instance_id].idle_timer, instance_destroy_timer); - if( instance[instance_id].party_id && (p = party_search(instance[instance_id].party_id)) != NULL ) - p->instance_id = 0; // Update Party information + instances[instance_id].vars = NULL; - ShowInfo("[Instance] Destroyed %s.\n", instance[instance_id].name); - memset( &instance[instance_id], 0x00, sizeof(instance[0]) ); + aFree(instances[instance_id].map); + + memset( &instances[instance_id], 0x00, sizeof(struct instance_data) ); - instance[instance_id].state = INSTANCE_FREE; } /*-------------------------------------- * Checks if there are users in the instance or not to start idle timer *--------------------------------------*/ -void instance_check_idle(int instance_id) -{ +void instance_check_idle(int instance_id) { bool idle = true; - time_t now = time(NULL); + unsigned int now = (unsigned int)time(NULL); - if( !instance_is_valid(instance_id) || instance[instance_id].idle_timeoutval == 0 ) + if( !instance->valid(instance_id) || instances[instance_id].idle_timeoutval == 0 ) return; - if( instance[instance_id].users ) + if( instances[instance_id].users ) idle = false; - if( instance[instance_id].idle_timer != INVALID_TIMER && !idle ) - { - delete_timer(instance[instance_id].idle_timer, instance_destroy_timer); - instance[instance_id].idle_timer = INVALID_TIMER; - instance[instance_id].idle_timeout = 0; + if( instances[instance_id].idle_timer != INVALID_TIMER && !idle ) { + delete_timer(instances[instance_id].idle_timer, instance_destroy_timer); + instances[instance_id].idle_timer = INVALID_TIMER; + instances[instance_id].idle_timeout = 0; clif->instance(instance_id, 3, 0); // Notify instance users normal instance expiration - } - else if( instance[instance_id].idle_timer == INVALID_TIMER && idle ) - { - instance[instance_id].idle_timeout = now + instance[instance_id].idle_timeoutval; - instance[instance_id].idle_timer = add_timer( gettick() + (unsigned int)instance[instance_id].idle_timeoutval * 1000, instance_destroy_timer, instance_id, 0); + } else if( instances[instance_id].idle_timer == INVALID_TIMER && idle ) { + instances[instance_id].idle_timeout = now + instances[instance_id].idle_timeoutval; + instances[instance_id].idle_timer = add_timer( gettick() + instances[instance_id].idle_timeoutval * 1000, instance_destroy_timer, instance_id, 0); clif->instance(instance_id, 4, 0); // Notify instance users it will be destroyed of no user join it again in "X" time } } @@ -418,54 +491,46 @@ void instance_check_idle(int instance_id) *--------------------------------------*/ void instance_set_timeout(int instance_id, unsigned int progress_timeout, unsigned int idle_timeout) { - time_t now = time(0); + unsigned int now = time(0); - if( !instance_is_valid(instance_id) ) + if( !instance->valid(instance_id) ) return; - if( instance[instance_id].progress_timer != INVALID_TIMER ) - delete_timer( instance[instance_id].progress_timer, instance_destroy_timer); - if( instance[instance_id].idle_timer != INVALID_TIMER ) - delete_timer( instance[instance_id].idle_timer, instance_destroy_timer); - - if( progress_timeout ) - { - instance[instance_id].progress_timeout = now + progress_timeout; - instance[instance_id].progress_timer = add_timer( gettick() + progress_timeout * 1000, instance_destroy_timer, instance_id, 0); - } - else - { - instance[instance_id].progress_timeout = 0; - instance[instance_id].progress_timer = INVALID_TIMER; + if( instances[instance_id].progress_timer != INVALID_TIMER ) + delete_timer( instances[instance_id].progress_timer, instance_destroy_timer); + if( instances[instance_id].idle_timer != INVALID_TIMER ) + delete_timer( instances[instance_id].idle_timer, instance_destroy_timer); + + if( progress_timeout ) { + instances[instance_id].progress_timeout = now + progress_timeout; + instances[instance_id].progress_timer = add_timer( gettick() + progress_timeout * 1000, instance_destroy_timer, instance_id, 0); + } else { + instances[instance_id].progress_timeout = 0; + instances[instance_id].progress_timer = INVALID_TIMER; } - if( idle_timeout ) - { - instance[instance_id].idle_timeoutval = idle_timeout; - instance[instance_id].idle_timer = INVALID_TIMER; + if( idle_timeout ) { + instances[instance_id].idle_timeoutval = idle_timeout; + instances[instance_id].idle_timer = INVALID_TIMER; instance_check_idle(instance_id); - } - else - { - instance[instance_id].idle_timeoutval = 0; - instance[instance_id].idle_timeout = 0; - instance[instance_id].idle_timer = INVALID_TIMER; + } else { + instances[instance_id].idle_timeoutval = 0; + instances[instance_id].idle_timeout = 0; + instances[instance_id].idle_timer = INVALID_TIMER; } - if( instance[instance_id].idle_timer == INVALID_TIMER && instance[instance_id].progress_timer != INVALID_TIMER ) + if( instances[instance_id].idle_timer == INVALID_TIMER && instances[instance_id].progress_timer != INVALID_TIMER ) clif->instance(instance_id, 3, 0); } /*-------------------------------------- * Checks if sd in on a instance and should be kicked from it *--------------------------------------*/ -void instance_check_kick(struct map_session_data *sd) -{ +void instance_check_kick(struct map_session_data *sd) { int16 m = sd->bl.m; clif->instance_leave(sd->fd); - if( map[m].instance_id ) - { // User was on the instance map + if( map[m].instance_id >= 0 ) { // User was on the instance map if( map[m].save.map ) pc_setpos(sd, map[m].save.map, map[m].save.x, map[m].save.y, CLR_TELEPORT); else @@ -473,16 +538,41 @@ void instance_check_kick(struct map_session_data *sd) } } -void do_final_instance(void) -{ +void do_final_instance(void) { int i; - for( i = 1; i < MAX_INSTANCE; i++ ) + for(i = 0; i < instance->instances; i++) { instance_destroy(i); + } + + aFree(instances); } -void do_init_instance(void) -{ - memset(instance, 0x00, sizeof(instance)); +void do_init_instance(void) { add_timer_func_list(instance_destroy_timer, "instance_destroy_timer"); } + +void instance_defaults(void) { + instance = &instance_s; + + instance->init = do_init_instance; + instance->final = do_final_instance; + + /* start point */ + instance->start_id = 0; + /* count */ + instance->instances = 0; + + /* */ + instance->create = instance_create; + instance->add_map = instance_add_map; + instance->del_map = instance_del_map; + instance->map2imap = instance_map2imap; + instance->mapid2imapid = instance_mapid2imapid; + instance->destroy = instance_destroy; + instance->start = instance_init; + instance->check_idle = instance_check_idle; + instance->check_kick = instance_check_kick; + instance->set_timeout = instance_set_timeout; + instance->valid = instance_is_valid; +} diff --git a/src/map/instance.h b/src/map/instance.h index 03b0d0898..e86586e44 100644 --- a/src/map/instance.h +++ b/src/map/instance.h @@ -1,51 +1,71 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file +// Portions Copyright (c) Athena Dev Teams #ifndef _INSTANCE_H_ #define _INSTANCE_H_ -#define MAX_MAP_PER_INSTANCE 10 -#define MAX_INSTANCE 500 - #define INSTANCE_NAME_LENGTH (60+1) -typedef enum instance_state { INSTANCE_FREE, INSTANCE_IDLE, INSTANCE_BUSY } instance_state; +typedef enum instance_state { + INSTANCE_FREE, + INSTANCE_IDLE, + INSTANCE_BUSY +} instance_state; + +enum instance_owner_type { + IOT_NONE, + IOT_CHAR, + IOT_PARTY, + IOT_GUILD, + /* ... */ + IOT_MAX, +}; -struct s_instance { +struct instance_data { + unsigned short id; char name[INSTANCE_NAME_LENGTH]; // Instance Name - required for clif functions. instance_state state; - short instance_id; - int party_id; + enum instance_owner_type owner_type; + int owner_id; - int map[MAX_MAP_PER_INSTANCE]; - int num_map; - int users; + unsigned short *map; + unsigned short num_map; + unsigned short users; struct DBMap* vars; // Instance Variable for scripts int progress_timer; - time_t progress_timeout; + unsigned int progress_timeout; int idle_timer; - time_t idle_timeout, idle_timeoutval; + unsigned int idle_timeout, idle_timeoutval; }; -extern int instance_start; -extern struct s_instance instance[MAX_INSTANCE]; +struct instance_data *instances; -int instance_create(int party_id, const char *name); -int instance_add_map(const char *name, int instance_id, bool usebasename); -void instance_del_map(int16 m); -int instance_map2imap(int16 m, int instance_id); -int instance_mapid2imapid(int16 m, int instance_id); -void instance_destroy(int instance_id); -void instance_init(int instance_id); +struct instance_interface { + void (*init) (void); + void (*final) (void); + /* start point */ + unsigned short start_id; + unsigned short instances; + /* */ + int (*create) (int party_id, const char *name, enum instance_owner_type type); + int (*add_map) (const char *name, int instance_id, bool usebasename, const char *map_name); + void (*del_map) (int16 m); + int (*map2imap) (int16 m, int instance_id); + int (*mapid2imapid) (int16 m, int instance_id); + void (*destroy) (int instance_id); + void (*start) (int instance_id); + void (*check_idle) (int instance_id); + void (*check_kick) (struct map_session_data *sd); + void (*set_timeout) (int instance_id, unsigned int progress_timeout, unsigned int idle_timeout); + bool (*valid) (int instance_id); +} instance_s; -void instance_check_idle(int instance_id); -void instance_check_kick(struct map_session_data *sd); -void instance_set_timeout(int instance_id, unsigned int progress_timeout, unsigned int idle_timeout); +struct instance_interface *instance; -void do_final_instance(void); -void do_init_instance(void); +void instance_defaults(void); #endif diff --git a/src/map/intif.c b/src/map/intif.c index 93bb8add7..9e6403f10 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -467,7 +467,7 @@ int intif_party_changemap(struct map_session_data *sd,int online) if(!sd) return 0; - if( (m=map_mapindex2mapid(sd->mapindex)) >= 0 && map[m].instance_id ) + if( (m=map_mapindex2mapid(sd->mapindex)) >= 0 && map[m].instance_id >= 0 ) mapindex = map[map[m].instance_src_map].index; else mapindex = sd->mapindex; diff --git a/src/map/irc-bot.c b/src/map/irc-bot.c index 0e155011e..bfaf18af0 100644 --- a/src/map/irc-bot.c +++ b/src/map/irc-bot.c @@ -22,12 +22,16 @@ char send_string[200]; int irc_connect_timer(int tid, unsigned int tick, int id, intptr_t data) { + struct hSockOpt opt; if( ircbot->isOn || ++ircbot->fails >= 3 ) return 0; + opt.silent = 1; + opt.setTimeo = 0; + ircbot->last_try = gettick(); - if( ( ircbot->fd = make_connection(ircbot->ip,hChSys.irc_server_port,true) ) > 0 ){ + if( ( ircbot->fd = make_connection(ircbot->ip,hChSys.irc_server_port,&opt) ) > 0 ){ session[ircbot->fd]->func_parse = ircbot->parse; session[ircbot->fd]->flag.server = 1; add_timer(gettick() + 3000, ircbot->identify_timer, 0, 0); diff --git a/src/map/map.c b/src/map/map.c index d4c15cd6e..54646d1c3 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -101,7 +101,7 @@ static DBMap* id_db=NULL; // int id -> struct block_list* static DBMap* pc_db=NULL; // int id -> struct map_session_data* static DBMap* mobid_db=NULL; // int id -> struct mob_data* static DBMap* bossid_db=NULL; // int id -> struct mob_data* (MVP db) -static DBMap* map_db=NULL; // unsigned int mapindex -> struct map_data* +static DBMap* map_db=NULL; // unsigned int mapindex -> struct map_data_other_server* static DBMap* nick_db=NULL; // int char_id -> struct charid2nick* (requested names of offline characters) static DBMap* charid_db=NULL; // int char_id -> struct map_session_data* static DBMap* regen_db=NULL; // int id -> struct block_list* (status_natural_heal processing) @@ -150,6 +150,8 @@ struct map_cache_map_info { int32 len; }; +uint16 index2mapid[MAX_MAPINDEX]; + char db_path[256] = "db"; char help_txt[256] = "conf/help.txt"; char help2_txt[256] = "conf/help2.txt"; @@ -162,6 +164,7 @@ int enable_grf = 0; //To enable/disable reading maps from GRF files, bypassing m /* [Ind/Hercules] */ struct eri *map_iterator_ers; +char *map_cache_buffer = NULL; // Has the uncompressed gat data of all maps, so just one allocation has to be made /*========================================== * server player count (of all mapservers) @@ -397,8 +400,7 @@ int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick) if (sc->data[SC_PROPERTYWALK] && sc->data[SC_PROPERTYWALK]->val3 >= skill->get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) status_change_end(bl,SC_PROPERTYWALK,INVALID_TIMER); - } else - if (bl->type == BL_NPC) + } else if (bl->type == BL_NPC) npc_unsetcells((TBL_NPC*)bl); if (moveblock) map_delblock(bl); @@ -469,8 +471,7 @@ int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick) skill->unit_move_unit_group(skill->id2group(sc->data[SC_HAWKEYES]->val4), bl->m, x1-x0, y1-y0); } } - } else - if (bl->type == BL_NPC) + } else if (bl->type == BL_NPC) npc_setcells((TBL_NPC*)bl); return 0; @@ -1194,8 +1195,7 @@ int map_foreachinpath(int (*func)(struct block_list*,va_list),int16 m,int16 x0,i } // Copy of map_foreachincell, but applied to the whole map. [Skotlex] -int map_foreachinmap(int (*func)(struct block_list*,va_list), int16 m, int type,...) -{ +int map_foreachinmap(int (*func)(struct block_list*,va_list), int16 m, int type,...) { int b, bsize; int returnCount = 0; //total sum of returned values of func() [Skotlex] struct block_list *bl; @@ -1233,6 +1233,52 @@ int map_foreachinmap(int (*func)(struct block_list*,va_list), int16 m, int type, bl_list_count = blockcount; return returnCount; } +// Copy of map_foreachinmap, but applied to all maps in a instance id. [Ind/Hercules] +int map_foreachininstance(int (*func)(struct block_list*,va_list), int16 instance_id, int type,...) { + int b, bsize; + int returnCount = 0; //total sum of returned values of func() [Skotlex] + struct block_list *bl; + int blockcount = bl_list_count, i, j; + int16 m; + va_list ap; + + for( j = 0; j < instances[instance_id].num_map; j++ ) { + + m = instances[instance_id].map[j]; + + bsize = map[ m ].bxs * map[ m ].bys; + + if( type&~BL_MOB ) + for( b = 0; b < bsize; b++ ) + for( bl = map[ m ].block[ b ]; bl != NULL; bl = bl->next ) + if( bl->type&type && bl_list_count < BL_LIST_MAX ) + bl_list[ bl_list_count++ ] = bl; + + if( type&BL_MOB ) + for( b = 0; b < bsize; b++ ) + for( bl = map[ m ].block_mob[ b ]; bl != NULL; bl = bl->next ) + if( bl_list_count < BL_LIST_MAX ) + bl_list[ bl_list_count++ ] = bl; + + if( bl_list_count >= BL_LIST_MAX ) + ShowWarning("map_foreachininstance: block count too many!\n"); + + map_freeblock_lock(); + + for( i = blockcount; i < bl_list_count ; i++ ) + if( bl_list[ i ]->prev ) { //func() may delete this bl_list[] slot, checking for prev ensures it wasnt queued for deletion. + va_start(ap, type); + returnCount += func(bl_list[ i ], ap); + va_end(ap); + } + + map_freeblock_unlock(); + + } + + bl_list_count = blockcount; + return returnCount; +} /// Generates a new flooritem object id from the interval [MIN_FLOORITEM, MAX_FLOORITEM). @@ -1637,8 +1683,19 @@ int map_quit(struct map_session_data *sd) { pc_itemcd_do(sd,false); + for( i = 0; i < sd->queues_count; i++ ) { + struct hQueue *queue; + if( (queue = script->queue(sd->queues[i])) && queue->onLogOut[0] != '\0' ) { + npc_event(sd, queue->onLogOut, 0); + } + } + /* two times, the npc event above may assign a new one or delete others */ + for( i = 0; i < sd->queues_count; i++ ) { + script->queue_remove(sd->queues[i],sd->status.account_id); + } + npc_script_event(sd, NPCE_LOGOUT); - + //Unit_free handles clearing the player related data, //map_quit handles extra specific data which is related to quitting normally //(changing map-servers invokes unit_free but bypasses map_quit) @@ -1712,7 +1769,7 @@ int map_quit(struct map_session_data *sd) { unit_remove_map_pc(sd,CLR_TELEPORT); - if( map[sd->bl.m].instance_id ) { // Avoid map conflicts and warnings on next login + if( map[sd->bl.m].instance_id >= 0 ) { // Avoid map conflicts and warnings on next login int16 m; struct point *pt; if( map[sd->bl.m].save.map ) @@ -1720,8 +1777,7 @@ int map_quit(struct map_session_data *sd) { else pt = &sd->status.save_point; - if( (m=map_mapindex2mapid(pt->map)) >= 0 ) - { + if( (m=map_mapindex2mapid(pt->map)) >= 0 ) { sd->bl.m = m; sd->bl.x = pt->x; sd->bl.y = pt->y; @@ -2160,8 +2216,7 @@ bool mapit_exists(struct s_mapiterator* mapit) /*========================================== * Add npc-bl to id_db, basically register npc to map *------------------------------------------*/ -bool map_addnpc(int16 m,struct npc_data *nd) -{ +bool map_addnpc(int16 m,struct npc_data *nd) { nullpo_ret(nd); if( m < 0 || m >= map_num ) @@ -2188,8 +2243,7 @@ int map_addmobtolist(unsigned short m, struct spawn_data *spawn) { size_t i; ARR_FIND( 0, MAX_MOB_LIST_PER_MAP, i, map[m].moblist[i] == NULL ); - if( i < MAX_MOB_LIST_PER_MAP ) - { + if( i < MAX_MOB_LIST_PER_MAP ) { map[m].moblist[i] = spawn; return i; } @@ -2250,13 +2304,11 @@ int map_removemobs_timer(int tid, unsigned int tick, int id, intptr_t data) int count; const int16 m = id; - if (m < 0 || m >= MAX_MAP_PER_SERVER) - { //Incorrect map id! + if (m < 0 || m >= map_num) { //Incorrect map id! ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, m); return 0; } - if (map[m].mob_delete_timer != tid) - { //Incorrect timer call! + if (map[m].mob_delete_timer != tid) { //Incorrect timer call! ShowError("map_removemobs_timer mismatch: %d != %d (map %s)\n",map[m].mob_delete_timer, tid, map[m].name); return 0; } @@ -2283,8 +2335,7 @@ void map_removemobs(int16 m) /*========================================== * Hookup, get map_id from map_name *------------------------------------------*/ -int16 map_mapname2mapid(const char* name) -{ +int16 map_mapname2mapid(const char* name) { unsigned short map_index; map_index = mapindex_name2id(name); if (!map_index) @@ -2295,24 +2346,18 @@ int16 map_mapname2mapid(const char* name) /*========================================== * Returns the map of the given mapindex. [Skotlex] *------------------------------------------*/ -int16 map_mapindex2mapid(unsigned short mapindex) -{ - struct map_data *md=NULL; +int16 map_mapindex2mapid(unsigned short mapindex) { - if (!mapindex) + if (!mapindex || mapindex > MAX_MAPINDEX) return -1; - md = (struct map_data*)uidb_get(map_db,(unsigned int)mapindex); - if(md==NULL || md->cell==NULL) - return -1; - return md->m; + return index2mapid[mapindex] == 0 ? -1 : index2mapid[mapindex]; } /*========================================== * Switching Ip, port ? (like changing map_server) get ip/port from map_name *------------------------------------------*/ -int map_mapname2ipport(unsigned short name, uint32* ip, uint16* port) -{ +int map_mapname2ipport(unsigned short name, uint32* ip, uint16* port) { struct map_data_other_server *mdos; mdos = (struct map_data_other_server*)uidb_get(map_db,(unsigned int)name); @@ -2443,8 +2488,7 @@ inline static struct mapcell map_gat2cell(int gat) { return cell; } -static int map_cell2gat(struct mapcell cell) -{ +static int map_cell2gat(struct mapcell cell) { if( cell.walkable == 1 && cell.shootable == 1 && cell.water == 0 ) return 0; if( cell.walkable == 0 && cell.shootable == 0 && cell.water == 0 ) return 1; if( cell.walkable == 1 && cell.shootable == 1 && cell.water == 1 ) return 3; @@ -2453,17 +2497,42 @@ static int map_cell2gat(struct mapcell cell) ShowWarning("map_cell2gat: cell has no matching gat type\n"); return 1; // default to 'wall' } +int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk); +void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag); +void map_cellfromcache(struct map_data *m) { + char decode_buffer[MAX_MAP_SIZE]; + struct map_cache_map_info *info = NULL; + + if( (info = (struct map_cache_map_info *)m->cellPos) ) { + unsigned long size, xy; + int i; + + size = (unsigned long)info->xs*(unsigned long)info->ys; + + // TO-DO: Maybe handle the scenario, if the decoded buffer isn't the same size as expected? [Shinryo] + decode_zip(decode_buffer, &size, m->cellPos+sizeof(struct map_cache_map_info), info->len); + CREATE(m->cell, struct mapcell, size); + + for( xy = 0; xy < size; ++xy ) + m->cell[xy] = map_gat2cell(decode_buffer[xy]); + + m->getcellp = map_getcellp; + m->setcell = map_setcell; + + for(i = 0; i < m->npc_num; i++) { + npc_setcells(m->npc[i]); + } + } +} /*========================================== * Confirm if celltype in (m,x,y) match the one given in cellchk *------------------------------------------*/ -int map_getcell(int16 m,int16 x,int16 y,cell_chk cellchk) -{ - return (m < 0 || m >= MAX_MAP_PER_SERVER) ? 0 : map_getcellp(&map[m],x,y,cellchk); +int map_getcell(int16 m,int16 x,int16 y,cell_chk cellchk) { + return (m < 0 || m >= map_num) ? 0 : map[m].getcellp(&map[m],x,y,cellchk); } -int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk) -{ +int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk) { struct mapcell cell; nullpo_ret(m); @@ -2474,8 +2543,7 @@ int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk) cell = m->cell[x + y*m->xs]; - switch(cellchk) - { + switch(cellchk) { // gat type retrieval case CELL_GETTYPE: return map_cell2gat(cell); @@ -2534,13 +2602,19 @@ int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk) } } +/* [Ind/Hercules] */ +int map_sub_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk) { + map_cellfromcache(m); + m->getcellp = map_getcellp; + m->setcell = map_setcell; + return m->getcellp(m,x,y,cellchk); +} /*========================================== * Change the type/flags of a map cell * 'cell' - which flag to modify * 'flag' - true = on, false = off *------------------------------------------*/ -void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag) -{ +void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag) { int j; if( m < 0 || m >= map_num || x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) @@ -2565,7 +2639,16 @@ void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag) break; } } - +void map_sub_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag) { + + if( m < 0 || m >= map_num || x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) + return; + + map_cellfromcache(&map[m]); + map[m].setcell = map_setcell; + map[m].getcellp = map_getcellp; + map[m].setcell(m,x,y,cell,flag); +} void map_setgatcell(int16 m, int16 x, int16 y, int gat) { int j; @@ -2635,8 +2718,8 @@ bool map_iwall_set(int16 m, int16 x, int16 y, int size, int8 dir, bool shootable if( map_getcell(m, x1, y1, CELL_CHKNOREACH) ) break; // Collision - map_setcell(m, x1, y1, CELL_WALKABLE, false); - map_setcell(m, x1, y1, CELL_SHOOTABLE, shootable); + map[m].setcell(m, x1, y1, CELL_WALKABLE, false); + map[m].setcell(m, x1, y1, CELL_SHOOTABLE, shootable); clif->changemapcell(0, m, x1, y1, map_getcell(m, x1, y1, CELL_GETTYPE), ALL_SAMEMAP); } @@ -2682,8 +2765,8 @@ void map_iwall_remove(const char *wall_name) for( i = 0; i < iwall->size; i++ ) { map_iwall_nextxy(iwall->x, iwall->y, iwall->dir, i, &x1, &y1); - map_setcell(iwall->m, x1, y1, CELL_SHOOTABLE, true); - map_setcell(iwall->m, x1, y1, CELL_WALKABLE, true); + map[iwall->m].setcell(iwall->m, x1, y1, CELL_SHOOTABLE, true); + map[iwall->m].setcell(iwall->m, x1, y1, CELL_WALKABLE, true); clif->changemapcell(0, iwall->m, x1, y1, map_getcell(iwall->m, x1, y1, CELL_GETTYPE), ALL_SAMEMAP); } @@ -2740,8 +2823,7 @@ int map_eraseallipport_sub(DBKey key, DBData *data, va_list va) return 0; } -int map_eraseallipport(void) -{ +int map_eraseallipport(void) { map_db->foreach(map_db,map_eraseallipport_sub); return 1; } @@ -2749,8 +2831,7 @@ int map_eraseallipport(void) /*========================================== * Delete mapindex from db of another map server *------------------------------------------*/ -int map_eraseipport(unsigned short mapindex, uint32 ip, uint16 port) -{ +int map_eraseipport(unsigned short mapindex, uint32 ip, uint16 port) { struct map_data_other_server *mdos; mdos = (struct map_data_other_server*)uidb_get(map_db,(unsigned int)mapindex); @@ -2800,13 +2881,12 @@ static char *map_init_mapcache(FILE *fp) * Map cache reading * [Shinryo]: Optimized some behaviour to speed this up *==========================================*/ -int map_readfromcache(struct map_data *m, char *buffer, char *decode_buffer) -{ +int map_readfromcache(struct map_data *m, char *buffer) { int i; struct map_cache_main_header *header = (struct map_cache_main_header *)buffer; struct map_cache_map_info *info = NULL; char *p = buffer + sizeof(struct map_cache_main_header); - + for(i = 0; i < header->map_count; i++) { info = (struct map_cache_map_info *)p; @@ -2818,8 +2898,8 @@ int map_readfromcache(struct map_data *m, char *buffer, char *decode_buffer) } if( info && i < header->map_count ) { - unsigned long size, xy; - + unsigned long size; + if( info->xs <= 0 || info->ys <= 0 ) return 0;// Invalid @@ -2831,51 +2911,30 @@ int map_readfromcache(struct map_data *m, char *buffer, char *decode_buffer) ShowWarning("map_readfromcache: %s exceeded MAX_MAP_SIZE of %d\n", info->name, MAX_MAP_SIZE); return 0; // Say not found to remove it from list.. [Shinryo] } - - // TO-DO: Maybe handle the scenario, if the decoded buffer isn't the same size as expected? [Shinryo] - decode_zip(decode_buffer, &size, p+sizeof(struct map_cache_map_info), info->len); - - CREATE(m->cell, struct mapcell, size); - - - for( xy = 0; xy < size; ++xy ) - m->cell[xy] = map_gat2cell(decode_buffer[xy]); - + + m->cellPos = p; + m->cell = (struct mapcell *)0xdeadbeaf; + return 1; } return 0; // Not found } -int map_addmap(char* mapname) -{ - if( strcmpi(mapname,"clear")==0 ) - { - map_num = 0; - instance_start = 0; - return 0; - } - - if( map_num >= MAX_MAP_PER_SERVER - 1 ) - { - ShowError("Could not add map '"CL_WHITE"%s"CL_RESET"', the limit of maps has been reached.\n",mapname); - return 1; - } - mapindex_getmapname(mapname, map[map_num].name); - map_num++; +int map_addmap(char* mapname) { + map[map_num].instance_id = -1; + mapindex_getmapname(mapname, map[map_num++].name); return 0; } -static void map_delmapid(int id) -{ +static void map_delmapid(int id) { ShowNotice("Removing map [ %s ] from maplist"CL_CLL"\n",map[id].name); memmove(map+id, map+id+1, sizeof(map[0])*(map_num-id-1)); map_num--; } -int map_delmap(char* mapname) -{ +int map_delmap(char* mapname) { int i; char map_name[MAP_NAME_LENGTH]; @@ -2958,13 +3017,62 @@ void map_zone_db_clear(void) { } aFree(map_zone_all.capped_skills); } - +void map_clean(int i) { + int v; + if(map[i].cell && map[i].cell != (struct mapcell *)0xdeadbeaf) aFree(map[i].cell); + if(map[i].block) aFree(map[i].block); + if(map[i].block_mob) aFree(map[i].block_mob); + + if(battle_config.dynamic_mobs) { //Dynamic mobs flag by [random] + int j; + if(map[i].mob_delete_timer != INVALID_TIMER) + delete_timer(map[i].mob_delete_timer, map_removemobs_timer); + for (j=0; j<MAX_MOB_LIST_PER_MAP; j++) + if (map[i].moblist[j]) aFree(map[i].moblist[j]); + } + + if( map[i].unit_count ) { + for(v = 0; v < map[i].unit_count; v++) { + aFree(map[i].units[v]); + } + if( map[i].units ) { + aFree(map[i].units); + map[i].units = NULL; + } + map[i].unit_count = 0; + } + + if( map[i].skill_count ) { + for(v = 0; v < map[i].skill_count; v++) { + aFree(map[i].skills[v]); + } + if( map[i].skills ) { + aFree(map[i].skills); + map[i].skills = NULL; + } + map[i].skill_count = 0; + } + + if( map[i].zone_mf_count ) { + for(v = 0; v < map[i].zone_mf_count; v++) { + aFree(map[i].zone_mf[v]); + } + if( map[i].zone_mf ) { + aFree(map[i].zone_mf); + map[i].zone_mf = NULL; + } + map[i].zone_mf_count = 0; + } + + if( map[i].channel ) + clif->chsys_delete(map[i].channel); +} void do_final_maps(void) { int i, v = 0; for( i = 0; i < map_num; i++ ) { - if(map[i].cell) aFree(map[i].cell); + if(map[i].cell && map[i].cell != (struct mapcell *)0xdeadbeaf ) aFree(map[i].cell); if(map[i].block) aFree(map[i].block); if(map[i].block_mob) aFree(map[i].block_mob); @@ -3009,6 +3117,12 @@ void do_final_maps(void) { map[i].zone_mf_count = 0; } + if( map[i].drop_list_count ) { + map[i].drop_list_count = 0; + } + if( map[i].drop_list != NULL ) + aFree(map[i].drop_list); + if( map[i].channel ) clif->chsys_delete(map[i].channel); } @@ -3028,7 +3142,10 @@ void map_flags_init(void) { map[i].nocommand = 0; // nocommand mapflag level map[i].bexp = 100; // per map base exp multiplicator map[i].jexp = 100; // per map job exp multiplicator - memset(map[i].drop_list, 0, sizeof(map[i].drop_list)); // pvp nightmare drop list + if( map[i].drop_list != NULL ) + aFree(map[i].drop_list); + map[i].drop_list = NULL; + map[i].drop_list_count = 0; if( map[i].unit_count ) { for(v = 0; v < map[i].unit_count; v++) { @@ -3153,14 +3270,12 @@ int map_readgat (struct map_data* m) /*====================================== * Add/Remove map to the map_db *--------------------------------------*/ -void map_addmap2db(struct map_data *m) -{ - uidb_put(map_db, (unsigned int)m->index, m); +void map_addmap2db(struct map_data *m) { + index2mapid[m->index] = m->m; } -void map_removemapdb(struct map_data *m) -{ - uidb_remove(map_db, (unsigned int)m->index); +void map_removemapdb(struct map_data *m) { + index2mapid[m->index] = 0; } /*====================================== @@ -3170,8 +3285,6 @@ int map_readallmaps (void) { int i; FILE* fp=NULL; int maps_removed = 0; - char *map_cache_buffer = NULL; // Has the uncompressed gat data of all maps, so just one allocation has to be made - char map_cache_decode_buffer[MAX_MAP_SIZE]; if( enable_grf ) ShowStatus("Loading maps (using GRF files)...\n"); @@ -3203,7 +3316,7 @@ int map_readallmaps (void) { if( ! (enable_grf? map_readgat(&map[i]) - :map_readfromcache(&map[i], map_cache_buffer, map_cache_decode_buffer)) + :map_readfromcache(&map[i], map_cache_buffer)) ) { map_delmapid(i); maps_removed++; @@ -3213,10 +3326,9 @@ int map_readallmaps (void) { map[i].index = mapindex_name2id(map[i].name); - if (uidb_get(map_db,(unsigned int)map[i].index) != NULL) - { + if ( index2mapid[map[i].index] != 0 ) { ShowWarning("Map %s already loaded!"CL_CLL"\n", map[i].name); - if (map[i].cell) { + if (map[i].cell && map[i].cell != (struct mapcell *)0xdeadbeaf) { aFree(map[i].cell); map[i].cell = NULL; } @@ -3225,10 +3337,10 @@ int map_readallmaps (void) { i--; continue; } - + + map[i].m = i; map_addmap2db(&map[i]); - map[i].m = i; memset(map[i].moblist, 0, sizeof(map[i].moblist)); //Initialize moblist [Skotlex] map[i].mob_delete_timer = INVALID_TIMER; //Initialize timer [Skotlex] @@ -3238,6 +3350,9 @@ int map_readallmaps (void) { size = map[i].bxs * map[i].bys * sizeof(struct block_list*); map[i].block = (struct block_list**)aCalloc(size, 1); map[i].block_mob = (struct block_list**)aCalloc(size, 1); + + map[i].getcellp = map_sub_getcellp; + map[i].setcell = map_sub_setcell; } // intialization and configuration-dependent adjustments of mapflags @@ -3245,14 +3360,11 @@ int map_readallmaps (void) { if( !enable_grf ) { fclose(fp); - - // The cache isn't needed anymore, so free it.. [Shinryo] - aFree(map_cache_buffer); } // finished map loading ShowInfo("Successfully loaded '"CL_WHITE"%d"CL_RESET"' maps."CL_CLL"\n",map_num); - instance_start = map_num; // Next Map Index will be instances + instance->start_id = map_num; // Next Map Index will be instances if (maps_removed) ShowNotice("Maps removed: '"CL_WHITE"%d"CL_RESET"'\n",maps_removed); @@ -3267,20 +3379,17 @@ static int char_ip_set = 0; /*========================================== * Read map server configuration files (conf/map_server.conf...) *------------------------------------------*/ -int map_config_read(char *cfgName) -{ +int map_config_read(char *cfgName) { char line[1024], w1[1024], w2[1024]; FILE *fp; fp = fopen(cfgName,"r"); - if( fp == NULL ) - { + if( fp == NULL ) { ShowError("Map configuration file not found at: %s\n", cfgName); return 1; } - while( fgets(line, sizeof(line), fp) ) - { + while( fgets(line, sizeof(line), fp) ) { char* ptr; if( line[0] == '/' && line[1] == '/' ) @@ -3320,9 +3429,9 @@ int map_config_read(char *cfgName) clif->setport(atoi(w2)); map_port = (atoi(w2)); } else if (strcmpi(w1, "map") == 0) - map_addmap(w2); + map_num++; else if (strcmpi(w1, "delmap") == 0) - map_delmap(w2); + map_num--; else if (strcmpi(w1, "npc") == 0) npc_addsrcfile(w2); else if (strcmpi(w1, "delnpc") == 0) @@ -3362,7 +3471,43 @@ int map_config_read(char *cfgName) fclose(fp); return 0; } - +int map_config_read_sub(char *cfgName) { + char line[1024], w1[1024], w2[1024]; + FILE *fp; + + fp = fopen(cfgName,"r"); + if( fp == NULL ) { + ShowError("Map configuration file not found at: %s\n", cfgName); + return 1; + } + + while( fgets(line, sizeof(line), fp) ) { + char* ptr; + + if( line[0] == '/' && line[1] == '/' ) + continue; + if( (ptr = strstr(line, "//")) != NULL ) + *ptr = '\n'; //Strip comments + if( sscanf(line, "%[^:]: %[^\t\r\n]", w1, w2) < 2 ) + continue; + + //Strip trailing spaces + ptr = w2 + strlen(w2); + while (--ptr >= w2 && *ptr == ' '); + ptr++; + *ptr = '\0'; + + if (strcmpi(w1, "map") == 0) + map_addmap(w2); + else if (strcmpi(w1, "delmap") == 0) + map_delmap(w2); + else if (strcmpi(w1, "import") == 0) + map_config_read_sub(w2); + } + + fclose(fp); + return 0; +} void map_reloadnpc_sub(char *cfgName) { char line[1024], w1[1024], w2[1024]; @@ -4849,11 +4994,12 @@ void read_map_zone_db(void) { /** * @see DBApply */ -int map_db_final(DBKey key, DBData *data, va_list ap) -{ +int map_db_final(DBKey key, DBData *data, va_list ap) { struct map_data_other_server *mdos = DB->data2ptr(data); - if(mdos && mdos->cell == NULL) + + if(mdos && malloclib->verify_ptr(mdos) && mdos->cell == NULL) aFree(mdos); + return 0; } @@ -4877,8 +5023,7 @@ int nick_db_final(DBKey key, DBData *data, va_list args) return 0; } -int cleanup_sub(struct block_list *bl, va_list ap) -{ +int cleanup_sub(struct block_list *bl, va_list ap) { nullpo_ret(bl); switch(bl->type) { @@ -4932,6 +5077,8 @@ void do_final(void) map_quit(sd); mapit->free(iter); + instance->final(); + /* prepares npcs for a faster shutdown process */ do_clear_npc(); @@ -4954,7 +5101,6 @@ void do_final(void) clif->final(); do_final_npc(); script->final(); - do_final_instance(); do_final_itemdb(); do_final_storage(); guild->final(); @@ -4993,6 +5139,9 @@ void do_final(void) aFree(map); + if( !enable_grf ) + aFree(map_cache_buffer); + ShowStatus("Finished.\n"); } @@ -5196,10 +5345,12 @@ void map_defaults(void) { void load_defaults(void) { atcommand_defaults(); battle_defaults(); + battleground_defaults(); buyingstore_defaults(); clif_defaults(); guild_defaults(); homunculus_defaults(); + instance_defaults(); ircbot_defaults(); log_defaults(); map_defaults(); @@ -5228,79 +5379,50 @@ int do_init(int argc, char *argv[]) rnd_init(); - for( i = 1; i < argc ; i++ ) - { + for( i = 1; i < argc ; i++ ) { const char* arg = argv[i]; - if( arg[0] != '-' && ( arg[0] != '/' || arg[1] == '-' ) ) - {// -, -- and / + if( arg[0] != '-' && ( arg[0] != '/' || arg[1] == '-' ) ) {// -, -- and / ShowError("Unknown option '%s'.\n", argv[i]); exit(EXIT_FAILURE); - } - else if( (++arg)[0] == '-' ) - {// long option + } else if( (++arg)[0] == '-' ) {// long option arg++; - if( strcmp(arg, "help") == 0 ) - { + if( strcmp(arg, "help") == 0 ) { map_helpscreen(true); - } - else if( strcmp(arg, "version") == 0 ) - { + } else if( strcmp(arg, "version") == 0 ) { map_versionscreen(true); - } - else if( strcmp(arg, "map-config") == 0 ) - { + } else if( strcmp(arg, "map-config") == 0 ) { if( map_arg_next_value(arg, i, argc) ) MAP_CONF_NAME = argv[++i]; - } - else if( strcmp(arg, "battle-config") == 0 ) - { + } else if( strcmp(arg, "battle-config") == 0 ) { if( map_arg_next_value(arg, i, argc) ) BATTLE_CONF_FILENAME = argv[++i]; - } - else if( strcmp(arg, "atcommand-config") == 0 ) - { + } else if( strcmp(arg, "atcommand-config") == 0 ) { if( map_arg_next_value(arg, i, argc) ) ATCOMMAND_CONF_FILENAME = argv[++i]; - } - else if( strcmp(arg, "script-config") == 0 ) - { + } else if( strcmp(arg, "script-config") == 0 ) { if( map_arg_next_value(arg, i, argc) ) SCRIPT_CONF_NAME = argv[++i]; - } - else if( strcmp(arg, "msg-config") == 0 ) - { + } else if( strcmp(arg, "msg-config") == 0 ) { if( map_arg_next_value(arg, i, argc) ) MSG_CONF_NAME = argv[++i]; - } - else if( strcmp(arg, "grf-path-file") == 0 ) - { + } else if( strcmp(arg, "grf-path-file") == 0 ) { if( map_arg_next_value(arg, i, argc) ) GRF_PATH_FILENAME = argv[++i]; - } - else if( strcmp(arg, "inter-config") == 0 ) - { + } else if( strcmp(arg, "inter-config") == 0 ) { if( map_arg_next_value(arg, i, argc) ) INTER_CONF_NAME = argv[++i]; - } - else if( strcmp(arg, "log-config") == 0 ) - { + } else if( strcmp(arg, "log-config") == 0 ) { if( map_arg_next_value(arg, i, argc) ) LOG_CONF_NAME = argv[++i]; - } - else if( strcmp(arg, "run-once") == 0 ) // close the map-server as soon as its done.. for testing [Celest] - { + } else if( strcmp(arg, "run-once") == 0 ) { // close the map-server as soon as its done.. for testing [Celest] runflag = CORE_ST_STOP; - } - else - { + } else { ShowError("Unknown option '%s'.\n", argv[i]); exit(EXIT_FAILURE); } - } - else switch( arg[0] ) - {// short option + } else switch( arg[0] ) {// short option case '?': case 'h': map_helpscreen(true); @@ -5313,12 +5435,13 @@ int do_init(int argc, char *argv[]) exit(EXIT_FAILURE); } } - - CREATE(map,struct map_data,MAX_MAP_PER_SERVER); - + memset(&index2mapid, 0, sizeof(index2mapid)); + load_defaults(); - map_config_read(MAP_CONF_NAME); + CREATE(map,struct map_data,map_num); + map_num = 0; + map_config_read_sub(MAP_CONF_NAME); // loads npcs map_reloadnpc(false); @@ -5384,7 +5507,7 @@ int do_init(int argc, char *argv[]) atcommand->init(); battle->init(); - do_init_instance(); + instance->init(); do_init_chrif(); clif->init(); ircbot->init(); diff --git a/src/map/map.h b/src/map/map.h index f524e8840..850913474 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -27,20 +27,18 @@ enum E_MAPSERVER_ST { MAPSERVER_ST_LAST }; - #define MAX_NPC_PER_MAP 512 #define AREA_SIZE battle_config.area_size #define DAMAGELOG_SIZE 30 #define LOOTITEM_SIZE 10 #define MAX_MOBSKILL 50 //Max 128, see mob skill_idx type if need this higher -#define MAX_MOB_LIST_PER_MAP 128 +#define MAX_MOB_LIST_PER_MAP 100 #define MAX_EVENTQUEUE 2 #define MAX_EVENTTIMER 32 #define NATURAL_HEAL_INTERVAL 500 #define MIN_FLOORITEM 2 #define MAX_FLOORITEM START_ACCOUNT_NUM #define MAX_LEVEL 150 -#define MAX_DROP_PER_MAP 48 #define MAX_IGNORE_LIST 20 // official is 14 #define MAX_VENDING 12 #define MAX_MAP_SIZE 512*512 // Wasn't there something like this already? Can't find it.. [Shinryo] @@ -558,6 +556,12 @@ void map_zone_change2(int m, struct map_zone_data *zone); struct map_zone_data map_zone_all;/* used as a base on all maps */ struct map_zone_data map_zone_pk;/* used for (pk_mode) */ +struct map_drop_list { + int drop_id; + int drop_type; + int drop_per; +}; + struct map_data { char name[MAP_NAME_LENGTH]; @@ -625,11 +629,8 @@ struct map_data { } flag; struct point save; struct npc_data *npc[MAX_NPC_PER_MAP]; - struct { - int drop_id; - int drop_type; - int drop_per; - } drop_list[MAX_DROP_PER_MAP]; + struct map_drop_list *drop_list; + unsigned short drop_list_count; struct spawn_data *moblist[MAX_MOB_LIST_PER_MAP]; // [Wizputer] int mob_delete_timer; // [Skotlex] @@ -676,6 +677,14 @@ struct map_data { unsigned short short_damage_rate; /* long_damage_rate mapflag */ unsigned short long_damage_rate; + + /* instance unique name */ + char *cName; + + /* */ + int (*getcellp)(struct map_data* m,int16 x,int16 y,cell_chk cellchk); + void (*setcell) (int16 m, int16 x, int16 y, cell_t cell, bool flag); + char *cellPos; }; /// Stores information about a remote map (for multi-mapserver setups). @@ -689,8 +698,6 @@ struct map_data_other_server { }; int map_getcell(int16 m,int16 x,int16 y,cell_chk cellchk); -int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk); -void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag); void map_setgatcell(int16 m, int16 x, int16 y, int gat); struct map_data *map; @@ -711,6 +718,8 @@ extern char charhelp_txt[]; extern char wisp_server_name[]; +void map_cellfromcache(struct map_data *m); + // users void map_setusers(int); int map_getusers(void); @@ -733,6 +742,7 @@ int map_foreachinmovearea(int (*func)(struct block_list*,va_list), struct block_ int map_foreachincell(int (*func)(struct block_list*,va_list), int16 m, int16 x, int16 y, int type, ...); int map_foreachinpath(int (*func)(struct block_list*,va_list), int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int16 range, int length, int type, ...); int map_foreachinmap(int (*func)(struct block_list*,va_list), int16 m, int type, ...); +int map_foreachininstance(int (*func)(struct block_list*,va_list), int16 instance_id, int type,...); //blocklist nb in one cell int map_count_oncell(int16 m,int16 x,int16 y,int type); struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int16 x,int16 y,uint16 skill_id,struct skill_unit *, int flag); @@ -831,6 +841,7 @@ void map_removemobs(int16 m); // [Wizputer] void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex] void map_addmap2db(struct map_data *m); void map_removemapdb(struct map_data *m); +void map_clean(int i); extern char *INTER_CONF_NAME; extern char *LOG_CONF_NAME; diff --git a/src/map/mob.c b/src/map/mob.c index 57325ba1c..433e979f3 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -137,7 +137,7 @@ void mvptomb_create(struct mob_data *md, char *killer, time_t time) nd->bl.id = md->tomb_nid = npc_get_new_npc_id(); - nd->ud.dir = md->ud.dir; + nd->dir = md->ud.dir; nd->bl.m = md->bl.m; nd->bl.x = md->bl.x; nd->bl.y = md->bl.y; @@ -262,8 +262,7 @@ int mob_parse_dataset(struct spawn_data *data) /*========================================== * Generates the basic mob data using the spawn_data provided. *------------------------------------------*/ -struct mob_data* mob_spawn_dataset(struct spawn_data *data) -{ +struct mob_data* mob_spawn_dataset(struct spawn_data *data) { struct mob_data *md = (struct mob_data*)aCalloc(1, sizeof(struct mob_data)); md->bl.id= npc_get_new_npc_id(); md->bl.type = BL_MOB; @@ -290,7 +289,7 @@ struct mob_data* mob_spawn_dataset(struct spawn_data *data) status_set_viewdata(&md->bl, md->class_); status_change_init(&md->bl); unit_dataset(&md->bl); - + map_addiddb(&md->bl); return md; } diff --git a/src/map/npc.c b/src/map/npc.c index 7d0d5c6e1..bff6be30c 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -122,7 +122,7 @@ struct view_data* npc_get_viewdata(int class_) static int npc_isnear_sub(struct block_list* bl, va_list args) { struct npc_data *nd = (struct npc_data*)bl; - if( nd->sc.option & (OPTION_HIDE|OPTION_INVISIBLE) ) + if( nd->option & (OPTION_HIDE|OPTION_INVISIBLE) ) return 0; return 1; @@ -175,7 +175,7 @@ int npc_enable_sub(struct block_list *bl, va_list ap) { TBL_PC *sd = (TBL_PC*)bl; - if (nd->sc.option&OPTION_INVISIBLE) + if (nd->option&OPTION_INVISIBLE) return 1; if( npc_ontouch_event(sd,nd) > 0 && npc_ontouch2_event(sd,nd) > 0 ) @@ -204,20 +204,20 @@ int npc_enable(const char* name, int flag) } if (flag&1) { - nd->sc.option&=~OPTION_INVISIBLE; + nd->option&=~OPTION_INVISIBLE; clif->spawn(&nd->bl); } else if (flag&2) - nd->sc.option&=~OPTION_HIDE; + nd->option&=~OPTION_HIDE; else if (flag&4) - nd->sc.option|= OPTION_HIDE; + nd->option|= OPTION_HIDE; else { //Can't change the view_data to invisible class because the view_data for all npcs is shared! [Skotlex] - nd->sc.option|= OPTION_INVISIBLE; + nd->option|= OPTION_INVISIBLE; clif->clearunit_area(&nd->bl,CLR_OUTSIGHT); // Hack to trick maya purple card [Xazax] } if (nd->class_ == WARP_CLASS || nd->class_ == FLAG_CLASS) { //Client won't display option changes for these classes [Toms] - if (nd->sc.option&(OPTION_HIDE|OPTION_INVISIBLE)) + if (nd->option&(OPTION_HIDE|OPTION_INVISIBLE)) clif->clearunit_area(&nd->bl, CLR_OUTSIGHT); else clif->spawn(&nd->bl); @@ -385,8 +385,7 @@ static int npc_event_do_sub(DBKey key, DBData *data, va_list ap) nullpo_ret(c = va_arg(ap, int *)); nullpo_ret(name = va_arg(ap, const char *)); - if( p && strcmpi(name, p) == 0 ) - { + if( p && strcmpi(name, p) == 0 ) { run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id); (*c)++; } @@ -815,7 +814,7 @@ int npc_event_sub(struct map_session_data* sd, struct event_data* ev, const char ShowWarning("npc_event: player's event queue is full, can't add event '%s' !\n", eventname); return 1; } - if( ev->nd->sc.option&OPTION_INVISIBLE ) + if( ev->nd->option&OPTION_INVISIBLE ) { //Disabled npc, shouldn't trigger event. npc_event_dequeue(sd); @@ -835,22 +834,20 @@ int npc_event(struct map_session_data* sd, const char* eventname, int ontouch) nullpo_ret(sd); - if( ev == NULL || (nd = ev->nd) == NULL ) - { + if( ev == NULL || (nd = ev->nd) == NULL ) { if( !ontouch ) ShowError("npc_event: event not found [%s]\n", eventname); return ontouch; } - switch(ontouch) - { - case 1: - nd->touching_id = sd->bl.id; - sd->touching_id = nd->bl.id; - break; - case 2: - sd->areanpc_id = nd->bl.id; - break; + switch(ontouch) { + case 1: + nd->touching_id = sd->bl.id; + sd->touching_id = nd->bl.id; + break; + case 2: + sd->areanpc_id = nd->bl.id; + break; } return npc_event_sub(sd,ev,eventname); @@ -928,9 +925,8 @@ int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y) //if(sd->npc_id) // return 1; - for(i=0;i<map[m].npc_num;i++) - { - if (map[m].npc[i]->sc.option&OPTION_INVISIBLE) { + for(i=0;i<map[m].npc_num;i++) { + if (map[m].npc[i]->option&OPTION_INVISIBLE) { f=0; // a npc was found, but it is disabled; don't print warning continue; } @@ -1008,13 +1004,11 @@ int npc_touch_areanpc2(struct mob_data *md) struct event_data* ev; int xs, ys; - for( i = 0; i < map[m].npc_num; i++ ) - { - if( map[m].npc[i]->sc.option&OPTION_INVISIBLE ) + for( i = 0; i < map[m].npc_num; i++ ) { + if( map[m].npc[i]->option&OPTION_INVISIBLE ) continue; - switch( map[m].npc[i]->subtype ) - { + switch( map[m].npc[i]->subtype ) { case WARP: if( !( battle_config.mob_warp&1 ) ) continue; @@ -1087,27 +1081,25 @@ int npc_check_areanpc(int flag, int16 m, int16 x, int16 y, int16 range) if (!i) return 0; //No NPC_CELLs. //Now check for the actual NPC on said range. - for(i=0;i<map[m].npc_num;i++) - { - if (map[m].npc[i]->sc.option&OPTION_INVISIBLE) + for(i=0;i<map[m].npc_num;i++) { + if (map[m].npc[i]->option&OPTION_INVISIBLE) continue; - switch(map[m].npc[i]->subtype) - { - case WARP: - if (!(flag&1)) - continue; - xs=map[m].npc[i]->u.warp.xs; - ys=map[m].npc[i]->u.warp.ys; - break; - case SCRIPT: - if (!(flag&2)) + switch(map[m].npc[i]->subtype) { + case WARP: + if (!(flag&1)) + continue; + xs=map[m].npc[i]->u.warp.xs; + ys=map[m].npc[i]->u.warp.ys; + break; + case SCRIPT: + if (!(flag&2)) + continue; + xs=map[m].npc[i]->u.scr.xs; + ys=map[m].npc[i]->u.scr.ys; + break; + default: continue; - xs=map[m].npc[i]->u.scr.xs; - ys=map[m].npc[i]->u.scr.ys; - break; - default: - continue; } if( x1 >= map[m].npc[i]->bl.x-xs && x0 <= map[m].npc[i]->bl.x+xs @@ -1206,7 +1198,7 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd) if ((nd = npc_checknear(sd,&nd->bl)) == NULL) return 1; //Hidden/Disabled npc. - if (nd->class_ < 0 || nd->sc.option&(OPTION_INVISIBLE|OPTION_HIDE)) + if (nd->class_ < 0 || nd->option&(OPTION_INVISIBLE|OPTION_HIDE)) return 1; switch(nd->subtype) { @@ -1291,7 +1283,7 @@ int npc_buysellsel(struct map_session_data* sd, int id, int type) sd->npc_id=0; return 1; } - if (nd->sc.option & OPTION_INVISIBLE) // can't buy if npc is not visible (hack?) + if (nd->option & OPTION_INVISIBLE) // can't buy if npc is not visible (hack?) return 1; if( nd->class_ < 0 && !sd->state.callshop ) {// not called through a script and is not a visible NPC so an invalid call @@ -1759,8 +1751,7 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) //Atempt to remove an npc from a map //This doesn't remove it from map_db -int npc_remove_map(struct npc_data* nd) -{ +int npc_remove_map(struct npc_data* nd) { int16 m,i; nullpo_retr(1, nd); @@ -1892,6 +1883,11 @@ int npc_unload(struct npc_data* nd, bool single) { guild->flag_remove(nd); } + if( nd->ud != &npc_base_ud ) { + aFree(nd->ud); + nd->ud = NULL; + } + script_stop_sleeptimers(nd->bl.id); aFree(nd); @@ -2060,8 +2056,7 @@ static void npc_parsename(struct npc_data* nd, const char* name, const char* sta } //Add then display an npc warp on map -struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y) -{ +struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y) { int i, flag = 0; struct npc_data *nd; @@ -2100,8 +2095,7 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short npc_setcells(nd); map_addblock(&nd->bl); status_set_viewdata(&nd->bl, nd->class_); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); + nd->ud = &npc_base_ud; if( map[nd->bl.m].users ) clif->spawn(&nd->bl); strdb_put(npcname_db, nd->exname, nd); @@ -2166,8 +2160,7 @@ static const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const npc_setcells(nd); map_addblock(&nd->bl); status_set_viewdata(&nd->bl, nd->class_); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); + nd->ud = &npc_base_ud; if( map[nd->bl.m].users ) clif->spawn(&nd->bl); strdb_put(npcname_db, nd->exname, nd); @@ -2275,18 +2268,15 @@ static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const ++npc_shop; nd->bl.type = BL_NPC; nd->subtype = type; - if( m >= 0 ) - {// normal shop npc + if( m >= 0 ) {// normal shop npc map_addnpc(m,nd); map_addblock(&nd->bl); status_set_viewdata(&nd->bl, nd->class_); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); - nd->ud.dir = dir; + nd->ud = &npc_base_ud; + nd->dir = dir; if( map[nd->bl.m].users ) clif->spawn(&nd->bl); - } else - {// 'floating' shop? + } else {// 'floating' shop? map_addiddb(&nd->bl); } strdb_put(npcname_db, nd->exname, nd); @@ -2485,23 +2475,18 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons nd->bl.type = BL_NPC; nd->subtype = SCRIPT; - if( m >= 0 ) - { + if( m >= 0 ) { map_addnpc(m, nd); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); - nd->ud.dir = dir; + nd->ud = &npc_base_ud; + nd->dir = dir; npc_setcells(nd); map_addblock(&nd->bl); - if( class_ >= 0 ) - { + if( class_ >= 0 ) { status_set_viewdata(&nd->bl, nd->class_); if( map[nd->bl.m].users ) clif->spawn(&nd->bl); } - } - else - { + } else { // we skip map_addnpc, but still add it to the list of ID's map_addiddb(&nd->bl); } @@ -2576,13 +2561,10 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch type = dnd->subtype; // get placement - if( (type==SHOP || type==CASHSHOP || type==SCRIPT) && strcmp(w1, "-") == 0 ) - {// floating shop/chashshop/script + if( (type==SHOP || type==CASHSHOP || type==SCRIPT) && strcmp(w1, "-") == 0 ) {// floating shop/chashshop/script x = y = dir = 0; m = -1; - } - else - { + } else { if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 )// <map name>,<x>,<y>,<facing> { ShowError("npc_parse_duplicate: Invalid placement format for duplicate in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); @@ -2599,8 +2581,7 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch if( type == WARP && sscanf(w4, "%d,%d", &xs, &ys) == 2 );// <spanx>,<spany> else if( type == SCRIPT && sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3);// <sprite id>,<triggerX>,<triggerY> else if( type != WARP ) class_ = atoi(w4);// <sprite id> - else - { + else { ShowError("npc_parse_duplicate: Invalid span format for duplicate warp in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4); return end;// next line, try to continue } @@ -2618,56 +2599,50 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch nd->src_id = src_id; nd->bl.type = BL_NPC; nd->subtype = (enum npc_subtype)type; - switch( type ) - { - case SCRIPT: - ++npc_script; - nd->u.scr.xs = xs; - nd->u.scr.ys = ys; - nd->u.scr.script = dnd->u.scr.script; - nd->u.scr.label_list = dnd->u.scr.label_list; - nd->u.scr.label_list_num = dnd->u.scr.label_list_num; - break; - - case SHOP: - case CASHSHOP: - ++npc_shop; - nd->u.shop.shop_item = dnd->u.shop.shop_item; - nd->u.shop.count = dnd->u.shop.count; - break; - - case WARP: - ++npc_warp; - if( !battle_config.warp_point_debug ) - nd->class_ = WARP_CLASS; - else - nd->class_ = WARP_DEBUG_CLASS; - nd->u.warp.xs = xs; - nd->u.warp.ys = ys; - nd->u.warp.mapindex = dnd->u.warp.mapindex; - nd->u.warp.x = dnd->u.warp.x; - nd->u.warp.y = dnd->u.warp.y; - break; + switch( type ) { + case SCRIPT: + ++npc_script; + nd->u.scr.xs = xs; + nd->u.scr.ys = ys; + nd->u.scr.script = dnd->u.scr.script; + nd->u.scr.label_list = dnd->u.scr.label_list; + nd->u.scr.label_list_num = dnd->u.scr.label_list_num; + break; + + case SHOP: + case CASHSHOP: + ++npc_shop; + nd->u.shop.shop_item = dnd->u.shop.shop_item; + nd->u.shop.count = dnd->u.shop.count; + break; + + case WARP: + ++npc_warp; + if( !battle_config.warp_point_debug ) + nd->class_ = WARP_CLASS; + else + nd->class_ = WARP_DEBUG_CLASS; + nd->u.warp.xs = xs; + nd->u.warp.ys = ys; + nd->u.warp.mapindex = dnd->u.warp.mapindex; + nd->u.warp.x = dnd->u.warp.x; + nd->u.warp.y = dnd->u.warp.y; + break; } //Add the npc to its location - if( m >= 0 ) - { + if( m >= 0 ) { map_addnpc(m, nd); - status_change_init(&nd->bl); - unit_dataset(&nd->bl); - nd->ud.dir = dir; + nd->ud = &npc_base_ud; + nd->dir = dir; npc_setcells(nd); map_addblock(&nd->bl); - if( class_ >= 0 ) - { + if( class_ >= 0 ) { status_set_viewdata(&nd->bl, nd->class_); if( map[nd->bl.m].users ) clif->spawn(&nd->bl); } - } - else - { + } else { // we skip map_addnpc, but still add it to the list of ID's map_addiddb(&nd->bl); } @@ -2694,25 +2669,21 @@ const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const ch int npc_duplicate4instance(struct npc_data *snd, int16 m) { char newname[NAME_LENGTH]; - if( map[m].instance_id == 0 ) + if( map[m].instance_id == -1 ) return 1; snprintf(newname, ARRAYLENGTH(newname), "dup_%d_%d", map[m].instance_id, snd->bl.id); - if( npc_name2id(newname) != NULL ) - { // Name already in use + if( npc_name2id(newname) != NULL ) { // Name already in use ShowError("npc_duplicate4instance: the npcname (%s) is already in use while trying to duplicate npc %s in instance %d.\n", newname, snd->exname, map[m].instance_id); return 1; } - if( snd->subtype == WARP ) - { // Adjust destination, if instanced + if( snd->subtype == WARP ) { // Adjust destination, if instanced struct npc_data *wnd = NULL; // New NPC int dm = map_mapindex2mapid(snd->u.warp.mapindex), im; if( dm < 0 ) return 1; - im = instance_mapid2imapid(dm, map[m].instance_id); - if( im == -1 ) - { + if( ( im = instance->mapid2imapid(dm, map[m].instance_id) ) == -1 ) { ShowError("npc_duplicate4instance: warp (%s) leading to instanced map (%s), but instance map is not attached to current instance.\n", map[dm].name, snd->exname); return 1; } @@ -2738,18 +2709,15 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) { npc_setcells(wnd); map_addblock(&wnd->bl); status_set_viewdata(&wnd->bl, wnd->class_); - status_change_init(&wnd->bl); - unit_dataset(&wnd->bl); + wnd->ud = &npc_base_ud; if( map[wnd->bl.m].users ) clif->spawn(&wnd->bl); strdb_put(npcname_db, wnd->exname, wnd); - } - else - { + } else { static char w1[50], w2[50], w3[50], w4[50]; const char* stat_buf = "- call from instancing subsystem -\n"; - snprintf(w1, sizeof(w1), "%s,%d,%d,%d", map[m].name, snd->bl.x, snd->bl.y, snd->ud.dir); + snprintf(w1, sizeof(w1), "%s,%d,%d,%d", map[m].name, snd->bl.x, snd->bl.y, snd->dir); snprintf(w2, sizeof(w2), "duplicate(%s)", snd->exname); snprintf(w3, sizeof(w3), "%s::%s", snd->name, newname); @@ -2765,39 +2733,36 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) { } //Set mapcell CELL_NPC to trigger event later -void npc_setcells(struct npc_data* nd) -{ +void npc_setcells(struct npc_data* nd) { int16 m = nd->bl.m, x = nd->bl.x, y = nd->bl.y, xs, ys; int i,j; - switch(nd->subtype) - { - case WARP: - xs = nd->u.warp.xs; - ys = nd->u.warp.ys; - break; - case SCRIPT: - xs = nd->u.scr.xs; - ys = nd->u.scr.ys; - break; - default: - return; // Other types doesn't have touch area + switch(nd->subtype) { + case WARP: + xs = nd->u.warp.xs; + ys = nd->u.warp.ys; + break; + case SCRIPT: + xs = nd->u.scr.xs; + ys = nd->u.scr.ys; + break; + default: + return; // Other types doesn't have touch area } - if (m < 0 || xs < 0 || ys < 0) //invalid range or map + if (m < 0 || xs < 0 || ys < 0 || map[m].cell == (struct mapcell *)0xdeadbeaf) //invalid range or map return; for (i = y-ys; i <= y+ys; i++) { for (j = x-xs; j <= x+xs; j++) { if (map_getcell(m, j, i, CELL_CHKNOPASS)) continue; - map_setcell(m, j, i, CELL_NPC, true); + map[m].setcell(m, j, i, CELL_NPC, true); } } } -int npc_unsetcells_sub(struct block_list* bl, va_list ap) -{ +int npc_unsetcells_sub(struct block_list* bl, va_list ap) { struct npc_data *nd = (struct npc_data*)bl; int id = va_arg(ap,int); if (nd->bl.id == id) return 0; @@ -2805,8 +2770,7 @@ int npc_unsetcells_sub(struct block_list* bl, va_list ap) return 1; } -void npc_unsetcells(struct npc_data* nd) -{ +void npc_unsetcells(struct npc_data* nd) { int16 m = nd->bl.m, x = nd->bl.x, y = nd->bl.y, xs, ys; int i,j, x0, x1, y0, y1; @@ -2818,7 +2782,7 @@ void npc_unsetcells(struct npc_data* nd) ys = nd->u.scr.ys; } - if (m < 0 || xs < 0 || ys < 0) + if (m < 0 || xs < 0 || ys < 0 || map[m].cell == (struct mapcell *)0xdeadbeaf) return; //Locate max range on which we can locate npc cells @@ -2831,7 +2795,7 @@ void npc_unsetcells(struct npc_data* nd) //Erase this npc's cells for (i = y-ys; i <= y+ys; i++) for (j = x-xs; j <= x+xs; j++) - map_setcell(m, j, i, CELL_NPC, false); + map[m].setcell(m, j, i, CELL_NPC, false); //Re-deploy NPC cells for other nearby npcs. map_foreachinarea( npc_unsetcells_sub, m, x0, y0, x1, y1, BL_NPC, nd->bl.id ); @@ -2910,7 +2874,7 @@ int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const c return 1; } - if( ev->nd->sc.option&OPTION_INVISIBLE ) { // Disabled npc, shouldn't trigger event. + if( ev->nd->option&OPTION_INVISIBLE ) { // Disabled npc, shouldn't trigger event. npc_event_dequeue(sd); return 2; } @@ -2994,8 +2958,7 @@ void npc_parse_mob2(struct spawn_data* mob) { int i; - for( i = mob->active; i < mob->num; ++i ) - { + for( i = mob->active; i < mob->num; ++i ) { struct mob_data* md = mob_spawn_dataset(mob); md->spawn = mob; md->spawn->active++; @@ -3150,19 +3113,17 @@ static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const c memcpy(data, &mob, sizeof(struct spawn_data)); // spawn / cache the new mobs - if( battle_config.dynamic_mobs && map_addmobtolist(data->m, data) >= 0 ) - { + if( battle_config.dynamic_mobs && map_addmobtolist(data->m, data) >= 0 ) { data->state.dynamic = true; npc_cache_mob += data->num; // check if target map has players // (usually shouldn't occur when map server is just starting, // but not the case when we do @reloadscript - if( map[data->m].users > 0 ) + if( map[data->m].users > 0 ) { npc_parse_mob2(data); - } - else - { + } + } else { data->state.dynamic = false; npc_parse_mob2(data); npc_delay_mob += data->num; @@ -3282,16 +3243,11 @@ const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, const char else if (!strcmpi(drop_arg2,"all")) drop_type = 3; - if (drop_id != 0){ - int i; - for (i = 0; i < MAX_DROP_PER_MAP; i++) { - if (map[m].drop_list[i].drop_id == 0){ - map[m].drop_list[i].drop_id = drop_id; - map[m].drop_list[i].drop_type = drop_type; - map[m].drop_list[i].drop_per = drop_per; - break; - } - } + if (drop_id != 0) { + RECREATE(map[m].drop_list, struct map_drop_list, ++map[m].drop_list_count); + map[m].drop_list[map[m].drop_list_count-1].drop_id = drop_id; + map[m].drop_list[map[m].drop_list_count-1].drop_type = drop_type; + map[m].drop_list[map[m].drop_list_count-1].drop_per = drop_per; map[m].flag.pvp_nightmaredrop = 1; } } else if (!state) //Disable @@ -3892,10 +3848,7 @@ int npc_reload(void) { "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", npc_id - npc_new_min, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); - do_final_instance(); - - for( i = 0; i < ARRAYLENGTH(instance); ++i ) - instance_init(instance[i].instance_id); + instance->final(); map_zone_init(); @@ -4002,8 +3955,18 @@ int do_init_npc(void) struct npc_src_list *file; int i; + memset(&npc_base_ud, 0, sizeof( struct unit_data) ); + npc_base_ud.bl = NULL; + npc_base_ud.walktimer = INVALID_TIMER; + npc_base_ud.skilltimer = INVALID_TIMER; + npc_base_ud.attacktimer = INVALID_TIMER; + npc_base_ud.attackabletime = + npc_base_ud.canact_tick = + npc_base_ud.canmove_tick = gettick(); + //Stock view data for normal npcs. memset(&npc_viewdb, 0, sizeof(npc_viewdb)); + npc_viewdb[0].class_ = INVISIBLE_CLASS; //Invisible class is stored here. for( i = 1; i < MAX_NPC_CLASS; i++ ) npc_viewdb[i].class_ = i; @@ -4030,7 +3993,7 @@ int do_init_npc(void) "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Cached\n" "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", npc_id - START_NPC_NUM, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); - + map_zone_init(); npc->motd = npc_name2id("HerculesMOTD"); /* [Ind/Hercules] */ @@ -4068,6 +4031,6 @@ int do_init_npc(void) } void npc_defaults(void) { npc = &npc_s; - + npc->motd = NULL; } diff --git a/src/map/npc.h b/src/map/npc.h index 8a8b14d6e..16e6fe74c 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -12,6 +12,7 @@ struct block_list; struct npc_data; struct view_data; +struct unit_data npc_base_ud; struct npc_timerevent_list { int timer,pos; @@ -26,9 +27,9 @@ struct npc_item_list { struct npc_data { struct block_list bl; - struct unit_data ud; //Because they need to be able to move.... + struct unit_data *ud; struct view_data *vd; - struct status_change sc; //They can't have status changes, but.. they want the visual opt values. + unsigned int option; struct npc_data *master_nd; short class_; short speed; @@ -37,12 +38,13 @@ struct npc_data { int chat_id; int touching_id; unsigned int next_walktime; - + uint8 dir; + unsigned size : 2; struct status_data status; - unsigned int level; - unsigned int stat_point; + unsigned short level; + unsigned short stat_point; void* chatdb; // pointer to a npc_parse struct (see npc_chat.c) char* path;/* path dir */ diff --git a/src/map/packets.h b/src/map/packets.h index 7e14305b7..92df54367 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -1869,6 +1869,10 @@ packet(0x020d,-1); packet(0x0838,6,clif->pSolveCharName,2); packet(0x0439,8,clif->pUseItem,2,4); packet(0x08d2,10); + packet(0x08d7,28,clif->pBGQueueRegister,2); + packet(0x090a,26,clif->pBGQueueCheckState,2); + packet(0x08da,26,clif->pBGQueueRevokeReq,2); + packet(0x08e0,51,clif->pBGQueueBattleBeginAck,2); #endif //2011-11-02aRagexe @@ -1985,7 +1989,6 @@ packet(0x020d,-1); packet(0x08FB,6,clif->pDull,2); //bookingcanceljoinparty packet(0x0907,5,clif->pMoveItem,2,4); packet(0x0908,5); - packet(0x08D7,28,clif->pDull,2,4); //battlegroundreg packet(0x08CF,10);//Amulet spirits packet(0x0977,14);//Monster HP Bar #endif diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index c873d3ad3..9d1f9b280 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -87,6 +87,15 @@ enum packet_headers { #else unit_walkingType = 0x914, #endif + bgqueue_ackType = 0x8d8, + bgqueue_notice_deleteType = 0x8db, + bgqueue_registerType = 0x8d7, + bgqueue_updateinfoType = 0x8d9, + bgqueue_checkstateType = 0x90a, + bgqueue_revokereqType = 0x8da, + bgqueue_battlebeginackType = 0x8e0, + bgqueue_notify_entryType = 0x8d9, + bgqueue_battlebegins = 0x8df, #if PACKETVER > 20130000 /* not sure date */ dropflooritemType = 0x84b, #else @@ -391,6 +400,59 @@ struct packet_maptypeproperty2 { } flag; } __attribute__((packed)); +struct packet_bgqueue_ack { + short PacketType; + short type; + char bg_name[NAME_LENGTH]; +} __attribute__((packed)); + +struct packet_bgqueue_notice_delete { + short PacketType; + short type; + char bg_name[NAME_LENGTH]; +} __attribute__((packed)); + +struct packet_bgqueue_register { + short PacketType; + short type; + char bg_name[NAME_LENGTH]; +} __attribute__((packed)); + +struct packet_bgqueue_update_info { + short PacketType; + char bg_name[NAME_LENGTH]; + int position; +} __attribute__((packed)); + +struct packet_bgqueue_checkstate { + short PacketType; + char bg_name[NAME_LENGTH]; +} __attribute__((packed)); + +struct packet_bgqueue_revoke_req { + short PacketType; + char bg_name[NAME_LENGTH]; +} __attribute__((packed)); + +struct packet_bgqueue_battlebegin_ack { + short PacketType; + short result; + char bg_name[NAME_LENGTH]; + char game_name[NAME_LENGTH]; +} __attribute__((packed)); + +struct packet_bgqueue_notify_entry { + short PacketType; + char name[NAME_LENGTH]; + int position; +} __attribute__((packed)); + +struct packet_bgqueue_battlebegins { + short PacketType; + char bg_name[NAME_LENGTH]; + char game_name[NAME_LENGTH]; +} __attribute__((packed)); + #pragma pack(pop) #endif /* _PACKETS_STRUCT_H_ */ diff --git a/src/map/party.c b/src/map/party.c index 8a632a8ef..4462d4f07 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -50,8 +50,6 @@ static void party_fill_member(struct party_member* member, struct map_session_da member->online = 1; member->leader = leader; } - - /// Get the member_id of a party member. /// Return -1 if not in party. int party_getmemberid(struct party_data* p, struct map_session_data* sd) @@ -101,14 +99,21 @@ static TBL_PC* party_sd_check(int party_id, int account_id, int char_id) return sd; } - +int party_db_final(DBKey key, DBData *data, va_list ap) { + struct party_data *p; + + if( ( p = DB->data2ptr(data) ) && p->instance ) + aFree(p->instance); + + return 0; +} /*========================================== * Destructor * Called in map shutdown, cleanup var *------------------------------------------*/ void do_final_party(void) { - party_db->destroy(party_db,NULL); + party_db->destroy(party_db,party_db_final); party_booking_db->destroy(party_booking_db,NULL); // Party Booking [Spiria] } // Constructor, init vars @@ -251,16 +256,14 @@ int party_recv_info(struct party* sp, int char_id) int removed_count = 0; int added[MAX_PARTY];// member_id in new data int added_count = 0; - int i; + int i,j; int member_id; nullpo_ret(sp); p = (struct party_data*)idb_get(party_db, sp->party_id); - if( p != NULL )// diff members - { - for( member_id = 0; member_id < MAX_PARTY; ++member_id ) - { + if( p != NULL ) {// diff members + for( member_id = 0; member_id < MAX_PARTY; ++member_id ) { member = &p->party.member[member_id]; if( member->char_id == 0 ) continue;// empty @@ -270,8 +273,7 @@ int party_recv_info(struct party* sp, int char_id) if( i == MAX_PARTY ) removed[removed_count++] = member_id; } - for( member_id = 0; member_id < MAX_PARTY; ++member_id ) - { + for( member_id = 0; member_id < MAX_PARTY; ++member_id ) { member = &sp->member[member_id]; if( member->char_id == 0 ) continue;// empty @@ -281,17 +283,16 @@ int party_recv_info(struct party* sp, int char_id) if( i == MAX_PARTY ) added[added_count++] = member_id; } - } - else - { + } else { for( member_id = 0; member_id < MAX_PARTY; ++member_id ) if( sp->member[member_id].char_id != 0 ) added[added_count++] = member_id; CREATE(p, struct party_data, 1); + p->instance = NULL; + p->instances = 0; idb_put(party_db, sp->party_id, p); } - while( removed_count > 0 )// no longer in party - { + while( removed_count > 0 ) {// no longer in party member_id = removed[--removed_count]; sd = p->data[member_id].sd; if( sd == NULL ) @@ -301,16 +302,14 @@ int party_recv_info(struct party* sp, int char_id) memcpy(&p->party, sp, sizeof(struct party)); memset(&p->state, 0, sizeof(p->state)); memset(&p->data, 0, sizeof(p->data)); - for( member_id = 0; member_id < MAX_PARTY; member_id++ ) - { + for( member_id = 0; member_id < MAX_PARTY; member_id++ ) { member = &p->party.member[member_id]; if ( member->char_id == 0 ) continue;// empty p->data[member_id].sd = party_sd_check(sp->party_id, member->account_id, member->char_id); } party_check_state(p); - while( added_count > 0 )// new in party - { + while( added_count > 0 ) { // new in party member_id = added[--added_count]; sd = p->data[member_id].sd; if( sd == NULL ) @@ -319,8 +318,12 @@ int party_recv_info(struct party* sp, int char_id) clif->party_member_info(p,sd); clif->party_option(p,sd,0x100); clif->party_info(p,NULL); - if( p->instance_id != 0 ) - clif->instance_join(sd->fd, p->instance_id); + for( j = 0; j < p->instances; j++ ) { + if( instances[p->instance[j]].idle_timer == INVALID_TIMER && instances[p->instance[j]].progress_timer == INVALID_TIMER ) + continue; + clif->instance_join(sd->fd, p->instance[j]); + break; + } } if( char_id != 0 )// requester { @@ -429,19 +432,21 @@ void party_member_joined(struct map_session_data *sd) { struct party_data* p = party_search(sd->status.party_id); int i; - if (!p) - { + if (!p) { party_request_info(sd->status.party_id, sd->status.char_id); return; } ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd->status.account_id && p->party.member[i].char_id == sd->status.char_id ); - if (i < MAX_PARTY) - { + if (i < MAX_PARTY) { + int j; p->data[i].sd = sd; - if( p->instance_id ) - clif->instance_join(sd->fd,p->instance_id); - } - else + for( j = 0; j < p->instances; j++ ) { + if( instances[p->instance[j]].idle_timer == INVALID_TIMER && instances[p->instance[j]].progress_timer == INVALID_TIMER ) + continue; + clif->instance_join(sd->fd, p->instance[j]); + break; + } + } else sd->status.party_id = 0; //He does not belongs to the party really? } @@ -451,7 +456,7 @@ int party_member_added(int party_id,int account_id,int char_id, int flag) { struct map_session_data *sd = map_id2sd(account_id),*sd2; struct party_data *p = party_search(party_id); - int i; + int i, j; if(sd == NULL || sd->status.char_id != char_id || !sd->party_joining ) { if (!flag) //Char logged off before being accepted into party. @@ -496,9 +501,13 @@ int party_member_added(int party_id,int account_id,int char_id, int flag) clif->party_xy(sd); clif->charnameupdate(sd); //Update char name's display [Skotlex] - if( p->instance_id ) - clif->instance_join(sd->fd, p->instance_id); - + for( j = 0; j < p->instances; j++ ) { + if( instances[p->instance[j]].idle_timer == INVALID_TIMER && instances[p->instance[j]].progress_timer == INVALID_TIMER ) + continue; + clif->instance_join(sd->fd, p->instance[j]); + break; + } + return 0; } @@ -551,12 +560,10 @@ int party_member_withdraw(int party_id, int account_id, int char_id) struct map_session_data* sd = map_id2sd(account_id); struct party_data* p = party_search(party_id); - if( p ) - { + if( p ) { int i; ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id ); - if( i < MAX_PARTY ) - { + if( i < MAX_PARTY ) { clif->party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->data[i], 0, sizeof(p->data[0])); @@ -565,13 +572,12 @@ int party_member_withdraw(int party_id, int account_id, int char_id) } } - if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) - { + if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) { sd->status.party_id = 0; clif->charnameupdate(sd); //Update name display [Skotlex] //TODO: hp bars should be cleared too - if( p->instance_id ) - instance_check_kick(sd); + if( p->instances ) + instance->check_kick(sd); } return 0; @@ -581,22 +587,19 @@ int party_member_withdraw(int party_id, int account_id, int char_id) int party_broken(int party_id) { struct party_data* p; - int i; + int i, j; p = party_search(party_id); if( p == NULL ) return 0; - if( p->instance_id ) - { - instance[p->instance_id].party_id = 0; - instance_destroy( p->instance_id ); + for( j = 0; j < p->instances; j++ ) { + instance->destroy( p->instance[j] ); + instances[p->instance[j]].owner_id = 0; } - - for( i = 0; i < MAX_PARTY; i++ ) - { - if( p->data[i].sd!=NULL ) - { + + for( i = 0; i < MAX_PARTY; i++ ) { + if( p->data[i].sd!=NULL ) { clif->party_withdraw(p,p->data[i].sd,p->party.member[i].account_id,p->party.member[i].name,0x10); p->data[i].sd->status.party_id=0; } diff --git a/src/map/party.h b/src/map/party.h index 12fe7a2bc..3978324e4 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -25,7 +25,8 @@ struct party_data { struct party party; struct party_member_data data[MAX_PARTY]; uint8 itemc; //For item distribution, position of last picker in party - unsigned int instance_id; + unsigned short *instance; + unsigned short instances; struct { unsigned monk : 1; //There's at least one monk in party? unsigned sg : 1; //There's at least one Star Gladiator in party? diff --git a/src/map/path.c b/src/map/path.c index 3bbd8d20b..8ab63d390 100644 --- a/src/map/path.c +++ b/src/map/path.c @@ -167,12 +167,10 @@ int path_blownpos(int16 m,int16 x0,int16 y0,int16 dx,int16 dy,int count) dy=(dy>0)?1:((dy<0)?-1:0); } - while( count > 0 && (dx != 0 || dy != 0) ) - { - if( !map_getcellp(md,x0+dx,y0+dy,CELL_CHKPASS) ) - {// attempt partial movement - int fx = ( dx != 0 && map_getcellp(md,x0+dx,y0,CELL_CHKPASS) ); - int fy = ( dy != 0 && map_getcellp(md,x0,y0+dy,CELL_CHKPASS) ); + while( count > 0 && (dx != 0 || dy != 0) ) { + if( !md->getcellp(md,x0+dx,y0+dy,CELL_CHKPASS) ) {// attempt partial movement + int fx = ( dx != 0 && md->getcellp(md,x0+dx,y0,CELL_CHKPASS) ); + int fy = ( dy != 0 && md->getcellp(md,x0,y0+dy,CELL_CHKPASS) ); if( fx && fy ) { if(rnd()&1) @@ -225,7 +223,7 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16 spd->x[0] = x0; spd->y[0] = y0; - if (map_getcellp(md,x1,y1,cell)) + if (md->getcellp(md,x1,y1,cell)) return false; if (dx > abs(dy)) { @@ -238,7 +236,7 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16 while (x0 != x1 || y0 != y1) { - if (map_getcellp(md,x0,y0,cell)) + if (md->getcellp(md,x0,y0,cell)) return false; wx += dx; wy += dy; @@ -290,10 +288,10 @@ bool path_search(struct walkpath_data *wpd,int16 m,int16 x0,int16 y0,int16 x1,in //Do not check starting cell as that would get you stuck. if( x0 < 0 || x0 >= md->xs || y0 < 0 || y0 >= md->ys ) #else - if( x0 < 0 || x0 >= md->xs || y0 < 0 || y0 >= md->ys /*|| map_getcellp(md,x0,y0,cell)*/ ) + if( x0 < 0 || x0 >= md->xs || y0 < 0 || y0 >= md->ys /*|| md->getcellp(md,x0,y0,cell)*/ ) #endif return false; - if( x1 < 0 || x1 >= md->xs || y1 < 0 || y1 >= md->ys || map_getcellp(md,x1,y1,cell) ) + if( x1 < 0 || x1 >= md->xs || y1 < 0 || y1 >= md->ys || md->getcellp(md,x1,y1,cell) ) return false; // calculate (sgn(x1-x0), sgn(y1-y0)) @@ -317,7 +315,7 @@ bool path_search(struct walkpath_data *wpd,int16 m,int16 x0,int16 y0,int16 x1,in if( dx == 0 && dy == 0 ) break; // success - if( map_getcellp(md,x,y,cell) ) + if( md->getcellp(md,x,y,cell) ) break; // obstacle = failure } @@ -365,29 +363,29 @@ bool path_search(struct walkpath_data *wpd,int16 m,int16 x0,int16 y0,int16 x1,in // dc[2] : y-- // dc[3] : x++ - if(y < ys && !map_getcellp(md,x ,y+1,cell)) { + if(y < ys && !md->getcellp(md,x ,y+1,cell)) { f |= 1; dc[0] = (y >= y1 ? 20 : 0); e+=add_path(heap,tp,x ,y+1,dist,rp,cost+dc[0]); // (x, y+1) } - if(x > 0 && !map_getcellp(md,x-1,y ,cell)) { + if(x > 0 && !md->getcellp(md,x-1,y ,cell)) { f |= 2; dc[1] = (x <= x1 ? 20 : 0); e+=add_path(heap,tp,x-1,y ,dist,rp,cost+dc[1]); // (x-1, y ) } - if(y > 0 && !map_getcellp(md,x ,y-1,cell)) { + if(y > 0 && !md->getcellp(md,x ,y-1,cell)) { f |= 4; dc[2] = (y <= y1 ? 20 : 0); e+=add_path(heap,tp,x ,y-1,dist,rp,cost+dc[2]); // (x , y-1) } - if(x < xs && !map_getcellp(md,x+1,y ,cell)) { + if(x < xs && !md->getcellp(md,x+1,y ,cell)) { f |= 8; dc[3] = (x >= x1 ? 20 : 0); e+=add_path(heap,tp,x+1,y ,dist,rp,cost+dc[3]); // (x+1, y ) } - if( (f & (2+1)) == (2+1) && !map_getcellp(md,x-1,y+1,cell)) + if( (f & (2+1)) == (2+1) && !md->getcellp(md,x-1,y+1,cell)) e+=add_path(heap,tp,x-1,y+1,dist+4,rp,cost+dc[1]+dc[0]-6); // (x-1, y+1) - if( (f & (2+4)) == (2+4) && !map_getcellp(md,x-1,y-1,cell)) + if( (f & (2+4)) == (2+4) && !md->getcellp(md,x-1,y-1,cell)) e+=add_path(heap,tp,x-1,y-1,dist+4,rp,cost+dc[1]+dc[2]-6); // (x-1, y-1) - if( (f & (8+4)) == (8+4) && !map_getcellp(md,x+1,y-1,cell)) + if( (f & (8+4)) == (8+4) && !md->getcellp(md,x+1,y-1,cell)) e+=add_path(heap,tp,x+1,y-1,dist+4,rp,cost+dc[3]+dc[2]-6); // (x+1, y-1) - if( (f & (8+1)) == (8+1) && !map_getcellp(md,x+1,y+1,cell)) + if( (f & (8+1)) == (8+1) && !md->getcellp(md,x+1,y+1,cell)) e+=add_path(heap,tp,x+1,y+1,dist+4,rp,cost+dc[3]+dc[0]-6); // (x+1, y+1) tp[rp].flag=1; if(e || heap[0]>=MAX_HEAP-5) diff --git a/src/map/pc.c b/src/map/pc.c index 55ce993b4..241e0fbb3 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -518,8 +518,7 @@ int pc_makesavestatus(struct map_session_data *sd) #else sd->status.option = sd->sc.option&(OPTION_INVISIBLE|OPTION_CART|OPTION_FALCON|OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_WUGRIDER|OPTION_MADOGEAR); #endif - if (sd->sc.data[SC_JAILED]) - { //When Jailed, do not move last point. + if (sd->sc.data[SC_JAILED]) { //When Jailed, do not move last point. if(pc_isdead(sd)){ pc_setrestartvalue(sd,0); } else { @@ -543,13 +542,25 @@ int pc_makesavestatus(struct map_session_data *sd) sd->status.last_point.y = sd->bl.y; } - if(map[sd->bl.m].flag.nosave){ + if(map[sd->bl.m].flag.nosave || map[sd->bl.m].instance_id >= 0){ struct map_data *m=&map[sd->bl.m]; if(m->save.map) memcpy(&sd->status.last_point,&m->save,sizeof(sd->status.last_point)); else memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point)); } + if( sd->status.last_point.map == 0 ) { + sd->status.last_point.map = 1; + sd->status.last_point.x = 0; + sd->status.last_point.y = 0; + } + + if( sd->status.save_point.map == 0 ) { + sd->status.save_point.map = 1; + sd->status.save_point.x = 0; + sd->status.save_point.y = 0; + } + return 0; } @@ -905,8 +916,7 @@ int pc_isequip(struct map_session_data *sd,int n) * No problem with the session id * set the status that has been sent from char server *------------------------------------------*/ -bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers) -{ +bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers) { int i; unsigned long tick = gettick(); uint32 ip = session[sd->fd]->client_addr; @@ -1014,6 +1024,17 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->disguise = -1; + sd->instance = NULL; + sd->instances = 0; + + sd->bg_queue.arena = NULL; + sd->bg_queue.ready = 0; + sd->bg_queue.client_has_bg_data = 0; + sd->bg_queue.type = 0; + + sd->queues = NULL; + sd->queues_count = 0; + // Event Timers for( i = 0; i < MAX_EVENTTIMER; i++ ) sd->eventtimer[i] = INVALID_TIMER; @@ -1075,7 +1096,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim /** * Fixes login-without-aura glitch (the screen won't blink at this point, don't worry :P) **/ - clif->changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y); + clif->changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y); } /** @@ -4680,36 +4701,62 @@ int pc_steal_coin(struct map_session_data *sd,struct block_list *target) * 1 - Invalid map index. * 2 - Map not in this map-server, and failed to locate alternate map-server. *------------------------------------------*/ -int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, clr_type clrtype) -{ - struct party_data *p; +int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, clr_type clrtype) { int16 m; nullpo_ret(sd); - if( !mapindex || !mapindex_id2name(mapindex) ) - { + if( !mapindex || !mapindex_id2name(mapindex) ) { ShowDebug("pc_setpos: Passed mapindex(%d) is invalid!\n", mapindex); return 1; } - if( pc_isdead(sd) ) - { //Revive dead people before warping them + if( pc_isdead(sd) ) { //Revive dead people before warping them pc_setstand(sd); pc_setrestartvalue(sd,1); } - m = map_mapindex2mapid(mapindex); - if( map[m].flag.src4instance && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - { - // Request the mapid of this src map into the instance of the party - int im = instance_map2imap(m, p->instance_id); - if( im < 0 ) - ; // Player will enter the src map for instances - else - { // Changes destiny to the instance map, not the source map - m = im; - mapindex = map_id2index(m); + + if( map[m].flag.src4instance ) { + struct party_data *p; + bool stop = false; + int i = 0, j = 0; + + if( sd->instances ) { + for( i = 0; i < sd->instances; i++ ) { + ARR_FIND(0, instances[sd->instance[i]].num_map, j, map[instances[sd->instance[i]].map[j]].instance_src_map == m && !map[instances[sd->instance[i]].map[j]].cName); + if( j != instances[sd->instance[i]].num_map ) + break; + } + if( i != sd->instances ) { + m = instances[sd->instance[i]].map[j]; + mapindex = map[m].index; + stop = true; + } + } + if ( !stop && sd->status.party_id && (p = party_search(sd->status.party_id)) && p->instances ) { + for( i = 0; i < p->instances; i++ ) { + ARR_FIND(0, instances[p->instance[i]].num_map, j, map[instances[p->instance[i]].map[j]].instance_src_map == m && !map[instances[p->instance[i]].map[j]].cName); + if( j != instances[p->instance[i]].num_map ) + break; + } + if( i != p->instances ) { + m = instances[p->instance[i]].map[j]; + mapindex = map[m].index; + stop = true; + } + } + if ( !stop && sd->status.guild_id && sd->guild && sd->guild->instances ) { + for( i = 0; i < sd->guild->instances; i++ ) { + ARR_FIND(0, instances[sd->guild->instance[i]].num_map, j, map[instances[sd->guild->instance[i]].map[j]].instance_src_map == m && !map[instances[sd->guild->instance[i]].map[j]].cName); + if( j != instances[sd->guild->instance[i]].num_map ) + break; + } + if( i != sd->guild->instances ) { + m = instances[sd->guild->instance[i]].map[j]; + mapindex = map[m].index; + stop = true; + } } } @@ -4718,6 +4765,17 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y if( sd->state.changemap ) { // Misc map-changing settings int i; sd->state.pmap = sd->bl.m; + + for( i = 0; i < sd->queues_count; i++ ) { + struct hQueue *queue; + if( (queue = script->queue(sd->queues[i])) && queue->onMapChange[0] != '\0' ) { + pc_setregstr(sd, add_str("QMapChangeTo"), map[m].name); + npc_event(sd, queue->onMapChange, 0); + } + } + + if( map[m].cell == (struct mapcell *)0xdeadbeaf ) + map_cellfromcache(&map[m]); if (sd->sc.count) { // Cancel some map related stuff. if (sd->sc.data[SC_JAILED]) return 1; //You may not get out! @@ -4761,8 +4819,7 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y } - if( m < 0 ) - { + if( m < 0 ) { uint32 ip; uint16 port; //if can't find any map-servers, just abort setting position. @@ -4787,14 +4844,12 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y return 0; } - if( x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) - { + if( x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) { ShowError("pc_setpos: attempt to place player %s (%d:%d) on invalid coordinates (%s-%d,%d)\n", sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(mapindex),x,y); x = y = 0; // make it random } - if( x == 0 && y == 0 ) - {// pick a random walkable cell + if( x == 0 && y == 0 ) {// pick a random walkable cell do { x=rnd()%(map[m].xs-2)+1; y=rnd()%(map[m].ys-2)+1; @@ -4808,7 +4863,7 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y if(sd->bl.prev != NULL){ unit_remove_map_pc(sd,clrtype); - clif->changemap(sd,map[m].index,x,y); // [MouseJstr] + clif->changemap(sd,m,x,y); // [MouseJstr] } else if(sd->state.active) //Tag player for rewarping after map-loading is done. [Skotlex] sd->state.rewarp = 1; @@ -4818,31 +4873,27 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y sd->bl.x = sd->ud.to_x = x; sd->bl.y = sd->ud.to_y = y; - if( sd->status.guild_id > 0 && map[m].flag.gvg_castle ) - { // Increased guild castle regen [Valaris] + if( sd->status.guild_id > 0 && map[m].flag.gvg_castle ) { // Increased guild castle regen [Valaris] struct guild_castle *gc = guild->mapindex2gc(sd->mapindex); if(gc && gc->guild_id == sd->status.guild_id) sd->regen.state.gc = 1; } - if( sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0 ) - { + if( sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0 ) { sd->pd->bl.m = m; sd->pd->bl.x = sd->pd->ud.to_x = x; sd->pd->bl.y = sd->pd->ud.to_y = y; sd->pd->ud.dir = sd->ud.dir; } - if( homun_alive(sd->hd) ) - { + if( homun_alive(sd->hd) ) { sd->hd->bl.m = m; sd->hd->bl.x = sd->hd->ud.to_x = x; sd->hd->bl.y = sd->hd->ud.to_y = y; sd->hd->ud.dir = sd->ud.dir; } - if( sd->md ) - { + if( sd->md ) { sd->md->bl.m = m; sd->md->bl.x = sd->md->ud.to_x = x; sd->md->bl.y = sd->md->ud.to_y = y; @@ -6588,15 +6639,22 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) pc_setglobalreg(sd,"PC_DIE_COUNTER",sd->die_counter+1); pc_setparam(sd, SP_KILLERRID, src?src->id:0); - if( sd->bg_id ) { + if( sd->bg_id ) {/* TODO: purge when bgqueue is deemed ok */ struct battleground_data *bg; if( (bg = bg_team_search(sd->bg_id)) != NULL && bg->die_event[0] ) npc_event(sd, bg->die_event, 0); } - + + for( i = 0; i < sd->queues_count; i++ ) { + struct hQueue *queue; + if( (queue = script->queue(sd->queues[i])) && queue->onDeath[0] != '\0' ) + npc_event(sd, queue->onDeath, 0); + } + + npc_script_event(sd,NPCE_DIE); + // Clear anything NPC-related when you die and was interacting with one. - if (sd->npc_id) - { + if (sd->npc_id) { if (sd->state.using_fake_npc) { clif->clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); sd->state.using_fake_npc = 0; @@ -6611,8 +6669,6 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) sd->st->state = END; } - npc_script_event(sd,NPCE_DIE); - /* e.g. not killed thru pc_damage */ if( pc_issit(sd) ) { clif->sc_end(&sd->bl,sd->bl.id,SELF,SI_SIT); @@ -6788,7 +6844,7 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) if(map[sd->bl.m].flag.pvp_nightmaredrop) { // Moved this outside so it works when PVP isn't enabled and during pk mode [Ancyker] - for(j=0;j<MAX_DROP_PER_MAP;j++){ + for(j=0;j<map[sd->bl.m].drop_list_count;j++){ int id = map[sd->bl.m].drop_list[j].drop_id; int type = map[sd->bl.m].drop_list[j].drop_type; int per = map[sd->bl.m].drop_list[j].drop_per; diff --git a/src/map/pc.h b/src/map/pc.h index b1fa3e741..5207c4c34 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -10,6 +10,7 @@ #include "../common/timer.h" // INVALID_TIMER #include "atcommand.h" // AtCommandType #include "battle.h" // battle_config +#include "battleground.h" #include "buyingstore.h" // struct s_buyingstore #include "itemdb.h" // MAX_ITEMGROUP #include "map.h" // RC_MAX @@ -519,6 +520,20 @@ struct map_session_data { struct sc_display_entry **sc_display; unsigned char sc_display_count; + unsigned short *instance; + unsigned short instances; + + /* Possible Thanks to Yommy~! */ + struct { + unsigned int ready : 1;/* did he accept the 'match is about to start, enter' dialog? */ + unsigned int client_has_bg_data : 1; /* flags whether the client has the "in queue" window (aka the client knows it is in a queue) */ + struct bg_arena *arena; + enum bg_queue_types type; + } bg_queue; + + int *queues; + unsigned int queues_count; + // temporary debugging of bug #3504 const char* delunit_prevfile; int delunit_prevline; diff --git a/src/map/script.c b/src/map/script.c index 41f8e7472..d42d3d3e3 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -816,10 +816,12 @@ const char* parse_callfunc(const char* p, int require_paren, int is_custom) func = add_word(p); if( str_data[func].type == C_FUNC ){ + char argT = 0; // buildin function add_scriptl(func); add_scriptc(C_ARG); arg = script->buildin[str_data[func].val]; + if( !arg ) arg = &argT; } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){ // script defined function add_scriptl(buildin_callsub_ref); @@ -2358,8 +2360,8 @@ void get_val(struct script_state* st, struct script_data* data) } break; case '\'': - if (st->instance_id) { - data->u.str = (char*)idb_get(instance[st->instance_id].vars,reference_getuid(data)); + if ( st->instance_id >= 0 ) { + data->u.str = (char*)idb_get(instances[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; @@ -2423,8 +2425,8 @@ void get_val(struct script_state* st, struct script_data* data) } break; case '\'': - if( st->instance_id ) - data->u.num = (int)idb_iget(instance[st->instance_id].vars,reference_getuid(data)); + if( st->instance_id >= 0 ) + data->u.num = (int)idb_iget(instances[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; @@ -2484,9 +2486,9 @@ static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* nam } 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)); + if( st->instance_id >= 0 ) { + idb_remove(instances[st->instance_id].vars, num); + if( str[0] ) idb_put(instances[st->instance_id].vars, num, aStrdup(str)); } return 1; default: @@ -2532,10 +2534,10 @@ static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* nam } return 1; case '\'': - if( st->instance_id ) { - idb_remove(instance[st->instance_id].vars, num); + if( st->instance_id >= 0 ) { + idb_remove(instances[st->instance_id].vars, num); if( val != 0 ) - idb_iput(instance[st->instance_id].vars, num, val); + idb_iput(instances[st->instance_id].vars, num, val); } return 1; default: @@ -3462,7 +3464,7 @@ void run_script_main(struct script_state *st) script_attach_state(st); nd = map_id2nd(st->oid); - if( nd && map[nd->bl.m].instance_id > 0 ) + if( nd ) st->instance_id = map[nd->bl.m].instance_id; if(st->state == RERUNLINE) { @@ -3845,9 +3847,25 @@ void do_final_script(void) { script->buildin[i] = NULL; } } - - aFree(script->buildin); + aFree(script->buildin); + + if( script->hqs ) { + for( i = 0; i < script->hqs; i++ ) { + if( script->hq[i].item != NULL ) + aFree(script->hq[i].item); + } + } + if( script->hqis ) { + for( i = 0; i < script->hqis; i++ ) { + if( script->hqi[i].item != NULL ) + aFree(script->hqi[i].item); + } + } + if( script->hq != NULL ) + aFree(script->hq); + if( script->hqi != NULL ) + aFree(script->hqi); } /*========================================== * Initialization @@ -8457,13 +8475,10 @@ BUILDIN(monster) if (sd && strcmp(mapn, "this") == 0) m = sd->bl.m; - else - { + 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) - { + if (map[m].flag.src4instance && st->instance_id >= 0) { // 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 false; } @@ -8528,27 +8543,22 @@ BUILDIN(areamonster) struct map_session_data* sd; int16 m; - if (script_hasdata(st,10)) - { + if (script_hasdata(st,10)) { event = script_getstr(st, 10); check_event(st, event); } - if (script_hasdata(st, 11)) - { + if (script_hasdata(st, 11)) { size = script_getnum(st, 11); - if (size > 3) - { + if (size > 3) { ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_); return false; } } - if (script_hasdata(st, 12)) - { + if (script_hasdata(st, 12)) { ai = script_getnum(st, 12); - if (ai > 4) - { + if (ai > 4) { ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_); return false; } @@ -8558,13 +8568,10 @@ BUILDIN(areamonster) if (sd && strcmp(mapn, "this") == 0) m = sd->bl.m; - else - { + 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) - { + if (map[m].flag.src4instance && st->instance_id >= 0) { // 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 false; } @@ -8624,7 +8631,7 @@ BUILDIN(killmonster) if( (m=map_mapname2mapid(mapname))<0 ) return true; - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) + if( map[m].flag.src4instance && st->instance_id >= 0 && (m = instance->mapid2imapid(m, st->instance_id)) < 0 ) return true; if( script_hasdata(st,4) ) { @@ -8665,7 +8672,7 @@ BUILDIN(killmonsterall) if( (m = map_mapname2mapid(mapname))<0 ) return true; - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) + if( map[m].flag.src4instance && st->instance_id >= 0 && (m = instance->mapid2imapid(m, st->instance_id)) < 0 ) return true; if( script_hasdata(st,3) ) { @@ -11168,8 +11175,7 @@ BUILDIN(mobcount) // Added by RoVeRT return true; } - if( map[m].flag.src4instance && map[m].instance_id == 0 && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - { + if( map[m].flag.src4instance && map[m].instance_id >= 0 && st->instance_id >= 0 && (m = instance->mapid2imapid(m, st->instance_id)) < 0 ) { script_pushint(st,-1); return true; } @@ -12491,8 +12497,7 @@ BUILDIN(jump_zero) /*========================================== * movenpc [MouseJstr] *------------------------------------------*/ -BUILDIN(movenpc) -{ +BUILDIN(movenpc) { TBL_NPC *nd = NULL; const char *npc; int x,y; @@ -12505,7 +12510,7 @@ BUILDIN(movenpc) return -1; if (script_hasdata(st,5)) - nd->ud.dir = script_getnum(st,5) % 8; + nd->dir = script_getnum(st,5) % 8; npc_movenpc(nd, x, y); return true; } @@ -12559,17 +12564,20 @@ BUILDIN(npcspeed) speed = script_getnum(st,2); nd =(struct npc_data *)map_id2bl(st->oid); - if( nd ) - { + if( nd ) { + if( nd->ud == &npc_base_ud ) { + nd->ud = NULL; + CREATE(nd->ud, struct unit_data, 1); + unit_dataset(&nd->bl); + } nd->speed = speed; - nd->ud.state.speed_changed = 1; + nd->ud->state.speed_changed = 1; } return true; } // make an npc walk to a position [Valaris] -BUILDIN(npcwalkto) -{ +BUILDIN(npcwalkto) { struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); int x=0,y=0; @@ -12577,6 +12585,12 @@ BUILDIN(npcwalkto) y=script_getnum(st,3); if(nd) { + if( nd->ud == &npc_base_ud ) { + nd->ud = NULL; + CREATE(nd->ud, struct unit_data, 1); + unit_dataset(&nd->bl); + } + if (!nd->status.hp) { status_calc_npc(nd, true); } else { @@ -15341,7 +15355,7 @@ BUILDIN(setcell) for( y = y1; y <= y2; ++y ) for( x = x1; x <= x2; ++x ) - map_setcell(m, x, y, type, flag); + map[m].setcell(m, x, y, type, flag); return true; } @@ -15865,25 +15879,28 @@ BUILDIN(bg_get_data) * Instancing Script Commands *------------------------------------------*/ -BUILDIN(instance_create) -{ +BUILDIN(instance_create) { const char *name; - int party_id, res; + int owner_id, res; + int type = IOT_PARTY; name = script_getstr(st, 2); - party_id = script_getnum(st, 3); + owner_id = script_getnum(st, 3); + if( script_hasdata(st,4) ) { + type = script_getnum(st, 4); + if( type < IOT_NONE || type >= IOT_MAX ) { + ShowError("buildin_instance_create: unknown instance type %d for '%s'\n",type,name); + return true; + } + } - res = instance_create(party_id, name); - if( res == -4 ) // Already exists - { + res = instance->create(owner_id, name, (enum instance_owner_type) type); + if( res == -4 ) { // Already exists script_pushint(st, -1); return true; - } - else if( res < 0 ) - { + } else if( res < 0 ) { const char *err; - switch(res) - { + switch(res) { case -3: err = "No free instances"; break; case -2: err = "Invalid party ID"; break; case -1: err = "Invalid type"; break; @@ -15898,44 +15915,39 @@ BUILDIN(instance_create) return true; } -BUILDIN(instance_destroy) -{ - int instance_id; - struct map_session_data *sd; - struct party_data *p; +BUILDIN(instance_destroy) { + int instance_id = -1; if( script_hasdata(st, 2) ) instance_id = script_getnum(st, 2); - else if( st->instance_id ) + else if( st->instance_id >= 0 ) 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 true; - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - { + if( !instance->valid(instance_id) ) { ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id); return true; } - instance_destroy(instance_id); + instance->destroy(instance_id); return true; } -BUILDIN(instance_attachmap) -{ - const char *name; +BUILDIN(instance_attachmap) { + const char *name, *map_name = NULL; int16 m; - int instance_id; + int instance_id = -1; bool usebasename = false; name = script_getstr(st,2); instance_id = script_getnum(st,3); - if( script_hasdata(st,4) && script_getnum(st,4) > 0) + if( script_hasdata(st,4) && script_getnum(st,4) > 0 ) usebasename = true; - if( (m = instance_add_map(name, instance_id, usebasename)) < 0 ) // [Saithis] - { + if( script_hasdata(st, 5) ) + map_name = script_getstr(st, 5); + + if( (m = instance->add_map(name, instance_id, usebasename, map_name)) < 0 ) { // [Saithis] ShowError("buildin_instance_attachmap: instance creation failed (%s): %d\n", name, m); script_pushconststr(st, ""); return true; @@ -15945,109 +15957,81 @@ BUILDIN(instance_attachmap) return true; } -BUILDIN(instance_detachmap) -{ - struct map_session_data *sd; - struct party_data *p; +BUILDIN(instance_detachmap) { const char *str; int16 m; - int instance_id; + int instance_id = -1; str = script_getstr(st, 2); if( script_hasdata(st, 3) ) instance_id = script_getnum(st, 3); - else if( st->instance_id ) + else if( st->instance_id >= 0 ) 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 true; - if( (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m,instance_id)) < 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 true; } - instance_del_map(m); + instance->del_map(m); return true; } -BUILDIN(instance_attach) -{ - int instance_id; +BUILDIN(instance_attach) { + int instance_id = -1; instance_id = script_getnum(st, 2); - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) + if( !instance->valid(instance_id) ) return true; st->instance_id = instance_id; return true; } -BUILDIN(instance_id) -{ - int instance_id; - - if( script_hasdata(st, 2) ) - { - struct party_data *p; - struct map_session_data *sd; - int type; - 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); +BUILDIN(instance_id) { + script_pushint(st, st->instance_id); return true; } BUILDIN(instance_set_timeout) { int progress_timeout, idle_timeout; - int instance_id; - struct map_session_data *sd; - struct party_data *p; + int instance_id = -1; 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 ) + else if( st->instance_id >= 0 ) 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 true; - if( instance_id > 0 ) - instance_set_timeout(instance_id, progress_timeout, idle_timeout); + if( instance_id >= 0 ) + instance->set_timeout(instance_id, progress_timeout, idle_timeout); return true; } -BUILDIN(instance_init) -{ +BUILDIN(instance_init) { int instance_id = script_getnum(st, 2); - if( instance[instance_id].state != INSTANCE_IDLE ) - { + if( !instance->valid(instance_id) ) { + ShowError("instance_init: invalid instance id %d.\n",instance_id); + return true; + } + + if( instances[instance_id].state != INSTANCE_IDLE ) { ShowError("instance_init: instance already initialized.\n"); return true; } - instance_init(instance_id); + instance->start(instance_id); return true; } -BUILDIN(instance_announce) -{ +BUILDIN(instance_announce) { int instance_id = script_getnum(st,2); const char *mes = script_getstr(st,3); int flag = script_getnum(st,4); @@ -16058,53 +16042,40 @@ BUILDIN(instance_announce) 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 ) + if( instance_id == -1 ) { + if( st->instance_id >= 0 ) 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 true; + else + return true; } - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) + if( !instance->valid(instance_id) ) return true; - for( i = 0; i < instance[instance_id].num_map; i++ ) - map_foreachinmap(buildin_announce_sub, instance[instance_id].map[i], BL_PC, + for( i = 0; i < instances[instance_id].num_map; i++ ) + map_foreachinmap(buildin_announce_sub, instances[instance_id].map[i], BL_PC, mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY); return true; } -BUILDIN(instance_npcname) -{ +BUILDIN(instance_npcname) { const char *str; - int instance_id = 0; - - struct map_session_data *sd; - struct party_data *p; + int instance_id = -1; 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 ) + else if( st->instance_id >= 0 ) 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 ) - { + if( instance_id >= 0 && (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 - { + } else { ShowError("script:instance_npcname: invalid instance NPC (instance_id: %d, NPC name: \"%s\".)\n", instance_id, str); st->state = END; return false; @@ -16113,24 +16084,56 @@ BUILDIN(instance_npcname) return true; } -BUILDIN(has_instance) -{ +BUILDIN(has_instance) { struct map_session_data *sd; - struct party_data *p; const char *str; int16 m; - int instance_id = 0; + int instance_id = -1; str = script_getstr(st, 2); + + if( (m = map_mapname2mapid(str)) < 0 ) { + script_pushconststr(st, ""); + return true; + } + if( script_hasdata(st, 3) ) instance_id = script_getnum(st, 3); - else if( st->instance_id ) + else if( st->instance_id >= 0 ) 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 if( (sd = script_rid2sd(st)) != NULL ) { + struct party_data *p; + int i = 0, j = 0; + if( sd->instances ) { + for( i = 0; i < sd->instances; i++ ) { + ARR_FIND(0, instances[sd->instance[i]].num_map, j, map[instances[sd->instance[i]].map[j]].instance_src_map == m); + if( j != instances[sd->instance[i]].num_map ) + break; + } + if( i != sd->instances ) + instance_id = sd->instance[i]; + } + if( instance_id == -1 && sd->status.party_id && (p = party_search(sd->status.party_id)) && p->instances ) { + for( i = 0; i < p->instances; i++ ) { + ARR_FIND(0, instances[p->instance[i]].num_map, j, map[instances[p->instance[i]].map[j]].instance_src_map == m); + if( j != instances[p->instance[i]].num_map ) + break; + } + if( i != p->instances ) + instance_id = p->instance[i]; + } + if( instance_id == -1 && sd->guild && sd->guild->instances ) { + for( i = 0; i < sd->guild->instances; i++ ) { + ARR_FIND(0, instances[sd->guild->instance[i]].num_map, j, map[instances[sd->guild->instance[i]].map[j]].instance_src_map == m); + if( j != instances[sd->guild->instance[i]].num_map ) + break; + } + if( i != sd->guild->instances ) + instance_id = sd->guild->instance[i]; + } + } - if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m, instance_id)) < 0 ) - { + if( !instance->valid(instance_id) || (m = instance->map2imap(m, instance_id)) < 0 ) { script_pushconststr(st, ""); return true; } @@ -16138,38 +16141,41 @@ BUILDIN(has_instance) script_pushconststr(st, map[m].name); return true; } - -BUILDIN(instance_warpall) -{ - struct map_session_data *pl_sd; - int16 m, i; - int instance_id; +static int buildin_instance_warpall_sub(struct block_list *bl,va_list ap) { + struct map_session_data *sd = ((TBL_PC*)bl); + int mapindex = va_arg(ap,int); + int x = va_arg(ap,int); + int y = va_arg(ap,int); + + pc_setpos(sd,mapindex,x,y,CLR_TELEPORT); + + return 0; +} +BUILDIN(instance_warpall) { + int16 m; + int instance_id = -1; const char *mapn; int x, y; - unsigned short mapindex; - struct party_data *p = NULL; + int mapindex; 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 ) + else if( st->instance_id >= 0 ) 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 true; - - if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapid2imapid(m, instance_id)) < 0) ) + else return true; - if( !(p = party_search(instance[instance_id].party_id)) ) + if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance->mapid2imapid(m, instance_id)) < 0) ) return true; - + 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); + map_foreachininstance(buildin_instance_warpall_sub, instance_id, BL_PC,mapindex,x,y); + return true; } @@ -16183,8 +16189,7 @@ BUILDIN(instance_warpall) * Example: instance_check_party (getcharid(1){,amount}{,min}{,max}); * Example 2: instance_check_party (getcharid(1),1,1,99); *------------------------------------------*/ -BUILDIN(instance_check_party) -{ +BUILDIN(instance_check_party) { struct map_session_data *pl_sd; int amount, min, max, i, party_id, c = 0; struct party_data *p = NULL; @@ -16298,13 +16303,12 @@ BUILDIN(areamobuseskill) int16 m; int range,mobid,skill_id,skill_lv,casttime,emotion,target,cancel; - if( (m = map_mapname2mapid(script_getstr(st,2))) < 0 ) - { + if( (m = map_mapname2mapid(script_getstr(st,2))) < 0 ) { ShowError("areamobuseskill: invalid map name.\n"); return true; } - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) + if( map[m].flag.src4instance && st->instance_id >= 0 && (m = instance->mapid2imapid(m, st->instance_id)) < 0 ) return true; center.m = m; @@ -16994,6 +16998,309 @@ BUILDIN(npcskill) return true; } +struct hQueue *script_hqueue_get(int idx) { + if( idx < 0 || idx >= script->hqs || script->hq[idx].items == -1 ) + return NULL; + return &script->hq[idx]; +} +/* set .@id,queue(); */ +/* creates queue, returns created queue id */ +BUILDIN(queue) { + int idx = script->hqs; + int i; + + for(i = 0; i < script->hqs; i++) { + if( script->hq[i].items == -1 ) { + break; + } + } + + if( i == script->hqs ) { + RECREATE(script->hq, struct hQueue, ++script->hqs); + script->hq[ idx ].item = NULL; + } else + idx = i; + + script->hq[ idx ].id = idx; + script->hq[ idx ].items = 0; + script->hq[ idx ].onDeath[0] = '\0'; + script->hq[ idx ].onLogOut[0] = '\0'; + script->hq[ idx ].onMapChange[0] = '\0'; + + script_pushint(st,idx); + return true; +} +/* set .@length,queuesize(.@queue_id); */ +/* returns queue length */ +BUILDIN(queuesize) { + int idx = script_getnum(st, 2); + + if( idx < 0 || idx >= script->hqs || script->hq[idx].items == -1 ) { + ShowWarning("buildin_queuesize: unknown queue id %d\n",idx); + script_pushint(st, 0); + } else + script_pushint(st, script->hq[ idx ].items ); + + return true; +} +bool script_hqueue_add(int idx, int var) { + if( idx < 0 || idx >= script->hqs || script->hq[idx].items == -1 ) { + ShowWarning("script_hqueue_add: unknown queue id %d\n",idx); + return true; + } else { + struct map_session_data *sd; + int i; + + for(i = 0; i < script->hq[idx].items; i++) { + if( script->hq[idx].item[i] == var ) { + return true; + } + } + + if( i == script->hq[idx].items ) { + + for(i = 0; i < script->hq[idx].items; i++) { + if( script->hq[idx].item[i] == 0 ) { + break; + } + } + + if( i == script->hq[idx].items ) + RECREATE(script->hq[idx].item, int, ++script->hq[idx].items); + + script->hq[idx].item[i] = var; + + if( var >= START_ACCOUNT_NUM && (sd = map_id2sd(var)) ) { + for(i = 0; i < sd->queues_count; i++) { + if( sd->queues[i] == -1 ) { + break; + } + } + + if( i == sd->queues_count ) + RECREATE(sd->queues, int, ++sd->queues_count); + + sd->queues[i] = idx; + } + + } + } + return false; +} +/* queueadd(.@queue_id,.@var_id); */ +/* adds a new entry to the queue, returns 1 if already in queue, 0 otherwise */ +BUILDIN(queueadd) { + int idx = script_getnum(st, 2); + int var = script_getnum(st, 3); + + script_pushint(st,script->queue_add(idx,var)?1:0); + + return true; +} +bool script_hqueue_remove(int idx, int var) { + if( idx < 0 || idx >= script->hqs || script->hq[idx].items == -1 ) { + ShowWarning("script_hqueue_remove: unknown queue id %d (used with var %d)\n",idx,var); + return true; + } else { + int i; + + for(i = 0; i < script->hq[idx].items; i++) { + if( script->hq[idx].item[i] == var ) { + return true; + } + } + + if( i != script->hq[idx].items ) { + struct map_session_data *sd; + script->hq[idx].item[i] = 0; + + if( var >= START_ACCOUNT_NUM && (sd = map_id2sd(var)) ) { + for(i = 0; i < sd->queues_count; i++) { + if( sd->queues[i] == var ) { + break; + } + } + + if( i != sd->queues_count ) + sd->queues[i] = -1; + } + + } + } + return false; +} +/* queueremove(.@queue_id,.@var_id); */ +/* removes a entry from the queue, returns 1 if not in queue, 0 otherwise */ +BUILDIN(queueremove) { + int idx = script_getnum(st, 2); + int var = script_getnum(st, 3); + + script_pushint(st, script->queue_remove(idx,var)?1:0); + + return true; +} + +/* queueopt(.@queue_id,optionType,<optional val>); */ +/* modifies the queue's options, when val is not provided the option is removed */ +/* when OnMapChange event is triggered, it sets a temp char var @QMapChangeTo$ with the destination map name */ +/* returns 1 when fails, 0 on success */ +BUILDIN(queueopt) { + int idx = script_getnum(st, 2); + int var = script_getnum(st, 3); + + if( idx < 0 || idx >= script->hqs || script->hq[idx].items == -1 ) { + ShowWarning("buildin_queueopt: unknown queue id %d\n",idx); + script_pushint(st, 1); + } else if( var <= HQO_NONE || var >= HQO_MAX ) { + ShowWarning("buildin_queueopt: unknown optionType %d\n",var); + script_pushint(st, 1); + } else { + switch( (enum hQueueOpt)var ) { + case HQO_OnDeath: + if( script_hasdata(st, 4) ) + safestrncpy(script->hq[idx].onDeath, script_getstr(st, 4), EVENT_NAME_LENGTH); + else + script->hq[idx].onDeath[0] = '\0'; + break; + case HQO_onLogOut: + if( script_hasdata(st, 4) ) + safestrncpy(script->hq[idx].onLogOut, script_getstr(st, 4), EVENT_NAME_LENGTH); + else + script->hq[idx].onLogOut[0] = '\0'; + break; + case HQO_OnMapChange: + if( script_hasdata(st, 4) ) + safestrncpy(script->hq[idx].onMapChange, script_getstr(st, 4), EVENT_NAME_LENGTH); + else + script->hq[idx].onMapChange[0] = '\0'; + break; + default: + ShowWarning("buildin_queueopt: unsupported optionType %d\n",var); + script_pushint(st, 1); + break; + } + } + + return true; +} +bool script_hqueue_del(int idx) { + if( idx < 0 || idx >= script->hqs || script->hq[idx].items == -1 ) { + ShowWarning("script_queue_del: unknown queue id %d\n",idx); + return true; + } else { + struct map_session_data *sd; + int i; + + for(i = 0; i < script->hq[idx].items; i++) { + if( script->hq[idx].item[i] >= START_ACCOUNT_NUM && (sd = map_id2sd(script->hq[idx].item[i])) ) { + int j; + for(j = 0; j < sd->queues_count; j++) { + if( sd->queues[j] == script->hq[idx].item[i] ) { + break; + } + } + + if( j != sd->queues_count ) + sd->queues[j] = -1; + } + } + + script->hq[idx].items = -1; + } + return false; +} +/* queuedel(.@queue_id); */ +/* deletes queue of id .@queue_id, returns 1 if id not found, 0 otherwise */ +BUILDIN(queuedel) { + int idx = script_getnum(st, 2); + + script_pushint(st,script->queue_del(idx)?1:0); + + return true; +} + +/* set .@id, queueiterator(.@queue_id); */ +/* creates a new queue iterator, returns its id */ +BUILDIN(queueiterator) { + int qid = script_getnum(st, 2); + struct hQueue *queue = NULL; + int idx = script->hqis; + int i; + + if( qid < 0 || qid >= script->hqs || script->hq[idx].items == -1 || !(queue = script->queue(qid)) ) { + ShowWarning("queueiterator: invalid queue id %d\n",qid); + return true; + } + + for(i = 0; i < script->hqis; i++) { + if( script->hqi[i].items == -1 ) { + break; + } + } + + if( i == script->hqis ) + RECREATE(script->hqi, struct hQueueIterator, ++script->hqis); + else + idx = i; + + RECREATE(script->hqi[ idx ].item, int, queue->items); + + memcpy(&script->hqi[idx].item, &queue->item, sizeof(int)*queue->items); + + script->hqi[ idx ].items = queue->items; + script->hqi[ idx ].pos = 0; + + script_pushint(st,idx); + return true; +} +/* Queue Iterator Get Next */ +/* returns next/first member in the iterator, 0 if none */ +BUILDIN(qiget) { + int idx = script_getnum(st, 2); + + if( idx < 0 || idx >= script->hqis ) { + ShowWarning("buildin_qiget: unknown queue iterator id %d\n",idx); + script_pushint(st, 0); + } else if ( script->hqi[idx].pos == script->hqi[idx].items ) { + script_pushint(st, 0); + } else { + struct hQueueIterator *it = &script->hqi[idx]; + script_pushint(st, it->item[it->pos++]); + } + + return true; +} +/* Queue Iterator Check */ +/* returns 1:0 if there is a next member in the iterator */ +BUILDIN(qicheck) { + int idx = script_getnum(st, 2); + + if( idx < 0 || idx >= script->hqis ) { + ShowWarning("buildin_qicheck: unknown queue iterator id %d\n",idx); + script_pushint(st, 0); + } else if ( script->hqi[idx].pos == script->hqi[idx].items ) { + script_pushint(st, 0); + } else { + script_pushint(st, 1); + } + + return true; +} +/* Queue Iterator Check */ +BUILDIN(qiclear) { + int idx = script_getnum(st, 2); + + if( idx < 0 || idx >= script->hqis ) { + ShowWarning("buildin_qiclear: unknown queue iterator id %d\n",idx); + script_pushint(st, 1); + } else { + script->hqi[idx].items = -1; + script_pushint(st, 0); + } + + return true; +} + // declarations that were supposed to be exported from npc_chat.c #ifdef PCRE_SUPPORT BUILDIN(defpattern); @@ -17442,12 +17749,12 @@ void script_parse_builtin(void) { BUILDIN_DEF(bg_updatescore,"sii"), // Instancing - BUILDIN_DEF(instance_create,"si"), + BUILDIN_DEF(instance_create,"si?"), BUILDIN_DEF(instance_destroy,"?"), - BUILDIN_DEF(instance_attachmap,"si?"), + BUILDIN_DEF(instance_attachmap,"si??"), BUILDIN_DEF(instance_detachmap,"s?"), BUILDIN_DEF(instance_attach,"i"), - BUILDIN_DEF(instance_id,"?"), + BUILDIN_DEF(instance_id,""), BUILDIN_DEF(instance_set_timeout,"ii?"), BUILDIN_DEF(instance_init,"i"), BUILDIN_DEF(instance_announce,"isi?????"), @@ -17492,6 +17799,20 @@ void script_parse_builtin(void) { BUILDIN_DEF(checkquest, "i?"), BUILDIN_DEF(changequest, "ii"), BUILDIN_DEF(showevent, "ii"), + + /** + * hQueue [Ind/Hercules] + **/ + BUILDIN_DEF(queue,""), + BUILDIN_DEF(queuesize,"i"), + BUILDIN_DEF(queueadd,"ii"), + BUILDIN_DEF(queueremove,"ii"), + BUILDIN_DEF(queueopt,"ii?"), + BUILDIN_DEF(queuedel,"i"), + BUILDIN_DEF(queueiterator,"i"), + BUILDIN_DEF(qicheck,"i"), + BUILDIN_DEF(qiget,"i"), + BUILDIN_DEF(qiclear,"i"), }; int i,n, len = ARRAYLENGTH(BUILDIN), start = script->buildin_count; char* p; @@ -17545,6 +17866,11 @@ void script_parse_builtin(void) { void script_defaults(void) { script = &script_s; + script->hq = NULL; + script->hqi = NULL; + script->hqs = script->hqis = 0; + memset(&script->hqe, 0, sizeof(script->hqe)); + script->buildin_count = 0; script->buildin = NULL; @@ -17555,4 +17881,9 @@ void script_defaults(void) { script->addScript = script_hp_add; script->conv_num = conv_num; script->conv_str = conv_str; + + script->queue = script_hqueue_get; + script->queue_add = script_hqueue_add; + script->queue_del = script_hqueue_del; + script->queue_remove = script_hqueue_remove; } diff --git a/src/map/script.h b/src/map/script.h index a0d282bfe..70ced5d43 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -4,6 +4,8 @@ #ifndef _SCRIPT_H_ #define _SCRIPT_H_ +#include "map.h" //EVENT_NAME_LENGTH + #define NUM_WHISPER_VAR 10 struct map_session_data; @@ -110,6 +112,30 @@ struct script_stack { struct DBMap* var_function;// scope variables }; +enum hQueueOpt { + HQO_NONE, + HQO_onLogOut, + HQO_OnDeath, + HQO_OnMapChange, + HQO_MAX, +}; + +/* [Ind/Hercules] */ +struct hQueue { + int id; + int *item; + int items; + /* events */ + char onLogOut[EVENT_NAME_LENGTH]; + char onDeath[EVENT_NAME_LENGTH]; + char onMapChange[EVENT_NAME_LENGTH]; +}; + +struct hQueueIterator { + int *item; + int items; + int pos; +}; // // Script state @@ -129,6 +155,7 @@ struct script_state { int instance_id; //For backing up purposes struct script_state *bk_st; + unsigned char hIterator; int bk_npcid; unsigned freeloop : 1;// used by buildin_freeloop unsigned op2ref : 1;// used by op_2 @@ -291,8 +318,14 @@ struct script_function { char *name; char *arg; }; + /* script.c interface (incomplete) */ struct script_interface { + /* */ + struct hQueue *hq; + struct hQueueIterator *hqi; + int hqs, hqis; + int hqe[HQO_MAX]; /* */ char **buildin; unsigned int buildin_count; @@ -304,6 +337,11 @@ struct script_interface { bool (*addScript) (char *name, char *args, bool (*func)(struct script_state *st)); int (*conv_num) (struct script_state *st,struct script_data *data); const char* (*conv_str) (struct script_state *st,struct script_data *data); + /* */ + struct hQueue *(*queue) (int idx); + bool (*queue_add) (int idx, int var); + bool (*queue_del) (int idx); + bool (*queue_remove) (int idx, int var); } script_s; struct script_interface *script; diff --git a/src/map/skill.c b/src/map/skill.c index 06bfca5f8..911410727 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -14464,7 +14464,7 @@ void skill_unitsetmapcell (struct skill_unit *src, uint16 skill_id, uint16 skill 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); + map[src->bl.m].setcell(src->bl.m, x, y, cell, flag); } /*========================================== diff --git a/src/map/status.c b/src/map/status.c index b7e906910..30adf0d0f 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -6079,14 +6079,15 @@ void status_set_viewdata(struct block_list *bl, int class_) /// 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; + 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 NULL; + 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; } diff --git a/src/map/unit.c b/src/map/unit.c index cbc695c4a..3ab1008cb 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -50,7 +50,7 @@ struct unit_data* unit_bl2ud(struct block_list *bl) if( bl->type == BL_PC) return &((struct map_session_data*)bl)->ud; if( bl->type == BL_MOB) return &((struct mob_data*)bl)->ud; if( bl->type == BL_PET) return &((struct pet_data*)bl)->ud; - if( bl->type == BL_NPC) return &((struct npc_data*)bl)->ud; + if( bl->type == BL_NPC) return ((struct npc_data*)bl)->ud; if( bl->type == BL_HOM) return &((struct homun_data*)bl)->ud; if( bl->type == BL_MER) return &((struct mercenary_data*)bl)->ud; if( bl->type == BL_ELEM) return &((struct elemental_data*)bl)->ud; @@ -678,10 +678,12 @@ int unit_setdir(struct block_list *bl,unsigned char dir) return 0; } -uint8 unit_getdir(struct block_list *bl) -{ +uint8 unit_getdir(struct block_list *bl) { struct unit_data *ud; - nullpo_ret(bl ); + nullpo_ret(bl); + + if( bl->type == BL_NPC ) + return ((TBL_NPC*)bl)->dir; ud = unit_bl2ud(bl); if (!ud) return 0; return ud->dir; @@ -1961,8 +1963,7 @@ int unit_skillcastcancel(struct block_list *bl,int type) } // unit_data initialization process -void unit_dataset(struct block_list *bl) -{ +void unit_dataset(struct block_list *bl) { struct unit_data *ud; nullpo_retv(ud = unit_bl2ud(bl)); @@ -2121,8 +2122,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, npc_touchnext_areanpc(sd,true); // Check if warping and not changing the map. - if ( sd->state.warping && !sd->state.changemap ) - { + if ( sd->state.warping && !sd->state.changemap ) { status_change_end(bl, SC_CLOAKING, INVALID_TIMER); status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); } @@ -2163,18 +2163,15 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, sd->state.active, sd->state.connect_new, sd->state.rewarp, sd->state.changemap, sd->state.debug_remove_map, map[bl->m].name, map[bl->m].users, sd->debug_file, sd->debug_line, sd->debug_func, file, line, func); - } - else - if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex] + } else if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex] map_removemobs(bl->m); if( !(sd->sc.option&OPTION_INVISIBLE) ) {// decrement the number of active pvp players on the map --map[bl->m].users_pvp; } - if( map[bl->m].instance_id ) - { - instance[map[bl->m].instance_id].users--; - instance_check_idle(map[bl->m].instance_id); + if( map[bl->m].instance_id >= 0 ) { + instances[map[bl->m].instance_id].users--; + instance->check_idle(map[bl->m].instance_id); } sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS] sd->debug_file = file; @@ -2356,9 +2353,19 @@ int unit_free(struct block_list *bl, clr_type clrtype) ers_free(pc_sc_display_ers, sd->sc_display[i]); } sd->sc_display_count = 0; + } + if( sd->sc_display != NULL ) { aFree(sd->sc_display); sd->sc_display = NULL; } + if( sd->instance != NULL ) { + aFree(sd->instance); + sd->instance = NULL; + } + if( sd->queues != NULL ) { + aFree(sd->queues); + sd->queues = NULL; + } break; } case BL_PET: |