Mercurial > hg > early-roguelike
view arogue7/actions.c @ 265:7fcb2f9f57e6
Mention UltraRogue in the top-level README.
author | John "Elwin" Edwards |
---|---|
date | Sun, 19 Feb 2017 19:54:17 -0500 |
parents | f9ef86cf22b2 |
children |
line wrap: on
line source
/* * 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 void m_breathe(struct thing *tp); void m_select(struct thing *th, bool flee); void m_sonic(struct thing *tp); void m_spell(struct thing *tp); void m_summon(struct thing *tp); bool m_use_it(struct thing *tp, bool flee, struct room *rer, struct room *ree); bool m_use_pack(struct thing *monster, coord *monst_pos, coord *defend_pos, int dist, coord *shoot_dir); /* * Did we disrupt a spell? */ void dsrpt_monster(struct thing *tp, bool always, bool 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 */ } } void dsrpt_player(void) { 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, 0, 0, 0); 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. */ void m_act(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. */ void m_breathe(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. * flee: True if running away or player is inaccessible in wall */ void m_select(struct thing *th, bool flee) { 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. */ void m_sonic(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. */ void m_spell(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. */ void m_summon(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(struct thing *tp, bool flee, struct room *rer, struct room *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 * we must have a straight shot at him. Note that * shoot_dir must get set before rer is checked so * that we get a valid value. */ if (on(*prey, ISINWALL) || ((shoot_dir = can_shoot(er, ee)) == NULL && (rer == NULL || rer != ree))) return(FALSE); /* * If we can't see the prey then forget it */ if (on(*prey, ISINVIS) && off(*tp, CANSEE)) return(FALSE); /* How far are we from our prey? */ dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* * Shall we summon aid so we don't have to get our hands dirty? * For now, we will only summon aid against the player. * We'll wait until he's within 2 dots of a missile length. */ if (on(*tp, CANSUMMON) && dest_player && dist < (BOLT_LENGTH+2)*(BOLT_LENGTH+2) && rnd(tp->t_summon) < tp->t_stats.s_lvl && monsters[tp->t_index].m_numsum > 0 && fallpos(&hero, FALSE, 2) != NULL) { tp->t_action = A_SUMMON; /* We're going to summon help */ tp->t_no_move = movement(tp); /* It takes time! */ return(TRUE); } /* * If the creature can cast a slow spell and if the prey is within * 2 dots of a missile fire, then see whether we will cast it. * if next to player, lessen chance because we don't like being * disrupted */ if (on(*tp, CANSLOW) && dest_player && dist < (BOLT_LENGTH+5)*(BOLT_LENGTH+5) && rnd(100) < (dist > 3 ? tp->t_cast : tp->t_cast/2)) { tp->t_action = A_SLOW; /* We're going to slow him */ tp->t_no_move = 3 * movement(tp); /* Takes time! */ debug("casting slow spell!"); return(TRUE); } /* * If we have a special magic item, we might use it. We will restrict * this options to uniques with relics and creatures with wands for now. * Also check for the quartermaster. Don't want him shooting wands.... */ if ((on(*tp, ISUNIQUE) || on(*tp, CARRYSTICK)) && off(*tp, CANSELL) && dest_player && m_use_pack(tp, er, ee, dist, shoot_dir)) { return(TRUE); } /* From now on, we must have a direct shot at the prey */ if (shoot_dir == NULL) return(FALSE); /* We may use a sonic blast if we can, only on the player */ if (on(*tp, CANSONIC) && dest_player && (dist < BOLT_LENGTH*2) && (rnd(100) < tp->t_breathe)) { tp->t_newpos = *shoot_dir; /* Save the direction */ tp->t_action = A_SONIC; /* We're going to sonic blast */ tp->t_no_move = 2 * movement(tp); /* Takes 2 movement periods */ } /* If we can breathe, we may do so */ else if (on(*tp, CANBREATHE) && (dist < BOLT_LENGTH*BOLT_LENGTH) && (rnd(100) < tp->t_breathe)) { tp->t_newpos = *shoot_dir; /* Save the direction */ tp->t_action = A_BREATHE; /* We're going to breathe */ tp->t_no_move = movement(tp); /* It takes 1 movement period */ } /* * We may shoot missiles if we can * if next to player, lessen chance so we don't get disrupted as often */ else if (on(*tp,CANMISSILE) && rnd(100) < (dist > 3 ? tp->t_cast : tp->t_cast/2)){ tp->t_newpos = *shoot_dir; /* Save the direction */ tp->t_action = A_MISSILE; /* We're going to shoot MM's */ tp->t_no_move = 3 * movement(tp); /* Takes time! */ } /* * If we can shoot or throw something, we might do so. * If next to player, then forget it */ else if ((on(*tp,CANSHOOT) || on(*tp,CARRYWEAPON) || on(*tp,CARRYDAGGER) || on(*tp, CARRYAXE)) && dist > 3 && off(*tp, CANSELL) && (weapon = get_hurl(tp))) { tp->t_newpos = *shoot_dir; /* Save the direction */ tp->t_action = A_THROW; /* We're going to throw something */ tp->t_using = weapon; /* Save our weapon */ tp->t_no_move = 2 * movement(tp); /* Takes 2 movement periods */ } /* We couldn't find anything to do */ else return(FALSE); return(TRUE); } /* * runners: * Make all the awake monsters try to do something. * segments: Number of segments since last called */ int runners(int segments) { register struct linked_list *item; register struct thing *tp = NULL; register int min_time = 20; /* Minimum time until a monster can act */ /* * loop thru the list of running (wandering) monsters and see what * each one will do this time. * * Note: the special case that one of this buggers kills another. * if this happens than we have to see if the monster killed * himself or someone else. In case its himself we have to get next * one immediately. If it wasn't we have to get next one at very * end in case he killed the next one. */ for (item = mlist; item != NULL; item = next(item)) { tp = THINGPTR(item); turn_on(*tp, ISREADY); } for (;;) { for (item = mlist; item != NULL; item = next(item)) { tp = THINGPTR(item); if (on(*tp, ISREADY)) break; } if (item == NULL) break; turn_off(*tp, ISREADY); /* If we are not awake, just skip us */ if (off(*tp, ISRUN) && off(*tp, ISHELD)) continue; /* See if it's our turn */ tp->t_no_move -= segments; if (tp->t_no_move > 0) { if (tp->t_no_move < min_time) min_time = tp->t_no_move; continue; } /* If we were frozen, we're moving now */ if (tp->t_action == A_FREEZE) tp->t_action = A_NIL; if (on(*tp, ISHELD)) { /* Make sure the action and using are nil */ tp->t_action = A_NIL; tp->t_using = NULL; /* Can we break free? */ if (rnd(tp->t_stats.s_lvl) > 11) { turn_off(*tp, ISHELD); runto(tp, &hero); if (cansee(tp->t_pos.y, tp->t_pos.x)) msg("%s breaks free from the hold spell", prname(monster_name(tp), TRUE)); } /* Too bad -- try again later */ else tp->t_no_move = movement(tp); } /* Heal the creature if it's not in the middle of some action */