mirror of
https://github.com/tbamud/tbamud.git
synced 2026-03-27 22:56:34 +01:00
Fix saving throw bonus logic
This commit is contained in:
parent
6a743b5276
commit
ff5f254fce
7 changed files with 109 additions and 36 deletions
|
|
@ -1274,14 +1274,15 @@ static void do_stat_character(struct char_data *ch, struct char_data *k)
|
|||
CCCYN(ch, C_NRM), GET_DEX(k), CCNRM(ch, C_NRM),
|
||||
CCCYN(ch, C_NRM), GET_CON(k), CCNRM(ch, C_NRM),
|
||||
CCCYN(ch, C_NRM), GET_CHA(k), CCNRM(ch, C_NRM));
|
||||
send_to_char(ch, "Saving Throws: Str: [%s%d%s] Int: [%s%d%s] Wis: [%s%d%s] "
|
||||
"Dex: [%s%d%s] Con: [%s%d%s] Cha: [%s%d%s]\r\n",
|
||||
CCCYN(ch, C_NRM), GET_SAVE(k, ABIL_STR), CCNRM(ch, C_NRM),
|
||||
CCCYN(ch, C_NRM), GET_SAVE(k, ABIL_DEX), CCNRM(ch, C_NRM),
|
||||
CCCYN(ch, C_NRM), GET_SAVE(k, ABIL_CON), CCNRM(ch, C_NRM),
|
||||
CCCYN(ch, C_NRM), GET_SAVE(k, ABIL_INT), CCNRM(ch, C_NRM),
|
||||
CCCYN(ch, C_NRM), GET_SAVE(k, ABIL_WIS), CCNRM(ch, C_NRM),
|
||||
CCCYN(ch, C_NRM), GET_SAVE(k, ABIL_CHA), CCNRM(ch, C_NRM));
|
||||
send_to_char(ch, "Saving Throws: Str: [%s%+d%s (%+d)] Dex: [%s%+d%s (%+d)] "
|
||||
"Con: [%s%+d%s (%+d)] Int: [%s%+d%s (%+d)] Wis: [%s%+d%s (%+d)] "
|
||||
"Cha: [%s%+d%s (%+d)]\r\n",
|
||||
CCCYN(ch, C_NRM), get_save_mod(k, ABIL_STR), CCNRM(ch, C_NRM), GET_SAVE(k, ABIL_STR),
|
||||
CCCYN(ch, C_NRM), get_save_mod(k, ABIL_DEX), CCNRM(ch, C_NRM), GET_SAVE(k, ABIL_DEX),
|
||||
CCCYN(ch, C_NRM), get_save_mod(k, ABIL_CON), CCNRM(ch, C_NRM), GET_SAVE(k, ABIL_CON),
|
||||
CCCYN(ch, C_NRM), get_save_mod(k, ABIL_INT), CCNRM(ch, C_NRM), GET_SAVE(k, ABIL_INT),
|
||||
CCCYN(ch, C_NRM), get_save_mod(k, ABIL_WIS), CCNRM(ch, C_NRM), GET_SAVE(k, ABIL_WIS),
|
||||
CCCYN(ch, C_NRM), get_save_mod(k, ABIL_CHA), CCNRM(ch, C_NRM), GET_SAVE(k, ABIL_CHA));
|
||||
|
||||
send_to_char(ch, "Hit p.:[%s%d/%d+%d%s] Mana p.:[%s%d/%d+%d%s] Move p.:[%s%d/%d+%d%s]\r\n",
|
||||
CCGRN(ch, C_NRM), GET_HIT(k), GET_MAX_HIT(k), hit_gain(k), CCNRM(ch, C_NRM),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ int invalid_class(struct char_data *ch, struct obj_data *obj);
|
|||
int level_exp(int chclass, int level);
|
||||
int parse_class(char arg);
|
||||
void roll_real_abils(struct char_data *ch);
|
||||
bool has_save_proficiency(int class_num, int ability);
|
||||
|
||||
/* Global variables */
|
||||
|
||||
|
|
|
|||
44
src/magic.c
44
src/magic.c
|
|
@ -29,29 +29,22 @@ static int mag_materials(struct char_data *ch, IDXTYPE item0, IDXTYPE item1, IDX
|
|||
static void perform_mag_groups(int level, struct char_data *ch, struct char_data *tch, int spellnum, int savetype);
|
||||
|
||||
|
||||
/* Negative apply_saving_throw[] values make saving throws better! So do
|
||||
* negative modifiers. Though people may be used to the reverse of that.
|
||||
* It's due to the code modifying the target saving throw instead of the
|
||||
* random number of the character as in some other systems. */
|
||||
int mag_savingthrow(struct char_data *ch, int type, int modifier)
|
||||
/* Roll a 5e-style saving throw: d20 + ability mod + proficiency vs DC. */
|
||||
int mag_savingthrow(struct char_data *ch, int ability, int dc)
|
||||
{
|
||||
/* NPCs use fighter tables according to some book */
|
||||
int class_sav = CLASS_FIGHTER;
|
||||
int save;
|
||||
int roll, total, target;
|
||||
|
||||
if (!IS_NPC(ch))
|
||||
class_sav = GET_CLASS(ch);
|
||||
if (!ch)
|
||||
return FALSE;
|
||||
|
||||
save = GET_SAVE(ch, type);
|
||||
save += GET_SAVE(ch, type);
|
||||
save += modifier;
|
||||
roll = roll_d20();
|
||||
total = roll + get_save_mod(ch, ability);
|
||||
target = MAX(1, dc);
|
||||
|
||||
/* Throwing a 0 is always a failure. */
|
||||
if (MAX(1, save) < rand_number(0, 99))
|
||||
return (TRUE);
|
||||
if (total >= target)
|
||||
return TRUE;
|
||||
|
||||
/* Oops, failed. Sorry. */
|
||||
return (FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* affect_update: called from comm.c (causes spells to wear off) */
|
||||
|
|
@ -287,7 +280,7 @@ int mag_damage(int level, struct char_data *ch, struct char_data *victim,
|
|||
|
||||
|
||||
/* divide damage by two if victim makes his saving throw */
|
||||
if (mag_savingthrow(victim, savetype, 0))
|
||||
if (mag_savingthrow(victim, savetype, compute_save_dc(ch, level, spellnum)))
|
||||
dam /= 2;
|
||||
|
||||
/* and finally, inflict the damage */
|
||||
|
|
@ -307,11 +300,14 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
|
|||
bool accum_affect = FALSE, accum_duration = FALSE;
|
||||
const char *to_vict = NULL, *to_room = NULL;
|
||||
int i, j;
|
||||
int save_dc;
|
||||
|
||||
|
||||
if (victim == NULL || ch == NULL)
|
||||
return;
|
||||
|
||||
save_dc = compute_save_dc(ch, level, spellnum);
|
||||
|
||||
for (i = 0; i < MAX_SPELL_AFFECTS; i++) {
|
||||
new_affect(&(af[i]));
|
||||
af[i].spell = spellnum;
|
||||
|
|
@ -321,7 +317,7 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
|
|||
|
||||
case SPELL_CHILL_TOUCH:
|
||||
af[0].location = APPLY_STR;
|
||||
if (mag_savingthrow(victim, ABIL_CON, savetype))
|
||||
if (mag_savingthrow(victim, ABIL_CON, save_dc))
|
||||
af[0].duration = 1; /* resisted: brief weakening */
|
||||
else
|
||||
af[0].duration = 4; /* failed: longer effect */
|
||||
|
|
@ -354,7 +350,7 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
|
|||
|
||||
case SPELL_BLINDNESS:
|
||||
if (MOB_FLAGGED(victim, MOB_NOBLIND) || GET_LEVEL(victim) >= LVL_IMMORT ||
|
||||
mag_savingthrow(victim, ABIL_CON, savetype)) {
|
||||
mag_savingthrow(victim, ABIL_CON, save_dc)) {
|
||||
send_to_char(ch, "You fail.\r\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -374,7 +370,7 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
|
|||
break;
|
||||
|
||||
case SPELL_CURSE:
|
||||
if (mag_savingthrow(victim, ABIL_WIS, savetype)) {
|
||||
if (mag_savingthrow(victim, ABIL_WIS, save_dc)) {
|
||||
send_to_char(ch, "%s", CONFIG_NOEFFECT);
|
||||
return;
|
||||
}
|
||||
|
|
@ -445,7 +441,7 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
|
|||
break;
|
||||
|
||||
case SPELL_POISON:
|
||||
if (mag_savingthrow(victim, ABIL_CON, savetype)) {
|
||||
if (mag_savingthrow(victim, ABIL_CON, save_dc)) {
|
||||
send_to_char(ch, "%s", CONFIG_NOEFFECT);
|
||||
return;
|
||||
}
|
||||
|
|
@ -478,7 +474,7 @@ void mag_affects(int level, struct char_data *ch, struct char_data *victim,
|
|||
return;
|
||||
if (MOB_FLAGGED(victim, MOB_NOSLEEP))
|
||||
return;
|
||||
if (mag_savingthrow(victim, ABIL_WIS, savetype))
|
||||
if (mag_savingthrow(victim, ABIL_WIS, save_dc))
|
||||
return;
|
||||
|
||||
af[0].duration = 4 + (GET_LEVEL(ch) / 4);
|
||||
|
|
|
|||
11
src/spells.c
11
src/spells.c
|
|
@ -106,9 +106,13 @@ ASPELL(spell_teleport)
|
|||
#define SUMMON_FAIL "You failed.\r\n"
|
||||
ASPELL(spell_summon)
|
||||
{
|
||||
int save_dc;
|
||||
|
||||
if (ch == NULL || victim == NULL)
|
||||
return;
|
||||
|
||||
save_dc = compute_save_dc(ch, level, SPELL_SUMMON);
|
||||
|
||||
if (GET_LEVEL(victim) > MIN(LVL_IMMORT - 1, level + 3)) {
|
||||
send_to_char(ch, "%s", SUMMON_FAIL);
|
||||
return;
|
||||
|
|
@ -143,7 +147,7 @@ ASPELL(spell_summon)
|
|||
}
|
||||
|
||||
if (MOB_FLAGGED(victim, MOB_NOSUMMON) ||
|
||||
(IS_NPC(victim) && mag_savingthrow(victim, SAVING_CHA, 0))) {
|
||||
(IS_NPC(victim) && mag_savingthrow(victim, SAVING_CHA, save_dc))) {
|
||||
send_to_char(ch, "%s", SUMMON_FAIL);
|
||||
return;
|
||||
}
|
||||
|
|
@ -248,10 +252,13 @@ ASPELL(spell_locate_object)
|
|||
ASPELL(spell_charm)
|
||||
{
|
||||
struct affected_type af;
|
||||
int save_dc;
|
||||
|
||||
if (victim == NULL || ch == NULL)
|
||||
return;
|
||||
|
||||
save_dc = compute_save_dc(ch, level, SPELL_CHARM);
|
||||
|
||||
if (victim == ch)
|
||||
send_to_char(ch, "You like yourself even better!\r\n");
|
||||
else if (!IS_NPC(victim) && !PRF_FLAGGED(victim, PRF_SUMMONABLE))
|
||||
|
|
@ -269,7 +276,7 @@ ASPELL(spell_charm)
|
|||
send_to_char(ch, "You fail - shouldn't be doing it anyway.\r\n");
|
||||
else if (circle_follow(victim, ch))
|
||||
send_to_char(ch, "Sorry, following in circles is not allowed.\r\n");
|
||||
else if (mag_savingthrow(victim, SAVING_WIS, 0))
|
||||
else if (mag_savingthrow(victim, SAVING_WIS, save_dc))
|
||||
send_to_char(ch, "Your victim resists!\r\n");
|
||||
else {
|
||||
if (victim->master)
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ void init_spell_levels(void);
|
|||
const char *skill_name(int num);
|
||||
|
||||
/* From magic.c */
|
||||
int mag_savingthrow(struct char_data *ch, int type, int modifier);
|
||||
int mag_savingthrow(struct char_data *ch, int ability, int dc);
|
||||
void affect_update(void);
|
||||
|
||||
/* from spell_parser.c */
|
||||
|
|
|
|||
66
src/utils.c
66
src/utils.c
|
|
@ -1640,6 +1640,70 @@ int GET_ABILITY_MOD(int score) {
|
|||
return mod;
|
||||
}
|
||||
|
||||
/* Helper: derive a class-level proficiency bonus (no situational modifiers). */
|
||||
int get_level_proficiency_bonus(struct char_data *ch)
|
||||
{
|
||||
int level;
|
||||
int bonus;
|
||||
|
||||
if (!ch)
|
||||
return 0;
|
||||
|
||||
level = MAX(1, GET_LEVEL(ch));
|
||||
bonus = 2 + ((level - 1) / 4);
|
||||
if (bonus > 6)
|
||||
bonus = 6;
|
||||
return bonus;
|
||||
}
|
||||
|
||||
int get_total_proficiency_bonus(struct char_data *ch)
|
||||
{
|
||||
if (!ch)
|
||||
return 0;
|
||||
return get_level_proficiency_bonus(ch) + GET_PROF_MOD(ch);
|
||||
}
|
||||
|
||||
static int get_ability_mod_from_index(struct char_data *ch, int ability)
|
||||
{
|
||||
if (!ch)
|
||||
return 0;
|
||||
|
||||
switch (ability) {
|
||||
case ABIL_STR: return GET_ABILITY_MOD(GET_STR(ch));
|
||||
case ABIL_DEX: return GET_ABILITY_MOD(GET_DEX(ch));
|
||||
case ABIL_CON: return GET_ABILITY_MOD(GET_CON(ch));
|
||||
case ABIL_INT: return GET_ABILITY_MOD(GET_INT(ch));
|
||||
case ABIL_WIS: return GET_ABILITY_MOD(GET_WIS(ch));
|
||||
case ABIL_CHA: return GET_ABILITY_MOD(GET_CHA(ch));
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int get_save_mod(struct char_data *ch, int ability)
|
||||
{
|
||||
int mod;
|
||||
|
||||
if (!ch)
|
||||
return 0;
|
||||
|
||||
mod = GET_SAVE(ch, ability);
|
||||
mod += get_ability_mod_from_index(ch, ability);
|
||||
|
||||
if (has_save_proficiency(GET_CLASS(ch), ability))
|
||||
mod += get_total_proficiency_bonus(ch);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
int compute_save_dc(struct char_data *caster, int level, int spellnum)
|
||||
{
|
||||
if (caster)
|
||||
return GET_SPELL_SAVE_DC(caster, spellnum, 0);
|
||||
|
||||
/* Non-caster fallback for scrolls/wands/etc. */
|
||||
return MAX(1, 8 + (level / 2));
|
||||
}
|
||||
|
||||
/* Converts a skill percentage (0-100) into a 5e-like proficiency bonus. */
|
||||
int GET_PROFICIENCY(int pct) {
|
||||
if (pct <= 14) return 0;
|
||||
|
|
@ -1799,4 +1863,4 @@ struct mob_loadout *loadout_deep_copy(const struct mob_loadout *src) {
|
|||
else { tail->next = n; tail = n; }
|
||||
}
|
||||
return head;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,10 @@ struct ac_breakdown {
|
|||
|
||||
int GET_ABILITY_MOD(int score);
|
||||
int GET_PROFICIENCY(int pct);
|
||||
int get_level_proficiency_bonus(struct char_data *ch);
|
||||
int get_total_proficiency_bonus(struct char_data *ch);
|
||||
int get_save_mod(struct char_data *ch, int ability);
|
||||
int compute_save_dc(struct char_data *caster, int level, int spellnum);
|
||||
int compute_ascending_ac(struct char_data *ch);
|
||||
int GET_SITUATIONAL_AC(struct char_data *ch);
|
||||
int compute_armor_class_asc(struct char_data *ch);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue