From ef7452b497d24f6f475d214f1244ab589b304c7c Mon Sep 17 00:00:00 2001 From: zephyrus Date: Sat, 20 Jun 2009 17:56:01 +0000 Subject: - Instancing System (Thanks to Sirius White who did most of the code, with some of my work to implement client side information, some optimizations and bugfixes). Also thanks to contributions from UEAUP team and Orcao. - Fixed a bug on areamobuseskill and changed it to make it as Aegis. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@13901 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/common/db.c | 15 ++ src/common/db.h | 3 + src/common/mapindex.c | 46 ++-- src/common/mapindex.h | 3 + src/common/mmo.h | 2 +- src/map/chrif.c | 5 +- src/map/clif.c | 106 +++++++++- src/map/clif.h | 5 + src/map/intif.c | 10 +- src/map/map.c | 573 +++++++++++++++++++++++++++++++++++++++++++++++++- src/map/map.h | 36 ++++ src/map/npc.c | 2 +- src/map/npc.h | 3 + src/map/party.c | 24 +++ src/map/party.h | 2 + src/map/pc.c | 18 +- src/map/script.c | 436 +++++++++++++++++++++++++++++++++++--- src/map/script.h | 3 + src/map/unit.c | 5 + 19 files changed, 1244 insertions(+), 53 deletions(-) (limited to 'src') 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); @@ -13042,6 +13047,105 @@ int clif_font_single(int fd, struct map_session_data *sd) return 1; } +/*========================================== + * 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 .63B .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 .W + // To announce Instancing queue creation if no maps available + WBUFW(buf,0) = 0x02CC; + WBUFW(buf,2) = flag; + clif_send(buf,packet_len(0x02CC),&sd->bl,PARTY); + break; + case 3: + case 4: + // S 0x2cd .61B .L .L + WBUFW(buf,0) = 0x02CD; + memcpy(WBUFP(buf,2),instance[instance_id].name,61); + if( type == 3 ) + { + WBUFL(buf,63) = (uint32)instance[instance_id].progress_timeout; + WBUFL(buf,67) = 0; + } + else + { + WBUFL(buf,63) = 0; + WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout; + } + clif_send(buf,packet_len(0x02CD),&sd->bl,PARTY); + break; + case 5: // R 02CE .L + // S 0x2ce .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)); +} + /*========================================== * パケットデバッグ *------------------------------------------*/ @@ -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 @@ -46,6 +46,17 @@ static void party_fill_member(struct party_member *member, struct map_session_da member->leader = 0; } +/*========================================== + * 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;idata[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)); @@ -13636,6 +13720,288 @@ BUILDIN_FUNC(bg_get_data) return 0; } +/*========================================== + * 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; ioid); + 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> 8 ) & 0x00FFFFFF) ^ crctab[(crc ^ *dat++) & 0xFF]; + + return (crc ^ 0xFFFFFFFF); +} + /*========================================== * Custom Fonts *------------------------------------------*/ @@ -13695,37 +14061,38 @@ static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap) return 0; } /*========================================== - * areamobuseskill "Map Name",,,,,,"Skill Name"/,,,,,; + * areamobuseskill "Map Name",,,,,"Skill Name"/,,,,,; *------------------------------------------*/ 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, ¢er, 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; -- cgit v1.2.3-70-g09d2