浏览代码

Questinfo correction (#5523)

* Corrected questinfo bubble not always being displayed
* Docs updated
* Changed qi_display to std::vector

Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>

Thanks to @lighta @aleos89 @Lemongrass3110 !
Atemo 4 年之前
父节点
当前提交
a33887b59f
共有 9 个文件被更改,包括 93 次插入93 次删除
  1. 1 1
      doc/script_commands.txt
  2. 14 17
      src/map/map.cpp
  3. 1 8
      src/map/map.hpp
  4. 2 0
      src/map/npc.cpp
  5. 14 0
      src/map/npc.hpp
  6. 39 52
      src/map/pc.cpp
  7. 7 2
      src/map/pc.hpp
  8. 14 8
      src/map/script.cpp
  9. 1 5
      src/map/unit.cpp

+ 1 - 1
doc/script_commands.txt

@@ -9260,7 +9260,7 @@ Examples:
 
 
 *questinfo <Icon>{,<Map Mark Color>{,"<condition>"}};
 *questinfo <Icon>{,<Map Mark Color>{,"<condition>"}};
 
 
-This command should only be used in an OnInit label.
+This command should only be used in OnInit/OnInstanceInit labels.
 Show an emotion on top of a NPC, and optionally, a colored mark in the mini-map like "viewpoint".
 Show an emotion on top of a NPC, and optionally, a colored mark in the mini-map like "viewpoint".
 When a user is doing some action, each NPC is checked for questinfo that has been set on the map.
 When a user is doing some action, each NPC is checked for questinfo that has been set on the map.
 If questinfo is present, it will check if the player fulfill the condition.
 If questinfo is present, it will check if the player fulfill the condition.

+ 14 - 17
src/map/map.cpp

@@ -3691,10 +3691,6 @@ void map_data_copy(struct map_data *dst_map, struct map_data *src_map) {
 	dst_map->skill_duration.insert(src_map->skill_duration.begin(), src_map->skill_duration.end());
 	dst_map->skill_duration.insert(src_map->skill_duration.begin(), src_map->skill_duration.end());
 
 
 	dst_map->zone = src_map->zone;
 	dst_map->zone = src_map->zone;
-
-	// Mimic questinfo
-	if (!src_map->qi_data.empty())
-		src_map->qi_data = dst_map->qi_data;
 }
 }
 
 
 /**
 /**
@@ -4343,29 +4339,27 @@ int log_sql_init(void)
 
 
 void map_remove_questinfo(int m, struct npc_data *nd) {
 void map_remove_questinfo(int m, struct npc_data *nd) {
 	struct map_data *mapdata = map_getmapdata(m);
 	struct map_data *mapdata = map_getmapdata(m);
-	struct s_questinfo *qi;
 
 
 	nullpo_retv(nd);
 	nullpo_retv(nd);
 	nullpo_retv(mapdata);
 	nullpo_retv(mapdata);
 
 
-	for (int i = 0; i < mapdata->qi_data.size(); i++) {
-		qi = &mapdata->qi_data[i];
-		if (qi && qi->nd == nd) {
-			script_free_code(qi->condition);
-			mapdata->qi_data.erase(mapdata->qi_data.begin() + i);
-		}
-	}
+	util::vector_erase_if_exists(mapdata->qi_npc, nd->bl.id);
+	nd->qi_data.clear();
 }
 }
 
 
 static void map_free_questinfo(struct map_data *mapdata) {
 static void map_free_questinfo(struct map_data *mapdata) {
 	nullpo_retv(mapdata);
 	nullpo_retv(mapdata);
 
 
-	for (const auto &it : mapdata->qi_data) {
-		if (it.condition)
-			script_free_code(it.condition);
+	for (const auto &it : mapdata->qi_npc) {
+		struct npc_data *nd = map_id2nd(it);
+
+		if (!nd || nd->qi_data.empty())
+			continue;
+
+		nd->qi_data.clear();
 	}
 	}
 
 
-	mapdata->qi_data.clear();
+	mapdata->qi_npc.clear();
 }
 }
 
 
 /**
 /**
@@ -4876,6 +4870,10 @@ void do_final(void){
 		map_quit(sd);
 		map_quit(sd);
 	mapit_free(iter);
 	mapit_free(iter);
 
 
+	for (int i = 0; i < map_num; i++) {
+		map_free_questinfo(map_getmapdata(i));
+	}
+
 	/* prepares npcs for a faster shutdown process */
 	/* prepares npcs for a faster shutdown process */
 	do_clear_npc();
 	do_clear_npc();
 
 
