Pārlūkot izejas kodu

questinfo requirement update (#4168)

* The <quest ID> is now removed, all the conditions are handled by a <condition> argument. The <condition> is using the achievement_check_condition (like in achievement_db.yml) so any condition (in theory) can be used to display the icon.
* A new script command has been added to refresh the bubble for the given player anytime.

Thanks to @aleos89 and @secretdataz for the review!
Atemo 5 gadi atpakaļ
vecāks
revīzija
b412c4fcab

+ 36 - 47
doc/script_commands.txt

@@ -9118,13 +9118,15 @@ Examples:
 =========================
 ---------------------------------------
 
-*questinfo <Quest ID>,<Icon>{,<Map Mark Color>{,<Job Class>}};
+*questinfo <Icon>{,<Map Mark Color>{,"<condition>"}};
 
-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.
+This command should only be used in an OnInit label.
+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.
+If questinfo is present, it will check if the player fulfill the condition.
+If he/she does or no condition has been set, the bubble will appear.
 
-For Icon, use one of the following:
+Available <Icon>:
 
 No Icon			: QTYPE_NONE
 ! Quest Icon	: QTYPE_QUEST
@@ -9141,59 +9143,46 @@ Daily Quest		: QTYPE_DAILYQUEST (Only for packetver >= 20170315)
 Job Quest		: QTYPE_JOBQUEST (Only for packetver >= 20170315)
 Jumping Poring	: QTYPE_JUMPING_PORING (Only for packetver >= 20170315)
 
-Map Mark Color, when used, creates a mark in the user's mini map on the position of the NPC,
+<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 (default)
-1 - Yellow Marker
-2 - Green Marker
-3 - Purple Marker
+QMARK_NONE   - No Marker (default)
+QMARK_YELLOW - Yellow Marker
+QMARK_GREEN  - Green Marker
+QMARK_PURPLE - 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.
+<condition> can be any expression similarly to the <condition> in the 'if' command.
 
-Optionally, you can also specify a Job Class if the quest bubble should only appear for a certain class.
+List of the player's actions to trigger the questinfo condition:
+-	Item added to/removed from player inventory
+-	Base/Job level change
+-	Job change
+-	Quest given/erased/completed
+-	Quest objective updated (character killed a monster quest target)
+-	Warp
 
-Example
+
+Example:
 izlude,100,100,4	script	Test	844,{
 	mes "[Test]";
 	mes "Hello World.";
 	close;
 
-	OnInit:
-		questinfo 1001, QTYPE_QUEST, 0, Job_Novice;
-		end;
-}
-
----------------------------------------
-
-*setquestinfo_level <quest_id>,<min_level>,<max_level>;
-
-Add level range criteria for quest info with quest id 'quest_id'.
-
-This command must be used after 'questinfo'.
-
----------------------------------------
-
-*setquestinfo_req <quest_id>,<req_quest_id>,<state>{,<req_quest_id>,<state>,...};
-
-Add 'req_quest_id' as requirement for quest info with quest id 'quest_id'.
-
-Value os 'state' are:
-    0: Player doesn't started 'req_quest_id'.
-    1: Player has 'req_quest_id' (state is either "inactive" or "active").
-    2: Player has 'req_quest_id' completed
+OnInit:
+	// Display an icon if the player has completed the given hunting quest and his/her variable 'unknown_var' is above 0
+	questinfo QTYPE_QUEST, QMARK_YELLOW, "checkquest(1001,HUNTING) == 2 && unknown_var > 0";
 
-This command must be used after 'questinfo'.
+	//.. or display an icon if the player didn't start the given quest and he/she has one red potion in inventory
+	questinfo QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(1001) && countitem(501) == 1";
+	end;
+}
 
 ---------------------------------------
 
-*setquestinfo_job <quest_id>,<job_id>{,<job_id>...};
-
-Add job criteria for quest info with quest id 'quest_id'.
-The job criteria may more than one job_id.
+*questinfo_refresh {<char_id>};
 
-This command must be used after 'questinfo'.
+This command refreshes each quest bubble that has been set on the map according
+to the questinfo condition for the attached/given player.
 
 ---------------------------------------
 
@@ -9275,10 +9264,10 @@ Warg			: QTYPE_WARG
 Warg Face		: QTYPE_WARG2 (Only for packetver >= 20120410)
 
 Mark Color:
-0 - No Mark
-1 - Yellow Mark
-2 - Green Mark
-3 - Purple Mark
+QMARK_NONE   - No Marker (default)
+QMARK_YELLOW - Yellow Marker
+QMARK_GREEN  - Green Marker
+QMARK_PURPLE - Purple Marker
 
 ---------------------------------------
 

+ 34 - 62
npc/re/custom/lasagna/lasagna_npcs.txt

@@ -89,7 +89,7 @@ lasa_fild01,49,304,3	script	Vigilante Ajegna#doram0	4_DR_SOLDIER,{
 		close;
 	}
 OnInit:
-	questinfo 7712, QTYPE_QUEST, 1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7712)" );
 	end;
 }
 
@@ -165,7 +165,7 @@ OnTouch:
 	end;
 OnInit:
 	if (strnpcinfo(0) == "Applicant Paul#doramt13")
-		questinfo 7723, QTYPE_QUEST, 1;
+		questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7723)" );
 	end;
 }
 lasa_fild01,50,321,6	duplicate(Applicant Paul#doramt13)	Applicant MacCarnie#doramt13	4_M_MERCAT1
@@ -236,8 +236,7 @@ lasa_fild01,54,325,3	script	Vigilante Gnocchi#doramt13	4_DR_SOLDIER,{
 	}
 	close;
 OnInit:
-	questinfo 7723, QTYPE_QUEST, 1;
-	setquestinfo_req 7723, 7723,1;// todo : display when isbegin_quest(7723) == 1 only
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7723) == 1" );
 	end;
 }
 
@@ -393,7 +392,7 @@ lasa_fild01,60,322,3	script	Vigilante Aglio#doram02	4_DR_AGLIO,{
 			mes "Also, I'll give you a new staff.";
 			mes "It should be better than that sprout you brought with you.";
 			mes "Now you know how to equip an item because I already explained it to you, don't you?";
-			completequest 7714;// Refreshing Apple Juice
+			erasequest 7714;// Refreshing Apple Juice
 			getitem 569,30;// Novice Potion
 			getitem 1687,1;// Beginner Foxtail Staff [1]
 			getexp 70,70;
@@ -532,12 +531,7 @@ lasa_fild01,60,322,3	script	Vigilante Aglio#doram02	4_DR_AGLIO,{
 	mes "Well, problems do occur at the harbor from time to time, but I thought it wouldn't be an issue for you.";
 	close;
 OnInit:
-	questinfo 7715, QTYPE_QUEST, 1;
-	setquestinfo_req 7715, 7713,2;
-	questinfo 7712, QTYPE_QUEST, 1;
-	setquestinfo_req 7712, 7712,1;
-	// questinfo 7713, QTYPE_QUEST, 1;
-	// setquestinfo_req 7713, 7713,2;// todo display when HUNTING is QUEST_COMPLETE
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7712) > 0 && (checkquest(7713,HUNTING) == -1 || checkquest(7713,HUNTING) == 2)" );
 	end;
 }
 
@@ -646,7 +640,7 @@ OnTouch:
 		npctalk "Hey, there! Get over here for a minute!", "", bc_self;
 	end;
 OnInit:
-	questinfo 7719, QTYPE_QUEST, 1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7719)" );
 	end;
 }
 
@@ -818,8 +812,7 @@ OnTouch:
 	end;
 
 OnInit:
-	questinfo 7721, QTYPE_QUEST, 1;
-	setquestinfo_req 7721, 7716,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7716) == 1 && isbegin_quest(7721) != 2" );
 	end;
 }
 
@@ -876,8 +869,7 @@ lasagna,168,125,3	script	Kid#doram04t	4_DR_KID_01,{
 	npctalk "You're too young! Just join the Vigilante Corp.", "Tool Merchant#doram05t", bc_self;
 	end;
 OnInit:
-	questinfo 7716, QTYPE_QUEST, 1;
-	setquestinfo_req 7716, 7716,1;// todo display when Active, removed when completed
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7716) == 1" );
 	end;
 }
 
@@ -992,6 +984,10 @@ lasagna,165,125,5	script	Tool Merchant#doram05t	4_DR_M_01,{
 	close2;
 	callshop "tool_shop_lasagna",2;
 	end;
+
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7717) == 1" );
+	end;
 }
 
 -	shop	tool_shop_lasagna	-1,1750:-1,611:-1,501:-1,502:-1,503:-1,504:-1,645:-1,656:-1,713:-1,601:-1,602:-1,1065:-1
@@ -1215,7 +1211,8 @@ lasagna,224,126,5	script	Healer Haru#doram07t	4_DR_M_02,{
 	}
 
 OnInit:
-	questinfo 7722, QTYPE_QUEST, 1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7722) == 0" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7717) == 1" );
 	end;
 }
 
@@ -1674,7 +1671,8 @@ OnTouch:
 		npctalk "Meow! Hey, a new friend! I've got a story to tell you. You don't want to miss this!", "", bc_self;
 	end;
 OnInit:
-	questinfo 7720, QTYPE_QUEST, 1;// perma for human
+	// note: permanent for human
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7720)" );
 	end;
 }
 
@@ -1731,8 +1729,7 @@ OnTouch:
 		npctalk "Please don't miss the announcement about the details of Con-Chliina!", "", bc_self;
 	end;
 OnInit:
-	questinfo 14545, QTYPE_QUEST, 1;
-	setquestinfo_req 14545, 11445,2;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14545)" );
 	end;
 }
 
@@ -2635,9 +2632,7 @@ lasa_fild01,353,370,5	script	Shepherd#dorcon	4_DR_KID_01,{
 		close;
 	}
 OnInit:
-	questinfo 14546, QTYPE_QUEST, 1;
-	setquestinfo_req 14546, 14592,0;
-	setquestinfo_level 14546,6,9;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14546) && !isbegin_quest(14592) && BaseLevel >= 6 && BaseLevel <= 9" );
 	end;
 }
 
@@ -2784,9 +2779,7 @@ lasa_fild02,102,372,5	script	Florin#dorcon	4_DR_F_01,{
 	}
 
 OnInit:
-	questinfo 14588, QTYPE_QUEST, 1;
-	setquestinfo_req 14588, 14592,0;
-	setquestinfo_level 14588,10,13;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14588) && !isbegin_quest(14592) && BaseLevel >= 10 && BaseLevel <= 13" );
 	end;
 }
 
@@ -2928,9 +2921,7 @@ lasa_fild02,177,241,5	script	Prok#dorcon	4_DR_M_02,{
 	}
 
 OnInit:
-	questinfo 14589, QTYPE_QUEST, 1;
-	setquestinfo_req 14589, 14592,0;
-	setquestinfo_level 14589,14,15;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14589) && !isbegin_quest(14592) && (BaseLevel == 14 || BaseLevel == 15)" );
 	end;
 }
 
@@ -3056,9 +3047,7 @@ lasa_fild02,326,170,5	script	Fisher#dorcon	4_CAT_ADV2,{
 	}
 
 OnInit:
-	questinfo 14590, QTYPE_QUEST, 1;
-	setquestinfo_req 14590, 14592,0;
-	setquestinfo_level 14590,16,19;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14590) && !isbegin_quest(14592) && BaseLevel >= 16 && BaseLevel <= 19" );
 	end;
 }
 
@@ -3245,9 +3234,7 @@ lasa_fild02,331,251,5	script	Sleepy meow#dorcon	4_CAT_REST,{
 	}
 
 OnInit:
-	questinfo 14591, QTYPE_QUEST, 1;
-	setquestinfo_req 14591, 14592,0;
-	setquestinfo_level 14591,20,20;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14591) && !isbegin_quest(14592) && BaseLevel == 20" );
 	end;
 }
 
@@ -3740,21 +3727,8 @@ lasagna,222,187,3	script	Chef#dorcon	4_CAT_CHEF,{
 	end;
 
 OnInit:
-	questinfo 14546, QTYPE_QUEST, 1;
-	setquestinfo_req 14546, 14592,0, 14588,0, 14589,0, 14590,0;
-	setquestinfo_level 14546,6,9;
-
-	questinfo 14588, QTYPE_QUEST, 1;
-	setquestinfo_req 14588, 14592,0, 14546,0, 14589,0, 14590,0;
-	setquestinfo_level 14588,10,13;
-
-	questinfo 14589, QTYPE_QUEST, 1;
-	setquestinfo_req 14589, 14592,0, 14546,0, 14588,0, 14590,0;
-	setquestinfo_level 14589,14,15;
-
-	questinfo 14590, QTYPE_QUEST, 1;
-	setquestinfo_req 14590, 14592,0, 14546,0, 14588,0, 14589,0;
-	setquestinfo_level 14590,16,19;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW,
+		"BaseLevel >= 6 && BaseLevel <= 19 && !isbegin_quest(14546) && !isbegin_quest(14592) && !isbegin_quest(14588) && !isbegin_quest(14589) && !isbegin_quest(14590)" );
 	end;
 }
 
@@ -4030,7 +4004,7 @@ OnTouch:
 	npctalk "Vigilante Guide: If you're ever in need of help, please visit the Vigilante Corp at any time.", "", bc_self;
 	end;
 OnInit:
