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