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