1 /* SCCS Id: @(#)write.c 3.4 2001/11/29 */
2 /* NetHack may be freely redistributed. See license for details. */
6 /* it would have been too much to ask to simply have a function that checks write costs for base items without requiring
7 * an actual dummy item to exist... --Amy */
14 register struct obj
*pseudo
= mksobj(SCR_BLANK_PAPER
, FALSE
, 2, FALSE
);
16 writecostbah
= writecost(pseudo
);
18 obfree(pseudo
, (struct obj
*)0); /* now, get rid of it */
24 * returns basecost of a scroll or a spellbook
28 register struct obj
*otmp
;
31 if (otmp
->oclass
== SPBOOK_CLASS
)
32 return(10 * objects
[otmp
->otyp
].oc_level
);
34 /* KMH, balance patch -- restoration of marker charges */
47 case SCR_GOLD_DETECTION
:
48 case SCR_FOOD_DETECTION
:
49 case SCR_TRAP_DETECTION
:
50 case SCR_MAGIC_MAPPING
:
52 case SCR_INSTANT_AMNESIA
:
56 case SCR_CURE_BLINDNESS
:
57 case SCR_ROOT_PASSWORD_DETECTION
:
61 case SCR_DESTROY_ARMOR
:
62 case SCR_DESTROY_WEAPON
:
64 case SCR_CREATE_MONSTER
:
65 case SCR_CREATE_VICTIM
:
66 case SCR_SUMMON_UNDEAD
:
70 case SCR_CREATE_CREATE_SCROLL
:
72 case SCR_PROOF_WEAPON
:
76 case SCR_INFERIOR_MATERIAL
:
78 case SCR_CONFUSE_MONSTER
:
81 case SCR_PROOF_ACCESSORY
:
86 case SCR_SCARE_MONSTER
:
92 case SCR_BUBBLE_BOBBLE
:
95 case SCR_TELEPORTATION
:
100 case SCR_CRYSTALLIZATION
:
112 case SCR_VISIBLE_ITEM
:
114 case SCR_EVIL_VARIANT
:
118 case SCR_DETECT_WATER
:
119 case SCR_CHAOS_TERRAIN
:
123 case SCR_MASS_MURDER
:
124 case SCR_TRAP_CREATION
:
125 case SCR_CREATE_TRAP
:
126 case SCR_GROUP_SUMMONING
:
127 case SCR_UNDO_GENOCIDE
:
128 case SCR_RANDOM_ENCHANTMENT
:
129 case SCR_BAD_EQUIPMENT
:
130 case SCR_COURSE_TRAVELING
:
132 case SCR_REGULAR_MATERIAL
:
135 /* KMH, balance patch -- more useful scrolls cost more */
136 case SCR_STINKING_CLOUD
:
137 case SCR_ENCHANT_ARMOR
:
138 case SCR_REMOVE_CURSE
:
139 case SCR_ENCHANT_WEAPON
:
149 case SCR_OFFLEVEL_ITEM
:
150 case SCR_REPAIR_ITEM
:
151 case SCR_EXTRA_HEALING
:
160 case SCR_ARMOR_SPECIALIZATION
:
161 case SCR_SUMMON_BOSS
:
164 case SCR_ELEMENTALISM
:
165 case SCR_TRAP_DISARMING
:
168 case SCR_MATERIAL_CHANGE
:
169 case SCR_CREATE_FACILITY
:
170 case SCR_SUMMON_GHOST
:
171 case SCR_GREATER_MANA_RESTORATION
:
172 case SCR_NASTY_CURSE
:
173 case SCR_TERRAFORMING
:
175 case SCR_SKILL_GROWTH
:
180 case SCR_CREATE_ALTAR
:
184 case SCR_CREATE_FAMILIAR
:
185 case SCR_ITEM_GENOCIDE
:
186 case SCR_POWER_HEALING
:
187 case SCR_REVERSE_IDENTIFY
:
188 case SCR_SUPERIOR_MATERIAL
:
189 case SCR_BRANCH_TELEPORT
:
191 case SCR_CONSECRATION
:
192 case SCR_BOSS_COMPANION
:
194 case SCR_SECURE_CURSE_REMOVAL
:
195 case SCR_INVENTORY_ID
:
197 case SCR_SECURE_IDENTIFY
:
198 case SCR_ALTER_REALITY
:
199 case SCR_HYBRIDIZATION
:
200 case SCR_GREATER_ENCHANT_WEAPON
:
201 case SCR_GREATER_ENCHANT_ARMOR
:
202 case SCR_POWER_CHARGING
:
207 case SCR_ASTRALCENSION
: /* more expensive than the max # of charges in a marker on purpose --Amy */
208 case SCR_EXTRA_SKILL_POINT
:
210 case SCR_BLANK_PAPER
:
213 case SCR_ARTIFACT_CREATION
:
214 case SCR_MISSING_CODE
:
215 case SCR_ARTIFACT_JACKPOT
:
216 case SCR_RESURRECTION
:
217 case SCR_ACQUIREMENT
:
218 case SCR_ENTHRONIZATION
:
219 case SCR_WELL_BUILDING
:
221 case SCR_TABLE_FURNITURE
:
223 case SCR_MATTRESS_SLEEPING
:
224 case SCR_MAKE_PENTAGRAM
:
225 case SCR_FOUNTAIN_BUILDING
:
227 case SCR_CREATE_SINK
:
230 /*impossible*/pline("You can't write such a weird scroll!");
235 static NEARDATA
const char write_on
[] = { SCROLL_CLASS
, SPBOOK_CLASS
, 0 };
239 register struct obj
*pen
;
241 register struct obj
*paper
;
242 char namebuf
[BUFSZ
], *nm
, *bp
;
243 register struct obj
*new_obj
;
244 int basecost
, actualcost
;
248 boolean by_descr
= FALSE
;
249 const char *typeword
;
251 int oldspe
, oldrecharged
, oldartifact
; /* for spellbooks */
254 if (nohands(youmonst
.data
) && !Race_if(PM_TRANSFORMER
) ) {
255 You("need hands to be able to write!");
256 if (yn("Attempt it anyway?") == 'y') {
257 if (rn2(3) && !polyskillchance()) {
258 drain_en(rnz(monster_difficulty() + 1) );
259 pline("You lose Mana");
260 if (!rn2(20)) badeffect();
268 pline("%s from your %s.",
269 Tobjnam(pen
, "slip"), makeplural(body_part(FINGER
)));
274 /* get paper to write on */
275 paper
= getobj(write_on
,"write on");
278 typeword
= (paper
->oclass
== SPBOOK_CLASS
) ? "spellbook" : "scroll";
279 if(Blind
&& !paper
->dknown
) {
280 You("don't know if that %s is blank or not!", typeword
);
284 if(paper
->otyp
!= SCR_BLANK_PAPER
&& paper
->otyp
!= SPE_BLANK_PAPER
) {
285 pline("That %s is not blank!", typeword
);
286 exercise(A_WIS
, FALSE
);
291 sprintf(qbuf
, "What type of %s do you want to write?", typeword
);
292 getlin(qbuf
, namebuf
);
293 (void)mungspaces(namebuf
); /* remove any excess whitespace */
294 if(namebuf
[0] == '\033' || !namebuf
[0])
297 if (!strncmpi(nm
, "scroll ", 7)) nm
+= 7;
298 else if (!strncmpi(nm
, "spellbook ", 10)) nm
+= 10;
299 if (!strncmpi(nm
, "of ", 3)) nm
+= 3;
301 if ((bp
= strstri(nm
, " armour")) != 0) {
302 (void)strncpy(bp
, " armor ", 7); /* won't add '\0' */
303 (void)mungspaces(bp
+ 1); /* remove the extra space */
306 first
= bases
[(int)paper
->oclass
];
307 last
= bases
[(int)paper
->oclass
+ 1] - 1;
308 for (i
= first
; i
<= last
; i
++) {
309 /* extra shufflable descr not representing a real object */
310 if (!OBJ_NAME(objects
[i
])) continue;
312 if (!strcmpi(OBJ_NAME(objects
[i
]), nm
))
314 if (!strcmpi(OBJ_DESCR(objects
[i
]), nm
)) {
320 There("is no such %s!", typeword
);
324 if (i
== SCR_BLANK_PAPER
|| i
== SPE_BLANK_PAPER
) {
325 You_cant("write that!");
326 pline("It's obscene!");
328 } else if (i
== SPE_BOOK_OF_THE_DEAD
) {
329 pline("No mere dungeon adventurer could write that.");
331 } else if (i
== SCR_COPYING
) {
332 You("don't know how to break copy protect.");
334 pline("(I know it, but not tell to you.)");
336 } else if (i
== SCR_WISHING
|| i
== SCR_ARTIFACT_CREATION
|| i
== SCR_MISSING_CODE
|| i
== SCR_ARTIFACT_JACKPOT
|| i
== SCR_RESURRECTION
|| i
== SCR_ACQUIREMENT
|| i
== SCR_ENTHRONIZATION
|| i
== SCR_MAKE_PENTAGRAM
|| i
== SCR_WELL_BUILDING
|| i
== SCR_DRIVING
|| i
== SCR_TABLE_FURNITURE
|| i
== SCR_EMBEDDING
|| i
== SCR_MATTRESS_SLEEPING
|| i
== SCR_FOUNTAIN_BUILDING
|| i
== SCR_SINKING
|| i
== SCR_CREATE_SINK
|| i
== SCR_WC
) {
337 pline("This scroll refuses to be written.");
339 } else if (by_descr
&& paper
->oclass
== SPBOOK_CLASS
&&
340 !objects
[i
].oc_name_known
) {
341 /* can't write unknown spellbooks by description */
343 "Unfortunately you don't have enough information to go on.");
347 if (Race_if(PM_PLAYABLE_NEANDERTHAL
)) {
348 pline("Apparently you forgot that you're illiterate. Anyway, your attempt to write fails.");
353 u
.uconduct
.literate
++;
355 new_obj
= mksobj(i
, FALSE
, FALSE
, FALSE
);
357 pline("Scroll creation failed!");
360 new_obj
->bknown
= (paper
->bknown
&& pen
->bknown
);
361 new_obj
->oinvis
= paper
->oinvis
;
362 new_obj
->oinvisreal
= paper
->oinvisreal
;
364 /* shk imposes a flat rate per use, not based on actual charges used */
367 /* see if there's enough ink */
368 basecost
= writecost(new_obj
);
370 if (basecost
>= 1000) { /* impossible! */
374 if (!(objects
[new_obj
->otyp
].oc_name_known
)) {
375 pline("That item isn't type-identified. If it also isn't type-named, writing it may fail depending on your luck.");
376 if (yn("Try anyway?") != 'y') {
377 obfree(new_obj
, (struct obj
*) 0);
381 pline("Writing that will cost up to %d ink.", basecost
);
382 if (yn("Do you want to give it a try?") != 'y') {
383 obfree(new_obj
, (struct obj
*) 0);
389 if(pen
->spe
< basecost
/2 && (objects
[new_obj
->otyp
].oc_name_known
) ) {
390 Your("marker is too dry to write that!");
391 obfree(new_obj
, (struct obj
*) 0);
395 /* we're really going to write now, so calculate cost
397 actualcost
= rn1(basecost
/2,basecost
/2);
398 if (isfriday
&& !rn2(10)) actualcost
*= 2;
399 if (pen
->oartifact
== ART_WRITE_THE_UNKNOWN
&& paper
->oclass
== SCROLL_CLASS
) actualcost
*= (2 + rnd(2));
401 if (!(PlayerCannotUseSkills
)) {
402 switch (P_SKILL(P_DEVICES
)) {
404 case P_BASIC
: actualcost
= (actualcost
* 14 / 15); break;
405 case P_SKILLED
: actualcost
= (actualcost
* 13 / 15); break;
406 case P_EXPERT
: actualcost
= (actualcost
* 12 / 15); break;
407 case P_MASTER
: actualcost
= (actualcost
* 11 / 15); break;
408 case P_GRAND_MASTER
: actualcost
= (actualcost
* 10 / 15); break;
409 case P_SUPREME_MASTER
: actualcost
= (actualcost
* 9 / 15); break;
413 curseval
= bcsign(pen
) + bcsign(paper
);
414 exercise(A_WIS
, TRUE
);
416 if (pen
->spe
< actualcost
) {
417 pen
->spe
-= rnd(pen
->spe
);
418 if (issoviet
) pen
->spe
= 0;
419 if (!pen
->spe
&& !issoviet
) Your("marker dries out!");
420 if (issoviet
) pline("Medved' khar khar, Sovet reshil, chto markery stanovyatsya pustymi, eto zdorovo. Poproshchaytes' s nim!");
421 else pline("Unfortunately, after writing for a bit you notice that there's not enough ink left. You stop writing to salvage at least some of the precious ink.");
422 /* scrolls disappear, spellbooks don't */
423 if (paper
->oclass
== SPBOOK_CLASS
) {
425 "spellbook is left unfinished and your writing fades.");
426 update_inventory(); /* pen charges */
429 if (!(paper
->oartifact
== ART_SCRIBE_WHAT_YOU_WANT_TO_SC
)) {
430 pline_The("scroll is now useless and disappears!");
434 obfree(new_obj
, (struct obj
*) 0);
437 pen
->spe
-= actualcost
;
439 /* can't write if we don't know it - unless we're lucky */
440 if(!(objects
[new_obj
->otyp
].oc_name_known
) &&
441 !(objects
[new_obj
->otyp
].oc_uname
) &&
442 !(pen
->oartifact
== ART_WRITE_THE_UNKNOWN
&& paper
->oclass
== SCROLL_CLASS
) &&
443 (rnl(Role_if(PM_WIZARD
) ? 3 : Role_if(PM_SAGE
) ? 2 : Role_if(PM_SOFTWARE_ENGINEER
) ? 11 : 15))) {
444 You("%s to write that!", by_descr
? "fail" : "don't know how");
445 /* scrolls disappear, spellbooks don't */
446 if (paper
->oclass
== SPBOOK_CLASS
) {
448 "write in your best handwriting: \"My Diary\", but it quickly fades.");
449 update_inventory(); /* pen charges */
452 strcpy(namebuf
, OBJ_DESCR(objects
[new_obj
->otyp
]));
453 wipeout_text(namebuf
, (6+MAXULEV
- u
.ulevel
)/6, 0);
455 sprintf(namebuf
, "%s was here!", playeraliasname
);
457 if (paper
->oartifact
== ART_SCRIBE_WHAT_YOU_WANT_TO_SC
) You("write \"%s\" on the scroll, which doesn't actually have any effect.", namebuf
);
458 else You("write \"%s\" and the scroll disappears.", namebuf
);
460 if (!(paper
->oartifact
== ART_SCRIBE_WHAT_YOU_WANT_TO_SC
)) {
464 obfree(new_obj
, (struct obj
*) 0);
468 /* useup old scroll / spellbook */
471 oldrecharged
= (int)paper
->recharged
; /* for spellbooks */
472 oldknown
= paper
->known
;
474 if (paper
->oartifact
) oldartifact
= paper
->oartifact
;
477 use_skill(P_DEVICES
,10);
478 if (Race_if(PM_FAWN
)) {
479 use_skill(P_DEVICES
,10);
481 if (Race_if(PM_SATRE
)) {
482 use_skill(P_DEVICES
,10);
483 use_skill(P_DEVICES
,10);
487 if (pen
->oartifact
== ART_SEEP_INTO_THE_SOUL
) {
489 Your("intelligence seeps out of your brain and into your soul...");
491 if (ABASE(A_INT
) < 2) {
493 pline("Your last thought fades away.");
494 killer
= "being too stupid to write";
495 killer_format
= KILLED_BY
;
498 pline("Unfortunately, your brain is still gone.");
499 killer
= "being too stupid to write";
500 killer_format
= KILLED_BY
;
502 /* lifesaved again */
503 You_feel("like a scarecrow.");
510 gain_alla(actualcost
);
513 if (evilfriday
&& !rn2(3)) { /* EPI that was talked about in #hardfought by several people */
515 if (ABASE(A_INT
) < 2) {
517 pline("Your last thought fades away.");
518 killer
= "being too stupid to write";
519 killer_format
= KILLED_BY
;
522 pline("Unfortunately, your brain is still gone.");
523 killer
= "being too stupid to write";
524 killer_format
= KILLED_BY
;
526 /* lifesaved again */
527 You_feel("like a scarecrow.");
530 } else if (Race_if(PM_SUSTAINER
) && rn2(50)) {
531 pline("The stat drain doesn't seem to affect you.");
532 } else if (Role_if(PM_ASTRONAUT
) && rn2(2)) {
533 pline("Your steeled body prevents the stat loss!");
536 u
.cnd_permstatdamageamount
++;
538 pline("Your intelligence seeps into the thing you wrote, and you feel stupid!");
545 pline("Your act of writing transfers some of your intelligence to the paper...");
546 adjattrib(A_INT
, -1, FALSE
, TRUE
);
551 if (new_obj
->oclass
== SPBOOK_CLASS
) {
552 /* acknowledge the change in the object's description... */
553 pline_The("spellbook warps strangely, then turns %s.",
554 OBJ_DESCR(objects
[new_obj
->otyp
]));
556 /* for some reason the charges weren't being used at all!!! --Amy */
557 new_obj
->spe
= oldspe
;
558 new_obj
->recharged
= oldrecharged
;
560 /* can't simply make a blank spellbook with dozens of charges and write a hard-to-recharge book, you cheater */
561 if ((new_obj
->otyp
== SPE_TIME
|| new_obj
->otyp
== SPE_GAIN_LEVEL
|| new_obj
->otyp
== SPE_MAP_LEVEL
|| new_obj
->otyp
== SPE_INERTIA
|| new_obj
->otyp
== SPE_CHARGING
|| new_obj
->otyp
== SPE_GENOCIDE
|| new_obj
->otyp
== SPE_GODMODE
|| new_obj
->otyp
== SPE_CHARACTER_RECURSION
|| new_obj
->otyp
== SPE_PETRIFY
|| new_obj
->otyp
== SPE_ACQUIREMENT
|| new_obj
->otyp
== SPE_THRONE_GAMBLE
|| new_obj
->otyp
== SPE_WISHING
|| new_obj
->otyp
== SPE_WORLD_FALL
|| new_obj
->otyp
== SPE_REROLL_ARTIFACT
|| new_obj
->otyp
== SPE_ATTUNE_MAGIC
|| new_obj
->otyp
== SPE_GAIN_SPACT
|| new_obj
->otyp
== SPE_CLONE_MONSTER
|| new_obj
->otyp
== SPE_TIME_STOP
|| new_obj
->otyp
== SPE_ALTER_REALITY
|| new_obj
->otyp
== SPE_AULE_SMITHING
) && new_obj
->spe
> 1) new_obj
->spe
= 1;
563 if (oldknown
== TRUE
) new_obj
->known
= TRUE
;
566 if (oldartifact
> 0) {
568 if (oldartifact
== ART_PAGAN_POETRY
) {
569 artilist
[ART_PAGAN_POETRY
].otyp
= new_obj
->otyp
;
570 new_obj
= onameX(new_obj
, artiname(oldartifact
));
571 artilist
[ART_PAGAN_POETRY
].otyp
= SPE_BLANK_PAPER
;
574 if (oldartifact
== ART_SCRIBE_WHAT_YOU_WANT_TO_SC
) {
575 artilist
[ART_SCRIBE_WHAT_YOU_WANT_TO_SC
].otyp
= new_obj
->otyp
;
576 new_obj
= onameX(new_obj
, artiname(oldartifact
));
577 artilist
[ART_SCRIBE_WHAT_YOU_WANT_TO_SC
].otyp
= SCR_BLANK_PAPER
;
582 new_obj
->blessed
= (curseval
> 0);
583 new_obj
->cursed
= (curseval
< 0);
585 if (pen
&& pen
->oartifact
== ART_PEN_OF_RANDOMNESS
) {
586 new_obj
->blessed
= 0;
588 if (!rn2(3)) new_obj
->blessed
= 1;
589 else if (!rn2(2)) new_obj
->cursed
= 1;
591 new_obj
->selfmade
= TRUE
;
593 if (new_obj
->otyp
== SCR_MAIL
) new_obj
->spe
= 1;
595 new_obj
= hold_another_object(new_obj
, "Oops! %s out of your grasp!",
596 The(aobjnam(new_obj
, "slip")),