-	questinfo 11435, QTYPE_QUEST, 1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(11435)" );
 	end;
 }
 lasagna,202,291,3	duplicate(Vigilante Guide#lasa)	Vigilante Guide#lasa2	4_DR_SOLDIER,5,5
@@ -5044,7 +5018,7 @@ OnTouch:
 	callsub S_quest21;
 	end;
 OnInit:
-	questinfo 14545, QTYPE_QUEST, 1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14545)" );
 	end;
 }
 
@@ -5256,12 +5230,7 @@ conch_in,65,58,3	script	Purser#dorcon	4_CAT_ADV1,{
 	close;
 
 OnInit:
-	questinfo 14532, QTYPE_QUEST, 1;
-	setquestinfo_req 14532,14531,1;
-	questinfo 14536, QTYPE_QUEST, 1;
-	setquestinfo_req 14536,14535,1;
-	questinfo 14547, QTYPE_QUEST, 1;
-	setquestinfo_req 14547,14531,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(14531) == 1" );
 	end;
 }
 
@@ -7861,8 +7830,7 @@ lasagna,298,84,5	script	Vigilante#dorcon01	4_DR_SOLDIER,{
 	mes "The wind is very uneasy today.";
 	close;
 OnInit:
-	questinfo 14541, QTYPE_QUEST, 1;
-	setquestinfo_req 14541,14539,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(14548) == 1 || isbegin_quest(14539) == 1" );
 	end;
 }
 
@@ -8080,6 +8048,9 @@ lasa_dun01,153,103,3	script	Vigilante#dorcon02	4_DR_SOLDIER,{
 	mes "[Machie]";
 	mes "Tut.";
 	close;
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(14541) == 1 || isbegin_quest(14549) == 1" );
+	end;
 }
 
 lasa_dun_q,162,158,3	script	Tartufi#dorcon01	4_TARUTUPI,{
@@ -8963,6 +8934,8 @@ lasagna,226,181,3	script	Kalu#dorcon	4_CAT_CHEF,{
 	if (lasagna_quest[2] == 8) {
 		mes "[Kalu]";
 		mes "Just deliver this big lunchbox to Panna who's out on a cave patrol.";
+		if (countitem(25048) < 1)
+			getitem 25048,1;// Hearty Lunchbox
 		close;
 	}
 	if (lasagna_quest[2] == 9) {
@@ -9088,8 +9061,7 @@ lasagna,226,181,3	script	Kalu#dorcon	4_CAT_CHEF,{
 	end;
 
 OnInit:
-	questinfo 14567, QTYPE_QUEST, 1;
-	setquestinfo_level 14567,140,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(14567) && BaseLevel >= 140" );
 	end;
 }
 

+ 34 - 72
npc/re/jobs/novice/academy.txt

@@ -83,7 +83,7 @@ OnTouch:
 	end;
 
 OnInit:
-	questinfo 21001, QTYPE_QUEST, 1, Job_Novice;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(21001) && Class == Job_Novice" );
 	end;
 }
 iz_int01,56,32,3	duplicate(Wounded Swordsman#intro_npc01_iz_int)	Wounded Swordsman#intro_npc01_iz_int01	4_TOWER_01,5,5
@@ -125,7 +125,7 @@ iz_int,56,32,3	script	Wounded Swordsman#intro_npc02_iz_int	HIDDEN_WARP_NPC,{
 	end;
 
 OnInit:
-	questinfo 21001, QTYPE_QUEST, 1, Job_Novice;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(21001) && Class == Job_Novice" );
 	end;
 }
 iz_int01,56,32,3	duplicate(Wounded Swordsman#intro_npc02_iz_int)	Wounded Swordsman#intro_npc02_iz_int01	HIDDEN_WARP_NPC
@@ -237,7 +237,7 @@ int_land,78,103,5	script	Captain Carocc#intro_npc03	4_M_REINDEER,{
 	close;
 
 OnInit:
-	questinfo 21008, QTYPE_QUEST, 1, Job_Novice;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(21008) && Class == Job_Novice" );
 	end;
 }
 
@@ -328,8 +328,7 @@ int_land,73,100,3	script	Lumin#new_ship	4_M_NOV_RUMIN,{
 	end;
 
 OnInit:
-	questinfo 7471, QTYPE_QUEST, 1, Job_Novice;
-	setquestinfo_req 7471,21008,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "Class == Job_Novice && !isbegin_quest(7471) && isbegin_quest(21008) == 1" );
 	end;
 }
 int_land01,73,100,3	duplicate(Lumin#new_ship)	Lumin#new_ship01	4_M_NOV_RUMIN
@@ -380,8 +379,7 @@ int_land,58,69,5	script	Sailor#intro_npc04	4W_SAILOR,{
 	close;
 
 OnInit:
-	questinfo 21002, QTYPE_QUEST, 1, Job_Novice;
-	setquestinfo_req 21002,21008,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "Class == Job_Novice && !isbegin_quest(21002) && isbegin_quest(21008) == 1" );
 	end;
 }
 
@@ -793,9 +791,7 @@ izlude,198,213,3	script	Captain Carocc#iz	4_M_REINDEER,5,5,{
 	end;
 
 OnInit:
-	questinfo 7472, QTYPE_QUEST, 1, Job_Novice;
-	setquestinfo_level 7472,1,14;
-	setquestinfo_req 7472,7473,0;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "Class == Job_Novice && BaseLevel <= 14 && !isbegin_quest(7472) && !isbegin_quest(7473)" );
 	end;
 
 OnTouch:
@@ -1115,8 +1111,7 @@ izlude,122,207,3	script	Criatura Academy Staff#0	4_M_KHKYEL,3,3,{
 	}
 
 OnInit:
-	questinfo 7473, QTYPE_QUEST, 1, Job_Novice;
-	setquestinfo_level 7473,1,14;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "Class == Job_Novice && BaseLevel <= 14 && !isbegin_quest(7473)" );
 	end;
 
 OnTouch:
@@ -1636,13 +1631,8 @@ izlude,120,207,4	script	Information Staff#0	8W_SOLDIER,{
 	}
 
 OnInit:
-	questinfo 7474, QTYPE_QUEST, 1;
-	setquestinfo_level 7474,1,14;
-
-	// questinfo 7495, QTYPE_QUEST, 1;// officially disabled
-	// setquestinfo_req 7495,7475,1;
-	// setquestinfo_req 7495,7476,1;
-	// setquestinfo_req 7495,7477,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel <= 14 && !isbegin_quest(7474)" );
+	// questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7495) && isbegin_quest(7475) == 1 && isbegin_quest(7476) == 1 && isbegin_quest(7477) == 1" );// officially disabled
 	end;
 }
 
@@ -1775,8 +1765,7 @@ izlude,179,75,3	script	Airship#iz	4_BULLETIN_BOARD2,3,3,{
 	}
 
 OnInit:
-	questinfo 7475, QTYPE_QUEST, 0;
-	setquestinfo_req 7475,7474,1;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7475) && isbegin_quest(7474) == 1" );
 	end;
 
 OnTouch:
@@ -1918,8 +1907,7 @@ izlude,207,167,3	script	Arena#iz	4_BULLETIN_BOARD2,3,3,{
 	}
 
 OnInit:
-	questinfo 7476, QTYPE_QUEST, 0;
-	setquestinfo_req 7476,7474,1;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7476) && isbegin_quest(7474) == 1" );
 	end;
 
 OnTouch:
@@ -2061,8 +2049,7 @@ izlude,45,94,3	script	Bulletin Board#iz	4_BULLETIN_BOARD2,3,3,{
 	}
 
 OnInit:
-	questinfo 7477, QTYPE_QUEST, 0;
-	setquestinfo_req 7477,7474,1;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7477) && isbegin_quest(7474) == 1" );
 	end;
 
 OnTouch:
@@ -2275,8 +2262,7 @@ iz_ac01,59,43,3	script	Therapist#ac	4_M_6THPRIN1,{
 	}
 
 OnInit:
-	questinfo 7478, QTYPE_QUEST, 0;
-	setquestinfo_req 7478,4269,2;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7478) && isbegin_quest(4269) == 2" );
 	end;
 }
 
@@ -2419,8 +2405,7 @@ izlude,140,249,7	script	Academy Student#0	4_M_NOV_RUMIN,{
 	end;
 
 OnInit:
-	questinfo 7479, QTYPE_QUEST, 1;
-	setquestinfo_req 7479,7478,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7479) && isbegin_quest(7478) == 1" );
 	end;
 }
 
@@ -2589,7 +2574,7 @@ izlude,140,260,3	script	Instructor Argos#iz	4_M_LIEMAN,{
 	}
 
 OnInit:
-	questinfo 15001, QTYPE_QUEST, 0, Job_Novice;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "Class == Job_Novice && !isbegin_quest(15001)" );
 	end;
 }
 
@@ -2800,7 +2785,7 @@ iz_ac01,100,39,5	script	Academy Receptionist#1	4_F_01,{
 	}
 
 OnInit:
-	questinfo 4269, QTYPE_QUEST, 0, Job_Novice;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "Class == Job_Novice && !isbegin_quest(4269)" );
 	end;
 }
 
@@ -3657,7 +3642,7 @@ izlude,115,181,5	script	Shop Helper#iz	4_F_KHELLISIA,{
 	}
 
 OnInit:
-	questinfo 1237, QTYPE_QUEST, 0;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(1237)" );
 	end;
 }
 
@@ -3843,7 +3828,7 @@ iz_ac01,53,74,3	script	Attribute Expert#ac	1_M_WIZARD,{
 	}
 
 OnInit:
-	questinfo 2299, QTYPE_QUEST, 0;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(2299)" );
 	end;
 }
 
@@ -4396,10 +4381,7 @@ iz_ac01,147,50,3	script	Dacquoise#ac	4_COOK,{
 	}
 
 OnInit:
-	questinfo 14154, QTYPE_QUEST, 1, Job_Novice;
-	questinfo 14155, QTYPE_QUEST, 1, Job_Novice;
-	questinfo 14156, QTYPE_QUEST, 1, Job_Novice;
-	questinfo 14157, QTYPE_QUEST, 1, Job_Novice;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "Class == Job_Novice && (!isbegin_quest(14154) || !isbegin_quest(14155) || !isbegin_quest(14156) || !isbegin_quest(14157))" );
 	end;
 }
 
@@ -4738,16 +4720,8 @@ L_Quest:
 	}
 
 OnInit:
-	questinfo 11339, QTYPE_QUEST ,0 ,Job_Novice;
-	questinfo 11340, QTYPE_QUEST ,0 ,Job_Novice;
-	questinfo 11341, QTYPE_QUEST ,0 ,Job_Novice;
-	questinfo 11342, QTYPE_QUEST ,0 ,Job_Novice;
-	questinfo 11344, QTYPE_QUEST ,0 ,Job_Novice;
-	setquestinfo_req 11339,4269,2;
-	setquestinfo_req 11340,4269,2;
-	setquestinfo_req 11341,4269,2;
-	setquestinfo_req 11342,4269,2;
-	setquestinfo_req 11344,4269,2;
+	questinfo( QTYPE_QUEST, QMARK_NONE,
+		"Class == Job_Novice && isbegin_quest(4269) == 2 && (!isbegin_quest(11339) || !isbegin_quest(11340) || !isbegin_quest(11341) || !isbegin_quest(11342) || !isbegin_quest(11344))" );
 	end;
 }
 
@@ -4907,7 +4881,7 @@ iz_ac01,45,80,5	script	Adept Adventurer#ac	4_M_JOB_BLACKSMITH,{
 	}
 
 OnInit:
-	questinfo 2298, QTYPE_QUEST, 0;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(2298)" );
 	end;
 }
 
@@ -9569,8 +9543,7 @@ gef_fild07,89,208,6	script	Magician's Guild Guide#1	2_F_MAGICMASTER,{
 	close;
 
 OnInit:
-	questinfo 9265, QTYPE_QUEST, 1;
-	setquestinfo_req 9265,9264,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(9265) && isbegin_quest(9264) == 1" );
 	end;
 }
 
@@ -9614,8 +9587,7 @@ geffen,43,123,6	script	Magician's Guild Guide#2	2_F_MAGICMASTER,{
 	close;
 
 OnInit:
-	questinfo 9266, QTYPE_QUEST, 1;
-	setquestinfo_req 9266,9265,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(9266) && isbegin_quest(9265) == 1" );
 	end;
 }
 
@@ -10805,12 +10777,9 @@ iz_ac02,52,136,3	script	Guest Lecturer Mayssel#0	4_F_MAYSEL,{
 	}
 
 OnInit:
-	questinfo 7480, QTYPE_QUEST, 0, Job_Novice;
-	setquestinfo_req 7480,4269,2;
-	questinfo 7483, QTYPE_QUEST, 0;
-	setquestinfo_req 7483,7481,1,7482,1;
-
-	// questinfo 7495, QTYPE_QUEST, 0;// officially disabled
+	questinfo( QTYPE_QUEST, QMARK_NONE, "Class == Job_Novice && !isbegin_quest(7480) && isbegin_quest(4269) == 2" );
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7483) && isbegin_quest(7481) == 1 && isbegin_quest(7482) == 1" );
+	// questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7495)" );// officially disabled
 	// setquestinfo_req 7495,7484,2,7485,3;
 	end;
 }