@@ -4939,7 +4937,6 @@ void do_final(void){
 			for (int j=0; j<MAX_MOB_LIST_PER_MAP; j++)
 			for (int j=0; j<MAX_MOB_LIST_PER_MAP; j++)
 				if (mapdata->moblist[j]) aFree(mapdata->moblist[j]);
 				if (mapdata->moblist[j]) aFree(mapdata->moblist[j]);
 		}
 		}
-		map_free_questinfo(mapdata);
 		mapdata->damage_adjust = {};
 		mapdata->damage_adjust = {};
 	}
 	}
 
 

+ 1 - 8
src/map/map.hpp

@@ -722,13 +722,6 @@ struct iwall_data {
 	bool shootable;
 	bool shootable;
 };
 };
 
 
-struct s_questinfo {
-	struct npc_data *nd;
-	e_questinfo_types icon;
-	e_questinfo_markcolor color;
-	struct script_code* condition;
-};
-
 struct map_data {
 struct map_data {
 	char name[MAP_NAME_LENGTH];
 	char name[MAP_NAME_LENGTH];
 	uint16 index; // The map index used by the mapindex* functions.
 	uint16 index; // The map index used by the mapindex* functions.
@@ -766,7 +759,7 @@ struct map_data {
 	struct Channel *channel;
 	struct Channel *channel;
 
 
 	/* ShowEvent Data Cache */
 	/* ShowEvent Data Cache */
-	std::vector<s_questinfo> qi_data;
+	std::vector<int> qi_npc;
 
 
 	/* speeds up clif_updatestatus processing by causing hpmeter to run only when someone with the permission can view it */
 	/* speeds up clif_updatestatus processing by causing hpmeter to run only when someone with the permission can view it */
 	unsigned short hpmeter_visible;
 	unsigned short hpmeter_visible;

+ 2 - 0
src/map/npc.cpp

@@ -2483,6 +2483,8 @@ int npc_unload(struct npc_data* nd, bool single) {
 		}
 		}
 	}
 	}
 
 
+	nd->qi_data.clear();
+
 	script_stop_sleeptimers(nd->bl.id);
 	script_stop_sleeptimers(nd->bl.id);
 	aFree(nd);
 	aFree(nd);
 
 

+ 14 - 0
src/map/npc.hpp

