view srogue/command.c @ 114:a5433ba4cabf

arogue5: fix some daemon-related pointer/int casting. Daemons and fuses take a single argument, nominally an int but either ignored or unsafely cast to a pointer. Its type has now been changed to void*. The save/restore code no longer tries to store this argument in the savefile. For doctor(), this is not a problem, because player is the only argument it is ever given as a daemon. However, alchemy() will fail to do anything when passed NULL. Fixing this would be complicated but possible. Summary: the code is slightly safer, but alchemy jugs are guaranteed to stop working after save and restore, instead of just extremely likely.
author John "Elwin" Edwards
date Fri, 28 Mar 2014 10:57:03 -0700
parents 1e88eb1942a5
children 458df24e973d
line wrap: on
line source

/*
 * Read and execute the user commands
 *
 * @(#)command.c	9.0	(rdk)	 7/17/84
 *
 * Super-Rogue
 * Copyright (C) 1984 Robert D. Kindelberger
 * All rights reserved.
 *
 * Based on "Rogue: Exploring the Dungeons of Doom"
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

#include <ctype.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "rogue.h"
#include "rogue.ext"
#ifdef __DJGPP__
#include <process.h>
#else
#include <unistd.h>
#endif

/*
 * command:
 *	Process the user commands
 */
command()
{
	reg char ch;
	reg int ntimes = 1;		/* Number of player moves */
	static char countch, direction, newcount = FALSE;

	if (pl_on(ISHASTE))
		ntimes++;
	/*
	 * Let the daemons start up
	 */
	while (ntimes-- > 0) {
		do_daemons(BEFORE);
		look(TRUE);
		if (!running)
			door_stop = FALSE;
		lastscore = purse;
		wmove(cw, hero.y, hero.x);
		if (!(running || count))
			draw(cw);			/* Draw screen */
		take = 0;
		after = TRUE;
		/*
		 * Read command or continue run
		 */
		if (wizard)
			waswizard = TRUE;
		if (player.t_nocmd <= 0) {
			player.t_nocmd = 0;
			if (running)
				ch = runch;
			else if (count)
				ch = countch;
			else {
				ch = readchar();
				if (mpos != 0 && !running) /* Erase message if its there */
					msg("");
			}
		}
		else
			ch = '.';
		if (player.t_nocmd > 0) {
			if (--player.t_nocmd <= 0)
				msg("You can move again.");	
		}
		else {
			/*
			 * check for prefixes
			 */
			if (isdigit(ch)) {
				count = 0;
				newcount = TRUE;
				while (isdigit(ch)) {
					count = count * 10 + (ch - '0');
					ch = readchar();
				}
				countch = ch;
				/*
				 * turn off count for commands which don't make sense
				 * to repeat
				 */
				switch (ch) {
					case 'h': case 'j': case 'k': case 'l':
					case 'y': case 'u': case 'b': case 'n':
					case 'H': case 'J': case 'K': case 'L':
					case 'Y': case 'U': case 'B': case 'N':
					case 'q': case 'r': case 's': case 'f':
					case 't': case 'C': case 'I': case '.':
					case 'z': case 'p':
						break;
					default:
						count = 0;
				}
			}
			switch (ch) {
			case 'f':
			case 'g':
				if (pl_off(ISBLIND)) {
					door_stop = TRUE;
					firstmove = TRUE;
				}
				if (count && !newcount)
					ch = direction;
				else
					ch = readchar();
				switch (ch) {
					case 'h': case 'j': case 'k': case 'l':
					case 'y': case 'u': case 'b': case 'n':
						ch = toupper(ch);
				}
				direction = ch;
			}
			newcount = FALSE;
			/*
			 * execute a command
			 */
			if (count && !running)
				count--;
			switch (ch) {
				case '!' : shell(); after = FALSE;
				when 'h' : do_move(0, -1);
				when 'j' : do_move(1, 0);
				when 'k' : do_move(-1, 0);
				when 'l' : do_move(0, 1);
				when 'y' : do_move(-1, -1);
				when 'u' : do_move(-1, 1);
				when 'b' : do_move(1, -1);
				when 'n' : do_move(1, 1);
				when 'H' : do_run('h');
				when 'J' : do_run('j');
				when 'K' : do_run('k');
				when 'L' : do_run('l');
				when 'Y' : do_run('y');
				when 'U' : do_run('u');
				when 'B' : do_run('b');
				when 'N' : do_run('n');
				when 't':
					if (!get_dir())
						after = FALSE;
					else
						missile(delta.y, delta.x);
				when 'Q' : after = FALSE; quit(-1);
				when 'i' : after = FALSE; inventory(pack, 0);
				when 'I' : after = FALSE; picky_inven();
				when 'd' : drop(NULL);
				when 'q' : quaff();
				when 'r' : read_scroll();
				when 'e' : eat();
				when 'w' : wield();
				when 'W' : wear();
				when 'T' : take_off();
				when 'P' : ring_on();
				when 'R' : ring_off();
				when 'O' : option();
				when 'c' : call();
				when '>' : after = FALSE; d_level();
				when '<' : after = FALSE; u_level();
				when '?' : after = FALSE; help();
				when '/' : after = FALSE; identify(0);
				when 's' : search();
				when 'z' : do_zap(FALSE);
				when 'p':
					if (get_dir())
						do_zap(TRUE);
					else
						after = FALSE;
				when 'v': after = FALSE; msg("Super Rogue version %s.",release);
				when 'D': dip_it();
				when CTRL('L') : after = FALSE; restscr(cw);
				when CTRL('R') : after = FALSE; msg(huh);
				when 'a': after = FALSE; dispmax();
				when '@' : if (author())
					msg("Hero @ %d,%d : Stairs @ %d,%d",hero.y,hero.x,stairs.y,stairs.x);
				when 'S' : 
					after = FALSE;
					if (save_game()) {
						wclear(cw);
						draw(cw);
						endwin();
						byebye(0);
					}
				when '.' : ;				/* Rest command */
				when ' ' : after = FALSE;	/* do nothing */
				when '=' :
					if (author()) {
						activity();
						after = FALSE;
					}
#ifdef WIZARD
				when CTRL('P') :
					after = FALSE;
					if (wizard) {
						wizard = FALSE;
						msg("Not wizard any more");
					}
					else {
						wizard = passwd();
						if (wizard) {
							msg("Welcome back, Bob!!!!!");
							waswizard = TRUE;
						}
						else
							msg("Sorry");
					}
#endif
				when ESCAPE :	/* Escape */
					door_stop = FALSE;
					count = 0;
					after = FALSE;
				when '#':
					if (levtype == POSTLEV)		/* buy something */
						buy_it();
					after = FALSE;
				when '$':
					if (levtype == POSTLEV)		/* price something */
						price_it();
					after = FALSE;
				when '%':
					if (levtype == POSTLEV)		/* sell something */
						sell_it();
					after = FALSE;
				otherwise :
					after = FALSE;
					if (wizard) switch (ch) {
					case CTRL('A') : ;
					when 'C'     :	create_obj(FALSE);
					when CTRL('I') :	inventory(lvl_obj, 1);
					when CTRL('W') :	whatis(NULL);
					when CTRL('D') :	level++; new_level(NORMLEV);
					when CTRL('U') :	if (level > 1) level--; new_level(NORMLEV);
					when CTRL('F') :	displevl();
					when CTRL('X') :	dispmons();
					when CTRL('T') :	teleport(rndspot,&player);
					when CTRL('E') :	msg("food left: %d", food_left);
					when CTRL('O') :	add_pass();
					when 'M' : {
						int tlev, whichlev;
						prbuf[0] = '\0';
						msg("Which level? ");
						if (get_str(prbuf,cw) == NORM) {
							whichlev = NORMLEV;
							tlev = atoi(prbuf);
							if (tlev < 1)
								level = 1;
							if (tlev >= 200) {
								tlev -= 199;
								whichlev = MAZELEV;
							}
							else if (tlev >= 100) {
								tlev -= 99;
								whichlev = POSTLEV;
							}
							level = tlev;
							new_level(whichlev);
						}
					}
					when CTRL('N') : {
						struct linked_list *item;

						item = get_item("charge", STICK);
						if (item != NULL) {
							(OBJPTR(item))->o_charges = 10000;
							msg("");
						}
					}
					when CTRL('H') : {
						int i;
						struct linked_list *item;
						struct object *obj;

						him->s_exp = e_levels[him->s_lvl + 7] + 1;
						check_level();
						/*
						 * Give the rogue a very good sword
						 */
						item = new_thing(FALSE, WEAPON, TWOSWORD);
						obj = OBJPTR(item);
						obj->o_hplus = 3;
						obj->o_dplus = 3;
						obj->o_flags = ISKNOW;
						i = add_pack(item, TRUE);
						if (i)
							cur_weapon = obj;
						else
							discard(item);
						/*
						 * And his suit of armor
						 */
						item = new_thing(FALSE, ARMOR, PLATEARMOR);
						obj = OBJPTR(item);
						obj->o_ac = -8;
						obj->o_flags = ISKNOW;
						i = add_pack(item, TRUE);
						if (i)
							cur_armor = obj;
						else
							discard(item);
						nochange = FALSE;
					}
					otherwise:
						msg(illegal, unctrl(ch));
						count = 0;
				}
				else {
					msg(illegal, unctrl(ch));
					count = 0;
				}
			}
			/*
			 * turn off flags if no longer needed
			 */
			if (!running)
				door_stop = FALSE;
		}
		/*
		 * If he ran into something to take, let the
		 * hero pick it up if not in a trading post.
		 */
		if (take != 0 && levtype != POSTLEV)
			pick_up(take);
		if (!running)
			door_stop = FALSE;
	}
	/*
	 * Kick off the rest if the daemons and fuses
	 */
	if (after) {
		int  j;

		look(FALSE);
		do_daemons(AFTER);
		do_fuses();
		if (pl_on(ISSLOW))
			waste_time();
		for (j = LEFT; j <= RIGHT; j++) {
			if (cur_ring[j] != NULL) {
				if (cur_ring[j]->o_which == R_SEARCH)
					search();
				else if (cur_ring[j]->o_which == R_TELEPORT)
					if (rnd(100) < 5)
						teleport(rndspot, &player);
	 		}
		}
	}
}


