ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/maps/perl/jeweler.ext
Revision: 1.3
Committed: Wed Aug 9 11:54:02 2006 UTC (17 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.2: +2 -3 lines
Log Message:
the ipo and board are now converted to JSON and the board works now too.

File Contents

# User Rev Content
1 elmex 1.1 #! perl
2     use POSIX;
3     use Data::Dumper;
4     use List::Util qw/max min sum/;
5 elmex 1.3 use Jeweler;
6 elmex 1.1 use strict;
7    
8     my $DEBUG = 1;
9     my $CFG;
10    
11     # this is a simple quad polynom, it takes it's factors from
12     # the configuration
13     sub fx {
14     my ($x, $setting) = @_;
15     my $facts = getcfg (functions => $setting);
16     return $facts->[0] * ($x ** 2) + $facts->[1] * $x + $facts->[2];
17     }
18    
19     # makes a template arch (for example to get the value)
20     sub get_arch {
21     my ($outarch) = @_;
22     unless ($CFG->{arch}->{$outarch}) {
23     $CFG->{arch}->{$outarch} = cf::object::new $outarch;
24    
25     unless ($CFG->{arch}->{$outarch}) {
26     warn "ERROR: Couldn't make $outarch in conversion for $outarch!";
27     return;
28     }
29     }
30     $CFG->{arch}->{$outarch}
31     }
32    
33     sub calc_costs {
34     my ($ring) = @_;
35    
36     my $costs = {};
37    
38     for my $resnam (keys %{$ring->{resist} || {}}) {
39    
40     my $res = $ring->{resist}->{$resnam};
41    
42     next unless $res > 0;
43    
44     $costs->{"food_$resnam"} += $res;
45     $costs->{"resist_" . $Jeweler::RESMAP{$resnam} . "_potion"}++;
46    
47     my $diamonds;
48     if (grep { $resnam eq $_ } @Jeweler::EFFECT_RESISTS) {
49     $diamonds += fx ($res, 'effect_resist_diamonds');
50     } else {
51     $diamonds += fx ($res, 'attack_resist_diamonds');
52     }
53     $costs->{diamonds} += $diamonds;
54     }
55    
56     $costs = calc_stat_costs ($ring->{stat}, $costs);
57     $costs = calc_special_costs ($ring->{spec}, $costs);
58    
59     warn
60     sprintf "JEWEL ANALYSE: %40s: %s" ,
61     $ring->{name},
62     join (', ', map { "$_: " . sprintf "%.2f", $costs->{$_} } keys %$costs);
63     return $costs;
64     }
65    
66     sub read_config {
67     my ($filename) = @_;
68    
69     # open my $fh, $filename
70     # or die "Couldn't open '$filename': $!";
71    
72 elmex 1.3 my $cfg = {}; #LoadFile $filename;
73 elmex 1.1
74     # my $section = 'main';
75     #
76     # my $cont = join '', <$fh>;
77     #
78     # for (<$fh>) {
79     # s/#.*$//;
80     #
81     # if (m/^\[([^\]]+)\]/) {
82     # $section = $1;
83     #
84     # } elsif (m/^\s*(\S+)\s*=\s*(.*?)\s*$/) {
85     # my ($k, $v) = ($1, $2);
86     #
87     # my @v = split /\s*,\s*/, $v;
88     #
89     # if (@v > 1) {
90     # $v = [ @v ];
91     # }
92     #
93     # $cfg->{$section}->{$k} = $v;
94     # }
95     # }
96    
97     return $cfg;
98     }
99    
100     sub getcfg {
101     my ($sect, $key) = @_;
102     my $cfg = $CFG->{$sect}->{$key}
103     or die "Couldn't find $sect/$key in configuration!";
104    
105     $cfg
106     }
107    
108     sub ring_or_ammy_to_hash {
109     my ($thing) = @_;
110    
111     my $obj = {};
112    
113     for (@Jeweler::RESISTS) {
114     $obj->{resist}->{$_} = $thing->get_resistance ($_);
115     }
116    
117     my $stats = $thing->stats;
118    
119     for (qw/Str Dex Con Wis Cha Int Pow/) {
120     $obj->{stat}->{$_} = $stats->$_;
121     }
122    
123     $obj->{spec}->{regen} = $thing->hp;
124     $obj->{spec}->{magic} = $thing->sp;
125     $obj->{spec}->{wc} = $thing->wc;
126     $obj->{spec}->{dam} = $thing->dam;
127     $obj->{spec}->{ac} = $thing->ac;
128     $obj->{spec}->{speed} = $thing->stats->exp;
129     $obj->{spec}->{suste} = $thing->food;
130    
131     $obj->{name} = $thing->name;
132    
133     $obj
134     }
135    
136     sub split_diamonds {
137     my ($cost, $diamonds, $category) = @_;
138    
139     my $stat_split = getcfg (diamond_split => $category);
140    
141     my $emarch = get_arch ('emerald');
142     my $saarch = get_arch ('sapphire');
143     my $pearch = get_arch ('pearl');
144     my $ruarch = get_arch ('ruby');
145     my $diarch = get_arch ('gem');
146    
147     my $sumvalue = $diarch->value * $diamonds;
148    
149     $cost->{emeralds} += ceil (($sumvalue * $stat_split->[0]) / $emarch->{value});
150     $cost->{sapphires} += ceil (($sumvalue * $stat_split->[1]) / $saarch->{value});
151     $cost->{pearls} += ceil (($sumvalue * $stat_split->[2]) / $pearch->{value});
152     $cost->{rubies} += ceil (($sumvalue * $stat_split->[3]) / $ruarch->{value});
153     $cost->{diamonds} += ceil (($sumvalue * $stat_split->[4]) / $diarch->{value});
154    
155     return $cost;
156     }
157    
158     sub calc_stat_level {
159     my ($stats) = @_;
160    
161     my $maxlevel = getcfg (maxlevels => 'stat_level');
162     my $maxstat = getcfg (maximprovements => 'stats');
163    
164     my $stat_cnt = scalar (grep { $_ > 0 } values %$stats);
165     my $stat_sum = sum (values %$stats);
166     my $level = int (($maxlevel / $maxstat) * $stat_sum);
167    
168     ($level, $stat_cnt)
169     }
170    
171     sub calc_resist_level {
172     my ($resists) = @_;
173    
174     my $att_res_lvl = getcfg (maxlevels => 'resist_level');
175     my $efc_res_lvl = getcfg (maxlevels => 'effect_resist_level');
176     my $max_att_res = getcfg (maximprovements => 'attack_resistances');
177     my $max_efc_res = getcfg (maximprovements => 'effect_resistances');
178     my $max_ovr_res = getcfg (maximprovements => 'resistances');
179    
180     my $ressum = 0;
181     my $rescnt = 0;
182     my @reslevels;
183    
184     for my $resnam (keys %$resists) {
185     my $res = $resists->{$resnam};
186    
187     $rescnt++
188     if $res > 0; # negative resistancies are not an improvement
189    
190     $ressum += $res; # note: negative resistancies lower the sum
191    
192     next unless $res > 0;
193    
194     my $level = 0;
195     if (grep { $resnam eq $_ } @Jeweler::EFFECT_RESISTS) {
196     $level = ceil (($efc_res_lvl / $max_efc_res) * $res);
197     } else {
198     $level = ceil (($att_res_lvl / $max_att_res) * $res);
199     }
200     push @reslevels, $level;
201     }
202    
203     my $overall_lvl = ($att_res_lvl / $max_ovr_res) * $ressum;
204    
205     (max (@reslevels, $overall_lvl), $rescnt);
206     }
207    
208     sub calc_special_level {
209     my ($specials) = @_;
210    
211     my $max_spc_lvl = getcfg (maxlevels => 'spec_level');
212     my $max_specials = getcfg (maximprovements => 'specials');
213    
214     my @speclvls;
215     my $specsum = 0;
216     my $imprs = 0;
217    
218     for my $spcnam (keys %$specials) {
219     my $spc = $specials->{$spcnam};
220     next unless $spc > 0;
221    
222     $specsum += $spc;
223     $imprs++;
224    
225     my $max_spc = getcfg (maxspecial => $spcnam);
226    
227     my $lvl = ($max_spc_lvl / $max_spc) * $spc;
228     push @speclvls, $lvl;
229     }
230    
231     my $sumlvl = ($max_spc_lvl / $max_specials) * $specsum;
232    
233     (max (@speclvls, $sumlvl), $imprs)
234     }
235    
236     sub calc_stat_costs {
237     my ($stats, $cost) = @_;
238    
239     my $sum = sum grep { $_ > 0 } values %$stats;
240    
241     return $cost unless $sum > 0;
242    
243     $cost->{stat_potions} += fx ($sum, 'stat_potions');
244     $cost->{diamonds} += fx ($sum, 'stat_diamonds');
245    
246     $cost
247     }
248    
249     sub calc_special_costs {
250     my ($specials, $cost) = @_;
251    
252     my $sum = sum grep { $_ > 0 } values %$specials;
253    
254     return $cost unless $sum > 0;
255    
256     $cost->{spec_potions} += fx ($sum, 'spec_potions');
257     $cost->{diamonds} += fx ($sum, 'spec_diamonds');
258    
259     $cost
260     }
261    
262     # this function calculated the 'level' of an amulet or a ring
263     sub power_to_level {
264     my ($ring) = @_;
265    
266     my $max_imprs = getcfg (maximprovements => 'improvements');
267     my $max_impr_lvl = getcfg (maxlevels => 'improve_level');
268    
269     my ($stat_lvl, $stat_imprs) = calc_stat_level ($ring->{stat} || {});
270     my ($resist_lvl, $res_imprs) = calc_resist_level ($ring->{resist} || {});
271     my ($spec_lvl, $spec_imprs) = calc_special_level ($ring->{spec} || {});
272    
273     my $impr_lvl = ceil (($max_impr_lvl / ($max_imprs + 1)) * ($stat_imprs + $res_imprs + $spec_imprs - 1));
274    
275     my $levl = int max ($stat_lvl, $resist_lvl, $impr_lvl, $spec_lvl, 0);
276    
277     my $cost = calc_costs ($ring);
278     warn sprintf "%3d: %50s: %s\n", $levl, $ring->{name}, "stat: $stat_lvl, resist: $resist_lvl, improve: $impr_lvl, spec: $spec_lvl (num impr $stat_imprs + $res_imprs + $spec_imprs - 1)";
279     # warn sprintf " %s\n", join (',', map { sprintf "$_: %5d", $cost->{$_} } keys %$cost);
280    
281     $levl
282     }
283    
284     # this function converts metals/minerals into a raw ring (of adornment)
285     sub simple_converter {
286     my ($pl, $ingred, $chdl, $conv) = @_;
287    
288     $conv = lc $conv;
289     my $cnvs = $CFG->{conversions};
290    
291     return unless $cnvs->{$conv};
292    
293     my %ingred_groups;
294    
295     my @conv_cfg = @{$cnvs->{$conv}};
296     my $outarch = $conv;
297     my ($ingr_grp, $outarchvalfact, $srcarchname, $xp_gain) = @conv_cfg;
298    
299     unless (@conv_cfg <= 4) {
300     warn "ERROR: Conversion for '$outarch' has only " . (@conv_cfg) . " arguments!";
301     return;
302     }
303    
304     unless ($xp_gain > 0) {
305     warn "WARNING: xp gain isn't > 0 in convesion '$outarch'\n";
306     return;
307     }
308    
309     unless ($outarchvalfact) {
310     warn "ERROR: source-arch-value-multiplier == 0 in convesion '$outarch'\n";
311     return;
312     }
313    
314     unless ($outarchvalfact >= 1) {
315     warn "WARNING: source-arch-value-multiplier < 1 in convesion '$outarch', results in more valuable output!\n";
316     }
317    
318     my $archvalsum = $ingred->value ($cnvs->{$outarch}->[0], $cnvs->{$outarch}->[2]);
319     $ingred->remove ($cnvs->{$outarch}->[0], $cnvs->{$outarch}->[2]);
320    
321     my $outarchval = get_arch ($outarch)->value;
322    
323     my $nrof = int ($archvalsum / (($outarchval || 1000) * $outarchvalfact));
324     if ($nrof) {
325     # XXX: yes, i know what i'm doing here, i don't set nrof, but it didn't work somehow (pls. chek sometimes)
326     $chdl->put (cf::object::new $outarch) for 1..$nrof;
327    
328     my $xp_sum = ($xp_gain * $nrof);
329    
330     if ($xp_sum) {
331     $pl->ob->message ("You got $xp_sum xp by making $nrof ${outarch}s");
332     $pl->ob->change_exp ($xp_sum, "jeweler", cf::SK_EXP_ADD_SKILL);
333     }
334     }
335     }
336    
337    
338    
339     sub put_ingred_to_bench {
340     my ($ingred, $bench) = @_;
341    
342     for my $ik (keys %$ingred) {
343     for (@{$ingred->{$ik} || []}) {
344     $_->insert_ob_in_ob ($bench);
345     }
346     }
347     }
348    
349     my %lvl_diff_chances = (
350     +5 => 100,
351     +4 => 95,
352     +3 => 85,
353     +2 => 75,
354     +1 => 65,
355     0 => 50,
356     -1 => 45,
357     -2 => 35,
358     -3 => 25,
359     -4 => 10,
360     -5 => 0
361     );
362    
363     my %lvl_diff_msg = (
364     -5 => '%s is way above your skill',
365     -4 => 'The chance to make %s is very low',
366     -3 => 'You hava a slight chance to make %s',
367     -2 => 'There is a low chance you finish %s',
368     -1 => 'You could make %s with a chance of nearly 50:50',
369     0 => 'The chances to fininsh %s is 50:50',
370     1 => 'To make %s your chance is slightly above 50:50',
371     2 => 'You could make with a good chance %s if you concentrate a lot',
372     3 => 'The chance you finish %s with some efford is high',
373     4 => 'You are nearly confident to finish %s',
374     5 => 'There is no chance you could fail to make %s',
375     );
376    
377     sub level_diff_to_str {
378     my ($delta) = @_;
379     $delta = -5 if $delta < -5;
380     $delta = 5 if $delta > 5;
381     return $lvl_diff_msg{$delta}
382     }
383    
384     sub level_diff_to_chance_perc {
385     my ($delta) = @_;
386     $delta = -5 if $delta < -5;
387     $delta = 5 if $delta > 5;
388     return $lvl_diff_chances{$delta}
389     }
390    
391     sub grep_for_match {
392     my ($thing, @matchar) = @_;
393    
394     my $i = 0;
395     for my $match (@matchar) {
396     if ($match =~ m/^\s*$/) {
397     $i++;
398     next;
399     }
400    
401     if ($i % 3 == 0) {
402     # warn ":FE1:" . $thing->name . ": $match\n";
403     $thing->name eq $match
404     and return 1;
405     } elsif ($i % 3 == 1) {
406     # warn ":FE2:" . $thing->title . ": $match\n";
407     $thing->title eq $match
408     and return 1;
409     } else { # $i % 3 == 2
410     # warn ":FE3:" . $thing->archetype->name . ": $match\n";
411     $thing->archetype->name eq $match
412     and return 1;
413     }
414     $i++;
415     }
416     return 0;
417     }
418    
419     sub get_plan {
420     my ($ingred) = @_;
421    
422     for my $pot (@{$ingred->{potions}}) {
423     for my $plan (keys %{$CFG->{plans}}) {
424     my $plg = $CFG->{plans}->{$plan};
425     my @plga = ();
426     unless (ref $plg eq 'ARRAY') {
427     push @plga, $plg;
428     } else {
429     @plga = @$plg;
430     }
431     next unless @plga > 0;
432     if (grep_for_match ($pot, @plga)) {
433     warn "MATCHED: $plan: @plga\n";
434     return $plan;
435     }
436     }
437     }
438     }
439    
440     sub get_ring {
441     my ($ingred) = @_;
442     return @{$ingred->{rings} || []};
443     }
444    
445     sub get_improv_amount {
446     my ($plan, $ingred) = @_;
447    
448     if ($plan =~ m/^stat_(\S+)$/) {
449     my $plingred = getcfg (plan_ingred => $plan)
450     or die "ingredients for plan '$plan' not defined!";
451    
452     my $cnt = 0;
453     for my $pot (@{$ingred->{potions}}) {
454     if (grep_for_match ($pot, @$plingred)) {
455     $cnt += $pot->nrof;
456     }
457     }
458     warn "Found $cnt potions for plan $plan\n";
459    
460     my $amount = 0;
461     for my $x (1..10) {
462     my $y = fx ($x, 'stat_potions');
463     warn "TEST: fx($x): $y <= $cnt \n";
464     warn "FE: " . ($y == $cnt) . "\n";
465     if ($y <= $cnt) {
466     $amount = $x;
467     warn "Found stat increase at: $x\n";
468     last;
469     }
470     }
471    
472     return $amount;
473     } elsif ($plan =~ m/^spec_(\S+)$/) {
474     return 0;
475     } elsif ($plan =~ m/^resist_(\S+)$/) {
476     return 0;
477     }
478     }
479    
480     sub get_plan_costs {
481     my ($plan, $ring, $amount) = @_;
482    
483     my $pre_costs = calc_costs ($ring);
484    
485     # alter ring spec
486     if ($plan =~ m/^stat_(\S+)$/) {
487     } elsif ($plan =~ m/^spec_(\S+)$/) {
488     } elsif ($plan =~ m/^resist_(\S+)$/) {
489     my ($resid) = map { $Jeweler::RESMAP{$_} } grep { $Jeweler::RESMAP{$_} eq $1 } keys %Jeweler::RESMAP;
490     unless (defined $resid) {
491     die "Couldn't find resistancy for plan: '$plan'\n";
492     }
493    
494     $ring->{resist}->{$resid} += $amount;
495     }
496    
497     my $post_costs = calc_costs ($ring);
498    
499     my $delta_costs = {};
500     for (keys %{$post_costs}) {
501     my $cost = $post_costs->{$_} - $pre_costs->{$_};
502     if ($cost > 0) {
503     warn "DELTA COST: $_ => $cost\n";
504     $delta_costs->{$_} = $cost;
505     }
506     }
507     return $delta_costs
508     }
509    
510     sub on_player_use_skill {
511 root 1.2 return 0; # disabled not yet ready#d##TODO#
512 elmex 1.1 my ($ob, $part, $sk, $dir, $msg) = @_;
513     my $pl = $ob;
514    
515     my $skobj = $sk;
516    
517     my $chdl = new Jeweler::CauldronHandler;
518    
519     return 0 unless $sk->subtype == cf::SK_JEWELER;
520    
521     my $rv = 1;
522     eval {
523     $CFG = read_config ($ENV{CROSSFIRE_LIBDIR} . '/jeweler.yaml'); #XXX: This has to become cached properly!
524     $DEBUG ||= $CFG->{main}->{debug};
525    
526     my $player = $ob->contr;#cf::player::find $ob->name;
527    
528     unless ($chdl->find_cauldron ('jeweler_bench', $ob->map->at ($ob->x, $ob->y))) {
529     $rv = 0;
530     return # return 0 if no cauldron found (default action: identify)
531     }
532    
533     my $ingred = $chdl->extract_jeweler_ingredients;
534    
535     if ($msg =~ m/^\s*analy[sz]e\s*$/i) {
536     for ($chdl->grep_by_type (cf::RING, cf::AMULET)) {
537     my $sklvl = cf::exp_to_level ($sk->stats->exp);
538     my $ringlvl = power_to_level (ring_or_ammy_to_hash ($_));
539    
540     if ($pl->get_flag (cf::FLAG_WIZ)) {
541     $pl->message ("The " . $_->name . " is at level $ringlvl chance for you: " . level_diff_to_chance_perc ($sklvl - $ringlvl));
542     } else {
543     my $tmpl = level_diff_to_str ($sklvl - $ringlvl);
544     my $msg = sprintf $tmpl, $_->name;
545     $pl->message ($msg);
546     }
547     }
548    
549     } elsif ($msg =~ m/^\s*make\s+(\S+)\s*$/i) {
550     unless ($CFG->{conversions}->{lc $1}) {
551     $pl->message ("You don't know how to make '$1', is does such a thing even exist?");
552     return
553     }
554    
555     simple_converter ($player, $ingred, $chdl, $1);
556    
557     # for (@{$ingred->{rings}}) {
558     # ring_or_ammy_to_hash ($_);
559     # }
560     #put_ingred_to_bench ($ingred, $c[0]);
561    
562     } else {
563     my $plan = get_plan ($ingred);
564    
565     if ($plan) {
566     my @ring = get_ring ($ingred);
567    
568     if (@ring > 1) {
569     # actually the algorithm cant handle more than one improvement at a time
570     $pl->message ("You can't manage to improve more than one ring!");
571    
572     } elsif (@ring < 1) {
573     # actually the algorithm cant
574     $pl->message ("You slap yourself, you forgot the ring!");
575    
576     } else {
577     my $ringh = ring_or_ammy_to_hash ($ring[0]);
578     my $amount = get_improv_amount ($plan, $ingred);
579     # my $costs = get_plan_costs ($plan[0], $ringh, $amount);
580     #
581     # if (my $chk = check_plan_costs ($costs, $ingred)) {
582     # # output some error that he lacks the ingredients
583     # } else {
584     # execute_plan ($costs, $ringh, $ingred);
585     # }
586     }
587    
588     } else {
589     $pl->message ("You've got no idea what you are planning to do!");
590     }
591     }
592     };
593     $@ and warn "ERROR: $@\n";
594    
595     my $r = cf::random_roll (0, 101, $pl, cf::PREFER_LOW);
596     $rv;
597     }