瀏覽代碼

Allow creating instance without timer or data (#5112)

* Allow creating instance with no timer
* Added NoNpc to prevent copying NPCs from the source map
* Added NoMapFlag to prevent copying Mapflags from the source map
* Added instance_list script command to retrieve the instance IDs for the given map name/instance mode

Co-authored-by: Aleos <aleos89@users.noreply.github.com>
Co-authored-by: Atemo <atemo@users.noreply.github.com>
Co-authored-by: Lemongrass3110 <3517879+Lemongrass3110@users.noreply.github.com>
Sader Fawall 3 年之前
父節點
當前提交
ac7292c92d

+ 5 - 3
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

+ 5 - 3
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:

+ 5 - 3
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

+ 5 - 3
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

+ 17 - 0
doc/script_commands.txt

@@ -9481,6 +9481,23 @@ Examples:
 
 ---------------------------------------
 
+*instance_list(<"map name">{,<instance mode>});
+
+Creates the array '.@instance_list' with possible instance IDs for the given <map name> and optional <mode>.
+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(<variable>,<instance id>);
 
 Returns a reference to an instance variable (' prefix) of the specific instance ID.

+ 4 - 2
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.

+ 51 - 15
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<s_instance_data> 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<s_instance_db> db = instance_db.find(idata->id);
@@ -430,7 +456,6 @@ bool instance_stopidletimer(std::shared_ptr<s_instance_data> 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<s_instance_data> 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<unsigned int>(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<unsigned int>(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<s_instance_data> 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<s_instance_data> 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<s_instance_db> db = instance_db.find(idata->id);
 
-			if (db)
+			if (db && db->limit > 0)
 				idata->keep_limit = static_cast<unsigned int>(time(nullptr)) + db->limit;
 		}
 	}

+ 5 - 1
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<s_instance_map> 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<int16> maplist; ///< Maps in instance
@@ -96,7 +100,7 @@ struct s_instance_db {
 
 class InstanceDatabase : public TypesafeYamlDatabase<int32, s_instance_db> {
 public:
-	InstanceDatabase() : TypesafeYamlDatabase("INSTANCE_DB", 1) {
+	InstanceDatabase() : TypesafeYamlDatabase("INSTANCE_DB", 2, 1) {
 
 	}
 

+ 5 - 3
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<s_instance_data> 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]);
 	}

+ 1 - 1
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);

+ 37 - 0
src/map/script.cpp

@@ -21589,6 +21589,42 @@ BUILDIN_FUNC(instance_live_info)
 	}
 	return SCRIPT_CMD_SUCCESS;
 }
+/*==========================================
+ * instance_list(<"map name">{,<instance mode>});
+ * 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<e_instance_mode>(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<s_instance_data> 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
 	 **/