Welcome to the Builder Academy

Question Healer Spec Proc. Modified from a Trigger I built originally.

More
05 Nov 2026 14:45 - 05 Nov 2026 14:46 #11067 by wlessard1
This is about the same as the trigger I built a couple years ago which was inspired by an Old Diku MUD originally. 

Pretty much, just put this in the spec_procs.c file at the end. Go through the MENU part to make sure of what you want for prices and such. Also there is one spell that you probably don't have and that is Demon_Form. Since I run my mud locally only for myself, I kind of make it a little bit of a power mud. Also make a MOB to be the healer and assign it in spec_assign.c as healer

I also had Grok Bot help me refine it and clear up bugs and memory leaks I had originally when I did this.

Hope you find it useful.
Code:
/* ====================================================================== * Bramage: healer -- a paid priest/healer, the C version of DG trigger * #1006 (Rahnkara the healer, Temple of T'Salchoic, mob 1000). * * To use it, put this in spec_assign.c, inside assign_mobiles(): *     ASSIGNMOB(1000, healer); * If you switch Rahnkara over, remove trigger 1006 from the mob (the * "T 1006" line in 10.mob, or via medit) so you don't get both menus. * * Commands while the healer is in the room: *   list  or  buy        -> shows the menu *   buy <keyword>        -> buys it (same keywords as #1006: heal, sanc...) *   <keyword>            -> Bramage: just typing 'sanc' works too, like a *                           shop. Those words live in interpreter.c's *                           cmd_info as do_healer_word, full-word only. * * The order inside every purchase is always the same, and it matters: *   1. Is it allowed?  (stat already 18, etc.) -- check BEFORE taking money. *   2. Can they pay?   GET_GOLD() >= price. *   3. Do the work.    If the work fails (no-magic room), refund the gold. *   4. Take the gold and tell the buyer AND the room what happened. * ====================================================================== */ /* Bramage: what kind of thing a menu line sells. A small enum keeps the * big switch() further down easy to read. */ enum healer_kind { HK_SPELL, HK_SANC, HK_RHIT, HK_RMAN, HK_RMOV, HK_STAT, HK_STRADD }; /* Bramage: one row per thing for sale. "which" is the spell number for * HK_SPELL, or which stat (0=str 1=int 2=wis 3=dex 4=con 5=cha) for HK_STAT. * The menu, the prices and the purchase code all read from this table, * so to change a price you only edit it here. */ struct healer_item {   const char *keyword;   int price;   enum healer_kind kind;   int which;   const char *desc;      /* menu text */   const char *to_buyer;  /* sent to the buyer (with color) */   const char *to_room;   /* act() to everyone else, $N = buyer */ }; static const struct healer_item healer_menu[] = {   { "heal",  500, HK_SPELL, SPELL_HEAL, "and you will be healed",     "The healer takes 500 gold and warmth floods your wounds.",     "$n lays hands on $N, whose wounds close." },   { "sanc", 1000, HK_SANC, SPELL_SANCTUARY, "and you will have Sanctuary for 20 hours",     "The healer takes 1000 gold and a white aura surrounds you.",     "A white aura surrounds $N." },   { "armor", 500, HK_SPELL, SPELL_ARMOR, "and you will be granted armor",     "The healer takes 500 gold and an unseen shield settles over you.",     "$n blesses $N with a shimmering ward." },   { "regen", 500, HK_SPELL, SPELL_REGENERATION, "and you will regenerate faster",     "The healer takes 500 gold and your body begins to mend itself.",     "$n touches $N, whose skin glows with renewed life." },   { "demon", 500, HK_SPELL, SPELL_DEMON_FORM, "to give you demon form",     "The healer takes 500 gold and your skin hardens like demon hide.",     "$N's skin darkens and hardens into demon hide." },   { "rhit", 1000, HK_RHIT, 0, "will restore your hitpoints completely",     "The healer takes 1000 gold and your body is made whole.",     "$n restores $N to full health." },   { "rman", 1000, HK_RMAN, 0, "to restore your mana completely",     "The healer takes 1000 gold and arcane power rushes back into you.",     "$N's eyes flare as $S mana is restored." },   { "rmov", 1000, HK_RMOV, 0, "to restore your moves completely",     "The healer takes 1000 gold and the weariness leaves your legs.",     "$N stands taller, refreshed and ready to travel." },   { "astr", 10000, HK_STAT, 0, "will add 1 strength point",     "The healer takes 10,000 gold and your strength grows.",     "Divine light pours into $N for a moment." },   { "aadd", 10000, HK_STRADD, 0, "will add 10 points of 18/XX",     "The healer takes 10,000 gold and divine strength swells your muscles.",     "$N's muscles swell with divine strength." },   { "aint", 10000, HK_STAT, 1, "will add 1 Intelligence point",     "The healer takes 10,000 gold and your intelligence grows.",     "Divine light pours into $N for a moment." },   { "awis", 10000, HK_STAT, 2, "will add 1 Wisdom point",     "The healer takes 10,000 gold and your wisdom grows.",     "Divine light pours into $N for a moment." },   { "adex", 10000, HK_STAT, 3, "will add 1 Dexterity point",     "The healer takes 10,000 gold and your dexterity grows.",     "Divine light pours into $N for a moment." },   { "acon", 10000, HK_STAT, 4, "will add 1 Constitution point",     "The healer takes 10,000 gold and your constitution grows.",     "Divine light pours into $N for a moment." },   { "acha", 10000, HK_STAT, 5, "will add 1 Charisma point",     "The healer takes 10,000 gold and your charisma grows.",     "Divine light pours into $N for a moment." },   { NULL, 0, 0, 0, NULL, NULL, NULL } }; /* Bramage: a pointer to the REAL (unmodified) stat. We change real_abils, * never aff_abils, because aff_abils gets rebuilt from real_abils + gear + * spells every time affect_total() runs -- a change there would vanish. */ static sbyte *healer_stat_ptr(struct char_data *ch, int which) {   switch (which) {     case 0: return &ch->real_abils.str;     case 1: return &ch->real_abils.intel;     case 2: return &ch->real_abils.wis;     case 3: return &ch->real_abils.dex;     case 4: return &ch->real_abils.con;     default: return &ch->real_abils.cha;   } } static void healer_show_menu(struct char_data *ch, struct char_data *healer) {   int i;   send_to_char(ch, "\tC%s tells you, 'Just type the word to buy (or buy <word>):'\tn\r\n", GET_NAME(healer));   for (i = 0; healer_menu[i].keyword; i++)     send_to_char(ch, "  \tW%-5s\tn for \tY%6d\tn gold %s\r\n",                  healer_menu[i].keyword, healer_menu[i].price, healer_menu[i].desc);   send_to_char(ch, "Stats can be raised to a maximum of 18, and 18/xx to 18/100.\r\n"); } SPECIAL(healer) {   struct char_data *healer = (struct char_data *) me;   const struct healer_item *item = NULL;   char arg[MAX_INPUT_LENGTH];   int i, level, bare;   /* Bramage: cmd == 0 is the heartbeat tick; NPCs (charmies, pets) can't shop. */   if (!cmd || IS_NPC(ch))     return (FALSE);   /* Bramage: besides list/buy, a bare menu word is a purchase. Those words    * are do_healer_word commands in interpreter.c, so they reach us here. */   bare = (complete_cmd_info[cmd].command_pointer == do_healer_word);   if (!CMD_IS("buy") && !CMD_IS("list") && !bare)     return (FALSE);   /* Bramage: an asleep or fighting healer can't do business.    * Returning FALSE lets the normal list/buy run, which just says there's    * no shopkeeper here -- harmless. */   if (!AWAKE(healer) || FIGHTING(healer))     return (FALSE);   if (!CAN_SEE(healer, ch)) {     act("$n looks around, puzzled. 'Who said that?'", FALSE, healer, 0, 0, TO_ROOM);     return (TRUE);   }   /* Bramage: for a bare word, the command name itself is the item, so we    * copy it into arg and fall into the same buy code below. */   if (bare)     strlcpy(arg, complete_cmd_info[cmd].command, sizeof(arg));   else     one_argument(argument, arg);   /* Bramage: 'list' or a bare 'buy' shows the menu, no charge. */   if (CMD_IS("list") || !*arg) {     healer_show_menu(ch, healer);     return (TRUE);   }   for (i = 0; healer_menu[i].keyword; i++)     if (!str_cmp(arg, healer_menu[i].keyword)) {       item = &healer_menu[i];       break;     }   if (!item) {     send_to_char(ch, "\tY%s tells you, 'I don't offer that. Type list to see what I do.'\tn\r\n",                  GET_NAME(healer));     return (TRUE);   }   /* Step 1: is it allowed? (no gold taken yet) */   if (item->kind == HK_STAT && *healer_stat_ptr(ch, item->which) >= 18) {     send_to_char(ch, "\tYYou are already at the peak of mortal ability there.\tn\r\n");     return (TRUE);   }   if (item->kind == HK_STRADD) {     /* Bramage: 18/xx is only for classes with "ExceptionalStr yes" in their      * .cls file. Before this, anyone with 18 strength could buy it. */     if (!CLASS_EXCEPTIONAL_STR(GET_CLASS(ch))) {       send_to_char(ch, "\tYOnly those trained in exceptional strength can be blessed with 18/xx.\tn\r\n");       return (TRUE);     }     if (ch->real_abils.str != 18) {       send_to_char(ch, "\tYOnly those with 18 strength can be blessed with 18/xx.\tn\r\n");       return (TRUE);     }     if (ch->real_abils.str_add >= 100) {       send_to_char(ch, "\tYYour strength is already 18/100.\tn\r\n");       return (TRUE);     }   }   /* Step 2: can they pay? */   if (GET_GOLD(ch) < item->price) {     send_to_char(ch, "\tY%s tells you, 'I'm sorry, that costs %d gold, which you don't have.'\tn\r\n",                  GET_NAME(healer), item->price);     return (TRUE);   }   /* Step 3: do the work. The healer casts at its own level, clamped so a    * low-level mob still gives a useful spell and a huge one can't exceed    * mortal limits. */   level = MAX(20, MIN(GET_LEVEL(healer), LVL_IMMORT - 1));   switch (item->kind) {     case HK_SPELL:       /* Bramage: call_magic() does all the normal spell work (healing,        * affects, messages). It returns 0 if magic can't happen here, e.g.        * a NOMAGIC room -- in that case we haven't taken the gold yet. */       if (call_magic(healer, ch, NULL, item->which, level, CAST_SPELL) <= 0) {         send_to_char(ch, "\tY%s's prayer fizzles. 'The gods are not listening here.'\tn\r\n",                      GET_NAME(healer));         return (TRUE);       }       break;     case HK_SANC: {       /* Bramage: #1006 gives sanctuary for a flat 20 hours, so we build the        * affect by hand instead of using the spell's level-based duration.        * affect_join() with avg_dur FALSE / add_dur FALSE just refreshes it. */       struct affected_type af;       new_affect(&af);       af.spell = SPELL_SANCTUARY;       af.duration = 20;       SET_BIT_AR(af.bitvector, AFF_SANCTUARY);       affect_join(ch, &af, FALSE, FALSE, FALSE, FALSE);       break;     }     case HK_RHIT: GET_HIT(ch)  = GET_MAX_HIT(ch);  break;     case HK_RMAN: GET_MANA(ch) = GET_MAX_MANA(ch); break;     case HK_RMOV: GET_MOVE(ch) = GET_MAX_MOVE(ch); break;     case HK_STAT:       (*healer_stat_ptr(ch, item->which))++;       affect_total(ch);   /* Bramage: rebuild the stats you play with */       break;     case HK_STRADD:       ch->real_abils.str_add = MIN(100, ch->real_abils.str_add + 10);       affect_total(ch);       break;   }   /* Step 4: take the gold and tell everyone. Green for success to the    * buyer; the room gets a plain act() line. */   decrease_gold(ch, item->price);   send_to_char(ch, "\tG%s\tn\r\n", item->to_buyer);   act(item->to_room, FALSE, healer, 0, ch, TO_NOTVICT);   /* Bramage: permanent stat buys cost 10,000 -- save right away so a    * crash can't lose them. */   if (item->kind == HK_STAT || item->kind == HK_STRADD)     save_char(ch);   return (TRUE); }

Enjoy

Just a guy coding a mud at home for no reason but the challenge.
Last edit: 05 Nov 2026 14:46 by wlessard1.

Please Log in or Create an account to join the conversation.

Time to create page: 0.270 seconds