1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name date.cpp - The date source file. */
12 //
13 //      (c) Copyright 2018-2019 by Andrettin
14 //
15 //      This program is free software; you can redistribute it and/or modify
16 //      it under the terms of the GNU General Public License as published by
17 //      the Free Software Foundation; only version 2 of the License.
18 //
19 //      This program is distributed in the hope that it will be useful,
20 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //      GNU General Public License for more details.
23 //
24 //      You should have received a copy of the GNU General Public License
25 //      along with this program; if not, write to the Free Software
26 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 //      02111-1307, USA.
28 //
29 
30 /*----------------------------------------------------------------------------
31 --  Includes
32 ----------------------------------------------------------------------------*/
33 
34 #include "stratagus.h"
35 
36 #include "time/date.h"
37 
38 #include "time/calendar.h"
39 #include "time/timeline.h"
40 #include "civilization.h"
41 #include "player.h"
42 #include "quest.h"
43 
44 /*----------------------------------------------------------------------------
45 --  Variables
46 ----------------------------------------------------------------------------*/
47 
48 unsigned long long CDate::CurrentTotalHours = 0;
49 
50 /*----------------------------------------------------------------------------
51 --  Functions
52 ----------------------------------------------------------------------------*/
53 
FromString(const std::string & date_str)54 CDate CDate::FromString(const std::string &date_str)
55 {
56 	CDate date;
57 
58 	std::vector<std::string> date_vector = SplitString(date_str, ".");
59 
60 	CCalendar *calendar = nullptr;
61 	size_t offset = 0;
62 
63 	if (date_vector.size() >= 1 && !IsStringNumber(date_vector[0])) {
64 		calendar = CCalendar::GetCalendar(date_vector[0]);
65 		if (calendar) {
66 			offset += 1;
67 		} else if (!CTimeline::GetTimeline(date_vector[0])) { //is neither a calendar nor a timeline
68 			fprintf(stderr, "Calendar \"%s\" does not exist.\n", date_vector[0].c_str());
69 		}
70 	}
71 
72 	if (date_vector.size() >= (1 + offset) && !IsStringNumber(date_vector[0 + offset])) {
73 		CTimeline *timeline = CTimeline::GetTimeline(date_vector[0 + offset]);
74 		if (timeline) {
75 			date.Timeline = timeline;
76 		} else {
77 			fprintf(stderr, "Timeline \"%s\" does not exist.\n", date_vector[0 + offset].c_str());
78 		}
79 		offset += 1;
80 	}
81 
82 	if (date_vector.size() >= (1 + offset)) {
83 		date.Year = std::stoi(date_vector[0 + offset]);
84 	}
85 
86 	if (date_vector.size() >= (2 + offset)) {
87 		date.Month = std::stoi(date_vector[1 + offset]);
88 	}
89 
90 	if (date_vector.size() >= (3 + offset)) {
91 		date.Day = std::stoi(date_vector[2 + offset]);
92 	}
93 
94 	if (date_vector.size() >= (4 + offset)) {
95 		date.Hour = std::stoi(date_vector[3 + offset]);
96 	}
97 
98 	if (calendar) {
99 		date = date.ToBaseCalendar(calendar);
100 	}
101 
102 	return date;
103 }
104 
Clear()105 void CDate::Clear()
106 {
107 	Year = 0;
108 	Month = 1;
109 	Day = 1;
110 	Hour = DEFAULT_HOURS_PER_DAY / 2;
111 	Timeline = nullptr;
112 }
113 
ContainsDate(const CDate & date) const114 bool CDate::ContainsDate(const CDate &date) const
115 {
116 	if (this->Timeline == date.Timeline) {
117 		return *this >= date;
118 	}
119 
120 	if (this->Timeline) {
121 		return this->Timeline->PointOfDivergence.ContainsDate(date);
122 	}
123 
124 	return false;
125 }
126 
AddYears(const int years)127 void CDate::AddYears(const int years)
128 {
129 	this->Year += years;
130 
131 	if (this->Year == 0) {
132 		if (years < 0) {
133 			this->Year = -1;
134 		} else {
135 			this->Year = 1;
136 		}
137 	}
138 }
139 
AddMonths(const CCalendar * calendar,const int months)140 void CDate::AddMonths(const CCalendar *calendar, const int months)
141 {
142 	this->Month += months;
143 
144 	if (this->Month > 0) {
145 		while (this->Month > (int) calendar->Months.size()) {
146 			this->Month -= static_cast<char>(calendar->Months.size());
147 			this->AddYears(1);
148 		}
149 	} else {
150 		while (this->Month <= 0) {
151 			this->Month += static_cast<char>(calendar->Months.size());
152 			this->AddYears(-1);
153 		}
154 	}
155 }
156 
AddDays(const CCalendar * calendar,const int days,const int day_multiplier)157 void CDate::AddDays(const CCalendar *calendar, const int days, const int day_multiplier)
158 {
159 	int current_day = this->Day;
160 	current_day += days * day_multiplier;
161 
162 	if (current_day > 0) {
163 		while (current_day > calendar->DaysPerYear) {
164 			current_day -= calendar->DaysPerYear;
165 			this->AddYears(1);
166 		}
167 
168 		while (current_day > calendar->Months[this->Month - 1]->Days) {
169 			current_day -= calendar->Months[this->Month - 1]->Days;
170 			this->AddMonths(calendar, 1);
171 		}
172 	} else {
173 		while (current_day <= (-calendar->DaysPerYear + 1)) {
174 			current_day += calendar->DaysPerYear;
175 			this->AddYears(-1);
176 		}
177 
178 		while (current_day <= 0) {
179 			current_day += calendar->Months[this->Month - 1]->Days;
180 			this->AddMonths(calendar, -1);
181 		}
182 	}
183 
184 	this->Day = current_day;
185 }
186 
AddHours(const CCalendar * calendar,const long long int hours,const int day_multiplier)187 void CDate::AddHours(const CCalendar *calendar, const long long int hours, const int day_multiplier)
188 {
189 	this->AddDays(calendar, hours / calendar->HoursPerDay, day_multiplier);
190 
191 	this->Hour += hours % calendar->HoursPerDay;
192 
193 	if (this->Hour >= 0) {
194 		while (this->Hour >= calendar->HoursPerDay) {
195 			this->Hour -= calendar->HoursPerDay;
196 			this->AddDays(calendar, 1, day_multiplier);
197 		}
198 	} else {
199 		while (this->Hour < 0) {
200 			this->Hour += calendar->HoursPerDay;
201 			this->AddDays(calendar, -1, day_multiplier);
202 		}
203 	}
204 }
205 
ToCalendar(CCalendar * current_calendar,CCalendar * new_calendar) const206 CDate CDate::ToCalendar(CCalendar *current_calendar, CCalendar *new_calendar) const
207 {
208 	if (current_calendar == new_calendar) {
209 		return *this;
210 	}
211 
212 	CDate date;
213 	date.Timeline = this->Timeline;
214 	date.Year = this->Year;
215 	date.Month = this->Month;
216 	date.Day = this->Day;
217 	date.Hour = this->Hour;
218 
219 	std::pair<CDate, CDate> chronological_intersection = current_calendar->GetBestChronologicalIntersectionForDate(new_calendar, *this);
220 
221 	if (chronological_intersection.first.Year != 0) { //whether the chronological intersection returned is valid
222 		if (current_calendar->DaysPerYear == new_calendar->DaysPerYear) { //if the quantity of days per year is the same in both calendars, then we can just use the year difference in the intersection to get the resulting year for this date in the new calendar
223 			date.Year += chronological_intersection.second.Year - chronological_intersection.first.Year;
224 		} else if (current_calendar->HoursPerDay == new_calendar->HoursPerDay) { //if the quantity of days per years is different, but the hours in a day are the same, add the year difference in days
225 			date.AddDays(new_calendar, chronological_intersection.second.Year * new_calendar->DaysPerYear - chronological_intersection.first.Year * current_calendar->DaysPerYear);
226 		} else {
227 			date.AddHours(new_calendar, (long long int) chronological_intersection.second.Year * new_calendar->DaysPerYear * new_calendar->HoursPerDay - (long long int) chronological_intersection.first.Year * current_calendar->DaysPerYear * current_calendar->HoursPerDay);
228 		}
229 	} else {
230 		fprintf(stderr, "Dates in calendar \"%s\" cannot be converted to calendar \"%s\", as no chronological intersections are present.\n", current_calendar->Ident.c_str(), new_calendar->Ident.c_str());
231 	}
232 
233 	return date;
234 }
235 
ToBaseCalendar(CCalendar * current_calendar) const236 CDate CDate::ToBaseCalendar(CCalendar *current_calendar) const
237 {
238 	if (!CCalendar::BaseCalendar) {
239 		fprintf(stderr, "No base calendar has been defined.\n");
240 		return *this;
241 	}
242 
243 	return this->ToCalendar(current_calendar, CCalendar::BaseCalendar);
244 }
245 
ToString(const CCalendar * calendar) const246 std::string CDate::ToString(const CCalendar *calendar) const
247 {
248 	std::string date_string;
249 
250 	date_string += std::to_string((long long) this->Year);
251 	date_string += "." + std::to_string((long long) this->Month);
252 	date_string += "." + std::to_string((long long) this->Day);
253 	date_string += "." + std::to_string((long long) this->Hour);
254 
255 	return date_string;
256 }
257 
ToDisplayString(const CCalendar * calendar,const bool year_only) const258 std::string CDate::ToDisplayString(const CCalendar *calendar, const bool year_only) const
259 {
260 	std::string display_string;
261 
262 	if (!year_only) {
263 		display_string += std::to_string((long long) this->Day) + "." + std::to_string((long long) this->Month) + ".";
264 	}
265 
266 	display_string += std::to_string((long long) abs(this->Year));
267 
268 	if (!calendar) {
269 		fprintf(stderr, "Calendar does not exist.\n");
270 		return display_string;
271 	}
272 
273 	if (this->Year < 0) {
274 		if (!calendar->NegativeYearLabel.empty()) {
275 			display_string += " " + calendar->NegativeYearLabel;
276 		} else {
277 			fprintf(stderr, "Calendar \"%s\" has no negative year label.\n", calendar->Ident.c_str());
278 		}
279 	} else {
280 		if (!calendar->YearLabel.empty()) {
281 			display_string += " " + calendar->YearLabel;
282 		} else {
283 			fprintf(stderr, "Calendar \"%s\" has no year label.\n", calendar->Ident.c_str());
284 		}
285 	}
286 
287 	return display_string;
288 }
289 
290 /**
291 **	@brief	Get the total amount of days counting from the date 1.1.1 of the calendar this date is presumed to use
292 **
293 **	@param	calendar	The calendar
294 **
295 **	@return	The amount of days
296 */
GetTotalDays(const CCalendar * calendar) const297 int CDate::GetTotalDays(const CCalendar *calendar) const
298 {
299 	int days = 0;
300 
301 	days += (this->Year < 0 ? this->Year : this->Year - 1) * calendar->DaysPerYear;
302 	for (int i = 0; i < (this->Month - 1); ++i) {
303 		days += calendar->Months[i]->Days;
304 	}
305 	days += this->Day - 1;
306 
307 	return days;
308 }
309 
GetTotalHours(CCalendar * calendar) const310 unsigned long long CDate::GetTotalHours(CCalendar *calendar) const
311 {
312 	CDate date = this->ToBaseCalendar(calendar);
313 
314 	unsigned long long hours = date.Hour;
315 
316 	unsigned long long days = 0;
317 	days += (date.Year - 1 + BaseCalendarYearOffsetForHours) * calendar->DaysPerYear;
318 	for (int i = 0; i < (date.Month - 1); ++i) {
319 		days += calendar->Months[i]->Days;
320 	}
321 	days += date.Day - 1;
322 	hours += days * calendar->HoursPerDay;
323 
324 	return hours;
325 }
326 
327 /**
328 **	@brief	Get the day of the week for this date in a calendar (this date is presumed to already be in the calendar)
329 **
330 **	@param	calendar	The calendar
331 **
332 **	@return	The ID of the day of the week
333 */
GetDayOfTheWeek(const CCalendar * calendar) const334 int CDate::GetDayOfTheWeek(const CCalendar *calendar) const
335 {
336 	if (!calendar->DaysOfTheWeek.empty() && calendar->BaseDayOfTheWeek) {
337 		int day_of_the_week = calendar->BaseDayOfTheWeek->ID;
338 
339 		day_of_the_week += this->GetTotalDays(calendar) % calendar->DaysOfTheWeek.size();
340 		if (day_of_the_week < 0) {
341 			day_of_the_week += calendar->DaysOfTheWeek.size();
342 		}
343 
344 		return day_of_the_week;
345 	}
346 
347 	return -1;
348 }
349 
350 /**
351 **	@brief	Set the current date for a particular calendar
352 **
353 **	@param	calendar_ident	The calendar's string identifier
354 **	@param	date_string		The date's string representation
355 */
SetCurrentDate(const std::string & calendar_ident,const std::string & date_string)356 void SetCurrentDate(const std::string &calendar_ident, const std::string &date_string)
357 {
358 	CCalendar *calendar = CCalendar::GetCalendar(calendar_ident);
359 
360 	if (!calendar) {
361 		return;
362 	}
363 
364 	calendar->CurrentDate = CDate::FromString(date_string);
365 }
366 
367 /**
368 **	@brief	Set the current day of the week for a particular calendar
369 **
370 **	@param	calendar_ident	The calendar's string identifier
371 **	@param	day_of_the_week	The day of the week's ID
372 */
SetCurrentDayOfTheWeek(const std::string & calendar_ident,const int day_of_the_week)373 void SetCurrentDayOfTheWeek(const std::string &calendar_ident, const int day_of_the_week)
374 {
375 	CCalendar *calendar = CCalendar::GetCalendar(calendar_ident);
376 
377 	if (!calendar || calendar->DaysOfTheWeek.empty() || !calendar->BaseDayOfTheWeek) {
378 		return;
379 	}
380 
381 	calendar->CurrentDayOfTheWeek = day_of_the_week;
382 }
383 
384 /**
385 **	@brief	Set the current total in-game hours
386 **
387 **	@param	hours	The amount of hours
388 */
SetCurrentTotalHours(const unsigned long long hours)389 void SetCurrentTotalHours(const unsigned long long hours)
390 {
391 	CDate::CurrentTotalHours = hours;
392 }
393