diff --git a/data/json/flags.json b/data/json/flags.json index 2e727c742dec0..57dce0fbfc388 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -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", diff --git a/doc/MAGIC.md b/doc/MAGIC.md index fbcc8b0225ee9..5e3d2e95193dc 100644 --- a/doc/MAGIC.md +++ b/doc/MAGIC.md @@ -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. diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index 83b654cbc9ee3..af19059b5eecb 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -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 ) ) { diff --git a/src/magic.cpp b/src/magic.cpp index a1f11b82bd866..5b0f08957a863 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -147,7 +147,6 @@ std::string enum_to_string( 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"; @@ -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 ) ) { diff --git a/src/magic.h b/src/magic.h index c0b25824249a3..c2abfa62c5d30 100644 --- a/src/magic.h +++ b/src/magic.h @@ -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. diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index c5ab192ca3647..957f8f3adc7bc 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -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( 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 granted; + + for( int i = 0; i < sp.damage( caster ); i++ ) { + if( sp.has_flag( spell_flag::SPAWN_GROUP ) ) { + std::vector 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( 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 );