diff -Npbaur -x '*~' -x '#*#' nethack-base/NOTES nethack-alchemy/NOTES --- nethack-base/NOTES 1970-01-01 10:00:00.000000000 +1000 +++ nethack-alchemy/NOTES 2006-06-15 14:58:30.000000000 +1000 @@ -0,0 +1,16 @@ +Started with patches: + +* Dylan's litmus patch +* Combined alchemy patch (colour + gems) +* Brewing + +Added Alchemy skill: +* Used for colour and gem alchemy (not brewing) +* % success = 30 + 10 * skill level + INT + DEX +* Available to: + Archeologist (Basic) + Healer (Expert) + Monk (Basic) + Rogue (Basic) + Tourist (Basic) + Wizard (Skilled) \ No newline at end of file diff -Npbaur -x '*~' -x '#*#' nethack-base/include/decl.h nethack-alchemy/include/decl.h --- nethack-base/include/decl.h 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/include/decl.h 2006-06-15 14:58:30.000000000 +1000 @@ -268,15 +268,16 @@ E NEARDATA struct mvitals { E NEARDATA struct c_color_names { const char *const c_black, *const c_amber, *const c_golden, - *const c_light_blue,*const c_red, *const c_green, - *const c_silver, *const c_blue, *const c_purple, - *const c_white; + *const c_light_blue, *const c_red, *const c_orange, + *const c_green, *const c_silver, *const c_blue, + *const c_purple, *const c_white; } c_color_names; #define NH_BLACK c_color_names.c_black #define NH_AMBER c_color_names.c_amber #define NH_GOLDEN c_color_names.c_golden #define NH_LIGHT_BLUE c_color_names.c_light_blue #define NH_RED c_color_names.c_red +#define NH_ORANGE c_color_names.c_orange #define NH_GREEN c_color_names.c_green #define NH_SILVER c_color_names.c_silver #define NH_BLUE c_color_names.c_blue diff -Npbaur -x '*~' -x '#*#' nethack-base/include/extern.h nethack-alchemy/include/extern.h --- nethack-base/include/extern.h 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/include/extern.h 2006-06-15 14:58:30.000000000 +1000 @@ -1556,6 +1556,7 @@ E void FDECL(potionhit, (struct monst *, E void FDECL(potionbreathe, (struct obj *)); E boolean FDECL(get_wet, (struct obj *)); E int NDECL(dodip); +E void FDECL(ferment, (genericptr_t, long)); E void FDECL(djinni_from_bottle, (struct obj *)); E struct monst *FDECL(split_mon, (struct monst *,struct monst *)); E const char *NDECL(bottlename); diff -Npbaur -x '*~' -x '#*#' nethack-base/include/skills.h nethack-alchemy/include/skills.h --- nethack-base/include/skills.h 2006-06-15 14:47:05.000000000 +1000 +++ nethack-alchemy/include/skills.h 2006-06-15 14:59:25.000000000 +1000 @@ -70,8 +70,9 @@ #define P_FIRST_H_TO_H P_BARE_HANDED_COMBAT #define P_LAST_H_TO_H P_TWO_WEAPON_COMBAT #define P_RIDING 39 /* How well you control your steed */ +#define P_ALCHEMY 40 #define P_FIRST_NON_COMBAT P_RIDING -#define P_LAST_NON_COMBAT P_RIDING +#define P_LAST_NON_COMBAT P_ALCHEMY #define P_NUM_SKILLS (P_LAST_NON_COMBAT+1) /* These roles qualify for a martial arts bonus */ diff -Npbaur -x '*~' -x '#*#' nethack-base/include/timeout.h nethack-alchemy/include/timeout.h --- nethack-base/include/timeout.h 2003-12-08 10:39:13.000000000 +1100 +++ nethack-alchemy/include/timeout.h 2006-06-15 14:58:30.000000000 +1000 @@ -28,7 +28,8 @@ typedef void FDECL((*timeout_proc), (gen #define BURN_OBJECT 3 #define HATCH_EGG 4 #define FIG_TRANSFORM 5 -#define NUM_TIME_FUNCS 6 +#define FERMENT 6 +#define NUM_TIME_FUNCS 7 /* used in timeout.c */ typedef struct fe { diff -Npbaur -x '*~' -x '#*#' nethack-base/src/decl.c nethack-alchemy/src/decl.c --- nethack-base/src/decl.c 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/src/decl.c 2006-06-15 14:58:30.000000000 +1000 @@ -209,7 +209,7 @@ NEARDATA struct mvitals mvitals[NUMMONS] NEARDATA struct c_color_names c_color_names = { "black", "amber", "golden", - "light blue", "red", "green", + "light blue", "red", "orange", "green", "silver", "blue", "purple", "white" }; diff -Npbaur -x '*~' -x '#*#' nethack-base/src/invent.c nethack-alchemy/src/invent.c --- nethack-base/src/invent.c 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/src/invent.c 2006-06-15 14:58:30.000000000 +1000 @@ -2359,6 +2359,11 @@ mergable(otmp, obj) /* returns TRUE if o if (obj->otyp == POT_OIL && obj->lamplit) return FALSE; + /* fermenting potions don't merge */ + if (obj->otyp == POT_FRUIT_JUICE) { + if (obj->corpsenm || otmp->corpsenm) return FALSE; + } + /* don't merge surcharged item with base-cost item */ if (obj->unpaid && !same_price(obj, otmp)) return FALSE; diff -Npbaur -x '*~' -x '#*#' nethack-base/src/objects.c nethack-alchemy/src/objects.c --- nethack-base/src/objects.c 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/src/objects.c 2006-06-15 14:58:30.000000000 +1000 @@ -709,11 +709,11 @@ POTION("confusion", "orange", POTION("blindness", "yellow", 1, BLINDED, 40, 150, CLR_YELLOW), POTION("paralysis", "emerald", 1, 0, 42, 300, CLR_BRIGHT_GREEN), POTION("speed", "dark green", 1, FAST, 42, 200, CLR_GREEN), -POTION("levitation", "cyan", 1, LEVITATION, 42, 200, CLR_CYAN), +POTION("levitation", "viscous", 1, LEVITATION, 42, 200, CLR_GRAY), POTION("hallucination", "sky blue", 1, HALLUC, 40, 100, CLR_CYAN), POTION("invisibility", "brilliant blue",1, INVIS, 40, 150, CLR_BRIGHT_BLUE), POTION("see invisible", "magenta", 1, SEE_INVIS, 42, 50, CLR_MAGENTA), -POTION("healing", "purple-red", 1, 0, 57, 100, CLR_MAGENTA), +POTION("healing", "amber", 1, 0, 57, 100, CLR_ORANGE), POTION("extra healing", "puce", 1, 0, 47, 100, CLR_RED), POTION("gain level", "milky", 1, 0, 20, 300, CLR_WHITE), POTION("enlightenment", "swirly", 1, 0, 20, 200, CLR_BROWN), diff -Npbaur -x '*~' -x '#*#' nethack-base/src/potion.c nethack-alchemy/src/potion.c --- nethack-base/src/potion.c 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/src/potion.c 2006-06-15 14:58:30.000000000 +1000 @@ -13,6 +13,7 @@ static NEARDATA const char beverages[] = STATIC_DCL long FDECL(itimeout, (long)); STATIC_DCL long FDECL(itimeout_incr, (long,int)); STATIC_DCL void NDECL(ghost_from_bottle); +STATIC_OVL void NDECL(alchemy_init); STATIC_DCL short FDECL(mixtype, (struct obj *,struct obj *)); /* force `val' to be within valid range for intrinsic timeout value */ @@ -1311,45 +1312,161 @@ register struct obj *obj; } } +/* new alchemy scheme based on color mixing + * YANI by Graham Cox + * Implemented by Nephi Allred on 15 Apr 2003 + * + * Alchemical tables are based on 4 bits describing dark/light level, yellow, blue and red + * + * DYBR + * 0000 white + * 0001 pink + * 0010 sky-blue + * 0011 puce + * 0100 yellow + * 0101 orange + * 0110 emerald + * 0111 brown + * 1000 black + * 1001 ruby + * 1010 brilliant blue + * 1011 magenta + * 1100 golden + * 1101 amber + * 1110 dark green + * 1111 brown + */ + +/* Assumes gain ability is first potion and water is last */ +char alchemy_table1[POT_WATER - POT_GAIN_ABILITY]; +short alchemy_table2[17]; + +#define ALCHEMY_WHITE 0 +#define ALCHEMY_BLACK 8 +#define ALCHEMY_GRAY (alchemy_table2[16]) +#define IS_PRIMARY_COLOR(x) (((x)&7)==1 || ((x)&7)==2 || ((x)&7)==4) +#define IS_SECONDARY_COLOR(x) (((x)&7)==3 || ((x)&7)==5 || ((x)&7)==6) +#define IS_LIGHT_COLOR(x) (((x)&8)==0) +#define IS_DARK_COLOR(x) ((x)&8) + +/* do a one-time set up of alchemical tables */ +STATIC_OVL void +alchemy_init() +{ + static boolean init = FALSE; + + if(init==FALSE) { + short i; + const char* potion_desc; + + for(i=POT_GAIN_ABILITY;i<=POT_WATER;i++) { + potion_desc = OBJ_DESCR(objects[i]); + if(0==strcmp(potion_desc,"white")) { + alchemy_table1[i-POT_GAIN_ABILITY]=0; + alchemy_table2[0]=i; + } else if (0==strcmp(potion_desc,"pink")) { + alchemy_table1[i-POT_GAIN_ABILITY]=1; + alchemy_table2[1]=i; + } else if (0==strcmp(potion_desc,"sky blue")) { + alchemy_table1[i-POT_GAIN_ABILITY]=2; + alchemy_table2[2]=i; + } else if (0==strcmp(potion_desc,"puce")) { + alchemy_table1[i-POT_GAIN_ABILITY]=3; + alchemy_table2[3]=i; + } else if (0==strcmp(potion_desc,"yellow")) { + alchemy_table1[i-POT_GAIN_ABILITY]=4; + alchemy_table2[4]=i; + } else if (0==strcmp(potion_desc,"orange")) { + alchemy_table1[i-POT_GAIN_ABILITY]=5; + alchemy_table2[5]=i; + } else if (0==strcmp(potion_desc,"emerald")) { + alchemy_table1[i-POT_GAIN_ABILITY]=6; + alchemy_table2[6]=i; + } else if (0==strcmp(potion_desc,"brown")) { + alchemy_table1[i-POT_GAIN_ABILITY]=7; + alchemy_table2[7]=i; + alchemy_table2[15]=i; + } else if (0==strcmp(potion_desc,"black")) { + alchemy_table1[i-POT_GAIN_ABILITY]=8; + alchemy_table2[8]=i; + } else if (0==strcmp(potion_desc,"ruby")) { + alchemy_table1[i-POT_GAIN_ABILITY]=9; + alchemy_table2[9]=i; + } else if (0==strcmp(potion_desc,"brilliant blue")) { + alchemy_table1[i-POT_GAIN_ABILITY]=10; + alchemy_table2[10]=i; + } else if (0==strcmp(potion_desc,"magenta")) { + alchemy_table1[i-POT_GAIN_ABILITY]=11; + alchemy_table2[11]=i; + } else if (0==strcmp(potion_desc,"golden")) { + alchemy_table1[i-POT_GAIN_ABILITY]=12; + alchemy_table2[12]=i; + } else if (0==strcmp(potion_desc,"amber")) { + alchemy_table1[i-POT_GAIN_ABILITY]=13; + alchemy_table2[13]=i; + } else if (0==strcmp(potion_desc,"dark green")) { + alchemy_table1[i-POT_GAIN_ABILITY]=14; + alchemy_table2[14]=i; + } else if (0==strcmp(potion_desc,"cloudy")) { + alchemy_table1[i-POT_GAIN_ABILITY]=-1; + alchemy_table2[16]=i; + } else { + alchemy_table1[i-POT_GAIN_ABILITY]=-1; + } + } + init = TRUE; + } +} + STATIC_OVL short mixtype(o1, o2) register struct obj *o1, *o2; /* returns the potion type when o1 is dipped in o2 */ { - /* cut down on the number of cases below */ - if (o1->oclass == POTION_CLASS && - (o2->otyp == POT_GAIN_LEVEL || - o2->otyp == POT_GAIN_ENERGY || - o2->otyp == POT_HEALING || - o2->otyp == POT_EXTRA_HEALING || - o2->otyp == POT_FULL_HEALING || - o2->otyp == POT_ENLIGHTENMENT || - o2->otyp == POT_FRUIT_JUICE)) { - struct obj *swp; + if(o1->oclass == POTION_CLASS) { + char i1,i2,result; - swp = o1; o1 = o2; o2 = swp; - } + alchemy_init(); + i1 = alchemy_table1[o1->otyp-POT_GAIN_ABILITY]; + i2 = alchemy_table1[o2->otyp-POT_GAIN_ABILITY]; - switch (o1->otyp) { - case POT_HEALING: - switch (o2->otyp) { - case POT_SPEED: - case POT_GAIN_LEVEL: - case POT_GAIN_ENERGY: - return POT_EXTRA_HEALING; - } - case POT_EXTRA_HEALING: - switch (o2->otyp) { - case POT_GAIN_LEVEL: - case POT_GAIN_ENERGY: - return POT_FULL_HEALING; - } - case POT_FULL_HEALING: - switch (o2->otyp) { - case POT_GAIN_LEVEL: - case POT_GAIN_ENERGY: - return POT_GAIN_ABILITY; + /* check that both potions are of mixable types */ + if(i1<0 || i2<0) + return 0; + + /* swap for simplified checks */ + if(i2==ALCHEMY_WHITE || (i2==ALCHEMY_BLACK && i1!=ALCHEMY_WHITE)) { + result = i1; + i1 = i2; + i2 = result; + } + + if(i1==ALCHEMY_WHITE && i2==ALCHEMY_BLACK) { + return ALCHEMY_GRAY; + } else if( (IS_PRIMARY_COLOR(i1) && IS_PRIMARY_COLOR(i2)) + || (IS_SECONDARY_COLOR(i1) && IS_SECONDARY_COLOR(i2)) ) { + /* bitwise OR simulates pigment addition */ + result = i1 | i2; + /* adjust light/dark level if necessary */ + if((i1^i2)&8) { + if(o1->odiluted==o2->odiluted) { + /* same dilution level, randomly toggle */ + result ^= (rn2(2)<<3); + } else { + /* use dark/light level of undiluted potion */ + result ^= (o1->odiluted ? i1:i2)&8; + } + } + } else if ( (i1==ALCHEMY_WHITE && IS_DARK_COLOR(i2)) + || (i1==ALCHEMY_BLACK && IS_LIGHT_COLOR(i2))) { + /* toggle light/dark bit */ + result = i2 ^ 8; + } else { + return 0; } + return alchemy_table2[result]; + } else { + switch (o1->otyp) { case UNICORN_HORN: switch (o2->otyp) { case POT_SICKNESS: @@ -1364,45 +1481,128 @@ register struct obj *o1, *o2; if (o2->otyp == POT_BOOZE) return POT_FRUIT_JUICE; break; - case POT_GAIN_LEVEL: - case POT_GAIN_ENERGY: - switch (o2->otyp) { - case POT_CONFUSION: - return (rn2(3) ? POT_BOOZE : POT_ENLIGHTENMENT); - case POT_HEALING: - return POT_EXTRA_HEALING; - case POT_EXTRA_HEALING: - return POT_FULL_HEALING; - case POT_FULL_HEALING: - return POT_GAIN_ABILITY; - case POT_FRUIT_JUICE: - return POT_SEE_INVISIBLE; - case POT_BOOZE: - return POT_HALLUCINATION; } - break; - case POT_FRUIT_JUICE: - switch (o2->otyp) { - case POT_SICKNESS: - return POT_SICKNESS; - case POT_SPEED: - return POT_BOOZE; - case POT_GAIN_LEVEL: - case POT_GAIN_ENERGY: - return POT_SEE_INVISIBLE; } + + /* MRKR: Extra alchemical effects. */ + + if (o2->otyp == POT_ACID && o1->oclass == GEM_CLASS) { + char *potion_descr = NULL; + + /* Note: you can't create smoky, milky or clear potions */ + + switch (o1->otyp) { + + /* white */ + + case DILITHIUM_CRYSTAL: + /* explodes - special treatment in dodip */ + /* here we just want to return something non-zero */ + return POT_WATER; break; - case POT_ENLIGHTENMENT: - switch (o2->otyp) { - case POT_LEVITATION: - if (rn2(3)) return POT_GAIN_LEVEL; + case DIAMOND: + /* won't dissolve */ break; - case POT_FRUIT_JUICE: - return POT_BOOZE; - case POT_BOOZE: - return POT_CONFUSION; - } + case OPAL: + potion_descr = "cloudy"; break; + + /* red */ + + case RUBY: + potion_descr = "ruby"; + break; + case GARNET: + potion_descr = "pink"; + break; + case JASPER: + potion_descr = "murky"; + break; + + /* orange */ + + case JACINTH: + potion_descr = "orange"; + break; + case AGATE: + potion_descr = "swirly"; + break; + + /* yellow */ + + case CITRINE: + potion_descr = "yellow"; + break; + case CHRYSOBERYL: + potion_descr = "golden"; + break; + + /* yellowish brown */ + + case AMBER: + potion_descr = "amber"; + break; + case TOPAZ: + potion_descr = "brown"; + break; + + /* green */ + + case EMERALD: + potion_descr = "emerald"; + break; + case TURQUOISE: + potion_descr = "sky blue"; + break; + case AQUAMARINE: + potion_descr = "viscous"; + break; + case JADE: + potion_descr = "dark green"; + break; + + /* blue */ + + case SAPPHIRE: + potion_descr = "brilliant blue"; + break; + + /* violet */ + + case AMETHYST: + potion_descr = "magenta"; + break; + case FLUORITE: + potion_descr = "white"; + break; + + /* black */ + + case BLACK_OPAL: + potion_descr = "black"; + break; + case JET: + potion_descr = "dark"; + break; + case OBSIDIAN: + potion_descr = "effervescent"; + break; + } + + if (potion_descr) { + int typ; + + /* find a potion that matches the description */ + + for (typ = bases[POTION_CLASS]; + objects[typ].oc_class == POTION_CLASS; + typ++) { + + if (strcmp(potion_descr, OBJ_DESCR(objects[typ])) == 0) { + return typ; + } + } + } } return 0; @@ -1431,8 +1631,10 @@ register struct obj *obj; if (obj->otyp == POT_ACID) { pline("It boils vigorously!"); You("are caught in the explosion!"); - losehp(rnd(10), "elementary chemistry", KILLED_BY); + losehp(Acid_resistance ? rnd(5) : rnd(10), + "elementary chemistry", KILLED_BY); makeknown(obj->otyp); + useup(obj); update_inventory(); return (TRUE); } @@ -1517,7 +1719,7 @@ register struct obj *obj; int dodip() { - register struct obj *potion, *obj; + struct obj *potion, *obj; struct obj *singlepotion; const char *tmp; uchar here; @@ -1547,7 +1749,6 @@ dodip() rider_cant_reach(); /* not skilled enough to reach */ } else { (void) get_wet(obj); - if (obj->otyp == POT_ACID) useup(obj); } return 1; } @@ -1559,6 +1760,13 @@ dodip() pline("That is a potion bottle, not a Klein bottle!"); return 0; } + + if(potion->otyp != POT_WATER && obj->otyp == POT_WATER) { + /* swap roles, to ensure symmetry */ + struct obj *otmp = potion; + potion = obj; + obj = otmp; + } potion->in_use = TRUE; /* assume it will be used up */ if(potion->otyp == POT_WATER) { boolean useeit = !Blind; @@ -1654,17 +1862,37 @@ dodip() potion->in_use = FALSE; /* didn't go poof */ return(1); } else if(obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) { + int chance; /* Mixing potions is dangerous... */ + /* Give a clue to what's going on ... */ + if(potion->dknown && obj->dknown) { + You("mix the %s potion with the %s one ...", + OBJ_DESCR(objects[potion->otyp]), + OBJ_DESCR(objects[obj->otyp])); + } else pline_The("potions mix..."); + + if (obj->cursed || obj->otyp == POT_ACID || + potion->cursed || potion->otyp == POT_ACID) { /* KMH, balance patch -- acid is particularly unstable */ - if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) { + chance = 0; + } + else { + /* MRKR Success depends on alchemy skill */ + chance = compute_alchemy_prob(obj->quan); + } + + if (rn2(100) >= chance) { pline("BOOM! They explode!"); exercise(A_STR, FALSE); if (!breathless(youmonst.data) || haseyes(youmonst.data)) potionbreathe(obj); useup(obj); useup(potion); - losehp(rnd(10), "alchemic blast", KILLED_BY_AN); + /* MRKR: an alchemy smock ought to be */ + /* some protection against this: */ + losehp(Acid_resistance ? rnd(5) : rnd(10), + "alchemic blast", KILLED_BY_AN); return(1); } @@ -1708,7 +1936,7 @@ dodip() pline_The("mixture looks %s.", hcolor(OBJ_DESCR(objects[obj->otyp]))); } - + use_skill(P_ALCHEMY, 1); useup(potion); return(1); } @@ -1735,6 +1963,15 @@ dodip() } #endif + if(potion->otyp == POT_ACID && obj->otyp == CORPSE && + obj->corpsenm == PM_LICHEN & !Blind) { + pline("%s %s %s around the edges.", The(cxname(obj)), + otense(obj, "turn"), potion->odiluted ? + hcolor(NH_ORANGE) : hcolor(NH_RED)); + potion->in_use = FALSE; /* didn't go poof */ + return(1); + } + if(is_poisonable(obj)) { if(potion->otyp == POT_SICKNESS && !obj->opoisoned) { char buf[BUFSZ]; @@ -1852,7 +2089,7 @@ dodip() } potion->in_use = FALSE; /* didn't go poof */ - if ((obj->otyp == UNICORN_HORN || obj->otyp == AMETHYST) && + if ((obj->otyp == UNICORN_HORN || obj->oclass == GEM_CLASS) && (mixture = mixtype(obj, potion)) != 0) { char oldbuf[BUFSZ], newbuf[BUFSZ]; short old_otyp = potion->otyp; @@ -1871,10 +2108,61 @@ dodip() singlepotion = splitobj(potion, 1L); } else singlepotion = potion; + /* MRKR: Gems dissolve in acid to produce new potions */ + + if (obj->oclass == GEM_CLASS && potion->otyp == POT_ACID) { + + struct obj *singlegem = (obj->quan > 1L ? + splitobj(obj, 1L) : obj); + int chance; + if (obj->otyp == DILITHIUM_CRYSTAL || + potion->cursed) { + chance = 0; + } + else { + chance = compute_alchemy_prob(1); + } + + if (rn2(100) >= chance) { + /* Just to keep them on their toes */ + + if (Hallucination && obj->otyp == DILITHIUM_CRYSTAL) { + /* Thanks to Robin Johnson */ + pline("Warning, Captain! The warp core has been breached!"); + } + pline("BOOM! %s explodes!", The(xname(singlegem))); + exercise(A_STR, FALSE); + if (!breathless(youmonst.data) || haseyes(youmonst.data)) + potionbreathe(singlepotion); + useup(singlegem); + useup(singlepotion); + /* MRKR: an alchemy smock ought to be */ + /* some protection against this: */ + losehp(Acid_resistance ? rnd(5) : rnd(10), + "alchemic blast", KILLED_BY_AN); + return(1); + } + + pline("%s dissolves in %s.", The(xname(singlegem)), + the(xname(singlepotion))); + makeknown(POT_ACID); + use_skill(P_ALCHEMY, 1); + useup(singlegem); + } + if(singlepotion->unpaid && costly_spot(u.ux, u.uy)) { You("use it, you pay for it."); bill_dummy_object(singlepotion); } + + if (singlepotion->otyp == mixture) { + /* no change - merge it back in */ + if (more_than_one && !merged(&potion, &singlepotion)) { + /* should never happen */ + impossible("singlepotion won't merge with parent potion."); + } + } + else { singlepotion->otyp = mixture; singlepotion->blessed = 0; if (mixture == POT_WATER) @@ -1909,13 +2197,111 @@ dodip() "You juggle and drop %s!", doname(singlepotion), (const char *)0); update_inventory(); + } + return(1); } + if (potion->otyp == POT_FRUIT_JUICE && obj->otyp == CORPSE) { + switch (obj->corpsenm) { + case PM_BROWN_MOLD: + case PM_GREEN_MOLD: + case PM_YELLOW_MOLD: + case PM_RED_MOLD: + case PM_VIOLET_FUNGUS: + /* MRKR: Molds and fungi have various medicinal properties */ + + pline("%s %s.", The(cxname(obj)), otense(obj, "dissolve")); + potion->corpsenm = obj->corpsenm; + useup(obj); + /* fermentation takes a while... */ + start_timer(50 + rn2(50), TIMER_OBJECT, + FERMENT, (genericptr_t)potion); + break; + } + } + pline("Interesting..."); return(1); } +int +compute_alchemy_prob(quantity) +int quantity; +{ + int prob; + prob = 40 + P_SKILL(P_ALCHEMY) * 10; + prob += ACURR(A_INT) + ACURR(A_DEX); + prob -= 5 * quantity; + return prob; +} + +void +ferment(arg, timeout) +genericptr_t arg; +long timeout; +{ + struct obj *potion = (struct obj *)arg; + boolean need_newsym; + xchar x, y; + char whose[BUFSZ]; + short new_otyp; + + if (!potion) { +#ifdef DEBUG + pline("null potion in ferment()"); +#endif + return; + } + + /* Make sure it hasn't been transformed in the meantime */ + if (potion->otyp != POT_FRUIT_JUICE) return; + + switch (potion->corpsenm) { + case PM_BROWN_MOLD: + new_otyp = POT_BOOZE; + break; + case PM_GREEN_MOLD: + new_otyp = POT_HEALING; + break; + case PM_YELLOW_MOLD: + new_otyp = POT_CONFUSION; + break; + case PM_RED_MOLD: + new_otyp = POT_SLEEPING; + break; + case PM_VIOLET_FUNGUS: + new_otyp = POT_HALLUCINATION; + break; + default: + impossible("Strange yeast! (%d)", potion->corpsenm); + break; + } + + need_newsym = FALSE; + if (get_obj_location(potion, &x, &y, 0) && !Blind && cansee(x, y)) { + /* set up `whose[]' to be "Your" or "Fred's" or "The goblin's" */ + (void) Shk_Your(whose, potion); + switch (potion->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline("%s %s %s.", + whose, aobjnam(potion, "turn"), + hcolor(OBJ_DESCR(objects[new_otyp]))); + break; + case OBJ_FLOOR: + You("see %s turn %s.", + (potion->quan > 1 ? + an(aobjnam(potion, NULL)) : aobjnam(potion, NULL)), + hcolor(OBJ_DESCR(objects[new_otyp]))); + break; + need_newsym = TRUE; + } + } + potion->otyp = new_otyp; + if (need_newsym) newsym(x, y); + +} void djinni_from_bottle(obj) diff -Npbaur -x '*~' -x '#*#' nethack-base/src/timeout.c nethack-alchemy/src/timeout.c --- nethack-base/src/timeout.c 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/src/timeout.c 2006-06-15 14:58:30.000000000 +1000 @@ -1310,7 +1310,8 @@ static const ttable timeout_funcs[NUM_TI TTAB(revive_mon, (timeout_proc)0, "revive_mon"), TTAB(burn_object, cleanup_burn, "burn_object"), TTAB(hatch_egg, (timeout_proc)0, "hatch_egg"), - TTAB(fig_transform, (timeout_proc)0, "fig_transform") + TTAB(fig_transform, (timeout_proc)0, "fig_transform"), + TTAB(ferment, (timeout_proc)0, "ferment"), }; #undef TTAB diff -Npbaur -x '*~' -x '#*#' nethack-base/src/u_init.c nethack-alchemy/src/u_init.c --- nethack-base/src/u_init.c 2006-06-15 14:45:32.000000000 +1000 +++ nethack-alchemy/src/u_init.c 2006-06-15 14:58:30.000000000 +1000 @@ -260,6 +260,7 @@ static const struct def_skill Skill_A[] { P_RIDING, P_BASIC }, { P_TWO_WEAPON_COMBAT, P_BASIC }, { P_BARE_HANDED_COMBAT, P_EXPERT }, + { P_ALCHEMY, P_BASIC }, { P_NONE, 0 } }; @@ -306,6 +307,7 @@ static const struct def_skill Skill_H[] { P_SHURIKEN, P_SKILLED }, { P_UNICORN_HORN, P_EXPERT }, { P_HEALING_SPELL, P_EXPERT }, { P_BARE_HANDED_COMBAT, P_BASIC }, + { P_ALCHEMY, P_EXPERT }, { P_NONE, 0 } }; @@ -338,6 +340,7 @@ static const struct def_skill Skill_Mon[ { P_CLERIC_SPELL, P_SKILLED }, { P_ESCAPE_SPELL, P_BASIC }, { P_MATTER_SPELL, P_BASIC }, { P_MARTIAL_ARTS, P_GRAND_MASTER }, + { P_ALCHEMY, P_BASIC }, { P_NONE, 0 } }; @@ -372,6 +375,7 @@ static const struct def_skill Skill_R[] { P_RIDING, P_BASIC }, { P_TWO_WEAPON_COMBAT, P_EXPERT }, { P_BARE_HANDED_COMBAT, P_EXPERT }, + { P_ALCHEMY, P_BASIC }, { P_NONE, 0 } }; @@ -430,6 +434,7 @@ static const struct def_skill Skill_T[] { P_RIDING, P_BASIC }, { P_TWO_WEAPON_COMBAT, P_SKILLED }, { P_BARE_HANDED_COMBAT, P_SKILLED }, + { P_ALCHEMY, P_BASIC }, { P_NONE, 0 } }; @@ -464,6 +469,7 @@ static const struct def_skill Skill_W[] { P_MATTER_SPELL, P_EXPERT }, { P_RIDING, P_BASIC }, { P_BARE_HANDED_COMBAT, P_BASIC }, + { P_ALCHEMY, P_SKILLED }, { P_NONE, 0 } }; diff -Npbaur -x '*~' -x '#*#' nethack-base/src/weapon.c nethack-alchemy/src/weapon.c --- nethack-base/src/weapon.c 2006-06-15 14:45:38.000000000 +1000 +++ nethack-alchemy/src/weapon.c 2006-06-15 15:00:57.000000000 +1000 @@ -25,6 +25,7 @@ #define PN_CLERIC_SPELL (-12) #define PN_ESCAPE_SPELL (-13) #define PN_MATTER_SPELL (-14) +#define PN_ALCHEMY (-15) STATIC_DCL void FDECL(give_may_advance_msg, (int)); @@ -50,7 +51,8 @@ STATIC_VAR NEARDATA const short skill_na PN_CLERIC_SPELL, PN_ESCAPE_SPELL, PN_MATTER_SPELL, PN_BARE_HANDED, PN_TWO_WEAPONS, - PN_RIDING + PN_RIDING, + PN_ALCHEMY }; /* note: entry [0] isn't used */ @@ -70,6 +72,7 @@ STATIC_VAR NEARDATA const char * const o "clerical spells", "escape spells", "matter spells", + "alchemy", }; /* indexed vis `is_martial() */ STATIC_VAR NEARDATA const char * const barehands_or_martial[] = {