Skip to content

Commit

Permalink
Add support for partial armor coverage for monsters (CleverRaven#51304)
Browse files Browse the repository at this point in the history
* Modify load_damage_array to specify default

* Add weakpoint.h

* Implement weakpoint.cpp

* Various compiler fixes

* Add weakpoints to mtype.

* Let weakpoints modify monster armor.

* Compiler fixes

* weakpoint bugfixes.

* Run astyle

* Add json documentation

* Run json_formatter

* Run clang tidy

* Fix GCC-only warning

* clang-tidy

* Clarify weakpoint docs and switch to percentage coverage.

* Rename armor offset to armor penalty, flipping the sign.
  • Loading branch information
Joshua-Chin authored and Venera3 committed Sep 21, 2021
1 parent 4d92c40 commit 9e78b99
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 4 deletions.
2 changes: 2 additions & 0 deletions data/json/monsters/misc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"morale": 100,
"melee_cut": 0,
"vision_day": 1,
"armor_bullet": 100,
"weakpoints": [ { "name": "knee", "armor_mult": { "bullet": 0.5 }, "armor_penalty": { "bullet": 25 }, "coverage": 50 } ],
"harvest": "exempt",
"death_function": { "corpse_type": "NO_CORPSE", "message": "The %s melts away." },
"regenerates": 50,
Expand Down
13 changes: 13 additions & 0 deletions doc/MONSTERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Monsters may also have any of these optional properties:
| `armor_stab` | (integer) Monster's protection from stab damage
| `armor_acid` | (integer) Monster's protection from acid damage
| `armor_fire` | (integer) Monster's protection from fire damage
| `weakpoints` | (array of objects) Weakpoints in the monster's protection
| `vision_day` | (integer) Vision range in full daylight, with `50` being the typical maximum
| `vision_night` | (integer) Vision range in total darkness, ex. coyote `5`, bear `10`, sewer rat `30`, flaming eye `40`
| `tracking_distance` | (integer) Amount of tiles the monster will keep between itself and its current tracked enemy or followed leader. Defaults to `3`.
Expand Down Expand Up @@ -316,6 +317,18 @@ Amount of cutting damage added to die roll on monster melee attack.

Monster protection from bashing, cutting, stabbing, acid and fire damage.

## "weakpoints"
(array of objects, optional)

Weakpoints in the monster's protection.

| field | description
| --- | ---
| `name` | Name of the weakpoint.
| `coverage` | Base percentage chance of hitting the weakpoint. May be increased by skill level. (e.g. A coverage of 5 means a 5% base chance of hitting the weakpoint)
| `armor_multiplier` | multipler on the monster's base protection when hitting the weakpoint.
| `armor_penalty` | a flat penalty to the monster's protection, applied after the multiplier.

## "vision_day", "vision_night"
(integer, optional)

Expand Down
5 changes: 3 additions & 2 deletions src/damage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,10 +430,11 @@ damage_instance load_damage_instance_inherit( const JsonArray &jarr, const damag
return di;
}

std::array<float, static_cast<int>( damage_type::NUM )> load_damage_array( const JsonObject &jo )
std::array<float, static_cast<int>( damage_type::NUM )> load_damage_array( const JsonObject &jo,
float default_value )
{
std::array<float, static_cast<int>( damage_type::NUM )> ret;
float init_val = jo.get_float( "all", 0.0f );
float init_val = jo.get_float( "all", default_value );

float phys = jo.get_float( "physical", init_val );
ret[ static_cast<int>( damage_type::BASH ) ] = jo.get_float( "bash", phys );
Expand Down
3 changes: 2 additions & 1 deletion src/damage.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ resistances load_resistances_instance( const JsonObject &jo );

// Returns damage or resistance data
// Handles some shorthands
std::array<float, static_cast<int>( damage_type::NUM )> load_damage_array( const JsonObject &jo );
std::array<float, static_cast<int>( damage_type::NUM )> load_damage_array( const JsonObject &jo,
float default_value = 0.0f );

#endif // CATA_SRC_DAMAGE_H
11 changes: 10 additions & 1 deletion src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "trap.h"
#include "units.h"
#include "viewer.h"
#include "weakpoint.h"
#include "weather.h"

static const efftype_id effect_badpoison( "badpoison" );
Expand Down Expand Up @@ -1492,10 +1493,18 @@ bool monster::block_hit( Creature *, bodypart_id &, damage_instance & )

void monster::absorb_hit( const bodypart_id &, damage_instance &dam )
{
resistances r = resistances( *this );
const weakpoint *weakpoint = type->weakpoints.select_weakpoint();
weakpoint->apply_to( r );
for( auto &elem : dam.damage_units ) {
add_msg_debug( debugmode::DF_MONSTER, "Dam Type: %s :: Ar Pen: %.1f :: Armor Mult: %.1f",
name_by_dt( elem.type ), elem.res_pen, elem.res_mult );
elem.amount -= std::min( resistances( *this ).get_effective_resist( elem ) +
add_msg_debug( debugmode::DF_MONSTER,
"Weakpoint: %s :: Armor Mult: %.1f :: Armor Penalty: %.1f :: Resist: %.1f",
weakpoint->id, weakpoint->armor_mult[static_cast<int>( elem.type )],
weakpoint->armor_penalty[static_cast<int>( elem.type )],
r.get_effective_resist( elem ) );
elem.amount -= std::min( r.get_effective_resist( elem ) +
get_worn_armor_val( elem.type ), elem.amount );
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/monstergenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "rng.h"
#include "translations.h"
#include "units.h"
#include "weakpoint.h"

namespace behavior
{
Expand Down Expand Up @@ -718,6 +719,11 @@ void mtype::load( const JsonObject &jo, const std::string &src )
assign( jo, "armor_acid", armor_acid, strict, 0 );
assign( jo, "armor_fire", armor_fire, strict, 0 );

if( jo.has_array( "weakpoints" ) ) {
weakpoints.clear();
weakpoints.load( jo.get_array( "weakpoints" ) );
}

optional( jo, was_loaded, "bleed_rate", bleed_rate, 100 );

assign( jo, "vision_day", vision_day, strict, 0 );
Expand Down
1 change: 1 addition & 0 deletions src/mtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "monstergenerator.h"
#include "translations.h"
#include "units.h"
#include "weakpoint.h"

static const itype_id itype_bone( "bone" );
static const itype_id itype_bone_tainted( "bone_tainted" );
Expand Down
2 changes: 2 additions & 0 deletions src/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "translations.h"
#include "type_id.h"
#include "units.h" // IWYU pragma: keep
#include "weakpoint.h"

class Creature;
class monster;
Expand Down Expand Up @@ -332,6 +333,7 @@ struct mtype {
int armor_bullet = -1; /** innate armor vs. bullet */
int armor_acid = -1; /** innate armor vs. acid */
int armor_fire = -1; /** innate armor vs. fire */
::weakpoints weakpoints;

// Bleed rate in percent, 0 makes the monster immune to bleeding
int bleed_rate = 100;
Expand Down
77 changes: 77 additions & 0 deletions src/weakpoint.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "weakpoint.h"

#include <string>
#include <utility>

#include "assign.h"
#include "creature.h"
#include "damage.h"
#include "item.h"
#include "rng.h"

class JsonArray;
class JsonObject;

weakpoint::weakpoint()
{
// arrays must be filled manually to avoid UB.
armor_mult.fill( 1.0f );
armor_penalty.fill( 0.0f );
}

void weakpoint::load( const JsonObject &jo )
{
assign( jo, "id", id );
assign( jo, "name", name );
assign( jo, "coverage", coverage, false, 0.0f, 100.0f );
if( jo.has_object( "armor_mult" ) ) {
armor_mult = load_damage_array( jo.get_object( "armor_mult" ), 1.0f );
}
if( jo.has_object( "armor_penalty" ) ) {
armor_penalty = load_damage_array( jo.get_object( "armor_penalty" ), 0.0f );
}
// Set the ID to the name, if not provided.
if( id.empty() ) {
id = name;
}
}

void weakpoint::apply_to( resistances &resistances ) const
{
for( int i = 0; i < static_cast<int>( damage_type::NUM ); ++i ) {
resistances.resist_vals[i] *= armor_mult[i];
resistances.resist_vals[i] -= armor_penalty[i];
}
}

float weakpoint::hit_chance() const
{
return coverage;
}

const weakpoint *weakpoints::select_weakpoint() const
{
float idx = rng_float( 0.0f, 100.0f );
for( const weakpoint &weakpoint : weakpoint_list ) {
float hit_chance = weakpoint.hit_chance( );
if( hit_chance <= idx ) {
return &weakpoint;
}
idx -= hit_chance;
}
return &default_weakpoint;
}

void weakpoints::clear()
{
weakpoint_list.clear();
}

void weakpoints::load( const JsonArray &ja )
{
for( const JsonObject jo : ja ) {
weakpoint tmp;
tmp.load( jo );
weakpoint_list.push_back( std::move( tmp ) );
}
}
48 changes: 48 additions & 0 deletions src/weakpoint.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once
#ifndef CATA_SRC_WEAKPOINT_H
#define CATA_SRC_WEAKPOINT_H

#include <array>
#include <string>
#include <vector>

#include "creature.h"
#include "damage.h"

class JsonArray;
class JsonObject;

struct weakpoint {
// ID of the weakpoint. Equal to the name, if not provided.
std::string id;
// Name of the weakpoint. Can be empty.
std::string name;
// Percent chance of hitting the weakpoint. Can be increased by skill.
float coverage = 100.0f;
// Multiplier for existing armor values. Defaults to 1.
std::array<float, static_cast<int>( damage_type::NUM )> armor_mult;
// Flat penalty to armor values. Applied after the multiplier.
std::array<float, static_cast<int>( damage_type::NUM )> armor_penalty;

weakpoint();
// Apply the armor multipliers and offsets to a set of resistances.
void apply_to( resistances &resistances ) const;
// Return the change of the creature hitting the weakpoint.
float hit_chance( ) const;
void load( const JsonObject &jo );
};

struct weakpoints {
// List of weakpoints. Each weakpoint should have a unique id.
std::vector<weakpoint> weakpoint_list;
// Default weakpoint to return.
weakpoint default_weakpoint;

// Selects a weakpoint to hit.
const weakpoint *select_weakpoint( ) const;

void clear();
void load( const JsonArray &ja );
};

#endif // CATA_SRC_WEAKPOINT_H

0 comments on commit 9e78b99

Please sign in to comment.