view srogue/move.c @ 315:ad2570b5b21f

Advanced Rogue 5, 7: fix some trading post messages. When attempting to buy an unaffordable object, messages were often of the form "You can't afford that a scroll of hold monster !", because the object description (stored in curpurch) was the same text used in inventory displays. This has been worked around by inspecting the contents of curpurch and using different message templates.
author John "Elwin" Edwards
date Sun, 24 Oct 2021 20:26:21 -0400
parents e52a8a7ad4c5
children
line wrap: on
line source

/*
 * Hero movement commands
 *
 * @(#)move.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 <string.h>
#include <ctype.h>
#include "rogue.h"
#include "rogue.ext"

/*
 * Used to hold the new hero position
 */

struct coord nh;

/*
 * do_run:
 *	Start the hero running
 */

void
do_run(char ch)
{
	running = TRUE;
	after = FALSE;
	runch = ch;
}

/*
 * do_move:
 *	Check to see that a move is legal.  If it is handle the
 *	consequences (fighting, picking up, etc.)
 */

void
do_move(int dy, int dx)
{
	reg int ch;
	reg struct room *rp;

	firstmove = FALSE;
	curprice = -1;
	inpool = FALSE;

	if (player.t_nomove > 0) {
		player.t_nomove -= 1;
		msg("You are still stuck in the bear trap.");
		return;
	}
	/*
	 * Do a confused move (maybe)
	 */
	if ((rnd(100) < 80 && pl_on(ISHUH)) ||
	  (iswearing(R_DELUS) && rnd(100) < 25))
		nh = *rndmove(&player);
	else {
		nh.y = hero.y + dy;
		nh.x = hero.x + dx;
	}
	/*
	 * Check if he tried to move off the screen or make
	 *  an illegal diagonal move, and stop him if he did.
	 */
	if (!cordok(nh.y, nh.x) ||
	  (pl_off(ISETHER) && !diag_ok(&hero, &nh))) {
		after = running = FALSE;
		return;
	}
	if (running) {
		ch = winat(nh.y, nh.x);
		if (dead_end(ch)) {
			reg int gox, goy, apsg, whichway;

			gox = goy = apsg = 0;
			if (dy == 0) {
				ch = show(hero.y+1,hero.x);
				if (ch == PASSAGE) {
					apsg += 1;
					goy = 1;
				}
				ch = show(hero.y-1,hero.x);
				if (ch == PASSAGE) {
					apsg += 1;
					goy = -1;
				}
			}
			else if (dx == 0) {
				ch = show(hero.y,hero.x+1);
				if (ch == PASSAGE) {
					gox = 1;
					apsg += 1;
				}
				ch = show(hero.y,hero.x-1);
				if (ch == PASSAGE) {
					gox = -1;
					apsg += 1;
				}
			}
			if (apsg != 1) {
				running = after = FALSE;
				return;
			}
			else {			/* can still run here */
				nh.y = hero.y + goy;
				nh.x = hero.x + gox;
				whichway = (goy + 1) * 3 + gox + 1;
				switch(whichway) {
					case 0: runch = 'y';
					when 1: runch = 'k';
					when 2: runch = 'u';
					when 3: runch = 'h';
					when 4: runch = '.';	/* shouldn't do */
					when 5: runch = 'l';
					when 6: runch = 'b';
					when 7: runch = 'j';
					when 8: runch = 'n';
				}
			}
		}
	}
	if (running && ce(hero, nh))
		after = running = FALSE;
	ch = winat(nh.y, nh.x);
	if (pl_on(ISHELD) && ch != 'F' && ch != 'd') {
		msg("You are being held.");
		return;
	}
	if (pl_off(ISETHER)) {
		if (isatrap(ch)) {
			ch = be_trapped(&nh, &player);
			if (nlmove) {
				nlmove = FALSE;
				return;
			}
			else if (ch == POOL)
				inpool = TRUE;
		}
	 	else if (dead_end(ch)) {
			after = running = FALSE;
			return;
		}
		else {
			switch(ch) {
				case GOLD:	case POTION:	case SCROLL:
				case FOOD:	case WEAPON:	case ARMOR:
				case RING:	case AMULET:	case STICK:
					running = FALSE;
					take = ch;
				default:
					if (illeg_ch(ch)) {
						running = FALSE;
						mvaddch(nh.y, nh.x, FLOOR);
						teleport(rndspot, &player);
						light(&nh);
						msg("The spatial warp disappears !");
						return;
					}
			}
		}
	}
	rp = roomin(&nh);
	if (ch == DOOR) {		/* just stepped on a door */
		running = FALSE;
		if (rp != NULL && rf_on(rp, ISTREAS)) {
			struct linked_list *item;
			struct thing *tp;

			for (item = mlist; item != NULL; item = next(item)) {
				tp = THINGPTR(item);
				if (tp->t_room == rp)
					runto(&tp->t_pos, &hero);
			}
		}
	}
	else if (ch == STAIRS && pl_off(ISETHER))
		running = FALSE;
	else if (isalpha(ch) && pl_off(ISETHER)) {
		running = FALSE;
		fight(&nh, cur_weapon, FALSE);
		return;
	}
	if (rp == NULL && player.t_room != NULL)
		light(&hero);		/* exiting a room */
	else if (rp != NULL && player.t_room == NULL)
		light(&nh);			/* just entering a room */
	if (pl_on(ISBLIND))
		ch = ' ';
	else
		ch = player.t_oldch;
	mvwaddch(cw, hero.y, hero.x, ch);
	mvwaddch(cw, nh.y, nh.x, PLAYER);
	hero = nh;
	player.t_room = rp;
	player.t_oldch = mvinch(hero.y, hero.x) & A_CHARTEXT;
}

