Browse Source

Fixed quest information icons not displaying properly on NPC. (bugreport:8156, bugreport:8157) (Hercules 1ab0017)

aleos89 11 years ago
parent
commit
5e02af25af
7 changed files with 256 additions and 23 deletions
  1. 10 0
      db/const.txt
  2. 67 14
      doc/script_commands.txt
  3. 18 1
      src/map/clif.c
  4. 48 0
      src/map/map.c
  5. 16 0
      src/map/map.h
  6. 3 0
      src/map/npc.c
  7. 94 8
      src/map/script.c

+ 10 - 0
db/const.txt

@@ -4515,6 +4515,16 @@ HAVEQUEST	0
 PLAYTIME	1
 HUNTING	2
 
+QTYPE_NONE	0x270f
+QTYPE_QUEST	0x00
+QTYPE_QUEST2	0x01
+QTYPE_JOB	0x02
+QTYPE_JOB2	0x03
+QTYPE_EVENT	0x04
+QTYPE_EVENT2	0x05
+QTYPE_WARG	0x06
+QTYPE_WARG2	0x08
+
 FW_DONTCARE	0
 FW_THIN	100
 FW_EXTRALIGHT	200

+ 67 - 14
doc/script_commands.txt

@@ -7633,10 +7633,56 @@ if (instance_check_party(getcharid(1),2,2,149)) {
 =========================
 ---------------------------------------
 
+*questinfo <Quest ID>, <Icon> {, <Map Mark Color>{, <Job Class>}};
+
+This is esentially a combination of checkquest and showevent. Use this only
+in an OnInit label. For the Quest ID, specify the quest ID that you want
+checked if it has been started yet.
+
+For Icon, use one of the following:
+
+No Icon			: QTYPE_NONE
+! Quest Icon	: QTYPE_QUEST
+? Quest Icon	: QTYPE_QUEST2
+! Job Icon		: QTYPE_JOB
+? Job Icon		: QTYPE_JOB2
+! Event Icon	: QTYPE_EVENT
+? Event Icon	: QTYPE_EVENT2
+Warg			: QTYPE_WARG
+Warg Face		: QTYPE_WARG2 (Only for packetver >= 20120410)
+
+Map Mark Color, when used, creates a mark in the user's mini map on the position of the NPC,
+the available color values are:
+
+0 - No Marker
+1 - Yellow Marker
+2 - Green Marker
+3 - Purple Marker
+
+When a user shows up on a map, each NPC is checked for questinfo that has been set.
+If questinfo is present, it will check if the quest has been started, if it has not, the bubble will appear.
+
+Optionally, you can also specify a Job Class if the quest bubble should only appear for a certain class.
+
+Example
+izlude,100,100,4	script	Test	844,{
+	mes "[Test]";
+	mes "Hello World.";
+	close;
+
+	OnInit:
+		questinfo 1001, QTYPE_QUEST, 0, Job_Novice;
+		end;
+}
+
+---------------------------------------
+
 *setquest <ID>;
 
 Place quest of <ID> in the users quest log, the state of which is "active".
 
+If *questinfo is set, and the same ID is specified here, the icon will be cleared when the quest is set.
+
 ---------------------------------------
 
 *completequest <ID>;
@@ -7689,23 +7735,30 @@ Return the state of the quest:
 
 ---------------------------------------
 
-*showevent <state>, <color>;
+*showevent <icon>{,<mark color>}
+
+Show an emotion on top of a NPC, and optionally,
+a colored mark in the mini-map like "viewpoint".
+This is used to indicate that a NPC has a quest or an event to 
+a certain player. 
 
-Show a colored mark in the mini-map like "viewpoint" and an emotion on top of a NPC.
-This is used to indicate that a NPC has a quest or an event to certain players.
+Available Icons:
 
-state can be:
-	0 = disable (Used to disable and remove the mark and the emotion from the NPC.)
-	1 = exclamation emotion (Used to show an important quest event to certain player.)
-	2 = interrogation emotion (Used to show an non-important quest event to certain player.)
-	Other values may cause client crashes.
+Remove Icon		: QTYPE_NONE
+! Quest Icon	: QTYPE_QUEST
+? Quest Icon	: QTYPE_QUEST2
+! Job Icon		: QTYPE_JOB
+? Job Icon		: QTYPE_JOB2
+! Event Icon	: QTYPE_EVENT
+? Event Icon	: QTYPE_EVENT2
+Warg			: QTYPE_WARG
+Warg Face		: QTYPE_WARG2 (Only for packetver >= 20120410)
 
-color can be:
-	0 = yellow "Quest"
-	1 = orange "Job"
-	2 = green "Event"
-	3 = an MVP flag
-	Other values show a transparent mark in the mini-map.
+Mark Color:
+0 - No Mark
+1 - Yellow Mark
+2 - Green Mark
+3 - Purple Mark
 
 ---------------------------------------
 

+ 18 - 1
src/map/clif.c

@@ -9450,6 +9450,8 @@ void clif_parse_WantToConnection(int fd, struct map_session_data* sd)
 /// 007d
 void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 {
+	int i;
+
 	if(sd->bl.prev != NULL)
 		return;
 
@@ -9775,9 +9777,24 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		clif_changed_dir(&sd->bl, SELF);
 	}
 
-// Trigger skill effects if you appear standing on them
+	// Trigger skill effects if you appear standing on them
 	if(!battle_config.pc_invincible_time)
 		skill_unit_move(&sd->bl,gettick(),1);
+
+	// NPC Quest / Event Icon Check [Kisuka]
+#if PACKETVER >= 20090218
+	for(i = 0; i < map[sd->bl.m].qi_count; i++) {
+		struct questinfo *qi = &map[sd->bl.m].qi_data[i];
+		if( quest_check(sd, qi->quest_id, HAVEQUEST) == -1 ) {// Check if quest is not started
+			if( qi->hasJob ) { // Check if quest is job-specific, check is user is said job class.
+				if( sd->class_ == qi->job )
+					clif_quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color);
+			} else {
+				clif_quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color);
+			}
+		}
+	}
+#endif
 }
 
 

