/* ************************************************************************ * File: act.other.c EmpireMUD AD 1.0 * * Usage: Miscellaneous player-level commands * * * * All rights reserved. See license.doc for complete information. * * * * Code base by Paul Clarke. EmpireMUD Project, a tbgMUD Production. * * Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ #define __ACT_OTHER_C__ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "interpreter.h" #include "handler.h" #include "db.h" #include "empire.h" #include "skills.h" #include "vnums.h" ACMD(do_afk) { char **msg; bool change = FALSE; if (IS_NPC(ch)) return; skip_spaces(&argument); if (IS_AFK(ch) && !*argument) { (ch)->player_specials->isafk = FALSE; send_to_char("You announce that you are no longer AFK.\r\n", ch); act("$n announces that $e is no longer away from $s keyboard.", TRUE, ch, 0, 0, TO_ROOM); if (AFK_MSGS(ch)) send_to_char("Type MESSAGES to receive missed tells.\r\n", ch); return; } if (IS_NPC(ch)) return; if (IS_AFK(ch)) change = TRUE; msg = &(AFK_MSG(ch)); if (*msg) free(*msg); if (!*argument) *msg = NULL; else *msg = str_dup(argument); (ch)->player_specials->isafk = TRUE; if (!change) { send_to_char("You announce that you are going AFK.\r\n", ch); act("$n announces that $e is going to be away from $s keyboard.", FALSE, ch, 0, 0, TO_ROOM); if (*argument) { sprintf(buf, "$e says, '%s'", argument); act(buf, FALSE, ch, 0, 0, TO_ROOM); } } else send_to_char("Your AFK message has been changed.\r\n", ch); } ACMD(do_messages) { char **msg; if (!AFK_MSGS(ch)) { send_to_char("You have no new messages.\r\n", ch); return; } page_string(ch->desc, AFK_MSGS(ch), 1); msg = &(AFK_MSGS(ch)); if (*msg) free(*msg); if (!*argument) *msg = NULL; } /* Either displays current prompt, or sets one */ ACMD(do_prompt) { char temp[MAX_INPUT_LENGTH]; int i, j; skip_spaces(&argument); if (!*argument) { if (GET_PROMPT(ch)) { strcpy(buf, GET_PROMPT(ch)); for (i = 0, j = 0; i <= strlen(buf); i++) if (buf[i] != '&') temp[i+j] = buf[i]; else { temp[i+j] = temp[i+j+1] = buf[i]; j++; } } sprintf(buf, "Your prompt is currently: %s\r\n", (GET_PROMPT(ch) ? temp : "n/a")); send_to_char(buf, ch); return; } delete_doubledollar(argument); if (strlen(argument) > 120) argument[120 - 1] = '\0'; if (!str_cmp(argument, "off")) { if (GET_PROMPT(ch)) free(GET_PROMPT(ch)); GET_PROMPT(ch) = NULL; send_to_char(OK, ch); } else { if (GET_PROMPT(ch)) free(GET_PROMPT(ch)); GET_PROMPT(ch) = str_dup(argument); for (i = 0, j = 0; i <= strlen(argument); i++) if (argument[i] != '&') temp[i+j] = argument[i]; else { temp[i+j] = temp[i+j+1] = argument[i]; j++; } sprintf(buf, "Okay, set your prompt to: %s\r\n", temp); send_to_char(buf, ch); } } ACMD(do_quit) { ACMD(do_vc); void Crash_rentsave(Creature ch); void die(Creature ch); Descr d, next_d; if (IS_NPC(ch) || !ch->desc) return; if (subcmd != SCMD_QUIT && GET_LEVEL(ch) < LVL_GOD) msg_to_char(ch, "You have to type quit--no less, to quit!\r\n"); else if (GET_POS(ch) == POS_FIGHTING || FIGHTING(ch)) msg_to_char(ch, "No way! You're fighting for your life!\r\n"); else if (GET_QUIT_TIMER(ch) > 0 && !IS_IMMORTAL(ch)) msg_to_char(ch, "You can't quit so soon after fighting!\r\n"); else if (GET_FED_ON_BY(ch)) msg_to_char(ch, "You can't quit with fangs in your neck!\r\n"); else if (GET_FEEDING_FROM(ch)) msg_to_char(ch, "You can't quit while drinking blood!\r\n"); else if (GET_POS(ch) < POS_STUNNED) { send_to_char("You die before your time...\r\n", ch); die(ch); } else { if (!GET_INVIS_LEV(ch)) act("$n has left the game.", TRUE, ch, 0, 0, TO_ROOM); syslog(GET_INVIS_LEV(ch), TRUE, "%s has quit the game.", GET_NAME(ch)); if (GET_INVIS_LEV(ch) == 0) mortlog(ch, "%s has left the game", PERS(ch, ch, 1)); send_to_char("Goodbye, friend.. Come back soon!\r\n", ch); /* Close out of the virtual channel, if a member of one */ if (GET_VC(ch) > 0) do_vc(ch, "close", 0, 0); /* * kill off all sockets connected to the same player as the one who is * trying to quit. Helps to maintain sanity as well as prevent duping. */ for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (d == ch->desc) continue; if (d->character && (GET_IDNUM(d->character) == GET_IDNUM(ch))) STATE(d) = CON_DISCONNECT; } Crash_rentsave(ch); SAVE_CHAR(ch); extract_char(ch); } } ACMD(do_save) { void write_aliases(Creature ch); if (IS_NPC(ch) || !ch->desc) return; if (cmd) msg_to_char(ch, "Saving %s.\r\n", GET_NAME(ch)); write_aliases(ch); SAVE_CHAR(ch); Crash_crashsave(ch); } ACMD(do_visible) { void perform_immort_vis(Creature ch); if (GET_LEVEL(ch) >= LVL_GOD) { perform_immort_vis(ch); return; } if (SHOULD_APPEAR(ch)) appear(ch); else send_to_char("You are already visible.\r\n", ch); } ACMD(do_title) { void set_title(Creature ch, char *title); skip_spaces(&argument); delete_doubledollar(argument); if (IS_NPC(ch)) send_to_char("Your title is fine... go away.\r\n", ch); else if (PLR_FLAGGED(ch, PLR_NOTITLE)) send_to_char("You can't title yourself -- you shouldn't have abused it!\r\n", ch); else if (strstr(argument, "(") || strstr(argument, ")") || strstr(argument, "%")) send_to_char("Titles can't contain the (, ), or % characters.\r\n", ch); else if (strlen(argument) > MAX_TITLE_LENGTH) { sprintf(buf, "Sorry, titles can't be longer than %d characters.\r\n", MAX_TITLE_LENGTH); send_to_char(buf, ch); } else { set_title(ch, argument); msg_to_char(ch, "Okay, you're now %s%s.\r\n", PERS(ch, ch, 1), GET_TITLE(ch)); } } int perform_party(Creature ch, Creature vict) { if (AFF_FLAGGED(vict, AFF_PARTY) || !CAN_SEE(ch, vict)) return (0); SET_BIT(AFF_FLAGS(vict), AFF_PARTY); if (ch != vict) act("$N is now a member of your party.", FALSE, ch, 0, vict, TO_CHAR); act("You are now a member of $n's party.", FALSE, ch, 0, vict, TO_VICT); act("$N is now a member of $n's party.", FALSE, ch, 0, vict, TO_NOTVICT); return (1); } void print_party(Creature ch) { Creature k; struct follow_type *f; if (!AFF_FLAGGED(ch, AFF_PARTY)) send_to_char("But you are not the member of a party!\r\n", ch); else { send_to_char("Your party consists of:\r\n", ch); k = (ch->master ? ch->master : ch); if (AFF_FLAGGED(k, AFF_PARTY)) act(" $N (Leader of party)", FALSE, ch, 0, k, TO_CHAR); for (f = k->followers; f; f = f->next) { if (!AFF_FLAGGED(f->follower, AFF_PARTY)) continue; act(" $N", FALSE, ch, 0, f->follower, TO_CHAR); } } } ACMD(do_party) { Creature vict; struct follow_type *f; int found; one_argument(argument, buf); if (!*buf) { print_party(ch); return; } if (ch->master) { act("You can't allow others to join a party you don't even lead!", FALSE, ch, 0, 0, TO_CHAR); return; } if (!str_cmp(buf, "all")) { perform_party(ch, ch); for (found = 0, f = ch->followers; f; f = f->next) found += perform_party(ch, f->follower); if (!found) send_to_char("Everyone following you is already in your party.\r\n", ch); return; } if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM))) send_to_char(NOPERSON, ch); else if ((vict->master != ch) && (vict != ch)) act("$N must follow you to enter your party.", FALSE, ch, 0, vict, TO_CHAR); else { if (!AFF_FLAGGED(vict, AFF_PARTY)) perform_party(ch, vict); else { if (ch != vict) act("$N is no longer a member of your party.", FALSE, ch, 0, vict, TO_CHAR); act("You have been kicked out of $n's party!", FALSE, ch, 0, vict, TO_VICT); act("$N has been kicked out of $n's party!", FALSE, ch, 0, vict, TO_NOTVICT); REMOVE_BIT(AFF_FLAGS(vict), AFF_PARTY); } } } ACMD(do_unparty) { struct follow_type *f, *next_fol; Creature tch; one_argument(argument, buf); if (!*buf) { if (ch->master || !(AFF_FLAGGED(ch, AFF_PARTY))) { send_to_char("But you lead no party!\r\n", ch); return; } sprintf(buf2, "%s has disbanded the party.\r\n", PERS(ch, ch, 0)); for (f = ch->followers; f; f = next_fol) { next_fol = f->next; if (AFF_FLAGGED(f->follower, AFF_PARTY)) { REMOVE_BIT(AFF_FLAGS(f->follower), AFF_PARTY); send_to_char(buf2, f->follower); if (!AFF_FLAGGED(f->follower, AFF_CHARM)) stop_follower(f->follower); } } REMOVE_BIT(AFF_FLAGS(ch), AFF_PARTY); send_to_char("You disband the party.\r\n", ch); return; } if (!(tch = get_char_vis(ch, buf, FIND_CHAR_ROOM))) { send_to_char("There is no such person!\r\n", ch); return; } if (tch->master != ch) { send_to_char("That person is not following you!\r\n", ch); return; } if (!AFF_FLAGGED(tch, AFF_PARTY)) { send_to_char("That person isn't in your party.\r\n", ch); return; } REMOVE_BIT(AFF_FLAGS(tch), AFF_PARTY); act("$N is no longer a member of your party.", FALSE, ch, 0, tch, TO_CHAR); act("You have been kicked out of $n's party!", FALSE, ch, 0, tch, TO_VICT); act("$N has been kicked out of $n's party!", FALSE, ch, 0, tch, TO_NOTVICT); if (!AFF_FLAGGED(tch, AFF_CHARM)) stop_follower(tch); } ACMD(do_report) { extern const char *health_levels[]; extern const char *move_levels[]; Creature k; struct follow_type *f; if (!AFF_FLAGGED(ch, AFF_PARTY)) { send_to_char("But you are not a member of any party!\r\n", ch); return; } sprintf(buf, "$n reports that $e is %s and feels %s.", health_levels[GET_DAMAGE(ch)], move_levels[(GET_MOVE(ch) * 100 / GET_MAX_MOVE(ch)) / 10]); k = (ch->master ? ch->master : ch); for (f = k->followers; f; f = f->next) if (AFF_FLAGGED(f->follower, AFF_PARTY) && f->follower != ch) act(buf, FALSE, ch, 0, f->follower, TO_VICT); if (k != ch) act(buf, FALSE, ch, 0, k, TO_VICT); send_to_char("You report to the party.\r\n", ch); } ACMD(do_gen_write) { extern int max_filesize; FILE *fl; char *tmp; const char *filename; char *name; struct stat fbuf; time_t ct; switch (subcmd) { case SCMD_BUG: filename = BUG_FILE; name = "bug"; break; case SCMD_TYPO: filename = TYPO_FILE; name = "typo"; break; case SCMD_IDEA: filename = IDEA_FILE; name = "idea"; break; default: return; } ct = time(0); tmp = asctime(localtime(&ct)); if (IS_NPC(ch)) { send_to_char("Monsters can't have ideas - Go away.\r\n", ch); return; } skip_spaces(&argument); delete_doubledollar(argument); if (!*argument) { send_to_char("That must be a mistake...\r\n", ch); return; } syslog(GET_INVIS_LEV(ch), FALSE, "%s %s: %s", GET_NAME(ch), name, argument); if (stat(filename, &fbuf) < 0) { perror("SYSERR: Can't stat() file"); return; } if (fbuf.st_size >= max_filesize) { send_to_char("Sorry, the file is full right now.. try again later.\r\n", ch); return; } if (!(fl = fopen(filename, "a"))) { perror("SYSERR: do_gen_write"); send_to_char("Could not open the file. Sorry.\r\n", ch); return; } fprintf(fl, "%-8s (%6.6s) %s\n", GET_NAME(ch), (tmp + 4), argument); fclose(fl); send_to_char("Okay. Thanks!\r\n", ch); } #define TOG_OFF 0 #define TOG_ON 1 #define PRF_TOG_CHK(ch,flag) ((TOGGLE_BIT(PRF_FLAGS(ch), (flag))) & (flag)) ACMD(do_gen_tog) { long result; const char *tog_messages[][2] = { { "You will no longer see mortal logs.\r\n", "You will now see mortal logs.\r\n" }, { "Compact mode off.\r\n", "Compact mode on.\r\n" }, { "You can now hear tells.\r\n", "You are now deaf to tells.\r\n" }, { "You can now hear shouts.\r\n", "You are now deaf to shouts.\r\n" }, { "You can now hear gossip.\r\n", "You are now deaf to gossip.\r\n" }, { "You can now hear the out-of-character channel.\r\n", "You are now deaf to the out-of-character channel.\r\n" }, { "You can now hear the Wiz-channel.\r\n", "You are now deaf to the Wiz-channel.\r\n" }, { "You will now have your communication repeated.\r\n", "You will no longer have your communication repeated.\r\n" }, { "HolyLight mode off.\r\n", "HolyLight mode on.\r\n" }, { "You will now see normal map colors.\r\n", "You will now see political colors on the map.\r\n" }, { "You will no longer see players on the map.\r\n", "You will now see other players on the map.\r\n" }, { "Your color is now off.\r\n", "Your &1color&0 is now on.\r\n" }, { "The screen will now clear when you look.\r\n", "The screen will no longer clear when you look.\r\n" }, { "Your e-mail address will now be shown.\r\n", "Your e-mail address is now hidden.\r\n" }, { "You will now see color on the world map.\r\n", "You will no longer see colors on the world map.\r\n" }, { "You will no longer automatically kill other people in combat.\r\n", "You will now automatically kill other people in combat.\r\n" }, { "Long messages will now be paged instead of scrolled.\r\n", "Long messages will now be scrolled instead of paged.\r\n" }, { "You will now see the full map and room descriptions.\r\n", "You will now see the short map and no room descriptions.\r\n" }, { "You will no longer allow yourself to be bit by vampires.\r\n", "You now allow vampires to bite you freely.\r\n" }, { "You will no longer accept blood freely.\r\n", "You now allow yourself to be fed blood from vampires.\r\n" }, { "You will no longer allow yourself to be taught disciplines.\r\n", "You will now allow yourself to be taught disciplines.\r\n" }, { "You are no longer role-playing.\r\n", "You are now role-playing.\r\n" }, }; if (IS_NPC(ch)) return; switch (subcmd) { case SCMD_RP: result = PRF_TOG_CHK(ch, PRF_RP); break; case SCMD_NOBITE: result = PRF_TOG_CHK(ch, PRF_BITEABLE); break; case SCMD_NOFEED: result = PRF_TOG_CHK(ch, PRF_FEEDABLE); break; case SCMD_NOTEACH: result = PRF_TOG_CHK(ch, PRF_TEACHABLE); break; case SCMD_BRIEF: result = PRF_TOG_CHK(ch, PRF_BRIEF); break; case SCMD_SCROLLING: result = PRF_TOG_CHK(ch, PRF_SCROLLING); break; case SCMD_AUTOKILL: result = PRF_TOG_CHK(ch, PRF_AUTOKILL); break; case SCMD_NOCLEAR: result = PRF_TOG_CHK(ch, PRF_NOCLEAR); break; case SCMD_COLOR: result = PRF_TOG_CHK(ch, PRF_COLOR); break; case SCMD_NOMAPCOL: result = PRF_TOG_CHK(ch, PRF_NOMAPCOL); break; case SCMD_MAPPC: result = PRF_TOG_CHK(ch, PRF_MAPPC); break; case SCMD_MORTLOG: result = PRF_TOG_CHK(ch, PRF_MORTLOG); break; case SCMD_POLITICAL: result = PRF_TOG_CHK(ch, PRF_POLITICAL); break; case SCMD_COMPACT: result = PRF_TOG_CHK(ch, PRF_COMPACT); break; case SCMD_NOTELL: result = PRF_TOG_CHK(ch, PRF_NOTELL); break; case SCMD_DEAF: result = PRF_TOG_CHK(ch, PRF_DEAF); break; case SCMD_NOGOSSIP: result = PRF_TOG_CHK(ch, PRF_NOGOSS); break; case SCMD_NOOOC: result = PRF_TOG_CHK(ch, PRF_NOOOC); break; case SCMD_NOWIZ: result = PRF_TOG_CHK(ch, PRF_NOWIZ); break; case SCMD_NOREPEAT: result = PRF_TOG_CHK(ch, PRF_NOREPEAT); break; case SCMD_HOLYLIGHT: result = PRF_TOG_CHK(ch, PRF_HOLYLIGHT); break; case SCMD_HIDEEMAIL: result = PRF_TOG_CHK(ch, PRF_HIDEEMAIL); break; default: log("SYSERR: Unknown subcmd %d in do_gen_toggle.", subcmd); return; } if (result) send_to_char(tog_messages[subcmd][TOG_ON], ch); else send_to_char(tog_messages[subcmd][TOG_OFF], ch); SAVE_CHAR(ch); } ACMD(do_attach) { char arg1[MAX_INPUT_LENGTH]; Object obj1, obj2, new; two_arguments(argument, arg, arg1); if (!*arg || !*arg1) { send_to_char("Attach what to what?\r\n", ch); return; } if (!(obj1 = get_obj_in_list_vis(ch, arg, ch->carrying))) { msg_to_char(ch, "You don't seem to have a '%s'.\r\n", arg); return; } if (!(obj2 = get_obj_in_list_vis(ch, arg1, ch->carrying))) { msg_to_char(ch, "You don't seem to have a '%s'.\r\n", arg1); return; } if (obj1 == obj2) { msg_to_char(ch, "You can't attach that to itself!\r\n"); return; } switch (GET_OBJ_VNUM(obj1)) { case o_HANDLE: if (GET_OBJ_VNUM(obj2) == o_HANDAXE) { act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR); act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM); obj_to_char(read_object(o_AXE, VIRTUAL), ch); extract_obj(obj1); extract_obj(obj2); return; } if (GET_OBJ_VNUM(obj2) == o_SPEARHEAD) { act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR); act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM); obj_to_char(read_object(o_SPEAR, VIRTUAL), ch); extract_obj(obj1); extract_obj(obj2); return; } break; case o_HANDAXE: if (GET_OBJ_VNUM(obj2) == o_HANDLE) { act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR); act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM); obj_to_char(read_object(o_AXE, VIRTUAL), ch); extract_obj(obj1); extract_obj(obj2); return; } break; case o_ROCK: if (GET_OBJ_VNUM(obj2) == o_FLINT) { obj_to_char((new = read_object(o_FLINT_SET, VIRTUAL)), ch); act("You now have $p.", FALSE, ch, new, 0, TO_CHAR); extract_obj(obj1); extract_obj(obj2); return; } break; case o_FLINT: if (GET_OBJ_VNUM(obj2) == o_ROCK) { obj_to_char((new = read_object(o_FLINT_SET, VIRTUAL)), ch); act("You now have $p.", FALSE, ch, new, 0, TO_CHAR); extract_obj(obj1); extract_obj(obj2); return; } break; case o_SPEARHEAD: if (GET_OBJ_VNUM(obj2) == o_HANDLE) { act("You attach $p to $P!", FALSE, ch, obj1, obj2, TO_CHAR); act("$n attaches $p to $P!", TRUE, ch, obj1, obj2, TO_ROOM); obj_to_char(read_object(o_SPEAR, VIRTUAL), ch); extract_obj(obj1); extract_obj(obj2); return; } break; } strcpy(buf1, GET_OBJ_DESC(obj2, ch, 1)); sprintf(buf, "You can't attach %s to %s.\r\n", GET_OBJ_DESC(obj1, ch, 1), buf1); send_to_char(buf, ch); } /* * do_confirm: * This function both allows players to speed up a reboot, and to cause the reboot * if all players have confirmed. Because of this second function, it is called * by update_reboot() every tick to make sure that everything is okay. Because it * is called by that function, it must be made to run WITHOUT (ch). -pc 10/21/2k */ ACMD(do_confirm) { ACMD(do_reboot); Descr d; bool found = FALSE; if (ch) { if (IS_NPC(ch)) return; if (GET_POS(ch) == POS_FIGHTING) { msg_to_char(ch, "How can you be ready? You're fighting for your life!\r\n"); return; } if (reboot.time == -1) { msg_to_char(ch, "No reboot has been set!\r\n"); return; } if (REBOOT_CONF(ch)) msg_to_char(ch, "You've already confirmed that you're ready for the reboot.\r\n"); else { REBOOT_CONF(ch) = TRUE; msg_to_char(ch, "You have confirmed that you're ready for the reboot.\r\n"); } } for (d = descriptor_list; d; d = d->next) if (STATE(d) != CON_PLAYING) found = TRUE; else if (IS_NPC(d->character)) continue; else if (!REBOOT_CONF(d->character)) found = TRUE; if (found) return; do_reboot(NULL, "", 0, reboot.type); } ACMD(do_douse) { Object obj; byte amount; room_rnum room = HOME_ROOM(ch->in_room); for (obj = ch->carrying; obj && !(GET_OBJ_TYPE(obj) == ITEM_DRINKCON && GET_OBJ_VAL(obj, 1)); obj = obj->next_content); if (ch->in_room == NOWHERE) msg_to_char(ch, "Unexpected error in douse.\r\n"); else if (SECT(ch->in_room) != SECT_BUILDING && SECT(ch->in_room) != SECT_INSIDE) msg_to_char(ch, "You can't really douse a fire here.\r\n"); else if (!world[room].burning) msg_to_char(ch, "There's no fire here!\r\n"); else if (!obj) msg_to_char(ch, "What do you plan to douse the fire with?\r\n"); else if (GET_OBJ_VAL(obj, 2) != LIQ_WATER) msg_to_char(ch, "Only water will douse a fire!\r\n"); else { amount = MIN(12, GET_OBJ_VAL(obj, 1)); GET_OBJ_VAL(obj, 1) -= amount; world[room].burning = MIN(120, world[room].burning + amount); act("You throw some water from $p onto the flames!", FALSE, ch, obj, 0, TO_CHAR); act("$n throws some water from $p onto the flames!", FALSE, ch, obj, 0, TO_CHAR); if (world[room].burning == 120) { act("The flames have been extinguished!", FALSE, ch, 0, 0, TO_CHAR | TO_ROOM); world[room].burning = 0; } } } #define EXPERIENCE_HEADER \ " Experience Costs:\r\n" \ " Attribute: next level x 4\r\n" \ " Ability: next level x 2 New Ability: 3\r\n" \ " Discipline: next level x 5 New Discipline: 10\r\n" \ " Path (primary): next level x 5\r\n" \ " Path (secondary): next level x 6\r\n" \ "" #define BUY_STAT(name, ch, stat, cost_have, cost_new) \ else if (is_abbrev(argument, name)) { \ if ((cost_new) == -1 && !(stat)) \ msg_to_char(ch, "You can't buy that as a new trait.\r\n"); \ else if ((stat) >= att_max(ch)) \ msg_to_char(ch, "That trait is already at maximum.\r\n"); \ else if (MAX(1, (cost_new)) > GET_EXPERIENCE(ch) && !(stat)) \ msg_to_char(ch, "You don't have enough experience to buy that trait.\r\n"); \ else if ((cost_have) * ((stat) + 1) > GET_EXPERIENCE(ch) && (stat) > 0) \ msg_to_char(ch, "You don't have enough experience to buy a new level of that trait.\r\n"); \ else { \ if ((stat) == 0) \ gain_experience(ch, -1 * (cost_new)); \ else \ gain_experience(ch, -1 * ((stat) + 1) * (cost_have)); \ (stat) += 1; \ msg_to_char(ch, OK); \ SAVE_CHAR(ch); \ } \ } #define BUY_DISC(name, ch, discipline) \ else if (is_abbrev(argument, name)) { \ if (!IS_VAMPIRE(ch) && !IS_GHOUL(ch)) \ msg_to_char(ch, "You can't buy disciplines!\r\n"); \ else if (GET_REAL_DISC(ch, discipline) >= disc_max(ch)) \ msg_to_char(ch, "That discipline is already at maximum.\r\n"); \ else if (discipline == DISC_THAUMATURGY && GET_REAL_DISC(ch, discipline) >= 5) \ msg_to_char(ch, "That discipline is already at maximum.\r\n"); \ else if (clan[GET_CLAN(ch)].disc[0] != discipline && clan[GET_CLAN(ch)].disc[1] != discipline && clan[GET_CLAN(ch)].disc[2] != discipline) \ msg_to_char(ch, "You cannot buy a discipline from another clan.\r\n"); \ else if (DISC_COST_NEW > GET_EXPERIENCE(ch) && !GET_REAL_DISC(ch, discipline)) \ msg_to_char(ch, "You don't have enough experience to buy that discipline.\r\n"); \ else if (DISC_COST_HAVE * (GET_REAL_DISC(ch, discipline) + 1) > GET_EXPERIENCE(ch) && GET_REAL_DISC(ch, discipline) > 0) \ msg_to_char(ch, "You don't have enough experience to buy a new level of that discipline.\r\n"); \ else { \ if (GET_REAL_DISC(ch, discipline) == 0) \ gain_experience(ch, -1 * DISC_COST_NEW); \ else \ gain_experience(ch, -1 * (GET_REAL_DISC(ch, discipline) + 1) * DISC_COST_HAVE); \ GET_REAL_DISC(ch, discipline) += 1; \ if (discipline == DISC_THAUMATURGY) \ GET_REAL_PATH(ch, (int) GET_REAL_PRIMARY_PATH(ch)) = GET_THAUMATURGY(ch); \ msg_to_char(ch, OK); \ SAVE_CHAR(ch); \ } \ } #define BUY_PATH(name, ch, path) \ else if (is_abbrev(argument, name)) { \ if (!IS_VAMPIRE(ch)) \ msg_to_char(ch, "You can't buy paths!\r\n"); \ else if (GET_REAL_PATH(ch, path) >= 5) \ msg_to_char(ch, "That path is already at maximum.\r\n"); \ else if (path != GET_PRIMARY_PATH(ch) && GET_REAL_PATH(ch, path) + 1 >= GET_REAL_PATH(ch, (int) GET_REAL_PRIMARY_PATH(ch)) && GET_REAL_PATH(ch, (int) GET_REAL_PRIMARY_PATH(ch)) < 5) \ msg_to_char(ch, "You can't raise another path to or above the rating in your primary path.\r\n"); \ else if (path == GET_PRIMARY_PATH(ch)) \ msg_to_char(ch, "Just purchase more Thaumaturgy.\r\n"); \ else if (GET_REAL_PATH(ch, path) >= GET_THAUMATURGY(ch)) \ msg_to_char(ch, "You can't raise a path past your Thaumaturgy rating.\r\n"); \ else if (PATH_COST_NEW > GET_EXPERIENCE(ch) && !GET_REAL_PATH(ch, path)) \ msg_to_char(ch, "You don't have enough experience to buy that path.\r\n"); \ else if (path != GET_PRIMARY_PATH(ch) && PATH_COST_SECONDARY * (GET_REAL_PATH(ch, path) + 1) > GET_EXPERIENCE(ch) && GET_REAL_PATH(ch, path) > 0) \ msg_to_char(ch, "You don't have enough experience to buy a new level of that path.\r\n"); \ else { \ if (GET_REAL_PATH(ch, path) == 0) \ gain_experience(ch, -1 * PATH_COST_NEW); \ else \ gain_experience(ch, -1 * (GET_REAL_PATH(ch, path) + 1) * PATH_COST_SECONDARY); \ GET_REAL_PATH(ch, path) += 1; \ msg_to_char(ch, OK); \ SAVE_CHAR(ch); \ } \ } ACMD(do_experience) { skip_spaces(&argument); if (IS_NPC(ch)) msg_to_char(ch, "NPC's do not have experience.\r\n"); else if (!*argument) msg_to_char(ch, "You have %d experience point%s to spend.\r\n%s", GET_EXPERIENCE(ch), GET_EXPERIENCE(ch) != 1 ? "s" : "", EXPERIENCE_HEADER); /* Attributes: Physical */ BUY_STAT("strength", ch, ch->real_abils.strength, ATTRIBUTE_COST, -1) BUY_STAT("dexterity", ch, ch->real_abils.dexterity, ATTRIBUTE_COST, -1) BUY_STAT("stamina", ch, ch->real_abils.stamina, ATTRIBUTE_COST, -1) /* Attributes: Social */ BUY_STAT("charisma", ch, ch->real_abils.charisma, ATTRIBUTE_COST, -1) BUY_STAT("manipulation", ch, ch->real_abils.manipulation, ATTRIBUTE_COST, -1) BUY_STAT("appearance", ch, ch->real_abils.appearance, ATTRIBUTE_COST, -1) /* Attributes: Mental */ BUY_STAT("perception", ch, ch->real_abils.perception, ATTRIBUTE_COST, -1) BUY_STAT("intelligence", ch, ch->real_abils.intelligence, ATTRIBUTE_COST, -1) BUY_STAT("wits", ch, ch->real_abils.wits, ATTRIBUTE_COST, -1) /* Abilities: Talents */ BUY_STAT("acting", ch, ch->player_specials->saved.talents[TALENT_ACTING], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("alertness", ch, ch->player_specials->saved.talents[TALENT_ALERTNESS], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("athletics", ch, ch->player_specials->saved.talents[TALENT_ATHLETICS], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("brawl", ch, ch->player_specials->saved.talents[TALENT_BRAWL], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("dodge", ch, ch->player_specials->saved.talents[TALENT_DODGE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("empathy", ch, ch->player_specials->saved.talents[TALENT_EMPATHY], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("intimidation", ch, ch->player_specials->saved.talents[TALENT_INTIMIDATION], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("larceny", ch, ch->player_specials->saved.talents[TALENT_LARCENY], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("leadership", ch, ch->player_specials->saved.talents[TALENT_LEADERSHIP], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("primal-urge", ch, ch->player_specials->saved.talents[TALENT_PRIMAL_URGE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("subterfuge", ch, ch->player_specials->saved.talents[TALENT_SUBTERFUGE], ABILITY_COST, ABILITY_COST_NEW) /* Abilities: Skills */ BUY_STAT("animal ken", ch, ch->player_specials->saved.skills[SKILL_ANIMAL_KEN], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("archery", ch, ch->player_specials->saved.skills[SKILL_ARCHERY], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("crafts", ch, ch->player_specials->saved.skills[SKILL_CRAFTS], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("etiquette", ch, ch->player_specials->saved.skills[SKILL_ETIQUETTE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("herbalism", ch, ch->player_specials->saved.skills[SKILL_HERBALISM], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("melee", ch, ch->player_specials->saved.skills[SKILL_MELEE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("music", ch, ch->player_specials->saved.skills[SKILL_MUSIC], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("performance", ch, ch->player_specials->saved.skills[SKILL_PERFORMANCE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("ride", ch, ch->player_specials->saved.skills[SKILL_RIDE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("stealth", ch, ch->player_specials->saved.skills[SKILL_STEALTH], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("survival", ch, ch->player_specials->saved.skills[SKILL_SURVIVAL], ABILITY_COST, ABILITY_COST_NEW) /* Abilities: Knowledges */ BUY_STAT("academics", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_ACADEMICS], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("enigmas", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_ENIGMAS], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("hearth wisdom", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_HEARTH_WISDOM], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("investigation", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_INVESTIGATION], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("law", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_LAW], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("medicine", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_MEDICINE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("occult", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_OCCULT], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("politics", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_POLITICS], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("science", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_SCIENCE], ABILITY_COST, ABILITY_COST_NEW) BUY_STAT("seneschal", ch, ch->player_specials->saved.knowledges[KNOWLEDGE_SENESCHAL], ABILITY_COST, ABILITY_COST_NEW) /* Disciplines */ BUY_DISC("animalism", ch, DISC_ANIMALISM) BUY_DISC("auspex", ch, DISC_AUSPEX) BUY_DISC("celerity", ch, DISC_CELERITY) BUY_DISC("chimerstry", ch, DISC_CHIMERSTRY) BUY_DISC("dementation", ch, DISC_DEMENTATION) BUY_DISC("dominate", ch, DISC_DOMINATE) else if (affected_by_spell(ch, ATYPE_BESTOW_VIGOR) && is_abbrev(argument, "fortitude")) msg_to_char(ch, "You can't purchase Fortitude right now.\r\n"); BUY_DISC("fortitude", ch, DISC_FORTITUDE) BUY_DISC("mortis", ch, DISC_MORTIS) BUY_DISC("obfuscate", ch, DISC_OBFUSCATE) BUY_DISC("obtenebration", ch, DISC_OBTENEBRATION) BUY_DISC("potence", ch, DISC_POTENCE) BUY_DISC("presence", ch, DISC_PRESENCE) BUY_DISC("protean", ch, DISC_PROTEAN) BUY_DISC("quietus", ch, DISC_QUIETUS) BUY_DISC("serpentis", ch, DISC_SERPENTIS) BUY_DISC("thaumaturgy", ch, DISC_THAUMATURGY) BUY_DISC("vicissitude", ch, DISC_VICISSITUDE) BUY_PATH("rego vitae", ch, PATH_REGO_VITAE) BUY_PATH("creo ignem", ch, PATH_CREO_IGNEM) BUY_PATH("rego motus", ch, PATH_REGO_MOTUS) BUY_PATH("rego tempestas", ch, PATH_REGO_TEMPESTAS) BUY_PATH("rego aquam", ch, PATH_REGO_AQUAM) BUY_PATH("rego elementum", ch, PATH_REGO_ELEMENTUM) BUY_PATH("way of levinbolt", ch, PATH_WAY_OF_LEVINBOLT) BUY_PATH("path of warding", ch, PATH_PATH_OF_WARDING) else msg_to_char(ch, "Invalid choice.\r\n"); }