root/trunk/src/birth.c

Revision 987, 40.1 kB (checked in by takkaria, 1 month ago)
  • Remove a pointless variable
  • Make all torches always start with FUEL_TORCH fuel, as intended
  • Add birth_money option, which gives starting money instead of equipment
  • Property svn:eol-style set to native
Line 
1 /*
2  * File: birth.c
3  * Purpose: Character creation
4  *
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6  *
7  * This work is free software; you can redistribute it and/or modify it
8  * under the terms of either:
9  *
10  * a) the GNU General Public License as published by the Free Software
11  *    Foundation, version 2, or
12  *
13  * b) the "Angband licence":
14  *    This software may be copied and distributed for educational, research,
15  *    and not for profit purposes provided that this copyright and statement
16  *    are included in all such copies.  Other copyrights may also apply.
17  */
18 #include "angband.h"
19 #include "tvalsval.h"
20 #include "cmds.h"
21 #include "game-event.h"
22 #include "game-cmd.h"
23 #include "ui-menu.h"
24
25 /*
26  * Overview
27  * ========
28  * This file contains the game-mechanical part of the birth process.
29  * To follow the code, start at player_birth towards the bottom of
30  * the file - that is the only external entry point to the functions
31  * defined here.
32  *
33  * Player (in the Angband sense of character) birth is a series of
34  * steps which must be carried out in a specified order, with choices
35  * in one step affecting those further along (e.g. race and class
36  * choices determine maximum values for stats in the autoroller).
37  *
38  * We implement this through a system of "birth stages".  As the
39  * game enters each birth stage, we typically do some minor
40  * housekeeping before sending a signal to the UI so that it can update
41  * its display, or do whatever else it deems fit.  Then we send other
42  * messages (such as updates of the progress of the autoroller) or
43  * request a command (such as buying a stat, or stepping back in the
44  * process).  This file simply responds to such commands by stepping
45  * back and forth through the birth sequence, updating values, and so on,
46  * until a suitable character has been chosen.  It then does more
47  * housekeeping to ensure the "player" is ready to start the game
48  * (clearing the history log, making sure options are set, etc) before
49  * returning contrl to the game proper.
50  */
51
52
53 /*
54  * Forward declare
55  */
56 typedef struct birther /*lovely*/ birther; /*sometimes we think she's a dream*/
57
58 /*
59  * A structure to hold "rolled" information, and any
60  * other useful state for the birth process.
61  */
62 struct birther
63 {
64         byte sex;
65         byte race;
66         byte class;
67
68         s16b age;
69         s16b wt;
70         s16b ht;
71         s16b sc;
72
73         s32b au;
74
75         s16b stat[A_MAX];
76
77         char history[250];
78 };
79
80
81
82 /*
83  * Save the currently rolled data into the supplied 'player'.
84  */
85 static void save_roller_data(birther *player)
86 {
87         int i;
88
89         /* Save the data */
90         player->sex = p_ptr->psex;
91         player->race = p_ptr->prace;
92         player->class = p_ptr->pclass;
93         player->age = p_ptr->age;
94         player->wt = p_ptr->wt_birth;
95         player->ht = p_ptr->ht_birth;
96         player->sc = p_ptr->sc;
97         player->au = p_ptr->au_birth;
98
99         /* Save the stats */
100         for (i = 0; i < A_MAX; i++)
101         {
102                 player->stat[i] = p_ptr->stat_birth[i];
103         }
104
105         /* Save the history */
106         my_strcpy(player->history, p_ptr->history, sizeof(player->history));
107 }
108
109
110 /*
111  * Load stored player data from 'player' as the currently rolled data,
112  * optionally placing the current data in 'prev_player' (if 'prev_player'
113  * is non-NULL).
114  *
115  * It is perfectly legal to specify the same "birther" for both 'player'
116  * and 'prev_player'.
117  */
118 static void load_roller_data(birther *player, birther *prev_player)
119 {
120         int i;
121
122     /* The initialisation is just paranoia - structure assignment is
123            (perhaps) not strictly defined to work with uninitialised parts
124            of structures. */
125         birther temp;
126         WIPE(&temp, birther);
127
128         /*** Save the current data if we'll need it later ***/
129         if (prev_player)
130                 save_roller_data(&temp);
131
132         /*** Load the previous data ***/
133
134         /* Load the data */
135         p_ptr->psex = player->sex;
136         p_ptr->prace = player->race;
137         p_ptr->pclass = player->class;
138         p_ptr->age = player->age;
139         p_ptr->wt = p_ptr->wt_birth = player->wt;
140         p_ptr->ht = p_ptr->ht_birth = player->ht;
141         p_ptr->sc = player->sc;
142         p_ptr->au = p_ptr->au_birth = player->au;
143
144         /* Load the stats */
145         for (i = 0; i < A_MAX; i++)
146         {
147                 p_ptr->stat_max[i] = p_ptr->stat_cur[i] = p_ptr->stat_birth[i] = player->stat[i];
148         }
149
150         /* Load the history */
151         my_strcpy(p_ptr->history, player->history, sizeof(p_ptr->history));
152
153
154         /*** Save the current data if the caller is interested in it. ***/
155         if (prev_player)
156                 *prev_player = temp;
157 }
158
159
160 /*
161  * Adjust a stat by an amount.
162  *
163  * This just uses "modify_stat_value()" unless "maximize" mode is false,
164  * and a positive bonus is being applied, in which case, a special hack
165  * is used, with the "auto_roll" flag affecting the result.
166  *
167  * The "auto_roll" flag selects "maximal" changes for use with the
168  * auto-roller initialization code.  Otherwise, if "maximize" mode
169  * is being used, the changes are fixed.  Otherwise, semi-random
170  * changes will occur, with larger changes at lower values.
171  */
172 static int adjust_stat(int value, int amount, int auto_roll)
173 {
174         /* Negative amounts or maximize mode */
175         if ((amount < 0) || adult_maximize)
176         {
177                 return (modify_stat_value(value, amount));
178         }
179
180         /* Special hack */
181         else
182         {
183                 int i;
184
185                 /* Apply reward */
186                 for (i = 0; i < amount; i++)
187                 {
188                         if (value < 18)
189                         {
190                                 value++;
191                         }
192                         else if (value < 18+70)
193                         {
194                                 value += ((auto_roll ? 15 : randint1(15)) + 5);
195                         }
196                         else if (value < 18+90)
197                         {
198                                 value += ((auto_roll ? 6 : randint1(6)) + 2);
199                         }
200                         else if (value < 18+100)
201                         {
202                                 value++;
203                         }
204                 }
205         }
206
207         /* Return the result */
208         return (value);
209 }
210
211
212
213
214 /*
215  * Roll for a characters stats
216  *
217  * For efficiency, we include a chunk of "calc_bonuses()".
218  */
219 static void get_stats(int stat_use[A_MAX])
220 {
221         int i, j;
222
223         int bonus;
224
225         int dice[18];
226
227
228         /* Roll and verify some stats */
229         while (TRUE)
230         {
231                 /* Roll some dice */
232                 for (j = i = 0; i < 18; i++)
233                 {
234                         /* Roll the dice */
235                         dice[i] = randint1(3 + i % 3);
236
237                         /* Collect the maximum */
238                         j += dice[i];
239                 }
240
241                 /* Verify totals */
242                 if ((j > 42) && (j < 54)) break;
243         }
244
245         /* Roll the stats */
246         for (i = 0; i < A_MAX; i++)
247         {
248                 /* Extract 5 + 1d3 + 1d4 + 1d5 */
249                 j = 5 + dice[3*i] + dice[3*i+1] + dice[3*i+2];
250
251                 /* Save that value */
252                 p_ptr->stat_max[i] = j;
253
254                 /* Obtain a "bonus" for "race" and "class" */
255                 bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i];
256
257                 /* Variable stat maxes */
258                 if (adult_maximize)
259                 {
260                         /* Start fully healed */
261                         p_ptr->stat_cur[i] = p_ptr->stat_max[i];
262
263                         /* Efficiency -- Apply the racial/class bonuses */
264                         stat_use[i] = modify_stat_value(p_ptr->stat_max[i], bonus);
265                 }
266
267                 /* Fixed stat maxes */
268                 else
269                 {
270                         /* Apply the bonus to the stat (somewhat randomly) */
271                         stat_use[i] = adjust_stat(p_ptr->stat_max[i], bonus, FALSE);
272
273                         /* Save the resulting stat maximum */
274                         p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stat_use[i];
275                 }
276
277                 p_ptr->stat_birth[i] = p_ptr->stat_max[i];
278         }
279 }
280
281
282 /*
283  * Roll for some info that the auto-roller ignores
284  */
285 static void get_extra(void)
286 {
287         int i, j, min_value, max_value;
288
289         /* Level one */
290         p_ptr->max_lev = p_ptr->lev = 1;
291
292         /* Experience factor */
293         p_ptr->expfact = rp_ptr->r_exp + cp_ptr->c_exp;
294
295         /* Hitdice */
296         p_ptr->hitdie = rp_ptr->r_mhp + cp_ptr->c_mhp;
297
298         /* Initial hitpoints */
299         p_ptr->mhp = p_ptr->hitdie;
300
301         /* Minimum hitpoints at highest level */
302         min_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 3) / 8;
303         min_value += PY_MAX_LEVEL;
304
305         /* Maximum hitpoints at highest level */
306         max_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 5) / 8;
307         max_value += PY_MAX_LEVEL;
308
309         /* Pre-calculate level 1 hitdice */
310         p_ptr->player_hp[0] = p_ptr->hitdie;
311
312         /* Roll out the hitpoints */
313         while (TRUE)
314         {
315                 /* Roll the hitpoint values */
316                 for (i = 1; i < PY_MAX_LEVEL; i++)
317                 {
318                         j = randint1(p_ptr->hitdie);
319                         p_ptr->player_hp[i] = p_ptr->player_hp[i-1] + j;
320                 }
321
322                 /* XXX Could also require acceptable "mid-level" hitpoints */
323
324                 /* Require "valid" hitpoints at highest level */
325                 if (p_ptr->player_hp[PY_MAX_LEVEL-1] < min_value) continue;
326                 if (p_ptr->player_hp[PY_MAX_LEVEL-1] > max_value) continue;
327
328                 /* Acceptable */
329                 break;
330         }
331 }
332
333
334 static void get_bonuses()
335 {
336         /* Calculate the bonuses and hitpoints */
337         p_ptr->update |= (PU_BONUS | PU_HP);
338
339         /* Update stuff */
340         update_stuff();
341
342         /* Fully healed */
343         p_ptr->chp = p_ptr->mhp;
344
345         /* Fully rested */
346         p_ptr->csp = p_ptr->msp;
347 }
348
349
350 /*
351  * Get the racial history, and social class, using the "history charts".
352  */
353 static void get_history(void)
354 {
355         int i, chart, roll, social_class;
356
357
358         /* Clear the previous history strings */
359         p_ptr->history[0] = '\0';
360
361
362         /* Initial social class */
363         social_class = randint1(4);
364
365         /* Starting place */
366         chart = rp_ptr->hist;
367
368
369         /* Process the history */
370         while (chart)
371         {
372                 /* Start over */
373                 i = 0;
374
375                 /* Roll for nobility */
376                 roll = randint1(100);
377
378                 /* Get the proper entry in the table */
379                 while ((chart != h_info[i].chart) || (roll > h_info[i].roll)) i++;
380
381                 /* Get the textual history */
382                 my_strcat(p_ptr->history, (h_text + h_info[i].text), sizeof(p_ptr->history));
383
384                 /* Add in the social class */
385                 social_class += (int)(h_info[i].bonus) - 50;
386
387                 /* Enter the next chart */
388                 chart = h_info[i].next;
389         }
390
391
392
393         /* Verify social class */
394         if (social_class > 75) social_class = 75;
395         else if (social_class < 1) social_class = 1;
396
397         /* Save the social class */
398         p_ptr->sc = social_class;
399 }
400
401
402 /*
403  * Computes character's age, height, and weight
404  */
405 static void get_ahw(void)
406 {
407         /* Calculate the age */
408         p_ptr->age = rp_ptr->b_age + randint1(rp_ptr->m_age);
409
410         /* Calculate the height/weight for males */
411         if (p_ptr->psex == SEX_MALE)
412         {
413                 p_ptr->ht = p_ptr->ht_birth = Rand_normal(rp_ptr->m_b_ht, rp_ptr->m_m_ht);
414                 p_ptr->wt = p_ptr->wt_birth = Rand_normal(rp_ptr->m_b_wt, rp_ptr->m_m_wt);
415         }
416
417         /* Calculate the height/weight for females */
418         else if (p_ptr->psex == SEX_FEMALE)
419         {
420                 p_ptr->ht = p_ptr->ht_birth = Rand_normal(rp_ptr->f_b_ht, rp_ptr->f_m_ht);
421                 p_ptr->wt = p_ptr->wt_birth = Rand_normal(rp_ptr->f_b_wt, rp_ptr->f_m_wt);
422         }
423 }
424
425
426
427
428 /*
429  * Get the player's starting money
430  */
431 static void get_money(int stat_use[A_MAX])
432 {
433         int i;
434
435         int gold;
436
437         /* Social Class determines starting gold */
438         gold = (p_ptr->sc * 6) + randint1(100) + 300;
439
440         /* Process the stats */
441         for (i = 0; i < A_MAX; i++)
442         {
443                 /* Mega-Hack -- reduce gold for high stats */
444                 if (stat_use[i] >= 18+50) gold -= 300;
445                 else if (stat_use[i] >= 18+20) gold -= 200;
446                 else if (stat_use[i] > 18) gold -= 150;
447                 else gold -= (stat_use[i] - 8) * 10;
448         }
449
450         /* Minimum 200 gold */
451         if (OPT(birth_money)) gold = MAX(500, gold);
452         else gold = MAX(200, gold);
453
454         /* Save the gold */
455         p_ptr->au = p_ptr->au_birth = gold;
456 }
457
458
459
460 /*
461  * Clear all the global "character" data
462  */
463 static void player_wipe(void)
464 {
465         int i;
466
467         /* Wipe the player */
468         (void)WIPE(p_ptr, player_type);
469
470         /* Clear the inventory */
471         for (i = 0; i < INVEN_TOTAL; i++)
472         {
473                 object_wipe(&inventory[i]);
474         }
475
476
477         /* Start with no artifacts made yet */
478         for (i = 0; i < z_info->a_max; i++)
479         {
480                 artifact_type *a_ptr = &a_info[i];
481                 a_ptr->cur_num = 0;
482         }
483
484
485         /* Start with no quests */
486         for (i = 0; i < MAX_Q_IDX; i++)
487         {
488                 q_list[i].level = 0;
489         }
490
491         /* Add a special quest */
492         q_list[0].level = 99;
493
494         /* Add a second quest */
495         q_list[1].level = 100;
496
497
498         /* Reset the "objects" */
499         for (i = 1; i < z_info->k_max; i++)
500         {
501                 object_kind *k_ptr = &k_info[i];
502
503                 /* Reset "tried" */
504                 k_ptr->tried = FALSE;
505
506                 /* Reset "aware" */
507                 k_ptr->aware = FALSE;
508         }
509
510
511         /* Reset the "monsters" */
512         for (i = 1; i < z_info->r_max; i++)
513         {
514                 monster_race *r_ptr = &r_info[i];
515                 monster_lore *l_ptr = &l_list[i];
516
517                 /* Hack -- Reset the counter */
518                 r_ptr->cur_num = 0;
519
520                 /* Hack -- Reset the max counter */
521                 r_ptr->max_num = 100;
522
523                 /* Hack -- Reset the max counter */
524                 if (r_ptr->flags[0] & (RF0_UNIQUE)) r_ptr->max_num = 1;
525
526                 /* Clear player kills */
527                 l_ptr->pkills = 0;
528         }
529
530
531         /* Hack -- no ghosts */
532         r_info[z_info->r_max-1].max_num = 0;
533
534
535         /* Hack -- Well fed player */
536         p_ptr->food = PY_FOOD_FULL - 1;
537
538
539         /* None of the spells have been learned yet */
540         for (i = 0; i < PY_MAX_SPELLS; i++) p_ptr->spell_order[i] = 99;
541
542
543         /* First turn. */
544         turn = old_turn = 1;
545 }
546
547 /*
548  * Try to wield everything wieldable in the inventory.
549  */
550 static void wield_all(void)
551 {
552         object_type *o_ptr;
553         object_type *i_ptr;
554         object_type object_type_body;
555
556         int slot;
557         int item;
558
559         /* Scan through the slots backwards */
560         for (item = INVEN_PACK - 1; item >= 0; item--)
561         {
562                 o_ptr = &inventory[item];
563
564                 /* Skip non-objects */
565                 if (!o_ptr->k_idx) continue;
566
567                 /* Make sure we can wield it and that there's nothing else in that slot */
568                 slot = wield_slot(o_ptr);
569                 if (slot < INVEN_WIELD) continue;
570                 if (inventory[slot].k_idx) continue;
571
572                 /* Get local object */
573                 i_ptr = &object_type_body;
574                 object_copy(i_ptr, o_ptr);
575
576                 /* Modify quantity */
577                 i_ptr->number = 1;
578
579                 /* Decrease the item (from the pack) */
580                 if (item >= 0)
581                 {
582                         inven_item_increase(item, -1);
583                         inven_item_optimize(item);
584                 }
585
586                 /* Decrease the item (from the floor) */
587                 else
588                 {
589                         floor_item_increase(0 - item, -1);
590                         floor_item_optimize(0 - item);
591                 }
592
593                 /* Get the wield slot */
594                 o_ptr = &inventory[slot];
595
596                 /* Wear the new stuff */
597                 object_copy(o_ptr, i_ptr);
598
599                 /* Increase the weight */
600                 p_ptr->total_weight += i_ptr->weight;
601
602                 /* Increment the equip counter by hand */
603                 p_ptr->equip_cnt++;
604         }
605
606         return;
607 }
608
609
610 /*
611  * Init players with some belongings
612  *
613  * Having an item identifies it and makes the player "aware" of its purpose.
614  */
615 static void player_outfit(void)
616 {
617         int i;
618         const start_item *e_ptr;
619         object_type *i_ptr;
620         object_type object_type_body;
621
622
623         /* Hack -- Give the player his equipment */
624         for (i = 0; i < MAX_START_ITEMS; i++)
625         {
626                 /* Access the item */
627                 e_ptr = &(cp_ptr->start_items[i]);
628
629                 /* Get local object */
630                 i_ptr = &object_type_body;
631
632                 /* Hack -- Give the player an object */
633                 if (e_ptr->tval > 0)
634                 {
635                         /* Get the object_kind */
636                         int k_idx = lookup_kind(e_ptr->tval, e_ptr->sval);
637
638                         /* Valid item? */
639                         if (!k_idx) continue;
640
641                         /* Prepare the item */
642                         object_prep(i_ptr, k_idx);
643                         i_ptr->number = (byte)rand_range(e_ptr->min, e_ptr->max);
644                         i_ptr->origin = ORIGIN_BIRTH;
645
646                         object_aware(i_ptr);
647                         object_known(i_ptr);
648                         (void)inven_carry(i_ptr);
649                         k_info[k_idx].everseen = TRUE;
650                 }
651         }
652
653
654         /* Hack -- give the player hardcoded equipment XXX */
655
656         /* Get local object */
657         i_ptr = &object_type_body;
658
659         /* Hack -- Give the player some food */
660         object_prep(i_ptr, lookup_kind(TV_FOOD, SV_FOOD_RATION));
661         i_ptr->number = (byte)rand_range(3, 7);
662         i_ptr->origin = ORIGIN_BIRTH;
663         object_aware(i_ptr);
664         object_known(i_ptr);
665         k_info[i_ptr->k_idx].everseen = TRUE;
666         (void)inven_carry(i_ptr);
667
668
669         /* Get local object */
670         i_ptr = &object_type_body;
671
672         /* Hack -- Give the player some torches */
673         object_prep(i_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH));
674         i_ptr->number = (byte)rand_range(3, 7);
675         i_ptr->timeout = FUEL_TORCH;
676         i_ptr->origin = ORIGIN_BIRTH;
677         object_aware(i_ptr);
678         object_known(i_ptr);
679         k_info[i_ptr->k_idx].everseen = TRUE;
680         (void)inven_carry(i_ptr);
681
682
683         /* Now try wielding everything */
684         wield_all();
685 }
686
687
688 /*
689  * Get a command for the birth process, handling the command cases here
690  * (i.e. quitting and options.
691  *
692  * NOTE: We would also handle help here if it was eventually decided
693  * there should be a game help mode rather than it being entirely at
694  * the UI level.
695  */
696 static game_command get_birth_command()
697 {
698         game_command cmd = { CMD_NULL, 0, {0} };
699
700         while (cmd.command == CMD_NULL)
701         {
702                 cmd = get_game_command();
703
704                 if (cmd.command == CMD_QUIT)
705                         quit(NULL);
706
707                 if (cmd.command == CMD_OPTIONS)
708                 {
709                         /* TODO: Change this to use whatever sort of message passing
710                            system we eventually decide on for options.  That might
711                            still be calling do_cmd_option. :) */
712                         do_cmd_options();
713
714                         /* We've already handled it, so don't pass it on. */
715                         cmd.command = CMD_NULL;
716                 }
717         }
718
719         /* TODO: Check against list of permitted commands for the given stage. Probably. */
720
721         return cmd;
722 }
723
724 /*
725  * Cost of each "point" of a stat.
726  */
727 static const int birth_stat_costs[18 + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 4 };
728
729 /* It is feasible to get base 17 in 3 stats with the autoroller */
730 #define MAX_BIRTH_POINTS 24 /* 3 * (1+1+1+1+1+1+2) */
731
732
733 static void recalculate_stats(int *stats, int points_left)
734 {
735         int i;
736
737         /* Process stats */
738         for (i = 0; i < A_MAX; i++)
739         {
740                 /* Variable stat maxes */
741                 if (adult_maximize)
742                 {
743                         /* Reset stats */
744                         p_ptr->stat_cur[i] = p_ptr->stat_max[i] = p_ptr->stat_birth[i] = stats[i];
745                 }
746                
747                 /* Fixed stat maxes */
748                 else
749                 {
750                         /* Obtain a "bonus" for "race" and "class" */
751                         int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i];
752                        
753                         /* Apply the racial/class bonuses */
754                         p_ptr->stat_cur[i] = p_ptr->stat_max[i] =
755                                 modify_stat_value(stats[i], bonus);
756                 }
757         }
758        
759         /* Gold is inversely proportional to cost */
760         p_ptr->au = OPT(birth_money) ? 500 : 200;
761         p_ptr->au += (50 * points_left);
762         p_ptr->au_birth = p_ptr->au;
763
764         /* Update bonuses, hp, etc. */
765         get_bonuses();
766
767         /* Tell the UI about all this stuff that's changed. */
768         event_signal(EVENT_GOLD);
769         event_signal(EVENT_AC);
770         event_signal(EVENT_HP);
771         event_signal(EVENT_STATS);
772 }
773
774
775 /*
776  * Due to its relative complexity, point based birth has been split off into
777  * this function.  'reset' is TRUE if we're entering from earlier in the
778  * birth process - i.e. we want to start a character from scratch rather
779  * than having another go at one already chosen.
780  */
781 static enum birth_stage do_point_based(bool reset)
782 {
783         game_command cmd = { CMD_NULL, 0, {0} };
784        
785         int i, j;
786         int stats[A_MAX];
787
788         int points_spent[A_MAX];
789         int points_left;
790
791         /* If the first command is BIRTH_BACK, we step back a stage, so this
792            is a reasonable default. */
793         enum birth_stage next_stage = BIRTH_ROLLER_CHOICE;
794
795         if (reset)
796         {
797                 /* Roll for base hitpoints, age/height/weight and social class */
798                 get_extra();
799                 get_ahw();
800                 get_history();
801         }
802
803         /* Signal that we're entering the point based birth arena. */
804         event_signal_birthstage(BIRTH_POINTBASED, NULL);
805        
806         /* Calculate and signal initial stats and points totals. */
807         points_left = MAX_BIRTH_POINTS;
808
809         for (i = 0; i < A_MAX; i++)
810         {
811                 /* Initial stats are all 10 and costs or zero */
812                 stats[i] = 10;
813                 points_spent[i] = 0;
814
815                 /* If not resetting, we use the stored stat values from p_ptr
816                    to simulate buying the stats, just using the same method as
817                    when the BUY_STAT command is received. */
818                 if (!reset)
819                 {
820                         for (j = 10; j < p_ptr->stat_birth[i]; j++)
821                         {
822                                 int stat_cost = birth_stat_costs[j + 1];
823                                 stats[i]++;
824                                 points_spent[i] += stat_cost;
825                                 points_left -= stat_cost;
826                         }
827                 }
828         }
829
830         /* Use the new "birth stat" values to work out the "other"
831            stat values (i.e. after modifiers) and tell the UI things have
832            changed. */
833         recalculate_stats(stats, points_left);
834         event_signal_birthstats(points_spent, points_left);     
835        
836         /* Then on to the interactive part - two ways to leave */
837         while (cmd.command != CMD_ACCEPT_STATS &&
838                    cmd.command != CMD_BIRTH_BACK)
839         {               
840                 cmd = get_birth_command();
841                
842                 /* If BIRTH_BACK isn't the first command, or we didn't start
843                    by resetting the stats, we redo this stage rather than actually
844                    stepping back. */
845                 if (cmd.command != CMD_BIRTH_BACK || !reset)
846                         next_stage = BIRTH_POINTBASED;
847
848                 switch (cmd.command)
849                 {
850                         case CMD_BUY_STAT:
851                         {
852                                 /* The choice is the index of the stat in the list to "buy" */
853                                 int choice = cmd.params.choice;
854                                 if (choice >= A_MAX || choice < 0) continue;
855
856                                 /* Can't increase stats past a "base" of 18 */
857                                 if (stats[choice] < 18)
858                                 {
859                                         /* Get the cost of buying the extra point (beyond what
860                                            it has already cost to get this far). */
861                                         int stat_cost = birth_stat_costs[stats[choice] + 1];
862
863                                         if (stat_cost <= points_left)
864                                         {
865                                                 stats[choice]++;
866                                                 points_spent[choice] += stat_cost;
867                                                 points_left -= stat_cost;
868                                
869                                                 /* Tell the UI the new points situation. */
870                                                 event_signal_birthstats(points_spent, points_left);
871
872                                                 /* Recalculate everything that's changed because
873                                                    the stat has changed, and inform the UI. */
874                                                 recalculate_stats(stats, points_left);
875                                         }
876                                 }
877
878                                 break;
879                         }
880                        
881                         case CMD_SELL_STAT:
882                         {
883                                 /* The choice is the index of the stat in the list to "sell" */
884                                 int choice = cmd.params.choice;
885                                 if (choice >= A_MAX || choice < 0) continue;
886
887                                 /* We can't "sell" stats below the base of 10. */
888                                 if (stats[choice] > 10)
889                                 {
890                                         int stat_cost = birth_stat_costs[stats[choice]];
891                                        
892                                         stats[choice]--;
893                                         points_spent[choice] -= stat_cost;
894                                         points_left += stat_cost;
895                                        
896                                         /* Tell the UI the new points situation. */
897                                         event_signal_birthstats(points_spent, points_left);
898
899                                         /* Recalculate everything that's changed because
900                                            the stat has changed, and inform the UI. */
901                                         recalculate_stats(stats, points_left);
902                                 }                               
903                                 break;
904                         }
905
906                         case CMD_ACCEPT_STATS:
907                         {
908                                 next_stage = BIRTH_NAME_CHOICE;
909                                 break;
910                         }
911                         default:
912                         {
913                                 /* This state shouldn't be reached. */
914                         }
915                 }
916         }
917
918         return next_stage;
919 }
920        
921