diff arogue7/actions.c @ 125:adfa37e67084

Import Advanced Rogue 7.7 from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Fri, 08 May 2015 15:24:40 -0400
parents
children 1cd604c827a3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/arogue7/actions.c	Fri May 08 15:24:40 2015 -0400
@@ -0,0 +1,982 @@
+/*
+ * actions.c  -  functions for dealing with monster actions
+ *
+ * Advanced Rogue
+ * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T
+ * 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 <limits.h>
+#include "curses.h"
+#include "rogue.h"
+#define	MAXINT	INT_MAX
+#define	MININT	INT_MIN
+/* 
+ * Did we disrupt a spell? 
+ */
+dsrpt_monster(tp, always, see_him)
+register struct thing *tp;
+bool always, see_him;
+{
+    switch (tp->t_action) {
+    case A_SUMMON:
+    case A_MISSILE:
+    case A_SLOW:
+	tp->t_action = A_NIL; /* Just make the old fellow start over again */
+	tp->t_no_move = movement(tp);
+	tp->t_using = NULL;/* Just to be on the safe side */
+	turn_on(*tp, WASDISRUPTED);
+	if (see_him)
+	    msg("%s's spell has been disrupted.",prname(monster_name(tp),TRUE));
+	/*
+	 * maybe choose something else to do next time since player
+	 * is disrupting us
+	 */
+	tp->t_summon *= 2;
+	tp->t_cast /= 2;
+	return;
+    }
+
+    /* We may want to disrupt other actions, too */
+    if (always) {
+	tp->t_action = A_NIL; /* Just make the old fellow start over again */
+	tp->t_no_move = movement(tp);
+	tp->t_using = NULL;/* Just to be on the safe side */
+    }
+}
+
+dsrpt_player()
+{
+    int which, action;
+    struct linked_list *item;
+    struct object *obj;
+    
+    action = player.t_action;
+    which = player.t_selection;
+
+    switch (action) {
+    case C_CAST: /* Did we disrupt a spell? */
+    case C_PRAY:
+    case C_CHANT:
+    {
+	msg("Your %s was disrupted!", action == C_CAST ? "spell" : "prayer");
+
+	/* Charge him anyway */
+	if (action == C_CAST)
+	    spell_power += magic_spells[which].s_cost;
+	else if (action == C_PRAY)
+	    pray_time += cleric_spells[which].s_cost;
+	else if (action == C_CHANT)
+	    chant_time += druid_spells[which].s_cost;
+    }
+    when C_COUNT: /* counting of gold? */
+    {
+	if (purse > 0) {
+	    msg("Your gold goes flying everywhere!");
+	    do {
+		item = spec_item(GOLD, NULL, NULL, NULL);
+		obj = OBJPTR(item);
+		obj->o_count = min(purse, rnd(10)+1);
+		purse -= obj->o_count;
+		obj->o_pos = hero;
+		fall(item, FALSE);
+	    } while (purse > 0 && rnd(10) != 1);
+	}
+    }
+    when C_EAT:
+	msg("You gag on your food for a moment.");
+	del_pack(player.t_using);
+	
+    when A_PICKUP:
+	msg("You drop what you are picking up!");
+
+    when C_SEARCH:	/* searching for traps and secret doors... */
+	msg("Oww....You decide to stop searching.");
+	count = 0;	/* don't search again */
+
+    when C_SETTRAP:
+	msg("Oww....You can't get a trap set.");
+
+    when A_NIL:
+    default:
+	return;
+    }
+    player.t_no_move = movement(&player); /* disoriented for a while */
+    player.t_action = A_NIL;
+    player.t_selection = 0;
+    player.t_using = NULL;
+}
+
+/*
+ * m_act:
+ *	If the critter isn't doing anything, choose an action for it.
+ *	Otherwise, let it perform its chosen action.
+ */
+
+m_act(tp)
+register struct thing *tp;
+{
+    struct object *obj;
+    bool flee;	/* Are we scared? */
+
+    /* What are we planning to do? */
+    switch (tp->t_action) {
+	default:
+	    /* An unknown action! */
+	    msg("Unknown monster action (%d)", tp->t_action);
+
+	    /* Fall through */
+
+	case A_NIL:
+	    /* If the monster is fairly intelligent and about to die, it
+	     * may turn tail and run.  But if we are a FRIENDLY creature
+	     * in the hero's service, don't run.
+	     */
+	    if (off(*tp, ISFLEE)					&&
+		tp->t_stats.s_hpt < tp->maxstats.s_hpt			&&
+		tp->t_stats.s_hpt < max(10, tp->maxstats.s_hpt/6)	&&
+		(off(*tp, ISFRIENDLY) || tp->t_dest != &hero)		&&
+		rnd(25) < tp->t_stats.s_intel) {
+		    turn_on(*tp, ISFLEE);
+
+		    /* It is okay to turn tail */
+		    tp->t_oldpos = tp->t_pos;
+		}
+
+	    /* Should the monster run away? */
+	    flee = on(*tp, ISFLEE) ||
+		((tp->t_dest == &hero) && on(player, ISINWALL) &&
+		 off(*tp, CANINWALL));
+
+	    m_select(tp, flee);	/* Select an action */
+	    return;
+
+	when A_ATTACK:
+	    /* 
+	     * We're trying to attack the player or monster at t_newpos 
+	     * if the prey moved, do nothing
+	     */
+	    obj = tp->t_using ? OBJPTR(tp->t_using) : NULL;
+	    if (ce(tp->t_newpos, hero)) {
+		attack(tp, obj, FALSE);
+	    }
+	    else if (mvwinch(mw, tp->t_newpos.y, tp->t_newpos.x) &&
+		     step_ok(tp->t_newpos.y, tp->t_newpos.x, FIGHTOK, tp)) {
+		skirmish(tp, &tp->t_newpos, obj, FALSE);
+	    }
+
+	when A_SELL:
+	    /* Is the player still next to us? */
+	    if (ce(tp->t_newpos, hero)) sell(tp);
+
+	    /* The darned player moved away */
+	    else if (off(player, ISBLIND) &&
+		cansee(unc(tp->t_pos)) &&
+		(off(*tp, ISINVIS)     || on(player, CANSEE)) &&
+		(off(*tp, ISSHADOW)    || on(player, CANSEE)) &&
+		(off(*tp, CANSURPRISE) || ISWEARING(R_ALERT)))
+		msg("%s grunts with frustration",prname(monster_name(tp),TRUE));
+
+	when A_MOVE:
+	    /* Let's try to move */
+	    do_chase(tp);
+
+	    /* If t_no_move > 0, we found that we have to fight! */
+	    if (tp->t_no_move > 0) return;
+
+	when A_BREATHE:
+	    /* Breathe on the critter */
+	    m_breathe(tp);
+
+	when A_SLOW:
+	    /* make him move slower */
+	    add_slow();
+	    turn_off(*tp, CANSLOW);
+
+	when A_MISSILE:
+	    /* Start up a magic missile spell */
+	    m_spell(tp);
+
+	when A_SONIC:
+	    /* Let out a sonic blast! */
+	    m_sonic(tp);
+
+	when A_THROW:
+	    /* We're throwing something (like an arrow) */
+	    missile(tp->t_newpos.y, tp->t_newpos.x, tp->t_using, tp);
+
+	when A_SUMMON:
+	    /* We're summoning help */
+	    m_summon(tp);
+
+	when A_USERELIC:
+	    /* Use our relic */
+	    m_use_relic(tp);
+
+	when A_USEWAND:
+	    /* use the wand we have */
+	    m_use_wand(tp);
+    }
+
+    /* No action now */
+    tp->t_action = A_NIL;
+    tp->t_using = NULL;
+}
+
+/*
+ * m_breathe:
+ *	Breathe in the chosen direction.
+ */
+
+m_breathe(tp)
+register struct thing *tp;
+{
+    register int damage;
+    register char *breath;
+
+    damage = tp->t_stats.s_hpt;
+    turn_off(*tp, CANSURPRISE);
+
+    /* Will it breathe at random */
+    if (on(*tp, CANBRANDOM)) {
+	/* Turn off random breath */
+	turn_off(*tp, CANBRANDOM);
+
+	/* Select type of breath */
+	switch (rnd(10)) {
+	    case 0: breath = "acid";
+		    turn_on(*tp, NOACID);
+	    when 1: breath = "flame";
+		    turn_on(*tp, NOFIRE);
+	    when 2: breath = "lightning bolt";
+		    turn_on(*tp, NOBOLT);
+	    when 3: breath = "chlorine gas";
+		    turn_on(*tp, NOGAS);
+	    when 4: breath = "ice";
+		    turn_on(*tp, NOCOLD);
+	    when 5: breath = "nerve gas";
+		    turn_on(*tp, NOPARALYZE);
+	    when 6: breath = "sleeping gas";
+		    turn_on(*tp, NOSLEEP);
+	    when 7: breath = "slow gas";
+		    turn_on(*tp, NOSLOW);
+	    when 8: breath = "confusion gas";
+		    turn_on(*tp, ISCLEAR);
+	    when 9: breath = "fear gas";
+		    turn_on(*tp, NOFEAR);
+	}
+    }
+
+    /* Or can it breathe acid? */
+    else if (on(*tp, CANBACID)) {
+	turn_off(*tp, CANBACID);
+	breath = "acid";
+    }
+
+    /* Or can it breathe fire */
+    else if (on(*tp, CANBFIRE)) {
+	turn_off(*tp, CANBFIRE);
+	breath = "flame";
+    }
+
+    /* Or can it breathe electricity? */
+    else if (on(*tp, CANBBOLT)) {
+	turn_off(*tp, CANBBOLT);
+	breath = "lightning bolt";
+    }
+
+    /* Or can it breathe gas? */
+    else if (on(*tp, CANBGAS)) {
+	turn_off(*tp, CANBGAS);
+	breath = "chlorine gas";
+    }
+
+    /* Or can it breathe ice? */
+    else if (on(*tp, CANBICE)) {
+	turn_off(*tp, CANBICE);
+	breath = "ice";
+    }
+
+    else if (on(*tp, CANBPGAS)) {
+	turn_off(*tp, CANBPGAS);
+	breath = "nerve gas";
+    }
+
+    /* can it breathe sleeping gas */
+    else if (on(*tp, CANBSGAS)) {
+	turn_off(*tp, CANBSGAS);
+	breath = "sleeping gas";
+    }
+
+    /* can it breathe slow gas */
+    else if (on(*tp, CANBSLGAS)) {
+	turn_off(*tp, CANBSLGAS);
+	breath = "slow gas";
+    }
+
+    /* can it breathe confusion gas */
+    else if (on(*tp, CANBCGAS)) {
+	turn_off(*tp, CANBCGAS);
+	breath = "confusion gas";
+    }
+
+    /* can it breathe fear gas */
+    else {
+	turn_off(*tp, CANBFGAS);
+	breath = "fear gas";
+    }
+
+    /* Now breathe -- sets "monst_dead" if it kills someone */
+    shoot_bolt(tp, tp->t_pos, tp->t_newpos, FALSE, 
+		    tp->t_index, breath, damage);
+
+    running = FALSE;
+    if (fight_flush) md_flushinp();
+}
+
+/*
+ * m_select:
+ *	Select an action for the monster.
+ */
+
+m_select(th, flee)
+register struct thing *th;
+register bool flee; /* True if running away or player is inaccessible in wall */
+{
+    register struct room *rer, *ree;	/* room of chaser, room of chasee */
+    int dist = MININT;
+    int mindist = MAXINT, maxdist = MININT;
+    bool rundoor;			/* TRUE means run to a door */
+    char sch;
+    coord *last_door=0,			/* Door we just came from */
+	   this;			/* Temporary destination for chaser */
+
+    rer = roomin(&th->t_pos);	/* Find room of chaser */
+    ree = roomin(th->t_dest);	/* Find room of chasee */
+
+    /* First see if we want to use an ability or weapon */
+    if (m_use_it(th, flee, rer, ree)) return;
+
+    /*
+     * We don't count monsters on doors as inside rooms here because when
+     * a monster is in a room and the player is not in that room, the
+     * monster looks for the best door out.  If we counted doors as part
+     * of the room, the monster would already be on the best door out;
+     * so he would never move.
+     */
+    if ((sch = CCHAR( mvwinch(stdscr, th->t_pos.y, th->t_pos.x) )) == DOOR ||
+	sch == SECRETDOOR || sch == PASSAGE) {
+	rer = NULL;
+    }
+    this = *th->t_dest;
+
+    /*
+     * If we are in a room heading for the player and the player is not
+     * in the room with us, we run to the "best" door.
+     * If we are in a room fleeing from the player, then we run to the
+     * "best" door if he IS in the same room.
+     *
+     * Note:  We don't bother with doors in mazes or if we can walk
+     * through walls.
+     */
+    if (rer != NULL && levtype != MAZELEV && off(*th, CANINWALL)) {
+	if (flee) rundoor = (rer == ree);
+	else rundoor = (rer != ree);
+    }
+    else rundoor = FALSE;
+
+    if (rundoor) {
+	register struct linked_list *exitptr;	/* For looping through exits */
+	coord *exit,				/* A particular door */
+	      *entrance;			/* Place just inside doorway */
+	int exity, exitx;			/* Door's coordinates */
+	char dch='\0';				/* Door character */
+
+	if (th->t_doorgoal)
+	    dch = CCHAR( mvwinch(stdscr, th->t_doorgoal->y, th->t_doorgoal->x) );
+	    
+	/* Do we have a valid goal? */
+	if ((dch == PASSAGE || dch == DOOR) &&  /* A real door */
+	    (!flee || !ce(*th->t_doorgoal, *th->t_dest))) { /* Prey should not
+						             * be at door if
+							     * we are running
+							     * away
+							     */
+	    /* Make sure the player is not in the doorway, either */
+	    entrance = doorway(rer, th->t_doorgoal);
+	    if (!flee || entrance == NULL || !ce(*entrance, *th->t_dest)) {
+		this = *th->t_doorgoal;
+		dist = 0;	/* Indicate that we have our door */
+	    }
+	}
+
+	/* Go through all the doors */
+	else for (exitptr = rer->r_exit; exitptr; exitptr = next(exitptr)) {
+	    exit = DOORPTR(exitptr);
+	    exity = exit->y;
+	    exitx = exit->x;
+
+	    /* Make sure it is a real door */
+	    dch = CCHAR( mvwinch(stdscr, exity, exitx) );
+	    if (dch == PASSAGE || dch == DOOR) {
+		/* Don't count a door if we are fleeing from someone and
+		 * he is standing on it.  Also, don't count it if he is
+		 * standing in the doorway.
+		 */
+		if (flee) {
+		    if (ce(*exit, *th->t_dest)) continue;
+
+		    entrance = doorway(rer, exit);
+		    if (entrance != NULL && ce(*entrance, *th->t_dest))
+			continue;
+		}
+		
+		/* Were we just on this door? */
+		if (ce(*exit, th->t_oldpos)) last_door = exit;
+
+		else {
+		    dist = DISTANCE(th->t_dest->y, th->t_dest->x, exity, exitx);
+
+		    /* If fleeing, we want to maximize distance from door to
+		     * what we flee, and minimize distance from door to us.
+		     */
+		    if (flee)
+		       dist -= DISTANCE(th->t_pos.y, th->t_pos.x, exity, exitx);
+
+		    /* Maximize distance if fleeing, otherwise minimize it */
+		    if ((flee && (dist > maxdist)) ||
+			(!flee && (dist < mindist))) {
+			th->t_doorgoal = exit;	/* Use this door */
+			this = *exit;
+			mindist = maxdist = dist;
+		    }
+		}
+	    }
+	}
+
+	/* Could we not find a door? */
+	if (dist == MININT) {
+	    /* If we were on a door, go ahead and use it */
+	    if (last_door) {
+		th->t_doorgoal = last_door;
+		this = th->t_oldpos;
+		dist = 0;	/* Indicate that we found a door */
+	    }
+	    else th->t_doorgoal = NULL;	/* No more door goal */
+	}
+
+	/* Indicate that we do not want to flee from the door */
+	if (dist != MININT) flee = FALSE;
+    }
+    else th->t_doorgoal = 0;	/* Not going to any door */
+
+    /* Now select someplace to go and start the action */
+    chase(th, &this, rer, ree, flee);
+}
+
+/*
+ * m_sonic:
+ *	The monster is sounding a sonic blast.
+ */
+
+m_sonic(tp)
+register struct thing *tp;
+{
+    register int damage;
+    static struct object blast =
+    {
+	MISSILE, {0, 0}, "", 0, "", "150" , NULL, 0, 0, 0, 0
+    };
+
+    turn_off(*tp, CANSONIC);
+    turn_off(*tp, CANSURPRISE);
+    do_motion(&blast, tp->t_newpos.y, tp->t_newpos.x, tp);
+    damage = 150;
+    if (save(VS_BREATH, &player, -3))
+	damage /= 2;
+    msg ("%s's sonic blast hits you", prname(monster_name(tp), TRUE));
+    if ((pstats.s_hpt -= damage) <= 0)
+	death(tp->t_index);
+
+    running = FALSE;
+    if (fight_flush) md_flushinp();
+    dsrpt_player();
+}
+
+/*
+ * m_spell:
+ *	The monster casts a spell.  Currently this is limited to
+ *	magic missile.
+ */
+m_spell(tp)
+register struct thing *tp;
+{
+    static struct object missile =
+    {
+	MISSILE, {0, 0}, "", 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1
+    };
+
+    sprintf(missile.o_hurldmg, "%dd4", tp->t_stats.s_lvl);
+    do_motion(&missile, tp->t_newpos.y, tp->t_newpos.x, tp);
+    hit_monster(unc(missile.o_pos), &missile, tp);
+    turn_off(*tp, CANMISSILE);
+    turn_off(*tp, CANSURPRISE);
+
+    running = FALSE;
+    if (fight_flush) md_flushinp();
+}
+
+/*
+ * m_summon:
+ *	Summon aid.
+ */
+
+m_summon(tp)
+register struct thing *tp;
+{
+    register char *helpname, *mname;
+    int fail, numsum;
+    register int which, i;
+
+    /* Let's make sure our prey is still here */
+    if (!cansee(unc(tp->t_pos)) || fallpos(&hero, FALSE, 2) == NULL) return;
+
+    /*
+     * Non-uniques can only summon once.  Uniques get fewer
+     * creatures with each successive summoning. Also, the
+     * probability of summoning goes down
+     */
+    if (off(*tp, ISUNIQUE))
+	    turn_off(*tp, CANSUMMON);
+
+    turn_off(*tp, CANSURPRISE);
+    mname = monster_name(tp);
+    helpname = monsters[tp->t_index].m_typesum;
+    which = findmindex(helpname);
+
+    if ((off(*tp, ISINVIS)     || on(player, CANSEE)) &&
+	(off(*tp, ISSHADOW)    || on(player, CANSEE)) &&
+	(off(*tp, CANSURPRISE) || ISWEARING(R_ALERT))) {
+	if (monsters[which].m_normal == FALSE) { /* genocided? */
+	    msg("%s appears dismayed", prname(mname, TRUE));
+	    monsters[tp->t_index].m_numsum = 0;
+	}
+	else {
+	    msg("%s summons %ss for help", prname(mname, TRUE), helpname);
+	}
+    }
+    else {
+	if (monsters[which].m_normal == FALSE) /* genocided? */
+	    monsters[tp->t_index].m_numsum = 0;
+	else {
+	    msg("%ss seem to appear from nowhere!", helpname);
+	}
+    }
+    numsum = monsters[tp->t_index].m_numsum;
+    if (numsum && on(*tp, ISUNIQUE)) {   /* UNIQUEs summon less each time */
+	monsters[tp->t_index].m_numsum--; 
+	tp->t_summon *= 2; /* cut probability in half */
+    }
+
+    /*
+     * try to make all the creatures around player but remember
+     * if unsuccessful
+     */
+    for (i=0, fail=0; i<numsum; i++) {
+	 if (!creat_mons(&player, which, FALSE))
+	     fail++;	/* remember the failures */
+    }
+
+    /*
+     * try once again to make the buggers
+     */
+    for (i=0; i<fail; i++)
+	 creat_mons(tp, which, FALSE);
+    
+    /* Now let the poor fellow see all the trouble */
+    light(&hero);
+    turn_on(*tp, HASSUMMONED);
+}
+
+/*
+ * m_use_it:
+ *	See if the monster (tp) has anything useful it can do
+ *	(ie. an ability or a weapon) other than just move.
+ */
+
+bool
+m_use_it(tp, flee, rer, ree)
+register struct thing *tp;
+bool flee;
+register struct room *rer, *ree;
+{
+    int dist;
+    register coord *ee = tp->t_dest, *er = &tp->t_pos; 
+    coord *shoot_dir;
+    struct linked_list *weapon;
+    struct thing *prey;
+    bool dest_player;	/* Are we after the player? */
+
+    /*
+     * If we are fleeing, there's a chance, depending on our
+     * intelligence, that we'll just run in terror.
+     */
+    if (flee && rnd(25) >= tp->t_stats.s_intel) return(FALSE);
+
+    /*
+     * Make sure that we have a living destination, and record whether
+     * it is the player.
+     */
+    if (ee != NULL) {
+        if (ce(*ee, hero)) {
+	    dest_player = TRUE;
+	    prey = &player;
+	}
+	else {
+	    struct linked_list *item;
+
+	    dest_player = FALSE;
+
+	    /* What is the monster we're chasing? */
+	    item = find_mons(ee->y, ee->x);
+	    if (item != NULL) prey = THINGPTR(item);
+	    else return(FALSE);
+	}
+    }
+    else return(FALSE);
+
+    /*
+     * If we are friendly to the hero, we don't do anything.
+     */
+    if (on(*tp, ISFRIENDLY) && dest_player) return(FALSE);
+
+    /*
+     * Also, for now, if our prey is in a wall, we won't do
+     * anything.  The prey must be in the same room as we are OR