Index: src/map/clif.c =================================================================== --- src/map/clif.c (revision 17368) +++ src/map/clif.c (working copy) @@ -9185,10 +9185,10 @@ 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 ) { +/* if( map[sd->bl.m].instance_id ) { instance[map[sd->bl.m].instance_id].users++; instance_check_idle(map[sd->bl.m].instance_id); - } + }*/ sd->state.debug_remove_map = 0; // temporary state to track double remove_map's [FlavioJS] // reset the callshop flag if the player changes map @@ -15264,102 +15264,95 @@ /*========================================== - * Instancing Window + * Notifies party members of instance change *------------------------------------------*/ -int clif_instance(int instance_id, int type, int flag) +void clif_instance_create(struct map_session_data *sd, const char *name, int num, int flag) { - struct map_session_data *sd; - struct party_data *p; - unsigned char buf[255]; +#if PACKETVER >= 20071128 + unsigned char buf[65]; - if( (p = party_search(instance[instance_id].party_id)) == NULL || (sd = party_getavailablesd(p)) == NULL ) - return 0; + nullpo_retv(sd); - switch( type ) - { - case 1: - // S 0x2cb .61B .W - // Required to start the instancing information window on Client - // This window re-appear each "refresh" of client automatically until type 4 is send to client. - WBUFW(buf,0) = 0x02CB; - memcpy(WBUFP(buf,2),instance[instance_id].name,INSTANCE_NAME_LENGTH); - WBUFW(buf,63) = flag; - clif_send(buf,packet_len(0x02CB),&sd->bl,PARTY); - break; - case 2: - // S 0x2cc .W - // To announce Instancing queue creation if no maps available - WBUFW(buf,0) = 0x02CC; - WBUFW(buf,2) = flag; - clif_send(buf,packet_len(0x02CC),&sd->bl,PARTY); - break; - case 3: - case 4: - // S 0x2cd .61B .L .L - WBUFW(buf,0) = 0x02CD; - memcpy(WBUFP(buf,2),instance[instance_id].name,61); - if( type == 3 ) - { - WBUFL(buf,63) = (uint32)instance[instance_id].progress_timeout; - WBUFL(buf,67) = 0; - } - else - { - WBUFL(buf,63) = 0; - WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout; - } - clif_send(buf,packet_len(0x02CD),&sd->bl,PARTY); - break; - case 5: - // S 0x2ce .L - // 0 = Notification (EnterLimitDate update?) - // 1 = The Memorial Dungeon expired; it has been destroyed - // 2 = The Memorial Dungeon's entry time limit expired; it has been destroyed - // 3 = The Memorial Dungeon has been removed. - // 4 = Create failure (removes the instance window) - WBUFW(buf,0) = 0x02CE; - WBUFL(buf,2) = flag; - //WBUFL(buf,6) = EnterLimitDate; - clif_send(buf,packet_len(0x02CE),&sd->bl,PARTY); - break; + WBUFW(buf,0) = 0x2cb; + memcpy(WBUFP(buf,2),name,61); + WBUFB(buf,62) = '\0'; // \0 terminal + WBUFW(buf,63) = num; + if(flag) { // A timer has changed or been added + clif_send(buf,packet_len(0x2cb),&sd->bl,PARTY); + } else { // No notification + memcpy(WFIFOP(sd->fd,0),buf,packet_len(0x2cb)); + WFIFOSET(sd->fd,packet_len(0x2cb)); } - return 0; +#endif + + return; } -void clif_instance_join(int fd, int instance_id) +void clif_instance_changewait(struct map_session_data *sd, int num, int flag) { - if( instance[instance_id].idle_timer != INVALID_TIMER ) { - WFIFOHEAD(fd,packet_len(0x02CD)); - WFIFOW(fd,0) = 0x02CD; - memcpy(WFIFOP(fd,2),instance[instance_id].name,61); - WFIFOL(fd,63) = 0; - WFIFOL(fd,67) = (uint32)instance[instance_id].idle_timeout; - WFIFOSET(fd,packet_len(0x02CD)); - } else if( instance[instance_id].progress_timer != INVALID_TIMER ) { - WFIFOHEAD(fd,packet_len(0x02CD)); - WFIFOW(fd,0) = 0x02CD; - memcpy(WFIFOP(fd,2),instance[instance_id].name,61); - WFIFOL(fd,63) = (uint32)instance[instance_id].progress_timeout;; - WFIFOL(fd,67) = 0; - WFIFOSET(fd,packet_len(0x02CD)); - } else { - WFIFOHEAD(fd,packet_len(0x02CB)); - WFIFOW(fd,0) = 0x02CB; - memcpy(WFIFOP(fd,2),instance[instance_id].name,61); - WFIFOW(fd,63) = 0; - WFIFOSET(fd,packet_len(0x02CB)); +#if PACKETVER >= 20071128 + unsigned char buf[4]; + + nullpo_retv(sd); + + WBUFW(buf,0) = 0x2cc; + WBUFW(buf,2) = num; + if(flag) { // A timer has changed or been added + clif_send(buf,packet_len(0x2cc),&sd->bl,PARTY); + } else { // No notification + memcpy(WFIFOP(sd->fd,0),buf,packet_len(0x2cc)); + WFIFOSET(sd->fd,packet_len(0x2cc)); } +#endif + + return; } -void clif_instance_leave(int fd) +void clif_instance_status(struct map_session_data *sd, const char *name, unsigned int limit1, unsigned int limit2, int flag) { - WFIFOHEAD(fd,packet_len(0x02CE)); - WFIFOW(fd,0) = 0x02ce; - WFIFOL(fd,2) = 4; - WFIFOSET(fd,packet_len(0x02CE)); +#if PACKETVER >= 20071128 + unsigned char buf[71]; + + nullpo_retv(sd); + + WBUFW(buf,0) = 0x2cd; + memcpy(WBUFP(buf,2),name,61); + WBUFB(buf,62) = '\0'; // \0 terminal + WBUFL(buf,63) = limit1; + WBUFL(buf,67) = limit2; + if(flag) { // A timer has changed or been added + clif_send(buf,packet_len(0x2cd),&sd->bl,PARTY); + } else { // No notification + memcpy(WFIFOP(sd->fd,0),buf,packet_len(0x2cd)); + WFIFOSET(sd->fd,packet_len(0x2cd)); + } +#endif + + return; } +void clif_instance_changestatus(struct map_session_data *sd, int type, unsigned int limit, int flag) +{ +#if PACKETVER >= 20071128 + unsigned char buf[10]; + nullpo_retv(sd); + + WBUFW(buf,0) = 0x2ce; + WBUFL(buf,2) = type; + WBUFL(buf,6) = limit; + if(flag) { // A timer has changed or been added + clif_send(buf,packet_len(0x2ce),&sd->bl,PARTY); + } else { // No notification + memcpy(WFIFOP(sd->fd,0),buf,packet_len(0x2ce)); + WFIFOSET(sd->fd,packet_len(0x2ce)); + } +#endif + + return; +} + + /// Notifies clients about item picked up by a party member (ZC_ITEM_PICKUP_PARTY). /// 02b8 .L .W .B .B .B .W .W .W .W .W .B void clif_party_show_picker(struct map_session_data * sd, struct item * item_data) Index: src/map/clif.h =================================================================== --- src/map/clif.h (revision 17368) +++ src/map/clif.h (working copy) @@ -570,9 +570,10 @@ void clif_sendbgemblem_single(int fd, struct map_session_data *sd); // Instancing -int clif_instance(int instance_id, int type, int flag); -void clif_instance_join(int fd, int instance_id); -void clif_instance_leave(int fd); +void clif_instance_create(struct map_session_data *sd, const char *name, int num, int flag); +void clif_instance_changewait(struct map_session_data *sd, int num, int flag); +void clif_instance_status(struct map_session_data *sd, const char *name, unsigned int limit1, unsigned int limit2, int flag); +void clif_instance_changestatus(struct map_session_data *sd, int type, unsigned int limit, int flag); // Custom Fonts void clif_font(struct map_session_data *sd); Index: src/map/instance.c =================================================================== --- src/map/instance.c (revision 17368) +++ src/map/instance.c (working copy) @@ -24,27 +24,244 @@ #include #include +#define MAX_INSTANCE_DB 15 // Max number of instance types +#define INSTANCE_INTERVAL 60000 // Interval used to check when an instance is to be destroyed (ms) +#define INSTANCE_LIMIT 30000 // Idle timer before instance is destroyed (ms) + int instance_start = 0; // To keep the last index + 1 of normal map inserted in the map[ARRAY] -struct s_instance instance[MAX_INSTANCE]; +struct instance_data instance_data[MAX_INSTANCE_DATA]; -/// Checks whether given instance id is valid or not. -static bool instance_is_valid(int instance_id) +static struct instance_db{ + short type; + char name[61]; + int limit; + struct { + char mapname[MAP_NAME_LENGTH_EXT]; + short x, y; + } enter; + char mapname[MAX_MAP_PER_INSTANCE][MAP_NAME_LENGTH_EXT]; +} instance_db[MAX_INSTANCE_DB]; + +static struct { + int id[MAX_INSTANCE_DATA]; + int count; + int timer; +} instance_wait; + +/*========================================== + * Searches for an instance ID in the database + *------------------------------------------*/ +static struct instance_db *instance_searchtype_db(short instance_type) { - if( instance_id < 1 || instance_id >= ARRAYLENGTH(instance) ) - {// out of range - return false; + int i; + + for(i=0; i < MAX_INSTANCE_DB; i++) { + if(instance_db[i].type == instance_type) + return &instance_db[i]; } - if( instance[instance_id].state == INSTANCE_FREE ) - {// uninitialized/freed instance slot - return false; + return NULL; +} + +/*========================================== + * Searches for an instance name in the database + *------------------------------------------*/ +static struct instance_db *instance_searchname_db(const char *instance_name) +{ + int i; + + for(i=0; i < MAX_INSTANCE_DB; i++) { + if(strcmp(instance_db[i].name, instance_name) == 0) + return &instance_db[i]; } - return true; + return NULL; } +/*========================================== + * Deletes an instance timer (Destroys instance) + *------------------------------------------*/ +static int instance_delete_timer(int tid, unsigned int tick, int id, intptr_t data) +{ + instance_destroy(id); + return 0; +} + +/*========================================== + * Create subscription timer for party + *------------------------------------------*/ +static int instance_subscription_timer(int tid, unsigned int tick, int id, intptr_t data) +{ + int i, j, ret; + int instance_id = instance_wait.id[0]; + struct party_data *p; + + if(instance_wait.count == 0) + return 0; + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return 0; + + ShowDebug("Instance ID to add maps to:%d.\n", instance_id); + + // Check that maps have been added + ret = instance_addmap(instance_id); + + ShowDebug("Number of maps returned:%d.\n", ret); + + // If no maps are created, tell party to wait + if(ret == 0) { + if((p = party_search(instance_data[instance_id].party_id)) != NULL) { + for(i = 0; i < MAX_PARTY; i++) { + if(p->data[i].sd) { + clif_instance_changewait(p->data[i].sd, 0xffff, 1); + break; + } + } + } + } + + instance_wait.count--; + memmove(&instance_wait.id[0],&instance_wait.id[1],sizeof(instance_wait.id[0])*instance_wait.count); + memset(&instance_wait.id[instance_wait.count], 0, sizeof(instance_wait.id[0])); + + for(i = 0; i < instance_wait.count; i++) { + if(instance_data[instance_wait.id[i]].state == INSTANCE_IDLE) { + if((p = party_search(instance_data[instance_wait.id[i]].party_id)) != NULL) { + for(j = 0; j < MAX_PARTY; j++) { + if(p->data[j].sd) { + clif_instance_changewait(p->data[j].sd, i+1, 1); + break; + } + } + } + } + } + + if(instance_wait.count) + instance_wait.timer = add_timer(gettick()+INSTANCE_INTERVAL, instance_subscription_timer, 0, 0); + else + instance_wait.timer = -1; + + return 0; +} + +/*========================================== + * Adds timer back to party entering instance + *------------------------------------------*/ +static int instance_startkeeptimer(struct instance_data *im, int instance_id) +{ + struct instance_db *db; + struct party_data *p; + int i; + + nullpo_retr(0, im); + + // No timer + if(im->keep_timer != -1) + return 1; + + if((db = instance_searchtype_db(im->type)) == NULL) + return 1; + + // Add timer + im->keep_limit = (unsigned int)time(NULL) + db->limit; + im->keep_timer = add_timer(gettick()+db->limit*1000, instance_delete_timer, instance_id, 0); + + // Notify party of the added instance timer + if((p = party_search(im->party_id)) != NULL) { + for(i = 0; i < MAX_PARTY; i++) { + if(p->data[i].sd) { + ShowDebug("Sending startkeeptimer to player.\n"); + clif_instance_status(p->data[i].sd, db->name, im->keep_limit, im->idle_limit, 1); + break; + } + } + } + + return 0; +} + +/*========================================== + * Creates idle timer + * Default before instance destroy is 5 minutes + *------------------------------------------*/ +static int instance_startidletimer(struct instance_data *im, int instance_id) +{ + struct instance_db *db; + struct party_data *p; + int i; + + nullpo_retr(1, im); + + // No current timer + if(im->idle_timer != -1) + return 1; + + // Add the timer + im->idle_limit = (unsigned int)time(NULL) + INSTANCE_LIMIT; + im->idle_timer = add_timer(gettick()+INSTANCE_LIMIT, instance_delete_timer, instance_id, 0); + + // Notify party of added instance timer + if((p = party_search(im->party_id)) && (db = instance_searchtype_db(im->type))) { + for(i = 0; i < MAX_PARTY; i++) { + if(p->data[i].sd) { + clif_instance_status(p->data[i].sd, db->name, im->keep_limit, im->idle_limit, 1); + break; + } + } + } + + return 0; +} + +/*========================================== + * Delete the idle timer + *------------------------------------------*/ +static int instance_stopidletimer(struct instance_data *im) +{ + struct party_data *p; + int i; + + nullpo_retr(0, im); + + // No timer + if(im->idle_timer == -1) + return 1; + + // Delete the timer - Party has returned or instance is destroyed + im->idle_limit = 0; + delete_timer(im->idle_timer, instance_delete_timer); + im->idle_timer = -1; + + // Notify the party + if((p = party_search(im->party_id)) != NULL) { + for(i = 0; i < MAX_PARTY; i++) { + if(p->data[i].sd) { + clif_instance_changestatus(p->data[i].sd, 0, im->idle_limit, 1); + break; + } + } + } + + return 0; +} + +/*========================================== + * Add an NPC to an instance + *------------------------------------------*/ +static int instance_addnpc(struct block_list *bl, va_list ap) +{ + struct npc_data* nd; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, nd = (struct npc_data *)bl); + + return npc_duplicate4instance(nd, va_arg(ap, int)); +} + /*-------------------------------------- * name : instance name * Return value could be @@ -53,11 +270,16 @@ *--------------------------------------*/ int instance_create(int party_id, const char *name) { - int i; - struct party_data* p; + int i, k; + struct instance_db *db = instance_searchname_db(name); + struct party_data *p = party_search(party_id); - if( ( p = party_search(party_id) ) == NULL ) - { + if(db == NULL) { + ShowError("instance_create: map %s not found in instance database.\n", name); + return -1; + } + + if( p == NULL ) { ShowError("instance_create: party %d not found for instance '%s'.\n", party_id, name); return -2; } @@ -67,422 +289,416 @@ // 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"); + ARR_FIND(1, MAX_INSTANCE_DB, i, instance_data[i].state == INSTANCE_FREE); + if( i >= MAX_INSTANCE_DB ) { + ShowDebug("This shouldn't happen.\n"); return -3; } + // Checks that there aren't more than MAX_INSTANCE_DATA instances of that map + if(instance_wait.count >= MAX_INSTANCE_DATA) { + ShowError("instance_create: no free instances, consider increasing MAX_INSTANCE_DATA.\n"); + return -3; + } - 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); + instance_data[i].type = db->type; + instance_data[i].state = INSTANCE_IDLE; + instance_data[i].party_id = p->party.party_id; + instance_data[i].keep_limit = 0; + instance_data[i].keep_timer = -1; + instance_data[i].idle_limit = 0; + instance_data[i].idle_timer = -1; + instance_data[i].users = 0; + memset(instance_data[i].map, 0, sizeof(instance_data[i].map)); - safestrncpy( instance[i].name, name, sizeof(instance[i].name) ); - memset( instance[i].map, 0x00, sizeof(instance[i].map) ); + //Always start at instance ID of 1 p->instance_id = i; - clif_instance(i, 1, 0); // Start instancing window + ShowDebug("Created instance id %d.\n",p->instance_id); + + instance_wait.id[instance_wait.count++] = p->instance_id; + if(instance_wait.timer == -1) + instance_wait.timer = add_timer(gettick()+3000, instance_subscription_timer, 0, 0); + + for(k = 0; k < MAX_PARTY; k++) { + if(p->data[k].sd) { + ShowDebug("Sending created instance timer to player.\n"); + clif_instance_create(p->data[k].sd, name, instance_wait.count, 1); + break; + } + } + ShowInfo("[Instance] Created: %s.\n", name); + return i; } /*-------------------------------------- - * Add a map to the instance using src map "name" + * Adds maps to the instance *--------------------------------------*/ -int instance_add_map(const char *name, int instance_id, bool usebasename) +int instance_addmap(int instance_id) { - int16 m = map_mapname2mapid(name); - int i, im = -1; - size_t num_cell, size; + int i, m; + int cnt_map = 0, cnt_npc = 0; + struct instance_data *im; + struct instance_db *db; + struct party_data *p; - if( m < 0 ) - return -1; // source map not found + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return 0; - if( !instance_is_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); - return -2; - } - 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; - } + im = &instance_data[instance_id]; - 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; + // If the instance isn't idle, we can't do anything + if(im->state != INSTANCE_IDLE) { + ShowDebug("Instance isn't idle.\n"); + return 0; } - else im = map_num++; // Using next map index - 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 - map[im].index = mapindex_addmap(-1, map[im].name); // Add map index - - 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 + if((db = instance_searchtype_db(im->type)) == NULL) { + ShowDebug("Instance isn't in db.\n"); + return 0; } - // Reallocate cells - num_cell = map[im].xs * map[im].ys; - CREATE( map[im].cell, struct mapcell, num_cell ); - memcpy( map[im].cell, map[m].cell, num_cell * sizeof(struct mapcell) ); + ShowDebug("Are we pulling currect DB? type=%d name=%s first_map=%s\n", db->type, db->name, db->mapname[0]); - size = map[im].bxs * map[im].bys * sizeof(struct block_list*); - map[im].block = (struct block_list**)aCalloc(size, 1); - map[im].block_mob = (struct block_list**)aCalloc(size, 1); + // Set to busy, update timers + im->state = INSTANCE_BUSY; + im->idle_limit = (unsigned int)time(NULL) + INSTANCE_LIMIT; + im->idle_timer = add_timer(gettick()+INSTANCE_LIMIT, instance_delete_timer, instance_id, 0); - memset(map[im].npc, 0x00, sizeof(map[i].npc)); - map[im].npc_num = 0; + // Add the maps + for(i = 0; i < MAX_MAP_PER_INSTANCE; i++) { + if(strlen(db->mapname[i]) < 1 || (m = map_addinstancemap(db->mapname[i], instance_id)) < 0) + continue; + im->map[cnt_map].m = m; + im->map[cnt_map].src_m = map_mapname2mapid(db->mapname[i]); + cnt_map++; + } - memset(map[im].moblist, 0x00, sizeof(map[im].moblist)); - map[im].mob_delete_timer = INVALID_TIMER; + im->cnt_map = cnt_map; - map[im].m = im; - map[im].instance_id = instance_id; - map[im].instance_src_map = m; - map[m].flag.src4instance = 1; // Flag this map as a src map for instances + // Create NPCs on all maps + for(i = 0; i < cnt_map; i++) { + ShowDebug("Creating NPCs for map #%d\n",i); + cnt_npc += map_foreachinarea(instance_addnpc, im->map[i].src_m, 0, 0, map[im->map[i].src_m].xs, map[im->map[i].src_m].ys, BL_NPC, im->map[i].m); + } - instance[instance_id].map[instance[instance_id].num_map++] = im; // Attach to actual instance - map_addmap2db(&map[im]); + // Inform party members of the created instance + if((p = party_search(im->party_id)) != NULL) { + for(i = 0; i < MAX_PARTY; i++) { + ShowDebug("Sending instance timer to player.\n"); + clif_instance_status(p->data[i].sd, db->name, im->keep_limit, im->idle_limit, 1); + break; + } + } - return im; + return cnt_map; } /*-------------------------------------- - * m : source map of this instance - * party_id : source party of this instance - * type : result (0 = map id | 1 = instance id) + * name : source map + * instance_id : where to search + * result : mapid of map "name" in this instance *--------------------------------------*/ -int instance_map2imap(int16 m, int instance_id) +int instance_mapname2mapid(const char *name, int instance_id) { - int i; + struct instance_data *im; + int m = map_mapname2mapid(name); + char iname[12]; + int i; - if( !instance_is_valid(instance_id) ) - { - return -1; - } + ShowDebug("We attempt to enter instance map %s. Index=%d.\n",name,mapindex_name2id(name)); - 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]; - } - return -1; -} + if(instance_id <= 0 || instance_id > MAX_MAP_PER_SERVER) + return m; -/*-------------------------------------- - * m : source map - * instance_id : where to search - * result : mapid of map "m" in this instance - *--------------------------------------*/ -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 - ShowError("map_instance_mapid2imapid: already instanced (%d / %d)\n", m, instance_id); - return -1; + im = &instance_data[instance_id]; + if(im->state != INSTANCE_BUSY) + return m; + + for(i = 0; i < MAX_MAP_PER_INSTANCE; i++) { + if(im->map[i].src_m == m) { + snprintf(iname, sizeof(iname), "%03d%s", instance_id, name); + ShowDebug("Now we pull a map name %s and ID %d.\n",iname,mapindex_name2id(iname)); + return mapindex_name2id(iname); + } } - if( !instance_is_valid(instance_id) ) - return -1; - - return instance_map2imap(m, instance_id); + return m; } -/*-------------------------------------- - * 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) +/*========================================== + * Removes a instance, all its maps and npcs. + *------------------------------------------*/ +int instance_destroy(int instance_id) { - struct npc_data* nd = (struct npc_data*)bl; - int16 m = va_arg(args, int); // Destination Map + struct instance_data *im; + struct party_data *p; + int i, j, type = 0, count = 0; + unsigned int now = (unsigned int)time(NULL); - npc_duplicate4instance(nd, m); - return 1; -} + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return 1; -/*-------------------------------------- - * Init all map on the instance. Npcs are created here - *--------------------------------------*/ -void instance_init(int instance_id) -{ - int i; + im = &instance_data[instance_id]; - if( !instance_is_valid(instance_id) ) - return; // nothing to do + if(im->state == INSTANCE_FREE) { + return 1; + } - 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]); + if(im->state == INSTANCE_IDLE) { + for(i = 0; i < instance_wait.count; i++) { + if(instance_wait.id[i] == instance_id) { + instance_wait.count--; + memmove(&instance_wait.id[i],&instance_wait.id[i+1],sizeof(instance_wait.id[0])*(instance_wait.count-i)); + memset(&instance_wait.id[instance_wait.count], 0, sizeof(instance_wait.id[0])); - instance[instance_id].state = INSTANCE_BUSY; - ShowInfo("[Instance] Initialized %s.\n", instance[instance_id].name); -} + for(i = 0; i < instance_wait.count; i++) { + if(instance_data[instance_wait.id[i]].state == INSTANCE_IDLE) { + if((p = party_search(instance_data[instance_wait.id[i]].party_id)) != NULL) { + for(j = 0; j < MAX_PARTY; j++) { + if(p->data[j].sd) { + clif_instance_changewait(p->data[j].sd, i+1, 1); + break; + } + } + } + } + } -/*-------------------------------------- - * 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) -{ - int16 m = va_arg(args,int); - if( !sd || sd->bl.m != m ) - return 0; + if(instance_wait.count) + instance_wait.timer = add_timer(gettick()+INSTANCE_INTERVAL, instance_subscription_timer, 0, 0); + else + instance_wait.timer = -1; + type = 0; + break; + } + } + } + else { + if(im->keep_limit && im->keep_limit <= now) + type = 1; + else if(im->idle_limit && im->idle_limit <= now) + type = 2; + else + type = 3; - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_OUTSIGHT); - return 1; -} + for(i = 0; i < MAX_MAP_PER_INSTANCE; i++) + count += map_delinstancemap(im->map[i].m); + } -/* for npcs behave differently when being unloaded within a instance */ -int instance_cleanup_sub(struct block_list *bl, va_list ap) { - nullpo_ret(bl); + if(im->keep_timer != -1) { + delete_timer(im->keep_timer, instance_delete_timer); + im->keep_timer = -1; + } + if(im->idle_timer != -1) { + delete_timer(im->idle_timer, instance_delete_timer); + im->idle_timer = -1; + } - switch(bl->type) { - case BL_PC: - map_quit((struct map_session_data *) bl); - break; - case BL_NPC: - npc_unload((struct npc_data *)bl,true); - break; - case BL_MOB: - unit_free(bl,CLR_OUTSIGHT); - break; - case BL_PET: - //There is no need for this, the pet is removed together with the player. [Skotlex] - break; - case BL_ITEM: - map_clearflooritem(bl); - break; - case BL_SKILL: - skill_delunit((struct skill_unit *) bl); - break; + if((p = party_search(im->party_id))) { + p->instance_id = 0; + for(j = 0; j < MAX_PARTY; j++) { + if(p->data[j].sd) { + if(type) + clif_instance_changestatus(p->data[j].sd, type, 0, 1); + else + clif_instance_changewait(p->data[j].sd, 0xffff, 1); + break; + } + } } - return 1; + im->type = 0; + im->state = INSTANCE_FREE; + im->party_id = 0; + im->keep_limit = 0; + im->idle_limit = 0; + im->users = 0; + ShowInfo("[Instance] Destroyed %d.\n", instance_data[i].map); + + memset(instance_data[i].map, 0, sizeof(instance_data[i].map)); + + return 0; } -/*-------------------------------------- - * Removes a simple instance map - *--------------------------------------*/ -void instance_del_map(int16 m) +/*========================================== + * Allows a user to enter an instance + *------------------------------------------*/ +int instance_enter(struct map_session_data *sd, const char *name) { - int i; - if( m <= 0 || !map[m].instance_id ) - { - ShowError("Tried to remove non-existing instance map (%d)\n", m); - return; - } + struct instance_data *im; + struct instance_db *db = instance_searchname_db(name); + struct party_data *p; + int m; - map_foreachpc(instance_del_load, m); - map_foreachinmap(instance_cleanup_sub, m, BL_ALL); + nullpo_retr(-1, sd); - if( map[m].mob_delete_timer != INVALID_TIMER ) - delete_timer(map[m].mob_delete_timer, map_removemobs_timer); + if(db == NULL) + return -1; - mapindex_removemap( map[m].index ); + // Character must be in instance party + if(sd->status.party_id == 0) + return -2; + if((p = party_search(sd->status.party_id)) == NULL) + return -2; - // Free memory - aFree(map[m].cell); - aFree(map[m].block); - aFree(map[m].block_mob); + // Party must have an instance + if(p->instance_id == 0) + return -3; - // 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]; - 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); + im = &instance_data[p->instance_id]; + if(im->party_id != p->party.party_id) + return -3; + if(im->state != INSTANCE_BUSY) + return -3; + if(im->type != db->type) + return -3; - map_removemapdb(&map[m]); - memset(&map[m], 0x00, sizeof(map[0])); + // Does the instance match? + if((m = instance_mapname2mapid(db->enter.mapname, p->instance_id)) < 0) + return -4; - /* for it is default and makes it not try to delete a non-existent timer since we did not delete this entry. */ - map[m].mob_delete_timer = INVALID_TIMER; -} + if(pc_setpos(sd, m, db->enter.x, db->enter.y, 0)) + return -4; -/*-------------------------------------- - * 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); + // If there was an idle timer, let's stop it + instance_stopidletimer(im); + + // Now we start the instance timer + instance_startkeeptimer(im, p->instance_id); + return 0; } -/*-------------------------------------- - * Removes a instance, all its maps and npcs. - *--------------------------------------*/ -void instance_destroy(int instance_id) +/*========================================== + * Request some info about the instance + *------------------------------------------*/ +int instance_reqinfo(struct map_session_data *sd, int instance_id) { - int last = 0, type; - struct party_data *p; - time_t now = time(NULL); + struct instance_data *im; + struct instance_db *db; + int i; - if( !instance_is_valid(instance_id) ) - return; // nothing to do + nullpo_retr(1, sd); - if( instance[instance_id].progress_timeout && instance[instance_id].progress_timeout <= now ) - type = 1; - else if( instance[instance_id].idle_timeout && instance[instance_id].idle_timeout <= now ) - type = 2; - else - type = 3; + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return 1; - clif_instance(instance_id, 5, type); // Report users this instance has been destroyed + im = &instance_data[instance_id]; - 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] ); + if((db = instance_searchtype_db(im->type)) == NULL) + return 1; + + // Say it's created if instance is not busy + if(im->state == INSTANCE_IDLE) { + for(i = 0; i < instance_wait.count; i++) { + if(instance_wait.id[i] == instance_id) { + clif_instance_create(sd, db->name, i+1, 0); + break; + } + } } + // Give info on the instance if busy + else if(im->state == INSTANCE_BUSY) + clif_instance_status(sd, db->name, im->keep_limit, im->idle_limit, 0); - if( instance[instance_id].vars ) - db_destroy(instance[instance_id].vars); + return 0; +} - 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); +/*========================================== + * Add players to the instance (for timers) + *------------------------------------------*/ +int instance_addusers(int instance_id) +{ + struct instance_data *im; - instance[instance_id].vars = NULL; + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return 1; - if( instance[instance_id].party_id && (p = party_search(instance[instance_id].party_id)) != NULL ) - p->instance_id = 0; // Update Party information + im = &instance_data[instance_id]; + if(im->state != INSTANCE_BUSY) + return 1; - ShowInfo("[Instance] Destroyed %s.\n", instance[instance_id].name); - memset( &instance[instance_id], 0x00, sizeof(instance[0]) ); + // Increment the amount of players in instance + im->users++; - instance[instance_id].state = INSTANCE_FREE; + // Stop the idle timer if we had one + instance_stopidletimer(im); + + // Start the instance keep timer + instance_startkeeptimer(im, instance_id); + + return 0; } -/*-------------------------------------- - * Checks if there are users in the instance or not to start idle timer - *--------------------------------------*/ -void instance_check_idle(int instance_id) +/*========================================== + * Delete players from the instance (for timers) + *------------------------------------------*/ +int instance_delusers(int instance_id) { - bool idle = true; - time_t now = time(NULL); + struct instance_data *im; - if( !instance_is_valid(instance_id) || instance[instance_id].idle_timeoutval == 0 ) - return; + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return 1; - if( instance[instance_id].users ) - idle = false; + im = &instance_data[instance_id]; + if(im->state != INSTANCE_BUSY) + return 1; - 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; - 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); - clif_instance(instance_id, 4, 0); // Notify instance users it will be destroyed of no user join it again in "X" time - } + // Increment the amount of players in instance + im->users--; + + // If no one is in the instance, start the idle timer + if(im->users <= 0) + instance_startidletimer(im, instance_id); + + return 0; } -/*-------------------------------------- - * Set instance Timers - *--------------------------------------*/ -void instance_set_timeout(int instance_id, unsigned int progress_timeout, unsigned int idle_timeout) +static bool instance_readdb(char* str[], int columns, int current) { - time_t now = time(0); + + int i, type, k=0; - if( !instance_is_valid(instance_id) ) - return; + type=atoi(str[0]); - 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); + instance_db[type].type=type; + safestrncpy(instance_db[type].name, str[1], 24); + instance_db[type].limit=atoi(str[2]); + safestrncpy(instance_db[type].enter.mapname, str[3], MAP_NAME_LENGTH); + instance_db[type].enter.x=atoi(str[4]); + instance_db[type].enter.y=atoi(str[5]); - 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); + //Instance maps + for(i=6; ibl.m; + memset(&instance_db, 0, sizeof(instance_db)); - clif_instance_leave(sd->fd); - if( map[m].instance_id ) - { // User was on the instance map - if( map[m].save.map ) - pc_setpos(sd, map[m].save.map, map[m].save.x, map[m].save.y, CLR_TELEPORT); - else - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); - } + sv_readdb(db_path, DBPATH"instance_db.txt", ',', 7, 14, MAX_INSTANCE_DB, &instance_readdb); + memset(instance_data, 0, sizeof(instance_data)); + memset(&instance_wait, 0, sizeof(instance_wait)); + instance_wait.timer = -1; + + add_timer_func_list(instance_delete_timer,"instance_delete_timer"); + add_timer_func_list(instance_subscription_timer,"instance_subscription_timer"); } void do_final_instance(void) { int i; - for( i = 1; i < MAX_INSTANCE; i++ ) + for( i = 1; i < MAX_INSTANCE_DATA; i++ ) instance_destroy(i); } - -void do_init_instance(void) -{ - memset(instance, 0x00, sizeof(instance)); - add_timer_func_list(instance_destroy_timer, "instance_destroy_timer"); -} Index: src/map/instance.h =================================================================== --- src/map/instance.h (revision 17368) +++ src/map/instance.h (working copy) @@ -4,48 +4,43 @@ #ifndef _INSTANCE_H_ #define _INSTANCE_H_ -#define MAX_MAP_PER_INSTANCE 10 -#define MAX_INSTANCE 500 +#define MAX_INSTANCE_DATA 50 +#define MAX_MAP_PER_INSTANCE 8 // Max number of maps per instance #define INSTANCE_NAME_LENGTH (60+1) typedef enum instance_state { INSTANCE_FREE, INSTANCE_IDLE, INSTANCE_BUSY } instance_state; -struct s_instance { - char name[INSTANCE_NAME_LENGTH]; // Instance Name - required for clif functions. - instance_state state; - short instance_id; +struct instance_data { + short type, cnt_map; + int state; int party_id; - - int map[MAX_MAP_PER_INSTANCE]; - int num_map; + unsigned int keep_limit; + int keep_timer; + unsigned int idle_limit; + int idle_timer; int users; struct DBMap* vars; // Instance Variable for scripts - - int progress_timer; - time_t progress_timeout; - - int idle_timer; - time_t idle_timeout, idle_timeoutval; + struct { + int m; + int src_m; + } map[MAX_MAP_PER_INSTANCE]; }; extern int instance_start; -extern struct s_instance instance[MAX_INSTANCE]; +extern struct instance_data instance_data[MAX_INSTANCE_DATA]; 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); +int instance_destroy(int instance_id); +int instance_enter(struct map_session_data *sd, const char *name); +int instance_reqinfo(struct map_session_data *sd, int instance_id); +int instance_addusers(int instance_id); +int instance_delusers(int instance_id); +int instance_mapname2mapid(const char *name, int instance_id); +int instance_addmap(int instance_id); -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); - +void do_init_instance(void); void do_final_instance(void); -void do_init_instance(void); #endif Index: src/map/map.c =================================================================== --- src/map/map.c (revision 17368) +++ src/map/map.c (working copy) @@ -2170,6 +2170,137 @@ return true; } +/*========================================== + * Add an instance map + *------------------------------------------*/ +int map_addinstancemap(const char *name, int id) +{ + int src_m = map_mapname2mapid(name); + int dst_m = -1, i; + size_t size; + + if(src_m < 0) + return -1; + + if(strlen(name) > 20) { + // against buffer overflow + ShowError("map_addisntancemap: can't add long map name \"%s\"\n", name); + return -1; + } + + for(i = instance_start; i < MAX_MAP_PER_SERVER; i++) { + if(!map[i].name[0]) + break; + } + if(i < map_num) // Destination map value overwrites another + dst_m = i; + else if(i < MAX_MAP_PER_SERVER) // Destination map value increments to new map + dst_m = map_num++; + else { + // Out of bounds + ShowError("map_addinstancemap failed. map_num(%d) > map_max(%d)\n",map_num, MAX_MAP_PER_SERVER); + return -1; + } + + // Copy the map + memcpy(&map[dst_m], &map[src_m], sizeof(struct map_data)); + + // Alter the name + snprintf(map[dst_m].name, sizeof(map[dst_m].name), "%03d%s", id, name); + map[dst_m].name[MAP_NAME_LENGTH-1] = '\0'; + + map[dst_m].m = dst_m; + map[dst_m].instance_id = id; + map[dst_m].users = 0; + + memset(map[dst_m].npc, 0, sizeof(map[dst_m].npc)); + map[dst_m].npc_num = 0; + + size = map[dst_m].bxs * map[dst_m].bys * sizeof(struct block_list*); + map[dst_m].block = (struct block_list **)aCalloc(1,size); + map[dst_m].block_mob = (struct block_list **)aCalloc(1,size); + + map[dst_m].index = mapindex_addmap(-1, map[dst_m].name); + + map_addmap2db(&map[dst_m]); + + ShowDebug("Created map %s for instancing with id %d.\n",map[dst_m].name, map[dst_m].m); + + return dst_m; +} + +/*========================================== + * Set player to save point when they leave + *------------------------------------------*/ +static int map_instancemap_leave(struct block_list *bl, va_list ap) +{ + struct map_session_data* sd; + + nullpo_retr(0, bl); + nullpo_retr(0, sd = (struct map_session_data *)bl); + + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 3); + + return 1; +} + +/*========================================== + * Remove all units from instance + *------------------------------------------*/ +static int map_instancemap_clean(struct block_list *bl, va_list ap) +{ + nullpo_retr(0, bl); + switch(bl->type) { + case BL_PC: + map_quit((struct map_session_data *) bl); + break; + case BL_NPC: + npc_unload((struct npc_data *)bl,true); + break; + case BL_MOB: + unit_free(bl,CLR_OUTSIGHT); + break; + case BL_PET: + //There is no need for this, the pet is removed together with the player. [Skotlex] + break; + case BL_ITEM: + map_clearflooritem(bl); + break; + case BL_SKILL: + skill_delunit((struct skill_unit *) bl); + break; + } + + return 1; +} + +/*========================================== + * Deleting an instance map + *------------------------------------------*/ +int map_delinstancemap(int m) +{ + if(m < 0) + return 0; + if(map[m].instance_id == 0) + return 0; + + // Kick everyone out + map_foreachinarea(map_instancemap_leave, m, 0, 0, map[m].xs, map[m].ys, BL_PC); + + // Do the unit cleanup + map_foreachinarea(map_instancemap_clean, m, 0, 0, map[m].xs, map[m].ys, BL_ALL); + + if(map[m].block) + aFree(map[m].block); + if(map[m].block_mob) + aFree(map[m].block_mob); + + map_removemapdb(&map[m]); + memset(&map[m], 0x00, sizeof(map[0])); + + return 1; +} + /*========================================= * Dynamic Mobs [Wizputer] *-----------------------------------------*/ @@ -3085,7 +3216,7 @@ // 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 = map_num + 1; // Next Map Index will be instances if (maps_removed) ShowNotice("Maps removed: '"CL_WHITE"%d"CL_RESET"'\n",maps_removed); Index: src/map/map.h =================================================================== --- src/map/map.h (revision 17368) +++ src/map/map.h (working copy) @@ -37,7 +37,7 @@ #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_MOBSKILL 50 //Max 128, see mob skill_idx type if need this higher #define MAX_MOB_LIST_PER_MAP 128 #define MAX_EVENTQUEUE 2 #define MAX_EVENTTIMER 32 @@ -46,9 +46,9 @@ #define MAX_FLOORITEM START_ACCOUNT_NUM #define MAX_LEVEL 160 #define MAX_DROP_PER_MAP 48 -#define MAX_IGNORE_LIST 20 // official is 14 +#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] +#define MAX_MAP_SIZE 512*512 // Wasn't there something like this already? Can't find it.. [Shinryo] // Added definitions for WoESE objects. [L0ne_W0lf] enum MOBID { @@ -684,6 +684,10 @@ void map_clearflooritem(struct block_list* bl); int map_addflooritem(struct item *item_data,int amount,int16 m,int16 x,int16 y,int first_charid,int second_charid,int third_charid,int flags); +// instances +int map_addinstancemap(const char*,int); +int map_delinstancemap(int); + // player to map session void map_addnickdb(int charid, const char* nick); void map_delnickdb(int charid, const char* nick); Index: src/map/npc.c =================================================================== --- src/map/npc.c (revision 17368) +++ src/map/npc.c (working copy) @@ -2712,7 +2712,7 @@ int dm = map_mapindex2mapid(snd->u.warp.mapindex), im; if( dm < 0 ) return 1; - im = instance_mapid2imapid(dm, map[m].instance_id); + im = instance_mapname2mapid(map[m].name, map[m].instance_id); if( im == -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); @@ -2748,6 +2748,8 @@ } else { + char evname[EVENT_NAME_LENGTH]; + struct event_data *ev; static char w1[50], w2[50], w3[50], w4[50]; const char* stat_buf = "- call from instancing subsystem -\n"; @@ -2761,6 +2763,11 @@ snprintf(w4, sizeof(w4), "%d", snd->class_); npc_parse_duplicate(w1, w2, w3, w4, stat_buf, stat_buf, "INSTANCING"); + + snprintf(evname, ARRAYLENGTH(evname), "%s::OnInstanceInit", snd->exname); + + if( ( ev = (struct event_data*)strdb_get(ev_db, evname) ) ) + run_script(snd->u.scr.script,ev->pos,0,snd->bl.id); } return 0; @@ -3774,9 +3781,6 @@ do_final_instance(); - for( i = 0; i < ARRAYLENGTH(instance); ++i ) - instance_init(instance[i].instance_id); - //Re-read the NPC Script Events cache. npc_read_event_script(); Index: src/map/party.c =================================================================== --- src/map/party.c (revision 17368) +++ src/map/party.c (working copy) @@ -320,7 +320,7 @@ clif_party_option(p,sd,0x100); clif_party_info(p,NULL); if( p->instance_id != 0 ) - clif_instance_join(sd->fd, p->instance_id); + instance_reqinfo(sd,p->instance_id); } if( char_id != 0 )// requester { @@ -439,7 +439,7 @@ { p->data[i].sd = sd; if( p->instance_id ) - clif_instance_join(sd->fd,p->instance_id); + instance_reqinfo(sd,p->instance_id); } else sd->status.party_id = 0; //He does not belongs to the party really? @@ -498,7 +498,7 @@ clif_charnameupdate(sd); //Update char name's display [Skotlex] if( p->instance_id ) - clif_instance_join(sd->fd, p->instance_id); + instance_reqinfo(sd,p->instance_id); return 0; } @@ -575,8 +575,16 @@ 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->instance_id ) { + int16 m = sd->bl.m; + + if( map[m].instance_id ) { // User was on the instance map + if( map[m].save.map ) + pc_setpos(sd, map[m].save.map, map[m].save.x, map[m].save.y, CLR_TELEPORT); + else + pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); + } + } } return 0; @@ -594,7 +602,7 @@ if( p->instance_id ) { - instance[p->instance_id].party_id = 0; + instance_data[p->instance_id].party_id = 0; instance_destroy( p->instance_id ); } Index: src/map/pc.c =================================================================== --- src/map/pc.c (revision 17368) +++ src/map/pc.c (working copy) @@ -4777,11 +4777,10 @@ 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); + int im = instance_mapname2mapid(map[m].name, 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 + else { // Changes destination to the instance map, not the source map m = im; mapindex = map_id2index(m); } Index: src/map/script.c =================================================================== --- src/map/script.c (revision 17368) +++ src/map/script.c (working copy) @@ -2566,7 +2566,7 @@ break; case '\'': if (st->instance_id) { - data->u.str = (char*)idb_get(instance[st->instance_id].vars,reference_getuid(data)); + data->u.str = (char*)idb_get(instance_data[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; @@ -2631,7 +2631,7 @@ break; case '\'': if( st->instance_id ) - data->u.num = (int)idb_iget(instance[st->instance_id].vars,reference_getuid(data)); + data->u.num = (int)idb_iget(instance_data[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; @@ -2692,8 +2692,8 @@ 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)); + idb_remove(instance_data[st->instance_id].vars, num); + if( str[0] ) idb_put(instance_data[st->instance_id].vars, num, aStrdup(str)); } return 1; default: @@ -2740,9 +2740,9 @@ return 1; case '\'': if( st->instance_id ) { - idb_remove(instance[st->instance_id].vars, num); + idb_remove(instance_data[st->instance_id].vars, num); if( val != 0 ) - idb_iput(instance[st->instance_id].vars, num, val); + idb_iput(instance_data[st->instance_id].vars, num, val); } return 1; default: @@ -8856,7 +8856,7 @@ 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 ((m = instance_mapname2mapid(map[m].name, st->instance_id)) < 0) { ShowError("buildin_monster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn); return 1; @@ -8957,7 +8957,7 @@ 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 ((m = instance_mapname2mapid(map[m].name, st->instance_id)) < 0) { ShowError("buildin_areamonster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn); return 1; @@ -9018,7 +9018,7 @@ if( (m=map_mapname2mapid(mapname))<0 ) return 0; - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) + if( map[m].flag.src4instance && st->instance_id && (m = instance_mapname2mapid(map[m].name, st->instance_id)) < 0 ) return 0; if( script_hasdata(st,4) ) { @@ -9059,7 +9059,7 @@ if( (m = map_mapname2mapid(mapname))<0 ) return 0; - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) + if( map[m].flag.src4instance && st->instance_id && (m = instance_mapname2mapid(map[m].name, st->instance_id)) < 0 ) return 0; if( script_hasdata(st,3) ) { @@ -11569,7 +11569,7 @@ return 0; } - 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 && (m = instance_mapname2mapid(map[m].name, st->instance_id)) < 0 ) { script_pushint(st,-1); return 0; @@ -16480,33 +16480,32 @@ BUILDIN_FUNC(instance_create) { - const char *name; - int party_id, res; - name = script_getstr(st, 2); - party_id = script_getnum(st, 3); + struct map_session_data *sd; + int res; - res = instance_create(party_id, name); - if( res == -4 ) // Already exists - { - script_pushint(st, -1); - return 0; + if((sd = script_rid2sd(st)) == NULL) + return 1; + + if(!sd->status.party_id) { + ShowError("script:instance_create: attempting to start an instance with no attached party.\n"); + return 1; } - else if( res < 0 ) - { + + res = instance_create(sd->status.party_id, script_getstr(st, 2)); + if( res < 0 ) { const char *err; - switch(res) - { - case -3: err = "No free instances"; break; - case -2: err = "Invalid party ID"; break; - case -1: err = "Invalid type"; break; - default: err = "Unknown"; break; + switch(res) { + case -4: err = "Instance already exists"; break; + case -3: err = "No free instances"; break; + case -2: err = "Invalid party ID"; break; + case -1: err = "Invalid type"; break; + default: err = "Unknown"; break; } ShowError("buildin_instance_create: %s [%d].\n", err, res); - script_pushint(st, -2); - return 0; } + st->instance_id = res; script_pushint(st, res); return 0; } @@ -16519,14 +16518,11 @@ if( script_hasdata(st, 2) ) instance_id = script_getnum(st, 2); - else if( st->instance_id ) - instance_id = st->instance_id; else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) instance_id = p->instance_id; else return 0; - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - { + if( instance_id <= 0 || instance_id >= MAX_MAP_PER_SERVER ) { ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id); return 0; } @@ -16535,164 +16531,27 @@ return 0; } -BUILDIN_FUNC(instance_attachmap) +/*========================================== + * Warps player to instance + * Results: + * 0: Success + * -1: Instance not in DB + * -2: Character not in party + * -3: Party doesn't have instance + * -4: Instance doesn't match with party + *------------------------------------------*/ +BUILDIN_FUNC(instance_enter) { - const char *name; - int16 m; - int instance_id; - bool usebasename = false; - - name = script_getstr(st,2); - instance_id = script_getnum(st,3); - if( script_hasdata(st,4) && script_getnum(st,4) > 0) - usebasename = true; - - if( (m = instance_add_map(name, instance_id, usebasename)) < 0 ) // [Saithis] - { - ShowError("buildin_instance_attachmap: instance creation failed (%s): %d\n", name, m); - script_pushconststr(st, ""); - return 0; - } - script_pushconststr(st, map[m].name); - - return 0; -} - -BUILDIN_FUNC(instance_detachmap) -{ struct map_session_data *sd; - struct party_data *p; - const char *str; - int16 m; - int instance_id; - str = script_getstr(st, 2); - if( script_hasdata(st, 3) ) - instance_id = script_getnum(st, 3); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - - if( (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m,instance_id)) < 0 ) - { - ShowError("buildin_instance_detachmap: Trying to detach invalid map %s\n", str); - return 0; - } - - instance_del_map(m); - return 0; -} - -BUILDIN_FUNC(instance_attach) -{ - int instance_id; - - instance_id = script_getnum(st, 2); - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - return 0; - - st->instance_id = instance_id; - return 0; -} - -BUILDIN_FUNC(instance_id) -{ - int instance_id; - - if( script_hasdata(st, 2) ) - { - struct map_session_data *sd; - struct party_data *p; - 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; - } + if((sd = script_rid2sd(st)) != NULL) + script_pushint(st,instance_enter(sd,script_getstr(st, 2))); else - instance_id = st->instance_id; - - script_pushint(st, instance_id); + return 1; return 0; -} -BUILDIN_FUNC(instance_set_timeout) -{ - int progress_timeout, idle_timeout; - int instance_id; - struct map_session_data *sd; - struct party_data *p; - - progress_timeout = script_getnum(st, 2); - idle_timeout = script_getnum(st, 3); - - if( script_hasdata(st, 4) ) - instance_id = script_getnum(st, 4); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - - if( instance_id > 0 ) - instance_set_timeout(instance_id, progress_timeout, idle_timeout); - - return 0; } -BUILDIN_FUNC(instance_init) -{ - int instance_id = script_getnum(st, 2); - - if( instance[instance_id].state != INSTANCE_IDLE ) - { - ShowError("instance_init: instance already initialized.\n"); - return 0; - } - - instance_init(instance_id); - return 0; -} - -BUILDIN_FUNC(instance_announce) -{ - int instance_id = script_getnum(st,2); - const char *mes = script_getstr(st,3); - int flag = script_getnum(st,4); - const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL; - int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL) - int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize - int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign - int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY - - int i; - struct map_session_data *sd; - struct party_data *p; - - if( instance_id == 0 ) - { - if( st->instance_id ) - instance_id = st->instance_id; - else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; - } - - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - return 0; - - for( i = 0; i < instance[instance_id].num_map; i++ ) - map_foreachinmap(buildin_announce_sub, instance[instance_id].map[i], BL_PC, - mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY); - - return 0; -} - BUILDIN_FUNC(instance_npcname) { const char *str; @@ -16742,7 +16601,7 @@ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) instance_id = p->instance_id; - if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m, instance_id)) < 0 ) + if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_mapname2mapid(map[m].name, instance_id)) < 0 ) { script_pushconststr(st, ""); return 0; @@ -16752,6 +16611,30 @@ return 0; } +BUILDIN_FUNC(instance_id) +{ + int instance_id; + + if( script_hasdata(st, 2) ) + { + struct map_session_data *sd; + struct party_data *p; + 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); + return 0; +} + BUILDIN_FUNC(instance_warpall) { struct map_session_data *pl_sd; @@ -16773,10 +16656,10 @@ instance_id = p->instance_id; else return 0; - if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapid2imapid(m, instance_id)) < 0) ) + if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapname2mapid(map[m].name, st->instance_id)) < 0) ) return 0; - if( !(p = party_search(instance[instance_id].party_id)) ) + if( !(p = party_search(instance_data[instance_id].party_id)) ) return 0; mapindex = map_id2index(m); @@ -16917,7 +16800,7 @@ return 0; } - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) + if( map[m].flag.src4instance && st->instance_id && (m = instance_mapname2mapid(map[m].name, st->instance_id)) < 0 ) return 0; center.m = m; @@ -18123,15 +18006,10 @@ BUILDIN_DEF(bg_updatescore,"sii"), // Instancing - BUILDIN_DEF(instance_create,"si"), + BUILDIN_DEF(instance_create,"s"), BUILDIN_DEF(instance_destroy,"?"), - BUILDIN_DEF(instance_attachmap,"si?"), - BUILDIN_DEF(instance_detachmap,"s?"), - BUILDIN_DEF(instance_attach,"i"), BUILDIN_DEF(instance_id,"?"), - BUILDIN_DEF(instance_set_timeout,"ii?"), - BUILDIN_DEF(instance_init,"i"), - BUILDIN_DEF(instance_announce,"isi?????"), + BUILDIN_DEF(instance_enter,"s"), BUILDIN_DEF(instance_npcname,"s?"), BUILDIN_DEF(has_instance,"s?"), BUILDIN_DEF(instance_warpall,"sii?"), Index: src/map/unit.c =================================================================== --- src/map/unit.c (revision 17368) +++ src/map/unit.c (working copy) @@ -2206,11 +2206,8 @@ {// 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[sd->bl.m].instance_id) + instance_delusers(map[sd->bl.m].instance_id); sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS] sd->debug_file = file; sd->debug_line = line;