@@ -51,6 +51,18 @@ struct s_npc_buy_list {
 #pragma pack(pop)
 #pragma pack(pop)
 #endif // not NetBSD < 6 / Solaris
 #endif // not NetBSD < 6 / Solaris
 
 
+struct s_questinfo {
+	e_questinfo_types icon;
+	e_questinfo_markcolor color;
+	struct script_code* condition;
+
+	~s_questinfo(){
+		if( this->condition != nullptr ){
+			script_free_code( this->condition );
+		}
+	}
+};
+
 struct npc_data {
 struct npc_data {
 	struct block_list bl;
 	struct block_list bl;
 	struct unit_data ud; //Because they need to be able to move....
 	struct unit_data ud; //Because they need to be able to move....
@@ -112,6 +124,8 @@ struct npc_data {
 	struct sc_display_entry **sc_display;
 	struct sc_display_entry **sc_display;
 	unsigned char sc_display_count;
 	unsigned char sc_display_count;
 
 
+	std::vector<std::shared_ptr<s_questinfo>> qi_data;
+
 	struct {
 	struct {
 		t_tick timeout;
 		t_tick timeout;
 		unsigned long color;
 		unsigned long color;

+ 39 - 52
src/map/pc.cpp

@@ -1690,9 +1690,6 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
 	sd->vars_ok = false;
 	sd->vars_ok = false;
 	sd->vars_received = 0x0;
 	sd->vars_received = 0x0;
 
 
-	sd->qi_display = nullptr;
-	sd->qi_count = 0;
-
 	//warp player
 	//warp player
 	if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, CLR_OUTSIGHT)) != SETPOS_OK) {
 	if ((i=pc_setpos(sd,sd->status.last_point.map, sd->status.last_point.x, sd->status.last_point.y, CLR_OUTSIGHT)) != SETPOS_OK) {
 		ShowError ("Last_point_map %s - id %d not found (error code %d)\n", mapindex_id2name(sd->status.last_point.map), sd->status.last_point.map, i);
 		ShowError ("Last_point_map %s - id %d not found (error code %d)\n", mapindex_id2name(sd->status.last_point.map), sd->status.last_point.map, i);
@@ -13319,33 +13316,6 @@ void pc_validate_skill(struct map_session_data *sd) {
 	}
 	}
 }
 }
 
 
-/**
- * Toggle to remember if the questinfo is displayed yet or not.
- * @param qi_display Display flag
- * @param show If show is true and qi_display is 0, set qi_display to 1 and show the event bubble.
- *             If show is false and qi_display is 1, set qi_display to 0 and hide the event bubble.
- **/
-static void pc_show_questinfo_sub(struct map_session_data *sd, bool *qi_display, struct s_questinfo *qi, bool show) {
-	if (show) {
-		// Check if need to be displayed
-		if ((*qi_display) != 1) {
-			(*qi_display) = 1;
-			clif_quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color);
-		}
-	}
-	else {
-		// Check if need to be hide
-		if ((*qi_display) != 0) {
-			(*qi_display) = 0;
-#if PACKETVER >= 20120410
-			clif_quest_show_event(sd, &qi->nd->bl, QTYPE_NONE, QMARK_NONE);
-#else
-			clif_quest_show_event(sd, &qi->nd->bl, QTYPE_QUEST, QMARK_NONE);
-#endif
-		}
-	}
-}
-
 /**
 /**
  * Show available NPC Quest / Event Icon Check [Kisuka]
  * Show available NPC Quest / Event Icon Check [Kisuka]
  * @param sd Player
  * @param sd Player
@@ -13360,28 +13330,45 @@ void pc_show_questinfo(struct map_session_data *sd) {
 	struct map_data *mapdata = map_getmapdata(sd->bl.m);
 	struct map_data *mapdata = map_getmapdata(sd->bl.m);
 	nullpo_retv(mapdata);
 	nullpo_retv(mapdata);
 
 
-	if (mapdata->qi_data.empty())
+	if (mapdata->qi_npc.empty())
 		return;
 		return;
-	if (mapdata->qi_data.size() != sd->qi_count)
+	if (mapdata->qi_npc.size() != sd->qi_display.size())
 		return; // init was not called yet
 		return; // init was not called yet
 
 
-	struct s_questinfo *qi = nullptr;
-	bool show;
+	for (int i = 0; i < mapdata->qi_npc.size(); i++) {
+		struct npc_data *nd = map_id2nd(mapdata->qi_npc[i]);
+
+		if (!nd || nd->qi_data.empty())
+			continue;
 
 
-	for (int i = 0; i < mapdata->qi_data.size(); i++) {
-		qi = &mapdata->qi_data[i];
- 		if (!qi)
- 			continue;
+		bool show = false;
 
 
-		if (!qi->condition)
-			show = true;
-		else {
-			if (achievement_check_condition(qi->condition, sd))
+		for (auto &qi : nd->qi_data) {
+			if (!qi->condition || achievement_check_condition(qi->condition, sd)) {
 				show = true;
 				show = true;
-			else
-				show = false;
+				// Check if need to be displayed
+				if (!sd->qi_display[i].is_active || qi->icon != sd->qi_display[i].icon || qi->color != sd->qi_display[i].color) {
+					sd->qi_display[i].is_active = true;
+					sd->qi_display[i].icon = qi->icon;
+					sd->qi_display[i].color = qi->color;
+					clif_quest_show_event(sd, &nd->bl, qi->icon, qi->color);
+				}
+				break;
+			}
+		}
+		if (show == false) {
+			// Check if need to be hide
+			if (sd->qi_display[i].is_active) {
+				sd->qi_display[i].is_active = false;
+				sd->qi_display[i].icon = QTYPE_NONE;
+				sd->qi_display[i].color = QMARK_NONE;
+#if PACKETVER >= 20120410
+				clif_quest_show_event(sd, &nd->bl, QTYPE_NONE, QMARK_NONE);
+#else
+				clif_quest_show_event(sd, &nd->bl, QTYPE_QUEST, QMARK_NONE);
+#endif
+			}
 		}
 		}
-		pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, show);
 	}
 	}
 #endif
 #endif
 }
 }
@@ -13394,11 +13381,7 @@ void pc_show_questinfo_reinit(struct map_session_data *sd) {
 #if PACKETVER >= 20090218
 #if PACKETVER >= 20090218
 	nullpo_retv(sd);
 	nullpo_retv(sd);
 
 
-	if (sd->qi_display) {
-		aFree(sd->qi_display);
-		sd->qi_display = nullptr;
-	}
-	sd->qi_count = 0;
+	sd->qi_display.clear();
 
 
 	if (sd->bl.m < 0 || sd->bl.m >= MAX_MAPINDEX)
 	if (sd->bl.m < 0 || sd->bl.m >= MAX_MAPINDEX)
 		return;
 		return;
@@ -13406,10 +13389,14 @@ void pc_show_questinfo_reinit(struct map_session_data *sd) {
 	struct map_data *mapdata = map_getmapdata(sd->bl.m);
 	struct map_data *mapdata = map_getmapdata(sd->bl.m);
 	nullpo_retv(mapdata);
 	nullpo_retv(mapdata);
 
 
-	if (mapdata->qi_data.empty())
+	if (mapdata->qi_npc.empty())
 		return;
 		return;
 
 
-	CREATE(sd->qi_display, bool, (sd->qi_count = mapdata->qi_data.size()));
+	sd->qi_display.reserve( mapdata->qi_npc.size() );
+
+	for( int i = 0; i < mapdata->qi_npc.size(); i++ ){
+		sd->qi_display.push_back( s_qi_display() );
+	}
 #endif
 #endif
 }
 }
 
 

+ 7 - 2
src/map/pc.hpp

@@ -267,6 +267,12 @@ struct s_combos {
 	uint32 pos;
 	uint32 pos;
 };
 };
 
 
+struct s_qi_display {
+	bool is_active;
+	e_questinfo_types icon;
+	e_questinfo_markcolor color;
+};
+
 struct map_session_data {
 struct map_session_data {
 	struct block_list bl;
 	struct block_list bl;
 	struct unit_data ud;
 	struct unit_data ud;
@@ -678,8 +684,7 @@ struct map_session_data {
 	std::vector<int> cloaked_npc;
 	std::vector<int> cloaked_npc;
 
 
 	/* ShowEvent Data Cache flags from map */
 	/* ShowEvent Data Cache flags from map */
-	bool *qi_display;
-	int qi_count;
+	std::vector<s_qi_display> qi_display;
 
 
 	// temporary debug [flaviojs]
 	// temporary debug [flaviojs]
 	const char* debug_file;
 	const char* debug_file;

+ 14 - 8
src/map/script.cpp

@@ -19733,9 +19733,6 @@ BUILDIN_FUNC(questinfo)
 		return SCRIPT_CMD_FAILURE;
 		return SCRIPT_CMD_FAILURE;
 	}
 	}
 
 
