Index: src/map/atcommand.c
===================================================================
--- src/map/atcommand.c (revision 17368)
+++ 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 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 <Instance name>.61B <Standby Position>.W
- // Required to start the instancing information window on Client
- // This window re-appear each "refresh" of client automatically until type 4 is send to client.
- WBUFW(buf,0) = 0x02CB;
- memcpy(WBUFP(buf,2),instance[instance_id].name,INSTANCE_NAME_LENGTH);
- WBUFW(buf,63) = flag;
- clif_send(buf,packet_len(0x02CB),&sd->bl,PARTY);
- break;
- case 2:
- // S 0x2cc <Standby Position>.W
- // To announce Instancing queue creation if no maps available
- WBUFW(buf,0) = 0x02CC;
- WBUFW(buf,2) = flag;
- clif_send(buf,packet_len(0x02CC),&sd->bl,PARTY);
- break;
- case 3:
- case 4:
- // S 0x2cd <Instance Name>.61B <Instance Remaining Time>.L <Instance Noplayers close time>.L
- WBUFW(buf,0) = 0x02CD;
- memcpy(WBUFP(buf,2),instance[instance_id].name,61);
- if( type == 3 )
- {
- WBUFL(buf,63) = (uint32)instance[instance_id].progress_timeout;
- WBUFL(buf,67) = 0;
- }
- else
- {
- WBUFL(buf,63) = 0;
- WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout;
- }
- clif_send(buf,packet_len(0x02CD),&sd->bl,PARTY);
- break;
- case 5:
- // S 0x2ce <Message ID>.L
- // 0 = Notification (EnterLimitDate update?)
- // 1 = The Memorial Dungeon expired; it has been destroyed
- // 2 = The Memorial Dungeon's entry time limit expired; it has been destroyed
- // 3 = The Memorial Dungeon has been removed.
- // 4 = Create failure (removes the instance window)
- WBUFW(buf,0) = 0x02CE;
- WBUFL(buf,2) = flag;
- //WBUFL(buf,6) = EnterLimitDate;
- clif_send(buf,packet_len(0x02CE),&sd->bl,PARTY);
- break;
+ 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 <account id>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B
void clif_party_show_picker(struct map_session_data * sd, struct item * item_data)
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,251 @@
#include <stdarg.h>
#include <time.h>
+#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 || instance_id <= 0)
+ return 0;
+
+ // 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, short 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, short 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) {
+ 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 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) {
+ ShowDebug("Stopping idle timer for player.\n");
+ 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_sub(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));
+}
+
+// Separate function used for reloading
+void instance_addnpc(struct instance_data *im)
+{
+ int i;
+
+ 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);
+}
+
/*--------------------------------------
* name : instance name
* Return value could be
@@ -53,11 +277,17 @@
*--------------------------------------*/
int instance_create(int party_id, const char *name)
{
- int i;
- struct party_data* p;
+ short i;
+ int 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 +297,456 @@
// 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 )
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) );
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;
+
+ 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 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(short instance_id)
{
- int16 m = map_mapname2mapid(name);
- int i, im = -1;
- size_t num_cell, size;
+ int i, m;
+ int cnt_map = 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)
+ 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)
+ 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++;
+ }
+ }
- 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
+ instance_addnpc(im);
- instance[instance_id].map[instance[instance_id].num_map++] = im; // Attach to actual instance
- map_addmap2db(&map[im]);
- return im;
-}
-
-/*--------------------------------------
- * 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)
-{
- int i;
-
- if( !instance_is_valid(instance_id) )
- {
- return -1;
+ // 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;
+ }
}
- 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 cnt_map;
}
-/*--------------------------------------
- * m : source map
+
+/*==========================================
+ * Returns an instance map ID using a map name
+ * name : source map
* instance_id : where to search
- * result : mapid of map "m" in this instance
- *--------------------------------------*/
-int instance_mapid2imapid(int16 m, int instance_id)
+ * result : mapid of map "name" in this instance
+ *------------------------------------------*/
+int instance_mapname2mapid(const char *name, short 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;
+ struct instance_data *im;
+ int m = map_mapname2mapid(name);
+ char iname[12];
+ int i;
+
+ if(m < 0) {
+ ShowError("instance_mapname2mapid: map name %s does not exist.\n",name);
+ return m;
}
- if( !instance_is_valid(instance_id) )
- return -1;
+ ShowDebug("We attempt to enter instance map %s. Index=%d.\n",name,mapindex_name2id(name));
- return instance_map2imap(m, instance_id);
-}
+ if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA)
+ 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)
-{
- struct npc_data* nd = (struct npc_data*)bl;
- int16 m = va_arg(args, int); // Destination Map
+ im = &instance_data[instance_id];
+ if(im->state != INSTANCE_BUSY)
+ return m;
- npc_duplicate4instance(nd, m);
- return 1;
+ 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);
+ }
+ }
+
+ return m;
}
-/*--------------------------------------
- * Init all map on the instance. Npcs are created here
- *--------------------------------------*/
-void instance_init(int instance_id)
+/*==========================================
+ * Removes a instance, all its maps and npcs.
+ *------------------------------------------*/
+int instance_destroy(short instance_id)
{
- int i;
+ struct instance_data *im;
+ struct party_data *p;
+ int i, j, type = 0, count = 0;
+ unsigned int now = (unsigned int)time(NULL);
- if( !instance_is_valid(instance_id) )
- return; // nothing to do
+ if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA)
+ 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]);
+ im = &instance_data[instance_id];
- instance[instance_id].state = INSTANCE_BUSY;
- ShowInfo("[Instance] Initialized %s.\n", instance[instance_id].name);
-}
+ if(im->state == INSTANCE_FREE)
+ return 1;
-/*--------------------------------------
- * 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(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]));
- 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 < 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;
+ }
+ }
+ }
+ }
+ }
-/* 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(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;
- 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;
+ for(i = 0; i < MAX_MAP_PER_INSTANCE; i++)
+ count += map_delinstancemap(im->map[i].m);
}
- return 1;
+ 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;
+ }
+
+ 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;
+ }
+ }
+ }
+
+ 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, 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; i<columns; i++) {
+ if(strlen(str[i])) {
+ ShowDebug("Adding map name %s to type %d\n", str[i], atoi(str[0]));
+ safestrncpy(instance_db[type].mapname[k], str[i], MAP_NAME_LENGTH);
+ k++;
+ }
}
- else
- {
- instance[instance_id].progress_timeout = 0;
- instance[instance_id].progress_timer = INVALID_TIMER;
- }
- if( idle_timeout )
- {
- instance[instance_id].idle_timeoutval = idle_timeout;
- instance[instance_id].idle_timer = INVALID_TIMER;
- instance_check_idle(instance_id);
- }
- else
- {
- instance[instance_id].idle_timeoutval = 0;
- instance[instance_id].idle_timeout = 0;
- instance[instance_id].idle_timer = INVALID_TIMER;
- }
-
- if( instance[instance_id].idle_timer == INVALID_TIMER && instance[instance_id].progress_timer != INVALID_TIMER )
- clif_instance(instance_id, 3, 0);
+ return true;
}
-/*--------------------------------------
- * Checks if sd in on a instance and should be kicked from it
- *--------------------------------------*/
-void instance_check_kick(struct map_session_data *sd)
+/*==========================================
+ * Reloads the instance in runtime (reloadscript)
+ *------------------------------------------*/
+void do_reload_instance(void)
{
- int16 m = sd->bl.m;
+ struct instance_data *im;
+ struct instance_db *db;
+ struct s_mapiterator* iter;
+ struct map_session_data* pl_sd;
+ int i;
- 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);
+ for( i = 1; i < MAX_INSTANCE_DATA; i++ ) {
+ im = &instance_data[i];
+ if(!im->type)
+ 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;
+ // Reset player to instance beginning
+ iter = mapit_getallusers();
+ for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
+ if(pl_sd && map[pl_sd->bl.m].instance_id) {
+ struct party_data *p;
+ if(!(p = party_search(pl_sd->status.party_id)) || instance_enter(pl_sd,db->name)) // Something went wrong
+ ShowError("do_reload_instance: Error setting character at instance start: character_id=%d instance=%s.\n",pl_sd->status.char_id,db->name);
+ else // All good
+ clif_displaymessage(pl_sd->fd, msg_txt(pl_sd,515)); // Instance has been reloaded
+ }
+ mapit_free(iter);
+ }
}
}
+void do_init_instance(void)
+{
+ memset(&instance_db, 0, sizeof(instance_db));
+
+ 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,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 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 -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]);
+
+ 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 {
@@ -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 17368)
+++ 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 )// <map name>,<x>,<y>,<facing>
- {
+ } else {
+ if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ) { // <map name>,<x>,<y>,<facing>
ShowError("npc_parse_duplicate: Invalid placement format for duplicate in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
return end;// next line, try to continue
}
@@ -2601,8 +2597,7 @@
if( type == WARP && sscanf(w4, "%d,%d", &xs, &ys) == 2 );// <spanx>,<spany>
else if( type == SCRIPT && sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3);// <sprite id>,<triggerX>,<triggerY>
else if( type != WARP ) class_ = atoi(w4);// <sprite id>
- else
- {
+ else {
ShowError("npc_parse_duplicate: Invalid span format for duplicate warp in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
return end;// next line, try to continue
}
@@ -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;
+ ShowDebug("We created an instance NPC %s with instance %d.\n",nd->bl.id,map[m].instance_id);
+ 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 17368)
+++ 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 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)
@@ -4756,36 +4756,21 @@
*------------------------------------------*/
int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y, clr_type clrtype)
{
- struct party_data *p;
int16 m;
nullpo_ret(sd);
- if( !mapindex || !mapindex_id2name(mapindex) )
- {
+ if( !mapindex || !mapindex_id2name(mapindex) ) {
ShowDebug("pc_setpos: Passed mapindex(%d) is invalid!\n", mapindex);
return 1;
}
- if( pc_isdead(sd) )
- { //Revive dead people before warping them
+ if( pc_isdead(sd) ) { //Revive dead people before warping them
pc_setstand(sd);
pc_setrestartvalue(sd,1);
}
m = map_mapindex2mapid(mapindex);
- if( map[m].flag.src4instance && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- {
- // Request the mapid of this src map into the instance of the party
- int im = instance_map2imap(m, p->instance_id);
- if( im < 0 )
- ; // Player will enter the src map for instances
- else
- { // Changes destiny to the instance map, not the source map
- m = im;
- mapindex = map_id2index(m);
- }
- }
sd->state.changemap = (sd->mapindex != mapindex);
sd->state.warping = 1;
Index: src/map/script.c
===================================================================
--- src/map/script.c (revision 17368)
+++ 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);
@@ -2691,11 +2699,15 @@
}
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);
}
@@ -2739,12 +2751,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 +3681,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 +8826,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 +8857,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 +8917,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 +8943,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 +8998,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 +9036,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 +11543,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,58 +16443,80 @@
}
/*==========================================
- * Instancing Script Commands
+ * Instancing System
*------------------------------------------*/
+//Returns an Instance ID
+//Checks NPC first, then if player is attached we check party
+static int buildin_instancegetid_sub(struct script_state* st)
+{
+ short instance_id = 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;
+ }
+
+ return instance_id;
+}
+
+/*==========================================
+ * Creates the instance
+ * Returns Instance ID if created successfully
+ *------------------------------------------*/
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;
}
script_pushint(st, res);
return 0;
}
+/*==========================================
+ * Destroys an instance (unofficial)
+ * Officially instances are only destroyed by timeout
+ *
+ * instance_destroy {<instance_id>};
+ *
+ *------------------------------------------*/
BUILDIN_FUNC(instance_destroy)
{
- int instance_id;
- struct map_session_data *sd;
- struct party_data *p;
+ short instance_id;
- 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( script_hasdata(st,2) )
+ instance_id = script_getnum(st,2);
+ else
+ instance_id = buildin_instancegetid_sub(st);
- 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,189 +16525,51 @@
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;
-}
-
+/*==========================================
+ * Returns the name of a duplicated NPC
+ *
+ * instance_npcname <npc_name>{,<instance_id};
+ *
+ *------------------------------------------*/
BUILDIN_FUNC(instance_npcname)
{
const char *str;
- int instance_id = 0;
+ short instance_id = 0;
- struct map_session_data *sd;
- struct party_data *p;
struct npc_data *nd;
- str = script_getstr(st, 2);
- if( script_hasdata(st, 3) )
- instance_id = script_getnum(st, 3);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
+ 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 +16578,85 @@
return 0;
}
-BUILDIN_FUNC(has_instance)
+/*==========================================
+ * Returns the name of a duplicated map
+ *
+ * instance_mapname <map_name>{,<instance_id};
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(instance_mapname)
{
- struct map_session_data *sd;
- struct party_data *p;
const char *str;
int16 m;
- int instance_id = 0;
+ short instance_id = 0;
- str = script_getstr(st, 2);
- if( script_hasdata(st, 3) )
- instance_id = script_getnum(st, 3);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
+ 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 )
- {
+ if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_mapname2mapid(map[m].name, instance_id)) < 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");
+ 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 <map_name>,<x>,<y>{,<instance_id>};
+ *
+ *------------------------------------------*/
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 +16681,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 +16786,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 +17994,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 17368)
+++ 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 17368)
+++ 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;