@@ -11333,10 +11302,8 @@ iz_ac02,49,134,5	script	Vicente#ac	4_M_HUMAN_02,{
 	}
 
 OnInit:
-	questinfo 7481, QTYPE_QUEST, 0;
-	setquestinfo_req 7481,7480,1;
-	questinfo 7484, QTYPE_QUEST, 0;
-	setquestinfo_req 7484,7483,1;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7481) && isbegin_quest(7480) == 1" );
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7484) && isbegin_quest(7483) == 1" );
 	end;
 }
 
@@ -11773,10 +11740,8 @@ iz_ac02,55,134,3	script	Verkhasel#ac	4_M_GUILLOTINE,{
 	}
 
 OnInit:
-	questinfo 7482, QTYPE_QUEST, 0;
-	setquestinfo_req 7482,7480,1;
-	questinfo 7485, QTYPE_QUEST, 0;
-	setquestinfo_req 7485,7483,1;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7482) && isbegin_quest(7480) == 1" );
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7485) && isbegin_quest(7483) == 1" );
 	end;
 }
 
@@ -11944,8 +11909,7 @@ iz_ac02,62,139,3	script	Lumin#ac	4_M_NOV_RUMIN,{
 	}
 
 OnInit:
-	questinfo 7487, QTYPE_QUEST, 0;
-	setquestinfo_req 7487,7485,1;
+	questinfo( QTYPE_QUEST, QMARK_NONE, "!isbegin_quest(7487) && isbegin_quest(7485) == 1" );
 	end;
 }
 
@@ -12810,8 +12774,7 @@ izlude,153,126,1	script	Refinery Owner Han#iz	4_M_02,{
 	}
 
 OnInit:
-	questinfo 5153, QTYPE_QUEST, 1;
-	setquestinfo_level 5153,1,20;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel <= 20 && !isbegin_quest(5153)" );
 	end;
 }
 
@@ -13950,8 +13913,7 @@ izlude,96,125,7	script	Enchanter Mounds#iz	4_CAT_SAILOR2,{
 	}
 
 OnInit:
-	questinfo 5157, QTYPE_QUEST, 1;
-	setquestinfo_level 5157,1,20;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel <= 20 && !isbegin_quest(5157)" );
 	end;
 }
 

+ 3 - 2
npc/re/merchants/nightmare_biolab.txt

@@ -1603,7 +1603,7 @@ OnTouch:
 	end;
 
 OnInit:
-	// questinfo 14683,QTYPE_QUEST,1;	// display when quest 14683 active
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(14683) == 1" );
 	end;
 }
 
@@ -1729,7 +1729,8 @@ OnTouch:
 	end;
 
 OnInit:
-	questinfo atoi(strnpcinfo(2)),QTYPE_QUEST,1;
+	.@condition$ = "!isbegin_quest(" + strnpcinfo(2) + ")";
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, .@condition$ );
 	end;
 }
 

+ 1 - 2
npc/re/quests/quests_14_3.txt

@@ -196,8 +196,7 @@ morocc,138,238,4	script	Piled Rags#ep14bs	4_M_DIEMAN,{
 	close;
 
 OnInit:
-	questinfo 11349, QTYPE_QUEST, 1;
-	setquestinfo_level 11349,140,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel >= 140 && !isbegin_quest(11349)" );
 	end;
 }
 

+ 9 - 37
npc/re/quests/quests_14_3_bis.txt

@@ -244,12 +244,7 @@ moro_vol,98,107,3	script	Magic Scholar#143avt01	4_M_FAIRYAVANT,{
 	end;
 
 OnInit:
-	questinfo 7583, QTYPE_QUEST2;
-	setquestinfo_req 7583,7581,1;
-	setquestinfo_req 7583,7584,0;
-	setquestinfo_level 7583,140,175;
-	// 3759 is probably ep14_2_mylord
-	// SetQuestItem 7583 3759 33 "<"
+	questinfo( QTYPE_QUEST2, QMARK_NONE, "BaseLevel >= 140 && ep14_2_mylord > 33 && !isbegin_quest(7583) && !isbegin_quest(7584) && isbegin_quest(7581) == 1" );
 	end;
 }
 
@@ -414,12 +409,7 @@ moro_vol,95,108,7	script	Hishieh#143hisie01	4_M_FARIY_HISIE,{
 	end;
 
 OnInit:
-	questinfo 7595, QTYPE_QUEST2;
-	setquestinfo_req 7595,7593,1;
-	setquestinfo_req 7595,7600,0;
-	setquestinfo_level 7595,160,175;
-	// 3759 = [ep14_2_mylord]
-	// SetQuestItem 7595 3759 33 "<"
+	questinfo( QTYPE_QUEST2, QMARK_NONE, "BaseLevel >= 160 && ep14_2_mylord > 33 && !isbegin_quest(7595) && !isbegin_quest(7600) && isbegin_quest(7593) == 1" );
 	end;
 }
 
@@ -603,16 +593,8 @@ moro_vol,108,88,5	script	Commander Hibba Agip#14301	4_M_REDSWORD,{
 	end;
 
 OnInit:
-	questinfo 7593, QTYPE_QUEST2;
-	setquestinfo_req 7593,7597,0;
-	setquestinfo_req 7593,7598,0;
-	setquestinfo_level 7593,160,175;
-	// 3789 = [ep4_3_mockill]
-	// SetQuestItem 7593 3789 0 "<"
-
-	questinfo 7598, QTYPE_QUEST2;
-	setquestinfo_req 7598,7593,0;
-	setquestinfo_req 7598,7597,1;
+	questinfo( QTYPE_QUEST2, QMARK_NONE, "BaseLevel >= 160 && ep4_3_mockill > 0 && !isbegin_quest(7593) && !isbegin_quest(7597) && !isbegin_quest(7598)" );
+	questinfo( QTYPE_QUEST2, QMARK_NONE, "!isbegin_quest(7598) && !isbegin_quest(7593) && isbegin_quest(7597) == 1" );
 	end;
 }
 
@@ -700,10 +682,7 @@ moro_vol,110,90,3	script	Adjutant Abidal#14302	4_M_SAGE_C,{
 	end;
 
 OnInit:
-	questinfo 7594, QTYPE_QUEST2;
-	setquestinfo_req 7594,7593,1;
-	setquestinfo_req 7594,7599,0;
-	setquestinfo_level 7594,160,175;
+	questinfo( QTYPE_QUEST2, QMARK_NONE, "BaseLevel >= 160 && !isbegin_quest(7594) && !isbegin_quest(7599) && isbegin_quest(7593) == 1" );
 	end;
 }
 
@@ -795,9 +774,7 @@ moro_vol,111,87,1	script	 Instructor Igrid#14303	4_M_CRU,{
 	end;
 
 OnInit:
-	questinfo 7581, QTYPE_QUEST2, 0;
-	setquestinfo_req 7581,7582,0;
-	setquestinfo_level 7581,140,175;
+	questinfo( QTYPE_QUEST2, QMARK_NONE, "BaseLevel >= 140 && !isbegin_quest(7581) && !isbegin_quest(7582)" );
 	end;
 }
 
@@ -1038,16 +1015,11 @@ moro_vol,136,84,3	script	Jeruth Hesranta#ep143	4_MAN_JERUTOO,{
 
 OnInit:
 /* officially disabled
-	questinfo 1, QTYPE_QUEST2;
-	setquestinfo_level 1,140,160;
+	questinfo QTYPE_QUEST2, QMARK_NONE, "BaseLevel >= 140 && BaseLevel <= 160";
+	// 3650 = [ep13_3_secret]
 	SetQuestItem 1 3650 24
 */
-
-	questinfo 7579, QTYPE_QUEST2;
-	setquestinfo_req 7579,7580,0;
-	setquestinfo_level 7579,140,160;
-	// 3650 = [ep13_3_secret]
-	// SetQuestItem 7579 3650 26 "<"
+	questinfo( QTYPE_QUEST2, QMARK_NONE, "BaseLevel >= 140 && BaseLevel <= 160 && ep13_3_secret > 26 && !isbegin_quest(7579) && !isbegin_quest(7580)" );
 	end;
 }
 

+ 20 - 29
npc/re/quests/quests_15_1.txt

@@ -72,8 +72,7 @@ prontera,121,77,3	script	Phantasmagorika Spokesperson	4_F_ZONDAGIRL,{
 	}
 
 OnInit:
-	questinfo 7610, QTYPE_QUEST, 1;
-	setquestinfo_level 7610, 140,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel >= 140 && isbegin_quest(7610) != 2" );
 	end;
 }
 geffen,135,64,3	duplicate(Phantasmagorika Spokesperson)	Phantasmagorika Spokesperson#2	4_F_ZONDAGIRL
@@ -187,8 +186,7 @@ moc_para01,38,175,5	script	Lime Evenor#evtat01	4_F_EDEN_OFFICER,{
 	}
 
 OnInit:
-	questinfo 7607, QTYPE_QUEST, 1;
-	setquestinfo_level 7607,140,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel >= 140 && isbegin_quest(7607) != 2" );
 	end;
 }
 
@@ -810,10 +808,8 @@ ver_eju,114,40,4	script	Manager Michelle#atnd05	4_M_LGTGUARD,{
 	}
 
 OnInit:
-	questinfo 7611,QTYPE_QUEST,1;
-	setquestinfo_req 7611,7612,0;
-	// questinfo 7612,QTYPE_QUEST,1;
-	// setquestinfo_req 7612,7611,2;// todo : display when HUNTING completed
+	// note: only 7612 required
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7612)" );
 	end;
 }
 
@@ -915,8 +911,7 @@ verus04,182,168,5	script	Receptionist Nara#atnd06	4_F_HUWOMAN,{
 	close;
 
 OnInit:
-	questinfo 7610, QTYPE_QUEST, 1;
-	setquestinfo_level 7610,140,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7610) != 2" );
 	end;
 }
 
@@ -1136,15 +1131,10 @@ verus04,179,165,5	script	Commander Louis#atnd07	4_M_LGTGUARD,{
 	}
 
 OnInit:
-	questinfo 7611,QTYPE_QUEST,1;
-	setquestinfo_req 7611,7612,0;
-	// questinfo 7612,QTYPE_QUEST,1;
-	// setquestinfo_req 7612,7611,2;// todo : display when HUNTING completed
-
-	questinfo 7613,QTYPE_QUEST,1;
-	setquestinfo_req 7613,7614,0;
-	// questinfo 7614,QTYPE_QUEST,1;
-	// setquestinfo_req 7614,7613,2;// todo : display when HUNTING completed
+	// note: only playtime quest required
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7612)" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7614)" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7617) == 1" );
 	end;
 }
 
@@ -2214,7 +2204,7 @@ verus04,141,193,5	script	Archaeologist Aures#atnd09	4_M_OLDSCHOLAR,{
 		mes "but I want to know how much progress Lloyd has made with his investigation.";
 		mes "Could you go check up on him?";
 		mes "Thank you.";
-		erasequest 7619;// Assisting the Excavation Team
+		completequest 7619;// Assisting the Excavation Team
 		setquest 7620;// Young Blood?
 		VER_MAIN = 9;
 		close3;
@@ -2407,7 +2397,7 @@ verus04,141,193,5	script	Archaeologist Aures#atnd09	4_M_OLDSCHOLAR,{
 	}
 
 OnInit:
-	questinfo 7615,QTYPE_QUEST,1;// inaccurate
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7619) && VER_MAIN < 8" );
 	end;
 }
 
@@ -3231,8 +3221,8 @@ S_Daily:
 	}
 
 OnInit:
-	questinfo 7615,QTYPE_QUEST,1;// inaccurate
-	setquestinfo_req 7615,7616,0;
+	// note: only playtime quest required
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(7616)" );
 	end;
 }
 
@@ -3292,6 +3282,10 @@ verus03,167,257,5	script	Access Controller Damhw#atnd	4_M_LGTGUARD,{
 	mes "Then I expect there to be good results!";
 	completequest 7641;// Access Permitted (note : player can enter in verus01 without this quest)
 	close;
+
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(7641) == 1" );
+	end;
 }
 verus03,172,257,3	duplicate(Access Controller Damhw#atnd)	Access Controller Gyuhw#atnd	4_M_LGTGUARD
 verus03,52,250,5	duplicate(Access Controller Damhw#atnd)	Access Controller Geuma#atnd	4_M_LGTGUARD
@@ -5675,8 +5669,7 @@ juperos_01,242,91,3	script	Rekenber Guard#atd06	4_M_LGTGUARD,{
 	close;
 
 OnInit:
-	questinfo 7610, QTYPE_QUEST, 1;
-	setquestinfo_level 7610, 140,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel >= 140 && isbegin_quest(7610) != 2" );
 	end;
 }
 
@@ -5697,8 +5690,7 @@ yuno_fild07,211,179,5	script	Excavator Guide#atd07	4_M_LGTGUARD,{
 	close;
 
 OnInit:
-	questinfo 7610, QTYPE_QUEST, 1;
-	setquestinfo_level 7610, 140,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel >= 140 && isbegin_quest(7610) != 2" );
 	end;
 }
 
