Skip to content

Commit

Permalink
Merge pull request #77520 from John-Candlebury/afs-cool-shields2
Browse files Browse the repository at this point in the history
Infrastructure for Energy Shield type Armor
  • Loading branch information
Maleclypse authored Nov 12, 2024
2 parents 0c29c27 + e100653 commit dc1a524
Show file tree
Hide file tree
Showing 14 changed files with 111 additions and 12 deletions.
5 changes: 5 additions & 0 deletions data/json/flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -2293,6 +2293,11 @@
"info": "This is meant for exoskeleton foot plating.",
"inherit": false
},
{
"id": "ENERGY_SHIELD",
"type": "json_flag",
"info": "This piece of armor is an energy barrier and will break after absorbing a certain amount of damage."
},
{
"id": "ROBOFAC_ROBOT_MEDIUM",
"type": "json_flag",
Expand Down
30 changes: 30 additions & 0 deletions data/json/items/armor/cloaks.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,36 @@
}
]
},
{
"id": "debug_energy_shield",
"type": "ARMOR",
"name": { "str": "weak energy shield" },
"description": "A weak energy shield that protects against ballistic damage.",
"weight": "1 g",
"volume": "1 ml",
"symbol": "o",
"color": "blue",
"material": [ "ballistic_shield" ],
"material_thickness": 0.3,
"max_energy_shield_hp": 4,
"flags": [
"AURA",
"ENERGY_SHIELD",
"OVERSIZE",
"ONLY_ONE",
"TRADER_AVOID",
"NO_TAKEOFF",
"NONCONDUCTIVE",
"ALLOWS_NATURAL_ATTACKS"
],
"armor": [
{
"encumbrance": 0,
"coverage": 100,
"covers": [ "leg_l", "leg_r", "torso", "arm_l", "arm_r", "hand_l", "hand_r", "head", "foot_l", "foot_r", "mouth", "eyes" ]
}
]
},
{
"id": "pride_flag",
"type": "ARMOR",
Expand Down
15 changes: 15 additions & 0 deletions data/json/materials.json
Original file line number Diff line number Diff line change
Expand Up @@ -2263,6 +2263,21 @@
"cut_dmg_verb": "agitated",
"resist": { "bash": 160, "cut": 200, "acid": 100, "heat": 100, "bullet": 160 }
},
{
"type": "material",
"id": "ballistic_shield",
"name": "Ballistic Barrier",
"density": 1,
"specific_heat_liquid": 0.52,
"specific_heat_solid": 0.52,
"latent_heat": 5200,
"chip_resist": 100,
"breathability": "SECOND_SKIN",
"dmg_adj": [ "resonating", "cascading", "unraveling", "fractal" ],
"bash_dmg_verb": "agitated",
"cut_dmg_verb": "agitated",
"resist": { "bash": 0, "cut": 0, "acid": 0, "heat": 0, "bullet": 100 }
},
{
"type": "material",
"id": "monolith_heavy",
Expand Down
1 change: 1 addition & 0 deletions doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ Some armor flags, such as `WATCH` and `ALARMCLOCK` are compatible with other ite
- ```DEAF``` Makes the player deaf.
- ```DECAY_EXPOSED_ATMOSPHERE``` Consumable will go bad once exposed to the atmosphere (such as MREs).
- ```ELECTRIC_IMMUNE``` This gear completely protects you from electric discharges.
- ```ENERGY_SHIELD``` Marks a piece of armor as an energy shield. Energy shields do not suffer degradation from attacks and instead have an hp pool defined by the dialogue variable `ENERGY_SHIELD_HP` that is depleted by blocked attacks and a second variable `ENERGY_SHIELD_MAX_HP` which just stores the max hp of the shield in case it's needed for EOC manipulation. When the hp pool is depleted, the shield is destroyed. The fields `ENERGY SHIELD_HP` and `ENERGY_SHIELD_MAX_HP` are dialogue variables stored in the item, and can be modified through effects on condition.
- ```EXTRA_PLATING``` Item can be worn over some armors, as additional layer of protection (like armor above brigandine); specifically can be put in pocket for armor with this flag restriction.
- ```FANCY``` Wearing this clothing gives a morale bonus if the player has the `Stylish` trait.
- ```FIN``` This item is swim fins aka diving fins aka flippets, and provide speed boost when you swim.
Expand Down
1 change: 1 addition & 0 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3672,6 +3672,7 @@ Armor can be defined like this:
"cover_vitals": 10, // What percentage of critical hit damage is mitigated
"material_thickness" : 1, // Thickness of material, in millimeter units (approximately). Ordinary clothes range from 0.1 to 0.5. Particularly rugged cloth may reach as high as 1-2mm, and armor or protective equipment can range as high as 10 or rarely more.
"power_armor" : false, // If this is a power armor item (those are special).
"energy_shield_max_hp" : false, // Determines the inital value for the `ENERGY_SHIELD_HP` and `ENERGY_SHIELD_MAX_HP` item variables used by energy shields. The field has no effect for armor pieces without the `ENERGY_SHIELD` flag.
"non_functional" : "destroyed", //this is the itype_id of an item that this turns into when destroyed. Currently only works for ablative armor.
"damage_verb": "makes a crunch, something has shifted", // if an item uses non-functional this will be the description when it turns into its non functional variant.
"valid_mods" : ["steel_padded"], // List of valid clothing mods. Note that if the clothing mod doesn't have "restricted" listed, this isn't needed.
Expand Down
23 changes: 17 additions & 6 deletions src/character_armor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,18 +285,23 @@ bool Character::armor_absorb( damage_unit &du, item &armor, const bodypart_id &b
armor.energy_consume( units::from_kilojoule( du.amount ),
pos(), nullptr );
}
// We copy the damage unit here since it will be mutated by mitigate_damage()
damage_unit pre_mitigation = du;

// reduce the damage
// -1 is passed as roll so that each material is rolled individually
armor.mitigate_damage( du, sbp, -1 );

// check if the armor was damaged
item::armor_status damaged = armor.damage_armor_durability( du, bp, calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );
item::armor_status damaged = armor.damage_armor_durability( du, pre_mitigation, bp,
calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );

// describe what happened if the armor took damage
if( damaged == item::armor_status::DAMAGED || damaged == item::armor_status::DESTROYED ) {
describe_damage( du, armor );
}

return damaged == item::armor_status::DESTROYED;
}

