Mercurial > hg > early-roguelike
comparison xrogue/bolt.c @ 133:e6179860cb76
Import XRogue 8.0 from the Roguelike Restoration Project (r1490)
| author | John "Elwin" Edwards |
|---|---|
| date | Tue, 21 Apr 2015 08:55:20 -0400 |
| parents | |
| children | f54901b9c39b |
comparison
equal
deleted
inserted
replaced
| 124:d10fc4a065ac | 133:e6179860cb76 |
|---|---|
| 1 /* | |
| 2 bolt.c - functions shooting an object across the room | |
| 3 | |
| 4 XRogue: Expeditions into the Dungeons of Doom | |
| 5 Copyright (C) 1991 Robert Pietkivitch | |
| 6 All rights reserved. | |
| 7 | |
| 8 Based on "Advanced Rogue" | |
| 9 Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T | |
| 10 All rights reserved. | |
| 11 | |
| 12 Based on "Rogue: Exploring the Dungeons of Doom" | |
| 13 Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman | |
| 14 All rights reserved. | |
| 15 | |
| 16 See the file LICENSE.TXT for full copyright and licensing information. | |
| 17 */ | |
| 18 | |
| 19 #include <curses.h> | |
| 20 #include <ctype.h> | |
| 21 #include "rogue.h" | |
| 22 | |
| 23 /* | |
| 24 * shoot_bolt fires a bolt from the given starting point in the | |
| 25 * given direction | |
| 26 */ | |
| 27 | |
| 28 shoot_bolt(shooter, start, dir, get_points, reason, name, damage) | |
| 29 struct thing *shooter; | |
| 30 coord start, dir; | |
| 31 bool get_points; | |
| 32 short reason; | |
| 33 char *name; | |
| 34 int damage; | |
| 35 { | |
| 36 unsigned char dirch = 0, ch; | |
| 37 bool used, change, see_him; | |
| 38 short y, x, bounces; | |
| 39 coord pos; | |
| 40 struct linked_list *target=NULL; | |
| 41 struct { | |
| 42 coord place; | |
| 43 char oldch; | |
| 44 } spotpos[BOLT_LENGTH]; | |
| 45 | |
| 46 switch (dir.y + dir.x) { | |
| 47 case 0: dirch = '/'; | |
| 48 when 1: case -1: dirch = (dir.y == 0 ? '-' : '|'); | |
| 49 when 2: case -2: dirch = '\\'; | |
| 50 } | |
| 51 pos.y = start.y + dir.y; | |
| 52 pos.x = start.x + dir.x; | |
| 53 used = FALSE; | |
| 54 change = FALSE; | |
| 55 | |
| 56 bounces = 0; /* No bounces yet */ | |
| 57 nofont(cw); | |
| 58 for (y = 0; y < BOLT_LENGTH && !used; y++) { | |
| 59 ch = winat(pos.y, pos.x); | |
| 60 spotpos[y].place = pos; | |
| 61 spotpos[y].oldch = mvwinch(cw, pos.y, pos.x); | |
| 62 | |
| 63 /* Are we at hero? */ | |
| 64 if (ce(pos, hero)) goto at_hero; | |
| 65 | |
| 66 switch (ch) { | |
| 67 case SECRETDOOR: | |
| 68 case VERTWALL: | |
| 69 case HORZWALL: | |
| 70 case ' ': | |
| 71 if (dirch == '-' || dirch == '|') { | |
| 72 dir.y = -dir.y; | |
| 73 dir.x = -dir.x; | |
| 74 } | |
| 75 else { | |
| 76 unsigned char chx = mvinch(pos.y-dir.y, pos.x), | |
| 77 chy = mvinch(pos.y, pos.x-dir.x); | |
| 78 bool anychange = FALSE; /* Did we change anthing */ | |
| 79 | |
| 80 if (chy == WALL || chy == SECRETDOOR || | |
| 81 chy == HORZWALL || chy == VERTWALL) { | |
| 82 dir.y = -dir.y; | |
| 83 change ^= TRUE; /* Change at least one direction */ | |
| 84 anychange = TRUE; | |
| 85 } | |
| 86 if (chx == WALL || chx == SECRETDOOR || | |
| 87 chx == HORZWALL || chx == VERTWALL) { | |
| 88 dir.x = -dir.x; | |
| 89 change ^= TRUE; /* Change at least one direction */ | |
| 90 anychange = TRUE; | |
| 91 } | |
| 92 | |
| 93 /* If we didn't make any change, make both changes */ | |
| 94 if (!anychange) { | |
| 95 dir.x = -dir.x; | |
| 96 dir.y = -dir.y; | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 /* Do we change how the bolt looks? */ | |
| 101 if (change) { | |
| 102 change = FALSE; | |
| 103 if (dirch == '\\') dirch = '/'; | |
| 104 else if (dirch == '/') dirch = '\\'; | |
| 105 } | |
| 106 | |
| 107 y--; /* The bounce doesn't count as using up the bolt */ | |
| 108 | |
| 109 /* Make sure we aren't in an infinite bounce */ | |
| 110 if (++bounces > BOLT_LENGTH) used = TRUE; | |
| 111 msg("The %s bounces", name); | |
| 112 break; | |
| 113 default: | |
| 114 if (isalpha(ch)) { | |
| 115 register struct linked_list *item; | |
| 116 struct thing *tp; | |
| 117 register char *mname; | |
| 118 bool see_monster = cansee(pos.y, pos.x); | |
| 119 | |
| 120 item = find_mons(unc(pos)); | |
| 121 assert(item != NULL); | |
| 122 tp = THINGPTR(item); | |
| 123 mname = monster_name(tp); | |
| 124 | |
| 125 /* | |
| 126 * If our prey shot this, let's record the fact that | |
| 127 * he can shoot, regardless of whether he hits us. | |
| 128 */ | |
| 129 if (tp->t_dest != NULL && ce(*tp->t_dest, shooter->t_pos)) | |
| 130 tp->t_wasshot = TRUE; | |
| 131 | |
| 132 if (!save(VS_BREATH, tp, -(shooter->t_stats.s_lvl/10))) { | |
| 133 if (see_monster) { | |
| 134 if (on(*tp, ISDISGUISE) && | |
| 135 (tp->t_type != tp->t_disguise)) { | |
| 136 msg("Wait! That's a %s!", mname); | |
| 137 turn_off(*tp, ISDISGUISE); | |
| 138 } | |
| 139 | |
| 140 turn_off(*tp, CANSURPRISE); | |
| 141 msg("The %s hits %s", name, prname(mname, FALSE)); | |
| 142 } | |
| 143 | |
| 144 /* Should we start to chase the shooter? */ | |
| 145 if (shooter != &player && | |
| 146 shooter != tp && | |
| 147 shooter->t_index != tp->t_index && | |
| 148 (tp->t_dest == NULL || rnd(100) < 25)) { | |
| 149 /* | |
| 150 * If we're intelligent enough to realize that this | |
| 151 * is a friendly monster, we will attack the hero | |
| 152 * instead. | |
| 153 */ | |
| 154 if (on(*shooter, ISFRIENDLY) && | |
| 155 roll(3,6) < tp->t_stats.s_intel) | |
| 156 runto(tp, &hero); | |
| 157 | |
| 158 /* Otherwise, let's chase the monster */ | |
| 159 else runto(tp, &shooter->t_pos); | |
| 160 } | |
| 161 else if (shooter == &player) { | |
| 162 runto(tp, &hero); | |
| 163 | |
| 164 /* | |
| 165 * If the player shot a charmed monster, it may | |
| 166 * not like being shot at. | |
| 167 */ | |
| 168 if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) { | |
| 169 msg("The eyes of %s turn clear.", | |
| 170 prname(mname, FALSE)); | |
| 171 turn_off(*tp, ISCHARMED); | |
| 172 mname = monster_name(tp); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 /* | |
| 177 * Let the defender know that the attacker has | |
| 178 * missiles! | |
| 179 */ | |
| 180 if (ce(*tp->t_dest, shooter->t_pos)) | |
| 181 tp->t_wasshot = TRUE; | |
| 182 | |
| 183 used = TRUE; | |
| 184 | |
| 185 /* Hit the monster -- does it do anything? */ | |
| 186 if ((EQUAL(name,"ice") && on(*tp, NOCOLD)) || | |
| 187 (EQUAL(name,"flame") && on(*tp, NOFIRE)) || | |
| 188 (EQUAL(name,"acid") && on(*tp, NOACID)) || | |
| 189 (EQUAL(name,"lightning bolt")&& on(*tp,NOBOLT)) || | |
| 190 (EQUAL(name,"nerve gas") &&on(*tp,NOPARALYZE))|| | |
| 191 (EQUAL(name,"sleeping gas") && | |
| 192 (on(*tp, NOSLEEP) || on(*tp, ISUNDEAD))) || | |
| 193 (EQUAL(name,"slow gas") && on(*tp,NOSLOW)) || | |
| 194 (EQUAL(name,"fear gas") && on(*tp,NOFEAR)) || | |
| 195 (EQUAL(name,"confusion gas") && on(*tp,ISCLEAR)) || | |
| 196 (EQUAL(name,"chlorine gas") && on(*tp,NOGAS))) { | |
| 197 if (see_monster) | |
| 198 msg("The %s has no effect on %s.", | |
| 199 name, prname(mname, FALSE)); | |
| 200 } | |
| 201 | |
| 202 else { | |
| 203 see_him = !invisible(tp); | |
| 204 | |
| 205 /* Did a spell get disrupted? */ | |
| 206 dsrpt_monster(tp, FALSE, see_him); | |
| 207 | |
| 208 /* | |
| 209 * Check for gas with special effects | |
| 210 */ | |
| 211 if (EQUAL(name, "nerve gas")) { | |
| 212 tp->t_no_move = movement(tp) * FREEZETIME; | |
| 213 tp->t_action = A_FREEZE; | |
| 214 } | |
| 215 else if (EQUAL(name, "sleeping gas")) { | |
| 216 tp->t_no_move = movement(tp) * SLEEPTIME; | |
| 217 tp->t_action = A_FREEZE; | |
| 218 } | |
| 219 else if (EQUAL(name, "slow gas")) { | |
| 220 if (on(*tp, ISHASTE)) | |
| 221 turn_off(*tp, ISHASTE); | |
| 222 else | |
| 223 turn_on(*tp, ISSLOW); | |
| 224 } | |
| 225 else if (EQUAL(name, "fear gas")) { | |
| 226 turn_on(*tp, ISFLEE); | |
| 227 tp->t_dest = &hero; | |
| 228 | |
| 229 /* It is okay to turn tail */ | |
| 230 tp->t_oldpos = tp->t_pos; | |
| 231 } | |
| 232 else if (EQUAL(name, "confusion gas")) { | |
| 233 turn_on(*tp, ISHUH); | |
| 234 tp->t_dest = &hero; | |
| 235 } | |
| 236 else if ((EQUAL(name, "lightning bolt")) && | |
| 237 on(*tp, BOLTDIVIDE)) { | |
| 238 if (creat_mons(tp, tp->t_index, FALSE)) { | |
| 239 if (see_monster) | |
| 240 msg("The %s divides %s.", | |
| 241 name,prname(mname, FALSE)); | |
| 242 light(&hero); | |
| 243 } | |
| 244 else if (see_monster) | |
| 245 msg("The %s has no effect on %s.", | |
| 246 name, prname(mname, FALSE)); | |
| 247 } | |
| 248 else { | |
| 249 if (save(VS_BREATH, tp, | |
| 250 -(shooter->t_stats.s_lvl/10))) | |
| 251 damage /= 2; | |
| 252 | |
| 253 /* The poor fellow got killed! */ | |
| 254 if ((tp->t_stats.s_hpt -= damage) <= 0) { | |
| 255 if (see_monster) | |
| 256 msg("The %s kills %s", | |
| 257 name, prname(mname, FALSE)); | |
| 258 else | |
| 259 msg("You hear a faint groan in the distance"); | |
| 260 /* | |
| 261 * Instead of calling killed() here, we | |
| 262 * will record that the monster was killed | |
| 263 * and call it at the end of the routine, | |
| 264 * after we restore what was under the bolt. | |
| 265 * We have to do this because in the case | |
| 266 * of a bolt that first misses the monster | |
| 267 * and then gets it on the bounce. If we | |
| 268 * call killed here, the 'missed' space in | |
| 269 * spotpos puts the monster back on the | |
| 270 * screen | |
| 271 */ | |
| 272 target = item; | |
| 273 } | |
| 274 else { /* Not dead, so just scream */ | |
| 275 if (!see_monster) | |
| 276 msg("You hear a scream in the distance"); | |
| 277 } | |
| 278 } | |
| 279 } | |
| 280 } | |
| 281 else if (isalpha(show(pos.y, pos.x))) { | |
| 282 if (see_monster) { | |
| 283 if (terse) | |
| 284 msg("%s misses", name); | |
| 285 else | |
| 286 msg("The %s whizzes past %s", | |
| 287 name, prname(mname, FALSE)); | |
| 288 } | |
| 289 if (get_points) runto(tp, &hero); | |
| 290 } | |
| 291 } | |
| 292 else if (pos.y == hero.y && pos.x == hero.x) { | |
| 293 at_hero: if (!save(VS_BREATH, &player, | |
| 294 -(shooter->t_stats.s_lvl/10))){ | |
| 295 if (terse) | |
| 296 msg("The %s hits you", name); | |
| 297 else | |
| 298 msg("You are hit by the %s", name); | |
| 299 used = TRUE; | |
| 300 | |
| 301 /* | |
| 302 * The Amulet of Yendor protects against all "breath" | |
| 303 * | |
| 304 * The following two if statements could be combined | |
| 305 * into one, but it makes the compiler barf, so split | |
| 306 * it up | |
| 307 */ | |
| 308 if (cur_relic[YENDOR_AMULET] || | |
| 309 (EQUAL(name,"chlorine gas")&&on(player, NOGAS)) || | |
| 310 (EQUAL(name,"acid")&&on(player, NOACID)) || | |
| 311 (EQUAL(name,"sleeping gas")&&ISWEARING(R_ALERT))){ | |
| 312 msg("The %s has no effect", name); | |
| 313 } | |
| 314 else if((EQUAL(name, "flame") && on(player, NOFIRE)) || | |
| 315 (EQUAL(name, "ice") && on(player, NOCOLD)) || | |
| 316 (EQUAL(name,"lightning bolt")&& | |
| 317 on(player,NOBOLT)) || | |
| 318 (EQUAL(name,"fear gas")&&ISWEARING(R_HEROISM))){ | |
| 319 msg("The %s has no effect", name); | |
| 320 } | |
| 321 | |
| 322 else { | |
| 323 dsrpt_player(); | |
| 324 | |
| 325 /* | |
| 326 * Check for gas with special effects | |
| 327 */ | |
| 328 if (EQUAL(name, "nerve gas")) { | |
| 329 msg("The nerve gas paralyzes you."); | |
| 330 player.t_no_move += | |
| 331 movement(&player) * FREEZETIME; | |
| 332 player.t_action = A_FREEZE; | |
| 333 } | |
