PageRenderTime 44ms CodeModel.GetById 15ms app.highlight 23ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ui-birth.c

https://bitbucket.org/ekolis/jackband
C | 981 lines | 599 code | 180 blank | 202 comment | 135 complexity | 0f1d18a0fb0cda18b9c06c97be895626 MD5 | raw file
  1/*
  2 * File: ui-birth.c
  3 * Purpose: Text-based user interface for character creation
  4 *
  5 * Copyright (c) 1987 - 2007 Angband contributors
  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 "ui-menu.h"
 20#include "ui-birth.h"
 21#include "game-event.h"
 22#include "game-cmd.h"
 23#include "cmds.h"
 24
 25/*
 26 * Overview
 27 * ========
 28 * This file implements the user interface side of the birth process
 29 * for the classic terminal-based UI of Angband.
 30 *
 31 * It models birth as a series of steps which must be carried out in 
 32 * a specified order, with the option of stepping backwards to revisit
 33 * past choices.
 34 *
 35 * It starts when we receive the EVENT_ENTER_BIRTH event from the game,
 36 * and ends when we receive the EVENT_LEAVE_BIRTH event.  In between,
 37 * we will repeatedly be asked to supply a game command, which change
 38 * the state of the character being rolled.  Once the player is happy
 39 * with their character, we send the CMD_ACCEPT_CHARACTER command.
 40 */
 41
 42
 43/* A local-to-this-file global to hold the most important bit of state
 44   between calls to the game proper.  Probably not strictly necessary,
 45   but reduces complexity a bit. */
 46enum birth_stage
 47{
 48	BIRTH_BACK = -1,
 49	BIRTH_RESET = 0,
 50	BIRTH_QUICKSTART,
 51	BIRTH_SEX_CHOICE,
 52	BIRTH_RACE_CHOICE,
 53	//BIRTH_CLASS_CHOICE,
 54	BIRTH_ROLLER_CHOICE,
 55	BIRTH_POINTBASED,
 56	BIRTH_ROLLER,
 57	BIRTH_NAME_CHOICE,
 58	BIRTH_FINAL_CONFIRM,
 59	BIRTH_COMPLETE
 60};
 61
 62
 63enum birth_questions
 64{
 65	BQ_METHOD = 0,
 66	BQ_SEX,
 67	BQ_RACE,
 68	//BQ_CLASS,
 69	BQ_ROLLER,
 70	MAX_BIRTH_QUESTIONS
 71};
 72
 73enum birth_rollers
 74{
 75	BR_POINTBASED = 0,
 76	BR_NORMAL,
 77	MAX_BIRTH_ROLLERS
 78};
 79
 80
 81static void point_based_start(void);
 82static bool quickstart_allowed = FALSE;
 83
 84/* ------------------------------------------------------------------------
 85 * Quickstart? screen.
 86 * ------------------------------------------------------------------------ */
 87static enum birth_stage get_quickstart_command(void)
 88{
 89	const char *prompt = "['Y' to use this character, 'N' to start afresh, 'C' to change name]";
 90	ui_event_data ke;
 91
 92	enum birth_stage next = BIRTH_QUICKSTART;
 93
 94	/* Prompt for it */
 95	prt("New character based on previous one:", 0, 0);
 96	prt(prompt, Term->hgt - 1, Term->wid / 2 - strlen(prompt) / 2);
 97	
 98	/* Buttons */
 99	button_kill_all();
100	button_add("[Y]", 'y');
101	button_add("[N]", 'n');
102	button_add("[C]", 'c');
103	redraw_stuff();
104	
105	do
106	{
107		/* Get a key */
108		ke = inkey_ex();
109		
110		if (ke.key == 'N' || ke.key == 'n')
111		{
112			cmd_insert(CMD_BIRTH_RESET, TRUE);
113			next = BIRTH_SEX_CHOICE;
114		}
115		else if (ke.key == KTRL('X'))
116		{
117			cmd_insert(CMD_QUIT);
118			next = BIRTH_COMPLETE;
119		}
120		else if (ke.key == 'C' || ke.key == 'c')
121		{
122			next = BIRTH_NAME_CHOICE;
123		}
124		else if (ke.key == 'Y' || ke.key == 'y')
125		{
126			cmd_insert(CMD_ACCEPT_CHARACTER);
127			next = BIRTH_COMPLETE;
128		}
129	} while (next == BIRTH_QUICKSTART);
130	
131	/* Buttons */
132	button_kill_all();
133	redraw_stuff();
134
135	/* Clear prompt */
136	clear_from(23);
137
138	return next;
139}
140
141/* ------------------------------------------------------------------------
142 * The various "menu" bits of the birth process - namely choice of sex,
143 * race, class, and roller type.
144 * ------------------------------------------------------------------------ */
145
146/* The various menus */
147static menu_type sex_menu, race_menu, class_menu, roller_menu;
148
149/* Locations of the menus, etc. on the screen */
150#define HEADER_ROW       1
151#define QUESTION_ROW     7
152#define TABLE_ROW       10
153
154#define QUESTION_COL     2
155#define SEX_COL          2
156#define RACE_COL        14
157#define RACE_AUX_COL    29
158#define CLASS_COL       29
159#define CLASS_AUX_COL   50
160
161static region gender_region = {SEX_COL, TABLE_ROW, 15, -2};
162static region race_region = {RACE_COL, TABLE_ROW, 15, -2};
163static region class_region = {CLASS_COL, TABLE_ROW, 19, -2};
164static region roller_region = {44, TABLE_ROW, 21, -2};
165
166/* We use different menu "browse functions" to display the help text
167   sometimes supplied with the menu items - currently just the list
168   of bonuses, etc, corresponding to each race and class. */
169typedef void (*browse_f) (int oid, void *db, const region *l);
170
171/* We have one of these structures for each menu we display - it holds
172   the useful information for the menu - text of the menu items, "help"
173   text, current (or default) selection, and whether random selection
174   is allowed. */
175struct birthmenu_data 
176{
177	const char **items;
178	const char *hint;
179	bool allow_random;
180};
181
182/* A custom "display" function for our menus that simply displays the
183   text from our stored data in a different colour if it's currently
184   selected. */
185static void birthmenu_display(menu_type *menu, int oid, bool cursor,
186			      int row, int col, int width)
187{
188	struct birthmenu_data *data = menu->menu_data;
189
190	byte attr = curs_attrs[CURS_KNOWN][0 != cursor];
191	c_put_str(attr, data->items[oid], row, col);
192}
193
194/* We defer the choice of actual actions until outside of the menu API 
195   in menu_question(), so this can be a reasonably simple function
196   for when a menu "command" is activated. */
197static bool birthmenu_handler(char cmd, void *db, int oid)
198{
199	return TRUE;
200}
201
202/* Our custom menu iterator, only really needed to allow us to override
203   the default handling of "commands" in the standard iterators (hence
204   only defining the display and handler parts). */
205static const menu_iter birth_iter = { NULL, NULL, birthmenu_display, birthmenu_handler };
206
207static void race_help(int i, void *db, const region *l)
208{
209	int j;
210
211	/* Output to the screen */
212	text_out_hook = text_out_to_screen;
213	
214	/* Indent output */
215	text_out_indent = RACE_AUX_COL;
216	Term_gotoxy(RACE_AUX_COL, TABLE_ROW);
217
218	for (j = 0; j < A_MAX; j++) 
219	{  
220		text_out_e("%s%+d\n", stat_names_reduced[j], p_info[i].r_adj[j]);
221	}
222	
223	text_out_e("Hit die: %d\n", p_info[i].r_mhp);
224	text_out_e("Experience: %d%%\n", p_info[i].r_exp);
225	text_out_e("Infravision: %d ft", p_info[i].infra * 10);
226	
227	/* Reset text_out() indentation */
228	text_out_indent = 0;
229}
230
231static void class_help(int i, void *db, const region *l)
232{
233	int j;
234
235	/* Output to the screen */
236	text_out_hook = text_out_to_screen;
237	
238	/* Indent output */
239	text_out_indent = CLASS_AUX_COL;
240	Term_gotoxy(CLASS_AUX_COL, TABLE_ROW);
241
242	for (j = 0; j < A_MAX; j++) 
243	{  
244		text_out_e("%s%+d\n", stat_names_reduced[j], c_info[i].c_adj[j]); 
245	}
246
247	text_out_e("Hit die: %d\n", c_info[i].c_mhp);   
248	text_out_e("Experience: %d%%", c_info[i].c_exp);
249	
250	/* Reset text_out() indentation */
251	text_out_indent = 0;
252}
253
254/* Set up one of our menus ready to display choices for a birth question.
255   This is slightly involved. */
256static void init_birth_menu(menu_type *menu, int n_choices, int initial_choice, const region *reg, bool allow_random, browse_f aux)
257{
258	struct birthmenu_data *menu_data;
259
260	/* A couple of behavioural flags - we want selections letters in
261	   lower case and a double tap to act as a selection. */
262	menu->selections = lower_case;
263	menu->flags = MN_DBL_TAP;
264
265	/* Set the number of choices in the menu to the same as the game
266	   has told us we've got to offer. */
267	menu->count = n_choices;
268
269	/* Allocate sufficient space for our own bits of menu information. */
270	menu_data = mem_alloc(sizeof *menu_data);
271
272	/* Copy across the game's suggested initial selection, etc. */
273	menu->cursor = initial_choice;
274	menu_data->allow_random = allow_random;
275
276	/* Allocate space for an array of menu item texts and help texts
277	   (where applicable) */
278	menu_data->items = mem_alloc(menu->count * sizeof *menu_data->items);
279
280	/* Poke our menu data in to the assigned slot in the menu structure. */
281	menu->menu_data = menu_data;
282
283	/* Set up the "browse" hook to display help text (where applicable). */
284	menu->browse_hook = aux;
285
286	/* Get ui-menu to initialise whatever it wants to to give us a scrollable
287	   menu. */
288	menu_init(menu, MN_SKIN_SCROLL, &birth_iter, reg);
289}
290
291
292
293static void setup_menus()
294{
295	int i;
296
297	const char *roller_choices[MAX_BIRTH_ROLLERS] = { 
298		"Point-based", 
299		"Standard roller" 
300	};
301
302	struct birthmenu_data *mdata;
303
304	/* Sex menu fairly straightforward */
305	init_birth_menu(&sex_menu, MAX_SEXES, p_ptr->psex, &gender_region, TRUE, NULL);
306	mdata = sex_menu.menu_data;
307	for (i = 0; i < MAX_SEXES; i++)
308	{	
309		mdata->items[i] = sex_info[i].title;
310	}
311	mdata->hint = "Your 'sex' does not have any significant gameplay effects.";
312
313	/* Race menu more complicated. */
314	init_birth_menu(&race_menu, z_info->p_max, p_ptr->prace, &race_region, TRUE, race_help);
315	mdata = race_menu.menu_data;
316
317	for (i = 0; i < z_info->p_max; i++)
318	{	
319		mdata->items[i] = p_name + p_info[i].name;
320	}
321	mdata->hint = "Your 'race' determines various intrinsic factors and bonuses.";
322
323	/* Class menu similar to race. */
324	init_birth_menu(&class_menu, z_info->c_max, p_ptr->pclass, &class_region, TRUE, class_help);
325	mdata = class_menu.menu_data;
326
327	for (i = 0; i < z_info->c_max; i++)
328	{	
329		mdata->items[i] = c_name + c_info[i].name;
330	}
331	mdata->hint = "Your 'class' determines various intrinsic abilities and bonuses";
332		
333	/* Roller menu straightforward again */
334	init_birth_menu(&roller_menu, MAX_BIRTH_ROLLERS, 0, &roller_region, FALSE, NULL);
335	mdata = roller_menu.menu_data;
336	for (i = 0; i < MAX_BIRTH_ROLLERS; i++)
337	{	
338		mdata->items[i] = roller_choices[i];
339	}
340	mdata->hint = "Your choice of character generation.  Point-based is recommended.";
341}
342
343/* Cleans up our stored menu info when we've finished with it. */
344static void free_birth_menu(menu_type *menu)
345{
346	struct birthmenu_data *data = menu->menu_data;
347
348	if (data)
349	{
350		mem_free(data->items);
351		mem_free(data);
352	}
353}
354
355static void free_birth_menus()
356{
357	/* We don't need these any more. */
358	free_birth_menu(&sex_menu);
359	free_birth_menu(&race_menu);
360	free_birth_menu(&class_menu);
361	free_birth_menu(&roller_menu);
362}
363
364/*
365 * Clear the previous question
366 */
367static void clear_question(void)
368{
369	int i;
370
371	for (i = QUESTION_ROW; i < TABLE_ROW; i++)
372	{
373		/* Clear line, position cursor */
374		Term_erase(0, i, 255);
375	}
376}
377
378
379#define BIRTH_MENU_HELPTEXT \
380	"{lightblue}Please select your character from the menu below:{/}\n\n" \
381	"Use the {lightgreen}movement keys{/} to scroll the menu, " \
382	"{lightgreen}Enter{/} to select the current menu item, '{lightgreen}*{/}' " \
383	"for a random menu item, '{lightgreen}ESC{/}' to step back through the " \
384	"birth process, '{lightgreen}={/}' for the birth options, '{lightgreen}?{/} " \
385	"for help, or '{lightgreen}Ctrl-X{/}' to quit."
386
387/* Show the birth instructions on an otherwise blank screen */	
388static void print_menu_instructions(void)
389{
390	/* Clear screen */
391	Term_clear();
392	
393	/* Output to the screen */
394	text_out_hook = text_out_to_screen;
395	
396	/* Indent output */
397	text_out_indent = QUESTION_COL;
398	Term_gotoxy(QUESTION_COL, HEADER_ROW);
399	
400	/* Display some helpful information */
401	text_out_e(BIRTH_MENU_HELPTEXT);
402	
403	/* Reset text_out() indentation */
404	text_out_indent = 0;
405}
406
407/* Allow the user to select from the current menu, and return the 
408   corresponding command to the game.  Some actions are handled entirely
409   by the UI (displaying help text, for instance). */
410static enum birth_stage menu_question(enum birth_stage current, menu_type *current_menu, cmd_code choice_command)
411{
412	struct birthmenu_data *menu_data = current_menu->menu_data;
413	int cursor = current_menu->cursor;
414	ui_event_data cx;
415
416	enum birth_stage next = BIRTH_RESET;
417	
418	/* Print the question currently being asked. */
419	clear_question();
420	Term_putstr(QUESTION_COL, QUESTION_ROW, -1, TERM_YELLOW, menu_data->hint);
421
422	current_menu->cmd_keys = "?=*\r\n\x18";	 /* ?, ,= *, \n, <ctl-X> */
423
424	while (next == BIRTH_RESET)
425	{
426		/* Display the menu, wait for a selection of some sort to be made. */
427		cx = menu_select(current_menu, &cursor, EVT_CMD);
428
429		/* As all the menus are displayed in "hierarchical" style, we allow
430		   use of "back" (left arrow key or equivalent) to step back in 
431		   the proces as well as "escape". */
432		if (cx.type == EVT_BACK || cx.type == EVT_ESCAPE)
433		{
434			next = BIRTH_BACK;
435		}
436		/* '\xff' is a mouse selection, '\r' a keyboard one. */
437		else if (cx.key == '\xff' || cx.key == '\r') 
438		{
439			if (current == BIRTH_ROLLER_CHOICE)
440			{
441				if (cursor)
442				{
443					/* Do a first roll of the stats */
444					cmd_insert(CMD_ROLL_STATS);
445					next = current + 2;
446				}
447				else
448				{
449					/* 
450					 * Make sure we've got a point-based char to play with. 
451					 * We call point_based_start here to make sure we get
452					 * an update on the points totals before trying to
453					 * display the screen.  The call to CMD_RESET_STATS
454					 * forces a rebuying of the stats to give us up-to-date
455					 * totals.  This is, it should go without saying, a hack.
456					 */
457					point_based_start();
458					cmd_insert(CMD_RESET_STATS, TRUE);
459					next = current + 1;
460				}
461			}
462			else
463			{
464				cmd_insert(choice_command, cursor);
465				next = current + 1;
466			}
467		}
468		/* '*' chooses an option at random from those the game's provided. */
469		else if (cx.key == '*' && menu_data->allow_random) 
470		{
471			current_menu->cursor = randint0(current_menu->count);
472			cmd_insert(choice_command, current_menu->cursor);
473
474			menu_refresh(current_menu);
475			next = current + 1;
476		}
477		else if (cx.key == '=') 
478		{
479			do_cmd_options();
480			next = current;
481		}
482		else if (cx.key == KTRL('X')) 
483		{
484			cmd_insert(CMD_QUIT);
485			next = BIRTH_COMPLETE;
486		}
487		else if (cx.key == '?')
488		{
489			do_cmd_help();
490		}
491	}
492	
493	return next;
494}
495
496/* ------------------------------------------------------------------------
497 * The rolling bit of the roller.
498 * ------------------------------------------------------------------------ */
499#define ROLLERCOL 42
500
501static enum birth_stage roller_command(bool first_call)
502{
503	char prompt[80] = "";
504	size_t promptlen = 0;
505
506	ui_event_data ke;
507	char ch;
508
509	enum birth_stage next = BIRTH_ROLLER;
510
511	/* Used to keep track of whether we've rolled a character before or not. */
512	static bool prev_roll = FALSE;
513
514   	/* Display the player - a bit cheaty, but never mind. */
515	display_player(0);
516
517	if (first_call)
518		prev_roll = FALSE;
519
520	/* Add buttons */
521	button_add("[ESC]", ESCAPE);
522	button_add("[Enter]", '\r');
523	button_add("[r]", 'r');
524	if (prev_roll) button_add("[p]", 'p');
525	clear_from(Term->hgt - 2);
526	redraw_stuff();
527
528	/* Prepare a prompt (must squeeze everything in) */
529	strnfcat(prompt, sizeof (prompt), &promptlen, "['r' to reroll");
530	if (prev_roll) 
531		strnfcat(prompt, sizeof(prompt), &promptlen, ", 'p' for prev");
532	strnfcat(prompt, sizeof (prompt), &promptlen, " or 'Enter' to accept]");
533
534	/* Prompt for it */
535	prt(prompt, Term->hgt - 1, Term->wid / 2 - promptlen / 2);
536	
537	/* Prompt and get a command */
538	ke = inkey_ex();
539	ch = ke.key;
540
541	if (ch == ESCAPE) 
542	{
543		button_kill('r');
544		button_kill('p');
545
546		next = BIRTH_BACK;
547	}
548
549	/* 'Enter' accepts the roll */
550	if ((ch == '\r') || (ch == '\n')) 
551	{
552		next = BIRTH_NAME_CHOICE;
553	}
554
555	/* Reroll this character */
556	else if ((ch == ' ') || (ch == 'r'))
557	{
558		cmd_insert(CMD_ROLL_STATS);
559		prev_roll = TRUE;
560	}
561
562	/* Previous character */
563	else if (prev_roll && (ch == 'p'))
564	{
565		cmd_insert(CMD_PREV_STATS);
566	}
567
568	/* Quit */
569	else if (ch == KTRL('X')) 
570	{
571		cmd_insert(CMD_QUIT);
572		next = BIRTH_COMPLETE;
573	}
574
575	/* Help XXX */
576	else if (ch == '?')
577	{
578		do_cmd_help();
579	}
580
581	/* Nothing handled directly here */
582	else
583	{
584		bell("Illegal roller command!");
585	}
586
587	/* Kill buttons */
588	button_kill(ESCAPE);
589	button_kill('\r');
590	button_kill('r');
591	button_kill('p');
592	redraw_stuff();
593
594	return next;
595}
596
597/* ------------------------------------------------------------------------
598 * Point-based stat allocation.
599 * ------------------------------------------------------------------------ */
600
601/* The locations of the "costs" area on the birth screen. */
602#define COSTS_ROW 2
603#define COSTS_COL (42 + 32)
604#define TOTAL_COL (42 + 19)
605
606/* This is called whenever a stat changes.  We take the easy road, and just
607   redisplay them all using the standard function. */
608static void point_based_stats(game_event_type type, game_event_data *data, void *user)
609{
610	display_player_stat_info();
611}
612
613/* This is called whenever any of the other miscellaneous stat-dependent things
614   changed.  We are hooked into changes in the amount of gold in this case,
615   but redisplay everything because it's easier. */
616static void point_based_misc(game_event_type type, game_event_data *data, void *user)
617{
618	display_player_xtra_info();
619}
620
621
622/* This is called whenever the points totals are changed (in birth.c), so
623   that we can update our display of how many points have been spent and
624   are available. */
625static void point_based_points(game_event_type type, game_event_data *data, void *user)
626{
627	int i;
628	int sum = 0;
629	int *stats = data->birthstats.stats;
630
631	/* Display the costs header */
632	put_str("Cost", COSTS_ROW - 1, COSTS_COL);
633	
634	/* Display the costs */
635	for (i = 0; i < A_MAX; i++)
636	{
637		/* Display cost */
638		put_str(format("%4d", stats[i]), COSTS_ROW + i, COSTS_COL);
639		sum += stats[i];
640	}
641	
642	put_str(format("Total Cost: %2d/%2d", sum, data->birthstats.remaining + sum), COSTS_ROW + A_MAX, TOTAL_COL);
643}
644
645static void point_based_start(void)
646{
647	const char *prompt = "[up/down to move, left/right to modify, 'r' to reset, 'Enter' to accept]";
648
649	/* Clear */
650	Term_clear();
651
652	/* Display the player */
653	display_player_xtra_info();
654	display_player_stat_info();
655
656	prt(prompt, Term->hgt - 1, Term->wid / 2 - strlen(prompt) / 2);
657
658	/* Register handlers for various events - cheat a bit because we redraw
659	   the lot at once rather than each bit at a time. */
660	event_add_handler(EVENT_BIRTHPOINTS, point_based_points, NULL);	
661	event_add_handler(EVENT_STATS, point_based_stats, NULL);	
662	event_add_handler(EVENT_GOLD, point_based_misc, NULL);	
663}
664
665static void point_based_stop(void)
666{
667	event_remove_handler(EVENT_BIRTHPOINTS, point_based_points, NULL);	
668	event_remove_handler(EVENT_STATS, point_based_stats, NULL);	
669	event_remove_handler(EVENT_GOLD, point_based_misc, NULL);	
670}
671
672static enum birth_stage point_based_command(void)
673{
674	static int stat = 0;
675	char ch;
676	enum birth_stage next = BIRTH_POINTBASED;
677
678/*	point_based_display();*/
679
680	/* Place cursor just after cost of current stat */
681	Term_gotoxy(COSTS_COL + 4, COSTS_ROW + stat);
682
683	/* Get key */
684	ch = inkey();
685	
686	if (ch == KTRL('X')) 
687	{
688		cmd_insert(CMD_QUIT);
689		next = BIRTH_COMPLETE;
690	}
691	
692	/* Go back a step, or back to the start of this step */
693	else if (ch == ESCAPE) 
694	{
695		next = BIRTH_BACK;
696	}
697
698	else if (ch == 'r' || ch == 'R') 
699	{
700		cmd_insert(CMD_RESET_STATS, FALSE);
701	}
702	
703	/* Done */
704	else if ((ch == '\r') || (ch == '\n')) 
705	{
706		next = BIRTH_NAME_CHOICE;
707	}
708	else
709	{
710		ch = target_dir(ch);
711		
712		/* Prev stat, looping round to the bottom when going off the top */
713		if (ch == 8)
714			stat = (stat + A_MAX - 1) % A_MAX;
715		
716		/* Next stat, looping round to the top when going off the bottom */
717		if (ch == 2)
718			stat = (stat + 1) % A_MAX;
719		
720		/* Decrease stat (if possible) */
721		if (ch == 4)
722		{
723			cmd_insert(CMD_SELL_STAT, stat);
724		}
725		
726		/* Increase stat (if possible) */
727		if (ch == 6)
728		{
729			cmd_insert(CMD_BUY_STAT, stat);
730		}
731	}
732
733	return next;
734}
735	
736/* ------------------------------------------------------------------------
737 * Asking for the player's chosen name.
738 * ------------------------------------------------------------------------ */
739static enum birth_stage get_name_command(void)
740{
741	enum birth_stage next;
742	char name[32];
743
744	if (get_name(name, sizeof(name)))
745	{	
746		cmd_insert(CMD_NAME_CHOICE, name);
747		next = BIRTH_FINAL_CONFIRM;
748	}
749	else
750	{
751		next = BIRTH_BACK;
752	}
753
754	return next;
755}
756
757/* ------------------------------------------------------------------------
758 * Final confirmation of character.
759 * ------------------------------------------------------------------------ */
760static enum birth_stage get_confirm_command(void)
761{
762	const char *prompt = "['ESC' to step back, 'S' to start over, or any other key to continue]";
763	ui_event_data ke;
764
765	enum birth_stage next;
766
767	/* Prompt for it */
768	prt(prompt, Term->hgt - 1, Term->wid / 2 - strlen(prompt) / 2);
769	
770	/* Buttons */
771	button_kill_all();
772	button_add("[Continue]", 'q');
773	button_add("[ESC]", ESCAPE);
774	button_add("[S]", 'S');
775	redraw_stuff();
776	
777	/* Get a key */
778	ke = inkey_ex();
779	
780	/* Start over */
781	if (ke.key == 'S' || ke.key == 's')
782	{
783		next = BIRTH_RESET;
784	}
785	else if (ke.key == KTRL('X'))
786	{
787		cmd_insert(CMD_QUIT);
788		next = BIRTH_COMPLETE;
789	}
790	else if (ke.key == ESCAPE)
791	{
792		next = BIRTH_BACK;
793	}
794	else
795	{
796		cmd_insert(CMD_ACCEPT_CHARACTER);
797		next = BIRTH_COMPLETE;
798	}
799	
800	/* Buttons */
801	button_kill_all();
802	redraw_stuff();
803
804	/* Clear prompt */
805	clear_from(23);
806
807	return next;
808}
809
810
811
812/* ------------------------------------------------------------------------
813 * Things that relate to the world outside this file: receiving game events
814 * and being asked for game commands.
815 * ------------------------------------------------------------------------ */
816
817/*
818 * This is called when we receive a request for a command in the birth 
819 * process.
820
821 * The birth process continues until we send a final character confirmation
822 * command (or quit), so this is effectively called in a loop by the main
823 * game.
824 *
825 * We're imposing a step-based system onto the main game here, so we need
826 * to keep track of where we're up to, where each step moves on to, etc.
827 */
828errr get_birth_command(bool wait)
829{
830	static enum birth_stage current_stage = BIRTH_RESET;
831	static enum birth_stage prev;
832	static enum birth_stage roller = BIRTH_RESET;
833	enum birth_stage next = current_stage;
834
835	switch (current_stage)
836	{
837		case BIRTH_RESET:
838		{
839			cmd_insert(CMD_BIRTH_RESET, TRUE);
840			roller = BIRTH_RESET;
841			
842			if (quickstart_allowed)
843				next = BIRTH_QUICKSTART;
844			else
845				next = BIRTH_SEX_CHOICE;
846
847			break;
848		}
849
850		case BIRTH_QUICKSTART:
851		{
852			display_player(0);
853			next = get_quickstart_command();
854			break;
855		}
856
857		case BIRTH_SEX_CHOICE:
858		//case BIRTH_CLASS_CHOICE:
859		case BIRTH_RACE_CHOICE:
860		case BIRTH_ROLLER_CHOICE:
861		{
862			menu_type *menu = &sex_menu;
863			cmd_code command = CMD_CHOOSE_SEX;
864
865			Term_clear();
866			print_menu_instructions();
867
868			if (current_stage > BIRTH_SEX_CHOICE)
869			{
870				menu_refresh(&sex_menu);
871				menu = &race_menu;
872				command = CMD_CHOOSE_RACE;
873			}
874			
875			if (current_stage > BIRTH_RACE_CHOICE)
876			{
877				menu_refresh(&race_menu);
878				menu = &roller_menu;
879				command = CMD_NULL;
880			}
881
882			next = menu_question(current_stage, menu, command);
883
884			if (next == BIRTH_BACK)
885				next = current_stage - 1;
886
887			/* Make sure that the character gets reset before quickstarting */
888			if (next == BIRTH_QUICKSTART) 
889				next = BIRTH_RESET;
890
891			break;
892		}
893
894		case BIRTH_POINTBASED:
895		{
896			roller = BIRTH_POINTBASED;
897	
898			if (prev > BIRTH_POINTBASED)
899				point_based_start();
900
901			next = point_based_command();
902
903			if (next == BIRTH_BACK)
904				next = BIRTH_ROLLER_CHOICE;
905
906			if (next != BIRTH_POINTBASED)
907				point_based_stop();
908
909			break;
910		}
911
912		case BIRTH_ROLLER:
913		{
914			roller = BIRTH_ROLLER;
915			next = roller_command(prev < BIRTH_ROLLER);
916			if (next == BIRTH_BACK)
917				next = BIRTH_ROLLER_CHOICE;
918
919			break;
920		}
921
922		case BIRTH_NAME_CHOICE:
923		{
924			if (prev < BIRTH_NAME_CHOICE)
925				display_player(0);
926
927			next = get_name_command();
928			if (next == BIRTH_BACK)
929				next = roller;
930
931			break;
932		}
933
934		case BIRTH_FINAL_CONFIRM:
935		{
936			if (prev < BIRTH_FINAL_CONFIRM)
937				display_player(0);
938
939			next = get_confirm_command();
940			if (next == BIRTH_BACK)
941				next = BIRTH_NAME_CHOICE;
942
943			break;
944		}
945
946		default:
947		{
948			/* Remove dodgy compiler warning, */
949		}
950	}
951
952	prev = current_stage;
953	current_stage = next;
954
955	return 0;
956}
957
958/*
959 * Called when we enter the birth mode - so we set up handlers, command hooks,
960 * etc, here.
961 */
962static void ui_enter_birthscreen(game_event_type type, game_event_data *data, void *user)
963{
964	/* Set the ugly static global that tells us if quickstart's available. */
965	quickstart_allowed = data->flag;
966
967	setup_menus();
968}
969
970static void ui_leave_birthscreen(game_event_type type, game_event_data *data, void *user)
971{
972	free_birth_menus();
973}
974
975
976void ui_init_birthstate_handlers(void)
977{
978	event_add_handler(EVENT_ENTER_BIRTH, ui_enter_birthscreen, NULL);
979	event_add_handler(EVENT_LEAVE_BIRTH, ui_leave_birthscreen, NULL);
980}
981