@@ -7175,8 +7167,7 @@ yuno_fild07,216,157,5	script	Guide Elisha#ep15_1bs2	4_F_ZONDAGIRL,{
 	close;
 
 OnInit:
-	questinfo 11363,QTYPE_QUEST,1;
-	setquestinfo_req 11363,11364,0,11365,0,11366,0;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "!isbegin_quest(11363) && !isbegin_quest(11364) && !isbegin_quest(11365) && !isbegin_quest(11366)" );
 	end;
 }
 

+ 263 - 236
npc/re/quests/quests_rockridge.txt

@@ -163,8 +163,7 @@ OnTouch:
 	end;
 
 OnInit:
-	questinfo 7790, QTYPE_QUEST, 1;
-	setquestinfo_level 7790,100,175;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel >= 100 && !isbegin_quest(7790)" );
 	end;
 }
 
@@ -238,9 +237,9 @@ harboro1,80,218,5	script	Costello#harboro1	10210,{
 		mes "[Costello]";
 		mes "Go to the Sheriff and tell him that you came to help with the Buffalo Bandits situation. He'll tell you what to do.";
 		mes "Please help Pink Rose? I mean, Rock Ridge.";
+		rock_main_quest = 2;
 		erasequest 7791;
 		setquest 7792;// Problems in Rock Ridge 2
-		rock_main_quest = 2;
 		next;
 		select("Who's the sheriff?");
 		mes "[Costello]";
@@ -307,9 +306,9 @@ harboro1,80,218,5	script	Costello#harboro1	10210,{
 		next;
 		mes "^4d4dffCostello is looking around him and babbling anxiously.";
 		mes "I should go tell Wyatt that Costello wants to hear what the bandits have to say.^000000";
+		rock_main_quest = 4;
 		erasequest 7793;
 		setquest 7794;// Expected Response
-		rock_main_quest = 4;
 		close;
 	}
 	if (rock_main_quest == 4 || rock_main_quest == 5) {
@@ -417,10 +416,7 @@ harboro1,80,218,5	script	Costello#harboro1	10210,{
 	close;
 
 OnInit:
-	questinfo 7792, QTYPE_QUEST, 1;
-	setquestinfo_req 7792,7791,1;
-	questinfo 7794, QTYPE_QUEST, 1;
-	setquestinfo_req 7794,7793,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 1 || rock_main_quest == 3" );
 	end;
 }
 
@@ -539,9 +535,9 @@ har_in01,20,30,5	script	Wyatt Warp#har_in01	10211,{
 		mes "If you don't think you can find it, then just follow the railroad.";
 		mes "We'll wait for him by the train.";
 		mes "Let's go see how ugly that leader of the Buffalo Bandits, ^4d4dffJohnny James^000000, is.";
+		rock_main_quest = 5;
 		erasequest 7794;// Expected Response
 		setquest 7795;// Negotiation on the Railroad
-		rock_main_quest = 5;
 		close;
 	}
 	if (rock_main_quest == 5) {
@@ -598,6 +594,7 @@ har_in01,20,30,5	script	Wyatt Warp#har_in01	10211,{
 		mes "but Ivoka has a different idea.";
 		mes "Let's listen to what he has to say.";
 		rock_main_quest = 7;
+		questinfo_refresh();	// the update of the variable refreshes questinfo
 		close;
 	}
 	if (rock_main_quest == 7) {
@@ -627,9 +624,9 @@ har_in01,20,30,5	script	Wyatt Warp#har_in01	10211,{
 		next;
 		mes "[Wyatt Warp]";
 		mes "Who knows? You might find something to use against the Buffalo Bandits.";
+		rock_main_quest = 10;
 		erasequest 7798;
 		setquest 7799;// Shawn McCurdy's Weapon 1
-		rock_main_quest = 10;
 		close;
 	}
 	if (rock_main_quest == 10) {
@@ -729,9 +726,9 @@ har_in01,20,30,5	script	Wyatt Warp#har_in01	10211,{
 		mes "Now go to ^4d4dffShawn McCurdy^000000, will you?";
 		mes "We're almost done with the bandits!";
 		mes "Hang in there!";
+		rock_main_quest = 16;
 		erasequest 7804;// Location of the Ores
 		setquest 7805;// Location of the Ores 2
-		rock_main_quest = 16;
 		close;
 	}
 	if (rock_main_quest == 16) {
@@ -801,14 +798,14 @@ har_in01,20,30,5	script	Wyatt Warp#har_in01	10211,{
 		mes "Here, you've earned it.";
 		mes "Be proud of yourself.";
 		mes "And this is reward money.";
+		rock_main_quest = 22;
 		delitem 25248,1;// Purple_Ore_Crate
+		erasequest 7808;// Case Solved
 		setquest 9457;// Solved Anyway
 		completequest 9457;// Solved Anyway
-		completequest 7808;// Case Solved
 		getitem .@item_id[.@s],1;// Sheriff's_Left_Badge
 		getitem 25250,200;// Rock_Ridge_Coin
 		getexp 2500000,1200000;
-		rock_main_quest = 22;
 		close;
 	}
 	mes "[Wyatt Warp]";
@@ -818,18 +815,9 @@ har_in01,20,30,5	script	Wyatt Warp#har_in01	10211,{
 	close;
 
 OnInit:
-	questinfo 7793, QTYPE_QUEST, 1;
-	setquestinfo_req 7793,7792,1;
-	questinfo 7795, QTYPE_QUEST, 1;
-	setquestinfo_req 7795,7794,1;
-	questinfo 7797, QTYPE_QUEST, 1;
-	setquestinfo_req 7797,7796,1;
-	questinfo 7799, QTYPE_QUEST, 1;
-	setquestinfo_req 7799,7798,1;
-	questinfo 7809, QTYPE_QUEST, 1;
-	setquestinfo_req 7809,7803,1;
-	questinfo 7805, QTYPE_QUEST, 1;
-	setquestinfo_req 7805,7804,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW,
+		"rock_main_quest == 2 || rock_main_quest == 4 || rock_main_quest == 6 || rock_main_quest == 9 || rock_main_quest == 15 || rock_main_quest == 21" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 14 && isbegin_quest(7809) == 0 && countitem(25247) > 0" );
 	end;
 }
 
@@ -918,9 +906,9 @@ har_in01,14,24,5	script	Ivoka Skudi#rr03	10212,{
 		mes "Ivoka's right.";
 		mes "Let the spy see you talk to Johnny.";
 		mes "And convince Johnny that Albert has given up on you.";
+		rock_main_quest = 8;
 		erasequest 7796;
 		setquest 7797;// Hear Me Out 1
-		rock_main_quest = 8;
 		close;
 	}
 	if (rock_main_quest == 8) {
@@ -956,8 +944,7 @@ har_in01,14,24,5	script	Ivoka Skudi#rr03	10212,{
 	close;
 
 OnInit:
-	questinfo 7797, QTYPE_QUEST, 1;
-	setquestinfo_req 7797,7796,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 6 || rock_main_quest == 7" );
 	end;
 }
 
@@ -1134,9 +1121,9 @@ har_in01,89,26,5	script	Johnny James#har_in01	10213,{
 		mes "...Damn it!";
 		mes "Don't come back until you have ^4d4dffAlbert's mane^000000 to show to me, got it?";
 		mes "Now get outta here!";
+		rock_main_quest = 9;
 		erasequest 7797;
 		setquest 7798;// Perfectly Prime
-		rock_main_quest = 9;
 		close;
 	}
 	if (rock_main_quest < 14) {
@@ -1187,10 +1174,10 @@ har_in01,89,26,5	script	Johnny James#har_in01	10213,{
 		mes "It'll take many of you wimps to lift the elevator.";
 		mes "Good luck.";
 		mes "And don't forget to tell the sheriff I cooperated.";
+		rock_main_quest = 15;
 		delitem 25249,1;// Buffalo_Bandit_Mane
 		erasequest 7803;// Happy for Broken Trust
 		setquest 7804;// Location of the Ores
-		rock_main_quest = 15;
 		close;
 	}
 	if (rock_main_quest == 15) {
@@ -1205,8 +1192,7 @@ har_in01,89,26,5	script	Johnny James#har_in01	10213,{
 	close;
 
 OnInit:
-	questinfo 7798, QTYPE_QUEST, 1;
-	setquestinfo_req 7798,7797,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 8 || rock_main_quest == 14" );
 	end;
 }
 
@@ -1339,18 +1325,17 @@ rockrdg1,156,312,3	script	Unmoving Freight Train#	4_ENERGY_RED,{
 		classchange( HIDDEN_WARP_NPC, "Johnny James#revt04", bc_self );
 		sleep2 2000;
 		npctalk "Let's go back, shall we?", "Wyatt Warp#revt03", bc_self;
+		rock_main_quest = 6;
 		erasequest 7795;
 		setquest 7796;// Unexpected Arrest
-		rock_main_quest = 6;
 		setpcblock PCBLOCK_NPC, false;
-		close;
+		end;
 	}
 	mes "It's worthless and abandoned.";
 	close;
 
 OnInit:
-	questinfo 7796, QTYPE_QUEST, 1;
-	setquestinfo_req 7796,7795,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 5" );
 	end;
 }
 
@@ -1462,9 +1447,9 @@ harboro1,288,193,5	script	Shawn McCurdy#harboro1	10215,{
 		mes "[Shawn McCurdy]";
 		mes "The entrance to the second floor of the mine is ^4d4dfffarther south from the main entrance of the mine^000000.";
 		mes "I need to make some arrangements. I'll meet you there.";
+		rock_main_quest = 11;
 		erasequest 7799;
 		setquest 7800;// Shawn McCurdy's Weapon 2
-		rock_main_quest = 11;
 		close;
 	}
 	if (rock_main_quest == 11) {
@@ -1507,8 +1492,7 @@ harboro1,288,193,5	script	Shawn McCurdy#harboro1	10215,{
 	end;
 
 OnInit:
-	questinfo 7800, QTYPE_QUEST, 1;
-	setquestinfo_req 7800,7799,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 10" );
 	end;
 }
 
@@ -1594,8 +1578,7 @@ rockrdg2,347,216,5	script	Shawn McCurdy#rockrdg2	10215,{
 	close;
 
 OnInit:
-	questinfo 7801, QTYPE_QUEST, 1;
-	setquestinfo_req 7801,7800,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 11 || rock_main_quest == 12" );
 	end;
 }
 
@@ -1610,9 +1593,9 @@ rockmi2,10,86,5	script	Shawn McCurdy#rockmi2	10215,{
 		mes "[Shawn McCurdy]";
 		mes "In the meantime, I'll check the pulley that we need to use to go back up.";
 		mes "We made it in a hurry, so it's a bit unsafe sometimes. Hah hah.";
+		rock_main_quest = 12;
 		erasequest 7800;
 		setquest 7801;// Shawn McCurdy's Weapon 3
-		rock_main_quest = 12;
 		close;
 	}
 	if (rock_main_quest == 12) {
@@ -1652,9 +1635,9 @@ rockmi2,10,86,5	script	Shawn McCurdy#rockmi2	10215,{
 		mes "[Shawn McCurdy]";
 		mes "The bandits might stand guard around the mine.";
 		mes "Please be careful.";
+		rock_main_quest = 17;
 		erasequest 7805;// Location of the Ores 2
 		setquest 7806;// Location of the Ores 3
-		rock_main_quest = 17;
 		close;
 	}
 	if (rock_main_quest == 17) {
@@ -1709,8 +1692,7 @@ rockmi2,10,86,5	script	Shawn McCurdy#rockmi2	10215,{
 	end;
 
 OnInit:
-	questinfo 7801, QTYPE_QUEST, 1;
-	setquestinfo_req 7801,7800,1;
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 11 || rock_main_quest == 16" );
 	end;
 }
 
@@ -2362,8 +2344,11 @@ rockrdg1,341,133,3	script	Mrs. Cacturon#rrrem01	10220,{
 		close;
 	}
 	end;
-
-// fix me : questinfo when main quest completed only, playtime 2 or hunting 2
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && (!isbegin_quest(16078) || (checkquest(16078,HUNTING) == 2 && countitem(25246) >= 5))" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && checkquest(16080,HUNTING) == 2" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && checkquest(16079,PLAYTIME) == 2" );
+	end;
 }
 
 rockrdg1,339,134,3	script	Little Lecter#rrrem02	10222,{
@@ -3301,7 +3286,35 @@ harboro1,357,155,3	script	Steel Vigilante#1	4_M_MASKMAN,{
 		else npctalk "Enthusiastic Job Seeker: I want to work, too!", "Enthusiastic Job Seeker#11";
 		end;
 	}
-	getexp 8640,0;	// fix me: seem to be (base exp = count * mob base exp, job exp = count * mob job exp)
+	switch(.@quest_id) {
+	case 12391:// Buffaloes with Rifles
+		.@mob_id = 3736;
+		.@count = 15;
+		break;
+	case 12392:// Punishing the Red Masks
+		.@mob_id = 3737;
+		.@count = 15;
+		break;
+	case 12393:// Eye Patch Desperadoes
+		.@mob_id = 3738;
+		.@count = 15;
+		break;
+	case 12394:// Exterminate Gray Four-legged Beasts
+		.@mob_id = 3739;
+		.@count = 20;
+		break;
+	case 12395:// Exterminate Swamp Arclouzes
+		.@mob_id = 3787;
+		.@count = 10;
+		break;
+	case 12396:// Exterminate Brown Rats
+		.@mob_id = 3788;
+		.@count = 10;
+		break;
+	}
+	.@base_exp = getmonsterinfo(.@mob_id, MOB_BASEEXP) * .@count;
+	.@job_exp = getmonsterinfo(.@mob_id, MOB_JOBEXP) * .@count;
+	getexp .@base_exp, .@job_exp;
 	getitem 25250,2;// Rock Ridge Coin
 	erasequest .@quest_id;
 	setquest 12390;// Relieved for Now
