Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support creating items from item groups with spells #64600

Merged
merged 2 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions data/json/flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -1065,11 +1065,6 @@
"type": "json_flag",
"//": "pain altering spells can't be resisted (like with the deadened trait)"
},
{
"id": "WITH_CONTAINER",
"type": "json_flag",
"//": "items spawned by spells are put in their containers."
},
{
"id": "SPAWN_WITH_DEATH_DROPS",
"type": "json_flag",
Expand Down
1 change: 0 additions & 1 deletion doc/MAGIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,6 @@ Flag | Description
`TARGET_TELEPORT` | Teleport spell changes to maximum range target with aoe as variation around target.
`UNSAFE_TELEPORT` | Teleport spell risks killing the caster or others.
`VERBAL` | Spell makes noise at caster location, mouth encumbrance affects fail %.
`WITH_CONTAINER` | Items spawned with container.
`WONDER` | This drastically alters the behavior of the parent spell: The spell itself doesn't cast, but the damage and range information are used to cast the `extra_effects`. A n number of `extra_effects` will be chosen to be cast at random, where n is the current damage of the spell (stacks with the `RANDOM_DAMAGE` flag), the message of the casted spell will also be displayed. If this spell's message is not wanted, make sure `message` is an empty string.


Expand Down
11 changes: 8 additions & 3 deletions src/debug_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,9 +749,14 @@ static void spell_description(
}

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

if( spl.has_flag( spell_flag::SPAWN_GROUP ) ) {
// todo: more user-friendly presentation
damage_string = string_format( _( "Spawn item group %1$s %2$d times" ), spl.effect_data(),
spl.damage( chrc ) );
} else {
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";
if( spl.has_flag( spell_flag::SPAWN_GROUP ) ) {
Expand Down
11 changes: 8 additions & 3 deletions src/magic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ std::string enum_to_string<spell_flag>( spell_flag data )
case spell_flag::RANDOM_CRITTER: return "RANDOM_CRITTER";
case spell_flag::MUTATE_TRAIT: return "MUTATE_TRAIT";
case spell_flag::PAIN_NORESIST: return "PAIN_NORESIST";
case spell_flag::WITH_CONTAINER: return "WITH_CONTAINER";
case spell_flag::SPAWN_GROUP: return "SPAWN_GROUP";
case spell_flag::IGNITE_FLAMMABLE: return "IGNITE_FLAMMABLE";
case spell_flag::NO_FAIL: return "NO_FAIL";
Expand Down Expand Up @@ -2254,8 +2253,14 @@ void spellcasting_callback::spell_info_text( const spell &sp, int width )
aoe_string = string_format( "%s: %d", _( "Variance" ), sp.aoe( pc ) );
}
} else if( sp.effect() == "spawn_item" ) {
damage_string = string_format( "%s %d %s", _( "Spawn" ), sp.damage( pc ),
item::nname( itype_id( sp.effect_data() ), sp.damage( pc ) ) );
if( sp.has_flag( spell_flag::SPAWN_GROUP ) ) {
// todo: more user-friendly presentation
damage_string = string_format( _( "Spawn item group %1$s %2$d times" ), sp.effect_data(),
sp.damage( pc ) );
} else {
damage_string = string_format( "%s %d %s", _( "Spawn" ), sp.damage( pc ),
item::nname( itype_id( sp.effect_data() ), sp.damage( pc ) ) );
}
} else if( sp.effect() == "summon" ) {
std::string monster_name = "FIXME";
if( sp.has_flag( spell_flag::SPAWN_GROUP ) ) {
Expand Down
1 change: 0 additions & 1 deletion src/magic.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ enum class spell_flag : int {
EXTRA_EFFECTS_FIRST, // the extra effects are cast before the main spell.
PAIN_NORESIST, // pain altering spells can't be resisted (like with the deadened trait)
NO_FAIL, // this spell cannot fail when you cast it
WITH_CONTAINER, // items spawned with container
SPAWN_GROUP, // spawn or summon from an item or monster group, instead of individual item/monster ID
IGNITE_FLAMMABLE, // if spell effect area has any thing flammable, a fire will be produced
MUST_HAVE_CLASS_TO_LEARN, // you can't learn the spell unless you already have the class.
Expand Down
56 changes: 32 additions & 24 deletions src/magic_spell_effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1060,33 +1060,41 @@ void spell_effect::directed_push( const spell &sp, Creature &caster, const tripo

void spell_effect::spawn_ethereal_item( const spell &sp, Creature &caster, const tripoint & )
{
item granted( sp.effect_data(), calendar::turn );
// Comestibles are never ethereal. Other spawned items are ethereal unless permanent and max level.
if( !granted.is_comestible() && !( sp.has_flag( spell_flag::PERMANENT ) &&
sp.is_max_level( caster ) ) &&
!sp.has_flag( spell_flag::PERMANENT_ALL_LEVELS ) ) {
granted.set_var( "ethereal", to_turns<int>( sp.duration_turns( caster ) ) );
granted.ethereal = true;
}
if( granted.count_by_charges() && sp.damage( caster ) > 0 ) {
granted.charges = sp.damage( caster );
if( !caster.is_avatar() ) {
debugmsg( "Spells that spawn items are only supported for the avatar, not for %s.",
caster.disp_name() );
return;
}
if( sp.has_flag( spell_flag::WITH_CONTAINER ) ) {
granted = granted.in_its_container();

std::vector<item> granted;

for( int i = 0; i < sp.damage( caster ); i++ ) {
if( sp.has_flag( spell_flag::SPAWN_GROUP ) ) {
std::vector<item> group_items = item_group::items_from( item_group_id( sp.effect_data() ),
calendar::turn );
granted.insert( granted.end(), group_items.begin(), group_items.end() );
} else {
granted.emplace_back( sp.effect_data(), calendar::turn );
}
}

avatar &player_character = get_avatar();
if( player_character.can_wear( granted ).success() ) {
granted.set_flag( json_flag_FIT );
player_character.wear_item( granted, false );
} else if( !player_character.has_wield_conflicts( granted ) &&
player_character.wield( granted, 0 ) ) {
// nothing to do
} else {
player_character.i_add( granted );
}
if( !granted.count_by_charges() ) {
for( int i = 1; i < sp.damage( caster ); i++ ) {
player_character.i_add( granted );
for( item &it : granted ) {
// Spawned items are ethereal unless permanent and max level. Comestibles are never ethereal.
if( !it.is_comestible() && !sp.has_flag( spell_flag::PERMANENT_ALL_LEVELS ) &&
!( sp.has_flag( spell_flag::PERMANENT ) && sp.is_max_level( caster ) ) ) {
it.set_var( "ethereal", to_turns<int>( sp.duration_turns( caster ) ) );
it.ethereal = true;
}

if( player_character.can_wear( it ).success() ) {
it.set_flag( json_flag_FIT );
player_character.wear_item( it, false );
} else if( !player_character.has_wield_conflicts( it ) &&
player_character.wield( it, 0 ) ) {
// nothing to do
} else {
player_character.i_add( it );
}
}
sp.make_sound( caster.pos(), caster );
Expand Down