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