@@ -3401,7 +3414,27 @@ harboro1,357,152,3	script	Steel Vigilante#2	4_M_ACROSS,{
 			end;
 		}
 	}
-	getexp 50800,47160;// fix me: seem to be (base exp = count * mob base exp, job exp = count * mob job exp)
+	switch(.@quest_id) {
+	case 12399:// Hunt Heinous Criminals
+		.@mob_id = 3747;
+		.@count = 10;
+		break;
+	case 12400:// Hunt Heinous Criminals
+		.@mob_id = 3748;
+		.@count = 10;
+		break;
+	case 12401:// Hunt Heinous Criminals
+		.@mob_id = 3749;
+		.@count = 10;
+		break;
+	case 12402:// Eliminate Dangerous Gas
+		.@mob_id = 3740;
+		.@count = 10;
+		break;
+	}
+	.@base_exp = getmonsterinfo(.@mob_id, MOB_BASEEXP) * .@count;
+	.@job_exp = getmonsterinfo(.@mob_id, MOB_JOBEXP) * .@count;
+	getexp .@base_exp, .@job_exp;
 	getitem 25250,3;// Rock Ridge Coin
 	erasequest .@quest_id;
 	setquest 12398;// You're Good
@@ -4319,7 +4352,11 @@ harboro2,164,80,5	script	Demon#srdg01	4_M_NFMAN,{
 	}
 	end;
 
-// fix me : questinfo when playtime 2
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && !isbegin_quest(14672) && checkquest(14673,PLAYTIME) == -1" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && checkquest(14672,HUNTING) == 2 && countitem(25277) > 9 && countitem(6213) > 0" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && checkquest(14673,PLAYTIME) == 2" );
+	end;
 }
 
 harboro2,166,80,5	script	Traces of Fire#srdg01	4_TRACE,{
@@ -4471,15 +4508,14 @@ rockrdg2,295,328,5	script	Frail Ghast#rockdaily	10231,{
 	end;
 
 OnInit:
-	questinfo 1298, QTYPE_QUEST, 1;
-	// fix me: questinfo 9457
-	// on hunting == -1
-	// on hunting == 2
-	// on playtime == 2
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && !isbegin_quest(1298) && checkquest(1299,PLAYTIME) == -1" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && checkquest(1298,HUNTING) == 2" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "rock_main_quest == 22 && checkquest(1299,PLAYTIME) == 2" );
 	end;
 }
 
-// fix me: incomplete, missing quest 1323 (same text)
+
+// Mrs. McPhy
 harboro1,347,65,3	script	Mrs. McPhy#rockdaily	1_F_MERCHANT_02,{
 	if (BaseLevel < 100) {
 		mes "[Mrs. McPhy]";
@@ -4495,7 +4531,6 @@ harboro1,347,65,3	script	Mrs. McPhy#rockdaily	1_F_MERCHANT_02,{
 		mes "Grr, I can't believe you! You're getting only half your dinner tonight!";
 		close;
 	}
-
 	switch( checkquest(1329,PLAYTIME) ) {
 	case -1:
 		break;
@@ -4516,29 +4551,91 @@ harboro1,347,65,3	script	Mrs. McPhy#rockdaily	1_F_MERCHANT_02,{
 			mes "We have no one else to turn to. Please come back if you change your mind.";
 			close;
 		}
-		if (rand(2) == 0) {
-			.@quest_id = 1325;
+		.@r = rand(4);
+		if (.@r == 0)
+			.@string$ = "glove";
+		else if (.@r == 1)
+			.@string$ = "alarm clock";
+		else if (.@r == 2)
 			.@string$ = "spoon";
-		}
-		else {
-			.@quest_id = 1326;
+		else
 			.@string$ = "hat";
-		}
 		mes "[Mrs. McPhy]";
 		mes "Today Spotty took a " + .@string$ + ". Could you look for it? It may be easily replaceable, but I can't keep buying things every time Spotty takes something.";
 		next;
 		mes "[Mrs. McPhy]";
 		mes "Go outside the village and talk to my husband. He'll tell you where it might be.";
 		erasequest 1329;// Spotty in the Dreamland
-		setquest .@quest_id;// Spotty, No!
+		setquest rand(1323,1327);// Spotty, No!
+		if (isbegin_quest(1330))	// debug
+			erasequest 1330;
+		if (isbegin_quest(1322) == 0) {	// debug
+			setquest 1322;
+			completequest 1322;
+		}
+		setquest 1330;// Spotty's Holes
 		close;
 	}
+	for ( .@quest_id = 1321; .@quest_id < 1329; .@quest_id++ ) {
+		if (isbegin_quest(.@quest_id) == 1)
+			break;
+	}
 
-	if (isbegin_quest(1330) == 0) {
-		if (isbegin_quest(1321) == 1) {// debug: player can't finish the quest on official
-			setquest 1330;
-			end;
-		}
+	switch( .@quest_id ) {
+	case 1321:
+	case 1323:
+	case 1324:
+	case 1325:
+	case 1326:
+	case 1327:
+		mes "[Mrs. McPhy]";
+		mes "Spotty would be a perfect dog if he doesn't take things out of the house. Sigh...";
+		if (isbegin_quest(1330) == 0)	// debug
+			setquest 1330;// Spotty's Holes
+		close;
+
+	case 1322:// First time doing the quest
+		mes "[Mrs. McPhy]";
+		mes "Adventurer, we've found the ring!";
+		mes "After all the time we spent searching the village, we've found it inside his house.";
+		next;
+		mes "[Mrs. McPhy]";
+		mes "By the way, what's that shining thing in your hand?";
+		next;
+		select("Show what's in your hand.");
+		mes "[Mrs. McPhy]";
+		mes "This is a Rock Ridge Coin. You can use it like money inside the village. I wasn't comfortable letting you help us for free. I'm glad you can at least have this.";
+		next;
+		mes "[Mrs. McPhy]";
+		mes "That Rock Ridge Coin is yours. Thank you for helping us.";
+		erasequest 1330;// Spotty's Holes
+		completequest 1322;// Found Something
+		getitem 25250,1;// Rock Ridge Coin
+		setquest 1329;// Spotty in the Dreamland
+		next;
+		mes "[Mrs. McPhy]";
+		mes "And, mm... Do you mind helping us again tomorrow? Spotty keeps taking things from the house to the wasteland, and we need them back.";
+		next;
+		mes "[Mrs. McPhy]";
+		mes "Spotty tired itself out and fell asleep. Could you come back tomorrow after 4:00 a.m.? We'll have something to give you next time.";
+		close;
+
+	case 1328:
+		mes "[Mrs. McPhy]";
+		mes "You've found it! Thank you so much. We've been trying to correct Spotty's behavior, and it's difficult.";
+		next;
+		mes "[Mrs. McPhy]";
+		mes "Here's the Rock Ridge Coin we promised. Thank you for searching the wasteland for us.";
+		erasequest 1328;// Found Missing Items
+		erasequest 1330;// Spotty's Holes
+		getitem 25250,1;// Rock Ridge Coin
+		setquest 1329;// Spotty in the Dreamland
+		next;
+		mes "[Mrs. McPhy]";
+		mes "Could you come back again? If you're not busy, you're welcome anytime after 4:00 a.m each day.";
+		close;
+
+	default:// first time
 		mes "[Mrs. McPhy]";
 		mes "How many times do I have to tell you to keep your dog away from our things? Do you really want me to send it away on a ship?";
 		next;
@@ -4644,72 +4741,34 @@ harboro1,347,65,3	script	Mrs. McPhy#rockdaily	1_F_MERCHANT_02,{
 		mes "S-should I? Uh, mm... Okay.";
 		close;
 	}
-	if (isbegin_quest(1321) == 1 || isbegin_quest(1325) == 1 || isbegin_quest(1326) == 1) {
-		mes "[Mrs. McPhy]";
-		mes "Spotty would be a perfect dog if he doesn't take things out of the house. Sigh...";
-		close;
-	}
-	if (isbegin_quest(1322) == 1) {// First time doing the quest
-		mes "[Mrs. McPhy]";
-		mes "Adventurer, we've found the ring!";
-		mes "After all the time we spent searching the village, we've found it inside his house.";
-		next;
-		mes "[Mrs. McPhy]";
-		mes "By the way, what's that shining thing in your hand?";
-		next;
-		select("Show what's in your hand.");
-		mes "[Mrs. McPhy]";
-		mes "This is a Rock Ridge Coin. You can use it like money inside the village. I wasn't comfortable letting you help us for free. I'm glad you can at least have this.";
-		next;
-		mes "[Mrs. McPhy]";
-		mes "That Rock Ridge Coin is yours. Thank you for helping us.";
-		erasequest 1322;// Found Something
-		completequest 1330;// Spotty's Holes
-		getitem 25250,1;// Rock Ridge Coin
-		setquest 1329;// Spotty in the Dreamland
-		next;
-		mes "[Mrs. McPhy]";
-		mes "And, mm... Do you mind helping us again tomorrow? Spotty keeps taking things from the house to the wasteland, and we need them back.";
-		next;
-		mes "[Mrs. McPhy]";
-		mes "Spotty tired itself out and fell asleep. Could you come back tomorrow after 4:00 a.m.? We'll have something to give you next time.";
-		close;
-	}
-	if (isbegin_quest(1328) == 1) {
-		mes "[Mrs. McPhy]";
-		mes "You've found it! Thank you so much. We've been trying to correct Spotty's behavior, and it's difficult.";
-		next;
-		mes "[Mrs. McPhy]";
-		mes "Here's the Rock Ridge Coin we promised. Thank you for searching the wasteland for us.";
-		erasequest 1328;// Found Missing Items
-		setquest 1329;// Spotty in the Dreamland
-		getitem 25250,1;// Rock Ridge Coin
-		next;
-		mes "[Mrs. McPhy]";
-		mes "Could you come back again? If you're not busy, you're welcome anytime after 4:00 a.m each day.";
-		close;
-	}
 	end;
 
 OnInit:
-	questinfo 1321, QTYPE_QUEST, 1;
-	setquestinfo_level 1321,100,175;
-	questinfo 1329, QTYPE_QUEST, 1;
-	setquestinfo_req 1329,1322,1;
-	// questinfo if playtime == 2
+	questinfo( QTYPE_QUEST, QMARK_YELLOW,
+		"BaseLevel > 99 && checkquest(1329,PLAYTIME) == -1 && !isbegin_quest(1321) && !isbegin_quest(1322) && !isbegin_quest(1323) && !isbegin_quest(1324) && !isbegin_quest(1325) && !isbegin_quest(1326) && !isbegin_quest(1327) && !isbegin_quest(1328)" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel > 99 && (isbegin_quest(1322) == 1 || isbegin_quest(1328) == 1)" );
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "BaseLevel > 99 && checkquest(1329,PLAYTIME) == 2" );
 	end;
 }
 
 rockrdg1,36,250,5	script	Mr. McPhy#rkdlout	4W_M_02,{
