mirror of
https://github.com/tbamud/tbamud.git
synced 2026-03-19 18:56:33 +01:00
2012 lines
56 KiB
C
2012 lines
56 KiB
C
/**************************************************************************
|
||
* File: act.other.c Part of tbaMUD *
|
||
* Usage: Miscellaneous player-level commands. *
|
||
* *
|
||
* All rights reserved. See license for complete information. *
|
||
* *
|
||
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
|
||
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
|
||
**************************************************************************/
|
||
|
||
#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 "spells.h"
|
||
#include "screen.h"
|
||
#include "house.h"
|
||
#include "constants.h"
|
||
#include "dg_scripts.h"
|
||
#include "act.h"
|
||
#include "spec_procs.h"
|
||
#include "class.h"
|
||
#include "fight.h"
|
||
#include "mail.h" /* for has_mail() */
|
||
#include "shop.h"
|
||
#include "quest.h"
|
||
#include "modify.h"
|
||
|
||
/* Local defined utility functions */
|
||
/* do_group utility functions */
|
||
static void print_group(struct char_data *ch);
|
||
static void display_group_list(struct char_data * ch);
|
||
|
||
static bool change_has_emote_tokens(const char *text)
|
||
{
|
||
for (; text && *text; text++) {
|
||
switch (*text) {
|
||
case '~': case '!': case '%': case '^':
|
||
case '#': case '&': case '=': case '+':
|
||
case '@':
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static bool change_ends_with_punct(const char *text)
|
||
{
|
||
size_t len = text ? strlen(text) : 0;
|
||
if (len == 0)
|
||
return FALSE;
|
||
return (text[len - 1] == '.' || text[len - 1] == '!' || text[len - 1] == '?');
|
||
}
|
||
|
||
static void change_trim_trailing_spaces(char *text)
|
||
{
|
||
size_t len = text ? strlen(text) : 0;
|
||
while (len > 0 && isspace((unsigned char)text[len - 1])) {
|
||
text[len - 1] = '\0';
|
||
len--;
|
||
}
|
||
}
|
||
|
||
#define REROLL_REVIEW_SECONDS (2 * SECS_PER_REAL_HOUR)
|
||
|
||
static void reroll_clear_saved(struct char_data *ch)
|
||
{
|
||
GET_REROLL_EXPIRES(ch) = 0;
|
||
memset(&GET_REROLL_OLD_ABILS(ch), 0, sizeof(struct char_ability_data));
|
||
}
|
||
|
||
ACMD(do_quit)
|
||
{
|
||
char first[MAX_INPUT_LENGTH];
|
||
char *rest;
|
||
|
||
if (IS_NPC(ch) || !ch->desc)
|
||
return;
|
||
|
||
/* Parse optional "ooc" sub-arg: quit ooc <message> */
|
||
rest = (char *)argument;
|
||
skip_spaces(&rest);
|
||
rest = one_argument(rest, first);
|
||
bool quit_ooc = (*first && is_abbrev(first, "ooc")) ? TRUE : FALSE;
|
||
|
||
/* Keep original safety controls */
|
||
if (!quit_ooc && subcmd != SCMD_QUIT && GET_LEVEL(ch) < LVL_IMMORT)
|
||
send_to_char(ch, "You have to type quit--no less, to quit!\r\n");
|
||
else if (GET_POS(ch) == POS_FIGHTING)
|
||
send_to_char(ch, "No way! You're fighting for your life!\r\n");
|
||
else if (GET_POS(ch) < POS_STUNNED) {
|
||
send_to_char(ch, "You die before your time...\r\n");
|
||
die(ch, NULL);
|
||
}
|
||
/* New: normal quit must be in a QUIT room (mortals only). */
|
||
else if (!quit_ooc && GET_LEVEL(ch) < LVL_IMMORT &&
|
||
!ROOM_FLAGGED(IN_ROOM(ch), ROOM_QUIT)) {
|
||
send_to_char(ch, "You cannot quit here. Find a room marked [Quit].\r\n");
|
||
}
|
||
/* For quit ooc, require a message for staff context. */
|
||
else if (quit_ooc) {
|
||
skip_spaces(&rest);
|
||
if (!*rest) {
|
||
send_to_char(ch, "Usage must include a reason to quit ooc: quit ooc <message>\r\n");
|
||
return;
|
||
}
|
||
|
||
act("$n has left the game.", TRUE, ch, 0, 0, TO_ROOM);
|
||
mudlog(CMP, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE,
|
||
"%s used QUIT OOC in room %d: %s",
|
||
GET_NAME(ch), GET_ROOM_VNUM(IN_ROOM(ch)), rest);
|
||
|
||
if (GET_QUEST_TIME(ch) != -1)
|
||
quest_timeout(ch);
|
||
|
||
send_to_char(ch, "You step out-of-character and leave the world...\r\n");
|
||
|
||
/* Save character and objects */
|
||
save_char(ch);
|
||
Crash_rentsave(ch, 0);
|
||
|
||
/* Requirement: respawn in the same (possibly non-QUIT) room. */
|
||
GET_LOADROOM(ch) = GET_ROOM_VNUM(IN_ROOM(ch));
|
||
|
||
/* Stop snooping so you can't see passwords during deletion or change. */
|
||
if (ch->desc->snoop_by) {
|
||
write_to_output(ch->desc->snoop_by, "Your victim is no longer among us.\r\n");
|
||
ch->desc->snoop_by->snooping = NULL;
|
||
ch->desc->snoop_by = NULL;
|
||
}
|
||
|
||
extract_char(ch); /* Char is saved before extracting. */
|
||
}
|
||
else {
|
||
/* Normal quit (in a QUIT room, or immortal bypass) */
|
||
act("$n has left the game.", TRUE, ch, 0, 0, TO_ROOM);
|
||
mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE,
|
||
"%s has quit the game.", GET_NAME(ch));
|
||
|
||
if (GET_QUEST_TIME(ch) != -1)
|
||
quest_timeout(ch);
|
||
|
||
send_to_char(ch, "Goodbye, friend.. Come back soon!\r\n");
|
||
|
||
/* Save character and objects */
|
||
save_char(ch);
|
||
Crash_rentsave(ch, 0);
|
||
|
||
/* Requirement: respawn in the same QUIT room they logged out in. */
|
||
GET_LOADROOM(ch) = GET_ROOM_VNUM(IN_ROOM(ch));
|
||
|
||
/* Stop snooping so you can't see passwords during deletion or change. */
|
||
if (ch->desc->snoop_by) {
|
||
write_to_output(ch->desc->snoop_by, "Your victim is no longer among us.\r\n");
|
||
ch->desc->snoop_by->snooping = NULL;
|
||
ch->desc->snoop_by = NULL;
|
||
}
|
||
|
||
SET_BIT_AR(PLR_FLAGS(ch), PLR_QUITING);
|
||
extract_char(ch); /* Char is saved before extracting. */
|
||
}
|
||
}
|
||
|
||
ACMD(do_save)
|
||
{
|
||
char a1[MAX_INPUT_LENGTH], a2[MAX_INPUT_LENGTH];
|
||
|
||
/* Accept both orders: "save room" or "room save" */
|
||
two_arguments(argument, a1, a2);
|
||
|
||
/* order-agnostic check */
|
||
int wants_room = ((*a1 && !str_cmp(a1, "room")) ||
|
||
(*a2 && !str_cmp(a2, "room")));
|
||
|
||
if (wants_room) {
|
||
room_rnum rnum = IN_ROOM(ch);
|
||
|
||
if (rnum == NOWHERE) {
|
||
send_to_char(ch, "You're not in a valid room.\r\n");
|
||
return;
|
||
}
|
||
|
||
/* Not a SAVE room? Fall back to normal character save semantics. */
|
||
if (!ROOM_FLAGGED(rnum, ROOM_SAVE)) {
|
||
send_to_char(ch, "Saving %s.\r\n", GET_NAME(ch));
|
||
save_char(ch);
|
||
Crash_crashsave(ch); /* keep whatever your tree normally calls */
|
||
return;
|
||
}
|
||
|
||
/* Room is flagged SAVE → persist its contents */
|
||
if (RoomSave_now(rnum)) {
|
||
send_to_char(ch, "Saving room.\r\n");
|
||
mudlog(NRM, LVL_IMMORT, FALSE,
|
||
"RoomSave: manual save of room %d by %s.",
|
||
world[rnum].number, GET_NAME(ch));
|
||
/* If you added a dirty-save API and want to clear the bit on manual save,
|
||
you can optionally call it here (guard with a macro if desired):
|
||
#ifdef ROOMSAVE_HAVE_DIRTY_API
|
||
RoomSave_clear_dirty(rnum);
|
||
#endif
|
||
*/
|
||
} else {
|
||
send_to_char(ch, "Room save failed; see logs.\r\n");
|
||
mudlog(NRM, LVL_IMMORT, TRUE,
|
||
"SYSERR: RoomSave: manual save FAILED for room %d by %s.",
|
||
world[rnum].number, GET_NAME(ch));
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* No "room" token present → normal character save */
|
||
send_to_char(ch, "Saving %s.\r\n", GET_NAME(ch));
|
||
save_char(ch);
|
||
Crash_crashsave(ch);
|
||
}
|
||
|
||
ACMD(do_change)
|
||
{
|
||
char option[MAX_INPUT_LENGTH];
|
||
char suffix[MAX_INPUT_LENGTH];
|
||
char base_buf[MAX_INPUT_LENGTH];
|
||
char ldesc[MAX_STRING_LENGTH];
|
||
char *rest = argument;
|
||
const char *base;
|
||
|
||
rest = one_argument(rest, option);
|
||
if (!*option) {
|
||
send_to_char(ch, "Usage: change ldesc <string>\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!is_abbrev(option, "ldesc")) {
|
||
send_to_char(ch, "Unknown change option. Available: ldesc\r\n");
|
||
return;
|
||
}
|
||
|
||
skip_spaces(&rest);
|
||
if (!*rest) {
|
||
send_to_char(ch, "Usage: change ldesc <string>\r\n");
|
||
return;
|
||
}
|
||
|
||
if (change_has_emote_tokens(rest)) {
|
||
send_to_char(ch, "You can't use emote tokens in your ldesc.\r\n");
|
||
return;
|
||
}
|
||
|
||
strlcpy(suffix, rest, sizeof(suffix));
|
||
change_trim_trailing_spaces(suffix);
|
||
if (!*suffix) {
|
||
send_to_char(ch, "Usage: change ldesc <string>\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!change_ends_with_punct(suffix))
|
||
strlcat(suffix, ".", sizeof(suffix));
|
||
|
||
base = (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? GET_SHORT_DESC(ch) : GET_NAME(ch);
|
||
if (!base || !*base)
|
||
base = "someone";
|
||
|
||
strlcpy(base_buf, base, sizeof(base_buf));
|
||
if (*base_buf)
|
||
base_buf[0] = UPPER(*base_buf);
|
||
|
||
snprintf(ldesc, sizeof(ldesc), "%s %s\r\n", base_buf, suffix);
|
||
|
||
if (ch->player.long_descr) {
|
||
if (!IS_NPC(ch) || GET_MOB_RNUM(ch) == NOBODY ||
|
||
ch->player.long_descr != mob_proto[GET_MOB_RNUM(ch)].player.long_descr) {
|
||
free(ch->player.long_descr);
|
||
}
|
||
}
|
||
ch->player.long_descr = strdup(ldesc);
|
||
ch->char_specials.custom_ldesc = TRUE;
|
||
|
||
send_to_char(ch, "Long description updated.\r\n");
|
||
}
|
||
|
||
ACMD(do_reroll)
|
||
{
|
||
char arg[MAX_INPUT_LENGTH];
|
||
struct char_data *vict;
|
||
time_t now;
|
||
time_t remaining;
|
||
bool expired = FALSE;
|
||
|
||
one_argument(argument, arg);
|
||
|
||
if (IS_NPC(ch)) {
|
||
send_to_char(ch, "You can't reroll stats.\r\n");
|
||
return;
|
||
}
|
||
|
||
now = time(0);
|
||
if (GET_REROLL_EXPIRES(ch) > 0 && now >= GET_REROLL_EXPIRES(ch)) {
|
||
reroll_clear_saved(ch);
|
||
save_char(ch);
|
||
expired = TRUE;
|
||
}
|
||
|
||
if (*arg && is_abbrev(arg, "undo")) {
|
||
if (!GET_REROLL_USED(ch)) {
|
||
send_to_char(ch, "You haven't rerolled yet.\r\n");
|
||
return;
|
||
}
|
||
if (GET_REROLL_EXPIRES(ch) <= 0 || expired) {
|
||
send_to_char(ch, "You no longer have a reroll pending; your stats are permanent.\r\n");
|
||
return;
|
||
}
|
||
|
||
ch->real_abils = GET_REROLL_OLD_ABILS(ch);
|
||
affect_total(ch);
|
||
reroll_clear_saved(ch);
|
||
save_char(ch);
|
||
|
||
send_to_char(ch, "Your original stats have been restored. You cannot reroll again.\r\n");
|
||
send_to_char(ch, "Stats: Str %d, Int %d, Wis %d, Dex %d, Con %d, Cha %d\r\n",
|
||
GET_STR(ch), GET_INT(ch), GET_WIS(ch),
|
||
GET_DEX(ch), GET_CON(ch), GET_CHA(ch));
|
||
return;
|
||
}
|
||
|
||
if (*arg && GET_LEVEL(ch) >= LVL_GRGOD) {
|
||
if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
|
||
send_to_char(ch, "There is no such player.\r\n");
|
||
else if (IS_NPC(vict))
|
||
send_to_char(ch, "You can't do that to a mob!\r\n");
|
||
else {
|
||
roll_real_abils(vict);
|
||
affect_total(vict);
|
||
send_to_char(ch, "Rerolled...\r\n");
|
||
log("(GC) %s has rerolled %s.", GET_NAME(ch), GET_NAME(vict));
|
||
send_to_char(ch, "New stats: Str %d, Int %d, Wis %d, Dex %d, Con %d, Cha %d\r\n",
|
||
GET_STR(vict), GET_INT(vict), GET_WIS(vict),
|
||
GET_DEX(vict), GET_CON(vict), GET_CHA(vict));
|
||
save_char(vict);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (*arg) {
|
||
send_to_char(ch, "Usage: reroll | reroll undo\r\n");
|
||
return;
|
||
}
|
||
|
||
if (GET_REROLL_USED(ch)) {
|
||
if (GET_REROLL_EXPIRES(ch) > 0 && now < GET_REROLL_EXPIRES(ch)) {
|
||
remaining = GET_REROLL_EXPIRES(ch) - now;
|
||
int hours = remaining / SECS_PER_REAL_HOUR;
|
||
int mins = (remaining % SECS_PER_REAL_HOUR) / SECS_PER_REAL_MIN;
|
||
|
||
if (hours > 0)
|
||
send_to_char(ch, "You have already rerolled. You can 'reroll undo' within %d hour%s %d minute%s.\r\n",
|
||
hours, hours == 1 ? "" : "s", mins, mins == 1 ? "" : "s");
|
||
else
|
||
send_to_char(ch, "You have already rerolled. You can 'reroll undo' within %d minute%s.\r\n",
|
||
mins, mins == 1 ? "" : "s");
|
||
} else {
|
||
send_to_char(ch, "You have already rerolled and cannot reroll again.\r\n");
|
||
}
|
||
return;
|
||
}
|
||
|
||
GET_REROLL_OLD_ABILS(ch) = ch->real_abils;
|
||
roll_real_abils(ch);
|
||
affect_total(ch);
|
||
GET_REROLL_USED(ch) = TRUE;
|
||
GET_REROLL_EXPIRES(ch) = now + REROLL_REVIEW_SECONDS;
|
||
save_char(ch);
|
||
|
||
send_to_char(ch, "Your stats have been rerolled. You have 2 hours to review them.\r\n");
|
||
send_to_char(ch, "New stats: Str %d, Int %d, Wis %d, Dex %d, Con %d, Cha %d\r\n",
|
||
GET_STR(ch), GET_INT(ch), GET_WIS(ch),
|
||
GET_DEX(ch), GET_CON(ch), GET_CHA(ch));
|
||
send_to_char(ch, "Use 'reroll undo' to restore your original stats before the timer expires.\r\n");
|
||
}
|
||
|
||
/* Generic function for commands which are normally overridden by special
|
||
* procedures - i.e., shop commands, mail commands, etc. */
|
||
ACMD(do_not_here)
|
||
{
|
||
send_to_char(ch, "Sorry, but you cannot do that here!\r\n");
|
||
}
|
||
|
||
#define STEALTH_BASE_DC 10
|
||
#define SLEIGHT_BASE_DC 10
|
||
|
||
int get_stealth_skill_value(struct char_data *ch)
|
||
{
|
||
int skill = GET_SKILL(ch, SKILL_STEALTH);
|
||
int legacy = MAX(GET_SKILL(ch, SKILL_HIDE), GET_SKILL(ch, SKILL_SNEAK));
|
||
|
||
if (skill <= 0 && legacy > 0) {
|
||
skill = MIN(legacy, 100);
|
||
SET_SKILL(ch, SKILL_STEALTH, skill);
|
||
}
|
||
|
||
return skill;
|
||
}
|
||
|
||
int roll_stealth_check(struct char_data *ch)
|
||
{
|
||
int mode = has_stealth_disadv(ch) ? -1 : 0;
|
||
|
||
get_stealth_skill_value(ch);
|
||
return roll_skill_check(ch, SKILL_STEALTH, mode, NULL);
|
||
}
|
||
|
||
int roll_sleight_check(struct char_data *ch)
|
||
{
|
||
int total = roll_skill_check(ch, SKILL_SLEIGHT_OF_HAND, 0, NULL);
|
||
|
||
if (FIGHTING(ch))
|
||
total -= 4;
|
||
|
||
return total;
|
||
}
|
||
|
||
static int compute_steal_dc(struct char_data *thief, struct char_data *vict, int weight)
|
||
{
|
||
int dc = SLEIGHT_BASE_DC + MAX(0, weight);
|
||
|
||
if (!vict)
|
||
return dc;
|
||
|
||
if (GET_LEVEL(vict) >= LVL_IMMORT)
|
||
return 1000;
|
||
|
||
if (GET_POS(vict) < POS_SLEEPING)
|
||
return MAX(0, weight);
|
||
|
||
if (!AWAKE(vict)) {
|
||
dc = MAX(0, dc - 5);
|
||
if (thief && AFF_FLAGGED(thief, AFF_HIDE))
|
||
dc -= 5;
|
||
return MAX(0, dc);
|
||
}
|
||
|
||
dc += GET_ABILITY_MOD(GET_WIS(vict));
|
||
if (GET_SKILL(vict, SKILL_PERCEPTION) > 0)
|
||
dc += get_total_proficiency_bonus(vict);
|
||
if (FIGHTING(vict))
|
||
dc -= 4;
|
||
if (AFF_FLAGGED(vict, AFF_SCAN))
|
||
dc += 5;
|
||
if (thief && AFF_FLAGGED(thief, AFF_HIDE))
|
||
dc -= 5;
|
||
if (GET_MOB_SPEC(vict) == shop_keeper)
|
||
dc += 20;
|
||
|
||
return dc;
|
||
}
|
||
|
||
static struct obj_data *find_container_on_character(struct char_data *viewer,
|
||
struct char_data *vict,
|
||
const char *name)
|
||
{
|
||
struct obj_data *obj;
|
||
int eq;
|
||
|
||
if (!viewer || !vict || !name || !*name)
|
||
return NULL;
|
||
|
||
for (obj = vict->carrying; obj; obj = obj->next_content) {
|
||
if (!CAN_SEE_OBJ(viewer, obj))
|
||
continue;
|
||
if (isname(name, obj->name))
|
||
return obj;
|
||
}
|
||
|
||
for (eq = 0; eq < NUM_WEARS; eq++) {
|
||
if (!(obj = GET_EQ(vict, eq)))
|
||
continue;
|
||
if (!CAN_SEE_OBJ(viewer, obj))
|
||
continue;
|
||
if (isname(name, obj->name))
|
||
return obj;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static bool sleight_can_take_obj(struct char_data *ch, struct obj_data *obj)
|
||
{
|
||
if (!obj)
|
||
return FALSE;
|
||
|
||
if (GET_OBJ_TYPE(obj) == ITEM_MONEY)
|
||
update_money_obj(obj);
|
||
|
||
if (!CAN_WEAR(obj, ITEM_WEAR_TAKE)) {
|
||
act("$p: you can't take that!", FALSE, ch, obj, 0, TO_CHAR);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_NOHASSLE)) {
|
||
if (IS_CARRYING_N(ch) >= CAN_CARRY_N(ch)) {
|
||
act("$p: you can't carry that many items.", FALSE, ch, obj, 0, TO_CHAR);
|
||
return FALSE;
|
||
}
|
||
if ((IS_CARRYING_W(ch) + GET_OBJ_WEIGHT(obj)) > CAN_CARRY_W(ch)) {
|
||
act("$p: you can't carry that much weight.", FALSE, ch, obj, 0, TO_CHAR);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void sleight_check_money(struct char_data *ch, struct obj_data *obj)
|
||
{
|
||
if (!obj || GET_OBJ_TYPE(obj) != ITEM_MONEY)
|
||
return;
|
||
update_money_obj(obj);
|
||
}
|
||
|
||
static int count_coins_in_list(const struct obj_data *list)
|
||
{
|
||
int total = 0;
|
||
const struct obj_data *obj;
|
||
|
||
for (obj = list; obj; obj = obj->next_content) {
|
||
if (GET_OBJ_TYPE(obj) == ITEM_MONEY)
|
||
total += MAX(0, GET_OBJ_VAL(obj, 0));
|
||
if (obj->contains)
|
||
total += count_coins_in_list(obj->contains);
|
||
}
|
||
|
||
return total;
|
||
}
|
||
|
||
static int remove_coins_from_list(struct obj_data *list, int amount)
|
||
{
|
||
struct obj_data *obj, *next_obj;
|
||
int removed = 0;
|
||
|
||
for (obj = list; obj && removed < amount; obj = next_obj) {
|
||
next_obj = obj->next_content;
|
||
|
||
if (GET_OBJ_TYPE(obj) == ITEM_MONEY) {
|
||
int pile = MAX(0, GET_OBJ_VAL(obj, 0));
|
||
int take = MIN(pile, amount - removed);
|
||
|
||
if (take > 0) {
|
||
if (take == pile) {
|
||
removed += take;
|
||
extract_obj(obj);
|
||
continue;
|
||
}
|
||
GET_OBJ_VAL(obj, 0) = pile - take;
|
||
update_money_obj(obj);
|
||
removed += take;
|
||
}
|
||
}
|
||
|
||
if (obj->contains && removed < amount)
|
||
removed += remove_coins_from_list(obj->contains, amount - removed);
|
||
}
|
||
|
||
return removed;
|
||
}
|
||
|
||
static bool sleight_merge_money_pile(struct char_data *ch, struct obj_data *obj)
|
||
{
|
||
struct obj_data *target;
|
||
int coins;
|
||
|
||
if (!ch || !obj || GET_OBJ_TYPE(obj) != ITEM_MONEY)
|
||
return FALSE;
|
||
|
||
for (target = ch->carrying; target; target = target->next_content) {
|
||
if (target != obj && GET_OBJ_TYPE(target) == ITEM_MONEY)
|
||
break;
|
||
}
|
||
|
||
if (!target)
|
||
return FALSE;
|
||
|
||
coins = MAX(0, GET_OBJ_VAL(obj, 0));
|
||
if (coins <= 0)
|
||
return FALSE;
|
||
|
||
GET_OBJ_VAL(target, 0) += coins;
|
||
update_money_obj(target);
|
||
GET_COINS(ch) = MIN(MAX_COINS, GET_COINS(ch) + coins);
|
||
extract_obj(obj);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static bool sleight_observer_notices(struct char_data *actor,
|
||
struct char_data *viewer,
|
||
int sleight_total)
|
||
{
|
||
int d20, total;
|
||
|
||
if (!viewer || viewer == actor)
|
||
return FALSE;
|
||
if (!AWAKE(viewer))
|
||
return FALSE;
|
||
|
||
if (GET_LEVEL(viewer) >= LVL_IMMORT) {
|
||
gain_skill(viewer, "perception", TRUE);
|
||
return TRUE;
|
||
}
|
||
|
||
if (can_scan_for_sneak(viewer)) {
|
||
total = roll_skill_check(viewer, SKILL_PERCEPTION, 0, &d20);
|
||
if (FIGHTING(viewer))
|
||
total -= 4;
|
||
} else {
|
||
d20 = roll_d20();
|
||
total = d20;
|
||
}
|
||
|
||
if (d20 == 1) {
|
||
gain_skill(viewer, "perception", FALSE);
|
||
return FALSE;
|
||
}
|
||
|
||
if (d20 == 20 || total >= sleight_total) {
|
||
gain_skill(viewer, "perception", TRUE);
|
||
return TRUE;
|
||
}
|
||
|
||
gain_skill(viewer, "perception", FALSE);
|
||
return FALSE;
|
||
}
|
||
|
||
static void sleight_send_notice(struct char_data *viewer,
|
||
struct char_data *actor,
|
||
const char *verb,
|
||
const char *prep,
|
||
const char *item_desc,
|
||
const char *container_desc)
|
||
{
|
||
char line[MAX_STRING_LENGTH];
|
||
char actor_desc[MAX_INPUT_LENGTH];
|
||
char item_clean[MAX_STRING_LENGTH];
|
||
char cont_clean[MAX_STRING_LENGTH];
|
||
|
||
strlcpy(actor_desc, PERS(actor, viewer), sizeof(actor_desc));
|
||
strlcpy(item_clean, item_desc ? item_desc : "something", sizeof(item_clean));
|
||
strlcpy(cont_clean, container_desc ? container_desc : "something", sizeof(cont_clean));
|
||
|
||
if (!strn_cmp(item_clean, "a ", 2))
|
||
memmove(item_clean, item_clean + 2, strlen(item_clean) - 1);
|
||
else if (!strn_cmp(item_clean, "an ", 3))
|
||
memmove(item_clean, item_clean + 3, strlen(item_clean) - 2);
|
||
|
||
if (!strn_cmp(cont_clean, "a ", 2))
|
||
memmove(cont_clean, cont_clean + 2, strlen(cont_clean) - 1);
|
||
else if (!strn_cmp(cont_clean, "an ", 3))
|
||
memmove(cont_clean, cont_clean + 3, strlen(cont_clean) - 2);
|
||
|
||
snprintf(line, sizeof(line), "%s tries to %s %s %s %s %s.",
|
||
actor_desc, verb, item_clean, prep, HSHR(actor), cont_clean);
|
||
|
||
send_to_char(viewer, "You notice:\r\n %s\r\n", line);
|
||
}
|
||
|
||
static void sleight_check_observers(struct char_data *actor,
|
||
int sleight_total,
|
||
const char *verb,
|
||
const char *prep,
|
||
const char *item_desc,
|
||
const char *container_desc)
|
||
{
|
||
struct char_data *viewer;
|
||
|
||
if (!actor || IN_ROOM(actor) == NOWHERE)
|
||
return;
|
||
|
||
for (viewer = world[IN_ROOM(actor)].people; viewer; viewer = viewer->next_in_room) {
|
||
if (viewer == actor)
|
||
continue;
|
||
if (sleight_observer_notices(actor, viewer, sleight_total))
|
||
sleight_send_notice(viewer, actor, verb, prep, item_desc, container_desc);
|
||
}
|
||
}
|
||
|
||
static int sneak_effect_duration(struct char_data *ch)
|
||
{
|
||
int skill = get_stealth_skill_value(ch);
|
||
if (skill <= 0)
|
||
return 1;
|
||
|
||
return MAX(1, skill / 10);
|
||
}
|
||
|
||
bool can_scan_for_sneak(struct char_data *ch)
|
||
{
|
||
if (!AFF_FLAGGED(ch, AFF_SCAN))
|
||
return FALSE;
|
||
if (!GET_SKILL(ch, SKILL_PERCEPTION))
|
||
return FALSE;
|
||
if (AFF_FLAGGED(ch, AFF_BLIND))
|
||
return FALSE;
|
||
if (IS_DARK(IN_ROOM(ch)) && !CAN_SEE_IN_DARK(ch))
|
||
return FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
int roll_scan_perception(struct char_data *ch)
|
||
{
|
||
int total = roll_skill_check(ch, SKILL_PERCEPTION, 0, NULL);
|
||
|
||
if (FIGHTING(ch))
|
||
total -= 4;
|
||
|
||
return total;
|
||
}
|
||
|
||
static int listen_effect_duration(struct char_data *ch)
|
||
{
|
||
int skill = GET_SKILL(ch, SKILL_PERCEPTION);
|
||
|
||
if (skill <= 0)
|
||
return 1;
|
||
|
||
return MAX(1, skill / 10);
|
||
}
|
||
|
||
ACMD(do_sneak)
|
||
{
|
||
struct affected_type af;
|
||
int total, dc;
|
||
int stealth_skill = get_stealth_skill_value(ch);
|
||
|
||
if (!stealth_skill) {
|
||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (FIGHTING(ch)){
|
||
send_to_char(ch, "While fighting!?\r\n");
|
||
return;
|
||
} /* you can't sneak while in active melee */
|
||
|
||
send_to_char(ch, "Okay, you'll try to move silently for a while.\r\n");
|
||
|
||
/* Remove prior sneak affect if present (refresh logic) */
|
||
if (AFF_FLAGGED(ch, AFF_SNEAK))
|
||
affect_from_char(ch, SKILL_SNEAK);
|
||
|
||
/* --- 5e-style Stealth check (DEX + proficiency) --- */
|
||
total = roll_stealth_check(ch);
|
||
dc = STEALTH_BASE_DC;
|
||
|
||
if (total < dc) {
|
||
gain_skill(ch, "stealth", FALSE);
|
||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||
GET_STAMINA(ch) -= 10;
|
||
return;
|
||
}
|
||
|
||
/* Success: apply Sneak affect */
|
||
new_affect(&af);
|
||
af.spell = SKILL_SNEAK;
|
||
af.location = APPLY_NONE;
|
||
af.modifier = 0;
|
||
af.duration = sneak_effect_duration(ch);
|
||
memset(af.bitvector, 0, sizeof(af.bitvector));
|
||
SET_BIT_AR(af.bitvector, AFF_SNEAK);
|
||
affect_to_char(ch, &af);
|
||
|
||
/* Store a stealth check value for movement contests (reuse Hide’s field) */
|
||
/* If you’ve already hidden with a higher roll, keep the stronger value. */
|
||
SET_STEALTH_CHECK(ch, MAX(GET_STEALTH_CHECK(ch), total));
|
||
|
||
gain_skill(ch, "stealth", TRUE);
|
||
GET_STAMINA(ch) -= 10;
|
||
}
|
||
|
||
ACMD(do_hide)
|
||
{
|
||
int total, dc;
|
||
int stealth_skill = get_stealth_skill_value(ch);
|
||
|
||
if (!stealth_skill) {
|
||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||
return;
|
||
}
|
||
|
||
send_to_char(ch, "You attempt to hide yourself.\r\n");
|
||
|
||
/* If already hidden, drop it before re-attempting */
|
||
if (AFF_FLAGGED(ch, AFF_HIDE)) {
|
||
REMOVE_BIT_AR(AFF_FLAGS(ch), AFF_HIDE);
|
||
GET_STEALTH_CHECK(ch) = 0;
|
||
}
|
||
|
||
if (FIGHTING(ch)){
|
||
send_to_char(ch, "While fighting!?\r\n");
|
||
return;
|
||
} /* you can't hide while in active melee */
|
||
|
||
/* --- 5e Stealth (DEX) ability check --- */
|
||
/* Baseline difficulty: hiding in general */
|
||
/* TODO: Maybe change dc based on terrain/populated rooms in the future */
|
||
dc = STEALTH_BASE_DC;
|
||
total = roll_stealth_check(ch);
|
||
|
||
if (total < dc) {
|
||
/* Failure */
|
||
gain_skill(ch, "stealth", FALSE);
|
||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||
GET_STAMINA(ch) -= 10;
|
||
return;
|
||
}
|
||
|
||
/* Success: set flag and store this specific Stealth result */
|
||
SET_BIT_AR(AFF_FLAGS(ch), AFF_HIDE);
|
||
GET_STEALTH_CHECK(ch) = total;
|
||
|
||
send_to_char(ch, "You hide yourself as best you can.\r\n");
|
||
gain_skill(ch, "stealth", TRUE);
|
||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||
GET_STAMINA(ch) -= 10;
|
||
}
|
||
|
||
static void remember_scan_target(struct char_data *ch, struct char_data *tch)
|
||
{
|
||
struct scan_result_data *node;
|
||
long uid;
|
||
|
||
if (!ch || !tch)
|
||
return;
|
||
if (IS_NPC(ch))
|
||
return;
|
||
if (!ch->player_specials || ch->player_specials == &dummy_mob)
|
||
return;
|
||
|
||
uid = char_script_id(tch);
|
||
for (node = GET_SCAN_RESULTS(ch); node; node = node->next) {
|
||
if (node->target_uid == uid) {
|
||
node->room = IN_ROOM(ch);
|
||
return;
|
||
}
|
||
}
|
||
|
||
CREATE(node, struct scan_result_data, 1);
|
||
node->target_uid = uid;
|
||
node->room = IN_ROOM(ch);
|
||
node->next = GET_SCAN_RESULTS(ch);
|
||
GET_SCAN_RESULTS(ch) = node;
|
||
}
|
||
|
||
static void forget_scan_target(struct char_data *ch, struct char_data *tch)
|
||
{
|
||
struct scan_result_data **node;
|
||
long uid;
|
||
|
||
if (!ch || !tch)
|
||
return;
|
||
if (IS_NPC(ch))
|
||
return;
|
||
if (!ch->player_specials || ch->player_specials == &dummy_mob)
|
||
return;
|
||
|
||
uid = char_script_id(tch);
|
||
for (node = &GET_SCAN_RESULTS(ch); *node; node = &((*node)->next)) {
|
||
if ((*node)->target_uid == uid) {
|
||
struct scan_result_data *old = *node;
|
||
*node = old->next;
|
||
free(old);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
void stealth_process_room_movement(struct char_data *ch, room_rnum room, int dir, bool leaving)
|
||
{
|
||
struct char_data *viewer;
|
||
int stealth_total;
|
||
bool base_failure;
|
||
const char *dir_word;
|
||
char msg[MAX_INPUT_LENGTH];
|
||
char sdesc_buf[MAX_INPUT_LENGTH];
|
||
const char *name_desc;
|
||
const char *format;
|
||
|
||
if (!ch || room == NOWHERE)
|
||
return;
|
||
|
||
stealth_total = roll_stealth_check(ch);
|
||
base_failure = (stealth_total < STEALTH_BASE_DC);
|
||
|
||
if (dir >= 0 && dir < NUM_OF_DIRS) {
|
||
dir_word = leaving ? dirs[dir] : dirs[rev_dir[dir]];
|
||
} else {
|
||
dir_word = "somewhere";
|
||
}
|
||
|
||
if (get_char_sdesc(ch) && *get_char_sdesc(ch)) {
|
||
strlcpy(sdesc_buf, get_char_sdesc(ch), sizeof(sdesc_buf));
|
||
if (*sdesc_buf)
|
||
sdesc_buf[0] = UPPER(sdesc_buf[0]);
|
||
name_desc = sdesc_buf;
|
||
} else {
|
||
name_desc = "Someone";
|
||
}
|
||
|
||
format = leaving ?
|
||
"%s tries to stealthily move to the %s." :
|
||
"%s stealthily moves in from the %s.";
|
||
snprintf(msg, sizeof(msg), format, name_desc, dir_word);
|
||
|
||
for (viewer = world[room].people; viewer; viewer = viewer->next_in_room) {
|
||
bool viewer_can_scan, saw_with_scan = FALSE, send_echo = FALSE;
|
||
|
||
if (viewer == ch)
|
||
continue;
|
||
|
||
viewer_can_scan = can_scan_for_sneak(viewer);
|
||
|
||
if (viewer_can_scan) {
|
||
int perception_total = roll_scan_perception(viewer);
|
||
|
||
if (perception_total >= stealth_total) {
|
||
saw_with_scan = TRUE;
|
||
send_echo = TRUE;
|
||
remember_scan_target(viewer, ch);
|
||
} else if (!base_failure) {
|
||
forget_scan_target(viewer, ch);
|
||
}
|
||
|
||
gain_skill(viewer, "perception", saw_with_scan ? TRUE : FALSE);
|
||
}
|
||
|
||
if (!send_echo && base_failure) {
|
||
if (!viewer_can_scan && !CAN_SEE(viewer, ch))
|
||
continue;
|
||
send_echo = TRUE;
|
||
if (viewer_can_scan)
|
||
remember_scan_target(viewer, ch);
|
||
}
|
||
|
||
if (send_echo)
|
||
send_to_char(viewer, "%s\r\n", msg);
|
||
}
|
||
}
|
||
|
||
void clear_scan_results(struct char_data *ch)
|
||
{
|
||
struct scan_result_data *node, *next;
|
||
|
||
if (!ch || IS_NPC(ch))
|
||
return;
|
||
if (!ch->player_specials || ch->player_specials == &dummy_mob)
|
||
return;
|
||
|
||
for (node = GET_SCAN_RESULTS(ch); node; node = next) {
|
||
next = node->next;
|
||
free(node);
|
||
}
|
||
|
||
GET_SCAN_RESULTS(ch) = NULL;
|
||
}
|
||
|
||
bool scan_can_target(struct char_data *ch, struct char_data *tch)
|
||
{
|
||
struct scan_result_data *node;
|
||
long uid;
|
||
|
||
if (!ch || !tch)
|
||
return FALSE;
|
||
if (IS_NPC(ch))
|
||
return FALSE;
|
||
if (!ch->player_specials || ch->player_specials == &dummy_mob)
|
||
return FALSE;
|
||
if (!AFF_FLAGGED(ch, AFF_SCAN))
|
||
return FALSE;
|
||
|
||
uid = char_script_id(tch);
|
||
|
||
for (node = GET_SCAN_RESULTS(ch); node; node = node->next)
|
||
if (node->target_uid == uid && node->room == IN_ROOM(ch))
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static int scan_target_dc(struct char_data *tch)
|
||
{
|
||
if (GET_STEALTH_CHECK(tch) <= 0)
|
||
SET_STEALTH_CHECK(tch, 5);
|
||
|
||
/* Give hiders a modest buffer so high skill matters but success remains possible. */
|
||
return GET_STEALTH_CHECK(tch) + 2;
|
||
}
|
||
|
||
bool scan_confirm_target(struct char_data *ch, struct char_data *tch)
|
||
{
|
||
int total;
|
||
|
||
if (!ch || !tch)
|
||
return FALSE;
|
||
if (!AFF_FLAGGED(ch, AFF_SCAN))
|
||
return FALSE;
|
||
if (!GET_SKILL(ch, SKILL_PERCEPTION))
|
||
return FALSE;
|
||
|
||
total = roll_skill_check(ch, SKILL_PERCEPTION, 0, NULL);
|
||
|
||
if (FIGHTING(ch))
|
||
total -= 4;
|
||
|
||
if (total >= scan_target_dc(tch)) {
|
||
remember_scan_target(ch, tch);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static int scan_effect_duration(struct char_data *ch)
|
||
{
|
||
int skill = GET_SKILL(ch, SKILL_PERCEPTION);
|
||
int minutes;
|
||
|
||
if (skill < 20)
|
||
minutes = 15;
|
||
else if (skill < 40)
|
||
minutes = 20;
|
||
else if (skill < 60)
|
||
minutes = 25;
|
||
else if (skill < 80)
|
||
minutes = 30;
|
||
else
|
||
minutes = 45;
|
||
|
||
/* Affect durations tick once per mud hour (75 seconds). */
|
||
return MAX(1, (minutes * SECS_PER_REAL_MIN) / SECS_PER_MUD_HOUR);
|
||
}
|
||
|
||
bool perform_scan_sweep(struct char_data *ch)
|
||
{
|
||
struct char_data *tch;
|
||
int total;
|
||
bool had_targets = FALSE;
|
||
bool found_any = FALSE;
|
||
|
||
if (ch == NULL || IN_ROOM(ch) == NOWHERE)
|
||
return FALSE;
|
||
if (!AFF_FLAGGED(ch, AFF_SCAN))
|
||
return FALSE;
|
||
if (!GET_SKILL(ch, SKILL_PERCEPTION))
|
||
return FALSE;
|
||
if (AFF_FLAGGED(ch, AFF_BLIND))
|
||
return FALSE;
|
||
if (IS_DARK(IN_ROOM(ch)) && !CAN_SEE_IN_DARK(ch))
|
||
return FALSE;
|
||
|
||
for (tch = world[IN_ROOM(ch)].people; tch; tch = tch->next_in_room) {
|
||
if (tch == ch)
|
||
continue;
|
||
if (!AFF_FLAGGED(tch, AFF_HIDE))
|
||
continue;
|
||
if (IS_NPC(tch))
|
||
continue;
|
||
had_targets = TRUE;
|
||
}
|
||
|
||
if (!had_targets)
|
||
return FALSE;
|
||
|
||
total = roll_skill_check(ch, SKILL_PERCEPTION, 0, NULL);
|
||
|
||
if (FIGHTING(ch))
|
||
total -= 4;
|
||
|
||
for (tch = world[IN_ROOM(ch)].people; tch; tch = tch->next_in_room) {
|
||
if (tch == ch)
|
||
continue;
|
||
if (!AFF_FLAGGED(tch, AFF_HIDE))
|
||
continue;
|
||
if (IS_NPC(tch))
|
||
continue;
|
||
|
||
if (total >= scan_target_dc(tch)) {
|
||
char hidden_ldesc[MAX_STRING_LENGTH];
|
||
if (build_hidden_ldesc(tch, hidden_ldesc, sizeof(hidden_ldesc)))
|
||
send_to_char(ch, "%s", hidden_ldesc);
|
||
else
|
||
send_to_char(ch, "A shadowy figure.\r\n");
|
||
remember_scan_target(ch, tch);
|
||
found_any = TRUE;
|
||
} else {
|
||
forget_scan_target(ch, tch);
|
||
}
|
||
}
|
||
|
||
gain_skill(ch, "perception", found_any ? TRUE : FALSE);
|
||
return found_any;
|
||
}
|
||
|
||
/* Scan: apply a perception-based buff that auto-checks rooms while it lasts */
|
||
ACMD(do_scan)
|
||
{
|
||
struct affected_type af;
|
||
|
||
if (!GET_SKILL(ch, SKILL_PERCEPTION)) {
|
||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (AFF_FLAGGED(ch, AFF_SCAN)) {
|
||
affect_from_char(ch, SKILL_PERCEPTION);
|
||
affect_from_char(ch, SPELL_SCAN_AFFECT);
|
||
send_to_char(ch, "You lower your guard and stop scanning the area.\r\n");
|
||
act("$n relaxes, no longer scanning so intently.", TRUE, ch, 0, 0, TO_ROOM);
|
||
return;
|
||
}
|
||
|
||
new_affect(&af);
|
||
af.spell = SPELL_SCAN_AFFECT;
|
||
af.location = APPLY_NONE;
|
||
af.modifier = 0;
|
||
af.duration = scan_effect_duration(ch);
|
||
memset(af.bitvector, 0, sizeof(af.bitvector));
|
||
SET_BIT_AR(af.bitvector, AFF_SCAN);
|
||
affect_to_char(ch, &af);
|
||
|
||
send_to_char(ch, "You sharpen your senses and begin scanning for hidden threats.\r\n");
|
||
act("$n studies $s surroundings with a wary gaze.", TRUE, ch, 0, 0, TO_ROOM);
|
||
|
||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||
GET_STAMINA(ch) -= 10;
|
||
}
|
||
|
||
ACMD(do_listen)
|
||
{
|
||
struct affected_type af;
|
||
|
||
if (!GET_SKILL(ch, SKILL_PERCEPTION)) {
|
||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (AFF_FLAGGED(ch, AFF_LISTEN)) {
|
||
affect_from_char(ch, SPELL_LISTEN_AFFECT);
|
||
send_to_char(ch, "You stop actively listening for hushed voices.\r\n");
|
||
return;
|
||
}
|
||
|
||
new_affect(&af);
|
||
af.spell = SPELL_LISTEN_AFFECT;
|
||
af.location = APPLY_NONE;
|
||
af.modifier = 0;
|
||
af.duration = listen_effect_duration(ch);
|
||
memset(af.bitvector, 0, sizeof(af.bitvector));
|
||
SET_BIT_AR(af.bitvector, AFF_LISTEN);
|
||
affect_to_char(ch, &af);
|
||
|
||
send_to_char(ch, "You focus entirely on every whisper and distant sound.\r\n");
|
||
|
||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||
GET_STAMINA(ch) -= 10;
|
||
}
|
||
|
||
ACMD(do_palm)
|
||
{
|
||
struct obj_data *container, *item;
|
||
char item_name[MAX_INPUT_LENGTH], cont_name[MAX_INPUT_LENGTH];
|
||
char item_desc[MAX_STRING_LENGTH], cont_desc[MAX_STRING_LENGTH];
|
||
int sleight_total;
|
||
bool base_fail;
|
||
|
||
if (!GET_SKILL(ch, SKILL_SLEIGHT_OF_HAND)) {
|
||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (ROOM_FLAGGED(IN_ROOM(ch), ROOM_PEACEFUL)) {
|
||
send_to_char(ch, "This room just has such a peaceful, easy feeling...\r\n");
|
||
return;
|
||
}
|
||
|
||
two_arguments(argument, item_name, cont_name);
|
||
|
||
if (!*item_name || !*cont_name) {
|
||
send_to_char(ch, "Usage: palm <item> <container>\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!(container = find_container_on_character(ch, ch, cont_name))) {
|
||
send_to_char(ch, "You aren't carrying or wearing anything like that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!obj_is_storage(container) && GET_OBJ_TYPE(container) != ITEM_FURNITURE) {
|
||
send_to_char(ch, "That's not even a container.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (obj_storage_is_closed(container)) {
|
||
send_to_char(ch, "You'd better open it first.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!(item = get_obj_in_list_vis(ch, item_name, NULL, container->contains))) {
|
||
send_to_char(ch, "You don't see that inside %s.\r\n", OBJS(container, ch));
|
||
return;
|
||
}
|
||
|
||
if (!sleight_can_take_obj(ch, item))
|
||
return;
|
||
|
||
strlcpy(item_desc, OBJS(item, ch), sizeof(item_desc));
|
||
strlcpy(cont_desc, OBJS(container, ch), sizeof(cont_desc));
|
||
|
||
sleight_total = roll_sleight_check(ch);
|
||
base_fail = (sleight_total < SLEIGHT_BASE_DC);
|
||
|
||
sleight_check_observers(ch, sleight_total,
|
||
"palm", "from", item_desc, cont_desc);
|
||
|
||
if (!get_otrigger(item, ch))
|
||
return;
|
||
|
||
int coin_amt = (GET_OBJ_TYPE(item) == ITEM_MONEY) ? GET_OBJ_VAL(item, 0) : 0;
|
||
|
||
obj_from_obj(item);
|
||
obj_to_char(item, ch);
|
||
if (!sleight_merge_money_pile(ch, item))
|
||
sleight_check_money(ch, item);
|
||
if (coin_amt == 1)
|
||
send_to_char(ch, "There was one coin.\r\n");
|
||
else if (coin_amt > 1)
|
||
send_to_char(ch, "There were %d coins.\r\n", coin_amt);
|
||
|
||
if (base_fail)
|
||
send_to_char(ch, "You get %s from your %s.\r\n", item_desc, cont_desc);
|
||
else
|
||
send_to_char(ch, "You quietly palm %s from your %s.\r\n", item_desc, cont_desc);
|
||
|
||
gain_skill(ch, "sleight of hand", base_fail ? FALSE : TRUE);
|
||
}
|
||
|
||
ACMD(do_slip)
|
||
{
|
||
struct obj_data *container, *obj;
|
||
char obj_name[MAX_INPUT_LENGTH], cont_name[MAX_INPUT_LENGTH], arg3[MAX_INPUT_LENGTH];
|
||
char item_desc[MAX_STRING_LENGTH], cont_desc[MAX_STRING_LENGTH];
|
||
int sleight_total;
|
||
bool base_fail;
|
||
int howmany = 1;
|
||
int amount_specified = 0;
|
||
|
||
if (!GET_SKILL(ch, SKILL_SLEIGHT_OF_HAND)) {
|
||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (ROOM_FLAGGED(IN_ROOM(ch), ROOM_PEACEFUL)) {
|
||
send_to_char(ch, "This room just has such a peaceful, easy feeling...\r\n");
|
||
return;
|
||
}
|
||
|
||
one_argument(two_arguments(argument, obj_name, cont_name), arg3);
|
||
|
||
if (*arg3 && is_number(obj_name)) {
|
||
howmany = atoi(obj_name);
|
||
strlcpy(obj_name, cont_name, sizeof(obj_name));
|
||
strlcpy(cont_name, arg3, sizeof(cont_name));
|
||
amount_specified = 1;
|
||
}
|
||
|
||
if (!*obj_name || !*cont_name) {
|
||
send_to_char(ch, "Usage: slip <item> <container>\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!(container = find_container_on_character(ch, ch, cont_name))) {
|
||
send_to_char(ch, "You aren't carrying or wearing anything like that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!obj_is_storage(container) && GET_OBJ_TYPE(container) != ITEM_FURNITURE) {
|
||
send_to_char(ch, "That's not even a container.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (obj_storage_is_closed(container)) {
|
||
send_to_char(ch, "You'd better open it first.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!(obj = get_obj_in_list_vis(ch, obj_name, NULL, ch->carrying))) {
|
||
send_to_char(ch, "You aren't even carrying that.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (amount_specified && GET_OBJ_TYPE(obj) == ITEM_MONEY && howmany > 0) {
|
||
int pile = GET_OBJ_VAL(obj, 0);
|
||
|
||
if (howmany < pile) {
|
||
struct obj_data *split = create_money(howmany);
|
||
if (!split) {
|
||
send_to_char(ch, "You fumble the coins.\r\n");
|
||
return;
|
||
}
|
||
GET_OBJ_VAL(obj, 0) = pile - howmany;
|
||
update_money_obj(obj);
|
||
GET_COINS(ch) = MAX(0, GET_COINS(ch) - howmany);
|
||
obj = split;
|
||
}
|
||
}
|
||
|
||
if (OBJ_FLAGGED(obj, ITEM_NODROP)) {
|
||
send_to_char(ch, "It refuses to leave your hands.\r\n");
|
||
return;
|
||
}
|
||
|
||
if ((GET_OBJ_VAL(container, 0) > 0) &&
|
||
(GET_OBJ_WEIGHT(container) + GET_OBJ_WEIGHT(obj) > GET_OBJ_VAL(container, 0))) {
|
||
act("$p won't fit inside $P.", FALSE, ch, obj, container, TO_CHAR);
|
||
return;
|
||
}
|
||
|
||
strlcpy(item_desc, OBJS(obj, ch), sizeof(item_desc));
|
||
strlcpy(cont_desc, OBJS(container, ch), sizeof(cont_desc));
|
||
|
||
sleight_total = roll_sleight_check(ch);
|
||
base_fail = (sleight_total < SLEIGHT_BASE_DC);
|
||
|
||
sleight_check_observers(ch, sleight_total,
|
||
"slip", "into", item_desc, cont_desc);
|
||
|
||
if (!drop_otrigger(obj, ch))
|
||
return;
|
||
|
||
if (obj->carried_by)
|
||
obj_from_char(obj);
|
||
else if (obj->in_obj)
|
||
obj_from_obj(obj);
|
||
else if (IN_ROOM(obj) != NOWHERE)
|
||
obj_from_room(obj);
|
||
obj_to_obj(obj, container);
|
||
|
||
if (base_fail)
|
||
send_to_char(ch, "You put %s in your %s.\r\n", item_desc, cont_desc);
|
||
else
|
||
send_to_char(ch, "You quietly slip %s into your %s.\r\n", item_desc, cont_desc);
|
||
|
||
gain_skill(ch, "sleight of hand", base_fail ? FALSE : TRUE);
|
||
}
|
||
|
||
ACMD(do_steal)
|
||
{
|
||
struct char_data *vict;
|
||
struct obj_data *obj;
|
||
char vict_name[MAX_INPUT_LENGTH], obj_name[MAX_INPUT_LENGTH];
|
||
int coins, eq_pos, ohoh = 0;
|
||
int sleight_total, dc;
|
||
|
||
if (!GET_SKILL(ch, SKILL_SLEIGHT_OF_HAND)) {
|
||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||
return;
|
||
}
|
||
if (ROOM_FLAGGED(IN_ROOM(ch), ROOM_PEACEFUL)) {
|
||
send_to_char(ch, "This room just has such a peaceful, easy feeling...\r\n");
|
||
return;
|
||
}
|
||
|
||
two_arguments(argument, obj_name, vict_name);
|
||
|
||
if (!(vict = get_char_vis(ch, vict_name, NULL, FIND_CHAR_ROOM))) {
|
||
send_to_char(ch, "Steal what from who?\r\n");
|
||
return;
|
||
} else if (vict == ch) {
|
||
send_to_char(ch, "Come on now, that's rather stupid!\r\n");
|
||
return;
|
||
}
|
||
|
||
if (GET_LEVEL(vict) >= LVL_IMMORT) {
|
||
send_to_char(ch, "You cannot steal from an immortal.\r\n");
|
||
return;
|
||
}
|
||
|
||
sleight_total = roll_sleight_check(ch);
|
||
|
||
if (str_cmp(obj_name, "coins") && str_cmp(obj_name, "coin")) {
|
||
|
||
if (!(obj = get_obj_in_list_vis(ch, obj_name, NULL, vict->carrying))) {
|
||
|
||
for (eq_pos = 0; eq_pos < NUM_WEARS; eq_pos++)
|
||
if (GET_EQ(vict, eq_pos) &&
|
||
(isname(obj_name, GET_EQ(vict, eq_pos)->name)) &&
|
||
CAN_SEE_OBJ(ch, GET_EQ(vict, eq_pos))) {
|
||
obj = GET_EQ(vict, eq_pos);
|
||
break;
|
||
}
|
||
if (!obj) {
|
||
act("$E hasn't got that item.", FALSE, ch, 0, vict, TO_CHAR);
|
||
return;
|
||
} else { /* It is equipment */
|
||
if ((GET_POS(vict) > POS_STUNNED)) {
|
||
send_to_char(ch, "Steal the equipment now? Impossible!\r\n");
|
||
return;
|
||
} else {
|
||
if (!give_otrigger(obj, vict, ch) ||
|
||
!receive_mtrigger(ch, vict, obj) ) {
|
||
send_to_char(ch, "Impossible!\r\n");
|
||
return;
|
||
}
|
||
act("You unequip $p and steal it.", FALSE, ch, obj, 0, TO_CHAR);
|
||
act("$n steals $p from $N.", FALSE, ch, obj, vict, TO_NOTVICT);
|
||
obj_to_char(unequip_char(vict, eq_pos), ch);
|
||
}
|
||
}
|
||
} else { /* obj found in inventory */
|
||
|
||
dc = compute_steal_dc(ch, vict, GET_OBJ_WEIGHT(obj));
|
||
|
||
if (GET_LEVEL(ch) < LVL_IMMORT && sleight_total < dc) {
|
||
ohoh = TRUE;
|
||
send_to_char(ch, "Oops..\r\n");
|
||
act("$n tried to steal something from you!", FALSE, ch, 0, vict, TO_VICT);
|
||
act("$n tries to steal something from $N.", TRUE, ch, 0, vict, TO_NOTVICT);
|
||
gain_skill(ch, "sleight of hand", FALSE);
|
||
} else { /* Steal the item */
|
||
if (IS_CARRYING_N(ch) + 1 < CAN_CARRY_N(ch)) {
|
||
if (!give_otrigger(obj, vict, ch) || !receive_mtrigger(ch, vict, obj) ) {
|
||
send_to_char(ch, "Impossible!\r\n");
|
||
return;
|
||
}
|
||
if (IS_CARRYING_W(ch) + GET_OBJ_WEIGHT(obj) < CAN_CARRY_W(ch)) {
|
||
obj_from_char(obj);
|
||
obj_to_char(obj, ch);
|
||
send_to_char(ch, "Got it!\r\n");
|
||
}
|
||
} else
|
||
send_to_char(ch, "You cannot carry that much.\r\n");
|
||
}
|
||
}
|
||
} else { /* Steal some coins */
|
||
dc = compute_steal_dc(ch, vict, 0);
|
||
if (GET_LEVEL(ch) < LVL_IMMORT && AWAKE(vict) && (sleight_total < dc)) {
|
||
ohoh = TRUE;
|
||
send_to_char(ch, "Oops..\r\n");
|
||
act("You discover that $n has $s hands in your wallet.", FALSE, ch, 0, vict, TO_VICT);
|
||
act("$n tries to steal coins from $N.", TRUE, ch, 0, vict, TO_NOTVICT);
|
||
gain_skill(ch, "sleight of hand", FALSE);
|
||
} else {
|
||
int total = count_coins_in_list(vict->carrying);
|
||
int max_steal = total / 10;
|
||
if (max_steal > 0) {
|
||
coins = rand_number(1, max_steal);
|
||
coins = remove_coins_from_list(vict->carrying, coins);
|
||
} else {
|
||
coins = 0;
|
||
}
|
||
|
||
if (coins > 0) {
|
||
GET_COINS(vict) = MAX(0, GET_COINS(vict) - coins);
|
||
add_coins_to_char(ch, coins);
|
||
gain_skill(ch, "sleight of hand", TRUE);
|
||
if (coins > 1)
|
||
send_to_char(ch, "Bingo! You got %d coins.\r\n", coins);
|
||
else
|
||
send_to_char(ch, "You manage to swipe a solitary coin.\r\n");
|
||
} else {
|
||
send_to_char(ch, "You couldn't get any coins...\r\n");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ohoh && IS_NPC(vict) && AWAKE(vict))
|
||
hit(vict, ch, TYPE_UNDEFINED);
|
||
}
|
||
|
||
ACMD(do_skills)
|
||
{
|
||
|
||
list_skills(ch);
|
||
|
||
}
|
||
|
||
ACMD(do_visible)
|
||
{
|
||
if (GET_LEVEL(ch) >= LVL_IMMORT) {
|
||
perform_immort_vis(ch);
|
||
return;
|
||
}
|
||
|
||
if AFF_FLAGGED(ch, AFF_INVISIBLE) {
|
||
appear(ch);
|
||
send_to_char(ch, "You break the spell of invisibility.\r\n");
|
||
} else
|
||
send_to_char(ch, "You are already visible.\r\n");
|
||
}
|
||
|
||
static void print_group(struct char_data *ch)
|
||
{
|
||
struct char_data * k;
|
||
|
||
send_to_char(ch, "Your group consists of:\r\n");
|
||
|
||
while ((k = (struct char_data *) simple_list(ch->group->members)) != NULL)
|
||
send_to_char(ch, "%-*s: %s[%4d/%-4d]H [%4d/%-4d]M [%4d/%-4d]V%s\r\n",
|
||
count_color_chars(GET_NAME(k))+22, GET_NAME(k),
|
||
GROUP_LEADER(GROUP(ch)) == k ? CBGRN(ch, C_NRM) : CCGRN(ch, C_NRM),
|
||
GET_HIT(k), GET_MAX_HIT(k),
|
||
GET_MANA(k), GET_MAX_MANA(k),
|
||
GET_STAMINA(k), GET_MAX_STAMINA(k),
|
||
CCNRM(ch, C_NRM));
|
||
}
|
||
|
||
static void display_group_list(struct char_data * ch)
|
||
{
|
||
struct group_data * group;
|
||
int count = 0;
|
||
|
||
if (group_list->iSize) {
|
||
send_to_char(ch, "# Group Leader # of Members In Zone\r\n"
|
||
"---------------------------------------------------\r\n");
|
||
|
||
while ((group = (struct group_data *) simple_list(group_list)) != NULL) {
|
||
if (IS_SET(GROUP_FLAGS(group), GROUP_NPC))
|
||
continue;
|
||
if (GROUP_LEADER(group) && !IS_SET(GROUP_FLAGS(group), GROUP_ANON))
|
||
send_to_char(ch, "%-2d) %s%-12s %-2d %s%s\r\n",
|
||
++count,
|
||
IS_SET(GROUP_FLAGS(group), GROUP_OPEN) ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM),
|
||
GET_NAME(GROUP_LEADER(group)), group->members->iSize, zone_table[world[IN_ROOM(GROUP_LEADER(group))].zone].name,
|
||
CCNRM(ch, C_NRM));
|
||
else
|
||
send_to_char(ch, "%-2d) Hidden\r\n", ++count);
|
||
|
||
}
|
||
}
|
||
if (count)
|
||
send_to_char(ch, "\r\n"
|
||
"%sSeeking Members%s\r\n"
|
||
"%sClosed%s\r\n",
|
||
CCGRN(ch, C_NRM), CCNRM(ch, C_NRM),
|
||
CCRED(ch, C_NRM), CCNRM(ch, C_NRM));
|
||
else
|
||
send_to_char(ch, "\r\n"
|
||
"Currently no groups formed.\r\n");
|
||
}
|
||
|
||
/* Vatiken's Group System: Version 1.1 */
|
||
ACMD(do_group)
|
||
{
|
||
char buf[MAX_STRING_LENGTH];
|
||
struct char_data *vict;
|
||
|
||
argument = one_argument(argument, buf);
|
||
|
||
if (!*buf) {
|
||
if (GROUP(ch))
|
||
print_group(ch);
|
||
else
|
||
send_to_char(ch, "You must specify a group option, or type HELP GROUP for more info.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (is_abbrev(buf, "new")) {
|
||
if (GROUP(ch))
|
||
send_to_char(ch, "You are already in a group.\r\n");
|
||
else
|
||
create_group(ch);
|
||
} else if (is_abbrev(buf, "list"))
|
||
display_group_list(ch);
|
||
else if (is_abbrev(buf, "join")) {
|
||
skip_spaces(&argument);
|
||
if (!(vict = get_char_vis(ch, argument, NULL, FIND_CHAR_ROOM))) {
|
||
send_to_char(ch, "Join who?\r\n");
|
||
return;
|
||
} else if (vict == ch) {
|
||
send_to_char(ch, "That would be one lonely grouping.\r\n");
|
||
return;
|
||
} else if (GROUP(ch)) {
|
||
send_to_char(ch, "But you are already part of a group.\r\n");
|
||
return;
|
||
} else if (!GROUP(vict)) {
|
||
act("$E$u is not part of a group!", FALSE, ch, 0, vict, TO_CHAR);
|
||
return;
|
||
} else if (!IS_SET(GROUP_FLAGS(GROUP(vict)), GROUP_OPEN)) {
|
||
send_to_char(ch, "That group isn't accepting members.\r\n");
|
||
return;
|
||
}
|
||
join_group(ch, GROUP(vict));
|
||
} else if (is_abbrev(buf, "kick")) {
|
||
skip_spaces(&argument);
|
||
if (!(vict = get_char_vis(ch, argument, NULL, FIND_CHAR_ROOM))) {
|
||
send_to_char(ch, "Kick out who?\r\n");
|
||
return;
|
||
} else if (vict == ch) {
|
||
send_to_char(ch, "There are easier ways to leave the group.\r\n");
|
||
return;
|
||
} else if (!GROUP(ch) ) {
|
||
send_to_char(ch, "But you are not part of a group.\r\n");
|
||
return;
|
||
} else if (GROUP_LEADER(GROUP(ch)) != ch ) {
|
||
send_to_char(ch, "Only the group's leader can kick members out.\r\n");
|
||
return;
|
||
} else if (GROUP(vict) != GROUP(ch)) {
|
||
act("$E$u is not a member of your group!", FALSE, ch, 0, vict, TO_CHAR);
|
||
return;
|
||
}
|
||
send_to_char(ch, "You have kicked %s out of the group.\r\n", GET_NAME(vict));
|
||
send_to_char(vict, "You have been kicked out of the group.\r\n");
|
||
leave_group(vict);
|
||
} else if (is_abbrev(buf, "regroup")) {
|
||
if (!GROUP(ch)) {
|
||
send_to_char(ch, "But you aren't part of a group!\r\n");
|
||
return;
|
||
}
|
||
vict = GROUP_LEADER(GROUP(ch));
|
||
if (ch == vict) {
|
||
send_to_char(ch, "You are the group leader and cannot re-group.\r\n");
|
||
} else {
|
||
leave_group(ch);
|
||
join_group(ch, GROUP(vict));
|
||
}
|
||
} else if (is_abbrev(buf, "leave")) {
|
||
|
||
if (!GROUP(ch)) {
|
||
send_to_char(ch, "But you aren't part of a group!\r\n");
|
||
return;
|
||
}
|
||
|
||
leave_group(ch);
|
||
} else if (is_abbrev(buf, "option")) {
|
||
skip_spaces(&argument);
|
||
if (!GROUP(ch)) {
|
||
send_to_char(ch, "But you aren't part of a group!\r\n");
|
||
return;
|
||
} else if (GROUP_LEADER(GROUP(ch)) != ch) {
|
||
send_to_char(ch, "Only the group leader can adjust the group flags.\r\n");
|
||
return;
|
||
}
|
||
if (is_abbrev(argument, "open")) {
|
||
TOGGLE_BIT(GROUP_FLAGS(GROUP(ch)), GROUP_OPEN);
|
||
send_to_char(ch, "The group is now %s to new members.\r\n", IS_SET(GROUP_FLAGS(GROUP(ch)), GROUP_OPEN) ? "open" : "closed");
|
||
} else if (is_abbrev(argument, "anonymous")) {
|
||
TOGGLE_BIT(GROUP_FLAGS(GROUP(ch)), GROUP_ANON);
|
||
send_to_char(ch, "The group location is now %s to other players.\r\n", IS_SET(GROUP_FLAGS(GROUP(ch)), GROUP_ANON) ? "invisible" : "visible");
|
||
} else
|
||
send_to_char(ch, "The flag options are: Open, Anonymous\r\n");
|
||
} else {
|
||
send_to_char(ch, "You must specify a group option, or type HELP GROUP for more info.\r\n");
|
||
}
|
||
|
||
}
|
||
|
||
ACMD(do_report)
|
||
{
|
||
struct group_data *group;
|
||
|
||
if ((group = GROUP(ch)) == NULL) {
|
||
send_to_char(ch, "But you are not a member of any group!\r\n");
|
||
return;
|
||
}
|
||
|
||
send_to_group(NULL, group, "%s reports: %d/%dH, %d/%dM, %d/%dV\r\n",
|
||
GET_NAME(ch),
|
||
GET_HIT(ch), GET_MAX_HIT(ch),
|
||
GET_MANA(ch), GET_MAX_MANA(ch),
|
||
GET_STAMINA(ch), GET_MAX_STAMINA(ch));
|
||
}
|
||
|
||
ACMD(do_split)
|
||
{
|
||
char buf[MAX_INPUT_LENGTH];
|
||
int amount, num = 0, share, rest;
|
||
size_t len;
|
||
struct char_data *k;
|
||
|
||
if (IS_NPC(ch))
|
||
return;
|
||
|
||
one_argument(argument, buf);
|
||
|
||
if (is_number(buf)) {
|
||
amount = atoi(buf);
|
||
if (amount <= 0) {
|
||
send_to_char(ch, "Sorry, you can't do that.\r\n");
|
||
return;
|
||
}
|
||
if (amount > GET_COINS(ch)) {
|
||
send_to_char(ch, "You don't seem to have that many coins to split.\r\n");
|
||
return;
|
||
}
|
||
|
||
if (GROUP(ch))
|
||
while ((k = (struct char_data *) simple_list(GROUP(ch)->members)) != NULL)
|
||
if (IN_ROOM(ch) == IN_ROOM(k) && !IS_NPC(k))
|
||
num++;
|
||
|
||
if (num && GROUP(ch)) {
|
||
share = amount / num;
|
||
rest = amount % num;
|
||
} else {
|
||
send_to_char(ch, "With whom do you wish to share your coins?\r\n");
|
||
return;
|
||
}
|
||
|
||
decrease_coins(ch, share * (num - 1));
|
||
|
||
/* Abusing signed/unsigned to make sizeof work. */
|
||
len = snprintf(buf, sizeof(buf), "%s splits %d coins; you receive %d.\r\n",
|
||
GET_NAME(ch), amount, share);
|
||
if (rest && len < sizeof(buf)) {
|
||
snprintf(buf + len, sizeof(buf) - len,
|
||
"%d coin%s %s not splitable, so %s keeps the coins.\r\n", rest,
|
||
(rest == 1) ? "" : "s", (rest == 1) ? "was" : "were", GET_NAME(ch));
|
||
}
|
||
|
||
while ((k = (struct char_data *) simple_list(GROUP(ch)->members)) != NULL)
|
||
if (k != ch && IN_ROOM(ch) == IN_ROOM(k) && !IS_NPC(k)) {
|
||
increase_coins(k, share);
|
||
send_to_char(k, "%s", buf);
|
||
}
|
||
|
||
send_to_char(ch, "You split %d coins among %d members -- %d coins each.\r\n",
|
||
amount, num, share);
|
||
|
||
if (rest) {
|
||
send_to_char(ch, "%d coin%s %s not splitable, so you keep the coins.\r\n",
|
||
rest, (rest == 1) ? "" : "s", (rest == 1) ? "was" : "were");
|
||
}
|
||
} else {
|
||
send_to_char(ch, "How many coins do you wish to split with your group?\r\n");
|
||
return;
|
||
}
|
||
}
|
||
|
||
ACMD(do_use)
|
||
{
|
||
char buf[MAX_INPUT_LENGTH], arg[MAX_INPUT_LENGTH];
|
||
struct obj_data *mag_item;
|
||
|
||
half_chop(argument, arg, buf);
|
||
if (!*arg) {
|
||
send_to_char(ch, "What do you want to %s?\r\n", CMD_NAME);
|
||
return;
|
||
}
|
||
mag_item = GET_EQ(ch, WEAR_HOLD);
|
||
|
||
if (!mag_item || !isname(arg, mag_item->name)) {
|
||
switch (subcmd) {
|
||
case SCMD_RECITE:
|
||
case SCMD_QUAFF:
|
||
if (!(mag_item = get_obj_in_list_vis(ch, arg, NULL, ch->carrying))) {
|
||
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg), arg);
|
||
return;
|
||
}
|
||
break;
|
||
case SCMD_USE:
|
||
send_to_char(ch, "You don't seem to be holding %s %s.\r\n", AN(arg), arg);
|
||
return;
|
||
default:
|
||
log("SYSERR: Unknown subcmd %d passed to do_use.", subcmd);
|
||
/* SYSERR_DESC: This is the same as the unhandled case in do_gen_ps(),
|
||
* but in the function which handles 'quaff', 'recite', and 'use'. */
|
||
return;
|
||
}
|
||
}
|
||
switch (subcmd) {
|
||
case SCMD_QUAFF:
|
||
if (GET_OBJ_TYPE(mag_item) != ITEM_POTION) {
|
||
send_to_char(ch, "You can only quaff potions.\r\n");
|
||
return;
|
||
}
|
||
break;
|
||
case SCMD_RECITE:
|
||
if (GET_OBJ_TYPE(mag_item) != ITEM_SCROLL) {
|
||
send_to_char(ch, "You can only recite scrolls.\r\n");
|
||
return;
|
||
}
|
||
break;
|
||
case SCMD_USE:
|
||
if ((GET_OBJ_TYPE(mag_item) != ITEM_WAND) &&
|
||
(GET_OBJ_TYPE(mag_item) != ITEM_STAFF)) {
|
||
send_to_char(ch, "You can't seem to figure out how to use it.\r\n");
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
|
||
mag_objectmagic(ch, mag_item, buf);
|
||
}
|
||
|
||
ACMD(do_display)
|
||
{
|
||
size_t i;
|
||
|
||
if (IS_NPC(ch)) {
|
||
send_to_char(ch, "Monsters don't need displays. Go away.\r\n");
|
||
return;
|
||
}
|
||
skip_spaces(&argument);
|
||
|
||
if (!*argument) {
|
||
send_to_char(ch, "Usage: prompt { { H | M | S } | all | auto | none }\r\n");
|
||
return;
|
||
}
|
||
|
||
if (!str_cmp(argument, "auto")) {
|
||
TOGGLE_BIT_AR(PRF_FLAGS(ch), PRF_DISPAUTO);
|
||
send_to_char(ch, "Auto prompt %sabled.\r\n", PRF_FLAGGED(ch, PRF_DISPAUTO) ? "en" : "dis");
|
||
return;
|
||
}
|
||
|
||
if (!str_cmp(argument, "on") || !str_cmp(argument, "all")) {
|
||
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
|
||
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
|
||
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
|
||
} else if (!str_cmp(argument, "off") || !str_cmp(argument, "none")) {
|
||
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
|
||
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
|
||
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
|
||
} else {
|
||
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
|
||
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
|
||
REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
|
||
|
||
for (i = 0; i < strlen(argument); i++) {
|
||
switch (LOWER(argument[i])) {
|
||
case 'h':
|
||
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPHP);
|
||
break;
|
||
case 'm':
|
||
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPMANA);
|
||
break;
|
||
case 's':
|
||
case 'v':
|
||
SET_BIT_AR(PRF_FLAGS(ch), PRF_DISPSTAMINA);
|
||
break;
|
||
default:
|
||
send_to_char(ch, "Usage: prompt { { H | M | S } | all | auto | none }\r\n");
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
send_to_char(ch, "%s", CONFIG_OK);
|
||
}
|
||
|
||
#define TOG_OFF 0
|
||
#define TOG_ON 1
|
||
ACMD(do_gen_tog)
|
||
{
|
||
long result;
|
||
int i;
|
||
char arg[MAX_INPUT_LENGTH];
|
||
|
||
const char *tog_messages[][2] = {
|
||
{"You are now safe from summoning by other players.\r\n",
|
||
"You may now be summoned by other players.\r\n"},
|
||
{"Nohassle disabled.\r\n",
|
||
"Nohassle enabled.\r\n"},
|
||
{"Brief mode off.\r\n",
|
||
"Brief mode on.\r\n"},
|
||
{"Compact mode off.\r\n",
|
||
"Compact mode on.\r\n"},
|
||
{"You can now hear shouts.\r\n",
|
||
"You are now deaf to shouts.\r\n"},
|
||
{"You can now hear the Wiz-channel.\r\n",
|
||
"You are now deaf to the Wiz-channel.\r\n"},
|
||
{"You are no longer part of the Quest.\r\n",
|
||
"Okay, you are part of the Quest!\r\n"},
|
||
{"You will no longer see the room flags.\r\n",
|
||
"You will now see the room flags.\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"},
|
||
{"Nameserver_is_slow changed to NO; IP addresses will now be resolved.\r\n",
|
||
"Nameserver_is_slow changed to YES; sitenames will no longer be resolved.\r\n"},
|
||
{"Autoexits disabled.\r\n",
|
||
"Autoexits enabled.\r\n"},
|
||
{"Will no longer track through doors.\r\n",
|
||
"Will now track through doors.\r\n"},
|
||
{"Will no longer clear screen in OLC.\r\n",
|
||
"Will now clear screen in OLC.\r\n"},
|
||
{"Buildwalk Off.\r\n",
|
||
"Buildwalk On.\r\n"},
|
||
{"AFK flag is now off.\r\n",
|
||
"AFK flag is now on.\r\n"},
|
||
{"Autoloot disabled.\r\n",
|
||
"Autoloot enabled.\r\n"},
|
||
{"Autosplit disabled.\r\n",
|
||
"Autosplit enabled.\r\n"},
|
||
{"Autoassist disabled.\r\n",
|
||
"Autoassist enabled.\r\n"},
|
||
{"Automap disabled.\r\n",
|
||
"Automap enabled.\r\n"},
|
||
{"Autokey disabled.\r\n",
|
||
"Autokey enabled.\r\n"},
|
||
{"Autodoor disabled.\r\n",
|
||
"Autodoor enabled.\r\n"},
|
||
{"ZoneResets disabled.\r\n",
|
||
"ZoneResets enabled.\r\n"}
|
||
};
|
||
|
||
if (IS_NPC(ch))
|
||
return;
|
||
|
||
switch (subcmd) {
|
||
case SCMD_NOSUMMON:
|
||
result = PRF_TOG_CHK(ch, PRF_SUMMONABLE);
|
||
break;
|
||
case SCMD_NOHASSLE:
|
||
result = PRF_TOG_CHK(ch, PRF_NOHASSLE);
|
||
break;
|
||
case SCMD_BRIEF:
|
||
result = PRF_TOG_CHK(ch, PRF_BRIEF);
|
||
break;
|
||
case SCMD_COMPACT:
|
||
result = PRF_TOG_CHK(ch, PRF_COMPACT);
|
||
break;
|
||
case SCMD_NOSHOUT:
|
||
result = PRF_TOG_CHK(ch, PRF_NOSHOUT);
|
||
break;
|
||
case SCMD_NOWIZ:
|
||
result = PRF_TOG_CHK(ch, PRF_NOWIZ);
|
||
break;
|
||
case SCMD_QUEST:
|
||
result = PRF_TOG_CHK(ch, PRF_QUEST);
|
||
break;
|
||
case SCMD_SHOWVNUMS:
|
||
result = PRF_TOG_CHK(ch, PRF_SHOWVNUMS);
|
||
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_AUTOEXIT:
|
||
result = PRF_TOG_CHK(ch, PRF_AUTOEXIT);
|
||
break;
|
||
case SCMD_CLS:
|
||
result = PRF_TOG_CHK(ch, PRF_CLS);
|
||
break;
|
||
case SCMD_BUILDWALK:
|
||
if (GET_LEVEL(ch) < LVL_BUILDER) {
|
||
send_to_char(ch, "Builders only, sorry.\r\n");
|
||
return;
|
||
}
|
||
result = PRF_TOG_CHK(ch, PRF_BUILDWALK);
|
||
if (PRF_FLAGGED(ch, PRF_BUILDWALK)) {
|
||
one_argument(argument, arg);
|
||
for (i=0; *arg && *(sector_types[i]) != '\n'; i++)
|
||
if (is_abbrev(arg, sector_types[i]))
|
||
break;
|
||
if (*(sector_types[i]) == '\n')
|
||
i=0;
|
||
GET_BUILDWALK_SECTOR(ch) = i;
|
||
send_to_char(ch, "Default sector type is %s\r\n", sector_types[i]);
|
||
|
||
mudlog(CMP, GET_LEVEL(ch), TRUE,
|
||
"OLC: %s turned buildwalk on. Allowed zone %d", GET_NAME(ch), GET_OLC_ZONE(ch));
|
||
} else
|
||
mudlog(CMP, GET_LEVEL(ch), TRUE,
|
||
"OLC: %s turned buildwalk off. Allowed zone %d", GET_NAME(ch), GET_OLC_ZONE(ch));
|
||
break;
|
||
case SCMD_AFK:
|
||
result = PRF_TOG_CHK(ch, PRF_AFK);
|
||
if (PRF_FLAGGED(ch, PRF_AFK))
|
||
act("$n has gone AFK.", TRUE, ch, 0, 0, TO_ROOM);
|
||
else {
|
||
act("$n has come back from AFK.", TRUE, ch, 0, 0, TO_ROOM);
|
||
if (has_mail(GET_IDNUM(ch)))
|
||
send_to_char(ch, "You have mail waiting.\r\n");
|
||
}
|
||
break;
|
||
case SCMD_AUTOLOOT:
|
||
result = PRF_TOG_CHK(ch, PRF_AUTOLOOT);
|
||
break;
|
||
case SCMD_AUTOSPLIT:
|
||
result = PRF_TOG_CHK(ch, PRF_AUTOSPLIT);
|
||
break;
|
||
case SCMD_AUTOASSIST:
|
||
result = PRF_TOG_CHK(ch, PRF_AUTOASSIST);
|
||
break;
|
||
case SCMD_AUTOMAP:
|
||
result = PRF_TOG_CHK(ch, PRF_AUTOMAP);
|
||
break;
|
||
case SCMD_AUTOKEY:
|
||
result = PRF_TOG_CHK(ch, PRF_AUTOKEY);
|
||
break;
|
||
case SCMD_AUTODOOR:
|
||
result = PRF_TOG_CHK(ch, PRF_AUTODOOR);
|
||
break;
|
||
case SCMD_ZONERESETS:
|
||
result = PRF_TOG_CHK(ch, PRF_ZONERESETS);
|
||
break;
|
||
default:
|
||
log("SYSERR: Unknown subcmd %d in do_gen_toggle.", subcmd);
|
||
return;
|
||
}
|
||
|
||
if (result)
|
||
send_to_char(ch, "%s", tog_messages[subcmd][TOG_ON]);
|
||
else
|
||
send_to_char(ch, "%s", tog_messages[subcmd][TOG_OFF]);
|
||
|
||
return;
|
||
}
|