/*
 * Called to illuminate a room.
 * If it is dark, remove anything that might move.
 */
void
light(struct coord *cp)
{
	reg struct room *rp;
	reg int j, k, x, y;
	reg char ch, rch;
	reg struct linked_list *item;

	rp = roomin(cp);
	if (rp == NULL)
		return;
	if (pl_on(ISBLIND)) {
		for (j = 0; j < rp->r_max.y; j += 1) {
			for (k = 0; k < rp->r_max.x; k += 1) {
				y = rp->r_pos.y + j;
				x = rp->r_pos.x + k;
				mvwaddch(cw, y, x, ' ');
			}
		}
		look(FALSE);
		return;
	}
	if (iswearing(R_LIGHT))
		rp->r_flags &= ~ISDARK;
	for (j = 0; j < rp->r_max.y; j += 1) {
		for (k = 0; k < rp->r_max.x; k += 1) {
			y = rp->r_pos.y + j;
			x = rp->r_pos.x + k;
			if (levtype == MAZELEV && !cansee(y, x))
				continue;
			ch = show(y, x);
			wmove(cw, y, x);
			/*
			 * Figure out how to display a secret door
			 */
			if (ch == SECRETDOOR) {
				if (j == 0 || j == rp->r_max.y - 1)
					ch = '-';
				else
					ch = '|';
			}
			if (isalpha(ch)) {
				struct thing *mit;

				item = wake_monster(y, x);
				if (item == NULL) {
					ch = FLOOR;
					mvaddch(y, x, ch);
				}
				else {
					mit = THINGPTR(item);
					if (mit->t_oldch == ' ')
						if (!rf_on(rp,ISDARK))
							mit->t_oldch = mvinch(y, x) & A_CHARTEXT;
					if (levtype == MAZELEV)
						ch = mvinch(y, x) & A_CHARTEXT;
				}
			}
			if (rf_on(rp,ISDARK)) {
				rch = mvwinch(cw, y, x) & A_CHARTEXT;
				if (isatrap(rch)) {
					ch = rch;			/* if its a trap */
				}
				else {					/* try other things */
					switch (rch) {
						case DOOR:	case STAIRS:	case '|':
						case '-':
							ch = rch;
						otherwise:
							ch = ' ';
					}
				}
			}
			mvwaddch(cw, y, x, ch);
		}
	}
}

/*
 * show:
 *	returns what a certain thing will display as to the un-initiated
 */
