Browse Source

Extended TimeLimit format (quest DB) (#8116)

Atemo 1 year ago
parent
commit
ed2d03d811
3 changed files with 69 additions and 14 deletions
  1. 22 3
      doc/quest_db.txt
  2. 46 11
      src/map/quest.cpp
  3. 1 0
      src/map/quest.hpp

+ 22 - 3
doc/quest_db.txt

@@ -20,11 +20,30 @@ Title: Quest title.
 
 TimeLimit: Amount of time before the quest expires.
 
-Use a number followed by "d" for day(s), "h" for hour(s), "mn" for minute(s), and "s" for second(s).
 Specifying with "+" will mark how long until the quest expires.
-Specifying without "+" will mark the exact time the quest expires. Format: "d" (optional), [0-23]"h" (required), [0-59]"mn" (optional), [0-59]"s" (optional).
+Use a number followed by "d" for day(s), "h" for hour(s), "mn" for minute(s), and "s" for second(s).
+Format: "d" (optional), [0-23]"h" (optional), [0-59]"mn" (optional), [0-59]"s" (optional).
+
+Example:
+  - Id: 2069
+    Title: Tierra Gorge Battle
+    # The quest expires 5 minutes after being taken.
+    TimeLimit: +5mn
+
+Specifying without "+" will mark the exact time the quest expires.
+Use a number followed by "d" for day(s) to shift the exact timer to the given day(s) or use the days of the week to set the expiration day,
+and "h" for hour(s), "mn" for minute(s), and "s" for second(s).
+Format: [days of the week] or "d" (optionals), [0-23]"h" (optional), [0-59]"mn" (optional), [0-59]"s" (optional).
 
-Please note the number before "d" only shifts the exact timer to the given day(s).
+Examples:
+  - Id: 9419
+    Title: Attack Sky Fortress Invading Prontera
+    # The quest expires 3 days after being taken at 4am.
+    TimeLimit: 3d 4h
+  - Id: 5965
+    Title: "[Standby] Devil's Special"
+    # The quest expires Monday at 4am.
+    TimeLimit: Monday 4h
 
 ---------------------------------------
 

+ 46 - 11
src/map/quest.cpp

@@ -28,7 +28,7 @@
 
 using namespace rathena;
 
-static int split_exact_quest_time(char* modif_p, int* day, int* hour, int* minute, int *second);
+static int split_exact_quest_time(char* modif_p, int* week, int* day, int* hour, int* minute, int *second);
 
 const std::string QuestDatabase::getDefaultLocation() {
 	return std::string(db_path) + "/quest_db.yml";
@@ -81,20 +81,25 @@ uint64 QuestDatabase::parseBodyNode(const ryml::NodeRef& node) {
 			quest->time = static_cast<time_t>(timediff);
 		}
 		else {// '+' not found, set to specific time
-			int32 day, hour, minute, second;
+			int32 day, hour, minute, second, week;
 
-			if (split_exact_quest_time(const_cast<char *>(time.c_str()), &day, &hour, &minute, &second) == 0) {
+			if (split_exact_quest_time(const_cast<char *>(time.c_str()), &week, &day, &hour, &minute, &second) == 0) {
 				this->invalidWarning(node["TimeLimit"], "Incorrect TimeLimit format %s given, skipping.\n", time.c_str());
 				return 0;
 			}
-			quest->time = day * 86400 + hour * 3600 + minute * 60 + second;
+			if (week > 0)
+				quest->time = hour * 3600 + minute * 60 + second;
+			else
+				quest->time = day * 86400 + hour * 3600 + minute * 60 + second;
 			quest->time_at = true;
+			quest->time_week = week;
 		}
 
 	} else {
 		if (!exists) {
 			quest->time = 0;
 			quest->time_at = false;
+			quest->time_week = -1;
 		}
 	}
 
@@ -441,8 +446,8 @@ uint64 QuestDatabase::parseBodyNode(const ryml::NodeRef& node) {
 }
 
 
-static int split_exact_quest_time(char* modif_p, int* day, int* hour, int* minute, int *second) {
-	int d = -1, h = -1, mn = -1, s = -1;
+static int split_exact_quest_time(char* modif_p, int* week, int* day, int* hour, int* minute, int *second) {
+	int w = -1, d = -1, h = -1, mn = -1, s = -1;
 
 	nullpo_retr(0, modif_p);
 
@@ -453,7 +458,28 @@ static int split_exact_quest_time(char* modif_p, int* day, int* hour, int* minut
 			modif_p++;
 		while (modif_p[0] >= '0' && modif_p[0] <= '9')
 			modif_p++;
-		if (modif_p[0] == 's') {
+		if (strncasecmp(modif_p, "SUNDAY", 6) == 0) {
+			w = 0;
+			modif_p = modif_p + 6;
+		} else if (strncasecmp(modif_p, "MONDAY", 6) == 0) {
+			w = 1;
+			modif_p = modif_p + 6;
+		} else if (strncasecmp(modif_p, "TUESDAY", 7) == 0) {
+			w = 2;
+			modif_p = modif_p + 7;
+		} else if (strncasecmp(modif_p, "WEDNESDAY", 9) == 0) {
+			w = 3;
+			modif_p = modif_p + 9;
+		} else if (strncasecmp(modif_p, "THURSDAY", 8) == 0) {
+			w = 4;
+			modif_p = modif_p + 8;
+		} else if (strncasecmp(modif_p, "FRIDAY", 6) == 0) {
+			w = 5;
+			modif_p = modif_p + 6;
+		} else if (strncasecmp(modif_p, "SATURDAY", 8) == 0) {
+			w = 6;
+			modif_p = modif_p + 8;
+		} else if (modif_p[0] == 's') {
 			s = value;
 			modif_p++;
 		} else if (modif_p[0] == 'm' && modif_p[1] == 'n') {
@@ -473,6 +499,7 @@ static int split_exact_quest_time(char* modif_p, int* day, int* hour, int* minut
 	if (h < 0 || h > 23 || mn > 59 || s > 59)	// hour is required
 		return 0;
 
+	*week = w;
 	*day = max(0,d);
 	*hour = h;
 	*minute = max(0,mn);
@@ -536,10 +563,18 @@ static time_t quest_time(std::shared_ptr<s_quest_db> qi)
 		struct tm *lt = localtime(&t);
 		uint32 time_today = lt->tm_hour * 3600 + lt->tm_min * 60 + lt->tm_sec;
 
-		if (time_today < (qi->time % 86400))
-			return static_cast<time_t>(t + qi->time - time_today);
-		else // Carry over to the next day
-			return static_cast<time_t>(t + 86400 + qi->time - time_today);
+		int32 day_shift = 0;
+
+		if (time_today >= (qi->time % 86400)) // Carry over to the next day
+			day_shift = 1;
+
+		if (qi->time_week > -1) {
+			if (qi->time_week < (lt->tm_wday + day_shift))
+				day_shift = qi->time_week + 7 - lt->tm_wday;
+			else
+				day_shift = qi->time_week - lt->tm_wday;
+		}
+		return static_cast<time_t>(t + (day_shift * 86400) + qi->time - time_today);
 	}
 
 	return 0;

+ 1 - 0
src/map/quest.hpp

@@ -42,6 +42,7 @@ struct s_quest_db {
 	int32 id;
 	time_t time;
 	bool time_at;
+	int32 time_week;
 	std::vector<std::shared_ptr<s_quest_objective>> objectives;
 	std::vector<std::shared_ptr<s_quest_dropitem>> dropitem;
 	std::string name;