diff --git a/src/date.cpp b/src/date.cpp
index 9122b21..c54ef2e 100644
--- a/src/date.cpp
+++ b/src/date.cpp
@@ -26,6 +26,7 @@ Year      _cur_year;   ///< Current year, starting at 0
 Month     _cur_month;  ///< Current month (0..11)
 Date      _date;       ///< Current date in days (day counter)
 DateFract _date_fract;
+uint8     _date_daylength_factor;	///< Setting for DAY_LENGTH
 
 
 void SetDate(Date date)
diff --git a/src/date_type.h b/src/date_type.h
index e0481ff..208478c 100644
--- a/src/date_type.h
+++ b/src/date_type.h
@@ -19,11 +19,21 @@
  * 1 day is thus about 2 seconds (74 * 30 = 2220) on a machine that can run OpenTTD normally
  */
 enum {
-	DAY_TICKS = 74,          ///< ticks per day
+	ORIG_DAY_TICKS = 74,     ///< unscaled ticks per day
 	DAYS_IN_YEAR = 365,      ///< days per year
 	DAYS_IN_LEAP_YEAR = 366, ///< sometimes, you need one day more...
 };
 
+/**
+ * Scale factor for DAY_TICKS
+ */
+extern uint8 _date_daylength_factor;
+
+/**
+ * Day length in ticks, calculated using a configureable scale factor.
+ */
+#define DAY_TICKS (ORIG_DAY_TICKS * _date_daylength_factor)
+
 /*
  * ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR and DAYS_TILL_ORIGINAL_BASE_YEAR are
  * primarily used for loading newgrf and savegame data and returning some
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 555b63a..9257c89 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1289,6 +1289,8 @@ STR_CONFIG_SETTING_TOWN_FOUNDING                                :{LTBLUE}Foundin
 STR_CONFIG_SETTING_TOWN_FOUNDING_FORBIDDEN                      :forbidden
 STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED                        :allowed
 STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT          :allowed, custom town layout
+STR_CONFIG_SETTING_DAYLENGTH_FACTOR                             :{LTBLUE}Day length factor: {ORANGE}{STRING}
+STR_CONFIG_SETTING_TOWN_CARGO_FACTOR                            :{LTBLUE}Town cargo generation factor (less < 0 < more): {ORANGE}{STRING1}
 
 STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT                         :{LTBLUE}In game placement of trees: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NONE                    :none {RED}(breaks lumber mill)
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 1911e1e..497a25a 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1354,6 +1354,7 @@ static SettingEntry _settings_economy_towns[] = {
 	SettingEntry("economy.town_growth_rate"),
 	SettingEntry("economy.larger_towns"),
 	SettingEntry("economy.initial_city_size"),
+	SettingEntry("economy.town_cargo_factor"),
 };
 /** Towns sub-page */
 static SettingsPage _settings_economy_towns_page = {_settings_economy_towns, lengthof(_settings_economy_towns)};
@@ -1375,6 +1376,7 @@ static SettingEntry _settings_economy[] = {
 	SettingEntry("economy.moving_average_unit"),
 	SettingEntry("economy.moving_average_length"),
 	SettingEntry("economy.feeder_payment_share"),
+	SettingEntry("daylength_factor"),
 };
 /** Economy sub-page */
 static SettingsPage _settings_economy_page = {_settings_economy, lengthof(_settings_economy)};
