… | |
… | |
7 | The Jeweler skill helper module. |
7 | The Jeweler skill helper module. |
8 | |
8 | |
9 | =cut |
9 | =cut |
10 | |
10 | |
11 | package Jeweler; |
11 | package Jeweler; |
|
|
12 | use strict; |
|
|
13 | use YAML; |
12 | |
14 | |
13 | =over 4 |
15 | =over 4 |
14 | |
16 | |
15 | =item @RESISTS |
17 | =item @RESISTS |
16 | |
18 | |
17 | List of all resistancies that can occur on rings and amulets. |
19 | List of all resistancies that can occur on rings and amulets. |
18 | |
20 | |
19 | =cut |
21 | =cut |
20 | |
22 | |
|
|
23 | our $CFG; |
|
|
24 | |
|
|
25 | sub read_config { |
|
|
26 | my ($filename) = @_; |
|
|
27 | |
|
|
28 | unless (-e $filename) { |
|
|
29 | warn "$filename doesn't exists! no config for jeweler skill loaded!\n"; |
|
|
30 | $CFG = {}; |
|
|
31 | return |
|
|
32 | } |
|
|
33 | |
|
|
34 | $CFG = YAML::LoadFile $filename; |
|
|
35 | } |
|
|
36 | |
|
|
37 | sub getcfg { |
|
|
38 | my ($sect, $key) = @_; |
|
|
39 | return $CFG->{$sect} unless defined $key; |
|
|
40 | |
|
|
41 | my $cfg = $CFG->{$sect}->{$key} |
|
|
42 | or die "Couldn't find $sect/$key in configuration!"; |
|
|
43 | |
|
|
44 | $cfg |
|
|
45 | } |
|
|
46 | |
21 | my @RESISTS = ( |
47 | our @RESISTS = ( |
22 | cf::ATNR_PHYSICAL, |
48 | cf::ATNR_PHYSICAL, |
23 | cf::ATNR_MAGIC, |
49 | cf::ATNR_MAGIC, |
24 | cf::ATNR_FIRE, |
50 | cf::ATNR_FIRE, |
25 | cf::ATNR_ELECTRICITY, |
51 | cf::ATNR_ELECTRICITY, |
26 | cf::ATNR_COLD, |
52 | cf::ATNR_COLD, |
… | |
… | |
47 | =item @EFFECT_RESISTS |
73 | =item @EFFECT_RESISTS |
48 | |
74 | |
49 | List of all effect resistancies that occur on rings and amulets. |
75 | List of all effect resistancies that occur on rings and amulets. |
50 | The difference is made because effect resistancies are less effective at lower levels. |
76 | The difference is made because effect resistancies are less effective at lower levels. |
51 | |
77 | |
52 | =cut |
78 | =back |
53 | |
79 | |
|
|
80 | =cut |
|
|
81 | |
54 | my @EFFECT_RESISTS = ( |
82 | our @EFFECT_RESISTS = ( |
55 | cf::ATNR_CONFUSION, |
83 | cf::ATNR_CONFUSION, |
56 | cf::ATNR_DRAIN, |
84 | cf::ATNR_DRAIN, |
57 | cf::ATNR_POISON, |
85 | cf::ATNR_POISON, |
58 | cf::ATNR_SLOW, |
86 | cf::ATNR_SLOW, |
59 | cf::ATNR_PARALYZE, |
87 | cf::ATNR_PARALYZE, |
… | |
… | |
63 | cf::ATNR_DEATH, |
91 | cf::ATNR_DEATH, |
64 | cf::ATNR_BLIND, |
92 | cf::ATNR_BLIND, |
65 | cf::ATNR_DISEASE, |
93 | cf::ATNR_DISEASE, |
66 | ); |
94 | ); |
67 | |
95 | |
68 | my %RESMAP = ( |
96 | our %RESMAP = ( |
69 | cf::ATNR_PHYSICAL => "PHYSICAL", |
97 | cf::ATNR_PHYSICAL => "PHYSICAL", |
70 | cf::ATNR_MAGIC => "MAGIC", |
98 | cf::ATNR_MAGIC => "MAGIC", |
71 | cf::ATNR_FIRE => "FIRE", |
99 | cf::ATNR_FIRE => "FIRE", |
72 | cf::ATNR_ELECTRICITY => "ELECTRICITY", |
100 | cf::ATNR_ELECTRICITY => "ELECTRICITY", |
73 | cf::ATNR_COLD => "COLD", |
101 | cf::ATNR_COLD => "COLD", |
… | |
… | |
88 | cf::ATNR_LIFE_STEALING => "LIFE_STEALING", |
116 | cf::ATNR_LIFE_STEALING => "LIFE_STEALING", |
89 | cf::ATNR_BLIND => "BLIND", |
117 | cf::ATNR_BLIND => "BLIND", |
90 | cf::ATNR_DISEASE => "DISEASE", |
118 | cf::ATNR_DISEASE => "DISEASE", |
91 | ); |
119 | ); |
92 | |
120 | |
93 | =back |
121 | our %LVL_DIFF_CHANCES = ( |
|
|
122 | +5 => 100, |
|
|
123 | +4 => 95, |
|
|
124 | +3 => 85, |
|
|
125 | +2 => 75, |
|
|
126 | +1 => 65, |
|
|
127 | 0 => 50, |
|
|
128 | -1 => 45, |
|
|
129 | -2 => 35, |
|
|
130 | -3 => 25, |
|
|
131 | -4 => 10, |
|
|
132 | -5 => 0 |
|
|
133 | ); |
94 | |
134 | |
95 | =cut |
135 | our %LVL_DIFF_MSG = ( |
|
|
136 | -5 => 'Way above your skill', |
|
|
137 | -4 => 'Very low', |
|
|
138 | -3 => 'Slight chance', |
|
|
139 | -2 => 'Low', |
|
|
140 | -1 => 'Nearly 50:50', |
|
|
141 | 0 => '50:50', |
|
|
142 | 1 => 'Slightly above 50:50', |
|
|
143 | 2 => 'Good', |
|
|
144 | 3 => 'High', |
|
|
145 | 4 => 'Nearly confident', |
|
|
146 | 5 => '100%', |
|
|
147 | ); |
|
|
148 | |
|
|
149 | sub level_diff_to_str { |
|
|
150 | my ($delta) = @_; |
|
|
151 | $delta = -5 if $delta < -5; |
|
|
152 | $delta = 5 if $delta > 5; |
|
|
153 | return $LVL_DIFF_MSG{$delta} |
|
|
154 | } |
|
|
155 | |
|
|
156 | sub level_diff_to_chance_perc { |
|
|
157 | my ($delta) = @_; |
|
|
158 | $delta = -5 if $delta < -5; |
|
|
159 | $delta = 5 if $delta > 5; |
|
|
160 | return $LVL_DIFF_CHANCES{$delta} |
|
|
161 | } |
|
|
162 | |
|
|
163 | sub analyze { |
|
|
164 | my ($sk, $chdl, $pl) = @_; |
|
|
165 | |
|
|
166 | my $hadunid = 0; |
|
|
167 | for ($chdl->grep_by_type (cf::RING, cf::AMULET)) { |
|
|
168 | if (!$_->flag (cf::FLAG_IDENTIFIED) && $_->need_identify) { |
|
|
169 | $hadunid = 1; |
|
|
170 | next; |
|
|
171 | } |
|
|
172 | my $r = Jeweler::Object->new (object => $_); |
|
|
173 | my $msg = $r->analyze ($sk, $pl); |
|
|
174 | $pl->message ($r->to_string . ": " . $msg); |
|
|
175 | if ($pl->flag (cf::FLAG_WIZ)) { |
|
|
176 | $r->wiz_analyze ($pl); |
|
|
177 | } |
|
|
178 | } |
|
|
179 | if ($hadunid) { |
|
|
180 | $pl->message ("You couldn't identify the other rings and not analyze them!"); |
|
|
181 | } |
|
|
182 | } |
|
|
183 | |
|
|
184 | # this function converts metals/minerals into a raw ring (of adornment) |
|
|
185 | sub simple_converter { |
|
|
186 | my ($pl, $ingred, $chdl, $conv) = @_; |
|
|
187 | |
|
|
188 | $conv = lc $conv; |
|
|
189 | my $cnvs = $CFG->{conversions}; |
|
|
190 | |
|
|
191 | return unless $cnvs->{$conv}; |
|
|
192 | |
|
|
193 | my %ingred_groups; |
|
|
194 | |
|
|
195 | my @conv_cfg = @{$cnvs->{$conv}}; |
|
|
196 | my $outarch = $conv; |
|
|
197 | my ($ingr_grp, $outarchvalfact, $srcarchname, $xp_gain) = @conv_cfg; |
|
|
198 | |
|
|
199 | unless (@conv_cfg <= 4) { |
|
|
200 | warn "ERROR: Conversion for '$outarch' has only " . (@conv_cfg) . " arguments!"; |
|
|
201 | return; |
|
|
202 | } |
|
|
203 | |
|
|
204 | unless ($xp_gain > 0) { |
|
|
205 | warn "WARNING: xp gain isn't > 0 in convesion '$outarch'\n"; |
|
|
206 | return; |
|
|
207 | } |
|
|
208 | |
|
|
209 | unless ($outarchvalfact) { |
|
|
210 | warn "ERROR: source-arch-value-multiplier == 0 in convesion '$outarch'\n"; |
|
|
211 | return; |
|
|
212 | } |
|
|
213 | |
|
|
214 | unless ($outarchvalfact >= 1) { |
|
|
215 | warn "WARNING: source-arch-value-multiplier < 1 in convesion '$outarch', results in more valuable output!\n"; |
|
|
216 | } |
|
|
217 | |
|
|
218 | my $archvalsum = $ingred->value ($ingr_grp, $srcarchname); |
|
|
219 | $ingred->remove ($ingr_grp, $srcarchname); |
|
|
220 | |
|
|
221 | my $outarchval = cf::arch::find ($outarch)->clone->value; |
|
|
222 | |
|
|
223 | my $nrof = int ($archvalsum / (($outarchval || 1000) * $outarchvalfact)); |
|
|
224 | if ($nrof) { |
|
|
225 | # XXX: yes, i know what i'm doing here, i don't set nrof, but it didn't work somehow (pls. chek sometimes) |
|
|
226 | $chdl->put (cf::object::new $outarch) for 1..$nrof; |
|
|
227 | |
|
|
228 | my $xp_sum = ($xp_gain * $nrof); |
|
|
229 | |
|
|
230 | if ($xp_sum) { |
|
|
231 | $pl->ob->message ("You got $xp_sum xp by making $nrof ${outarch}s"); |
|
|
232 | $pl->ob->change_exp ($xp_sum, "jeweler", cf::SK_EXP_ADD_SKILL); |
|
|
233 | } |
|
|
234 | } |
|
|
235 | } |
|
|
236 | |
96 | |
237 | |
97 | package Jeweler::CauldronHandler; |
238 | package Jeweler::CauldronHandler; |
|
|
239 | use strict; |
98 | |
240 | |
99 | =head2 CauldronHandler |
241 | =head2 CauldronHandler |
100 | |
242 | |
101 | The Jeweler::CauldronHandler package, that helps you with handling the |
243 | The Jeweler::CauldronHandler package, that helps you with handling the |
102 | cauldron stuff. Can also be used for other skills. |
244 | cauldron stuff. Can also be used for other skills. |
… | |
… | |
179 | |
321 | |
180 | for ($self->{cauldron}->inv) { |
322 | for ($self->{cauldron}->inv) { |
181 | |
323 | |
182 | if (my $k = $type_to_key{$_->type}) { |
324 | if (my $k = $type_to_key{$_->type}) { |
183 | push @{$ingreds->{$k}}, $_; |
325 | push @{$ingreds->{$k}}, $_; |
184 | |
|
|
185 | } else { |
|
|
186 | Jeweler::Util::remove ($_); |
|
|
187 | } |
326 | } |
188 | } |
327 | } |
189 | |
328 | |
190 | return Jeweler::Ingredients->new (ingredients => $ingreds, cauldron_helper => $self) |
329 | return Jeweler::Ingredients->new (ingredients => $ingreds, cauldron_helper => $self) |
191 | } |
330 | } |
… | |
… | |
198 | |
337 | |
199 | sub put { |
338 | sub put { |
200 | my ($self, $obj) = @_; |
339 | my ($self, $obj) = @_; |
201 | |
340 | |
202 | return undef unless $self->{cauldron}; |
341 | return undef unless $self->{cauldron}; |
203 | |
|
|
204 | $obj->insert_ob_in_ob ($self->{cauldron}); |
342 | $obj->insert_ob_in_ob ($self->{cauldron}); |
205 | } |
343 | } |
206 | |
344 | |
207 | =back |
345 | =back |
208 | |
346 | |
209 | =cut |
347 | =cut |
210 | |
348 | |
211 | package Jeweler::Ingredients; |
349 | package Jeweler::Ingredients; |
|
|
350 | use Storable qw/dclone/; |
|
|
351 | use strict; |
212 | |
352 | |
213 | =head2 Ingredients |
353 | =head2 Ingredients |
214 | |
354 | |
215 | This class handles the ingredients. |
355 | This class handles the ingredients. |
216 | |
356 | |
… | |
… | |
245 | $_->archetype->name eq $archname |
385 | $_->archetype->name eq $archname |
246 | } @{$self->{ingredients}->{$group} || []}; |
386 | } @{$self->{ingredients}->{$group} || []}; |
247 | |
387 | |
248 | my $sum = 0; |
388 | my $sum = 0; |
249 | for (@objs) { |
389 | for (@objs) { |
250 | $sum += $_->nrof * $_->value; |
390 | $sum += ($_->nrof || 1) * $_->value; |
251 | } |
391 | } |
252 | |
392 | |
253 | return $sum; |
393 | return $sum; |
254 | } |
394 | } |
255 | |
395 | |
256 | =item remove ($group, $archname) |
396 | =item remove ($group, $archname) |
257 | |
397 | |
258 | Removes the ingredients in C<$group> with archname C<$archname>. |
398 | Removes the ingredients in C<$group> with archname C<$archname>. |
|
|
399 | It removes all in C<$group> if archname is undef. |
259 | |
400 | |
260 | =cut |
401 | =cut |
261 | |
402 | |
262 | sub remove { |
403 | sub remove { |
263 | my ($self, $group, $archname) = @_; |
404 | my ($self, $group, $archname) = @_; |
… | |
… | |
265 | my $ingred = $self->{ingredients}; |
406 | my $ingred = $self->{ingredients}; |
266 | |
407 | |
267 | my @out; |
408 | my @out; |
268 | |
409 | |
269 | for (@{$ingred->{$group}}) { |
410 | for (@{$ingred->{$group}}) { |
|
|
411 | if (defined $archname) { |
270 | if ($_->archetype->name eq $archname) { |
412 | if ($_->archetype->name eq $archname) { |
|
|
413 | Jeweler::Util::remove ($_); |
|
|
414 | } else { |
|
|
415 | push @out, $_; |
|
|
416 | } |
|
|
417 | } else { |
271 | Jeweler::Util::remove ($_); |
418 | Jeweler::Util::remove ($_); |
|
|
419 | } |
|
|
420 | } |
|
|
421 | |
|
|
422 | @{$ingred->{$group}} = @out; |
|
|
423 | } |
|
|
424 | |
|
|
425 | sub get_plan { |
|
|
426 | my ($self) = @_; |
|
|
427 | |
|
|
428 | my $ingred = $self->{ingredients}; |
|
|
429 | |
|
|
430 | for my $grp (keys %$ingred) { |
|
|
431 | for my $pot (@{$ingred->{$grp}}) { |
|
|
432 | for my $plan (keys %{$Jeweler::CFG->{plans}}) { |
|
|
433 | my $plg = $Jeweler::CFG->{plans}->{$plan}; |
|
|
434 | my @plga = (); |
|
|
435 | unless (ref $plg eq 'ARRAY') { |
|
|
436 | push @plga, $plg; |
|
|
437 | } else { |
|
|
438 | @plga = @$plg; |
|
|
439 | } |
|
|
440 | next unless @plga > 0; |
|
|
441 | if (Jeweler::Util::grep_for_match ($pot, @plga)) { |
|
|
442 | return $plan; |
|
|
443 | } |
|
|
444 | } |
|
|
445 | } |
|
|
446 | } |
|
|
447 | } |
|
|
448 | |
|
|
449 | sub get_ring { |
|
|
450 | my ($self) = @_; |
|
|
451 | return ( |
|
|
452 | @{$self->{ingredients}->{ammys} || []}, |
|
|
453 | @{$self->{ingredients}->{rings} || []} |
|
|
454 | ); |
|
|
455 | } |
|
|
456 | |
|
|
457 | sub improve_ring_by_plan { |
|
|
458 | my ($self, $plan, $ring) = @_; |
|
|
459 | |
|
|
460 | $ring = dclone ($ring); |
|
|
461 | |
|
|
462 | my $ingred = $self->{ingredients}; |
|
|
463 | my $impr = {}; |
|
|
464 | |
|
|
465 | if ($plan =~ m/^stat_(\S+)$/) { |
|
|
466 | my $statname = $1; |
|
|
467 | my $plingred = Jeweler::getcfg (plans => $plan) |
|
|
468 | or die "ingredients for plan '$plan' not defined!"; |
|
|
469 | |
|
|
470 | my $cnt = 0; |
|
|
471 | for my $pot (@{$ingred->{potions}}) { |
|
|
472 | if (Jeweler::Util::grep_for_match ($pot, @$plingred)) { |
|
|
473 | $cnt += $pot->nrof; |
|
|
474 | } |
|
|
475 | } |
|
|
476 | |
|
|
477 | my $maxstat = Jeweler::getcfg (maximprovements => 'stats'); |
|
|
478 | my $did_impr = 0; |
|
|
479 | for my $x (1..$maxstat) { |
|
|
480 | my $y = Jeweler::Object::fx ($x, 'stat_potions'); |
|
|
481 | |
|
|
482 | if ($cnt <= $y->[0]) { |
|
|
483 | $ring->{hash}->{stat}->{$statname} += $x; |
|
|
484 | $did_impr = 1; |
|
|
485 | last; |
|
|
486 | } |
|
|
487 | } |
|
|
488 | |
|
|
489 | # we want at least this improvement if we have a plan... |
|
|
490 | $ring->{hash}->{stat}->{$statname} += 1 unless $did_impr; |
|
|
491 | |
|
|
492 | } elsif ($plan =~ m/^spec_(\S+)$/) { |
|
|
493 | } elsif ($plan =~ m/^resist_(\S+)$/) { |
|
|
494 | } |
|
|
495 | |
|
|
496 | return $ring; |
|
|
497 | } |
|
|
498 | |
|
|
499 | sub do_grep { |
|
|
500 | my ($self, $cb, @grepar) = @_; |
|
|
501 | |
|
|
502 | my $ingred = $self->{ingredients}; |
|
|
503 | |
|
|
504 | |
|
|
505 | for my $cat (keys %$ingred) { |
|
|
506 | my @rem; |
|
|
507 | for my $ing (@{$ingred->{$cat}}) { |
|
|
508 | if (Jeweler::Util::grep_for_match ($ing, @grepar)) { |
|
|
509 | unless ($cb->($ing)) { |
|
|
510 | push @rem, $ing; |
|
|
511 | } |
|
|
512 | } else { |
|
|
513 | push @rem, $ing; |
|
|
514 | } |
|
|
515 | } |
|
|
516 | @{$ingred->{$cat}} = @rem; |
|
|
517 | } |
|
|
518 | } |
|
|
519 | |
|
|
520 | sub check_costs { |
|
|
521 | my ($self, $costs, $do_remove) = @_; |
|
|
522 | |
|
|
523 | my $costs = dclone ($costs); |
|
|
524 | |
|
|
525 | for my $key (keys %$costs) { |
|
|
526 | my @grepar; |
|
|
527 | if ($key =~ m/^stat_(\S+)$/) { |
|
|
528 | @grepar = @{Jeweler::getcfg (plans => $key) || []}; |
272 | } else { |
529 | } else { |
273 | push @out, $_; |
530 | @grepar = (undef, undef, $key); |
|
|
531 | } |
|
|
532 | |
|
|
533 | if ($do_remove) { |
|
|
534 | my $rem = $costs->{$key}; |
|
|
535 | $self->do_grep (sub { if ($rem) { $rem = Jeweler::Util::remove ($_[0], $rem); } 1 }, @grepar); |
|
|
536 | if ($rem > 0) { |
|
|
537 | warn "JEWELER BUG: removed ingredients $rem > 0 after removing!"; |
274 | } |
538 | } |
|
|
539 | } else { |
|
|
540 | my $nr; |
|
|
541 | $self->do_grep (sub { $nr += $_[0]->nrof; 0 }, @grepar); |
|
|
542 | $costs->{$key} -= $nr; |
|
|
543 | } |
275 | } |
544 | } |
276 | |
545 | |
277 | @{$ingred->{$grp}} = @out; |
546 | return $costs; |
278 | } |
547 | } |
279 | |
548 | |
280 | =back |
549 | =back |
281 | |
550 | |
282 | =cut |
551 | =cut |
283 | |
552 | |
|
|
553 | sub put_to_bench { |
|
|
554 | my ($self, $bench) = @_; |
|
|
555 | |
|
|
556 | my $ingred = $self->{ingredients}; |
|
|
557 | |
|
|
558 | for my $ik (keys %$ingred) { |
|
|
559 | for (@{$ingred->{$ik} || []}) { |
|
|
560 | $bench->put ($_); |
|
|
561 | } |
|
|
562 | } |
|
|
563 | } |
|
|
564 | |
|
|
565 | package Jeweler::Object; |
|
|
566 | use strict; |
|
|
567 | use POSIX; |
|
|
568 | use List::Util qw/max min sum/; |
|
|
569 | |
|
|
570 | sub new { |
|
|
571 | my ($class, %arg) = @_; |
|
|
572 | |
|
|
573 | my $self = bless { }, $class; |
|
|
574 | |
|
|
575 | $self->ring_or_ammy_to_hash ($arg{object}); |
|
|
576 | |
|
|
577 | $self; |
|
|
578 | } |
|
|
579 | |
|
|
580 | sub analyze { |
|
|
581 | my ($self, $sk, $pl) = @_; |
|
|
582 | |
|
|
583 | my $sklvl = cf::exp_to_level ($sk->stats->exp); |
|
|
584 | my $ringlvl = $self->power_to_level; |
|
|
585 | |
|
|
586 | my $tmpl; |
|
|
587 | if ($pl->flag (cf::FLAG_WIZ)) { |
|
|
588 | $tmpl = Jeweler::level_diff_to_chance_perc ($sklvl - $ringlvl); |
|
|
589 | } else { |
|
|
590 | $tmpl = Jeweler::level_diff_to_str ($sklvl - $ringlvl); |
|
|
591 | } |
|
|
592 | my $msg = sprintf "Projected success rate: %s", $tmpl; |
|
|
593 | return $msg; |
|
|
594 | } |
|
|
595 | |
|
|
596 | sub wiz_analyze { |
|
|
597 | my ($self, $pl) = @_; |
|
|
598 | my $costs = $self->calc_costs; |
|
|
599 | my $desc = ""; |
|
|
600 | my $lvl = $self->power_to_level (\$desc); |
|
|
601 | $pl->message ("costs: " . join (', ', map { "$_: " . sprintf "%.2f", $costs->{$_} } keys %$costs)); |
|
|
602 | $pl->message ("level: " . $desc); |
|
|
603 | } |
|
|
604 | |
|
|
605 | |
|
|
606 | sub get_chance_perc { |
|
|
607 | my ($self, $sk) = @_; |
|
|
608 | my $sklvl = cf::exp_to_level ($sk->stats->exp); |
|
|
609 | my $ringlvl = $self->power_to_level; |
|
|
610 | return Jeweler::level_diff_to_chance_perc ($sklvl - $ringlvl); |
|
|
611 | } |
|
|
612 | |
|
|
613 | sub fx { |
|
|
614 | my ($res, $cfg) = @_; |
|
|
615 | my $or = $res; |
|
|
616 | my $ar = $Jeweler::CFG->{functions}->{$cfg}; |
|
|
617 | if (ref $ar->[0] eq 'ARRAY') { |
|
|
618 | $res = $res - 1; |
|
|
619 | } else { |
|
|
620 | $res = ceil ($res / 5) - 1; |
|
|
621 | } |
|
|
622 | $ar->[max (min ($res, @$ar - 1), 0)]; |
|
|
623 | } |
|
|
624 | |
|
|
625 | sub improve_by_ring { |
|
|
626 | my ($self, @rings) = @_; |
|
|
627 | my $ring = $self; |
|
|
628 | for my $iring (@rings) { |
|
|
629 | for my $cat (qw/stat spec resist/) { |
|
|
630 | for my $k (keys %{$iring->{hash}->{$cat}}) { |
|
|
631 | if ($ring->{hash}->{$cat}->{$k} < $iring->{hash}->{$cat}->{$k}) { |
|
|
632 | $ring->{hash}->{$cat}->{$k} = $iring->{hash}->{$cat}->{$k}; |
|
|
633 | } |
|
|
634 | } |
|
|
635 | } |
|
|
636 | } |
|
|
637 | } |
|
|
638 | |
|
|
639 | sub negate { |
|
|
640 | my ($self) = @_; |
|
|
641 | for my $cat (qw/stat spec resist/) { |
|
|
642 | for my $k (keys %{$self->{hash}->{$cat}}) { |
|
|
643 | if ($self->{hash}->{$cat}->{$k} > 0) { |
|
|
644 | $self->{hash}->{$cat}->{$k} *= -1; |
|
|
645 | } |
|
|
646 | } |
|
|
647 | } |
|
|
648 | } |
|
|
649 | |
|
|
650 | sub to_string { |
|
|
651 | my ($self) = @_; |
|
|
652 | my $r = $self->{hash}; |
|
|
653 | return |
|
|
654 | $r->{arch} . " " . |
|
|
655 | join ("", |
|
|
656 | grep { $_ ne "" } |
|
|
657 | join ("", |
|
|
658 | (map { |
|
|
659 | my $rv = $r->{resist}->{$_}; |
|
|
660 | "(resist " . (lc $Jeweler::RESMAP{$_}) . " " . ($rv > 0 ? '+' : '') . $rv . ")" |
|
|
661 | } grep { $r->{resist}->{$_} } @Jeweler::RESISTS), |
|
|
662 | (map { |
|
|
663 | my $rv = $r->{stat}->{$_}; |
|
|
664 | "(" . (lc $_) . ($rv > 0 ? '+' : '') . $rv . ")" |
|
|
665 | } grep { $r->{stat}->{$_} } keys %{$r->{stat}}), |
|
|
666 | (map { |
|
|
667 | my $rv = $r->{spec}->{$_}; |
|
|
668 | "(" . (lc $_) . ($rv > 0 ? '+' : '') . $rv . ")" |
|
|
669 | } grep { $r->{spec}->{$_} } keys %{$r->{spec}}))) |
|
|
670 | } |
|
|
671 | |
|
|
672 | sub ring_or_ammy_to_hash { |
|
|
673 | my ($self, $thing) = @_; |
|
|
674 | |
|
|
675 | my $obj = {}; |
|
|
676 | |
|
|
677 | for (@Jeweler::RESISTS) { |
|
|
678 | $obj->{resist}->{$_} = $thing->resistance ($_); |
|
|
679 | } |
|
|
680 | |
|
|
681 | my $stats = $thing->stats; |
|
|
682 | |
|
|
683 | for (qw/Str Dex Con Wis Cha Int Pow/) { |
|
|
684 | $obj->{stat}->{lc $_} = $stats->$_; |
|
|
685 | } |
|
|
686 | |
|
|
687 | $obj->{spec}{regen} = $stats->hp; |
|
|
688 | $obj->{spec}{magic} = $stats->sp; |
|
|
689 | $obj->{spec}{wc} = $stats->wc; |
|
|
690 | $obj->{spec}{dam} = $stats->dam; |
|
|
691 | $obj->{spec}{ac} = $stats->ac; |
|
|
692 | $obj->{spec}{speed} = $stats->exp; |
|
|
693 | $obj->{spec}{food} = $stats->food; |
|
|
694 | |
|
|
695 | $obj->{name} = $thing->name; |
|
|
696 | $obj->{arch} = $thing->archetype->name; |
|
|
697 | $obj->{face} = $thing->face; |
|
|
698 | |
|
|
699 | $self->{hash} = $obj |
|
|
700 | } |
|
|
701 | |
|
|
702 | sub to_object { |
|
|
703 | my ($self) = @_; |
|
|
704 | |
|
|
705 | my $obj = cf::object::new $self->{hash}->{arch}; |
|
|
706 | |
|
|
707 | $obj->face ($self->{hash}{face}); |
|
|
708 | |
|
|
709 | my $stats = $obj->stats; |
|
|
710 | |
|
|
711 | $stats->hp ($self->{hash}{spec}{regen}); |
|
|
712 | $stats->sp ($self->{hash}{spec}{magic}); |
|
|
713 | $stats->wc ($self->{hash}{spec}{wc}); |
|
|
714 | $stats->dam ($self->{hash}{spec}{dam}); |
|
|
715 | $stats->ac ($self->{hash}{spec}{ac}); |
|
|
716 | $stats->exp ($self->{hash}{spec}{speed}); |
|
|
717 | $stats->food ($self->{hash}{spec}{food}); |
|
|
718 | |
|
|
719 | $stats->$_ ($self->{hash}{stat}{lc $_}) |
|
|
720 | for qw/Str Dex Con Wis Cha Int Pow/; |
|
|
721 | |
|
|
722 | for (@Jeweler::RESISTS) { |
|
|
723 | $obj->resistance ($_, $self->{hash}->{resist}->{$_}); |
|
|
724 | } |
|
|
725 | |
|
|
726 | $obj->flag (cf::FLAG_IDENTIFIED, 1); |
|
|
727 | |
|
|
728 | return $obj; |
|
|
729 | } |
|
|
730 | |
|
|
731 | sub stat_level { |
|
|
732 | my ($self) = @_; |
|
|
733 | my $stats = $self->{hash}->{stat} || {}; |
|
|
734 | |
|
|
735 | my $maxlevel = Jeweler::getcfg (maxlevels => 'stat_level'); |
|
|
736 | my $maxstat = Jeweler::getcfg (maximprovements => 'stats'); |
|
|
737 | |
|
|
738 | my $stat_cnt = scalar (grep { $_ > 0 } values %$stats); |
|
|
739 | my $stat_sum = sum (values %$stats); |
|
|
740 | my $level = int (($maxlevel / $maxstat) * $stat_sum); |
|
|
741 | |
|
|
742 | ($level, $stat_cnt) |
|
|
743 | } |
|
|
744 | |
|
|
745 | sub resist_level { |
|
|
746 | my ($self) = @_; |
|
|
747 | |
|
|
748 | my $resists = $self->{hash}->{resist} || {}; |
|
|
749 | |
|
|
750 | my $att_res_lvl = Jeweler::getcfg (maxlevels => 'resist_level'); |
|
|
751 | my $efc_res_lvl = Jeweler::getcfg (maxlevels => 'effect_resist_level'); |
|
|
752 | my $max_att_res = Jeweler::getcfg (maximprovements => 'attack_resistances'); |
|
|
753 | my $max_efc_res = Jeweler::getcfg (maximprovements => 'effect_resistances'); |
|
|
754 | my $max_ovr_res = Jeweler::getcfg (maximprovements => 'resistances'); |
|
|
755 | |
|
|
756 | my $ressum = 0; |
|
|
757 | my $rescnt = 0; |
|
|
758 | my @reslevels; |
|
|
759 | |
|
|
760 | for my $resnam (keys %$resists) { |
|
|
761 | my $res = $resists->{$resnam}; |
|
|
762 | |
|
|
763 | $rescnt++ |
|
|
764 | if $res > 0; # negative resistancies are not an improvement |
|
|
765 | |
|
|
766 | $ressum += $res; # note: negative resistancies lower the sum |
|
|
767 | |
|
|
768 | next unless $res > 0; |
|
|
769 | |
|
|
770 | my $level = 0; |
|
|
771 | if (grep { $resnam eq $_ } @Jeweler::EFFECT_RESISTS) { |
|
|
772 | $level = ceil (($efc_res_lvl / $max_efc_res) * $res); |
|
|
773 | } else { |
|
|
774 | $level = ceil (($att_res_lvl / $max_att_res) * $res); |
|
|
775 | } |
|
|
776 | push @reslevels, $level; |
|
|
777 | } |
|
|
778 | |
|
|
779 | my $overall_lvl = ($att_res_lvl / $max_ovr_res) * $ressum; |
|
|
780 | |
|
|
781 | (max (@reslevels, $overall_lvl), $rescnt); |
|
|
782 | } |
|
|
783 | |
|
|
784 | sub special_level { |
|
|
785 | my ($self) = @_; |
|
|
786 | |
|
|
787 | my $specials = $self->{hash}->{spec} || {}; |
|
|
788 | |
|
|
789 | my $max_spc_lvl = Jeweler::getcfg (maxlevels => 'spec_level'); |
|
|
790 | my $max_specials = Jeweler::getcfg (maximprovements => 'specials'); |
|
|
791 | |
|
|
792 | my @speclvls; |
|
|
793 | my $specsum = 0; |
|
|
794 | my $imprs = 0; |
|
|
795 | |
|
|
796 | for my $spcnam (keys %$specials) { |
|
|
797 | my $spc = $specials->{$spcnam}; |
|
|
798 | next unless $spc > 0; |
|
|
799 | |
|
|
800 | $specsum += $spc; |
|
|
801 | $imprs++; |
|
|
802 | |
|
|
803 | my $max_spc = Jeweler::getcfg (maxspecial => $spcnam); |
|
|
804 | |
|
|
805 | my $lvl = ($max_spc_lvl / $max_spc) * $spc; |
|
|
806 | push @speclvls, $lvl; |
|
|
807 | } |
|
|
808 | |
|
|
809 | my $sumlvl = ($max_spc_lvl / $max_specials) * $specsum; |
|
|
810 | |
|
|
811 | (max (@speclvls, $sumlvl), $imprs) |
|
|
812 | } |
|
|
813 | |
|
|
814 | |
|
|
815 | # this function calculated the 'level' of an amulet or a ring |
|
|
816 | sub power_to_level { |
|
|
817 | my ($self, $lvldescr) = @_; |
|
|
818 | |
|
|
819 | my $max_imprs = Jeweler::getcfg (maximprovements => 'improvements'); |
|
|
820 | my $max_impr_lvl = Jeweler::getcfg (maxlevels => 'improve_level'); |
|
|
821 | |
|
|
822 | my ($stat_lvl, $stat_imprs) = $self->stat_level; |
|
|
823 | my ($resist_lvl, $res_imprs) = $self->resist_level; |
|
|
824 | my ($spec_lvl, $spec_imprs) = $self->special_level; |
|
|
825 | |
|
|
826 | my $impr_sum = $stat_imprs + $res_imprs + $spec_imprs; |
|
|
827 | |
|
|
828 | my $impr_lvl = ceil (($max_impr_lvl / ($max_imprs + 1)) * ($impr_sum - 1)); # 1 improvemnt bonus |
|
|
829 | |
|
|
830 | my $levl = int max ($stat_lvl, $resist_lvl, $impr_lvl, $spec_lvl, 0); |
|
|
831 | |
|
|
832 | if ($lvldescr) { |
|
|
833 | $$lvldescr = |
|
|
834 | sprintf "%3d: %s\n", $levl, |
|
|
835 | "stat: $stat_lvl, resist: $resist_lvl, improve: $impr_lvl, " |
|
|
836 | ."spec: $spec_lvl (num impr $stat_imprs + $res_imprs + $spec_imprs - 1)"; |
|
|
837 | } |
|
|
838 | |
|
|
839 | $levl |
|
|
840 | } |
|
|
841 | |
|
|
842 | sub add_stat_costs { |
|
|
843 | my ($self, $cost) = @_; |
|
|
844 | |
|
|
845 | my $stats = $self->{hash}->{stat}; |
|
|
846 | |
|
|
847 | for my $stat (keys %$stats) { |
|
|
848 | my $sum = $stats->{$stat}; |
|
|
849 | |
|
|
850 | next unless $sum > 0; |
|
|
851 | |
|
|
852 | my $statfx = fx ($sum, 'stat_potions'); |
|
|
853 | $cost->{"stat_$stat"} += $statfx->[0]; |
|
|
854 | split_diamonds ($cost, $statfx->[1], 'stat_' . $stat); |
|
|
855 | } |
|
|
856 | } |
|
|
857 | |
|
|
858 | sub add_special_costs { |
|
|
859 | my ($self, $cost) = @_; |
|
|
860 | |
|
|
861 | my $specials = $self->{hash}->{spec}; |
|
|
862 | |
|
|
863 | for my $spec (keys %$specials) { |
|
|
864 | my $sum = $specials->{$spec}; |
|
|
865 | |
|
|
866 | next unless $sum > 0; |
|
|
867 | |
|
|
868 | my $specfx = fx ($sum, 'spec_potions'); |
|
|
869 | $cost->{"spec_$spec"} += $specfx->[0]; |
|
|
870 | split_diamonds ($cost, $specfx->[1], 'spec_' . $spec); |
|
|
871 | } |
|
|
872 | } |
|
|
873 | |
|
|
874 | sub calc_costs { |
|
|
875 | my ($self) = @_; |
|
|
876 | |
|
|
877 | my $costs = {}; |
|
|
878 | |
|
|
879 | my $ring = $self->{hash}; |
|
|
880 | |
|
|
881 | for my $resnam (keys %{$ring->{resist} || {}}) { |
|
|
882 | |
|
|
883 | my $res = $ring->{resist}->{$resnam}; |
|
|
884 | |
|
|
885 | next unless $res > 0; |
|
|
886 | |
|
|
887 | $costs->{"resist_" . $Jeweler::RESMAP{$resnam} . "_item"} += $res; |
|
|
888 | |
|
|
889 | my $diamonds; |
|
|
890 | if (grep { $resnam eq $_ } @Jeweler::EFFECT_RESISTS) { |
|
|
891 | $diamonds += fx ($res, 'effect_resist_diamonds'); |
|
|
892 | } else { |
|
|
893 | $diamonds += fx ($res, 'attack_resist_diamonds'); |
|
|
894 | } |
|
|
895 | |
|
|
896 | split_diamonds ($costs, $diamonds, 'resist_' . $Jeweler::RESMAP{$resnam}); |
|
|
897 | } |
|
|
898 | |
|
|
899 | $self->add_stat_costs ($costs); |
|
|
900 | $self->add_special_costs ($costs); |
|
|
901 | |
|
|
902 | return $costs; |
|
|
903 | } |
|
|
904 | |
|
|
905 | sub split_diamonds { |
|
|
906 | my ($cost, $diamonds, $category) = @_; |
|
|
907 | |
|
|
908 | my $stat_split = Jeweler::getcfg (diamond_split => $category); |
|
|
909 | |
|
|
910 | my $sum = sum (@$stat_split); |
|
|
911 | if ($sum < (1 - 0.0001)) { |
|
|
912 | warn "JEWELER BUG: sum (@$stat_split) = $sum < 1 for $category!"; |
|
|
913 | } |
|
|
914 | |
|
|
915 | my $emarch = cf::arch::find 'emerald'; |
|
|
916 | my $saarch = cf::arch::find 'sapphire'; |
|
|
917 | my $pearch = cf::arch::find 'pearl'; |
|
|
918 | my $ruarch = cf::arch::find 'ruby'; |
|
|
919 | my $diarch = cf::arch::find 'gem'; |
|
|
920 | |
|
|
921 | my $sumvalue = $diarch->clone->value * $diamonds; |
|
|
922 | |
|
|
923 | $cost->{emerald} += ceil $sumvalue * $stat_split->[0] / max 1, $emarch->clone->value; |
|
|
924 | $cost->{sapphire} += ceil $sumvalue * $stat_split->[1] / max 1, $saarch->clone->value; |
|
|
925 | $cost->{pearl} += ceil $sumvalue * $stat_split->[2] / max 1, $pearch->clone->value; |
|
|
926 | $cost->{ruby} += ceil $sumvalue * $stat_split->[3] / max 1, $ruarch->clone->value; |
|
|
927 | $cost->{gem} += ceil $sumvalue * $stat_split->[4] / max 1, $diarch->clone->value; |
|
|
928 | } |
|
|
929 | |
|
|
930 | |
|
|
931 | |
284 | package Jeweler::Util; |
932 | package Jeweler::Util; |
|
|
933 | use strict; |
285 | |
934 | |
286 | =head2 Util |
935 | =head2 Util |
287 | |
936 | |
288 | Some utility functions for the Jeweler skill. |
937 | Some utility functions for the Jeweler skill. |
289 | |
938 | |
290 | =over 4 |
939 | =over 4 |
291 | |
940 | |
292 | =item remove ($object) |
941 | =item remove ($object[, $nrof]) |
293 | |
942 | |
294 | Removes the C<$object> and it's inventory recursivley from the game. |
943 | Removes the C<$object>. If C<$nrof> is given, remove only C<$nrof> objects. |
|
|
944 | The returnvalue is the number of 'single' objects that couldn't be removed. |
295 | |
945 | |
296 | =cut |
946 | =cut |
297 | |
947 | |
298 | sub remove { |
948 | sub remove { |
299 | my ($obj) = @_; |
949 | my ($obj, $nrof) = @_; |
300 | |
950 | |
|
|
951 | my $cnt; |
|
|
952 | |
|
|
953 | if (defined $nrof) { |
|
|
954 | return 0 if ($nrof * 1) == 0; |
|
|
955 | $cnt = int (($obj->nrof || 1) - (1 * $nrof)); |
|
|
956 | |
|
|
957 | if ($cnt > 0) { |
|
|
958 | $obj->nrof ($cnt); |
|
|
959 | return 0; |
|
|
960 | } |
|
|
961 | } |
|
|
962 | |
301 | remove ($_) for ($obj->inv); |
963 | remove ($_) for $obj->inv; |
302 | $obj->remove; |
964 | $obj->remove; |
303 | $obj->free; |
965 | $obj->free; |
|
|
966 | return $cnt; |
|
|
967 | } |
|
|
968 | |
|
|
969 | sub grep_for_match { |
|
|
970 | my ($thing, @matchar) = @_; |
|
|
971 | |
|
|
972 | my $i = 0; |
|
|
973 | for my $match (@matchar) { |
|
|
974 | if ($match =~ m/^\s*$/) { |
|
|
975 | $i++; |
|
|
976 | next; |
|
|
977 | } |
|
|
978 | |
|
|
979 | if ($i % 3 == 0) { |
|
|
980 | $thing->name eq $match |
|
|
981 | and return 1; |
|
|
982 | } elsif ($i % 3 == 1) { |
|
|
983 | $thing->title eq $match |
|
|
984 | and return 1; |
|
|
985 | } else { # $i % 3 == 2 |
|
|
986 | $thing->archetype->name eq $match |
|
|
987 | and return 1; |
|
|
988 | } |
|
|
989 | $i++; |
|
|
990 | } |
|
|
991 | return 0; |
304 | } |
992 | } |
305 | |
993 | |
306 | =back |
994 | =back |
307 | |
995 | |
308 | =back |
996 | =back |