Index: src/map/atcommand.c =================================================================== --- src/map/atcommand.c (revision 17381) +++ src/map/atcommand.c (working copy) @@ -3945,8 +3945,6 @@ strcat(atcmd_output, msg_txt(sd,1097)); // GuildLock | if (map[m_id].flag.loadevent) strcat(atcmd_output, msg_txt(sd,1098)); // Loadevent | - if (map[m_id].flag.src4instance) - strcat(atcmd_output, msg_txt(sd,1099)); // Src4instance | if (map[m_id].flag.chmautojoin) strcat(atcmd_output, msg_txt(sd,1100)); // Chmautojoin | if (map[m_id].flag.nousecart) @@ -7653,7 +7651,7 @@ checkflag(nogo); checkflag(nobaseexp); checkflag(nojobexp); checkflag(nomobloot); checkflag(nomvploot); checkflag(nightenabled); checkflag(restricted); checkflag(nodrop); checkflag(novending); checkflag(loadevent); - checkflag(nochat); checkflag(partylock); checkflag(guildlock); checkflag(src4instance); + checkflag(nochat); checkflag(partylock); checkflag(guildlock); clif_displaymessage(sd->fd," "); clif_displaymessage(sd->fd,msg_txt(sd,1312)); // Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On) clif_displaymessage(sd->fd,msg_txt(sd,1313)); // Type "@mapflag available" to list the available mapflags. @@ -7673,7 +7671,7 @@ setflag(nogo); setflag(nobaseexp); setflag(nojobexp); setflag(nomobloot); setflag(nomvploot); setflag(nightenabled); setflag(restricted); setflag(nodrop); setflag(novending); setflag(loadevent); - setflag(nochat); setflag(partylock); setflag(guildlock); setflag(src4instance); + setflag(nochat); setflag(partylock); setflag(guildlock); clif_displaymessage(sd->fd,msg_txt(sd,1314)); // Invalid flag name or flag. clif_displaymessage(sd->fd,msg_txt(sd,1312)); // Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On) @@ -7685,7 +7683,7 @@ clif_displaymessage(sd->fd,"nozenypenalty, notrade, noskill, nowarp, nowarpto, noicewall, snow, clouds, clouds2,"); clif_displaymessage(sd->fd,"fog, fireworks, sakura, leaves, nogo, nobaseexp, nojobexp, nomobloot,"); clif_displaymessage(sd->fd,"nomvploot, nightenabled, restricted, nodrop, novending, loadevent, nochat, partylock,"); - clif_displaymessage(sd->fd,"guildlock, src4instance"); + clif_displaymessage(sd->fd,"guildlock"); #undef checkflag #undef setflag Index: src/map/clif.c =================================================================== --- src/map/clif.c (revision 17381) +++ src/map/clif.c (working copy) @@ -9186,10 +9186,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 @@ -15265,102 +15265,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 17381) +++ 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 17381) +++ src/map/instance.c (working copy) @@ -24,465 +24,723 @@ #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); -/*-------------------------------------- - * name : instance name - * Return value could be - * -4 = already exists | -3 = no free instances | -2 = party not found | -1 = invalid type - * On success return instance_id - *--------------------------------------*/ -int instance_create(int party_id, const char *name) + return 0; +} + +/*========================================== + * Create subscription timer for party + *------------------------------------------*/ +static int instance_subscription_timer(int tid, unsigned int tick, int id, intptr_t data) { - int i; - struct party_data* p; + int i, j, ret; + int instance_id = instance_wait.id[0]; + 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; + if(instance_wait.count == 0 || instance_id <= 0) + return 0; + + // Check that maps have been added + ret = instance_addmap(instance_id); + + // 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; + } + } + } } - if( p->instance_id ) - return -4; // Party already instancing + 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])); - // 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; + 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; + } + } + } + } } - 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); + if(instance_wait.count) + instance_wait.timer = add_timer(gettick()+INSTANCE_INTERVAL, instance_subscription_timer, 0, 0); + else + instance_wait.timer = -1; - safestrncpy( instance[i].name, name, sizeof(instance[i].name) ); - memset( instance[i].map, 0x00, sizeof(instance[i].map) ); - p->instance_id = i; - - clif_instance(i, 1, 0); // Start instancing window - ShowInfo("[Instance] Created: %s.\n", name); - return i; + return 0; } -/*-------------------------------------- - * Add a map to the instance using src map "name" - *--------------------------------------*/ -int instance_add_map(const char *name, int instance_id, bool usebasename) +/*========================================== + * Adds timer back to party entering instance + *------------------------------------------*/ +static int instance_startkeeptimer(struct instance_data *im, short instance_id) { - int16 m = map_mapname2mapid(name); - int i, im = -1; - size_t num_cell, size; + struct instance_db *db; + struct party_data *p; + int i; - if( m < 0 ) - return -1; // source map not found + nullpo_retr(0, im); - 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; - } + // No timer + if(im->keep_timer != -1) + return 1; - 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; - } - else im = map_num++; // Using next map index + if((db = instance_searchtype_db(im->type)) == NULL) + return 1; - 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 + // 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); - 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 + // 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; + } + } } - // 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) ); + return 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); +/*========================================== + * Creates idle timer + * Default before instance destroy is 5 minutes + *------------------------------------------*/ +static int instance_startidletimer(struct instance_data *im, short instance_id) +{ + struct instance_db *db; + struct party_data *p; + int i; - memset(map[im].npc, 0x00, sizeof(map[i].npc)); - map[im].npc_num = 0; + nullpo_retr(1, im); - memset(map[im].moblist, 0x00, sizeof(map[im].moblist)); - map[im].mob_delete_timer = INVALID_TIMER; + // No current timer + if(im->idle_timer != -1) + return 1; - 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 + // 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); - instance[instance_id].map[instance[instance_id].num_map++] = im; // Attach to actual instance - map_addmap2db(&map[im]); + // 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) { + ShowDebug("Sending idle timer to player.\n"); + clif_instance_status(p->data[i].sd, db->name, im->keep_limit, im->idle_limit, 1); + break; + } + } + } - return im; + return 0; } -/*-------------------------------------- - * m : source map of this instance - * party_id : source party of this instance - * type : result (0 = map id | 1 = instance id) - *--------------------------------------*/ -int instance_map2imap(int16 m, int instance_id) +/*========================================== + * Delete the idle timer + *------------------------------------------*/ +static int instance_stopidletimer(struct instance_data *im) { - int i; + struct party_data *p; + int i; - if( !instance_is_valid(instance_id) ) - { - return -1; + 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) { + ShowDebug("Stopping idle timer for player.\n"); + clif_instance_changestatus(p->data[i].sd, 0, im->idle_limit, 1); + break; + } + } } - 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; + return 0; } -/*-------------------------------------- - * m : source map - * instance_id : where to search - * result : mapid of map "m" in this instance - *--------------------------------------*/ -int instance_mapid2imapid(int16 m, int instance_id) +/*========================================== + * Add an NPC to an instance + *------------------------------------------*/ +static int instance_addnpc_sub(struct block_list *bl, va_list ap) { - 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; - } + struct npc_data* nd; - if( !instance_is_valid(instance_id) ) - return -1; + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, nd = (struct npc_data *)bl); - return instance_map2imap(m, instance_id); + return npc_duplicate4instance(nd, va_arg(ap, int)); } -/*-------------------------------------- - * 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) +// Separate function used for reloading +void instance_addnpc(struct instance_data *im) { - struct npc_data* nd = (struct npc_data*)bl; - int16 m = va_arg(args, int); // Destination Map + int i; - npc_duplicate4instance(nd, m); - return 1; + for(i = 0; i < im->cnt_map; i++) + map_foreachinarea(instance_addnpc_sub, 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); } /*-------------------------------------- - * Init all map on the instance. Npcs are created here + * name : instance name + * Return value could be + * -4 = already exists | -3 = no free instances | -2 = party not found | -1 = invalid type + * On success return instance_id *--------------------------------------*/ -void instance_init(int instance_id) +int instance_create(int party_id, const char *name) { - int i; + short i; + int k; + struct instance_db *db = instance_searchname_db(name); + struct party_data *p = party_search(party_id); - if( !instance_is_valid(instance_id) ) - return; // nothing to do + if(db == NULL) + 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( p == NULL ) + return 2; - instance[instance_id].state = INSTANCE_BUSY; - ShowInfo("[Instance] Initialized %s.\n", instance[instance_id].name); + if( p->instance_id ) + return 3; // Party already instancing + + // Searching a Free Instance + // 0 is ignored as this mean "no instance" on maps + ARR_FIND(1, MAX_INSTANCE_DB, i, instance_data[i].state == INSTANCE_FREE); + if( i >= MAX_INSTANCE_DB ) + return 4; + + 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; + instance_data[i].vars = idb_alloc(DB_OPT_RELEASE_DATA); + memset(instance_data[i].map, 0, sizeof(instance_data[i].map)); + + p->instance_id = i; + + ShowDebug("Created instance id %d.\n",p->instance_id); + + instance_wait.id[instance_wait.count++] = p->instance_id; + + 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; + } + } + + instance_subscription_timer(0,0,0,0); + + ShowInfo("[Instance] Created: %s.\n", name); + + return 0; } /*-------------------------------------- - * Used on instance deleting process. - * Warps all players on each instance map to its save points. + * Adds maps to the instance *--------------------------------------*/ -int instance_del_load(struct map_session_data* sd, va_list args) +int instance_addmap(short instance_id) { - int16 m = va_arg(args,int); - if( !sd || sd->bl.m != m ) + int i, m; + int cnt_map = 0; + struct instance_data *im; + struct instance_db *db; + struct party_data *p; + + if(instance_id <= 0) return 0; - pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_OUTSIGHT); - return 1; -} + im = &instance_data[instance_id]; -/* 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 the instance isn't idle, we can't do anything + if(im->state != INSTANCE_IDLE) + return 0; - switch(bl->type) { - case BL_PC: - map_quit((struct map_session_data *) bl); + if((db = instance_searchtype_db(im->type)) == NULL) + return 0; + + // 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); + + // Add the maps + for(i = 0; i < MAX_MAP_PER_INSTANCE; i++) { + if(strlen(db->mapname[i]) < 1) + continue; + else if( (m = map_addinstancemap(db->mapname[i], instance_id)) < 0) { + // An error occured adding a map + ShowError("instance_addmap: No maps added to instance %d.\n",instance_id); + return 0; + } else { + im->map[cnt_map].m = m; + im->map[cnt_map].src_m = map_mapname2mapid(db->mapname[i]); + cnt_map++; + } + } + + im->cnt_map = cnt_map; + + // Create NPCs on all maps + instance_addnpc(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; - 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; + return cnt_map; } -/*-------------------------------------- - * Removes a simple instance map - *--------------------------------------*/ -void instance_del_map(int16 m) + +/*========================================== + * Returns an instance map ID using a map name + * name : source map + * instance_id : where to search + * result : mapid of map "name" in this instance + *------------------------------------------*/ +int instance_mapname2mapid(const char *name, short instance_id) { + struct instance_data *im; + int m = map_mapname2mapid(name); + char iname[12]; int i; - if( m <= 0 || !map[m].instance_id ) - { - ShowError("Tried to remove non-existing instance map (%d)\n", m); - return; + + if(m < 0) { + ShowError("instance_mapname2mapid: map name %s does not exist.\n",name); + return m; } - map_foreachpc(instance_del_load, m); - map_foreachinmap(instance_cleanup_sub, m, BL_ALL); + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return m; - if( map[m].mob_delete_timer != INVALID_TIMER ) - delete_timer(map[m].mob_delete_timer, map_removemobs_timer); + im = &instance_data[instance_id]; + if(im->state != INSTANCE_BUSY) + return m; - mapindex_removemap( map[m].index ); + 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); + return mapindex_name2id(iname); + } - // Free memory - aFree(map[m].cell); - aFree(map[m].block); - aFree(map[m].block_mob); + return m; +} - // 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; +/*========================================== + * Removes a instance, all its maps and npcs. + *------------------------------------------*/ +int instance_destroy(short instance_id) +{ + struct instance_data *im; + struct party_data *p; + int i, j, type = 0, count = 0; + unsigned int now = (unsigned int)time(NULL); + + if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA) + return 1; + + im = &instance_data[instance_id]; + + if(im->state == INSTANCE_FREE) + return 1; + + 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])); + + 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; + 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; + + for(i = 0; i < MAX_MAP_PER_INSTANCE; i++) + count += map_delinstancemap(im->map[i].m); } - 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); - map_removemapdb(&map[m]); - memset(&map[m], 0x00, sizeof(map[0])); + 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; + } - /* 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((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; + } + } + } + + if( im->vars ) + db_destroy(im->vars); + + im->type = 0; + im->state = INSTANCE_FREE; + im->party_id = 0; + im->keep_limit = 0; + im->idle_limit = 0; + im->users = 0; + im->vars = NULL; + ShowInfo("[Instance] Destroyed %d.\n", instance_data[i].map); + + memset(instance_data[i].map, 0, sizeof(instance_data[i].map)); + + return 0; } -/*-------------------------------------- - * Timer to destroy instance by process or idle - *--------------------------------------*/ -int instance_destroy_timer(int tid, unsigned int tick, int id, intptr_t data) +/*========================================== + * Allows a user to enter an instance + *------------------------------------------*/ +int instance_enter(struct map_session_data *sd, const char *name) { - instance_destroy(id); + struct instance_data *im; + struct instance_db *db = instance_searchname_db(name); + struct party_data *p; + int m; + + nullpo_retr(-1, sd); + + if(db == NULL) + return 1; + + // 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; + + // Party must have an instance + if(p->instance_id == 0) + return 3; + + 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; + + // Does the instance match? + if((m = instance_mapname2mapid(db->enter.mapname, p->instance_id)) < 0) + return 4; + + if(pc_setpos(sd, m, db->enter.x, db->enter.y, 0)) + return 4; + + // 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, short 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(short 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(short 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) +/*========================================== + * Read the instance_db.txt file + *------------------------------------------*/ +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; itype) + continue; + else { + // First we load the NPCs again + instance_addnpc(im); + + // Create new keep timer + if((db = instance_searchtype_db(im->type)) != NULL) + im->keep_limit = (unsigned int)time(NULL) + db->limit; + } } - else - { - instance[instance_id].idle_timeoutval = 0; - instance[instance_id].idle_timeout = 0; - instance[instance_id].idle_timer = INVALID_TIMER; - } - if( instance[instance_id].idle_timer == INVALID_TIMER && instance[instance_id].progress_timer != INVALID_TIMER ) - clif_instance(instance_id, 3, 0); + // Reset player to instance beginning + iter = mapit_getallusers(); + for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) + if(sd && map[sd->bl.m].instance_id) { + struct party_data *p; + if(!(p = party_search(sd->status.party_id)) || p->instance_id != map[sd->bl.m].instance_id) // Someone not in party is on instance map + continue; + im = &instance_data[p->instance_id]; + if((db = instance_searchtype_db(im->type)) != NULL && !instance_enter(sd,db->name)) { // All good + clif_displaymessage(sd->fd, msg_txt(sd,515)); // Instance has been reloaded + instance_reqinfo(sd,p->instance_id); + } else // Something went wrong + ShowError("do_reload_instance: Error setting character at instance start: character_id=%d instance=%s.\n",sd->status.char_id,db->name); + } + mapit_free(iter); } -/*-------------------------------------- - * Checks if sd in on a instance and should be kicked from it - *--------------------------------------*/ -void instance_check_kick(struct map_session_data *sd) +void do_init_instance(void) { - int16 m = sd->bl.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 17381) +++ src/map/instance.h (working copy) @@ -4,48 +4,45 @@ #ifndef _INSTANCE_H_ #define _INSTANCE_H_ -#define MAX_MAP_PER_INSTANCE 10 -#define MAX_INSTANCE 500 +#define MAX_INSTANCE_DATA 500 // Essentially how many instances we can create, but instance creation is primarily decided by MAX_MAP_PER_SERVER +#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(short instance_id); +int instance_enter(struct map_session_data *sd, const char *name); +int instance_reqinfo(struct map_session_data *sd, short instance_id); +int instance_addusers(short instance_id); +int instance_delusers(short instance_id); +int instance_mapname2mapid(const char *name, short instance_id); +int instance_addmap(short 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 instance_addnpc(struct instance_data *im); +void do_reload_instance(void); +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 17381) +++ src/map/map.c (working copy) @@ -2170,6 +2170,135 @@ 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 -2; + } + + 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 -3; + } + + // 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]); + + 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 +3214,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 17381) +++ 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 { @@ -573,7 +573,6 @@ unsigned nochat :1; unsigned partylock :1; unsigned guildlock :1; - unsigned src4instance : 1; // To flag this map when it's used as a src map for instances unsigned reset :1; // [Daegaladh] unsigned chmautojoin : 1; //prevent to auto join map channel unsigned nousecart : 1; //prevent open up cart @FIXME client side only atm @@ -684,6 +683,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 17381) +++ src/map/npc.c (working copy) @@ -2579,15 +2579,11 @@ 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 - { - if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 )// ,,, - { + } else { + if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ) { // ,,, 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); return end;// next line, try to continue } @@ -2601,8 +2597,7 @@ if( type == WARP && sscanf(w4, "%d,%d", &xs, &ys) == 2 );// , else if( type == SCRIPT && sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3);// ,, else if( type != WARP ) class_ = atoi(w4);// - 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 } @@ -2620,56 +2615,51 @@ 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; + 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 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; + 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; 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); } @@ -2688,6 +2678,18 @@ npc_timerevent_export(nd, i); } + if(!strcmp(filepath,"INSTANCING")) { //Instance NPCs will use this for commands + char evname[EVENT_NAME_LENGTH]; + struct event_data *ev; + + nd->instance_id = map[m].instance_id; + + snprintf(evname, ARRAYLENGTH(evname), "%s::OnInstanceInit", nd->exname); + + if( ( ev = (struct event_data*)strdb_get(ev_db, evname) ) ) + run_script(nd->u.scr.script,ev->pos,0,nd->bl.id); + } + nd->u.scr.timerid = INVALID_TIMER; return end; @@ -2700,21 +2702,18 @@ 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 ) - { + 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); return 1; } @@ -2745,9 +2744,7 @@ 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"; @@ -3772,11 +3769,8 @@ "\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(); + do_reload_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/npc.h =================================================================== --- src/map/npc.h (revision 17381) +++ src/map/npc.h (working copy) @@ -29,19 +29,16 @@ struct view_data *vd; struct status_change sc; //They can't have status changes, but.. they want the visual opt values. struct npc_data *master_nd; - short class_; - short speed; + short class_,speed,instance_id; char name[NAME_LENGTH+1];// display name char exname[NAME_LENGTH+1];// unique npc name - int chat_id; - int touching_id; + int chat_id,touching_id; unsigned int next_walktime; unsigned size : 2; struct status_data status; - unsigned int level; - unsigned int stat_point; + unsigned int level,stat_point; void* chatdb; // pointer to a npc_parse struct (see npc_chat.c) char* path;/* path dir */ Index: src/map/party.c =================================================================== --- src/map/party.c (revision 17381) +++ 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 17381) +++ src/map/pc.c (working copy) @@ -4762,29 +4762,24 @@ 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( sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id ) { + int im = map_mapname2mapid(map[m].name); + if( im < 0 ) { + mapindex = sd->status.save_point.map; + x = sd->status.save_point.x; + y = sd->status.save_point.y; } } Index: src/map/script.c =================================================================== --- src/map/script.c (revision 17381) +++ src/map/script.c (working copy) @@ -2565,12 +2565,16 @@ } break; case '\'': - if (st->instance_id) { - data->u.str = (char*)idb_get(instance[st->instance_id].vars,reference_getuid(data)); - } else { + { + struct npc_data *nd; + nd = map_id2nd(st->oid); + if( nd && nd->instance_id > 0 ) + data->u.str = (char*)idb_get(instance_data[nd->instance_id].vars,reference_getuid(data)); + else { ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to \"\"\n", name); data->u.str = NULL; } + } break; default: data->u.str = pc_readglobalreg_str(sd, name); @@ -2630,12 +2634,16 @@ } break; case '\'': - if( st->instance_id ) - data->u.num = (int)idb_iget(instance[st->instance_id].vars,reference_getuid(data)); + { + struct npc_data *nd; + nd = map_id2nd(st->oid); + if( nd && nd->instance_id > 0 ) + data->u.num = (int)idb_iget(instance_data[nd->instance_id].vars,reference_getuid(data)); else { ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to 0\n", name); data->u.num = 0; } + } break; default: data->u.num = pc_readglobalreg(sd, name); @@ -2668,8 +2676,7 @@ { char prefix = name[0]; - if( is_string_variable(name) ) - {// string variable + if( is_string_variable(name) ) {// string variable const char* str = (const char*)value; switch (prefix) { case '@': @@ -2691,17 +2698,19 @@ } 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)); + { + struct npc_data *nd; + nd = map_id2nd(st->oid); + if( nd && nd->instance_id > 0 ) { + idb_remove(instance_data[nd->instance_id].vars, num); + if( str[0] ) idb_put(instance_data[nd->instance_id].vars, num, aStrdup(str)); + } + return 1; } - return 1; default: return pc_setglobalreg_str(sd, name, str); } - } - else - {// integer variable + } else {// integer variable int val = (int)__64BPRTSIZE(value); if(str_data[num&0x00ffffff].type == C_PARAM) { @@ -2739,12 +2748,16 @@ } return 1; case '\'': - if( st->instance_id ) { - idb_remove(instance[st->instance_id].vars, num); - if( val != 0 ) - idb_iput(instance[st->instance_id].vars, num, val); + { + struct npc_data *nd; + nd = map_id2nd(st->oid); + if( nd && nd->instance_id > 0 ) { + idb_remove(instance_data[nd->instance_id].vars, num); + if( val != 0 ) + idb_iput(instance_data[nd->instance_id].vars, num, val); + } + return 1; } - return 1; default: return pc_setglobalreg(sd, name, val); } @@ -3665,14 +3678,9 @@ int gotocount = script_config.check_gotocount; TBL_PC *sd; struct script_stack *stack=st->stack; - struct npc_data *nd; script_attach_state(st); - nd = map_id2nd(st->oid); - if( nd && map[nd->bl.m].instance_id > 0 ) - st->instance_id = map[nd->bl.m].instance_id; - if(st->state == RERUNLINE) { run_func(st); if(st->state == GOTO) @@ -8815,34 +8823,28 @@ struct map_session_data* sd; int16 m; - if (script_hasdata(st, 8)) - { + if (script_hasdata(st, 8)) { event = script_getstr(st, 8); check_event(st, event); } - if (script_hasdata(st, 9)) - { + if (script_hasdata(st, 9)) { size = script_getnum(st, 9); - if (size > 3) - { + if (size > 3) { ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_); return 1; } } - if (script_hasdata(st, 10)) - { + if (script_hasdata(st, 10)) { ai = script_getnum(st, 10); - if (ai > 4) - { + if (ai > 4) { ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_); return 1; } } - if (class_ >= 0 && !mobdb_checkid(class_)) - { + if (class_ >= 0 && !mobdb_checkid(class_)) { ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_); return 1; } @@ -8852,17 +8854,7 @@ if (sd && strcmp(mapn, "this") == 0) m = sd->bl.m; else - { m = map_mapname2mapid(mapn); - if (map[m].flag.src4instance && st->instance_id) - { // Try to redirect to the instance map, not the src map - if ((m = instance_mapid2imapid(m, st->instance_id)) < 0) - { - ShowError("buildin_monster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn); - return 1; - } - } - } mob_once_spawn(sd, m, x, y, str, class_, amount, event, size, ai); return 0; @@ -8922,27 +8914,22 @@ 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 1; } } - 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 1; } @@ -8953,17 +8940,7 @@ if (sd && strcmp(mapn, "this") == 0) m = sd->bl.m; else - { m = map_mapname2mapid(mapn); - if (map[m].flag.src4instance && st->instance_id) - { // Try to redirect to the instance map, not the src map - if ((m = instance_mapid2imapid(m, st->instance_id)) < 0) - { - ShowError("buildin_areamonster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn); - return 1; - } - } - } mob_once_spawn_area(sd, m, x0, y0, x1, y1, str, class_, amount, event, size, ai); return 0; @@ -9018,9 +8995,6 @@ if( (m=map_mapname2mapid(mapname))<0 ) return 0; - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - return 0; - if( script_hasdata(st,4) ) { if ( script_getnum(st,4) == 1 ) { map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag); @@ -9059,9 +9033,6 @@ if( (m = map_mapname2mapid(mapname))<0 ) return 0; - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - return 0; - if( script_hasdata(st,3) ) { if ( script_getnum(st,3) == 1 ) { map_foreachinmap(buildin_killmonsterall_sub,m,BL_MOB); @@ -11569,12 +11540,6 @@ return 0; } - if( map[m].flag.src4instance && map[m].instance_id == 0 && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - { - script_pushint(st,-1); - return 0; - } - script_pushint(st,map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event)); return 0; @@ -16475,249 +16440,131 @@ } /*========================================== - * Instancing Script Commands + * Instancing System *------------------------------------------*/ -BUILDIN_FUNC(instance_create) +//Returns an Instance ID +//Checks NPC first, then if player is attached we check party +static int buildin_instancegetid_sub(struct script_state* st) { - const char *name; - int party_id, res; + short instance_id = 0; - name = script_getstr(st, 2); - party_id = script_getnum(st, 3); - - res = instance_create(party_id, name); - if( res == -4 ) // Already exists - { - script_pushint(st, -1); - return 0; + struct npc_data *nd; + if( (nd = map_id2nd(st->oid)) && nd->instance_id > 0 ) + instance_id = nd->instance_id; + else { + struct map_session_data *sd; + struct party_data *p; + 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( res < 0 ) - { - const char *err; - switch(res) - { - case -3: err = "No free instances"; break; - case -2: err = "Invalid party ID"; break; - case -1: err = "Invalid type"; break; - default: err = "Unknown"; break; - } - ShowError("buildin_instance_create: %s [%d].\n", err, res); - script_pushint(st, -2); - return 0; - } - script_pushint(st, res); - return 0; + return instance_id; } -BUILDIN_FUNC(instance_destroy) +/*========================================== + * Creates the instance + * Returns Instance ID if created successfully + *------------------------------------------*/ +BUILDIN_FUNC(instance_create) { - int instance_id; + struct map_session_data *sd; - struct party_data *p; + int res; - 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((sd = script_rid2sd(st)) == NULL) + return 1; - if( instance_id <= 0 || instance_id >= MAX_INSTANCE ) - { - ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id); - return 0; + if(!sd->status.party_id) { + ShowError("script:instance_create: attempting to start an instance with no attached party.\n"); + return 1; } - instance_destroy(instance_id); - return 0; -} - -BUILDIN_FUNC(instance_attachmap) -{ - const char *name; - int16 m; - int instance_id; - bool usebasename = false; - - name = script_getstr(st,2); - instance_id = script_getnum(st,3); - if( script_hasdata(st,4) && script_getnum(st,4) > 0) - usebasename = true; - - if( (m = instance_add_map(name, instance_id, usebasename)) < 0 ) // [Saithis] - { - ShowError("buildin_instance_attachmap: instance creation failed (%s): %d\n", name, m); - script_pushconststr(st, ""); - return 0; + res = instance_create(sd->status.party_id, script_getstr(st, 2)); + if( res < 0 ) { + const char *err; + switch(res) { + case 4: err = "No free instances"; break; + case 3: err = "Instance already exists"; break; + case 2: err = "Invalid party ID"; break; + case 1: err = "Invalid type"; break; + default: err = "Unknown"; break; + } + ShowError("script:instance_create: %s [%d].\n", err, res); } - script_pushconststr(st, map[m].name); + script_pushint(st, res); return 0; } -BUILDIN_FUNC(instance_detachmap) +/*========================================== + * Destroys an instance (unofficial) + * Officially instances are only destroyed by timeout + * + * instance_destroy {}; + *------------------------------------------*/ +BUILDIN_FUNC(instance_destroy) { - struct map_session_data *sd; - struct party_data *p; - const char *str; - int16 m; - int instance_id; + short 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( script_hasdata(st,2) ) + instance_id = script_getnum(st,2); + else + instance_id = buildin_instancegetid_sub(st); - 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); + if( instance_id <= 0 || instance_id >= MAX_MAP_PER_SERVER ) { + ShowError("script:instance_destroy: Trying to destroy invalid instance %d.\n", instance_id); return 0; } - instance_del_map(m); + instance_destroy(instance_id); return 0; } -BUILDIN_FUNC(instance_attach) +/*========================================== + * 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) { - int instance_id; + struct map_session_data *sd; - 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; -} - +/*========================================== + * Returns the name of a duplicated NPC + * + * instance_npcname {,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; + str = script_getstr(st,2); + if( script_hasdata(st,3) ) + instance_id = script_getnum(st,3); + else + instance_id = buildin_instancegetid_sub(st); - if( instance_id && (nd = npc_name2id(str)) != NULL ) - { + if( instance_id && (nd = npc_name2id(str)) != NULL ) { static char npcname[NAME_LENGTH]; snprintf(npcname, sizeof(npcname), "dup_%d_%d", instance_id, nd->bl.id); script_pushconststr(st,npcname); - } - else - { + } else { ShowError("script:instance_npcname: invalid instance NPC (instance_id: %d, NPC name: \"%s\".)\n", instance_id, str); st->state = END; return 1; @@ -16726,62 +16573,88 @@ return 0; } -BUILDIN_FUNC(has_instance) +/*========================================== + * Returns the name of a duplicated map + * + * instance_mapname {,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; + str = script_getstr(st,2); + if( script_hasdata(st,3) ) + instance_id = script_getnum(st,3); + else + instance_id = buildin_instancegetid_sub(st); - if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m, instance_id)) < 0 ) - { + // Build the instance mapname + snprintf(iname, sizeof(iname), "%03d%s", instance_id, str); + + // Check that instance mapname is a valid map + if( !instance_id || (m = map_mapname2mapid(iname)) < 0 ) script_pushconststr(st, ""); - return 0; + else + script_pushconststr(st, map[m].name); + + return 0; +} + +/*========================================== + * Returns an Instance ID + *------------------------------------------*/ +BUILDIN_FUNC(instance_id) +{ + short instance_id; + + instance_id = buildin_instancegetid_sub(st); + + if(!instance_id) { + ShowError("script:instance_id: No instance attached to NPC or player"); + script_pushint(st, 0); + return 1; } - script_pushconststr(st, map[m].name); + script_pushint(st, instance_id); + return 0; } +/*========================================== + * Warps all players inside an instance + * + * instance_warpall ,,{,}; + *------------------------------------------*/ BUILDIN_FUNC(instance_warpall) { + struct party_data *p; struct map_session_data *pl_sd; int16 m, i; - int instance_id; + short instance_id; const char *mapn; int x, y; unsigned short mapindex; - struct party_data *p = NULL; mapn = script_getstr(st,2); x = script_getnum(st,3); y = script_getnum(st,4); if( script_hasdata(st,5) ) instance_id = script_getnum(st,5); - else if( st->instance_id ) - instance_id = st->instance_id; - else if( (pl_sd = script_rid2sd(st)) != NULL && pl_sd->status.party_id && (p = party_search(pl_sd->status.party_id)) != NULL && p->instance_id ) - instance_id = p->instance_id; - else return 0; + else + instance_id = buildin_instancegetid_sub(st); - if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapid2imapid(m, instance_id)) < 0) ) + if( !instance_id || (m = map_mapname2mapid(mapn)) < 0 || (m = instance_mapname2mapid(map[m].name,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); 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); + if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == instance_id ) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT); return 0; } @@ -16806,39 +16679,39 @@ min = script_hasdata(st,4) ? script_getnum(st,4) : 1; // Minimum Level needed to join the Instance. max = script_hasdata(st,5) ? script_getnum(st,5) : MAX_LEVEL; // Maxium Level allowed to join the Instance. - if( min < 1 || min > MAX_LEVEL){ - ShowError("instance_check_party: Invalid min level, %d\n", min); - return 0; - }else if( max < 1 || max > MAX_LEVEL){ - ShowError("instance_check_party: Invalid max level, %d\n", max); - return 0; + if( min < 1 || min > MAX_LEVEL) { + ShowError("script:check_party: Invalid min level, %d\n", min); + return 1; + } else if( max < 1 || max > MAX_LEVEL) { + ShowError("script:check_party: Invalid max level, %d\n", max); + return 1; } if( script_hasdata(st,2) ) party_id = script_getnum(st,2); else return 0; - if( !(p = party_search(party_id)) ){ + if( !(p = party_search(party_id)) ) { script_pushint(st, 0); // Returns false if party does not exist. return 0; } for( i = 0; i < MAX_PARTY; i++ ) if( (pl_sd = p->data[i].sd) ) - if(map_id2bl(pl_sd->bl.id)){ - if(pl_sd->status.base_level < min){ + if(map_id2bl(pl_sd->bl.id)) { + if(pl_sd->status.base_level < min) { script_pushint(st, 0); return 0; - }else if(pl_sd->status.base_level > max){ + } else if(pl_sd->status.base_level > max) { script_pushint(st, 0); return 0; } c++; } - if(c < amount){ + if(c < amount) script_pushint(st, 0); // Not enough Members in the Party to join Instance. - }else + else script_pushint(st, 1); return 0; @@ -16911,15 +16784,11 @@ 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 0; } - if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 ) - return 0; - center.m = m; center.x = script_getnum(st,3); center.y = script_getnum(st,4); @@ -18123,17 +17992,12 @@ 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_id,""), + BUILDIN_DEF(instance_enter,"s"), BUILDIN_DEF(instance_npcname,"s?"), - BUILDIN_DEF(has_instance,"s?"), + BUILDIN_DEF(instance_mapname,"s?"), BUILDIN_DEF(instance_warpall,"sii?"), BUILDIN_DEF(instance_check_party,"i???"), /** Index: src/map/script.h =================================================================== --- src/map/script.h (revision 17381) +++ src/map/script.h (working copy) @@ -126,7 +126,6 @@ struct sleep_data { int tick,timer,charid; } sleep; - int instance_id; //For backing up purposes struct script_state *bk_st; int bk_npcid; Index: src/map/unit.c =================================================================== --- src/map/unit.c (revision 17381) +++ src/map/unit.c (working copy) @@ -2100,8 +2100,7 @@ status_change_end(bl, SC_TINDER_BREAKER2, INVALID_TIMER); status_change_end(bl, SC_HIDING, INVALID_TIMER); // Ensure the bl is a PC; if so, we'll handle the removal of cloaking and cloaking exceed later - if ( bl->type != BL_PC ) - { + if ( bl->type != BL_PC ) { status_change_end(bl, SC_CLOAKING, INVALID_TIMER); status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); } @@ -2156,8 +2155,7 @@ 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); } @@ -2183,8 +2181,7 @@ if( map[bl->m].users <= 0 || sd->state.debug_remove_map ) {// this is only place where map users is decreased, if the mobs were removed too soon then this function was executed too many times [FlavioJS] - if( sd->debug_file == NULL || !(sd->state.debug_remove_map) ) - { + if( sd->debug_file == NULL || !(sd->state.debug_remove_map) ) { sd->debug_file = ""; sd->debug_line = 0; sd->debug_func = ""; @@ -2206,11 +2203,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 && sd->state.changemap) + 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;