char
show(int y, int x)
{
	reg char ch = winat(y, x);
	reg struct linked_list *it;
	reg struct thing *tp;
	reg struct trap *ta;

	if (isatrap(ch)) {
		if ((ta = trap_at(y, x)) == NULL)
			return FLOOR;
		if (iswearing(R_FTRAPS))
			ta->tr_flags |= ISFOUND;
		return ((ta->tr_flags & ISFOUND) ? ta->tr_type : FLOOR);
	}
	if (ch == SECRETDOOR && iswearing(R_FTRAPS)) {
		mvaddch(y,x,DOOR);
		return DOOR;
	}
	if ((it = find_mons(y, x)) != NULL) {	/* maybe a monster */
		tp = THINGPTR(it);
		if (ch == 'M' || (tp->t_flags & ISINVIS)) {
			if (ch == 'M')
				ch = tp->t_disguise;
			else if (pl_off(CANSEE)) {
				if (ch == 's')
					ch = ' ';		/* shadows show as a blank */
				else
					ch = mvinch(y, x) & A_CHARTEXT;	/* hide invisibles */
			}
		}
	}
	return ch;
}

/*
 * be_trapped:
 *	Hero or monster stepped on a trap.
 */
int
be_trapped(struct coord *tc, struct thing *th)
{
	reg struct trap *trp;
	reg int ch, ishero;
	char stuckee[35], seeit, sayso;

	if ((trp = trap_at(tc->y, tc->x)) == NULL)
		return 0;
	ishero = (th == &player);
	if (ishero) {
		strcpy(stuckee, "You");
		count = running = FALSE;
	}
	else {
		sprintf(stuckee, "The %s", monsters[th->t_indx].m_name);
	}
	seeit = cansee(tc->y, tc->x);
	if (seeit)
		mvwaddch(cw, tc->y, tc->x, trp->tr_type);
	trp->tr_flags |= ISFOUND;
	sayso = TRUE;
	switch (ch = trp->tr_type) {
		case POST:
			if (ishero) {
				nlmove = TRUE;
				new_level(POSTLEV);
			}
			else
				goto goner;
		when MAZETRAP:
			if (ishero) {
				nlmove = TRUE;
				level += 1;
				new_level(MAZELEV);
				msg("You are surrounded by twisty passages!");
			}
			else
				goto goner;
		when TELTRAP:
			nlmove = TRUE;
			teleport(trp->tr_goto, th);
		when TRAPDOOR:
			if (ishero) {
				level += 1;
				new_level(NORMLEV);
			}
			else {		/* monsters get lost */
goner:
				ch = GONER;
			}
			nlmove = TRUE;
			if (seeit && sayso)
				msg("%s fell into a trap!", stuckee);
		when BEARTRAP:
			th->t_nomove += BEARTIME;
			if (seeit) {
				strcat(stuckee, (ishero ? " are" : " is"));
				msg("%s caught in a bear trap.", stuckee);
			}
		when SLEEPTRAP:
			if (ishero && pl_on(ISINVINC))
				msg("You feel momentarily dizzy.");
			else {
				if (ishero)
					th->t_nocmd += SLEEPTIME;
				else
					th->t_nomove += SLEEPTIME;
				if (seeit)
					msg("%s fall%s asleep in a strange white mist.",
					  stuckee, (ishero ? "":"s"));
			}
		when ARROWTRAP: {
			int resist, ac;
			struct stats *it;

			stuckee[0] = tolower(stuckee[0]);
			it = &th->t_stats;
			if (ishero && cur_armor != NULL)
				ac = cur_armor->o_ac;
			else
				ac = it->s_arm;
			resist = ac + getpdex(it, FALSE);
			if (ishero && pl_on(ISINVINC))
				resist = -100;		/* invincible is impossible to hit */
			if (swing(3 + (level / 4), resist, 1)) {
				if (seeit)
					msg("%sAn arrow shot %s.", (ishero ? "Oh no! " : ""),
					  stuckee);
				if (ishero)
					chg_hpt(-roll(1,6),FALSE,K_ARROW);
				else {
					it->s_hpt -= roll(1,6);
					if (it->s_hpt < 1) {
						sayso = FALSE;
						goto goner;
					}
				}
			}
			else {
				struct linked_list *item;
				struct object *arrow;

				if (seeit)
					msg("An arrow shoots past %s.", stuckee);
				item = new_thing(FALSE, WEAPON, ARROW);
				arrow = OBJPTR(item);
				arrow->o_hplus = 3;
				arrow->o_dplus = rnd(2);
				arrow->o_count = 1;
		 		arrow->o_pos = th->t_pos;
				fall(item, FALSE);
			}
		}
		when DARTTRAP: {
			int resist, ac;
			struct stats *it;

			stuckee[0] = tolower(stuckee[0]);
			it = &th->t_stats;
			if (ishero && cur_armor != NULL)
				ac = cur_armor->o_ac;
			else
				ac = it->s_arm;
			resist = ac + getpdex(it, FALSE);
			if (ishero && pl_on(ISINVINC))
				resist = -100;		/* invincible is impossible to hit */
			if (swing(3 + (level / 4), resist, 0)) {
				if (seeit)
					msg("A small dart just hit %s.", stuckee);
				if (ishero) {
					if (!save(VS_POISON))
						chg_abil(CON,-1,TRUE);
					if (!iswearing(R_SUSTSTR))
						chg_abil(STR,-1,TRUE);
					chg_hpt(-roll(1, 4),FALSE,K_DART);
				}
				else {
					if (!save_throw(VS_POISON, th))
						it->s_ef.a_str -= 1;
					it->s_hpt -= roll(1, 4);
					if (it->s_hpt < 1) {
						sayso = FALSE;
						goto goner;
					}
				}
			}
			else if (seeit)
				msg("A small dart whizzes by %s.", stuckee);
		}
	    when POOL:
			if (!ishero && rnd(100) < 10) {
				if (seeit)
					msg("The %s drowns !!", stuckee);
				goto goner;
			}
			if ((trp->tr_flags & ISGONE) && rnd(100) < 10) {
				nlmove = TRUE;
				if (rnd(100) < 15)
					teleport(rndspot, th);	   /* teleport away */
				else if(rnd(100) < 15 && level > 2) {
					level -= rnd(2) + 1;
					new_level(NORMLEV);
					msg("You here a faint groan from below.");
				}
				else if(rnd(100) < 40) {
					level += rnd(4);
					new_level(NORMLEV);
					msg("You find yourself in strange surroundings.");
				}
				else if(rnd(100) < 6 && pl_off(ISINVINC)) {
					msg("Oh no!!! You drown in the pool!!! --More--");
					wait_for(cw, ' ');
					death(K_POOL);
				}
				else
					 nlmove = FALSE;
		}
	}
	flushinp();		/* flush typeahead */
	return ch;
}