Expand All @@ -318,18 +323,23 @@ bool Character::armor_absorb( damage_unit &du, item &armor, const bodypart_id &b
armor.energy_consume( units::from_kilojoule( du.amount ),
pos(), nullptr );
}
// We copy the damage unit here since it will be mutated by mitigate_damage()
damage_unit pre_mitigation = du;

// reduce the damage
// -1 is passed as roll so that each material is rolled individually
armor.mitigate_damage( du, bp, -1 );

// check if the armor was damaged
item::armor_status damaged = armor.damage_armor_durability( du, bp, calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );
item::armor_status damaged = armor.damage_armor_durability( du, pre_mitigation, bp,
calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );

// describe what happened if the armor took damage
if( damaged == item::armor_status::DAMAGED || damaged == item::armor_status::DESTROYED ) {
describe_damage( du, armor );
}

return damaged == item::armor_status::DESTROYED;
}

Expand Down Expand Up @@ -361,8 +371,9 @@ bool Character::ablative_armor_absorb( damage_unit &du, item &armor, const sub_b
damaged = ablative_armor.damage_armor_transforms( pre_mitigation, calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );
} else {
damaged = ablative_armor.damage_armor_durability( du, bp->parent, calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );
damaged = ablative_armor.damage_armor_durability( du, pre_mitigation, bp->parent,
calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );
}