diff --git a/src/settings_type.h b/src/settings_type.h
index ecb2d1d..fa21adf 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -350,6 +350,7 @@ struct EconomySettings {
 	uint16 town_noise_population[3];         ///< population to base decision on noise evaluation (@see town_council_tolerance)
 	uint16 moving_average_unit;              ///< unit of time to use for calculating the moving average of capacities and usage of links
 	uint16 moving_average_length;            ///< length of the moving average for capacities and usage of links
+ 	int8   town_cargo_factor;                ///< power-of-two multiplier for town (passenger, mail) generation. May be negative.
 };
 
 struct LinkGraphSettings {
diff --git a/src/table/settings.h b/src/table/settings.h
index 19829b7..41bdaf5 100644
--- a/src/table/settings.h
+++ b/src/table/settings.h
@@ -369,7 +369,9 @@ const SettingDesc _settings[] = {
 	    SDT_BOOL(GameSettings, station.never_expire_airports,                                       0,NN, false,                    STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS,  NULL),
 	 SDT_CONDVAR(GameSettings, economy.town_layout,                  SLE_UINT8, 59, SL_MAX_VERSION, 0,MS,TL_ORIGINAL,TL_BEGIN,NUM_TLS - 1, 1, STR_CONFIG_SETTING_TOWN_LAYOUT,  TownFoundingChanged),
 	SDT_CONDBOOL(GameSettings, economy.allow_town_roads,                       113, SL_MAX_VERSION, 0, 0,  true,                    STR_CONFIG_SETTING_ALLOW_TOWN_ROADS,       NULL),
+	SDTG_CONDVAR("daylength_factor",                                 SLE_UINT8, 0, 0, _date_daylength_factor, 1,     1,     255, 1, STR_CONFIG_SETTING_DAYLENGTH_FACTOR,       NULL,                               133, SL_MAX_VERSION),
 	 SDT_CONDVAR(GameSettings, economy.found_town,                   SLE_UINT8,128, SL_MAX_VERSION, 0,MS,TF_FORBIDDEN,TF_BEGIN,TF_END - 1, 1, STR_CONFIG_SETTING_TOWN_FOUNDING, TownFoundingChanged),
+	 SDT_CONDVAR(GameSettings, economy.town_cargo_factor,            SLE_INT8, 133, SL_MAX_VERSION, 0, 0,     0,   -16,      +8, 1,       STR_CONFIG_SETTING_TOWN_CARGO_FACTOR,      NULL),
 
 	     SDT_VAR(GameSettings, vehicle.train_acceleration_model,     SLE_UINT8,                     0,MS,     0,     0,       1, 1, STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL, TrainAccelerationModelChanged),
 	 SDT_CONDVAR(GameSettings, vehicle.train_slope_steepness,        SLE_UINT8,133, SL_MAX_VERSION, 0, 0,     3,     0,      10, 1, STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS,  NULL),
diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp
index 4cde4b0..71f123a 100644
--- a/src/timetable_cmd.cpp
+++ b/src/timetable_cmd.cpp
@@ -267,7 +267,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
 			/* Round the time taken up to the nearest day, as this will avoid
 			 * confusion for people who are timetabling in days, and can be
 			 * adjusted later by people who aren't. */
-			time_taken = (((time_taken - 1) / DAY_TICKS) + 1) * DAY_TICKS;
+			time_taken = (((time_taken - 1) / ORIG_DAY_TICKS) + 1) * ORIG_DAY_TICKS;
 
 			ChangeTimetable(v, v->cur_order_index, time_taken, travelling);
 		}
diff --git a/src/town.h b/src/town.h
index 08d376f..04ecdce 100644
--- a/src/town.h
+++ b/src/town.h
@@ -173,7 +173,7 @@ enum TownRatingCheckType {
 /** This is the number of ticks between towns being processed for building new
  * houses or roads. This value originally came from the size of the town array
  * in TTD. */
-static const byte TOWN_GROWTH_FREQUENCY = 70;
+#define TOWN_GROWTH_FREQUENCY (70 * _date_daylength_factor)
 
 /** This enum is used in conjonction with town->flags.
  * IT simply states what bit is used for.
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index 0003a51..f046792 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -433,6 +433,61 @@ static void MakeTownHouseBigger(TileIndex tile)
 }
 
 /**
+ * Generate cargo for a town (house).
+ *
+ * The amount of cargo should be and will be greater than zero.
+ *
+ * @param t current town
+ * @param ct type of cargo to generate, usually CT_PASSENGERS or CT_MAIL
+ * @param amount how many units of cargo
+ * @param stations available stations for this house
+ */
+static void TownGenerateCargo (Town *t, CargoID ct, uint amount, StationFinder &stations)
+{
+	// custom cargo generation factor
+	int cf = _settings_game.economy.town_cargo_factor;
+
+	// when the economy flunctuates, everyone wants to stay at home
+	if (_economy.fluct <= 0)
+		amount = (amount + 1) >> 1; 
+	
+	// apply custom factor?
+	if (cf < 0)
+		// approx (amount / 2^cf)
+		// adjust with a constant offset of {(2 ^ cf) - 1} (i.e. add cf * 1-bits) before dividing to ensure that it doesn't become zero
+		// this skews the curve a little so that isn't entirely exponential, but will still decrease
+		amount = (amount + ((1 << -cf) - 1)) >> -cf;
+
+	else if (cf > 0)
+		// approx (amount * 2^cf)
+		// XXX: overflow?
+		amount = amount << cf;
+	
+	// with the adjustments above, this should never happen
+	assert(amount > 0);
+	
+	// send cargo to station
+	uint moved = MoveGoodsToStation(ct, amount, ST_TOWN, t->index, stations.GetStations());
+	
+	// calculate for town stats
+	const CargoSpec *cs = CargoSpec::Get(ct);
+	switch (cs->town_effect) {
+		case TE_PASSENGERS:
+			t->new_max_pass += amount;
+			t->new_act_pass += moved;
+			break;
+
+		case TE_MAIL:
+			t->new_max_mail += amount;
+			t->new_act_mail += moved;
+			break;
+
+		default:
+			break;
+	}
+}
+
+/**
  * Tile callback function.
  *
  * Periodic tic handler for houses and town
@@ -479,39 +534,20 @@ static void TileLoop_Town(TileIndex tile)
 			uint amt = GB(callback, 0, 8);
 			if (amt == 0) continue;
 
-			uint moved = MoveGoodsToStation(cargo, amt, ST_TOWN, t->index, stations.GetStations());
-
-			const CargoSpec *cs = CargoSpec::Get(cargo);
-			switch (cs->town_effect) {
-				case TE_PASSENGERS:
-					t->new_max_pass += amt;
-					t->new_act_pass += moved;
-					break;
-
-				case TE_MAIL:
-					t->new_max_mail += amt;
-					t->new_act_mail += moved;
-					break;
-
-				default:
-					break;
-			}
+			// XXX: no economy flunctuation for GRF cargos?
+			TownGenerateCargo(t, cargo, amt, stations);
 		}
 	} else {
 		if (GB(r, 0, 8) < hs->population) {
 			uint amt = GB(r, 0, 8) / 8 + 1;
-
-			if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
-			t->new_max_pass += amt;
-			t->new_act_pass += MoveGoodsToStation(CT_PASSENGERS, amt, ST_TOWN, t->index, stations.GetStations());
+			
+			TownGenerateCargo(t, CT_PASSENGERS, amt, stations);
 		}
 
 		if (GB(r, 8, 8) < hs->mail_generation) {
 			uint amt = GB(r, 8, 8) / 8 + 1;
-
-			if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
-			t->new_max_mail += amt;
-			t->new_act_mail += MoveGoodsToStation(CT_MAIL, amt, ST_TOWN, t->index, stations.GetStations());
+			
+			TownGenerateCargo(t, CT_MAIL, amt, stations);
 		}
 	}
 
