Skip to content

Commit

Permalink
Active defence system rework (#1470)
Browse files Browse the repository at this point in the history
* Energy_Stored serialization. ADS math.

* More background work on energy_stored

* Complete rework of bio_ads functions

God I thought this'd be easier.

* Deactivation effects

Clear energy_stored, refund energy (won't bring you over cap)

* Description and cost

* Removed floor() function, added comments.

So you can understand somewhat the method to my madness.

* Update character.cpp

* Fix description

Just in case I go with percentage on bullets as well.

* Update bionics.json

* Code streamline. Balancing. Description rewrite.

Use std::min to streamline recharge code (thanks chaos).
Further balanced numbers, halved recharge when hit.
Updated description of bionic in items and bionics UI.

* Update comment on hack

* Fixes and formatting

* Flavour

* Flavour Update
  • Loading branch information
KheirFerrum authored and joveeater committed Jul 10, 2022
1 parent e529ad7 commit f3aa067
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 15 deletions.
4 changes: 2 additions & 2 deletions data/json/bionics.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
"id": "bio_ads",
"type": "bionic",
"name": { "str": "Active Defense System" },
"description": "A thin forcefield surrounds your body, continually draining power. Anything attempting to penetrate this field has a chance of being deflected at the cost of energy, reducing their ability to deal damage. Bullets will be deflected more than melee weapons and those in turn more than massive objects.",
"description": "This bionic charges a forcefield around your body that deflects incoming attacks. Bullets are heavily deflected, piercing well deflected, slashing moderately deflected and bashing barely deflected. This bionic charges at half speed for 3 turns after being hit, and doesn't consume power when fully charged.",
"occupied_bodyparts": [ [ "torso", 10 ], [ "head", 1 ], [ "arm_l", 1 ], [ "arm_r", 1 ], [ "leg_l", 2 ], [ "leg_r", 2 ] ],
"flags": [ "BIONIC_TOGGLED", "BIONIC_NPC_USABLE" ],
"act_cost": "10 kJ",
"act_cost": "5 kJ",
"react_cost": "10 kJ",
"time": 1
},
Expand Down
2 changes: 1 addition & 1 deletion data/json/items/bionics.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"type": "BIONIC_ITEM",
"name": { "str": "Active Defense System CBM" },
"looks_like": "bio_int_enhancer",
"description": "This bionic projects a thin forcefield around the user's body. Anything attempting to penetrate this field has a chance of being deflected at the cost of energy, reducing their ability to deal damage. Bullets will be deflected more than swords and those in turn more than massive objects.",
"description": "This bionic charges a forcefield around your body that deflects incoming attacks. Bullets are heavily deflected, piercing well deflected, slashing moderately deflected and bashing barely deflected. This bionic charges at half speed for 3 turns after being hit, and doesn't consume power when fully charged.",
"price": 950000,
"weight": "1000 g",
"difficulty": 7
Expand Down
33 changes: 33 additions & 0 deletions src/bionics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ static const skill_id skill_mechanics( "mechanics" );

static const bionic_id bio_adrenaline( "bio_adrenaline" );
static const bionic_id bio_advreactor( "bio_advreactor" );
static const bionic_id bio_ads( "bio_ads" );
static const bionic_id bio_blaster( "bio_blaster" );
static const bionic_id bio_blood_anal( "bio_blood_anal" );
static const bionic_id bio_blood_filter( "bio_blood_filter" );
Expand Down Expand Up @@ -1124,6 +1125,9 @@ bool Character::deactivate_bionic( int b, bool eff_only )
}
} else if( bio.id == bio_tools ) {
invalidate_crafting_inventory();
} else if( bio.id == bio_ads ) {
mod_power_level( bio.energy_stored );
bio.energy_stored = 0_kJ;
}

