Mercurial > hg > early-roguelike
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