+ 48 - 0
src/map/map.c

@@ -2260,6 +2260,13 @@ int map_addinstancemap(const char *name, int id)
 		snprintf(map[dst_m].name, sizeof(map[dst_m].name),"%.3d%s", id, iname);
 	map[dst_m].name[MAP_NAME_LENGTH-1] = '\0';
 
+	// Mimic questinfo
+	if( map[src_m].qi_count ) {
+		map[dst_m].qi_count = map[src_m].qi_count;
+		CREATE( map[dst_m].qi_data, struct questinfo, map[dst_m].qi_count );
+		memcpy( map[dst_m].qi_data, map[src_m].qi_data, map[dst_m].qi_count * sizeof(struct questinfo) );
+	}
+
 	map[dst_m].m = dst_m;
 	map[dst_m].instance_id = id;
 	map[dst_m].users = 0;
@@ -2356,6 +2363,9 @@ int map_delinstancemap(int m)
 	map_removemapdb(&map[m]);
 	memset(&map[m], 0x00, sizeof(map[0]));
 
+	if( map[m].qi_data )
+		aFree(map[m].qi_data);
+
 	// Make delete timers invalid to avoid errors
 	map[m].mob_delete_timer = INVALID_TIMER;
 
@@ -3102,6 +3112,12 @@ void map_flags_init(void)
 		// adjustments
 		if( battle_config.pk_mode )
 			map[i].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris]
+
+		if( map[i].qi_data )
+			aFree(map[i].qi_data);
+
+		map[i].qi_data = NULL;
+		map[i].qi_count = 0;
 	}
 }
 
@@ -3678,6 +3694,37 @@ int log_sql_init(void)
 	return 0;
 }
 