/*
 * quit:
 *	Have player make certain, then exit.
 */
void
quit(int a)
{
	reg char ch, good;
	/*
	 * Reset the signal in case we got here via an interrupt
	 */
	if (signal(SIGINT, quit) != quit)
		mpos = 0;
	msg("Really quit? [y/n/s]");
/*	ch = tolower(readchar());*/
	ch = readchar();
	if (ch == 'y') {
		clear();
		move(LINES-1, 0);
		refresh();
		writelog(purse, CHICKEN, 0);
		score(purse, CHICKEN, 0);
		printf("[Press return to exit]\n");
		fflush(NULL);
		getchar();
		byebye(0);
	}
	else if (ch == 's') {
		good = save_game();
		if (good) {
			wclear(cw);
			draw(cw);
			endwin();
			byebye(0);
		}
	}
	else {
		signal(SIGINT, quit);
		wmove(cw, 0, 0);
		wclrtoeol(cw);
		draw(cw);
		mpos = 0;
		count = 0;
		nochange = FALSE;
	}
}

/*
 * search:
 *	Player gropes about him to find hidden things.
 */

search()
{
	reg int x, y;
	reg char ch;

	/*
	 * Look all around the hero, if there is something hidden there,
	 * give him a chance to find it.  If its found, display it.
	 */
	if (pl_on(ISBLIND))
		return;
	for (x = hero.x - 1; x <= hero.x + 1; x++) {
		for (y = hero.y - 1; y <= hero.y + 1; y++) {
			ch = winat(y, x);
			if (isatrap(ch)) {		/* see if its a trap */
				reg struct trap *tp;

				if ((tp = trap_at(y, x)) == NULL)
					break;
				if (tp->tr_flags & ISFOUND)
					break;		/* no message if its seen */
				if (mvwinch(cw, y, x) == ch)
					break;
				if (rnd(100) > (him->s_lvl * 9 + herowis() * 5))
					 break;
				tp->tr_flags |= ISFOUND;
				mvwaddch(cw, y, x, tp->tr_type);
				count = 0;
				running = FALSE;
				msg(tr_name(tp->tr_type));
			}
			else if(ch == SECRETDOOR) {
				if (rnd(100) < (him->s_lvl * 4 + herowis() * 5)) {
					mvaddch(y, x, DOOR);
					count = 0;
				}
			}
		}
	}
}

