diff --git a/db/import-tmpl/instance_db.yml b/db/import-tmpl/instance_db.yml index 180561dd32..67798532a1 100644 --- a/db/import-tmpl/instance_db.yml +++ b/db/import-tmpl/instance_db.yml @@ -24,8 +24,10 @@ ########################################################################### # - Id Instance ID. # Name Instance Name. -# TimeLimit Total lifetime of instance in seconds. (Default: 3600) -# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# TimeLimit Total lifetime of instance in seconds. Use 0 to define infinite time. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. Use 0 to define infinite idle time. (Default: 300) +# NoNpc Prevent copying NPCs from the source map. (Default: false) +# NoMapFlag Prevent copying Mapflags from the source map. (Default: false) # Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true) # Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it. # Enter: Instance entrance coordinates. @@ -37,4 +39,4 @@ Header: Type: INSTANCE_DB - Version: 1 + Version: 2 diff --git a/db/instance_db.yml b/db/instance_db.yml index a243cf4b28..a9acf55170 100644 --- a/db/instance_db.yml +++ b/db/instance_db.yml @@ -24,8 +24,10 @@ ########################################################################### # - Id Instance ID. # Name Instance Name. -# TimeLimit Total lifetime of instance in seconds. (Default: 3600) -# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# TimeLimit Total lifetime of instance in seconds. Use 0 to define infinite time. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. Use 0 to define infinite idle time. (Default: 300) +# NoNpc Prevent copying NPCs from the source map. (Default: false) +# NoMapFlag Prevent copying Mapflags from the source map. (Default: false) # Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true) # Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it. # Enter: Instance entrance coordinates. @@ -37,7 +39,7 @@ Header: Type: INSTANCE_DB - Version: 1 + Version: 2 Footer: Imports: diff --git a/db/pre-re/instance_db.yml b/db/pre-re/instance_db.yml index f89c988e78..1b0a59f332 100644 --- a/db/pre-re/instance_db.yml +++ b/db/pre-re/instance_db.yml @@ -24,8 +24,10 @@ ########################################################################### # - Id Instance ID. # Name Instance Name. -# TimeLimit Total lifetime of instance in seconds. (Default: 3600) -# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# TimeLimit Total lifetime of instance in seconds. Use 0 to define infinite time. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. Use 0 to define infinite idle time. (Default: 300) +# NoNpc Prevent copying NPCs from the source map. (Default: false) +# NoMapFlag Prevent copying Mapflags from the source map. (Default: false) # Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true) # Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it. # Enter: Instance entrance coordinates. @@ -37,7 +39,7 @@ Header: Type: INSTANCE_DB - Version: 1 + Version: 2 Body: - Id: 1 diff --git a/db/re/instance_db.yml b/db/re/instance_db.yml index af9326c3e2..3d11642a43 100644 --- a/db/re/instance_db.yml +++ b/db/re/instance_db.yml @@ -24,8 +24,10 @@ ########################################################################### # - Id Instance ID. # Name Instance Name. -# TimeLimit Total lifetime of instance in seconds. (Default: 3600) -# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# TimeLimit Total lifetime of instance in seconds. Use 0 to define infinite time. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. Use 0 to define infinite idle time. (Default: 300) +# NoNpc Prevent copying NPCs from the source map. (Default: false) +# NoMapFlag Prevent copying Mapflags from the source map. (Default: false) # Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true) # Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it. # Enter: Instance entrance coordinates. @@ -37,7 +39,7 @@ Header: Type: INSTANCE_DB - Version: 1 + Version: 2 Body: - Id: 1 diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 5c19218c2e..ed50b6ce5e 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -9481,6 +9481,23 @@ Examples: --------------------------------------- +*instance_list(<"map name">{,}); + +Creates the array '.@instance_list' with possible instance IDs for the given and optional . +Return '.@instance_list' array size. + +Instance mode options: IM_NONE, IM_CHAR, IM_PARTY, IM_GUILD, or IM_CLAN +If the instance mode is not provided then it will return all the instance IDs for that map. + +Examples: + // This example assumes that there are several instances on the map of Prontera. + .@size = instance_list("prontera"); + for ( .@i = 0; .@i < .@size; ++.@i ) + mes instance_mapname("prontera", .@instance_list[.@i]); + //the output would be a list of all prontera copies that are active in the server. + +--------------------------------------- + *getinstancevar(,); Returns a reference to an instance variable (' prefix) of the specific instance ID. diff --git a/doc/yaml/db/instance_db.yml b/doc/yaml/db/instance_db.yml index f708f78280..cf16177da2 100644 --- a/doc/yaml/db/instance_db.yml +++ b/doc/yaml/db/instance_db.yml @@ -7,8 +7,10 @@ ########################################################################### # - Id Instance ID. # Name Instance Name. -# TimeLimit Total lifetime of instance in seconds. (Default: 3600) -# IdleTimeOut Time before an idle instance is destroyed in seconds. (Default: 300) +# TimeLimit Total lifetime of instance in seconds. Use 0 to define infinite time. (Default: 3600) +# IdleTimeOut Time before an idle instance is destroyed in seconds. Use 0 to define infinite idle time. (Default: 300) +# NoNpc Prevent copying NPCs from the source map. (Default: false) +# NoMapFlag Prevent copying Mapflags from the source map. (Default: false) # Destroyable Toggles the ability to destroy the instance using instance 'Destroy' button. (Default: true) # Note: the button is displayed based on parties. For any mode, it requires the party leader to be the instance owner to destroy it. # Enter: Instance entrance coordinates. diff --git a/src/map/instance.cpp b/src/map/instance.cpp index ad9b74e2db..5e81cd6ea8 100644 --- a/src/map/instance.cpp +++ b/src/map/instance.cpp @@ -112,6 +112,32 @@ uint64 InstanceDatabase::parseBodyNode(const YAML::Node &node) { instance->timeout = 300; } + if (this->nodeExists(node, "NoNpc")) { + bool nonpc; + + if (!this->asBool(node, "NoNpc", nonpc)) + return 0; + + instance->nonpc = nonpc; + } + else { + if (!exists) + instance->nonpc = false; + } + + if (this->nodeExists(node, "NoMapFlag")) { + bool nomapflag; + + if (!this->asBool(node, "NoMapFlag", nomapflag)) + return 0; + + instance->nomapflag = nomapflag; + } + else { + if (!exists) + instance->nomapflag = false; + } + if (this->nodeExists(node, "Destroyable")) { bool destroy; @@ -332,7 +358,7 @@ static TIMER_FUNC(instance_subscription_timer){ bool instance_startkeeptimer(std::shared_ptr idata, int instance_id) { // No timer - if (!idata || idata->keep_timer != INVALID_TIMER) + if (!idata || idata->keep_timer != INVALID_TIMER || idata->keep_limit == 0) return false; std::shared_ptr db = instance_db.find(idata->id); @@ -430,7 +456,6 @@ bool instance_stopidletimer(std::shared_ptr idata, int instance return false; // Delete the timer - Party has returned or instance is destroyed - idata->idle_limit = 0; delete_timer(idata->idle_timer, instance_delete_timer); idata->idle_timer = INVALID_TIMER; @@ -439,19 +464,19 @@ bool instance_stopidletimer(std::shared_ptr idata, int instance break; case IM_CHAR: if (map_charid2sd(idata->owner_id)) // Notify the player - clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); + clif_instance_changestatus(instance_id, IN_NOTIFY, 0); break; case IM_PARTY: if (party_search(idata->owner_id)) // Notify the party - clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); + clif_instance_changestatus(instance_id, IN_NOTIFY, 0); break; case IM_GUILD: if (guild_search(idata->owner_id)) // Notify the guild - clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); + clif_instance_changestatus(instance_id, IN_NOTIFY, 0); break; case IM_CLAN: if (clan_search(idata->owner_id)) // Notify the clan - clif_instance_changestatus(instance_id, IN_NOTIFY, idata->idle_limit); + clif_instance_changestatus(instance_id, IN_NOTIFY, 0); break; default: return false; @@ -651,13 +676,22 @@ int instance_addmap(int instance_id) { // Set to busy, update timers idata->state = INSTANCE_BUSY; - idata->idle_limit = static_cast(time(nullptr)) + db->timeout; - idata->idle_timer = add_timer(gettick() + db->timeout * 1000, instance_delete_timer, instance_id, 0); + if (db->timeout > 0) { + idata->idle_limit = static_cast(time(nullptr)) + db->timeout; + idata->idle_timer = add_timer(gettick() + db->timeout * 1000, instance_delete_timer, instance_id, 0); + } + + if (db->limit > 0) { + //This will allow the instance to get a time in 'instance_startkeeptimer' + idata->keep_limit = 1; + } + idata->nomapflag = db->nomapflag; + idata->nonpc = db->nonpc; int16 m; // Add initial map - if ((m = map_addinstancemap(db->enter.map, instance_id)) < 0) { + if ((m = map_addinstancemap(db->enter.map, instance_id, db->nomapflag)) < 0) { ShowError("instance_addmap: Failed to create initial map for instance '%s' (%d).\n", db->name.c_str(), instance_id); return 0; } @@ -670,7 +704,7 @@ int instance_addmap(int instance_id) { // Add extra maps (if any) for (const auto &it : db->maplist) { - if ((m = map_addinstancemap(it, instance_id)) < 0) { // An error occured adding a map + if ((m = map_addinstancemap(it, instance_id, db->nomapflag)) < 0) { // An error occured adding a map ShowError("instance_addmap: No maps added to instance '%s' (%d).\n", db->name.c_str(), instance_id); return 0; } else { @@ -681,7 +715,8 @@ int instance_addmap(int instance_id) { } // Create NPCs on all maps - instance_addnpc(idata); + if(!db->nonpc) + instance_addnpc(idata); switch(idata->mode) { case IM_NONE: @@ -1098,7 +1133,7 @@ bool instance_addusers(int instance_id) { std::shared_ptr idata = util::umap_find(instances, instance_id); - if(!idata || idata->state != INSTANCE_BUSY) + if(!idata || idata->state != INSTANCE_BUSY || idata->idle_limit == 0) return false; // Stop the idle timer if we had one @@ -1119,7 +1154,7 @@ bool instance_delusers(int instance_id) { std::shared_ptr idata = util::umap_find(instances, instance_id); - if(!idata || idata->state != INSTANCE_BUSY) + if(!idata || idata->state != INSTANCE_BUSY || idata->idle_limit == 0) return false; int users = 0; @@ -1148,12 +1183,13 @@ void do_reload_instance(void) continue; else { // First we load the NPCs again - instance_addnpc(idata); + if(!idata->nonpc) + instance_addnpc(idata); // Create new keep timer std::shared_ptr db = instance_db.find(idata->id); - if (db) + if (db && db->limit > 0) idata->keep_limit = static_cast(time(nullptr)) + db->limit; } } diff --git a/src/map/instance.hpp b/src/map/instance.hpp index 1d37372331..766795b309 100644 --- a/src/map/instance.hpp +++ b/src/map/instance.hpp @@ -67,6 +67,8 @@ struct s_instance_data { int keep_timer; ///< Life time ID unsigned int idle_limit; ///< Idle time of instance int idle_timer; ///< Idle timer ID + bool nonpc; + bool nomapflag; struct reg_db regs; ///< Instance variables for scripts std::vector map; ///< Array of maps in instance @@ -89,6 +91,8 @@ struct s_instance_db { std::string name; ///< Instance name uint32 limit, ///< Duration limit timeout; ///< Timeout limit + bool nonpc; + bool nomapflag; bool destroyable; ///< Destroyable flag struct point enter; ///< Instance entry point std::vector maplist; ///< Maps in instance @@ -96,7 +100,7 @@ struct s_instance_db { class InstanceDatabase : public TypesafeYamlDatabase { public: - InstanceDatabase() : TypesafeYamlDatabase("INSTANCE_DB", 1) { + InstanceDatabase() : TypesafeYamlDatabase("INSTANCE_DB", 2, 1) { } diff --git a/src/map/map.cpp b/src/map/map.cpp index 6fdc226f1e..139dda02b7 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -2705,7 +2705,7 @@ bool map_addnpc(int16 m,struct npc_data *nd) /*========================================== * Add an instance map *------------------------------------------*/ -int map_addinstancemap(int src_m, int instance_id) +int map_addinstancemap(int src_m, int instance_id, bool no_mapflag) { if(src_m < 0) return -1; @@ -2771,7 +2771,8 @@ int map_addinstancemap(int src_m, int instance_id) dst_map->channel = nullptr; dst_map->mob_delete_timer = INVALID_TIMER; - map_data_copy(dst_map, src_map); + if(!no_mapflag) + map_data_copy(dst_map, src_map); ShowInfo("[Instance] Created map '%s' (%d) from '%s' (%d).\n", dst_map->name, dst_map->m, name, src_map->m); @@ -3707,7 +3708,8 @@ void map_data_copyall (void) { return; for (int i = instance_start; i < map_num; i++) { struct map_data *mapdata = &map[i]; - if (!mapdata || mapdata->name[0] == '\0' || !mapdata->instance_src_map) + std::shared_ptr idata = util::umap_find(instances, mapdata->instance_id); + if (!mapdata || mapdata->name[0] == '\0' || !mapdata->instance_src_map || (idata && idata->nomapflag)) continue; map_data_copy(mapdata, &map[mapdata->instance_src_map]); } diff --git a/src/map/map.hpp b/src/map/map.hpp index c1d20bcd49..22429b0110 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -1085,7 +1085,7 @@ void map_clearflooritem(struct block_list* bl); int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect = false); // instances -int map_addinstancemap(int src_m, int instance_id); +int map_addinstancemap(int src_m, int instance_id, bool no_mapflag); int map_delinstancemap(int m); void map_data_copyall(void); void map_data_copy(struct map_data *dst_map, struct map_data *src_map); diff --git a/src/map/script.cpp b/src/map/script.cpp index 703abfed4b..92d8b667b7 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -21589,6 +21589,42 @@ BUILDIN_FUNC(instance_live_info) } return SCRIPT_CMD_SUCCESS; } +/*========================================== + * instance_list(<"map name">{,}); + * set '.@instance_list' to a list of the live instance ids for the map with the mode. + * return the array size of '.@instance_list' + *------------------------------------------*/ +BUILDIN_FUNC(instance_list) +{ + int src_id = map_mapname2mapid(script_getstr(st, 2)); + if (src_id == 0) { + ShowError("buildin_instance_list: map '%s' doesn't exist\n", script_getstr(st, 2)); + return SCRIPT_CMD_FAILURE; + } + + e_instance_mode mode = IM_MAX; + if (script_hasdata(st, 3)) { + mode = static_cast(script_getnum(st, 3)); + if (mode < IM_NONE || mode >= IM_MAX) { + ShowError("buildin_instance_list: Unknown instance mode %d for '%s'\n", mode, script_getstr(st, 3)); + return SCRIPT_CMD_FAILURE; + } + } + + int j = 0; + for (int i = instance_start; i < map_num; i++) { + struct map_data* mapdata = &map[i]; + if (mapdata->instance_src_map == src_id) { + std::shared_ptr idata = util::umap_find(instances, mapdata->instance_id); + if (idata && (mode == IM_MAX || idata->mode == mode)) { + setd_sub_num(st, nullptr, ".@instance_list", j, mapdata->instance_id, nullptr); + j++; + } + } + } + script_pushint(st, j); + return SCRIPT_CMD_SUCCESS; +} /*========================================== * Custom Fonts @@ -26242,6 +26278,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(instance_check_clan,"i???"), BUILDIN_DEF(instance_info,"si?"), BUILDIN_DEF(instance_live_info,"i?"), + BUILDIN_DEF(instance_list, "s?"), /** * 3rd-related **/