+void map_add_questinfo(int m, struct questinfo *qi) {
+	unsigned short i;
+
+	/* duplicate, override */
+	for(i = 0; i < map[m].qi_count; i++) {
+		if( map[m].qi_data[i].nd == qi->nd )
+			break;
+	}
+
+	if( i == map[m].qi_count )
+		RECREATE(map[m].qi_data, struct questinfo, ++map[m].qi_count);
+
+	memcpy(&map[m].qi_data[i], qi, sizeof(struct questinfo));
+}
+
+bool map_remove_questinfo(int m, struct npc_data *nd) {
+	unsigned short i;
+
+	for(i = 0; i < map[m].qi_count; i++) {
+		struct questinfo *qi = &map[m].qi_data[i];
+		if( qi->nd == nd ) {
+			memset(&map[m].qi_data[i], 0, sizeof(struct questinfo));
+			if( i != --map[m].qi_count )
+				memmove(&map[m].qi_data[i],&map[m].qi_data[i+1],sizeof(struct questinfo)*(map[m].qi_count-i));
+			return true;
+		}
+	}
+
+	return false;
+}
+
 /**
  * @see DBApply
  */
@@ -3813,6 +3860,7 @@ void do_final(void)
 		if(map[i].cell) aFree(map[i].cell);
 		if(map[i].block) aFree(map[i].block);
 		if(map[i].block_mob) aFree(map[i].block_mob);