/*
 * help:
 *	Give single character help, or the whole mess if he wants it
 */
help()
{
	extern struct h_list helpstr[];
	reg struct h_list *strp;
	reg char helpch;
	reg int cnt;

	strp = &helpstr[0];
	msg("Character you want help for (* for all): ");
	helpch = readchar();
	mpos = 0;
	/*
	 * If its not a *, print the right help string
	 * or an error if he typed a funny character.
	 */
	if (helpch != '*') {
		wmove(cw, 0, 0);
		while (strp->h_ch) {
			if (strp->h_ch == helpch) {
				msg("%s%s", unctrl(strp->h_ch), strp->h_desc);
				break;
			}
			strp++;
		}
		if (strp->h_ch != helpch)
			msg("Unknown character '%s'", unctrl(helpch));
		return;
	}
	/*
	 * Here we print help for everything.
	 * Then wait before we return to command mode
	 */
	wclear(hw);
	cnt = 0;
	while (strp->h_ch) {
		mvwaddstr(hw, cnt % 23, cnt > 22 ? 40 : 0, unctrl(strp->h_ch));
		waddstr(hw, strp->h_desc);
		cnt++;
		strp++;
	}
	wmove(hw, LINES-1, 0);
	wprintw(hw,spacemsg);
	draw(hw);
	wait_for(hw,' ');
	wclear(hw);
	draw(hw);
	wmove(cw, 0, 0);
	wclrtoeol(cw);
	touchwin(cw);
	nochange = FALSE;
}


