Skip to content

Commit

Permalink
Put constants for criticals in constants.txt
Browse files Browse the repository at this point in the history
Resolves #376 .

Fixes a mismatch between the object information calculation of melee
criticals and the combat calculations:  1/40 chance of 5 additional dice
+ 39/40 * (1/12 chance of 4 additional dice + 11/12 * (1/3 chance of 3
additional dice + 2/3 * 2 additional dice)) gives an expected number of
additional dice of 1217 / 480 not 537 / 240 (i.e. 1074 / 480) that
obj-info.c used.

For object information calculations, calculate_melee_crits() and
calculate_missile_crits() now report values rounded to the nearest
integer.  However, since there is further scaling for those results,
the final values reported to the player still aren't guaranteed to
be rounded to the nearest tenth.

Because of the correction and change to rounding, the reported damage
for a level 2 easterling warrior with 18/30 strength and 18 dexterity
wielding a +0,+0 2d5 broad sword (15 pounds; 2.00 blows), a +2,+3 1d4 dagger
(1.2 pounds; 3.33 blows), or +0,+0 x2 shortbow (3 pounds; 1.0 shots) with +0,+0
2d4 arrows (0.2 pounds) changes as follows:

                 old   new   old+armsman  new+armsman  old+marksman  new+mksm
                 ---   ---   -----------  -----------  ------------  --------
sword, wielded   17.9  18.5     19.9         20.6
dagger, wielded  17.1  18.3     20.2         21.3
dagger, thrown    7.5   8.1                                 8.5        9.0
arrow, launched  12.4  12.5                                13.4       13.3
  • Loading branch information
backwardsEric committed Dec 7, 2023
1 parent 6ecc727 commit a1f8759
Show file tree
Hide file tree
Showing 11 changed files with 1,759 additions and 73 deletions.
108 changes: 108 additions & 0 deletions lib/gamedata/constants.txt
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,111 @@ player:start-gold:600

# Number of turns that 1% of player food capacity feeds them for
player:food-value:100

#---------------------------------------------------------------------
# Constants for critical calculations
# In general, those calculations compute a "power" of the critical
# by scaling the chance of a hit. The "power" is then converted to
# a chance of a critical (neglecting the armsman and marksman
# abilities) using (a * power) / (b * power + c) as the chance where
# a, b, and c are constants specified here. For more details, see
# critical_melee() and critical_shot() in player-attack.c.
#---------------------------------------------------------------------

# The numerator for the scale factor applied to the combined to-hit value to
# get the power of the critical
melee-critical:power-toh-scale-numerator:1

# The denominator for the scale factor applied to the combined to-hit value to
# get the power of the critical
melee-critical:power-toh-scale-denominator:1

# The scale factor for the critical's power in the numerator for the chance
# of the critical
melee-critical:chance-power-scale-numerator:1

# The scale factor for the critical's power in the denominator for the chance
# of the critical
melee-critical:chance-power-scale-denominator:1

# Value of an added term in the denominator for the chance of a critical
melee-critical:chance-add-denominator:240

# Players with the armsman ability have a chance, 1 / armsman-chance,
# of inflicting a critical on any melee hit against an obvious opponent.
# If they do not get a critical from that they still can get a critical
# the standard way. armsman-chance must be positive.
melee-critical:armsman-chance:6

# Define each of the levels for melee criticals. They are considered in the
# order they appear so it's convenient to put the least likely first. If no
# critical levels are defined, then there's no extra damage due to critical
# hits.
# Except for the last level, one over the first value is the probability that
# this level occurs when none of the prior levels have been selected; the last
# level will always be used if none of the prior levels have been selected.
# The first value must be positive.
# The second value is the number of dice to add for the critical. It must be
# non-negative.
# The third value is the name of the message, from list-messages.h, to use
# when a critical happens for that power level.
melee-critical-level:40:5:HIT_HI_GREAT
melee-critical-level:12:4:HIT_SUPERB
melee-critical-level:3:3:HIT_GREAT
melee-critical-level:1:2:HIT_GOOD

# Players with the mana burn ability inflict mana-burn-dice additional
# dice of damage when a melee critical causes extra damage against a target
# with non-innate spells. That critical also impairs the target's spell
# casting. mana-burn-dice must be non-negative.
melee-critical:mana-burn-dice:1

# The numerator for the scale factor applied to the combined to-hit value to
# get the power of the critical with a launched missile
ranged-critical:power-launched-toh-scale-numerator:1

# The denominator for the scale factor applied to the combined to-hit value to
# get the power of the critical whith a launched missile
ranged-critical:power-launched-toh-scale-denominator:1

# The numerator for the scale factor applied to the combined to-hit value to
# get the power of the critical with a thrown missile; this and the
# denominator are currently set so thrown missiles get more criticals
ranged-critical:power-thrown-toh-scale-numerator:3