/*
 * dip_it:
 *	Dip an object into a magic pool
 */
void
dip_it(void)
{
	reg struct linked_list *what;
	reg struct object *ob;
	reg struct trap *tp;
	reg int wh;

	tp = trap_at(hero.y,hero.x);
	if (tp == NULL || inpool == FALSE || (tp->tr_flags & ISGONE))
		return;

	if ((what = get_item("dip",0)) == NULL)
		return;
	ob = OBJPTR(what);
	mpos = 0;
	/*
	 * If hero is trying to dip an object OTHER than his
	 * current weapon, make sure that he could drop his
	 * current weapon
	 */
	if (ob != cur_weapon) {
		if (cur_weapon != NULL && o_on(cur_weapon, ISCURSED)) {
			msg("You are unable to release your weapon.");
			after = FALSE;
			return;
		}
	}
	if (ob == cur_armor) {
		msg("You have to take off your armor before you can dip it.");
		after = FALSE;
		return;
	}
	else if (ob == cur_ring[LEFT] || ob == cur_ring[RIGHT]) {
		msg("You have to take that ring off before you can dip it.");
		after = FALSE;
		return;
	}
	wh = ob->o_which;
	tp->tr_flags |= ISGONE;
	if (ob != NULL && o_off(ob,ISPROT)) {
		setoflg(ob,ISKNOW);
		switch(ob->o_type) {
		case WEAPON:
			if(rnd(100) < 20) {		/* enchant weapon here */
				if (o_off(ob,ISCURSED)) {
					ob->o_hplus += 1;
					ob->o_dplus += 1;
				}
				else {		/* weapon was prev cursed here */
					ob->o_hplus = rnd(2);
					ob->o_dplus = rnd(2);
				}
				resoflg(ob,ISCURSED);
			}
			else if(rnd(100) < 10) {	/* curse weapon here */
				if (o_off(ob,ISCURSED)) {
					ob->o_hplus = -(rnd(2)+1);
					ob->o_dplus = -(rnd(2)+1);
				}
				else {			/* if already cursed */
					ob->o_hplus--;
					ob->o_dplus--;
				}
				setoflg(ob,ISCURSED);
			}			
			msg("The %s glows for a moment.",w_magic[wh].mi_name);
		when ARMOR:
			if (rnd(100) < 30) {			/* enchant armor */
				if(o_off(ob,ISCURSED))
					ob->o_ac -= rnd(2) + 1;
				else
					ob->o_ac = -rnd(3)+ armors[wh].a_class;
				resoflg(ob,ISCURSED);
			}
			else if(rnd(100) < 15){			/* curse armor */
				if (o_off(ob,ISCURSED))
					ob->o_ac = rnd(3)+ armors[wh].a_class;
				else
					ob->o_ac += rnd(2) + 1;
				setoflg(ob,ISCURSED);
			}
			msg("The %s glows for a moment.",a_magic[wh].mi_name);
		when STICK: {
			int i;
			struct rod *rd;

			i = rnd(8) + 1;
			if(rnd(100) < 25)		/* add charges */
				ob->o_charges += i;
			else if(rnd(100) < 10) {	/* remove charges */
				if ((ob->o_charges -= i) < 0)
					ob->o_charges = 0;
			}
			ws_know[wh] = TRUE;
			rd = &ws_stuff[wh];
			msg("The %s %s glows for a moment.",rd->ws_made,rd->ws_type);
		}
		when SCROLL:
			s_know[wh] = TRUE;
			msg("The '%s' scroll unfurls.",s_names[wh]);
		when POTION:
			p_know[wh] = TRUE;
			msg("The %s potion bubbles for a moment.",p_colors[wh]);
		when RING:
			r_know[wh] = TRUE;
			if (magring(ob)) {
				if(rnd(100) < 25) {	 		/* enchant ring */
					if (o_off(ob,ISCURSED))
						ob->o_ac += rnd(2) + 1;
					else
						ob->o_ac = rnd(2) + 1;
					resoflg(ob,ISCURSED);
				}
				else if(rnd(100) < 10) {	 /* curse ring */
					if (o_off(ob,ISCURSED))
						ob->o_ac = -(rnd(2) + 1);
					else
						ob->o_ac -= (rnd(2) + 1);
					setoflg(ob,ISCURSED);
				}
			}
			msg("The %s ring vibrates for a moment.",r_stones[wh]);
		otherwise:
			msg("The pool bubbles for a moment.");
		}
	}
	cur_weapon = ob;	/* hero has to weild item to dip it */
}


