summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/db.c15
-rw-r--r--src/common/db.h3
-rw-r--r--src/common/mapindex.c46
-rw-r--r--src/common/mapindex.h3
-rw-r--r--src/common/mmo.h2
-rw-r--r--src/map/chrif.c5
-rw-r--r--src/map/clif.c106
-rw-r--r--src/map/clif.h5
-rw-r--r--src/map/intif.c10
-rw-r--r--src/map/map.c573
-rw-r--r--src/map/map.h36
-rw-r--r--src/map/npc.c2
-rw-r--r--src/map/npc.h3
-rw-r--r--src/map/party.c24
-rw-r--r--src/map/party.h2
-rw-r--r--src/map/pc.c18
-rw-r--r--src/map/script.c436
-rw-r--r--src/map/script.h3
-rw-r--r--src/map/unit.c5
19 files changed, 1244 insertions, 53 deletions
diff --git a/src/common/db.c b/src/common/db.c
index 6506fb889..537f26e6a 100644
--- a/src/common/db.c
+++ b/src/common/db.c
@@ -2562,6 +2562,21 @@ void linkdb_insert( struct linkdb_node** head, void *key, void* data)
node->data = data;
}
+void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... )
+{
+ va_list args;
+ struct linkdb_node *node;
+
+ if( head == NULL ) return;
+
+ va_start(args, func);
+ node = *head;
+ while ( node ) {
+ func( node->key, node->data, args );
+ node = node->next;
+ }
+}
+
void* linkdb_search( struct linkdb_node** head, void *key)
{
int n = 0;
diff --git a/src/common/db.h b/src/common/db.h
index 15cf28295..c1b224bcd 100644
--- a/src/common/db.h
+++ b/src/common/db.h
@@ -774,11 +774,14 @@ struct linkdb_node {
void *data;
};
+typedef void (*LinkDBFunc)(void* key, void* data, va_list args);
+
void linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない
void linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する
void* linkdb_search ( struct linkdb_node** head, void *key);
void* linkdb_erase ( struct linkdb_node** head, void *key);
void linkdb_final ( struct linkdb_node** head );
+void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
diff --git a/src/common/mapindex.c b/src/common/mapindex.c
index 72db1af6d..01843c936 100644
--- a/src/common/mapindex.c
+++ b/src/common/mapindex.c
@@ -47,17 +47,22 @@ const char* mapindex_getmapname(const char* string, char* output)
/// Result gets placed either into 'buf' or in a static local buffer.
const char* mapindex_getmapname_ext(const char* string, char* output)
{
- static char buf[MAP_NAME_LENGTH_EXT];
+ char buf[MAP_NAME_LENGTH_EXT];
char* dest = (output != NULL) ? output : buf;
-
- size_t len = safestrnlen(string, MAP_NAME_LENGTH);
+
+ size_t len;
+
+ strcpy(buf,string);
+ sscanf(string,"%*[^#]%*[#]%s",buf);
+
+ len = safestrnlen(buf, MAP_NAME_LENGTH);
+
if (len == MAP_NAME_LENGTH) {
- ShowWarning("(mapindex_normalize_name) Map name '%*s' is too long!\n", 2*MAP_NAME_LENGTH, string);
+ ShowWarning("(mapindex_normalize_name) Map name '%*s' is too long!", 2*MAP_NAME_LENGTH, buf);
len--;
}
-
- strncpy(dest, string, len+1);
-
+ strncpy(dest, buf, len+1);
+
if (len < 4 || stricmp(&dest[len-4], ".gat") != 0) {
strcpy(&dest[len], ".gat");
len += 4; // add .gat extension
@@ -70,10 +75,19 @@ const char* mapindex_getmapname_ext(const char* string, char* output)
/// Adds a map to the specified index
/// Returns 1 if successful, 0 oherwise
-static int mapindex_addmap(int index, const char* name)
+int mapindex_addmap(int index, const char* name)
{
char map_name[MAP_NAME_LENGTH];
+ if (index == -1){
+ for (index = 1; index < max_index; index++)
+ {
+ //if (strcmp(indexes[index].name,"#CLEARED#")==0)
+ if (indexes[index].name[0] == '\0')
+ break;
+ }
+ }
+
if (index < 0 || index >= MAX_MAPINDEX) {
ShowError("(mapindex_add) Map index (%d) for \"%s\" out of range (max is %d)\n", index, name, MAX_MAPINDEX);
return 0;
@@ -85,10 +99,11 @@ static int mapindex_addmap(int index, const char* name)
ShowError("(mapindex_add) Cannot add maps with no name.\n");
return 0;
}
- //if (strlen(map_name) >= MAP_NAME_LENGTH) {
- // ShowError("(mapindex_add) Map name %s is too long. Maps are limited to %d characters.\n", map_name, MAP_NAME_LENGTH);
- // return 0;
- //}
+
+ if (strlen(map_name) >= MAP_NAME_LENGTH) {
+ ShowError("(mapindex_add) Map name %s is too long. Maps are limited to %d characters.\n", map_name, MAP_NAME_LENGTH);
+ return 0;
+ }
if (mapindex_exists(index))
ShowWarning("(mapindex_add) Overriding index %d: map \"%s\" -> \"%s\"\n", index, indexes[index].name, map_name);
@@ -97,7 +112,7 @@ static int mapindex_addmap(int index, const char* name)
if (max_index <= index)
max_index = index+1;
- return 1;
+ return index;
}
unsigned short mapindex_name2id(const char* name)
@@ -170,6 +185,11 @@ void mapindex_init(void)
fclose(fp);
}
+int mapindex_removemap(int index){
+ indexes[index].name[0] = '\0';
+ return 0;
+}
+
void mapindex_final(void)
{
}
diff --git a/src/common/mapindex.h b/src/common/mapindex.h
index cd4c09d37..06131d0a4 100644
--- a/src/common/mapindex.h
+++ b/src/common/mapindex.h
@@ -47,4 +47,7 @@ const char* mapindex_id2name(unsigned short);
void mapindex_init(void);
void mapindex_final(void);
+int mapindex_addmap(int index, const char* name);
+int mapindex_removemap(int index);
+
#endif /* _MAPINDEX_H_ */
diff --git a/src/common/mmo.h b/src/common/mmo.h
index cb3f2b934..933893697 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -18,7 +18,7 @@
//The number is the max number of hotkeys to save (27 = 9 skills x 3 bars)
#define MAX_HOTKEYS 27
-#define MAX_MAP_PER_SERVER 1024
+#define MAX_MAP_PER_SERVER 1500 // Increased to allow creation of Instance Maps
#define MAX_INVENTORY 100
//Max number of characters per account. Note that changing this setting alone is not enough if the client is not hexed to support more characters as well.
#define MAX_CHARS 9
diff --git a/src/map/chrif.c b/src/map/chrif.c
index 71324f02a..aba1accee 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -313,9 +313,10 @@ int chrif_sendmap(int fd)
{
int i;
ShowStatus("Sending maps to char server...\n");
- WFIFOHEAD(fd, 4 + map_num * 4);
+ // Sending normal maps, not instances
+ WFIFOHEAD(fd, 4 + map_instance_start * 4);
WFIFOW(fd,0) = 0x2afa;
- for(i = 0; i < map_num; i++)
+ for(i = 0; i < map_instance_start; i++)
WFIFOW(fd,4+i*4) = map[i].index;
WFIFOW(fd,2) = 4 + i * 4;
WFIFOSET(fd,WFIFOW(fd,2));
diff --git a/src/map/clif.c b/src/map/clif.c
index 660beeb92..16cf4720c 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -7990,6 +7990,11 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
if (map[sd->bl.m].users++ == 0 && battle_config.dynamic_mobs) //Skotlex
map_spawnmobs(sd->bl.m);
+ if( map[sd->bl.m].instance_id )
+ {
+ instance[map[sd->bl.m].instance_id].users++;
+ map_instance_check_idle(map[sd->bl.m].instance_id);
+ }
sd->state.debug_remove_map = 0; // temporary state to track double remove_map's [FlavioJS]
map_addblock(&sd->bl);
@@ -13043,6 +13048,105 @@ int clif_font_single(int fd, struct map_session_data *sd)
}
/*==========================================
+ * Instancing Window
+ *------------------------------------------*/
+int clif_instance(int instance_id, int type, int flag)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ unsigned char buf[255];
+
+ if( (p = party_search(instance[instance_id].party_id)) == NULL || (sd = party_getavailablesd(p)) == NULL )
+ return 0;
+
+ switch( type )
+ {
+ case 1:
+ // S 0x2cb <Instance name>.63B <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,61);
+ 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: // R 02CE <message ID>.L
+ // S 0x2ce <Message ID>.L
+ // 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 = Just remove the window, maybe party/guild leave
+ WBUFW(buf,0) = 0x02CE;
+ WBUFL(buf,2) = flag;
+ clif_send(buf,packet_len(0x02CE),&sd->bl,PARTY);
+ break;
+ }
+ return 0;
+}
+
+void clif_instance_join(int fd, int instance_id)
+{
+ if( instance[instance_id].idle_timer != INVALID_TIMER )
+ {
+ WFIFOHEAD(fd,packet_len(0x02CD));
+ WFIFOW(fd,0) = 0x02CD;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOL(fd,63) = 0;
+ WFIFOL(fd,67) = (uint32)instance[instance_id].idle_timeout;
+ WFIFOSET(fd,packet_len(0x02CD));
+ }
+ else if( instance[instance_id].progress_timer != INVALID_TIMER )
+ {
+ WFIFOHEAD(fd,packet_len(0x02CD));
+ WFIFOW(fd,0) = 0x02CD;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOL(fd,63) = (uint32)instance[instance_id].progress_timeout;;
+ WFIFOL(fd,67) = 0;
+ WFIFOSET(fd,packet_len(0x02CD));
+ }
+ else
+ {
+ WFIFOHEAD(fd,packet_len(0x02CB));
+ WFIFOW(fd,0) = 0x02CB;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOW(fd,63) = 0;
+ WFIFOSET(fd,packet_len(0x02CB));
+ }
+}
+
+void clif_instance_leave(int fd)
+{
+ WFIFOHEAD(fd,packet_len(0x02CE));
+ WFIFOW(fd,0) = 0x02ce;
+ WFIFOL(fd,2) = 4;
+ WFIFOSET(fd,packet_len(0x02CE));
+}
+
+/*==========================================
* パケットデバッグ
*------------------------------------------*/
void clif_parse_debug(int fd,struct map_session_data *sd)
@@ -13314,7 +13418,7 @@ static int packetdb_readdb(void)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,191, 0, 0, 0, 0, 0, 0,
//#0x02C0
- 0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 30, 0, 0, 0, 3, 0, 65, 4, 71, 10, 0,
0, 0, 0, 0, 0, 0, 6, -1, 10, 10, 3, 0, -1, 32, 6, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
diff --git a/src/map/clif.h b/src/map/clif.h
index f78bbb666..de39ece38 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -350,6 +350,11 @@ int clif_bg_updatescore_single(struct map_session_data *sd);
int clif_sendbgemblem_area(struct map_session_data *sd);
int 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);
+
// Custom Fonts
int clif_font_area(struct map_session_data *sd);
int clif_font_single(int fd, struct map_session_data *sd);
diff --git a/src/map/intif.c b/src/map/intif.c
index 1a8e5e75b..e6b5768b4 100644
--- a/src/map/intif.c
+++ b/src/map/intif.c
@@ -423,17 +423,25 @@ int intif_party_leave(int party_id,int account_id, int char_id)
// パーティ移動要求
int intif_party_changemap(struct map_session_data *sd,int online)
{
+ int m, mapindex;
+
if (CheckForCharServer())
return 0;
if(!sd)
return 0;
+ if( (m=map_mapindex2mapid(sd->mapindex)) >= 0 && map[m].instance_id )
+ mapindex = map[map[m].instance_map[0]].index;
+ else
+ mapindex = sd->mapindex;
+
+
WFIFOHEAD(inter_fd,19);
WFIFOW(inter_fd,0)=0x3025;
WFIFOL(inter_fd,2)=sd->status.party_id;
WFIFOL(inter_fd,6)=sd->status.account_id;
WFIFOL(inter_fd,10)=sd->status.char_id;
- WFIFOW(inter_fd,14)=sd->mapindex;
+ WFIFOW(inter_fd,14)=mapindex;
WFIFOB(inter_fd,16)=online;
WFIFOW(inter_fd,17)=sd->status.base_level;
WFIFOSET(inter_fd,19);
diff --git a/src/map/map.c b/src/map/map.c
index 65e22fe48..f5b980506 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -114,6 +114,8 @@ static int bl_list_count = 0;
struct map_data map[MAX_MAP_PER_SERVER];
int map_num = 0;
+int map_instance_start = 0; // To keep the last index + 1 of normal map inserted in the map[ARRAY]
+struct map_instance instance[MAX_INSTANCE];
int map_port=0;
@@ -1583,6 +1585,25 @@ int map_quit(struct map_session_data *sd)
if( sd->state.storage_flag == 1 ) sd->state.storage_flag = 0; // No need to Double Save Storage on Quit.
unit_remove_map_pc(sd,3);
+
+ if( map[sd->bl.m].instance_id )
+ { // Avoid map conflicts and warnings on next login
+ int m;
+ struct point *pt;
+ if( map[sd->bl.m].save.map )
+ pt = &map[sd->bl.m].save;
+ else
+ pt = &sd->status.save_point;
+
+ if( (m=map_mapindex2mapid(pt->map)) >= 0 )
+ {
+ sd->bl.m = m;
+ sd->bl.x = pt->x;
+ sd->bl.y = pt->y;
+ sd->mapindex = pt->map;
+ }
+ }
+
pc_makesavestatus(sd);
pc_clean_skilltree(sd);
chrif_save(sd,1);
@@ -2645,12 +2666,15 @@ int map_readfromcache(struct map_data *m, FILE *fp)
int map_addmap(char* mapname)
{
- if (strcmpi(mapname,"clear")==0) {
- map_num=0;
+ if( strcmpi(mapname,"clear")==0 )
+ {
+ map_num = 0;
+ map_instance_start = 0;
return 0;
}
- if (map_num >= MAX_MAP_PER_SERVER - 1) {
+ if( map_num >= MAX_MAP_PER_SERVER - 1 )
+ {
ShowError("Could not add map '"CL_WHITE"%s"CL_RESET"', the limit of maps has been reached.\n",mapname);
return 1;
}
@@ -2841,6 +2865,7 @@ int map_readallmaps (void)
// finished map loading
ShowInfo("Successfully loaded '"CL_WHITE"%d"CL_RESET"' maps."CL_CLL"\n",map_num);
+ map_instance_start = map_num; // Next Map Index will be instances
if (maps_removed)
ShowNotice("Maps removed: '"CL_WHITE"%d"CL_RESET"'\n",maps_removed);
@@ -2848,6 +2873,540 @@ int map_readallmaps (void)
return 0;
}
+/*======================================
+ * Instancing [by Sirius White]
+ *--------------------------------------*/
+
+/*--------------------------------------
+ * map_instance_map2imap
+ * m : source map of this instance
+ * sd : source party/guild of this instance
+ * type : result (0 = map id | 1 = instance id)
+ *--------------------------------------*/
+int map_instance_map2imap(int m, struct map_session_data *sd, int type)
+{
+ int i;
+ struct party_data *p;
+ if( !sd->status.party_id || (p = party_search(sd->status.party_id)) == NULL || !p->instance_id )
+ return -1;
+
+ for( i = 0; i < instance[p->instance_id].num_map; i++ )
+ {
+ if( instance[p->instance_id].map[i] && map[instance[p->instance_id].map[i]].instance_map[0] == m )
+ {
+ if( type == 0 )
+ return instance[p->instance_id].map[i];
+ else
+ return p->instance_id;
+ }
+ }
+ return -1;
+}
+
+/*--------------------------------------
+ * map_instance_mapid2imapid
+ * m : source map
+ * instance_id : where to search
+ * result : mapid of map "m" in this instance
+ *--------------------------------------*/
+int map_instance_mapid2imapid(int m, int instance_id)
+{
+ int i, max;
+ if( map[m].instance_map[0] == 0 )
+ return m; // not instances found for this map
+ else if( map[m].instance_id )
+ { // This map is a instance
+ ShowError("map_instance_mapid2imapid: already instanced (%d / %d)\n", m, instance_id);
+ return -1;
+ }
+
+ if( instance_id <= 0 )
+ return -1;
+
+ max = instance[instance_id].num_map;
+
+ for( i = 0; i < max; i++ )
+ if( map[instance[instance_id].map[i]].instance_map[0] == m )
+ return instance[instance_id].map[i];
+
+ return -1;
+}
+
+/*--------------------------------------
+ * map_instance_create
+ * type : 0 = Party | 1 = Guild
+ * group_id : party_id / guild_id
+ * name_id : ...
+ * name : instance name
+ * Return value could be
+ * -4 = already exists | -3 = no free instances | -2 = missing parameter | -1 = invalid type
+ * On success return instance_id
+ *--------------------------------------*/
+int map_instance_create(int party_id, int name_id, const char *name)
+{
+ int i;
+ struct party_data *p = NULL;
+
+ if( !party_id || !name_id || !name )
+ {
+ ShowError("map_instance_create: missing parameter.\n");
+ return -2;
+ }
+
+ p = party_search(party_id);
+ if( !p || p->instance_id )
+ return -4; // Party already instancing
+
+ // Searching a Free Instance
+ // 0 is ignored as this mean "no instance" on maps
+ ARR_FIND(1, MAX_INSTANCE, i, instance[i].party_id == 0);
+ if( i == MAX_INSTANCE )
+ {
+ ShowError("map_instance_create: no free instances, consider increasing MAX_INSTANCE.\n");
+ return -3;
+ }
+
+ instance[i].instance_id = i;
+ instance[i].name_id = name_id;
+ 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 = instance[i].progress_timeoutval = 0;
+ instance[i].users = 0;
+ instance[i].party_id = party_id;
+ instance[i].ivar = NULL;
+ instance[i].svar = NULL;
+
+ memcpy( 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;
+}
+
+/*--------------------------------------
+ * map_instance_npcname
+ * Conver "name" to "name_MEM000" format
+ *--------------------------------------*/
+char *map_instance_npcname(char *name, int instance_id)
+{
+ static char npc_name[NAME_LENGTH+1];
+ uint32 crc = crc32(name, strlen(name));
+
+ snprintf(npc_name, sizeof(npc_name), "MEM_%.8x_%u", crc, instance_id);
+ return &npc_name[0];
+}
+
+/*--------------------------------------
+ * map_instance_map
+ * Add a map to the instance using src map "name"
+ *--------------------------------------*/
+int map_instance_map(const char *name, int instance_id)
+{
+ int m = map_mapname2mapid(name), i, ii = -1, im = -1;
+ size_t num_cell, size;
+
+ if( m < 0 )
+ return -1; // source map not found
+
+ if( !instance[instance_id].name_id )
+ {
+ ShowError("map_instance_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("map_instance_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 is a Instance.
+ ShowError("map_instance_map: trying to instance already instanced map %s.\n", name);
+ return -4;
+ }
+
+ ARR_FIND(map_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("map_instance_map: no more free space to create maps on this server.\n");
+ return -5;
+ }
+ else im = map_num++; // Using next map index
+
+ // Grab instance map id
+ ARR_FIND(0, ARRAYLENGTH(map[m].instance_map), i, !map[m].instance_map[i]);
+ if( i >= ARRAYLENGTH(map[m].instance_map) )
+ {
+ ShowError("map_instance_map: limit of instances per map reach.\n");
+ return -2;
+ }
+
+ ii = i;
+
+ // Copy source map
+ memcpy( &map[im], &map[m], sizeof(struct map_data));
+
+ // Add map index
+ snprintf(map[im].name, MAP_NAME_LENGTH, "%.3d%s", ii, name);
+ map[im].index = mapindex_addmap(-1, map[im].name);
+ if( !map[im].index )
+ {
+ map[im].name[0] = '\0';
+ ShowError("map_instance_map: no more free map indexes.\n");
+ return -3; // No free map index
+ }
+
+ // Reallocate cells
+ num_cell = map[im].xs * map[im].ys;
+ CREATE( map[im].cell, struct mapcell, num_cell);
+ memcpy( map[im].cell, map[m].cell, num_cell * sizeof(struct mapcell));
+
+ 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);
+
+ memset(map[im].npc, 0x00, sizeof(map[i].npc));
+ map[im].npc_num = 0;
+
+ memset(map[im].moblist, 0x00, sizeof(map[im].moblist));
+ map[im].mob_delete_timer = INVALID_TIMER;
+
+ map[im].m = im;
+ map[im].instance_id = instance_id;
+ map[m].instance_map[ii] = im; // add the mapid of this instance to the source map
+ map[im].instance_map[0] = m; // uses index [0] to store source map for this instance
+
+ instance[instance_id].map[instance[instance_id].num_map++] = im; // Attach to actual instance
+ uidb_put(map_db, (unsigned int)map[im].index, &map[im]); // Put to map list
+
+ return im;
+}
+
+/*--------------------------------------
+ * map_instance_map_npcsub
+ * Used on Init instance. Duplicates each script on source map
+ *--------------------------------------*/
+int map_instance_map_npcsub(struct block_list* bl, va_list args)
+{
+ char *inst_name;
+ static char w1[50], w2[50], w3[50], w4[50];
+ const char* stat_buf = "- call from instancing subsystem -\n";
+ struct npc_data* nd = (struct npc_data*)bl;
+ int m = va_arg(args, int);
+
+ inst_name = map_instance_npcname(nd->exname, map[m].instance_id);
+ if( inst_name == NULL )
+ return 1;
+
+ if( nd->subtype == WARP )
+ { // Adjust destination, if instanced
+ struct npc_data *wnd;
+ int dm = map_mapindex2mapid(nd->u.warp.mapindex), im;
+
+ if( dm < 0 ) return 1;
+
+ im = map_instance_mapid2imapid(dm, map[m].instance_id);
+ if( im == -1 )
+ {
+ ShowError("map_instance_map_npcsub: warp (%s) leading to instanced map (%s), but instance map is not attached to current instance.\n", map[dm].name, nd->exname);
+ return 1;
+ }
+
+ CREATE(wnd, struct npc_data, 1);
+ wnd->bl.id = npc_get_new_npc_id();
+ map_addnpc(m, wnd);
+ wnd->bl.prev = wnd->bl.next = NULL;
+ wnd->bl.m = m;
+ wnd->bl.x = nd->bl.x;
+ wnd->bl.y = nd->bl.y;
+ safestrncpy(wnd->name, "", ARRAYLENGTH(wnd->name));
+ safestrncpy(wnd->exname, inst_name, ARRAYLENGTH(wnd->exname));
+ wnd->class_ = WARP_CLASS;
+ wnd->speed = 200;
+ wnd->u.warp.mapindex = map_id2index(im);
+ wnd->u.warp.x = nd->u.warp.x;
+ wnd->u.warp.y = nd->u.warp.y;
+ wnd->u.warp.xs = nd->u.warp.xs;
+ wnd->u.warp.ys = nd->u.warp.ys;
+ wnd->bl.type = BL_NPC;
+ wnd->subtype = WARP;
+ npc_setcells(wnd);
+ map_addblock(&wnd->bl);
+ status_set_viewdata(&wnd->bl, wnd->class_);
+ status_change_init(&wnd->bl);
+ unit_dataset(&wnd->bl);
+ clif_spawn(&wnd->bl);
+ strdb_put(npcname_db, wnd->exname, wnd);
+
+ return 1;
+ }
+
+ snprintf(w1, sizeof(w1), "%s,%d,%d,%d", map[m].name, nd->bl.x, nd->bl.y, nd->ud.dir);
+ snprintf(w2, sizeof(w2), "duplicate(%s)", nd->exname);
+ snprintf(w3, sizeof(w3), "%s::%s", nd->name, inst_name);
+ if( nd->u.scr.xs >= 0 && nd->u.scr.ys >= 0 )
+ snprintf(w4, sizeof(w4), "%d,%d,%d", nd->class_, nd->u.scr.xs, nd->u.scr.ys);
+ else
+ snprintf(w4, sizeof(w4), "%d", nd->class_);
+
+ npc_parse_duplicate(w1, w2, w3, w4, stat_buf, stat_buf, "INSTANCING");
+ return 1;
+}
+
+/*--------------------------------------
+ * map_instance_init
+ * Init all map on the instance. Npcs are created here
+ *--------------------------------------*/
+void map_instance_init(int instance_id)
+{
+ int i;
+ if( !instance_id )
+ return;
+
+ for( i = 0; i < instance[instance_id].num_map; i++ )
+ map_foreachinmap(map_instance_map_npcsub, map[instance[instance_id].map[i]].instance_map[0], BL_NPC, instance[instance_id].map[i]);
+
+ ShowInfo("[Instance] Initialized %s.\n", instance[instance_id].name);
+}
+
+/*--------------------------------------
+ * map_instance_del_load
+ * Used on instance deleting process.
+ * Warps all players on each instance map to its save points.
+ *--------------------------------------*/
+int map_instance_del_load(struct map_session_data* sd, va_list args)
+{
+ int m = va_arg(args,int);
+ if( !sd || sd->bl.m != m )
+ return 0;
+
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 0);
+ return 1;
+}
+
+/*--------------------------------------
+ * map_instance_del
+ * Removes a simple instance map
+ *--------------------------------------*/
+void map_instance_del(int m)
+{
+ int sm, i;
+ if( m <= 0 || !map[m].instance_id )
+ {
+ ShowError("Tried to remove non-existing instance map (%d)\n", m);
+ return;
+ }
+
+ sm = map[m].instance_map[0];
+ map_foreachpc(map_instance_del_load, m);
+ map_foreachinmap(cleanup_sub, m, BL_ALL);
+
+ if( map[m].mob_delete_timer != INVALID_TIMER )
+ delete_timer(map[m].mob_delete_timer, map_removemobs_timer);
+
+ mapindex_removemap( map[m].index );
+
+ // Free memory
+ aFree(map[m].cell);
+ aFree(map[m].block);
+ aFree(map[m].block_mob);
+
+ // Remove from instance
+ for( i = 0; i < instance[map[m].instance_id].num_map; i++ )
+ {
+ if( instance[map[m].instance_id].map[i] == m )
+ {
+ instance[map[m].instance_id].num_map--;
+ for( ; i < instance[map[m].instance_id].num_map; i++ )
+ instance[map[m].instance_id].map[i] = instance[map[m].instance_id].map[i+1];
+ 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);
+
+ uidb_remove(map_db, map[m].index); // Remove from mapindex db
+
+ map[m].m = map[m].index = 0;
+ map[m].name[0] = '\0';
+
+ ARR_FIND(0, ARRAYLENGTH(map[sm].instance_map), i, map[sm].instance_map[i] == m);
+ if( i < ARRAYLENGTH(map[sm].instance_map) )
+ {
+ memmove(&map[sm].instance_map[i], &map[sm].instance_map[i+1], sizeof(map[sm].instance_map) - (i+1)*sizeof(map[sm].instance_map[0]));
+ map[sm].instance_map[ARRAYLENGTH(map[sm].instance_map) - 1] = 0; // Because i am not sure if the final value is set to 0 by "memmove"
+ }
+ else
+ ShowError("Failed to remove instance from instance db.\n");
+
+ memset(&map[m], 0x00, sizeof(map[0]));
+}
+
+/*--------------------------------------
+ * map_instance_destroy_freesvar
+ * Used for instance variables. Clean each variable from memory.
+ *--------------------------------------*/
+void map_instance_destroy_freesvar(void *key, void *data, va_list args)
+{
+ if( data ) aFree(data);
+}
+
+/*--------------------------------------
+ * map_instance_destroy_timer
+ * Timer to destroy instance by process or idle
+ *--------------------------------------*/
+int map_instance_destroy_timer(int tid, unsigned int tick, int id, intptr data)
+{
+ map_instance_destroy(id);
+ return 0;
+}
+
+/*--------------------------------------
+ * map_instance_destroy
+ * Removes a instance, all its maps and npcs.
+ *--------------------------------------*/
+void map_instance_destroy(int instance_id)
+{
+ int last = 0, type;
+ time_t now = time(NULL);
+
+ if( !instance_id || !instance[instance_id].name_id )
+ return;
+
+ 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;
+
+ clif_instance(instance_id, 5, type); // Report users this instance has been destroyed
+
+ while( instance[instance_id].num_map && last != instance[instance_id].map[0] )
+ {
+ last = instance[instance_id].map[0];
+ map_instance_del( instance[instance_id].map[0] );
+ }
+
+ if( instance[instance_id].ivar )
+ linkdb_final( &instance[instance_id].ivar );
+
+ if( instance[instance_id].svar )
+ {
+ linkdb_foreach( &instance[instance_id].svar, map_instance_destroy_freesvar );
+ linkdb_final( &instance[instance_id].svar );
+ }
+
+ if( instance[instance_id].progress_timer != INVALID_TIMER )
+ delete_timer( instance[instance_id].progress_timer, map_instance_destroy_timer);
+ if( instance[instance_id].idle_timer != INVALID_TIMER )
+ delete_timer( instance[instance_id].idle_timer, map_instance_destroy_timer);
+
+ instance[instance_id].ivar = NULL;
+ instance[instance_id].svar = NULL;
+
+ ShowInfo("[Instance] Destroyed %s.\n", instance[instance_id].name);
+ memset( &instance[instance_id], 0x00, sizeof(instance[0]) );
+}
+
+/*--------------------------------------
+ * map_instance_check_idle
+ * Checks if there are users in the instance or not to start idle timer
+ *--------------------------------------*/
+void map_instance_check_idle(int instance_id)
+{
+ bool idle = true;
+ time_t now = time(NULL);
+
+ if( !instance_id || instance[instance_id].idle_timeoutval == 0 )
+ return;
+
+ if( instance[instance_id].users )
+ idle = false;
+
+ if( instance[instance_id].idle_timer != INVALID_TIMER && !idle )
+ {
+ delete_timer(instance[instance_id].idle_timer, map_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, map_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
+ }
+}
+
+/*--------------------------------------
+ * map_instance_set_timeout
+ * Set instance Timers
+ *--------------------------------------*/
+void map_instance_set_timeout(int instance_id, unsigned int progress_timeout, unsigned int idle_timeout)
+{
+ time_t now = time(0);
+
+ if( !instance_id )
+ return;
+
+ if( instance[instance_id].progress_timer != INVALID_TIMER )
+ delete_timer( instance[instance_id].progress_timer, map_instance_destroy_timer);
+ if( instance[instance_id].idle_timer != INVALID_TIMER )
+ delete_timer( instance[instance_id].idle_timer, map_instance_destroy_timer);
+
+ if( progress_timeout )
+ {
+ instance[instance_id].progress_timeoutval = progress_timeout;
+ instance[instance_id].progress_timeout = now + progress_timeout;
+ instance[instance_id].progress_timer = add_timer( gettick() + progress_timeout * 1000, map_instance_destroy_timer, instance_id, 0);
+ }
+ else
+ {
+ instance[instance_id].progress_timeoutval = 0;
+ 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;
+ map_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);
+}
+
+/*--------------------------------------
+ * map_instance_check_mapkick
+ * Checks if sd in on a instance and should be kicked from it
+ *--------------------------------------*/
+void map_instance_check_kick(struct map_session_data *sd)
+{
+ int m = sd->bl.m;
+
+ 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, 3);
+ else
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 3);
+ }
+}
+
////////////////////////////////////////////////////////////////////////
static int map_ip_set = 0;
static int char_ip_set = 0;
@@ -3264,7 +3823,10 @@ void do_final(void)
for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
map_quit(sd);
mapit_free(iter);
-
+
+ for( i = 0; i < MAX_INSTANCE; i++ )
+ if( instance[i].party_id ) map_instance_destroy(i);
+
id_db->foreach(id_db,cleanup_db_sub);
chrif_char_reset_offline();
chrif_flush_fifo();
@@ -3489,6 +4051,8 @@ int do_init(int argc, char *argv[])
map_sql_init();
#endif /* not TXT_ONLY */
+ memset(instance, 0x00, sizeof(instance));
+
mapindex_init();
if(enable_grf)
grfio_init(GRF_PATH_FILENAME);
@@ -3498,6 +4062,7 @@ int do_init(int argc, char *argv[])
add_timer_func_list(map_freeblock_timer, "map_freeblock_timer");
add_timer_func_list(map_clearflooritem_timer, "map_clearflooritem_timer");
add_timer_func_list(map_removemobs_timer, "map_removemobs_timer");
+ add_timer_func_list(map_instance_destroy_timer, "map_instance_destroy_timer");
add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000);
do_init_atcommand();
diff --git a/src/map/map.h b/src/map/map.h
index d07871951..ca40bdd98 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -45,6 +45,8 @@ struct item_data;
#define MAX_IGNORE_LIST 20 // official is 14
#define MAX_VENDING 12
#define MOBID_EMPERIUM 1288
+#define MAX_MAP_PER_INSTANCE 10
+#define MAX_INSTANCE 500
//The following system marks a different job ID system used by the map server,
//which makes a lot more sense than the normal one. [Skotlex]
@@ -475,6 +477,8 @@ struct map_data {
int jexp; // map experience multiplicator
int bexp; // map experience multiplicator
int nocommand; //Blocks @/# commands for non-gms. [Skotlex]
+ int instance_id;
+ int instance_map[1000];
};
/// Stores information about a remote map (for multi-mapserver setups).
@@ -487,6 +491,22 @@ struct map_data_other_server {
uint16 port;
};
+// Instancing
+struct map_instance {
+ char name[61];
+ unsigned int name_id;
+// int m;
+ unsigned int instance_id;
+ int party_id;
+ int map[MAX_MAP_PER_INSTANCE];
+ int num_map;
+ struct linkdb_node *ivar, *svar;
+ time_t progress_timeout, idle_timeout;
+ time_t progress_timeoutval, idle_timeoutval;
+ int progress_timer, idle_timer;
+ int users;
+};
+
int map_getcell(int,int,int,cell_chk);
int map_getcellp(struct map_data*,int,int,cell_chk);
void map_setcell(int m, int x, int y, cell_t cell, bool flag);
@@ -494,6 +514,7 @@ void map_setgatcell(int m, int x, int y, int gat);
extern struct map_data map[];
extern int map_num;
+extern int map_instance_start;
extern int autosave_interval;
extern int minsave_interval;
@@ -616,6 +637,21 @@ void map_spawnmobs(int); // [Wizputer]
void map_removemobs(int); // [Wizputer]
void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex]
+// Instancing
+int map_instance_map(const char *name, int instance_id);
+void map_instance_del(int m);
+int map_instance_create(int party_id, int name_id, const char *name);
+void map_instance_init(int instance_id);
+int map_instance_mapid2imapid(int m, int instance_id);
+int map_instance_map2imap(int m, struct map_session_data *sd, int type);
+void map_instance_destroy(int instance_id);
+void map_instance_check_idle(int instance_id);
+void map_instance_check_kick(struct map_session_data *sd);
+char *map_instance_npcname(char *name, int instance_id);
+void map_instance_set_timeout(int instance_id, unsigned int progress_timeout, unsigned int idle_timeout);
+
+extern struct map_instance instance[MAX_INSTANCE];
+
extern char *INTER_CONF_NAME;
extern char *LOG_CONF_NAME;
extern char *MAP_CONF_NAME;
diff --git a/src/map/npc.c b/src/map/npc.c
index 97e54a8fe..0d6c9a9a9 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -74,7 +74,7 @@ int npc_get_new_npc_id(void)
}
static DBMap* ev_db; // const char* event_name -> struct event_data*
-static DBMap* npcname_db; // const char* npc_name -> struct npc_data*
+DBMap* npcname_db; // const char* npc_name -> struct npc_data*
struct event_data {
struct npc_data *nd;
diff --git a/src/map/npc.h b/src/map/npc.h
index 411e7e026..56d2bad5c 100644
--- a/src/map/npc.h
+++ b/src/map/npc.h
@@ -145,8 +145,11 @@ int npc_reload(void);
void npc_read_event_script(void);
int npc_script_event(struct map_session_data* sd, enum npce_event type);
+const char* npc_parse_duplicate(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath);
+
int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int points);
extern struct npc_data* fake_nd;
+extern DBMap* npcname_db;
#endif /* _NPC_H_ */
diff --git a/src/map/party.c b/src/map/party.c
index 46ddf2952..a71344ed7 100644
--- a/src/map/party.c
+++ b/src/map/party.c
@@ -47,6 +47,17 @@ static void party_fill_member(struct party_member *member, struct map_session_da
}
/*==========================================
+ * Request an available sd of this party
+ *------------------------------------------*/
+struct map_session_data* party_getavailablesd(struct party_data *p)
+{
+ int i;
+ nullpo_retr(NULL, p);
+ ARR_FIND(0, MAX_PARTY, i, p->data[i].sd != NULL);
+ return( i < MAX_PARTY ) ? p->data[i].sd : NULL;
+}
+
+/*==========================================
* Retrieves and validates the sd pointer for this party member [Skotlex]
*------------------------------------------*/
@@ -385,7 +396,11 @@ void party_member_joined(struct map_session_data *sd)
}
ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == sd->status.account_id && p->party.member[i].char_id == sd->status.char_id );
if (i < MAX_PARTY)
+ {
p->data[i].sd = sd;
+ if( p->instance_id )
+ clif_instance_join(sd->fd,p->instance_id);
+ }
else
sd->status.party_id = 0; //He does not belongs to the party really?
}
@@ -450,6 +465,9 @@ int party_member_added(int party_id,int account_id,int char_id, int flag)
clif_party_xy(sd);
clif_charnameupdate(sd); //Update char name's display [Skotlex]
+ if( p->instance_id )
+ clif_instance_join(sd->fd, p->instance_id);
+
return 0;
}
@@ -521,7 +539,10 @@ int party_member_leaved(int party_id, int account_id, int char_id)
sd->status.party_id = 0;
clif_charnameupdate(sd); //Update name display [Skotlex]
//TODO: hp bars should be cleared too
+ if( p->instance_id )
+ map_instance_check_kick(sd);
}
+
return 0;
}
@@ -534,6 +555,9 @@ int party_broken(int party_id)
p = party_search(party_id);
if( p == NULL )
return 0;
+
+ if( p->instance_id )
+ map_instance_destroy( p->instance_id );
for(i=0;i<MAX_PARTY;i++){
if(p->data[i].sd!=NULL){
diff --git a/src/map/party.h b/src/map/party.h
index 9ad729c28..bdaccc7e1 100644
--- a/src/map/party.h
+++ b/src/map/party.h
@@ -22,6 +22,7 @@ struct party_data {
struct party party;
struct party_member_data data[MAX_PARTY];
uint8 itemc; //For item distribution, position of last picker in party
+ unsigned int instance_id;
struct {
unsigned monk : 1; //There's at least one monk in party?
unsigned sg : 1; //There's at least one Star Gladiator in party?
@@ -35,6 +36,7 @@ void do_init_party(void);
void do_final_party(void);
struct party_data* party_search(int party_id);
struct party_data* party_searchname(const char* str);
+struct map_session_data* party_getavailablesd(struct party_data *p);
int party_create(struct map_session_data *sd,char *name, int item, int item2);
void party_created(int account_id,int char_id,int fail,int party_id,char *name);
diff --git a/src/map/pc.c b/src/map/pc.c
index 21173b673..3c596ec13 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -3826,6 +3826,20 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
pc_setrestartvalue(sd,1);
}
+ m = map_mapindex2mapid(mapindex);
+ if( map[m].instance_map[0] && map[m].instance_id == 0 )
+ { // Source Instance Map
+ int im = map_instance_map2imap(m, sd, 0);
+ if( im <= 0 )
+ {
+ ShowError("pc_setpos: player %s trying to enter instance map '%s' without instanced copy.\n", sd->status.name, map[m].name);
+ return 2; // map not found
+ }
+
+ m = im;
+ mapindex = map_id2index(m);
+ }
+
sd->state.changemap = (sd->mapindex != mapindex);
if( sd->state.changemap )
{ // Misc map-changing settings
@@ -3862,8 +3876,8 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
sd->regen.state.gc = 0;
}
- m=map_mapindex2mapid(mapindex);
- if(m<0) {
+ if( m < 0 )
+ {
uint32 ip;
uint16 port;
//if can't find any map-servers, just abort setting position.
diff --git a/src/map/script.c b/src/map/script.c
index 465d6fe85..dd4022220 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -71,6 +71,8 @@
// - remove GETVALUE / SETVALUE
// - clean up the set_reg / set_val / setd_sub mess
+void crc32_init();
+
//
@@ -153,8 +155,8 @@
/// Composes the uid of a reference from the id and the index
#define reference_uid(id,idx) ( (int32)((((uint32)(id)) & 0x00ffffff) | (((uint32)(idx)) << 24)) )
-#define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.')
-#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' )
+#define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.' && (prefix) != '\'')
+#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' && (prefix) != '\'' )
#define is_string_variable(name) ( (name)[strlen(name) - 1] == '$' )
#define FETCH(n, t) \
@@ -277,6 +279,7 @@ typedef struct script_function {
extern script_function buildin_func[];
static struct linkdb_node* sleep_db;// int oid -> struct script_state*
+uint32 crctab[256];
/*==========================================
* ローカルプロトタイプ宣言 (必要な物のみ)
@@ -797,6 +800,8 @@ const char* skip_word(const char* p)
++p; break;
case '#':// account variable
p += ( p[1] == '#' ? 2 : 1 ); break;
+ case '\'':// instance variable
+ ++p; break;
case '.':// npc variable
p += ( p[1] == '@' ? 2 : 1 ); break;
case '$':// global variable
@@ -2203,6 +2208,14 @@ void get_val(struct script_state* st, struct script_data* data)
data->u.str = (char*)linkdb_search(n, (void*)reference_getuid(data));
}
break;
+ case '\'':
+ {
+ struct linkdb_node** n = NULL;
+ if( st->instance_id )
+ n = &instance[st->instance_id].svar;
+ data->u.str = (char*)linkdb_search(n, (void*)reference_getuid(data));
+ }
+ break;
default:
data->u.str = pc_readglobalreg_str(sd, name);
break;
@@ -2257,6 +2270,14 @@ void get_val(struct script_state* st, struct script_data* data)
data->u.num = (int)linkdb_search(n, (void*)reference_getuid(data));
}
break;
+ case '\'':
+ {
+ struct linkdb_node** n = NULL;
+ if( st->instance_id )
+ n = &instance[st->instance_id].ivar;
+ data->u.num = (int)linkdb_search(n, (void*)reference_getuid(data));
+ }
+ break;
default:
data->u.num = pc_readglobalreg(sd, name);
break;
@@ -2309,6 +2330,17 @@ static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* nam
if (str[0]) linkdb_insert(n, (void*)num, aStrdup(str));
}
return 1;
+ case '\'': {
+ char *p;
+ struct linkdb_node** n = NULL;
+ if( st->instance_id )
+ n = &instance[st->instance_id].svar;
+
+ p = (char*)linkdb_erase(n, (void*)num);
+ if (p) aFree(p);
+ if( str[0] ) linkdb_insert(n, (void*)num, aStrdup(str));
+ }
+ return 1;
default:
return pc_setglobalreg_str(sd, name, str);
}
@@ -2349,6 +2381,18 @@ static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* nam
linkdb_replace(n, (void*)num, (void*)val);
}
return 1;
+ case '\'':
+ {
+ struct linkdb_node** n = NULL;
+ if( st->instance_id )
+ n = &instance[st->instance_id].ivar;
+
+ if( val == 0 )
+ linkdb_erase(n, (void*)num);
+ else
+ linkdb_replace(n, (void*)num, (void*)val);
+ return 1;
+ }
default:
return pc_setglobalreg(sd, name, val);
}
@@ -3098,6 +3142,7 @@ void run_script_main(struct script_state *st)
struct script_state *bk_st = NULL;
int bk_npcid = 0;
struct script_stack *stack=st->stack;
+ struct npc_data *nd;
sd = map_id2sd(st->rid);
@@ -3110,6 +3155,10 @@ void run_script_main(struct script_state *st)
sd->npc_id = st->oid;
}
+ 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)
@@ -3398,6 +3447,7 @@ int do_final_script()
*------------------------------------------*/
int do_init_script()
{
+ crc32_init();
userfunc_db=strdb_alloc(DB_OPT_DUP_KEY,0);
scriptlabel_db=strdb_alloc((DBOptions)(DB_OPT_DUP_KEY|DB_OPT_ALLOW_NULL_DATA),50);
@@ -7361,7 +7411,7 @@ BUILDIN_FUNC(guildchangegm)
*------------------------------------------*/
BUILDIN_FUNC(monster)
{
- const char* map = script_getstr(st,2);
+ const char* mapn = script_getstr(st,2);
int x = script_getnum(st,3);
int y = script_getnum(st,4);
const char* str = script_getstr(st,5);
@@ -7385,10 +7435,21 @@ BUILDIN_FUNC(monster)
sd = map_id2sd(st->rid);
- if( sd && strcmp(map,"this") == 0 )
+ if( sd && strcmp(mapn,"this") == 0 )
m = sd->bl.m;
else
- m = map_mapname2mapid(map);
+ {
+ m = map_mapname2mapid(mapn);
+ if( map[m].instance_map[0] && map[m].instance_id == 0 && st->instance_id )
+ {
+ m = map_instance_mapid2imapid(m, st->instance_id);
+ if( m < 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);
return 0;
@@ -7434,7 +7495,7 @@ BUILDIN_FUNC(getmobdrops)
*------------------------------------------*/
BUILDIN_FUNC(areamonster)
{
- const char* map = script_getstr(st,2);
+ const char* mapn = script_getstr(st,2);
int x0 = script_getnum(st,3);
int y0 = script_getnum(st,4);
int x1 = script_getnum(st,5);
@@ -7455,11 +7516,22 @@ BUILDIN_FUNC(areamonster)
sd = map_id2sd(st->rid);
- if( sd && strcmp(map,"this") == 0 )
+ if( sd && strcmp(mapn,"this") == 0 )
m = sd->bl.m;
else
- m = map_mapname2mapid(map);
-
+ {
+ m = map_mapname2mapid(mapn);
+ if( map[m].instance_map[0] && map[m].instance_id == 0 && st->instance_id )
+ {
+ m = map_instance_mapid2imapid(m, st->instance_id);
+ if( m < 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);
return 0;
}
@@ -7513,6 +7585,9 @@ BUILDIN_FUNC(killmonster)
if( (m=map_mapname2mapid(mapname))<0 )
return 0;
+ if( map[m].instance_map[0] && map[m].instance_id == 0 && st->instance_id && (m=map_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);
@@ -7551,6 +7626,9 @@ BUILDIN_FUNC(killmonsterall)
if( (m=map_mapname2mapid(mapname))<0 )
return 0;
+ if( map[m].instance_map[0] && map[m].instance_id == 0 && st->instance_id && (m=map_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);
@@ -9807,6 +9885,12 @@ BUILDIN_FUNC(mobcount) // Added by RoVeRT
script_pushint(st,-1);
return 0;
}
+
+ if( map[m].instance_map[0] && map[m].instance_id == 0 && st->instance_id && (m=map_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));
@@ -13637,6 +13721,288 @@ BUILDIN_FUNC(bg_get_data)
}
/*==========================================
+ * Instancing Script Commands
+ *------------------------------------------*/
+
+BUILDIN_FUNC(instance_create)
+{
+ const char *name;
+ int party_id, name_id;
+ int res;
+
+ name = script_getstr(st, 2);
+ party_id = script_getnum(st, 3);
+ name_id = script_getnum(st, 4);
+
+ res = map_instance_create(party_id, name_id, name);
+
+ if( res == -4 ) // Already exists
+ {
+ script_pushint(st, -1);
+ return 0;
+ }
+ else if( res < 0 )
+ {
+ char *err;
+ switch(res)
+ {
+ case -3: err = "No free instances"; break;
+ case -2: err = "Missing parameter"; break;
+ case -1: err = "Invalid type"; break;
+ default: err = "Unknown"; break;
+ }
+ ShowError("buildin_instance_create: %s [%d].\n", err, res);
+ script_pushint(st, -2);
+ return 0;
+ }
+
+ script_pushint(st, res);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_destroy)
+{
+ int instance_id;
+
+ if( script_hasdata(st, 2) )
+ instance_id = script_getnum(st, 2);
+ else
+ instance_id = st->instance_id;
+
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ {
+ ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id);
+ return 0;
+ }
+
+ map_instance_destroy(instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_attachmap)
+{
+ const char *name;
+ int m;
+ int instance_id;
+
+ instance_id = script_getnum(st, 2);
+ name = script_getstr(st, 3);
+
+ m = map_instance_map(name, instance_id);
+
+ if( m < 0 )
+ {
+ 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)
+{
+ const char *name;
+ int m;
+
+ name = script_getstr(st, 2);
+
+ m = map_mapname2mapid(name);
+ if( m < 0 )
+ {
+ ShowError("buildin_instance_detachmap: Trying to detach invalid map %s\n", name);
+ return 0;
+ }
+ map_instance_del(m);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_attach)
+{
+ int instance_id;
+
+ instance_id = script_getnum(st, 2);
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ return 0;
+
+ st->instance_id = instance_id;
+ return 0;
+}
+
+BUILDIN_FUNC(instance_id)
+{
+ int type, instance_id;
+ struct map_session_data *sd = script_rid2sd(st);
+ struct party_data *p;
+
+ if( script_hasdata(st, 2) )
+ {
+ type = script_getnum(st, 2);
+ if( type == 0 )
+ instance_id = st->instance_id;
+ else if( type == 1 && sd && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL )
+ instance_id = p->instance_id;
+ else
+ instance_id = 0;
+ }
+ else
+ instance_id = st->instance_id;
+ script_pushint(st, instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_set_timeout)
+{
+ int progress_timeout, idle_timeout;
+ int instance_id;
+
+ progress_timeout = script_getnum(st, 2);
+ idle_timeout = script_getnum(st, 3);
+
+ if( script_hasdata(st, 4) )
+ instance_id = script_getnum(st, 4);
+ else
+ instance_id = st->instance_id;
+
+ if( instance_id > 0 )
+ map_instance_set_timeout(instance_id, progress_timeout, idle_timeout);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_init)
+{
+ int instance_id;
+
+ instance_id = script_getnum(st, 2);
+
+ map_instance_init(instance_id);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_announce)
+{
+ const char *str, *color=NULL;
+ int flag,instance_id,i;
+
+ instance_id=script_getnum(st,2);
+ str=script_getstr(st,3);
+ flag=script_getnum(st,4);
+ if (script_hasdata(st,5))
+ color=script_getstr(st,5);
+
+ if( instance_id == 0 )
+ instance_id = st->instance_id;
+
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ return 0;
+
+ for(i=0; i<instance[instance_id].num_map; i++)
+ map_foreachinmap(buildin_mapannounce_sub, instance[instance_id].map[i], BL_PC, str,strlen(str)+1,flag&0x10, color);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_npcname)
+{
+ const char *str;
+ struct npc_data *nd = map_id2nd(st->oid);
+ int instance_id;
+
+ str = script_getstr(st, 2);
+ if( script_hasdata(st, 3) )
+ instance_id = script_getnum(st, 3);
+ else
+ instance_id = st->instance_id;
+
+ script_pushconststr(st, map_instance_npcname((char*)str, instance_id));
+ return 0;
+}
+
+BUILDIN_FUNC(has_instance)
+{
+ TBL_PC* sd = script_rid2sd(st);
+ const char *str;
+ int m;
+
+ str = script_getstr(st, 2);
+
+ if( (m = map_mapname2mapid(str)) < 0 || (m = map_instance_map2imap(m,sd,0)) < 0 )
+ {
+ script_pushconststr(st, "");
+ return 0;
+ }
+
+ script_pushconststr(st, map[m].name);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_warpall)
+{
+ TBL_PC *pl_sd;
+ int m, i;
+ const char *mapn;
+ int x, y;
+ unsigned short mapindex;
+ struct party_data *p = NULL;
+
+ if( !st->instance_id )
+ return 0;
+
+ mapn = script_getstr(st, 2);
+ x = script_getnum(st, 3);
+ y = script_getnum(st, 4);
+
+ if( (m=map_mapname2mapid(mapn)) < 0 || (map[m].instance_map[0] && (m=map_instance_mapid2imapid(m,st->instance_id)) < 0) )
+ return 0;
+
+ if( !(p = party_search(instance[st->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,3);
+
+ return 0;
+}
+
+void crc32_init()
+{
+ uint32 crc, poly;
+ uint32 i, c;
+
+ poly = 0xEDB88320;
+
+ for( i = 0; i < 256; i++ )
+ {
+ crc = i;
+ for( c = 8; c; c-- )
+ {
+ if( crc & 1 )
+ crc = (crc >> 1) ^ poly;
+ else
+ crc >>= 1;
+ }
+
+ crctab[i] = crc;
+ }
+}
+
+uint32 crc32(char *dat, int len)
+{
+ uint32 crc, i;
+
+ crc = 0xFFFFFFFF;
+ for( i=0; i<len; i++ )
+ crc = ((crc >> 8 ) & 0x00FFFFFF) ^ crctab[(crc ^ *dat++) & 0xFF];
+
+ return (crc ^ 0xFFFFFFFF);
+}
+
+/*==========================================
* Custom Fonts
*------------------------------------------*/
BUILDIN_FUNC(setfont)
@@ -13695,37 +14061,38 @@ static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap)
return 0;
}
/*==========================================
- * areamobuseskill "Map Name",<x1>,<y1>,<x2>,<y2>,<Mob ID>,"Skill Name"/<Skill ID>,<Skill Lv>,<Cast Time>,<Cancelable>,<Emotion>,<Target Type>;
+ * areamobuseskill "Map Name",<x>,<y>,<range>,<Mob ID>,"Skill Name"/<Skill ID>,<Skill Lv>,<Cast Time>,<Cancelable>,<Emotion>,<Target Type>;
*------------------------------------------*/
BUILDIN_FUNC(areamobuseskill)
{
- int m,x1,y1,x2,y2,mobid,skillid,skilllv,casttime,emotion,target;
+ struct block_list center;
+ int m,range,mobid,skillid,skilllv,casttime,emotion,target;
bool 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;
}
- skillid = ( script_isstring(st,8) ? skill_name2id(script_getstr(st,8)) : script_getnum(st,8) );
+ if( map[m].instance_map[0] && map[m].instance_id == 0 && st->instance_id && (m=map_instance_mapid2imapid(m, st->instance_id)) < 0 )
+ return 0;
- skilllv = script_getnum(st,9);
- if( skilllv > battle_config.mob_max_skilllvl )
+ center.m = m;
+ center.x = script_getnum(st,3);
+ center.y = script_getnum(st,4);
+ range = script_getnum(st,5);
+ mobid = script_getnum(st,6);
+ skillid = ( script_isstring(st,7) ? skill_name2id(script_getstr(st,7)) : script_getnum(st,7) );
+ if( (skilllv = script_getnum(st,8)) > battle_config.mob_max_skilllvl )
skilllv = battle_config.mob_max_skilllvl;
- x1 = script_getnum(st,3);
- y1 = script_getnum(st,4);
- x2 = script_getnum(st,5);
- y2 = script_getnum(st,6);
-
- mobid = script_getnum(st,7);
- casttime = script_getnum(st,10);
- cancel = script_getnum(st,11);
- emotion = script_getnum(st,12);
- target = script_getnum(st,13);
-
- map_foreachinarea(buildin_mobuseskill_sub, m, x1, y1, x2, y2, BL_MOB, mobid,skillid,skilllv,casttime,cancel,emotion,target);
+ casttime = script_getnum(st,9);
+ cancel = script_getnum(st,10);
+ emotion = script_getnum(st,11);
+ target = script_getnum(st,12);
+
+ map_foreachinrange(buildin_mobuseskill_sub, &center, range, BL_MOB, mobid, skillid, skilllv, casttime, cancel, emotion, target);
return 0;
}
@@ -14088,7 +14455,7 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(mercenary_set_faith,"ii"),
BUILDIN_DEF(readbook,"ii"),
BUILDIN_DEF(setfont,"i"),
- BUILDIN_DEF(areamobuseskill,"siiiiiviiiii"),
+ BUILDIN_DEF(areamobuseskill,"siiiiviiiii"),
// WoE SE
BUILDIN_DEF(agitstart2,""),
BUILDIN_DEF(agitend2,""),
@@ -14106,5 +14473,18 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(bg_get_data,"ii"),
BUILDIN_DEF(bg_getareausers,"isiiii"),
BUILDIN_DEF(bg_updatescore,"sii"),
+ // Instancing
+ BUILDIN_DEF(instance_create,"sii?"),
+ BUILDIN_DEF(instance_destroy,"?"),
+ BUILDIN_DEF(instance_attachmap,"is"),
+ BUILDIN_DEF(instance_detachmap,"is"),
+ BUILDIN_DEF(instance_init,"i"),
+ BUILDIN_DEF(instance_announce,"isi*"),
+ BUILDIN_DEF(instance_attach,"i"),
+ BUILDIN_DEF(instance_npcname,"s?"),
+ BUILDIN_DEF(has_instance,"s"),
+ BUILDIN_DEF(instance_id,"?"),
+ BUILDIN_DEF(instance_warpall,"sii"),
+ BUILDIN_DEF(instance_set_timeout,"ii?"),
{NULL,NULL,NULL},
};
diff --git a/src/map/script.h b/src/map/script.h
index fa287fc50..904ddf740 100644
--- a/src/map/script.h
+++ b/src/map/script.h
@@ -117,6 +117,7 @@ struct script_state {
struct sleep_data {
int tick,timer,charid;
} sleep;
+ int instance_id;
};
struct script_reg {
@@ -165,4 +166,6 @@ int add_str(const char* p);
const char* get_str(int id);
int script_reload(void);
+uint32 crc32(char *dat, int len);
+
#endif /* _SCRIPT_H_ */
diff --git a/src/map/unit.c b/src/map/unit.c
index 3d15ba6c8..88304940b 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -1840,6 +1840,11 @@ int unit_remove_map_(struct block_list *bl, int clrtype, const char* file, int l
else
if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
map_removemobs(bl->m);
+ if( map[bl->m].instance_id )
+ {
+ instance[map[bl->m].instance_id].users--;
+ map_instance_check_idle(map[bl->m].instance_id);
+ }
sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS]
sd->debug_file = file;
sd->debug_line = line;