mirror of
https://github.com/tbamud/tbamud.git
synced 2026-03-20 03:06:32 +01:00
Stealth skill update
This commit is contained in:
parent
d7aec2e6c2
commit
06d581b011
10 changed files with 194 additions and 67 deletions
|
|
@ -242,6 +242,7 @@ bool perform_scan_sweep(struct char_data *ch);
|
|||
void clear_scan_results(struct char_data *ch);
|
||||
bool scan_can_target(struct char_data *ch, struct char_data *tch);
|
||||
bool scan_confirm_target(struct char_data *ch, struct char_data *tch);
|
||||
void stealth_process_room_movement(struct char_data *ch, room_rnum room, int dir, bool leaving);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
|
|||
|
|
@ -412,12 +412,12 @@ static void list_one_char(struct char_data *i, struct char_data *ch)
|
|||
|
||||
/* Otherwise, use short description (PC or NPC) if present, else name. */
|
||||
{
|
||||
const char *sdesc = GET_SHORT_DESC(i);
|
||||
const char *sdesc = get_char_sdesc(i);
|
||||
if (sdesc && *sdesc) {
|
||||
/* Capitalize first letter for room list */
|
||||
send_to_char(ch, "%c%s", UPPER(*sdesc), sdesc + 1);
|
||||
} else {
|
||||
send_to_char(ch, "%s", GET_NAME(i));
|
||||
send_to_char(ch, "Someone");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -274,8 +274,9 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
|
|||
GET_MOVE(ch) -= need_movement;
|
||||
|
||||
/* Generate the leave message and display to others in the was_in room. */
|
||||
if (!AFF_FLAGGED(ch, AFF_SNEAK))
|
||||
{
|
||||
if (AFF_FLAGGED(ch, AFF_SNEAK)) {
|
||||
stealth_process_room_movement(ch, was_in, dir, TRUE);
|
||||
} else {
|
||||
snprintf(leave_message, sizeof(leave_message), "$n leaves %s.", dirs[dir]);
|
||||
act(leave_message, TRUE, ch, 0, 0, TO_ROOM);
|
||||
}
|
||||
|
|
@ -299,7 +300,9 @@ int do_simple_move(struct char_data *ch, int dir, int need_specials_check)
|
|||
}
|
||||
|
||||
/* Display arrival information to anyone in the destination room... */
|
||||
if (!AFF_FLAGGED(ch, AFF_SNEAK))
|
||||
if (AFF_FLAGGED(ch, AFF_SNEAK))
|
||||
stealth_process_room_movement(ch, going_to, dir, FALSE);
|
||||
else
|
||||
act("$n has arrived.", TRUE, ch, 0, 0, TO_ROOM);
|
||||
|
||||
/* ... and the room description to the character. */
|
||||
|
|
|
|||
191
src/act.other.c
191
src/act.other.c
|
|
@ -189,13 +189,74 @@ ACMD(do_not_here)
|
|||
send_to_char(ch, "Sorry, but you cannot do that here!\r\n");
|
||||
}
|
||||
|
||||
#define STEALTH_BASE_DC 10
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static int roll_stealth_check(struct char_data *ch)
|
||||
{
|
||||
int skill = get_stealth_skill_value(ch);
|
||||
int bonus = GET_ABILITY_MOD(GET_DEX(ch)) + GET_PROFICIENCY(skill);
|
||||
bool disadv = has_stealth_disadv(ch) ? TRUE : FALSE;
|
||||
int rolla = rand_number(1, 20);
|
||||
int rollb = rand_number(1, 20);
|
||||
int roll = disadv ? MIN(rolla, rollb) : rolla;
|
||||
|
||||
return roll + bonus;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static int roll_scan_perception(struct char_data *ch)
|
||||
{
|
||||
int bonus = GET_ABILITY_MOD(GET_WIS(ch)) +
|
||||
GET_PROFICIENCY(GET_SKILL(ch, SKILL_PERCEPTION));
|
||||
int total = rand_number(1, 20) + bonus;
|
||||
|
||||
if (FIGHTING(ch))
|
||||
total -= 4;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
ACMD(do_sneak)
|
||||
{
|
||||
struct affected_type af;
|
||||
int rolla, rollb, roll, bonus, total, dc;
|
||||
bool disadv = FALSE;
|
||||
int total, dc;
|
||||
int stealth_skill = get_stealth_skill_value(ch);
|
||||
|
||||
if (!GET_SKILL(ch, SKILL_SNEAK)) {
|
||||
if (!stealth_skill) {
|
||||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -212,25 +273,11 @@ ACMD(do_sneak)
|
|||
affect_from_char(ch, SKILL_SNEAK);
|
||||
|
||||
/* --- 5e-style Stealth check (DEX + proficiency) --- */
|
||||
bonus = GET_ABILITY_MOD(GET_DEX(ch)) +
|
||||
GET_PROFICIENCY(GET_SKILL(ch, SKILL_SNEAK));
|
||||
|
||||
dc = 10;
|
||||
|
||||
disadv = has_stealth_disadv(ch) ? TRUE : FALSE;
|
||||
|
||||
rolla = rand_number(1, 20);
|
||||
if (disadv) {
|
||||
rollb = rand_number(1, 20);
|
||||
roll = MIN(rolla, rollb); /* disadvantage: take lower roll */
|
||||
} else {
|
||||
roll = rolla;
|
||||
}
|
||||
|
||||
total = roll + bonus;
|
||||
total = roll_stealth_check(ch);
|
||||
dc = STEALTH_BASE_DC;
|
||||
|
||||
if (total < dc) {
|
||||
gain_skill(ch, "sneak", FALSE);
|
||||
gain_skill(ch, "stealth", FALSE);
|
||||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||||
GET_MOVE(ch) -= 10;
|
||||
return;
|
||||
|
|
@ -241,7 +288,7 @@ ACMD(do_sneak)
|
|||
af.spell = SKILL_SNEAK;
|
||||
af.location = APPLY_NONE;
|
||||
af.modifier = 0;
|
||||
af.duration = GET_LEVEL(ch); /* keep stock duration; adjust if desired */
|
||||
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);
|
||||
|
|
@ -250,16 +297,16 @@ ACMD(do_sneak)
|
|||
/* 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, "sneak", TRUE);
|
||||
gain_skill(ch, "stealth", TRUE);
|
||||
GET_MOVE(ch) -= 10;
|
||||
}
|
||||
|
||||
ACMD(do_hide)
|
||||
{
|
||||
int rolla, rollb, roll, bonus, total, dc;
|
||||
bool disadv = FALSE;
|
||||
int total, dc;
|
||||
int stealth_skill = get_stealth_skill_value(ch);
|
||||
|
||||
if (!GET_SKILL(ch, SKILL_HIDE)) {
|
||||
if (!stealth_skill) {
|
||||
send_to_char(ch, "You have no idea how to do that.\r\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -278,28 +325,14 @@ ACMD(do_hide)
|
|||
} /* you can't hide while in active melee */
|
||||
|
||||
/* --- 5e Stealth (DEX) ability check --- */
|
||||
bonus = GET_ABILITY_MOD(GET_DEX(ch)) + GET_PROFICIENCY(GET_SKILL(ch, SKILL_HIDE));
|
||||
|
||||
/* Baseline difficulty: hiding in general */
|
||||
/* TODO: Maybe change dc based on terrain/populated rooms in the future */
|
||||
dc = 10;
|
||||
|
||||
/* Armor/gear can impose disadvantage */
|
||||
disadv = has_stealth_disadv(ch) ? TRUE : FALSE;
|
||||
|
||||
rolla = rand_number(1, 20);
|
||||
if (disadv) {
|
||||
rollb = rand_number(1, 20);
|
||||
roll = MIN(rolla, rollb); /* disadvantage: take the lower */
|
||||
} else {
|
||||
roll = rolla;
|
||||
}
|
||||
|
||||
total = roll + bonus;
|
||||
dc = STEALTH_BASE_DC;
|
||||
total = roll_stealth_check(ch);
|
||||
|
||||
if (total < dc) {
|
||||
/* Failure */
|
||||
gain_skill(ch, "hide", FALSE);
|
||||
gain_skill(ch, "stealth", FALSE);
|
||||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||||
GET_MOVE(ch) -= 10;
|
||||
return;
|
||||
|
|
@ -310,7 +343,7 @@ ACMD(do_hide)
|
|||
GET_STEALTH_CHECK(ch) = total;
|
||||
|
||||
send_to_char(ch, "You hide yourself as best you can.\r\n");
|
||||
gain_skill(ch, "hide", TRUE);
|
||||
gain_skill(ch, "stealth", TRUE);
|
||||
WAIT_STATE(ch, PULSE_VIOLENCE / 2);
|
||||
GET_MOVE(ch) -= 10;
|
||||
}
|
||||
|
|
@ -365,6 +398,78 @@ static void forget_scan_target(struct char_data *ch, struct char_data *tch)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
12
src/class.c
12
src/class.c
|
|
@ -349,8 +349,7 @@ void grant_class_skills(struct char_data *ch, bool reset)
|
|||
break;
|
||||
|
||||
case CLASS_ROGUE:
|
||||
SET_SKILL(ch, SKILL_SNEAK, 5);
|
||||
SET_SKILL(ch, SKILL_HIDE, 5);
|
||||
SET_SKILL(ch, SKILL_STEALTH, 5);
|
||||
SET_SKILL(ch, SKILL_TRACK, 5);
|
||||
SET_SKILL(ch, SKILL_STEAL, 5);
|
||||
SET_SKILL(ch, SKILL_BACKSTAB, 5);
|
||||
|
|
@ -386,8 +385,7 @@ void grant_class_skills(struct char_data *ch, bool reset)
|
|||
break;
|
||||
|
||||
case CLASS_RANGER:
|
||||
SET_SKILL(ch, SKILL_SNEAK, 5);
|
||||
SET_SKILL(ch, SKILL_HIDE, 5);
|
||||
SET_SKILL(ch, SKILL_STEALTH, 5);
|
||||
SET_SKILL(ch, SKILL_BANDAGE, 5);
|
||||
SET_SKILL(ch, SKILL_TRACK, 5);
|
||||
SET_SKILL(ch, SKILL_BASH, 5);
|
||||
|
|
@ -642,16 +640,15 @@ void init_spell_levels(void)
|
|||
spell_level(SKILL_SHIELD_USE, CLASS_CLERIC, 1);
|
||||
|
||||
/* ROGUES */
|
||||
spell_level(SKILL_SNEAK, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_PICK_LOCK, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_BACKSTAB, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_STEAL, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_HIDE, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_TRACK, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_UNARMED, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_PIERCING_WEAPONS, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_SHIELD_USE, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_PERCEPTION, CLASS_ROGUE, 1);
|
||||
spell_level(SKILL_STEALTH, CLASS_ROGUE, 1);
|
||||
|
||||
/* FIGHTERS */
|
||||
spell_level(SKILL_KICK, CLASS_FIGHTER, 1);
|
||||
|
|
@ -676,8 +673,6 @@ void init_spell_levels(void)
|
|||
spell_level(SKILL_PERCEPTION, CLASS_BARBARIAN, 1);
|
||||
|
||||
/* RANGERS */
|
||||
spell_level(SKILL_SNEAK, CLASS_RANGER, 1);
|
||||
spell_level(SKILL_HIDE, CLASS_RANGER, 1);
|
||||
spell_level(SKILL_BANDAGE, CLASS_RANGER, 1);
|
||||
spell_level(SKILL_TRACK, CLASS_RANGER, 1);
|
||||
spell_level(SKILL_BASH, CLASS_RANGER, 1);
|
||||
|
|
@ -686,6 +681,7 @@ void init_spell_levels(void)
|
|||
spell_level(SKILL_PIERCING_WEAPONS, CLASS_RANGER, 1);
|
||||
spell_level(SKILL_SHIELD_USE, CLASS_RANGER, 1);
|
||||
spell_level(SKILL_PERCEPTION, CLASS_RANGER, 1);
|
||||
spell_level(SKILL_STEALTH, CLASS_RANGER, 1);
|
||||
|
||||
/* BARDS */
|
||||
spell_level(SPELL_ARMOR, CLASS_BARD, 1);
|
||||
|
|
|
|||
|
|
@ -442,10 +442,22 @@ int load_char(const char *name, struct char_data *ch)
|
|||
else if (!strcmp(tag, "ScrW")) GET_SCREEN_WIDTH(ch) = atoi(line);
|
||||
else if (!strcmp(tag, "Skil")) load_skills(fl, ch);
|
||||
else if (!strcmp(tag, "SkGt")) { /* Skill Gain Timers */
|
||||
char *p = line;
|
||||
for (int i = 1; i <= MAX_SKILLS; i++) {
|
||||
long t = 0;
|
||||
if (fscanf(fl, " %ld", &t) != 1)
|
||||
t = 0;
|
||||
|
||||
while (*p && isspace((unsigned char)*p))
|
||||
++p;
|
||||
|
||||
if (*p) {
|
||||
char *endptr = p;
|
||||
t = strtol(p, &endptr, 10);
|
||||
if (endptr == p)
|
||||
t = 0;
|
||||
else
|
||||
p = endptr;
|
||||
}
|
||||
|
||||
GET_SKILL_NEXT_GAIN(ch, i) = (time_t)t;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -935,5 +935,5 @@ void mag_assign_spells(void) {
|
|||
skillo(SKILL_SLASHING_WEAPONS, "slashing weapons");
|
||||
skillo(SKILL_BLUDGEONING_WEAPONS, "bludgeoning weapons");
|
||||
skillo(SKILL_PERCEPTION, "perception");
|
||||
skillo(SKILL_STEALTH, "stealth");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@
|
|||
#define SKILL_SLASHING_WEAPONS 145 /* Reserved Skill[] DO NOT CHANGE */
|
||||
#define SKILL_BLUDGEONING_WEAPONS 146 /* Reserved Skill[] DO NOT CHANGE */
|
||||
#define SKILL_PERCEPTION 147 /* Reserved Skill[] DO NOT CHANGE */
|
||||
#define SKILL_STEALTH 148 /* Shared stealth skill for hide/sneak */
|
||||
|
||||
/* New skills may be added here up to MAX_SKILLS (200) */
|
||||
|
||||
|
|
|
|||
15
src/utils.c
15
src/utils.c
|
|
@ -1642,7 +1642,20 @@ void remove_from_string(char *string, const char *to_remove)
|
|||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const char *get_char_sdesc(const struct char_data *ch)
|
||||
{
|
||||
if (!ch)
|
||||
return "someone";
|
||||
|
||||
if (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch))
|
||||
return GET_SHORT_DESC(ch);
|
||||
|
||||
if (GET_NAME(ch))
|
||||
return GET_NAME(ch);
|
||||
|
||||
return "someone";
|
||||
}
|
||||
|
||||
/* 5e system helpers */
|
||||
|
|
|
|||
10
src/utils.h
10
src/utils.h
|
|
@ -79,6 +79,7 @@ int count_non_protocol_chars(char * str);
|
|||
char *right_trim_whitespace(const char *string);
|
||||
void remove_from_string(char *string, const char *to_remove);
|
||||
const char *const *obj_value_labels(int item_type);
|
||||
const char *get_char_sdesc(const struct char_data *ch);
|
||||
|
||||
/* 5e system helpers */
|
||||
|
||||
|
|
@ -892,15 +893,10 @@ do \
|
|||
|
||||
/* Display name for a character as seen by 'vict'.
|
||||
* - If vict can’t see ch: "someone"
|
||||
* - If NPC: use short_descr if set, else personal name
|
||||
* - If PC: use short_descr if set, else personal name
|
||||
* - Otherwise: prefer short_descr, fall back to NPC name or a generic label
|
||||
*/
|
||||
#define PERS(ch, vict) \
|
||||
(CAN_SEE((vict), (ch)) ? \
|
||||
((GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) ? \
|
||||
GET_SHORT_DESC(ch) : \
|
||||
GET_NAME(ch)) : \
|
||||
"someone")
|
||||
(CAN_SEE((vict), (ch)) ? get_char_sdesc(ch) : "someone")
|
||||
|
||||
/** If vict can see obj, return obj short description, else return
|
||||
* "something". */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue