File Coverage

blib/lib/Games/SMTNocturne/Demons.pm
Criterion Covered Total %
statement 70 144 48.6
branch 44 90 48.8
condition 18 32 56.2
subroutine 15 18 83.3
pod 5 5 100.0
total 152 289 52.6


line stmt bran cond sub pod time code
1             package Games::SMTNocturne::Demons;
2             BEGIN {
3 3     3   75750 $Games::SMTNocturne::Demons::AUTHORITY = 'cpan:DOY';
4             }
5             $Games::SMTNocturne::Demons::VERSION = '0.02';
6 3     3   27 use strict;
  3         7  
  3         100  
7 3     3   21 use warnings;
  3         6  
  3         120  
8             # ABSTRACT: look up information about demon fusion in Shin Megami Tensei: Nocturne
9              
10 3     3   15 use Exporter 5.58 'import';
  3         105  
  3         176  
11             our @EXPORT_OK = qw(demon demons_of_type all_demons fuse fusions_for);
12              
13 3     3   2253 use Games::SMTNocturne::Demons::Demon;
  3         11  
  3         181  
14 3     3   2685 use Games::SMTNocturne::Demons::Fusion;
  3         8  
  3         78  
15 3     3   2815 use Games::SMTNocturne::Demons::FusionChart;
  3         14  
  3         6688  
16              
17              
18              
19             sub demon {
20 2723     2723 1 3678 my ($demon) = @_;
21              
22 2723         25519 return Games::SMTNocturne::Demons::Demon->from_name($demon);
23             }
24              
25              
26             sub demons_of_type {
27 0     0 1 0 my ($type) = @_;
28              
29 0         0 return Games::SMTNocturne::Demons::Demon->from_type($type);
30             }
31              
32              
33             sub all_demons {
34 0     0 1 0 return Games::SMTNocturne::Demons::Demon->all_demons;
35             }
36              
37              
38             sub fuse {
39 133342     133342 1 541606 my ($demon1, $demon2, $options) = @_;
40 133342 100       162670 $options = { %{ $options || {} } };
  133342         665728  
41              
42 133342 100       411341 $demon1 = demon($demon1) unless ref($demon1);
43 133342 100       283006 $demon2 = demon($demon2) unless ref($demon2);
44 133342 100       322502 if ($options->{sacrifice}) {
45 21 100       61 $options->{sacrifice} = demon($options->{sacrifice})
46             unless ref($options->{sacrifice});
47             }
48              
49 133342 100       281096 if (!$options->{basic}) {
50 66682 100       143436 if (my $demon = _try_special_fusion($demon1, $demon2, $options)) {
51             # XXX this is the wrong place for this, but not sure how to do
52             # it better
53 32 50 33     75 return if $demon->type eq 'Fiend'
      66        
54             && ($demon1->type eq 'Fiend' || $demon2->type eq 'Fiend');
55 32         118 return $demon;
56             }
57             else {
58 66650         177361 $options->{fusion_type} = 'normal';
59             }
60             }
61              
62 133310 100 100     433901 if ($demon1->type eq 'Element' && $demon2->type eq 'Element') {
    100 100        
    100 100        
    100 100        
    100          
63 48         348 return _fuse_mitama($demon1, $demon2, $options);
64             }
65             elsif ($demon1->type eq 'Element' || $demon2->type eq 'Element') {
66 5732 100       15067 return _element_fusion(
67             ($demon1->type eq 'Element'
68             ? ($demon1, $demon2) : ($demon2, $demon1)),
69             $options
70             );
71             }
72             elsif ($demon1->type eq 'Mitama' && $demon2->type eq 'Mitama') {
73 48         167 return;
74             }
75             elsif ($demon1->type eq 'Mitama' || $demon2->type eq 'Mitama') {
76 5600 100       20292 return _mitama_fusion(
77             ($demon1->type eq 'Mitama'
78             ? ($demon1, $demon2) : ($demon2, $demon1)),
79             $options
80             );
81             }
82             elsif ($demon1->type eq $demon2->type) {
83 4203         9930 return _fuse_element($demon1, $demon2, $options);
84             }
85             else {
86 117679         291686 return _normal_fusion($demon1, $demon2, $options);
87             }
88             }
89              
90              
91             sub fusions_for {
92 0     0 1 0 my ($demon, $options) = @_;
93              
94 0 0       0 $demon = demon($demon) unless ref($demon);
95              
96 0         0 my @fusions;
97             my %seen;
98 0         0 for my $types (Games::SMTNocturne::Demons::FusionChart::unfuse($demon->type)) {
99 0         0 my ($type1, $type2) = @$types;
100 0         0 for my $demon1 (Games::SMTNocturne::Demons::Demon->from_type($type1)) {
101 0 0 0     0 next if defined $options->{max_level}
102             && $options->{max_level} < $demon1->level;
103 0         0 for my $demon2 (Games::SMTNocturne::Demons::Demon->from_type($type2)) {
104 0 0 0     0 next if defined $options->{max_level}
105             && $options->{max_level} < $demon2->level;
106 0 0 0     0 push @fusions, [ $options, $demon1, $demon2 ]
107             if (fuse($demon1, $demon2, $options) || '') eq $demon;
108             }
109             }
110             }
111              
112 0         0 my $special = Games::SMTNocturne::Demons::FusionChart::special_fusion_for(
113             $demon->name
114             );
115 0         0 my @special_fusions;
116 0 0       0 if ($special) {
117 0         0 for my $key (qw(demon1 demon2 demon3 target sacrifice)) {
118 0 0       0 next unless $special->{$key};
119 0 0       0 if (my $name = $special->{$key}{name}) {
    0          
120 0         0 $special->{$key} = [ demon($name) ];
121             }
122             elsif (my $type = $special->{$key}{type}) {
123 0 0       0 my @types = ref($type) ? (@$type) : ($type);
124 0         0 $special->{$key} = [
125             map {
126 0         0 Games::SMTNocturne::Demons::Demon->from_type($_)
127             } @types
128             ];
129             }
130 0         0 $special->{$key} = [
131 0         0 grep { $_->level <= $options->{max_level} }
132 0 0 0     0 @{ $special->{$key} }
133             ] if $key ne 'target' && defined $options->{max_level};
134             }
135              
136 0 0       0 if ($special->{demon3}) {
    0          
    0          
137 0         0 for my $demon1 (@{ $special->{demon1} }) {
  0         0  
138 0         0 for my $demon2 (@{ $special->{demon2} }) {
  0         0  
139 0         0 for my $demon3 (@{ $special->{demon3} }) {
  0         0  
140 0         0 push @special_fusions, [
141             $options, $demon1, $demon2, $demon3
142             ];
143 0         0 push @special_fusions, [
144             $options, $demon1, $demon3, $demon2
145             ];
146 0         0 push @special_fusions, [
147             $options, $demon2, $demon3, $demon1
148             ];
149             }
150             }
151             }
152             }
153             elsif ($special->{demon2}) {
154 0         0 for my $demon1 (@{ $special->{demon1} }) {
  0         0  
155 0         0 for my $demon2 (@{ $special->{demon2} }) {
  0         0  
156 0         0 push @special_fusions, [ $options, $demon1, $demon2 ];
157             }
158             }
159             }
160             elsif ($special->{demon1}) {
161 0 0       0 if ($special->{target}) {
162 0         0 my @target_fusions = map {
163 0         0 $_->raw
164             } map {
165 0         0 fusions_for($_, $options)
166 0         0 } @{ $special->{target} };
167 0         0 push @special_fusions, grep {
168 0         0 my $fusion = $_;
169 0 0       0 grep { $_ eq $fusion->[0] || $_ eq $fusion->[1] }
  0         0  
170 0         0 @{ $special->{demon1} }
171             } @target_fusions;
172             }
173             else {
174 0         0 die "???";
175             }
176             }
177             else {
178 0 0       0 if ($special->{target}) {
179 0         0 my @new_special = map {
180 0         0 $_->raw
181             } map {
182 0         0 fusions_for($_, $options)
183 0         0 } @{ $special->{target} };
184 0 0       0 if ($demon->type eq 'Fiend') {
185 0 0       0 @new_special = grep {
186 0         0 $_->[1]->type ne 'Fiend' && $_->[2]->type ne 'Fiend'
187             } @new_special;
188             }
189 0         0 push @special_fusions, @new_special;
190             }
191             else {
192 0         0 die "???";
193             }
194             }
195              
196 0 0       0 if ($special->{sacrifice}) {
197 0         0 @special_fusions = map {
198 0         0 my $sac = $_;
199 0         0 map { [ @$_, $sac ] } @special_fusions
  0         0  
200 0         0 } @{ $special->{sacrifice} };
201             }
202              
203 0 0       0 if ($special->{deathstone}) {
204 0         0 push @$_, '' for @special_fusions;
205             }
206              
207 0 0       0 if ($special->{kagutsuchi}) {
208 0         0 push @$_, $special->{kagutsuchi} for @special_fusions;
209             }
210             }
211              
212 0         0 return map { Games::SMTNocturne::Demons::Fusion->new(@$_) }
  0         0  
213             @fusions, @special_fusions;
214             }
215              
216             sub _try_special_fusion {
217 66682     66682   111605 my ($demon1, $demon2, $options) = @_;
218              
219 66682         218668 my $fused = Games::SMTNocturne::Demons::FusionChart::special_fusion(
220             $demon1, $demon2, $options
221             );
222 66682 100       273538 return unless $fused;
223              
224 40         74 my $demon = demon($fused);
225              
226 40 100       59 my %bosses = map { $_ => 1 } @{ $options->{bosses} || [] };
  22         61  
  40         318  
227 40 100 100     125 return if $demon->boss && !$bosses{$demon->name};
228              
229 32         122 return $demon;
230             }
231              
232             sub _fuse_mitama {
233 48     48   78 my ($element1, $element2) = @_;
234              
235 48         121 my $mitama = Games::SMTNocturne::Demons::FusionChart::fuse_mitama(
236             $element1->name, $element2->name
237             );
238 48 50       109 return unless $mitama;
239 48         178 return demon($mitama);
240             }
241              
242             sub _element_fusion {
243 5732     5732   13742 my ($element, $demon, $options) = @_;
244              
245 5732         14122 my $direction = Games::SMTNocturne::Demons::FusionChart::element_fusion(
246             $demon->type, $element->name
247             );
248 5732 100       20763 return unless $direction;
249              
250 4708 50       37931 return Games::SMTNocturne::Demons::Demon->from_fusion_stats({
251             type => $demon->type,
252             level => $demon->level,
253             offset => $direction,
254 4708         13233 %{ $options || {} },
255             });
256             }
257              
258             sub _mitama_fusion {
259 5600     5600   8678 my ($mitama, $demon) = @_;
260              
261 5600         37116 return $demon;
262             }
263              
264             sub _fuse_element {
265 4203     4203   7520 my ($demon1, $demon2) = @_;
266              
267 4203         12241 my $element = Games::SMTNocturne::Demons::FusionChart::fuse_element(
268             $demon1->type
269             );
270 4203 100       18099 return unless $element;
271 2483         5499 return demon($element);
272             }
273              
274             sub _normal_fusion {
275 117679     117679   186208 my ($demon1, $demon2, $options) = @_;
276              
277 117679         356256 my $new_type = Games::SMTNocturne::Demons::FusionChart::fuse(
278             $demon1->type, $demon2->type
279             );
280 117679 100       397805 return unless $new_type;
281              
282 99699         312837 my $new_level = ($demon1->level + $demon2->level) / 2 + 1;
283              
284 99699 50       840011 return Games::SMTNocturne::Demons::Demon->from_fusion_stats({
285             type => $new_type,
286             level => $new_level,
287 99699         195467 %{ $options || {} },
288             });
289             }
290              
291              
292             1;
293              
294             __END__