/*
 * identify:
 *	Tell the player what a certain thing is.
 */
char *
identify(what)
int what;
{
	reg char ch, *str;

	if (what == 0) {
		msg("What do you want identified? ");
		ch = readchar();
		mpos = 0;
		if (ch == ESCAPE) {
			msg("");
			return NULL;
		}
	}
	else
		ch = what;
	if (isalpha(ch))
		str = monsters[midx(ch)].m_name;
	else {
		switch(ch) {
			case '|':
			case '-':	str = "the wall of a room";
			when GOLD:	str = "gold";
			when STAIRS:	str = "passage leading up/down";
			when DOOR:	str = "door";
			when FLOOR:	str = "room floor";
			when PLAYER:	str = "you";
			when PASSAGE:	str = "passage";
			when POST:	str = "trading post";
			when MAZETRAP:	str = "maze trap";
			when TRAPDOOR:	str = "trapdoor";
			when ARROWTRAP:	str = "arrow trap";
			when SLEEPTRAP:	str = "sleeping gas trap";
			when BEARTRAP:	str = "bear trap";
			when TELTRAP:	str = "teleport trap";
			when DARTTRAP:	str = "dart trap";
			when POOL:	str = "magic pool";
			when POTION:	str = "potion";
			when SCROLL:	str = "scroll";
			when FOOD:	str = "food";
			when WEAPON:	str = "weapon";
			when ' ' :	str = "solid rock";
			when ARMOR:	str = "armor";
			when AMULET:	str = "The Amulet of Yendor";
			when RING:	str = "ring";
			when STICK:	str = "wand or staff";
			otherwise:
				if (what == 0)
					str = "unknown character";
				else
					str = "a magical ghost";
		}
	}
	if (what == 0)
		msg("'%s' : %s", unctrl(ch), str);
	return str;
}

