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

Optimize item spawn list creation #55099

Merged
merged 1 commit into from
Feb 5, 2022
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
90 changes: 49 additions & 41 deletions src/item_group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@

static const std::string null_item_id( "null" );

Item_spawn_data::ItemList Item_spawn_data::create(
const time_point &birthday, spawn_flags flags ) const
std::size_t Item_spawn_data::create( ItemList &list,
const time_point &birthday, spawn_flags flags ) const
{
RecursionList rec;
return create( birthday, rec, flags );
return create( list, birthday, rec, flags );
}

item Item_spawn_data::create_single( const time_point &birthday ) const
Expand All @@ -52,7 +52,9 @@ void Item_spawn_data::check_consistency() const
// Spawn ourselves with all possible items being definitely spawned, so as
// to verify e.g. that if a container item was specified it can actually
// contain what was wanted.
create( calendar::turn_zero, spawn_flags::maximized );
ItemList dummy_list;
dummy_list.reserve( 20 );
create( dummy_list, calendar::turn_zero, spawn_flags::maximized );
}

void Item_spawn_data::relic_generator::load( const JsonObject &jo )
Expand Down Expand Up @@ -103,7 +105,8 @@ static item_pocket::pocket_type guess_pocket_for( const item &container, const i
}

static void put_into_container(
Item_spawn_data::ItemList &items, const cata::optional<itype_id> &container_type,
Item_spawn_data::ItemList &items, std::size_t num_items,
const cata::optional<itype_id> &container_type,
time_point birthday, Item_spawn_data::overflow_behaviour on_overflow,
const std::string &context )
{
Expand All @@ -113,25 +116,26 @@ static void put_into_container(

// Randomly permute the list of items so that when some don't fit it's
// not always the ones at the end which are rejected.
std::shuffle( items.begin(), items.end(), rng_get_engine() );
cata_assert( items.size() >= num_items );
std::shuffle( items.end() - num_items, items.end(), rng_get_engine() );

item ctr( *container_type, birthday );
Item_spawn_data::ItemList excess;
for( const item &it : items ) {
if( ctr.can_contain( it ).success() ) {
const item_pocket::pocket_type pk_type = guess_pocket_for( ctr, it );
ctr.put_in( it, pk_type );
for( auto it = items.end() - num_items; it != items.end(); ++it ) {
if( ctr.can_contain( *it ).success() ) {
const item_pocket::pocket_type pk_type = guess_pocket_for( ctr, *it );
ctr.put_in( *it, pk_type );
} else {
switch( on_overflow ) {
case Item_spawn_data::overflow_behaviour::none:
debugmsg( "item %s does not fit in container %s when spawning item group %s. "
"This can be resolved either by changing the container or contents "
"to ensure that they fit, or by specifying an overflow behaviour via "
"\"on_overflow\" on the item group.",
it.typeId().str(), container_type->str(), context );
it->typeId().str(), container_type->str(), context );
break;
case Item_spawn_data::overflow_behaviour::spill:
excess.push_back( it );
excess.push_back( *it );
break;
case Item_spawn_data::overflow_behaviour::discard:
break;
Expand All @@ -142,7 +146,8 @@ static void put_into_container(
}
}
excess.push_back( ctr );
items = std::move( excess );
items.erase( items.end() - num_items, items.end() );
items.insert( items.end(), excess.begin(), excess.end() );
}

Single_item_creator::Single_item_creator( const std::string &_id, Type _type, int _probability,
Expand Down Expand Up @@ -203,10 +208,10 @@ item Single_item_creator::create_single( const time_point &birthday, RecursionLi
return tmp;
}

Item_spawn_data::ItemList Single_item_creator::create(
const time_point &birthday, RecursionList &rec, spawn_flags flags ) const
std::size_t Single_item_creator::create( ItemList &list,
const time_point &birthday, RecursionList &rec, spawn_flags flags ) const
{
ItemList result;
std::size_t prev_list_size = list.size();
int cnt = 1;
if( modifier ) {
auto modifier_count = modifier->count;
Expand All @@ -220,43 +225,44 @@ Item_spawn_data::ItemList Single_item_creator::create(
float spawn_rate = get_option<float>( "ITEM_SPAWNRATE" );
for( ; cnt > 0; cnt-- ) {
if( type == S_ITEM ) {
const item itm = create_single( birthday, rec );
item itm = create_single( birthday, rec );
if( flags & spawn_flags::use_spawn_rate && !itm.has_flag( STATIC( flag_id( "MISSION_ITEM" ) ) ) &&
rng_float( 0, 1 ) > spawn_rate ) {
continue;
}
if( !itm.is_null() ) {
result.push_back( itm );
list.emplace_back( std::move( itm ) );
}
} else {
item_group_id group_id( id );
if( std::find( rec.begin(), rec.end(), group_id ) != rec.end() ) {
debugmsg( "recursion in item spawn list %s", id.c_str() );
return result;
return list.size() - prev_list_size;
}
rec.push_back( group_id );
Item_spawn_data *isd = item_controller->get_group( group_id );
if( isd == nullptr ) {
debugmsg( "unknown item spawn list %s", id.c_str() );
return result;
return list.size() - prev_list_size;
}
ItemList tmplist = isd->create( birthday, rec, flags );
rec.erase( rec.end() - 1 );
std::size_t tmp_list_size = isd->create( list, birthday, rec, flags );
cata_assert( list.size() >= tmp_list_size );
rec.pop_back();
if( modifier ) {
for( auto &elem : tmplist ) {
modifier->modify( elem, "modifier for " + context() );
for( auto it = list.end() - tmp_list_size; it != list.end(); ++it ) {
modifier->modify( *it, "modifier for " + context() );
}
}
result.insert( result.end(), tmplist.begin(), tmplist.end() );
}
}
if( artifact ) {
for( item &it : result ) {
it.overwrite_relic( artifact->generate_relic( it.typeId() ) );
for( auto it = list.begin() + prev_list_size; it != list.end(); ++it ) {
it->overwrite_relic( artifact->generate_relic( it->typeId() ) );
}
}
put_into_container( result, container_item, birthday, on_overflow, context() );
return result;
const std::size_t items_created = list.size() - prev_list_size;
put_into_container( list, items_created, container_item, birthday, on_overflow, context() );
return list.size() - prev_list_size;
}

void Single_item_creator::check_consistency() const
Expand Down Expand Up @@ -530,7 +536,8 @@ void Item_modifier::modify( item &new_item, const std::string &context ) const
}

if( contents != nullptr ) {
Item_spawn_data::ItemList contentitems = contents->create( new_item.birthday() );
Item_spawn_data::ItemList contentitems;
contents->create( contentitems, new_item.birthday() );
for( const item &it : contentitems ) {
const item_pocket::pocket_type pk_type = guess_pocket_for( new_item, it );
new_item.put_in( it, pk_type );
Expand Down Expand Up @@ -654,17 +661,16 @@ void Item_group::add_entry( std::unique_ptr<Item_spawn_data> ptr )
items.push_back( std::move( ptr ) );
}

Item_spawn_data::ItemList Item_group::create(
const time_point &birthday, RecursionList &rec, spawn_flags flags ) const
std::size_t Item_group::create( Item_spawn_data::ItemList &list,
const time_point &birthday, RecursionList &rec, spawn_flags flags ) const
{
ItemList result;
std::size_t prev_list_size = list.size();
if( type == G_COLLECTION ) {
for( const auto &elem : items ) {
if( !( flags & spawn_flags::maximized ) && rng( 0, 99 ) >= elem->get_probability( false ) ) {
continue;
}
ItemList tmp = elem->create( birthday, rec, flags );
result.insert( result.end(), tmp.begin(), tmp.end() );
elem->create( list, birthday, rec, flags );
}
} else if( type == G_DISTRIBUTION ) {
int p = rng( 0, sum_prob - 1 );
Expand All @@ -676,14 +682,13 @@ Item_spawn_data::ItemList Item_group::create(
if( ( ev_based && prob == 0 ) || p >= 0 ) {
continue;
}
ItemList tmp = elem->create( birthday, rec, flags );
result.insert( result.end(), tmp.begin(), tmp.end() );
elem->create( list, birthday, rec, flags );
break;
}
}
put_into_container( result, container_item, birthday, on_overflow, context() );

return result;
const std::size_t items_created = list.size() - prev_list_size;
put_into_container( list, items_created, container_item, birthday, on_overflow, context() );
return list.size() - prev_list_size;
}

item Item_group::create_single( const time_point &birthday, RecursionList &rec ) const
Expand Down Expand Up @@ -793,7 +798,10 @@ item_group::ItemList item_group::items_from( const item_group_id &group_id,
if( group == nullptr ) {
return ItemList();
}
return group->create( birthday, flags );
ItemList result;
result.reserve( 20 );
group->create( result, birthday, flags );
return result;
}

item_group::ItemList item_group::items_from( const item_group_id &group_id )
Expand Down
15 changes: 10 additions & 5 deletions src/item_group.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,16 @@ class Item_spawn_data
/**
* Create a list of items. The create list might be empty.
* No item of it will be the null item.
* @param[out] list New items are appended to the list
* @param[in] birthday All items have that value as birthday.
* @param[out] rec Recursion list, output goes here.
* @param[in] spawn_flags Extra information to change how items are spawned.
* @return The number of new items appended to the list
*/
virtual ItemList create( const time_point &birthday, RecursionList &rec,
spawn_flags = spawn_flags::none ) const = 0;
ItemList create( const time_point &birthday, spawn_flags = spawn_flags::none ) const;
virtual std::size_t create( ItemList &list, const time_point &birthday, RecursionList &rec,
spawn_flags = spawn_flags::none ) const = 0;
std::size_t create( ItemList &list, const time_point &birthday,
spawn_flags = spawn_flags::none ) const;
/**
* The same as create, but create a single item only.
* The returned item might be a null item!
Expand Down Expand Up @@ -321,7 +324,8 @@ class Single_item_creator : public Item_spawn_data

void inherit_ammo_mag_chances( int ammo, int mag );

ItemList create( const time_point &birthday, RecursionList &rec, spawn_flags ) const override;
std::size_t create( ItemList &list, const time_point &birthday, RecursionList &rec,
spawn_flags ) const override;
item create_single( const time_point &birthday, RecursionList &rec ) const override;
void check_consistency() const override;
bool remove_item( const itype_id &itemid ) override;
Expand Down Expand Up @@ -369,7 +373,8 @@ class Item_group : public Item_spawn_data
*/
void add_entry( std::unique_ptr<Item_spawn_data> ptr );

ItemList create( const time_point &birthday, RecursionList &rec, spawn_flags ) const override;
std::size_t create( ItemList &list, const time_point &birthday, RecursionList &rec,
spawn_flags ) const override;
item create_single( const time_point &birthday, RecursionList &rec ) const override;
void check_consistency() const override;
bool remove_item( const itype_id &itemid ) override;
Expand Down
6 changes: 4 additions & 2 deletions src/mapgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2151,8 +2151,10 @@ class jmapgen_loot : public jmapgen_piece
const std::string &/*context*/ ) const override {
if( rng( 0, 99 ) < chance ) {
const Item_spawn_data *const isd = &result_group;
const std::vector<item> spawn = isd->create( calendar::start_of_cataclysm,
spawn_flags::use_spawn_rate );
std::vector<item> spawn;
spawn.reserve( 20 );
isd->create( spawn, calendar::start_of_cataclysm,
spawn_flags::use_spawn_rate );
dat.m.spawn_items( tripoint( rng( x.val, x.valmax ), rng( y.val, y.valmax ),
dat.m.get_abs_sub().z ), spawn );
}
Expand Down