Skip to content

Commit

Permalink
Allow spells to use dialog variables. (CleverRaven#64416)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ramza13 authored Mar 24, 2023
1 parent 738341d commit b411b72
Show file tree
Hide file tree
Showing 28 changed files with 818 additions and 801 deletions.
4 changes: 3 additions & 1 deletion doc/MAGIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ Another mandatory field is the spell shape. This dictates how the area of effect

The following JSON fields are also used in spells and, while optional, greatly expand how spells can behave.

All fields that are numeric can also be a "variable object"( see [NPCs.md](NPCs.md) for full details) instead of a number.

| Field group | Description | Example
| --- | --- | ---
| `min_X`, `max_X`, ``X_increment`` | Minimun value, maximum value, and the value, that add each level. Note: your spell don't take max_X on the max lvl, it will always add the correct amount of X_increment to your spell - in spell with `min_damage: 0, max_damage: 100, damage_increment: 5` and `max_level: 10`, on level 10 the spell will deal only 50 damage, as [ 0 + (5\*10) ]. Opposite is also true, in spell with `min_damage: 0, max_damage: 25, damage_increment: 5` on level 10 will deal only 25 damage, as the highest value, described in `max_X` | `min_damage`, `min_range`, `min_aoe`
Expand All @@ -228,7 +230,7 @@ The following JSON fields are also used in spells and, while optional, greatly e
| `base_casting_time`, `final_casting_time`, `casting_time_increment` | Respond for amount of time user cast the spell. Same as duration, writed in moves, which allow to make spells, that can be casted for 0.5 or 0.25 seconds. Doesn't work for monsters and for items, that cast spells | "base_casting_time": 1000, <br>"final_casting_time": 100, <br>"casting_time_increment": -50,
| `base_energy_cost`, `final_energy_cost`, `energy_increment` | Respond for amount of energy you spend for cast | "base_energy_cost": 30, <br>"final_energy_cost": 100, <br>"energy_increment": -6,
| `field_id`, `field_chance`, `min_field_intensity`, `max_field_intensity`, `field_intensity_increment`, `field_intensity_variance` | Respond for field spawn. `field_id` describe what field will be spawned; `field_chance` describe the chance of spawning the field, as "one in `field_chance`" (1 means the probability is 100%, 2 is 50%, 3 is 33% etc.); `min_field_intensity`, `max_field_intensity` and `field_intensity_increment` are respond for the field intensity and it grow depend on level (for example, for fd_electricity intensity 1 represent spark, when intensity 10 is electric cloud); `field_intensity_variance` allow the spell randomly increase and decrease the intensity of the spell as a percent (intencity 10 and variance 0.1 means it can grow or shring at 10%, from 9 to 11)| "field_id": "fd_blood", <br>"field_chance": 100, <br>"min_field_intensity": 10, <br>"max_field_intensity": 10, <br>"field_intensity_increment": 1, <br>"field_intensity_variance": 0.1
| `effect_str` | By default respond for effect, the spell can deal (see EFFECTS_JSON.md), but can vary depend on effect | "effect_str": "zapped",
| `effect_str` | By default respond for effect, the spell can deal (see [EFFECTS_JSON](EFFECTS_JSON.md)), but can vary depend on effect | "effect_str": "zapped",
| `max_level` | How much you can train the spell. 0 by default | "max_level": 10,
| `difficulty` | How hard to cast the spell. Higher difficulty make spell easier to fail, but also train your spell skill to the `difficulty` level (spell with difficulty 10 can train user to spellcasting lvl 10) | "difficulty": 7,
| `affected_body_parts` | Respond on which body part `effect_str` will occur. Doesn't respond what body part the spell will target if you damage the target, it always aim a `torso` no matter what | "affected_body_parts": [ "head" ]
Expand Down
4 changes: 2 additions & 2 deletions object_creator/fake_spell_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ creator::fake_spell_window::fake_spell_window( QWidget *parent, Qt::WindowFlags
[&]() {
editable_spell.id = spell_id( id_box.text().toStdString() );
if( editable_spell.id.is_valid() ) {
max_level_box.setMaximum( editable_spell.id->max_level );
min_level_box.setMaximum( editable_spell.id->max_level );
max_level_box.setMaximum( editable_spell.id->max_level.min.dbl_val.value() );
min_level_box.setMaximum( editable_spell.id->max_level.min.dbl_val.value() );
}
} );
QObject::connect( &id_box, &QLineEdit::textChanged, this, &fake_spell_window::modified );
Expand Down
214 changes: 107 additions & 107 deletions object_creator/spell_window.cpp

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3559,7 +3559,7 @@ void activity_handlers::spellcasting_finish( player_activity *act, Character *yo
if( !success ) {
you->add_msg_if_player( game_message_params{ m_bad, gmf_bypass_cooldown },
_( "You lose your concentration!" ) );
if( !spell_being_cast.is_max_level() && level_override == -1 ) {
if( !spell_being_cast.is_max_level( *you ) && level_override == -1 ) {
// still get some experience for trying
spell_being_cast.gain_exp( *you, exp_gained / 5 );
you->add_msg_if_player( m_good, _( "You gain %i experience. New total %i." ), exp_gained / 5,
Expand Down Expand Up @@ -3605,7 +3605,7 @@ void activity_handlers::spellcasting_finish( player_activity *act, Character *yo

}
if( level_override == -1 ) {
if( !spell_being_cast.is_max_level() ) {
if( !spell_being_cast.is_max_level( *you ) ) {
// reap the reward
int old_level = spell_being_cast.get_level();
if( old_level == 0 ) {
Expand Down Expand Up @@ -3653,8 +3653,8 @@ void activity_handlers::study_spell_do_turn( player_activity *act, Character *yo
const int xp = roll_remainder( studying.exp_modifier( *you ) / to_turns<float>( 6_seconds ) );
act->values[0] += xp;
studying.gain_exp( *you, xp );
bool leveled_up = you->practice( studying.skill(), xp, studying.get_difficulty(), true );
if( leveled_up && studying.get_difficulty() < you->get_skill_level( studying.skill() ) ) {
bool leveled_up = you->practice( studying.skill(), xp, studying.get_difficulty( *you ), true );
if( leveled_up && studying.get_difficulty( *you ) < you->get_skill_level( studying.skill() ) ) {
you->handle_skill_warning( studying.skill(),
true ); // show the skill warning on level up, since we suppress it in practice() above
}
Expand Down
2 changes: 2 additions & 0 deletions src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3031,6 +3031,8 @@ std::unique_ptr<talker> get_talker_for( const Creature &me )
{
if( !me.is_monster() ) {
return std::make_unique<talker_character_const>( static_cast<const Character *>( &me ) );
} else if( me.is_monster() ) {
return std::make_unique<talker_monster_const>( static_cast<const monster *>( &me ) );
} else {
debugmsg( "Invalid creature type %s.", me.get_name() );
standard_npc default_npc( "Default" );
Expand Down
77 changes: 41 additions & 36 deletions src/debug_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,21 +661,21 @@ static void spell_description(
description << spl.description() << '\n';

// Spell Casting flags
description << spell_desc::enumerate_spell_data( spl ) << '\n';
description << spell_desc::enumerate_spell_data( spl, chrc ) << '\n';

// Spell Level: 0 / 0 (MAX)
description << string_format(
//~ %1$s - spell current level, %2$s - spell max level, %3$s - is max level
_( "Spell Level: %1$s / %2$d %3$s" ),
spl_level == -1 ? _( "Unlearned" ) : std::to_string( spl_level ),
spl.get_max_level(),
spl_level == spl.get_max_level() ? _( "(MAX)" ) : "" ) << '\n';
spl.get_max_level( chrc ),
spl_level == spl.get_max_level( chrc ) ? _( "(MAX)" ) : "" ) << '\n';

// Difficulty: 0 ( 0.0 % Failure Chance)
description << string_format(
//~ %1$d - difficulty, %2$s - failure chance
_( "Difficulty: %1$d (%2$s)" ),
spl.get_difficulty(), spl.colorized_fail_percent( chrc ) ) << '\n';
spl.get_difficulty( chrc ), spl.colorized_fail_percent( chrc ) ) << '\n';

const std::string impeded = _( "(impeded)" );

Expand All @@ -686,14 +686,15 @@ static void spell_description(
spl.energy_cost_string( chrc ),
spell_desc::energy_cost_encumbered( spl, chrc ) ? impeded : "",
spl.energy_cur_string( chrc ) ) << '\n';

dialogue d( get_talker_for( chrc ), nullptr );
// Casting Time: 0 (impeded)
description << string_format(
//~ %1$s - cast time, %2$s - is casting impeded, %3$s - casting base time
_( "Casting Time: %1$s %2$s (%3$s base time) " ),
to_string( time_duration::from_moves( spl.casting_time( chrc ) ) ),
spell_desc::casting_time_encumbered( spl, chrc ) ? impeded : "",
to_string( time_duration::from_moves( std::get<0>( spl_data ).base_casting_time ) ) ) << '\n';
to_string( time_duration::from_moves( std::get<0>( spl_data ).base_casting_time.evaluate(
d ) ) ) ) << '\n';

std::string targets;
if( spl.is_valid_target( spell_target::none ) ) {
Expand All @@ -708,7 +709,7 @@ static void spell_description(
description << string_format( _( "Only affects the monsters: %1$s" ), target_ids ) << '\n';
}

const int damage = spl.damage();
const int damage = spl.damage( chrc );
const std::string spl_eff = spl.effect();
std::string damage_string;
std::string range_string;
Expand All @@ -717,19 +718,19 @@ static void spell_description(
if( spl_eff == "attack" ) {
if( damage > 0 ) {
std::string dot_string;
if( spl.damage_dot() ) {
if( spl.damage_dot( chrc ) ) {
//~ amount of damage per second, abbreviated
dot_string = string_format( _( ", %1$d/sec" ), spl.damage_dot() );
dot_string = string_format( _( ", %1$d/sec" ), spl.damage_dot( chrc ) );
}
damage_string = string_format( _( "Damage: %1$s %2$s%3$s" ), spl.damage_string(),
damage_string = string_format( _( "Damage: %1$s %2$s%3$s" ), spl.damage_string( chrc ),
spl.damage_type_string(), dot_string );
damage_string = colorize( damage_string, spl.damage_type_color() );
} else if( damage < 0 ) {
damage_string = string_format( _( "Healing: %1$s" ), colorize( spl.damage_string(),
damage_string = string_format( _( "Healing: %1$s" ), colorize( spl.damage_string( chrc ),
light_green ) );
}

if( spl.aoe() > 0 ) {
if( spl.aoe( chrc ) > 0 ) {
std::string aoe_string_temp = _( "Spell Radius" );
std::string degree_string;
if( spl.shape() == spell_shape::cone ) {
Expand All @@ -738,17 +739,18 @@ static void spell_description(
} else if( spl.shape() == spell_shape::line ) {
aoe_string_temp = _( "Line Width" );
}
aoe_string = string_format( _( "%1$s: %2$d %3$s" ), aoe_string_temp, spl.aoe(), degree_string );
aoe_string = string_format( _( "%1$s: %2$d %3$s" ), aoe_string_temp, spl.aoe( chrc ),
degree_string );
}

} else if( spl_eff == "teleport_random" ) {
if( spl.aoe() > 0 ) {
aoe_string = string_format( _( "Variance: %1$d" ), spl.aoe() );
if( spl.aoe( chrc ) > 0 ) {
aoe_string = string_format( _( "Variance: %1$d" ), spl.aoe( chrc ) );
}

} else if( spl_eff == "spawn_item" ) {
damage_string = string_format( _( "Spawn %1$d %2$s" ), spl.damage(),
item::nname( itype_id( spl.effect_data() ), spl.damage() ) );
damage_string = string_format( _( "Spawn %1$d %2$s" ), spl.damage( chrc ),
item::nname( itype_id( spl.effect_data() ), spl.damage( chrc ) ) );

} else if( spl_eff == "summon" ) {
std::string monster_name = "FIXME";
Expand All @@ -762,8 +764,8 @@ static void spell_description(
} else {
monster_name = monster( mtype_id( spl.effect_data() ) ).get_name();
}
damage_string = string_format( _( "Summon: %1$d %2$s" ), spl.damage(), monster_name );
aoe_string = string_format( _( "Spell Radius: %1$d" ), spl.aoe() );
damage_string = string_format( _( "Summon: %1$d %2$s" ), spl.damage( chrc ), monster_name );
aoe_string = string_format( _( "Spell Radius: %1$d" ), spl.aoe( chrc ) );

} else if( spl_eff == "targeted_polymorph" ) {
std::string monster_name = spl.effect_data();
Expand All @@ -779,23 +781,23 @@ static void spell_description(
} else {
monster_name = mtype_id( spl.effect_data() )->nname();
}
damage_string = string_format( _( "Targets under: %1$dhp become a %2$s" ), spl.damage(),
damage_string = string_format( _( "Targets under: %1$dhp become a %2$s" ), spl.damage( chrc ),
monster_name );

} else if( spl_eff == "ter_transform" ) {
aoe_string = string_format( "Spell Radius: %1$s", spl.aoe_string() );
aoe_string = string_format( "Spell Radius: %1$s", spl.aoe_string( chrc ) );

} else if( spl_eff == "banishment" ) {
damage_string = string_format( _( "Damage: %1$s %2$s" ), spl.damage_string(),
damage_string = string_format( _( "Damage: %1$s %2$s" ), spl.damage_string( chrc ),
spl.damage_type_string() );
if( spl.aoe() > 0 ) {
aoe_string = string_format( _( "Spell Radius: %1$d" ), spl.aoe() );
if( spl.aoe( chrc ) > 0 ) {
aoe_string = string_format( _( "Spell Radius: %1$d" ), spl.aoe( chrc ) );
}
}

// Range / AOE in two columns
description << string_format( _( "Range: %1$s" ),
spl.range() <= 0 ? _( "self" ) : std::to_string( spl.range() ) ) << '\n';
spl.range( chrc ) <= 0 ? _( "self" ) : std::to_string( spl.range( chrc ) ) ) << '\n';

description << aoe_string << '\n';

Expand All @@ -805,8 +807,8 @@ static void spell_description(
// todo: damage over time here, when it gets implemented

// Show duration for spells that endure
if( spl.duration() > 0 || spl.has_flag( spell_flag::PERMANENT ) ) {
description << string_format( _( "Duration: %1$s" ), spl.duration_string() ) << '\n';
if( spl.duration( chrc ) > 0 || spl.has_flag( spell_flag::PERMANENT ) ) {
description << string_format( _( "Duration: %1$s" ), spl.duration_string( chrc ) ) << '\n';
}

// helper function for printing tool and item component requirement lists
Expand Down Expand Up @@ -1003,6 +1005,7 @@ static void change_spells( Character &character )
calcStartPos( spells_start, spell_selected, TERMY - 2, relative_size );

int line_number = 1;
dialogue d( get_talker_for( get_player_character() ), nullptr );
for( int i = spells_start; i < relative_size; ++i ) {
if( line_number == TERMY - 1 ) {
break;
Expand All @@ -1017,7 +1020,7 @@ static void change_spells( Character &character )
mvwprintz( w_name.window, point( 2, line_number ),
spell_color, splt.name.translated() );
mvwprintz( w_level.window, point( 2, line_number++ ), spell_color,
_( "%1$-3d/%2$3d" ), spell_level, splt.max_level );
_( "%1$-3d/%2$3d" ), spell_level, static_cast<int>( splt.max_level.evaluate( d ) ) );
}

nc_color gray = c_light_gray;
Expand Down Expand Up @@ -1091,10 +1094,11 @@ static void change_spells( Character &character )
spell_middle_or_id( spellid );
};

auto toggle_all_spells = [&]( int level ) {
auto toggle_all_spells = [&]( int level, Character & character ) {
dialogue d( get_talker_for( character ), nullptr );
// -2 sets it to max level
for( spell_tuple *spt : spells_relative ) {
std::get<1>( *spt ) = level > -2 ? level : std::get<0>( *spt ).max_level;
std::get<1>( *spt ) = level > -2 ? level : std::get<0>( *spt ).max_level.evaluate( d );
set_spell( std::get<0>( *spt ), std::get<1>( *spt ) );
std::get<2>( *spt ).clear();
}
Expand All @@ -1111,7 +1115,7 @@ static void change_spells( Character &character )
bool showing_only_learned = false;

bool force_update_description = false;

dialogue d( get_talker_for( character ), nullptr );
while( true ) {
update_description( force_update_description );
force_update_description = false;
Expand Down Expand Up @@ -1160,14 +1164,15 @@ static void change_spells( Character &character )
} else if( action == "RIGHT" ) {
int &spell_level = std::get<1>( *spells_relative[spell_selected] );
spell_level = std::min( spell_level + 1,
std::get<0>( *spells_relative[spell_selected] ).max_level );
static_cast<int>( std::get<0>( *spells_relative[spell_selected] ).max_level.evaluate( d ) ) );
set_spell( std::get<0>( *spells_relative[spell_selected] ), spell_level );
force_update_description = true;

} else if( action == "CONFIRM" ) {
int &spell_level = std::get<1>( *spells_relative[spell_selected] );
query_int( spell_level, _( "Set spell level to? Currently: %1$d" ), spell_level );
spell_level = clamp( spell_level, -1, std::get<0>( *spells_relative[spell_selected] ).max_level );
spell_level = clamp( spell_level, -1,
static_cast<int>( std::get<0>( *spells_relative[spell_selected] ).max_level.evaluate( d ) ) );
set_spell( std::get<0>( *spells_relative[spell_selected] ), spell_level );
force_update_description = true;

Expand All @@ -1180,13 +1185,13 @@ static void change_spells( Character &character )
} else if( action == "TOGGLE_ALL_SPELL" ) {
if( toggle_spells_state == 0 ) {
toggle_spells_state = 1;
toggle_all_spells( -1 ); // unlearn all spells
toggle_all_spells( -1, character ); // unlearn all spells
} else if( toggle_spells_state == 1 ) {
toggle_spells_state = 2;
toggle_all_spells( 0 ); // sets all spells to the minimum level
toggle_all_spells( 0, character ); // sets all spells to the minimum level
} else {
toggle_spells_state = 0;
toggle_all_spells( -2 ); // max level
toggle_all_spells( -2, character ); // max level
}

} else if( action == "SHOW_ONLY_LEARNED" ) {
Expand Down
2 changes: 1 addition & 1 deletion src/handle_action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1684,7 +1684,7 @@ bool bionic::activate_spell( Character &caster ) const
// the return value tells us if the spell fails. if it has no spell it can't fail
return true;
}
spell sp = id->spell_on_activate->get_spell();
spell sp = id->spell_on_activate->get_spell( caster );
return caster.cast_spell( sp, true );
}

Expand Down
2 changes: 1 addition & 1 deletion src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6036,7 +6036,7 @@ nc_color item::color_in_inventory( const Character *const ch ) const
for( const std::string &spell_id_str : actor_ptr->spells ) {
const spell_id sp_id( spell_id_str );
if( player_character.magic->knows_spell( sp_id ) &&
!player_character.magic->get_spell( sp_id ).is_max_level() ) {
!player_character.magic->get_spell( sp_id ).is_max_level( player_character ) ) {
ret = c_yellow;
}
if( !player_character.magic->knows_spell( sp_id ) &&
Expand Down
Loading

0 comments on commit b411b72

Please sign in to comment.