1/* $NetBSD: pack.c,v 1.11 2009/08/12 08:44:45 dholland Exp $ */ 2 3/* 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Timothy C. Stoehr. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include <sys/cdefs.h> 36#ifndef lint 37#if 0 38static char sccsid[] = "@(#)pack.c 8.1 (Berkeley) 5/31/93"; 39#else 40__RCSID("$NetBSD: pack.c,v 1.11 2009/08/12 08:44:45 dholland Exp $"); 41#endif 42#endif /* not lint */ 43 44/* 45 * pack.c 46 * 47 * This source herein may be modified and/or distributed by anybody who 48 * so desires, with the following restrictions: 49 * 1.) No portion of this notice shall be removed. 50 * 2.) Credit shall not be taken for the creation of this source. 51 * 3.) This code is not to be traded, sold, or used for personal 52 * gain or profit. 53 * 54 */ 55 56#include "rogue.h" 57 58const char curse_message[] = "you can't, it appears to be cursed"; 59 60static object *check_duplicate(object *, object *); 61static boolean is_pack_letter(short *, unsigned short *); 62static boolean mask_pack(const object *, unsigned short); 63static short next_avail_ichar(void); 64 65object * 66add_to_pack(object *obj, object *pack, int condense) 67{ 68 object *op; 69 70 if (condense) { 71 if ((op = check_duplicate(obj, pack)) != NULL) { 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 92void 93take_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 105object * 106pick_up(int row, int col, short *status) 107{ 108 object *obj; 109 110 *status = 1; 111 112 if (levitate) { 113 messagef(0, "you're floating in the air!"); 114 return NULL; 115 } 116 obj = object_at(&level_objects, row, col); 117 if (!obj) { 118 messagef(1, "pick_up(): inconsistent"); 119 return(obj); 120 } 121 if ( (obj->what_is == SCROL) && 122 (obj->which_kind == SCARE_MONSTER) && 123 obj->picked_up) { 124 messagef(0, "the scroll turns to dust as you pick it up"); 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 messagef(1, "pack too full"); 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 151void 152drop(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 messagef(0, "there's already something there"); 160 return; 161 } 162 if (!rogue.pack.next_object) { 163 messagef(0, "you have nothing to drop"); 164 return; 165 } 166 if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) { 167 return; 168 } 169 if (!(obj = get_letter_object(ch))) { 170 messagef(0, "no such item."); 171 return; 172 } 173 if (obj->in_use_flags & BEING_WIELDED) { 174 if (obj->is_cursed) { 175 messagef(0, "%s", curse_message); 176 return; 177 } 178 unwield(rogue.weapon); 179 } else if (obj->in_use_flags & BEING_WORN) { 180 if (obj->is_cursed) { 181 messagef(0, "%s", curse_message); 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 messagef(0, "%s", curse_message); 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 get_desc(obj, desc, sizeof(desc)); 209 messagef(0, "dropped %s", desc); 210 (void)reg_move(); 211} 212 213static object * 214check_duplicate(object *obj, object *pack) 215{ 216 object *op; 217 218 if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) { 219 return(0); 220 } 221 if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) { 222 return(0); 223 } 224 op = pack->next_object; 225 226 while (op) { 227 if ((op->what_is == obj->what_is) && 228 (op->which_kind == obj->which_kind)) { 229 230 if ((obj->what_is != WEAPON) || 231 ((obj->what_is == WEAPON) && 232 ((obj->which_kind == ARROW) || 233 (obj->which_kind == DAGGER) || 234 (obj->which_kind == DART) || 235 (obj->which_kind == SHURIKEN)) && 236 (obj->quiver == op->quiver))) { 237 op->quantity += obj->quantity; 238 return(op); 239 } 240 } 241 op = op->next_object; 242 } 243 return(0); 244} 245 246static short 247next_avail_ichar(void) 248{ 249 object *obj; 250 int i; 251 boolean ichars[26]; 252 253 for (i = 0; i < 26; i++) { 254 ichars[i] = 0; 255 } 256 obj = rogue.pack.next_object; 257 while (obj) { 258 if (obj->ichar >= 'a' && obj->ichar <= 'z') { 259 ichars[(obj->ichar - 'a')] = 1; 260 } 261 obj = obj->next_object; 262 } 263 for (i = 0; i < 26; i++) { 264 if (!ichars[i]) { 265 return(i + 'a'); 266 } 267 } 268 return('?'); 269} 270 271void 272wait_for_ack(void) 273{ 274 while (rgetchar() != ' ') 275 ; 276} 277 278short 279pack_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 messagef(0, "nothing appropriate"); 286 return(CANCEL); 287 } 288 for (;;) { 289 290 messagef(0, "%s", prompt); 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 314void 315take_off(void) 316{ 317 char desc[DCOLS]; 318 object *obj; 319 320 if (rogue.armor) { 321 if (rogue.armor->is_cursed) { 322 messagef(0, "%s", curse_message); 323 } else { 324 mv_aquatars(); 325 obj = rogue.armor; 326 unwear(rogue.armor); 327 get_desc(obj, desc, sizeof(desc)); 328 messagef(0, "was wearing %s", desc); 329 print_stats(STAT_ARMOR); 330 (void)reg_move(); 331 } 332 } else { 333 messagef(0, "not wearing any"); 334 } 335} 336 337void 338wear(void) 339{ 340 short ch; 341 object *obj; 342 char desc[DCOLS]; 343 344 if (rogue.armor) { 345 messagef(0, "you're already wearing some"); 346 return; 347 } 348 ch = pack_letter("wear what?", ARMOR); 349 350 if (ch == CANCEL) { 351 return; 352 } 353 if (!(obj = get_letter_object(ch))) { 354 messagef(0, "no such item."); 355 return; 356 } 357 if (obj->what_is != ARMOR) { 358 messagef(0, "you can't wear that"); 359 return; 360 } 361 obj->identified = 1; 362 get_desc(obj, desc, sizeof(desc)); 363 messagef(0, "wearing %s", desc); 364 do_wear(obj); 365 print_stats(STAT_ARMOR); 366 (void)reg_move(); 367} 368 369void 370unwear(object *obj) 371{ 372 if (obj) { 373 obj->in_use_flags &= (~BEING_WORN); 374 } 375 rogue.armor = NULL; 376} 377 378void 379do_wear(object *obj) 380{ 381 rogue.armor = obj; 382 obj->in_use_flags |= BEING_WORN; 383 obj->identified = 1; 384} 385 386void 387wield(void) 388{ 389 short ch; 390 object *obj; 391 char desc[DCOLS]; 392 393 if (rogue.weapon && rogue.weapon->is_cursed) { 394 messagef(0, "%s", curse_message); 395 return; 396 } 397 ch = pack_letter("wield what?", WEAPON); 398 399 if (ch == CANCEL) { 400 return; 401 } 402 if (!(obj = get_letter_object(ch))) { 403 messagef(0, "No such item."); 404 return; 405 } 406 if (obj->what_is & (ARMOR | RING)) { 407 messagef(0, "you can't wield %s", 408 ((obj->what_is == ARMOR) ? "armor" : "rings")); 409 return; 410 } 411 if (obj->in_use_flags & BEING_WIELDED) { 412 messagef(0, "in use"); 413 } else { 414 unwield(rogue.weapon); 415 get_desc(obj, desc, sizeof(desc)); 416 messagef(0, "wielding %s", desc); 417 do_wield(obj); 418 (void)reg_move(); 419 } 420} 421 422void 423do_wield(object *obj) 424{ 425 rogue.weapon = obj; 426 obj->in_use_flags |= BEING_WIELDED; 427} 428 429void 430unwield(object *obj) 431{ 432 if (obj) { 433 obj->in_use_flags &= (~BEING_WIELDED); 434 } 435 rogue.weapon = NULL; 436} 437 438void 439call_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 messagef(0, "no such item."); 453 return; 454 } 455 if (!(obj->what_is & (SCROL | POTION | WAND | RING))) { 456 messagef(0, "surely you already know what that's called"); 457 return; 458 } 459 id_table = get_id_table(obj); 460 461 if (get_input_line("call it:", "", buf, sizeof(buf), 462 id_table[obj->which_kind].title, 1, 1)) { 463 id_table[obj->which_kind].id_status = CALLED; 464 (void)strlcpy(id_table[obj->which_kind].title, buf, 465 sizeof(id_table[obj->which_kind].title)); 466 } 467} 468 469short 470pack_count(const object *new_obj) 471{ 472 object *obj; 473 short count = 0; 474 475 obj = rogue.pack.next_object; 476 477 while (obj) { 478 if (obj->what_is != WEAPON) { 479 count += obj->quantity; 480 } else if (!new_obj) { 481 count++; 482 } else if ((new_obj->what_is != WEAPON) || 483 ((obj->which_kind != ARROW) && 484 (obj->which_kind != DAGGER) && 485 (obj->which_kind != DART) && 486 (obj->which_kind != SHURIKEN)) || 487 (new_obj->which_kind != obj->which_kind) || 488 (obj->quiver != new_obj->quiver)) { 489 count++; 490 } 491 obj = obj->next_object; 492 } 493 return(count); 494} 495 496static boolean 497mask_pack(const object *pack, unsigned short mask) 498{ 499 while (pack->next_object) { 500 pack = pack->next_object; 501 if (pack->what_is & mask) { 502 return(1); 503 } 504 } 505 return(0); 506} 507 508static boolean 509is_pack_letter(short *c, unsigned short *mask) 510{ 511 if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') || 512 (*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) { 513 switch(*c) { 514 case '?': 515 *mask = SCROL; 516 break; 517 case '!': 518 *mask = POTION; 519 break; 520 case ':': 521 *mask = FOOD; 522 break; 523 case ')': 524 *mask = WEAPON; 525 break; 526 case ']': 527 *mask = ARMOR; 528 break; 529 case '/': 530 *mask = WAND; 531 break; 532 case '=': 533 *mask = RING; 534 break; 535 case ',': 536 *mask = AMULET; 537 break; 538 } 539 *c = LIST; 540 return(1); 541 } 542 return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST)); 543} 544 545boolean 546has_amulet(void) 547{ 548 return(mask_pack(&rogue.pack, AMULET)); 549} 550 551void 552kick_into_pack(void) 553{ 554 object *obj; 555 char desc[DCOLS]; 556 short stat; 557 558 if (!(dungeon[rogue.row][rogue.col] & OBJECT)) { 559 messagef(0, "nothing here"); 560 } else { 561 if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) { 562 get_desc(obj, desc, sizeof(desc)); 563 if (obj->what_is == GOLD) { 564 messagef(0, "%s", desc); 565 free_object(obj); 566 } else { 567 messagef(0, "%s(%c)", desc, obj->ichar); 568 } 569 } 570 if (obj || (!stat)) { 571 (void)reg_move(); 572 } 573 } 574} 575