ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/maps/perl/jeweler.ext
Revision: 1.1
Committed: Sat Jul 15 12:57:41 2006 UTC (17 years, 10 months ago) by elmex
Branch: MAIN
Log Message:
Checking in the jeweler extension. It's still undfinished,
but it has been too long on my hard drive alone.

File Contents

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