Mercurial > hg > early-roguelike
view xrogue/actions.c @ 152:0c775afe0072
arogue7, xrogue: fix command repetition.
In both games, command() loops and calls the action function twice for
each command: once to schedule it by setting player.t_action and
player.t_no_move, and again, to actually do it, once player.t_no_move
has been reduced to 0. Each loop decremented count, so repeated
commands were only repeated half the number of times the player typed.
count is now decremented only when ch has been set to the repeated
command.
author | John "Elwin" Edwards |
---|---|
date | Thu, 28 May 2015 11:08:03 -0400 |
parents | e6179860cb76 |
children | f54901b9c39b |
line wrap: on
line source
/* actions.c - functions for dealing with monster actions XRogue: Expeditions into the Dungeons of Doom Copyright (C) 1991 Robert Pietkivitch All rights reserved. Based on "Advanced Rogue" Copyright (C) 1984, 1985 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 <curses.h> #include <limits.h> #include "rogue.h" int mf_count = 0; /* move_free counter - see actions.c(m_act()) */ int mf_jmpcnt = 0; /* move_free counter for # of jumps */ /* * 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 1/4 anyway */ if (action == C_CAST) spell_power += magic_spells[which].s_cost / 4; else if (action == C_PRAY) pray_time += cleric_spells[which].s_cost / 4; else if (action == C_CHANT) chant_time += druid_spells[which].s_cost / 4; } 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(20)+1); purse -= obj->o_count; obj->o_pos = hero; fall(item, FALSE); } while (purse > 0 && rnd(25) != 1); } } when C_EAT: msg("Ack! 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("Ouch! You decide to stop searching. "); count = 0; /* don't search again */ when C_SETTRAP: msg("Ouch! You can't set a trap right now. "); when A_NIL: default: return; } player.t_no_move = movement(&player); /* disoriented for a while */ player.t_action = A_NIL; player.t_selection = 0; } /* * 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 quartermaster still next to us? */ if (ce(tp->t_newpos, hero)) sell(tp); /* The quartermaster 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)) && (rnd(12) < 4)) 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); } /* Can we in fact move? (we might have solidified in solid rock) */ if (!step_ok(hero.y, hero.x, NOMONST, &player)) { if (move_free > 1) goto jump_over; /* avoid messages */ if (mf_count > 2) goto jump_over; /* limit messages */ if (pstats.s_hpt < 1) { pstats.s_hpt = -1; msg("You have merged into the surroundings! --More--"); wait_for(' '); death(D_PETRIFY); } else { mf_count += 1; /* count number of times we are here */ pstats.s_hpt -= rnd(2)+1; if (pstats.s_hpt < 1) { pstats.s_hpt = -1; msg("You have merged into the surroundings! --More--"); wait_for(' '); death(D_PETRIFY); } } switch (rnd(51)) { case 0: msg("Arrrggghhhhh!! "); when 5: msg("You can't move! "); when 10: msg("You motion angrily! "); when 15: msg("You feel so weird! "); when 20: msg("If only you could phase. "); when 25: msg("The rock maggots are closing in! "); when 30: msg("You wrench and wrench and wrench... "); when 35: msg("You wish you could teleport out of here! "); when 40: msg("Your feel your life force ebbing away... "); when 45: msg("You partially regain your senses. "); when 50: msg("The rock maggots have found you!!! "); otherwise: pstats.s_hpt -= rnd(4)+1; } if (pstats.s_hpt < 1) { pstats.s_hpt = -1; msg("You lose the urge to live... --More--"); wait_for(' '); death(D_PETRIFY); } jump_over: mf_jmpcnt++; /* count this jump */ if (mf_jmpcnt > 9) { /* take a few turns, then reset it */ mf_jmpcnt = 0; mf_count = 0; } } /* 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 = NULL; 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 */ shoot_bolt(tp, tp->t_pos, tp->t_newpos, FALSE, tp->t_index, breath, damage); running = FALSE; if (fight_flush) 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 = INT_MIN; int mindist = INT_MAX, maxdist = INT_MIN; 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 = 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.x != -1) && (th->t_doorgoal.y != -1)) dch = 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 = 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 == INT_MIN) { /* 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.x = th->t_doorgoal.y = -1; /* No more door goal */ } /* Indicate that we do not want to flee from the door */ if (dist != INT_MIN) flee = FALSE; } else th->t_doorgoal.x = th->t_doorgoal.y = -1; /* 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; 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 = rnd(61)+40; if (save(VS_BREATH, &player, -3)) damage /= 2; msg ("%s's ultra-sonic blast hits you", prname(monster_name(tp), TRUE)); if ((pstats.s_hpt -= damage) <= 0) { pstats.s_hpt = -1; death(tp->t_index); } running = FALSE; if (fight_flush) flushinp(); dsrpt_player(); } /* * m_spell: * The monster casts a spell. Currently this is limited to * magic missile. */ m_spell(tp) register struct thing *tp; { 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) 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 = NULL; coord straight_dir; int straight_shot = FALSE; 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 (can_shoot(er, ee, &straight_dir) == 0) shoot_dir = &straight_dir; else shoot_dir = NULL; if (on(*prey, ISINWALL) || ( (shoot_dir == 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, ee, dist, shoot_dir)) { return(TRUE); } /* From now on, we must have a direct shot at the prey */ if (!straight_shot) 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 */