/* KMH -- Talking artifacts are finally implemented */ void arti_speak(struct obj *obj) { const struct artifact *oart = get_artifact(obj); const char *line; char buf[BUFSZ]; /* Is this a speaking artifact? */ if (!oart || !(oart->spfx & SPFX_SPEAK)) return; line = getrumor(bcsign(obj), buf, TRUE); if (!*line) line = "NitroHack rumors file closed for renovation."; pline("%s:", Tobjnam(obj, "whisper")); verbalize("%s", line); return; }
struct obj * mksobj(int otyp, boolean init, boolean artif) { int mndx, tryct; struct obj *otmp; char let = objects[otyp].oc_class; otmp = newobj(0); *otmp = zeroobj; otmp->age = monstermoves; otmp->o_id = flags.ident++; if (!otmp->o_id) otmp->o_id = flags.ident++; /* ident overflowed */ otmp->quan = 1L; otmp->oclass = let; otmp->otyp = otyp; otmp->where = OBJ_FREE; otmp->dknown = index(dknowns, let) ? 0 : 1; if (otmp->otyp == AMULET_OF_YENDOR) otmp->orecursive = FALSE; if ((otmp->otyp >= ELVEN_SHIELD && otmp->otyp <= ORCISH_SHIELD) || otmp->otyp == SHIELD_OF_REFLECTION) otmp->dknown = 0; if (!objects[otmp->otyp].oc_uses_known) otmp->known = 1; if (is_rustprone(otmp) || is_corrodeable(otmp) || is_flammable(otmp)) otmp->rknown = 1; #ifdef INVISIBLE_OBJECTS otmp->oinvis = !rn2(1250); #endif if (init) switch (let) { case WEAPON_CLASS: otmp->quan = is_multigen(otmp) ? (long) rn1(6,6) : 1L; if(!rn2(11)) { otmp->spe = rne(3); otmp->blessed = rn2(2); } else if(!rn2(10)) { curse(otmp); otmp->spe = -rne(3); } else blessorcurse(otmp, 10); if (is_poisonable(otmp) && !rn2(100)) otmp->opoisoned = 1; if (artif && !rn2(20)) otmp = mk_artifact(otmp, (aligntyp)A_NONE); break; case FOOD_CLASS: otmp->odrained = 0; otmp->oeaten = 0; switch(otmp->otyp) { case CORPSE: /* possibly overridden by mkcorpstat() */ tryct = 50; do otmp->corpsenm = undead_to_corpse(rndmonnum()); while ((mvitals[otmp->corpsenm].mvflags & G_NOCORPSE) && (--tryct > 0)); if (tryct == 0) { /* perhaps rndmonnum() only wants to make G_NOCORPSE monsters on this level; let's create an adventurer's corpse instead, then */ otmp->corpsenm = PM_HUMAN; } /* timer set below */ break; case EGG: otmp->corpsenm = NON_PM; /* generic egg */ if (!rn2(3)) for (tryct = 200; tryct > 0; --tryct) { mndx = can_be_hatched(rndmonnum()); if (mndx != NON_PM && !dead_species(mndx, TRUE)) { otmp->corpsenm = mndx; /* typed egg */ attach_egg_hatch_timeout(otmp); break; } } break; case TIN: otmp->corpsenm = NON_PM; /* empty (so far) */ if (!rn2(6)) otmp->spe = 1; /* spinach */ else for (tryct = 200; tryct > 0; --tryct) { mndx = undead_to_corpse(rndmonnum()); if (mons[mndx].cnutrit && !(mvitals[mndx].mvflags & G_NOCORPSE)) { otmp->corpsenm = mndx; break; } } blessorcurse(otmp, 10); break; case SLIME_MOLD: otmp->spe = current_fruit; break; case KELP_FROND: otmp->quan = (long) rnd(2); break; } if (otmp->otyp == CORPSE || otmp->otyp == MEAT_RING || otmp->otyp == KELP_FROND) break; /* fall into next case */ case GEM_CLASS: if (otmp->otyp == LOADSTONE) curse(otmp); else if (otmp->otyp == ROCK) otmp->quan = (long) rn1(6,6); else if (otmp->otyp != LUCKSTONE && !rn2(6)) otmp->quan = 2L; else otmp->quan = 1L; break; case TOOL_CLASS: switch(otmp->otyp) { case TALLOW_CANDLE: case WAX_CANDLE: otmp->spe = 1; otmp->age = 20L * /* 400 or 200 */ (long)objects[otmp->otyp].oc_cost; otmp->lamplit = 0; otmp->quan = 1L + (long)(rn2(2) ? rn2(7) : 0); blessorcurse(otmp, 5); break; case BRASS_LANTERN: case OIL_LAMP: otmp->spe = 1; otmp->age = (long) rn1(500,1000); otmp->lamplit = 0; blessorcurse(otmp, 5); break; case MAGIC_LAMP: otmp->spe = 1; otmp->lamplit = 0; blessorcurse(otmp, 2); break; case IRON_SAFE: otmp->olocked = 1; case CHEST: case LARGE_BOX: if (otmp->otyp != IRON_SAFE) { otmp->olocked = !!(rn2(5)); /* clumsy tweak */ } otmp->otrapped = !(rn2(10)); case ICE_BOX: case SACK: case OILSKIN_SACK: case BAG_OF_HOLDING: mkbox_cnts(otmp); break; #ifdef TOURIST case EXPENSIVE_CAMERA: #endif case TINNING_KIT: case MAGIC_MARKER: otmp->spe = rn1(60,20); break; case CAN_OF_GREASE: otmp->spe = rnd(25); blessorcurse(otmp, 10); break; case CRYSTAL_BALL: otmp->spe = rnd(5); blessorcurse(otmp, 2); break; case HORN_OF_PLENTY: case BAG_OF_TRICKS: otmp->spe = rnd(20); break; case FIGURINE: { int tryct2 = 0; do otmp->corpsenm = rndmonnum(); while(is_human(&mons[otmp->corpsenm]) && tryct2++ < 30); blessorcurse(otmp, 4); break; } case BELL_OF_OPENING: otmp->spe = 3; break; case MAGIC_FLUTE: case MAGIC_HARP: case FROST_HORN: case FIRE_HORN: case DRUM_OF_EARTHQUAKE: otmp->spe = rn1(5,4); break; } break; case AMULET_CLASS: if (otmp->otyp == AMULET_OF_YENDOR) flags.made_amulet = TRUE; if(rn2(10) && (otmp->otyp == AMULET_OF_STRANGULATION || otmp->otyp == AMULET_OF_CHANGE || otmp->otyp == AMULET_OF_RESTFUL_SLEEP)) { curse(otmp); } else blessorcurse(otmp, 10); case VENOM_CLASS: case CHAIN_CLASS: case BALL_CLASS: break; case POTION_CLASS: if (otmp->otyp == POT_OIL) otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */ /* fall through */ case SCROLL_CLASS: #ifdef MAIL if (otmp->otyp != SCR_MAIL) #endif blessorcurse(otmp, 4); break; case SPBOOK_CLASS: blessorcurse(otmp, 17); break; case ARMOR_CLASS: if(rn2(10) && (otmp->otyp == FUMBLE_BOOTS || otmp->otyp == LEVITATION_BOOTS || otmp->otyp == HELM_OF_OPPOSITE_ALIGNMENT || otmp->otyp == GAUNTLETS_OF_FUMBLING || otmp->otyp == TINFOIL_HAT || !rn2(11))) { curse(otmp); otmp->spe = -rne(3); } else if(!rn2(10)) { otmp->blessed = rn2(2); otmp->spe = rne(3); } else blessorcurse(otmp, 10); if (artif && !rn2(40)) otmp = mk_artifact(otmp, (aligntyp)A_NONE); /* simulate lacquered armor for samurai */ if (Role_if(PM_SAMURAI) && otmp->otyp == SPLINT_MAIL && (moves <= 1 || In_quest(&u.uz))) { #ifdef UNIXPC /* optimizer bitfield bug */ otmp->oerodeproof = 1; otmp->rknown = 1; #else otmp->oerodeproof = otmp->rknown = 1; #endif } break; case WAND_CLASS: if (otmp->otyp == WAN_WISHING) { otmp->spe = (rnf(2,3) ? 1 : rnf(1,2) ? 0 : 2); otmp->recharged = 1; } else { otmp->spe = rn1(5, (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); otmp->recharged = 0; /* used to control recharging */ } blessorcurse(otmp, 17); break; case RING_CLASS: if(objects[otmp->otyp].oc_charged) { blessorcurse(otmp, 3); if(rn2(10)) { if(rn2(10) && bcsign(otmp)) otmp->spe = bcsign(otmp) * rne(3); else otmp->spe = rn2(2) ? rne(3) : -rne(3); } /* make useless +0 rings much less common */ if (otmp->spe == 0) otmp->spe = rn2(4) - rn2(3); /* negative rings are usually cursed */ if (otmp->spe < 0 && rn2(5)) curse(otmp); } else if(rn2(10) && (otmp->otyp == RIN_TELEPORTATION || otmp->otyp == RIN_POLYMORPH || otmp->otyp == RIN_AGGRAVATE_MONSTER || otmp->otyp == RIN_HUNGER || !rn2(9))) { curse(otmp); } break; case ROCK_CLASS: switch (otmp->otyp) { case STATUE: /* possibly overridden by mkcorpstat() */ otmp->corpsenm = rndmonnum(); if (!verysmall(&mons[otmp->corpsenm]) && rn2(level_difficulty()/2 + 10) > 10) (void) add_to_container(otmp, mkobj(SPBOOK_CLASS,FALSE)); } break; case COIN_CLASS: break; /* do nothing */ default: warning("impossible mkobj %d, sym '%c'.", otmp->otyp, objects[otmp->otyp].oc_class); return (struct obj *)0; } /* Some things must get done (timers) even if init = 0 */ switch (otmp->otyp) { case CORPSE: start_corpse_timeout(otmp); break; } /* unique objects may have an associated artifact entry */ if (objects[otyp].oc_unique && !otmp->oartifact) otmp = mk_artifact(otmp, (aligntyp)A_NONE); otmp->owt = weight(otmp); return(otmp); }
int dowrite(struct obj *pen, const struct nh_cmd_arg *arg) { struct obj *paper; const char *namebuf, *nm, *bp; struct obj *new_obj; int basecost, actualcost; int curseval; const char *qbuf; int first, last, i; boolean by_descr = FALSE, by_name = FALSE; const char *typeword; if (nohands(youmonst.data)) { pline(msgc_cancelled, "You need hands to be able to write!"); return 0; } else if (slippery_fingers(&youmonst)) { pline(msgc_cancelled1, "%s from your %s.", Tobjnam(pen, "slip"), makeplural(body_part(FINGER))); unwield_silently(pen); dropx(pen); return 1; } /* get paper to write on */ paper = getargobj(arg, write_on, "write on"); if (!paper) return 0; typeword = (paper->oclass == SPBOOK_CLASS) ? "spellbook" : "scroll"; if (Blind && !paper->dknown) { pline(msgc_cancelled1, "You don't know if that %s is blank or not!", typeword); return 1; } paper->dknown = 1; if (paper->otyp != SCR_BLANK_PAPER && paper->otyp != SPE_BLANK_PAPER) { pline(msgc_cancelled1, "That %s is not blank!", typeword); exercise(A_WIS, FALSE); return 1; } /* what to write */ qbuf = msgprintf("What type of %s do you want to write?", typeword); namebuf = getarglin(arg, qbuf); namebuf = msgmungspaces(namebuf); /* remove any excess whitespace */ if (namebuf[0] == '\033' || !namebuf[0]) return 1; nm = namebuf; if (!strncmpi(nm, "scroll ", 7)) nm += 7; else if (!strncmpi(nm, "spellbook ", 10)) nm += 10; if (!strncmpi(nm, "of ", 3)) nm += 3; if ((bp = strstri(nm, " armour")) != 0) nm = msgcat_many(msgchop(nm, bp-nm), " armor", bp+7, NULL); first = bases[(int)paper->oclass]; last = bases[(int)paper->oclass + 1] - 1; for (i = first; i <= last; i++) { /* extra shufflable descr not representing a real object */ if (!OBJ_NAME(objects[i])) continue; if (!strcmpi(OBJ_NAME(objects[i]), nm)) goto found; if (!strcmpi(OBJ_DESCR(objects[i]), nm)) { by_descr = TRUE; goto found; } if (objects[i].oc_uname && !strcmpi(objects[i].oc_uname, nm)) { by_name = TRUE; goto found; } } pline(msgc_cancelled1, "There is no such %s!", typeword); return 1; found: if (i == SCR_BLANK_PAPER || i == SPE_BLANK_PAPER) { pline(msgc_cancelled1, "You can't write that!"); pline(msgc_cancelled1, "It's obscene!"); return 1; } else if (i == SPE_BOOK_OF_THE_DEAD) { pline(msgc_cancelled1, "No mere dungeon adventurer could write that."); return 1; } else if ((by_descr || by_name) && paper->oclass == SPBOOK_CLASS && !objects[i].oc_name_known) { /* can't write unknown spellbooks by description */ pline(msgc_cancelled1, "Unfortunately you don't have enough information to go on."); return 1; } /* KMH, conduct */ break_conduct(conduct_illiterate); new_obj = mksobj(level, i, FALSE, FALSE, rng_main); new_obj->bknown = (paper->bknown && pen->bknown); /* shk imposes a flat rate per use, not based on actual charges used */ check_unpaid(pen); /* see if there's enough ink */ basecost = cost(new_obj); if (pen->spe < basecost / 2) { pline(msgc_failcurse, "Your marker is too dry to write that!"); obfree(new_obj, NULL); return 1; } /* we're really going to write now, so calculate cost no custom RNG used: too much influence from player actions */ actualcost = rn1(basecost / 2, basecost / 2); curseval = bcsign(pen) + bcsign(paper); exercise(A_WIS, TRUE); /* dry out marker */ if (pen->spe < actualcost) { pen->spe = 0; pline(msgc_itemloss, "Your marker dries out!"); /* scrolls disappear, spellbooks don't */ if (paper->oclass == SPBOOK_CLASS) { pline(msgc_failcurse, "The spellbook is left unfinished and your writing fades."); update_inventory(); /* pen charges */ } else { pline(msgc_failcurse, "The scroll is now useless and disappears!"); useup(paper); } obfree(new_obj, NULL); return 1; } pen->spe -= actualcost; /* can't write if we don't know it - unless we're lucky */ if (!(objects[new_obj->otyp].oc_name_known) && (rnl(Role_if(PM_WIZARD) ? 3 : 15))) { pline(msgc_failrandom, "You %s to write that!", by_descr ? "fail" : "don't know how"); /* scrolls disappear, spellbooks don't */ if (paper->oclass == SPBOOK_CLASS) { pline_implied(msgc_failrandom, "You write in your best handwriting: " "\"My Diary\", but it quickly fades."); update_inventory(); /* pen charges */ } else { const char *written; if (by_descr) { written = OBJ_DESCR(objects[new_obj->otyp]); written = eroded_text(written, (6 + MAXULEV - youmonst.m_lev) / 6, 0); } else written = msgprintf("%s was here!", u.uplname); pline_implied(msgc_failrandom, "You write \"%s\" and the scroll disappears.", written); useup(paper); } obfree(new_obj, NULL); return 1; } /* useup old scroll / spellbook */ useup(paper); /* success */ if (new_obj->oclass == SPBOOK_CLASS) { /* acknowledge the change in the object's description... */ pline(msgc_actionok, "The spellbook warps strangely, then turns %s.", OBJ_DESCR(objects[new_obj->otyp])); } new_obj->blessed = (curseval > 0); new_obj->cursed = (curseval < 0); hold_another_object(new_obj, "Oops! %s out of your grasp!", The(aobjnam(new_obj, "slip")), NULL); return 1; }
int doread() { register struct obj *scroll; register boolean confused = (Confusion != 0); known = FALSE; scroll = getobj(readable, "read"); /* "#-" added by GAN 10/22/86 */ if(!scroll) return(0); /* below added to allow reading of fortune cookies */ if(scroll->otyp == FORTUNE_COOKIE) { if(flags.verbose) You("break up the cookie and throw away the pieces."); outrumor(bcsign(scroll), TRUE); useup(scroll); return(1); } else if(scroll->olet != SCROLL_SYM #ifdef SPELLS && scroll->olet != SPBOOK_SYM #endif ) { pline("That is a silly thing to read."); return(0); } if(Blind) { #ifdef SPELLS if (scroll->olet == SPBOOK_SYM) { pline("Being blind, you cannot read the mystic runes."); return(0); } else #endif if (!scroll->dknown) { pline("Being blind, you cannot read the formula on the scroll."); return(0); } } #ifndef NO_SIGNAL scroll->in_use = TRUE; /* now being read */ #endif #ifdef SPELLS if(scroll->olet == SPBOOK_SYM) { if(confused) { You("cannot grasp the meaning of this tome."); useup(scroll); return(0); } else return(study_book(scroll)); } #endif if(scroll->otyp != SCR_BLANK_PAPER) { if(Blind) pline("As you pronounce the formula on it, the scroll disappears."); else pline("As you read the scroll, it disappears."); if(confused) { if (Hallucination) pline("Being so trippy, you screw up..."); else pline("Being confused, you mispronounce the magic words..."); } } if(!seffects(scroll)) { if(!objects[scroll->otyp].oc_name_known) { if(known && !confused) { makeknown(scroll->otyp); more_experienced(0,10); } else if(!objects[scroll->otyp].oc_uname) docall(scroll); } if(!(scroll->otyp == SCR_BLANK_PAPER) || confused) useup(scroll); #ifndef NO_SIGNAL else scroll->in_use = FALSE; #endif } return(1); }