1 /*
2 * buildings.c
3 * Copyright (C) 2009-2020 Joachim de Groot <jdegroot@web.de>
4 *
5 * NLarn is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * NLarn is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdlib.h>
20 #include <glib.h>
21
22 #include "config.h"
23 #include "container.h"
24 #include "display.h"
25 #include "game.h"
26 #include "gems.h"
27 #include "items.h"
28 #include "extdefs.h"
29 #include "player.h"
30 #include "scrolls.h"
31
32 static const char msg_outstanding[] = "The Larn Revenue Service has ordered " \
33 "us to not do business with tax evaders. " \
34 "They have also told us that you owe back " \
35 "taxes and, as we must comply with the " \
36 "law, we cannot serve you at this time." \
37 "\n\nSo Sorry.";
38
39 static void building_shop(player *p, inventory **inv, const char *title);
40 static int building_player_check(player *p, guint amount);
41 static void building_player_charge(player *p, guint amount);
42
43 static void building_item_add(inventory **inv, item *it);
44 static void building_item_sell(player *p, inventory **inv, item *it);
45 static void building_item_identify(player *p, inventory **inv, item *it);
46 static void building_item_repair(player *p, inventory **inv, item *it);
47 static void building_item_buy(player *p, inventory **inv, item *it);
48 static guint calc_tax_debt(guint income);
49
building_bank_calc_interest(game * g)50 void building_bank_calc_interest(game *g)
51 {
52 guint interest = 0;
53
54 /* pay interest every 1000 turns */
55 if (game_turn(g) % 1000 != 0 || g->p->bank_account <= 250)
56 return;
57
58 /* the bank pays an interest of 2.5% every ten mobuls */
59 interest = g->p->bank_account / 250;
60
61 /* add the interest to the bank account.. */
62 g->p->bank_account += interest;
63
64 /* ..and the statistics.. */
65 g->p->stats.gold_bank_interest += interest;
66
67 /* ..and keep track of the amount paid since the player's last visit to the bank */
68 g->p->bank_ieslvtb += interest;
69
70 /* calculate the tax debt */
71 g->p->outstanding_taxes += calc_tax_debt(interest);
72
73 }
74
building_bank(player * p)75 int building_bank(player *p)
76 {
77 guint amount = 0;
78 GString *greeting;
79
80 const char msg_title[] = "First National Bank of Larn";
81 const char msg_greet[] = "`white`Welcome to the First National Bank of Larn.`end`\n\n";
82
83 const char msg_branch[] = "Welcome to the 5th level branch office of the " \
84 "First National Bank of Larn.\n\n";
85
86 const char msg_frozen[] = "The Larn Revenue Service has ordered that your " \
87 "account be frozen until all levied taxes have " \
88 "been paid. They have also told us that you " \
89 "owe %d gold in taxes, and we must comply with " \
90 "them. We cannot serve you at this time. Sorry.\n\n" \
91 "We suggest you go to the LRS office and pay your taxes.";
92
93 g_assert(p != NULL);
94
95 if (Z(p->pos) == 0)
96 greeting = g_string_new(msg_greet);
97 else
98 greeting = g_string_new(msg_branch);
99
100 if (p->bank_ieslvtb > 0)
101 {
102 /* show the earned interest */
103 g_string_append_printf(greeting, "We have paid you an interest of %d " \
104 "gold since your last visit.\n", p->bank_ieslvtb);
105
106 /* add it to the game log for later reference */
107 log_add_entry(nlarn->log, "The bank has paid you an interest of %d "
108 "gold since your last visit.", p->bank_ieslvtb);
109
110 /* reset the value */
111 p->bank_ieslvtb = 0;
112 }
113
114 /* leave bank when taxes are unpaid */
115 if (p->outstanding_taxes)
116 {
117 g_string_append_printf(greeting, msg_frozen, p->outstanding_taxes);
118 display_show_message(msg_title, greeting->str, 0);
119 g_string_free(greeting, TRUE);
120
121 return 2; /* turns */
122 }
123
124 gboolean leaving = FALSE;
125 while (!leaving)
126 {
127 GString *text = g_string_new(greeting->str);
128
129 g_string_append_printf(text,
130 "You have %d gold pieces in the bank.\n" \
131 "You have %d gold pieces.\n\n",
132 p->bank_account, player_get_gold(p));
133
134 g_string_append(text, "Your wish? ");
135
136 if (player_get_gold(p) > 0)
137 g_string_append(text, "`lightgreen`d`end`)eposit ");
138
139 if (p->bank_account > 0)
140 g_string_append(text, "`lightgreen`w`end`)ithdraw ");
141
142 /* if player has gems, enable selling them */
143 if (inv_length_filtered(p->inventory, item_filter_gems))
144 g_string_append(text, "`lightgreen`s`end`)ell a gem");
145
146 display_window *bwin = display_popup(COLS / 2 - 23, LINES / 2 - 3,
147 47, msg_title, text->str, 0);
148
149 g_string_free(text, TRUE);
150
151 int cmd = display_getch(bwin->window);
152
153 switch (cmd)
154 {
155 case 'd': /* deposit */
156 if (inv_length_filtered(p->inventory, item_filter_gold) == 0)
157 break;
158
159 amount = display_get_count("How many gold pieces do you wish to deposit?",
160 player_get_gold(p));
161
162 if (amount && (amount <= player_get_gold(p)))
163 {
164 p->bank_account += amount;
165 player_remove_gold(p, amount);
166 log_add_entry(nlarn->log, "You deposited %d gold.", amount);
167
168 /* income tax for money earned in the caverns */
169 p->outstanding_taxes += calc_tax_debt(amount);
170 }
171 else if (amount)
172 {
173 log_add_entry(nlarn->log, "You don't have that much.");
174 }
175
176 break;
177
178 case 'w': /* withdraw */
179 if (p->bank_account == 0)
180 break;
181
182 amount = display_get_count("How many gold pieces do you wish to withdraw?",
183 p->bank_account);
184
185 if (amount && (amount <= p->bank_account))
186 {
187 item *gold = item_new(IT_GOLD, amount);
188
189 /* adding the gold might fail (too heavy) */
190 if (inv_add(&p->inventory, gold))
191 {
192 p->bank_account -= amount;
193 log_add_entry(nlarn->log, "You withdraw %d gold.", amount);
194 }
195 else
196 {
197 /* this item is no longer required */
198 item_destroy(gold);
199 }
200 }
201 else if (amount)
202 {
203 log_add_entry(nlarn->log, "You don't have that much in the bank!");
204 }
205
206 break;
207
208 case 's': /* sell gem */
209 {
210 if (inv_length_filtered(p->inventory, item_filter_gems) == 0)
211 break;
212
213 /* define callback functions */
214 GPtrArray *callbacks = g_ptr_array_new();
215
216 display_inv_callback *callback = g_malloc(sizeof(display_inv_callback));
217 callback->description = "(s)ell";
218 callback->helpmsg = "Sell the currently selected gem.";
219 callback->key = 's';
220 callback->inv = &nlarn->store_stock;
221 callback->function = &building_item_buy;
222 callback->checkfun = &player_item_is_sellable;
223 callback->active = FALSE;
224 g_ptr_array_add(callbacks, callback);
225
226 display_inventory("Sell gems", p, &p->inventory, callbacks,
227 TRUE, FALSE, TRUE, &item_filter_gems);
228
229 display_inv_callbacks_clean(callbacks);
230 }
231 break;
232
233 case KEY_ESC:
234 leaving = TRUE;
235 break;
236
237 default:
238 /* do nothing */
239 break;
240 }
241
242 /* every interaction in the bank takes two turns */
243 player_make_move(p, 2, FALSE, NULL);
244 display_window_destroy(bwin);
245 }
246
247 g_string_free(greeting, TRUE);
248
249 return 0;
250 }
251
building_dndstore(player * p)252 int building_dndstore(player *p)
253 {
254 int turns = 2;
255 const char title[] = "DND store";
256 const char msg_welcome[] = "`white`Welcome to the Larn Thrift Shoppe.`end`\n\n" \
257 "We stock many items explorers find useful in " \
258 "their adventures. Feel free to browse to your " \
259 "heart's content. Also be advised that if you " \
260 "break 'em, you pay for 'em.";
261
262 g_assert(p != NULL);
263
264 /* no business if player has outstanding taxes */
265 if (p->outstanding_taxes)
266 {
267 display_show_message(title, msg_outstanding, 0);
268 return turns;
269 }
270
271 display_show_message(title, msg_welcome, 0);
272 building_shop(p, &nlarn->store_stock, title);
273
274 return turns;
275 }
276
building_dndstore_init()277 void building_dndstore_init()
278 {
279 for (item_t type = IT_AMULET; type < IT_MAX; type++)
280 {
281 /*never generate gems or gold */
282 if (type == IT_GEM || type == IT_GOLD)
283 continue;
284
285 for (guint id = 0; id < item_max_id(type); id++)
286 {
287 int count;
288
289 /* do not generate unobtainable items except in wizard mode */
290 if (!game_wizardmode(nlarn) && !item_obtainable(type, id))
291 continue;
292
293 switch (type)
294 {
295 case IT_AMMO:
296 count = 50;
297 break;
298
299 case IT_BOOK:
300 count = 1;
301 break;
302
303 case IT_SCROLL:
304 count = game_wizardmode(nlarn) ? 10 : scroll_type_store_stock(id);
305 break;
306
307 case IT_POTION:
308 count = game_wizardmode(nlarn) ? 10 : potion_type_store_stock(id);
309 break;
310
311 default:
312 {
313 if (item_is_stackable(type))
314 count = game_wizardmode(nlarn) ? 10 : 3;
315 else
316 count = 1;
317 }
318 }
319
320 item *it = item_new(type, id);
321
322 if (item_is_identifyable(it->type))
323 {
324 /* make item attributes known */
325 it->bonus_known = TRUE;
326 it->blessed_known = TRUE;
327 }
328 it->count = count;
329
330 /* add item to store */
331 inv_add(&nlarn->store_stock, it);
332 }
333 }
334 }
335
building_home(player * p)336 int building_home(player *p)
337 {
338 int turns = 2;
339 GString *text;
340
341 const char title[] = "Your home";
342
343 const char msg_home[] = "`white`Welcome home, %s.`end`\n\nLatest word from the doctor " \
344 "is not good. The diagnosis is confirmed as " \
345 "dianthroritis. He guesses that your daughter " \
346 "has only %d mobuls left in this world. It's " \
347 "up to you, %s, to find the only hope for your " \
348 "daughter, the very rare potion of cure " \
349 "dianthroritis. It is rumored that only deep in " \
350 "the depths of the caves can this potion be found.\n";
351
352 const char msg_found[] = "`white`Congratulations!`end` You found the potion of cure " \
353 "dianthroritis! Frankly, No one thought you " \
354 "could do it. Boy! Did you surprise them!\n\n";
355
356 const char msg_died[] = "The doctor has the sad duty to inform you that " \
357 "your daughter died before your return. There was " \
358 "nothing he could do without the potion.\n\n" \
359 "You didn't make it in time. Too bad...";
360
361 const char msg_won[] = "The doctor is now administering the potion and, in " \
362 "a few moments, your daughter should be well on " \
363 "her way to recovery.\n\nThe potion is working! " \
364 "The doctor thinks that your daughter will recover " \
365 "in a few days.\n\nCongratulations!";
366
367 g_assert(p != NULL);
368
369 /* look for potion of cure dianthroritis in player's inventory */
370 if (inv_length_filtered(p->inventory, item_filter_pcd))
371 {
372 /* carrying the potion */
373 text = g_string_new(msg_found);
374
375 if (game_turn(nlarn) < TIMELIMIT)
376 {
377 /* won the game */
378 g_string_append(text, msg_won);
379 display_show_message("You saved your daughter!", text->str, 0);
380 g_string_free(text, TRUE);
381
382 /* remove the potion from the inventory as it has been used up */
383 item *pcd = inv_get_filtered(p->inventory, 0, item_filter_pcd);
384 inv_del_element(&p->inventory, pcd);
385 item_destroy(pcd);
386
387 /* increase difficulty level */
388 config_increase_difficulty(nlarn_inifile, nlarn->difficulty + 1);
389
390 player_die(p, PD_WON, 0);
391 }
392 else
393 {
394 /* lost the game */
395 g_string_append(text, msg_died);
396 display_show_message("You were too late!", text->str, 0);
397 g_string_free(text, TRUE);
398
399 player_die(p, PD_TOO_LATE, 0);
400 }
401 }
402 else if (game_turn(nlarn) > TIMELIMIT)
403 {
404 /* too late, no potion */
405 text = g_string_new(msg_died);
406 display_show_message("You were too late!", text->str, 0);
407 g_string_free(text, TRUE);
408
409 player_die(p, PD_LOST, 0);
410 }
411 else
412 {
413 /* casual visit, report remaining time */
414
415 int choice;
416 GPtrArray *callbacks;
417 display_inv_callback *callback;
418
419 text = g_string_new(NULL);
420 g_string_printf(text, msg_home, p->name,
421 gtime2mobuls(game_remaining_turns(nlarn)),
422 p->name);
423
424 /* check if the player can deposit something
425 at home or has already done so */
426 if ((inv_length_filtered(p->inventory, player_item_not_equipped) > 0)
427 || (inv_length(nlarn->player_home) > 0))
428 {
429 g_string_append_printf(text, "\n\nYou may\n");
430
431 if (inv_length_filtered(p->inventory, player_item_not_equipped) > 0)
432 g_string_append_printf(text, " `lightgreen`d`end`) "
433 "Deposit something here\n");
434
435 if (inv_length(nlarn->player_home) > 0)
436 g_string_append_printf(text, " `lightgreen`t`end`) "
437 "Take something with you\n");
438
439 g_string_append_c(text, '\n');
440 }
441
442 choice = display_show_message(title, text->str, 0);
443 g_string_free(text, TRUE);
444
445 switch (choice)
446 {
447 /* deposit something */
448 case 'd':
449 if (inv_length_filtered(p->inventory, player_item_not_equipped) > 0)
450 {
451 /* prepare callback functions */
452 callbacks = g_ptr_array_new();
453
454 callback = g_malloc0(sizeof(display_inv_callback));
455 callback->description = "(d)eposit";
456 callback->helpmsg = "Deposit the selected item in "
457 "your storage room at home.";
458 callback->key = 'd';
459 callback->inv = &nlarn->player_home;
460 callback->function = &container_item_add;
461 callback->active = TRUE;
462
463 g_ptr_array_add(callbacks, callback);
464
465 display_inventory(title, p, &p->inventory, callbacks, FALSE,
466 TRUE, FALSE, player_item_not_equipped);
467
468 display_inv_callbacks_clean(callbacks);
469 }
470 break;
471
472 /* take something */
473 case 't':
474 if (inv_length(nlarn->player_home) > 0)
475 {
476 /* prepare callback functions */
477 callbacks = g_ptr_array_new();
478
479 callback = g_malloc0(sizeof(display_inv_callback));
480 callback->description = "(t)ake";
481 callback->helpmsg = "Take the selected item out of your "
482 "storage room and put it into your pack.";
483 callback->key = 't';
484 callback->inv = &nlarn->player_home;
485 callback->function = &container_item_unpack;
486 callback->active = TRUE;
487
488 g_ptr_array_add(callbacks, callback);
489
490 display_inventory(title, p, &nlarn->player_home, callbacks,
491 FALSE, TRUE, FALSE, NULL);
492
493 display_inv_callbacks_clean(callbacks);
494 }
495 break;
496 }
497 }
498
499 return turns;
500 }
501
building_lrs(player * p)502 int building_lrs(player *p)
503 {
504 int turns = 2;
505 GString *text;
506
507 const char title[] ="Larn Revenue Service";
508 const char msg_greet[] = "Welcome to the Larn Revenue Service district office.\n\n";
509 const char msg_taxes[] = "You presently owe %d gold in taxes.\n\n";
510 const char msg_notax[] = "You do not owe us any taxes.";
511
512 g_assert(p != NULL);
513
514 text = g_string_new(msg_greet);
515
516 if (p->outstanding_taxes)
517 {
518 g_string_append_printf(text, msg_taxes, p->outstanding_taxes);
519
520 /* offer to pay taxes if player can afford to */
521 if (building_player_check(p, p->outstanding_taxes))
522 {
523 g_string_append(text, "Do you want to pay your taxes?");
524
525 if (display_get_yesno(text->str, title, NULL, NULL))
526 {
527 building_player_charge(p, p->outstanding_taxes);
528 p->stats.gold_spent_taxes += p->outstanding_taxes;
529 p->outstanding_taxes = 0;
530 log_add_entry(nlarn->log, "You have paid your taxes.");
531 }
532 else
533 {
534 log_add_entry(nlarn->log, "You chose not to pay your taxes.");
535 }
536 }
537 else
538 {
539 g_string_append(text, "Unfortunately, it seems that you cannot "
540 "afford to pay your taxes at this time.");
541 display_show_message(title, text->str, 0);
542 }
543 }
544 else
545 {
546 g_string_append(text, msg_notax);
547 display_show_message(title, text->str, 0);
548 }
549
550 g_string_free(text, TRUE);
551
552 return turns;
553 }
554
building_scribe_scroll(player * p)555 static int building_scribe_scroll(player *p)
556 {
557 int price;
558 int turns = 2;
559 int i;
560 gboolean split = FALSE;
561 item *bscroll;
562 char question[81] = { 0 };
563
564 /* check if the player owns a blank scroll */
565 if (!inv_length_filtered(p->inventory, item_filter_blank_scroll))
566 {
567 display_show_message("School", "To write a scroll, the scribes "
568 "require a blank scroll.", 0);
569
570 return turns;
571 }
572
573 bscroll = display_inventory("Choose a scroll to inscribe", p,
574 &p->inventory, NULL, FALSE, FALSE,
575 FALSE, item_filter_blank_scroll);
576
577 if (!bscroll)
578 {
579 log_add_entry(nlarn->log, "Okay then.");
580 return turns;
581 }
582
583 char *new_scroll = display_get_string("School", "Write what scroll?",
584 NULL, 45);
585 if (new_scroll == NULL)
586 {
587 display_show_message("School", "Okay then.", 0);
588 return turns;
589 }
590
591 for (i = 0; i < ST_MAX; i++)
592 {
593 if (g_strcmp0(new_scroll, scrolls[i].name) == 0)
594 break;
595 }
596
597 /* free memory alloc'd by display_get_string */
598 g_free(new_scroll);
599
600 if (i == ST_MAX)
601 {
602 display_show_message("School", "The scribes haven't ever heard of any "
603 "such scroll!", 0);
604 return turns;
605 }
606
607 /* jesters might want to get a scroll of blank paper */
608 if (i == ST_BLANK)
609 {
610 display_show_message("School", "The scribes can only write something "
611 "written!", 0);
612 return turns;
613 }
614
615 /* player has chosen which scroll to write, check if (s)he can afford it */
616 price = 2 * scrolls[i].price;
617 if (!building_player_check(p, price))
618 {
619 char *msg = g_strdup_printf("You cannot afford the %d gold for the "
620 "scroll of %s.", price, scrolls[i].name);
621 display_show_message("School", msg, 0);
622 g_free(msg);
623
624 return turns;
625 }
626
627 g_snprintf(question, 80, "Writing a scroll of %s costs %d gold.\n"
628 "Are you fine with that?", scrolls[i].name, price);
629
630 if (!display_get_yesno(question, "School", NULL, NULL))
631 {
632 return turns;
633 }
634
635 /** Okay, we write the scroll. */
636
637 // If necessary, split a stack of scrolls.
638 if (bscroll->count > 1)
639 {
640 bscroll = item_split(bscroll, 1);
641 split = TRUE;
642 }
643
644 bscroll->id = i;
645 p->identified_scrolls[i] = TRUE;
646
647 building_player_charge(p, price);
648 p->stats.gold_spent_shop += price;
649
650 /* writing a scroll takes 10 mobuls */
651 player_make_move(p, 1000, FALSE, "waiting for the scribes to write a "
652 "scroll of %s for you", scroll_name(bscroll));
653
654 log_add_entry(nlarn->log,
655 "The scribes finished writing a scroll of %s for you.",
656 scroll_name(bscroll));
657
658 if (split)
659 inv_add(&p->inventory, bscroll);
660
661 return 0;
662 }
663
664 /* school courses */
665 typedef struct school_course {
666 int course_time;
667 int prerequisite;
668 const char *description;
669 const char *message;
670 } school_course;
671
672 static const school_course school_courses[SCHOOL_COURSE_COUNT] =
673 {
674 { 10, -1, "Fighters Training I", "You feel stronger!" },
675 { 15, 0, "Fighters Training II", "You feel much stronger!" },
676 { 10, -1, "Introduction to Wizardry", "The task before you now seems more attainable!" },
677 { 20, 2, "Applied Wizardry", "The task before you now seems very attainable!" },
678 { 10, -1, "Faith for Today", "You now feel more confident that you can find the potion in time!" },
679 { 10, -1, "Contemporary Dance", "You feel like dancing!" },
680 { 5, -1, "History of Larn", "Your instructor told you that the Eye of Larn is rumored to be guarded by an invisible demon lord." },
681 };
682
building_school_take_course(player * p,int course,guint price)683 static void building_school_take_course(player *p, int course, guint price)
684 {
685
686 /* charge the player */
687 building_player_charge(p, price);
688 p->stats.gold_spent_college += price;
689
690 /* time usage */
691 guint course_turns = mobuls2gtime(school_courses[course].course_time);
692 player_make_move(p, course_turns, FALSE, "taking the course \"%s\"",
693 school_courses[course].description);
694
695 /* add the bonus gained by this course */
696 switch (course)
697 {
698 case 0:
699 p->strength += 2;
700 p->constitution++;
701 /* strength has been modified -> recalc burdened status */
702 player_inv_weight_recalc(p->inventory, NULL);
703 break;
704
705 case 1:
706 p->strength += 2;
707 p->constitution += 2;
708 /* strength has been modified -> recalc burdened status */
709 player_inv_weight_recalc(p->inventory, NULL);
710 break;
711
712 case 2:
713 p->intelligence += 2;
714 break;
715
716 case 3:
717 p->intelligence += 2;
718 break;
719
720 case 4:
721 p->wisdom += 2;
722 break;
723
724 case 5:
725 p->dexterity += 3;
726 break;
727
728 case 6:
729 p->intelligence++;
730 break;
731 }
732
733 /* mark the course as taken */
734 p->school_courses_taken[course] = 1;
735
736 log_add_entry(nlarn->log,
737 "You successfully complete the course \"%s\". %s",
738 school_courses[course].description,
739 school_courses[course].message);
740 }
741
building_school(player * p)742 int building_school(player *p)
743 {
744 const char msg_greet[] = "`white`Welcome to the College of Larn!`end`\n\n" \
745 "We offer the exciting opportunity of higher " \
746 "education to all inhabitants of the caves. " \
747 "Here is a list of the class schedule:\n\n";
748
749 const char msg_prerequisite[] = "Sorry, but this class has a prerequisite of \"%s\".";
750
751 g_assert(p != NULL);
752
753 gboolean leaving = FALSE;
754 while (!leaving)
755 {
756 GString *text = g_string_new(msg_greet);
757
758 for (int idx = 0; idx < SCHOOL_COURSE_COUNT; idx++)
759 {
760 if (!p->school_courses_taken[idx])
761 {
762 /* courses become more expensive with rising difficulty */
763 guint price = school_courses[idx].course_time
764 * (game_difficulty(nlarn) + 1) * 100;
765
766 g_string_append_printf(text,
767 " `lightgreen`%c`end`) %-24s - %2d mobuls, %4d gold\n",
768 idx + 'a', school_courses[idx].description,
769 school_courses[idx].course_time, price);
770 }
771 else
772 {
773 g_string_append(text, "\n");
774 }
775 }
776
777 g_string_append_printf(text, "\nAlternatively,\n"
778 " `lightgreen`%c`end`) %-24s - 10 mobuls\n\n",
779 SCHOOL_COURSE_COUNT + 'a', "Commission a scroll");
780
781 int selection = display_show_message("School", text->str, 0);
782 g_string_free(text, TRUE);
783
784 switch (selection)
785 {
786 case 'a' + SCHOOL_COURSE_COUNT:
787 player_make_move(p, building_scribe_scroll(p), FALSE, NULL);
788 break;
789
790 case KEY_ESC:
791 leaving = TRUE;
792 break;
793
794 default:
795 {
796 int course = selection - 'a';
797 if ((course < 0) || (course > SCHOOL_COURSE_COUNT)
798 || p->school_courses_taken[course])
799 {
800 /* invalid course or course already taken */
801 break;
802 }
803
804 /* course prices increase with rising difficulty */
805 guint price = school_courses[course].course_time
806 * (game_difficulty(nlarn) + 1) * 100;
807
808 if (!building_player_check(p, price))
809 {
810 char *msg = g_strdup_printf("You cannot afford "
811 "the %d gold for the course.", price);
812 display_show_message("School", msg, 0);
813 g_free(msg);
814 }
815 /* check if the selected course has a prerequisite
816 and if the player has taken that course */
817 else if ((school_courses[course].prerequisite >= 0) &&
818 !p->school_courses_taken[school_courses[course].prerequisite])
819 {
820 char *msg = g_strdup_printf(msg_prerequisite,
821 school_courses[school_courses[course].prerequisite]
822 .description);
823 display_show_message("School", msg, 0);
824 g_free(msg);
825 }
826 else
827 {
828 building_school_take_course(p, course, price);
829 }
830 }
831 }
832
833 }
834
835 return 0;
836 }
837
building_tradepost(player * p)838 int building_tradepost(player *p)
839 {
840 int turns = 2;
841 GPtrArray *callbacks;
842 display_inv_callback *callback;
843
844 const char title[] = "Trade Post";
845
846 const char msg_welcome[] = "`white`Welcome to the Larn Trading Post.`end`\n\nWe buy " \
847 "items that explorers no longer find useful.\n" \
848 "Since the condition of the items you bring in " \
849 "is not certain, and we incur great expense in " \
850 "reconditioning the items, we usually pay only " \
851 "20% of their value were they to be new.\n" \
852 "If the items are badly damaged, we will pay " \
853 "only 10% of their new value.";
854
855 g_assert(p != NULL);
856
857 /* no business if player has outstanding taxes */
858 if (p->outstanding_taxes)
859 {
860 display_show_message(title, msg_outstanding, 0);
861 return turns;
862 }
863
864 /* define callback functions */
865 callbacks = g_ptr_array_new();
866
867 callback = g_malloc(sizeof(display_inv_callback));
868 callback->description = "(s)ell";
869 callback->helpmsg = "Sell the selected item to the Trade Post.";
870 callback->key = 's';
871 callback->inv = &nlarn->store_stock;
872 callback->function = &building_item_buy;
873 callback->checkfun = &player_item_is_sellable;
874 callback->active = FALSE;
875 g_ptr_array_add(callbacks, callback);
876
877 callback = g_malloc(sizeof(display_inv_callback));
878 callback->description = "(i)dentify";
879 callback->helpmsg = "Have the Trade Post's experts identify your item.";
880 callback->key = 'i';
881 callback->function = &building_item_identify;
882 callback->checkfun = &player_item_is_identifiable;
883 callback->active = FALSE;
884 g_ptr_array_add(callbacks, callback);
885
886 callback = g_malloc(sizeof(display_inv_callback));
887 callback->description = "(r)epair";
888 callback->helpmsg = "Have the Trade Post's experts revert any damage done to the selected item.";
889 callback->key = 'r';
890 callback->function = &building_item_repair;
891 callback->checkfun = &player_item_is_damaged;
892 callback->active = FALSE;
893 g_ptr_array_add(callbacks, callback);
894
895 callback = g_malloc0(sizeof(display_inv_callback));
896 callback->description = "(e)quip";
897 callback->helpmsg = "Equip the selected item.";
898 callback->key = 'e';
899 callback->function = &player_item_equip;
900 callback->checkfun = &player_item_is_equippable;
901 g_ptr_array_add(callbacks, callback);
902
903 callback = g_malloc(sizeof(display_inv_callback));
904 callback->description = "(u)nequip";
905 callback->helpmsg = "Unequip the selected item.";
906 callback->key = 'u';
907 callback->function = &player_item_unequip_wrapper;
908 callback->checkfun = &player_item_is_unequippable;
909 callback->active = FALSE;
910 g_ptr_array_add(callbacks, callback);
911
912 callback = g_malloc(sizeof(display_inv_callback));
913 callback->description = "(n)ote";
914 callback->helpmsg = "Add a note to the item.";
915 callback->key = 'n';
916 callback->function = &player_item_notes;
917 callback->checkfun = NULL;
918 callback->active = FALSE;
919 g_ptr_array_add(callbacks, callback);
920
921 display_show_message(title, msg_welcome, 0);
922 display_inventory(title, p, &p->inventory, callbacks, FALSE,
923 FALSE, TRUE, &item_filter_not_gold);
924
925 /* clean up */
926 display_inv_callbacks_clean(callbacks);
927
928 return turns;
929 }
930
building_monastery(struct player * p)931 int building_monastery(struct player *p)
932 {
933 const char title[] = "The Monastery of Larn";
934 const char msg_welcome[] = "`white`Welcome to the Monastery of Larn!`end`\n\n" \
935 "We are here to help you when you are in need of " \
936 "care and offer a fine selection of items that might "
937 "be useful for your quests.\n\n" \
938 "Here you may\n\n" \
939 " `lightgreen`a`end`) buy something\n" \
940 " `lightgreen`b`end`) ask for curse removal\n" \
941 " `lightgreen`c`end`) recieve healing\n";
942
943 const char ayfwt[] = "Are you fine with that?";
944
945 gboolean leaving = FALSE;
946
947 while (!leaving)
948 {
949 GString *msg = g_string_new(msg_welcome);
950 int disease_count = 0;
951
952 /* buffer to store all diseases the player currently suffers from */
953 struct
954 {
955 effect_t et;
956 const char *desc;
957 } curable_diseases[10] = { { 0, NULL } };
958
959 /* fill the list of curable diseases */
960 if (player_effect(p, ET_POISON))
961 {
962 curable_diseases[disease_count].et = ET_POISON;
963 curable_diseases[disease_count++].desc = "poison";
964 }
965
966 if (player_effect(p, ET_BLINDNESS))
967 {
968 curable_diseases[disease_count].et = ET_BLINDNESS;
969 curable_diseases[disease_count++].desc = "blindness";
970 }
971
972 if (player_effect(p, ET_SICKNESS))
973 {
974 curable_diseases[disease_count].et = ET_SICKNESS;
975 curable_diseases[disease_count++].desc = "sickness";
976 }
977
978 if (player_effect(p, ET_CLUMSINESS))
979 {
980 curable_diseases[disease_count].et = ET_CLUMSINESS;
981 curable_diseases[disease_count++].desc = "clumsiness";
982 }
983
984 if (player_effect(p, ET_DIZZINESS))
985 {
986 curable_diseases[disease_count].et = ET_DIZZINESS;
987 curable_diseases[disease_count++].desc = "dizziness";
988 }
989
990 if (player_effect(p, ET_DEC_CON))
991 {
992 curable_diseases[disease_count].et = ET_DEC_CON;
993 curable_diseases[disease_count++].desc = "incapacitation";
994 }
995
996 if (player_effect(p, ET_DEC_DEX))
997 {
998 curable_diseases[disease_count].et = ET_DEC_DEX;
999 curable_diseases[disease_count++].desc = "awkwardness";
1000 }
1001
1002 if (player_effect(p, ET_DEC_INT))
1003 {
1004 curable_diseases[disease_count].et = ET_DEC_INT;
1005 curable_diseases[disease_count++].desc = "mental deficiency";
1006 }
1007
1008 if (player_effect(p, ET_DEC_STR))
1009 {
1010 curable_diseases[disease_count].et = ET_DEC_STR;
1011 curable_diseases[disease_count++].desc = "weakness";
1012 }
1013
1014 if (player_effect(p, ET_DEC_WIS))
1015 {
1016 curable_diseases[disease_count].et = ET_DEC_WIS;
1017 curable_diseases[disease_count++].desc = "ignorance";
1018 }
1019
1020 /* add found diseases to the menu */
1021 for (int idx = 0; idx < disease_count; idx++)
1022 {
1023 g_string_append_printf(msg, " `lightgreen`%c`end`) heal from %s\n",
1024 'd' + idx, curable_diseases[idx].desc);
1025 }
1026
1027 /* an empty line for the eye */
1028 g_string_append_c(msg, '\n');
1029
1030 /* offer the choices to the player */
1031 display_window *mwin = display_popup(COLS / 2 - 23, LINES / 2 - 3,
1032 47, title, msg->str, 0);
1033
1034 int selection = display_getch(mwin->window);
1035
1036 /* get rid of the temporary string */
1037 g_string_free(msg, TRUE);
1038
1039 switch (selection)
1040 {
1041 /* shop items */
1042 case 'a':
1043 building_shop(p, &nlarn->monastery_stock, title);
1044 break;
1045
1046 /* remove curse */
1047 case 'b':
1048 {
1049 item *it;
1050 int price;
1051 int choice;
1052 char *question;
1053 gchar *desc;
1054
1055 if (inv_length_filtered(p->inventory, item_filter_cursed_or_unknown) == 0)
1056 {
1057 display_show_message(title, "You do not possess any cursed item.", 0);
1058 break;
1059 }
1060
1061 it = display_inventory("Choose an item to uncurse", p, &p->inventory,
1062 NULL, FALSE, FALSE, FALSE,item_filter_cursed_or_unknown);
1063
1064 /* It is possible to abort the selection with ESC. */
1065 if (it == NULL)
1066 {
1067 display_show_message(title, "You chose to leave your items as "
1068 "they are.", 0);
1069 break;
1070 }
1071
1072 /* The cost of uncursing is 10 percent of item value.
1073 The item value for cursed items is reduced by 50%,
1074 hence divide by 5 */
1075 price = (item_price(it) / 5) * (game_difficulty(nlarn) + 1);
1076 /* some items are too cheap. */
1077 price = max(price, 1);
1078 /* Item stacks cost per item. */
1079 price *= it->count;
1080
1081 desc = item_describe(it, player_item_identified(p, it), FALSE, TRUE);
1082 question = g_strdup_printf("To remove the curse on %s, we ask you to "
1083 "donate %d gold for our abbey. %s", desc,
1084 price, ayfwt);
1085
1086 choice = display_get_yesno(question, NULL, NULL, NULL);
1087 g_free(question);
1088
1089 if (!choice)
1090 {
1091 char *msg = g_strdup_printf("You chose leave the curse on %s.", desc);
1092 display_show_message(title, msg, 0);
1093 g_free(msg);
1094 break;
1095 }
1096
1097 /* The player may chose to uncurse items that are actually only of
1098 unknown blessedness. */
1099 if (it->cursed)
1100 {
1101 char *msg = g_strdup_printf("The monks remove the curse on %s.", desc);
1102 display_show_message(title, msg, 0);
1103 g_free(msg);
1104 item_remove_curse(it);
1105 }
1106 else
1107 {
1108 char *msg = g_strdup_printf("The monks tell you that %s %sn't "
1109 "cursed. Well, now you know for sure...", desc,
1110 (it->count == 1) ? "was" : "were");
1111 display_show_message(title, msg, 0);
1112 g_free(msg);
1113 it->blessed_known = TRUE;
1114 }
1115 g_free(desc);
1116
1117 building_player_charge(p, price);
1118 p->stats.gold_spent_donation += price;
1119 }
1120 break;
1121
1122 /* healing */
1123 case 'c':
1124 {
1125 int choice;
1126 char *question;
1127 /* the price for healing depends on the severity of the injury */
1128 int price = (player_get_hp_max(p) - p->hp) * (game_difficulty(nlarn) + 1);
1129
1130 if (p->hp == player_get_hp_max(p))
1131 {
1132 display_show_message(title, "You are not in need of healing.", 0);
1133 break;
1134 }
1135
1136 question = g_strdup_printf("For healing you, we ask that you "
1137 "donate %d gold for our monastery. %s",
1138 price, ayfwt);
1139 choice = display_get_yesno(question, NULL, NULL, NULL);
1140 g_free(question);
1141
1142 if (choice)
1143 {
1144 /* player chose to be healed */
1145 player_effect_add(p, effect_new(ET_MAX_HP));
1146 building_player_charge(p, price);
1147 p->stats.gold_spent_donation += price;
1148
1149 }
1150 else
1151 {
1152 /* no, thanks */
1153 display_show_message(title, "You chose not to be healed.", 0);
1154 }
1155 }
1156 break;
1157
1158 /* leave the monastery */
1159 case KEY_ESC:
1160 leaving = TRUE;
1161 break;
1162
1163 default:
1164 selection -= 'd';
1165 /* healing of varous negative effects */
1166 if (selection >= 0 && selection < disease_count)
1167 {
1168 int choice;
1169 char *question;
1170 effect *e = player_effect_get(p, curable_diseases[selection].et);
1171 int price = e->turns * (game_difficulty(nlarn) + 1);
1172
1173 question = g_strdup_printf("For healing you from %s, we ask that you "
1174 "donate %d gold for our monastery. %s",
1175 curable_diseases[selection].desc, price, ayfwt);
1176
1177 choice = display_get_yesno(question, NULL, NULL, NULL);
1178 g_free(question);
1179
1180 if (choice)
1181 {
1182 /* player chose to be healed */
1183 player_effect_del(p, e);
1184 building_player_charge(p, price);
1185 p->stats.gold_spent_donation += price;
1186
1187 }
1188 else
1189 {
1190 /* no, thanks */
1191 char *msg = g_strdup_printf("You chose not to be cured from %s.",
1192 curable_diseases[selection].desc);
1193 display_show_message(title, msg, 0);
1194 g_free(msg);
1195 }
1196
1197 }
1198 break;
1199 }
1200
1201 /* track time usage */
1202 player_make_move(p, 2, FALSE, NULL);
1203
1204 display_window_destroy(mwin);
1205 }
1206
1207 return 0;
1208 }
1209
building_monastery_init()1210 void building_monastery_init()
1211 {
1212 int i = 0;
1213
1214 /* the list of items available in the monastery */
1215 struct
1216 {
1217 item_t type;
1218 int id;
1219 int count;
1220 } igen[] =
1221 {
1222 { IT_POTION, PO_HEAL, 5 },
1223 { IT_POTION, PO_MAX_HP, 1 },
1224 { IT_POTION, PO_RECOVERY, 5 },
1225 { IT_POTION, PO_WATER, 1 },
1226 { IT_SCROLL, ST_REMOVE_CURSE, 3 },
1227 { IT_NONE, 0, 0 },
1228 };
1229
1230 while (igen[i].type != IT_NONE)
1231 {
1232 for (int j = 0; j < igen[i].count; j++)
1233 {
1234 item *nitem = item_new(igen[i].type, igen[i].id);
1235 inv_add(&nlarn->monastery_stock, nitem);
1236 }
1237
1238 i++;
1239 };
1240 }
1241
building_shop(player * p,inventory ** inv,const char * title)1242 static void building_shop(player *p, inventory **inv, const char *title)
1243 {
1244 GPtrArray *callbacks;
1245 display_inv_callback *callback;
1246
1247 if (inv_length(*inv) == 0)
1248 {
1249 log_add_entry(nlarn->log, "Unfortunately we are sold out.");
1250 return;
1251 }
1252
1253 /* define callback functions */
1254 callbacks = g_ptr_array_new();
1255
1256 callback = g_malloc(sizeof(display_inv_callback));
1257 callback->description = "(b)uy";
1258 callback->helpmsg = "Buy the selected item. If the available quantity exceeds one, you may select the amount you want to purchase.";
1259 callback->key = 'b';
1260 callback->inv = inv;
1261 callback->function = &building_item_sell;
1262 callback->checkfun = &player_item_is_affordable;
1263 callback->active = FALSE;
1264 g_ptr_array_add(callbacks, callback);
1265
1266 display_inventory(title, p, inv, callbacks, TRUE,
1267 FALSE, TRUE, NULL);
1268
1269 /* clean up */
1270 display_inv_callbacks_clean(callbacks);
1271 }
1272
building_player_check(player * p,guint amount)1273 static int building_player_check(player *p, guint amount)
1274 {
1275 guint player_gold = player_get_gold(p);
1276
1277 if (player_gold >= amount)
1278 return TRUE;
1279
1280 if (p->bank_account >= amount)
1281 return TRUE;
1282
1283 return FALSE;
1284 }
1285
building_player_charge(player * p,guint amount)1286 static void building_player_charge(player *p, guint amount)
1287 {
1288 if (p->bank_account >= amount)
1289 {
1290 p->bank_account -= amount;
1291 log_add_entry(nlarn->log, "We have debited %d gold from your bank "
1292 "account.", amount);
1293 }
1294 else
1295 {
1296 player_remove_gold(p, amount);
1297 }
1298 }
1299
1300 /**
1301 *
1302 * add an item to the dnd store
1303 *
1304 * either refurbish and put the item into the store's inventory
1305 * or destroy it if it is a gem.
1306 *
1307 */
building_item_add(inventory ** inv,item * it)1308 static void building_item_add(inventory **inv, item *it)
1309 {
1310 if (it->type == IT_GEM
1311 || (it->type == IT_WEAPON && !weapon_is_unique(it)))
1312 {
1313 /* gems and non-unique weapons do not appear in the store */
1314 item_destroy(it);
1315 }
1316 else
1317 {
1318 /* refurbish item before putting it into the store */
1319 if (it->cursed)
1320 it->cursed = FALSE;
1321
1322 if (it->rusty)
1323 it->rusty = FALSE;
1324
1325 if (it->burnt)
1326 it->burnt = FALSE;
1327
1328 if (it->corroded)
1329 it->corroded = FALSE;
1330
1331 if (it->bonus < 0)
1332 it->bonus = 0;
1333
1334 /* identify item */
1335 it->bonus_known = TRUE;
1336 it->blessed_known = TRUE;
1337
1338 /* remove notes */
1339 if (it->notes)
1340 {
1341 g_free(it->notes);
1342 it->notes = NULL;
1343 }
1344
1345 inv_add(inv, it);
1346 }
1347 }
1348
building_item_sell(player * p,inventory ** inv,item * it)1349 static void building_item_sell(player *p, inventory **inv, item *it)
1350 {
1351 guint price;
1352 gpointer ioid = NULL; /* oid of purchased item */
1353 char text[81];
1354 gchar *name;
1355
1356 /* The item the player actually bought. Usually this is the passed
1357 * item, in the case of item stacks it might be a new item. */
1358 item *bought_itm = it;
1359
1360 g_assert(p != NULL && it != NULL && it->type > IT_NONE && it->type < IT_MAX);
1361
1362 price = item_price(it);
1363
1364 if (it->count > 1)
1365 {
1366 g_snprintf(text, 80, "How many %s do you want to buy?",
1367 it->type == IT_AMMO ? ammo_name(it) : item_name_pl(it->type));
1368
1369 /* get count */
1370 guint count = display_get_count(text, it->count);
1371
1372 if (count > it->count)
1373 {
1374 /* desired amount is larger than the available amount */
1375 log_add_entry(nlarn->log, "Wouldn't it be nice if the store "
1376 "had %d of those?", count);
1377 return;
1378 }
1379 else if (count == 0)
1380 {
1381 /* not buying anything */
1382 return;
1383 }
1384 else if (count < it->count)
1385 {
1386 /* player wants part of the stock */
1387 bought_itm = item_split(it, count);
1388 }
1389
1390 price *= count;
1391
1392 if (!building_player_check(p, price))
1393 {
1394 name = item_describe(bought_itm, TRUE, FALSE, TRUE);
1395 g_snprintf(text, 80, "You cannot afford the %d gold for %s.",
1396 price, name);
1397 g_free(name);
1398
1399 display_show_message(NULL, text, 0);
1400
1401 /* if the item has been split add it to the shop */
1402 if (it != bought_itm) inv_add(inv, bought_itm);
1403
1404 return;
1405 }
1406 }
1407 else
1408 {
1409 name = item_describe(it, TRUE, TRUE, TRUE);
1410 g_snprintf(text, 80, "Do you want to buy %s for %d gold?",
1411 name, price);
1412 g_free(name);
1413
1414 if (!display_get_yesno(text, NULL, NULL, NULL))
1415 return;
1416 }
1417
1418 /* prepare the item description for logging later */
1419 name = item_describe(bought_itm, TRUE, FALSE, FALSE);
1420
1421 /* try to transfer the item to the player's inventory */
1422 ioid = bought_itm->oid;
1423
1424 if (inv_add(&p->inventory, bought_itm))
1425 {
1426 /* the item has been added to player's inventory */
1427 if (it == bought_itm)
1428 {
1429 /* remove the item from the shop as the player has bought
1430 the entire stock. this has to be done by the oid as it_clone
1431 may have been destroyed if it was a stackable item. */
1432 inv_del_oid(inv, ioid);
1433 }
1434
1435 p->stats.items_bought += bought_itm->count;
1436 p->stats.gold_spent_shop += price;
1437
1438 /* identify the item */
1439 player_item_identify(p, &p->inventory, bought_itm);
1440 }
1441 else
1442 {
1443 /* item has not been added to player's inventory */
1444 /* if the item has been split, return it to the shop */
1445 if (it != bought_itm) inv_add(inv, bought_itm);
1446 return;
1447 }
1448
1449 /* log the event */
1450 log_add_entry(nlarn->log, "You buy %s. Thank you for your purchase.", name);
1451 g_free(name);
1452
1453 /* charge player for this purchase */
1454 building_player_charge(p, price);
1455
1456 player_make_move(p, 2, FALSE, NULL);
1457 }
1458
building_item_identify(player * p,inventory ** inv,item * it)1459 static void building_item_identify(player *p, inventory **inv __attribute__((unused)), item *it)
1460 {
1461 guint price;
1462 gchar *name_unknown;
1463 char message[81];
1464
1465 const char title[] = "Identify item";
1466
1467 g_assert(p != NULL && it != NULL && it->type > IT_NONE && it->type < IT_MAX);
1468
1469 /* The cost for identification is 10% of the item's base price. */
1470 price = (item_base_price(it) / 10) * (game_difficulty(nlarn) + 1);
1471
1472 /* Ensure it costs at least 50gp... */
1473 price = max(50, price);
1474
1475 name_unknown = item_describe(it, player_item_known(p, it), FALSE, TRUE);
1476
1477 if (building_player_check(p, price))
1478 {
1479 g_snprintf(message, 80, "Pay %d gold to identify %s?",
1480 price, name_unknown);
1481
1482 if (display_get_yesno(message, NULL, NULL, NULL))
1483 {
1484 player_item_identify(p, NULL, it);
1485 /* upper case first letter */
1486 name_unknown[0] = g_ascii_toupper(name_unknown[0]);
1487 gchar *name_known = item_describe(it, player_item_known(p, it), TRUE, FALSE);
1488
1489 log_add_entry(nlarn->log, "%s is %s.", name_unknown, name_known);
1490 g_free(name_known);
1491
1492 building_player_charge(p, price);
1493 p->stats.gold_spent_id_repair += price;
1494 player_make_move(p, 1, FALSE, NULL);
1495 }
1496 }
1497 else
1498 {
1499 g_snprintf(message, 80, "Identifying %s costs %d gold.",
1500 name_unknown, price);
1501 display_show_message(title, message, 0);
1502 }
1503
1504 g_free(name_unknown);
1505 }
1506
building_item_repair(player * p,inventory ** inv,item * it)1507 static void building_item_repair(player *p, inventory **inv __attribute__((unused)), item *it)
1508 {
1509 int damages = 0;
1510 guint price;
1511 gchar *name;
1512 char message[81];
1513
1514 const char title[] = "Repair item";
1515
1516 g_assert(p != NULL && it != NULL && it->type > IT_NONE && it->type < IT_MAX);
1517
1518 /* determine how much the item is damaged */
1519 damages += it->burnt;
1520 damages += it->corroded;
1521 damages += it->rusty;
1522
1523 /* The cost of repairing an item is 10% of the item's base price. */
1524 price = (item_base_price(it) / 10) * (game_difficulty(nlarn) + 1);
1525
1526 /* Take the level of damage into account. */
1527 price *= damages;
1528
1529 /* Charge repair of item stacks depending on the quantity. */
1530 if (item_is_stackable(it->type)) price *= it->count;
1531
1532 /* If an item is blessed and the player know it, use this and charge :) */
1533 if (it->blessed && it->blessed_known) price <<=1;
1534
1535 /* Ensure this costs at least 50gp... */
1536 price = max(50, price);
1537
1538 name = item_describe(it, player_item_known(p, it), FALSE, TRUE);
1539
1540 if (building_player_check(p, price))
1541 {
1542 g_snprintf(message, 80, "Pay %d gold to repair %s?", price, name);
1543
1544 if (display_get_yesno(message, NULL, NULL, NULL))
1545 {
1546 it->burnt = 0;
1547 it->corroded = 0;
1548 it->rusty = 0;
1549
1550 name[0] = g_ascii_toupper(name[0]);
1551 log_add_entry(nlarn->log, "%s has been repaired.", name);
1552 building_player_charge(p, price);
1553
1554 p->stats.gold_spent_id_repair += price;
1555 player_make_move(p, 1, FALSE, NULL);
1556 }
1557 }
1558 else
1559 {
1560 g_snprintf(message, 80, "Repairing the %s costs %d gold.", name, price);
1561 display_show_message(title, message, 0);
1562 }
1563
1564 g_free(name);
1565 }
1566
building_item_buy(player * p,inventory ** inv,item * it)1567 static void building_item_buy(player *p, inventory **inv, item *it)
1568 {
1569 int price;
1570 guint count = 0;
1571 char question[121];
1572 gchar *name;
1573
1574 g_assert(p != NULL && it != NULL && it->type > IT_NONE && it->type < IT_MAX);
1575
1576 price = item_price(it);
1577
1578 /* modify price if player sells stuff at the trading post */
1579 if (map_sobject_at(game_map(nlarn, Z(p->pos)), p->pos) == LS_TRADEPOST)
1580 {
1581 if (!player_item_is_damaged(p, it))
1582 {
1583 /* good items: 20% of value */
1584 price /= 5;
1585 }
1586 else
1587 {
1588 /* damaged items: 10% of value */
1589 price /= 10;
1590 }
1591 }
1592
1593 if (price < 1)
1594 price = 1;
1595
1596 if (it->count > 1)
1597 {
1598 g_snprintf(question, 120, "How many %s do you want to sell for %d gold?",
1599 it->type == IT_AMMO ? ammo_name(it) : item_name_pl(it->type), price);
1600
1601 /* get count */
1602 count = display_get_count(question, it->count);
1603
1604 if (count > it->count)
1605 {
1606 log_add_entry(nlarn->log, "Wouldn't it be nice to have %d of those?", count);
1607 return;
1608 }
1609
1610 if (count == 0)
1611 return;
1612
1613 price *= count;
1614 }
1615 else
1616 {
1617 count = 1;
1618 name = item_describe(it, player_item_known(p, it), TRUE, TRUE);
1619 g_snprintf(question, 120, "Do you want to sell %s for %d gold?",
1620 name, price);
1621
1622 g_free(name);
1623
1624 if (!display_get_yesno(question, NULL, NULL, NULL))
1625 return;
1626 }
1627
1628 /* put the money on the player's bank account */
1629 p->bank_account += price;
1630 /* and charge income taxes... */
1631 p->outstanding_taxes += calc_tax_debt(price);
1632
1633 guint count_orig = it->count;
1634 it->count = count;
1635
1636 name = item_describe(it, player_item_known(p, it), FALSE, FALSE);
1637 log_add_entry(nlarn->log, "You sell %s. The %d gold %s been "
1638 "transferred to your bank account.",
1639 name, price, (price == 1) ? "has" : "have");
1640
1641 g_free(name);
1642 it->count = count_orig;
1643
1644 if (it->type == IT_GEM)
1645 {
1646 p->stats.gems_sold += count;
1647 p->stats.gold_sold_gems += price;
1648 }
1649 else
1650 {
1651 p->stats.items_sold += count;
1652 p->stats.gold_sold_items += price;
1653 }
1654
1655 if ((it->count > 1) && (count < it->count))
1656 {
1657 building_item_add(inv, item_split(it, count));
1658 }
1659 else
1660 {
1661 if (!inv_del_element(&p->inventory, it))
1662 {
1663 return;
1664 }
1665 else
1666 {
1667 building_item_add(inv, it);
1668 }
1669 }
1670
1671 player_make_move(p, 1, FALSE, NULL);
1672 }
1673
calc_tax_debt(guint income)1674 static guint calc_tax_debt(guint income)
1675 {
1676 /* charge 5% income tax per difficulty level */
1677 return (income * min(game_difficulty(nlarn), 4) / 20);
1678 }
1679