- 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://svn.code.sf.net/p/rathena/svn/trunk@13901 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
zephyrus 2009-06-20 17:56:01 +00:00
parent e447a8a92a
commit 33e1fbbd66
19 changed files with 1244 additions and 53 deletions

View File

@ -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;

View File

@ -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, ... );

View File

@ -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)
{
}

View File

@ -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_ */

View File

@ -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

View File

@ -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));

View File

@ -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 <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));
}
/*==========================================
* ƒpƒPƒbƒgƒfƒoƒbƒO
*------------------------------------------*/
@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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_ */

View File

@ -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;i<MAX_PARTY;i++){
if(p->data[i].sd!=NULL){

View File

@ -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);

View File

@ -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.

View File

@ -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; 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
*------------------------------------------*/
@ -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},
};

View File

@ -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_ */

View File

@ -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;