date.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "network/network.h"
00014 #include "network/network_func.h"
00015 #include "currency.h"
00016 #include "window_func.h"
00017 #include "settings_type.h"
00018 #include "date_func.h"
00019 #include "vehicle_base.h"
00020 #include "rail_gui.h"
00021 #include "saveload/saveload.h"
00022 
00023 Year      _cur_year;   
00024 Month     _cur_month;  
00025 Date      _date;       
00026 DateFract _date_fract; 
00027 uint16 _tick_counter;  
00028 uint8     _date_daylength_factor; 
00029 
00035 void SetDate(Date date, DateFract fract)
00036 {
00037   assert(fract < DAY_TICKS);
00038 
00039   YearMonthDay ymd;
00040 
00041   _date = date;
00042   _date_fract = fract;
00043   ConvertDateToYMD(date, &ymd);
00044   _cur_year = ymd.year;
00045   _cur_month = ymd.month;
00046 }
00047 
00048 #define M(a, b) ((a << 5) | b)
00049 static const uint16 _month_date_from_year_day[] = {
00050   M( 0, 1), M( 0, 2), M( 0, 3), M( 0, 4), M( 0, 5), M( 0, 6), M( 0, 7), M( 0, 8), M( 0, 9), M( 0, 10), M( 0, 11), M( 0, 12), M( 0, 13), M( 0, 14), M( 0, 15), M( 0, 16), M( 0, 17), M( 0, 18), M( 0, 19), M( 0, 20), M( 0, 21), M( 0, 22), M( 0, 23), M( 0, 24), M( 0, 25), M( 0, 26), M( 0, 27), M( 0, 28), M( 0, 29), M( 0, 30), M( 0, 31),
00051   M( 1, 1), M( 1, 2), M( 1, 3), M( 1, 4), M( 1, 5), M( 1, 6), M( 1, 7), M( 1, 8), M( 1, 9), M( 1, 10), M( 1, 11), M( 1, 12), M( 1, 13), M( 1, 14), M( 1, 15), M( 1, 16), M( 1, 17), M( 1, 18), M( 1, 19), M( 1, 20), M( 1, 21), M( 1, 22), M( 1, 23), M( 1, 24), M( 1, 25), M( 1, 26), M( 1, 27), M( 1, 28), M( 1, 29),
00052   M( 2, 1), M( 2, 2), M( 2, 3), M( 2, 4), M( 2, 5), M( 2, 6), M( 2, 7), M( 2, 8), M( 2, 9), M( 2, 10), M( 2, 11), M( 2, 12), M( 2, 13), M( 2, 14), M( 2, 15), M( 2, 16), M( 2, 17), M( 2, 18), M( 2, 19), M( 2, 20), M( 2, 21), M( 2, 22), M( 2, 23), M( 2, 24), M( 2, 25), M( 2, 26), M( 2, 27), M( 2, 28), M( 2, 29), M( 2, 30), M( 2, 31),
00053   M( 3, 1), M( 3, 2), M( 3, 3), M( 3, 4), M( 3, 5), M( 3, 6), M( 3, 7), M( 3, 8), M( 3, 9), M( 3, 10), M( 3, 11), M( 3, 12), M( 3, 13), M( 3, 14), M( 3, 15), M( 3, 16), M( 3, 17), M( 3, 18), M( 3, 19), M( 3, 20), M( 3, 21), M( 3, 22), M( 3, 23), M( 3, 24), M( 3, 25), M( 3, 26), M( 3, 27), M( 3, 28), M( 3, 29), M( 3, 30),
00054   M( 4, 1), M( 4, 2), M( 4, 3), M( 4, 4), M( 4, 5), M( 4, 6), M( 4, 7), M( 4, 8), M( 4, 9), M( 4, 10), M( 4, 11), M( 4, 12), M( 4, 13), M( 4, 14), M( 4, 15), M( 4, 16), M( 4, 17), M( 4, 18), M( 4, 19), M( 4, 20), M( 4, 21), M( 4, 22), M( 4, 23), M( 4, 24), M( 4, 25), M( 4, 26), M( 4, 27), M( 4, 28), M( 4, 29), M( 4, 30), M( 4, 31),
00055   M( 5, 1), M( 5, 2), M( 5, 3), M( 5, 4), M( 5, 5), M( 5, 6), M( 5, 7), M( 5, 8), M( 5, 9), M( 5, 10), M( 5, 11), M( 5, 12), M( 5, 13), M( 5, 14), M( 5, 15), M( 5, 16), M( 5, 17), M( 5, 18), M( 5, 19), M( 5, 20), M( 5, 21), M( 5, 22), M( 5, 23), M( 5, 24), M( 5, 25), M( 5, 26), M( 5, 27), M( 5, 28), M( 5, 29), M( 5, 30),
00056   M( 6, 1), M( 6, 2), M( 6, 3), M( 6, 4), M( 6, 5), M( 6, 6), M( 6, 7), M( 6, 8), M( 6, 9), M( 6, 10), M( 6, 11), M( 6, 12), M( 6, 13), M( 6, 14), M( 6, 15), M( 6, 16), M( 6, 17), M( 6, 18), M( 6, 19), M( 6, 20), M( 6, 21), M( 6, 22), M( 6, 23), M( 6, 24), M( 6, 25), M( 6, 26), M( 6, 27), M( 6, 28), M( 6, 29), M( 6, 30), M( 6, 31),
00057   M( 7, 1), M( 7, 2), M( 7, 3), M( 7, 4), M( 7, 5), M( 7, 6), M( 7, 7), M( 7, 8), M( 7, 9), M( 7, 10), M( 7, 11), M( 7, 12), M( 7, 13), M( 7, 14), M( 7, 15), M( 7, 16), M( 7, 17), M( 7, 18), M( 7, 19), M( 7, 20), M( 7, 21), M( 7, 22), M( 7, 23), M( 7, 24), M( 7, 25), M( 7, 26), M( 7, 27), M( 7, 28), M( 7, 29), M( 7, 30), M( 7, 31),
00058   M( 8, 1), M( 8, 2), M( 8, 3), M( 8, 4), M( 8, 5), M( 8, 6), M( 8, 7), M( 8, 8), M( 8, 9), M( 8, 10), M( 8, 11), M( 8, 12), M( 8, 13), M( 8, 14), M( 8, 15), M( 8, 16), M( 8, 17), M( 8, 18), M( 8, 19), M( 8, 20), M( 8, 21), M( 8, 22), M( 8, 23), M( 8, 24), M( 8, 25), M( 8, 26), M( 8, 27), M( 8, 28), M( 8, 29), M( 8, 30),
00059   M( 9, 1), M( 9, 2), M( 9, 3), M( 9, 4), M( 9, 5), M( 9, 6), M( 9, 7), M( 9, 8), M( 9, 9), M( 9, 10), M( 9, 11), M( 9, 12), M( 9, 13), M( 9, 14), M( 9, 15), M( 9, 16), M( 9, 17), M( 9, 18), M( 9, 19), M( 9, 20), M( 9, 21), M( 9, 22), M( 9, 23), M( 9, 24), M( 9, 25), M( 9, 26), M( 9, 27), M( 9, 28), M( 9, 29), M( 9, 30), M( 9, 31),
00060   M(10, 1), M(10, 2), M(10, 3), M(10, 4), M(10, 5), M(10, 6), M(10, 7), M(10, 8), M(10, 9), M(10, 10), M(10, 11), M(10, 12), M(10, 13), M(10, 14), M(10, 15), M(10, 16), M(10, 17), M(10, 18), M(10, 19), M(10, 20), M(10, 21), M(10, 22), M(10, 23), M(10, 24), M(10, 25), M(10, 26), M(10, 27), M(10, 28), M(10, 29), M(10, 30),
00061   M(11, 1), M(11, 2), M(11, 3), M(11, 4), M(11, 5), M(11, 6), M(11, 7), M(11, 8), M(11, 9), M(11, 10), M(11, 11), M(11, 12), M(11, 13), M(11, 14), M(11, 15), M(11, 16), M(11, 17), M(11, 18), M(11, 19), M(11, 20), M(11, 21), M(11, 22), M(11, 23), M(11, 24), M(11, 25), M(11, 26), M(11, 27), M(11, 28), M(11, 29), M(11, 30), M(11, 31),
00062 };
00063 #undef M
00064 
00065 enum DaysTillMonth {
00066   ACCUM_JAN = 0,
00067   ACCUM_FEB = ACCUM_JAN + 31,
00068   ACCUM_MAR = ACCUM_FEB + 29,
00069   ACCUM_APR = ACCUM_MAR + 31,
00070   ACCUM_MAY = ACCUM_APR + 30,
00071   ACCUM_JUN = ACCUM_MAY + 31,
00072   ACCUM_JUL = ACCUM_JUN + 30,
00073   ACCUM_AUG = ACCUM_JUL + 31,
00074   ACCUM_SEP = ACCUM_AUG + 31,
00075   ACCUM_OCT = ACCUM_SEP + 30,
00076   ACCUM_NOV = ACCUM_OCT + 31,
00077   ACCUM_DEC = ACCUM_NOV + 30,
00078 };
00079 
00081 static const uint16 _accum_days_for_month[] = {
00082   ACCUM_JAN, ACCUM_FEB, ACCUM_MAR, ACCUM_APR,
00083   ACCUM_MAY, ACCUM_JUN, ACCUM_JUL, ACCUM_AUG,
00084   ACCUM_SEP, ACCUM_OCT, ACCUM_NOV, ACCUM_DEC,
00085 };
00086 
00092 void ConvertDateToYMD(Date date, YearMonthDay *ymd)
00093 {
00094   /* Year determination in multiple steps to account for leap
00095    * years. First do the large steps, then the smaller ones.
00096    */
00097 
00098   /* There are 97 leap years in 400 years */
00099   Year yr = 400 * (date / (DAYS_IN_YEAR * 400 + 97));
00100   int rem = date % (DAYS_IN_YEAR * 400 + 97);
00101   uint16 x;
00102 
00103   if (rem >= DAYS_IN_YEAR * 100 + 25) {
00104     /* There are 25 leap years in the first 100 years after
00105      * every 400th year, as every 400th year is a leap year */
00106     yr  += 100;
00107     rem -= DAYS_IN_YEAR * 100 + 25;
00108 
00109     /* There are 24 leap years in the next couple of 100 years */
00110     yr += 100 * (rem / (DAYS_IN_YEAR * 100 + 24));
00111     rem = (rem % (DAYS_IN_YEAR * 100 + 24));
00112   }
00113 
00114   if (!IsLeapYear(yr) && rem >= DAYS_IN_YEAR * 4) {
00115     /* The first 4 year of the century are not always a leap year */
00116     yr  += 4;
00117     rem -= DAYS_IN_YEAR * 4;
00118   }
00119 
00120   /* There is 1 leap year every 4 years */
00121   yr += 4 * (rem / (DAYS_IN_YEAR * 4 + 1));
00122   rem = rem % (DAYS_IN_YEAR * 4 + 1);
00123 
00124   /* The last (max 3) years to account for; the first one
00125    * can be, but is not necessarily a leap year */
00126   while (rem >= (IsLeapYear(yr) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR)) {
00127     rem -= IsLeapYear(yr) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR;
00128     yr++;
00129   }
00130 
00131   /* Skip the 29th of February in non-leap years */
00132   if (!IsLeapYear(yr) && rem >= ACCUM_MAR - 1) rem++;
00133 
00134   ymd->year = yr;
00135 
00136   x = _month_date_from_year_day[rem];
00137   ymd->month = x >> 5;
00138   ymd->day = x & 0x1F;
00139 }
00140 
00147 Date ConvertYMDToDate(Year year, Month month, Day day)
00148 {
00149   /* Day-offset in a leap year */
00150   int days = _accum_days_for_month[month] + day - 1;
00151 
00152   /* Account for the missing of the 29th of February in non-leap years */
00153   if (!IsLeapYear(year) && days >= ACCUM_MAR) days--;
00154 
00155   return DAYS_TILL(year) + days;
00156 }
00157 
00160 extern void EnginesDailyLoop();
00161 extern void DisasterDailyLoop();
00162 extern void IndustryDailyLoop();
00163 
00164 extern void CompaniesMonthlyLoop();
00165 extern void EnginesMonthlyLoop();
00166 extern void TownsMonthlyLoop();
00167 extern void IndustryMonthlyLoop();
00168 extern void StationMonthlyLoop();
00169 extern void SubsidyMonthlyLoop();
00170 
00171 extern void CompaniesYearlyLoop();
00172 extern void VehiclesYearlyLoop();
00173 extern void TownsYearlyLoop();
00174 
00175 extern void ShowEndGameChart();
00176 
00177 
00179 static const int _autosave_intervals[] = {
00180    0, 
00181    1, 
00182    7, 
00183    1, 
00184    3, 
00185    6, 
00186   12, 
00187 };
00188 
00192 static void OnNewYear()
00193 {
00194   CompaniesYearlyLoop();
00195   VehiclesYearlyLoop();
00196   TownsYearlyLoop();
00197   InvalidateWindowClassesData(WC_BUILD_STATION);
00198 #ifdef ENABLE_NETWORK
00199   if (_network_server) NetworkServerYearlyLoop();
00200 #endif /* ENABLE_NETWORK */
00201 
00202   if (_cur_year == _settings_client.gui.semaphore_build_before) ResetSignalVariant();
00203 
00204   /* check if we reached end of the game */
00205   if (_cur_year == ORIGINAL_END_YEAR) {
00206     ShowEndGameChart();
00207   /* check if we reached the maximum year, decrement dates by a year */
00208   } else if (_cur_year == MAX_YEAR + 1) {
00209     Vehicle *v;
00210     uint days_this_year;
00211 
00212     _cur_year--;
00213     days_this_year = IsLeapYear(_cur_year) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR;
00214     _date -= days_this_year;
00215     FOR_ALL_VEHICLES(v) v->date_of_last_service -= days_this_year;
00216 
00217 #ifdef ENABLE_NETWORK
00218     /* Because the _date wraps here, and text-messages expire by game-days, we have to clean out
00219      *  all of them if the date is set back, else those messages will hang for ever */
00220     NetworkInitChatMessage();
00221 #endif /* ENABLE_NETWORK */
00222   }
00223 
00224   if (_settings_client.gui.auto_euro) CheckSwitchToEuro();
00225 }
00226 
00230 static void OnNewMonth()
00231 {
00232   if (_settings_client.gui.autosave >= 3 && (_cur_month % _autosave_intervals[_settings_client.gui.autosave]) == 0) {
00233     _do_autosave = true;
00234     SetWindowDirty(WC_STATUS_BAR, 0);
00235   }
00236 
00237   SetWindowClassesDirty(WC_CHEATS);
00238   CompaniesMonthlyLoop();
00239   SubsidyMonthlyLoop();
00240   EnginesMonthlyLoop();
00241   TownsMonthlyLoop();
00242   IndustryMonthlyLoop();
00243   StationMonthlyLoop();
00244 #ifdef ENABLE_NETWORK
00245   if (_network_server) NetworkServerMonthlyLoop();
00246 #endif /* ENABLE_NETWORK */
00247 }
00248 
00252 static void OnNewDay()
00253 {
00254   // XXX: use day-of-month?
00255   if (_settings_client.gui.autosave != 0 && _settings_client.gui.autosave <= 2 && (_date % _autosave_intervals[_settings_client.gui.autosave]) == 0) {
00256     _do_autosave = true;
00257     SetWindowDirty(WC_STATUS_BAR, 0);
00258   }
00259 
00260 #ifdef ENABLE_NETWORK
00261   if (_network_server) NetworkServerDailyLoop();
00262 #endif /* ENABLE_NETWORK */
00263 
00264   DisasterDailyLoop();
00265   IndustryDailyLoop();
00266 
00267   SetWindowWidgetDirty(WC_STATUS_BAR, 0, 0);
00268   EnginesDailyLoop();
00269 
00270   /* Refresh after possible snowline change */
00271   SetWindowClassesDirty(WC_TOWN_VIEW);
00272 }
00273 
00278 void IncreaseDate()
00279 {
00280   /* increase day, and check if a new day is there? */
00281   _tick_counter++;
00282 
00283   if (_game_mode == GM_MENU) return;
00284 
00285   _date_fract++;
00286   if (_date_fract < DAY_TICKS) return;
00287   _date_fract = 0;
00288 
00289   /* increase day counter and call various daily loops */
00290   _date++;
00291   OnNewDay();
00292 
00293   YearMonthDay ymd;
00294 
00295   /* check if we entered a new month? */
00296   ConvertDateToYMD(_date, &ymd);
00297   if (ymd.month == _cur_month) return;
00298 
00299   /* yes, call various monthly loops */
00300   _cur_month = ymd.month;
00301   OnNewMonth();
00302 
00303   /* check if we entered a new year? */
00304   if (ymd.year == _cur_year) return;
00305 
00306   /* yes, call various yearly loops */
00307   _cur_year = ymd.year;
00308   OnNewYear();
00309 }