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