1 /*- 2 * Copyright (c) 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Timothy C. Stoehr. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)pack.c 8.1 (Berkeley) 5/31/93 33 * $FreeBSD: src/games/rogue/pack.c,v 1.8 1999/11/30 03:49:25 billf Exp $ 34 */ 35 36 /* 37 * pack.c 38 * 39 * This source herein may be modified and/or distributed by anybody who 40 * so desires, with the following restrictions: 41 * 1.) No portion of this notice shall be removed. 42 * 2.) Credit shall not be taken for the creation of this source. 43 * 3.) This code is not to be traded, sold, or used for personal 44 * gain or profit. 45 * 46 */ 47 48 #include <unistd.h> 49 #include "rogue.h" 50 51 const char curse_message[] = "you can't, it appears to be cursed"; 52 53 static object *check_duplicate(object *, object *); 54 static boolean is_pack_letter(short *, unsigned short *); 55 static boolean mask_pack(const object *, unsigned short); 56 static int next_avail_ichar(void); 57 58 object * 59 add_to_pack(object *obj, object *pack, int condense) 60 { 61 object *op; 62 63 if (condense) { 64 if ((op = check_duplicate(obj, pack)) != NULL) { 65 free_object(obj); 66 return(op); 67 } else { 68 obj->ichar = next_avail_ichar(); 69 } 70 } 71 if (pack->next_object == 0) { 72 pack->next_object = obj; 73 } else { 74 op = pack->next_object; 75 76 while (op->next_object) { 77 op = op->next_object; 78 } 79 op->next_object = obj; 80 } 81 obj->next_object = 0; 82 return(obj); 83 } 84 85 void 86 take_from_pack(object *obj, object *pack) 87 { 88 while (pack->next_object != obj) { 89 pack = pack->next_object; 90 } 91 pack->next_object = pack->next_object->next_object; 92 } 93 94 /* Note: *status is set to 0 if the rogue attempts to pick up a scroll 95 * of scare-monster and it turns to dust. *status is otherwise set to 1. 96 */ 97 98 object * 99 pick_up(int row, int col, short *status) 100 { 101 object *obj; 102 103 *status = 1; 104 105 if (levitate) { 106 message("you're floating in the air!", 0); 107 return(NULL); 108 } 109 obj = object_at(&level_objects, row, col); 110 if (!obj) { 111 message("pick_up(): inconsistent", 1); 112 return(obj); 113 } 114 if ( (obj->what_is == SCROL) && 115 (obj->which_kind == SCARE_MONSTER) && 116 obj->picked_up) { 117 message("the scroll turns to dust as you pick it up", 0); 118 dungeon[row][col] &= (~OBJECT); 119 vanish(obj, 0, &level_objects); 120 *status = 0; 121 if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) { 122 id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED; 123 } 124 return(NULL); 125 } 126 if (obj->what_is == GOLD) { 127 rogue.gold += obj->quantity; 128 dungeon[row][col] &= ~(OBJECT); 129 take_from_pack(obj, &level_objects); 130 print_stats(STAT_GOLD); 131 return(obj); /* obj will be free_object()ed in caller */ 132 } 133 if (pack_count(obj) >= MAX_PACK_COUNT) { 134 message("pack too full", 1); 135 return(NULL); 136 } 137 dungeon[row][col] &= ~(OBJECT); 138 take_from_pack(obj, &level_objects); 139 obj = add_to_pack(obj, &rogue.pack, 1); 140 obj->picked_up = 1; 141 return(obj); 142 } 143 144 void 145 drop(void) 146 { 147 object *obj, *new; 148 short ch; 149 char desc[DCOLS]; 150 151 if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) { 152 message("there's already something there", 0); 153 return; 154 } 155 if (!rogue.pack.next_object) { 156 message("you have nothing to drop", 0); 157 return; 158 } 159 if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) { 160 return; 161 } 162 if (!(obj = get_letter_object(ch))) { 163 message("no such item.", 0); 164 return; 165 } 166 if (obj->in_use_flags & BEING_WIELDED) { 167 if (obj->is_cursed) { 168 message(curse_message, 0); 169 return; 170 } 171 unwield(rogue.weapon); 172 } else if (obj->in_use_flags & BEING_WORN) { 173 if (obj->is_cursed) { 174 message(curse_message, 0); 175 return; 176 } 177 mv_aquatars(); 178 unwear(rogue.armor); 179 print_stats(STAT_ARMOR); 180 } else if (obj->in_use_flags & ON_EITHER_HAND) { 181 if (obj->is_cursed) { 182 message(curse_message, 0); 183 return; 184 } 185 un_put_on(obj); 186 } 187 obj->row = rogue.row; 188 obj->col = rogue.col; 189 190 if ((obj->quantity > 1) && (obj->what_is != WEAPON)) { 191 obj->quantity--; 192 new = alloc_object(); 193 *new = *obj; 194 new->quantity = 1; 195 obj = new; 196 } else { 197 obj->ichar = 'L'; 198 take_from_pack(obj, &rogue.pack); 199 } 200 place_at(obj, rogue.row, rogue.col); 201 strcpy(desc, "dropped "); 202 get_desc(obj, desc + 8); 203 message(desc, 0); 204 reg_move(); 205 } 206 207 static object * 208 check_duplicate(object *obj, object *pack) 209 { 210 object *op; 211 212 if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) { 213 return(0); 214 } 215 if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) { 216 return(0); 217 } 218 op = pack->next_object; 219 220 while (op) { 221 if ((op->what_is == obj->what_is) && 222 (op->which_kind == obj->which_kind)) { 223 224 if ((obj->what_is != WEAPON) || 225 ((obj->what_is == WEAPON) && 226 ((obj->which_kind == ARROW) || 227 (obj->which_kind == DAGGER) || 228 (obj->which_kind == DART) || 229 (obj->which_kind == SHURIKEN)) && 230 (obj->quiver == op->quiver))) { 231 op->quantity += obj->quantity; 232 return(op); 233 } 234 } 235 op = op->next_object; 236 } 237 return(0); 238 } 239 240 static int 241 next_avail_ichar(void) 242 { 243 object *obj; 244 int i; 245 boolean ichars[26]; 246 247 for (i = 0; i < 26; i++) { 248 ichars[i] = 0; 249 } 250 obj = rogue.pack.next_object; 251 while (obj) { 252 if (obj->ichar >= 'a' && obj->ichar <= 'z') { 253 ichars[(obj->ichar - 'a')] = 1; 254 } 255 obj = obj->next_object; 256 } 257 for (i = 0; i < 26; i++) { 258 if (!ichars[i]) { 259 return(i + 'a'); 260 } 261 } 262 return('?'); 263 } 264 265 void 266 wait_for_ack(void) 267 { 268 if (!isatty(0) || !isatty(1)) 269 return; 270 while (rgetchar() != ' ') 271 ; 272 } 273 274 short 275 pack_letter(const char *prompt, unsigned short mask) 276 { 277 short ch; 278 unsigned short tmask = mask; 279 280 if (!mask_pack(&rogue.pack, mask)) { 281 message("nothing appropriate", 0); 282 return(CANCEL); 283 } 284 for (;;) { 285 286 message(prompt, 0); 287 288 for (;;) { 289 ch = rgetchar(); 290 if (!is_pack_letter(&ch, &mask)) { 291 sound_bell(); 292 } else { 293 break; 294 } 295 } 296 297 if (ch == LIST) { 298 check_message(); 299 mask = tmask; 300 inventory(&rogue.pack, mask); 301 } else { 302 break; 303 } 304 mask = tmask; 305 } 306 check_message(); 307 return(ch); 308 } 309 310 void 311 take_off(void) 312 { 313 char desc[DCOLS]; 314 object *obj; 315 316 if (rogue.armor) { 317 if (rogue.armor->is_cursed) { 318 message(curse_message, 0); 319 } else { 320 mv_aquatars(); 321 obj = rogue.armor; 322 unwear(rogue.armor); 323 strcpy(desc, "was wearing "); 324 get_desc(obj, desc+12); 325 message(desc, 0); 326 print_stats(STAT_ARMOR); 327 reg_move(); 328 } 329 } else { 330 message("not wearing any", 0); 331 } 332 } 333 334 void 335 wear(void) 336 { 337 short ch; 338 object *obj; 339 char desc[DCOLS]; 340 341 if (rogue.armor) { 342 message("you're already wearing some", 0); 343 return; 344 } 345 ch = pack_letter("wear what?", ARMOR); 346 347 if (ch == CANCEL) { 348 return; 349 } 350 if (!(obj = get_letter_object(ch))) { 351 message("no such item.", 0); 352 return; 353 } 354 if (obj->what_is != ARMOR) { 355 message("you can't wear that", 0); 356 return; 357 } 358 obj->identified = 1; 359 strcpy(desc, "wearing "); 360 get_desc(obj, desc + 8); 361 message(desc, 0); 362 do_wear(obj); 363 print_stats(STAT_ARMOR); 364 reg_move(); 365 } 366 367 void 368 unwear(object *obj) 369 { 370 if (obj) { 371 obj->in_use_flags &= (~BEING_WORN); 372 } 373 rogue.armor = NULL; 374 } 375 376 void 377 do_wear(object *obj) 378 { 379 rogue.armor = obj; 380 obj->in_use_flags |= BEING_WORN; 381 obj->identified = 1; 382 } 383 384 void 385 wield(void) 386 { 387 short ch; 388 object *obj; 389 char desc[DCOLS]; 390 391 if (rogue.weapon && rogue.weapon->is_cursed) { 392 message(curse_message, 0); 393 return; 394 } 395 ch = pack_letter("wield what?", WEAPON); 396 397 if (ch == CANCEL) { 398 return; 399 } 400 if (!(obj = get_letter_object(ch))) { 401 message("No such item.", 0); 402 return; 403 } 404 if (obj->what_is & (ARMOR | RING)) { 405 sprintf(desc, "you can't wield %s", 406 ((obj->what_is == ARMOR) ? "armor" : "rings")); 407 message(desc, 0); 408 return; 409 } 410 if (obj->in_use_flags & BEING_WIELDED) { 411 message("in use", 0); 412 } else { 413 unwield(rogue.weapon); 414 strcpy(desc, "wielding "); 415 get_desc(obj, desc + 9); 416 message(desc, 0); 417 do_wield(obj); 418 reg_move(); 419 } 420 } 421 422 void 423 do_wield(object *obj) 424 { 425 rogue.weapon = obj; 426 obj->in_use_flags |= BEING_WIELDED; 427 } 428 429 void 430 unwield(object *obj) 431 { 432 if (obj) { 433 obj->in_use_flags &= (~BEING_WIELDED); 434 } 435 rogue.weapon = NULL; 436 } 437 438 void 439 call_it(void) 440 { 441 short ch; 442 object *obj; 443 struct id *id_table; 444 char buf[MAX_TITLE_LENGTH+2]; 445 446 ch = pack_letter("call what?", (SCROL | POTION | WAND | RING)); 447 448 if (ch == CANCEL) { 449 return; 450 } 451 if (!(obj = get_letter_object(ch))) { 452 message("no such item.", 0); 453 return; 454 } 455 if (!(obj->what_is & (SCROL | POTION | WAND | RING))) { 456 message("surely you already know what that's called", 0); 457 return; 458 } 459 id_table = get_id_table(obj); 460 461 if (get_input_line("call it:","",buf,id_table[obj->which_kind].title,1,1)) { 462 id_table[obj->which_kind].id_status = CALLED; 463 strcpy(id_table[obj->which_kind].title, buf); 464 } 465 } 466 467 short 468 pack_count(const object *new_obj) 469 { 470 object *obj; 471 short count = 0; 472 473 obj = rogue.pack.next_object; 474 475 while (obj) { 476 if (obj->what_is != WEAPON) { 477 count += obj->quantity; 478 } else if (!new_obj) { 479 count++; 480 } else if ((new_obj->what_is != WEAPON) || 481 ((obj->which_kind != ARROW) && 482 (obj->which_kind != DAGGER) && 483 (obj->which_kind != DART) && 484 (obj->which_kind != SHURIKEN)) || 485 (new_obj->which_kind != obj->which_kind) || 486 (obj->quiver != new_obj->quiver)) { 487 count++; 488 } 489 obj = obj->next_object; 490 } 491 return(count); 492 } 493 494 static boolean 495 mask_pack(const object *pack, unsigned short mask) 496 { 497 while (pack->next_object) { 498 pack = pack->next_object; 499 if (pack->what_is & mask) { 500 return(1); 501 } 502 } 503 return(0); 504 } 505 506 static boolean 507 is_pack_letter(short *c, unsigned short *mask) 508 { 509 if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') || 510 (*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) { 511 switch(*c) { 512 case '?': 513 *mask = SCROL; 514 break; 515 case '!': 516 *mask = POTION; 517 break; 518 case ':': 519 *mask = FOOD; 520 break; 521 case ')': 522 *mask = WEAPON; 523 break; 524 case ']': 525 *mask = ARMOR; 526 break; 527 case '/': 528 *mask = WAND; 529 break; 530 case '=': 531 *mask = RING; 532 break; 533 case ',': 534 *mask = AMULET; 535 break; 536 } 537 *c = LIST; 538 return(1); 539 } 540 return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST)); 541 } 542 543 boolean 544 has_amulet(void) 545 { 546 return(mask_pack(&rogue.pack, AMULET)); 547 } 548 549 void 550 kick_into_pack(void) 551 { 552 object *obj; 553 char desc[DCOLS]; 554 short n, stat; 555 556 if (!(dungeon[rogue.row][rogue.col] & OBJECT)) { 557 message("nothing here", 0); 558 } else { 559 if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) { 560 get_desc(obj, desc); 561 if (obj->what_is == GOLD) { 562 message(desc, 0); 563 free_object(obj); 564 } else { 565 n = strlen(desc); 566 desc[n] = '('; 567 desc[n+1] = obj->ichar; 568 desc[n+2] = ')'; 569 desc[n+3] = 0; 570 message(desc, 0); 571 } 572 } 573 if (obj || (!stat)) { 574 reg_move(); 575 } 576 } 577 } 578