diff --git a/bin/lacuna.psgi b/bin/lacuna.psgi index fe84cf47..41840a9f 100644 --- a/bin/lacuna.psgi +++ b/bin/lacuna.psgi @@ -215,6 +215,7 @@ my $app = builder { Lacuna::RPC::Building::SSLd Lacuna::RPC::Building::AmalgusMeadow Lacuna::RPC::Building::DentonBrambles + Lacuna::RPC::Building::MercenariesGuild )) { mount $building->new->to_app_with_url; } diff --git a/docs/Buildings.pod b/docs/Buildings.pod index 9369accd..ac490918 100644 --- a/docs/Buildings.pod +++ b/docs/Buildings.pod @@ -399,6 +399,8 @@ Below is a list of the different types of structures that can be built on planet =item L +=item L + =item L =item L diff --git a/docs/MercenariesGuild.pod b/docs/MercenariesGuild.pod new file mode 100644 index 00000000..714faf93 --- /dev/null +++ b/docs/MercenariesGuild.pod @@ -0,0 +1,281 @@ +=head1 Mercenaries Guild Methods + +The Mercenaries Guild is accessible via the URL C. It allows you to send spies to other players via spy pods. Due to the vast distance of space, you may only trade with other players in your zone. It costs essentia to use it. + +The list of methods below represents changes and additions to the methods that all L share. + + +=head2 add_to_market ( session_id, building_id, offer, ask, options ) + +Queues a trade for others to see. Returns: + + { + "trade_id" : "id-goes-here", + "status" : { ... }, + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the mercenaries guild. + +=head3 offer + +A hash reference containing the item you wish to trade. There is one type of item that can be traded via this mechanism: spies. + + +=over + +=item spies + +The hash reference for spiess looks like: + + { + "type" : "spy", + "spy_id" : "id-goes-here" + } + +=over + +=item type + +Must be exactly C. + +=item spy_id + +The unique id of the spy that you want to trade. See the C method for a list of your spies. + +=back + +=back + +=head3 ask + +An integer representing the amount of essentia you are asking for in this trade. Must be between 1 and 99. + +=head3 options + +A hash reference of options to modify the trade. + +=over + +=item ship_id + +The specific id of a ship you want to use for this trade. See C for details. + +=back + + +=head2 get_spies ( session_id, building_id ) + +Returns a list of spies that may be traded. Used with the C method. + + { + "spies" : [ + { + "id" : "id-goes-here", + "name" : "Jack Bauer", + "level" : "5" + }, + ... + ], + "cargo_space_used_each" : 350, + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of this building. + + +=head2 withdraw_from_market ( session_id, building_id, trade_id ) + +Remove a trade that you have offered and collect the items up for trade. + + { + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of this building. + +=head3 trade_id + +The unique id of the trade. + + + + +=head2 accept_from_market ( session_id, building_id, trade_id ) + +Accept a trade offer from the list of available trades. See C. + +Throws 1016. + + { + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of this building. + +=head3 trade_id + +The unique id of the trade. + + + +=head2 view_market ( session_id, building_id, [ page_number ] ) + +Displays a list of trades available at the present time. + + { + "trades" : [ + { + "date_offered" : "01 31 2010 13:09:05 +0600", + "id" : "id-goes-here", + "ask" : 25, # essentia + "offer" : [ + "Level 21 spy named Jack Bauer", + ], + "body" : { + "id" : "id-goes-here" # use with get_trade_ships() to determine travel time + }, + "empire" : { + "id" : "id-goes-here", + "name" : "Earthlings" + } + }, + ... + ], + "trade_count" : 1047, + "page_number" : 1, + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of this building. + +=head3 page_number + +Optional. An integer representing the page of trades (25 per page) to return. Defaults to 1. + + + + +=head2 view_my_market ( session_id, building_id, [ page_number ] ) + +Displays a list of trades the current user has posted. + + { + "trades" : [ + { + "date_offered" : "01 31 2010 13:09:05 +0600", + "id" : "id-goes-here", + "ask" : 25, # essentia + "offer" : [ + "Level 21 spy named Jack Bauer", + ] + }, + ... + ], + "trade_count" : 17, + "page_number" : 1, + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of this building. + +=head3 page_number + +An integer representing the page of trades (25 per page) to return. Defaults to 1. + + + +=head2 get_trade_ships ( session_id, building_id, [ target_body_id ] ) + +Returns a list of the ships that could be used to do a trade. + + { + "status" : { ... }, + "ships" : [ + { + "id" : "id-goes-here", + "type" : "spy_pod", + "name" : "Spy Pod 5", + "estimated_travel_time" : 3600, # in seconds, one way + ... + }, + ... + ] + } + + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of the trade ministry. + +=head3 target_body_id + +The unique id of the body you'll be shipping to. Optional. If included it will calculate the estimated travel time of the ships to this body. + + + + + +=head2 report_abuse ( session_id, building_id, trade_id ) + +Report a trade that you think is abusing the trade system. + + { + "status" : { ... } + } + +=head3 session_id + +A session id. + +=head3 building_id + +The unique id of this building. + +=head3 trade_id + +The unique id of the trade. + + + + +=cut diff --git a/lib/Lacuna/Constants.pm b/lib/Lacuna/Constants.pm index e6f3c98c..40e91413 100644 --- a/lib/Lacuna/Constants.pm +++ b/lib/Lacuna/Constants.pm @@ -76,6 +76,7 @@ use constant BUILDABLE_CLASSES => (qw( Lacuna::RPC::Building::Intelligence Lacuna::RPC::Building::Lapis Lacuna::RPC::Building::Malcud + Lacuna::RPC::Building::MercenariesGuild Lacuna::RPC::Building::Mine Lacuna::RPC::Building::MiningMinistry Lacuna::RPC::Building::Network19 diff --git a/lib/Lacuna/DB/Result/Building.pm b/lib/Lacuna/DB/Result/Building.pm index f5f1f125..df550251 100644 --- a/lib/Lacuna/DB/Result/Building.pm +++ b/lib/Lacuna/DB/Result/Building.pm @@ -1058,6 +1058,7 @@ sub spend_efficiency { 'Lacuna::DB::Result::Building::Module::Parliament' => 'Lacuna::DB::Result::Building::Module::Parliament', 'Lacuna::DB::Result::Building::Permanent::AmalgusMeadow' => 'Lacuna::DB::Result::Building::Permanent::AmalgusMeadow', 'Lacuna::DB::Result::Building::Permanent::DentonBrambles' => 'Lacuna::DB::Result::Building::Permanent::DentonBrambles', + 'Lacuna::DB::Result::Building::MercenariesGuild' => 'Lacuna::DB::Result::Building::MercenariesGuild', }); } diff --git a/lib/Lacuna/DB/Result/Building/MercenariesGuild.pm b/lib/Lacuna/DB/Result/Building/MercenariesGuild.pm new file mode 100644 index 00000000..a64ebd2e --- /dev/null +++ b/lib/Lacuna/DB/Result/Building/MercenariesGuild.pm @@ -0,0 +1,87 @@ +package Lacuna::DB::Result::Building::MercenariesGuild; + +use Moose; +use utf8; +no warnings qw(uninitialized); +extends 'Lacuna::DB::Result::Building::Trade'; + +with 'Lacuna::Role::SpyTrader'; + +around 'build_tags' => sub { + my ($orig, $class) = @_; + return ($orig->($class), qw(Infrastructure Intelligence Ships Trade)); +}; + +use constant controller_class => 'Lacuna::RPC::Building::MercenariesGuild'; + +use constant university_prereq => 10; + +use constant image => 'mercenariesguild'; + +use constant name => 'Mercenaries Guild'; + +use constant food_to_build => 700; + +use constant energy_to_build => 800; + +use constant ore_to_build => 900; + +use constant water_to_build => 700; + +use constant waste_to_build => 700; + +use constant time_to_build => 600; + +use constant food_consumption => 5; + +use constant energy_consumption => 28; + +use constant ore_consumption => 3; + +use constant water_consumption => 5; + +use constant waste_production => 1; + +use constant max_instances_per_planet => 1; + +sub add_to_market { + my ($self, $offer, $ask, $options) = @_; + my $ship = $self->next_available_trade_ship($options->{ship_id}); + unless (defined $ship) { + confess [1011, "You do not have any spy pods available."]; + } + unless ($ask >= 0.1 && $ask < 100 ) { + confess [1009, "You must ask for between 0.1 and 99 essentia to create a trade."]; + } + unless ($self->level > $self->my_market->count) { + confess [1009, "This Mercenaries Guild can only support ".$self->level." spies at one time."]; + } + my $space_used = $self->check_payload($offer, $ship->hold_size, undef, $ship); + my ($payload, $meta) = $self->structure_payload($offer, $space_used); + $ship->task('Waiting On Trade'); + $ship->update; + my %trade = ( + %{$meta}, + payload => $payload, + ask => $ask, + ship_id => $ship->id, + body_id => $self->body_id, + transfer_type => $self->transfer_type, + ); + return Lacuna->db->resultset('Lacuna::DB::Result::Market')->new(\%trade)->insert; +} + +sub trade_ships { + my $self = shift; + return Lacuna->db->resultset('Lacuna::DB::Result::Ships')->search({ + task => 'Docked', + type => 'spy_pod', + body_id => $self->body_id, + }, + { + order_by=> {-desc => ['name']} + }); +} + +no Moose; +__PACKAGE__->meta->make_immutable(inline_constructor => 0); diff --git a/lib/Lacuna/DB/Result/Market.pm b/lib/Lacuna/DB/Result/Market.pm index cfbeeaa1..9c8d2a7a 100644 --- a/lib/Lacuna/DB/Result/Market.pm +++ b/lib/Lacuna/DB/Result/Market.pm @@ -25,6 +25,7 @@ __PACKAGE__->add_columns( has_prisoner => { data_type => 'tinyint', default_value => 0 }, has_glyph => { data_type => 'tinyint', default_value => 0 }, has_plan => { data_type => 'tinyint', default_value => 0 }, + has_spy => { data_type => 'tinyint', default_value => 0 }, ); __PACKAGE__->belongs_to('body', 'Lacuna::DB::Result::Map::Body', 'body_id'); @@ -45,7 +46,12 @@ sub withdraw { my ($self, $body) = @_; $body ||= $self->body; $self->unload($body); - if ($self->ship_id) { + if ($self->has_spy) { + my $ship = Lacuna->db->resultset('Lacuna::DB::Result::Ships')->find($self->ship_id); + $ship->land->update if defined $ship; + $body->empire->add_essentia(1, 'Withdrew Mercenary Trade')->update; + } + elsif ($self->ship_id) { my $ship = Lacuna->db->resultset('Lacuna::DB::Result::Ships')->find($self->ship_id); $ship->land->update if defined $ship; } diff --git a/lib/Lacuna/DB/Result/Medals.pm b/lib/Lacuna/DB/Result/Medals.pm index ab819c3d..dcbabc51 100644 --- a/lib/Lacuna/DB/Result/Medals.pm +++ b/lib/Lacuna/DB/Result/Medals.pm @@ -319,6 +319,7 @@ use constant MEDALS => { CloakingLab => 'Built Cloaking Lab', AmalgusMeadow => 'Discovered an Amalgus Meadow', DentonBrambles => 'Discovered Denton Brambles', + MercenariesGuild => 'Build Mercenaries Guild', }; sub name { diff --git a/lib/Lacuna/RPC/Building/MercenariesGuild.pm b/lib/Lacuna/RPC/Building/MercenariesGuild.pm new file mode 100644 index 00000000..d6cc61a3 --- /dev/null +++ b/lib/Lacuna/RPC/Building/MercenariesGuild.pm @@ -0,0 +1,136 @@ +package Lacuna::RPC::Building::MercenariesGuild; + +use Moose; +use utf8; +no warnings qw(uninitialized); +extends 'Lacuna::RPC::Building::Trade'; +use Guard; + +with 'Lacuna::Role::SpyTraderRpc'; + +sub app_url { + return '/mercenariesguild'; +} + +sub model_class { + return 'Lacuna::DB::Result::Building::MercenariesGuild'; +} + +sub withdraw_from_market { + my ($self, $session_id, $building_id, $trade_id) = @_; + unless ($trade_id) { + confess [1002, 'You have not specified a trade to withdraw.']; + } + my $cache = Lacuna->cache; + if ($cache->get('trade_lock', $trade_id)) { + confess [1013, 'A buyer has placed an offer on this trade. Please wait a few moments and try again.']; + } + $cache->set('trade_lock',$trade_id,1,5); + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + my $trade = $building->market->find($trade_id); + unless (defined $trade) { + confess [1002, 'Could not find that trade. Perhaps it has already been accepted.']; + } + $trade->withdraw($building->body); + my $cost = sprintf "%.1f", 3 - $building->level * 0.1; + $empire->add_essentia($cost, 'Withdrew Mercenary Trade')->update; + return { + status => $self->format_status($empire, $building->body), + }; +} + +sub accept_from_market { + my ($self, $session_id, $building_id, $trade_id) = @_; + unless ($trade_id) { + confess [1002, 'You have not specified a trade to accept.']; + } + my $cache = Lacuna->cache; + if ($cache->get('trade_lock', $trade_id)) { + confess [1013, 'Another buyer has placed an offer on this trade. Please wait a few moments and try again.']; + } + $cache->set('trade_lock',$trade_id,1,5); + my $guard = guard { + $cache->delete('trade_lock',$trade_id); + }; + + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + confess [1013, 'You cannot use a mercenaries guild that has not yet been built.'] unless $building->level > 0; + + $empire->current_session->check_captcha; + + my $trade = $building->market->find($trade_id); + unless (defined $trade) { + confess [1002, 'Could not find that trade. Perhaps it has already been accepted.',$trade_id]; + } + my $offer_ship = Lacuna->db->resultset('Lacuna::DB::Result::Ships')->find($trade->ship_id); + unless (defined $offer_ship) { + $trade->withdraw; + confess [1009, 'Trade no longer available.']; + } + + my $body = $building->body; + unless ($empire->essentia >= $trade->ask) { + confess [1011, 'You need at least '.$trade->ask.' essentia to make this trade.'] + } + + $guard->cancel; + + $empire->spend_essentia($trade->ask, 'Trade Price')->update; + $trade->body->empire->add_essentia($trade->ask, 'Trade Income')->update; + #my $cargo_log = Lacuna->db->resultset('Lacuna::DB::Result::Log::Cargo'); + #$cargo_log->new({ + # message => 'mercenaries guild offer accepted', + # body_id => $trade->body_id, + # data => $trade->payload, + # object_type => ref($trade), + # object_id => $trade->id, + #})->insert; + $offer_ship->send( + target => $body, + payload => $trade->payload, + ); + #$cargo_log->new({ + # message => 'send offer', + # body_id => $offer_ship->foreign_body_id, + # data => $offer_ship->payload, + # object_type => ref($offer_ship), + # object_id => $offer_ship->id, + #})->insert; + + $trade->body->empire->send_predefined_message( + tags => ['Alert'], + filename => 'trade_accepted.txt', + params => [join("; ",@{$trade->format_description_of_payload}), $trade->ask.' essentia', $empire->id, $empire->name], + ); + $trade->delete; + + return { + status => $self->format_status($empire, $building->body), + }; +} + +sub add_to_market { + my ($self, $session_id, $building_id, $offer, $ask, $options) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + confess [1013, 'You cannot use a mercenaries guild that has not yet been built.'] unless $building->level > 0; + my $cost = sprintf "%.1f", 3 - $building->level * 0.1; + unless ($empire->essentia >= $cost) { + confess [1011, "You need $cost essentia to make a trade using the Mercenaries Guild."]; + } + $empire->spend_essentia($cost, 'Offered Mercenaries Trade')->update; + my $trade = $building->add_to_market($offer, $ask, $options); + return { + trade_id => $trade->id, + status => $self->format_status($empire, $building->body), + }; +} + +__PACKAGE__->register_rpc_method_names(qw(report_abuse view_my_market view_market accept_from_market withdraw_from_market add_to_market push_items get_trade_ships get_spies)); + + +no Moose; +__PACKAGE__->meta->make_immutable; + diff --git a/lib/Lacuna/Role/SpyTrader.pm b/lib/Lacuna/Role/SpyTrader.pm new file mode 100644 index 00000000..93bad8e0 --- /dev/null +++ b/lib/Lacuna/Role/SpyTrader.pm @@ -0,0 +1,70 @@ +package Lacuna::Role::SpyTrader; + +use Moose::Role; +use feature "switch"; +use Lacuna::Util qw(randint); + +my $have_exception = [1011, 'You cannot offer to trade something you do not have.']; +my $offer_nothing_exception = [1013, 'It appears that you have offered nothing.']; + +sub market { + return Lacuna->db->resultset('Lacuna::DB::Result::Market'); +} + +sub my_market { + my $self = shift; + return $self->market->search({body_id => $self->body_id, transfer_type => $self->transfer_type, has_spy => 1 }); +} + +sub available_market { + my $self = shift; + return $self->market->search( + { + body_id => {'!=' => $self->body_id}, + transfer_type => $self->transfer_type, + has_spy => 1, + }, + ) +} + +sub check_payload { + my ($self, $item, $transfer_ship) = @_; + my $body = $self->body; + + # validate + unless (ref $item eq 'HASH') { + confess 'The item you want to trade needs to be formatted as a hash reference.'; + } + + my $spy_ids = []; + given($item->{type}) { + when ('spy') { + confess [1002, 'You must specify a spy_id if you are pushing a spy.'] unless $item->{spy_id}; + my $spy = Lacuna->db->resultset('Lacuna::DB::Result::Spies')->find($item->{spy_id}); + confess $have_exception unless (defined $spy && $self->body_id eq $spy->on_body_id && $spy->task ~~ ['Counter Espionage','Idle']); + push @{$spy_ids}, $item->{spy_id}; + } + } + confess $offer_nothing_exception unless scalar @{$spy_ids}; + + return 350 * scalar @{$spy_ids}; +} + +sub structure_payload { + my ($self, $item, $space_used) = @_; + my $body = $self->body; + my $payload; + my %meta = ( offer_cargo_space_needed => $space_used ); + given($item->{type}) { + when ('spy') { + my $spy = Lacuna->db->resultset('Lacuna::DB::Result::Spies')->find($item->{spy_id}); + $spy->task('Mercenary Transport'); + $spy->update; + push @{$payload->{spies}}, $spy->id; + $meta{has_spy} = 1; + } + } + return ($payload, \%meta); +} + +1; diff --git a/lib/Lacuna/Role/SpyTraderRpc.pm b/lib/Lacuna/Role/SpyTraderRpc.pm new file mode 100644 index 00000000..045c9b6c --- /dev/null +++ b/lib/Lacuna/Role/SpyTraderRpc.pm @@ -0,0 +1,76 @@ +package Lacuna::Role::SpyTraderRpc; + +use Moose::Role; +use feature "switch"; +use Lacuna::Util qw(randint); + +with 'Lacuna::Role::TraderRpc' => { + -excludes => [ + 'view_market', 'get_ships', 'get_prisoners', + 'get_plans', 'get_glyphs', 'get_stored_resources', + ], +}; + +sub view_market { + my ($self, $session_id, $building_id, $page_number) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + $page_number ||=1; + my $all_trades = $building->available_market->search( + { 'has_spy' => 1 }, + { rows => 25, page => $page_number, join => 'body', order_by => 'ask' } + ); + my @trades; + while (my $trade = $all_trades->next) { + if ($trade->body->empire_id eq '') { + $trade->delete; + next; + } + push @trades, { + id => $trade->id, + date_offered => $trade->date_offered_formatted, + ask => $trade->ask, + offer => $trade->format_description_of_payload, + body => { + id => $trade->body_id, + }, + empire => { + id => $trade->body->empire->id, + name => $trade->body->empire->name, + }, + }; + } + return { + trades => \@trades, + trade_count => $all_trades->pager->total_entries, + page_number => $page_number, + status => $self->format_status($empire, $building->body), + }; +} + +sub get_spies { + my ($self, $session_id, $building_id) = @_; + my $empire = $self->get_empire_by_session($session_id); + my $building = $self->get_building($empire, $building_id); + my $spies = Lacuna->db->resultset('Lacuna::DB::Result::Spies')->search( + { on_body_id => $building->body_id, task => { in => ['Counter Espionage','Idle'] } }, + { order_by => 'name' } + ); + my @out; + while (my $spy = $spies->next) { + push @out, { + id => $spy->id, + name => $spy->name, + level => $spy->level, + }; + } + return { + spies => \@out, + cargo_space_used_each => 350, + status => $self->format_status($empire, $building->body), + }; +} + +1; + + diff --git a/lib/Lacuna/Role/Trader.pm b/lib/Lacuna/Role/Trader.pm index efc9f52d..73f009c0 100644 --- a/lib/Lacuna/Role/Trader.pm +++ b/lib/Lacuna/Role/Trader.pm @@ -17,7 +17,7 @@ sub market { sub my_market { my $self = shift; - return $self->market->search({body_id => $self->body_id, transfer_type => $self->transfer_type}); + return $self->market->search({body_id => $self->body_id, transfer_type => $self->transfer_type, has_spy => 0 }); } sub available_market { @@ -26,6 +26,7 @@ sub available_market { { body_id => {'!=' => $self->body_id}, transfer_type => $self->transfer_type, + has_spy => 0, }, ) } diff --git a/t/MercenariesGuild.t b/t/MercenariesGuild.t new file mode 100644 index 00000000..4bc2bc9f --- /dev/null +++ b/t/MercenariesGuild.t @@ -0,0 +1,112 @@ +use lib '../lib'; +use Test::More tests => 12; +use Test::Deep; +use Data::Dumper; +use 5.010; +use DateTime; + +use TestHelper; +my $tester = TestHelper->new->generate_test_empire->build_infrastructure; +my $session_id = $tester->session->id; +my $empire = $tester->empire; +my $home = $empire->home_planet; + +Lacuna->cache->set('captcha', $session_id, { guid => 1111, solution => 1111 }, 60 * 30 ); + +my $result; + +$result = $tester->post('captcha','solve', [$session_id, 1111, 1111]); + +$result = $tester->post('intelligence', 'build', [$session_id, $home->id, 0, 1]); +my $intelligence = $tester->get_building($result->{result}{building}{id}); +$intelligence->finish_upgrade; + +my $shipyard = Lacuna->db->resultset('Lacuna::DB::Result::Building')->new({ + x => 1, + y => 1, + class => 'Lacuna::DB::Result::Building::Shipyard', + level => 5, +}); +$home->build_building($shipyard); +$shipyard->finish_upgrade; + +my $spaceport = Lacuna->db->resultset('Lacuna::DB::Result::Building')->new({ + x => 1, + y => 2, + class => 'Lacuna::DB::Result::Building::SpacePort', + level => 5, +}); +$home->build_building($spaceport); +$spaceport->finish_upgrade; + +# need a spy done right now +Lacuna->db->resultset('Lacuna::DB::Result::Spies')->new({ + from_body_id => $home->id, + on_body_id => $home->id, + task => 'Idle', + available_on => DateTime->now, + empire_id => $empire->id, +})->insert; + +my $dory = Lacuna->db->resultset('Lacuna::DB::Result::Ships')->new({type=>'dory'}); +$shipyard->build_ship($dory); + +my $spy_pod = Lacuna->db->resultset('Lacuna::DB::Result::Ships')->new({type=>'spy_pod'}); +$shipyard->build_ship($spy_pod); + +my $finish = DateTime->now; +Lacuna->db->resultset('Lacuna::DB::Result::Ships')->search({shipyard_id=>$shipyard->id})->update({date_available=>$finish}); + + +$result = $tester->post('trade', 'build', [$session_id, $home->id, 0, 4]); +my $trade = $tester->get_building($result->{result}{building}{id}); +$trade->finish_upgrade; + +$result = $tester->post('trade', 'add_to_market', [$session_id, $trade->id, [{ type => 'water', quantity => 100}], 1]); + +$result = $tester->post('mercenariesguild', 'build', [$session_id, $home->id, 0, 3]); +ok($result->{result}{building}{id}, "built a mercenaries guild"); +my $merc = $tester->get_building($result->{result}{building}{id}); +$merc->finish_upgrade; + +$result = $tester->post('mercenariesguild', 'get_trade_ships', [$session_id, $merc->id]); +ok(exists $result->{result}, 'can call get_trade_ships'); + +$result = $tester->post('mercenariesguild', 'get_spies', [$session_id, $merc->id]); +ok(exists $result->{result}, 'can call get_spies'); +my $spy_id = $result->{result}{spies}[0]{id}; + +$result = $tester->post('mercenariesguild', 'add_to_market', [$session_id, $merc->id, { type => 'spy', spy_id => $spy_id}, 1]); +is($result->{error}{code}, 1011, 'can call add_to_market'); +ok($result->{error}{message} =~ / 2\.9 essentia /, 'requires 2.9 essentia to add_to_market'); + +$empire->add_essentia(100, 'Topping up'); +$empire->update(); + +$result = $tester->post('mercenariesguild', 'add_to_market', [$session_id, $merc->id, { type => 'algae', quantity => 100000}, 1]); +is($result->{error}{code}, 1013, 'no spy offered'); + +$result = $tester->post('mercenariesguild', 'add_to_market', [$session_id, $merc->id, { type => 'spy', spy_id => $spy_id}, 1, { ship_id => $spy_pod->id }]); +my $trade_id = $result->{result}{trade_id}; +ok($trade_id, 'spy offered'); + +$result = $tester->post('mercenariesguild', 'accept_from_market', [$session_id, $merc->id]); # no trade specified +is($result->{error}{code}, 1002, 'can call accept_from_market'); + +$result = $tester->post('mercenariesguild', 'view_my_market', [$session_id, $merc->id]); +is(scalar @{$result->{result}{trades}}, 1, 'view_my_market only shows one trade'); + +$result = $tester->post('mercenariesguild', 'withdraw_from_market', [$session_id, $merc->id, $trade_id]); # no trade specified +ok($result->{result}{status}, 'can call withdraw_from_market'); + +$result = $tester->post('mercenariesguild', 'view_market', [$session_id, $merc->id]); +is(scalar @{$result->{result}{trades}}, 0, 'view_market shows no trades'); + +$result = $tester->post('mercenariesguild', 'view_my_market', [$session_id, $merc->id]); +is(scalar @{$result->{result}{trades}}, 0, 'view_my_market shows no trades'); + +# this could use some more tests + +END { + $tester->cleanup; +} diff --git a/var/upgrades/3.0300.sql b/var/upgrades/3.0300.sql new file mode 100644 index 00000000..0d466a6f --- /dev/null +++ b/var/upgrades/3.0300.sql @@ -0,0 +1,2 @@ +alter table market add column has_spy tinyint default 0; + diff --git a/var/www/public/changes.txt b/var/www/public/changes.txt index 72ab377b..85799444 100644 --- a/var/www/public/changes.txt +++ b/var/www/public/changes.txt @@ -1,4 +1,5 @@ -3.0201 +3.0300 + - Add: Added Mercenaries Guild building. 3.0200 - Add: Added Amalgus Meadow and Denton Brambles glyph buildings. diff --git a/var/www/public/resources.json b/var/www/public/resources.json index 30756fc8..dc8b669b 100644 --- a/var/www/public/resources.json +++ b/var/www/public/resources.json @@ -102,6 +102,7 @@ "/intelligence" : { "description" : "Trains spies. You may train 1 spy per level of this building. You may only construct 1 Intelligence Ministry on a planet.", "wiki" : "http://community.lacunaexpanse.com/wiki/intelligence-ministry" }, "/lapis" : { "description" : "Produces lapis to be used as food by your empire. Lapis can only be grown in orbit 2 (its goldilox zone). The higher the level of the orchard the more food it produces. All plants need a source of phosphorus to photosynthesize their food. Therefore you need a good source of gypsum, sulfur, or monazite for plants to thrive.", "wiki" : "http://community.lacunaexpanse.com/wiki/lapis-orchard" }, "/malcud" : { "description" : "Produces fungus to be used as food by your empire. The Farm also consumes a small amount of waste and can be built in any orbit. The higher the level of the farm the more food is produced and waste consumed.", "wiki" : "http://community.lacunaexpanse.com/wiki/malcud-fungus-farm" }, + "/mercenariesguild" : { "description" : "A black market for buying and selling well trained spies.", "wiki" : "http://community.lacunaexpanse.com/wiki/mercenaries-guild" }, "/mine" : { "description" : "Extracts mineral from your planet in the form of ore. The amounts of each mineral extracted depends on the concentration and composition of the planet. The higher the level of the mine the more ore is extracted.", "wiki" : "http://community.lacunaexpanse.com/wiki/mine" }, "/miningministry" : { "description" : "Allows you to harvest resources from asteroids. Allows the construction of Mining Platform Ships. Every two levels allows an additional Mining Platform. Each level increases the output of all Mining Platforms controlled by the Ministry.", "wiki" : "http://community.lacunaexpanse.com/wiki/mining-ministry" }, "/network19" : { "description" : "Your empire's source for up to the minute news in your region of the Expanse. Get updates on what other empire's are doing and what you'd prefer they weren't doing. The Network 19 feed can be manipulated through the use of of espionage and political policy. Well informed people are happy people so the network also produces happiness. The higher the building level the more of the expanse you can get news from and the more happiness is produced. ", "wiki" : "http://community.lacunaexpanse.com/wiki/network-19-affiliate" }, -- 2.54.0 From 01f5097aca7eaa2ff878559fc321f22b1e50e9e2 Mon Sep 17 00:00:00 2001 From: Matthew Musgrove Date: Wed, 23 Mar 2011 21:42:09 -0500 Subject: [PATCH 2/2] Additional tweaks for Mercenaries Guild building. --- docs/MercenariesGuild.pod | 6 +++--- lib/Lacuna/Role/Container.pm | 23 +++++++++++++++++++++++ lib/Lacuna/Role/SpyTrader.pm | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/docs/MercenariesGuild.pod b/docs/MercenariesGuild.pod index 714faf93..b292b112 100644 --- a/docs/MercenariesGuild.pod +++ b/docs/MercenariesGuild.pod @@ -78,7 +78,7 @@ Returns a list of spies that may be traded. Used with the C method. { "id" : "id-goes-here", "name" : "Jack Bauer", - "level" : "5" + "level" : "9" }, ... ], @@ -153,7 +153,7 @@ Displays a list of trades available at the present time. "id" : "id-goes-here", "ask" : 25, # essentia "offer" : [ - "Level 21 spy named Jack Bauer", + "Level 9 spy named Jack Bauer (Mercenary Transport) Offense: 875, Defense: 875, Intel: 2, Mayhem: 0, Politics: 0, Theft: 0, Mission Count Offensive: 0 Defensive: 2)", ], "body" : { "id" : "id-goes-here" # use with get_trade_ships() to determine travel time @@ -196,7 +196,7 @@ Displays a list of trades the current user has posted. "id" : "id-goes-here", "ask" : 25, # essentia "offer" : [ - "Level 21 spy named Jack Bauer", + "Level 9 spy named Jack Bauer (Mercenary Transport) Offense: 875, Defense: 875, Intel: 2, Mayhem: 0, Politics: 0, Theft: 0, Mission Count Offensive: 0 Defensive: 2)", ] }, ... diff --git a/lib/Lacuna/Role/Container.pm b/lib/Lacuna/Role/Container.pm index 722611e4..2f3e0030 100644 --- a/lib/Lacuna/Role/Container.pm +++ b/lib/Lacuna/Role/Container.pm @@ -48,6 +48,17 @@ sub unload { } delete $payload->{prisoners}; } + if (exists $payload->{mercenaries}) { + foreach my $id (@{$payload->{mercenaries}}) { + my $spy = Lacuna->db->resultset('Lacuna::DB::Result::Spies')->find($id); + next unless defined $spy; + $spy->empire_id($body->empire_id); + $spy->from_body_id($body->id); + $spy->on_body_id($body->id); + $spy->update; + } + delete $payload->{mercenaries}; + } if (exists $payload->{ships}) { foreach my $id (@{$payload->{ships}}) { my $ship = Lacuna->db->resultset('Lacuna::DB::Result::Ships')->find($id); @@ -140,6 +151,18 @@ sub format_description_of_payload { } } + # mercenaries + if (exists $payload->{mercenaries}) { + foreach my $id (@{$payload->{mercenaries}}) { + my $spy = $spies->find($id); + next unless defined $spy; + push @items, sprintf("Level %d spy named %s (Mercenary Transport) Offense: %d, Defense: %d, Intel: %d, Mayhem: %d, Politics: %d, Theft: %d, Mission Count Offensive: %d Defensive: %d)", + $spy->level, $spy->name, $spy->offense, $spy->defense, + $spy->intel_xp, $spy->mayhem_xp, $spy->politics_xp, $spy->theft_xp, + $spy->offense_mission_count, $spy->defense_mission_count); + } + } + # prisoners if (exists $payload->{prisoners}) { foreach my $id (@{$payload->{prisoners}}) { diff --git a/lib/Lacuna/Role/SpyTrader.pm b/lib/Lacuna/Role/SpyTrader.pm index 93bad8e0..94566ace 100644 --- a/lib/Lacuna/Role/SpyTrader.pm +++ b/lib/Lacuna/Role/SpyTrader.pm @@ -60,7 +60,7 @@ sub structure_payload { my $spy = Lacuna->db->resultset('Lacuna::DB::Result::Spies')->find($item->{spy_id}); $spy->task('Mercenary Transport'); $spy->update; - push @{$payload->{spies}}, $spy->id; + push @{$payload->{mercenaries}}, $spy->id; $meta{has_spy} = 1; } }