-	if (isbegin_quest(1321) == 1) {
+	for ( .@quest_id = 1321; .@quest_id < 1330; .@quest_id++ ) {
+		if (isbegin_quest(.@quest_id) == 1)
+			break;
+	}
+	switch( .@quest_id ) {
+	case 1321:
 		mes "[Mr. McPhy]";
 		mes "My wife said Spotty went out the back door and ran straight past the barbed-wire fence.";
 		next;
 		mes "[Mr. McPhy]";
 		mes "But he couldn't have gone too far because he came back to me pretty quickly. Why don't you walk along the fence and look for holes with white hair floating around them?";
 		close;
-	}
-	if (isbegin_quest(1325) == 1 || isbegin_quest(1326) == 1) {
+	case 1323:
+	case 1324:
+	case 1325:
+	case 1326:
+	case 1327:
 		mes "[Mr. McPhy]";
 		mes "My wife sent me here to give you directions, but I have no idea where he might have been.";
 		next;
@@ -4719,13 +4778,13 @@ rockrdg1,36,250,5	script	Mr. McPhy#rkdlout	4W_M_02,{
 		mes "[Mr. McPhy]";
 		mes "And he doesn't seem to venture out too far. It's probably because he's still young. I've marked three possible locations in the middle of your map. You don't have to look further than those locations.";
 		close;
-	}
-	if (isbegin_quest(1322) == 1 || isbegin_quest(1328) == 1) {
+	case 1322:
+	case 1328:
 		mes "[Mr. McPhy]";
 		mes "Did you find something? Then let's go back!";
+		classchange( HIDDEN_WARP_NPC, "", bc_self );
 		close;
-	}
-	if (isbegin_quest(1329) == 1) {
+	case 1329:
 		mes "[Mr. McPhy]";
 		mes "Ah, Adventurer. Shhh! Please keep your voice down: the Buffalo Bandits might hear us.";
 		next;
@@ -4738,138 +4797,107 @@ rockrdg1,36,250,5	script	Mr. McPhy#rkdlout	4W_M_02,{
 		mes "[Mr. McPhy]";
 		mes "She didn't say anything when I quit my job because I was too scared of the Buffalo Bandits. She's a good woman.";
 		close;
+	default:
+		mes "[Mr. McPhy]";
+		mes "*Gasp* Buffalo Bandits!";
+		close2;
+		classchange( HIDDEN_WARP_NPC, "", bc_self );
+		end;
 	}
-	mes "[Mr. McPhy]";
-	mes "*Gasp* Buffalo Bandits!";
-	close2;
-	classchange( HIDDEN_WARP_NPC, "", bc_self );
 	end;
 
-// OnInit:
-// 	questinfo 1321, QTYPE_QUEST, 1;// fix me: quest ID
-// 	end;
-}
-
-rockrdg1,43,145,3	script	Hole#rkq1321	4_CRACK,{
-	if (isbegin_quest(1321) == 1) {
-		mes "- There's a hole in the ground.";
-		mes "It has something shining inside it,";
-		next;
-		mes "But it's not a gold ring.";
-		mes "I should take it with me for now. -";
-		erasequest 1321;// Spotty and Her Ring
-		setquest 1322;// Found Something
-		close;
-	}
-	mes "- There's a hole in the ground.";
-	mes "It only has a stone sticking out of the ground inside it. -";
-	close;
-}
-
-rockrdg1,170,233,3	script	Hole#rkdqgd1	4_CRACK,{
-	if (isbegin_quest(1321) == 0) {
-		mes "- There's a hole in the ground.";
-		mes "It has a little bit of white hair scattered inside it. -";
-		close;
-	}
-	mes "- There's a hole in the ground.";
-	mes "It's empty. -";
-	next;
-	mes "- I don't have to search farther than this. -";
-	close;
-
 OnInit:
-	// questinfo , QTYPE_QUEST, 1;// fix me: 1321 required ?
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(1321) > 0" );
 	end;
 }
 
-rockrdg1,183,143,3	script	Hole#rkdqgd2	4_CRACK,{
-	if (isbegin_quest(1321) == 0) {
+rockrdg1,170,233,3	script	Hole#rkdqgd1	4_CRACK,{
+	setarray .@list[0],1321,1323,1324,1325,1326,1327;
+	.@size = getarraysize(.@list);
+	for ( .@i = 0; .@i < .@size; .@i++ ) {
+		if (isbegin_quest(.@list[.@i]))
+			break;
+	}
+	if (.@list[.@i] == 0) {
 		mes "- There's a hole in the ground.";
 		mes "It has a little bit of white hair scattered inside it. -";
 		close;
 	}
 	mes "- There's a hole in the ground.";
-	mes "It's empty. -";
+	if (strnpcinfo(2) == "rkdqgd3")
+		mes "It only has wet soil inside it. -";
+	else
+		mes "It's empty. -";
 	next;
 	mes "- I don't have to search farther than this. -";
 	close;
 
 OnInit:
-	// questinfo , QTYPE_QUEST, 1;// fix me: 1321 required ?
+	questinfo( QTYPE_QUEST, QMARK_YELLOW, "isbegin_quest(1330) > 0" );
 	end;
 }
+rockrdg1,183,143,3	duplicate(Hole#rkdqgd1)	Hole#rkdqgd2	4_CRACK
+rockrdg1,215,72,3	duplicate(Hole#rkdqgd1)	Hole#rkdqgd3	4_CRACK
 
-rockrdg1,215,72,3	script	Hole#rkdqgd3	4_CRACK,{
-	if (isbegin_quest(1321) == 0) {
-		mes "- There's a hole in the ground.";
-		mes "It has a little bit of white hair scattered inside it. -";
-		close;
-	}
-	mes "- There's a hole in the ground.";
-	mes "It only has wet soil inside it. -";
-	next;
-	mes "- I don't have to search farther than this. -";
-	close;
 
-OnInit:
-	// questinfo , QTYPE_QUEST, 1;// fix me: 1321 required ?
-	end;
-}
+rockrdg1,43,145,3	script	Hole#rkq1321	4_CRACK,{
+	.@quest_id_npc = atoi( replacestr(strnpcinfo(2), "rkq", "") ) ;
+	setarray .@list[0],1321,1323,1324,1325,1326,1327;
+	.@size = getarraysize(.@list);
+	for ( .@i = 0; .@i < .@size; .@i++ ) {
+		if (isbegin_quest(.@list[.@i]))
+			break;
+	}
 
-rockrdg1,58,195,3	script	Hole#rkq1324	4_CRACK,{
 	mes "- There's a hole in the ground.";
-	mes "It has a tumbleweed rolling around inside it. -";
-	close;
-}
-
-rockrdg1,109,60,3	script	Hole#rkq1326	4_CRACK,{
-	if (isbegin_quest(1326) == 1) {
-		mes "- There's a hole in the ground.";
-		mes "It has something inside it. -";
-		next;
-		mes "- This must be what Mr. McPhy is looking for. I should bring it to her. -";
-		erasequest 1326;// Spotty, No!
-		setquest 1328;// Found Missing Items
+	if (.@list[.@i] == 0) {
+		mes "It has a little bit of white hair scattered inside it. -";
 		close;
 	}
-	mes "- There's a hole in the ground.";
-	mes "It has a little bit of white hair scattered inside it. -";
-	close;
-}
-
-rockrdg1,71,149,3	script	Hole#rkq1325	4_CRACK,{
-	if (isbegin_quest(1325) == 1) {
-		mes "- There's a hole in the ground.";
+	if (.@list[.@i] == .@quest_id_npc) {
+		if (.@list[.@i] == 1321) {
+			mes "It has something shining inside it,";
+			next;
+			mes "But it's not a gold ring.";
+			mes "I should take it with me for now. -";
+			erasequest 1321;
+			setquest 1322;// Found Something
+			close;
+		}
 		mes "It has something inside it. -";
 		next;
 		mes "- This must be what Mr. McPhy is looking for. I should bring it to her. -";
-		erasequest 1325;// Spotty, No!
+		erasequest .@list[.@i];
 		setquest 1328;// Found Missing Items
 		close;
 	}
-	mes "- There's a hole in the ground.";
-	mes "A small insect is scuttling around inside it. -";
-	close;
-}
-
-rockrdg1,100,230,3	script	Hole#rkq1323	4_CRACK,{
-	if (isbegin_quest(1323) == 1) {
-		mes "- There's a hole in the ground.";
-		mes "It has something inside it. -";
-		next;
-		mes "- This must be what Mr. McPhy is looking for. I should bring it to her. -";
-		erasequest 1323;// Spotty, No!
-		setquest 1328;// Found Missing Items
+	switch( .@list[.@i] ) {
+	case 1321:
+		mes "It only has a stone sticking out of the ground inside it. -";
+		close;
+	case 1323:
+		mes "It has a small animal bone inside it. -";
+		close;
+	case 1324:
+		mes "It has a tumbleweed rolling around inside it. -";
+		close;
+	case 1325:
+		mes "A small insect is scuttling around inside it. -";
+		close;
+	case 1326:
+		mes "It only has weeds growing inside it. -";
+		close;
+	case 1327:
+		mes "It's only filled with dust. -";
 		close;
 	}
 }
+rockrdg1,100,230,3	duplicate(Hole#rkq1321)	Hole#rkq1323	4_CRACK
+rockrdg1,58,195,3	duplicate(Hole#rkq1321)	Hole#rkq1324	4_CRACK
+rockrdg1,71,149,3	duplicate(Hole#rkq1321)	Hole#rkq1325	4_CRACK
+rockrdg1,109,60,3	duplicate(Hole#rkq1321)	Hole#rkq1326	4_CRACK
+rockrdg1,191,101,3	duplicate(Hole#rkq1321)	Hole#rkq1327	4_CRACK
 
-rockrdg1,100,230,3	script	Unknown #50572	4_CRACK,{
-	mes "- There's a hole in the ground.";
-	mes "It has a little bit of white hair scattered inside it. -";
-	close;
-}
 
 // Mr. McPhy quests
 harboro1,345,61,3	script	Spotty#rockdaily	4_DOG01,{
@@ -4880,17 +4908,17 @@ harboro1,345,61,3	script	Spotty#rockdaily	4_DOG01,{
 }
 
 harboro1,344,61,5	script	Mr. McPhy#rockdaily	4W_M_02,{
-	switch( isbegin_quest(1330) ) {
-	case 0:// First time
-		mes "[Mr. McPhy]";
-		mes "Spotty, do you want a treat? Hmm?";
-		close;
-	case 1:
-		if (isbegin_quest(1321) == 1) {
+	switch( isbegin_quest(1322) ) {
+	case 0:
+		if (isbegin_quest(1321) == 0) {
 			mes "[Mr. McPhy]";
-			mes "My wife and I decided to check the village separately, but I'm not sure if we'd be done before you're back.";
+			mes "Spotty, do you want a treat? Hmm?";
 			close;
 		}
+		mes "[Mr. McPhy]";
+		mes "My wife and I decided to check the village separately, but I'm not sure if we'd be done before you're back.";
+		close;
+	case 1:
 		mes "[Mr. McPhy]";
 		mes "Oh, poor Spotty. You're itching from losing so much hair. Do you want your daddy to brush you?";
 		close;
@@ -4985,11 +5013,10 @@ harboro1,344,61,5	script	Mr. McPhy#rockdaily	4W_M_02,{
 	}
 	end;
 
-// OnInit:
-	// fix me: questinfo 1331, QTYPE_QUEST, 1;
-	// playtime == 2
-	// display when hunting completed
-	// end;
+OnInit:
+	questinfo( QTYPE_QUEST, QMARK_YELLOW,
+		"isbegin_quest(1322) == 2 && (checkquest(1332,PLAYTIME) == 2 || checkquest(1332,PLAYTIME) == -1 && (checkquest(1331,HUNTING) == -1 || checkquest(1331,HUNTING) == 2))" );
+	end;
 }
 
 // Misc NPCs

+ 3 - 3
src/map/achievement.cpp

@@ -743,7 +743,7 @@ int *achievement_level(struct map_session_data *sd, bool flag)
 	return info;
 }
 
-static bool achievement_check_condition( struct script_code* condition, struct map_session_data* sd, const std::array<int, MAX_ACHIEVEMENT_OBJECTIVES> count ){
+bool achievement_check_condition( struct script_code* condition, struct map_session_data* sd ){
 	// Save the old script the player was attached to
 	struct script_state* previous_st = sd->st;
 
@@ -834,7 +834,7 @@ static bool achievement_update_objectives(struct map_session_data *sd, std::shar
 			if (!ad->condition)
 				return false;
 
-			if (!achievement_check_condition(ad->condition, sd, current_count)) // Parameters weren't met
+			if (!achievement_check_condition(ad->condition, sd)) // Parameters weren't met
 				return false;
 
 			changed = true;
@@ -855,7 +855,7 @@ static bool achievement_update_objectives(struct map_session_data *sd, std::shar
 					current_count[it.first] += update_count[it.first];
 			}
 
-			if (!achievement_check_condition(ad->condition, sd, current_count)) // Parameters weren't met
+			if (!achievement_check_condition(ad->condition, sd)) // Parameters weren't met
 				return false;
 
 			changed = true;

+ 1 - 0
src/map/achievement.hpp

@@ -120,6 +120,7 @@ void achievement_check_reward(struct map_session_data *sd, int achievement_id);
 void achievement_free(struct map_session_data *sd);
 int achievement_check_progress(struct map_session_data *sd, int achievement_id, int type);
 int *achievement_level(struct map_session_data *sd, bool flag);
+bool achievement_check_condition(struct script_code* condition, struct map_session_data* sd);
 void achievement_get_titles(uint32 char_id);
 void achievement_update_objective(struct map_session_data *sd, enum e_achievement_group group, uint8 arg_count, ...);
 void achievement_read_db(void);

+ 4 - 1
src/map/clif.cpp

@@ -17057,9 +17057,12 @@ void clif_quest_update_status(struct map_session_data *sd, int quest_id, bool ac
 ///     1 = orange
 ///     2 = green
 ///     3 = purple
-void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, short effect, short color)
+void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, e_questinfo_types effect, e_questinfo_markcolor color)
 {
 #if PACKETVER >= 20090218
+	nullpo_retv(sd);
+	nullpo_retv(bl);
+
 	int fd = sd->fd;
 
 	WFIFOHEAD(fd, packet_len(0x446));

+ 3 - 1
src/map/clif.hpp

@@ -13,6 +13,8 @@
 #include "../common/mmo.hpp"
 #include "../common/timer.hpp" // t_tick
 
+#include "script.hpp"
+
 struct Channel;
 struct clan;
 struct item;
@@ -910,7 +912,7 @@ void clif_quest_add(struct map_session_data * sd, struct quest * qd);
 void clif_quest_delete(struct map_session_data * sd, int quest_id);
 void clif_quest_update_status(struct map_session_data * sd, int quest_id, bool active);
 void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd, int mobid);
-void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, short state, short color);
+void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, e_questinfo_types effect, e_questinfo_markcolor color);
 void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, bool quest, bool lost);
 
 int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type);

+ 16 - 102
src/map/map.cpp

@@ -3682,15 +3682,10 @@ 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->zone = src_map->zone;
-	dst_map->qi_count = 0;
-	dst_map->qi_data = NULL;
 
 	// Mimic questinfo
-	if (src_map->qi_count) {
-		dst_map->qi_count = src_map->qi_count;
-		CREATE(dst_map->qi_data, struct questinfo, dst_map->qi_count);
-		memcpy(dst_map->qi_data, src_map->qi_data, dst_map->qi_count * sizeof(struct questinfo));
-	}
+	if (!src_map->qi_data.empty())
+		src_map->qi_data = dst_map->qi_data;
 }
 
 /**
@@ -3905,8 +3900,6 @@ int map_readallmaps (void)
 
 		memset(&mapdata->save, 0, sizeof(struct point));
 		mapdata->damage_adjust = {};
-		mapdata->qi_count = 0;
-		mapdata->qi_data = NULL;
 		mapdata->channel = NULL;
 	}
 
@@ -4353,110 +4346,31 @@ int log_sql_init(void)
 	return 0;
 }
 
-struct questinfo *map_add_questinfo(int m, struct questinfo *qi) {
-	unsigned short i;
+void map_remove_questinfo(int m, struct npc_data *nd) {
 	struct map_data *mapdata = map_getmapdata(m);
+	struct s_questinfo *qi;
 
-	/* duplicate, override */
-	for(i = 0; i < mapdata->qi_count; i++) {
-		if( &mapdata->qi_data[i] && mapdata->qi_data[i].nd == qi->nd && mapdata->qi_data[i].quest_id == qi->quest_id)
-			break;
-	}
-
-	if( i == mapdata->qi_count )
-		RECREATE(mapdata->qi_data, struct questinfo, ++mapdata->qi_count);
-	else { // clear previous criteria on override
-		if (mapdata->qi_data[i].jobid)
-			aFree(mapdata->qi_data[i].jobid);
-		mapdata->qi_data[i].jobid = NULL;
-		mapdata->qi_data[i].jobid_count = 0;
-		if (mapdata->qi_data[i].req)
-			aFree(mapdata->qi_data[i].req);
-		mapdata->qi_data[i].req = NULL;
-		mapdata->qi_data[i].req_count = 0;
-	}
-
-	memcpy(&mapdata->qi_data[i], qi, sizeof(struct questinfo));
-	return &mapdata->qi_data[i];
-}
-
-bool map_remove_questinfo(int m, struct npc_data *nd) {
-	unsigned short i, c;
-	struct map_data *mapdata = map_getmapdata(m);
-
-	for(i = 0; i < mapdata->qi_count; i++) {
-		struct questinfo *qi = &mapdata->qi_data[i];
-		if( qi->nd == nd ) {
-			if (qi->jobid)
-				aFree(qi->jobid);
-			qi->jobid = NULL;
-			qi->jobid_count = 0;
-
-			if (qi->req)
-				aFree(qi->req);
-			qi->req = NULL;
-			qi->req_count = 0;
-
-			memset(&mapdata->qi_data[i], 0, sizeof(mapdata->qi_data[i]));
-		}
-	}
-
-	// Move next data to empty slot
-	for(i = 0, c = 0; i < mapdata->qi_count; i++) {
-		struct questinfo *qi = &mapdata->qi_data[i];
-		if (!qi || !qi->nd)
-			continue;
+	nullpo_retv(nd);
+	nullpo_retv(mapdata);
 
-		if (i != c) {
-			mapdata->qi_data[c] = mapdata->qi_data[i];
-			memset(&mapdata->qi_data[i], 0, sizeof(mapdata->qi_data[i]));
+	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);
 		}
-
-		c++;
-	}
-
-	if (!(mapdata->qi_count = c)) {
-		aFree(mapdata->qi_data);
-		mapdata->qi_data = NULL;
 	}