/*
 * d_level:
 *	He wants to go down a level
 */
d_level()
{
	if (winat(hero.y, hero.x) != STAIRS)
		msg("I see no way down.");
	else {
		if (pl_on(ISHELD)) {
			msg("You are being held.");
			return;
		}
		level++;
		new_level(NORMLEV);
	}
}

/*
 * u_level:
 *	He wants to go up a level
 */
u_level()
{
	if (winat(hero.y, hero.x) == STAIRS)  {
		if (pl_on(ISHELD)) {
			msg("You are being held.");
			return;
		}
		else {				/* player not held here */
			if (amulet) {
				level--;
				if (level == 0)
					total_winner();
				new_level(NORMLEV);
				msg("You feel a wrenching sensation in your gut.");
				return;
			}
		}
	}
	msg("I see no way up.");
}


/*
 * Let him escape for a while
 */
shell()
{
	reg int pid;
	reg char *sh;
	int ret_status;

	/*
	 * Set the terminal back to original mode
	 */
	sh = getenv("SHELL");
	wclear(hw);
	wmove(hw, LINES-1, 0);
	draw(hw);
	endwin();
	in_shell = TRUE;
	fflush(stdout);
	/*
	 * Fork and do a shell
	 */
#ifndef __DJGPP__
	while((pid = fork()) < 0)
		sleep(1);
	if (pid == 0) {
		setuid(playuid);	/* Set back to original user */
		setgid(playgid);
		execl(sh == NULL ? "/bin/sh" : sh, "shell", "-i", 0);
		perror("No shelly");
		byebye(-1);
	}
	else {
		signal(SIGINT, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
		while (wait(&ret_status) != pid)
			continue;
		signal(SIGINT, quit);
		signal(SIGQUIT, endit);

#else
	{
		char shell[PATH_MAX];

		if (sh && *sh)
			strncpy(shell,sh,PATH_MAX);
		else
			sprintf(shell, "%s\\bin\\sh.exe", getenv("DJDIR"));

		if (spawnl(P_WAIT,shell, "shell", "-i", 0) == -1)
			msg("No shelly: %s", shell);

#endif
		printf("\n%s", retstr);
		fflush(stdout);
		nonl();
		noecho();
		crmode();
		in_shell = FALSE;
		wait_for(cw, '\n');
		restscr(cw);
	}
}


/*
 * call:
 *	Allow a user to call a potion, scroll, or ring something
 */
call()
{
	reg struct object *obj;
	reg struct linked_list *item;
	reg char **guess, *elsewise;
	int wh;

	if ((item = get_item("call", 0)) == NULL)
		return;
	obj = OBJPTR(item);
	wh = obj->o_which;
	switch (obj->o_type) {
	case RING:
		guess = r_guess;
		elsewise = (r_guess[wh] != NULL ? r_guess[wh] : r_stones[wh]);
	when POTION:
		guess = p_guess;
		elsewise = (p_guess[wh] != NULL ? p_guess[wh] : p_colors[wh]);
	when SCROLL:
		guess = s_guess;
		elsewise = (s_guess[wh] != NULL ? s_guess[wh] : s_names[wh]);
	when STICK:
		guess = ws_guess;
		elsewise =(ws_guess[wh] != NULL ?
			ws_guess[wh] : ws_stuff[wh].ws_made);
	otherwise:
		msg("You can't call %ss anything",obj->o_typname);
		return;
	}
	msg("Was called \"%s\"", elsewise);
	msg(callit);
	if (guess[wh] != NULL)
		free(guess[wh]);
	strcpy(prbuf, elsewise);
	if (get_str(prbuf, cw) == NORM) {
		guess[wh] = new(strlen(prbuf) + 1);
		strcpy(guess[wh], prbuf);
	}
}