+		if(map[i].qi_data) aFree(map[i].qi_data);
 		if(battle_config.dynamic_mobs) { //Dynamic mobs flag by [random]
 			if(map[i].mob_delete_timer != INVALID_TIMER)
 				delete_timer(map[i].mob_delete_timer, map_removemobs_timer);

+ 16 - 0
src/map/map.h

@@ -580,6 +580,15 @@ struct s_skill_damage {
 #define MAX_MAP_SKILL_MODIFIER 5
 #endif
 
+struct questinfo {
+	struct npc_data *nd;
+	unsigned short icon;
+	unsigned char color;
+	int quest_id;
+	bool hasJob;
+	unsigned short job;/* perhaps a mapid mask would be most flexible? */
+};
+
 struct map_data {
 	char name[MAP_NAME_LENGTH];
 	uint16 index; // The map index used by the mapindex* functions.
@@ -688,6 +697,10 @@ struct map_data {
 
 	/* rAthena Local Chat */
 	struct Channel *channel;
+
+	/* ShowEvent Data Cache */
+	struct questinfo *qi_data;
+	unsigned short qi_count;
 	
 	/* speeds up clif_updatestatus processing by causing hpmeter to run only when someone with the permission can view it */
 	unsigned short hpmeter_visible;
@@ -806,6 +819,9 @@ struct mob_data * map_id2boss(int id);
 // reload config file looking only for npcs
 void map_reloadnpc(bool clear);
 
+void map_add_questinfo(int m, struct questinfo *qi);
+bool map_remove_questinfo(int m, struct npc_data *nd);
+
 /// Bitfield of flags for the iterator.
 enum e_mapitflags
 {

+ 3 - 0
src/map/npc.c

@@ -1945,6 +1945,9 @@ int npc_unload(struct npc_data* nd, bool single) {
 			aFree(nd->path);/* remove now that no other instances exist */
 		}
 	}
+	
+	if( single && nd->bl.m != -1 )
+		map_remove_questinfo(nd->bl.m, nd);
 
 	if( (nd->subtype == SHOP || nd->subtype == CASHSHOP || nd->subtype == ITEMSHOP || nd->subtype == POINTSHOP) && nd->src_id == 0) //src check for duplicate shops [Orcao]
 		aFree(nd->u.shop.shop_item);

+ 94 - 8
src/map/script.c

@@ -16545,12 +16545,82 @@ BUILDIN_FUNC(readbook)
 Questlog script commands
 *******************/
 
+BUILDIN_FUNC(questinfo)
+{
+	TBL_NPC* nd = map_id2nd(st->oid);
+	int quest_id, icon, job, color = 0;
+	struct questinfo qi;
+
+	if( nd == NULL || nd->bl.m == -1 )
+		return true;
+
+	quest_id = script_getnum(st, 2);
+	icon = script_getnum(st, 3);
+
+	#if PACKETVER >= 20120410
+		if(icon < 0 || (icon > 8 && icon != 9999) || icon == 7)
+			icon = 9999; // Default to nothing if icon id is invalid.
+	#else
+		if(icon < 0 || icon > 7)
+			icon = 0;
+		else
+			icon = icon + 1;
+	#endif
+
+	qi.quest_id = quest_id;
+	qi.icon = (unsigned char)icon;
+	qi.nd = nd;
+
+	if( script_hasdata(st, 4) ) {
+		color = script_getnum(st, 4);
+		if( color < 0 || color > 3 ) {
+			ShowWarning("buildin_questinfo: invalid color '%d', changing to 0\n",color);
+			script_reportfunc(st);
+			color = 0;
+		}
+		qi.color = (unsigned char)color;
+	}
+
+	qi.hasJob = false;
+
+	if(script_hasdata(st, 5)) {
+		job = script_getnum(st, 5);
+
+		if (!pcdb_checkid(job))
+			ShowError("buildin_questinfo: Nonexistant Job Class.\n");
+		else {
+			qi.hasJob = true;
+			qi.job = (unsigned short)job;
+		}
+	}
+
+	map_add_questinfo(nd->bl.m,&qi);
+
+	return true;
+}
+
 BUILDIN_FUNC(setquest)
 {
 	struct map_session_data *sd = script_rid2sd(st);
+	int i, quest_id;
+
 	nullpo_ret(sd);
+	
+	quest_id = script_getnum(st, 2);
+
+	quest_add(sd, quest_id);
 
-	quest_add(sd, script_getnum(st, 2));
+	// If questinfo is set, remove quest bubble once quest is set.
+	for(i = 0; i < map[sd->bl.m].qi_count; i++) {
+		struct questinfo *qi = &map[sd->bl.m].qi_data[i];
+		if( qi->quest_id == quest_id ) {
+#if PACKETVER >= 20120410
+			clif_quest_show_event(sd, &qi->nd->bl, 9999, 0);
+#else
+			clif_quest_show_event(sd, &qi->nd->bl, 0, 0);
+#endif
+		}
+	}
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -16613,17 +16683,32 @@ BUILDIN_FUNC(showevent)
 {
 	TBL_PC *sd = script_rid2sd(st);
 	struct npc_data *nd = map_id2nd(st->oid);
-	int state, color;
+	int icon, color = 0;
 
 	if( sd == NULL || nd == NULL )
 	return 0;
-	state = script_getnum(st, 2);
-	color = script_getnum(st, 3);
 
-	if( color < 0 || color > 3 )
-	color = 0; // set default color
+	icon = script_getnum(st, 2);
+	if( script_hasdata(st, 3) ) {
+		color = script_getnum(st, 3);
+		if( color < 0 || color > 3 ) {
+			ShowWarning("buildin_showevent: invalid color '%d', changing to 0\n",color);
+			script_reportfunc(st);
+			color = 0;
+		}
+	}
+
+	#if PACKETVER >= 20120410
+		if(icon < 0 || (icon > 8 && icon != 9999) || icon == 7)
+			icon = 9999; // Default to nothing if icon id is invalid.
+	#else
+		if(icon < 0 || icon > 7)
+			icon = 0;
+		else
+			icon = icon + 1;
+	#endif
 
-	clif_quest_show_event(sd, &nd->bl, state, color);
+	clif_quest_show_event(sd, &nd->bl, icon, color);
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -19150,13 +19235,14 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(useatcmd, "s"),
 
 	//Quest Log System [Inkfish]
+	BUILDIN_DEF(questinfo, "ii??"),
 	BUILDIN_DEF(setquest, "i"),
 	BUILDIN_DEF(erasequest, "i"),
 	BUILDIN_DEF(completequest, "i"),
 	BUILDIN_DEF(checkquest, "i?"),
 	BUILDIN_DEF(isbegin_quest,"i"),
 	BUILDIN_DEF(changequest, "ii"),
-	BUILDIN_DEF(showevent, "ii"),
+	BUILDIN_DEF(showevent, "i?"),
 
 	//Bound items [Xantara] & [Akinari]
 	BUILDIN_DEF2(getitem,"getitembound","vii?"),