# The denominator for the scale factor applied to the combined to-hit value to
# get the power of the critical whith a thrown missile
ranged-critical:power-thrown-toh-scale-denominator:2

# The scale factor for the critical's power in the numerator for the chance
# of the critical
ranged-critical:chance-power-scale-numerator:1

# The scale factor for the critical's power in the denominator for the chance
# of the critical
ranged-critical:chance-power-scale-denominator:1

# Value of an added term in the denominator for the chance of a critical
ranged-critical:chance-add-denominator:360

# Players with the marksman ability have a chance, 1 / marksman-chance,
# of inflicting a critical on any ranged hit against a visible opponent.
# If they do not get a critical from that they still can get a critical
# the standard way. marksman-chance must be positive.
ranged-critical:marksman-chance:6

# Define each of the levels for ranged criticals. They are considered in the
# order they appear so it's convenient to put the least likely first. If no
# critical levels are defined, then there's no extra damage due to critical
# hits.
# Except for the last level, one over the first value is the probability that
# this level occurs when none of the prior levels have been selected; the last
# level will always be used if none of the prior levels have been selected.
# The first value must be positive.
# The second value is the number of dice to add for the critical. It must be
# non-negative.
# The third value is the name of the message, from list-messages.h, to use
# when a critical happens for that power level.
ranged-critical-level:50:3:HIT_SUPERB
ranged-critical-level:10:2:HIT_GREAT
ranged-critical-level:1:1:HIT_GOOD
154 changes: 154 additions & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "generate.h"
#include "hint.h"
#include "init.h"
#include "message.h"
#include "mon-init.h"
#include "mon-list.h"
#include "mon-lore.h"
Expand Down Expand Up @@ -728,6 +729,139 @@ static enum parser_error parse_constants_player(struct parser *p) {
return PARSE_ERROR_NONE;
}

static enum parser_error parse_constants_melee_critical(struct parser *p)
{
struct angband_constants *z = parser_priv(p);
const char *label = parser_getsym(p, "label");
int value = parser_getint(p, "value");

if (streq(label, "power-toh-scale-numerator")) {
z->m_crit_power_toh_scl_num = value;
} else if (streq(label, "power-toh-scale-denominator")) {
z->m_crit_power_toh_scl_den = value;
} else if (streq(label, "chance-power-scale-numerator")) {
z->m_crit_chance_power_scl_num = value;
} else if (streq(label, "chance-power-scale-denominator")) {
z->m_crit_chance_power_scl_den = value;
} else if (streq(label, "chance-add-denominator")) {
z->m_crit_chance_add_den = value;
} else if (streq(label, "armsman-chance")) {
if (value <= 0) {
return PARSE_ERROR_INVALID_VALUE;
}
z->m_armsman_chance = value;
} else if (streq(label, "mana-burn-dice")) {
if (value < 0) {
return PARSE_ERROR_INVALID_VALUE;
}
z->m_manaburn_dice = value;
} else {
return PARSE_ERROR_UNDEFINED_DIRECTIVE;
}

return PARSE_ERROR_NONE;
}

static enum parser_error parse_constants_melee_critical_level(struct parser *p)
{
struct angband_constants *z = parser_priv(p);
struct critical_level *new_level;
unsigned int chance = parser_getuint(p, "chance");
const char *msgt_str = parser_getstr(p, "msg");
int msgt = message_lookup_by_name(msgt_str);

if (chance == 0) {
return PARSE_ERROR_INVALID_VALUE;
}
if (msgt < 0) {
return PARSE_ERROR_INVALID_MESSAGE;
}
new_level = mem_alloc(sizeof(*new_level));
new_level->next = NULL;
new_level->chance = chance;
new_level->added_dice = parser_getuint(p, "dice");
new_level->msgt = msgt;
/* Add it to the end of the linked list. */
if (z->m_crit_level_head) {
struct critical_level *cursor = z->m_crit_level_head;

while (cursor->next) {
cursor = cursor->next;
}
cursor->next = new_level;
} else {
z->m_crit_level_head = new_level;
}

return PARSE_ERROR_NONE;
}

static enum parser_error parse_constants_ranged_critical(struct parser *p)
{
struct angband_constants *z = parser_priv(p);
const char *label = parser_getsym(p, "label");
int value = parser_getint(p, "value");

if (streq(label, "power-launched-toh-scale-numerator")) {
z->r_crit_power_launched_toh_scl_num = value;
} else if (streq(label, "power-launched-toh-scale-denominator")) {
z->r_crit_power_launched_toh_scl_den = value;
} else if (streq(label, "power-thrown-toh-scale-numerator")) {
z->r_crit_power_thrown_toh_scl_num = value;
} else if (streq(label, "power-thrown-toh-scale-denominator")) {
z->r_crit_power_thrown_toh_scl_den = value;
} else if (streq(label, "chance-power-scale-numerator")) {
z->r_crit_chance_power_scl_num = value;
} else if (streq(label, "chance-power-scale-denominator")) {
z->r_crit_chance_power_scl_den = value;
} else if (streq(label, "chance-add-denominator")) {
z->r_crit_chance_add_den = value;
} else if (streq(label, "marksman-chance")) {
if (value <= 0) {
return PARSE_ERROR_INVALID_VALUE;
}
z->r_marksman_chance = value;
} else {
return PARSE_ERROR_UNDEFINED_DIRECTIVE;
}

return PARSE_ERROR_NONE;
}