if( damaged == item::armor_status::TRANSFORMED ) {
Expand Down
2 changes: 1 addition & 1 deletion src/character_attire.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1950,7 +1950,7 @@ void outfit::absorb_damage( Character &guy, damage_unit &elem, bodypart_id bp,
// if not already destroyed to an armor absorb
destroy = guy.armor_absorb( elem, armor, bp, sbp, roll );
// for the torso we also need to consider if it hits anything hanging off the character or their neck
if( secondary_sbp != sub_bodypart_id() ) {
if( secondary_sbp != sub_bodypart_id() && !destroy ) {
destroy = guy.armor_absorb( elem, armor, bp, secondary_sbp, roll );
}
}
Expand Down
1 change: 1 addition & 0 deletions src/flag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const flag_id flag_EFFECT_LIMB_SCORE_MOD( "EFFECT_LIMB_SCORE_MOD" );
const flag_id flag_EFFECT_LIMB_SCORE_MOD_LOCAL( "EFFECT_LIMB_SCORE_MOD_LOCAL" );
const flag_id flag_ELECTRIC_IMMUNE( "ELECTRIC_IMMUNE" );
const flag_id flag_ELECTRONIC( "ELECTRONIC" );
const flag_id flag_ENERGY_SHIELD( "ENERGY_SHIELD" );
const flag_id flag_ETHEREAL_ITEM( "ETHEREAL_ITEM" );
const flag_id flag_EXO_ARM_PLATE( "EXO_ARM_PLATE" );
const flag_id flag_EXO_BOOT_PLATE( "EXO_BOOT_PLATE" );
Expand Down
1 change: 1 addition & 0 deletions src/flag.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ extern const flag_id flag_EFFECT_LIMB_SCORE_MOD;
extern const flag_id flag_EFFECT_LIMB_SCORE_MOD_LOCAL;
extern const flag_id flag_ELECTRIC_IMMUNE;
extern const flag_id flag_ELECTRONIC;
extern const flag_id flag_ENERGY_SHIELD;
extern const flag_id flag_ETHEREAL_ITEM;
extern const flag_id flag_EXO_ARM_PLATE;
extern const flag_id flag_EXO_BOOT_PLATE;
Expand Down
29 changes: 26 additions & 3 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,12 @@ item::item( const itype *type, time_point turn, int qty ) : type( type ), bday(
activate();
}

if( has_flag( flag_ENERGY_SHIELD ) ) {
const islot_armor *sh = find_armor_data();
set_var( "npctalk_var_MAX_ENERGY_SHIELD_HP", sh->max_energy_shield_hp );
set_var( "npctalk_var_ENERGY_SHIELD_HP", sh->max_energy_shield_hp );
}

if( has_flag( flag_COLLAPSE_CONTENTS ) ) {
for( item_pocket *pocket : contents.get_all_standard_pockets() ) {
pocket->settings.set_collapse( true );
Expand Down Expand Up @@ -9040,9 +9046,25 @@ int item::repairable_levels() const
: damage() > degradation(); // partial level of damage can still be repaired
}

item::armor_status item::damage_armor_durability( damage_unit &du, const bodypart_id &bp,
item::armor_status item::damage_armor_durability( damage_unit &du, damage_unit &premitigated,
const bodypart_id &bp,
double enchant_multiplier )
{
//Energy shields aren't damaged by attacks but do get their health variable reduced. They are also only
//damaged by the damage types they actually protect against.
if( has_var( "npctalk_var_ENERGY_SHIELD_HP" ) && resist( du.type, false, bp ) > 0.0f ) {
double shield_hp = get_var( "npctalk_var_ENERGY_SHIELD_HP", 0.0 );
shield_hp -= premitigated.amount;
set_var( "npctalk_var_ENERGY_SHIELD_HP", shield_hp );
if( shield_hp > 0 ) {
return armor_status::UNDAMAGED;
} else {
//Shields deliberately ignore the enchantment multiplier, as the health mechanic wouldn't make sense otherwise.
mod_damage( itype::damage_scale * 6 );
return armor_status::DESTROYED;
}
}

if( has_flag( flag_UNBREAKABLE ) || enchant_multiplier <= 0.0f ) {
return armor_status::UNDAMAGED;
}
Expand All @@ -9053,8 +9075,9 @@ item::armor_status item::damage_armor_durability( damage_unit &du, const bodypar
// This is some weird type that doesn't damage armors
return armor_status::UNDAMAGED;
}
// Fragile items take damage if the block more than 15% of their armor value
if( has_flag( flag_FRAGILE ) && du.amount / armors_own_resist > ( 0.15f / enchant_multiplier ) ) {
// Fragile items take damage if they block more than 15% of their armor value, this uses the pre-mitigated damage.
if( has_flag( flag_FRAGILE ) &&
premitigated.amount / armors_own_resist > ( 0.15f / enchant_multiplier ) ) {
return mod_damage( itype::damage_scale * enchant_multiplier ) ? armor_status::DESTROYED :
armor_status::DAMAGED;
}
Expand Down
3 changes: 2 additions & 1 deletion src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -1408,7 +1408,8 @@ class item : public visitable
* This version is for items with durability
* @return the state of the armor
*/
armor_status damage_armor_durability( damage_unit &du, const bodypart_id &bp,
armor_status damage_armor_durability( damage_unit &du, damage_unit &premitigated,
const bodypart_id &bp,
double enchant_multiplier = 1 );

/**
Expand Down
4 changes: 4 additions & 0 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2032,6 +2032,9 @@ void Item_factory::check_definitions() const
if( !type->category_force.is_valid() ) {
msg += "undefined category " + type->category_force.str() + "\n";
}
if( type->has_flag( flag_ENERGY_SHIELD ) && !type->armor ) {
msg += "has ENERGY_SHIELD flag specified but the item isn't armor";
}

if( type->armor ) {
cata::flat_set<bodypart_str_id> observed_bps;
Expand Down Expand Up @@ -3111,6 +3114,7 @@ void islot_armor::load( const JsonObject &jo )
optional( jo, was_loaded, "non_functional", non_functional, itype_id() );
optional( jo, was_loaded, "damage_verb", damage_verb );
optional( jo, was_loaded, "power_armor", power_armor, false );
optional( jo, was_loaded, "max_energy_shield_hp", max_energy_shield_hp, 0 );
optional( jo, was_loaded, "valid_mods", valid_mods );
}

Expand Down
6 changes: 6 additions & 0 deletions src/itype.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,12 @@ struct islot_armor {
* How much warmth this item provides.
*/
int warmth = 0;
/**
* The max health of an energy shield type armor. Value is completely ignored if the
* ENERGY_SHIELD flag is not set. This value and "energy_shield_hp" are then stored
* through item variables so that they might be manipulated with EOCS and magic.
*/
int max_energy_shield_hp = 0;
/**
* Whether this is a power armor item.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/melee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@ bool Character::block_hit( Creature *source, bodypart_id &bp_hit, damage_instanc

if( source != nullptr && !source->is_hallucination() ) {
for( damage_unit &du : dam.damage_units ) {
shield->damage_armor_durability( du, bp_hit, calculate_by_enchantment( 1,
shield->damage_armor_durability( du, du, bp_hit, calculate_by_enchantment( 1,
enchant_vals::mod::EQUIPMENT_DAMAGE_CHANCE ) );
}
}
Expand Down

0 comments on commit dc1a524

Please sign in to comment.