-	else
-		RECREATE(mapdata->qi_data, struct questinfo, mapdata->qi_count);
-
-	return true;
 }
 
 static void map_free_questinfo(struct map_data *mapdata) {
-	unsigned short i;
-	if (!mapdata)
-		return;
+	nullpo_retv(mapdata);
 
-	for(i = 0; i < mapdata->qi_count; i++) {
-		if (mapdata->qi_data[i].jobid)
-			aFree(mapdata->qi_data[i].jobid);
-		mapdata->qi_data[i].jobid = NULL;
-		mapdata->qi_data[i].jobid_count = 0;
-		if (mapdata->qi_data[i].req)
-			aFree(mapdata->qi_data[i].req);
-		mapdata->qi_data[i].req = NULL;
-		mapdata->qi_data[i].req_count = 0;
+	for (const auto &it : mapdata->qi_data) {
+		if (it.condition)
+			script_free_code(it.condition);
 	}
-	aFree(mapdata->qi_data);
-	mapdata->qi_data = NULL;
-	mapdata->qi_count = 0;
-}
-
-struct questinfo *map_has_questinfo(int m, struct npc_data *nd, int quest_id) {
-	unsigned short i;
-	struct map_data *mapdata = map_getmapdata(m);
 
-	for (i = 0; i < mapdata->qi_count; i++) {
-		struct questinfo *qi = &mapdata->qi_data[i];
-		if (qi->nd == nd && qi->quest_id == quest_id) {
-			return qi;
-		}
-	}
-
-	return NULL;
+	mapdata->qi_data.clear();
 }
 
 /**

+ 9 - 21
src/map/map.hpp

@@ -18,6 +18,8 @@
 #include "../common/timer.hpp"
 #include "../config/core.hpp"
 
+#include "script.hpp"
+
 struct npc_data;
 struct item_data;
 struct Channel;
@@ -705,22 +707,11 @@ struct iwall_data {
 	bool shootable;
 };
 
-struct questinfo_req {
-	unsigned int quest_id;
-	unsigned state : 2; // 0: Doesn't have, 1: Inactive, 2: Active, 3: Complete //! TODO: CONFIRM ME!!
-};
-
-struct questinfo {
+struct s_questinfo {
 	struct npc_data *nd;
-	unsigned short icon;
-	unsigned char color;
-	int quest_id;
-	unsigned short min_level,
-		max_level;
-	uint8 req_count;
-	uint8 jobid_count;
-	struct questinfo_req *req;
-	unsigned short *jobid;
+	e_questinfo_types icon;
+	e_questinfo_markcolor color;
+	struct script_code* condition;
 };
 
 struct map_data {
@@ -760,9 +751,8 @@ struct map_data {
 	struct Channel *channel;
 
 	/* ShowEvent Data Cache */
-	struct questinfo *qi_data;
-	unsigned short qi_count;
-	
+	std::vector<s_questinfo> qi_data;
+
 	/* speeds up clif_updatestatus processing by causing hpmeter to run only when someone with the permission can view it */
 	unsigned short hpmeter_visible;
 };
@@ -1088,9 +1078,7 @@ struct mob_data * map_id2boss(int id);
 // reload config file looking only for npcs
 void map_reloadnpc(bool clear);
 
-struct questinfo *map_add_questinfo(int m, struct questinfo *qi);
-bool map_remove_questinfo(int m, struct npc_data *nd);
-struct questinfo *map_has_questinfo(int m, struct npc_data *nd, int quest_id);
+void map_remove_questinfo(int m, struct npc_data *nd);
 
 /// Bitfield of flags for the iterator.
 enum e_mapitflags

+ 30 - 62
src/map/pc.cpp

@@ -1463,7 +1463,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
 	sd->vars_ok = false;
 	sd->vars_received = 0x0;
 
-	sd->qi_display = NULL;
+	sd->qi_display = nullptr;
 	sd->qi_count = 0;
 
 	//warp player
@@ -4909,6 +4909,7 @@ enum e_additem_result pc_additem(struct map_session_data *sd,struct item *item,i
 	}
 
 	achievement_update_objective(sd, AG_GET_ITEM, 1, id->value_sell);
+	pc_show_questinfo(sd);
 
 	return ADDITEM_SUCCESS;
 }
@@ -4945,6 +4946,8 @@ char pc_delitem(struct map_session_data *sd,int n,int amount,int type, short rea
 	if(!(type&2))
 		clif_updatestatus(sd,SP_WEIGHT);
 
+	pc_show_questinfo(sd);
+
 	return 0;
 }
 
@@ -6898,6 +6901,8 @@ int pc_checkjoblevelup(struct map_session_data *sd)
 
 	npc_script_event(sd, NPCE_JOBLVUP);
 	achievement_update_objective(sd, AG_GOAL_LEVEL, 1, sd->status.job_level);
+
+	pc_show_questinfo(sd);
 	return 1;
 }
 
@@ -12863,7 +12868,7 @@ void pc_validate_skill(struct map_session_data *sd) {
  * @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 questinfo *qi, bool show) {
+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) {
@@ -12876,9 +12881,9 @@ static void pc_show_questinfo_sub(struct map_session_data *sd, bool *qi_display,
 		if ((*qi_display) != 0) {
 			(*qi_display) = 0;
 #if PACKETVER >= 20120410
-			clif_quest_show_event(sd, &qi->nd->bl, 9999, 0);
+			clif_quest_show_event(sd, &qi->nd->bl, QTYPE_NONE, QMARK_NONE);
 #else
-			clif_quest_show_event(sd, &qi->nd->bl, 0, 0);
+			clif_quest_show_event(sd, &qi->nd->bl, QTYPE_QUEST, QMARK_NONE);
 #endif
 		}
 	}
@@ -12890,75 +12895,36 @@ static void pc_show_questinfo_sub(struct map_session_data *sd, bool *qi_display,
  **/
 void pc_show_questinfo(struct map_session_data *sd) {
 #if PACKETVER >= 20090218
-	struct questinfo *qi = NULL;
-	unsigned short i;
-	uint8 j;
-	int8 mystate = 0;
-	bool failed = false;
-
 	nullpo_retv(sd);
 
 	if (sd->bl.m < 0 || sd->bl.m >= MAX_MAPINDEX)
 		return;
 
 	struct map_data *mapdata = map_getmapdata(sd->bl.m);
+	nullpo_retv(mapdata);
 
-	if (!mapdata->qi_count || !mapdata->qi_data)
+	if (mapdata->qi_data.empty())
 		return;
-	if (mapdata->qi_count != sd->qi_count)
+	if (mapdata->qi_data.size() != sd->qi_count)
 		return; // init was not called yet
 
-	for(i = 0; i < mapdata->qi_count; i++) {
-		qi = &mapdata->qi_data[i];
-
-		if (!qi)
-			continue;
-
-		if (quest_check(sd, qi->quest_id, HAVEQUEST) != -1) { // Check if quest is not started
-			pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, false);
-			continue;
-		}
-
-		// Level range checks
-		if (sd->status.base_level < qi->min_level || sd->status.base_level > qi->max_level) {
-			pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, false);
-			continue;
-		}
+	struct s_questinfo *qi = nullptr;
+	bool show;
 
-		// Quest requirements
-		if (qi->req_count) {
-			failed = false;
-			for (j = 0; j < qi->req_count; j++) {
-				mystate = quest_check(sd, qi->req[j].quest_id, HAVEQUEST);
-				mystate = mystate + (mystate < 1);
-				if (mystate != qi->req[j].state) {
-					failed = true;
-					break;
-				}
-			}
-			if (failed) {
-				pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, false);
-				continue;
-			}
-		}
+	for (int i = 0; i < mapdata->qi_data.size(); i++) {
+		qi = &mapdata->qi_data[i];
+ 		if (!qi)
+ 			continue;
 
-		// Job requirements
-		if (qi->jobid_count) {
-			failed = true;
-			for (j = 0; j < qi->jobid_count; j++) {
-				if (pc_mapid2jobid(sd->class_,sd->status.sex) == qi->jobid[j]) {
-					pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, true);
-					failed = false;
-					break;
-				}
-			}
-			if (!failed)
-				continue;
-			pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, false);
-		}
+		if (!qi->condition)
+			show = true;
 		else {
-			pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, true);
+			if (achievement_check_condition(qi->condition, sd))
+				show = true;
+			else
+				show = false;
 		}
+		pc_show_questinfo_sub(sd, &sd->qi_display[i], qi, show);
 	}
 #endif
 }
@@ -12973,7 +12939,7 @@ void pc_show_questinfo_reinit(struct map_session_data *sd) {
 
 	if (sd->qi_display) {
 		aFree(sd->qi_display);
-		sd->qi_display = NULL;
+		sd->qi_display = nullptr;
 	}
 	sd->qi_count = 0;
 
@@ -12981,10 +12947,12 @@ void pc_show_questinfo_reinit(struct map_session_data *sd) {
 		return;
 
 	struct map_data *mapdata = map_getmapdata(sd->bl.m);
+	nullpo_retv(mapdata);
 
-	if (!mapdata->qi_count || !mapdata->qi_data)
+	if (mapdata->qi_data.empty())
 		return;
-	CREATE(sd->qi_display, bool, (sd->qi_count = mapdata->qi_count));
+
+	CREATE(sd->qi_display, bool, (sd->qi_count = mapdata->qi_data.size()));
 #endif
 }
 

+ 1 - 1
src/map/pc.hpp

@@ -643,7 +643,7 @@ struct map_session_data {
 
 	/* ShowEvent Data Cache flags from map */
 	bool *qi_display;
-	unsigned short qi_count;
+	int qi_count;
 
 	// temporary debug [flaviojs]
 	const char* debug_file;

+ 1 - 0
src/map/quest.cpp

@@ -322,6 +322,7 @@ void quest_update_objective(TBL_PC *sd, int mob_id)
 //				intif_broadcast_obtain_special_item(sd, dropitem->nameid, dropitem->mob_id, ITEMOBTAIN_TYPE_MONSTER_ITEM);
 		}
 	}
