File Coverage

blib/lib/Games/Lacuna/Task/Action/Excavate.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Games::Lacuna::Task::Action::Excavate;
2              
3 1     1   1683 use 5.010;
  1         3  
  1         58  
4             our $VERSION = $Games::Lacuna::Task::VERSION;
5              
6 1     1   591 use Moose;
  0            
  0            
7             extends qw(Games::Lacuna::Task::Action);
8             with qw(Games::Lacuna::Task::Role::Stars
9             Games::Lacuna::Task::Role::Ships
10             Games::Lacuna::Task::Role::PlanetRun);
11              
12             use List::Util qw(sum);
13             use Games::Lacuna::Client::Types qw(ore_types);
14              
15             has 'min_ore' => (
16             is => 'rw',
17             isa => 'Int',
18             documentation => 'Only select bodies with mininimal ore quantities [Default 5000]',
19             default => 5000,
20             required => 1,
21             );
22              
23             has 'ores' => (
24             is => 'rw',
25             isa => 'HashRef',
26             traits => ['Hash','NoGetopt'],
27             default => sub {
28             return {
29             map { $_ => 0 } ore_types()
30             }
31             },
32             required => 1,
33             handles => {
34             get_ore => 'get',
35             }
36             );
37              
38             has 'excavated_bodies' => (
39             is => 'rw',
40             isa => 'ArrayRef[Int]',
41             traits => [qw(NoGetopt Array)],
42             traits => ['Array','NoGetopt'],
43             handles => {
44             add_excavated_body => 'push',
45             }
46             );
47              
48             sub description {
49             return q[Building and dispatch excavators to best suited bodies];
50             }
51              
52             sub process_planet {}
53              
54             sub run {
55             my ($self) = @_;
56            
57             my %planets;
58            
59             foreach my $planet_stats ($self->get_planets) {
60             $self->log('info',"Processing planet %s",$planet_stats->{name});
61             my $available = $self->current_excavators($planet_stats);
62            
63             if ($available) {
64             $planets{$planet_stats->{id}} = $available;
65             }
66             }
67            
68             my $ore_type_count = scalar ore_types();
69             my $total_ores = sum(values %{$self->ores});
70             while (my ($key,$value) = each %{$self->ores}) {
71             $self->ores->{$key} = (1/$ore_type_count) / ($value / $total_ores);
72             }
73            
74             while (my ($key,$value) = each %planets) {
75             my $planet_stats = $self->my_body_status($key);
76             $self->dispatch_excavators($planet_stats,$value);
77             }
78            
79             }
80              
81             sub current_excavators {
82             my ($self,$planet_stats) = @_;
83            
84             # Get archaeology ministry
85             my $archaeology_ministry = $self->find_building($planet_stats->{id},'Archaeology');
86            
87             return
88             unless defined $archaeology_ministry;
89             return
90             unless $archaeology_ministry->{level} >= 11;
91            
92             my $archaeology_ministry_object = $self->build_object($archaeology_ministry);
93            
94             my $response = $self->request(
95             object => $archaeology_ministry_object,
96             method => 'view_excavators',
97             );
98            
99             my $possible_excavators = $response->{max_excavators} - scalar @{$response->{excavators}} - 1 - $response->{travelling};
100            
101             # Get all excavated bodies
102             foreach my $excavator (@{$response->{excavators}}) {
103             while (my ($key,$value) = each %{$excavator->{body}{ore}}) {
104             $self->ores->{$key} += $value * ($excavator->{glyph} / 100);
105             }
106             next
107             if $excavator->{id} == 0;
108            
109             $self->add_excavated_body($excavator->{body}{id});
110             }
111            
112             return $possible_excavators;
113             }
114              
115             sub dispatch_excavators {
116             my ($self,$planet_stats,$possible_excavators) = @_;
117            
118             # Get space port
119             my $spaceport = $self->find_building($planet_stats->{id},'Space Port');
120            
121             return
122             unless defined $spaceport;
123            
124             my $spaceport_object = $self->build_object($spaceport);
125            
126             # Get available excavators
127             my @avaliable_excavators = $self->get_ships(
128             planet => $planet_stats,
129             quantity => $possible_excavators,
130             travelling => 1,
131             type => 'excavator',
132             build => 1,
133             );
134            
135             # Check if we have available excavators
136             return
137             unless (scalar @avaliable_excavators);
138            
139             $self->log('debug','%i excavators available at %s',(scalar @avaliable_excavators),$planet_stats->{name});
140            
141             my @available_bodies;
142            
143             $self->search_stars_callback(
144             sub {
145             my ($star_data) = @_;
146            
147             my @possible_bodies;
148             # Check all bodies
149             foreach my $body (@{$star_data->{bodies}}) {
150             # Check if solar system is inhabited by hostile empires
151             return 1
152             if defined $body->{empire}
153             && $body->{empire}{alignment} =~ m/hostile/;
154            
155             # Check if body is inhabited
156             next
157             if defined $body->{empire};
158            
159             # Check if already excavated
160             next
161             if defined $body->{is_excavated}
162             && $body->{is_excavated};
163            
164             next
165             if $body->{id} ~~ $self->excavated_bodies;
166            
167             # Check body type
168             next
169             unless ($body->{type} eq 'asteroid' || $body->{type} eq 'habitable planet');
170            
171             my $total_ore = sum values %{$body->{ore}};
172            
173             # Check min ore
174             next
175             if $total_ore < $self->min_ore;
176            
177             push(@possible_bodies,$body);
178             }
179            
180             # All possible bodies
181             foreach my $body (@possible_bodies) {
182             my $weighted_ores = 0;
183             foreach my $ore (keys %{$body->{ore}}) {
184             $weighted_ores += $body->{ore}{$ore} * $self->get_ore($ore);
185             }
186             push(@available_bodies,[ $weighted_ores, $body ]);
187             }
188            
189             return 0
190             if scalar @available_bodies > 30;
191              
192             return 1;
193             },
194             x => $planet_stats->{x},
195             y => $planet_stats->{y},
196             is_known => 1,
197             distance => 1,
198             );
199            
200             foreach my $body_data (sort { $b->[0] <=> $a->[0] } @available_bodies) {
201            
202             my $body = $body_data->[1];
203             my $excavator = pop(@avaliable_excavators);
204            
205             return
206             unless defined $excavator;
207            
208             $self->log('notice',"Sending excavator from %s to %s",$planet_stats->{name},$body->{name});
209            
210             $self->add_excavated_body($body->{id});
211            
212             # Send excavator to body
213             my $response = $self->request(
214             object => $spaceport_object,
215             method => 'send_ship',
216             params => [ $excavator,{ "body_id" => $body->{id} } ],
217             catch => [
218             [
219             1010,
220             qr/already has an excavator from your empire or one is on the way/,
221             sub {
222             $self->log('debug',"Could not send excavator to %s",$body->{name});
223             push(@avaliable_excavators,$excavator);
224             return 0;
225             }
226             ],
227             [
228             1009,
229             qr/Can only be sent to asteroids and uninhabited planets/,
230             sub {
231             $self->log('debug',"Could not send excavator to %s",$body->{name});
232             push(@avaliable_excavators,$excavator);
233             return 0;
234             }
235             ]
236             ],
237             );
238            
239             # Set body exacavated
240             $self->set_body_excavated($body->{id});
241             }
242             }
243              
244             after 'run' => sub {
245             my ($self) = @_;
246            
247             my $excavated = join(',',@{$self->excavated_bodies});
248            
249             $self->log('debug',"Updating excavator cache");
250            
251             $self->storage_do('UPDATE body SET is_excavated = 0 WHERE is_excavated = 1 AND id NOT IN ('.$excavated.')');
252             };
253              
254             __PACKAGE__->meta->make_immutable;
255             no Moose;
256             1;