Skip to content

Commit

Permalink
Magiclysm: Spell encumbrance and minor spellcasting ui tweaks (#31902)
Browse files Browse the repository at this point in the history
  • Loading branch information
KorGgenT authored and ZhilkinSerg committed Jun 27, 2019
1 parent f8888f5 commit facb626
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4057,7 +4057,7 @@ void activity_handlers::spellcasting_finish( player_activity *act, player *p )
}
if( !no_mana ) {
// pay the cost
int cost = casting.energy_cost();
int cost = casting.energy_cost( *p );
switch( casting.energy_source() ) {
case mana_energy:
p->magic.mod_mana( *p, -cost );
Expand Down
2 changes: 1 addition & 1 deletion src/handle_action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ static void cast_spell()
return;
}

player_activity cast_spell( activity_id( "ACT_SPELLCASTING" ), sp.casting_time() );
player_activity cast_spell( activity_id( "ACT_SPELLCASTING" ), sp.casting_time( u ) );
// [0] this is used as a spell level override for items casting spells
cast_spell.values.emplace_back( -1 );
// [1] if this value is 1, the spell never fails
Expand Down
2 changes: 1 addition & 1 deletion src/iuse_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2304,7 +2304,7 @@ int cast_spell_actor::use( player &p, item &itm, bool, const tripoint & ) const
spell casting = spell( spell_id( item_spell ) );
int charges = itm.type->charges_to_use();

player_activity cast_spell( activity_id( "ACT_SPELLCASTING" ), casting.casting_time() );
player_activity cast_spell( activity_id( "ACT_SPELLCASTING" ), casting.casting_time( p ) );
// [0] this is used as a spell level override for items casting spells
cast_spell.values.emplace_back( spell_level );
if( no_fail ) {
Expand Down
161 changes: 134 additions & 27 deletions src/magic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ spell_id spell::id() const
return type->id;
}

trait_id spell::spell_class() const
{
return type->spell_class;
}

int spell::damage() const
{
if( type->min_damage >= 0 ) {
Expand Down Expand Up @@ -329,17 +334,24 @@ bool spell::can_learn( const player &p ) const
return p.has_trait( type->spell_class );
}

int spell::energy_cost() const
int spell::energy_cost( const player &p ) const
{
int cost;
if( type->base_energy_cost < type->final_energy_cost ) {
return std::min( type->final_energy_cost,
cost = std::min( type->final_energy_cost,
static_cast<int>( round( type->base_energy_cost + type->energy_increment * get_level() ) ) );
} else if( type->base_energy_cost > type->final_energy_cost ) {
return std::max( type->final_energy_cost,
cost = std::max( type->final_energy_cost,
static_cast<int>( round( type->base_energy_cost + type->energy_increment * get_level() ) ) );
} else {
return type->base_energy_cost;
cost = type->base_energy_cost;
}
if( !has_flag( spell_flag::NO_HANDS ) ) {
// the first 10 points of combined encumbrance is ignored, but quickly adds up
const int hands_encumb = std::max( 0, p.encumb( bp_hand_l ) + p.encumb( bp_hand_r ) - 10 );
cost += 10 * hands_encumb;
}
return cost;
}

bool spell::has_flag( const spell_flag &flag ) const
Expand All @@ -361,19 +373,19 @@ bool spell::can_cast( const player &p ) const
}
switch( type->energy_source ) {
case mana_energy:
return p.magic.available_mana() >= energy_cost();
return p.magic.available_mana() >= energy_cost( p );
case stamina_energy:
return p.stamina >= energy_cost();
return p.stamina >= energy_cost( p );
case hp_energy: {
for( int i = 0; i < num_hp_parts; i++ ) {
if( energy_cost() < p.hp_cur[i] ) {
if( energy_cost( p ) < p.hp_cur[i] ) {
return true;
}
}
return false;
}
case bionic_energy:
return p.power_level >= energy_cost();
return p.power_level >= energy_cost( p );
case fatigue_energy:
return p.get_fatigue() < EXHAUSTED;
case none_energy:
Expand All @@ -387,17 +399,30 @@ int spell::get_difficulty() const
return type->difficulty;
}

int spell::casting_time() const
int spell::casting_time( const player &p ) const
{
// casting time in moves
int casting_time = 0;
if( type->base_casting_time < type->final_casting_time ) {
return std::min( type->final_casting_time,
static_cast<int>( round( type->base_casting_time + type->casting_time_increment * get_level() ) ) );
casting_time = std::min( type->final_casting_time,
static_cast<int>( round( type->base_casting_time + type->casting_time_increment * get_level() ) ) );
} else if( type->base_casting_time > type->final_casting_time ) {
return std::max( type->final_casting_time,
static_cast<int>( round( type->base_casting_time + type->casting_time_increment * get_level() ) ) );
casting_time = std::max( type->final_casting_time,
static_cast<int>( round( type->base_casting_time + type->casting_time_increment * get_level() ) ) );
} else {
return type->base_casting_time;
casting_time = type->base_casting_time;
}
if( !has_flag( spell_flag::NO_LEGS ) ) {
// the first 20 points of encumbrance combined is ignored
const int legs_encumb = std::max( 0, p.encumb( bp_leg_l ) + p.encumb( bp_leg_r ) - 20 );
casting_time += legs_encumb * 3;
}
if( has_flag( spell_flag::SOMATIC ) ) {
// the first 20 points of encumbrance combined is ignored
const int arms_encumb = std::max( 0, p.encumb( bp_arm_l ) + p.encumb( bp_arm_r ) - 20 );
casting_time += arms_encumb * 2;
}
return casting_time;
}

std::string spell::name() const
Expand All @@ -420,7 +445,25 @@ float spell::spell_fail( const player &p ) const
} else if( effective_skill < 0.0f ) {
return 1.0f;
}
const float fail_chance = pow( ( effective_skill - 30.0f ) / 30.0f, 2 );
float fail_chance = pow( ( effective_skill - 30.0f ) / 30.0f, 2 );
if( has_flag( spell_flag::SOMATIC ) ) {
// the first 20 points of encumbrance combined is ignored
const int arms_encumb = std::max( 0, p.encumb( bp_arm_l ) + p.encumb( bp_arm_r ) - 20 );
// each encumbrance point beyond the "gray" color counts as half an additional fail %
fail_chance += arms_encumb / 200.0f;
}
if( has_flag( spell_flag::VERBAL ) ) {
// a little bit of mouth encumbrance is allowed, but not much
const int mouth_encumb = std::max( 0, p.encumb( bp_mouth ) - 5 );
fail_chance += mouth_encumb / 100.0f;
}
// concentration spells work better than you'd expect with a higher focus pool
if( has_flag( spell_flag::CONCENTRATE ) ) {
if( p.focus_pool <= 0 ) {
return 0.0f;
}
fail_chance /= p.focus_pool / 100.0f;
}
return clamp( fail_chance, 0.0f, 1.0f );
}

Expand Down Expand Up @@ -481,18 +524,18 @@ std::string spell::energy_cost_string( const player &p ) const
return _( "none" );
}
if( energy_source() == bionic_energy || energy_source() == mana_energy ) {
return colorize( to_string( energy_cost() ), c_light_blue );
return colorize( to_string( energy_cost( p ) ), c_light_blue );
}
if( energy_source() == hp_energy ) {
auto pair = get_hp_bar( energy_cost(), p.get_hp_max() / num_hp_parts );
auto pair = get_hp_bar( energy_cost( p ), p.get_hp_max() / num_hp_parts );
return colorize( pair.first, pair.second );
}
if( energy_source() == stamina_energy ) {
auto pair = get_hp_bar( energy_cost(), p.get_stamina_max() );
auto pair = get_hp_bar( energy_cost( p ), p.get_stamina_max() );
return colorize( pair.first, pair.second );
}
if( energy_source() == fatigue_energy ) {
return colorize( to_string( energy_cost() ), c_cyan );
return colorize( to_string( energy_cost( p ) ), c_cyan );
}
debugmsg( "ERROR: Spell %s has invalid energy source.", id().c_str() );
return _( "error: energy_type" );
Expand Down Expand Up @@ -952,7 +995,7 @@ std::vector<spell_id> known_magic::spells() const
// does the player have enough energy (of the type of the spell) to cast the spell?
bool known_magic::has_enough_energy( const player &p, spell &sp ) const
{
int cost = sp.energy_cost();
int cost = sp.energy_cost( p );
switch( sp.energy_source() ) {
case mana_energy:
return available_mana() >= cost;
Expand Down Expand Up @@ -1045,6 +1088,59 @@ static std::string moves_to_string( const int moves )
}
}

static bool casting_time_encumbered( const spell &sp, const player &p )
{
int encumb = 0;
if( !sp.has_flag( spell_flag::NO_LEGS ) ) {
// the first 20 points of encumbrance combined is ignored
encumb += std::max( 0, p.encumb( bp_leg_l ) + p.encumb( bp_leg_r ) - 20 );
}
if( sp.has_flag( spell_flag::SOMATIC ) ) {
// the first 20 points of encumbrance combined is ignored
encumb += std::max( 0, p.encumb( bp_arm_l ) + p.encumb( bp_arm_r ) - 20 );
}
if( encumb > 0 ) {
return true;
}
return false;
}

static bool energy_cost_encumbered( const spell &sp, const player &p )
{
if( !sp.has_flag( spell_flag::NO_HANDS ) ) {
return std::max( 0, p.encumb( bp_hand_l ) + p.encumb( bp_hand_r ) - 10 ) > 0;
}
return false;
}

// this prints various things about the spell out in a list
// including flags and things like "goes through walls"
static std::string enumerate_spell_data( const spell &sp )
{
std::vector<std::string> spell_data;
if( sp.has_flag( spell_flag::CONCENTRATE ) ) {
spell_data.emplace_back( _( "requires concentration" ) );
}
if( sp.has_flag( spell_flag::VERBAL ) ) {
spell_data.emplace_back( _( "verbal" ) );
}
if( sp.has_flag( spell_flag::SOMATIC ) ) {
spell_data.emplace_back( _( "somatic" ) );
}
if( !sp.has_flag( spell_flag::NO_HANDS ) ) {
spell_data.emplace_back( _( "impeded by gloves" ) );
} else {
spell_data.emplace_back( _( "does not require hands" ) );
}
if( !sp.has_flag( spell_flag::NO_LEGS ) ) {
spell_data.emplace_back( _( "requires mobility" ) );
}
if( sp.effect() == "target_attack" && sp.range() > 1 ) {
spell_data.emplace_back( _( "can be cast through walls" ) );
}
return enumerate_as_string( spell_data );
}

void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu )
{
const int h_offset = menu->w_width - menu->pad_right + 1;
Expand All @@ -1058,11 +1154,19 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu
int line = 1;
nc_color gray = c_light_gray;
nc_color light_green = c_light_green;
nc_color yellow = c_yellow;

print_colored_text( w_menu, line++, h_col1, yellow, yellow,
sp.spell_class() == trait_id( "NONE" ) ? _( "Classless" ) : sp.spell_class()->name() );

line += fold_and_print( w_menu, line, h_col1, info_width, gray, sp.description() );

line++;

line += fold_and_print( w_menu, line, h_col1, info_width, gray, enumerate_spell_data( sp ) );

line++;

print_colored_text( w_menu, line, h_col1, gray, gray,
string_format( "%s: %d %s", _( "Spell Level" ), sp.get_level(),
sp.is_max_level() ? _( "(MAX)" ) : "" ) );
Expand All @@ -1082,14 +1186,17 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu

line++;

const bool cost_encumb = energy_cost_encumbered( sp, g->u );
print_colored_text( w_menu, line++, h_col1, gray, gray,
string_format( "%s: %s %s%s", _( "Casting Cost" ), sp.energy_cost_string( g->u ),
sp.energy_string(),
string_format( "%s: %s %s%s", cost_encumb ? _( "Casting Cost (impeded)" ) : _( "Casting Cost" ),
sp.energy_cost_string( g->u ), sp.energy_string(),
sp.energy_source() == hp_energy ? "" : string_format( " ( %s current )",
sp.energy_cur_string( g->u ) ) ) );

print_colored_text( w_menu, line++, h_col1, gray, gray,
string_format( "%s: %s", _( "Casting Time" ), moves_to_string( sp.casting_time() ) ) );
const bool c_t_encumb = casting_time_encumbered( sp, g->u );
print_colored_text( w_menu, line++, h_col1, gray, gray, colorize(
string_format( "%s: %s", c_t_encumb ? _( "Casting Time (impeded)" ) : _( "Casting Time" ),
moves_to_string( sp.casting_time( g->u ) ) ),
c_t_encumb ? c_red : c_light_gray ) );

line++;

Expand All @@ -1110,11 +1217,11 @@ void spellcasting_callback::draw_spell_info( const spell &sp, const uilist *menu
// if it's any type of attack spell, the stats are normal.
if( fx == "target_attack" || fx == "projectile_attack" || fx == "cone_attack" ||
fx == "line_attack" ) {
if( damage >= 0 ) {
if( damage > 0 ) {
damage_string = string_format( "%s: %s %s", _( "Damage" ), colorize( to_string( damage ),
sp.damage_type_color() ),
colorize( sp.damage_type_string(), sp.damage_type_color() ) );
} else {
} else if( damage < 0 ) {
damage_string = string_format( "%s: %s", _( "Healing" ), colorize( "+" + to_string( -damage ),
light_green ) );
}
Expand Down
6 changes: 4 additions & 2 deletions src/magic.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class spell
// distance spell can be cast
int range() const;
// how much energy does the spell cost
int energy_cost() const;
int energy_cost( const player &p ) const;
// how long does this spell's effect last
int duration() const;
time_duration duration_turns() const;
Expand All @@ -237,7 +237,7 @@ class spell
float spell_fail( const player &p ) const;
std::string colorized_fail_percent( const player &p ) const;
// how long does it take to cast the spell
int casting_time() const;
int casting_time( const player &p ) const;

// can the player cast this spell?
bool can_cast( const player &p ) const;
Expand All @@ -254,6 +254,8 @@ class spell

// get spell id (from type)
spell_id id() const;
// get spell class (from type)
trait_id spell_class() const;
// get spell effect string (from type)
std::string effect() const;
// get spell effect_str data
Expand Down
2 changes: 1 addition & 1 deletion src/magic_spell_effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ static void damage_targets( const spell &sp, const std::set<tripoint> &targets )
}
if( sp.damage() > 0 ) {
cr->deal_projectile_attack( &g->u, atk, true );
} else {
} else if( sp.damage() < 0 ) {
sp.heal( target );
add_msg( m_good, _( "%s wounds are closing up!" ), cr->disp_name( true ) );
}
Expand Down

0 comments on commit facb626

Please sign in to comment.