// Recalculate stats (strength, mods from pain etc.) that could have been affected
Expand Down Expand Up @@ -1704,6 +1708,31 @@ void Character::process_bionic( int b )
bio.info().name );
deactivate_bionic( b );
}
} else if( bio.id == bio_ads ) {
if( bio.charge_timer < 2 ) {
bio.charge_timer = 2;
}
if( bio.energy_stored < 150_kJ ) {
// Max recharge rate is influenced by whether you've been hit or not.
// See character.cpp for how charge_timer keeps track of that for this bionic.
units::energy max_rate = 10_kJ;
if( bio.charge_timer > 2 ) {
max_rate /= 2;
}
units::energy ads_recharge = std::min( max_rate, 150_kJ - bio.energy_stored );
if( ads_recharge < get_power_level() ) {
mod_power_level( - ads_recharge );
bio.energy_stored += ads_recharge;
} else if( get_power_level() != 0_kJ ) {
mod_power_level( - get_power_level() );
bio.energy_stored += get_power_level();
}
if( bio.energy_stored == 150_kJ ) {
add_msg_if_player( m_good, _( "Your %s quietens to a satisfied thrum." ), bio.info().name );
}
} else if( bio.energy_stored > 150_kJ ) {
bio.energy_stored = 150_kJ;
}
} else if( bio.id == afs_bio_dopamine_stimulators ) {
// Aftershock
add_morale( MORALE_FEELING_GOOD, 20, 20, 30_minutes, 20_minutes, true );
Expand Down Expand Up @@ -2762,6 +2791,9 @@ void bionic::serialize( JsonOut &json ) const
if( is_auto_start_on() ) {
json.member( "auto_start_threshold", auto_start_threshold );
}
if( energy_stored > 0_kJ ) {
json.member( "energy_stored", energy_stored );
}

json.end_object();
}
Expand All @@ -2773,6 +2805,7 @@ void bionic::deserialize( JsonIn &jsin )
invlet = jo.get_int( "invlet" );
powered = jo.get_bool( "powered" );
charge_timer = jo.get_int( "charge" );
jo.read( "energy_stored", energy_stored, true );
if( jo.has_string( "ammo_loaded" ) ) {
jo.read( "ammo_loaded", ammo_loaded, true );
}
Expand Down
2 changes: 2 additions & 0 deletions src/bionics.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ struct bionic {
unsigned int ammo_count = 0;
/* An amount of time during which this bionic has been rendered inoperative. */
time_duration incapacitated_time;
/* The amount of energy the Bionic has stored for it's function. [Currently only used for ADS]*/
units::energy energy_stored = 0_kJ;
bionic()
: id( "bio_batteries" ), incapacitated_time( 0_turns ) {
}
Expand Down
61 changes: 49 additions & 12 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7995,20 +7995,57 @@ void Character::absorb_hit( const bodypart_id &bp, damage_instance &dam )
continue;
}

// The bio_ads CBM absorbs damage before hitting armor
if( has_active_bionic( bio_ads ) ) {
if( elem.amount > 0 && get_power_level() > 24_kJ ) {
if( elem.type == DT_BASH ) {
elem.amount -= rng( 1, 2 );
} else if( elem.type == DT_CUT ) {
elem.amount -= rng( 1, 4 );
} else if( elem.type == DT_STAB || elem.type == DT_BULLET ) {
elem.amount -= rng( 1, 8 );
// The bio_ads CBM absorbs percentage melee damage and ranged damage (where possible) after armour.
if( has_active_bionic( bio_ads ) && ( elem.amount > 0 ) && ( elem.type == DT_BASH ||
elem.type == DT_CUT || elem.type == DT_STAB || elem.type == DT_BULLET ) ) {
float elem_multi = 1;
// HACK: In the future this hopefully gets streamlined.
const auto &all_bionics = get_bionics();
size_t index;
for( index = 0; index < all_bionics.size(); index++ ) {
if( all_bionics[index] == bio_ads ) {
break;
}
mod_power_level( -25_kJ );
}
if( elem.amount < 0 ) {
elem.amount = 0;
bionic &bio = bionic_at_index( index );
// HACK: Halves charge rate when hit for the next 3 turns, doesn't stack. See bionics.cpp for more information.
bio.charge_timer = 6;
// Bullet affected significantly more than stab, stab more than cut, cut more than bash.
if( elem.type == DT_BASH ) {
elem_multi = 0.8;
} else if( elem.type == DT_CUT ) {
elem_multi = 0.7;
} else if( elem.type == DT_STAB ) {
elem_multi = 0.55;
} else if( elem.type == DT_BULLET ) {
elem_multi = 0.25;
}
units::energy ads_cost = elem.amount * 500_J;
if( bio.energy_stored >= ads_cost ) {
dam.mult_damage( elem_multi );
bio.energy_stored -= ads_cost;
} else if( bio.energy_stored < ads_cost && bio.energy_stored != 0_kJ ) {
// If you get hit and you lack energy it either deactivates, or deactivates and shorts out.
// Either way you still get protection.
dam.mult_damage( elem_multi );
bio.energy_stored = 0_kJ;
deactivate_bionic( index );
const units::energy shatter_thresh = ( elem.type == DT_BULLET ) ? 20_kJ : 15_kJ;
if( ads_cost >= shatter_thresh ) {
if( bio.incapacitated_time == 0_turns ) {
add_msg_if_player( m_bad, _( "Your forcefield shatters and the feedback shorts out the %s!" ),
bio.info().name );
}
int over = units::to_kilojoule( ads_cost - ( shatter_thresh - 5_kJ ) );
bio.incapacitated_time += ( ( over / 5 ) ) * 1_turns;
} else {
add_msg_if_player( m_bad, _( "Your forcefield crackles and the %s powers down." ),
bio.info().name );
}
} else {
//You tried to (re)activate it and immediately enter combat, no mitigation for you.
deactivate_bionic( index );
add_msg_if_player( m_bad, _( "The %s is interrupted and powers down." ), bio.info().name );
}
}

Expand Down

0 comments on commit f3aa067

Please sign in to comment.