+	pc_show_questinfo(sd);
 }
 
 /**

+ 62 - 149
src/map/script.cpp

@@ -19287,32 +19287,21 @@ BUILDIN_FUNC(readbook)
 /******************
 Questlog script commands
 *******************/
- /**
- * Add job criteria to questinfo
- * @param qi Quest Info
- * @param job
- * @author [Cydh]
- **/
-static void buildin_questinfo_setjob(struct questinfo *qi, int job) {
-	RECREATE(qi->jobid, unsigned short, qi->jobid_count+1);
-	qi->jobid[qi->jobid_count++] = job;
-}
-/**
- * questinfo <Quest ID>,<Icon>{,<Map Mark Color>{,<Job Class>}};
- **/
+
+/// questinfo <Icon>{,<Map Mark Color>{,<condition>}};
 BUILDIN_FUNC(questinfo)
 {
 	TBL_NPC* nd = map_id2nd(st->oid);
-	int quest_id, icon, color = 0;
-	struct questinfo qi, *q2;
 
-	if( nd == NULL || nd->bl.m == -1 ) {
+	if (!nd || nd->bl.m == -1) {
 		ShowError("buildin_questinfo: No NPC attached.\n");
 		return SCRIPT_CMD_FAILURE;
 	}
 
-	quest_id = script_getnum(st, 2);
-	icon = script_getnum(st, 3);
+	struct s_questinfo qi;
+	struct script_code *script = nullptr;
+	int color = QMARK_NONE;
+	int icon = script_getnum(st, 2);
 
 #if PACKETVER >= 20120410
 	switch(icon){
@@ -19342,45 +19331,60 @@ BUILDIN_FUNC(questinfo)
 			break;
 	}
 #else
-	if(icon < QTYPE_QUEST || icon > 7) // TODO: check why 7 and not QTYPE_WARG, might be related to icon + 1 below
+	if (icon < QTYPE_QUEST || icon > 7) // TODO: check why 7 and not QTYPE_WARG, might be related to icon + 1 below
 		icon = QTYPE_QUEST;
 	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);
+	if (script_hasdata(st, 3)) {
+		color = script_getnum(st, 3);
+		if (color < QMARK_NONE || color >= QMARK_MAX) {
+			ShowWarning("buildin_questinfo: invalid color '%d', defaulting to QMARK_NONE.\n",color);
 			script_reportfunc(st);
-			color = 0;
+			color = QMARK_NONE;
 		}
 	}
-	qi.color = (unsigned char)color;
 
-	qi.min_level = 1;
-	qi.max_level = MAX_LEVEL;
+	if (script_hasdata(st, 4)) {
+		const char *str = script_getstr(st, 4);
 
-	q2 = map_add_questinfo(nd->bl.m, &qi);
-	q2->req = NULL;
-	q2->req_count = 0;
-	q2->jobid = NULL;
-	q2->jobid_count = 0;
+		if (str) {
+			std::string condition(str);
 
-	if(script_hasdata(st, 5)) {
-		int job = script_getnum(st, 5);
+			if (condition.find( "achievement_condition" ) == std::string::npos)
+				condition = "achievement_condition( " + condition + " );";
 
-		if (!pcdb_checkid(job))
-			ShowError("buildin_questinfo: Nonexistant Job Class.\n");
-		else {
-			buildin_questinfo_setjob(q2, job);
+			script = parse_script(condition.c_str(), "questinfoparsing", 0, SCRIPT_IGNORE_EXTERNAL_BRACKETS);
+			if (!script) {
+				st->state = END;
+				return SCRIPT_CMD_FAILURE;
+			}
 		}
 	}
 
+	qi.nd = nd;
+	qi.icon = static_cast<e_questinfo_types>(icon);
+	qi.color = static_cast<e_questinfo_markcolor>(color);
+	qi.condition = script;
+
+	struct map_data *mapdata = map_getmapdata(nd->bl.m);
+	mapdata->qi_data.push_back(qi);
+
+	return SCRIPT_CMD_SUCCESS;
+}
+
+/**
+ * questinfo_refresh {<char_id>};
+ **/
+BUILDIN_FUNC(questinfo_refresh)
+{
+	struct map_session_data *sd;
+
+	if (!script_charid2sd(2, sd))
+		return SCRIPT_CMD_FAILURE;
+
+	pc_show_questinfo(sd); 
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -19503,36 +19507,38 @@ BUILDIN_FUNC(isbegin_quest)
 BUILDIN_FUNC(showevent)
 {
 	TBL_PC *sd;
-	struct npc_data *nd = map_id2nd(st->oid);
-	int icon, color = 0;
 
 	if (!script_charid2sd(4,sd))
 		return SCRIPT_CMD_FAILURE;
 
-	if( sd == NULL || nd == NULL )
+	struct npc_data *nd = map_id2nd(st->oid);
+
+	if (!nd)
 		return SCRIPT_CMD_SUCCESS;
 
-	icon = script_getnum(st, 2);
-	if( script_hasdata(st, 3) ) {
+	int color = QMARK_NONE;
+	int 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);
+		if (color < QMARK_NONE || color >= QMARK_MAX) {
+			ShowWarning("buildin_showevent: Invalid color '%d', defaulting to QMARK_NONE.\n",color);
 			script_reportfunc(st);
-			color = 0;
+			color = QMARK_NONE;
 		}
 	}
 
 #if PACKETVER >= 20120410
-	if(icon < 0 || (icon > 8 && icon != 9999) || icon == 7)
-		icon = 9999; // Default to nothing if icon id is invalid.
+	if (icon < 0 || (icon > 8 && icon != QTYPE_NONE) || icon == 7)
+		icon = QTYPE_NONE; // Default to nothing if icon id is invalid.
 #else
-	if(icon < 0 || icon > 7)
+	if (icon < 0 || icon > 7)
 		icon = 0;
 	else
 		icon = icon + 1;
 #endif
 
-	clif_quest_show_event(sd, &nd->bl, icon, color);
+	clif_quest_show_event(sd, &nd->bl, static_cast<e_questinfo_types>(icon), static_cast<e_questinfo_markcolor>(color));
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -22466,97 +22472,6 @@ BUILDIN_FUNC(geteleminfo) {
 	return SCRIPT_CMD_SUCCESS;
 }
 
-
-/**
- * Set the quest info of quest_id only showed on player in level range.
- * setquestinfo_level <quest_id>,<min_level>,<max_level>
- * @author [Cydh]
- **/
-BUILDIN_FUNC(setquestinfo_level) {
-	TBL_NPC* nd = map_id2nd(st->oid);
-	int quest_id = script_getnum(st, 2);
-	struct questinfo *qi = map_has_questinfo(nd->bl.m, nd, quest_id);
-
-	if (!qi) {
-		ShowError("buildin_setquestinfo_level: Quest with ID '%d' is not defined yet.\n", quest_id);
-		return SCRIPT_CMD_FAILURE;
-	}
-
-	qi->min_level = script_getnum(st, 3);
-	qi->max_level = script_getnum(st, 4);
-	if (!qi->max_level)
-		qi->max_level = MAX_LEVEL;
-
-	return SCRIPT_CMD_SUCCESS;
-}
-
-/**
- * Set the quest info of quest_id only showed for player that has quest criteria
- * setquestinfo_req <quest_id>,<quest_req_id>,<state>{,<quest_req_id>,<state>,...};
- * @author [Cydh]
- **/
-BUILDIN_FUNC(setquestinfo_req) {
-	TBL_NPC* nd = map_id2nd(st->oid);
-	int quest_id = script_getnum(st, 2);
-	struct questinfo *qi = map_has_questinfo(nd->bl.m, nd, quest_id);
-	uint8 i = 0;
-	uint8 num = script_lastdata(st);
-
-	if (!qi) {
-		ShowError("buildin_setquestinfo_req: Quest with ID '%d' is not defined yet.\n", quest_id);
-		return SCRIPT_CMD_FAILURE;
-	}
-
-	if (quest_search(quest_id) == &quest_dummy) {
-		ShowError("buildin_setquestinfo_req: Quest with ID '%d' is not found in Quest DB.\n", quest_id);
-		return SCRIPT_CMD_FAILURE;
-	}
-
-	if (num%2) {
-		ShowError("buildin_setquestinfo_req: Odd number of parameters(%d) - pairs of requirements are expected.\n", num-2);
-		return SCRIPT_CMD_FAILURE;
-	}
-
-	for (i = 3; i <= num; i += 2) {
-		RECREATE(qi->req, struct questinfo_req, qi->req_count+1);
-		qi->req[qi->req_count].quest_id = script_getnum(st, i);
-		qi->req[qi->req_count].state = (script_getnum(st, i+1) >= 2) ? 2 : (script_getnum(st, i+1) <= 0) ? 0 : 1;
-		qi->req_count++;
-	}
-
-	return SCRIPT_CMD_SUCCESS;
-}
-
-/**
- * Set the quest info of quest_id only showed for player that has specified Job
- * setquestinfo_job <quest_id>,<job>{,<job>...};
- * @author [Cydh]
- **/
-BUILDIN_FUNC(setquestinfo_job) {
-	TBL_NPC* nd = map_id2nd(st->oid);
-	int quest_id = script_getnum(st, 2);
-	struct questinfo *qi = map_has_questinfo(nd->bl.m, nd, quest_id);
-	int job_id = 0;
-	uint8 i = 0;
-	uint8 num = script_lastdata(st)+1;
-
-	if (!qi) {
-		ShowError("buildin_setquestinfo_job: Quest with ID '%d' is not defined yet.\n", quest_id);
-		return SCRIPT_CMD_FAILURE;
-	}
-
-	for (i = 3; i < num; i++) {
-		job_id = script_getnum(st, i);
-		if (!pcdb_checkid(job_id)) {
-			ShowError("buildin_setquestinfo_job: Invalid job id '%d' in Quest with ID %d.\n", job_id, quest_id);
-			continue;
-		}
-		buildin_questinfo_setjob(qi, job_id);
-	}
-
-	return SCRIPT_CMD_SUCCESS;
-}
-
 /**
  * opendressroom(<flag>{,<char_id>});
  */
@@ -25016,7 +24931,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(useatcmd, "s"),
 
 	//Quest Log System [Inkfish]
-	BUILDIN_DEF(questinfo, "ii??"),
+	BUILDIN_DEF(questinfo, "i??"),
 	BUILDIN_DEF(setquest, "i?"),
 	BUILDIN_DEF(erasequest, "i?"),
 	BUILDIN_DEF(completequest, "i?"),
@@ -25024,6 +24939,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(isbegin_quest,"i?"),
 	BUILDIN_DEF(changequest, "ii?"),
 	BUILDIN_DEF(showevent, "i??"),
+	BUILDIN_DEF(questinfo_refresh, "?"),
 
 	//Bound items [Xantara] & [Akinari]
 	BUILDIN_DEF2(getitem,"getitembound","vii?"),
@@ -25063,9 +24979,6 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(showscript,"s??"),
 	BUILDIN_DEF(ignoretimeout,"i?"),
 	BUILDIN_DEF(geteleminfo,"i?"),
-	BUILDIN_DEF(setquestinfo_level,"iii"),
-	BUILDIN_DEF(setquestinfo_req,"iii*"),
-	BUILDIN_DEF(setquestinfo_job,"ii*"),
 	BUILDIN_DEF(opendressroom,"i?"),
 	BUILDIN_DEF(navigateto,"s???????"),
 	BUILDIN_DEF(getguildalliance,"ii"),

+ 9 - 1
src/map/script.hpp

@@ -384,7 +384,7 @@ enum petinfo_types {
 	PETINFO_FOODID
 };
 
-enum questinfo_types {
+enum e_questinfo_types {
 	QTYPE_QUEST = 0,
 	QTYPE_QUEST2,
 	QTYPE_JOB,
@@ -402,6 +402,14 @@ enum questinfo_types {
 	QTYPE_NONE = 9999
 };
 
+enum e_questinfo_markcolor : uint8 {
+	QMARK_NONE = 0,
+	QMARK_YELLOW,
+	QMARK_GREEN,
+	QMARK_PURPLE,
+	QMARK_MAX
+};
+
 #ifndef WIN32
 	// These are declared in wingdi.h
 	/* Font Weights */

+ 6 - 0
src/map/script_constants.hpp

@@ -3889,6 +3889,12 @@
 	export_constant(QTYPE_JUMPING_PORING);
 	export_constant(QTYPE_NONE);
 
+	/* questinfo Map Mark Color */
+	export_constant(QMARK_NONE);
+	export_constant(QMARK_YELLOW);
+	export_constant(QMARK_GREEN);
+	export_constant(QMARK_PURPLE);
+
 	/* font weight types */
 	export_constant(FW_DONTCARE);
 	export_constant(FW_THIN);