-	struct s_questinfo qi;
-	struct script_code *script = nullptr;
-	int color = QMARK_NONE;
 	int icon = script_getnum(st, 2);
 	int icon = script_getnum(st, 2);
 
 
 #if PACKETVER >= 20120410
 #if PACKETVER >= 20120410
@@ -19772,6 +19769,8 @@ BUILDIN_FUNC(questinfo)
 		icon = icon + 1;
 		icon = icon + 1;
 #endif
 #endif
 
 
+	int color = QMARK_NONE;
+
 	if (script_hasdata(st, 3)) {
 	if (script_hasdata(st, 3)) {
 		color = script_getnum(st, 3);
 		color = script_getnum(st, 3);
 		if (color < QMARK_NONE || color >= QMARK_MAX) {
 		if (color < QMARK_NONE || color >= QMARK_MAX) {
@@ -19781,6 +19780,8 @@ BUILDIN_FUNC(questinfo)
 		}
 		}
 	}
 	}
 
 
+	struct script_code *script = nullptr;
+
 	if (script_hasdata(st, 4)) {
 	if (script_hasdata(st, 4)) {
 		const char *str = script_getstr(st, 4);
 		const char *str = script_getstr(st, 4);
 
 
@@ -19798,13 +19799,18 @@ BUILDIN_FUNC(questinfo)
 		}
 		}
 	}
 	}
 
 
-	qi.nd = nd;
-	qi.icon = static_cast<e_questinfo_types>(icon);
-	qi.color = static_cast<e_questinfo_markcolor>(color);
-	qi.condition = script;
+	std::shared_ptr<s_questinfo> qi = std::make_shared<s_questinfo>();
+
+	qi->icon = static_cast<e_questinfo_types>(icon);
+	qi->color = static_cast<e_questinfo_markcolor>(color);
+	qi->condition = script;
+
+	nd->qi_data.push_back(qi);
 
 
 	struct map_data *mapdata = map_getmapdata(nd->bl.m);
 	struct map_data *mapdata = map_getmapdata(nd->bl.m);
-	mapdata->qi_data.push_back(qi);
+
+	if (mapdata && !util::vector_exists(mapdata->qi_npc, nd->bl.id))
+		mapdata->qi_npc.push_back(nd->bl.id);
 
 
 	return SCRIPT_CMD_SUCCESS;
 	return SCRIPT_CMD_SUCCESS;
 }
 }

+ 1 - 5
src/map/unit.cpp

@@ -3397,11 +3397,7 @@ int unit_free(struct block_list *bl, clr_type clrtype)
 				sd->num_quests = sd->avail_quests = 0;
 				sd->num_quests = sd->avail_quests = 0;
 			}
 			}
 
 
-			if (sd->qi_display) {
-				aFree(sd->qi_display);
-				sd->qi_display = NULL;
-			}
-			sd->qi_count = 0;
+			sd->qi_display.clear();
 
 
 #if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO)
 #if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO)
 			sd->hatEffects.clear();
 			sd->hatEffects.clear();