diff --git a/docs/TheDillonForge.pod b/docs/TheDillonForge.pod index 2d62e4e8..1f905c32 100644 --- a/docs/TheDillonForge.pod +++ b/docs/TheDillonForge.pod @@ -4,4 +4,109 @@ The Dillon Forge is accessible via the URL C. The list of methods below represents changes and additions to the methods that all L share. -=cut \ No newline at end of file +=head2 view ( session_id, building_id ) + +This method is extended to include the list of tasks. + + { + "status " : { ... }, + "building" : { ... }, + "tasks" : { + "current_task" : "make_plan", + "seconds_remaining" : 120, + "can" : 0, + + "make_plan" : [ + { + "name" : "Algae", + "max_level" : 10, + "class" : "Food::Algae", + "seconds_per_level" : 5000, + },... + ], + "split_plan" : [ + { + "name" : "Beach [10]", + "class" : "Permanent::Beach10", + "level" : 1, + "extra_build_level" : 2, + "fail_chance" : 50, + "seconds_to_split", + },... + ] + } + } + +=head2 make_plan ( session_id, building_id, plan_class, level ) + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the Dillon Forge. + +=head3 plan_class + +The class of the plan, as returned in the B method + +=head3 level + +The level to build the plan. Note that The Dillon Forge can only build plans +up to the level of the building. A level 10 Dillon Forge can build any plan level +from 2+0 to 10+0. There must be at least 2x level 1+0 plans on a colony of the +specified class to build a level x+0 plan. + +The B is how long it will take to build a plan per level. + +=head2 split_plan ( session_id, building_id, plan_class, level, extra_build_level ) + +Split a plan into it's constituant glyphs. The glyph types that can be returned +depend upon the recipe to build that type of plan. The maximum number of glyphs +that can be returned depend upon the number of glyphs in the recipe for a level +1+0 plan, the level of the plan being split and the extra build levels. + +The B given in the view method, determines the number of glyphs that +are likely to be returned. For example, if a plan is 'worth' 4 glyphs then, on +average, a 50% fail_chance will return 2 glyphs (chosen at random from all glyph +types that make up the plan. + +The B is determined by the level of The Dillon Forge and is 3% per +level of the building. + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the Dillon Forge. + +=head3 plan_class + +The class of the plan, as returned in the B method + +=head3 level + +The level of the plan to be split. + +=head3 extra_build_level + +The extra build levels (if any) for that plan. + +=head2 subsidize ( session_id, building_id ) + +Will spend 2 essentia to complete the current task immediately. Returns B + +Throws 1011. + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the Dillon Forge. + +=cut -- 2.51.2 From b2ec930c9f1128e32a37e531e2d3b840ae51bfbc Mon Sep 17 00:00:00 2001 From: ian docherty Date: Tue, 17 Jan 2012 17:16:38 -0500 Subject: [PATCH 2/3] Completed and tested the Dillon Forge API --- docs/TheDillonForge.pod | 4 +- lib/Lacuna/DB/Result/Building/Archaeology.pm | 275 +----------------- .../Building/Permanent/TheDillonForge.pm | 128 ++++++++ lib/Lacuna/DB/Result/Plans.pm | 76 +++++ lib/Lacuna/RPC/Building/TheDillonForge.pm | 136 +++++++++ t/400_TheDillonForge.t | 157 ++++++++++ var/messages/plan_created_by_forge.txt | 7 + var/messages/plan_split_by_forge.txt | 7 + var/messages/plan_split_by_forge_failure.txt | 7 + var/www/public/changes.txt | 3 + 10 files changed, 533 insertions(+), 267 deletions(-) create mode 100644 t/400_TheDillonForge.t create mode 100644 var/messages/plan_created_by_forge.txt create mode 100644 var/messages/plan_split_by_forge.txt create mode 100644 var/messages/plan_split_by_forge_failure.txt diff --git a/docs/TheDillonForge.pod b/docs/TheDillonForge.pod index 1f905c32..d7ff2e7b 100644 --- a/docs/TheDillonForge.pod +++ b/docs/TheDillonForge.pod @@ -21,7 +21,7 @@ This method is extended to include the list of tasks. "name" : "Algae", "max_level" : 10, "class" : "Food::Algae", - "seconds_per_level" : 5000, + "reset_sec_per_level" : 5000, },... ], "split_plan" : [ @@ -31,7 +31,7 @@ This method is extended to include the list of tasks. "level" : 1, "extra_build_level" : 2, "fail_chance" : 50, - "seconds_to_split", + "reset_seconds", },... ] } diff --git a/lib/Lacuna/DB/Result/Building/Archaeology.pm b/lib/Lacuna/DB/Result/Building/Archaeology.pm index b0c021f5..d7cd51ab 100644 --- a/lib/Lacuna/DB/Result/Building/Archaeology.pm +++ b/lib/Lacuna/DB/Result/Building/Archaeology.pm @@ -118,281 +118,26 @@ before finish_work => sub { } }; -my %recipies = ( - anthracite => { - bauxite => { - beryl => { - chalcopyrite=> { - plan => 'Lacuna::DB::Result::Building::Permanent::LibraryOfJith', - }, - }, - }, - trona => { - kerogen => { - plan => 'Lacuna::DB::Result::Building::Permanent::BeeldebanNest', - }, - }, - }, - bauxite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Sand', - trona => { - kerogen => { - monazite => { - plan => 'Lacuna::DB::Result::Building::Permanent::MassadsHenge', - }, - }, - }, - }, - beryl => { - sulfur => { - monazite => { - galena => { - plan => 'Lacuna::DB::Result::Building::Permanent::CitadelOfKnope', - }, - }, - }, - trona => { - plan => 'Lacuna::DB::Result::Building::Permanent::AmalgusMeadow', - }, - }, - chalcopyrite=> { - plan => 'Lacuna::DB::Result::Building::Permanent::Lagoon', - sulfur => { - plan => 'Lacuna::DB::Result::Building::Permanent::GeoThermalVent', - }, - }, - chromite => { - bauxite => { - gold => { - kerogen => { - plan => 'Lacuna::DB::Result::Building::Permanent::GratchsGauntlet', - }, - }, - }, - halite => { - anthracite => { - beryl => { - plan => 'Lacuna::DB::Result::Building::Permanent::LibraryOfJith', - }, - }, - }, - }, - fluorite => { - kerogen => { - plan => 'Lacuna::DB::Result::Building::Permanent::MalcudField', - }, - }, - galena => { - gold => { - plan => 'Lacuna::DB::Result::Building::Permanent::KalavianRuins', - }, - }, - goethite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Lake', - halite => { - gypsum => { - trona => { - plan => 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk', - }, - }, - }, - }, - gold => { - uraninite => { - bauxite => { - goethite => { - plan => 'Lacuna::DB::Result::Building::Permanent::OracleOfAnid', - }, - }, - }, - anthracite => { - uraninite => { - bauxite => { - plan => 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk', - }, - }, - }, - }, - gypsum => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach1', - anthracite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach9', - }, - chalcopyrite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach7', - }, - chromite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach11', - }, - galena => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach13', - }, - goethite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach12', - }, - gypsum => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach2', - }, - halite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach5', - }, - magnetite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach3', - }, - methane => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach10', - }, - rutile => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach6', - }, - sulfur => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach8', - }, - trona => { - beryl => { - anthracite => { - plan => 'Lacuna::DB::Result::Building::Permanent::PantheonOfHagness', - }, - }, - }, - uraninite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Beach4', - }, - }, - halite => { - anthracite => { - plan => 'Lacuna::DB::Result::Building::Permanent::LapisForest', - }, - }, - kerogen => { - beryl => { - anthracite => { - monazite => { - plan => 'Lacuna::DB::Result::Building::Permanent::BlackHoleGenerator', - }, - }, - }, - methane => { - sulfur => { - zircon => { - plan => 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk', - }, - }, - }, - rutile => { - chromite => { - chalcopyrite=> { - plan => 'Lacuna::DB::Result::Building::Permanent::TempleOfTheDrajilites', - }, - }, - }, - }, - magnetite => { - halite => { - plan => 'Lacuna::DB::Result::Building::Permanent::NaturalSpring', - }, - uraninite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Volcano', - }, - }, - methane => { - plan => 'Lacuna::DB::Result::Building::Permanent::Grove', - zircon => { - fluorite => { - plan => 'Lacuna::DB::Result::Building::Permanent::InterDimensionalRift', - }, - magnetite => { - beryl => { - plan => 'Lacuna::DB::Result::Building::Permanent::TerraformingPlatform', - }, - }, - }, - }, - monazite => { - fluorite => { - beryl => { - magnetite => { - plan => 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk', - }, - }, - }, - trona => { - gold => { - bauxite => { - plan => 'Lacuna::DB::Result::Building::Permanent::CrashedShipSite', - }, - }, - }, - uraninite => { - sulfur => { - trona => { -# plan => 'Lacuna::DB::Result::Building::Permanent::KasternsKeep', - }, - }, - }, - }, - rutile => { - plan => 'Lacuna::DB::Result::Building::Permanent::Crater', - chromite => { - chalcopyrite => { - galena => { - plan => 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk', - }, - }, - }, - goethite => { - plan => 'Lacuna::DB::Result::Building::Permanent::DentonBrambles', - }, - }, - sulfur => { - methane => { - galena => { - anthracite => { - plan => 'Lacuna::DB::Result::Building::Permanent::GasGiantPlatform', - }, - }, - }, - }, - trona => { - plan => 'Lacuna::DB::Result::Building::Permanent::RockyOutcrop', - }, - uraninite => { - methane => { - plan => 'Lacuna::DB::Result::Building::Permanent::AlgaePond', - }, - }, - zircon => { - methane => { - galena => { - fluorite => { - plan => 'Lacuna::DB::Result::Building::Permanent::Ravine', - }, - }, - }, - }, -); - -#rutile chromite chalcopyrite galena gold anthracite uraninite bauxite -#goethite halite gypsum trona kerogen methane sulfur zircon monazite fluorite beryl magnetite - sub make_plan { my ($self, $ids) = @_; unless (ref $ids eq 'ARRAY' && scalar(@{$ids}) < 5) { confess [1009, 'It is not possible to combine more than 4 glyphs.']; } - my $match = clone(\%recipies); - my $glyphs = $self->body->glyphs; + + my @glyph_names; + my $glyphs_rs = $self->body->glyphs; foreach my $id (@{$ids}) { - my $glyph = $glyphs->find($id); + my $glyph = $glyphs_rs->find($id); confess [1002, 'You tried to combine a glyph you do not have.'] unless defined $glyph; - confess [1002, 'The glyphs specified do not fit together in that manner.'] unless exists $match->{$glyph->type}; - $match = $match->{$glyph->type}; + push @glyph_names,$glyph->type; } - unless (exists $match->{plan}) { + + my $plan_class = Lacuna::DB::Result::Plans->check_glyph_recipe(\@glyph_names); + if (not $plan_class) { confess [1002, 'The glyphs specified do not fit together in that manner.']; } - $glyphs->search({ id => { in => $ids}})->delete; - return $self->body->add_plan($match->{plan}, 1); + $glyphs_rs->search({ id => { in => $ids}})->delete; + return $self->body->add_plan($plan_class, 1); } no Moose; diff --git a/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm b/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm index e13a0336..b4ed8ff8 100644 --- a/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm +++ b/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm @@ -1,6 +1,9 @@ package Lacuna::DB::Result::Building::Permanent::TheDillonForge; use Moose; +use List::Util qw(shuffle); +use Data::Dumper; + use utf8; no warnings qw(uninitialized); extends 'Lacuna::DB::Result::Building::Permanent'; @@ -44,6 +47,131 @@ use constant name => 'The Dillon Forge'; use constant time_to_build => 0; use constant max_instances_per_planet => 1; +sub split_plan { + my ($self, $plan_class, $level, $extra_build_level) = @_; + + my $halls = $self->equivalent_halls($level, $extra_build_level); + my $class = 'Lacuna::DB::Result::Building::'.$plan_class; + my ($plan) = $self->body->plans->search({ + level => $level, + extra_build_level => $extra_build_level, + class => $class, + }); + if (not $plan) { + confess [1002, 'You cannot split a plan you do not have.']; + } + my $glyphs = Lacuna::DB::Result::Plans->get_glyph_recipe($class); + if (not $glyphs) { + confess [1002, 'You can only split plans that have a glyph recipe.']; + } + $plan->delete; + my $build_secs = int($halls * 30 * 3600 / $self->level); + $self->start_work({task => 'split_plan', class => $class, level => $level, extra_build_level => $extra_build_level}, $build_secs)->update; +} + +sub make_plan { + my ($self, $plan_class, $level) = @_; + + # Do we have the requisite number of level 1 plans? + my $class = 'Lacuna::DB::Result::Building::'.$plan_class; + + my $plans_rs = $self->body->plans->search({ + level => 1, + extra_build_level => 0, + class => $class, + }); + my $have_plans = $plans_rs->count; + if ($have_plans < $level * 2) { + confess [1002, 'You do not have enough level 1+0 plans.']; + } + if ($level > $self->level) { + confess [1002, 'Your Dillon Forge level is not high enough to build that high a plan level.']; + } + $plans_rs->search({},{ + rows => $level * 2, + })->delete_all; + + $self->start_work({task => 'make_plan', level => $level, class => $class}, ($level * 5000))->update; +} + +sub equivalent_halls { + my ($self, $level, $extra_build_level) = @_; + + my $arg_k = int($extra_build_level / 2 + 0.5); + my $arg_l = $level * 2 + $extra_build_level; + my $arg_m = ($extra_build_level % 2) ? 0 : $level + $extra_build_level / 2; + my $halls = $arg_k * $arg_l + $arg_m; + + return $halls; +} + +before finish_work => sub { + my $self = shift; + + my $work = $self->work; + my $body = $self->body; + my $empire = $body->empire; + my $plan_class = $work->{class}; + + if ($work->{task} eq 'make_plan') { + $self->body->add_to_plans({ + level => $work->{level}, + class => $plan_class, + extra_build_level => 0, + }); + $empire->send_predefined_message( + tags => ['Alert'], + filename => 'plan_created_by_forge.txt', + params => [$body->id, $body->name, $work->{level}, $plan_class->name], + ); + $body->add_news(100, sprintf('%s used the Dillon Forge to create a %s plan level %s on %s.', $empire->name, $plan_class->name, $work->{level}, $body->name)); + } + if ($work->{task} eq 'split_plan') { + # calculate the probability of success + my $success_percent = $self->level * 3; + my $halls = $self->equivalent_halls($work->{level}, $work->{extra_build_level}); + + if ($plan_class =~ m/HallsOfVrbansk$/) { + # create a random A,B,C or D hall + my @hall_types = ('A', 'B', 'C', 'D', 'E'); + my $hall_type = $hall_types[randint(0,4)]; + $plan_class .= " $hall_type"; + } + my $glyphs = Lacuna::DB::Result::Plans->get_glyph_recipe($plan_class); + my @many_glyphs = shuffle map { @$glyphs } (1..$halls); + my $glyphs_built; + my $total_glyphs = 0; + for my $glyph (@many_glyphs) { + if ($success_percent > rand(100)) { + $self->body->add_to_glyphs({ + type => $glyph, + }); + $glyphs_built->{$glyph} = $glyphs_built->{$glyph} ? $glyphs_built->{$glyph}+1 : 1; + $total_glyphs++; + } + } + my @report = map { [ $glyphs_built->{$_}, $_ ]} keys %$glyphs_built; + unshift (@report, ['Quantity','Glyph']); + + if ($total_glyphs > 0) { + $empire->send_predefined_message( + tags => ['Alert'], + filename => 'plan_split_by_forge.txt', + params => [$body->id, $body->name, $work->{level}, $work->{extra_build_level}, $plan_class->name], + attachments => { table => \@report }, + ); + $body->add_news(100, sprintf('%s used the Dillon Forge to split a %s level %s + %s plan into %s glyphs today on %s', $empire->name, $plan_class->name, $work->{level}, $work->{extra_build_level}, $total_glyphs, $body->name)); + } + else { + $empire->send_predefined_message( + tags => ['Alert'], + filename => 'plan_split_by_forge_failure.txt', + params => [$body->id, $body->name, $work->{level}, $work->{extra_build_level}, $plan_class->name], + ); + $body->add_news(100, sprintf('%s failed miserably in an attempt to run the Dillon Forge today on %s', $empire->name, $body->name)); + } + } +}; no Moose; __PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/Result/Plans.pm b/lib/Lacuna/DB/Result/Plans.pm index 1a0fee3f..82712dc3 100644 --- a/lib/Lacuna/DB/Result/Plans.pm +++ b/lib/Lacuna/DB/Result/Plans.pm @@ -1,5 +1,6 @@ package Lacuna::DB::Result::Plans; +use 5.010; use Moose; use utf8; no warnings qw(uninitialized); @@ -24,6 +25,81 @@ sub level_formatted { return $level; } +my $recipes = { + 'Lacuna::DB::Result::Building::Permanent::LibraryOfJith' => [qw(anthracite bauxite beryl chalcopyrite)], + 'Lacuna::DB::Result::Building::Permanent::BeeldebanNest' => [qw(anthracite trona kerogen)], + 'Lacuna::DB::Result::Building::Permanent::Sand' => [qw(bauxite)], + 'Lacuna::DB::Result::Building::Permanent::MassadsHenge' => [qw(bauxite trona kerogen monazite)], + 'Lacuna::DB::Result::Building::Permanent::CitadelOfKnope' => [qw(beryl sulfur monazite galena)], + 'Lacuna::DB::Result::Building::Permanent::AmalgusMeadow' => [qw(beryl trona)], + 'Lacuna::DB::Result::Building::Permanent::Lagoon' => [qw(chalcopyrite)], + 'Lacuna::DB::Result::Building::Permanent::GeoThermalVent' => [qw(chalcopyrite sulfur)], + 'Lacuna::DB::Result::Building::Permanent::GratchsGauntlet' => [qw(chromite bauxite gold kerogen)], + 'Lacuna::DB::Result::Building::Permanent::LibraryOfJith' => [qw(chromite halite anthracite beryl)], + 'Lacuna::DB::Result::Building::Permanent::MalcudField' => [qw(fluorite kerogen)], + 'Lacuna::DB::Result::Building::Permanent::KalavianRuins' => [qw(galena gold)], + 'Lacuna::DB::Result::Building::Permanent::Lake' => [qw(goethite)], + 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk A' => [qw(goethite halite gypsum trona)], + 'Lacuna::DB::Result::Building::Permanent::OracleOfAnid' => [qw(gold uraninite bauxite goethite)], + 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk B' => [qw(gold anthracite uraninite bauxite)], + 'Lacuna::DB::Result::Building::Permanent::Beach1' => [qw(gypsum)], + 'Lacuna::DB::Result::Building::Permanent::Beach9' => [qw(gypsum anthracite)], + 'Lacuna::DB::Result::Building::Permanent::Beach7' => [qw(gypsum chalcopyrite)], + 'Lacuna::DB::Result::Building::Permanent::Beach11' => [qw(gypsum chromite)], + 'Lacuna::DB::Result::Building::Permanent::Beach13' => [qw(gypsum galena)], + 'Lacuna::DB::Result::Building::Permanent::Beach12' => [qw(gypsum goethite)], + 'Lacuna::DB::Result::Building::Permanent::Beach2' => [qw(gypsum gypsum)], + 'Lacuna::DB::Result::Building::Permanent::Beach5' => [qw(gypsum halite)], + 'Lacuna::DB::Result::Building::Permanent::Beach3' => [qw(gypsum magnetite)], + 'Lacuna::DB::Result::Building::Permanent::Beach10' => [qw(gypsum methane)], + 'Lacuna::DB::Result::Building::Permanent::Beach6' => [qw(gypsum rutile)], + 'Lacuna::DB::Result::Building::Permanent::Beach8' => [qw(gypsum sulfur)], + 'Lacuna::DB::Result::Building::Permanent::PantheonOfHagness' => [qw(gypsum trona beryl anthracite)], + 'Lacuna::DB::Result::Building::Permanent::Beach4' => [qw(gypsum uraninite)], + 'Lacuna::DB::Result::Building::Permanent::LapisForest' => [qw(halite anthracite)], + 'Lacuna::DB::Result::Building::Permanent::BlackHoleGenerator' => [qw(kerogen beryl anthracite monazite)], + 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk C' => [qw(kerogen methane sulfur zircon)], + 'Lacuna::DB::Result::Building::Permanent::TempleOfTheDrajilites'=> [qw(kerogen rutile chromite chalcopyrite)], + 'Lacuna::DB::Result::Building::Permanent::NaturalSpring' => [qw(magnetite halite)], + 'Lacuna::DB::Result::Building::Permanent::Volcano' => [qw(magnetite uraninite)], + 'Lacuna::DB::Result::Building::Permanent::Grove' => [qw(methane)], + 'Lacuna::DB::Result::Building::Permanent::InterDimensionalRift' => [qw(methane zircon fluorite)], + 'Lacuna::DB::Result::Building::Permanent::TerraformingPlatform' => [qw(methane zircon magnetite)], + 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk D' => [qw(monazite fluorite beryl magnetite)], + 'Lacuna::DB::Result::Building::Permanent::CrashedShipSite' => [qw(monazite trona gold bauxite)], +# 'Lacuna::DB::Result::Building::Permanent::KasternsKeep' => [qw(monazite uraninite sulfur trona)], + 'Lacuna::DB::Result::Building::Permanent::Crater' => [qw(rutile)], + 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk E' => [qw(rutile chromite chalcopyrite galena)], + 'Lacuna::DB::Result::Building::Permanent::DentonBrambles' => [qw(rutile goethite)], + 'Lacuna::DB::Result::Building::Permanent::GasGiantPlatform' => [qw(sulfur methane galena anthracite)], + 'Lacuna::DB::Result::Building::Permanent::RockyOutcrop' => [qw(trona)], + 'Lacuna::DB::Result::Building::Permanent::AlgaePond' => [qw(uraninite methane)], + 'Lacuna::DB::Result::Building::Permanent::Ravine' => [qw(zircon methane galena fluorite)], +}; + +# given a plan class, return the glyph recipe +# (note, Halls must specify A,B,C,D or E) +# +sub get_glyph_recipe { + my ($class,$plan_class) = @_; + + return $recipes->{$plan_class}; +} + +# Given glyphs, return the plan (if any) +sub check_glyph_recipe { + my ($class, $glyphs) = @_; + + my ($plan_class) = grep {@{$recipes->{$_}} ~~ @$glyphs} keys %$recipes; + if (defined $plan_class) { + # Sort out different Halls recipes + $plan_class =~ s/HallsOfVrbansk.*$/HallsOfVrbansk/; + } + + return $plan_class; +} + + __PACKAGE__->belongs_to('body', 'Lacuna::DB::Result::Map::Body', 'body_id'); no Moose; diff --git a/lib/Lacuna/RPC/Building/TheDillonForge.pm b/lib/Lacuna/RPC/Building/TheDillonForge.pm index 615071b3..028d54a0 100644 --- a/lib/Lacuna/RPC/Building/TheDillonForge.pm +++ b/lib/Lacuna/RPC/Building/TheDillonForge.pm @@ -2,6 +2,8 @@ package Lacuna::RPC::Building::TheDillonForge; use Moose; use utf8; +use List::Util qw(min max); + no warnings qw(uninitialized); extends 'Lacuna::RPC::Building'; @@ -13,6 +15,140 @@ sub model_class { return 'Lacuna::DB::Result::Building::Permanent::TheDillonForge'; } +around 'view' => sub { + my ($orig, $self, $session_id, $building_id) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id, skip_offline => 1); + my $out = $orig->($self, $empire, $building); + if ($building->is_working) { + $out->{tasks} = { + seconds_remaining => $building->work_seconds_remaining, + can => 0, + }; + } + else { + $out->{tasks} = $self->_forge_tasks($building); + } + return $out; +}; + +# A number which represents how many Halls of Vrbansk it would take to build the plan +sub equivalent_halls { + my ($self, $plan) = @_; + + my $arg_k = int($plan->extra_build_level / 2 + 0.5); + my $arg_l = $plan->level * 2 + $plan->extra_build_level; + my $arg_m = ($plan->extra_build_level % 2) ? 0 : $plan->level + $plan->extra_build_level / 2; + my $halls = $arg_k * $arg_l + $arg_m; + + return $halls; +} + +sub _forge_tasks { + my ($self, $building) = @_; + + my $building_level = $building->level ? $building->level : 1; + + # How many plans do we have? + my $plans_rs = Lacuna->db->resultset('Lacuna::DB::Result::Plans')->search({ + body_id => $building->body_id, + }, { + order_by => [qw(class)], + group_by => [qw(class level extra_build_level)], + select => ['class','level','extra_build_level',{count => 'id', -as => 'count_plans'}], + as => ['class','level','extra_build_level','no_of_plans'], + }); + my @split_plans; +PLAN: + while (my $plan = $plans_rs->next) { + # Can only split plans with recipes + next PLAN if not Lacuna::DB::Result::Plans->get_glyph_recipe($plan->class); + + my ($class) = $plan->class =~ m/Lacuna::DB::Result::Building::(.*)$/; + + my $halls = $self->equivalent_halls($plan); + + push @split_plans, { + name => $plan->class->name, + class => $class, + level => $plan->level, + extra_build_level => $plan->extra_build_level, + fail_chance => 100 - ($building->level * 3), + reset_seconds => $halls * 30 * 3600 / $building->level, + }; + } + # now refine the search for level 1 plans only + $plans_rs = $plans_rs->search({ + body_id => $building->body_id, + level => 1, + extra_build_level => 0, + }); + + my @make_plans; + while (my $plan = $plans_rs->next) { + # It takes 2 level 1 plans for each level we are constructing. + my $max_level = min(int($plan->get_column('no_of_plans') / 2), $building_level); + if ($max_level >= 2) { + my ($class) = $plan->class =~ m/Lacuna::DB::Result::Building::(.*)$/; + push @make_plans, { + name => $plan->class->name, + max_level => $max_level, + class => $class, + reset_sec_per_level => 5000, + }; + } + } + + return { + can => 1, + make_plan => \@make_plans, + split_plan => \@split_plans, + }; +} + +sub split_plan { + my ($self, $session_id, $building_id, $plan_class, $level, $extra_build_level) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + + $building->split_plan($plan_class, $level, $extra_build_level); + + return $self->view($empire, $building); +} + +sub make_plan { + my ($self, $session_id, $building_id, $plan_class, $level) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + + $building->make_plan($plan_class, $level); + + return $self->view($empire, $building); +} + +sub subsidize { + my ($self, $session_id, $building_id) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + + unless ($building->is_working) { + confess [1010, "Nothing is being done!"]; + } + + unless ($empire->essentia >= 2) { + confess [1011, "Not enough essentia."]; + } + + $building->finish_work->update; + $empire->spend_essentia(2, 'Dillon Forge subsidy after the fact'); + $empire->update; + + return $self->view($empire, $building); +} + +__PACKAGE__->register_rpc_method_names(qw(split_plan make_plan subsidize)); + no Moose; __PACKAGE__->meta->make_immutable; +1; diff --git a/t/400_TheDillonForge.t b/t/400_TheDillonForge.t new file mode 100644 index 00000000..c6778218 --- /dev/null +++ b/t/400_TheDillonForge.t @@ -0,0 +1,157 @@ +use lib '../lib'; +use Test::More tests => 11; +use Test::Deep; +use Data::Dumper; +use 5.010; +use DateTime; + +use strict; +use warnings; + +use TestHelper; +TestHelper->clear_all_test_empires; + +my $tester = TestHelper->new->generate_test_empire->build_infrastructure; +my $session_id = $tester->session->id; +my $empire = $tester->empire; +my $home = $empire->home_planet; + +my $result; + +my $forge_level = 5; +my $forge = $tester->build_building('Lacuna::DB::Result::Building::Permanent::TheDillonForge', $forge_level); +my $arch = $tester->build_building('Lacuna::DB::Result::Building::Archaeology', 15); +my $trade = $tester->build_building('Lacuna::DB::Result::Building::Trade', 15); + +# create some plans to split and combine + +for (1..21) { + $home->add_plan('Lacuna::DB::Result::Building::SpacePort', 5); +} + +for (1..21) { + $home->add_plan('Lacuna::DB::Result::Building::SpacePort', 1); +} + +$home->add_plan('Lacuna::DB::Result::Building::Permanent::AlgaePond', 1); +$home->add_plan('Lacuna::DB::Result::Building::Permanent::AlgaePond', 1, 5); + +$result = $tester->post('thedillonforge', 'view', [$session_id, $forge->id]); + +is_deeply($result->{result}{tasks}, { + can => 1, + make_plan => [ + { + name => 'Space Port', + max_level => $forge_level, + class => 'SpacePort', + reset_sec_per_level => 5000, + } + ], + split_plan => [ + { + name => 'Algae Pond', + class => 'Permanent::AlgaePond', + level => 1, + extra_build_level => 0, + fail_chance => 85, + reset_seconds => 21600, + }, + { + name => 'Algae Pond', + class => 'Permanent::AlgaePond', + level => 1, + extra_build_level => 5, + fail_chance => 85, + reset_seconds => 453600, + } + ], +}); + +$result = $tester->post('thedillonforge', 'subsidize', [$session_id, $forge->id]); + +is_deeply($result->{error}, { + data => undef, + message => 'Nothing is being done!', + code => 1010, +}); + +$result = $tester->post('thedillonforge', 'make_plan', [$session_id, $forge->id, 'SpacePort', 6]); + +is_deeply($result->{error}, { + data => undef, + message => 'Your Dillon Forge level is not high enough to build that high a plan level.', + code => 1002, +}); + +$result = $tester->post('thedillonforge', 'make_plan', [$session_id, $forge->id, 'SpacePort', $forge_level]); + +is_deeply($result->{result}{tasks}, { + seconds_remaining => 25000, + can => 0, +}); + +$result = $tester->post('thedillonforge', 'subsidize', [$session_id, $forge->id]); + +is_deeply($result->{error}, { + data => undef, + message => 'Not enough essentia.', + code => 1011, +}); + +$empire->add_essentia(100,'testing The Dillon Forge')->update; + +$result = $tester->post('thedillonforge', 'subsidize', [$session_id, $forge->id]); + +my $plans_1 = $home->plans->search({ + level => 1, + class => 'Lacuna::DB::Result::Building::SpacePort', + extra_build_level => 0, +})->count; + +is($plans_1, 11, "We have consumed 10 level 1 plans"); + +my $plans_5 = $home->plans->search({ + level => 5, + class => 'Lacuna::DB::Result::Building::SpacePort', + extra_build_level => 0, +})->count; + +is($plans_5, 22, "We have created an extra level 5+0 plan"); + +$result = $tester->post('thedillonforge', 'split_plan', [$session_id, $forge->id, 'SpacePort', 1, 0]); + +is_deeply($result->{error}, { + data => undef, + message => 'You can only split plans that have a glyph recipe.', + code => 1002, +}); + +$result = $tester->post('thedillonforge', 'split_plan', [$session_id, $forge->id, 'Permanent::AlgaePond', 1, 5]); + +is_deeply($result->{result}{tasks}, { + seconds_remaining => 453600, + can => 0, +}); + +$result = $tester->post('thedillonforge', 'subsidize', [$session_id, $forge->id]); + +$plans_1 = $home->plans->search({ + level => 1, + class => 'Lacuna::DB::Result::Building::Permanent::AlgaePond', + extra_build_level => 5, +})->count; + +is($plans_1, 0, "We have split a plan into glyphs"); + +my $glyphs = $home->glyphs->search({ + +})->count; + +cmp_ok($glyphs, '>', 0, "We expect at least one glyph"); + +END { +# TestHelper->clear_all_test_empires; +} +1; + diff --git a/var/messages/plan_created_by_forge.txt b/var/messages/plan_created_by_forge.txt new file mode 100644 index 00000000..5ec96067 --- /dev/null +++ b/var/messages/plan_created_by_forge.txt @@ -0,0 +1,7 @@ +Dillon Forge Makes Plan +~~~ +The Dillon Forge on {Planet %s %s} today created a level %s+0 %s plan. This is cause for great joy and celebration throughout the Empire. + +Regards, + +Your Humble Assistant diff --git a/var/messages/plan_split_by_forge.txt b/var/messages/plan_split_by_forge.txt new file mode 100644 index 00000000..44cf9cda --- /dev/null +++ b/var/messages/plan_split_by_forge.txt @@ -0,0 +1,7 @@ +Dillon Forge Splits Plan +~~~ +The Dillon Forge on {Planet %s %s} today split a level %s+%s %s plan and created the following glyphs. + +Regards, + +Your Humble Assistant diff --git a/var/messages/plan_split_by_forge_failure.txt b/var/messages/plan_split_by_forge_failure.txt new file mode 100644 index 00000000..9beed98a --- /dev/null +++ b/var/messages/plan_split_by_forge_failure.txt @@ -0,0 +1,7 @@ +Dillon Forge Failure +~~~ +The Dillon Forge on {Planet %s %s} failed to split a level %s %s plan, perhaps your scientists need to do further research into it's operation? + +Regards, + +Your Humble Assistant diff --git a/var/www/public/changes.txt b/var/www/public/changes.txt index 2a64afaf..5d38f28a 100644 --- a/var/www/public/changes.txt +++ b/var/www/public/changes.txt @@ -1,3 +1,6 @@ +3.0835 + - Add: API updated to support The Dillon Forge + 3.0834: - Fix: Spies caught up in failed insurrection five to defender level * 2. - Fix: Scows show empty when coming back from stars. -- 2.51.2 From 0ee2bc08f1832ccff3c4e339509ef6abb1cd6bd8 Mon Sep 17 00:00:00 2001 From: ian docherty Date: Thu, 26 Jan 2012 15:24:14 -0500 Subject: [PATCH 3/3] Stopped being able to split platforms, combine halls and fixed a couple of glyph recipes --- lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm | 7 +++++++ lib/Lacuna/DB/Result/Plans.pm | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm b/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm index b4ed8ff8..6fb8af5b 100644 --- a/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm +++ b/lib/Lacuna/DB/Result/Building/Permanent/TheDillonForge.pm @@ -64,6 +64,9 @@ sub split_plan { if (not $glyphs) { confess [1002, 'You can only split plans that have a glyph recipe.']; } + if ($class =~ m/Platform$/) { + confess [1002, 'You cannot split a plan for a Platform.']; + } $plan->delete; my $build_secs = int($halls * 30 * 3600 / $self->level); $self->start_work({task => 'split_plan', class => $class, level => $level, extra_build_level => $extra_build_level}, $build_secs)->update; @@ -87,6 +90,10 @@ sub make_plan { if ($level > $self->level) { confess [1002, 'Your Dillon Forge level is not high enough to build that high a plan level.']; } + if ($class =~ m/HallsOfVrbansk/) { + confess [1002, 'It is not a good idea to create a plan you cannot use.']; + } + $plans_rs->search({},{ rows => $level * 2, })->delete_all; diff --git a/lib/Lacuna/DB/Result/Plans.pm b/lib/Lacuna/DB/Result/Plans.pm index 82712dc3..42d3973e 100644 --- a/lib/Lacuna/DB/Result/Plans.pm +++ b/lib/Lacuna/DB/Result/Plans.pm @@ -35,7 +35,6 @@ my $recipes = { 'Lacuna::DB::Result::Building::Permanent::Lagoon' => [qw(chalcopyrite)], 'Lacuna::DB::Result::Building::Permanent::GeoThermalVent' => [qw(chalcopyrite sulfur)], 'Lacuna::DB::Result::Building::Permanent::GratchsGauntlet' => [qw(chromite bauxite gold kerogen)], - 'Lacuna::DB::Result::Building::Permanent::LibraryOfJith' => [qw(chromite halite anthracite beryl)], 'Lacuna::DB::Result::Building::Permanent::MalcudField' => [qw(fluorite kerogen)], 'Lacuna::DB::Result::Building::Permanent::KalavianRuins' => [qw(galena gold)], 'Lacuna::DB::Result::Building::Permanent::Lake' => [qw(goethite)], @@ -64,7 +63,7 @@ my $recipes = { 'Lacuna::DB::Result::Building::Permanent::Volcano' => [qw(magnetite uraninite)], 'Lacuna::DB::Result::Building::Permanent::Grove' => [qw(methane)], 'Lacuna::DB::Result::Building::Permanent::InterDimensionalRift' => [qw(methane zircon fluorite)], - 'Lacuna::DB::Result::Building::Permanent::TerraformingPlatform' => [qw(methane zircon magnetite)], + 'Lacuna::DB::Result::Building::Permanent::TerraformingPlatform' => [qw(methane zircon magnetite beryl)], 'Lacuna::DB::Result::Building::Permanent::HallsOfVrbansk D' => [qw(monazite fluorite beryl magnetite)], 'Lacuna::DB::Result::Building::Permanent::CrashedShipSite' => [qw(monazite trona gold bauxite)], # 'Lacuna::DB::Result::Building::Permanent::KasternsKeep' => [qw(monazite uraninite sulfur trona)],