static enum parser_error parse_constants_ranged_critical_level(struct parser *p)
{
struct angband_constants *z = parser_priv(p);
struct critical_level *new_level;
unsigned int chance = parser_getuint(p, "chance");
const char *msgt_str = parser_getstr(p, "msg");
int msgt = message_lookup_by_name(msgt_str);

if (chance == 0) {
return PARSE_ERROR_INVALID_VALUE;
}
if (msgt < 0) {
return PARSE_ERROR_INVALID_MESSAGE;
}
new_level = mem_alloc(sizeof(*new_level));
new_level->next = NULL;
new_level->chance = chance;
new_level->added_dice = parser_getuint(p, "dice");
new_level->msgt = msgt;
/* Add it to the end of the linked list. */
if (z->r_crit_level_head) {
struct critical_level *cursor = z->r_crit_level_head;

while (cursor->next) {
cursor = cursor->next;
}
cursor->next = new_level;
} else {
z->r_crit_level_head = new_level;
}

return PARSE_ERROR_NONE;
}

static struct parser *init_parse_constants(void) {
struct angband_constants *z = mem_zalloc(sizeof *z);
struct parser *p = parser_new();
Expand All @@ -742,6 +876,14 @@ static struct parser *init_parse_constants(void) {
parser_reg(p, "store sym label int value", parse_constants_store);
parser_reg(p, "obj-make sym label int value", parse_constants_obj_make);
parser_reg(p, "player sym label int value", parse_constants_player);
parser_reg(p, "melee-critical sym label int value",
parse_constants_melee_critical);
parser_reg(p, "melee-critical-level uint chance uint dice str msg",
parse_constants_melee_critical_level);
parser_reg(p, "ranged-critical sym label int value",
parse_constants_ranged_critical);
parser_reg(p, "ranged-critical-level uint chance uint dice str msg",
parse_constants_ranged_critical_level);
return p;
}

Expand All @@ -755,8 +897,20 @@ static errr finish_parse_constants(struct parser *p) {
return 0;
}

static void cleanup_critical_levels(struct critical_level *head)
{
while (head) {
struct critical_level *target = head;

head = head->next;
mem_free(target);
}
}

static void cleanup_constants(void)
{
cleanup_critical_levels(z_info->m_crit_level_head);
cleanup_critical_levels(z_info->r_crit_level_head);
mem_free(z_info);
}

Expand Down
48 changes: 48 additions & 0 deletions src/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,21 @@
#include "z-bitflag.h"
#include "z-file.h"
#include "z-rand.h"
#include "z-util.h"
#include "datafile.h"
#include "object.h"

/* Define a level of serverity for a critial hit */
struct critical_level {
struct critical_level *next;
unsigned int chance; /* one in chance of this level unless
this is the last level; the rest
go to the next level */
unsigned int added_dice; /* number of dice added for this
level */
int msgt; /* mesage type to use for this level */
};

/**
* Information about maximal indices of certain arrays.
*
Expand Down Expand Up @@ -126,6 +138,42 @@ struct angband_constants
uint16_t max_range; /* Maximum missile and spell range */
uint16_t start_gold; /* Amount of gold the player starts with */
uint16_t food_value; /* Number of turns 1% of food lasts */

/*
* Constants for melee critical calculations; read from
* constants.txt
*/
int m_crit_power_toh_scl_num;
int m_crit_power_toh_scl_den;
int m_crit_chance_power_scl_num;
int m_crit_chance_power_scl_den;
int m_crit_chance_add_den;
int m_armsman_chance;
int m_manaburn_dice;
struct critical_level *m_crit_level_head;

/*
* For object information, critical levels do not depend on the
* properties of the player or weapon so they can be summed over once
* after loading the constants file and stored here.
*/
struct my_rational m_max_added;

/*
* Constants for ranged critical calculations; read from
* constants.txt
*/
int r_crit_power_launched_toh_scl_num;
int r_crit_power_launched_toh_scl_den;
int r_crit_power_thrown_toh_scl_num;
int r_crit_power_thrown_toh_scl_den;
int r_crit_chance_power_scl_num;
int r_crit_chance_power_scl_den;
int r_crit_chance_add_den;
int r_marksman_chance;
struct critical_level *r_crit_level_head;
/* See comment for m_max_added above. */
struct my_rational r_max_added;
};

struct init_module {
Expand Down
Loading

0 comments on commit a1f8759

Please sign in to comment.