diff --git a/docker-compose.yml b/docker-compose.yml index 9ab976d2..09c06d4d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -161,6 +161,7 @@ services: - 'lacuna-beanstalkd' environment: - 'TLE_NO_MIDDLEWARE=1' + - 'LACUNA_HIGH_SPEED_TEST_MODE=1' ports: - '3050:5000' volumes: diff --git a/lib/Lacuna.pm b/lib/Lacuna.pm index 85d75013..fe4f5658 100644 --- a/lib/Lacuna.pm +++ b/lib/Lacuna.pm @@ -42,4 +42,12 @@ sub queue { return $queue; } +# True only in the local dev docker-compose stack (LACUNA_HIGH_SPEED_TEST_MODE +# is set in docker-compose.yml's lacuna-server service, and deliberately never +# set in docker-compose.deployed.yml). Collapses build/travel/work timers to +# near-instant for manual testing; never changes random-chance odds. +sub high_speed_test_mode { + return $ENV{'LACUNA_HIGH_SPEED_TEST_MODE'} ? 1 : 0; +} + 1; diff --git a/lib/Lacuna/Constants.pm b/lib/Lacuna/Constants.pm index 4bef1bbc..4903befa 100644 --- a/lib/Lacuna/Constants.pm +++ b/lib/Lacuna/Constants.pm @@ -123,14 +123,23 @@ use constant SHIP_TRADE_TYPES => (qw( use constant SHIP_WASTE_TYPES => (qw( scow scow_fast scow_large scow_mega )); -use constant SHIP_SINGLE_USE_TYPES => (qw( probe short_range_colony_ship colony_ship spy_pod space_station +use constant SHIP_SINGLE_USE_TYPES => (qw( probe short_range_colony_ship colony_ship spy_pod space_station mining_platform_ship terraforming_platform_ship surveyor gas_giant_settlement_ship snark snark2 snark3 thud supply_pod supply_pod2 supply_pod3 supply_pod4 supply_pod5 - drone spy_shuttle observatory_seeker security_ministry_seeker + drone spy_shuttle observatory_seeker security_ministry_seeker spaceport_seeker excavator detonator scanner stake placebo placebo2 placebo3 placebo4 placebo5 placebo6 bleeder fissure_sealer )); + +# Timing knobs for Lacuna->high_speed_test_mode (see lib/Lacuna.pm). Only +# active when LACUNA_HIGH_SPEED_TEST_MODE is set, which is only true in the +# local dev docker-compose stack -- never in docker-compose.deployed.yml. +use constant HIGH_SPEED_TEST_BUILD_MAX_SECONDS => 5; # cap for builds/upgrades/spy training/ship construction/building work +use constant HIGH_SPEED_TEST_TRAVEL_DIVISOR => 100; # ship travel time divisor +use constant HIGH_SPEED_TEST_TRAVEL_MIN_SECONDS => 2; # floor for ship travel time +use constant HIGH_SPEED_TEST_DIG_INTERVAL_SECONDS => 5; # excavator dig-check cadence (normally 3600) + our @EXPORT_OK = qw( INFLATION SECONDS_IN_A_DAY @@ -144,6 +153,10 @@ our @EXPORT_OK = qw( SHIP_TRADE_TYPES SHIP_WASTE_TYPES SHIP_SINGLE_USE_TYPES + HIGH_SPEED_TEST_BUILD_MAX_SECONDS + HIGH_SPEED_TEST_TRAVEL_DIVISOR + HIGH_SPEED_TEST_TRAVEL_MIN_SECONDS + HIGH_SPEED_TEST_DIG_INTERVAL_SECONDS ); our %EXPORT_TAGS = ( @@ -160,6 +173,10 @@ our %EXPORT_TAGS = ( SHIP_TRADE_TYPES SHIP_WASTE_TYPES SHIP_SINGLE_USE_TYPES + HIGH_SPEED_TEST_BUILD_MAX_SECONDS + HIGH_SPEED_TEST_TRAVEL_DIVISOR + HIGH_SPEED_TEST_TRAVEL_MIN_SECONDS + HIGH_SPEED_TEST_DIG_INTERVAL_SECONDS )], ); diff --git a/lib/Lacuna/DB/Result/Building.pm b/lib/Lacuna/DB/Result/Building.pm index 7c0515ad..2fffa229 100755 --- a/lib/Lacuna/DB/Result/Building.pm +++ b/lib/Lacuna/DB/Result/Building.pm @@ -921,7 +921,11 @@ sub start_upgrade { $upgrade_ends = $now; } - $upgrade_ends->add(seconds=>$cost->{time}); + my $upgrade_time = $cost->{time}; + if (Lacuna->high_speed_test_mode && $upgrade_time > HIGH_SPEED_TEST_BUILD_MAX_SECONDS) { + $upgrade_time = HIGH_SPEED_TEST_BUILD_MAX_SECONDS; + } + $upgrade_ends->add(seconds=>$upgrade_time); # add to queue $self->update({ is_upgrading => 1, @@ -1086,6 +1090,11 @@ sub work_seconds_remaining { sub reschedule_work { my ($self, $new_work_ends) = @_; + if (Lacuna->high_speed_test_mode) { + my $max_ends = DateTime->now->add(seconds => HIGH_SPEED_TEST_BUILD_MAX_SECONDS); + $new_work_ends = $max_ends if $new_work_ends > $max_ends; + } + $self->is_working(1); $self->work_ends($new_work_ends); $self->update; @@ -1114,6 +1123,9 @@ sub start_work { my $now = DateTime->now; $self->is_working(1); $self->work_started($now); + if (Lacuna->high_speed_test_mode && $duration > HIGH_SPEED_TEST_BUILD_MAX_SECONDS) { + $duration = HIGH_SPEED_TEST_BUILD_MAX_SECONDS; + } my $ends = DateTime->now->add(seconds => $duration); $self->work_ends($ends); $self->work($work); diff --git a/lib/Lacuna/DB/Result/Map/Body/Planet.pm b/lib/Lacuna/DB/Result/Map/Body/Planet.pm index ed47858b..c88ceea3 100644 --- a/lib/Lacuna/DB/Result/Map/Body/Planet.pm +++ b/lib/Lacuna/DB/Result/Map/Body/Planet.pm @@ -5,7 +5,7 @@ use Carp; use utf8; no warnings qw(uninitialized); extends 'Lacuna::DB::Result::Map::Body'; -use Lacuna::Constants qw(FOOD_TYPES ORE_TYPES BUILDABLE_CLASSES SPACE_STATION_MODULES); +use Lacuna::Constants qw(FOOD_TYPES ORE_TYPES BUILDABLE_CLASSES SPACE_STATION_MODULES HIGH_SPEED_TEST_DIG_INTERVAL_SECONDS); use List::Util qw(shuffle max min none sum); use Lacuna::Util qw(randint format_date random_element); use DateTime; @@ -1704,10 +1704,11 @@ sub tick_to { # Process excavator sites if ( my $arch = $self->archaeology) { if ($arch->effective_efficiency == 100 and $arch->effective_level > 0) { + my $dig_interval = Lacuna->high_speed_test_mode ? HIGH_SPEED_TEST_DIG_INTERVAL_SECONDS : 3600; my $dig_sec = $now->epoch - $arch->last_check->epoch; - if ($dig_sec >= 3600) { - my $dig_hours = int($dig_sec/3600); - my $new_ld = $arch->last_check->add( seconds => ($dig_hours * 3600)); + if ($dig_sec >= $dig_interval) { + my $dig_hours = int($dig_sec/$dig_interval); + my $new_ld = $arch->last_check->add( seconds => ($dig_hours * $dig_interval)); $dig_hours = 3 if $dig_hours > 3; for (1..$dig_hours) { $arch->run_excavators; @@ -1723,136 +1724,151 @@ sub tick_to { # happiness $self->add_happiness(sprintf('%.0f', $self->happiness_hour * $tick_rate)); - # waste - if ($self->waste_hour < 0 ) { # if it gets negative, spend out of storage - $self->spend_waste(sprintf('%.0f',abs($self->waste_hour) * $tick_rate)); - } - else { - $self->add_waste(sprintf('%.0f', $self->waste_hour * $tick_rate)); - } - - # energy - if ($self->energy_hour < 0 ) { # if it gets negative, spend out of storage - $self->spend_energy(sprintf('%.0f',abs($self->energy_hour) * $tick_rate)); - } - else { - $self->add_energy(sprintf('%.0f', $self->energy_hour * $tick_rate)); - } - - # water - if ($self->water_hour < 0 ) { # if it gets negative, spend out of storage - $self->spend_water(sprintf('%.0f',abs($self->water_hour) * $tick_rate)); + if (Lacuna->high_speed_test_mode) { + # Skip normal production/consumption math: fill storage outright so + # testing isn't gated on waiting for realistic production rates. + $self->waste_stored(0); + $self->energy_stored($self->energy_capacity); + $self->water_stored($self->water_capacity); + + my $ore_share = int($self->ore_capacity / scalar(ORE_TYPES)); + $self->type_stored($_, $ore_share) for (ORE_TYPES); + + my $food_share = int($self->food_capacity / scalar(FOOD_TYPES)); + $self->type_stored($_, $food_share) for (FOOD_TYPES); } else { - $self->add_water(sprintf('%.0f', $self->water_hour * $tick_rate)); - } - - # ore - my %ore; - my $ore_produced = 0; - foreach my $type (ORE_TYPES) { - my $method = $type.'_hour'; - $ore{$type} = sprintf('%.0f', $self->$method() * $tick_rate); - if ($ore{$type} > 0) { - $ore_produced += $ore{$type}; + # waste + if ($self->waste_hour < 0 ) { # if it gets negative, spend out of storage + $self->spend_waste(sprintf('%.0f',abs($self->waste_hour) * $tick_rate)); } - } - my $ore_consumed = sprintf('%.0f', $self->ore_consumption_hour * $tick_rate); - if ($ore_produced > 0 and $ore_produced >= $ore_consumed) { - # then consumption comes out of production + else { + $self->add_waste(sprintf('%.0f', $self->waste_hour * $tick_rate)); + } + + # energy + if ($self->energy_hour < 0 ) { # if it gets negative, spend out of storage + $self->spend_energy(sprintf('%.0f',abs($self->energy_hour) * $tick_rate)); + } + else { + $self->add_energy(sprintf('%.0f', $self->energy_hour * $tick_rate)); + } + + # water + if ($self->water_hour < 0 ) { # if it gets negative, spend out of storage + $self->spend_water(sprintf('%.0f',abs($self->water_hour) * $tick_rate)); + } + else { + $self->add_water(sprintf('%.0f', $self->water_hour * $tick_rate)); + } + + # ore + my %ore; + my $ore_produced = 0; foreach my $type (ORE_TYPES) { + my $method = $type.'_hour'; + $ore{$type} = sprintf('%.0f', $self->$method() * $tick_rate); if ($ore{$type} > 0) { - $ore{$type} -= sprintf('%.0f', $ore{$type} * $ore_consumed / $ore_produced); + $ore_produced += $ore{$type}; } } - } - else { - # We are consuming more than we are producing - # The difference between consumed and produced comes out of storage - $ore_consumed -= $ore_produced; - if ($ore_consumed > 0) { - my $total_ore = $self->ore_stored; - if ($total_ore > 0) { - my $deduct_ratio = $ore_consumed / $total_ore; - $deduct_ratio = 1 if $deduct_ratio > 1; - foreach my $type (ORE_TYPES) { - my $type_stored = $self->type_stored($type); - $ore{$type} = 0 if $ore{$type} > 0; - my $to_deduct = sprintf('%.0f', $type_stored * $deduct_ratio); - $self->spend_ore_type($type, $to_deduct); - $ore_consumed -= $to_deduct; + my $ore_consumed = sprintf('%.0f', $self->ore_consumption_hour * $tick_rate); + if ($ore_produced > 0 and $ore_produced >= $ore_consumed) { + # then consumption comes out of production + foreach my $type (ORE_TYPES) { + if ($ore{$type} > 0) { + $ore{$type} -= sprintf('%.0f', $ore{$type} * $ore_consumed / $ore_produced); } - - } - # if we *still* have ore to consume when we have nothing then we are in trouble! - if ($ore_consumed > 20) { - # deduct an arbitrary ore-stuff, but allow for rounding (hence the '20') - $self->spend_ore_type('gold', $ore_consumed, 'complain'); } } - } - # Now deal with remaining individual ore stuffs - foreach my $type (ORE_TYPES) { - if ($ore{$type} > 0) { - $self->add_ore_type($type, $ore{$type}); + else { + # We are consuming more than we are producing + # The difference between consumed and produced comes out of storage + $ore_consumed -= $ore_produced; + if ($ore_consumed > 0) { + my $total_ore = $self->ore_stored; + if ($total_ore > 0) { + my $deduct_ratio = $ore_consumed / $total_ore; + $deduct_ratio = 1 if $deduct_ratio > 1; + foreach my $type (ORE_TYPES) { + my $type_stored = $self->type_stored($type); + $ore{$type} = 0 if $ore{$type} > 0; + my $to_deduct = sprintf('%.0f', $type_stored * $deduct_ratio); + $self->spend_ore_type($type, $to_deduct); + $ore_consumed -= $to_deduct; + } + + } + # if we *still* have ore to consume when we have nothing then we are in trouble! + if ($ore_consumed > 20) { + # deduct an arbitrary ore-stuff, but allow for rounding (hence the '20') + $self->spend_ore_type('gold', $ore_consumed, 'complain'); + } + } } - elsif ($ore{$type} < 0) { - $self->spend_ore_type($type, abs($ore{$type})); + # Now deal with remaining individual ore stuffs + foreach my $type (ORE_TYPES) { + if ($ore{$type} > 0) { + $self->add_ore_type($type, $ore{$type}); + } + elsif ($ore{$type} < 0) { + $self->spend_ore_type($type, abs($ore{$type})); + } } - } - # food - my %food; - my $food_produced = 0; - foreach my $type (FOOD_TYPES) { - my $production_hour_method = $type.'_production_hour'; - $food{$type} = sprintf('%.0f', $self->$production_hour_method() * $tick_rate); - if ($food{$type} > 0) { - $food_produced += $food{$type}; - } - } - my $food_consumed = sprintf('%.0f', $self->food_consumption_hour * $tick_rate); - if ($food_produced > 0 and $food_produced >= $food_consumed) { - # Then consumption just comes out of production + # food + my %food; + my $food_produced = 0; foreach my $type (FOOD_TYPES) { + my $production_hour_method = $type.'_production_hour'; + $food{$type} = sprintf('%.0f', $self->$production_hour_method() * $tick_rate); if ($food{$type} > 0) { - $food{$type} -= sprintf('%.0f', $food{$type} * $food_consumed / $food_produced); + $food_produced += $food{$type}; } } - } - else { - # We are consuming more than we are producing - # The difference between consumed and produced comes out of storage - $food_consumed -= $food_produced; - if ($food_consumed > 0) { - my $total_food = $self->food_stored; - if ($total_food > 0) { - # - my $deduct_ratio = $food_consumed / $total_food; - $deduct_ratio = 1 if $deduct_ratio > 1; - foreach my $type (FOOD_TYPES) { - my $type_stored = $self->type_stored($type); - $food{$type} = 0 if $food{$type} > 0; - my $to_deduct = sprintf('%.0f', $type_stored * $deduct_ratio); - $self->spend_food_type($type, $to_deduct); - $food_consumed -= $to_deduct; + my $food_consumed = sprintf('%.0f', $self->food_consumption_hour * $tick_rate); + if ($food_produced > 0 and $food_produced >= $food_consumed) { + # Then consumption just comes out of production + foreach my $type (FOOD_TYPES) { + if ($food{$type} > 0) { + $food{$type} -= sprintf('%.0f', $food{$type} * $food_consumed / $food_produced); } } - # if we *still* have food to consume when we have nothing then we are in trouble! - if ($food_consumed > 20) { - # deduct an arbitrary food-stuff, but allow for rounding errors (hence the 20) - $self->spend_food_type('algae', $food_consumed, 'complain'); - } } - } - # Now deal with remaining individual food stuffs - foreach my $type (FOOD_TYPES) { - if ($food{$type} > 0) { - $self->add_food_type($type, $food{$type}); + else { + # We are consuming more than we are producing + # The difference between consumed and produced comes out of storage + $food_consumed -= $food_produced; + if ($food_consumed > 0) { + my $total_food = $self->food_stored; + if ($total_food > 0) { + # + my $deduct_ratio = $food_consumed / $total_food; + $deduct_ratio = 1 if $deduct_ratio > 1; + foreach my $type (FOOD_TYPES) { + my $type_stored = $self->type_stored($type); + $food{$type} = 0 if $food{$type} > 0; + my $to_deduct = sprintf('%.0f', $type_stored * $deduct_ratio); + $self->spend_food_type($type, $to_deduct); + $food_consumed -= $to_deduct; + } + } + # if we *still* have food to consume when we have nothing then we are in trouble! + if ($food_consumed > 20) { + # deduct an arbitrary food-stuff, but allow for rounding errors (hence the 20) + $self->spend_food_type('algae', $food_consumed, 'complain'); + } + } } - elsif ($food{$type} < 0) { - $self->spend_food_type($type, abs($food{$type})); + # Now deal with remaining individual food stuffs + foreach my $type (FOOD_TYPES) { + if ($food{$type} > 0) { + $self->add_food_type($type, $food{$type}); + } + elsif ($food{$type} < 0) { + $self->spend_food_type($type, abs($food{$type})); + } } } # deal with negative amounts stored diff --git a/lib/Lacuna/DB/Result/Ships.pm b/lib/Lacuna/DB/Result/Ships.pm index 2040e388..c34729c1 100644 --- a/lib/Lacuna/DB/Result/Ships.pm +++ b/lib/Lacuna/DB/Result/Ships.pm @@ -5,6 +5,7 @@ use utf8; no warnings qw(uninitialized); extends 'Lacuna::DB::Result'; use Lacuna::Util qw(format_date randint); +use Lacuna::Constants qw(HIGH_SPEED_TEST_TRAVEL_DIVISOR HIGH_SPEED_TEST_TRAVEL_MIN_SECONDS); use DateTime; use Scalar::Util qw(weaken); use feature "switch"; @@ -751,8 +752,12 @@ sub travel_time { my $hours = $distance / $speed; my $seconds = 60 * 60 * $hours; $seconds += 300; -# $seconds = 1 if $seconds < 1; - $seconds = 300 if $seconds < 300; #minimum time for flights + my $minimum = 300; #minimum time for flights + if (Lacuna->high_speed_test_mode) { + $seconds /= HIGH_SPEED_TEST_TRAVEL_DIVISOR; + $minimum = HIGH_SPEED_TEST_TRAVEL_MIN_SECONDS; + } + $seconds = $minimum if $seconds < $minimum; return sprintf('%.0f', $seconds); }