/*
 * trap_at:
 *	Find the trap at (y,x) on screen.
 */
struct trap *
trap_at(int y, int x)
{
	reg struct trap *tp, *ep;

	ep = &traps[ntraps];
	for (tp = traps; tp < ep; tp += 1)
		if (tp->tr_pos.y == y && tp->tr_pos.x == x)
			break;
	if (tp >= ep)
		tp = NULL;
	return tp;
}

/*
 * rndmove:
 *	move in a random direction if the monster/person is confused
 */
struct coord *
rndmove(struct thing *who)
{
	reg int x, y, ex, ey, ch;
	int nopen = 0;
	struct linked_list *item;
	static struct coord ret;  /* what we will be returning */
	static struct coord dest;

	ret = who->t_pos;
	/*
	 * Now go through the spaces surrounding the player and
	 * set that place in the array to true if the space can be
	 * moved into
	 */
	ey = ret.y + 1;
	ex = ret.x + 1;
	for (y = who->t_pos.y - 1; y <= ey; y += 1) {
		for (x = who->t_pos.x - 1; x <= ex; x += 1) {
			if (!cordok(y, x))
				continue;
			ch = winat(y, x);
			if (step_ok(ch)) {
				dest.y = y;
				dest.x = x;
				if (!diag_ok(&who->t_pos, &dest))
					continue;
				if (ch == SCROLL && who != &player) {
					/*
					 * check for scare monster scrolls
					 */
					item = find_obj(y, x);
					if (item != NULL && (OBJPTR(item))->o_which == S_SCARE)
						continue;
				}
				if (rnd(++nopen) == 0)
					ret = dest;
			}
		}
	}
	return &ret;
}

/*
 * isatrap:
 *	Returns TRUE if this character is some kind of trap
 */
bool
isatrap(char ch)
{
	switch(ch) {
		case POST:
		case DARTTRAP:
		case POOL:
		case TELTRAP:
		case TRAPDOOR:
		case ARROWTRAP:
		case SLEEPTRAP:
		case BEARTRAP:
		case MAZETRAP:
			return TRUE;
		default:
			return FALSE;
	}
}