ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/maps/perl/Jeweler.pm
Revision: 1.2
Committed: Thu Aug 31 00:58:17 2006 UTC (17 years, 8 months ago) by elmex
Branch: MAIN
Changes since 1.1: +648 -14 lines
Log Message:
revived the jeweler skill!

File Contents

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