File Coverage

blib/lib/Games/Lacuna/Task/Action/Push.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::Push;
2              
3 1     1   1511 use 5.010;
  1         3  
  1         60  
4             our $VERSION = $Games::Lacuna::Task::VERSION;
5              
6 1     1   509 use Moose -traits => 'NoAutomatic';
  0            
  0            
7             extends qw(Games::Lacuna::Task::Action);
8             with 'Games::Lacuna::Task::Role::Ships',
9             'Games::Lacuna::Task::Role::CommonAttributes' => { attributes => ['mytarget_planet','home_planet'] };
10              
11             use List::Util qw(sum min max);
12             use Games::Lacuna::Client::Types qw(ore_types food_types is_ore_type is_food_type);
13              
14             our @RESOURCES_FULL = (@Games::Lacuna::Task::Constants::RESOURCES_ALL,ore_types(),food_types());
15              
16             has 'auto' => (
17             is => 'rw',
18             isa => 'Bool',
19             documentation => 'Automatically determine required ressources',
20             default => 0,
21             );
22              
23             has 'auto_target' => (
24             is => 'rw',
25             isa => 'Int',
26             documentation => 'Storage fill percentage target in auto mode',
27             default => 80,
28             );
29              
30             has 'auto_hours' => (
31             is => 'rw',
32             isa => 'Int',
33             documentation => 'Plan for n-hours if running in auto mode',
34             default => 1,
35             );
36              
37              
38             foreach my $resource (@RESOURCES_FULL) {
39             has $resource=> (
40             is => 'rw',
41             isa => 'Int',
42             documentation => ucfirst($resource).' quantity',
43             predicate => 'has_'.$resource,
44             );
45             }
46              
47             sub description {
48             return q[Push goods between your colonies];
49             }
50              
51             sub run {
52             my ($self) = @_;
53            
54             my $planet_home = $self->home_planet_data();
55             my $planet_target = $self->target_planet_data();
56            
57             my $trade_object = $self->get_building_object($planet_home,'Trade');
58             my $spaceport_object = $self->get_building_object($planet_home,'SpacePort');
59            
60             return $self->abort('Could not find trade ministry')
61             unless $trade_object;
62             return $self->abort('Could not find spaceport')
63             unless $spaceport_object;
64            
65            
66             my $ships_data = $self->request(
67             object => $spaceport_object,
68             method => 'view_all_ships',
69             params => [ { no_paging => 1 },{ tag => 'Trade', task => 'Docked' } ],
70             );
71            
72             my $available_hold_size = 0;
73             my $available_ships = [];
74            
75             # Find all avaliable ships
76             SHIPS:
77             foreach my $ship (@{$ships_data->{ships}}) {
78             # No reserved ships
79             next SHIPS
80             if $ship->{name} =~ m/!/;
81             next SHIPS
82             if $ship->{type} eq 'scow';
83            
84             $available_hold_size += $ship->{hold_size};
85            
86             push(@{$available_ships},$ship);
87             }
88            
89             # Get searchable ores
90             my $resources_stored_response = $self->request(
91             object => $trade_object,
92             method => 'get_stored_resources',
93             );
94            
95             # Cacl food & ore totals
96             my %resources_stored = %{$resources_stored_response->{resources}};
97             $resources_stored{ore} = 0;
98             $resources_stored{food} = 0;
99             foreach my $resource (keys %resources_stored) {
100             if (is_ore_type($resource)) {
101             $resources_stored{ore} += $resources_stored{$resource};
102             } elsif (is_food_type($resource)) {
103             $resources_stored{food} += $resources_stored{$resource};
104             }
105             }
106            
107             # Calc resources we need to push
108             my $resource_total;
109             my %resources_push;
110             my $resources_take = sub {
111             my ($resource,$quantity_required) = @_;
112             my $quantity_stored = $resources_stored{$resource} // 0;
113            
114             $self->abort('%s does not have enough %s (%i required, %i stored)',$planet_home->{name},$resource,$quantity_required,$quantity_stored)
115             if ($quantity_stored < $quantity_required);
116            
117             if (is_food_type($resource)) {
118             $resources_stored{'food'} -= $quantity_required;
119             } elsif (is_ore_type($resource)) {
120             $resources_stored{'ore'} -= $quantity_required;
121             }
122            
123             $resources_push{$resource} ||= 0;
124             $resources_stored{$resource} -= $quantity_required;
125             $resources_push{$resource} += $quantity_required;
126             $resource_total += $quantity_required;
127             };
128            
129             # Get requested resources
130             my %resources_requested;
131             if ($self->auto) {
132             my $resource_auto_total = 0;
133             foreach my $resource (@Games::Lacuna::Task::Constants::RESOURCES) {
134             my $resource_required = int(
135             ($planet_target->{$resource.'_capacity'} * ($self->auto_target / 100)) -
136             $planet_target->{$resource.'_stored'} +
137             ($planet_target->{$resource.'_hour'} * $self->auto_hours)
138             );
139             if ($resource_required > 0) {
140             $resource_required = min(int($planet_home->{$resource.'_stored'} * 0.8),$resource_required);
141             $resources_requested{$resource} = $resource_required;
142             $resource_auto_total += $resource_required;
143             }
144             }
145             if ($resource_auto_total > $available_hold_size) {
146             my $resource_coeficient = $available_hold_size/$resource_auto_total;
147             foreach my $resource (@Games::Lacuna::Task::Constants::RESOURCES) {
148             $resources_requested{$resource} = int($resource_coeficient * $resources_requested{$resource});
149             }
150             }
151             } else {
152             foreach my $resource (@RESOURCES_FULL) {
153             my $predicate_method = 'has_'.$resource;
154             if ($self->$predicate_method
155             && $self->$resource > 0) {
156             $resources_requested{$resource} = $self->$resource;
157             }
158             }
159             }
160            
161             # Take requested resources
162             foreach my $resource (keys %resources_requested) {
163             given ($resource) {
164             when ([qw(ore food)]) {
165             my $quantity_stored = $resources_stored{$resource} // 0;
166             my $resource_percentage = $resources_requested{$resource} / $quantity_stored;
167             no strict 'refs';
168             my @resource_sub = &{$_.'_types'}();
169             foreach my $resource_sub (@resource_sub) {
170             next
171             unless defined $resources_stored{$resource_sub};
172             $resources_take->($resource_sub,int($resource_percentage * $resources_stored{$resource_sub} + 0.5 // 0));
173             }
174             }
175             default {
176             $resources_take->($resource,$resources_requested{$resource});
177             }
178             }
179             }
180            
181             if ($resource_total > $available_hold_size) {
182             $self->abort('%s does not have enough cargo fleet capacity (%i required, %i available)',$planet_home->{name},$resource_total,$available_hold_size);
183             }
184            
185             my $resource_push_ship = sub {
186             my ($ship) = @_;
187             my @resources_push_ship;
188             my $resource_push_ship_quantity = 0;
189             foreach my $resource (keys %resources_push) {
190             next
191             if $resources_push{$resource} == 0;
192             my $quantity = min($resources_push{$resource},$ship->{hold_size}-$resource_push_ship_quantity);
193             $resource_push_ship_quantity += $quantity;
194             $resources_push{$resource} -= $quantity;
195             push(@resources_push_ship,{
196             type => $resource,
197             quantity=> $quantity,
198             });
199             last
200             if $resource_push_ship_quantity >= $ship->{hold_size};
201             }
202             $self->log('notice','Sending %i resources from %s to %s with %s',$resource_push_ship_quantity,$planet_home->{name},$planet_target->{name},$ship->{name});
203            
204             $self->request(
205             object => $trade_object,
206             method => 'push_items',
207             params => [ $planet_target->{id}, \@resources_push_ship, {
208             ship_id => $ship->{id},
209             stay => 0,
210             } ]
211             );
212             };
213            
214             # Try to find single ship with enough cargo space
215             foreach my $ship (sort { $a->{hold_size} <=> $b->{hold_size} } @{$available_ships}) {
216             if ($ship->{hold_size} > $resource_total) {
217             $resource_push_ship->($ship);
218             return;
219             }
220             }
221            
222             foreach my $ship (sort { $b->{hold_size} <=> $a->{hold_size} } @{$available_ships}) {
223             $resource_push_ship->($ship);
224             return
225             if sum(values %resources_push) == 0;
226             }
227             }
228              
229             __PACKAGE__->meta->make_immutable;
230             no Moose;
231             1;